This source file includes following definitions.
- DoNothingAfterSendToSocket
- full_ttl_
- Start
- Update
- Shutdown
- UpdateMetadata
- CreateSocket
- ProcessMessage
- ProccessQuery
- DoLoop
- OnDatagramReceived
- SendAnnouncement
- GetCurrentTLL
#include "cloud_print/gcp20/prototype/dns_sd_server.h"
#include <string.h>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/message_loop/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/dns_util.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/dns/dns_protocol.h"
namespace {
const char kDefaultIpAddressMulticast[] = "224.0.0.251";
const uint16 kDefaultPortMulticast = 5353;
const double kTimeToNextAnnouncement = 0.8;
const int kDnsBufSize = 65537;
const uint16 kSrvPriority = 0;
const uint16 kSrvWeight = 0;
void DoNothingAfterSendToSocket(int ) {
NOTREACHED();
}
}
DnsSdServer::DnsSdServer()
: recv_buf_(new net::IOBufferWithSize(kDnsBufSize)),
full_ttl_(0) {
}
DnsSdServer::~DnsSdServer() {
Shutdown();
}
bool DnsSdServer::Start(const ServiceParameters& serv_params, uint32 full_ttl,
const std::vector<std::string>& metadata) {
if (IsOnline())
return true;
if (!CreateSocket())
return false;
serv_params_ = serv_params;
full_ttl_ = full_ttl;
metadata_ = metadata;
VLOG(0) << "DNS server started";
LOG(WARNING) << "DNS server does not support probing";
SendAnnouncement(full_ttl_);
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&DnsSdServer::OnDatagramReceived, AsWeakPtr()));
return true;
}
void DnsSdServer::Update() {
if (!IsOnline())
return;
SendAnnouncement(full_ttl_);
}
void DnsSdServer::Shutdown() {
if (!IsOnline())
return;
SendAnnouncement(0);
socket_->Close();
socket_.reset(NULL);
VLOG(0) << "DNS server stopped";
}
void DnsSdServer::UpdateMetadata(const std::vector<std::string>& metadata) {
if (!IsOnline())
return;
metadata_ = metadata;
uint32 current_ttl = GetCurrentTLL();
if (!CommandLine::ForCurrentProcess()->HasSwitch("no-announcement")) {
DnsResponseBuilder builder(current_ttl);
builder.AppendTxt(serv_params_.service_name_, current_ttl, metadata_, true);
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() {
net::IPAddressNumber local_ip_any;
bool success = net::ParseIPLiteralToNumber("0.0.0.0", &local_ip_any);
DCHECK(success);
net::IPAddressNumber multicast_dns_ip_address;
success = net::ParseIPLiteralToNumber(kDefaultIpAddressMulticast,
&multicast_dns_ip_address);
DCHECK(success);
socket_.reset(new net::UDPSocket(net::DatagramSocket::DEFAULT_BIND,
net::RandIntCallback(), NULL,
net::NetLog::Source()));
net::IPEndPoint local_address = net::IPEndPoint(local_ip_any,
kDefaultPortMulticast);
multicast_address_ = net::IPEndPoint(multicast_dns_ip_address,
kDefaultPortMulticast);
socket_->AllowAddressReuse();
int status = socket_->Bind(local_address);
if (status < 0)
return false;
socket_->SetMulticastLoopbackMode(false);
status = socket_->JoinGroup(multicast_dns_ip_address);
if (status < 0)
return false;
DCHECK(socket_->is_connected());
return true;
}
void DnsSdServer::ProcessMessage(int len, net::IOBufferWithSize* buf) {
VLOG(1) << "Received new message with length: " << len;
DnsPacketParser parser(buf->data(), len);
if (!parser.IsValid())
return;
if (parser.header().flags & net::dns_protocol::kFlagResponse)
return;
DnsResponseBuilder builder(parser.header().id);
uint32 current_ttl = GetCurrentTLL();
DnsQueryRecord query;
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 {
VLOG(0) << "Broken package";
break;
}
}
scoped_refptr<net::IOBufferWithSize> buffer(builder.Build());
if (buffer.get() == NULL)
return;
VLOG(1) << "Current TTL for respond: " << current_ttl;
bool unicast_respond =
CommandLine::ForCurrentProcess()->HasSwitch("unicast-respond");
socket_->SendTo(buffer.get(), buffer.get()->size(),
unicast_respond ? recv_address_ : multicast_address_,
base::Bind(&DoNothingAfterSendToSocket));
VLOG(1) << "Responded to "
<< (unicast_respond ? recv_address_ : multicast_address_).ToString();
}
void DnsSdServer::ProccessQuery(uint32 current_ttl, const DnsQueryRecord& query,
DnsResponseBuilder* builder) const {
std::string log;
bool responded = false;
switch (query.qtype) {
case net::dns_protocol::kTypePTR:
log = "Processing PTR query";
if (query.qname == serv_params_.service_type_ ||
query.qname == serv_params_.secondary_service_type_) {
builder->AppendPtr(query.qname, current_ttl,
serv_params_.service_name_, true);
if (CommandLine::ForCurrentProcess()->HasSwitch("extended-response")) {
builder->AppendSrv(serv_params_.service_name_, current_ttl,
kSrvPriority, kSrvWeight, serv_params_.http_port_,
serv_params_.service_domain_name_, false);
builder->AppendA(serv_params_.service_domain_name_, current_ttl,
serv_params_.http_ipv4_, false);
builder->AppendAAAA(serv_params_.service_domain_name_, current_ttl,
serv_params_.http_ipv6_, false);
builder->AppendTxt(serv_params_.service_name_, current_ttl, metadata_,
false);
}
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_, true);
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_, true);
responded = true;
}
break;
case net::dns_protocol::kTypeAAAA:
log = "Processing AAAA query";
if (query.qname == serv_params_.service_domain_name_) {
builder->AppendAAAA(serv_params_.service_domain_name_, current_ttl,
serv_params_.http_ipv6_, true);
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_,
true);
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) {
do {
if (rv > 0)
ProcessMessage(rv, recv_buf_.get());
rv = socket_->RecvFrom(recv_buf_.get(), recv_buf_->size(), &recv_address_,
base::Bind(&DnsSdServer::DoLoop, AsWeakPtr()));
} while (rv > 0);
DCHECK(rv == net::ERR_IO_PENDING);
}
void DnsSdServer::OnDatagramReceived() {
DoLoop(0);
}
void DnsSdServer::SendAnnouncement(uint32 ttl) {
if (!CommandLine::ForCurrentProcess()->HasSwitch("no-announcement")) {
DnsResponseBuilder builder(ttl);
builder.AppendPtr(serv_params_.service_type_, ttl,
serv_params_.service_name_, true);
builder.AppendPtr(serv_params_.secondary_service_type_, ttl,
serv_params_.service_name_, true);
builder.AppendSrv(serv_params_.service_name_, ttl, kSrvPriority,
kSrvWeight, serv_params_.http_port_,
serv_params_.service_domain_name_, true);
builder.AppendA(serv_params_.service_domain_name_, ttl,
serv_params_.http_ipv4_, true);
builder.AppendAAAA(serv_params_.service_domain_name_, ttl,
serv_params_.http_ipv6_, true);
builder.AppendTxt(serv_params_.service_name_, ttl, metadata_, true);
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_);
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&DnsSdServer::Update, AsWeakPtr()),
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) {
current_ttl = 1;
LOG(ERROR) << "|current_ttl| was equal to zero.";
}
return current_ttl;
}