Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(849)

Side by Side Diff: net/tools/hresolv/hresolv.cc

Issue 428004: Command line utility to run the HostResolver. ... (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 11 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/net.gyp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 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.
4
5 // hrseolv is a command line utility which runs the HostResolver in the
6 // Chromium network stack.
7 //
8 // The user specifies the hosts to lookup and when to look them up.
9 // The hosts must be specified in order.
10 // The hosts can be contained in a file or on the command line. If no
11 // time is specified, the resolv is assumed to be the same time as the
12 // previous host - which is an offset of 0 for the very first host.
13 //
14 // The user can also control whether the lookups happen asynchronously
15 // or synchronously by specifying --async on the command line.
16 //
17 // Future ideas:
18 // Specify whether the lookup is speculative.
19 // Interleave synchronous and asynchronous lookups.
20 // Specify the address family.
21
22 #include "build/build_config.h"
23
24 #if defined(OS_WIN)
25 #include <ws2tcpip.h>
26 #else
27 #include <netdb.h>
28 #endif
29 #include <stdio.h>
30 #include <string>
31
32 #include "base/at_exit.h"
33 #include "base/command_line.h"
34 #include "base/condition_variable.h"
35 #include "base/file_path.h"
36 #include "base/file_util.h"
37 #include "base/string_util.h"
38 #include "base/thread.h"
39 #include "base/time.h"
40 #include "base/waitable_event.h"
41 #include "net/base/address_list.h"
42 #include "net/base/completion_callback.h"
43 #include "net/base/host_resolver_impl.h"
44 #include "net/base/net_errors.h"
45 #include "net/base/net_util.h"
46
47 struct FlagName {
48 int flag;
49 const char* name;
50 };
51
52 static const FlagName kAddrinfoFlagNames[] = {
53 {AI_PASSIVE, "AI_PASSIVE"},
54 {AI_CANONNAME, "AI_CANONNAME"},
55 {AI_NUMERICHOST, "AI_NUMERICHOST"},
56 {AI_V4MAPPED, "AI_V4MAPPED"},
57 {AI_ALL, "AI_ALL"},
58 {AI_ADDRCONFIG, "AI_ADDRCONFIG"},
59 #if defined(OS_LINUX) || defined(OS_WIN)
60 {AI_NUMERICSERV, "AI_NUMERICSERV"},
61 #endif
62 };
63
64 std::string FormatAddrinfoFlags(int ai_flags) {
65 std::string flag_names;
66 for (unsigned int i = 0; i < arraysize(kAddrinfoFlagNames); ++i) {
67 const FlagName& flag_name = kAddrinfoFlagNames[i];
68 if (ai_flags & flag_name.flag) {
69 ai_flags &= ~flag_name.flag;
70 if (!flag_names.empty()) {
71 flag_names += "|";
72 }
73 flag_names += flag_name.name;
74 }
75 }
76 if (ai_flags) {
77 if (!flag_names.empty()) {
78 flag_names += "|";
79 }
80 flag_names += StringPrintf("0x%x", ai_flags);
81 }
82 return flag_names;
83 }
84
85 const char* GetNameOfFlag(const FlagName* flag_names,
86 unsigned int num_flag_names,
87 int flag) {
88 for (unsigned int i = 0; i < num_flag_names; ++i) {
89 const FlagName& flag_name = flag_names[i];
90 if (flag_name.flag == flag) {
91 return flag_name.name;
92 }
93 }
94 return "UNKNOWN";
95 }
96
97 static const FlagName kFamilyFlagNames[] = {
98 {AF_UNSPEC, "AF_UNSPEC"},
eroman 2009/12/09 23:00:31 style-nit: I'm not sure on what the indentation sh
99 {AF_INET, "AF_INET"},
100 {AF_INET6, "AF_INET6"},
101 };
102
103 const char* FormatAddrinfoFamily(int ai_family) {
104 return GetNameOfFlag(kFamilyFlagNames,
105 arraysize(kFamilyFlagNames),
106 ai_family);
107 }
108
109 static const FlagName kSocktypeFlagNames[] = {
110 {SOCK_STREAM, "SOCK_STREAM"},
111 {SOCK_DGRAM, "SOCK_DGRAM"},
112 {SOCK_RAW, "SOCK_RAW"},
113 };
114
115 const char* FormatAddrinfoSocktype(int ai_socktype) {
116 return GetNameOfFlag(kSocktypeFlagNames,
117 arraysize(kSocktypeFlagNames),
118 ai_socktype);
119 }
120
121 static const FlagName kProtocolFlagNames[] = {
122 {IPPROTO_TCP, "IPPROTO_TCP"},
123 {IPPROTO_UDP, "IPPROTO_UDP"},
124 };
125
126 const char* FormatAddrinfoProtocol(int ai_protocol) {
127 return GetNameOfFlag(kProtocolFlagNames,
128 arraysize(kProtocolFlagNames),
129 ai_protocol);
130 }
131
132 std::string FormatAddrinfoDetails(const struct addrinfo& ai,
133 const char* indent) {
134 std::string ai_flags = FormatAddrinfoFlags(ai.ai_flags);
135 const char* ai_family = FormatAddrinfoFamily(ai.ai_family);
136 const char* ai_socktype = FormatAddrinfoSocktype(ai.ai_socktype);
137 const char* ai_protocol = FormatAddrinfoProtocol(ai.ai_protocol);
138 std::string ai_addr = net::NetAddressToString(&ai);
139 std::string ai_canonname;
140 if (ai.ai_canonname) {
141 ai_canonname = StringPrintf("%s ai_canonname: %s\n",
142 indent,
143 ai.ai_canonname);
144 }
145 return StringPrintf("%saddrinfo {\n"
146 "%s ai_flags: %s\n"
147 "%s ai_family: %s\n"
148 "%s ai_socktype: %s\n"
149 "%s ai_protocol: %s\n"
150 "%s ai_addrlen: %d\n"
151 "%s ai_addr: %s\n"
152 "%s"
153 "%s}\n",
154 indent,
155 indent, ai_flags.c_str(),
156 indent, ai_family,
157 indent, ai_socktype,
158 indent, ai_protocol,
159 indent, ai.ai_addrlen,
160 indent, ai_addr.c_str(),
161 ai_canonname.c_str(),
162 indent);
163 }
164
165 std::string FormatAddressList(const net::AddressList& address_list,
166 const std::string& host) {
167 std::string ret_string;
168 StringAppendF(&ret_string, "AddressList {\n");
169 StringAppendF(&ret_string, " Host: %s\n", host.c_str());
170 for (const struct addrinfo* it = address_list.head();
eroman 2009/12/09 23:00:31 style-nit: I suggest using a more descriptive name
171 it != NULL;
eroman 2009/12/09 23:00:31 minor-style-nit: Omit the "!= NULL"
172 it = it->ai_next) {
173 StringAppendF(&ret_string, "%s", FormatAddrinfoDetails(*it, " ").c_str());
174 }
175 StringAppendF(&ret_string, "}\n");
176 return ret_string;
177 }
178
179 class ResolverInvoker;
180
181 // DelayedResolve contains state for a DNS resolution to be performed later.
182 class DelayedResolve : public base::RefCounted<DelayedResolve> {
183 public:
184 DelayedResolve(const std::string& host, bool is_async,
185 net::HostResolver* resolver,
186 ResolverInvoker* invoker)
187 : host_(host),
188 address_list_(),
eroman 2009/12/09 23:00:31 nit: omit default constructor calls.
189 is_async_(is_async),
190 resolver_(resolver),
191 invoker_(invoker),
192 ALLOW_THIS_IN_INITIALIZER_LIST(
193 io_callback_(this, &DelayedResolve::OnResolveComplete)) {
194 }
195
196 void Start() {
197 net::CompletionCallback* callback = (is_async_) ? &io_callback_ : NULL;
198 net::HostResolver::RequestInfo request_info(host_, 80);
199 int rv = resolver_->Resolve(request_info,
200 &address_list_,
201 callback,
202 NULL,
203 NULL);
204 if (rv != net::ERR_IO_PENDING) {
205 OnResolveComplete(rv);
206 }
207 }
208
209 private:
210
eroman 2009/12/09 23:00:31 nit: please delete this blank line.
211 // Without this, VC++ complains about the private destructor below.
212 friend class base::RefCounted<DelayedResolve>;
213
214 // The destructor is called by Release.
215 ~DelayedResolve() {}
216
217 void OnResolveComplete(int result);
218
219 std::string host_;
220 net::AddressList address_list_;
221 bool is_async_;
222 scoped_refptr<net::HostResolver> resolver_;
223 ResolverInvoker* invoker_;
224 net::CompletionCallbackImpl<DelayedResolve> io_callback_;
225 };
226
227
228 struct HostAndTime {
229 // The host to resolve, i.e. www.google.com
230 std::string host;
231 // Time since the start of this program to actually kick off the resolution.
232 int delta_in_milliseconds;
233 };
234
235 // Invokes a sequence of host resolutions at specified times.
236 class ResolverInvoker {
237 public:
238 explicit ResolverInvoker(net::HostResolver* resolver)
239 : message_loop_(MessageLoop::TYPE_DEFAULT),
240 resolver_(resolver),
241 remaining_requests_(0) {
242 }
243
244 ~ResolverInvoker() {
245 }
246
247 // Resolves all specified hosts in the order provided. hosts_and_times is
248 // assumed to be ordered by the delta_in_milliseconds field. There is no
249 // guarantee that the resolutions will complete in the order specified when
250 // async is true. There is no guarantee that the DNS queries will be issued
251 // at exactly the time specified by delta_in_milliseconds, but they are
252 // guaranteed to be issued at a time >= delta_in_milliseconds.
253 //
254 // When async is true, HostResolver::Resolve will issue the DNS lookups
255 // asynchronously - this can be used to have multiple requests in flight at
256 // the same time.
257 //
258 // ResolveAll will block until all resolutions are complete.
259 void ResolveAll(const std::vector<HostAndTime>& hosts_and_times,
260 bool async) {
261 // Schedule all tasks on our message loop, and then run.
262 int num_requests = hosts_and_times.size();
263 remaining_requests_ = num_requests;
264 for (int i = 0; i < num_requests; ++i) {
265 const HostAndTime& host_and_time = hosts_and_times[i];
266 const std::string& host = host_and_time.host;
267 DelayedResolve* resolve_request = new DelayedResolve(host,
268 async, resolver_, this);
269 resolve_request->AddRef();
eroman 2009/12/09 23:00:31 nit: I recommend documenting that it is balanced b
270 message_loop_.PostDelayedTask(
271 FROM_HERE,
272 NewRunnableMethod(resolve_request, &DelayedResolve::Start),
273 host_and_time.delta_in_milliseconds);
274 }
275 message_loop_.Run();
276 }
277
278 private:
279 friend class DelayedResolve;
280
281 void OnResolved(int err, net::AddressList* address_list,
282 const std::string& host) {
283 if (err == net::OK) {
284 printf("%s", FormatAddressList(*address_list, host).c_str());
285 } else {
286 printf("Error resolving %s\n", host.c_str());
287 }
288 DCHECK(remaining_requests_ > 0);
289 --remaining_requests_;
290 if (remaining_requests_ == 0) {
291 message_loop_.Quit();
292 }
293 }
294
295 MessageLoop message_loop_;
296 scoped_refptr<net::HostResolver> resolver_;
297 int remaining_requests_;
298 };
299
300 void DelayedResolve::OnResolveComplete(int result) {
eroman 2009/12/09 23:00:31 nit: this definition could be arranged higher up i
301 invoker_->OnResolved(result, &address_list_, host_);
302 this->Release();
303 }
304
305 struct CommandLineOptions {
306 CommandLineOptions()
307 : verbose(false),
308 async(false),
309 cache_size(100),
310 cache_ttl(50),
311 input_path() {
312 }
313
314 bool verbose;
315 bool async;
316 int cache_size;
317 int cache_ttl;
318 FilePath input_path;
319 };
320
321 const char* kAsync = "async";
322 const char* kCacheSize = "cache-size";
323 const char* kCacheTtl = "cache-ttl";
324 const char* kInputPath = "input-path";
325
326 // Parses the command line values. Returns false if there is a problem,
327 // options otherwise.
328 bool ParseCommandLine(CommandLine* command_line, CommandLineOptions* options) {
329 options->async = command_line->HasSwitch(kAsync);
330 if (command_line->HasSwitch(kCacheSize)) {
331 std::string cache_size = command_line->GetSwitchValueASCII(kCacheSize);
332 bool valid_size = StringToInt(cache_size, &options->cache_size);
333 if (valid_size) {
334 valid_size = options->cache_size >= 0;
335 }
336 if (!valid_size) {
337 printf("Invalid --cachesize value: %s\n", cache_size.c_str());
338 return false;
339 }
340 }
341
342 if (command_line->HasSwitch(kCacheTtl)) {
343 std::string cache_ttl = command_line->GetSwitchValueASCII(kCacheTtl);
344 bool valid_ttl = StringToInt(cache_ttl, &options->cache_ttl);
345 if (valid_ttl) {
346 valid_ttl = options->cache_ttl >= 0;
347 }
348 if (!valid_ttl) {
349 printf("Invalid --cachettl value: %s\n", cache_ttl.c_str());
350 return false;
351 }
352 }
353
354 if (command_line->HasSwitch(kInputPath)) {
355 options->input_path = command_line->GetSwitchValuePath(kInputPath);
356 }
357
358 return true;
359 }
360
361 bool ReadHostsAndTimesFromLooseValues(
362 const std::vector<std::wstring>& loose_args,
363 std::vector<HostAndTime>* hosts_and_times) {
364 std::vector<std::wstring>::const_iterator loose_args_end = loose_args.end();
365 for (std::vector<std::wstring>::const_iterator it = loose_args.begin();
366 it != loose_args_end;
367 ++it) {
368 // TODO(cbentzel): Read time offset.
369 HostAndTime host_and_time = {WideToASCII(*it), 0};
370 hosts_and_times->push_back(host_and_time);
371 }
372 return true;
373 }
374
375 bool ReadHostsAndTimesFromFile(const FilePath& path,
376 std::vector<HostAndTime>* hosts_and_times) {
377 // TODO(cbentzel): There are smarter and safer ways to do this.
378 std::string file_contents;
379 if (!file_util::ReadFileToString(path, &file_contents)) {
380 return false;
381 }
382 std::vector<std::string> lines;
383 // TODO(cbentzel): This should probably handle CRLF-style separators as well.
384 // Maybe it's worth adding functionality like this to base tools.
385 SplitString(file_contents, '\n', &lines);
386 std::vector<std::string>::const_iterator line_end = lines.end();
387 int previous_timestamp = 0;
388 for (std::vector<std::string>::const_iterator it = lines.begin();
389 it != line_end;
390 ++it) {
391 std::vector<std::string> tokens;
392 SplitStringAlongWhitespace(*it, &tokens);
393 switch (tokens.size()) {
394 case 0:
395 // Unexpected, but keep going.
396 break;
397 case 1: {
398 HostAndTime host_and_time = {tokens[0], previous_timestamp};
399 hosts_and_times->push_back(host_and_time);
400 break;
401 }
402 case 2: {
403 int timestamp;
404 if (!StringToInt(tokens[1], &timestamp)) {
405 // Unexpected value - keep going.
406 }
407 if (timestamp < previous_timestamp) {
408 // Unexpected value - ignore.
409 }
410 previous_timestamp = timestamp;
411 HostAndTime host_and_time = {tokens[0], timestamp};
412 hosts_and_times->push_back(host_and_time);
413 break;
414 }
415 default:
416 DCHECK(false);
417 break;
418 }
419 }
420 return true;
421 }
422
423 int main(int argc, char** argv) {
424 base::AtExitManager at_exit_manager;
425 CommandLine::Init(argc, argv);
426 CommandLine* command_line = CommandLine::ForCurrentProcess();
427 CommandLineOptions options;
428 if (!ParseCommandLine(command_line, &options)) {
429 exit(1);
430 }
431
432 // Get the hosts and times - either from a file or command line options.
433 // TODO(cbentzel): Add stdin support to POSIX versions - not sure if
434 // there's an equivalent in Windows.
435 // TODO(cbentzel): If really large, may not want to spool the whole
436 // file into memory.
437 std::vector<HostAndTime> hosts_and_times;
438 if (options.input_path.empty()) {
439 if (!ReadHostsAndTimesFromLooseValues(command_line->GetLooseValues(),
440 &hosts_and_times)) {
441 exit(1);
442 }
443 } else {
444 if (!ReadHostsAndTimesFromFile(options.input_path, &hosts_and_times)) {
445 exit(1);
446 }
447 }
448
449 scoped_refptr<net::HostResolver> host_resolver(
450 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
451 ResolverInvoker invoker(host_resolver.get());
452 invoker.ResolveAll(hosts_and_times, options.async);
453
454 CommandLine::Reset();
455 return 0;
456 }
OLDNEW
« no previous file with comments | « net/net.gyp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698