summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/dns/mdns_cache.cc22
-rw-r--r--net/dns/mdns_cache_unittest.cc66
-rw-r--r--net/dns/mdns_client_unittest.cc55
3 files changed, 133 insertions, 10 deletions
diff --git a/net/dns/mdns_cache.cc b/net/dns/mdns_cache.cc
index b430f95..010a34f 100644
--- a/net/dns/mdns_cache.cc
+++ b/net/dns/mdns_cache.cc
@@ -4,6 +4,7 @@
#include "net/dns/mdns_cache.h"
+#include <algorithm>
#include <utility>
#include "base/stl_util.h"
@@ -89,31 +90,32 @@ const RecordParsed* MDnsCache::LookupKey(const Key& key) {
MDnsCache::UpdateType MDnsCache::UpdateDnsRecord(
scoped_ptr<const RecordParsed> record) {
- UpdateType type = NoChange;
-
Key cache_key = Key::CreateFor(record.get());
- base::Time expiration = GetEffectiveExpiration(record.get());
- if (next_expiration_ == base::Time() || expiration < next_expiration_) {
- next_expiration_ = expiration;
- }
+ // Ignore "goodbye" packets for records not in cache.
+ if (record->ttl() == 0 && mdns_cache_.find(cache_key) == mdns_cache_.end())
+ return NoChange;
+
+ base::Time new_expiration = GetEffectiveExpiration(record.get());
+ if (next_expiration_ != base::Time())
+ new_expiration = std::min(new_expiration, next_expiration_);
std::pair<RecordMap::iterator, bool> insert_result =
mdns_cache_.insert(std::make_pair(cache_key, (const RecordParsed*)NULL));
-
+ UpdateType type = NoChange;
if (insert_result.second) {
type = RecordAdded;
- insert_result.first->second = record.release();
} else {
const RecordParsed* other_record = insert_result.first->second;
- if (!record->IsEqual(other_record, true)) {
+ if (record->ttl() != 0 && !record->IsEqual(other_record, true)) {
type = RecordChanged;
}
delete other_record;
- insert_result.first->second = record.release();
}
+ insert_result.first->second = record.release();
+ next_expiration_ = new_expiration;
return type;
}
diff --git a/net/dns/mdns_cache_unittest.cc b/net/dns/mdns_cache_unittest.cc
index 8ac30ac..4e313e5 100644
--- a/net/dns/mdns_cache_unittest.cc
+++ b/net/dns/mdns_cache_unittest.cc
@@ -107,6 +107,38 @@ static const uint8 kTestResponseTwoRecords[] = {
'\x5f', '\x79', '\x5f', '\x79',
};
+static const uint8 kTestResponsesGoodbyePacket[] = {
+ // Answer 1
+ // ghs.l.google.com in DNS format. (Goodbye packet)
+ '\x03', 'g', 'h', 's',
+ '\x01', 'l',
+ '\x06', 'g', 'o', 'o', 'g', 'l', 'e',
+ '\x03', 'c', 'o', 'm',
+ '\x00',
+ '\x00', '\x01', // TYPE is A.
+ '\x00', '\x01', // CLASS is IN.
+ '\x00', '\x00', // TTL (4 bytes) is zero.
+ '\x00', '\x00',
+ '\x00', '\x04', // RDLENGTH is 4 bytes.
+ '\x4a', '\x7d', // RDATA is the IP: 74.125.95.121
+ '\x5f', '\x79',
+
+ // Answer 2
+ // ghs.l.google.com in DNS format.
+ '\x03', 'g', 'h', 's',
+ '\x01', 'l',
+ '\x06', 'g', 'o', 'o', 'g', 'l', 'e',
+ '\x03', 'c', 'o', 'm',
+ '\x00',
+ '\x00', '\x01', // TYPE is A.
+ '\x00', '\x01', // CLASS is IN.
+ '\x00', '\x00', // TTL (4 bytes) is 53 seconds.
+ '\x00', '\x35',
+ '\x00', '\x04', // RDLENGTH is 4 bytes.
+ '\x4a', '\x7d', // RDATA is the IP: 74.125.95.121
+ '\x5f', '\x79',
+};
+
class RecordRemovalMock {
public:
MOCK_METHOD1(OnRecordRemoved, void(const RecordParsed*));
@@ -266,6 +298,40 @@ TEST_F(MDnsCacheTest, RecordPreemptExpirationTime) {
EXPECT_EQ(default_time_ + ttl1, cache_.next_expiration());
}
+// Test that the cache handles mDNS "goodbye" packets correctly, not adding the
+// records to the cache if they are not already there, and eventually removing
+// records from the cache if they are.
+TEST_F(MDnsCacheTest, GoodbyePacket) {
+ DnsRecordParser parser(kTestResponsesGoodbyePacket,
+ sizeof(kTestResponsesGoodbyePacket),
+ 0);
+
+ scoped_ptr<const RecordParsed> record_goodbye;
+ scoped_ptr<const RecordParsed> record_hello;
+ scoped_ptr<const RecordParsed> record_goodbye2;
+ std::vector<const RecordParsed*> results;
+
+ record_goodbye = RecordParsed::CreateFrom(&parser, default_time_);
+ record_hello = RecordParsed::CreateFrom(&parser, default_time_);
+ parser = DnsRecordParser(kTestResponsesGoodbyePacket,
+ sizeof(kTestResponsesGoodbyePacket),
+ 0);
+ record_goodbye2 = RecordParsed::CreateFrom(&parser, default_time_);
+
+ base::TimeDelta ttl = base::TimeDelta::FromSeconds(record_hello->ttl());
+
+ EXPECT_EQ(base::Time(), cache_.next_expiration());
+ EXPECT_EQ(MDnsCache::NoChange, cache_.UpdateDnsRecord(record_goodbye.Pass()));
+ EXPECT_EQ(base::Time(), cache_.next_expiration());
+ EXPECT_EQ(MDnsCache::RecordAdded,
+ cache_.UpdateDnsRecord(record_hello.Pass()));
+ EXPECT_EQ(default_time_ + ttl, cache_.next_expiration());
+ EXPECT_EQ(MDnsCache::NoChange,
+ cache_.UpdateDnsRecord(record_goodbye2.Pass()));
+ EXPECT_EQ(default_time_ + base::TimeDelta::FromSeconds(1),
+ cache_.next_expiration());
+}
+
TEST_F(MDnsCacheTest, AnyRRType) {
DnsRecordParser parser(kTestResponseTwoRecords,
sizeof(kTestResponseTwoRecords),
diff --git a/net/dns/mdns_client_unittest.cc b/net/dns/mdns_client_unittest.cc
index b19d63e..89f6bcf 100644
--- a/net/dns/mdns_client_unittest.cc
+++ b/net/dns/mdns_client_unittest.cc
@@ -310,6 +310,29 @@ const char kSamplePacketAPrivet[] = {
'\x00', '\x02',
};
+const char kSamplePacketGoodbye[] = {
+ // Header
+ '\x00', '\x00', // ID is zeroed out
+ '\x81', '\x80', // Standard query response, RA, no error
+ '\x00', '\x00', // No questions (for simplicity)
+ '\x00', '\x01', // 2 RRs (answers)
+ '\x00', '\x00', // 0 authority RRs
+ '\x00', '\x00', // 0 additional RRs
+
+ // Answer 1
+ '\x07', '_', 'p', 'r', 'i', 'v', 'e', 't',
+ '\x04', '_', 't', 'c', 'p',
+ '\x05', 'l', 'o', 'c', 'a', 'l',
+ '\x00',
+ '\x00', '\x0c', // TYPE is PTR.
+ '\x00', '\x01', // CLASS is IN.
+ '\x00', '\x00', // TTL (4 bytes) is zero;
+ '\x00', '\x00',
+ '\x00', '\x08', // RDLENGTH is 8 bytes.
+ '\x05', 'z', 'z', 'z', 'z', 'z',
+ '\xc0', '\x0c',
+};
+
class PtrRecordCopyContainer {
public:
PtrRecordCopyContainer() {}
@@ -785,6 +808,38 @@ TEST_F(MDnsTest, TransactionReentrantCacheLookupStart) {
SimulatePacketReceive(kSamplePacket1, sizeof(kSamplePacket1));
}
+TEST_F(MDnsTest, GoodbyePacketNotification) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+
+ scoped_ptr<MDnsListener> listener_privet = test_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", &delegate_privet);
+ ASSERT_TRUE(listener_privet->Start());
+
+ SimulatePacketReceive(kSamplePacketGoodbye, sizeof(kSamplePacketGoodbye));
+
+ RunFor(base::TimeDelta::FromSeconds(2));
+}
+
+TEST_F(MDnsTest, GoodbyePacketRemoval) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+
+ scoped_ptr<MDnsListener> listener_privet = test_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", &delegate_privet);
+ ASSERT_TRUE(listener_privet->Start());
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(MDnsListener::RECORD_ADDED, _))
+ .Times(Exactly(1));
+
+ SimulatePacketReceive(kSamplePacket2, sizeof(kSamplePacket2));
+
+ SimulatePacketReceive(kSamplePacketGoodbye, sizeof(kSamplePacketGoodbye));
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(MDnsListener::RECORD_REMOVED, _))
+ .Times(Exactly(1));
+
+ RunFor(base::TimeDelta::FromSeconds(2));
+}
+
// In order to reliably test reentrant listener deletes, we create two listeners
// and have each of them delete both, so we're guaranteed to try and deliver a
// callback to at least one deleted listener.