| Index: chrome/browser/local_discovery/service_discovery_client_impl.cc
|
| diff --git a/chrome/browser/local_discovery/service_discovery_client_impl.cc b/chrome/browser/local_discovery/service_discovery_client_impl.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..22e4439cb700b15e40010b31a12c806bcfbdcf78
|
| --- /dev/null
|
| +++ b/chrome/browser/local_discovery/service_discovery_client_impl.cc
|
| @@ -0,0 +1,433 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include <utility>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/message_loop_proxy.h"
|
| +#include "chrome/browser/local_discovery/service_discovery_client_impl.h"
|
| +#include "net/dns/dns_protocol.h"
|
| +#include "net/dns/record_rdata.h"
|
| +
|
| +namespace local_discovery {
|
| +
|
| +ServiceDiscoveryClientImpl::ServiceDiscoveryClientImpl() {
|
| +}
|
| +
|
| +ServiceDiscoveryClientImpl::~ServiceDiscoveryClientImpl() {
|
| +}
|
| +
|
| +scoped_ptr<ServiceTypeWatcher>
|
| +ServiceDiscoveryClientImpl::CreateServiceTypeWatcher(
|
| + const std::string& service_type,
|
| + bool active,
|
| + bool alert_existing_services,
|
| + ServiceTypeWatcher::Delegate* delegate) {
|
| + return scoped_ptr<ServiceTypeWatcher>(new ServiceTypeWatcherImpl(
|
| + service_type, active, alert_existing_services, delegate));
|
| +}
|
| +
|
| +scoped_ptr<ServiceReader> ServiceDiscoveryClientImpl::CreateServiceReader(
|
| + const std::string& service_name,
|
| + ServiceReader::Delegate* delegate) {
|
| + return scoped_ptr<ServiceReader>(new ServiceReaderImpl(
|
| + service_name, delegate));
|
| +}
|
| +
|
| +ServiceTypeWatcherImpl::ServiceTypeWatcherImpl(
|
| + const std::string& service_type,
|
| + bool active,
|
| + bool alert_existing_services,
|
| + ServiceTypeWatcher::Delegate* delegate)
|
| + : service_type_(service_type), active_(active),
|
| + alert_existing_services_(alert_existing_services), delegate_(delegate),
|
| + started_(false) {
|
| +}
|
| +
|
| +bool ServiceTypeWatcherImpl::Start() {
|
| + DCHECK(!started_);
|
| + listener_ = net::MDnsClient::GetInstance()->CreateListener(
|
| + net::dns_protocol::kTypePTR, service_type_, this);
|
| + if (!listener_->Start()) return false;
|
| + if (!CreateTransaction(active_, alert_existing_services_, false))
|
| + return false;
|
| +
|
| + started_ = true;
|
| + return true;
|
| +}
|
| +
|
| +ServiceTypeWatcherImpl::~ServiceTypeWatcherImpl() {
|
| +}
|
| +
|
| +void ServiceTypeWatcherImpl::GetAvailableServices(
|
| + std::vector<std::string>* services) const {
|
| + DCHECK(started_);
|
| + DCHECK(services);
|
| + services->insert(services->begin(), services_.begin(), services_.end());
|
| +}
|
| +
|
| +void ServiceTypeWatcherImpl::DiscoverNewServices() {
|
| + DCHECK(started_);
|
| + CreateTransaction(true, false, false);
|
| +}
|
| +
|
| +void ServiceTypeWatcherImpl::ForceUpdateServices() {
|
| + DCHECK(started_);
|
| + CreateTransaction(true, false, true);
|
| +}
|
| +
|
| +bool ServiceTypeWatcherImpl::CreateTransaction(
|
| + bool active, bool alert_existing_services, bool force_refresh) {
|
| + int transaction_flags = 0;
|
| + if (active) transaction_flags |= net::MDnsTransaction::QUERY_NETWORK;
|
| +
|
| + if (alert_existing_services) {
|
| + transaction_flags |= net::MDnsTransaction::QUERY_CACHE;
|
| + }
|
| +
|
| + // TODO(noamsml): Add flag for force_refresh when support.
|
| +
|
| + if (transaction_flags) {
|
| + transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
|
| + net::dns_protocol::kTypePTR, service_type_, transaction_flags,
|
| + base::Bind(&ServiceTypeWatcherImpl::OnTransactionResponse,
|
| + base::Unretained(this)));
|
| + return transaction_->Start();
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +std::string ServiceTypeWatcherImpl::GetServiceType() const {
|
| + return listener_->GetName();
|
| +}
|
| +
|
| +bool ServiceTypeWatcherImpl::IsActive() const {
|
| + return active_;
|
| +}
|
| +
|
| +void ServiceTypeWatcherImpl::OnRecordUpdate(
|
| + net::MDnsListener::UpdateType update,
|
| + const net::RecordParsed* record) {
|
| + DCHECK(started_);
|
| + DCHECK(record->name() == GetServiceType());
|
| + DCHECK(record->type() == net::dns_protocol::kTypePTR);
|
| +
|
| + const net::PtrRecordRdata* rdata = record->rdata<net::PtrRecordRdata>();
|
| +
|
| + switch (update) {
|
| + case net::MDnsListener::RECORD_ADDED:
|
| + AddService(rdata->ptrdomain());
|
| + break;
|
| + case net::MDnsListener::RECORD_CHANGED:
|
| + NOTREACHED();
|
| + break;
|
| + case net::MDnsListener::RECORD_REMOVED:
|
| + RemoveService(rdata->ptrdomain());
|
| + break;
|
| + }
|
| +}
|
| +
|
| +void ServiceTypeWatcherImpl::OnCachePurged() {
|
| + // Not yet implemented.
|
| +}
|
| +
|
| +void ServiceTypeWatcherImpl::OnTransactionResponse(
|
| + net::MDnsTransaction::Result result,
|
| + const net::RecordParsed* record) {
|
| + DCHECK(started_);
|
| + if (result == net::MDnsTransaction::RESULT_RECORD) {
|
| + const net::PtrRecordRdata* rdata = record->rdata<net::PtrRecordRdata>();
|
| + DCHECK(rdata);
|
| + AddService(rdata->ptrdomain());
|
| + } else if (result == net::MDnsTransaction::RESULT_DONE) {
|
| + transaction_.reset();
|
| + }
|
| +
|
| + // Do nothing for NSEC records. It is an error for hosts to broadcast an NSEC
|
| + // record for PTR records on any name.
|
| +}
|
| +
|
| +void ServiceTypeWatcherImpl::AddService(const std::string& service) {
|
| + DCHECK(started_);
|
| + std::pair<std::set<std::string>::iterator, bool> found =
|
| + services_.insert(service);
|
| + if (found.second) { // Newly inserted.
|
| + delegate_->OnServiceStatusChanged(true, service);
|
| + }
|
| +}
|
| +
|
| +void ServiceTypeWatcherImpl::RemoveService(const std::string& service) {
|
| + DCHECK(started_);
|
| + if (services_.erase(service)) {
|
| + delegate_->OnServiceStatusChanged(false, service);
|
| + }
|
| +}
|
| +
|
| +void ServiceTypeWatcherImpl::OnNsecRecord(const std::string& name,
|
| + unsigned rrtype) {
|
| + // Do nothing. It is an error for hosts to broadcast an NSEC record for PTR
|
| + // on any name.
|
| +}
|
| +
|
| +ServiceReaderImpl::ServiceReaderImpl(
|
| + const std::string& service_name,
|
| + ServiceReader::Delegate* delegate)
|
| + : service_name_(service_name), delegate_(delegate),
|
| + has_address_(false), has_metadata_(false), started_(false) {
|
| + srv_listener_ = net::MDnsClient::GetInstance()->CreateListener(
|
| + net::dns_protocol::kTypeSRV, service_name, this);
|
| + txt_listener_ = net::MDnsClient::GetInstance()->CreateListener(
|
| + net::dns_protocol::kTypeTXT, service_name, this);
|
| +}
|
| +
|
| +bool ServiceReaderImpl::Start() {
|
| + if (!srv_listener_->Start()) return false;
|
| + if (!txt_listener_->Start()) return false;
|
| +
|
| + // Update initial values
|
| + if (!CreateTxtTransaction()) return false;
|
| + if (!CreateSrvTransaction()) return false;
|
| + started_ = true;
|
| + return true;
|
| +}
|
| +
|
| +ServiceReaderImpl::~ServiceReaderImpl() {
|
| +}
|
| +
|
| +bool ServiceReaderImpl::GetMetadata(
|
| + std::vector<std::string>* metadata) const {
|
| + DCHECK(started_);
|
| + if (has_metadata_) {
|
| + *metadata = metadata_;
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +void ServiceReaderImpl::ReadMetadata(const MetadataCallback& callback,
|
| + bool force_refresh) {
|
| + DCHECK(started_);
|
| + metadata_callbacks_.push_back(callback);
|
| + if (!txt_transaction_.get()) {
|
| + CreateTxtTransaction();
|
| + }
|
| +}
|
| +
|
| +bool ServiceReaderImpl::CreateTxtTransaction() {
|
| + txt_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
|
| + net::dns_protocol::kTypeTXT, service_name_,
|
| + net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE |
|
| + net::MDnsTransaction::QUERY_NETWORK,
|
| + base::Bind(&ServiceReaderImpl::TxtRecordTransactionResponse,
|
| + AsWeakPtr()));
|
| + return txt_transaction_->Start();
|
| +}
|
| +
|
| +bool ServiceReaderImpl::GetAddress(net::HostPortPair* address) const {
|
| + DCHECK(started_);
|
| + if (has_address_) {
|
| + *address = address_;
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +void ServiceReaderImpl::ReadAddress(const AddressCallback& callback,
|
| + bool force_refresh) {
|
| + DCHECK(started_);
|
| + address_callbacks_.push_back(callback);
|
| + if (!srv_transaction_.get()) {
|
| + CreateSrvTransaction();
|
| + }
|
| +}
|
| +
|
| +bool ServiceReaderImpl::CreateSrvTransaction() {
|
| + srv_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
|
| + net::dns_protocol::kTypeSRV, service_name_,
|
| + net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE |
|
| + net::MDnsTransaction::QUERY_NETWORK,
|
| + base::Bind(&ServiceReaderImpl::SrvRecordTransactionResponse,
|
| + AsWeakPtr()));
|
| + return srv_transaction_->Start();
|
| +}
|
| +
|
| +std::string ServiceReaderImpl::GetHumanReadableName() const {
|
| + // TODO(noamsml): Once we have escaping working, get this to
|
| + // parse escaped domains.
|
| + size_t first_period = service_name_.find_first_of('.');
|
| + return service_name_.substr(0, first_period);
|
| +}
|
| +
|
| +std::string ServiceReaderImpl::GetType() const {
|
| + // TODO(noamsml): Once we have escaping working, get this to
|
| + // parse escaped domains.
|
| + size_t first_period = service_name_.find_first_of('.');
|
| + if (first_period == std::string::npos) return "";
|
| + return service_name_.substr(first_period+1);
|
| +}
|
| +
|
| +std::string ServiceReaderImpl::GetName() const {
|
| + return service_name_;
|
| +}
|
| +
|
| +bool ServiceReaderImpl::IsAvailable() const {
|
| + DCHECK(started_);
|
| + // TODO(noamsml): This should ideally be based on the PTR record.
|
| + return has_address_;
|
| +}
|
| +
|
| +void ServiceReaderImpl::ReadLastSeen(
|
| + const ServiceReader::LastSeenCallback& callback) {
|
| + DCHECK(started_);
|
| + // HACK(noamsml): Because cache-based MDns transactions are currently
|
| + // synchronous, there can only be one last seen callback at a time, so
|
| + // no need to keep a callback list.
|
| +
|
| + last_seen_transaction_ = net::MDnsClient::GetInstance()->CreateTransaction(
|
| + net::dns_protocol::kTypeSRV, service_name_,
|
| + net::MDnsTransaction::SINGLE_RESULT | net::MDnsTransaction::QUERY_CACHE,
|
| + base::Bind(&ServiceReaderImpl::LastSeenTransactionResponse,
|
| + base::Unretained(this), callback));
|
| + last_seen_transaction_->Start();
|
| +}
|
| +
|
| +void ServiceReaderImpl::LastSeenTransactionResponse(
|
| + const ServiceReader::LastSeenCallback& callback,
|
| + net::MDnsTransaction::Result status,
|
| + const net::RecordParsed* record) {
|
| + last_seen_transaction_.reset();
|
| + DCHECK(started_);
|
| + if (status == net::MDnsTransaction::RESULT_RECORD) {
|
| + DCHECK(record);
|
| + callback.Run(record->time_created());
|
| + } else {
|
| + callback.Run(base::Time());
|
| + }
|
| +}
|
| +
|
| +void ServiceReaderImpl::OnRecordUpdate(net::MDnsListener::UpdateType update,
|
| + const net::RecordParsed* record) {
|
| + DCHECK(started_);
|
| + switch (record->type()) {
|
| + case net::dns_protocol::kTypeSRV: {
|
| + if (update == net::MDnsListener::RECORD_REMOVED) {
|
| + has_address_ = false;
|
| + address_ = net::HostPortPair();
|
| + } else {
|
| + has_address_ = true;
|
| + address_ = RecordToAddress(record);
|
| + }
|
| +
|
| + if (delegate_) {
|
| + delegate_->OnAddressChanged(service_name_,
|
| + address_);
|
| + }
|
| + break;
|
| + }
|
| + case net::dns_protocol::kTypeTXT: {
|
| + if (update == net::MDnsListener::RECORD_REMOVED) {
|
| + has_metadata_ = false;
|
| + metadata_ = std::vector<std::string>();
|
| + } else {
|
| + has_metadata_ = true;
|
| + metadata_ = RecordToMetadata(record);
|
| + }
|
| +
|
| + if (delegate_) {
|
| + delegate_->OnMetadataChanged(service_name_,
|
| + metadata_);
|
| + }
|
| + break;
|
| + }
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| +}
|
| +
|
| +void ServiceReaderImpl::OnCachePurged() {
|
| + // Not yet implemented.
|
| +}
|
| +
|
| +void ServiceReaderImpl::SrvRecordTransactionResponse(
|
| + net::MDnsTransaction::Result status, const net::RecordParsed* record) {
|
| + DCHECK(started_);
|
| + srv_transaction_.reset();
|
| +
|
| + if (record) {
|
| + address_ = RecordToAddress(record);
|
| + has_metadata_ = true;
|
| + }
|
| +
|
| + std::vector<ServiceReader::AddressCallback> address_callbacks;
|
| + address_callbacks.swap(address_callbacks_);
|
| +
|
| + ServiceReader::RequestStatus output_status = MDnsStatusToRequestStatus(
|
| + status);
|
| +
|
| + for (std::vector<ServiceReader::AddressCallback>::iterator i =
|
| + address_callbacks.begin(); i != address_callbacks.end(); ++i) {
|
| + i->Run(output_status, address_);
|
| + }
|
| +}
|
| +
|
| +void ServiceReaderImpl::TxtRecordTransactionResponse(
|
| + net::MDnsTransaction::Result status, const net::RecordParsed* record) {
|
| + DCHECK(started_);
|
| + txt_transaction_.reset();
|
| +
|
| + std::vector<ServiceReader::MetadataCallback> metadata_callbacks;
|
| + metadata_callbacks.swap(metadata_callbacks_);
|
| +
|
| + if (record) {
|
| + metadata_ = RecordToMetadata(record);
|
| + has_metadata_ = true;
|
| + }
|
| +
|
| + ServiceReader::RequestStatus output_status = MDnsStatusToRequestStatus(
|
| + status);
|
| +
|
| + for (std::vector<ServiceReader::MetadataCallback>::iterator i =
|
| + metadata_callbacks.begin(); i != metadata_callbacks.end(); ++i) {
|
| + i->Run(output_status, metadata_);
|
| + }
|
| +}
|
| +
|
| +void ServiceReaderImpl::OnNsecRecord(const std::string& name,
|
| + unsigned rrtype) {
|
| + // TODO(noamsml): Logic NSEC in listener case.
|
| +}
|
| +
|
| +ServiceReader::RequestStatus ServiceReaderImpl::MDnsStatusToRequestStatus(
|
| + net::MDnsTransaction::Result status) const {
|
| + switch (status) {
|
| + case net::MDnsTransaction::RESULT_RECORD:
|
| + return ServiceReader::STATUS_SUCCESS;
|
| + case net::MDnsTransaction::RESULT_NO_RESULTS:
|
| + return ServiceReader::STATUS_TIMEOUT;
|
| + case net::MDnsTransaction::RESULT_NSEC:
|
| + return ServiceReader::STATUS_KNOWN_NONEXISTENT;
|
| + case net::MDnsTransaction::RESULT_DONE:
|
| + NOTREACHED();
|
| + return ServiceReader::STATUS_TIMEOUT;
|
| + }
|
| +}
|
| +
|
| +const std::vector<std::string>& ServiceReaderImpl::RecordToMetadata(
|
| + const net::RecordParsed* record) const {
|
| + DCHECK(record->type() == net::dns_protocol::kTypeTXT);
|
| + const net::TxtRecordRdata* txt_rdata = record->rdata<net::TxtRecordRdata>();
|
| + DCHECK(txt_rdata);
|
| + return txt_rdata->texts();
|
| +}
|
| +
|
| +net::HostPortPair ServiceReaderImpl::RecordToAddress(
|
| + const net::RecordParsed* record) const {
|
| + DCHECK(record->type() == net::dns_protocol::kTypeSRV);
|
| + const net::SrvRecordRdata* srv_rdata = record->rdata<net::SrvRecordRdata>();
|
| + DCHECK(srv_rdata);
|
| + return net::HostPortPair(srv_rdata->target(), srv_rdata->port());
|
| +}
|
| +
|
| +} // namespace local_discovery
|
|
|