summaryrefslogtreecommitdiffstats
path: root/net/dns
diff options
context:
space:
mode:
authornoamsml@chromium.org <noamsml@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-30 08:07:42 +0000
committernoamsml@chromium.org <noamsml@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-30 08:07:42 +0000
commitbdd6c3d5e38ea165ed435002f7755ffbeed68814 (patch)
tree4e2c19ae90b12dd32439f6448e6d4925929929d5 /net/dns
parent4f0fd02be3a117b7b622833ae0332442ca62b53e (diff)
downloadchromium_src-bdd6c3d5e38ea165ed435002f7755ffbeed68814.zip
chromium_src-bdd6c3d5e38ea165ed435002f7755ffbeed68814.tar.gz
chromium_src-bdd6c3d5e38ea165ed435002f7755ffbeed68814.tar.bz2
Add the ability for MDnsListener to actively refresh records
If the MDnsListener is listening for a record we care about, actively refresh it. BUG=336883 R=vitalybuka@chromium.org,szym@chromium.org Review URL: https://codereview.chromium.org/132693025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@247889 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/dns')
-rw-r--r--net/dns/mdns_cache.h1
-rw-r--r--net/dns/mdns_client.h3
-rw-r--r--net/dns/mdns_client_impl.cc135
-rw-r--r--net/dns/mdns_client_impl.h19
-rw-r--r--net/dns/mdns_client_unittest.cc48
5 files changed, 168 insertions, 38 deletions
diff --git a/net/dns/mdns_cache.h b/net/dns/mdns_cache.h
index 27a14d8..6a38fc8 100644
--- a/net/dns/mdns_cache.h
+++ b/net/dns/mdns_cache.h
@@ -55,6 +55,7 @@ class NET_EXPORT_PRIVATE MDnsCache {
enum UpdateType {
RecordAdded,
RecordChanged,
+ RecordRemoved,
NoChange
};
diff --git a/net/dns/mdns_client.h b/net/dns/mdns_client.h
index ab7916e..67cc849 100644
--- a/net/dns/mdns_client.h
+++ b/net/dns/mdns_client.h
@@ -113,6 +113,9 @@ class NET_EXPORT MDnsListener {
// Start the listener. Return true on success.
virtual bool Start() = 0;
+ // Actively refresh any received records.
+ virtual void SetActiveRefresh(bool active_refresh) = 0;
+
// Get the host or service name for this query.
// Return an empty string for no name.
virtual const std::string& GetName() const = 0;
diff --git a/net/dns/mdns_client_impl.cc b/net/dns/mdns_client_impl.cc
index c95b86c..231f0c7 100644
--- a/net/dns/mdns_client_impl.cc
+++ b/net/dns/mdns_client_impl.cc
@@ -26,6 +26,13 @@ namespace net {
namespace {
const unsigned MDnsTransactionTimeoutSeconds = 3;
+// The fractions of the record's original TTL after which an active listener
+// (one that had |SetActiveRefresh(true)| called) will send a query to refresh
+// its cache. This happens both at 85% of the original TTL and again at 95% of
+// the original TTL.
+const double kListenerRefreshRatio1 = 0.85;
+const double kListenerRefreshRatio2 = 0.95;
+const unsigned kMillisecondsPerSecond = 1000;
} // namespace
@@ -192,7 +199,7 @@ void MDnsClientImpl::Core::HandlePacket(DnsResponse* response,
// Note: We store cache keys rather than record pointers to avoid
// erroneous behavior in case a packet contains multiple exclusive
// records with the same type and name.
- std::map<MDnsCache::Key, MDnsListener::UpdateType> update_keys;
+ std::map<MDnsCache::Key, MDnsCache::UpdateType> update_keys;
if (!response->InitParseWithoutQuery(bytes_read)) {
LOG(WARNING) << "Could not understand an mDNS packet.";
@@ -235,29 +242,10 @@ void MDnsClientImpl::Core::HandlePacket(DnsResponse* response,
// Cleanup time may have changed.
ScheduleCleanup(cache_.next_expiration());
- if (update != MDnsCache::NoChange) {
- MDnsListener::UpdateType update_external;
-
- switch (update) {
- case MDnsCache::RecordAdded:
- update_external = MDnsListener::RECORD_ADDED;
- break;
- case MDnsCache::RecordChanged:
- update_external = MDnsListener::RECORD_CHANGED;
- break;
- case MDnsCache::NoChange:
- default:
- NOTREACHED();
- // Dummy assignment to suppress compiler warning.
- update_external = MDnsListener::RECORD_CHANGED;
- break;
- }
-
- update_keys.insert(std::make_pair(update_key, update_external));
- }
+ update_keys.insert(std::make_pair(update_key, update));
}
- for (std::map<MDnsCache::Key, MDnsListener::UpdateType>::iterator i =
+ for (std::map<MDnsCache::Key, MDnsCache::UpdateType>::iterator i =
update_keys.begin(); i != update_keys.end(); i++) {
const RecordParsed* record = cache_.LookupKey(i->first);
if (!record)
@@ -311,14 +299,14 @@ void MDnsClientImpl::Core::OnConnectionError(int error) {
}
void MDnsClientImpl::Core::AlertListeners(
- MDnsListener::UpdateType update_type,
+ MDnsCache::UpdateType update_type,
const ListenerKey& key,
const RecordParsed* record) {
ListenerMap::iterator listener_map_iterator = listeners_.find(key);
if (listener_map_iterator == listeners_.end()) return;
FOR_EACH_OBSERVER(MDnsListenerImpl, *listener_map_iterator->second,
- AlertDelegate(update_type, record));
+ HandleRecordUpdate(update_type, record));
}
void MDnsClientImpl::Core::AddListener(
@@ -392,7 +380,7 @@ void MDnsClientImpl::Core::DoCleanup() {
void MDnsClientImpl::Core::OnRecordRemoved(
const RecordParsed* record) {
- AlertListeners(MDnsListener::RECORD_REMOVED,
+ AlertListeners(MDnsCache::RecordRemoved,
ListenerKey(record->name(), record->type()), record);
}
@@ -449,7 +437,14 @@ MDnsListenerImpl::MDnsListenerImpl(
MDnsListener::Delegate* delegate,
MDnsClientImpl* client)
: rrtype_(rrtype), name_(name), client_(client), delegate_(delegate),
- started_(false) {
+ started_(false), active_refresh_(false) {
+}
+
+MDnsListenerImpl::~MDnsListenerImpl() {
+ if (started_) {
+ DCHECK(client_->core());
+ client_->core()->RemoveListener(this);
+ }
}
bool MDnsListenerImpl::Start() {
@@ -463,10 +458,15 @@ bool MDnsListenerImpl::Start() {
return true;
}
-MDnsListenerImpl::~MDnsListenerImpl() {
+void MDnsListenerImpl::SetActiveRefresh(bool active_refresh) {
+ active_refresh_ = active_refresh;
+
if (started_) {
- DCHECK(client_->core());
- client_->core()->RemoveListener(this);
+ if (!active_refresh_) {
+ next_refresh_.Cancel();
+ } else if (last_update_ != base::Time()) {
+ ScheduleNextRefresh();
+ }
}
}
@@ -478,10 +478,40 @@ uint16 MDnsListenerImpl::GetType() const {
return rrtype_;
}
-void MDnsListenerImpl::AlertDelegate(MDnsListener::UpdateType update_type,
- const RecordParsed* record) {
+void MDnsListenerImpl::HandleRecordUpdate(MDnsCache::UpdateType update_type,
+ const RecordParsed* record) {
DCHECK(started_);
- delegate_->OnRecordUpdate(update_type, record);
+
+ if (update_type != MDnsCache::RecordRemoved) {
+ ttl_ = record->ttl();
+ last_update_ = record->time_created();
+
+ ScheduleNextRefresh();
+ }
+
+ if (update_type != MDnsCache::NoChange) {
+ MDnsListener::UpdateType update_external;
+
+ switch (update_type) {
+ case MDnsCache::RecordAdded:
+ update_external = MDnsListener::RECORD_ADDED;
+ break;
+ case MDnsCache::RecordChanged:
+ update_external = MDnsListener::RECORD_CHANGED;
+ break;
+ case MDnsCache::RecordRemoved:
+ update_external = MDnsListener::RECORD_REMOVED;
+ break;
+ case MDnsCache::NoChange:
+ default:
+ NOTREACHED();
+ // Dummy assignment to suppress compiler warning.
+ update_external = MDnsListener::RECORD_CHANGED;
+ break;
+ }
+
+ delegate_->OnRecordUpdate(update_external, record);
+ }
}
void MDnsListenerImpl::AlertNsecRecord() {
@@ -489,6 +519,47 @@ void MDnsListenerImpl::AlertNsecRecord() {
delegate_->OnNsecRecord(name_, rrtype_);
}
+void MDnsListenerImpl::ScheduleNextRefresh() {
+ DCHECK(last_update_ != base::Time());
+
+ if (!active_refresh_)
+ return;
+
+ // A zero TTL is a goodbye packet and should not be refreshed.
+ if (ttl_ == 0) {
+ next_refresh_.Cancel();
+ return;
+ }
+
+ next_refresh_.Reset(base::Bind(&MDnsListenerImpl::DoRefresh,
+ AsWeakPtr()));
+
+ // Schedule refreshes at both 85% and 95% of the original TTL. These will both
+ // be canceled and rescheduled if the record's TTL is updated due to a
+ // response being received.
+ base::Time next_refresh1 = last_update_ + base::TimeDelta::FromMilliseconds(
+ static_cast<int>(kMillisecondsPerSecond *
+ kListenerRefreshRatio1 * ttl_));
+
+ base::Time next_refresh2 = last_update_ + base::TimeDelta::FromMilliseconds(
+ static_cast<int>(kMillisecondsPerSecond *
+ kListenerRefreshRatio2 * ttl_));
+
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ next_refresh_.callback(),
+ next_refresh1 - base::Time::Now());
+
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ next_refresh_.callback(),
+ next_refresh2 - base::Time::Now());
+}
+
+void MDnsListenerImpl::DoRefresh() {
+ client_->core()->SendQuery(rrtype_, name_);
+}
+
MDnsTransactionImpl::MDnsTransactionImpl(
uint16 rrtype,
const std::string& name,
diff --git a/net/dns/mdns_client_impl.h b/net/dns/mdns_client_impl.h
index f755985..7660788 100644
--- a/net/dns/mdns_client_impl.h
+++ b/net/dns/mdns_client_impl.h
@@ -133,7 +133,7 @@ class NET_EXPORT_PRIVATE MDnsClientImpl : public MDnsClient {
ListenerMap;
// Alert listeners of an update to the cache.
- void AlertListeners(MDnsListener::UpdateType update_type,
+ void AlertListeners(MDnsCache::UpdateType update_type,
const ListenerKey& key, const RecordParsed* record);
// Schedule a cache cleanup to a specific time, cancelling other cleanups.
@@ -156,7 +156,7 @@ class NET_EXPORT_PRIVATE MDnsClientImpl : public MDnsClient {
MDnsClientImpl* client_;
MDnsCache cache_;
- base::CancelableCallback<void()> cleanup_callback_;
+ base::CancelableClosure cleanup_callback_;
base::Time scheduled_cleanup_;
scoped_ptr<MDnsConnection> connection_;
@@ -204,6 +204,9 @@ class MDnsListenerImpl : public MDnsListener,
// MDnsListener implementation:
virtual bool Start() OVERRIDE;
+ // Actively refresh any received records.
+ virtual void SetActiveRefresh(bool active_refresh) OVERRIDE;
+
virtual const std::string& GetName() const OVERRIDE;
virtual uint16 GetType() const OVERRIDE;
@@ -211,19 +214,27 @@ class MDnsListenerImpl : public MDnsListener,
MDnsListener::Delegate* delegate() { return delegate_; }
// Alert the delegate of a record update.
- void AlertDelegate(MDnsListener::UpdateType update_type,
- const RecordParsed* record_parsed);
+ void HandleRecordUpdate(MDnsCache::UpdateType update_type,
+ const RecordParsed* record_parsed);
// Alert the delegate of the existence of an Nsec record.
void AlertNsecRecord();
private:
+ void ScheduleNextRefresh();
+ void DoRefresh();
+
uint16 rrtype_;
std::string name_;
MDnsClientImpl* client_;
MDnsListener::Delegate* delegate_;
+ base::Time last_update_;
+ uint32 ttl_;
bool started_;
+ bool active_refresh_;
+
+ base::CancelableClosure next_refresh_;
DISALLOW_COPY_AND_ASSIGN(MDnsListenerImpl);
};
diff --git a/net/dns/mdns_client_unittest.cc b/net/dns/mdns_client_unittest.cc
index 7ea4ac9..0186839d 100644
--- a/net/dns/mdns_client_unittest.cc
+++ b/net/dns/mdns_client_unittest.cc
@@ -241,6 +241,25 @@ const uint8 kQueryPacketPrivet[] = {
0x00, 0x01, // CLASS is IN.
};
+const uint8 kQueryPacketPrivetA[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x00, 0x00, // No flags.
+ 0x00, 0x01, // One question.
+ 0x00, 0x00, // 0 RRs (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ // Question
+ // This part is echoed back from the respective query.
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x01, // TYPE is A.
+ 0x00, 0x01, // CLASS is IN.
+};
+
const uint8 kSamplePacketAdditionalOnly[] = {
// Header
0x00, 0x00, // ID is zeroed out
@@ -303,8 +322,8 @@ const uint8 kSamplePacketAPrivet[] = {
0x00,
0x00, 0x01, // TYPE is A.
0x00, 0x01, // CLASS is IN.
- 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
- 0x24, 0x74,
+ 0x00, 0x00, // TTL (4 bytes) is 5 seconds
+ 0x00, 0x05,
0x00, 0x04, // RDLENGTH is 4 bytes.
0xc0, 0x0c,
0x00, 0x02,
@@ -1008,6 +1027,31 @@ TEST_F(MDnsTest, NsecConflictRemoval) {
}
+TEST_F(MDnsTest, RefreshQuery) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+ scoped_ptr<MDnsListener> listener_privet =
+ test_client_.CreateListener(dns_protocol::kTypeA, "_privet._tcp.local",
+ &delegate_privet);
+
+ listener_privet->SetActiveRefresh(true);
+ ASSERT_TRUE(listener_privet->Start());
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(MDnsListener::RECORD_ADDED, _));
+
+ SimulatePacketReceive(kSamplePacketAPrivet,
+ sizeof(kSamplePacketAPrivet));
+
+ // Expecting 2 calls (one for ipv4 and one for ipv6) for each of the 2
+ // scheduled refresh queries.
+ EXPECT_CALL(socket_factory_, OnSendTo(
+ MakeString(kQueryPacketPrivetA, sizeof(kQueryPacketPrivetA))))
+ .Times(4);
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(MDnsListener::RECORD_REMOVED, _));
+
+ RunFor(base::TimeDelta::FromSeconds(6));
+}
+
// 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.
class SimpleMockSocketFactory : public MDnsSocketFactory {