| Index: 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 0ca8b4395f508f3225067019951632252f9f338c..2d2716b63dd4cd7f766843649a00c2fcb0bc9969 100644
|
| --- a/cloud_print/gcp20/prototype/dns_sd_server.cc
|
| +++ b/cloud_print/gcp20/prototype/dns_sd_server.cc
|
| @@ -7,7 +7,15 @@
|
| #include <string.h>
|
|
|
| #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<std::string>& 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<std::string>& 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<net::IOBufferWithSize> 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<net::IOBufferWithSize> 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<std::vector<uint8> > message(
|
| - new std::vector<uint8>(sizeof(net::dns_protocol::Header), 0)); // all is 0
|
| -
|
| - // TODO(maksymb): Create and implement DnsResponse class
|
| -
|
| - // Preparing for sending
|
| - scoped_refptr<net::IOBufferWithSize> buffer =
|
| - new net::IOBufferWithSize(static_cast<int>(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<net::IOBufferWithSize> 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<int64>(
|
| + 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;
|
| }
|
|
|
|
|