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

Side by Side Diff: chrome/browser/local_discovery/service_discovery_client_impl.cc

Issue 16272006: In-browser DNS-based service discovery system (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mdns_implementation
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 <utility>
6
7 #include "base/logging.h"
8 #include "base/message_loop_proxy.h"
9 #include "chrome/browser/local_discovery/service_discovery_client_impl.h"
10 #include "net/dns/dns_protocol.h"
11 #include "net/dns/record_rdata.h"
12
13 namespace local_discovery {
14
15 ServiceDiscoveryClientImpl::ServiceDiscoveryClientImpl() {
16 }
17
18 ServiceDiscoveryClientImpl::~ServiceDiscoveryClientImpl() {
19 }
20
21 scoped_ptr<ServiceTypeWatcher>
22 ServiceDiscoveryClientImpl::CreateServiceTypeWatcher(
23 const std::string& service_type,
24 bool active,
25 bool alert_existing_services,
26 ServiceTypeWatcher::Delegate* delegate) {
27 return scoped_ptr<ServiceTypeWatcher>(new ServiceTypeWatcherImpl(
28 service_type, active, alert_existing_services, delegate));
29 }
30
31 scoped_ptr<ServiceReader> ServiceDiscoveryClientImpl::CreateServiceReader(
32 const std::string& service_name,
33 ServiceReader::Delegate* delegate) {
34 return scoped_ptr<ServiceReader>(new ServiceReaderImpl(
35 service_name, delegate));
36 }
37
38 ServiceTypeWatcherImpl::ServiceTypeWatcherImpl(
39 const std::string& service_type,
40 bool active,
41 bool alert_existing_services,
42 ServiceTypeWatcher::Delegate* delegate)
43 : service_type_(service_type), active_(active),
44 alert_existing_services_(alert_existing_services), delegate_(delegate),
45 started_(false) {
46 }
47
48 bool ServiceTypeWatcherImpl::Start() {
49 DCHECK(!started_);
50 listener_ = net::MDnsClient::GetInstance()->CreateListener(
51 net::dns_protocol::kTypePTR, service_type_, this);
52 if (!listener_->Start()) return false;
Vitaly Buka (NO REVIEWS) 2013/06/15 00:22:30 Chromium coding styles requires line break after c
Noam Samuel 2013/06/18 22:46:03 Done.
53 if (!CreateTransaction(active_, alert_existing_services_, false))
54 return false;
55
56 started_ = true;
57 return true;
58 }
59
60 ServiceTypeWatcherImpl::~ServiceTypeWatcherImpl() {
61 }
62
63 void ServiceTypeWatcherImpl::GetAvailableServices(
64 std::vector<std::string>* services) const {
65 DCHECK(started_);
66 DCHECK(services);
67 services->insert(services->begin(), services_.begin(), services_.end());
68 }
69
70 void ServiceTypeWatcherImpl::DiscoverNewServices() {
71 DCHECK(started_);
72 CreateTransaction(true, false, false);
73 }
74
75 void ServiceTypeWatcherImpl::ForceUpdateServices() {
76 DCHECK(started_);
77 CreateTransaction(true, false, true);
78 }
79
80 bool ServiceTypeWatcherImpl::CreateTransaction(
81 bool active, bool alert_existing_services, bool force_refresh) {
82 int transaction_flags = 0;
83 if (active) transaction_flags |= net::MDnsTransaction::QUERY_NETWORK;
Vitaly Buka (NO REVIEWS) 2013/06/15 00:22:30 same
Noam Samuel 2013/06/18 22:46:03 Done.
84
85 if (alert_existing_services) {
86 transaction_flags |= net::MDnsTransaction::QUERY_CACHE;
87 }
88
89 // TODO(noamsml): Add flag for force_refresh when support.
90
91 if (transaction_flags) {
92 transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
93 net::dns_protocol::kTypePTR, service_type_, transaction_flags,
94 base::Bind(&ServiceTypeWatcherImpl::OnTransactionResponse,
95 base::Unretained(this)));
96 return transaction_->Start();
97 }
98
99 return true;
100 }
101
102 std::string ServiceTypeWatcherImpl::GetServiceType() const {
103 return listener_->GetName();
104 }
105
106 bool ServiceTypeWatcherImpl::IsActive() const {
107 return active_;
108 }
109
110 void ServiceTypeWatcherImpl::OnRecordUpdate(
111 net::MDnsListener::UpdateType update,
112 const net::RecordParsed* record) {
113 DCHECK(started_);
114 DCHECK(record->name() == GetServiceType());
115 DCHECK(record->type() == net::dns_protocol::kTypePTR);
116
117 const net::PtrRecordRdata* rdata = record->rdata<net::PtrRecordRdata>();
118
119 switch (update) {
120 case net::MDnsListener::RECORD_ADDED:
121 AddService(rdata->ptrdomain());
122 break;
123 case net::MDnsListener::RECORD_CHANGED:
124 NOTREACHED();
125 break;
126 case net::MDnsListener::RECORD_REMOVED:
127 RemoveService(rdata->ptrdomain());
128 break;
129 }
130 }
131
132 void ServiceTypeWatcherImpl::OnCachePurged() {
133 // Not yet implemented.
134 }
135
136 void ServiceTypeWatcherImpl::OnTransactionResponse(
137 net::MDnsTransaction::Result result,
138 const net::RecordParsed* record) {
139 DCHECK(started_);
140 if (result == net::MDnsTransaction::RESULT_RECORD) {
141 const net::PtrRecordRdata* rdata = record->rdata<net::PtrRecordRdata>();
142 DCHECK(rdata);
143 AddService(rdata->ptrdomain());
144 } else if (result == net::MDnsTransaction::RESULT_DONE) {
145 transaction_.reset();
146 }
147
148 // Do nothing for NSEC records. It is an error for hosts to broadcast an NSEC
149 // record for PTR records on any name.
150 }
151
152 void ServiceTypeWatcherImpl::AddService(const std::string& service) {
153 DCHECK(started_);
154 std::pair<std::set<std::string>::iterator, bool> found =
155 services_.insert(service);
156 if (found.second) { // Newly inserted.
157 delegate_->OnServiceStatusChanged(true, service);
158 }
159 }
160
161 void ServiceTypeWatcherImpl::RemoveService(const std::string& service) {
162 DCHECK(started_);
163 if (services_.erase(service)) {
164 delegate_->OnServiceStatusChanged(false, service);
165 }
166 }
167
168 void ServiceTypeWatcherImpl::OnNsecRecord(const std::string& name,
169 unsigned rrtype) {
170 // Do nothing. It is an error for hosts to broadcast an NSEC record for PTR
171 // on any name.
172 }
173
174 ServiceReaderImpl::ServiceReaderImpl(
175 const std::string& service_name,
176 ServiceReader::Delegate* delegate)
177 : service_name_(service_name), delegate_(delegate),
178 has_address_(false), has_metadata_(false), started_(false) {
179 srv_listener_ = net::MDnsClient::GetInstance()->CreateListener(
180 net::dns_protocol::kTypeSRV, service_name, this);
181 txt_listener_ = net::MDnsClient::GetInstance()->CreateListener(
182 net::dns_protocol::kTypeTXT, service_name, this);
183 }
184
185 bool ServiceReaderImpl::Start() {
186 if (!srv_listener_->Start()) return false;
Vitaly Buka (NO REVIEWS) 2013/06/15 00:22:30 again
Noam Samuel 2013/06/18 22:46:03 Done.
187 if (!txt_listener_->Start()) return false;
188
189 // Update initial values
190 if (!CreateTxtTransaction()) return false;
191 if (!CreateSrvTransaction()) return false;
192 started_ = true;
193 return true;
194 }
195
196 ServiceReaderImpl::~ServiceReaderImpl() {
197 }
198
199 bool ServiceReaderImpl::GetMetadata(
200 std::vector<std::string>* metadata) const {
201 DCHECK(started_);
202 if (has_metadata_) {
203 *metadata = metadata_;
204 return true;
205 }
206 return false;
207 }
208
209 void ServiceReaderImpl::ReadMetadata(const MetadataCallback& callback,
210 bool force_refresh) {
211 DCHECK(started_);
212 metadata_callbacks_.push_back(callback);
213 if (!txt_transaction_.get()) {
214 CreateTxtTransaction();
215 }
216 }
217
218 bool ServiceReaderImpl::CreateTxtTransaction() {
219 txt_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
220 net::dns_protocol::kTypeTXT, service_name_,
221 net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE |
222 net::MDnsTransaction::QUERY_NETWORK,
223 base::Bind(&ServiceReaderImpl::TxtRecordTransactionResponse,
224 AsWeakPtr()));
Vitaly Buka (NO REVIEWS) 2013/06/15 00:22:30 don't need to break bind line
Noam Samuel 2013/06/18 22:46:03 It doesn't fit by a couple of characters (at least
225 return txt_transaction_->Start();
226 }
227
228 bool ServiceReaderImpl::GetAddress(net::HostPortPair* address) const {
229 DCHECK(started_);
230 if (has_address_) {
231 *address = address_;
232 return true;
233 }
234 return false;
235 }
236
237 void ServiceReaderImpl::ReadAddress(const AddressCallback& callback,
238 bool force_refresh) {
239 DCHECK(started_);
240 address_callbacks_.push_back(callback);
241 if (!srv_transaction_.get()) {
242 CreateSrvTransaction();
243 }
244 }
245
246 bool ServiceReaderImpl::CreateSrvTransaction() {
247 srv_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
248 net::dns_protocol::kTypeSRV, service_name_,
249 net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE |
250 net::MDnsTransaction::QUERY_NETWORK,
251 base::Bind(&ServiceReaderImpl::SrvRecordTransactionResponse,
252 AsWeakPtr()));
253 return srv_transaction_->Start();
254 }
255
256 std::string ServiceReaderImpl::GetHumanReadableName() const {
257 // TODO(noamsml): Once we have escaping working, get this to
258 // parse escaped domains.
259 size_t first_period = service_name_.find_first_of('.');
260 return service_name_.substr(0, first_period);
261 }
262
263 std::string ServiceReaderImpl::GetType() const {
264 // TODO(noamsml): Once we have escaping working, get this to
265 // parse escaped domains.
266 size_t first_period = service_name_.find_first_of('.');
267 if (first_period == std::string::npos) return "";
Vitaly Buka (NO REVIEWS) 2013/06/15 00:22:30 more
Noam Samuel 2013/06/18 22:46:03 Done.
268 return service_name_.substr(first_period+1);
269 }
270
271 std::string ServiceReaderImpl::GetName() const {
272 return service_name_;
273 }
274
275 bool ServiceReaderImpl::IsAvailable() const {
276 DCHECK(started_);
277 // TODO(noamsml): This should ideally be based on the PTR record.
278 return has_address_;
279 }
280
281 void ServiceReaderImpl::ReadLastSeen(
282 const ServiceReader::LastSeenCallback& callback) {
283 DCHECK(started_);
284 // HACK(noamsml): Because cache-based MDns transactions are currently
285 // synchronous, there can only be one last seen callback at a time, so
286 // no need to keep a callback list.
287
288 last_seen_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
289 net::dns_protocol::kTypeSRV, service_name_,
290 net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE,
291 base::Bind(&ServiceReaderImpl::LastSeenTransactionResponse,
292 base::Unretained(this), callback));
293 last_seen_transaction_->Start();
294 }
295
296 void ServiceReaderImpl::LastSeenTransactionResponse(
297 const ServiceReader::LastSeenCallback& callback,
298 net::MDnsTransaction::Result status,
299 const net::RecordParsed* record) {
300 last_seen_transaction_.reset();
301 DCHECK(started_);
302 if (status == net::MDnsTransaction::RESULT_RECORD) {
303 DCHECK(record);
304 callback.Run(record->time_created());
305 } else {
306 callback.Run(base::Time());
307 }
308 }
309
310 void ServiceReaderImpl::OnRecordUpdate(net::MDnsListener::UpdateType update,
311 const net::RecordParsed* record) {
312 DCHECK(started_);
313 switch (record->type()) {
314 case net::dns_protocol::kTypeSRV: {
315 if (update == net::MDnsListener::RECORD_REMOVED) {
316 has_address_ = false;
317 address_ = net::HostPortPair();
Vitaly Buka (NO REVIEWS) 2013/06/15 00:22:30 same question as for has_metadata_ below
Noam Samuel 2013/06/18 22:46:03 Code path removed.
318 } else {
319 has_address_ = true;
320 address_ = RecordToAddress(record);
321 }
322
323 if (delegate_) {
324 delegate_->OnAddressChanged(service_name_,
325 address_);
326 }
327 break;
328 }
329 case net::dns_protocol::kTypeTXT: {
330 if (update == net::MDnsListener::RECORD_REMOVED) {
331 has_metadata_ = false;
Vitaly Buka (NO REVIEWS) 2013/06/15 00:22:30 can you just use metadata_.empty() instead of has_
Noam Samuel 2013/06/15 00:42:06 I think so. Technically, a service could have empt
332 metadata_ = std::vector<std::string>();
333 } else {
334 has_metadata_ = true;
335 metadata_ = RecordToMetadata(record);
336 }
337
338 if (delegate_) {
339 delegate_->OnMetadataChanged(service_name_,
340 metadata_);
Vitaly Buka (NO REVIEWS) 2013/06/15 00:22:30 one line
Noam Samuel 2013/06/18 22:46:03 Deleted.
341 }
342 break;
343 }
344 default:
345 NOTREACHED();
346 }
347 }
348
349 void ServiceReaderImpl::OnCachePurged() {
350 // Not yet implemented.
351 }
352
353 void ServiceReaderImpl::SrvRecordTransactionResponse(
354 net::MDnsTransaction::Result status, const net::RecordParsed* record) {
355 DCHECK(started_);
356 srv_transaction_.reset();
357
358 if (record) {
359 address_ = RecordToAddress(record);
360 has_metadata_ = true;
361 }
362
363 std::vector<ServiceReader::AddressCallback> address_callbacks;
364 address_callbacks.swap(address_callbacks_);
365
366 ServiceReader::RequestStatus output_status = MDnsStatusToRequestStatus(
367 status);
368
369 for (std::vector<ServiceReader::AddressCallback>::iterator i =
Vitaly Buka (NO REVIEWS) 2013/06/15 00:22:30 SrvRecordTransactionResponse and TxtRecordTransact
Noam Samuel 2013/06/15 00:42:06 Since they contain slightly different logic and ty
Noam Samuel 2013/06/18 22:46:03 Deleted.
370 address_callbacks.begin(); i != address_callbacks.end(); ++i) {
371 i->Run(output_status, address_);
372 }
373 }
374
375 void ServiceReaderImpl::TxtRecordTransactionResponse(
376 net::MDnsTransaction::Result status, const net::RecordParsed* record) {
377 DCHECK(started_);
378 txt_transaction_.reset();
379
380 std::vector<ServiceReader::MetadataCallback> metadata_callbacks;
381 metadata_callbacks.swap(metadata_callbacks_);
382
383 if (record) {
384 metadata_ = RecordToMetadata(record);
385 has_metadata_ = true;
386 }
387
388 ServiceReader::RequestStatus output_status = MDnsStatusToRequestStatus(
389 status);
390
391 for (std::vector<ServiceReader::MetadataCallback>::iterator i =
392 metadata_callbacks.begin(); i != metadata_callbacks.end(); ++i) {
393 i->Run(output_status, metadata_);
394 }
395 }
396
397 void ServiceReaderImpl::OnNsecRecord(const std::string& name,
398 unsigned rrtype) {
399 // TODO(noamsml): Logic NSEC in listener case.
400 }
401
402 ServiceReader::RequestStatus ServiceReaderImpl::MDnsStatusToRequestStatus(
403 net::MDnsTransaction::Result status) const {
404 switch (status) {
405 case net::MDnsTransaction::RESULT_RECORD:
406 return ServiceReader::STATUS_SUCCESS;
407 case net::MDnsTransaction::RESULT_NO_RESULTS:
408 return ServiceReader::STATUS_TIMEOUT;
409 case net::MDnsTransaction::RESULT_NSEC:
410 return ServiceReader::STATUS_KNOWN_NONEXISTENT;
411 case net::MDnsTransaction::RESULT_DONE:
412 NOTREACHED();
413 return ServiceReader::STATUS_TIMEOUT;
414 }
415 }
416
417 const std::vector<std::string>& ServiceReaderImpl::RecordToMetadata(
418 const net::RecordParsed* record) const {
419 DCHECK(record->type() == net::dns_protocol::kTypeTXT);
420 const net::TxtRecordRdata* txt_rdata = record->rdata<net::TxtRecordRdata>();
421 DCHECK(txt_rdata);
422 return txt_rdata->texts();
423 }
424
425 net::HostPortPair ServiceReaderImpl::RecordToAddress(
426 const net::RecordParsed* record) const {
427 DCHECK(record->type() == net::dns_protocol::kTypeSRV);
428 const net::SrvRecordRdata* srv_rdata = record->rdata<net::SrvRecordRdata>();
429 DCHECK(srv_rdata);
430 return net::HostPortPair(srv_rdata->target(), srv_rdata->port());
431 }
432
433 } // namespace local_discovery
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698