Skip to content

Commit

Permalink
Support for printing the dns configuration and hosts.
Browse files Browse the repository at this point in the history
It is also possible to specify a nameserver and a timeout from the command line.

BUG=128212
TEST=build and run gdig with the parameters --print_config --print_hosts --nameserver=an_ip:a_port and --timeout=milliseconds


Review URL: https://chromiumcodereview.appspot.com/10572018

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@147437 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
dcastagna@google.com committed Jul 19, 2012
1 parent a04b1f9 commit 8220e5d
Showing 1 changed file with 208 additions and 74 deletions.
282 changes: 208 additions & 74 deletions net/tools/gdig/gdig.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@
#include "base/message_loop.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/time.h"
#include "net/base/address_list.h"
#include "net/base/host_cache.h"
#include "net/base/host_resolver_impl.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/net_util.h"
#include "net/dns/dns_client.h"
#include "net/dns/dns_config_service.h"
#include "net/dns/dns_protocol.h"
#include "net/tools/gdig/file_net_log.h"

#if defined(OS_MACOSX)
Expand All @@ -32,6 +35,64 @@ namespace net {

namespace {

bool StringToIPEndPoint(const std::string& ip_address_and_port,
IPEndPoint* ip_end_point) {
DCHECK(ip_end_point);

std::string ip;
int port;
if (!ParseHostAndPort(ip_address_and_port, &ip, &port))
return false;
if (port == -1)
port = dns_protocol::kDefaultPort;

net::IPAddressNumber ip_number;
if (!net::ParseIPLiteralToNumber(ip, &ip_number))
return false;

*ip_end_point = net::IPEndPoint(ip_number, port);
return true;
}

// Convert DnsConfig to human readable text omitting the hosts member.
std::string DnsConfigToString(const DnsConfig& dns_config) {
std::string output;
output.append("search ");
for (size_t i = 0; i < dns_config.search.size(); ++i) {
output.append(dns_config.search[i] + " ");
}
output.append("\n");

for (size_t i = 0; i < dns_config.nameservers.size(); ++i) {
output.append("nameserver ");
output.append(dns_config.nameservers[i].ToString()).append("\n");
}

base::StringAppendF(&output, "options ndots:%d\n", dns_config.ndots);
base::StringAppendF(&output, "options timeout:%d\n",
static_cast<int>(dns_config.timeout.InMilliseconds()));
base::StringAppendF(&output, "options attempts:%d\n", dns_config.attempts);
if (dns_config.rotate)
output.append("options rotate\n");
if (dns_config.edns0)
output.append("options edns0\n");
return output;
}

// Convert DnsConfig hosts member to a human readable text.
std::string DnsHostsToString(const DnsHosts& dns_hosts) {
std::string output;
for (DnsHosts::const_iterator i = dns_hosts.begin();
i != dns_hosts.end();
++i) {
const DnsHostsKey& key = i->first;
std::string host_name = key.first;
output.append(IPEndPoint(i->second, -1).ToStringWithoutPort());
output.append(" ").append(host_name).append("\n");
}
return output;
}

class GDig {
public:
GDig();
Expand All @@ -41,6 +102,7 @@ class GDig {
RESULT_NO_CONFIG = -2,
RESULT_WRONG_USAGE = -1,
RESULT_OK = 0,
RESULT_PENDING = 1,
};

Result Main(int argc, const char* argv[]);
Expand All @@ -49,13 +111,18 @@ class GDig {
bool ParseCommandLine(int argc, const char* argv[]);

void Start();
void Finish(Result);

void OnDnsConfig(const DnsConfig& dns_config);
void OnDnsConfig(const DnsConfig& dns_config_const);
void OnResolveComplete(int val);
void OnTimeout();

base::TimeDelta timeout_;
base::TimeDelta config_timeout_;
std::string domain_name_;
bool print_config_;
bool print_hosts_;
net::IPEndPoint nameserver_;
base::TimeDelta timeout_;

Result result_;
AddressList addrlist_;
Expand All @@ -67,15 +134,19 @@ class GDig {
};

GDig::GDig()
: timeout_(base::TimeDelta::FromSeconds(5)),
result_(GDig::RESULT_OK) {
: config_timeout_(base::TimeDelta::FromSeconds(5)),
print_config_(false),
print_hosts_(false) {
}

GDig::Result GDig::Main(int argc, const char* argv[]) {
if (!ParseCommandLine(argc, argv)) {
fprintf(stderr,
"usage: %s [--net_log[=<basic|no_bytes|all>]]"
" [--config_timeout=<seconds>] domain_name\n",
" [--print_config] [--print_hosts]"
" [--nameserver=<ip_address[:port]>]"
" [--timeout=<milliseconds>] [--config_timeout=<seconds>]"
" domain_name\n",
argv[0]);
return RESULT_WRONG_USAGE;
}
Expand All @@ -88,91 +159,27 @@ GDig::Result GDig::Main(int argc, const char* argv[]) {
base::AtExitManager exit_manager;
MessageLoopForIO loop;

result_ = RESULT_PENDING;
Start();

MessageLoop::current()->Run();
if (result_ == RESULT_PENDING)
MessageLoop::current()->Run();

// Destroy it while MessageLoopForIO is alive.
dns_config_service_.reset();
return result_;
}

void GDig::OnResolveComplete(int val) {
MessageLoop::current()->Quit();
if (val != OK) {
fprintf(stderr, "Error trying to resolve hostname %s: %s\n",
domain_name_.c_str(), ErrorToString(val));
result_ = RESULT_NO_RESOLVE;
} else {
for (size_t i = 0; i < addrlist_.size(); ++i)
printf("%s\n", addrlist_[i].ToStringWithoutPort().c_str());
}
}

void GDig::OnTimeout() {
MessageLoop::current()->Quit();
fprintf(stderr, "Timed out waiting to load the dns config\n");
result_ = RESULT_NO_CONFIG;
}

void GDig::Start() {
dns_config_service_ = DnsConfigService::CreateSystemService();
dns_config_service_->Read(base::Bind(&GDig::OnDnsConfig,
base::Unretained(this)));

timeout_closure_.Reset(base::Bind(&GDig::OnTimeout, base::Unretained(this)));

MessageLoop::current()->PostDelayedTask(
FROM_HERE,
timeout_closure_.callback(),
timeout_);
}

void GDig::OnDnsConfig(const DnsConfig& dns_config) {
timeout_closure_.Cancel();
DCHECK(dns_config.IsValid());

scoped_ptr<DnsClient> dns_client(DnsClient::CreateClient(NULL));
dns_client->SetConfig(dns_config);
resolver_.reset(
new HostResolverImpl(
HostCache::CreateDefaultCache(),
PrioritizedDispatcher::Limits(NUM_PRIORITIES, 1),
HostResolverImpl::ProcTaskParams(NULL, 1),
scoped_ptr<DnsConfigService>(NULL),
dns_client.Pass(),
log_.get()));

HostResolver::RequestInfo info(HostPortPair(domain_name_.c_str(), 80));

CompletionCallback callback = base::Bind(&GDig::OnResolveComplete,
base::Unretained(this));
int ret = resolver_->Resolve(
info, &addrlist_, callback, NULL,
BoundNetLog::Make(log_.get(), net::NetLog::SOURCE_NONE));
DCHECK(ret == ERR_IO_PENDING);
}

bool GDig::ParseCommandLine(int argc, const char* argv[]) {
CommandLine::Init(argc, argv);
const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();

if (parsed_command_line.GetArgs().size() != 1)
return false;

#if defined(OS_WIN)
domain_name_ = WideToASCII(parsed_command_line.GetArgs()[0]);
#else
domain_name_ = parsed_command_line.GetArgs()[0];
#endif

if (parsed_command_line.HasSwitch("config_timeout")) {
int timeout_seconds = 0;
bool parsed = base::StringToInt(
parsed_command_line.GetSwitchValueASCII("config_timeout"),
&timeout_seconds);
if (parsed && timeout_seconds > 0) {
timeout_ = base::TimeDelta::FromSeconds(timeout_seconds);
config_timeout_ = base::TimeDelta::FromSeconds(timeout_seconds);
} else {
fprintf(stderr, "Invalid config_timeout parameter\n");
return false;
Expand All @@ -199,7 +206,134 @@ bool GDig::ParseCommandLine(int argc, const char* argv[]) {
log_.reset(new FileNetLog(stderr, level));
}

return true;
print_config_ = parsed_command_line.HasSwitch("print_config");
print_hosts_ = parsed_command_line.HasSwitch("print_hosts");

if (parsed_command_line.HasSwitch("nameserver")) {
std::string nameserver =
parsed_command_line.GetSwitchValueASCII("nameserver");
if (!StringToIPEndPoint(nameserver, &nameserver_)) {
fprintf(stderr,
"Cannot parse the namerserver string into an IPEndPoint\n");
return false;
}
}

if (parsed_command_line.HasSwitch("timeout")) {
int timeout_millis = 0;
bool parsed = base::StringToInt(
parsed_command_line.GetSwitchValueASCII("timeout"),
&timeout_millis);
if (parsed && timeout_millis > 0) {
timeout_ = base::TimeDelta::FromMilliseconds(timeout_millis);
} else {
fprintf(stderr, "Invalid timeout parameter\n");
return false;
}
}

if (parsed_command_line.GetArgs().size() == 1) {
#if defined(OS_WIN)
domain_name_ = WideToASCII(parsed_command_line.GetArgs()[0]);
#else
domain_name_ = parsed_command_line.GetArgs()[0];
#endif
} else if (parsed_command_line.GetArgs().size() != 0) {
return false;
}
return print_config_ || print_hosts_ || domain_name_.length() > 0;
}

void GDig::Start() {
if (nameserver_.address().size() > 0) {
DnsConfig dns_config;
dns_config.attempts = 1;
dns_config.nameservers.push_back(nameserver_);
OnDnsConfig(dns_config);
} else {
dns_config_service_ = DnsConfigService::CreateSystemService();
dns_config_service_->Read(base::Bind(&GDig::OnDnsConfig,
base::Unretained(this)));
timeout_closure_.Reset(base::Bind(&GDig::OnTimeout,
base::Unretained(this)));
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
timeout_closure_.callback(),
config_timeout_);
}
}

void GDig::Finish(Result result) {
DCHECK_NE(RESULT_PENDING, result);
result_ = result;
if (MessageLoop::current())
MessageLoop::current()->Quit();
}

void GDig::OnDnsConfig(const DnsConfig& dns_config_const) {
timeout_closure_.Cancel();
DCHECK(dns_config_const.IsValid());
DnsConfig dns_config = dns_config_const;

if (timeout_.InMilliseconds() > 0)
dns_config.timeout = timeout_;
if (print_config_)
printf("# Dns Configuration\n"
"%s", DnsConfigToString(dns_config).c_str());
if (print_hosts_)
printf("# Host Database\n"
"%s", DnsHostsToString(dns_config.hosts).c_str());

// If the user didn't specify a name to resolve we can stop here.
if (domain_name_.length() == 0) {
Finish(RESULT_OK);
return;
}

scoped_ptr<DnsClient> dns_client(DnsClient::CreateClient(NULL));
dns_client->SetConfig(dns_config);
resolver_.reset(
new HostResolverImpl(
HostCache::CreateDefaultCache(),
PrioritizedDispatcher::Limits(NUM_PRIORITIES, 1),
HostResolverImpl::ProcTaskParams(NULL, 1),
scoped_ptr<DnsConfigService>(NULL),
dns_client.Pass(),
log_.get()));

HostResolver::RequestInfo info(HostPortPair(domain_name_.c_str(), 80));

CompletionCallback callback = base::Bind(&GDig::OnResolveComplete,
base::Unretained(this));
int ret = resolver_->Resolve(
info, &addrlist_, callback, NULL,
BoundNetLog::Make(log_.get(), net::NetLog::SOURCE_NONE));
switch (ret) {
case OK:
OnResolveComplete(ret);
break;
case ERR_IO_PENDING: break;
default:
Finish(RESULT_NO_RESOLVE);
fprintf(stderr, "Error calling resolve %s\n", ErrorToString(ret));
}
}

void GDig::OnResolveComplete(int val) {
if (val != OK) {
fprintf(stderr, "Error trying to resolve hostname %s: %s\n",
domain_name_.c_str(), ErrorToString(val));
Finish(RESULT_NO_RESOLVE);
} else {
for (size_t i = 0; i < addrlist_.size(); ++i)
printf("%s\n", addrlist_[i].ToStringWithoutPort().c_str());
Finish(RESULT_OK);
}
}

void GDig::OnTimeout() {
fprintf(stderr, "Timed out waiting to load the dns config\n");
Finish(RESULT_NO_CONFIG);
}

} // empty namespace
Expand Down

0 comments on commit 8220e5d

Please sign in to comment.