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

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";
szym 2013/06/12 21:35:41 nit: use anonymous namespace instead of static
Noam Samuel 2013/06/13 01:08:40 Done.
21 static const char kMDnsMulticastGroupIPv6[] = "FF02::FB";
22
23 static const unsigned kMDnsTransactionNoResultsSeconds = 3;
24
25 MDnsClientImpl::Core::Core(MDnsClientImpl* client,
26 MDnsDatagramServerSocketFactory* socket_factory)
27 : client_(client), connection_(new MDnsConnection(socket_factory, this)) {
28 }
29
30 MDnsClientImpl::Core::~Core() {
31 cleanup_callback_.Cancel();
szym 2013/06/12 21:35:41 Not necessary. Destroying CancelableCallback cance
Noam Samuel 2013/06/13 01:08:40 Done.
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 DnsQuery query(0, name_dns, rrtype);
45 query.set_flags(0); // Remove the RD flag from the query. It is unneeded.
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 }
szym 2013/06/12 21:35:41 nit: Here and elsewhere in this CL. No need for {}
Noam Samuel 2013/06/13 01:08:40 Done.
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:
szym 2013/06/12 21:35:41 I suggest adding case default: here.
Noam Samuel 2013/06/13 01:08:40 Done.
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.
szym 2013/06/12 21:35:41 No need for "This line has the effect". Just "This
Noam Samuel 2013/06/13 01:08:40 Done.
177 cleanup_callback_.Reset(base::Bind(
178 &MDnsClientImpl::Core::DoCleanup, base::Unretained(this)));
179
180 // cleanup == base::Time means no cleanup necessary.
szym 2013/06/12 21:35:41 I suggest "If |cleanup| is empty, then no cleanup
Noam Samuel 2013/06/13 01:08:40 Done.
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(MDnsDatagramServerSocketFactory* socket_factory)
212 : listen_refs_(0), socket_factory_(socket_factory) {
213 }
214
215 MDnsClientImpl::~MDnsClientImpl() {
216 }
217
218 bool MDnsClientImpl::AddListenRef() {
219 if (!core_.get()) {
220 core_.reset(new Core(this, socket_factory_.get()));
221 if (!core_->Init()) {
222 core_.reset();
223 return false;
224 }
225 }
226 listen_refs_++;
227 return true;
228 }
229
230 void MDnsClientImpl::SubtractListenRef() {
231 listen_refs_--;
232 if (listen_refs_ == 0) {
233 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
234 &MDnsClientImpl::Shutdown, base::Unretained(this)));
235 }
236 }
237
238 void MDnsClientImpl::Shutdown() {
239 // We need to check that new listeners haven't been created.
240 if (listen_refs_ == 0) {
241 core_.reset();
242 }
243 }
244
245 bool MDnsClientImpl::IsListeningForTests() {
246 return core_.get() != NULL;
247 }
248
249 scoped_ptr<MDnsListener> MDnsClientImpl::CreateListener(
250 uint16 rrtype,
251 const std::string& name,
252 MDnsListener::Delegate* delegate) {
253 return scoped_ptr<net::MDnsListener>(
254 new MDnsListenerImpl(rrtype, name, delegate, this));
255 }
256
257 scoped_ptr<MDnsTransaction> MDnsClientImpl::CreateTransaction(
258 uint16 rrtype,
259 const std::string& name,
260 int flags,
261 const MDnsTransaction::ResultCallback& callback) {
262 return scoped_ptr<MDnsTransaction>(
263 new MDnsTransactionImpl(rrtype, name, flags, callback, this));
264 }
265
266 MDnsListenerImpl::MDnsListenerImpl(
267 uint16 rrtype,
268 const std::string& name,
269 MDnsListener::Delegate* delegate,
270 MDnsClientImpl* client)
271 : rrtype_(rrtype), name_(name), client_(client), delegate_(delegate),
272 started_(false) {
273 }
274
275 bool MDnsListenerImpl::Start() {
276 DCHECK(!started_);
277
278 if (!client_->AddListenRef()) return false;
279 started_ = true;
280
281 client_->core()->AddListener(this);
szym 2013/06/12 21:35:41 Do you need DCHECK(client_->core()) ?
Noam Samuel 2013/06/13 01:08:40 Done.
282
283 return true;
284 }
285
286 MDnsListenerImpl::~MDnsListenerImpl() {
287 if (started_) {
288 client_->core()->RemoveListener(this);
szym 2013/06/12 21:35:41 ditto
Noam Samuel 2013/06/13 01:08:40 Done.
289 client_->SubtractListenRef();
290 }
291 }
292
293 const std::string& MDnsListenerImpl::GetName() const {
294 return name_;
295 }
296
297 uint16 MDnsListenerImpl::GetType() const {
298 return rrtype_;
299 }
300
301 void MDnsListenerImpl::AlertDelegate(MDnsUpdateType update_type,
302 const RecordParsed* record) {
303 DCHECK(started_);
304 delegate_->OnRecordUpdate(update_type, record);
305 }
306
307 MDnsTransactionImpl::MDnsTransactionImpl(
308 uint16 rrtype,
309 const std::string& name,
310 int flags,
311 const MDnsTransaction::ResultCallback& callback,
312 MDnsClientImpl* client)
313 : rrtype_(rrtype), name_(name), callback_(callback), client_(client),
314 started_(false), flags_(flags) {
315 DCHECK((flags_ & kMDnsTransactionFlagMask) == flags_);
316 DCHECK(flags_ & kMDnsTransactionQueryCache ||
317 flags_ & kMDnsTransactionQueryNetwork);
318 }
319
320 MDnsTransactionImpl::~MDnsTransactionImpl() {
321 timeout_.Cancel();
322 }
323
324 bool MDnsTransactionImpl::Start() {
325 DCHECK(!started_);
326 started_ = true;
327 std::vector<const RecordParsed*> records;
328 base::WeakPtr<MDnsTransactionImpl> weak_this = AsWeakPtr();
329
330 if (flags_ & kMDnsTransactionQueryCache) {
331 if (client_->core()) {
332 client_->core()->QueryCache(rrtype_, name_, &records);
333 for (std::vector<const RecordParsed*>::iterator i = records.begin();
334 i != records.end() && weak_this; ++i) {
335 weak_this->TriggerCallback(kMDnsTransactionRecord, records.front());
336 }
337 }
338 }
339
340 if (!weak_this) return true;
341
342 if (is_active() && flags_ & kMDnsTransactionQueryNetwork) {
szym 2013/06/12 21:35:41 nit: wrap the & part in parens to avoid confusion.
Noam Samuel 2013/06/13 01:08:40 Done.
343 listener_ = client_->CreateListener(rrtype_, name_, this);
344 if (!listener_->Start()) return false;
345
346 DCHECK(client_->core());
347 if (!client_->core()->SendQuery(rrtype_, name_)) {
348 return false;
349 }
350
351 timeout_.Reset(base::Bind(&MDnsTransactionImpl::SignalTransactionOver,
352 weak_this));
353 base::MessageLoop::current()->PostDelayedTask(
354 FROM_HERE,
355 timeout_.callback(),
356 base::TimeDelta::FromSeconds(kMDnsTransactionNoResultsSeconds));
357
358 return listener_.get() != NULL;
359 } else {
360 // If this is a cache only query, signal that the transaction is over
361 // immediately.
362 SignalTransactionOver();
363 }
364
365 return true;
366 }
367
368 const std::string& MDnsTransactionImpl::GetName() const {
369 return name_;
370 }
371
372 uint16 MDnsTransactionImpl::GetType() const {
373 return rrtype_;
374 }
375
376 void MDnsTransactionImpl::CacheRecordFound(const RecordParsed* record) {
377 DCHECK(started_);
378 OnRecordUpdate(kMDnsRecordAdded, record);
379 }
380
381 void MDnsTransactionImpl::TriggerCallback(MDnsTransactionResult result,
382 const RecordParsed* record) {
383 DCHECK(started_);
384 if (!is_active()) return;
385
386 // Ensure callback is run after touching all class state, so that
387 // the callback can delete the transaction.
388 MDnsTransaction::ResultCallback callback = callback_;
389
390 if (flags_ & kMDnsTransactionSingleResult) {
391 Reset();
392 }
393
394 callback.Run(result, record);
395 }
396
397 void MDnsTransactionImpl::Reset() {
398 callback_.Reset();
399 listener_.reset();
400 timeout_.Cancel();
401 }
402
403 void MDnsTransactionImpl::OnRecordUpdate(MDnsUpdateType update,
404 const RecordParsed* record) {
405 DCHECK(started_);
406 if (update == kMDnsRecordAdded || update == kMDnsRecordChanged) {
407 TriggerCallback(kMDnsTransactionRecord, record);
408 }
409 }
410
411 void MDnsTransactionImpl::SignalTransactionOver() {
412 DCHECK(started_);
413 base::WeakPtr<MDnsTransactionImpl> weak_this = AsWeakPtr();
414
415 if (flags_ & kMDnsTransactionSingleResult) {
416 TriggerCallback(kMDnsTransactionNoResults, NULL);
417 } else {
418 TriggerCallback(kMDnsTransactionDone, NULL);
419 }
420
421 if (weak_this) {
422 weak_this->Reset();
423 }
424 }
425
426 void MDnsTransactionImpl::OnNsecRecord(const std::string& name, unsigned type) {
427 // TODO(noamsml): NSEC records not yet implemented
428 }
429
430 void MDnsTransactionImpl::OnCachePurged() {
431 // TODO(noamsml): Cache purge situations not yet implemented
432 }
433
434 MDnsConnection::SocketHandler::SocketHandler(
szym 2013/06/12 21:35:41 Move MDnsConnection to the top of the file to matc
Noam Samuel 2013/06/13 01:08:40 Done.
435 MDnsConnection* connection, const IPEndPoint& multicast_addr,
436 MDnsDatagramServerSocketFactory* socket_factory)
437 : socket_(socket_factory->CreateSocket()), connection_(connection),
438 response_(new DnsResponse(dns_protocol::kMaxMulticastSize)),
439 multicast_addr_(multicast_addr) {
440 }
441
442 MDnsConnection::SocketHandler::~SocketHandler() {
443 }
444
445 int MDnsConnection::SocketHandler::Start() {
446 int rv = BindSocket();
447 if (rv != OK) {
448 return rv;
449 }
450
451 return DoLoop(0);
452 }
453
454 int MDnsConnection::SocketHandler::DoLoop(int rv) {
455 do {
456 if (rv > 0) {
457 connection_->OnDatagramReceived(socket_.get(),
458 response_.get(), &recv_addr_, rv);
459 }
460
461 rv = socket_->RecvFrom(
462 response_->io_buffer(),
463 response_->io_buffer()->size(),
464 &recv_addr_,
465 base::Bind(&MDnsConnection::SocketHandler::OnDatagramReceived,
466 base::Unretained(this)));
467 } while (rv > 0);
468
469 if (rv != ERR_IO_PENDING) {
470 return rv;
471 }
472
473 return OK;
474 }
475
476 void MDnsConnection::SocketHandler::OnDatagramReceived(int rv) {
477 if (rv >= OK) {
478 rv = DoLoop(rv);
479 }
480
481 if (rv != OK) {
482 connection_->OnError(this, rv);
483 }
484 }
485
486 int MDnsConnection::SocketHandler::Send(IOBuffer* buffer, unsigned size) {
487 return socket_->SendTo(
488 buffer, size, multicast_addr_,
489 base::Bind(&MDnsConnection::SocketHandler::SendDone,
490 base::Unretained(this) ));
491 }
492
493 void MDnsConnection::SocketHandler::SendDone(int sent) {
494 // TODO(noamsml): Retry logic.
495 }
496
497 int MDnsConnection::SocketHandler::BindSocket() {
498 IPAddressNumber address_any;
499 address_any.resize(multicast_addr_.address().size(), 0);
szym 2013/06/12 21:35:41 You can do it in the initializer. IPAddressNumber
Noam Samuel 2013/06/13 01:08:40 Done.
500
501 IPEndPoint bind_endpoint(address_any, multicast_addr_.port());
502
503 socket_->AllowAddressReuse();
504 int rv = socket_->Listen(bind_endpoint);
505
506 if (rv < OK) return rv;
507
508 socket_->SetMulticastLoopbackMode(false);
509
510 return socket_->JoinGroup(multicast_addr_.address());
511 }
512
513 MDnsConnection::MDnsConnection(MDnsDatagramServerSocketFactory* socket_factory,
514 MDnsConnection::Delegate* delegate)
515 : socket_handler_ipv4_(this,
516 GetMDnsIPEndPoint(kMDnsMulticastGroupIPv4),
517 socket_factory),
518 socket_handler_ipv6_(this,
519 GetMDnsIPEndPoint(kMDnsMulticastGroupIPv6),
520 socket_factory),
521 delegate_(delegate) {
522 }
523
524 MDnsConnection::~MDnsConnection() {
525 }
526
527 int MDnsConnection::Init() {
528 int rv;
529
530 rv = socket_handler_ipv4_.Start();
531 if (rv != OK) return rv;
532 rv = socket_handler_ipv6_.Start();
533 if (rv != OK) return rv;
534
535 return OK;
536 }
537
538 int MDnsConnection::Send(IOBuffer* buffer, unsigned size) {
539 int rv;
540
541 rv = socket_handler_ipv4_.Send(buffer, size);
542 if (rv < OK && rv != ERR_IO_PENDING) return rv;
543
544 rv = socket_handler_ipv6_.Send(buffer, size);
545 if (rv < OK && rv != ERR_IO_PENDING) return rv;
546
547 return OK;
548 }
549
550 void MDnsConnection::OnError(SocketHandler* loop,
551 int error) {
552 // TODO(noamsml): Specific handling of intermittent errors that can be handled
553 // in the connection.
554 delegate_->OnConnectionError(error);
555 }
556
557 IPEndPoint MDnsConnection::GetMDnsIPEndPoint(const char* address) {
558 IPAddressNumber multicast_group_number;
559 bool success = ParseIPLiteralToNumber(address,
560 &multicast_group_number);
561 DCHECK(success);
562 return IPEndPoint(multicast_group_number,
563 dns_protocol::kDefaultPortMulticast);
564 }
565
566 void MDnsConnection::OnDatagramReceived(
567 DatagramServerSocket* socket,
568 DnsResponse* response,
569 IPEndPoint* recv_addr,
570 int bytes_read) {
571 // TODO(noamsml): More sophisticated error handling.
572 DCHECK_GT(bytes_read, 0);
573 delegate_->HandlePacket(response, bytes_read);
574 }
575
576 MDnsDatagramServerSocketFactoryImpl::MDnsDatagramServerSocketFactoryImpl() {
577 }
578
579 MDnsDatagramServerSocketFactoryImpl::~MDnsDatagramServerSocketFactoryImpl() {
580 }
581
582 scoped_ptr<DatagramServerSocket>
583 MDnsDatagramServerSocketFactoryImpl::CreateSocket() {
584 return scoped_ptr<DatagramServerSocket>(new UDPServerSocket(
585 NULL, NetLog::Source()));
586 }
587
588 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698