Index: net/tools/hresolv/hresolv.cc |
=================================================================== |
--- net/tools/hresolv/hresolv.cc (revision 0) |
+++ net/tools/hresolv/hresolv.cc (revision 0) |
@@ -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"}, |
eroman
2009/12/09 23:00:31
style-nit: I'm not sure on what the indentation sh
|
+ {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(); |
eroman
2009/12/09 23:00:31
style-nit: I suggest using a more descriptive name
|
+ it != NULL; |
eroman
2009/12/09 23:00:31
minor-style-nit: Omit the "!= 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_(), |
eroman
2009/12/09 23:00:31
nit: omit default constructor calls.
|
+ 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: |
+ |
eroman
2009/12/09 23:00:31
nit: please delete this blank line.
|
+ // 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(); |
eroman
2009/12/09 23:00:31
nit: I recommend documenting that it is balanced b
|
+ 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) { |
eroman
2009/12/09 23:00:31
nit: this definition could be arranged higher up i
|
+ 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], ×tamp)) { |
+ // 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)); |
eroman
2009/12/09 23:00:31
Depending who checks in first, someone will have s
|
+ ResolverInvoker invoker(host_resolver.get()); |
+ invoker.ResolveAll(hosts_and_times, options.async); |
+ |
+ CommandLine::Reset(); |
+ return 0; |
+} |
Property changes on: net\tools\hresolv\hresolv.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |