summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authornoamsml@chromium.org <noamsml@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-01 17:39:53 +0000
committernoamsml@chromium.org <noamsml@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-01 17:39:53 +0000
commit21df1696392acf9543ad6e3b3d3e7f0ab7120f4d (patch)
treeac58f8b6ba23a96aa1501f0ad0aeee4bd4491a13 /net
parent69fdab209f919fd0538b2c0c0fcafe0255836f37 (diff)
downloadchromium_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.cc23
-rw-r--r--net/dns/mdns_cache.h10
-rw-r--r--net/dns/mdns_cache_unittest.cc91
-rw-r--r--net/dns/mdns_client.h3
-rw-r--r--net/dns/mdns_client_impl.cc109
-rw-r--r--net/dns/mdns_client_impl.h21
-rw-r--r--net/dns/mdns_client_unittest.cc170
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.