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

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/memory/singleton.h"
9 #include "base/message_loop_proxy.h"
10 #include "base/stl_util.h"
11 #include "chrome/browser/local_discovery/service_discovery_client_impl.h"
12 #include "net/dns/dns_protocol.h"
13 #include "net/dns/record_rdata.h"
14
15 namespace local_discovery {
16
17 ServiceDiscoveryClientImpl::ServiceDiscoveryClientImpl() {
18 }
19
20 ServiceDiscoveryClientImpl::~ServiceDiscoveryClientImpl() {
21 }
22
23 // static
24 ServiceDiscoveryClientImpl* ServiceDiscoveryClientImpl::GetInstance() {
25 return Singleton<ServiceDiscoveryClientImpl>::get();
26 }
27
28 scoped_ptr<ServiceWatcher> ServiceDiscoveryClientImpl::CreateServiceWatcher(
29 const std::string& service_type,
30 ServiceWatcher::Delegate* delegate) {
31 return scoped_ptr<ServiceWatcher>(new ServiceWatcherImpl(
32 service_type, delegate));
33 }
34
35 scoped_ptr<ServiceResolver> ServiceDiscoveryClientImpl::CreateServiceResolver(
36 const std::string& service_name,
37 const ServiceResolver::ResolveCompleteCallback& callback) {
38 return scoped_ptr<ServiceResolver>(new ServiceResolverImpl(
39 service_name, callback));
40 }
41
42 ServiceWatcherImpl::ServiceWatcherImpl(
43 const std::string& service_type,
44 ServiceWatcher::Delegate* delegate)
45 : service_type_(service_type), delegate_(delegate), started_(false) {
46 }
47
48 bool ServiceWatcherImpl::Start() {
49 DCHECK(!started_);
50 listener_ = net::MDnsClient::GetInstance()->CreateListener(
51 net::dns_protocol::kTypePTR, service_type_, this);
52 if (!listener_->Start())
53 return false;
54
55 started_ = true;
56 return true;
57 }
58
59 ServiceWatcherImpl::~ServiceWatcherImpl() {
60 STLDeleteValues(&services_);
61 }
62
63 void ServiceWatcherImpl::GetAvailableServices(
64 std::vector<std::string>* services) const {
65 DCHECK(started_);
66 DCHECK(services);
67 services->reserve(services_.size());
68 for (std::map<std::string, ServiceListeners*>::const_iterator i =
69 services_.begin(); i != services_.end(); i++) {
70 services->push_back(i->first);
71 }
72 }
73
74 void ServiceWatcherImpl::DiscoverNewServices(bool force_update) {
75 DCHECK(started_);
76 if (force_update) services_.clear();
Vitaly Buka (NO REVIEWS) 2013/06/18 23:01:46 please break if
Noam Samuel 2013/06/19 18:46:22 Done.
77 CreateTransaction(true /*network*/, false /*cache*/, force_update,
78 &transaction_network_);
79 }
80
81 void ServiceWatcherImpl::ReadCachedServices() {
82 DCHECK(started_);
83 CreateTransaction(false /*network*/, true /*cache*/, false /*force refresh*/,
84 &transaction_cache_);
85 }
86
87 bool ServiceWatcherImpl::CreateTransaction(
88 bool network, bool cache, bool force_refresh,
89 scoped_ptr<net::MDnsTransaction>* transaction) {
90 int transaction_flags = 0;
91 if (network)
92 transaction_flags |= net::MDnsTransaction::QUERY_NETWORK;
93
94 if (cache)
95 transaction_flags |= net::MDnsTransaction::QUERY_CACHE;
96
97 // TODO(noamsml): Add flag for force_refresh when supported.
98
99 if (transaction_flags) {
100 *transaction = net::MDnsClient::GetInstance()->CreateTransaction(
101 net::dns_protocol::kTypePTR, service_type_, transaction_flags,
102 base::Bind(&ServiceWatcherImpl::OnTransactionResponse,
103 base::Unretained(this), transaction));
104 return (*transaction)->Start();
105 }
106
107 return true;
108 }
109
110 std::string ServiceWatcherImpl::GetServiceType() const {
111 return listener_->GetName();
112 }
113
114 void ServiceWatcherImpl::OnRecordUpdate(
115 net::MDnsListener::UpdateType update,
116 const net::RecordParsed* record) {
117 DCHECK(started_);
118 if (record->type() == net::dns_protocol::kTypePTR) {
119 DCHECK(record->name() == GetServiceType());
120 const net::PtrRecordRdata* rdata = record->rdata<net::PtrRecordRdata>();
121
122 switch (update) {
123 case net::MDnsListener::RECORD_ADDED:
124 AddService(rdata->ptrdomain());
125 break;
126 case net::MDnsListener::RECORD_CHANGED:
127 NOTREACHED();
128 break;
129 case net::MDnsListener::RECORD_REMOVED:
130 RemoveService(rdata->ptrdomain());
131 break;
132 }
133 } else {
134 DCHECK(record->type() == net::dns_protocol::kTypeSRV ||
135 record->type() == net::dns_protocol::kTypeTXT);
136 DCHECK(services_.find(record->name()) != services_.end());
gene 2013/06/18 23:46:53 PTR may easily arrive after SRV, will it cause pro
Noam Samuel 2013/06/19 18:46:22 The service won't be notified as "updated" if that
gene 2013/06/19 22:48:03 Could you please add a comment here, explaining th
137
138 delegate_->OnServiceChanged(record->name());
139 }
140 }
141
142 void ServiceWatcherImpl::OnCachePurged() {
143 // Not yet implemented.
144 }
145
146 void ServiceWatcherImpl::OnTransactionResponse(
147 scoped_ptr<net::MDnsTransaction>* transaction,
148 net::MDnsTransaction::Result result,
149 const net::RecordParsed* record) {
150 DCHECK(started_);
151 if (result == net::MDnsTransaction::RESULT_RECORD) {
152 const net::PtrRecordRdata* rdata = record->rdata<net::PtrRecordRdata>();
153 DCHECK(rdata);
gene 2013/06/18 23:46:53 Do you want to have DCHECK() here, or if() and pol
Noam Samuel 2013/06/19 18:46:22 Since the only transaction we have is a PTR record
154 AddService(rdata->ptrdomain());
155 } else if (result == net::MDnsTransaction::RESULT_DONE) {
156 transaction->reset();
157 }
158
159 // Do nothing for NSEC records. It is an error for hosts to broadcast an NSEC
160 // record for PTR records on any name.
161 }
162
163 ServiceWatcherImpl::ServiceListeners::ServiceListeners(
164 const std::string& service_name,
165 ServiceWatcherImpl* watcher) {
166 srv_listener_ = net::MDnsClient::GetInstance()->CreateListener(
167 net::dns_protocol::kTypeSRV, service_name, watcher);
168 txt_listener_ = net::MDnsClient::GetInstance()->CreateListener(
169 net::dns_protocol::kTypeTXT, service_name, watcher);
170 }
171
172 ServiceWatcherImpl::ServiceListeners::~ServiceListeners() {
173 }
174
175 bool ServiceWatcherImpl::ServiceListeners::Start() {
176 if (!srv_listener_->Start())
177 return false;
178 return txt_listener_->Start();
gene 2013/06/18 23:46:53 Is there are problems if src listener will start a
Noam Samuel 2013/06/19 18:46:22 It seems unlikely that would happen. If it does, t
179 }
180
181 void ServiceWatcherImpl::AddService(const std::string& service) {
182 DCHECK(started_);
183 std::pair<std::map<std::string, ServiceListeners*>::iterator, bool> found =
gene 2013/06/18 23:46:53 consider typedef std::map<std::string, ServiceList
Noam Samuel 2013/06/19 18:46:22 Done.
184 services_.insert(make_pair(service,
gene 2013/06/18 23:46:53 services_.find() can make this code a bit more rea
Noam Samuel 2013/06/19 18:46:22 Services_.insert() will do one lookup instead of t
185 static_cast<ServiceListeners*>(NULL)));
186 if (found.second) { // Newly inserted.
187 found.first->second = new ServiceListeners(service, this);
gene 2013/06/18 23:46:53 Do you want to keep found.first->second as a scope
Noam Samuel 2013/06/19 18:46:22 scoped_ptr doesn't play nice with STL containers.
188 bool success = found.first->second->Start();
189 DCHECK(success);
190 delegate_->OnServiceStatusChanged(true, service);
191 }
192 }
193
194 void ServiceWatcherImpl::RemoveService(const std::string& service) {
195 DCHECK(started_);
196 std::map<std::string, ServiceListeners*>::iterator found =
197 services_.find(service);
198 if (found != services_.end()) {
199 delete found->second; // Delete the listeners
200 services_.erase(found); // Erase the service
201 delegate_->OnServiceStatusChanged(false, service);
202 }
203 }
204
205 void ServiceWatcherImpl::OnNsecRecord(const std::string& name,
206 unsigned rrtype) {
207 // Do nothing. It is an error for hosts to broadcast an NSEC record for PTR
208 // on any name.
209 }
210
211 ServiceResolverImpl::ServiceResolverImpl(
212 const std::string& service_name,
213 const ResolveCompleteCallback& callback)
214 : service_name_(service_name), callback_(callback),
215 is_resolving_(false), has_resolved_(false), metadata_resolved_(false),
216 address_resolved_(false), current_service_index_(0) {
217 }
218
219 bool ServiceResolverImpl::StartResolving() {
220 if (!CreateTxtTransaction())
221 return false;
222 if (!CreateSrvTransaction())
223 return false;
224 is_resolving_ = true;
225 address_resolved_ = false;
226 metadata_resolved_ = false;
227 return true;
228 }
229
230 bool ServiceResolverImpl::IsResolving() const {
231 return is_resolving_;
232 }
233
234 bool ServiceResolverImpl::HasResolved() const {
235 return has_resolved_;
236 }
237
238 ServiceResolverImpl::~ServiceResolverImpl() {
239 }
240
241 bool ServiceResolverImpl::CreateTxtTransaction() {
242 txt_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
243 net::dns_protocol::kTypeTXT, service_name_,
244 net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE |
245 net::MDnsTransaction::QUERY_NETWORK,
246 base::Bind(&ServiceResolverImpl::TxtRecordTransactionResponse,
247 AsWeakPtr()));
248 return txt_transaction_->Start();
249 }
250
251 // TODO(noamsml): quick-resolve for AAAA records. Since A records tend to be in
252 void ServiceResolverImpl::CreateATransaction() {
253 a_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
254 net::dns_protocol::kTypeA,
255 staging_service()->address.host(),
256 net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE,
257 base::Bind(&ServiceResolverImpl::ARecordTransactionResponse,
258 AsWeakPtr()));
259 a_transaction_->Start();
260 }
261
262 bool ServiceResolverImpl::CreateSrvTransaction() {
263 srv_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
264 net::dns_protocol::kTypeSRV, service_name_,
265 net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE |
266 net::MDnsTransaction::QUERY_NETWORK,
267 base::Bind(&ServiceResolverImpl::SrvRecordTransactionResponse,
268 AsWeakPtr()));
269 return srv_transaction_->Start();
270 }
271
272 std::string ServiceResolverImpl::GetHumanReadableName() const {
273 // TODO(noamsml): Once we have escaping working, get this to
274 // parse escaped domains.
275 size_t first_period = service_name_.find_first_of('.');
276 return service_name_.substr(0, first_period);
277 }
278
279 std::string ServiceResolverImpl::GetType() const {
280 // TODO(noamsml): Once we have escaping working, get this to
281 // parse escaped domains.
282 size_t first_period = service_name_.find_first_of('.');
283 if (first_period == std::string::npos)
284 return "";
285 return service_name_.substr(first_period+1);
286 }
287
288 std::string ServiceResolverImpl::GetName() const {
289 return service_name_;
290 }
291
292 void ServiceResolverImpl::SrvRecordTransactionResponse(
293 net::MDnsTransaction::Result status, const net::RecordParsed* record) {
294 srv_transaction_.reset();
295 if (status == net::MDnsTransaction::RESULT_RECORD) {
296 DCHECK(record);
297 staging_service()->address = RecordToAddress(record);
298 staging_service()->last_seen = record->time_created();
299 CreateATransaction();
300 } else {
301 txt_transaction_.reset();
302 srv_transaction_.reset();
303 a_transaction_.reset();
304 is_resolving_ = false;
305 callback_.Run(MDnsStatusToRequestStatus(status));
306 }
307 }
308
309 void ServiceResolverImpl::TxtRecordTransactionResponse(
310 net::MDnsTransaction::Result status, const net::RecordParsed* record) {
311 txt_transaction_.reset();
312 if (status == net::MDnsTransaction::RESULT_RECORD) {
313 DCHECK(record);
314 staging_service()->metadata = RecordToMetadata(record);
315 } else {
316 staging_service()->metadata = std::vector<std::string>();
317 }
318
319 metadata_resolved_ = true;
320 AlertCallbackIfReady();
321 }
322
323 void ServiceResolverImpl::ARecordTransactionResponse(
324 net::MDnsTransaction::Result status, const net::RecordParsed* record) {
325 a_transaction_.reset();
326
327 if (status == net::MDnsTransaction::RESULT_RECORD) {
328 DCHECK(record);
329 staging_service()->ip_address = RecordToIPAddress(record);
330 } else {
331 staging_service()->ip_address = net::IPAddressNumber();
332 }
333
334 address_resolved_ = true;
335 AlertCallbackIfReady();
336 }
337
338 void ServiceResolverImpl::AlertCallbackIfReady() {
339 if (metadata_resolved_ && address_resolved_) {
340 txt_transaction_.reset();
341 srv_transaction_.reset();
342 a_transaction_.reset();
343 has_resolved_ = true;
344 is_resolving_ = false;
345 current_service_index_ = (current_service_index_ + 1) % 2;
346 callback_.Run(STATUS_SUCCESS);
347 }
348 }
349
350 const Service& ServiceResolverImpl::GetService() const {
351 return services_[current_service_index_];
352 }
353
354 ServiceResolver::RequestStatus ServiceResolverImpl::MDnsStatusToRequestStatus(
355 net::MDnsTransaction::Result status) const {
356 switch (status) {
357 case net::MDnsTransaction::RESULT_RECORD:
358 return ServiceResolver::STATUS_SUCCESS;
359 case net::MDnsTransaction::RESULT_NO_RESULTS:
360 return ServiceResolver::STATUS_TIMEOUT;
361 case net::MDnsTransaction::RESULT_NSEC:
362 return ServiceResolver::STATUS_KNOWN_NONEXISTENT;
363 case net::MDnsTransaction::RESULT_DONE:
364 NOTREACHED();
365 return ServiceResolver::STATUS_TIMEOUT;
366 }
367 }
368
369 const std::vector<std::string>& ServiceResolverImpl::RecordToMetadata(
370 const net::RecordParsed* record) const {
371 DCHECK(record->type() == net::dns_protocol::kTypeTXT);
372 const net::TxtRecordRdata* txt_rdata = record->rdata<net::TxtRecordRdata>();
373 DCHECK(txt_rdata);
374 return txt_rdata->texts();
375 }
376
377 net::HostPortPair ServiceResolverImpl::RecordToAddress(
378 const net::RecordParsed* record) const {
379 DCHECK(record->type() == net::dns_protocol::kTypeSRV);
380 const net::SrvRecordRdata* srv_rdata = record->rdata<net::SrvRecordRdata>();
381 DCHECK(srv_rdata);
382 return net::HostPortPair(srv_rdata->target(), srv_rdata->port());
383 }
384
385 const net::IPAddressNumber& ServiceResolverImpl::RecordToIPAddress(
386 const net::RecordParsed* record) const {
387 DCHECK(record->type() == net::dns_protocol::kTypeA);
388 const net::ARecordRdata* a_rdata = record->rdata<net::ARecordRdata>();
389 DCHECK(a_rdata);
390 return a_rdata->address();
391 }
392
393 } // namespace local_discovery
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698