| 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; |
| 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; |
| 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; |
| 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())); |
| 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 ""; |
| 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(); |
| 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; |
| 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_); |
| 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 = |
| 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 |