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

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: 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/udp/datagram_socket.h"
17
18 namespace net {
19
20 static const char kMDnsMulticastGroupIPv4[] = "224.0.0.251";
21 static const char kMDnsMulticastGroupIPv6[] = "FF02::FB";
22
23 static const unsigned kMDnsTransactionNoResultsSeconds = 3;
24
25 MDnsClientImpl::Core::Core(MDnsClientImpl* client,
26 MDnsConnectionFactory* connection_factory)
27 : client_(client), connection_(connection_factory->CreateConnection(this)) {
28 }
29
30 MDnsClientImpl::Core::~Core() {
31 cleanup_callback_.Cancel();
32 STLDeleteValues(&listeners_);
33 }
34
35 bool MDnsClientImpl::Core::Init() {
36 return connection_->Init() == OK;
37 }
38
39 bool MDnsClientImpl::Core::SendQuery(uint16 rrtype, std::string name) {
40 std::string name_dns;
41 if (!DNSDomainFromDot(name, &name_dns))
42 return false;
43
44 // TODO(noamsml): Remove RD flag from query and add known-answer suppression.
45 DnsQuery query(0, name_dns, rrtype);
46
47 return connection_->Send(query.io_buffer(), query.io_buffer()->size()) == OK;
48 }
49
50 void MDnsClientImpl::Core::HandlePacket(DnsResponse* response,
51 int bytes_read) {
52 unsigned offset;
53
54 if (!response->InitParseWithoutQuery(bytes_read)) {
55 LOG(WARNING) << "Could not understand an mDNS packet.";
56 return; // Message is unreadable.
57 }
58
59 // TODO(noamsml): duplicate query suppression.
60 if (!(response->flags() & dns_protocol::kFlagResponse)) {
61 return; // Message is a query. ignore it.
62 }
63 DnsRecordParser parser = response->Parser();
64 unsigned answer_count = response->answer_count() +
65 response->additional_answer_count();
66
67 for (unsigned i = 0; i < answer_count; i++) {
68 offset = parser.GetOffset();
69 scoped_ptr<const RecordParsed> scoped_record = RecordParsed::CreateFrom(
70 &parser, base::Time::Now());
71
72 if (!scoped_record) {
73 LOG(WARNING) << "Could not understand an mDNS record.";
74
75 if (offset == parser.GetOffset()) {
76 LOG(WARNING) << "Abandoned parsing the rest of the packet.";
77 return; // The parser did not advance, abort reading the packet.
78 } else {
79 continue; // We may be able to extract other records from the packet.
80 }
81 }
82
83 if ((scoped_record->klass() & dns_protocol::kMDnsClassMask) !=
84 dns_protocol::kClassIN) {
85 LOG(WARNING) << "Received an mDNS record with non-IN class. Ignoring.";
86 continue; // Ignore all records not in the IN class.
87 }
88
89 // We want to retain a copy of the record pointer for updating listeners
90 // but we are passing ownership to the cache.
91 const RecordParsed* record = scoped_record.get();
92 MDnsCache::UpdateType update = cache_.UpdateDnsRecord(scoped_record.Pass());
93
94 // Cleanup time may have changed.
95 ScheduleCleanup(cache_.next_expiration());
96
97 if (update != MDnsCache::NoChange) {
98 MDnsUpdateType update_external;
99
100 switch (update) {
101 case MDnsCache::RecordAdded:
102 update_external = kMDnsRecordAdded;
103 break;
104 case MDnsCache::RecordChanged:
105 update_external = kMDnsRecordChanged;
106 break;
107 case MDnsCache::NoChange:
108 NOTREACHED();
109 // Dummy assignment to suppress compiler warning.
110 update_external = kMDnsRecordChanged;
111 break;
112 }
113
114 AlertListeners(update_external,
115 ListenerKey(record->type(), record->name()), record);
116 // Alert listeners listening only for rrtype and not for name.
117 AlertListeners(update_external, ListenerKey(record->type(), ""), record);
118 }
119 }
120 }
121
122 void MDnsClientImpl::Core::OnConnectionError(int error) {
123 // TODO(noamsml): On connection error, recreate connection and flush cache.
124 }
125
126 void MDnsClientImpl::Core::AlertListeners(
127 MDnsUpdateType update_type,
128 const ListenerKey& key,
129 const RecordParsed* record) {
130 ListenerMap::iterator listener_map_iterator = listeners_.find(key);
131 if (listener_map_iterator == listeners_.end()) return;
132
133 FOR_EACH_OBSERVER(MDnsListenerImpl, *listener_map_iterator->second,
134 AlertDelegate(update_type, record));
135 }
136
137 void MDnsClientImpl::Core::AddListener(
138 MDnsListenerImpl* listener) {
139 ListenerKey key(listener->GetType(), listener->GetName());
140 std::pair<ListenerMap::iterator, bool> observer_insert_result =
141 listeners_.insert(
142 make_pair(key, static_cast<ObserverList<MDnsListenerImpl>*>(NULL)));
143
144 // If an equivalent key does not exist, actually create the observer list.
145 if (observer_insert_result.second) {
146 observer_insert_result.first->second = new ObserverList<MDnsListenerImpl>();
147 }
148
149 ObserverList<MDnsListenerImpl>* observer_list =
150 observer_insert_result.first->second;
151
152 observer_list->AddObserver(listener);
153 }
154
155 void MDnsClientImpl::Core::RemoveListener(MDnsListenerImpl* listener) {
156 ListenerKey key(listener->GetType(), listener->GetName());
157 ListenerMap::iterator observer_list_iterator = listeners_.find(key);
158
159 DCHECK(observer_list_iterator != listeners_.end());
160 DCHECK(observer_list_iterator->second->HasObserver(listener));
161
162 observer_list_iterator->second->RemoveObserver(listener);
163
164 // Remove the observer list from the map if it is empty
165 if (observer_list_iterator->second->size() == 0) {
166 delete observer_list_iterator->second;
167 listeners_.erase(observer_list_iterator);
168 }
169 }
170
171 void MDnsClientImpl::Core::ScheduleCleanup(base::Time cleanup) {
172 // Cleanup is already scheduled, no need to do anything.
173 if (cleanup == scheduled_cleanup_) return;
174 scheduled_cleanup_ = cleanup;
175
176 // This line has the effect of cancelling the previously scheduled cleanup.
177 cleanup_callback_.Reset(base::Bind(
178 &MDnsClientImpl::Core::DoCleanup, base::Unretained(this)));
179
180 // cleanup == base::Time means no cleanup necessary.
181 if (cleanup != base::Time()) {
182 base::MessageLoop::current()->PostDelayedTask(
183 FROM_HERE,
184 cleanup_callback_.callback(),
185 cleanup - base::Time::Now());
186 }
187 }
188
189 void MDnsClientImpl::Core::DoCleanup() {
190 cache_.CleanupRecords(base::Time::Now(), base::Bind(
191 &MDnsClientImpl::Core::OnRecordRemoved, base::Unretained(this)));
192
193 ScheduleCleanup(cache_.next_expiration());
194 }
195
196 void MDnsClientImpl::Core::OnRecordRemoved(
197 const RecordParsed* record) {
198 AlertListeners(kMDnsRecordRemoved,
199 ListenerKey(record->type(), record->name()), record);
200 // Alert listeners listening only for rrtype and not for name.
201 AlertListeners(kMDnsRecordRemoved, ListenerKey(record->type(), ""),
202 record);
203 }
204
205 void MDnsClientImpl::Core::QueryCache(
206 uint16 rrtype, const std::string& name,
207 std::vector<const RecordParsed*>* records) const {
208 cache_.FindDnsRecords(rrtype, name, records, base::Time::Now());
209 }
210
211 MDnsClientImpl::MDnsClientImpl()
212 : listen_refs_(0), connection_factory_owned_(
213 new MDnsConnectionImplFactory()) {
214 connection_factory_ = connection_factory_owned_.get();
215 }
216
217 MDnsClientImpl::MDnsClientImpl(MDnsConnectionFactory* connection_factory)
218 : listen_refs_(0), connection_factory_(connection_factory) {
219 }
220
221 MDnsClientImpl::~MDnsClientImpl() {
222 }
223
224 bool MDnsClientImpl::AddListenRef() {
225 if (!core_.get()) {
226 core_.reset(new Core(this, connection_factory_));
227 if (!core_->Init()) {
228 core_.reset();
229 return false;
230 }
231 }
232 listen_refs_++;
233 return true;
234 }
235
236 void MDnsClientImpl::SubtractListenRef() {
237 listen_refs_--;
238 if (listen_refs_ == 0) {
239 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
240 &MDnsClientImpl::Shutdown, base::Unretained(this)));
241 }
242 }
243
244 void MDnsClientImpl::Shutdown() {
245 // We need to check that new listeners haven't been created.
246 if (listen_refs_ == 0) {
247 core_.reset();
248 }
249 }
250
251 bool MDnsClientImpl::IsListeningForTests() {
252 return core_.get() != NULL;
253 }
254
255 scoped_ptr<MDnsListener> MDnsClientImpl::CreateListener(
256 uint16 rrtype,
257 const std::string& name,
258 MDnsListener::Delegate* delegate) {
259 return scoped_ptr<net::MDnsListener>(
260 new MDnsListenerImpl(rrtype, name, delegate, this));
261 }
262
263 scoped_ptr<MDnsTransaction> MDnsClientImpl::CreateTransaction(
264 uint16 rrtype,
265 const std::string& name,
266 int flags,
267 const MDnsTransaction::ResultCallback& callback) {
268 return scoped_ptr<MDnsTransaction>(
269 new MDnsTransactionImpl(rrtype, name, flags, callback, this));
270 }
271
272 MDnsListenerImpl::MDnsListenerImpl(
273 uint16 rrtype,
274 const std::string& name,
275 MDnsListener::Delegate* delegate,
276 MDnsClientImpl* client)
277 : rrtype_(rrtype), name_(name), client_(client), delegate_(delegate),
278 started_(false) {
279 }
280
281 bool MDnsListenerImpl::Start() {
282 DCHECK(!started_);
283
284 if (!client_->AddListenRef()) return false;
285 started_ = true;
286
287 client_->core()->AddListener(this);
288
289 return true;
290 }
291
292 MDnsListenerImpl::~MDnsListenerImpl() {
293 if (started_) {
294 client_->core()->RemoveListener(this);
295 client_->SubtractListenRef();
296 }
297 }
298
299 const std::string& MDnsListenerImpl::GetName() const {
300 return name_;
301 }
302
303 uint16 MDnsListenerImpl::GetType() const {
304 return rrtype_;
305 }
306
307 void MDnsListenerImpl::AlertDelegate(MDnsUpdateType update_type,
308 const RecordParsed* record) {
309 DCHECK(started_);
310 delegate_->OnRecordUpdate(update_type, record);
311 }
312
313 MDnsTransactionImpl::MDnsTransactionImpl(
314 uint16 rrtype,
315 const std::string& name,
316 int flags,
317 const MDnsTransaction::ResultCallback& callback,
318 MDnsClientImpl* client)
319 : rrtype_(rrtype), name_(name), callback_(callback), client_(client),
320 started_(false), flags_(flags) {
321 DCHECK((flags_ & kMDnsTransactionFlagMask) == flags_);
322 DCHECK(flags_ & kMDnsTransactionQueryCache ||
323 flags_ & kMDnsTransactionQueryNetwork);
324 }
325
326 MDnsTransactionImpl::~MDnsTransactionImpl() {
327 timeout_.Cancel();
328 }
329
330 bool MDnsTransactionImpl::Start() {
331 DCHECK(!started_);
332 started_ = true;
333 std::vector<const RecordParsed*> records;
334 base::WeakPtr<MDnsTransactionImpl> weak_this = AsWeakPtr();
335
336 if (flags_ & kMDnsTransactionQueryCache) {
337 if (client_->core()) {
338 client_->core()->QueryCache(rrtype_, name_, &records);
339 for (std::vector<const RecordParsed*>::iterator i = records.begin();
340 i != records.end() && weak_this; ++i) {
341 weak_this->TriggerCallback(kMDnsTransactionRecord, records.front());
342 }
343 }
344 }
345
346 if (!weak_this) return true;
347
348 if (is_active() && flags_ & kMDnsTransactionQueryNetwork) {
349 listener_ = client_->CreateListener(rrtype_, name_, this);
350 if (!listener_->Start()) return false;
351
352 DCHECK(client_->core());
353 if (!client_->core()->SendQuery(rrtype_, name_)) {
354 return false;
355 }
356
357 timeout_.Reset(base::Bind(&MDnsTransactionImpl::SignalTransactionOver,
358 weak_this));
359 base::MessageLoop::current()->PostDelayedTask(
360 FROM_HERE,
361 timeout_.callback(),
362 base::TimeDelta::FromSeconds(kMDnsTransactionNoResultsSeconds));
363
364 return listener_.get() != NULL;
365 } else {
366 // If this is a cache only query, signal that the transaction is over
367 // immediately.
368 SignalTransactionOver();
369 }
370
371 return true;
372 }
373
374 const std::string& MDnsTransactionImpl::GetName() const {
375 return name_;
376 }
377
378 uint16 MDnsTransactionImpl::GetType() const {
379 return rrtype_;
380 }
381
382 void MDnsTransactionImpl::CacheRecordFound(const RecordParsed* record) {
383 DCHECK(started_);
384 OnRecordUpdate(kMDnsRecordAdded, record);
385 }
386
387 void MDnsTransactionImpl::TriggerCallback(MDnsTransactionResult result,
388 const RecordParsed* record) {
389 DCHECK(started_);
390 if (!is_active()) return;
391
392 // Ensure callback is run after touching all class state, so that
393 // the callback can delete the transaction.
394 MDnsTransaction::ResultCallback callback = callback_;
395
396 if (flags_ & kMDnsTransactionSingleResult) {
397 Reset();
398 }
399
400 callback.Run(result, record);
401 }
402
403 void MDnsTransactionImpl::Reset() {
404 callback_.Reset();
405 listener_.reset();
406 timeout_.Cancel();
407 }
408
409 void MDnsTransactionImpl::OnRecordUpdate(MDnsUpdateType update,
410 const RecordParsed* record) {
411 DCHECK(started_);
412 if (update == kMDnsRecordAdded || update == kMDnsRecordChanged) {
413 TriggerCallback(kMDnsTransactionRecord, record);
414 }
415 }
416
417 void MDnsTransactionImpl::SignalTransactionOver() {
418 DCHECK(started_);
419 base::WeakPtr<MDnsTransactionImpl> weak_this = AsWeakPtr();
420
421 if (flags_ & kMDnsTransactionSingleResult) {
422 TriggerCallback(kMDnsTransactionNoResults, NULL);
423 } else {
424 TriggerCallback(kMDnsTransactionDone, NULL);
425 }
426
427 if (weak_this) {
428 weak_this->Reset();
429 }
430 }
431
432 void MDnsTransactionImpl::OnNsecRecord(const std::string& name, unsigned type) {
433 // TODO(noamsml): NSEC records not yet implemented
434 }
435
436 void MDnsTransactionImpl::OnCachePurged() {
437 // TODO(noamsml): Cache purge situations not yet implemented
438 }
439
440 MDnsConnectionImpl::SocketHandler::SocketHandler(
441 MDnsConnectionImpl* connection, const IPEndPoint& multicast_addr)
442 : socket_owned_(new UDPServerSocket(NULL, NetLog::Source())),
443 socket_(socket_owned_.get()), connection_(connection),
444 response_(new DnsResponse(dns_protocol::kMaxMulticastSize)),
445 multicast_addr_(multicast_addr) {
446 }
447
448 MDnsConnectionImpl::SocketHandler::SocketHandler(
449 DatagramServerSocket* socket,
450 MDnsConnectionImpl* connection, const IPEndPoint& multicast_addr)
451 : socket_(socket), connection_(connection),
452 response_(new DnsResponse(dns_protocol::kMaxMulticastSize)),
453 multicast_addr_(multicast_addr) {
454 }
455
456
457 MDnsConnectionImpl::SocketHandler::~SocketHandler() {
458 }
459
460 int MDnsConnectionImpl::SocketHandler::Start() {
461 int rv = BindSocket();
462 if (rv != OK) {
463 return rv;
464 }
465
466 return DoLoop(0);
467 }
468
469 int MDnsConnectionImpl::SocketHandler::DoLoop(int rv) {
470 do {
471 if (rv > 0) {
472 connection_->OnDatagramReceived(socket_,
473 response_.get(), &recv_addr_, rv);
474 }
475
476 rv = socket_->RecvFrom(
477 response_->io_buffer(),
478 response_->io_buffer()->size(),
479 &recv_addr_,
480 base::Bind(&MDnsConnectionImpl::SocketHandler::OnDatagramReceived,
481 base::Unretained(this)));
482 } while (rv > 0);
483
484 if (rv != ERR_IO_PENDING) {
485 return rv;
486 }
487
488 return OK;
489 }
490
491 void MDnsConnectionImpl::SocketHandler::OnDatagramReceived(int rv) {
492 if (rv >= OK) {
493 rv = DoLoop(rv);
494 }
495
496 if (rv != OK) {
497 connection_->OnError(this, rv);
498 }
499 }
500
501 int MDnsConnectionImpl::SocketHandler::Send(IOBuffer* buffer, unsigned size) {
502 return socket_->SendTo(
503 buffer, size, multicast_addr_,
504 base::Bind(&MDnsConnectionImpl::SocketHandler::SendDone,
505 base::Unretained(this) ));
506 }
507
508 void MDnsConnectionImpl::SocketHandler::SendDone(int sent) {
509 // TODO(noamsml): Retry logic.
510 }
511
512 int MDnsConnectionImpl::SocketHandler::BindSocket() {
513 IPAddressNumber address_any;
514 address_any.resize(multicast_addr_.address().size(), 0);
515
516 IPEndPoint bind_endpoint(address_any, multicast_addr_.port());
517
518 socket_->AllowAddressReuse();
519 int rv = socket_->Listen(bind_endpoint);
520
521 if (rv < OK) return rv;
522
523 socket_->SetMulticastLoopbackMode(false);
524
525 return socket_->JoinGroup(multicast_addr_.address());
526 }
527
528
529 MDnsConnectionImpl::MDnsConnectionImpl(MDnsConnection::Delegate* delegate)
530 : socket_handler_ipv4_(this, GetIPEndPoint(kMDnsMulticastGroupIPv4,
531 dns_protocol::kDefaultPortMulticast)),
532 socket_handler_ipv6_(this, GetIPEndPoint(kMDnsMulticastGroupIPv6,
533 dns_protocol::kDefaultPortMulticast)),
534 delegate_(delegate) {
535 }
536
537 MDnsConnectionImpl::MDnsConnectionImpl(DatagramServerSocket* socket_ipv4,
538 DatagramServerSocket* socket_ipv6,
539 MDnsConnection::Delegate* delegate)
540 : socket_handler_ipv4_(socket_ipv4, this,
541 GetIPEndPoint(kMDnsMulticastGroupIPv4,
542 dns_protocol::kDefaultPortMulticast)),
543 socket_handler_ipv6_(socket_ipv6, this,
544 GetIPEndPoint(kMDnsMulticastGroupIPv6,
545 dns_protocol::kDefaultPortMulticast)),
546 delegate_(delegate) {
547 }
548
549 MDnsConnectionImpl::~MDnsConnectionImpl() {
550 }
551
552 int MDnsConnectionImpl::Init() {
553 int rv;
554
555 rv = socket_handler_ipv4_.Start();
556 if (rv != OK) return rv;
557 rv = socket_handler_ipv6_.Start();
558 if (rv != OK) return rv;
559
560 return true;
561 }
562
563 int MDnsConnectionImpl::Send(IOBuffer* buffer, unsigned size) {
564 int rv;
565
566 rv = socket_handler_ipv4_.Send(buffer, size);
567 if (rv < OK && rv != ERR_IO_PENDING) return rv;
568
569 rv = socket_handler_ipv6_.Send(buffer, size);
570 if (rv < OK && rv != ERR_IO_PENDING) return rv;
571
572 return OK;
573 }
574
575 void MDnsConnectionImpl::OnError(SocketHandler* loop,
576 int error) {
577 // TODO(noamsml): Specific handling of intermittent errors that can be handled
578 // in the connection.
579 delegate_->OnConnectionError(error);
580 }
581
582 IPEndPoint MDnsConnectionImpl::GetIPEndPoint(const char* address, int port) {
583 IPAddressNumber multicast_group_number;
584 bool success = ParseIPLiteralToNumber(address,
585 &multicast_group_number);
586 DCHECK(success);
587 return IPEndPoint(multicast_group_number, port);
588 }
589
590 void MDnsConnectionImpl::OnDatagramReceived(
591 DatagramServerSocket* socket,
592 DnsResponse* response,
593 IPEndPoint* recv_addr,
594 int bytes_read) {
595 // TODO(noamsml): More sophisticated error handling.
596 DCHECK_GT(bytes_read, 0);
597 delegate_->HandlePacket(response, bytes_read);
598 }
599
600 MDnsConnectionImplFactory::MDnsConnectionImplFactory() {
601 }
602
603 MDnsConnectionImplFactory::~MDnsConnectionImplFactory() {
604 }
605
606 scoped_ptr<MDnsConnection> MDnsConnectionImplFactory::CreateConnection(
607 MDnsConnection::Delegate* delegate) {
608 return scoped_ptr<MDnsConnection>(new MDnsConnectionImpl(delegate));
609 }
610
611 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698