1 // Copyright (c) 2012 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.
8 #include "base/at_exit.h"
10 #include "base/cancelable_callback.h"
11 #include "base/command_line.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop.h"
14 #include "base/string_number_conversions.h"
15 #include "base/string_util.h"
16 #include "base/stringprintf.h"
17 #include "base/time.h"
18 #include "net/base/address_list.h"
19 #include "net/base/host_cache.h"
20 #include "net/base/host_resolver_impl.h"
21 #include "net/base/ip_endpoint.h"
22 #include "net/base/net_errors.h"
23 #include "net/base/net_log.h"
24 #include "net/base/net_util.h"
25 #include "net/dns/dns_client.h"
26 #include "net/dns/dns_config_service.h"
27 #include "net/dns/dns_protocol.h"
28 #include "net/tools/gdig/file_net_log.h"
30 #if defined(OS_MACOSX)
31 #include "base/mac/scoped_nsautorelease_pool.h"
38 bool StringToIPEndPoint(const std::string
& ip_address_and_port
,
39 IPEndPoint
* ip_end_point
) {
44 if (!ParseHostAndPort(ip_address_and_port
, &ip
, &port
))
47 port
= dns_protocol::kDefaultPort
;
49 net::IPAddressNumber ip_number
;
50 if (!net::ParseIPLiteralToNumber(ip
, &ip_number
))
53 *ip_end_point
= net::IPEndPoint(ip_number
, port
);
57 // Convert DnsConfig to human readable text omitting the hosts member.
58 std::string
DnsConfigToString(const DnsConfig
& dns_config
) {
60 output
.append("search ");
61 for (size_t i
= 0; i
< dns_config
.search
.size(); ++i
) {
62 output
.append(dns_config
.search
[i
] + " ");
66 for (size_t i
= 0; i
< dns_config
.nameservers
.size(); ++i
) {
67 output
.append("nameserver ");
68 output
.append(dns_config
.nameservers
[i
].ToString()).append("\n");
71 base::StringAppendF(&output
, "options ndots:%d\n", dns_config
.ndots
);
72 base::StringAppendF(&output
, "options timeout:%d\n",
73 static_cast<int>(dns_config
.timeout
.InMilliseconds()));
74 base::StringAppendF(&output
, "options attempts:%d\n", dns_config
.attempts
);
75 if (dns_config
.rotate
)
76 output
.append("options rotate\n");
78 output
.append("options edns0\n");
82 // Convert DnsConfig hosts member to a human readable text.
83 std::string
DnsHostsToString(const DnsHosts
& dns_hosts
) {
85 for (DnsHosts::const_iterator i
= dns_hosts
.begin();
88 const DnsHostsKey
& key
= i
->first
;
89 std::string host_name
= key
.first
;
90 output
.append(IPEndPoint(i
->second
, -1).ToStringWithoutPort());
91 output
.append(" ").append(host_name
).append("\n");
101 RESULT_NO_RESOLVE
= -3,
102 RESULT_NO_CONFIG
= -2,
103 RESULT_WRONG_USAGE
= -1,
108 Result
Main(int argc
, const char* argv
[]);
111 bool ParseCommandLine(int argc
, const char* argv
[]);
116 void OnDnsConfig(const DnsConfig
& dns_config_const
);
117 void OnResolveComplete(int val
);
120 base::TimeDelta config_timeout_
;
121 std::string domain_name_
;
124 net::IPEndPoint nameserver_
;
125 base::TimeDelta timeout_
;
128 AddressList addrlist_
;
130 base::CancelableClosure timeout_closure_
;
131 scoped_ptr
<DnsConfigService
> dns_config_service_
;
132 scoped_ptr
<FileNetLog
> log_
;
133 scoped_ptr
<HostResolver
> resolver_
;
137 : config_timeout_(base::TimeDelta::FromSeconds(5)),
138 print_config_(false),
139 print_hosts_(false) {
142 GDig::Result
GDig::Main(int argc
, const char* argv
[]) {
143 if (!ParseCommandLine(argc
, argv
)) {
145 "usage: %s [--net_log[=<basic|no_bytes|all>]]"
146 " [--print_config] [--print_hosts]"
147 " [--nameserver=<ip_address[:port]>]"
148 " [--timeout=<milliseconds>] [--config_timeout=<seconds>]"
151 return RESULT_WRONG_USAGE
;
154 #if defined(OS_MACOSX)
155 // Without this there will be a mem leak on osx.
156 base::mac::ScopedNSAutoreleasePool scoped_pool
;
159 base::AtExitManager exit_manager
;
160 MessageLoopForIO loop
;
162 result_
= RESULT_PENDING
;
164 if (result_
== RESULT_PENDING
)
165 MessageLoop::current()->Run();
167 // Destroy it while MessageLoopForIO is alive.
168 dns_config_service_
.reset();
172 bool GDig::ParseCommandLine(int argc
, const char* argv
[]) {
173 CommandLine::Init(argc
, argv
);
174 const CommandLine
& parsed_command_line
= *CommandLine::ForCurrentProcess();
176 if (parsed_command_line
.HasSwitch("config_timeout")) {
177 int timeout_seconds
= 0;
178 bool parsed
= base::StringToInt(
179 parsed_command_line
.GetSwitchValueASCII("config_timeout"),
181 if (parsed
&& timeout_seconds
> 0) {
182 config_timeout_
= base::TimeDelta::FromSeconds(timeout_seconds
);
184 fprintf(stderr
, "Invalid config_timeout parameter\n");
189 if (parsed_command_line
.HasSwitch("net_log")) {
190 std::string log_param
= parsed_command_line
.GetSwitchValueASCII("net_log");
191 NetLog::LogLevel level
= NetLog::LOG_ALL_BUT_BYTES
;
193 if (log_param
.length() > 0) {
194 std::map
<std::string
, NetLog::LogLevel
> log_levels
;
195 log_levels
["all"] = NetLog::LOG_ALL
;
196 log_levels
["no_bytes"] = NetLog::LOG_ALL_BUT_BYTES
;
197 log_levels
["basic"] = NetLog::LOG_BASIC
;
199 if (log_levels
.find(log_param
) != log_levels
.end()) {
200 level
= log_levels
[log_param
];
202 fprintf(stderr
, "Invalid net_log parameter\n");
206 log_
.reset(new FileNetLog(stderr
, level
));
209 print_config_
= parsed_command_line
.HasSwitch("print_config");
210 print_hosts_
= parsed_command_line
.HasSwitch("print_hosts");
212 if (parsed_command_line
.HasSwitch("nameserver")) {
213 std::string nameserver
=
214 parsed_command_line
.GetSwitchValueASCII("nameserver");
215 if (!StringToIPEndPoint(nameserver
, &nameserver_
)) {
217 "Cannot parse the namerserver string into an IPEndPoint\n");
222 if (parsed_command_line
.HasSwitch("timeout")) {
223 int timeout_millis
= 0;
224 bool parsed
= base::StringToInt(
225 parsed_command_line
.GetSwitchValueASCII("timeout"),
227 if (parsed
&& timeout_millis
> 0) {
228 timeout_
= base::TimeDelta::FromMilliseconds(timeout_millis
);
230 fprintf(stderr
, "Invalid timeout parameter\n");
235 if (parsed_command_line
.GetArgs().size() == 1) {
237 domain_name_
= WideToASCII(parsed_command_line
.GetArgs()[0]);
239 domain_name_
= parsed_command_line
.GetArgs()[0];
241 } else if (parsed_command_line
.GetArgs().size() != 0) {
244 return print_config_
|| print_hosts_
|| domain_name_
.length() > 0;
248 if (nameserver_
.address().size() > 0) {
249 DnsConfig dns_config
;
250 dns_config
.attempts
= 1;
251 dns_config
.nameservers
.push_back(nameserver_
);
252 OnDnsConfig(dns_config
);
254 dns_config_service_
= DnsConfigService::CreateSystemService();
255 dns_config_service_
->ReadConfig(base::Bind(&GDig::OnDnsConfig
,
256 base::Unretained(this)));
257 timeout_closure_
.Reset(base::Bind(&GDig::OnTimeout
,
258 base::Unretained(this)));
259 MessageLoop::current()->PostDelayedTask(
261 timeout_closure_
.callback(),
266 void GDig::Finish(Result result
) {
267 DCHECK_NE(RESULT_PENDING
, result
);
269 if (MessageLoop::current())
270 MessageLoop::current()->Quit();
273 void GDig::OnDnsConfig(const DnsConfig
& dns_config_const
) {
274 timeout_closure_
.Cancel();
275 DCHECK(dns_config_const
.IsValid());
276 DnsConfig dns_config
= dns_config_const
;
278 if (timeout_
.InMilliseconds() > 0)
279 dns_config
.timeout
= timeout_
;
281 printf("# Dns Configuration\n"
282 "%s", DnsConfigToString(dns_config
).c_str());
285 printf("# Host Database\n"
286 "%s", DnsHostsToString(dns_config
.hosts
).c_str());
289 // If the user didn't specify a name to resolve we can stop here.
290 if (domain_name_
.length() == 0) {
295 scoped_ptr
<DnsClient
> dns_client(DnsClient::CreateClient(NULL
));
296 dns_client
->SetConfig(dns_config
);
297 scoped_ptr
<HostResolverImpl
> resolver(
298 new HostResolverImpl(
299 HostCache::CreateDefaultCache(),
300 PrioritizedDispatcher::Limits(NUM_PRIORITIES
, 1),
301 HostResolverImpl::ProcTaskParams(NULL
, 1),
303 resolver
->SetDnsClient(dns_client
.Pass());
304 resolver_
= resolver
.Pass();
306 HostResolver::RequestInfo
info(HostPortPair(domain_name_
.c_str(), 80));
308 CompletionCallback callback
= base::Bind(&GDig::OnResolveComplete
,
309 base::Unretained(this));
310 int ret
= resolver_
->Resolve(
311 info
, &addrlist_
, callback
, NULL
,
312 BoundNetLog::Make(log_
.get(), net::NetLog::SOURCE_NONE
));
315 OnResolveComplete(ret
);
317 case ERR_IO_PENDING
: break;
319 Finish(RESULT_NO_RESOLVE
);
320 fprintf(stderr
, "Error calling resolve %s\n", ErrorToString(ret
));
324 void GDig::OnResolveComplete(int val
) {
326 fprintf(stderr
, "Error trying to resolve hostname %s: %s\n",
327 domain_name_
.c_str(), ErrorToString(val
));
328 Finish(RESULT_NO_RESOLVE
);
330 for (size_t i
= 0; i
< addrlist_
.size(); ++i
)
331 printf("%s\n", addrlist_
[i
].ToStringWithoutPort().c_str());
336 void GDig::OnTimeout() {
337 fprintf(stderr
, "Timed out waiting to load the dns config\n");
338 Finish(RESULT_NO_CONFIG
);
345 int main(int argc
, const char* argv
[]) {
347 return dig
.Main(argc
, argv
);