Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(48)

Side by Side Diff: net/dns/mdns_client_impl.cc

Issue 15733008: Multicast DNS implementation (initial) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mdns_implementation2
Patch Set: Renamed files from "listener" to "client" Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/dns/mdns_client_impl.h"
6
7 #include "base/bind.h"
8 #include "base/message_loop_proxy.h"
9 #include "base/stl_util.h"
10 #include "base/time/default_clock.h"
11 #include "net/base/dns_util.h"
12 #include "net/base/net_errors.h"
13 #include "net/base/net_log.h"
14 #include "net/base/rand_callback.h"
15 #include "net/dns/dns_protocol.h"
16 #include "net/dns/mdns_query.h"
17 #include "net/udp/datagram_socket.h"
18
19 namespace net {
20
21 static const char kMDNSMulticastGroupIPv4[] = "224.0.0.251";
22 static const char kMDNSMulticastGroupIPv6[] = "FF02::FB";
23
24 static const unsigned kMDnsTransactionTimeoutSeconds = 3;
25
26 MDnsClientImpl::Core::Core(MDnsClientImpl* client,
27 MDnsConnectionFactory* connection_factory,
28 base::TaskRunner* task_runner,
29 base::Clock* clock)
30 : client_(client), task_runner_(task_runner), clock_(clock),
szym 2013/06/02 19:01:23 The only reason I see for this dependency on TaskR
Noam Samuel 2013/06/04 00:08:03 Done.
31 connection_(connection_factory->CreateConnection(this, task_runner)) {
32 }
33
34 MDnsClientImpl::Core::~Core() {
35 cleanup_callback_.Cancel();
36 STLDeleteValues(&listeners_);
37 }
38
39 bool MDnsClientImpl::Core::Init() {
40 return connection_->Init();
41 }
42
43 bool MDnsClientImpl::Core::SendQuery(uint16 rrtype, std::string name) {
44 std::string name_dns;
45 if (!DNSDomainFromDot(name, &name_dns))
46 return false;
47
48 MDnsQuery query(name_dns, rrtype);
49
50 connection_->Send(query.io_buffer(), query.size());
51
52 return true;
53 }
54
55 void MDnsClientImpl::Core::HandlePacket(DnsResponse* response,
56 int bytes_read) {
57 unsigned offset;
58
59 if (!response->InitParseWithoutQuery(bytes_read)) {
60 LOG(WARNING) << "Could not understand an mDNS packet.";
61 return; // Message is unreadable.
62 }
63
64 // TODO(noamsml): duplicate query suppression.
65 if (!(response->flags() & dns_protocol::kFlagResponse)) {
66 return; // Message is a query. ignore it.
67 }
68 DnsRecordParser parser = response->Parser();
69 unsigned answer_count = response->answer_count() +
70 response->additional_answer_count();
71
72 for (unsigned i = 0; i < answer_count; i++) {
73 offset = parser.GetOffset();
74 scoped_ptr<const RecordParsed> scoped_record = RecordParsed::CreateFrom(
75 &parser, clock_->Now());
76
77 if (!scoped_record) {
78 LOG(WARNING) << "Could not understand an mDNS record.";
79
80 if (offset == parser.GetOffset()) {
81 LOG(WARNING) << "Abandoned parsing the rest of the packet.";
82 return; // The parser did not advance, abort reading the packet.
83 } else {
84 continue; // We may be able to extract other records from the packet.
85 }
86 }
87
88 if ((scoped_record->klass() & dns_protocol::kMDnsClassMask) !=
89 dns_protocol::kClassIN) {
90 LOG(WARNING) << "Received an mDNS record with non-IN class. Ignoring.";
91 continue; // Ignore all records not in the IN class.
92 }
93
94 // We want to retain a copy of the record pointer for updating listeners
95 // but we are passing ownership to the cache.
96 const RecordParsed* record = scoped_record.get();
97 MDnsCache::UpdateType update = cache_.UpdateDnsRecord(scoped_record.Pass());
98
99 // Cleanup time may have changed.
100 ScheduleCleanup(cache_.next_expiration());
101
102 if (update != MDnsCache::NoChange) {
103 MDnsUpdateType update_external;
104
105 switch (update) {
106 case MDnsCache::RecordAdded:
107 update_external = kMDnsRecordAdded;
108 break;
109 case MDnsCache::RecordChanged:
110 update_external = kMDnsRecordChanged;
111 break;
112 case MDnsCache::NoChange:
113 NOTREACHED();
114 // Dummy assignment to suppress compiler warning.
115 update_external = kMDnsRecordChanged;
116 break;
117 }
118
119 AlertListeners(update_external,
120 ListenerKey(record->type(), record->name()), record);
121 // Alert listeners listening only for rrtype and not for name.
122 AlertListeners(update_external, ListenerKey(record->type(), ""), record);
123 }
124 }
125 }
126
127 void MDnsClientImpl::Core::AlertListeners(
128 MDnsUpdateType update_type,
129 const ListenerKey& key,
130 const RecordParsed* record) {
131 ListenerMap::iterator listener_map_iterator = listeners_.find(key);
132 if (listener_map_iterator == listeners_.end()) return;
133
134 FOR_EACH_OBSERVER(MDnsListenerImpl, *listener_map_iterator->second,
135 AlertDelegate(update_type, record));
136 }
137
138 void MDnsClientImpl::Core::AddListener(
139 MDnsListenerImpl* listener, bool alert_existing_records) {
140 ListenerKey key(listener->GetType(), listener->GetName());
141 std::pair<ListenerMap::iterator, bool> observer_insert_result =
142 listeners_.insert(
143 make_pair(key, static_cast<ObserverList<MDnsListenerImpl>*>(NULL)));
144
145 // If an equivalent key does not exist, actually create the observer list.
146 if (observer_insert_result.second) {
147 observer_insert_result.first->second = new ObserverList<MDnsListenerImpl>();
148 }
149
150 ObserverList<MDnsListenerImpl>* observer_list =
151 observer_insert_result.first->second;
152
153 observer_list->AddObserver(listener);
154
155 if (alert_existing_records) {
156 std::vector<const RecordParsed*> records;
157
158 cache_.FindDnsRecords(listener->GetType(), listener->GetName(),
159 &records, clock_->Now());
160
161 for (std::vector<const RecordParsed*>::iterator i = records.begin();
162 i != records.end(); i++) {
163 listener->AlertDelegate(kMDnsRecordAdded, *i);
164 }
165 }
166 }
167
168 void MDnsClientImpl::Core::RemoveListener(MDnsListenerImpl* listener) {
169 ListenerKey key(listener->GetType(), listener->GetName());
170 ListenerMap::iterator observer_list_iterator = listeners_.find(key);
171
172 DCHECK(observer_list_iterator != listeners_.end());
173 DCHECK(observer_list_iterator->second->HasObserver(listener));
174
175 observer_list_iterator->second->RemoveObserver(listener);
176
177 // Remove the observer list from the map if it is empty
178 if (observer_list_iterator->second->size() == 0) {
179 delete observer_list_iterator->second;
180 listeners_.erase(observer_list_iterator);
181 }
182
183 // When we remove a listener, we notify client that a listen reference has
184 // been removed. This may cause the core to be deleted.
185 client_->SubtractListenRef();
186 }
187
188 void MDnsClientImpl::Core::ScheduleCleanup(base::Time cleanup) {
189 // Cleanup is already scheduled, no need to do anything.
190 if (cleanup == scheduled_cleanup_) return;
191 scheduled_cleanup_ = cleanup;
192
193 // This line has the effect of cancelling the previously scheduled cleanup.
194 cleanup_callback_.Reset(base::Bind(
195 &MDnsClientImpl::Core::DoCleanup, base::Unretained(this)));
196
197 // cleanup == base::Time means no cleanup necessary.
198 if (cleanup != base::Time()) {
199 task_runner_->PostDelayedTask(
200 FROM_HERE,
201 cleanup_callback_.callback(),
202 cleanup - clock_->Now());
203 }
204 }
205
206 void MDnsClientImpl::Core::DoCleanup() {
207 cache_.CleanupRecords(clock_->Now(), base::Bind(
208 &MDnsClientImpl::Core::OnRecordRemoved, base::Unretained(this)));
209
210 ScheduleCleanup(cache_.next_expiration());
211 }
212
213 void MDnsClientImpl::Core::OnRecordRemoved(
214 const RecordParsed* record) {
215 AlertListeners(kMDnsRecordRemoved,
216 ListenerKey(record->type(), record->name()), record);
217 // Alert listeners listening only for rrtype and not for name.
218 AlertListeners(kMDnsRecordRemoved, ListenerKey(record->type(), ""),
219 record);
220 }
221
222 void MDnsClientImpl::Core::QueryCache(
223 uint16 rrtype, const std::string& name,
224 std::vector<const RecordParsed*>* records) const {
225 cache_.FindDnsRecords(rrtype, name, records, clock_->Now());
226 }
227
228 MDnsClientImpl::MDnsClientImpl()
229 : listen_refs_(0), clock_owned_(new base::DefaultClock()),
230 connection_factory_owned_(new MDnsConnectionImplFactory()),
231 task_runner_(base::MessageLoopProxy::current()) {
232 clock_ = clock_owned_.get();
233 connection_factory_ = connection_factory_owned_.get();
234 }
235
236 MDnsClientImpl::MDnsClientImpl(base::Clock* clock,
237 base::TaskRunner* task_runner,
238 MDnsConnectionFactory* connection_factory)
239 : listen_refs_(0), clock_(clock), connection_factory_(connection_factory),
240 task_runner_(task_runner) {
241 }
242
243 MDnsClientImpl::~MDnsClientImpl() {
244 }
245
246 bool MDnsClientImpl::AddListenRef() {
247 if (!core_.get()) {
248 core_.reset(new Core(this, connection_factory_, task_runner_, clock_));
249 if (!core_->Init()) {
250 core_.reset();
251 return false;
252 }
253 }
254 listen_refs_++;
255 return true;
256 }
257
258 void MDnsClientImpl::SubtractListenRef() {
259 listen_refs_--;
260 if (listen_refs_ == 0) {
261 task_runner_->PostTask(FROM_HERE, base::Bind(
262 &MDnsClientImpl::Shutdown, base::Unretained(this)));
263 }
264 }
265
266 void MDnsClientImpl::Shutdown() {
267 // We need to check that new listeners haven't been created.
268 if (listen_refs_ == 0) {
269 core_.reset();
270 }
271 }
272
273 bool MDnsClientImpl::IsListeningForTests() {
274 return core_.get() != NULL;
275 }
276
277 scoped_ptr<MDnsListener> MDnsClientImpl::CreateListener(
278 uint16 rrtype,
279 const std::string& name,
280 bool active,
281 bool alert_existing_records,
282 MDnsListener::Delegate* delegate) {
283 if (!AddListenRef()) return scoped_ptr<net::MDnsListener>();
284
285 return scoped_ptr<net::MDnsListener>(
286 new MDnsListenerImpl(rrtype, name, active, alert_existing_records,
287 delegate, core_.get()));
288 }
289
290 scoped_ptr<MDnsTransaction> MDnsClientImpl::CreateTransaction(
291 uint16 rrtype,
292 const std::string& name,
293 const MDnsTransaction::ResultCallback& callback) {
294 scoped_ptr<MDnsTransactionImpl> transaction(
295 new MDnsTransactionImpl(rrtype, name, callback, task_runner_));
296
297 if (transaction->Init(this, core_.get())) {
298 return scoped_ptr<MDnsTransaction>(transaction.Pass());
299 } else {
300 return scoped_ptr<MDnsTransaction>();
301 }
302 }
303
304 MDnsListenerImpl::MDnsListenerImpl(
305 uint16 rrtype,
306 const std::string& name,
307 bool active,
308 bool alert_existing_records,
309 MDnsListener::Delegate* delegate,
310 MDnsClientImpl::Core* core)
311 : rrtype_(rrtype), name_(name), active_(active),
312 parent_(core), delegate_(delegate) {
313 parent_->AddListener(this, alert_existing_records);
314
315 if (active) SendQuery(false); // TODO(noamsml): Retry logic.
316 }
317
318 MDnsListenerImpl::~MDnsListenerImpl() {
319 parent_->RemoveListener(this);
320 }
321
322 const std::string& MDnsListenerImpl::GetName() const {
323 return name_;
324 }
325
326 uint16 MDnsListenerImpl::GetType() const {
327 return rrtype_;
328 }
329
330 bool MDnsListenerImpl::IsActive() const {
331 return active_;
332 }
333
334 bool MDnsListenerImpl::SendQuery(bool force_refresh_cache) {
335 // TODO(noamsml): Logic related to force_refresh_cache
336 if (name_.size() == 0) return false;
337 return parent_->SendQuery(rrtype_, name_);
338 }
339
340 bool MDnsListenerImpl::QueryCache(
341 std::vector<const RecordParsed*>* records) const {
342 if (name_.size() == 0) return false;
343 parent_->QueryCache(rrtype_, name_, records);
344 return true;
345 }
346
347 void MDnsListenerImpl::AlertDelegate(MDnsUpdateType update_type,
348 const RecordParsed* record) {
349 delegate_->OnRecordUpdate(update_type, record);
350 }
351
352 MDnsTransactionImpl::MDnsTransactionImpl(
353 uint16 rrtype,
354 const std::string& name,
355 const MDnsTransaction::ResultCallback& callback,
356 base::TaskRunner* task_runner)
357 : rrtype_(rrtype), name_(name), callback_(callback), triggered_(false),
358 task_runner_(task_runner) {
359 }
360
361 MDnsTransactionImpl::~MDnsTransactionImpl() {
362 }
363
364 bool MDnsTransactionImpl::Init(
365 MDnsClientImpl* client,
366 MDnsClientImpl::Core* core) {
367 DCHECK(client);
368 std::vector<const RecordParsed*> records;
369 if (core) {
370 core->QueryCache(rrtype_, name_, &records);
371 if (!records.empty()) {
372 scoped_ptr<const RecordParsed> record_clone = records.front()->Clone();
szym 2013/06/02 19:01:23 Why do you need to Clone?
Noam Samuel 2013/06/04 00:08:03 Removed.
373 task_runner_->PostTask(
szym 2013/06/02 19:01:23 Why not call CacheRecordFound directly?
Noam Samuel 2013/06/04 00:08:03 Synchronized.
374 FROM_HERE,
375 base::Bind(&MDnsTransactionImpl::CacheRecordFound,
376 AsWeakPtr(), base::Owned(
377 record_clone.release())) );
378
379 return true;
380 }
381 }
382
383 listener_ = client->CreateListener(rrtype_, name_, true /*active*/,
384 false /*alert existing*/, this);
385
386 timeout_.Reset(base::Bind(&MDnsTransactionImpl::OnTimedOut, AsWeakPtr()));
387 task_runner_->PostDelayedTask(
388 FROM_HERE,
389 timeout_.callback(),
390 base::TimeDelta::FromSeconds(kMDnsTransactionTimeoutSeconds));
391
392 return listener_.get() != NULL;
393 }
394
395 const std::string& MDnsTransactionImpl::GetName() const {
396 return name_;
397 }
398
399 uint16 MDnsTransactionImpl::GetType() const {
400 return rrtype_;
401 }
402
403 void MDnsTransactionImpl::CacheRecordFound(const RecordParsed* record) {
404 OnRecordUpdate(kMDnsRecordAdded, record);
405 }
406
407 void MDnsTransactionImpl::TriggerCallback(MDnsTransactionResult result,
408 const RecordParsed* record) {
409 if (triggered_) return;
410 triggered_ = true;
szym 2013/06/02 19:01:23 Instead of adding |triggered_|, I suggest: if (cal
Noam Samuel 2013/06/04 00:08:03 Done.
411
412 // Ensure callback is run after touching all class state, so that
413 // the callback can delete the transaction.
414 MDnsTransaction::ResultCallback callback = callback_;
415
416 callback_.Reset();
417 listener_.reset();
418 timeout_.Cancel();
419
420 callback.Run(result, record);
421 }
422
423 void MDnsTransactionImpl::OnRecordUpdate(MDnsUpdateType update,
424 const RecordParsed* record) {
425 if (update == kMDnsRecordAdded || update == kMDnsRecordChanged) {
426 TriggerCallback(kMDnsTransactionSuccess, record);
427 }
428 }
429
430 void MDnsTransactionImpl::OnTimedOut() {
431 TriggerCallback(kMDnsTransactionTimeout, NULL);
432 }
433
434 void MDnsTransactionImpl::OnNsecRecord(const std::string& name, unsigned type) {
435 // TODO(noamsml): NSEC records not yet implemented
436 }
437
438 MDnsConnectionImpl::MDnsConnectionImpl(MDnsConnection::Delegate* delegate,
439 base::TaskRunner* task_runner)
440 : socket_ipv4_(new UDPSocket(DatagramSocket::DEFAULT_BIND,
441 RandIntCallback(),
442 NULL, NetLog::Source())),
443 socket_ipv6_(new UDPSocket(DatagramSocket::DEFAULT_BIND,
444 RandIntCallback(),
445 NULL, NetLog::Source())),
446 response_ipv4_(new DnsResponse(dns_protocol::kMaxMulticastSize)),
447 response_ipv6_(new DnsResponse(dns_protocol::kMaxMulticastSize)),
448 delegate_(delegate),
449 task_runner_(task_runner) {
450 }
451
452 MDnsConnectionImpl::~MDnsConnectionImpl() {
453 socket_ipv4_->Close();
454 socket_ipv6_->Close();
455 }
456
457 bool MDnsConnectionImpl::Init() {
458 if (!BindSocket(socket_ipv4_.get(), kIPv4AddressSize,
459 kMDNSMulticastGroupIPv4))
460 return false;
461
462 if (!BindSocket(socket_ipv6_.get(), kIPv6AddressSize,
463 kMDNSMulticastGroupIPv6))
464 return false;
465
466 if (!ReceiveNextPacket(socket_ipv4_.get(),
467 response_ipv4_.get(),
468 &recv_addr_ipv4_))
469 return false;
470
471 if (!ReceiveNextPacket(socket_ipv6_.get(),
472 response_ipv6_.get(),
473 &recv_addr_ipv6_))
474 return false;
475
476 return true;
477 }
478
479 bool MDnsConnectionImpl::Send(IOBuffer* buffer, unsigned size) {
480 int rv = socket_ipv4_->SendTo(
481 buffer,
482 size,
483 GetIPv4SendEndpoint(),
484 base::Bind(&MDnsConnectionImpl::SendDone,
485 base::Unretained(this) ));
486 if (rv < OK && rv != ERR_IO_PENDING) return false;
487
488 rv = socket_ipv6_->SendTo(
489 buffer,
490 size,
491 GetIPv6SendEndpoint(),
492 base::Bind(&MDnsConnectionImpl::SendDone,
493 base::Unretained(this) ));
494 if (rv < OK && rv != ERR_IO_PENDING) return false;
495
496 return true;
497 }
498
499 void MDnsConnectionImpl::SendDone(int sent) {
500 // TODO(noamsml): Queueing and retry logic
501 }
502
503 bool MDnsConnectionImpl::BindSocket(
504 UDPSocket* socket,
505 int addr_size,
506 const char* multicast_group) {
507 IPAddressNumber address_any;
508 address_any.resize(addr_size, 0);
509
510 IPAddressNumber multicast_group_number;
511
512 IPEndPoint bind_endpoint(address_any, dns_protocol::kDefaultPortMulticast);
513
514 bool success = ParseIPLiteralToNumber(multicast_group,
515 &multicast_group_number);
516 DCHECK(success);
517
518 socket->AllowAddressReuse();
519 int status = socket->Bind(bind_endpoint);
520
521 if (status < 0)
522 return false;
523
524 socket->SetMulticastLoopbackMode(false);
525
526 status = socket->JoinGroup(multicast_group_number);
527
528 if (status < 0)
529 return false;
530
531 return true;
532 }
533
534 IPEndPoint MDnsConnectionImpl::GetIPv4SendEndpoint() {
535 IPAddressNumber multicast_group_number;
536 bool success = ParseIPLiteralToNumber(kMDNSMulticastGroupIPv4,
537 &multicast_group_number);
538 DCHECK(success);
539 return IPEndPoint(multicast_group_number,
540 dns_protocol::kDefaultPortMulticast);
541 }
542
543 IPEndPoint MDnsConnectionImpl::GetIPv6SendEndpoint() {
544 IPAddressNumber multicast_group_number;
545 bool success = ParseIPLiteralToNumber(kMDNSMulticastGroupIPv6,
546 &multicast_group_number);
547 DCHECK(success);
548 return IPEndPoint(multicast_group_number,
549 dns_protocol::kDefaultPortMulticast);
550 }
551
552 void MDnsConnectionImpl::OnDatagramReceived(
553 UDPSocket* socket,
554 DnsResponse* response,
555 IPEndPoint* recv_addr,
556 int bytes_read) {
557 // TODO(noamsml): More sophisticated error handling.
558 DCHECK_GT(bytes_read, 0);
559 delegate_->HandlePacket(response, bytes_read);
560 bool success = ReceiveNextPacket(socket, response, recv_addr);
561
562 DCHECK(success); // TODO(noamsml): exponential backoff.
563 }
564
565 bool MDnsConnectionImpl::ReceiveNextPacket(
566 UDPSocket* socket,
567 DnsResponse* response,
568 IPEndPoint* recv_addr) {
569 int rval;
570 do {
571 rval = socket->RecvFrom(
572 response->io_buffer(),
573 response->io_buffer()->size(),
574 recv_addr,
575 base::Bind(&MDnsConnectionImpl::OnDatagramReceived,
576 base::Unretained(this), socket, response, recv_addr));
577
578 if (rval > 0) {
579 delegate_->HandlePacket(response, rval);
580 }
581 } while (rval > 0);
582
583 if (rval != ERR_IO_PENDING) return false;
584 return true;
585 }
586
587 MDnsConnectionImplFactory::MDnsConnectionImplFactory() {
588 }
589
590 MDnsConnectionImplFactory::~MDnsConnectionImplFactory() {
591 }
592
593 scoped_ptr<MDnsConnection> MDnsConnectionImplFactory::CreateConnection(
594 MDnsConnection::Delegate* delegate,
595 base::TaskRunner* task_runner) {
596 return scoped_ptr<MDnsConnection>(new MDnsConnectionImpl(delegate,
597 task_runner));
598 }
599
600 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698