command_options.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <algorithm>
  15. #include <iostream>
  16. #include <string>
  17. #include <vector>
  18. #include <boost/lexical_cast.hpp>
  19. #include <getopt.h>
  20. #include "exceptions/exceptions.h"
  21. #include "util/strutil.h"
  22. #include "command_options.h"
  23. #include "option_info.h"
  24. #include "version.h"
  25. using namespace std;
  26. using namespace isc;
  27. namespace isc {
  28. namespace badpacket {
  29. // Reset stored values to the defaults.
  30. void
  31. CommandOptions::reset() {
  32. address_ = "127.0.0.1";
  33. port_ = 53;
  34. timeout_ = 500;
  35. qname_ = "www.example.com";
  36. for (int i = 0; i < OptionInfo::SIZE; ++i) {
  37. options_[i].minimum = OptionInfo::defval(i);
  38. options_[i].maximum = OptionInfo::defval(i);
  39. options_[i].present = false;
  40. }
  41. }
  42. /// Parses the command-line options and records the results.
  43. void
  44. CommandOptions::parse(int argc, char* const argv[]) {
  45. // Set up options for processing. The declaration of the string constants
  46. // as mutable arrays and putting the string variable into the "longopts"
  47. // structure (as opposed to just putting a string literal there) is
  48. // occasioned by a version of solaris which declares the first field as
  49. // "char*" (instead of the correct "const char*").
  50. // General options.
  51. char HELP[] = {"help"};
  52. char VERSION[] = {"version"};
  53. char ADDRESS[] = {"address"};
  54. char PORT[] = {"port"};
  55. char TIMEOUT[] = {"timeout"};
  56. // Settings for options in the flags field.
  57. char QR[] = {"qr"};
  58. char OP[] = {"op"};
  59. char AA[] = {"aa"};
  60. char TC[] = {"tc"};
  61. char RD[] = {"rd"};
  62. char RA[] = {"ra"};
  63. char Z[] = {"z"};
  64. char AD[] = {"ad"};
  65. char CD[] = {"cd"};
  66. char RC[] = {"rc"};
  67. // Settings for the count fields
  68. char QC[] = {"qc"};
  69. char AC[] = {"ac"};
  70. char UC[] = {"uc"};
  71. char DC[] = {"dc"};
  72. // Message size
  73. char MS[] = {"ms"};
  74. const struct option longopts[] = {
  75. {HELP, 0, NULL, 'h'}, // Print usage message and exit
  76. {VERSION, 0, NULL, 'v'}, // Print program version and exit
  77. {ADDRESS, 1, NULL, 'a'}, // Specify target server address
  78. {PORT, 1, NULL, 'p'}, // Specify target port
  79. {TIMEOUT, 1, NULL, 't'}, // Time to wait before timing out (ms)
  80. {QR, 1, NULL, 'Q'}, // Query/response flag
  81. {OP, 1, NULL, 'O'}, // Opcode
  82. {AA, 1, NULL, 'A'}, // Authoritative answer
  83. {TC, 1, NULL, 'T'}, // Truncated
  84. {RD, 1, NULL, 'D'}, // recursion Desired
  85. {RA, 1, NULL, 'R'}, // Recursion available
  86. {Z, 1, NULL, 'Z'}, // must be Zero (reserved bit)
  87. {AD, 1, NULL, 'U'}, // aUthenticated data
  88. {CD, 1, NULL, 'C'}, // Checking disabled
  89. {RC, 1, NULL, 'E'}, // rEsult code
  90. {QC, 1, NULL, 'Y'}, // querY section count
  91. {AC, 1, NULL, 'W'}, // ansWer section count
  92. {UC, 1, NULL, 'H'}, // autHority section count
  93. {DC, 1, NULL, 'I'}, // addItional section count
  94. {MS, 1, NULL, 'M'}, // Message size
  95. {NULL, 0, NULL, 0 }
  96. };
  97. const char* shortopts = "hva:p:t:Q:O:A:T:D:R:Z:U:C:E:Y:W:H:I:M:";
  98. // Set record of options to defaults before parsing
  99. reset();
  100. // Process command line
  101. int c; // Option being processed
  102. optind = 0; // Reset parsing
  103. while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
  104. switch (c) {
  105. case 'h': // --help
  106. usage();
  107. exit(0);
  108. case 'v': // --version
  109. version();
  110. exit(0);
  111. case 'a': // --address
  112. address_ = optarg;
  113. break;
  114. case 'p': // --port
  115. port_ = boost::lexical_cast<uint16_t>(string(optarg));
  116. break;
  117. case 't': // --timeout
  118. timeout_ = boost::lexical_cast<int>(string(optarg));
  119. break;
  120. case 'Q': // --qr (query/response)
  121. case 'O': // --op (operation code)
  122. case 'A': // --aa (authoritative answer)
  123. case 'T': // --tc (truncated)
  124. case 'D': // --rd (recursion desired)
  125. case 'R': // --ra (recursion available)
  126. case 'Z': // --z (zero: reserved bit)
  127. case 'U': // --ad (authenticated data)
  128. case 'C': // --cd (checking disabled)
  129. case 'E': // --rc (result code)
  130. case 'Y': // --qc (query count)
  131. case 'W': // --ac (answer count)
  132. case 'H': // --uc (authority count)
  133. case 'I': // --dc (additional count)
  134. case 'M': // --ms (message size)
  135. processOptionValue(c, optarg);
  136. break;
  137. default:
  138. isc_throw(isc::InvalidParameter,
  139. "unknown option given on the command line");
  140. }
  141. }
  142. // Pick up a parameter if there is one (and report excess).
  143. if (optind < argc) {
  144. qname_ = argv[optind++];
  145. }
  146. if (optind < argc) {
  147. isc_throw(isc::InvalidParameter,
  148. "only a single (optional) parameter may be specified on the command line");
  149. }
  150. }
  151. // Print usage information.
  152. void
  153. CommandOptions::usage() {
  154. cout << "Usage: badpacket [options] query\n"
  155. "\n"
  156. "Sends a sequence of DNS messages to the specified nameserver and prints the\n"
  157. " results. The packets are valid query packets but certain aspects of the\n"
  158. " packets (such as the flags fields, section count fields and message size) can\n"
  159. "be set to arbitrary values using the command-line switches.\n"
  160. "\n"
  161. "In the following list of command-line switches, '<range>' indicates a range of\n"
  162. "values specified as either <integer> or <integer1>-<integer2> (e.g. both '42'\n"
  163. "and '0-1' would be valid values for range). The program sends a sequence of\n"
  164. "messages that contain all combinations of the flag values. For example,\n"
  165. "specifying:\n"
  166. "\n"
  167. "--tc 0-1 --op 1-4 --aa 1 --rd 0-1\n"
  168. "\n"
  169. "... would send a total of 16 packets which would have all combinations of the\n"
  170. "the tc bit set to 0 and 1, the rd bit set to 0 and 1, and the opcode set to all\n"
  171. "values between 1 and 4. All other flags fields would be zero except for the aa\n"
  172. "bit which would always be 1.\n"
  173. "\n"
  174. "The long form of the option is given. It can also be specified as a single-\n"
  175. "character short-form, which is listed in sqare brackets in the description.\n"
  176. "\n"
  177. "General options are:\n"
  178. "\n"
  179. "--help [-h] Prints this message and exits.\n"
  180. "--version [-v] Prints the program version number.\n"
  181. "--address <address> [-a] Address of nameserver, which defaults to 127.0.0.1\n"
  182. "--port <port> [-p] Port to which to send query. Defaults to 53.\n"
  183. "--timeout <value> [-t] Timeout value for the query. Specified in ms, it\n"
  184. " defaults to 500ms.\n"
  185. "\n"
  186. "The following options set fields in the outgoing DNS message flags word:\n"
  187. "\n"
  188. "--qr <range> [-Q] Set query/response bit. Valid <range> is 0-1.\n"
  189. "--op <range> [-O] Set opcode. Valid <range> is 0-15.\n"
  190. "--aa <range> [-A] Set authoritative answer bit. Valid <range> is 0-1.\n"
  191. "--tc <range> [-T] Set truncated bit. Valid <range> is 0-1.\n"
  192. "--rd <range> [-D] Set recursion desired bit. Valid <range> is 0-1.\n"
  193. "--ra <range> [-D] Set recursion available bit. Valid <range> is 0-1.\n"
  194. "--z <range> [-Z] Set zero (reserved) bit. Valid <range> is 0-1.\n"
  195. "--ad <range> [-U] Set authenticated data bit. Valid <range> is 0-1.\n"
  196. "--cd <range> [-C] Set checking disabled bit. Valid <range> is 0-1.\n"
  197. "--rc <range> [-E] Set rcode value. Valid <range> is 0-15\n"
  198. "\n"
  199. "The following options set the various section counts (independent of what is\n"
  200. "actually in the section):\n"
  201. "\n"
  202. "--qc <range> [-Y] Set the query count. Valid range is 0-65535.\n"
  203. "--ac <range> [-W] Set the answer count. Valid range is 0-65535.\n"
  204. "--uc <range> [-H] Set the authority count. Valid range is 0-65535.\n"
  205. "--dc <range> [-I] Set the additional count. Valid range is 0-65535.\n"
  206. "\n"
  207. "Other options are:\n"
  208. "\n"
  209. "--ms <range> [-M] Set the size of the message. If the specified size\n"
  210. " smaller than the natural message size, it is truncated.\n"
  211. " If longer, the packet is extended with random values.\n"
  212. " Valid range is 2 to 65536\n"
  213. "\n"
  214. "query Name to query for. The query is for an 'IN' A record. If\n"
  215. " not given, the name 'www.example.com' is used.\n"
  216. "\n"
  217. "The output is a single (very long) line containing the settings of the various\n"
  218. "fields. The settings for the outgoing packet are reported in uppercase letters\n"
  219. "and that of the returned packet in lowercase.\n"
  220. ;
  221. }
  222. // Print version information,
  223. void
  224. CommandOptions::version() {
  225. cout << BADPACKET_VERSION << "\n";
  226. }
  227. // Process single flag that can be stored in the options_ member.
  228. void
  229. CommandOptions::processOptionValue(int c, const char* value) {
  230. // Get values for this option.
  231. int index = OptionInfo::getIndex(c);
  232. const char* name = OptionInfo::name(index);
  233. uint32_t minval = OptionInfo::minval(index);
  234. uint32_t maxval = OptionInfo::maxval(index);
  235. // Split the string up into one or two tokens.
  236. vector<string> tokens = isc::util::str::tokens(string(value), "-");
  237. if ((tokens.size() < 1) || (tokens.size() > 2)) {
  238. isc_throw(isc::BadValue, "value given for " << name << " is '" << value <<
  239. "': it must be in the form 'int' or 'int1-int2'");
  240. }
  241. // Convert to uint32.
  242. try {
  243. options_[index].minimum = boost::lexical_cast<uint32_t>(tokens[0]);
  244. if (tokens.size() == 2) {
  245. options_[index].maximum = boost::lexical_cast<uint32_t>(tokens[1]);
  246. } else {
  247. options_[index].maximum = options_[index].minimum;
  248. }
  249. } catch (boost::bad_lexical_cast) {
  250. isc_throw(isc::BadValue, "value given for " << name << " is '" << value <<
  251. "': it must be in the form 'int' or 'int1-int2'");
  252. }
  253. // Set the limits in the correct order.
  254. if (options_[index].minimum > options_[index].maximum) {
  255. swap(options_[index].minimum, options_[index].maximum);
  256. }
  257. // Check that tokens lie inside the allowed ranges.
  258. if ((tokens.size() == 1) &&
  259. ((options_[index].minimum < OptionInfo::minval(index)) || (options_[index].maximum > maxval))) {
  260. isc_throw(isc::BadValue, "the value of " << options_[index].minimum <<
  261. " given for " << name << " is outside the range of " <<
  262. minval << " to " << maxval);
  263. } else if (options_[index].minimum < minval) {
  264. isc_throw(isc::BadValue, "the lower limit of " << options_[index].minimum <<
  265. " given for " << name << " is below the minimum permitted"
  266. " value of " << minval);
  267. } else if (options_[index].maximum > maxval) {
  268. isc_throw(isc::BadValue, "the upper limit of " << options_[index].maximum <<
  269. " given for " << name << " is above the maximum permitted"
  270. " value of " << maxval);
  271. }
  272. // And finally note that the option was specified on the command line
  273. options_[index].present = true;
  274. }
  275. // Return information about the option - minimum and maximum values and whether
  276. // it was actually specified. (Note that if it wasn't, the maximum and minimum
  277. // values will have been set to the default recorded in the OptionInfo class.)
  278. uint32_t
  279. CommandOptions::minimum(int index) const {
  280. OptionInfo::checkIndex(index);
  281. return (options_[index].minimum);
  282. }
  283. uint32_t
  284. CommandOptions::maximum(int index) const {
  285. OptionInfo::checkIndex(index);
  286. return (options_[index].maximum);
  287. }
  288. bool
  289. CommandOptions::present(int index) const {
  290. OptionInfo::checkIndex(index);
  291. return (options_[index].present);
  292. }
  293. } // namespace badpacket
  294. } // namespace isc