Commit ead61a43 authored by maksymb@chromium.org's avatar maksymb@chromium.org

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
parent 98751ff1
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cloud_print/gcp20/prototype/dns_packet_parser.h"
#include "net/base/big_endian.h"
DnsPacketParser::DnsPacketParser(const char* packet, size_t length)
: packet_(packet),
length_(length),
record_parser_(packet, length, sizeof(header_)) {
net::BigEndianReader reader(packet, length);
is_header_read_ = reader.ReadU16(&header_.id) &&
reader.ReadU16(&header_.flags) &&
reader.ReadU16(&header_.qdcount) &&
reader.ReadU16(&header_.ancount) &&
reader.ReadU16(&header_.nscount) &&
reader.ReadU16(&header_.arcount);
}
bool DnsPacketParser::ReadRecord(DnsQueryRecord* out) {
DCHECK(packet_);
DnsQueryRecord result;
size_t consumed = ReadName(&result.qname);
if (!consumed)
return false;
net::BigEndianReader reader(packet_ + GetOffset() + consumed,
length_ - (GetOffset() + consumed));
if (reader.ReadU16(&result.qtype) && reader.ReadU16(&result.qclass) &&
record_parser_.SkipQuestion()) { // instead of |cur_ = reader.ptr();|
*out = result;
return true;
}
return false;
}
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CLOUD_PRINT_GCP20_PROTOTYPE_DNS_PACKET_PARSER_H_
#define CLOUD_PRINT_GCP20_PROTOTYPE_DNS_PACKET_PARSER_H_
#include <string>
#include "net/dns/dns_protocol.h"
#include "net/dns/dns_response.h"
// Parsed response record.
struct DnsQueryRecord {
DnsQueryRecord() : qtype(0), qclass(0) {}
~DnsQueryRecord() {}
std::string qname; // in dotted form
uint16 qtype;
uint16 qclass;
};
// Iterator to walk over records of the DNS response packet. Encapsulates
// |DnsRecordParser| object for using its functionality.
class DnsPacketParser {
public:
// Constructs an iterator to process the |packet| of given |length|.
DnsPacketParser(const char* packet, size_t length);
// Destroys the object.
~DnsPacketParser() {}
bool IsValid() const { return record_parser_.IsValid() && is_header_read_; }
// Returns |true| if no more bytes remain in the packet.
bool AtEnd() const { return record_parser_.AtEnd(); }
// Returns header of DNS packet.
const net::dns_protocol::Header& header() const { return header_; }
// Parses the next query record into |record|. Returns true if succeeded.
bool ReadRecord(DnsQueryRecord* record);
// Parses the next resource record into |record|. Returns true if succeeded.
bool ReadRecord(net::DnsResourceRecord* record) {
return record_parser_.ReadRecord(record);
}
private:
// Returns current offset into the packet.
size_t GetOffset() const { return record_parser_.GetOffset(); }
// Parses a (possibly compressed) DNS name from the packet starting at
// |pos|. Stores output (even partial) in |out| unless |out| is NULL. |out|
// is stored in the dotted form, e.g., "example.com". Returns number of bytes
// consumed or 0 on failure.
// This is exposed to allow parsing compressed names within RRDATA for TYPEs
// such as NS, CNAME, PTR, MX, SOA.
// See RFC 1035 section 4.1.4.
unsigned ReadName(std::string* out) const {
return record_parser_.ReadName(packet_ + GetOffset(), out);
}
const char* packet_;
size_t length_;
// Contents parsed header_;
net::dns_protocol::Header header_;
bool is_header_read_;
// Encapsulated parser.
net::DnsRecordParser record_parser_;
DISALLOW_COPY_AND_ASSIGN(DnsPacketParser);
};
#endif // CLOUD_PRINT_GCP20_PROTOTYPE_DNS_PACKET_PARSER_H_
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cloud_print/gcp20/prototype/dns_response_builder.h"
#include "base/logging.h"
#include "net/base/big_endian.h"
#include "net/base/dns_util.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/dns/dns_response.h"
namespace {
uint16 klass = net::dns_protocol::kClassIN;
} // namespace
DnsResponseRecord::DnsResponseRecord() : type(0), klass(0), ttl(0) {
}
DnsResponseRecord::~DnsResponseRecord() {
}
DnsResponseBuilder::DnsResponseBuilder(uint16 id) {
header_.id = id;
// TODO(maksymb): Check do we need AA flag enabled.
header_.flags = net::dns_protocol::kFlagResponse |
net::dns_protocol::kFlagAA;
header_.qdcount = 0;
header_.ancount = 0;
header_.nscount = 0;
header_.arcount = 0;
}
void DnsResponseBuilder::AppendPtr(const std::string& service_type, uint32 ttl,
const std::string& service_name) {
std::string rdata;
bool success = net::DNSDomainFromDot(service_name, &rdata);
DCHECK(success);
AddResponse(service_type, net::dns_protocol::kTypePTR, ttl, rdata);
}
void DnsResponseBuilder::AppendSrv(const std::string& service_name, uint32 ttl,
uint16 priority, uint16 weight,
uint16 http_port,
const std::string& service_domain_name) {
std::string domain_name;
bool success = net::DNSDomainFromDot(service_domain_name, &domain_name);
DCHECK(success);
std::vector<uint8> rdata(2 + 2 + 2 + domain_name.size());
net::BigEndianWriter writer(rdata.data(), rdata.size());
success = writer.WriteU16(priority) &&
writer.WriteU16(weight) &&
writer.WriteU16(http_port) &&
writer.WriteBytes(domain_name.data(), domain_name.size());
DCHECK(success);
DCHECK_EQ(writer.remaining(), 0); // For warranty of correct size allocation.
AddResponse(service_name, net::dns_protocol::kTypeSRV, ttl,
std::string(rdata.begin(), rdata.end()));
}
void DnsResponseBuilder::AppendA(const std::string& service_domain_name,
uint32 ttl, net::IPAddressNumber http_ipv4) {
// TODO(maksymb): IP to send must depends on interface from where query was
// received.
if (http_ipv4.empty()) {
LOG(ERROR) << "Invalid IP";
return;
}
AddResponse(service_domain_name, net::dns_protocol::kTypeA, ttl,
std::string(http_ipv4.begin(), http_ipv4.end()));
}
void DnsResponseBuilder::AppendTxt(const std::string& service_name, uint32 ttl,
const std::vector<std::string>& metadata) {
std::string rdata;
for (std::vector<std::string>::const_iterator str = metadata.begin();
str != metadata.end(); ++str) {
int len = static_cast<int>(str->size());
DCHECK_LT(len, 256);
rdata += static_cast<char>(len); // Set length byte.
rdata += *str;
}
AddResponse(service_name, net::dns_protocol::kTypeTXT, ttl, rdata);
}
scoped_refptr<net::IOBufferWithSize> DnsResponseBuilder::Build() {
size_t size = sizeof(header_);
for (std::vector<DnsResponseRecord>::const_iterator iter = responses_.begin();
iter != responses_.end(); ++iter) {
size += iter->name.size() + 2 + // Two dots: first and last.
sizeof(iter->type) + sizeof(iter->klass) + sizeof(iter->ttl) +
2 + // sizeof(RDLENGTH)
iter->rdata.size();
}
if (responses_.empty())
return NULL; // No answer.
header_.ancount = static_cast<uint16>(responses_.size());
scoped_refptr<net::IOBufferWithSize> message(
new net::IOBufferWithSize(static_cast<int>(size)));
net::BigEndianWriter writer(message->data(), message->size());
bool success = writer.WriteU16(header_.id) &&
writer.WriteU16(header_.flags) &&
writer.WriteU16(header_.qdcount) &&
writer.WriteU16(header_.ancount) &&
writer.WriteU16(header_.nscount) &&
writer.WriteU16(header_.arcount);
DCHECK(success);
std::string name_in_dns_format;
for (std::vector<DnsResponseRecord>::const_iterator iter = responses_.begin();
iter != responses_.end(); ++iter) {
success = net::DNSDomainFromDot(iter->name, &name_in_dns_format);
DCHECK(success);
DCHECK_EQ(name_in_dns_format.size(), iter->name.size() + 2);
success = writer.WriteBytes(name_in_dns_format.data(),
name_in_dns_format.size()) &&
writer.WriteU16(iter->type) &&
writer.WriteU16(iter->klass) &&
writer.WriteU32(iter->ttl) &&
writer.WriteU16(static_cast<uint16>(iter->rdata.size())) &&
writer.WriteBytes(iter->rdata.data(), iter->rdata.size());
DCHECK(success);
}
DCHECK_EQ(writer.remaining(), 0); // For warranty of correct size allocation.
return message;
}
void DnsResponseBuilder::AddResponse(const std::string& name, uint16 type,
uint32 ttl, const std::string& rdata) {
DnsResponseRecord response;
response.name = name;
response.klass = klass;
response.ttl = ttl;
response.type = type;
response.rdata = rdata;
responses_.push_back(response);
}
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CLOUD_PRINT_GCP20_PROTOTYPE_DNS_RESPONSE_BUILDER_H_
#define CLOUD_PRINT_GCP20_PROTOTYPE_DNS_RESPONSE_BUILDER_H_
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "net/base/net_util.h"
#include "net/dns/dns_protocol.h"
namespace net {
class IOBufferWithSize;
}
// Record for storing response data.
struct DnsResponseRecord {
DnsResponseRecord();
~DnsResponseRecord();
std::string name; // in dotted form
uint16 type;
uint16 klass;
uint32 ttl;
std::string rdata;
};
// Class for building service-specified responses.
class DnsResponseBuilder {
public:
// Initializes builder.
explicit DnsResponseBuilder(uint16 id);
// Destroys the object.
~DnsResponseBuilder() {}
// Methods for appending different types of responses to packet.
void AppendPtr(const std::string& service_type, uint32 ttl,
const std::string& service_name);
void AppendSrv(const std::string& service_name, uint32 ttl, uint16 priority,
uint16 weight, uint16 http_port,
const std::string& service_domain_name);
void AppendA(const std::string& service_domain_name, uint32 ttl,
net::IPAddressNumber http_ipv4);
void AppendTxt(const std::string& service_name, uint32 ttl,
const std::vector<std::string>& metadata);
// Serializes packet to byte sequence.
scoped_refptr<net::IOBufferWithSize> Build();
private:
// Appends response to packet.
void AddResponse(const std::string& name, uint16 type, uint32 ttl,
const std::string& rdata);
std::vector<DnsResponseRecord> responses_;
// Header of response package.
net::dns_protocol::Header header_;
DISALLOW_COPY_AND_ASSIGN(DnsResponseBuilder);
};
#endif // CLOUD_PRINT_GCP20_PROTOTYPE_DNS_RESPONSE_BUILDER_H_
......@@ -2,27 +2,40 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GCP20_PROTOTYPE_DNS_SD_H_
#define GCP20_PROTOTYPE_DNS_SD_H_
#ifndef CLOUD_PRINT_GCP20_PROTOTYPE_DNS_SD_SERVER_H_
#define CLOUD_PRINT_GCP20_PROTOTYPE_DNS_SD_SERVER_H_
#include <string>
#include <vector>
#include "net/dns/dns_protocol.h"
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "cloud_print/gcp20/prototype/service_parameters.h"
#include "net/base/ip_endpoint.h"
#include "net/udp/udp_socket.h"
namespace net {
class IOBufferWithSize;
}
struct DnsQueryRecord;
class DnsResponseBuilder;
// Class for sending multicast announcements, receiving queries and answering on
// them. Client should call |ProccessMessages| periodically to make server work.
// them.
// TODO(maksymb): Implement probing.
class DnsSdServer {
public:
// Constructs unstarted server.
// Constructor does not start server.
DnsSdServer();
// Stops server.
// Stops the server and destroys the object.
~DnsSdServer();
// Starts the server. Returns |true| if server works. Also sends
// announcement.
bool Start();
bool Start(const ServiceParameters& serv_params, uint32 full_ttl,
const std::vector<std::string>& metadata) WARN_UNUSED_RESULT;
// Sends announcement if server works.
void Update();
......@@ -30,24 +43,35 @@ class DnsSdServer {
// Stops server with announcement.
void Shutdown();
// Process pending queries for the server.
void ProcessMessages();
// Returns |true| if server works.
bool is_online() { return is_online_; }
bool IsOnline() { return !!socket_; }
// Updates data for TXT respond.
void UpdateMetadata(const std::vector<std::string>& metadata);
private:
// Binds a socket to multicast address. Returns |true| on success.
bool CreateSocket();
// Processes single query.
void ProccessQuery(uint32 current_ttl, const DnsQueryRecord& query,
DnsResponseBuilder* builder) const;
// Processes DNS message.
void ProcessMessage(int len, net::IOBufferWithSize* buf);
// CompletionCallback for receiving data from DNS.
void DoLoop(int rv);
// Function to start listening to socket (delegate to DoLoop function).
void OnDatagramReceived();
// Sends announcement.
void SendAnnouncement(uint32 ttl);
// Returns |true| if server received some questions.
bool CheckPendingQueries();
// Stores |true| if server was started.
bool is_online_;
// Calculates and returns current TTL (with accordance to last send
// announcement time.
uint32 GetCurrentTLL() const;
// Stores socket to multicast address.
scoped_ptr<net::UDPSocket> socket_;
......@@ -55,8 +79,26 @@ class DnsSdServer {
// Stores multicast address end point.
net::IPEndPoint multicast_address_;
// Stores time until last announcement is live.
base::Time time_until_live_;
// Stores service parameters (like service-name and service-type etc.)
ServiceParameters serv_params_;
// Stores the buffer for receiving messages.
scoped_refptr<net::IOBufferWithSize> recv_buf_;
// Stores address from where last message was sent.
net::IPEndPoint recv_address_;
// Stores information for TXT respond.
std::vector<std::string> metadata_;
// TTL for announcements
uint32 full_ttl_;
DISALLOW_COPY_AND_ASSIGN(DnsSdServer);
};
#endif // GCP20_PROTOTYPE_DNS_SD_H_
#endif // CLOUD_PRINT_GCP20_PROTOTYPE_DNS_SD_SERVER_H_
......@@ -2,12 +2,40 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <signal.h>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/run_loop.h"
#include "base/threading/platform_thread.h"
#include "cloud_print/gcp20/prototype/dns_sd_server.h"
#include "base/time.h"
#include "cloud_print/gcp20/prototype/printer.h"
namespace {
Printer printer;
base::AtExitManager at_exit;
void StopPrinter() {
printer.Stop();
}
void StartPrinter() {
bool success = printer.Start();
DCHECK(success);
at_exit.RegisterTask(base::Bind(&StopPrinter));
}
void OnAbort(int val) {
StopPrinter();
base::MessageLoop::current()->Quit();
}
} // namespace
int main(int argc, char* argv[]) {
CommandLine::Init(argc, argv);
......@@ -16,11 +44,12 @@ int main(int argc, char* argv[]) {
settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
logging::InitLogging(settings);
DnsSdServer dns_sd_server;
dns_sd_server.Start();
base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2));
dns_sd_server.Shutdown();
signal(SIGINT, OnAbort); // Handle Ctrl+C signal.
base::MessageLoop loop(base::MessageLoop::TYPE_IO);
base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&StartPrinter));
base::RunLoop runner;
runner.Run();
return 0;
}
......@@ -16,14 +16,33 @@
},
'targets': [
{
'target_name': 'gcp20_device',
'type': 'executable',
'target_name': 'gcp20_device_lib',
'type': 'static_library',
'dependencies': [
'<(DEPTH)/base/base.gyp:base',
'<(DEPTH)/net/net.gyp:net',
],
'sources': [
'dns_packet_parser.cc',
'dns_packet_parser.h',
'dns_response_builder.cc',
'dns_response_builder.h',
'dns_sd_server.cc',
'dns_sd_server.h',
'gcp20_device.cc',
'printer.cc',
'printer.h',
'service_parameters.cc',
'service_parameters.h',
],
},
{
'target_name': 'gcp20_device',
'type': 'executable',
'dependencies': [
'gcp20_device.gyp:gcp20_device_lib',
],
'sources': [
'gcp20_device.cc',
],
'msvs_settings': {
......
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cloud_print/gcp20/prototype/printer.h"
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "cloud_print/gcp20/prototype/service_parameters.h"
#include "net/base/net_util.h"
namespace {
const char* kServiceType = "_privet._tcp.local";
const char* kServiceNamePrefix = "first_gcp20_device";
const char* kServiceDomainName = "my-privet-device.local";
const uint16 kDefaultTTL = 60*60;
const uint16 kDefaultHttpPort = 10101;
uint16 ReadHttpPortFromCommandLine() {
uint32 http_port_tmp = kDefaultHttpPort;
std::string http_port_string_tmp =
CommandLine::ForCurrentProcess()->GetSwitchValueASCII("http-port");
base::StringToUint(http_port_string_tmp, &http_port_tmp);
if (http_port_tmp > kuint16max) {
LOG(ERROR) << "Port " << http_port_tmp << " is too large (maximum is " <<
kDefaultHttpPort << "). Using default port.";
http_port_tmp = kDefaultHttpPort;
}
VLOG(1) << "HTTP port for responses: " << http_port_tmp;
return static_cast<uint16>(http_port_tmp);
}
uint16 ReadTtlFromCommandLine() {
uint32 ttl = kDefaultTTL;
base::StringToUint(
CommandLine::ForCurrentProcess()->GetSwitchValueASCII("ttl"), &ttl);
VLOG(1) << "TTL for announcements: " << ttl;
return ttl;
}
// Returns local IP address number of first interface found (except loopback).
// Return value is empty if no interface found. Possible interfaces names are
// "eth0", "wlan0" etc. If interface name is empty, function will return IP
// address of first interface found.
net::IPAddressNumber GetLocalIp(const std::string& interface_name,
bool return_ipv6_number) {
net::NetworkInterfaceList interfaces;
bool success = net::GetNetworkList(&interfaces);
DCHECK(success);
size_t expected_address_size = return_ipv6_number ? net::kIPv6AddressSize
: net::kIPv4AddressSize;
for (net::NetworkInterfaceList::iterator iter = interfaces.begin();
iter != interfaces.end(); ++iter) {
if (iter->address.size() == expected_address_size &&
(interface_name.empty() || interface_name == iter->name)) {
LOG(INFO) << net::IPAddressToString(iter->address);
return iter->address;
}
}
return net::IPAddressNumber();
}
} // namespace
Printer::Printer() : initialized_(false) {
}
Printer::~Printer() {
Stop();
}
bool Printer::Start() {
if (initialized_)
return true;
// TODO(maksymb): Add switch for command line to control interface name.
net::IPAddressNumber ip = GetLocalIp("", false);
if (ip.empty()) {
LOG(ERROR) << "No local IP found. Cannot start printer.";
return false;
}
VLOG(1) << "Local address: " << net::IPAddressToString(ip);
// Starting DNS-SD server.
initialized_ = dns_server_.Start(
ServiceParameters(kServiceType,
kServiceNamePrefix,
kServiceDomainName,
ip,
ReadHttpPortFromCommandLine()),
ReadTtlFromCommandLine(),
CreateTxt());
return initialized_;
}
void Printer::Stop() {
if (!initialized_)
return;
dns_server_.Shutdown();
}
std::vector<std::string> Printer::CreateTxt() const {
std::vector<std::string> txt;
txt.push_back("txtvers=1");
txt.push_back("ty=Google Cloud Ready Printer GCP2.0 Prototype");
txt.push_back("note=Virtual printer");
txt.push_back("url=https://www.google.com/cloudprint");
txt.push_back("type=printer");
txt.push_back("id=");
txt.push_back("cs=offline");
return txt;
}
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GCP20_PROTOTYPE_PRINTER_H_
#define GCP20_PROTOTYPE_PRINTER_H_
#include <string>
#include <vector>
#include "cloud_print/gcp20/prototype/dns_sd_server.h"
// This class will maintain work of DNS-SD server, HTTP server and others.
class Printer {
public:
// Constructs uninitialized object.
Printer();
// Destroys the object.
~Printer();
// Starts all servers.
bool Start();
// Stops all servers.
void Stop();
private:
// Creates data for DNS TXT respond.
std::vector<std::string> CreateTxt() const;
// Contains |true| if initialized.
bool initialized_;
// Contains DNS-SD server.
DnsSdServer dns_server_;
DISALLOW_COPY_AND_ASSIGN(Printer);
};
#endif // GCP20_PROTOTYPE_PRINTER_H_
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cloud_print/gcp20/prototype/service_parameters.h"
ServiceParameters::ServiceParameters() : http_port_(0) {
}
ServiceParameters::ServiceParameters(const std::string& service_type,
const std::string& service_name_prefix,
const std::string& service_domain_name,
const net::IPAddressNumber& http_ipv4,
uint16 http_port)
: service_type_(service_type),
service_name_(service_name_prefix + "." + service_type),
service_domain_name_(service_domain_name),
http_ipv4_(http_ipv4),
http_port_(http_port) {
}
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CLOUD_PRINT_GCP20_PROTOTYPE_SERVICE_PARAMETERS_H_
#define CLOUD_PRINT_GCP20_PROTOTYPE_SERVICE_PARAMETERS_H_
#include <string>
#include "net/base/net_util.h"
// Stores information about service.
struct ServiceParameters {
ServiceParameters();
~ServiceParameters() {}
ServiceParameters(const std::string& service_type,
const std::string& service_name_prefix,
const std::string& service_domain_name,
const net::IPAddressNumber& http_ipv4,
uint16 http_port);
std::string service_type_;
std::string service_name_;
std::string service_domain_name_;
net::IPAddressNumber http_ipv4_;
uint16 http_port_;
};
#endif // CLOUD_PRINT_GCP20_PROTOTYPE_SERVICE_PARAMETERS_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment