From ead61a438feb008581b82fddca34a18527c868d1 Mon Sep 17 00:00:00 2001 From: "maksymb@chromium.org" Date: Sat, 22 Jun 2013 16:49:06 +0000 Subject: Finished DNS-SD server. Finished Privet-specified DNS-SD server. Review URL: https://chromiumcodereview.appspot.com/16975004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@208064 0039d316-1c4b-4281-b951-d872f2087c98 --- cloud_print/gcp20/prototype/dns_sd_server.cc | 247 ++++++++++++++++++++++----- 1 file changed, 205 insertions(+), 42 deletions(-) (limited to 'cloud_print/gcp20/prototype/dns_sd_server.cc') diff --git a/cloud_print/gcp20/prototype/dns_sd_server.cc b/cloud_print/gcp20/prototype/dns_sd_server.cc index 0ca8b43..2d2716b 100644 --- a/cloud_print/gcp20/prototype/dns_sd_server.cc +++ b/cloud_print/gcp20/prototype/dns_sd_server.cc @@ -7,7 +7,15 @@ #include #include "base/basictypes.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/message_loop.h" +#include "base/strings/stringprintf.h" +#include "cloud_print/gcp20/prototype/dns_packet_parser.h" +#include "cloud_print/gcp20/prototype/dns_response_builder.h" #include "net/base/big_endian.h" +#include "net/base/dns_util.h" +#include "net/base/net_errors.h" #include "net/base/net_util.h" #include "net/dns/dns_protocol.h" @@ -16,53 +24,91 @@ namespace { const char* kDefaultIpAddressMulticast = "224.0.0.251"; const uint16 kDefaultPortMulticast = 5353; -// TODO(maksymb): Add possibility to set constants via command line arguments -const uint32 kDefaultTTL = 60*60; // in seconds +const double kTimeToNextAnnouncement = 0.8; // relatively to TTL +const int kDnsBufSize = 65537; + +const uint16 kSrvPriority = 0; +const uint16 kSrvWeight = 0; + +void DoNothingAfterSendToSocket(int /*val*/) { + NOTREACHED(); + // TODO(maksymb): Delete this function once empty callback for SendTo() method + // will be allowed. +} } // namespace -DnsSdServer::DnsSdServer() : is_online_(false) { - // Do nothing +DnsSdServer::DnsSdServer() + : recv_buf_(new net::IOBufferWithSize(kDnsBufSize)), + full_ttl_(0) { } DnsSdServer::~DnsSdServer() { Shutdown(); } -bool DnsSdServer::Start() { - if (is_online_) +bool DnsSdServer::Start(const ServiceParameters& serv_params, uint32 full_ttl, + const std::vector& metadata) { + if (IsOnline()) return true; if (!CreateSocket()) return false; + // Initializing server with parameters from arguments. + serv_params_ = serv_params; + full_ttl_ = full_ttl; + metadata_ = metadata; + LOG(INFO) << "DNS server started"; + LOG(WARNING) << "DNS server does not support probing"; - SendAnnouncement(kDefaultTTL); + SendAnnouncement(full_ttl_); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&DnsSdServer::OnDatagramReceived, base::Unretained(this))); - is_online_ = true; return true; } void DnsSdServer::Update() { - if (!is_online_) + if (!IsOnline()) return; - SendAnnouncement(kDefaultTTL); + SendAnnouncement(full_ttl_); } void DnsSdServer::Shutdown() { - if (!is_online_) + if (!IsOnline()) return; - SendAnnouncement(0); // ttl is 0 + SendAnnouncement(0); // TTL is 0 socket_->Close(); - is_online_ = false; + socket_.reset(NULL); LOG(INFO) << "DNS server stopped"; } -void DnsSdServer::ProcessMessages() { - NOTIMPLEMENTED(); // implement this +void DnsSdServer::UpdateMetadata(const std::vector& metadata) { + if (!IsOnline()) + return; + + metadata_ = metadata; + + // TODO(maksymb): If less than 20% of full TTL left before next announcement + // then send it now. + + uint32 current_ttl = GetCurrentTLL(); + if (!CommandLine::ForCurrentProcess()->HasSwitch("no-announcement")) { + DnsResponseBuilder builder(current_ttl); + + builder.AppendTxt(serv_params_.service_name_, current_ttl, metadata_); + scoped_refptr buffer(builder.Build()); + + DCHECK(buffer.get() != NULL); + + socket_->SendTo(buffer.get(), buffer.get()->size(), multicast_address_, + base::Bind(&DoNothingAfterSendToSocket)); + } } bool DnsSdServer::CreateSocket() { @@ -77,8 +123,7 @@ bool DnsSdServer::CreateSocket() { socket_.reset(new net::UDPSocket(net::DatagramSocket::DEFAULT_BIND, - net::RandIntCallback(), - NULL, + net::RandIntCallback(), NULL, net::NetLog::Source())); net::IPEndPoint local_address = net::IPEndPoint(local_ip_any, @@ -103,35 +148,153 @@ bool DnsSdServer::CreateSocket() { return true; } -bool DnsSdServer::CheckPendingQueries() { - NOTIMPLEMENTED(); // implement this - return false; +void DnsSdServer::ProcessMessage(int len, net::IOBufferWithSize* buf) { + VLOG(1) << "Received new message with length: " << len; + + // Parse the message. + DnsPacketParser parser(buf->data(), len); + + if (!parser.IsValid()) // Was unable to parse header. + return; + + // TODO(maksymb): Handle truncated messages. + if (parser.header().flags & net::dns_protocol::kFlagResponse) // Not a query. + return; + + DnsResponseBuilder builder(parser.header().id); + + uint32 current_ttl = GetCurrentTLL(); + + DnsQueryRecord query; + // TODO(maksymb): Check known answers. + for (int query_idx = 0; query_idx < parser.header().qdcount; ++query_idx) { + bool success = parser.ReadRecord(&query); + if (success) { + ProccessQuery(current_ttl, query, &builder); + } else { // if (success) + LOG(INFO) << "Broken package"; + break; + } + } + + scoped_refptr buffer(builder.Build()); + if (buffer.get() == NULL) + return; // No answers. + + VLOG(1) << "Current TTL for respond: " << current_ttl; + + bool multicast_respond = + CommandLine::ForCurrentProcess()->HasSwitch("multicast-respond"); + socket_->SendTo(buffer.get(), buffer.get()->size(), + multicast_respond ? multicast_address_ : recv_address_, + base::Bind(&DoNothingAfterSendToSocket)); + VLOG(1) << "Responded to " + << (multicast_respond ? multicast_address_ : recv_address_).ToString(); } -void DoNothing(int /*var*/) { - // Do nothing +void DnsSdServer::ProccessQuery(uint32 current_ttl, const DnsQueryRecord& query, + DnsResponseBuilder* builder) const { + std::string log; + bool responded = false; + switch (query.qtype) { + // TODO(maksymb): Add IPv6 support. + case net::dns_protocol::kTypePTR: + log = "Processing PTR query"; + if (query.qname == serv_params_.service_type_) { + builder->AppendPtr(serv_params_.service_type_, current_ttl, + serv_params_.service_name_); + responded = true; + } + break; + case net::dns_protocol::kTypeSRV: + log = "Processing SRV query"; + if (query.qname == serv_params_.service_name_) { + builder->AppendSrv(serv_params_.service_name_, current_ttl, + kSrvPriority, kSrvWeight, serv_params_.http_port_, + serv_params_.service_domain_name_); + responded = true; + } + break; + case net::dns_protocol::kTypeA: + log = "Processing A query"; + if (query.qname == serv_params_.service_domain_name_) { + builder->AppendA(serv_params_.service_domain_name_, current_ttl, + serv_params_.http_ipv4_); + responded = true; + } + break; + case net::dns_protocol::kTypeTXT: + log = "Processing TXT query"; + if (query.qname == serv_params_.service_name_) { + builder->AppendTxt(serv_params_.service_name_, current_ttl, metadata_); + responded = true; + } + break; + default: + base::SStringPrintf(&log, "Unknown query type (%d)", query.qtype); + } + log += responded ? ": responded" : ": ignored"; + VLOG(1) << log; +} + +void DnsSdServer::DoLoop(int rv) { + // TODO(maksymb): Check what happened if buffer will be overflowed + do { + if (rv > 0) + ProcessMessage(rv, recv_buf_); + rv = socket_->RecvFrom(recv_buf_, recv_buf_->size(), &recv_address_, + base::Bind(&DnsSdServer::DoLoop, + base::Unretained(this))); + } while (rv > 0); + + // TODO(maksymb): Add handler for errors + DCHECK(rv == net::ERR_IO_PENDING); +} + +void DnsSdServer::OnDatagramReceived() { + DoLoop(0); } void DnsSdServer::SendAnnouncement(uint32 ttl) { - // Create a message with allocated space for header. - // DNS header is temporary empty. - scoped_ptr > message( - new std::vector(sizeof(net::dns_protocol::Header), 0)); // all is 0 - - // TODO(maksymb): Create and implement DnsResponse class - - // Preparing for sending - scoped_refptr buffer = - new net::IOBufferWithSize(static_cast(message.get()->size())); - memcpy(buffer.get()->data(), message.get()->data(), message.get()->size()); - - // Create empty callback (we don't need it at all) and send packet - net::CompletionCallback callback = base::Bind(DoNothing); - socket_->SendTo(buffer.get(), - buffer.get()->size(), - multicast_address_, - callback); - - LOG(INFO) << "Announcement was sent with TTL: " << ttl; + if (!CommandLine::ForCurrentProcess()->HasSwitch("no-announcement")) { + DnsResponseBuilder builder(ttl); + + builder.AppendPtr(serv_params_.service_type_, ttl, + serv_params_.service_name_); + builder.AppendSrv(serv_params_.service_name_, ttl, kSrvPriority, kSrvWeight, + serv_params_.http_port_, + serv_params_.service_domain_name_); + builder.AppendA(serv_params_.service_domain_name_, ttl, + serv_params_.http_ipv4_); + builder.AppendTxt(serv_params_.service_name_, ttl, metadata_); + scoped_refptr buffer(builder.Build()); + + DCHECK(buffer.get() != NULL); + + socket_->SendTo(buffer.get(), buffer.get()->size(), multicast_address_, + base::Bind(&DoNothingAfterSendToSocket)); + + VLOG(1) << "Announcement was sent with TTL: " << ttl; + } + + time_until_live_ = base::Time::Now() + + base::TimeDelta::FromSeconds(full_ttl_); + + // Schedule next announcement. + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&DnsSdServer::Update, base::Unretained(this)), + base::TimeDelta::FromSeconds(static_cast( + kTimeToNextAnnouncement*full_ttl_))); +} + +uint32 DnsSdServer::GetCurrentTLL() const { + uint32 current_ttl = (time_until_live_ - base::Time::Now()).InSeconds(); + if (time_until_live_ < base::Time::Now() || current_ttl == 0) { + // This should not be reachable. But still we don't need to fail. + current_ttl = 1; // Service is still alive. + LOG(ERROR) << "|current_ttl| was equal to zero."; + } + return current_ttl; } -- cgit v1.1