diff options
author | noamsml@chromium.org <noamsml@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-01 17:39:53 +0000 |
---|---|---|
committer | noamsml@chromium.org <noamsml@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-01 17:39:53 +0000 |
commit | 21df1696392acf9543ad6e3b3d3e7f0ab7120f4d (patch) | |
tree | ac58f8b6ba23a96aa1501f0ad0aeee4bd4491a13 /net | |
parent | 69fdab209f919fd0538b2c0c0fcafe0255836f37 (diff) | |
download | chromium_src-21df1696392acf9543ad6e3b3d3e7f0ab7120f4d.zip chromium_src-21df1696392acf9543ad6e3b3d3e7f0ab7120f4d.tar.gz chromium_src-21df1696392acf9543ad6e3b3d3e7f0ab7120f4d.tar.bz2 |
Add NSEC record support for MDnsClient
Add support for NSEC records to MDnsClient, which can be used to deny the
existence of records.
BUG=255232
Review URL: https://chromiumcodereview.appspot.com/17747004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@209452 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/dns/mdns_cache.cc | 23 | ||||
-rw-r--r-- | net/dns/mdns_cache.h | 10 | ||||
-rw-r--r-- | net/dns/mdns_cache_unittest.cc | 91 | ||||
-rw-r--r-- | net/dns/mdns_client.h | 3 | ||||
-rw-r--r-- | net/dns/mdns_client_impl.cc | 109 | ||||
-rw-r--r-- | net/dns/mdns_client_impl.h | 21 | ||||
-rw-r--r-- | net/dns/mdns_client_unittest.cc | 170 |
7 files changed, 379 insertions, 48 deletions
diff --git a/net/dns/mdns_cache.cc b/net/dns/mdns_cache.cc index f210d0ab..b430f95 100644 --- a/net/dns/mdns_cache.cc +++ b/net/dns/mdns_cache.cc @@ -44,12 +44,12 @@ MDnsCache::Key::~Key() { } bool MDnsCache::Key::operator<(const MDnsCache::Key& key) const { - if (type_ != key.type_) - return type_ < key.type_; - if (name_ != key.name_) return name_ < key.name_; + if (type_ != key.type_) + return type_ < key.type_; + if (optional_ != key.optional_) return optional_ < key.optional_; return false; // keys are equal @@ -154,8 +154,8 @@ void MDnsCache::FindDnsRecords(unsigned type, RecordMap::const_iterator i = mdns_cache_.lower_bound(Key(type, name, "")); for (; i != mdns_cache_.end(); ++i) { - if (i->first.type() != type || - (!name.empty() && i->first.name() != name)) { + if (i->first.name() != name || + (type != 0 && i->first.type() != type)) { break; } @@ -168,6 +168,19 @@ void MDnsCache::FindDnsRecords(unsigned type, } } +scoped_ptr<const RecordParsed> MDnsCache::RemoveRecord( + const RecordParsed* record) { + Key key = Key::CreateFor(record); + RecordMap::iterator found = mdns_cache_.find(key); + + if (found != mdns_cache_.end() && found->second == record) { + mdns_cache_.erase(key); + return scoped_ptr<const RecordParsed>(record); + } + + return scoped_ptr<const RecordParsed>(); +} + // static std::string MDnsCache::GetOptionalFieldForRecord( const RecordParsed* record) { diff --git a/net/dns/mdns_cache.h b/net/dns/mdns_cache.h index a73a50e..27a14d8 100644 --- a/net/dns/mdns_cache.h +++ b/net/dns/mdns_cache.h @@ -24,7 +24,7 @@ class RecordParsed; // guaranteed not to return expired records. It also has facilities for timely // record expiration. class NET_EXPORT_PRIVATE MDnsCache { -public: + public: // Key type for the record map. It is a 3-tuple of type, name and optional // value ordered by type, then name, then optional value. This allows us to // query for all records of a certain type and name, while also allowing us @@ -71,7 +71,7 @@ public: const RecordParsed* LookupKey(const Key& key); // Return records with type |type| and name |name|. Expired records will not - // be returned. If |name| is empty, return all records with type |type|. + // be returned. If |type| is zero, return all records with name |name|. void FindDnsRecords(unsigned type, const std::string& name, std::vector<const RecordParsed*>* records, @@ -87,9 +87,13 @@ public: // base::Time when the cache is empty. base::Time next_expiration() const { return next_expiration_; } + // Remove a record from the cache. Returns a scoped version of the pointer + // passed in if it was removed, scoped null otherwise. + scoped_ptr<const RecordParsed> RemoveRecord(const RecordParsed* record); + void Clear(); -private: + private: typedef std::map<Key, const RecordParsed*> RecordMap; // Get the effective expiration of a cache entry, based on its creation time diff --git a/net/dns/mdns_cache_unittest.cc b/net/dns/mdns_cache_unittest.cc index 69de38a..955fdbd 100644 --- a/net/dns/mdns_cache_unittest.cc +++ b/net/dns/mdns_cache_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <algorithm> + #include "base/bind.h" #include "net/dns/dns_response.h" #include "net/dns/dns_test_util.h" @@ -72,6 +74,39 @@ static const uint8 kTestResponsesSameAnswers[] = { 0x5f, 0x79, }; +static const uint8 kTestResponseTwoRecords[] = { + // Answer 1 + // ghs.l.google.com in DNS format. (A) + 0x03, 'g', 'h', 's', + 0x01, 'l', + 0x06, 'g', 'o', 'o', 'g', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x01, // TYPE is A. + 0x00, 0x01, // CLASS is IN. + 0x00, 0x00, // TTL (4 bytes) is 53 seconds. + 0x00, 0x35, + 0x00, 0x04, // RDLENGTH is 4 bytes. + 0x4a, 0x7d, // RDATA is the IP: 74.125.95.121 + 0x5f, 0x79, + // Answer 2 + // ghs.l.google.com in DNS format. (AAAA) + 0x03, 'g', 'h', 's', + 0x01, 'l', + 0x06, 'g', 'o', 'o', 'g', 'l', 'e', + 0x03, 'c', 'o', 'm', + 0x00, + 0x00, 0x1c, // TYPE is AAA. + 0x00, 0x01, // CLASS is IN. + 0x00, 0x00, // TTL (4 bytes) is 53 seconds. + 0x00, 0x35, + 0x00, 0x10, // RDLENGTH is 4 bytes. + 0x4a, 0x7d, 0x4a, 0x7d, + 0x5f, 0x79, 0x5f, 0x79, + 0x5f, 0x79, 0x5f, 0x79, + 0x5f, 0x79, 0x5f, 0x79, +}; + class RecordRemovalMock { public: MOCK_METHOD1(OnRecordRemoved, void(const RecordParsed*)); @@ -231,4 +266,58 @@ TEST_F(MDnsCacheTest, RecordPreemptExpirationTime) { EXPECT_EQ(default_time_ + ttl1, cache_.next_expiration()); } -} // namespace net +TEST_F(MDnsCacheTest, AnyRRType) { + DnsRecordParser parser(kTestResponseTwoRecords, + sizeof(kTestResponseTwoRecords), + 0); + + scoped_ptr<const RecordParsed> record1; + scoped_ptr<const RecordParsed> record2; + std::vector<const RecordParsed*> results; + + record1 = RecordParsed::CreateFrom(&parser, default_time_); + record2 = RecordParsed::CreateFrom(&parser, default_time_); + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass())); + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record2.Pass())); + + cache_.FindDnsRecords(0, "ghs.l.google.com", &results, default_time_); + + EXPECT_EQ(2u, results.size()); + EXPECT_EQ(default_time_, results.front()->time_created()); + + EXPECT_EQ("ghs.l.google.com", results[0]->name()); + EXPECT_EQ("ghs.l.google.com", results[1]->name()); + EXPECT_EQ(dns_protocol::kTypeA, + std::min(results[0]->type(), results[1]->type())); + EXPECT_EQ(dns_protocol::kTypeAAAA, + std::max(results[0]->type(), results[1]->type())); +} + +TEST_F(MDnsCacheTest, RemoveRecord) { + DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram), + sizeof(dns_protocol::Header)); + parser.SkipQuestion(); + + scoped_ptr<const RecordParsed> record1; + std::vector<const RecordParsed*> results; + + record1 = RecordParsed::CreateFrom(&parser, default_time_); + EXPECT_EQ(MDnsCache::RecordAdded, cache_.UpdateDnsRecord(record1.Pass())); + + cache_.FindDnsRecords(dns_protocol::kTypeCNAME, "codereview.chromium.org", + &results, default_time_); + + EXPECT_EQ(1u, results.size()); + + scoped_ptr<const RecordParsed> record_out = + cache_.RemoveRecord(results.front()); + + EXPECT_EQ(record_out.get(), results.front()); + + cache_.FindDnsRecords(dns_protocol::kTypeCNAME, "codereview.chromium.org", + &results, default_time_); + + EXPECT_EQ(0u, results.size()); +} + +} // namespace net diff --git a/net/dns/mdns_client.h b/net/dns/mdns_client.h index f1f5adb..9e3bdb7 100644 --- a/net/dns/mdns_client.h +++ b/net/dns/mdns_client.h @@ -128,8 +128,7 @@ class NET_EXPORT MDnsClient { public: virtual ~MDnsClient() {} - // Create listener object for RRType |rrtype| and name |name|. If |name| is - // an empty string, listen to all notification of type |rrtype|. + // Create listener object for RRType |rrtype| and name |name|. virtual scoped_ptr<MDnsListener> CreateListener( uint16 rrtype, const std::string& name, diff --git a/net/dns/mdns_client_impl.cc b/net/dns/mdns_client_impl.cc index 2c379e7..7388e75 100644 --- a/net/dns/mdns_client_impl.cc +++ b/net/dns/mdns_client_impl.cc @@ -13,8 +13,14 @@ #include "net/base/net_log.h" #include "net/base/rand_callback.h" #include "net/dns/dns_protocol.h" +#include "net/dns/record_rdata.h" #include "net/udp/datagram_socket.h" +// TODO(gene): Remove this temporary method of disabling NSEC support once it +// becomes clear whether this feature should be +// supported. http://crbug.com/255232 +#define ENABLE_NSEC + namespace net { namespace { @@ -284,16 +290,52 @@ void MDnsClientImpl::Core::HandlePacket(DnsResponse* response, for (std::map<MDnsCache::Key, MDnsListener::UpdateType>::iterator i = update_keys.begin(); i != update_keys.end(); i++) { const RecordParsed* record = cache_.LookupKey(i->first); - if (record) { - AlertListeners(i->second, ListenerKey(record->type(), record->name()), - record); - // Alert listeners listening only for rrtype and not for name. - AlertListeners(i->second, ListenerKey(record->type(), ""), + if (!record) + continue; + + if (record->type() == dns_protocol::kTypeNSEC) { +#if defined(ENABLE_NSEC) + NotifyNsecRecord(record); +#endif + } else { + AlertListeners(i->second, ListenerKey(record->name(), record->type()), record); } } } +void MDnsClientImpl::Core::NotifyNsecRecord(const RecordParsed* record) { + DCHECK_EQ(dns_protocol::kTypeNSEC, record->type()); + const NsecRecordRdata* rdata = record->rdata<NsecRecordRdata>(); + DCHECK(rdata); + + // Remove all cached records matching the nonexistent RR types. + std::vector<const RecordParsed*> records_to_remove; + + cache_.FindDnsRecords(0, record->name(), &records_to_remove, + base::Time::Now()); + + for (std::vector<const RecordParsed*>::iterator i = records_to_remove.begin(); + i != records_to_remove.end(); i++) { + if ((*i)->type() == dns_protocol::kTypeNSEC) + continue; + if (!rdata->GetBit((*i)->type())) { + scoped_ptr<const RecordParsed> record_removed = cache_.RemoveRecord((*i)); + DCHECK(record_removed); + OnRecordRemoved(record_removed.get()); + } + } + + // Alert all listeners waiting for the nonexistent RR types. + ListenerMap::iterator i = + listeners_.upper_bound(ListenerKey(record->name(), 0)); + for (; i != listeners_.end() && i->first.first == record->name(); i++) { + if (!rdata->GetBit(i->first.second)) { + FOR_EACH_OBSERVER(MDnsListenerImpl, *i->second, AlertNsecRecord()); + } + } +} + void MDnsClientImpl::Core::OnConnectionError(int error) { // TODO(noamsml): On connection error, recreate connection and flush cache. } @@ -311,7 +353,7 @@ void MDnsClientImpl::Core::AlertListeners( void MDnsClientImpl::Core::AddListener( MDnsListenerImpl* listener) { - ListenerKey key(listener->GetType(), listener->GetName()); + ListenerKey key(listener->GetName(), listener->GetType()); std::pair<ListenerMap::iterator, bool> observer_insert_result = listeners_.insert( make_pair(key, static_cast<ObserverList<MDnsListenerImpl>*>(NULL))); @@ -327,7 +369,7 @@ void MDnsClientImpl::Core::AddListener( } void MDnsClientImpl::Core::RemoveListener(MDnsListenerImpl* listener) { - ListenerKey key(listener->GetType(), listener->GetName()); + ListenerKey key(listener->GetName(), listener->GetType()); ListenerMap::iterator observer_list_iterator = listeners_.find(key); DCHECK(observer_list_iterator != listeners_.end()); @@ -337,8 +379,19 @@ void MDnsClientImpl::Core::RemoveListener(MDnsListenerImpl* listener) { // Remove the observer list from the map if it is empty if (observer_list_iterator->second->size() == 0) { - delete observer_list_iterator->second; - listeners_.erase(observer_list_iterator); + // Schedule the actual removal for later in case the listener removal + // happens while iterating over the observer list. + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind( + &MDnsClientImpl::Core::CleanupObserverList, AsWeakPtr(), key)); + } +} + +void MDnsClientImpl::Core::CleanupObserverList(const ListenerKey& key) { + ListenerMap::iterator found = listeners_.find(key); + if (found != listeners_.end() && found->second->size() == 0) { + delete found->second; + listeners_.erase(found); } } @@ -370,10 +423,7 @@ void MDnsClientImpl::Core::DoCleanup() { void MDnsClientImpl::Core::OnRecordRemoved( const RecordParsed* record) { AlertListeners(MDnsListener::RECORD_REMOVED, - ListenerKey(record->type(), record->name()), record); - // Alert listeners listening only for rrtype and not for name. - AlertListeners(MDnsListener::RECORD_REMOVED, ListenerKey(record->type(), ""), - record); + ListenerKey(record->name(), record->type()), record); } void MDnsClientImpl::Core::QueryCache( @@ -481,6 +531,11 @@ void MDnsListenerImpl::AlertDelegate(MDnsListener::UpdateType update_type, delegate_->OnRecordUpdate(update_type, record); } +void MDnsListenerImpl::AlertNsecRecord() { + DCHECK(started_); + delegate_->OnNsecRecord(name_, rrtype_); +} + MDnsTransactionImpl::MDnsTransactionImpl( uint16 rrtype, const std::string& name, @@ -541,8 +596,12 @@ void MDnsTransactionImpl::TriggerCallback(MDnsTransaction::Result result, // the callback can delete the transaction. MDnsTransaction::ResultCallback callback = callback_; - if (flags_ & MDnsTransaction::SINGLE_RESULT) + // Reset the transaction if it expects a single result, or if the result + // is a final one (everything except for a record). + if (flags_ & MDnsTransaction::SINGLE_RESULT || + result != MDnsTransaction::RESULT_RECORD) { Reset(); + } callback.Run(result, record); } @@ -563,17 +622,11 @@ void MDnsTransactionImpl::OnRecordUpdate(MDnsListener::UpdateType update, void MDnsTransactionImpl::SignalTransactionOver() { DCHECK(started_); - base::WeakPtr<MDnsTransactionImpl> weak_this = AsWeakPtr(); - if (flags_ & MDnsTransaction::SINGLE_RESULT) { TriggerCallback(MDnsTransaction::RESULT_NO_RESULTS, NULL); } else { TriggerCallback(MDnsTransaction::RESULT_DONE, NULL); } - - if (weak_this) { - weak_this->Reset(); - } } void MDnsTransactionImpl::ServeRecordsFromCache() { @@ -587,6 +640,20 @@ void MDnsTransactionImpl::ServeRecordsFromCache() { weak_this->TriggerCallback(MDnsTransaction::RESULT_RECORD, records.front()); } + +#if defined(ENABLE_NSEC) + if (records.empty()) { + DCHECK(weak_this); + client_->core()->QueryCache(dns_protocol::kTypeNSEC, name_, &records); + if (!records.empty()) { + const NsecRecordRdata* rdata = + records.front()->rdata<NsecRecordRdata>(); + DCHECK(rdata); + if (!rdata->GetBit(rrtype_)) + weak_this->TriggerCallback(MDnsTransaction::RESULT_NSEC, NULL); + } + } +#endif } } @@ -610,7 +677,7 @@ bool MDnsTransactionImpl::QueryAndListen() { } void MDnsTransactionImpl::OnNsecRecord(const std::string& name, unsigned type) { - // TODO(noamsml): NSEC records not yet implemented + TriggerCallback(RESULT_NSEC, NULL); } void MDnsTransactionImpl::OnCachePurged() { diff --git a/net/dns/mdns_client_impl.h b/net/dns/mdns_client_impl.h index cb6a0ae..667632b 100644 --- a/net/dns/mdns_client_impl.h +++ b/net/dns/mdns_client_impl.h @@ -99,8 +99,10 @@ class MDnsListenerImpl; class MDnsClientImpl : public MDnsClient { public: - // The core object exists while the MDnsClient is listening, and is - // deleted whenever the number of listeners reaches zero. + // The core object exists while the MDnsClient is listening, and is deleted + // whenever the number of listeners reaches zero. The deletion happens + // asychronously, so destroying the last listener does not immediately + // invalidate the core. class Core : public base::SupportsWeakPtr<Core>, MDnsConnection::Delegate { public: Core(MDnsClientImpl* client, @@ -113,8 +115,7 @@ class MDnsClientImpl : public MDnsClient { // Send a query with a specific rrtype and name. Returns true on success. bool SendQuery(uint16 rrtype, std::string name); - // Add/remove a listener to the list of listener. May cause network traffic - // if listener is active. + // Add/remove a listener to the list of listeners. void AddListener(MDnsListenerImpl* listener); void RemoveListener(MDnsListenerImpl* listener); @@ -128,7 +129,7 @@ class MDnsClientImpl : public MDnsClient { virtual void OnConnectionError(int error) OVERRIDE; private: - typedef std::pair<uint16, std::string> ListenerKey; + typedef std::pair<std::string, uint16> ListenerKey; typedef std::map<ListenerKey, ObserverList<MDnsListenerImpl>* > ListenerMap; @@ -145,6 +146,12 @@ class MDnsClientImpl : public MDnsClient { // Callback for when a record is removed from the cache. void OnRecordRemoved(const RecordParsed* record); + void NotifyNsecRecord(const RecordParsed* record); + + // Delete and erase the observer list for |key|. Only deletes the observer + // list if is empty. + void CleanupObserverList(const ListenerKey& key); + ListenerMap listeners_; MDnsClientImpl* client_; @@ -220,6 +227,10 @@ class MDnsListenerImpl : public MDnsListener, // Alert the delegate of a record update. void AlertDelegate(MDnsListener::UpdateType update_type, const RecordParsed* record_parsed); + + // Alert the delegate of the existence of an Nsec record. + void AlertNsecRecord(); + private: uint16 rrtype_; std::string name_; diff --git a/net/dns/mdns_client_unittest.cc b/net/dns/mdns_client_unittest.cc index 8329089..ee211ef 100644 --- a/net/dns/mdns_client_unittest.cc +++ b/net/dns/mdns_client_unittest.cc @@ -264,6 +264,52 @@ const char kSamplePacketAdditionalOnly[] = { 0xc0, 0x0c, }; +const char kSamplePacketNsec[] = { + // Header + 0x00, 0x00, // ID is zeroed out + 0x81, 0x80, // Standard query response, RA, no error + 0x00, 0x00, // No questions (for simplicity) + 0x00, 0x01, // 1 RR (answers) + 0x00, 0x00, // 0 authority RRs + 0x00, 0x00, // 0 additional RRs + + // Answer 1 + 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', + 0x04, '_', 't', 'c', 'p', + 0x05, 'l', 'o', 'c', 'a', 'l', + 0x00, + 0x00, 0x2f, // TYPE is NSEC. + 0x00, 0x01, // CLASS is IN. + 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds. + 0x24, 0x74, + 0x00, 0x06, // RDLENGTH is 6 bytes. + 0xc0, 0x0c, + 0x00, 0x02, 0x00, 0x08 // Only A record present +}; + +const char kSamplePacketAPrivet[] = { + // Header + 0x00, 0x00, // ID is zeroed out + 0x81, 0x80, // Standard query response, RA, no error + 0x00, 0x00, // No questions (for simplicity) + 0x00, 0x01, // 1 RR (answers) + 0x00, 0x00, // 0 authority RRs + 0x00, 0x00, // 0 additional RRs + + // Answer 1 + 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', + 0x04, '_', 't', 'c', 'p', + 0x05, 'l', 'o', 'c', 'a', 'l', + 0x00, + 0x00, 0x01, // TYPE is NSEC. + 0x00, 0x01, // CLASS is IN. + 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds. + 0x24, 0x74, + 0x00, 0x04, // RDLENGTH is 4 bytes. + 0xc0, 0x0c, + 0x00, 0x02, +}; + class PtrRecordCopyContainer { public: PtrRecordCopyContainer() {} @@ -392,7 +438,6 @@ void MDnsTest::Stop() { TEST_F(MDnsTest, PassiveListeners) { StrictMock<MockListenerDelegate> delegate_privet; StrictMock<MockListenerDelegate> delegate_printer; - StrictMock<MockListenerDelegate> delegate_ptr; PtrRecordCopyContainer record_privet; PtrRecordCopyContainer record_printer; @@ -401,12 +446,9 @@ TEST_F(MDnsTest, PassiveListeners) { dns_protocol::kTypePTR, "_privet._tcp.local", &delegate_privet); scoped_ptr<MDnsListener> listener_printer = test_client_->CreateListener( dns_protocol::kTypePTR, "_printer._tcp.local", &delegate_printer); - scoped_ptr<MDnsListener> listener_ptr = test_client_->CreateListener( - dns_protocol::kTypePTR, "", &delegate_ptr); ASSERT_TRUE(listener_privet->Start()); ASSERT_TRUE(listener_printer->Start()); - ASSERT_TRUE(listener_ptr->Start()); ASSERT_TRUE(test_client_->IsListeningForTests()); @@ -424,8 +466,6 @@ TEST_F(MDnsTest, PassiveListeners) { &record_printer, &PtrRecordCopyContainer::SaveWithDummyArg)); - EXPECT_CALL(delegate_ptr, OnRecordUpdate(MDnsListener::RECORD_ADDED, _)) - .Times(Exactly(2)); SimulatePacketReceive(kSamplePacket1, sizeof(kSamplePacket1)); SimulatePacketReceive(kSamplePacket1, sizeof(kSamplePacket1)); @@ -440,11 +480,6 @@ TEST_F(MDnsTest, PassiveListeners) { listener_printer.reset(); ASSERT_TRUE(test_client_->IsListeningForTests()); - - EXPECT_CALL(delegate_ptr, OnRecordUpdate(MDnsListener::RECORD_ADDED, _)) - .Times(Exactly(2)); - - SimulatePacketReceive(kSamplePacket2, sizeof(kSamplePacket2)); } TEST_F(MDnsTest, PassiveListenersCacheCleanup) { @@ -834,6 +869,119 @@ TEST_F(MDnsTest, DoubleRecordDisagreeing) { EXPECT_EQ("2.3.4.5", IPAddressToString(address)); } +TEST_F(MDnsTest, NsecWithListener) { + StrictMock<MockListenerDelegate> delegate_privet; + scoped_ptr<MDnsListener> listener_privet = test_client_->CreateListener( + dns_protocol::kTypeA, "_privet._tcp.local", &delegate_privet); + + // Test to make sure nsec callback is NOT called for PTR + // (which is marked as existing). + StrictMock<MockListenerDelegate> delegate_privet2; + scoped_ptr<MDnsListener> listener_privet2 = test_client_->CreateListener( + dns_protocol::kTypePTR, "_privet._tcp.local", &delegate_privet2); + + ASSERT_TRUE(listener_privet->Start()); + + EXPECT_CALL(delegate_privet, + OnNsecRecord("_privet._tcp.local", dns_protocol::kTypeA)); + + SimulatePacketReceive(kSamplePacketNsec, + sizeof(kSamplePacketNsec)); +} + +TEST_F(MDnsTest, NsecWithTransactionFromNetwork) { + scoped_ptr<MDnsTransaction> transaction_privet = + test_client_->CreateTransaction( + dns_protocol::kTypeA, "_privet._tcp.local", + MDnsTransaction::QUERY_NETWORK | + MDnsTransaction::QUERY_CACHE | + MDnsTransaction::SINGLE_RESULT, + base::Bind(&MDnsTest::MockableRecordCallback, + base::Unretained(this))); + + EXPECT_CALL(*socket_factory_, OnSendTo(_)) + .Times(2); + + ASSERT_TRUE(transaction_privet->Start()); + + EXPECT_CALL(*this, + MockableRecordCallback(MDnsTransaction::RESULT_NSEC, NULL)); + + SimulatePacketReceive(kSamplePacketNsec, + sizeof(kSamplePacketNsec)); +} + +TEST_F(MDnsTest, NsecWithTransactionFromCache) { + // Force mDNS to listen. + StrictMock<MockListenerDelegate> delegate_irrelevant; + scoped_ptr<MDnsListener> listener_irrelevant = + test_client_->CreateListener(dns_protocol::kTypePTR, "_privet._tcp.local", + &delegate_irrelevant); + listener_irrelevant->Start(); + + SimulatePacketReceive(kSamplePacketNsec, + sizeof(kSamplePacketNsec)); + + EXPECT_CALL(*this, + MockableRecordCallback(MDnsTransaction::RESULT_NSEC, NULL)); + + scoped_ptr<MDnsTransaction> transaction_privet_a = + test_client_->CreateTransaction( + dns_protocol::kTypeA, "_privet._tcp.local", + MDnsTransaction::QUERY_NETWORK | + MDnsTransaction::QUERY_CACHE | + MDnsTransaction::SINGLE_RESULT, + base::Bind(&MDnsTest::MockableRecordCallback, + base::Unretained(this))); + + ASSERT_TRUE(transaction_privet_a->Start()); + + // Test that a PTR transaction does NOT consider the same NSEC record to be a + // valid answer to the query + + scoped_ptr<MDnsTransaction> transaction_privet_ptr = + test_client_->CreateTransaction( + dns_protocol::kTypePTR, "_privet._tcp.local", + MDnsTransaction::QUERY_NETWORK | + MDnsTransaction::QUERY_CACHE | + MDnsTransaction::SINGLE_RESULT, + base::Bind(&MDnsTest::MockableRecordCallback, + base::Unretained(this))); + + EXPECT_CALL(*socket_factory_, OnSendTo(_)) + .Times(2); + + ASSERT_TRUE(transaction_privet_ptr->Start()); +} + +TEST_F(MDnsTest, NsecConflictRemoval) { + StrictMock<MockListenerDelegate> delegate_privet; + scoped_ptr<MDnsListener> listener_privet = test_client_->CreateListener( + dns_protocol::kTypeA, "_privet._tcp.local", &delegate_privet); + + ASSERT_TRUE(listener_privet->Start()); + + const RecordParsed* record1; + const RecordParsed* record2; + + EXPECT_CALL(delegate_privet, OnRecordUpdate(MDnsListener::RECORD_ADDED, _)) + .WillOnce(SaveArg<1>(&record1)); + + SimulatePacketReceive(kSamplePacketAPrivet, + sizeof(kSamplePacketAPrivet)); + + EXPECT_CALL(delegate_privet, OnRecordUpdate(MDnsListener::RECORD_REMOVED, _)) + .WillOnce(SaveArg<1>(&record2)); + + EXPECT_CALL(delegate_privet, + OnNsecRecord("_privet._tcp.local", dns_protocol::kTypeA)); + + SimulatePacketReceive(kSamplePacketNsec, + sizeof(kSamplePacketNsec)); + + EXPECT_EQ(record1, record2); +} + // Note: These tests assume that the ipv4 socket will always be created first. // This is a simplifying assumption based on the way the code works now. |