1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cloud_print/gcp20/prototype/dns_sd_server.h"
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/stringprintf.h"
14 #include "cloud_print/gcp20/prototype/dns_packet_parser.h"
15 #include "cloud_print/gcp20/prototype/dns_response_builder.h"
16 #include "net/base/big_endian.h"
17 #include "net/base/dns_util.h"
18 #include "net/base/net_errors.h"
19 #include "net/base/net_util.h"
20 #include "net/dns/dns_protocol.h"
24 const char kDefaultIpAddressMulticast[] = "224.0.0.251";
25 const uint16 kDefaultPortMulticast = 5353;
27 const double kTimeToNextAnnouncement = 0.8; // relatively to TTL
28 const int kDnsBufSize = 65537;
30 const uint16 kSrvPriority = 0;
31 const uint16 kSrvWeight = 0;
33 void DoNothingAfterSendToSocket(int /*val*/) {
35 // TODO(maksymb): Delete this function once empty callback for SendTo() method
41 DnsSdServer::DnsSdServer()
42 : recv_buf_(new net::IOBufferWithSize(kDnsBufSize)),
46 DnsSdServer::~DnsSdServer() {
50 bool DnsSdServer::Start(const ServiceParameters& serv_params, uint32 full_ttl,
51 const std::vector<std::string>& metadata) {
58 // Initializing server with parameters from arguments.
59 serv_params_ = serv_params;
63 LOG(INFO) << "DNS server started";
64 LOG(WARNING) << "DNS server does not support probing";
66 SendAnnouncement(full_ttl_);
67 base::MessageLoop::current()->PostTask(
69 base::Bind(&DnsSdServer::OnDatagramReceived, AsWeakPtr()));
74 void DnsSdServer::Update() {
78 SendAnnouncement(full_ttl_);
81 void DnsSdServer::Shutdown() {
85 SendAnnouncement(0); // TTL is 0
88 LOG(INFO) << "DNS server stopped";
91 void DnsSdServer::UpdateMetadata(const std::vector<std::string>& metadata) {
97 // TODO(maksymb): If less than 20% of full TTL left before next announcement
100 uint32 current_ttl = GetCurrentTLL();
101 if (!CommandLine::ForCurrentProcess()->HasSwitch("no-announcement")) {
102 DnsResponseBuilder builder(current_ttl);
104 builder.AppendTxt(serv_params_.service_name_, current_ttl, metadata_);
105 scoped_refptr<net::IOBufferWithSize> buffer(builder.Build());
107 DCHECK(buffer.get() != NULL);
109 socket_->SendTo(buffer.get(), buffer.get()->size(), multicast_address_,
110 base::Bind(&DoNothingAfterSendToSocket));
114 bool DnsSdServer::CreateSocket() {
115 net::IPAddressNumber local_ip_any;
116 bool success = net::ParseIPLiteralToNumber("0.0.0.0", &local_ip_any);
119 net::IPAddressNumber multicast_dns_ip_address;
120 success = net::ParseIPLiteralToNumber(kDefaultIpAddressMulticast,
121 &multicast_dns_ip_address);
125 socket_.reset(new net::UDPSocket(net::DatagramSocket::DEFAULT_BIND,
126 net::RandIntCallback(), NULL,
127 net::NetLog::Source()));
129 net::IPEndPoint local_address = net::IPEndPoint(local_ip_any,
130 kDefaultPortMulticast);
131 multicast_address_ = net::IPEndPoint(multicast_dns_ip_address,
132 kDefaultPortMulticast);
134 socket_->AllowAddressReuse();
136 int status = socket_->Bind(local_address);
140 socket_->SetMulticastLoopbackMode(false);
141 status = socket_->JoinGroup(multicast_dns_ip_address);
146 DCHECK(socket_->is_connected());
151 void DnsSdServer::ProcessMessage(int len, net::IOBufferWithSize* buf) {
152 VLOG(1) << "Received new message with length: " << len;
154 // Parse the message.
155 DnsPacketParser parser(buf->data(), len);
157 if (!parser.IsValid()) // Was unable to parse header.
160 // TODO(maksymb): Handle truncated messages.
161 if (parser.header().flags & net::dns_protocol::kFlagResponse) // Not a query.
164 DnsResponseBuilder builder(parser.header().id);
166 uint32 current_ttl = GetCurrentTLL();
168 DnsQueryRecord query;
169 // TODO(maksymb): Check known answers.
170 for (int query_idx = 0; query_idx < parser.header().qdcount; ++query_idx) {
171 bool success = parser.ReadRecord(&query);
173 ProccessQuery(current_ttl, query, &builder);
174 } else { // if (success)
175 LOG(INFO) << "Broken package";
180 scoped_refptr<net::IOBufferWithSize> buffer(builder.Build());
181 if (buffer.get() == NULL)
182 return; // No answers.
184 VLOG(1) << "Current TTL for respond: " << current_ttl;
186 bool unicast_respond =
187 CommandLine::ForCurrentProcess()->HasSwitch("unicast-respond");
188 socket_->SendTo(buffer.get(), buffer.get()->size(),
189 unicast_respond ? recv_address_ : multicast_address_,
190 base::Bind(&DoNothingAfterSendToSocket));
191 VLOG(1) << "Responded to "
192 << (unicast_respond ? recv_address_ : multicast_address_).ToString();
195 void DnsSdServer::ProccessQuery(uint32 current_ttl, const DnsQueryRecord& query,
196 DnsResponseBuilder* builder) const {
198 bool responded = false;
199 switch (query.qtype) {
200 // TODO(maksymb): Add IPv6 support.
201 case net::dns_protocol::kTypePTR:
202 log = "Processing PTR query";
203 if (query.qname == serv_params_.service_type_ ||
204 query.qname == serv_params_.secondary_service_type_) {
205 builder->AppendPtr(query.qname, current_ttl,
206 serv_params_.service_name_);
211 case net::dns_protocol::kTypeSRV:
212 log = "Processing SRV query";
213 if (query.qname == serv_params_.service_name_) {
214 builder->AppendSrv(serv_params_.service_name_, current_ttl,
215 kSrvPriority, kSrvWeight, serv_params_.http_port_,
216 serv_params_.service_domain_name_);
220 case net::dns_protocol::kTypeA:
221 log = "Processing A query";
222 if (query.qname == serv_params_.service_domain_name_) {
223 builder->AppendA(serv_params_.service_domain_name_, current_ttl,
224 serv_params_.http_ipv4_);
228 case net::dns_protocol::kTypeTXT:
229 log = "Processing TXT query";
230 if (query.qname == serv_params_.service_name_) {
231 builder->AppendTxt(serv_params_.service_name_, current_ttl, metadata_);
236 base::SStringPrintf(&log, "Unknown query type (%d)", query.qtype);
238 log += responded ? ": responded" : ": ignored";
242 void DnsSdServer::DoLoop(int rv) {
243 // TODO(maksymb): Check what happened if buffer will be overflowed
246 ProcessMessage(rv, recv_buf_.get());
247 rv = socket_->RecvFrom(recv_buf_.get(), recv_buf_->size(), &recv_address_,
248 base::Bind(&DnsSdServer::DoLoop, AsWeakPtr()));
251 // TODO(maksymb): Add handler for errors
252 DCHECK(rv == net::ERR_IO_PENDING);
255 void DnsSdServer::OnDatagramReceived() {
259 void DnsSdServer::SendAnnouncement(uint32 ttl) {
260 if (!CommandLine::ForCurrentProcess()->HasSwitch("no-announcement")) {
261 DnsResponseBuilder builder(ttl);
263 builder.AppendPtr(serv_params_.service_type_, ttl,
264 serv_params_.service_name_);
265 builder.AppendPtr(serv_params_.secondary_service_type_, ttl,
266 serv_params_.service_name_);
267 builder.AppendSrv(serv_params_.service_name_, ttl, kSrvPriority, kSrvWeight,
268 serv_params_.http_port_,
269 serv_params_.service_domain_name_);
270 builder.AppendA(serv_params_.service_domain_name_, ttl,
271 serv_params_.http_ipv4_);
272 builder.AppendTxt(serv_params_.service_name_, ttl, metadata_);
273 scoped_refptr<net::IOBufferWithSize> buffer(builder.Build());
275 DCHECK(buffer.get() != NULL);
277 socket_->SendTo(buffer.get(), buffer.get()->size(), multicast_address_,
278 base::Bind(&DoNothingAfterSendToSocket));
280 VLOG(1) << "Announcement was sent with TTL: " << ttl;
283 time_until_live_ = base::Time::Now() +
284 base::TimeDelta::FromSeconds(full_ttl_);
286 // Schedule next announcement.
287 base::MessageLoop::current()->PostDelayedTask(
289 base::Bind(&DnsSdServer::Update, AsWeakPtr()),
290 base::TimeDelta::FromSeconds(static_cast<int64>(
291 kTimeToNextAnnouncement*full_ttl_)));
294 uint32 DnsSdServer::GetCurrentTLL() const {
295 uint32 current_ttl = (time_until_live_ - base::Time::Now()).InSeconds();
296 if (time_until_live_ < base::Time::Now() || current_ttl == 0) {
297 // This should not be reachable. But still we don't need to fail.
298 current_ttl = 1; // Service is still alive.
299 LOG(ERROR) << "|current_ttl| was equal to zero.";