summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-10 02:38:03 +0000
committereroman@chromium.org <eroman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-10 02:38:03 +0000
commitc402da4e582bb1b5133270a2077104e752fc9704 (patch)
treeb25691fad942949f281500f6d98a7d4c4e1478c1
parent07ec104f30de62ac6114636eddb231df59021d34 (diff)
downloadchromium_src-c402da4e582bb1b5133270a2077104e752fc9704.zip
chromium_src-c402da4e582bb1b5133270a2077104e752fc9704.tar.gz
chromium_src-c402da4e582bb1b5133270a2077104e752fc9704.tar.bz2
Original patch by cbentzel@google.com
Original code review: http://codereview.chromium.org/428004 Command line utility to run the HostResolver. The user provides a list of hosts to resolve, as well as optional timestamps for when the resolutions should happen. These can be provided on the command line as additional arguments or in a specified file. Options: --async: Specifies that the resolution should happen asynchronously. --verbose: Spew out extra data. --cachesize: Size of the DNS cache. --cachettl: TTL of DNS cache entries in milliseconds. --inputpath: File containing host name and optional timestamp, one per line. BUG=NONE TEST=Built and ran on Linux and Windows. Built on OSX via trybots but not run. R=eroman Review URL: http://codereview.chromium.org/485006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@34225 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--net/net.gyp11
-rw-r--r--net/tools/hresolv/hresolv.cc456
2 files changed, 467 insertions, 0 deletions
diff --git a/net/net.gyp b/net/net.gyp
index b0672cf..4f50133 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -849,6 +849,17 @@
'tools/fetch/http_session.h',
],
},
+ {
+ 'target_name': 'hresolv',
+ 'type': 'executable',
+ 'dependencies': [
+ 'net_base',
+ ],
+ 'msvs_guid': 'FF1BAC48-D326-4CB4-96DA-8B03DE23ED6E',
+ 'sources': [
+ 'tools/hresolv/hresolv.cc',
+ ],
+ },
],
'conditions': [
['OS=="win"', {
diff --git a/net/tools/hresolv/hresolv.cc b/net/tools/hresolv/hresolv.cc
new file mode 100644
index 0000000..18a98ff
--- /dev/null
+++ b/net/tools/hresolv/hresolv.cc
@@ -0,0 +1,456 @@
+// Copyright (c) 2009 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.
+
+// hrseolv is a command line utility which runs the HostResolver in the
+// Chromium network stack.
+//
+// The user specifies the hosts to lookup and when to look them up.
+// The hosts must be specified in order.
+// The hosts can be contained in a file or on the command line. If no
+// time is specified, the resolv is assumed to be the same time as the
+// previous host - which is an offset of 0 for the very first host.
+//
+// The user can also control whether the lookups happen asynchronously
+// or synchronously by specifying --async on the command line.
+//
+// Future ideas:
+// Specify whether the lookup is speculative.
+// Interleave synchronous and asynchronous lookups.
+// Specify the address family.
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <ws2tcpip.h>
+#else
+#include <netdb.h>
+#endif
+#include <stdio.h>
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/condition_variable.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "base/time.h"
+#include "base/waitable_event.h"
+#include "net/base/address_list.h"
+#include "net/base/completion_callback.h"
+#include "net/base/host_resolver_impl.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+
+struct FlagName {
+ int flag;
+ const char* name;
+};
+
+static const FlagName kAddrinfoFlagNames[] = {
+ {AI_PASSIVE, "AI_PASSIVE"},
+ {AI_CANONNAME, "AI_CANONNAME"},
+ {AI_NUMERICHOST, "AI_NUMERICHOST"},
+ {AI_V4MAPPED, "AI_V4MAPPED"},
+ {AI_ALL, "AI_ALL"},
+ {AI_ADDRCONFIG, "AI_ADDRCONFIG"},
+#if defined(OS_LINUX) || defined(OS_WIN)
+ {AI_NUMERICSERV, "AI_NUMERICSERV"},
+#endif
+};
+
+std::string FormatAddrinfoFlags(int ai_flags) {
+ std::string flag_names;
+ for (unsigned int i = 0; i < arraysize(kAddrinfoFlagNames); ++i) {
+ const FlagName& flag_name = kAddrinfoFlagNames[i];
+ if (ai_flags & flag_name.flag) {
+ ai_flags &= ~flag_name.flag;
+ if (!flag_names.empty()) {
+ flag_names += "|";
+ }
+ flag_names += flag_name.name;
+ }
+ }
+ if (ai_flags) {
+ if (!flag_names.empty()) {
+ flag_names += "|";
+ }
+ flag_names += StringPrintf("0x%x", ai_flags);
+ }
+ return flag_names;
+}
+
+const char* GetNameOfFlag(const FlagName* flag_names,
+ unsigned int num_flag_names,
+ int flag) {
+ for (unsigned int i = 0; i < num_flag_names; ++i) {
+ const FlagName& flag_name = flag_names[i];
+ if (flag_name.flag == flag) {
+ return flag_name.name;
+ }
+ }
+ return "UNKNOWN";
+}
+
+static const FlagName kFamilyFlagNames[] = {
+ {AF_UNSPEC, "AF_UNSPEC"},
+ {AF_INET, "AF_INET"},
+ {AF_INET6, "AF_INET6"},
+};
+
+const char* FormatAddrinfoFamily(int ai_family) {
+ return GetNameOfFlag(kFamilyFlagNames,
+ arraysize(kFamilyFlagNames),
+ ai_family);
+}
+
+static const FlagName kSocktypeFlagNames[] = {
+ {SOCK_STREAM, "SOCK_STREAM"},
+ {SOCK_DGRAM, "SOCK_DGRAM"},
+ {SOCK_RAW, "SOCK_RAW"},
+};
+
+const char* FormatAddrinfoSocktype(int ai_socktype) {
+ return GetNameOfFlag(kSocktypeFlagNames,
+ arraysize(kSocktypeFlagNames),
+ ai_socktype);
+}
+
+static const FlagName kProtocolFlagNames[] = {
+ {IPPROTO_TCP, "IPPROTO_TCP"},
+ {IPPROTO_UDP, "IPPROTO_UDP"},
+};
+
+const char* FormatAddrinfoProtocol(int ai_protocol) {
+ return GetNameOfFlag(kProtocolFlagNames,
+ arraysize(kProtocolFlagNames),
+ ai_protocol);
+}
+
+std::string FormatAddrinfoDetails(const struct addrinfo& ai,
+ const char* indent) {
+ std::string ai_flags = FormatAddrinfoFlags(ai.ai_flags);
+ const char* ai_family = FormatAddrinfoFamily(ai.ai_family);
+ const char* ai_socktype = FormatAddrinfoSocktype(ai.ai_socktype);
+ const char* ai_protocol = FormatAddrinfoProtocol(ai.ai_protocol);
+ std::string ai_addr = net::NetAddressToString(&ai);
+ std::string ai_canonname;
+ if (ai.ai_canonname) {
+ ai_canonname = StringPrintf("%s ai_canonname: %s\n",
+ indent,
+ ai.ai_canonname);
+ }
+ return StringPrintf("%saddrinfo {\n"
+ "%s ai_flags: %s\n"
+ "%s ai_family: %s\n"
+ "%s ai_socktype: %s\n"
+ "%s ai_protocol: %s\n"
+ "%s ai_addrlen: %d\n"
+ "%s ai_addr: %s\n"
+ "%s"
+ "%s}\n",
+ indent,
+ indent, ai_flags.c_str(),
+ indent, ai_family,
+ indent, ai_socktype,
+ indent, ai_protocol,
+ indent, ai.ai_addrlen,
+ indent, ai_addr.c_str(),
+ ai_canonname.c_str(),
+ indent);
+}
+
+std::string FormatAddressList(const net::AddressList& address_list,
+ const std::string& host) {
+ std::string ret_string;
+ StringAppendF(&ret_string, "AddressList {\n");
+ StringAppendF(&ret_string, " Host: %s\n", host.c_str());
+ for (const struct addrinfo* it = address_list.head();
+ it != NULL;
+ it = it->ai_next) {
+ StringAppendF(&ret_string, "%s", FormatAddrinfoDetails(*it, " ").c_str());
+ }
+ StringAppendF(&ret_string, "}\n");
+ return ret_string;
+}
+
+class ResolverInvoker;
+
+// DelayedResolve contains state for a DNS resolution to be performed later.
+class DelayedResolve : public base::RefCounted<DelayedResolve> {
+ public:
+ DelayedResolve(const std::string& host, bool is_async,
+ net::HostResolver* resolver,
+ ResolverInvoker* invoker)
+ : host_(host),
+ address_list_(),
+ is_async_(is_async),
+ resolver_(resolver),
+ invoker_(invoker),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ io_callback_(this, &DelayedResolve::OnResolveComplete)) {
+ }
+
+ void Start() {
+ net::CompletionCallback* callback = (is_async_) ? &io_callback_ : NULL;
+ net::HostResolver::RequestInfo request_info(host_, 80);
+ int rv = resolver_->Resolve(request_info,
+ &address_list_,
+ callback,
+ NULL,
+ NULL);
+ if (rv != net::ERR_IO_PENDING) {
+ OnResolveComplete(rv);
+ }
+ }
+
+ private:
+
+ // Without this, VC++ complains about the private destructor below.
+ friend class base::RefCounted<DelayedResolve>;
+
+ // The destructor is called by Release.
+ ~DelayedResolve() {}
+
+ void OnResolveComplete(int result);
+
+ std::string host_;
+ net::AddressList address_list_;
+ bool is_async_;
+ scoped_refptr<net::HostResolver> resolver_;
+ ResolverInvoker* invoker_;
+ net::CompletionCallbackImpl<DelayedResolve> io_callback_;
+};
+
+
+struct HostAndTime {
+ // The host to resolve, i.e. www.google.com
+ std::string host;
+ // Time since the start of this program to actually kick off the resolution.
+ int delta_in_milliseconds;
+};
+
+// Invokes a sequence of host resolutions at specified times.
+class ResolverInvoker {
+ public:
+ explicit ResolverInvoker(net::HostResolver* resolver)
+ : message_loop_(MessageLoop::TYPE_DEFAULT),
+ resolver_(resolver),
+ remaining_requests_(0) {
+ }
+
+ ~ResolverInvoker() {
+ }
+
+ // Resolves all specified hosts in the order provided. hosts_and_times is
+ // assumed to be ordered by the delta_in_milliseconds field. There is no
+ // guarantee that the resolutions will complete in the order specified when
+ // async is true. There is no guarantee that the DNS queries will be issued
+ // at exactly the time specified by delta_in_milliseconds, but they are
+ // guaranteed to be issued at a time >= delta_in_milliseconds.
+ //
+ // When async is true, HostResolver::Resolve will issue the DNS lookups
+ // asynchronously - this can be used to have multiple requests in flight at
+ // the same time.
+ //
+ // ResolveAll will block until all resolutions are complete.
+ void ResolveAll(const std::vector<HostAndTime>& hosts_and_times,
+ bool async) {
+ // Schedule all tasks on our message loop, and then run.
+ int num_requests = hosts_and_times.size();
+ remaining_requests_ = num_requests;
+ for (int i = 0; i < num_requests; ++i) {
+ const HostAndTime& host_and_time = hosts_and_times[i];
+ const std::string& host = host_and_time.host;
+ DelayedResolve* resolve_request = new DelayedResolve(host,
+ async, resolver_, this);
+ resolve_request->AddRef();
+ message_loop_.PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(resolve_request, &DelayedResolve::Start),
+ host_and_time.delta_in_milliseconds);
+ }
+ message_loop_.Run();
+ }
+
+ private:
+ friend class DelayedResolve;
+
+ void OnResolved(int err, net::AddressList* address_list,
+ const std::string& host) {
+ if (err == net::OK) {
+ printf("%s", FormatAddressList(*address_list, host).c_str());
+ } else {
+ printf("Error resolving %s\n", host.c_str());
+ }
+ DCHECK(remaining_requests_ > 0);
+ --remaining_requests_;
+ if (remaining_requests_ == 0) {
+ message_loop_.Quit();
+ }
+ }
+
+ MessageLoop message_loop_;
+ scoped_refptr<net::HostResolver> resolver_;
+ int remaining_requests_;
+};
+
+void DelayedResolve::OnResolveComplete(int result) {
+ invoker_->OnResolved(result, &address_list_, host_);
+ this->Release();
+}
+
+struct CommandLineOptions {
+ CommandLineOptions()
+ : verbose(false),
+ async(false),
+ cache_size(100),
+ cache_ttl(50),
+ input_path() {
+ }
+
+ bool verbose;
+ bool async;
+ int cache_size;
+ int cache_ttl;
+ FilePath input_path;
+};
+
+const char* kAsync = "async";
+const char* kCacheSize = "cache-size";
+const char* kCacheTtl = "cache-ttl";
+const char* kInputPath = "input-path";
+
+// Parses the command line values. Returns false if there is a problem,
+// options otherwise.
+bool ParseCommandLine(CommandLine* command_line, CommandLineOptions* options) {
+ options->async = command_line->HasSwitch(kAsync);
+ if (command_line->HasSwitch(kCacheSize)) {
+ std::string cache_size = command_line->GetSwitchValueASCII(kCacheSize);
+ bool valid_size = StringToInt(cache_size, &options->cache_size);
+ if (valid_size) {
+ valid_size = options->cache_size >= 0;
+ }
+ if (!valid_size) {
+ printf("Invalid --cachesize value: %s\n", cache_size.c_str());
+ return false;
+ }
+ }
+
+ if (command_line->HasSwitch(kCacheTtl)) {
+ std::string cache_ttl = command_line->GetSwitchValueASCII(kCacheTtl);
+ bool valid_ttl = StringToInt(cache_ttl, &options->cache_ttl);
+ if (valid_ttl) {
+ valid_ttl = options->cache_ttl >= 0;
+ }
+ if (!valid_ttl) {
+ printf("Invalid --cachettl value: %s\n", cache_ttl.c_str());
+ return false;
+ }
+ }
+
+ if (command_line->HasSwitch(kInputPath)) {
+ options->input_path = command_line->GetSwitchValuePath(kInputPath);
+ }
+
+ return true;
+}
+
+bool ReadHostsAndTimesFromLooseValues(
+ const std::vector<std::wstring>& loose_args,
+ std::vector<HostAndTime>* hosts_and_times) {
+ std::vector<std::wstring>::const_iterator loose_args_end = loose_args.end();
+ for (std::vector<std::wstring>::const_iterator it = loose_args.begin();
+ it != loose_args_end;
+ ++it) {
+ // TODO(cbentzel): Read time offset.
+ HostAndTime host_and_time = {WideToASCII(*it), 0};
+ hosts_and_times->push_back(host_and_time);
+ }
+ return true;
+}
+
+bool ReadHostsAndTimesFromFile(const FilePath& path,
+ std::vector<HostAndTime>* hosts_and_times) {
+ // TODO(cbentzel): There are smarter and safer ways to do this.
+ std::string file_contents;
+ if (!file_util::ReadFileToString(path, &file_contents)) {
+ return false;
+ }
+ std::vector<std::string> lines;
+ // TODO(cbentzel): This should probably handle CRLF-style separators as well.
+ // Maybe it's worth adding functionality like this to base tools.
+ SplitString(file_contents, '\n', &lines);
+ std::vector<std::string>::const_iterator line_end = lines.end();
+ int previous_timestamp = 0;
+ for (std::vector<std::string>::const_iterator it = lines.begin();
+ it != line_end;
+ ++it) {
+ std::vector<std::string> tokens;
+ SplitStringAlongWhitespace(*it, &tokens);
+ switch (tokens.size()) {
+ case 0:
+ // Unexpected, but keep going.
+ break;
+ case 1: {
+ HostAndTime host_and_time = {tokens[0], previous_timestamp};
+ hosts_and_times->push_back(host_and_time);
+ break;
+ }
+ case 2: {
+ int timestamp;
+ if (!StringToInt(tokens[1], &timestamp)) {
+ // Unexpected value - keep going.
+ }
+ if (timestamp < previous_timestamp) {
+ // Unexpected value - ignore.
+ }
+ previous_timestamp = timestamp;
+ HostAndTime host_and_time = {tokens[0], timestamp};
+ hosts_and_times->push_back(host_and_time);
+ break;
+ }
+ default:
+ DCHECK(false);
+ break;
+ }
+ }
+ return true;
+}
+
+int main(int argc, char** argv) {
+ base::AtExitManager at_exit_manager;
+ CommandLine::Init(argc, argv);
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ CommandLineOptions options;
+ if (!ParseCommandLine(command_line, &options)) {
+ exit(1);
+ }
+
+ // Get the hosts and times - either from a file or command line options.
+ // TODO(cbentzel): Add stdin support to POSIX versions - not sure if
+ // there's an equivalent in Windows.
+ // TODO(cbentzel): If really large, may not want to spool the whole
+ // file into memory.
+ std::vector<HostAndTime> hosts_and_times;
+ if (options.input_path.empty()) {
+ if (!ReadHostsAndTimesFromLooseValues(command_line->GetLooseValues(),
+ &hosts_and_times)) {
+ exit(1);
+ }
+ } else {
+ if (!ReadHostsAndTimesFromFile(options.input_path, &hosts_and_times)) {
+ exit(1);
+ }
+ }
+
+ scoped_refptr<net::HostResolver> host_resolver(
+ new net::HostResolverImpl(NULL, options.cache_size, options.cache_ttl));
+ ResolverInvoker invoker(host_resolver.get());
+ invoker.ResolveAll(hosts_and_times, options.async);
+
+ CommandLine::Reset();
+ return 0;
+}