Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / tools / gdig / gdig.cc
blobcc490cd945d2d615c0af46793a1bc98c4380d88a
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.
5 #include <stdio.h>
6 #include <string>
8 #include "base/at_exit.h"
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/cancelable_callback.h"
12 #include "base/command_line.h"
13 #include "base/files/file_util.h"
14 #include "base/location.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/thread_task_runner_handle.h"
24 #include "base/time/time.h"
25 #include "net/base/address_list.h"
26 #include "net/base/ip_endpoint.h"
27 #include "net/base/net_errors.h"
28 #include "net/base/net_util.h"
29 #include "net/dns/dns_client.h"
30 #include "net/dns/dns_config_service.h"
31 #include "net/dns/dns_protocol.h"
32 #include "net/dns/host_cache.h"
33 #include "net/dns/host_resolver_impl.h"
34 #include "net/log/net_log.h"
35 #include "net/tools/gdig/file_net_log.h"
37 #if defined(OS_MACOSX)
38 #include "base/mac/scoped_nsautorelease_pool.h"
39 #endif
41 namespace net {
43 namespace {
45 bool StringToIPEndPoint(const std::string& ip_address_and_port,
46 IPEndPoint* ip_end_point) {
47 DCHECK(ip_end_point);
49 std::string ip;
50 int port;
51 if (!ParseHostAndPort(ip_address_and_port, &ip, &port))
52 return false;
53 if (port == -1)
54 port = dns_protocol::kDefaultPort;
56 net::IPAddressNumber ip_number;
57 if (!net::ParseIPLiteralToNumber(ip, &ip_number))
58 return false;
60 *ip_end_point = net::IPEndPoint(ip_number, static_cast<uint16>(port));
61 return true;
64 // Convert DnsConfig to human readable text omitting the hosts member.
65 std::string DnsConfigToString(const DnsConfig& dns_config) {
66 std::string output;
67 output.append("search ");
68 for (size_t i = 0; i < dns_config.search.size(); ++i) {
69 output.append(dns_config.search[i] + " ");
71 output.append("\n");
73 for (size_t i = 0; i < dns_config.nameservers.size(); ++i) {
74 output.append("nameserver ");
75 output.append(dns_config.nameservers[i].ToString()).append("\n");
78 base::StringAppendF(&output, "options ndots:%d\n", dns_config.ndots);
79 base::StringAppendF(&output, "options timeout:%d\n",
80 static_cast<int>(dns_config.timeout.InMilliseconds()));
81 base::StringAppendF(&output, "options attempts:%d\n", dns_config.attempts);
82 if (dns_config.rotate)
83 output.append("options rotate\n");
84 if (dns_config.edns0)
85 output.append("options edns0\n");
86 return output;
89 // Convert DnsConfig hosts member to a human readable text.
90 std::string DnsHostsToString(const DnsHosts& dns_hosts) {
91 std::string output;
92 for (DnsHosts::const_iterator i = dns_hosts.begin();
93 i != dns_hosts.end();
94 ++i) {
95 const DnsHostsKey& key = i->first;
96 std::string host_name = key.first;
97 output.append(IPEndPoint(i->second, 0).ToStringWithoutPort());
98 output.append(" ").append(host_name).append("\n");
100 return output;
103 struct ReplayLogEntry {
104 base::TimeDelta start_time;
105 std::string domain_name;
108 typedef std::vector<ReplayLogEntry> ReplayLog;
110 // Loads and parses a replay log file and fills |replay_log| with a structured
111 // representation. Returns whether the operation was successful. If not, the
112 // contents of |replay_log| are undefined.
114 // The replay log is a text file where each line contains
116 // timestamp_in_milliseconds domain_name
118 // The timestamp_in_milliseconds needs to be an integral delta from start of
119 // resolution and is in milliseconds. domain_name is the name to be resolved.
121 // The file should be sorted by timestamp in ascending time.
122 bool LoadReplayLog(const base::FilePath& file_path, ReplayLog* replay_log) {
123 std::string original_replay_log_contents;
124 if (!base::ReadFileToString(file_path, &original_replay_log_contents)) {
125 fprintf(stderr, "Unable to open replay file %s\n",
126 file_path.MaybeAsASCII().c_str());
127 return false;
130 // Strip out \r characters for Windows files. This isn't as efficient as a
131 // smarter line splitter, but this particular use does not need to target
132 // efficiency.
133 std::string replay_log_contents;
134 base::RemoveChars(original_replay_log_contents, "\r", &replay_log_contents);
136 std::vector<std::string> lines = base::SplitString(
137 replay_log_contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
138 base::TimeDelta previous_delta;
139 bool bad_parse = false;
140 for (unsigned i = 0; i < lines.size(); ++i) {
141 if (lines[i].empty())
142 continue;
143 std::vector<std::string> time_and_name = base::SplitString(
144 lines[i], " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
145 if (time_and_name.size() != 2) {
146 fprintf(
147 stderr,
148 "[%s %u] replay log should have format 'timestamp domain_name\\n'\n",
149 file_path.MaybeAsASCII().c_str(),
150 i + 1);
151 bad_parse = true;
152 continue;
155 int64 delta_in_milliseconds;
156 if (!base::StringToInt64(time_and_name[0], &delta_in_milliseconds)) {
157 fprintf(
158 stderr,
159 "[%s %u] replay log should have format 'timestamp domain_name\\n'\n",
160 file_path.MaybeAsASCII().c_str(),
161 i + 1);
162 bad_parse = true;
163 continue;
166 base::TimeDelta delta =
167 base::TimeDelta::FromMilliseconds(delta_in_milliseconds);
168 if (delta < previous_delta) {
169 fprintf(
170 stderr,
171 "[%s %u] replay log should be sorted by time\n",
172 file_path.MaybeAsASCII().c_str(),
173 i + 1);
174 bad_parse = true;
175 continue;
178 previous_delta = delta;
179 ReplayLogEntry entry;
180 entry.start_time = delta;
181 entry.domain_name = time_and_name[1];
182 replay_log->push_back(entry);
184 return !bad_parse;
187 class GDig {
188 public:
189 GDig();
190 ~GDig();
192 enum Result {
193 RESULT_NO_RESOLVE = -3,
194 RESULT_NO_CONFIG = -2,
195 RESULT_WRONG_USAGE = -1,
196 RESULT_OK = 0,
197 RESULT_PENDING = 1,
200 Result Main(int argc, const char* argv[]);
202 private:
203 bool ParseCommandLine(int argc, const char* argv[]);
205 void Start();
206 void Finish(Result);
208 void OnDnsConfig(const DnsConfig& dns_config_const);
209 void OnResolveComplete(unsigned index, AddressList* address_list,
210 base::TimeDelta time_since_start, int val);
211 void OnTimeout();
212 void ReplayNextEntry();
214 base::TimeDelta config_timeout_;
215 bool print_config_;
216 bool print_hosts_;
217 net::IPEndPoint nameserver_;
218 base::TimeDelta timeout_;
219 int parallellism_;
220 ReplayLog replay_log_;
221 unsigned replay_log_index_;
222 base::Time start_time_;
223 int active_resolves_;
224 Result result_;
226 base::CancelableClosure timeout_closure_;
227 scoped_ptr<DnsConfigService> dns_config_service_;
228 scoped_ptr<FileNetLogObserver> log_observer_;
229 scoped_ptr<NetLog> log_;
230 scoped_ptr<HostResolver> resolver_;
232 #if defined(OS_MACOSX)
233 // Without this there will be a mem leak on osx.
234 base::mac::ScopedNSAutoreleasePool scoped_pool_;
235 #endif
237 // Need AtExitManager to support AsWeakPtr (in NetLog).
238 base::AtExitManager exit_manager_;
241 GDig::GDig()
242 : config_timeout_(base::TimeDelta::FromSeconds(5)),
243 print_config_(false),
244 print_hosts_(false),
245 parallellism_(6),
246 replay_log_index_(0u),
247 active_resolves_(0) {
250 GDig::~GDig() {
251 if (log_)
252 log_->DeprecatedRemoveObserver(log_observer_.get());
255 GDig::Result GDig::Main(int argc, const char* argv[]) {
256 if (!ParseCommandLine(argc, argv)) {
257 fprintf(stderr,
258 "usage: %s [--net_log[=<basic|no_bytes|all>]]"
259 " [--print_config] [--print_hosts]"
260 " [--nameserver=<ip_address[:port]>]"
261 " [--timeout=<milliseconds>]"
262 " [--config_timeout=<seconds>]"
263 " [--j=<parallel resolves>]"
264 " [--replay_file=<path>]"
265 " [domain_name]\n",
266 argv[0]);
267 return RESULT_WRONG_USAGE;
270 base::MessageLoopForIO loop;
272 result_ = RESULT_PENDING;
273 Start();
274 if (result_ == RESULT_PENDING)
275 base::MessageLoop::current()->Run();
277 // Destroy it while MessageLoopForIO is alive.
278 dns_config_service_.reset();
279 return result_;
282 bool GDig::ParseCommandLine(int argc, const char* argv[]) {
283 base::CommandLine::Init(argc, argv);
284 const base::CommandLine& parsed_command_line =
285 *base::CommandLine::ForCurrentProcess();
287 if (parsed_command_line.HasSwitch("config_timeout")) {
288 int timeout_seconds = 0;
289 bool parsed = base::StringToInt(
290 parsed_command_line.GetSwitchValueASCII("config_timeout"),
291 &timeout_seconds);
292 if (parsed && timeout_seconds > 0) {
293 config_timeout_ = base::TimeDelta::FromSeconds(timeout_seconds);
294 } else {
295 fprintf(stderr, "Invalid config_timeout parameter\n");
296 return false;
300 if (parsed_command_line.HasSwitch("net_log")) {
301 std::string log_param = parsed_command_line.GetSwitchValueASCII("net_log");
302 NetLogCaptureMode capture_mode =
303 NetLogCaptureMode::IncludeCookiesAndCredentials();
305 if (log_param.length() > 0) {
306 std::map<std::string, NetLogCaptureMode> capture_modes;
307 capture_modes["all"] = NetLogCaptureMode::IncludeSocketBytes();
308 capture_modes["no_bytes"] =
309 NetLogCaptureMode::IncludeCookiesAndCredentials();
311 if (capture_modes.find(log_param) != capture_modes.end()) {
312 capture_mode = capture_modes[log_param];
313 } else {
314 fprintf(stderr, "Invalid net_log parameter\n");
315 return false;
318 log_.reset(new NetLog);
319 log_observer_.reset(new FileNetLogObserver(stderr));
320 log_->DeprecatedAddObserver(log_observer_.get(), capture_mode);
323 print_config_ = parsed_command_line.HasSwitch("print_config");
324 print_hosts_ = parsed_command_line.HasSwitch("print_hosts");
326 if (parsed_command_line.HasSwitch("nameserver")) {
327 std::string nameserver =
328 parsed_command_line.GetSwitchValueASCII("nameserver");
329 if (!StringToIPEndPoint(nameserver, &nameserver_)) {
330 fprintf(stderr,
331 "Cannot parse the namerserver string into an IPEndPoint\n");
332 return false;
336 if (parsed_command_line.HasSwitch("timeout")) {
337 int timeout_millis = 0;
338 bool parsed = base::StringToInt(
339 parsed_command_line.GetSwitchValueASCII("timeout"),
340 &timeout_millis);
341 if (parsed && timeout_millis > 0) {
342 timeout_ = base::TimeDelta::FromMilliseconds(timeout_millis);
343 } else {
344 fprintf(stderr, "Invalid timeout parameter\n");
345 return false;
349 if (parsed_command_line.HasSwitch("replay_file")) {
350 base::FilePath replay_path =
351 parsed_command_line.GetSwitchValuePath("replay_file");
352 if (!LoadReplayLog(replay_path, &replay_log_))
353 return false;
356 if (parsed_command_line.HasSwitch("j")) {
357 int parallellism = 0;
358 bool parsed = base::StringToInt(
359 parsed_command_line.GetSwitchValueASCII("j"),
360 &parallellism);
361 if (parsed && parallellism > 0) {
362 parallellism_ = parallellism;
363 } else {
364 fprintf(stderr, "Invalid parallellism parameter\n");
368 if (parsed_command_line.GetArgs().size() == 1) {
369 ReplayLogEntry entry;
370 entry.start_time = base::TimeDelta();
371 #if defined(OS_WIN)
372 entry.domain_name = base::UTF16ToASCII(parsed_command_line.GetArgs()[0]);
373 #else
374 entry.domain_name = parsed_command_line.GetArgs()[0];
375 #endif
376 replay_log_.push_back(entry);
377 } else if (parsed_command_line.GetArgs().size() != 0) {
378 return false;
380 return print_config_ || print_hosts_ || !replay_log_.empty();
383 void GDig::Start() {
384 if (nameserver_.address().size() > 0) {
385 DnsConfig dns_config;
386 dns_config.attempts = 1;
387 dns_config.nameservers.push_back(nameserver_);
388 OnDnsConfig(dns_config);
389 } else {
390 dns_config_service_ = DnsConfigService::CreateSystemService();
391 dns_config_service_->ReadConfig(base::Bind(&GDig::OnDnsConfig,
392 base::Unretained(this)));
393 timeout_closure_.Reset(base::Bind(&GDig::OnTimeout,
394 base::Unretained(this)));
395 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
396 FROM_HERE, timeout_closure_.callback(), config_timeout_);
400 void GDig::Finish(Result result) {
401 DCHECK_NE(RESULT_PENDING, result);
402 result_ = result;
403 if (base::MessageLoop::current())
404 base::MessageLoop::current()->Quit();
407 void GDig::OnDnsConfig(const DnsConfig& dns_config_const) {
408 timeout_closure_.Cancel();
409 DCHECK(dns_config_const.IsValid());
410 DnsConfig dns_config = dns_config_const;
412 if (timeout_.InMilliseconds() > 0)
413 dns_config.timeout = timeout_;
414 if (print_config_) {
415 printf("# Dns Configuration\n"
416 "%s", DnsConfigToString(dns_config).c_str());
418 if (print_hosts_) {
419 printf("# Host Database\n"
420 "%s", DnsHostsToString(dns_config.hosts).c_str());
423 if (replay_log_.empty()) {
424 Finish(RESULT_OK);
425 return;
428 scoped_ptr<DnsClient> dns_client(DnsClient::CreateClient(NULL));
429 dns_client->SetConfig(dns_config);
430 HostResolver::Options options;
431 options.max_concurrent_resolves = parallellism_;
432 options.max_retry_attempts = 1u;
433 scoped_ptr<HostResolverImpl> resolver(
434 new HostResolverImpl(options, log_.get()));
435 resolver->SetDnsClient(dns_client.Pass());
436 resolver_ = resolver.Pass();
438 start_time_ = base::Time::Now();
440 ReplayNextEntry();
443 void GDig::ReplayNextEntry() {
444 DCHECK_LT(replay_log_index_, replay_log_.size());
446 base::TimeDelta time_since_start = base::Time::Now() - start_time_;
447 while (replay_log_index_ < replay_log_.size()) {
448 const ReplayLogEntry& entry = replay_log_[replay_log_index_];
449 if (time_since_start < entry.start_time) {
450 // Delay call to next time and return.
451 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
452 FROM_HERE, base::Bind(&GDig::ReplayNextEntry, base::Unretained(this)),
453 entry.start_time - time_since_start);
454 return;
457 HostResolver::RequestInfo info(HostPortPair(entry.domain_name.c_str(), 80));
458 AddressList* addrlist = new AddressList();
459 unsigned current_index = replay_log_index_;
460 CompletionCallback callback = base::Bind(&GDig::OnResolveComplete,
461 base::Unretained(this),
462 current_index,
463 base::Owned(addrlist),
464 time_since_start);
465 ++active_resolves_;
466 ++replay_log_index_;
467 int ret = resolver_->Resolve(
468 info,
469 DEFAULT_PRIORITY,
470 addrlist,
471 callback,
472 NULL,
473 BoundNetLog::Make(log_.get(), net::NetLog::SOURCE_NONE));
474 if (ret != ERR_IO_PENDING)
475 callback.Run(ret);
479 void GDig::OnResolveComplete(unsigned entry_index,
480 AddressList* address_list,
481 base::TimeDelta resolve_start_time,
482 int val) {
483 DCHECK_GT(active_resolves_, 0);
484 DCHECK(address_list);
485 DCHECK_LT(entry_index, replay_log_.size());
486 --active_resolves_;
487 base::TimeDelta resolve_end_time = base::Time::Now() - start_time_;
488 base::TimeDelta resolve_time = resolve_end_time - resolve_start_time;
489 printf("%u %d %d %s %d ",
490 entry_index,
491 static_cast<int>(resolve_end_time.InMilliseconds()),
492 static_cast<int>(resolve_time.InMilliseconds()),
493 replay_log_[entry_index].domain_name.c_str(), val);
494 if (val != OK) {
495 std::string error_string = ErrorToString(val);
496 printf("%s", error_string.c_str());
497 } else {
498 for (size_t i = 0; i < address_list->size(); ++i) {
499 if (i != 0)
500 printf(" ");
501 printf("%s", (*address_list)[i].ToStringWithoutPort().c_str());
504 printf("\n");
505 if (active_resolves_ == 0 && replay_log_index_ >= replay_log_.size())
506 Finish(RESULT_OK);
509 void GDig::OnTimeout() {
510 fprintf(stderr, "Timed out waiting to load the dns config\n");
511 Finish(RESULT_NO_CONFIG);
514 } // empty namespace
516 } // namespace net
518 int main(int argc, const char* argv[]) {
519 net::GDig dig;
520 return dig.Main(argc, argv);