diff options
author | noamsml@chromium.org <noamsml@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-30 08:07:42 +0000 |
---|---|---|
committer | noamsml@chromium.org <noamsml@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-30 08:07:42 +0000 |
commit | bdd6c3d5e38ea165ed435002f7755ffbeed68814 (patch) | |
tree | 4e2c19ae90b12dd32439f6448e6d4925929929d5 /net/dns | |
parent | 4f0fd02be3a117b7b622833ae0332442ca62b53e (diff) | |
download | chromium_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.h | 1 | ||||
-rw-r--r-- | net/dns/mdns_client.h | 3 | ||||
-rw-r--r-- | net/dns/mdns_client_impl.cc | 135 | ||||
-rw-r--r-- | net/dns/mdns_client_impl.h | 19 | ||||
-rw-r--r-- | net/dns/mdns_client_unittest.cc | 48 |
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 { |