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

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: Specifying whether the lookup is speculative
eroman 2009/12/04 10:38:21 Another future idea could be to let you specify th
cbentzelold 2012/10/18 15:36:45 Done.
18 // or not. Interleave synchronous and asynchronous lookups.
19
20 #include "build/build_config.h"
21
22 #if defined(OS_WIN)
23 #include <ws2tcpip.h>
24 #else
25 #include <netdb.h>
26 #endif
27 #include <stdio.h>
28 #include <string>
29
30 #include "base/at_exit.h"
31 #include "base/command_line.h"
32 #include "base/condition_variable.h"
33 #include "base/file_path.h"
34 #include "base/file_util.h"
35 #include "base/string_util.h"
36 #include "base/thread.h"
37 #include "base/time.h"
38 #include "base/waitable_event.h"
39 #include "net/base/address_list.h"
40 #include "net/base/completion_callback.h"
41 #include "net/base/host_resolver_impl.h"
42 #include "net/base/net_errors.h"
43 #include "net/base/net_util.h"
44
45 struct FlagName {
46 int flag;
47 const char* name;
48 };
49
50 static const FlagName kAddrinfoFlagNames[] = {
51 {AI_PASSIVE, "AI_PASSIVE"},
52 {AI_CANONNAME, "AI_CANONNAME"},
53 {AI_NUMERICHOST, "AI_NUMERICHOST"},
54 {AI_V4MAPPED, "AI_V4MAPPED"},
55 {AI_ALL, "AI_ALL"},
56 {AI_ADDRCONFIG, "AI_ADDRCONFIG"},
57 #if defined(OS_LINUX) || defined(OS_WIN)
58 {AI_NUMERICSERV, "AI_NUMERICSERV"},
59 #endif
60 };
61
62 std::string FormatAddrinfoFlags(int ai_flags) {
63 std::string flag_names;
64 for (unsigned int i = 0; i < arraysize(kAddrinfoFlagNames); ++i) {
65 const FlagName& flag_name = kAddrinfoFlagNames[i];
66 if (ai_flags & flag_name.flag) {
67 ai_flags &= ~flag_name.flag;
68 if (!flag_names.empty()) {
69 flag_names += "|";
70 }
71 flag_names += flag_name.name;
72 }
73 }
74 if (ai_flags) {
75 if (!flag_names.empty()) {
76 flag_names += "|";
77 }
78 flag_names += StringPrintf("0x%x", ai_flags);
79 }
80 return flag_names;
81 }
82
83 const char* GetNameOfFlag(const FlagName* flag_names,
84 unsigned int num_flag_names,
85 int flag) {
86 for (unsigned int i = 0; i < num_flag_names; ++i) {
87 const FlagName& flag_name = flag_names[i];
88 if (flag_name.flag == flag) {
89 return flag_name.name;
90 }
91 }
92 return "UNKNOWN";
93 }
94
95 static const FlagName kFamilyFlagNames[] = {
96 {AF_UNSPEC, "AF_UNSPEC"},
97 {AF_INET, "AF_INET"},
98 {AF_INET6, "AF_INET6"},
99 };
100
101 const char* FormatAddrinfoFamily(int ai_family) {
102 return GetNameOfFlag(kFamilyFlagNames,
103 arraysize(kFamilyFlagNames),
104 ai_family);
105 }
106
107 static const FlagName kSocktypeFlagNames[] = {
108 {SOCK_STREAM, "SOCK_STREAM"},
109 {SOCK_DGRAM, "SOCK_DGRAM"},
110 {SOCK_RAW, "SOCK_RAW"},
111 };
112
113 const char* FormatAddrinfoSocktype(int ai_socktype) {
114 return GetNameOfFlag(kSocktypeFlagNames,
115 arraysize(kSocktypeFlagNames),
116 ai_socktype);
117 }
118
119 static const FlagName kProtocolFlagNames[] = {
120 {IPPROTO_TCP, "IPPROTO_TCP"},
121 {IPPROTO_UDP, "IPPROTO_UDP"},
122 };
123
124 const char* FormatAddrinfoProtocol(int ai_protocol) {
125 return GetNameOfFlag(kProtocolFlagNames,
126 arraysize(kProtocolFlagNames),
127 ai_protocol);
128 }
129
130 std::string FormatAddrinfoDetails(const struct addrinfo& ai,
131 const char* indent) {
132 std::string ai_flags = FormatAddrinfoFlags(ai.ai_flags);
133 const char* ai_family = FormatAddrinfoFamily(ai.ai_family);
134 const char* ai_socktype = FormatAddrinfoSocktype(ai.ai_socktype);
135 const char* ai_protocol = FormatAddrinfoProtocol(ai.ai_protocol);
136 std::string ai_addr = net::NetAddressToString(&ai);
137 std::string ai_canonname;
138 if (ai.ai_canonname) {
139 ai_canonname = StringPrintf("%s ai_canonname: %s\n",
140 indent,
141 ai.ai_canonname);
142 }
143 return StringPrintf("%saddrinfo {\n"
144 "%s ai_flags: %s\n"
145 "%s ai_family: %s\n"
146 "%s ai_socktype: %s\n"
147 "%s ai_protocol: %s\n"
148 "%s ai_addrlen: %d\n"
149 "%s ai_addr: %s\n"
150 "%s"
151 "%s}\n",
152 indent,
153 indent, ai_flags.c_str(),
154 indent, ai_family,
155 indent, ai_socktype,
156 indent, ai_protocol,
157 indent, ai.ai_addrlen,
158 indent, ai_addr.c_str(),
159 ai_canonname.c_str(),
160 indent);
161 }
162
163 std::string FormatAddressList(const net::AddressList& address_list,
164 const std::string& host) {
165 std::string ret_string;
166 StringAppendF(&ret_string, "AddressList {\n");
167 StringAppendF(&ret_string, " Host: %s\n", host.c_str());
168 for (const struct addrinfo* it = address_list.head();
169 it != NULL;
170 it = it->ai_next) {
171 StringAppendF(&ret_string, "%s", FormatAddrinfoDetails(*it, " ").c_str());
172 }
173 StringAppendF(&ret_string, "}\n");
174 return ret_string;
175 }
176
177 class ResolverInvoker;
178
179 // The ResolveTask is used to request a HostResolver::Resolve invocation
180 // on the HostResolver's message loop thread for a specified host.
181 class ResolveTask : public Task {
182 public:
183 ResolveTask(net::HostResolver* resolver,
184 ResolverInvoker* resolver_invoker,
185 const std::string& host,
186 net::AddressList* address_list,
187 net::CompletionCallback* completion_callback)
188 : resolver_(resolver),
189 resolver_invoker_(resolver_invoker),
190 address_list_(address_list),
191 host_(host),
192 completion_callback_(completion_callback) {
193 }
194
195 virtual ~ResolveTask() {
196 }
197
198 virtual void Run();
199
200 private:
201 scoped_refptr<net::HostResolver> resolver_;
202 ResolverInvoker* resolver_invoker_;
203 net::AddressList* address_list_;
204 std::string host_;
205 net::CompletionCallback* completion_callback_;
206 };
207
208 struct HostAndTime {
209 // The host to resolve, i.e. www.google.com
210 std::string host;
211 // Time since the start of this program to actually kick off the resolution.
212 int delta_in_milliseconds;
213 };
214
215 // Invokes a sequence of host resolutions at specified times.
216 class ResolverInvoker {
217 class AsyncContext;
218 public:
219 explicit ResolverInvoker(net::HostResolver* resolver)
220 : message_loop_(MessageLoop::TYPE_DEFAULT),
221 resolver_(resolver),
222 remaining_requests_(0) {
223 }
224
225 ~ResolverInvoker() {
226 }
227
228 // Resolves all specified hosts in the order provided. hosts_and_times is
229 // assumed to be ordered by the delta_in_milliseconds field. There is no
230 // guarantee that the resolutions will complete in the order specified when
231 // async is true. There is no guarantee that the DNS queries will be issued
232 // at exactly the time specified by delta_in_milliseconds, but they are
233 // guaranteed to be issued at a time >= delta_in_milliseconds.
234 //
235 // When async is true, HostResolver::Resolve will issue the DNS lookups
236 // asynchronously - this can be used to have multiple requests in flight at
237 // the same time.
238 //
239 // ResolveAll will block until all resolutions are complete.
240 void ResolveAll(const std::vector<HostAndTime>& hosts_and_times,
241 bool async) {
242 // Schedule all tasks on our message loop, and then run.
243 int num_requests = hosts_and_times.size();
244 remaining_requests_ = num_requests;
245 for (int i = 0; i < num_requests; ++i) {
246 const HostAndTime& host_and_time = hosts_and_times[i];
247 const std::string& host = host_and_time.host;
248 net::AddressList* address_list = new net::AddressList();
249 net::CompletionCallback* callback = NULL;
250 if (async) {
251 AsyncContext* async_context = new AsyncContext(this, address_list,
252 host);
253 callback = NewCallback(async_context, &AsyncContext::OnComplete);
254 async_context->set_callback(callback);
255 }
256 ResolveTask* task = new ResolveTask(resolver_,
257 this,
258 host,
259 address_list,
260 callback);
261 message_loop_.PostDelayedTask(FROM_HERE, task,
262 host_and_time.delta_in_milliseconds);
263 }
264 message_loop_.Run();
265 }
266
267 private:
268 friend class ResolveTask;
269
270 // AsyncContext is needed for async Resolve calls. The HostResolver takes
271 // a CompletionCallback - which only accepts a single integer argument
272 // indicating whether the resolution was successful. To match the response
273 // to the host and the address_list which is populated, additional context is
274 // necessary. If the HostResolver interface changed to take a delegate or a
275 // callback which accepted an address_list and a request_info object, the
276 // need for the AsyncContext class would go away.
277 class AsyncContext {
278 public:
279 AsyncContext(ResolverInvoker* resolver_invoker,
280 net::AddressList* address_list,
281 const std::string& host)
282 : resolver_invoker_(resolver_invoker),
283 address_list_(address_list),
284 host_(host),
285 callback_(NULL) {
286 }
287
288 ~AsyncContext() {}
289
290 void OnComplete(int err) {
291 resolver_invoker_->OnResolved(err, address_list_, host_);
292 delete callback_;
293 delete this;
294 }
295
296 void set_callback(net::CompletionCallback* callback) {
297 DCHECK(callback_ == NULL);
298 callback_ = callback;
299 }
300
301 private:
302 ResolverInvoker* resolver_invoker_;
303 net::AddressList* address_list_;
304 std::string host_;
305 net::CompletionCallback* callback_;
306 };
307
308 void OnResolved(int err, net::AddressList* address_list,
309 const std::string& host) {
310 if (err == net::OK) {
311 printf("%s", FormatAddressList(*address_list, host).c_str());
312 } else {
313 printf("Error resolving %s\n", host.c_str());
314 }
315 delete address_list;
316 DCHECK(remaining_requests_ > 0);
317 --remaining_requests_;
318 if (remaining_requests_ == 0) {
319 message_loop_.Quit();
320 }
321 }
322
323 MessageLoop message_loop_;
324 scoped_refptr<net::HostResolver> resolver_;
325 int remaining_requests_;
326 };
327
328 void ResolveTask::Run() {
329 net::HostResolver::RequestInfo request_info(host_, 80);
330 int err = resolver_->Resolve(request_info,
331 address_list_,
332 completion_callback_,
333 NULL,
334 NULL);
335 switch (err) {
336 case net::ERR_IO_PENDING:
337 // Normal async case - completion_callback_ will be invoked when done.
338 DCHECK(completion_callback_ != NULL);
339 break;
340 default:
341 if (completion_callback_) {
342 completion_callback_->Run(err);
343 } else {
344 resolver_invoker_->OnResolved(err, address_list_, host_);
345 }
346 break;
347 }
348 }
349
350 struct CommandLineOptions {
351 CommandLineOptions()
352 : verbose(false),
353 async(false),
354 cache_size(100),
355 cache_ttl(50),
356 input_path() {
357 }
358
359 bool verbose;
360 bool async;
361 int cache_size;
362 int cache_ttl;
363 FilePath input_path;
364 };
365
366 const char* kAsync = "async";
367 const char* kCacheSize = "cache-size";
368 const char* kCacheTtl = "cache-ttl";
369 const char* kInputPath = "input-path";
370
371 // Parses the command line values. Returns false if there is a problem,
372 // options otherwise.
373 bool ParseCommandLine(CommandLine* command_line, CommandLineOptions* options) {
374 options->async = command_line->HasSwitch(kAsync);
375 if (command_line->HasSwitch(kCacheSize)) {
376 std::string cache_size = command_line->GetSwitchValueASCII(kCacheSize);
377 bool valid_size = StringToInt(cache_size, &options->cache_size);
378 if (valid_size) {
379 valid_size = options->cache_size >= 0;
380 }
381 if (!valid_size) {
382 printf("Invalid --cachesize value: %s\n", cache_size.c_str());
383 return false;
384 }
385 }
386
387 if (command_line->HasSwitch(kCacheTtl)) {
388 std::string cache_ttl = command_line->GetSwitchValueASCII(kCacheTtl);
389 bool valid_ttl = StringToInt(cache_ttl, &options->cache_ttl);
390 if (valid_ttl) {
391 valid_ttl = options->cache_ttl >= 0;
392 }
393 if (!valid_ttl) {
394 printf("Invalid --cachettl value: %s\n", cache_ttl.c_str());
395 return false;
396 }
397 }
398
399 if (command_line->HasSwitch(kInputPath)) {
400 options->input_path = command_line->GetSwitchValuePath(kInputPath);
401 }
402
403 return true;
404 }
405
406 bool ReadHostsAndTimesFromLooseValues(
407 const std::vector<std::wstring>& loose_args,
408 std::vector<HostAndTime>* hosts_and_times) {
409 std::vector<std::wstring>::const_iterator loose_args_end = loose_args.end();
410 for (std::vector<std::wstring>::const_iterator it = loose_args.begin();
411 it != loose_args_end;
412 ++it) {
413 // TODO(cbentzel): Read time offset.
414 HostAndTime host_and_time = {WideToASCII(*it), 0};
415 hosts_and_times->push_back(host_and_time);
416 }
417 return true;
418 }
419
420 bool ReadHostsAndTimesFromFile(const FilePath& path,
421 std::vector<HostAndTime>* hosts_and_times) {
422 // TODO(cbentzel): There are smarter and safer ways to do this.
423 std::string file_contents;
424 if (!file_util::ReadFileToString(path, &file_contents)) {
425 return false;
426 }
427 std::vector<std::string> lines;
428 // TODO(cbentzel): This should probably handle CRLF-style separators as well.
429 // Maybe it's worth adding functionality like this to base tools.
430 SplitString(file_contents, '\n', &lines);
431 std::vector<std::string>::const_iterator line_end = lines.end();
432 int previous_timestamp = 0;
433 for (std::vector<std::string>::const_iterator it = lines.begin();
434 it != line_end;
435 ++it) {
436 std::vector<std::string> tokens;
437 SplitStringAlongWhitespace(*it, &tokens);
438 switch (tokens.size()) {
439 case 0:
440 // Unexpected, but keep going.
441 break;
442 case 1: {
443 HostAndTime host_and_time = {tokens[0], previous_timestamp};
444 hosts_and_times->push_back(host_and_time);
445 break;
446 }
447 case 2: {
448 int timestamp;
449 if (!StringToInt(tokens[1], &timestamp)) {
450 // Unexpected value - keep going.
451 }
452 if (timestamp < previous_timestamp) {
453 // Unexpected value - ignore.
454 }
455 previous_timestamp = timestamp;
456 HostAndTime host_and_time = {tokens[0], timestamp};
457 hosts_and_times->push_back(host_and_time);
458 break;
459 }
460 default:
461 DCHECK(false);
462 break;
463 }
464 }
465 return true;
466 }
467
468 int main(int argc, char** argv) {
469 base::AtExitManager at_exit_manager;
470 CommandLine::Init(argc, argv);
471 CommandLine* command_line = CommandLine::ForCurrentProcess();
472 CommandLineOptions options;
473 if (!ParseCommandLine(command_line, &options)) {
474 exit(1);
475 }
476
477 // Get the hosts and times - either from a file or command line options.
478 // TODO(cbentzel): Add stdin support to POSIX versions - not sure if
479 // there's an equivalent in Windows.
480 // TODO(cbentzel): If really large, may not want to spool the whole
481 // file into memory.
482 std::vector<HostAndTime> hosts_and_times;
483 if (options.input_path.empty()) {
484 if (!ReadHostsAndTimesFromLooseValues(command_line->GetLooseValues(),
485 &hosts_and_times)) {
486 exit(1);
487 }
488 } else {
489 if (!ReadHostsAndTimesFromFile(options.input_path, &hosts_and_times)) {
490 exit(1);
491 }
492 }
493
494 scoped_refptr<net::HostResolver> host_resolver(
495 new net::HostResolverImpl(NULL, options.cache_size, options.cache_ttl));
496 ResolverInvoker invoker(host_resolver.get());
497 invoker.ResolveAll(hosts_and_times, options.async);
498
499 CommandLine::Reset();
500 return 0;
501 }
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