Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |