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

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 }
61
62 void ServiceWatcherImpl::GetAvailableServices(
63 std::vector<std::string>* services) const {
64 DCHECK(started_);
65 DCHECK(services);
66 services->reserve(services_.size());
67 for (ServiceListenersMap::const_iterator i = services_.begin();
68 i != services_.end(); i++) {
69 services->push_back(i->first);
70 }
71 }
72
73 void ServiceWatcherImpl::DiscoverNewServices(bool force_update) {
74 DCHECK(started_);
75 if (force_update)
76 services_.clear();
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());
137
138 delegate_->OnServiceUpdated(UPDATE_CHANGED, 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);
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();
179 }
180
181 void ServiceWatcherImpl::AddService(const std::string& service) {
182 DCHECK(started_);
183 std::pair<ServiceListenersMap::iterator, bool> found = services_.insert(
184 make_pair(service, static_cast<ServiceListeners*>(NULL)));
185 if (found.second) { // Newly inserted.
186 found.first->second = linked_ptr<ServiceListeners>(
187 new ServiceListeners(service, this));
188 bool success = found.first->second->Start();
189 DCHECK(success);
190 delegate_->OnServiceUpdated(UPDATE_ADDED, service);
191 }
192 }
193
194 void ServiceWatcherImpl::RemoveService(const std::string& service) {
195 DCHECK(started_);
196 ServiceListenersMap::iterator found = services_.find(service);
197 if (found != services_.end()) {
198 services_.erase(found);
199 delegate_->OnServiceUpdated(UPDATE_REMOVED, service);
200 }
201 }
202
203 void ServiceWatcherImpl::OnNsecRecord(const std::string& name,
204 unsigned rrtype) {
205 // Do nothing. It is an error for hosts to broadcast an NSEC record for PTR
206 // on any name.
207 }
208
209 ServiceResolverImpl::ServiceResolverImpl(
210 const std::string& service_name,
211 const ResolveCompleteCallback& callback)
212 : service_name_(service_name), callback_(callback),
213 is_resolving_(false), has_resolved_(false), metadata_resolved_(false),
214 address_resolved_(false), service_staging_(new ServiceDescription),
215 service_final_(new ServiceDescription) {
216 service_staging_->service_name = service_name_;
217 service_final_->service_name = service_name_;
218 }
219
220 bool ServiceResolverImpl::StartResolving() {
221 is_resolving_ = true;
222 address_resolved_ = false;
223 metadata_resolved_ = false;
224
225 if (!CreateTxtTransaction())
226 return false;
227 if (!CreateSrvTransaction())
228 return false;
229 return true;
230 }
231
232 bool ServiceResolverImpl::IsResolving() const {
233 return is_resolving_;
234 }
235
236 bool ServiceResolverImpl::HasResolved() const {
237 return has_resolved_;
238 }
239
240 ServiceResolverImpl::~ServiceResolverImpl() {
241 }
242
243 bool ServiceResolverImpl::CreateTxtTransaction() {
244 txt_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
245 net::dns_protocol::kTypeTXT, service_name_,
246 net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE |
247 net::MDnsTransaction::QUERY_NETWORK,
248 base::Bind(&ServiceResolverImpl::TxtRecordTransactionResponse,
249 AsWeakPtr()));
250 return txt_transaction_->Start();
251 }
252
253 // TODO(noamsml): quick-resolve for AAAA records. Since A records tend to be in
254 void ServiceResolverImpl::CreateATransaction() {
255 a_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
256 net::dns_protocol::kTypeA,
257 service_staging_.get()->address.host(),
258 net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE,
259 base::Bind(&ServiceResolverImpl::ARecordTransactionResponse,
260 AsWeakPtr()));
261 a_transaction_->Start();
262 }
263
264 bool ServiceResolverImpl::CreateSrvTransaction() {
265 srv_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
266 net::dns_protocol::kTypeSRV, service_name_,
267 net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE |
268 net::MDnsTransaction::QUERY_NETWORK,
269 base::Bind(&ServiceResolverImpl::SrvRecordTransactionResponse,
270 AsWeakPtr()));
271 return srv_transaction_->Start();
272 }
273
274 std::string ServiceResolverImpl::GetName() const {
275 return service_name_;
276 }
277
278 void ServiceResolverImpl::SrvRecordTransactionResponse(
279 net::MDnsTransaction::Result status, const net::RecordParsed* record) {
280 srv_transaction_.reset();
281 if (status == net::MDnsTransaction::RESULT_RECORD) {
282 DCHECK(record);
283 service_staging_.get()->address = RecordToAddress(record);
284 service_staging_.get()->last_seen = record->time_created();
285 CreateATransaction();
286 } else {
287 ServiceNotFound(MDnsStatusToRequestStatus(status));
288 }
289 }
290
291 void ServiceResolverImpl::TxtRecordTransactionResponse(
292 net::MDnsTransaction::Result status, const net::RecordParsed* record) {
293 txt_transaction_.reset();
294 if (status == net::MDnsTransaction::RESULT_RECORD) {
295 DCHECK(record);
296 service_staging_.get()->metadata = RecordToMetadata(record);
297 } else {
298 service_staging_.get()->metadata = std::vector<std::string>();
299 }
300
301 metadata_resolved_ = true;
302 AlertCallbackIfReady();
303 }
304
305 void ServiceResolverImpl::ARecordTransactionResponse(
306 net::MDnsTransaction::Result status, const net::RecordParsed* record) {
307 a_transaction_.reset();
308
309 if (status == net::MDnsTransaction::RESULT_RECORD) {
310 DCHECK(record);
311 service_staging_.get()->ip_address = RecordToIPAddress(record);
312 } else {
313 service_staging_.get()->ip_address = net::IPAddressNumber();
314 }
315
316 address_resolved_ = true;
317 AlertCallbackIfReady();
318 }
319
320 void ServiceResolverImpl::AlertCallbackIfReady() {
321 if (metadata_resolved_ && address_resolved_) {
322 txt_transaction_.reset();
323 srv_transaction_.reset();
324 a_transaction_.reset();
325 has_resolved_ = true;
326 is_resolving_ = false;
327 service_final_.swap(service_staging_);
328 callback_.Run(STATUS_SUCCESS, GetServiceDescription());
329 }
330 }
331
332 void ServiceResolverImpl::ServiceNotFound(
333 ServiceResolver::RequestStatus status) {
334 txt_transaction_.reset();
335 srv_transaction_.reset();
336 a_transaction_.reset();
337 is_resolving_ = false;
338
339 callback_.Run(status, GetServiceDescription());
340 }
341
342 const ServiceDescription& ServiceResolverImpl::GetServiceDescription() const {
343 return *service_final_.get();
344 }
345
346 ServiceResolver::RequestStatus ServiceResolverImpl::MDnsStatusToRequestStatus(
347 net::MDnsTransaction::Result status) const {
348 switch (status) {
349 case net::MDnsTransaction::RESULT_RECORD:
350 return ServiceResolver::STATUS_SUCCESS;
351 case net::MDnsTransaction::RESULT_NO_RESULTS:
352 return ServiceResolver::STATUS_REQUEST_TIMEOUT;
353 case net::MDnsTransaction::RESULT_NSEC:
354 return ServiceResolver::STATUS_KNOWN_NONEXISTENT;
355 case net::MDnsTransaction::RESULT_DONE: // Pass through.
356 default:
357 NOTREACHED();
358 return ServiceResolver::STATUS_REQUEST_TIMEOUT;
359 }
360 }
361
362 const std::vector<std::string>& ServiceResolverImpl::RecordToMetadata(
363 const net::RecordParsed* record) const {
364 DCHECK(record->type() == net::dns_protocol::kTypeTXT);
365 const net::TxtRecordRdata* txt_rdata = record->rdata<net::TxtRecordRdata>();
366 DCHECK(txt_rdata);
367 return txt_rdata->texts();
368 }
369
370 net::HostPortPair ServiceResolverImpl::RecordToAddress(
371 const net::RecordParsed* record) const {
372 DCHECK(record->type() == net::dns_protocol::kTypeSRV);
373 const net::SrvRecordRdata* srv_rdata = record->rdata<net::SrvRecordRdata>();
374 DCHECK(srv_rdata);
375 return net::HostPortPair(srv_rdata->target(), srv_rdata->port());
376 }
377
378 const net::IPAddressNumber& ServiceResolverImpl::RecordToIPAddress(
379 const net::RecordParsed* record) const {
380 DCHECK(record->type() == net::dns_protocol::kTypeA);
381 const net::ARecordRdata* a_rdata = record->rdata<net::ARecordRdata>();
382 DCHECK(a_rdata);
383 return a_rdata->address();
384 }
385
386 } // namespace local_discovery
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698