summaryrefslogtreecommitdiffstats
path: root/cloud_print/gcp20/prototype/dns_sd_server.cc
diff options
context:
space:
mode:
authormaksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-22 16:49:06 +0000
committermaksymb@chromium.org <maksymb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-22 16:49:06 +0000
commitead61a438feb008581b82fddca34a18527c868d1 (patch)
tree97fee11962961af87dca2f24401c92eabfcebb7b /cloud_print/gcp20/prototype/dns_sd_server.cc
parent98751ff18ca3721338c6103d8f78d09dcb6e2a53 (diff)
downloadchromium_src-ead61a438feb008581b82fddca34a18527c868d1.zip
chromium_src-ead61a438feb008581b82fddca34a18527c868d1.tar.gz
chromium_src-ead61a438feb008581b82fddca34a18527c868d1.tar.bz2
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
Diffstat (limited to 'cloud_print/gcp20/prototype/dns_sd_server.cc')
-rw-r--r--cloud_print/gcp20/prototype/dns_sd_server.cc247
1 files changed, 205 insertions, 42 deletions
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 <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;
}