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

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_->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);
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_->OnServiceStatusChanged(true, 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_->OnServiceStatusChanged(false, 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), current_service_index_(0) {
215 services_[0].service_name = service_name_;
216 services_[1].service_name = service_name_;
217 }
218
219 bool ServiceResolverImpl::StartResolving() {
220 if (!CreateTxtTransaction())
gene 2013/06/19 22:48:03 Can result of the transaction arrive within this c
Noam Samuel 2013/06/19 23:53:29 Done.
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::GetName() const {
273 return service_name_;
274 }
275
276 void ServiceResolverImpl::SrvRecordTransactionResponse(
277 net::MDnsTransaction::Result status, const net::RecordParsed* record) {
278 srv_transaction_.reset();
279 if (status == net::MDnsTransaction::RESULT_RECORD) {
280 DCHECK(record);
281 staging_service()->address = RecordToAddress(record);
282 staging_service()->last_seen = record->time_created();
283 CreateATransaction();
284 } else {
285 txt_transaction_.reset();
286 srv_transaction_.reset();
287 a_transaction_.reset();
288 is_resolving_ = false;
gene 2013/06/19 22:48:03 Do you need to reset address_resolved_ and metadat
Noam Samuel 2013/06/19 23:53:29 address_resolved_ and metadata_resolved_ are reset
289 callback_.Run(MDnsStatusToRequestStatus(status), GetServiceDescription());
290 }
291 }
292
293 void ServiceResolverImpl::TxtRecordTransactionResponse(
294 net::MDnsTransaction::Result status, const net::RecordParsed* record) {
295 txt_transaction_.reset();
296 if (status == net::MDnsTransaction::RESULT_RECORD) {
297 DCHECK(record);
298 staging_service()->metadata = RecordToMetadata(record);
299 } else {
300 staging_service()->metadata = std::vector<std::string>();
301 }
302
303 metadata_resolved_ = true;
304 AlertCallbackIfReady();
305 }
306
307 void ServiceResolverImpl::ARecordTransactionResponse(
308 net::MDnsTransaction::Result status, const net::RecordParsed* record) {
309 a_transaction_.reset();
310
311 if (status == net::MDnsTransaction::RESULT_RECORD) {
312 DCHECK(record);
313 staging_service()->ip_address = RecordToIPAddress(record);
314 } else {
315 staging_service()->ip_address = net::IPAddressNumber();
316 }
317
318 address_resolved_ = true;
319 AlertCallbackIfReady();
320 }
321
322 void ServiceResolverImpl::AlertCallbackIfReady() {
323 if (metadata_resolved_ && address_resolved_) {
324 txt_transaction_.reset();
325 srv_transaction_.reset();
326 a_transaction_.reset();
327 has_resolved_ = true;
328 is_resolving_ = false;
329 current_service_index_ = (current_service_index_ + 1) % 2;
330 callback_.Run(STATUS_SUCCESS, GetServiceDescription());
331 }
332 }
333
334 const ServiceDescription& ServiceResolverImpl::GetServiceDescription() const {
335 return services_[current_service_index_];
336 }
337
338 ServiceResolver::RequestStatus ServiceResolverImpl::MDnsStatusToRequestStatus(
339 net::MDnsTransaction::Result status) const {
340 switch (status) {
341 case net::MDnsTransaction::RESULT_RECORD:
342 return ServiceResolver::STATUS_SUCCESS;
343 case net::MDnsTransaction::RESULT_NO_RESULTS:
344 return ServiceResolver::STATUS_TIMEOUT;
345 case net::MDnsTransaction::RESULT_NSEC:
346 return ServiceResolver::STATUS_KNOWN_NONEXISTENT;
347 case net::MDnsTransaction::RESULT_DONE:
348 NOTREACHED();
Vitaly Buka (NO REVIEWS) 2013/06/19 22:51:24 no default return value
Noam Samuel 2013/06/19 23:53:29 Done.
349 return ServiceResolver::STATUS_TIMEOUT;
350 }
351 }
352
353 const std::vector<std::string>& ServiceResolverImpl::RecordToMetadata(
354 const net::RecordParsed* record) const {
355 DCHECK(record->type() == net::dns_protocol::kTypeTXT);
356 const net::TxtRecordRdata* txt_rdata = record->rdata<net::TxtRecordRdata>();
357 DCHECK(txt_rdata);
358 return txt_rdata->texts();
359 }
360
361 net::HostPortPair ServiceResolverImpl::RecordToAddress(
362 const net::RecordParsed* record) const {
363 DCHECK(record->type() == net::dns_protocol::kTypeSRV);
364 const net::SrvRecordRdata* srv_rdata = record->rdata<net::SrvRecordRdata>();
365 DCHECK(srv_rdata);
366 return net::HostPortPair(srv_rdata->target(), srv_rdata->port());
367 }
368
369 const net::IPAddressNumber& ServiceResolverImpl::RecordToIPAddress(
370 const net::RecordParsed* record) const {
371 DCHECK(record->type() == net::dns_protocol::kTypeA);
372 const net::ARecordRdata* a_rdata = record->rdata<net::ARecordRdata>();
373 DCHECK(a_rdata);
374 return a_rdata->address();
375 }
376
377 } // namespace local_discovery
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698