Chromium Code Reviews| 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..c3d5f60668082cd97dc8f850433a8952d4ee5de2 100644 |
| --- a/cloud_print/gcp20/prototype/dns_sd_server.cc |
| +++ b/cloud_print/gcp20/prototype/dns_sd_server.cc |
| @@ -7,7 +7,14 @@ |
| #include <string.h> |
| #include "base/basictypes.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,29 +23,47 @@ 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; |
| + |
| +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() |
| + : is_online_(false), |
| + recv_buf_(new net::IOBufferWithSize(kDnsBufSize)), |
| + full_ttl_(0) { |
| } |
| DnsSdServer::~DnsSdServer() { |
| Shutdown(); |
| } |
| -bool DnsSdServer::Start() { |
| +bool DnsSdServer::Start(const ServiceParameters& serv_params, uint32 full_ttl, |
| + const std::string& txt_data) { |
| if (is_online_) |
| return true; |
| if (!CreateSocket()) |
| return false; |
| + // Initializing server with parameters from arguments. |
| + serv_params_ = serv_params; |
| + full_ttl_ = full_ttl; |
| + txt_data_ = txt_data; |
| + |
| 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; |
| @@ -48,21 +73,37 @@ void DnsSdServer::Update() { |
| if (!is_online_) |
| return; |
| - SendAnnouncement(kDefaultTTL); |
| + SendAnnouncement(full_ttl_); |
| } |
| void DnsSdServer::Shutdown() { |
| if (!is_online_) |
| return; |
| - SendAnnouncement(0); // ttl is 0 |
| + SendAnnouncement(0); // TTL is 0 |
| socket_->Close(); |
| is_online_ = false; |
| LOG(INFO) << "DNS server stopped"; |
| } |
| -void DnsSdServer::ProcessMessages() { |
| - NOTIMPLEMENTED(); // implement this |
| +void DnsSdServer::UpdateTxtData(const std::string& txt_data) { |
| + txt_data_ = txt_data; |
| + |
| + // 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(current_ttl, serv_params_, txt_data_); |
| + 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 +118,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 +143,137 @@ 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 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 ((responded = (query.qname == serv_params_.service_type_))) |
|
gene
2013/06/19 05:40:51
it will be easier to read:
if (query.qname == serv
maksymb
2013/06/19 21:47:21
I don't have this method. I hope this will be more
|
| + builder->AppendPtr(current_ttl, serv_params_); |
| + break; |
| + case net::dns_protocol::kTypeSRV: |
| + log = "Processing SRV query"; |
| + if ((responded = (query.qname == serv_params_.service_name_))) |
| + builder->AppendSrv(current_ttl, serv_params_); |
| + break; |
| + case net::dns_protocol::kTypeA: |
| + log = "Processing A query"; |
| + if ((responded = (query.qname == serv_params_.service_domain_name_))) |
| + builder->AppendA(current_ttl, serv_params_); |
| + break; |
| + case net::dns_protocol::kTypeTXT: |
| + log = "Processing TXT query"; |
| + if ((responded = (query.qname == serv_params_.service_name_))) |
| + builder->AppendTxt(current_ttl, serv_params_, txt_data_); |
| + break; |
| + default: |
| + base::SStringPrintf(&log, "Unknown query type (%d)", query.qtype); |
| + } |
| + log += responded ? ": responded" : ": ignored"; |
| + VLOG(1) << log; |
| } |
| -void DoNothing(int /*var*/) { |
| - // Do nothing |
| +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(ttl, serv_params_); |
| + builder.AppendSrv(ttl, serv_params_); |
| + builder.AppendA(ttl, serv_params_); |
| + builder.AppendTxt(ttl, serv_params_, txt_data_); |
| + 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_))); |
|
gene
2013/06/19 05:40:51
spaces around *
maksymb
2013/06/19 21:47:21
http://google-styleguide.googlecode.com/svn/trunk/
|
| +} |
| + |
| +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; |
| } |