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..65b5ab2bd3f31d807a07c8f3609e0e61670e8421 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,53 +23,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 (socket_.get() != NULL) |
Vitaly Buka (NO REVIEWS)
2013/06/21 01:03:39
if (socket_) or IsOnline()
maksymb
2013/06/21 03:22:48
Done.
|
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() { |
Vitaly Buka (NO REVIEWS)
2013/06/21 01:03:39
same
maksymb
2013/06/21 03:22:48
Done.
|
- if (!is_online_) |
+ if (socket_.get() == NULL) |
return; |
- SendAnnouncement(kDefaultTTL); |
+ SendAnnouncement(full_ttl_); |
} |
void DnsSdServer::Shutdown() { |
- if (!is_online_) |
+ if (socket_.get() == NULL) |
Vitaly Buka (NO REVIEWS)
2013/06/21 01:03:39
same
maksymb
2013/06/21 03:22:48
Done.
|
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 (socket_.get() == NULL) |
Vitaly Buka (NO REVIEWS)
2013/06/21 01:03:39
same
maksymb
2013/06/21 03:22:48
Done.
|
+ 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 +122,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 +147,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_); |
Vitaly Buka (NO REVIEWS)
2013/06/21 01:03:39
IP must be different depending on source of query
maksymb
2013/06/21 03:22:48
TODO added
|
+ 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_); |
Vitaly Buka (NO REVIEWS)
2013/06/21 01:03:39
same
maksymb
2013/06/21 03:22:48
TODO added
|
+ 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; |
} |