Commit bdd6c3d5 authored by noamsml@chromium.org's avatar noamsml@chromium.org

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
parent 4f0fd02b
...@@ -55,6 +55,7 @@ class NET_EXPORT_PRIVATE MDnsCache { ...@@ -55,6 +55,7 @@ class NET_EXPORT_PRIVATE MDnsCache {
enum UpdateType { enum UpdateType {
RecordAdded, RecordAdded,
RecordChanged, RecordChanged,
RecordRemoved,
NoChange NoChange
}; };
......
...@@ -113,6 +113,9 @@ class NET_EXPORT MDnsListener { ...@@ -113,6 +113,9 @@ class NET_EXPORT MDnsListener {
// Start the listener. Return true on success. // Start the listener. Return true on success.
virtual bool Start() = 0; 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. // Get the host or service name for this query.
// Return an empty string for no name. // Return an empty string for no name.
virtual const std::string& GetName() const = 0; virtual const std::string& GetName() const = 0;
......
...@@ -26,6 +26,13 @@ namespace net { ...@@ -26,6 +26,13 @@ namespace net {
namespace { namespace {
const unsigned MDnsTransactionTimeoutSeconds = 3; 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 } // namespace
...@@ -192,7 +199,7 @@ void MDnsClientImpl::Core::HandlePacket(DnsResponse* response, ...@@ -192,7 +199,7 @@ void MDnsClientImpl::Core::HandlePacket(DnsResponse* response,
// Note: We store cache keys rather than record pointers to avoid // Note: We store cache keys rather than record pointers to avoid
// erroneous behavior in case a packet contains multiple exclusive // erroneous behavior in case a packet contains multiple exclusive
// records with the same type and name. // 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)) { if (!response->InitParseWithoutQuery(bytes_read)) {
LOG(WARNING) << "Could not understand an mDNS packet."; LOG(WARNING) << "Could not understand an mDNS packet.";
...@@ -235,29 +242,10 @@ void MDnsClientImpl::Core::HandlePacket(DnsResponse* response, ...@@ -235,29 +242,10 @@ void MDnsClientImpl::Core::HandlePacket(DnsResponse* response,
// Cleanup time may have changed. // Cleanup time may have changed.
ScheduleCleanup(cache_.next_expiration()); ScheduleCleanup(cache_.next_expiration());
if (update != MDnsCache::NoChange) { update_keys.insert(std::make_pair(update_key, update));
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));
}
} }
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++) { update_keys.begin(); i != update_keys.end(); i++) {
const RecordParsed* record = cache_.LookupKey(i->first); const RecordParsed* record = cache_.LookupKey(i->first);
if (!record) if (!record)
...@@ -311,14 +299,14 @@ void MDnsClientImpl::Core::OnConnectionError(int error) { ...@@ -311,14 +299,14 @@ void MDnsClientImpl::Core::OnConnectionError(int error) {
} }
void MDnsClientImpl::Core::AlertListeners( void MDnsClientImpl::Core::AlertListeners(
MDnsListener::UpdateType update_type, MDnsCache::UpdateType update_type,
const ListenerKey& key, const ListenerKey& key,
const RecordParsed* record) { const RecordParsed* record) {
ListenerMap::iterator listener_map_iterator = listeners_.find(key); ListenerMap::iterator listener_map_iterator = listeners_.find(key);
if (listener_map_iterator == listeners_.end()) return; if (listener_map_iterator == listeners_.end()) return;
FOR_EACH_OBSERVER(MDnsListenerImpl, *listener_map_iterator->second, FOR_EACH_OBSERVER(MDnsListenerImpl, *listener_map_iterator->second,
AlertDelegate(update_type, record)); HandleRecordUpdate(update_type, record));
} }
void MDnsClientImpl::Core::AddListener( void MDnsClientImpl::Core::AddListener(
...@@ -392,7 +380,7 @@ void MDnsClientImpl::Core::DoCleanup() { ...@@ -392,7 +380,7 @@ void MDnsClientImpl::Core::DoCleanup() {
void MDnsClientImpl::Core::OnRecordRemoved( void MDnsClientImpl::Core::OnRecordRemoved(
const RecordParsed* record) { const RecordParsed* record) {
AlertListeners(MDnsListener::RECORD_REMOVED, AlertListeners(MDnsCache::RecordRemoved,
ListenerKey(record->name(), record->type()), record); ListenerKey(record->name(), record->type()), record);
} }
...@@ -449,7 +437,14 @@ MDnsListenerImpl::MDnsListenerImpl( ...@@ -449,7 +437,14 @@ MDnsListenerImpl::MDnsListenerImpl(
MDnsListener::Delegate* delegate, MDnsListener::Delegate* delegate,
MDnsClientImpl* client) MDnsClientImpl* client)
: rrtype_(rrtype), name_(name), client_(client), delegate_(delegate), : 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() { bool MDnsListenerImpl::Start() {
...@@ -463,10 +458,15 @@ bool MDnsListenerImpl::Start() { ...@@ -463,10 +458,15 @@ bool MDnsListenerImpl::Start() {
return true; return true;
} }
MDnsListenerImpl::~MDnsListenerImpl() { void MDnsListenerImpl::SetActiveRefresh(bool active_refresh) {
active_refresh_ = active_refresh;
if (started_) { if (started_) {
DCHECK(client_->core()); if (!active_refresh_) {
client_->core()->RemoveListener(this); next_refresh_.Cancel();
} else if (last_update_ != base::Time()) {
ScheduleNextRefresh();
}
} }
} }
...@@ -478,10 +478,40 @@ uint16 MDnsListenerImpl::GetType() const { ...@@ -478,10 +478,40 @@ uint16 MDnsListenerImpl::GetType() const {
return rrtype_; return rrtype_;
} }
void MDnsListenerImpl::AlertDelegate(MDnsListener::UpdateType update_type, void MDnsListenerImpl::HandleRecordUpdate(MDnsCache::UpdateType update_type,
const RecordParsed* record) { const RecordParsed* record) {
DCHECK(started_); 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() { void MDnsListenerImpl::AlertNsecRecord() {
...@@ -489,6 +519,47 @@ void MDnsListenerImpl::AlertNsecRecord() { ...@@ -489,6 +519,47 @@ void MDnsListenerImpl::AlertNsecRecord() {
delegate_->OnNsecRecord(name_, rrtype_); 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( MDnsTransactionImpl::MDnsTransactionImpl(
uint16 rrtype, uint16 rrtype,
const std::string& name, const std::string& name,
......
...@@ -133,7 +133,7 @@ class NET_EXPORT_PRIVATE MDnsClientImpl : public MDnsClient { ...@@ -133,7 +133,7 @@ class NET_EXPORT_PRIVATE MDnsClientImpl : public MDnsClient {
ListenerMap; ListenerMap;
// Alert listeners of an update to the cache. // 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); const ListenerKey& key, const RecordParsed* record);
// Schedule a cache cleanup to a specific time, cancelling other cleanups. // Schedule a cache cleanup to a specific time, cancelling other cleanups.
...@@ -156,7 +156,7 @@ class NET_EXPORT_PRIVATE MDnsClientImpl : public MDnsClient { ...@@ -156,7 +156,7 @@ class NET_EXPORT_PRIVATE MDnsClientImpl : public MDnsClient {
MDnsClientImpl* client_; MDnsClientImpl* client_;
MDnsCache cache_; MDnsCache cache_;
base::CancelableCallback<void()> cleanup_callback_; base::CancelableClosure cleanup_callback_;
base::Time scheduled_cleanup_; base::Time scheduled_cleanup_;
scoped_ptr<MDnsConnection> connection_; scoped_ptr<MDnsConnection> connection_;
...@@ -204,6 +204,9 @@ class MDnsListenerImpl : public MDnsListener, ...@@ -204,6 +204,9 @@ class MDnsListenerImpl : public MDnsListener,
// MDnsListener implementation: // MDnsListener implementation:
virtual bool Start() OVERRIDE; virtual bool Start() OVERRIDE;
// Actively refresh any received records.
virtual void SetActiveRefresh(bool active_refresh) OVERRIDE;
virtual const std::string& GetName() const OVERRIDE; virtual const std::string& GetName() const OVERRIDE;
virtual uint16 GetType() const OVERRIDE; virtual uint16 GetType() const OVERRIDE;
...@@ -211,19 +214,27 @@ class MDnsListenerImpl : public MDnsListener, ...@@ -211,19 +214,27 @@ class MDnsListenerImpl : public MDnsListener,
MDnsListener::Delegate* delegate() { return delegate_; } MDnsListener::Delegate* delegate() { return delegate_; }
// Alert the delegate of a record update. // Alert the delegate of a record update.
void AlertDelegate(MDnsListener::UpdateType update_type, void HandleRecordUpdate(MDnsCache::UpdateType update_type,
const RecordParsed* record_parsed); const RecordParsed* record_parsed);
// Alert the delegate of the existence of an Nsec record. // Alert the delegate of the existence of an Nsec record.
void AlertNsecRecord(); void AlertNsecRecord();
private: private:
void ScheduleNextRefresh();
void DoRefresh();
uint16 rrtype_; uint16 rrtype_;
std::string name_; std::string name_;
MDnsClientImpl* client_; MDnsClientImpl* client_;
MDnsListener::Delegate* delegate_; MDnsListener::Delegate* delegate_;
base::Time last_update_;
uint32 ttl_;
bool started_; bool started_;
bool active_refresh_;
base::CancelableClosure next_refresh_;
DISALLOW_COPY_AND_ASSIGN(MDnsListenerImpl); DISALLOW_COPY_AND_ASSIGN(MDnsListenerImpl);
}; };
......
...@@ -241,6 +241,25 @@ const uint8 kQueryPacketPrivet[] = { ...@@ -241,6 +241,25 @@ const uint8 kQueryPacketPrivet[] = {
0x00, 0x01, // CLASS is IN. 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[] = { const uint8 kSamplePacketAdditionalOnly[] = {
// Header // Header
0x00, 0x00, // ID is zeroed out 0x00, 0x00, // ID is zeroed out
...@@ -303,8 +322,8 @@ const uint8 kSamplePacketAPrivet[] = { ...@@ -303,8 +322,8 @@ const uint8 kSamplePacketAPrivet[] = {
0x00, 0x00,
0x00, 0x01, // TYPE is A. 0x00, 0x01, // TYPE is A.
0x00, 0x01, // CLASS is IN. 0x00, 0x01, // CLASS is IN.
0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds. 0x00, 0x00, // TTL (4 bytes) is 5 seconds
0x24, 0x74, 0x00, 0x05,
0x00, 0x04, // RDLENGTH is 4 bytes. 0x00, 0x04, // RDLENGTH is 4 bytes.
0xc0, 0x0c, 0xc0, 0x0c,
0x00, 0x02, 0x00, 0x02,
...@@ -1008,6 +1027,31 @@ TEST_F(MDnsTest, NsecConflictRemoval) { ...@@ -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. // 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. // This is a simplifying assumption based on the way the code works now.
class SimpleMockSocketFactory : public MDnsSocketFactory { class SimpleMockSocketFactory : public MDnsSocketFactory {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment