Parcourir la source

[trac703] Added ability to set the packet size

Stephen Morris il y a 14 ans
Parent
commit
bc88971bdb

+ 60 - 33
tests/tools/badpacket/command_options.cc

@@ -67,6 +67,9 @@ CommandOptions::parse(int argc, char* const argv[]) {
     char UC[] = {"uc"};
     char DC[] = {"dc"};
 
+    // Message size
+    char MS[] = {"ms"};
+
     const struct option longopts[] = {
         {HELP,    0, NULL, 'h'},  // Print usage message and exit
         {VERSION, 0, NULL, 'v'},  // Print program version and exit
@@ -87,9 +90,10 @@ CommandOptions::parse(int argc, char* const argv[]) {
         {AC,      1, NULL, 'W'},  // ansWer section count
         {UC,      1, NULL, 'H'},  // autHority section count
         {DC,      1, NULL, 'I'},  // addItional section count
+        {MS,      1, NULL, 'M'},  // Message size
         {NULL,    0, NULL, 0  }
     };
-    const char* shortopts = "hva:p:t:Q:O:A:T:D:R:Z:U:C:E:Y:W:H:I:";
+    const char* shortopts = "hva:p:t:Q:O:A:T:D:R:Z:U:C:E:Y:W:H:I:M:";
 
 
     // Set variables to defaults before parsing
@@ -134,6 +138,7 @@ CommandOptions::parse(int argc, char* const argv[]) {
             case 'W':   // --ac (answer count)
             case 'H':   // --uc (authority count)
             case 'I':   // --dc (additional count)
+            case 'M':   // --ms (message size)
                 processOptionValue(c, optarg);
                 break;
 
@@ -154,14 +159,15 @@ void
 CommandOptions::usage() {
     cout << "Usage: badpacket [options] query\n"
              "\n"
-            "Sends a sequence of packets to the specified nameserver and prints the results.\n"
-            "The packets are valid query packets but the flags field (third and fourth bytes\n"
-            "of the packet) can be set to arbitrary values using the command-line switches.\n"
+            "Sends a sequence of DNS messages to the specified nameserver and prints the\n"
+            " results.  The packets are valid query packets but certain aspects of the\n"
+            " packets (such as the flags fields, section count fields and message size) can\n"
+            "be set to arbitrary values using the command-line switches.\n"
             "\n"
             "In the following list of command-line switches, '<range>' indicates a range of\n"
             "values specified as either <integer> or <integer1>-<integer2> (e.g. both '42'\n"
             "and '0-1' would be valid values for range).  The program sends a sequence of\n"
-            "packets that contain all combinations of the flag values.  For example,\n"
+            "messages that contain all combinations of the flag values.  For example,\n"
             "specifying:\n"
             "\n"
             "--tc 0-1 --op 1-4 --aa 1 --rd 0-1\n"
@@ -174,6 +180,8 @@ CommandOptions::usage() {
             "The long form of the option is given.  It can also be specified as a single-\n"
             "character short-form, which is listed in sqare brackets in the description.\n"
             "\n"
+            "General options are:\n"
+            "\n"
             "--help              [-h] Prints this message and exits.\n"
             "--version           [-v] Prints the program version number.\n"
             "--address <address> [-a] Address of nameserver, which defaults to 127.0.0.1\n"
@@ -181,29 +189,40 @@ CommandOptions::usage() {
             "--timeout <value>   [-t] Timeout value for the query.  Specified in ms, it\n"
             "                         defaults to 500ms.\n"
             "\n"
-            "The following options set fields in the outgoing DNS message flags word\n"
+            "The following options set fields in the outgoing DNS message flags word:\n"
             "\n"
             "--qr <range>        [-Q] Set query/response bit.  Valid <range> is 0-1.\n"
             "--op <range>        [-O] Set opcode.  Valid <range> is 0-15.\n"
             "--aa <range>        [-A] Set authoritative answer bit.  Valid <range> is 0-1.\n"
             "--tc <range>        [-T] Set truncated bit.  Valid <range> is 0-1.\n"
-            "--rd <range>        [-T] Set recursion desired bit.  Valid <range> is 0-1.\n"
-            "--ra <range>        [-T] Set recursion available bit.  Valid <range> is 0-1.\n"
+            "--rd <range>        [-D] Set recursion desired bit.  Valid <range> is 0-1.\n"
+            "--ra <range>        [-D] Set recursion available bit.  Valid <range> is 0-1.\n"
             "--z <range>         [-Z] Set zero (reserved) bit.  Valid <range> is 0-1.\n"
             "--ad <range>        [-U] Set authenticated data bit.  Valid <range> is 0-1.\n"
             "--cd <range>        [-C] Set checking disabled bit.  Valid <range> is 0-1.\n"
             "--rc <range>        [-E] Set rcode value.  Valid <range> is 0-15\n"
             "\n"
             "The following options set the various section counts (independent of what is\n"
-            "actually in the section)\n"
+            "actually in the section):\n"
+            "\n"
+            "--qc <range>        [-Y] Set the query count.  Valid range is 0-65535.\n"
+            "--ac <range>        [-W] Set the answer count.  Valid range is 0-65535.\n"
+            "--uc <range>        [-H] Set the authority count.  Valid range is 0-65535.\n"
+            "--dc <range>        [-I] Set the additional count.  Valid range is 0-65535.\n"
+            "\n"
+            "Other options are:\n"
+            "\n"
+            "--ms <range>        [-M] Set the size of the message.  If the specified size\n"
+            "                         smaller than the natural message size, it is truncated.\n"
+            "                         If longer, the packet is extended with random values.\n"
+            "                         Valid range is 2 to 65536\n"
             "\n"
-            "--qc <range>        [-Q] Set the query count.  Valid range is 0-65535.\n"
-            "--ac <range>        [-Q] Set the answer count.  Valid range is 0-65535.\n"
-            "--uc <range>        [-Q] Set the authority count.  Valid range is 0-65535.\n"
-            "--dc <range>        [-Q] Set the additional count.  Valid range is 0-65535.\n"
+            "query               Name to query for.  The query is for an 'IN' A record.  If\n"
+            "                    not given, the name 'www.example.com' is used.\n"
             "\n"
-            "query               Name to query for.  The query is for an 'IN' A record.\n"
-            "                    If not given, the name 'www.example.com' is used.\n"
+            "The output is a single (very long) line containing the settings of the various\n"
+            "fields.  The settings for the outgoing packet are reported in uppercase letters\n"
+            "and that of the returned packet in lowercase.\n"
             ;
 }
 
@@ -231,52 +250,60 @@ CommandOptions::processOptionValue(int c, const char* value) {
     }
 
     // Convert to uint32.
-    int i = 0;
     try {
-        do {
-            limits_[index][i] = boost::lexical_cast<uint32_t>(tokens[i]);
-            ++i;
-        } while (i < tokens.size());
+        options_[index].minimum = boost::lexical_cast<uint32_t>(tokens[0]);
+        if (tokens.size() == 2) {
+            options_[index].maximum = boost::lexical_cast<uint32_t>(tokens[1]);
+        } else {
+            options_[index].maximum = options_[index].minimum;
+        }
     } catch (boost::bad_lexical_cast) {
         isc_throw(isc::BadValue, "value given for " << name << " is '" << value <<
                   "': it must be in the form 'int' or 'int1-int2'");
     }
 
     // Set the limits in the correct order.
-    if (tokens.size() == 1) {
-        limits_[index][1] = limits_[index][0];
-    } else if (limits_[index][0] > limits_[index][1]) {
-        swap(limits_[index][0], limits_[index][1]);
+    if (options_[index].minimum > options_[index].maximum) {
+        swap(options_[index].minimum, options_[index].maximum);
     }
 
     // Check that tokens lie inside the allowed ranges
-    if ((tokens.size() == 1) &&
-        ((limits_[index][0] < OptionInfo::minval(index)) || (limits_[index][0] > maxval))) {
-        isc_throw(isc::BadValue, "the value of " << limits_[index][0] <<
+    if ((tokens.size() == 2) &&
+        ((options_[index].minimum < OptionInfo::minval(index)) || (options_[index].maximum > maxval))) {
+        isc_throw(isc::BadValue, "the value of " << options_[index].minimum <<
                   " given for " << name << " is outside the range of " <<
                   minval << " to " << maxval);
-    } else if (limits_[index][0] < minval) {
-        isc_throw(isc::BadValue, "the lower limit of " << limits_[index][0] <<
+    } else if (options_[index].minimum < minval) {
+        isc_throw(isc::BadValue, "the lower limit of " << options_[index].minimum <<
                   " given for " << name << " is below the minimum permitted"
                   " value of " << minval);
-    } else if (limits_[index][1] > maxval) {
-        isc_throw(isc::BadValue, "the upper limit of " << limits_[index][1] <<
+    } else if (options_[index].maximum > maxval) {
+        isc_throw(isc::BadValue, "the upper limit of " << options_[index].maximum <<
                   " given for " << name << " is above the maximum permitted"
                   " value of " << maxval);
     }
+
+    // And finally note that the option was specified on the command line
+    options_[index].present = true;
 }
 
 // Minimum and maximum value of the flag
 uint32_t
 CommandOptions::minimum(int index) const {
     OptionInfo::checkIndex(index);
-    return (limits_[index][0]);
+    return (options_[index].minimum);
 }
 
 uint32_t
 CommandOptions::maximum(int index) const {
     OptionInfo::checkIndex(index);
-    return (limits_[index][1]);
+    return (options_[index].maximum);
+}
+
+bool
+CommandOptions::present(int index) const {
+    OptionInfo::checkIndex(index);
+    return (options_[index].present);
 }
 
 } // namespace badpacket

+ 16 - 2
tests/tools/badpacket/command_options.h

@@ -69,6 +69,14 @@ public:
     ///         the option was not specified on the command line).
     uint32_t maximum(int index) const;
 
+    /// \brief Return if option was given on command line
+    ///
+    /// \param index Index of the command-line option.
+    ///
+    /// \return true if the option was present, false if not.
+    bool present(int index) const;
+
+
     /// \brief Return Target Address
     std::string getAddress() const {
         return address_;
@@ -97,7 +105,9 @@ public:
         qname_ = "www.example.com";
 
         for (int i = 0; i < OptionInfo::SIZE; ++i) {
-            limits_[i][0] = limits_[i][1] = OptionInfo::defval(i);
+            options_[i].minimum = OptionInfo::defval(i);
+            options_[i].maximum = OptionInfo::defval(i);
+            options_[i].present = false;
         }
     }
 
@@ -134,7 +144,11 @@ protected:
     // Member variables
 
 private:
-    uint32_t        limits_[OptionInfo::SIZE][2];
+    struct {
+        uint32_t    minimum;        ///< Minimum value specified
+        uint32_t    maximum;        ///< Maximum value specified
+        bool        present;        ///< true if specified on command line
+    } options_[OptionInfo::SIZE];   ///< Information about command options
                                     ///< Value of options (minimum and maximum)
     std::string     address_;       ///< Address to where query is sent
     uint16_t        port_;          ///< Target port

+ 12 - 1
tests/tools/badpacket/option_info.cc

@@ -21,6 +21,16 @@ namespace {
 // information is duplicated here and where the long options are specified for
 // getopt_long, but this inconvenience is outweighed by the simplified command
 // processing.
+//
+// Fields are:
+// * Short option name
+// * Long option name
+// * Offset of 16-bit word holding datum in DNS message (if applicable)
+// * Bit mask for the data (if applicable)
+// * Offset of the bit field in the word (if applicable)
+// * Default value (this can be ignored if applicable)
+// * Minimum value specified on command line
+// * Maximum value specified on command line
 
 isc::badpacket::OptionInfo::Parameter option_information[] = {
     {'Q', "qr",  2, 0x8000,  15,  0, 0,      1},
@@ -36,7 +46,8 @@ isc::badpacket::OptionInfo::Parameter option_information[] = {
     {'Y', "qc",  4,      0,   0,  1, 0, 0xFFFF},
     {'W', "ac",  6,      0,   0,  0, 0, 0xFFFF},
     {'H', "uc",  8,      0,   0,  0, 0, 0xFFFF},
-    {'I', "dc", 10,      0,   0,  0, 0, 0xFFFF}
+    {'I', "dc", 10,      0,   0,  0, 0, 0xFFFF},
+    {'M', "ms",  0,      0,   0,  0, 1,  65536}
 };
 
 }   // Anonymous namespace

+ 22 - 15
tests/tools/badpacket/option_info.h

@@ -42,21 +42,28 @@ public:
     /// The data for the flags options are held in an array.  Although an enum,
     /// only the numeric values are used - they are indexes into arrays.
     enum Index {
-        QR = 0,     // Query/response
-        OP = 1,     // Opcode
-        AA = 2,     // Authoritative answer
-        TC = 3,     // Truncated
-        RD = 4,     // Recursion desired
-        RA = 5,     // Recursion available
-        Z  = 6,     // Zero (reserved)
-        AD = 7,     // Authenticated data
-        CD = 8,     // Checking disabled
-        RC = 9,     // Response code
-        QC = 10,    // Query count
-        AC = 11,    // Answer count
-        UC = 12,    // Authority count
-        DC = 13,    // Additional count
-        SIZE = 14   // Number of index values
+        FLAGS_START = 0,    // Start of flags field codes
+        QR = 0,             // Query/response
+        OP = 1,             // Opcode
+        AA = 2,             // Authoritative answer
+        TC = 3,             // Truncated
+        RD = 4,             // Recursion desired
+        RA = 5,             // Recursion available
+        Z  = 6,             // Zero (reserved)
+        AD = 7,             // Authenticated data
+        CD = 8,             // Checking disabled
+        RC = 9,             // Response code
+        FLAGS_END = 9,      // End of flags field codes
+        COUNT_START = 10,   // Start of count fields
+        QC = 10,            // Query count
+        AC = 11,            // Answer count
+        UC = 12,            // Authority count
+        DC = 13,            // Additional count
+        COUNT_END = 13,     // End of count fields
+        OTHER_START = 14,   // Start of other fields
+        MS = 14,            // Message size
+        OTHER_END = 14,     // End of other fields
+        SIZE = 15           // Number of index values
     };
 
     /// \brief Option Parameters

+ 124 - 96
tests/tools/badpacket/scan.cc

@@ -17,7 +17,7 @@
 #include <sstream>
 #include <string>
 
-#include <boost/scoped_ptr.hpp>
+#include <stdlib.h>
 
 #include <asio.hpp>
 
@@ -60,100 +60,125 @@ Scan::scan(const CommandOptions& options) {
     MessageRenderer renderer(*msgbuf);
     message.toWire(renderer);
 
-    iterateFlagsFields(msgbuf, options);
+    iterateFlagsStart(msgbuf, options);
 }
 
-// Iterate through the various settings in the flags fields
+// Iterate through the various settings in the flags fields.
 void
-Scan::iterateFlagsFields(OutputBufferPtr& msgbuf, const CommandOptions& options) {
-
-    // Loop through the flags setting data.  This is quite deep nesting,
-    // but we will continue to indent so as to make things clear (for those
-    // readers lucky enough to have a wide screen).
+Scan::iterateFlagsStart(OutputBufferPtr& msgbuf, const CommandOptions& options) {
     HeaderFlags flags;
-    for (uint32_t qr =  options.minimum(OptionInfo::QR);
-                  qr <= options.maximum(OptionInfo::QR); ++qr) {
-        flags.set(OptionInfo::QR, qr);
-
-        for (uint32_t op =  options.minimum(OptionInfo::OP);
-                      op <= options.maximum(OptionInfo::OP); ++op) {
-            flags.set(OptionInfo::OP, op);
-
-            for (uint32_t aa =  options.minimum(OptionInfo::AA);
-                          aa <= options.maximum(OptionInfo::AA); ++aa) {
-                flags.set(OptionInfo::AA, aa);
-
-                for (uint32_t tc =  options.minimum(OptionInfo::TC);
-                              tc <= options.maximum(OptionInfo::TC); ++tc) {
-                    flags.set(OptionInfo::TC, tc);
-
-                    for (uint32_t rd =  options.minimum(OptionInfo::RD);
-                                  rd <= options.maximum(OptionInfo::RD); ++rd) {
-                        flags.set(OptionInfo::RD, rd);
-
-                        for (uint32_t ra =  options.minimum(OptionInfo::RA);
-                                      ra <= options.maximum(OptionInfo::RA); ++ra) {
-                            flags.set(OptionInfo::RA, ra);
-
-                            for (uint32_t z =  options.minimum(OptionInfo::Z);
-                                          z <= options.maximum(OptionInfo::Z); ++z) {
-                                flags.set(OptionInfo::Z, z);
-
-                                for (uint32_t ad =  options.minimum(OptionInfo::AD);
-                                              ad <= options.maximum(OptionInfo::AD); ++ad) {
-                                    flags.set(OptionInfo::AD, ad);
-
-                                    for (uint32_t cd =  options.minimum(OptionInfo::CD);
-                                                  cd <= options.maximum(OptionInfo::CD); ++cd) {
-                                        flags.set(OptionInfo::CD, cd);
-
-                                        for (uint32_t rc =  options.minimum(OptionInfo::RC);
-                                                      rc <= options.maximum(OptionInfo::RC); ++rc) {
-                                            flags.set(OptionInfo::RC, rc);
-
-                                            // Set the flags in the message.
-                                            msgbuf->writeUint16At(flags.getValue(), 2);
-
-                                            // And for this flag combination, iterate over the section
-                                            // count fields.
-                                            iterateCountFields(msgbuf, options);
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
+    iterateFlags(msgbuf, options, flags, OptionInfo::FLAGS_START,
+                 OptionInfo::FLAGS_END);
+}
+void
+Scan::iterateFlags(OutputBufferPtr& msgbuf, const CommandOptions& options,
+                   HeaderFlags& flags, int index, int maxindex)
+{
+    // Is the index valid?
+    if (index <= maxindex) {
+
+        // Index is valid, did the command line specify a range of values for
+        // this field?
+        if (options.present(index)) {
+
+            // It did, so loop between minimum and maximum values given.
+            for (uint32_t i = options.minimum(index);
+                          i <= options.maximum(index); ++i) {
+                flags.set(index, i);
+
+                // Recurse to set the next option.
+                iterateFlags(msgbuf, options, flags, (index + 1), maxindex);
             }
+        } else {
+
+            // Not specified on command line, so set the default value in the
+            // flags and process the next index.
+            flags.set(index, OptionInfo::defval(index));
+            iterateFlags(msgbuf, options, flags, (index + 1), maxindex);
         }
+    } else {
+
+        // Index is not valid, so we have recursed enough to process all the
+        // flags fields.  Set the value in the message header and recurse.
+        // (In the next statement, the "word" offset of in the header is the
+        // same for all flags options, so the value for an arbitrary field
+        // (QR) has been used.)
+        msgbuf->writeUint16At(flags.getValue(),
+                              OptionInfo::word(OptionInfo::QR));
+        iterateCountStart(msgbuf, options);
     }
 }
 
 // Iterate through the various count fields
 void
-Scan::iterateCountFields(OutputBufferPtr& msgbuf, const CommandOptions& options) {
-    for (uint32_t qc =  options.minimum(OptionInfo::QC);
-                  qc <= options.maximum(OptionInfo::QC); ++qc) {
-        msgbuf->writeUint16At(qc, OptionInfo::word(OptionInfo::QC));
+Scan::iterateCountStart(OutputBufferPtr& msgbuf, const CommandOptions& options)
+{
+    iterateCount(msgbuf, options, OptionInfo::COUNT_START,
+                 OptionInfo::COUNT_END);
+}
 
-        for (uint32_t ac =  options.minimum(OptionInfo::AC);
-                      ac <= options.maximum(OptionInfo::AC); ++ac) {
-            msgbuf->writeUint16At(ac, OptionInfo::word(OptionInfo::AC));
+void
+Scan::iterateCount(OutputBufferPtr& msgbuf, const CommandOptions& options,
+                   int index, int maxindex)
+{
+    // If the index is valid, process the iteration over the range for this
+    // flags field.
+    if (index <= maxindex) {
+
+        // Index is valid, did the command line specify a range of values for
+        // this field?
+        if (options.present(index)) {
+
+            // It did, so loop between minimum and maximum values given.
+            for (uint32_t i = options.minimum(index);
+                          i <= options.maximum(index); ++i) {
+
+                // Set the value in the message buffer header and recurse to
+                // the next option.
+                msgbuf->writeUint16At(i, OptionInfo::word(index));
+                iterateCount(msgbuf, options, (index + 1), maxindex);
+            }
+        } else {
 
-            for (uint32_t uc =  options.minimum(OptionInfo::UC);
-                          uc <= options.maximum(OptionInfo::UC); ++uc) {
-                msgbuf->writeUint16At(uc, OptionInfo::word(OptionInfo::UC));
+            // Not specified on command line, so leave the default value and
+            // and process the next index.
+            iterateCount(msgbuf, options, (index + 1), maxindex);
+        }
+    } else {
 
-                for (uint32_t dc =  options.minimum(OptionInfo::DC);
-                              dc <= options.maximum(OptionInfo::DC); ++dc) {
-                    msgbuf->writeUint16At(dc, OptionInfo::word(OptionInfo::DC));
+        // Index is not valid, so we have recursed enough to process all the
+        // flags fields.  Do the next stage of the processing.
+        sizeMessage(msgbuf, options);
+    }
+}
+
+// Size the message
+void
+Scan::sizeMessage(OutputBufferPtr& msgbuf, const CommandOptions& options) {
 
-                    // Do the I/O.
-                    scanOne(msgbuf, options);
+    if (options.present(OptionInfo::MS)) {
 
-                }
+        // Iterate over the range of message sizes
+        for (size_t i = options.minimum(OptionInfo::MS);
+                    i <= options.maximum(OptionInfo::MS); ++i) {
+
+            // Copy the data into a new buffer.
+            OutputBufferPtr newbuf(new OutputBuffer(i));
+            newbuf->writeData(msgbuf->getData(), min(msgbuf->getLength(), i));
+
+            // Pad with junk (actually pseudo-random data) if the new buffer is
+            // longer than the old.
+            for (size_t j = newbuf->getLength(); j < i; ++j) {
+                newbuf->writeUint8(static_cast<uint8_t>(rand() & 0xFFU));
             }
+
+            // ... and process.
+            scanOne(newbuf, options);
         }
+    } else {
+
+        // No packet size given, just process the message as it.
+        scanOne(msgbuf, options);
     }
 }
 
@@ -212,35 +237,38 @@ Scan::getFields(isc::dns::OutputBufferPtr& msgbuf) {
 
     std::ostringstream os;
     os << std::hex << std::uppercase <<
-        "QR:" << flags.get(OptionInfo::QR) << " " <<
-        "OP:" << flags.get(OptionInfo::OP) << " " <<
-        "AA:" << flags.get(OptionInfo::AA) << " " <<
-        "TC:" << flags.get(OptionInfo::TC) << " " <<
-        "RD:" << flags.get(OptionInfo::RD) << " " <<
-        "RA:" << flags.get(OptionInfo::RA) << " " <<
-         "Z:" << flags.get(OptionInfo::Z)  << " " <<
-        "AD:" << flags.get(OptionInfo::AD) << " " <<
-        "CD:" << flags.get(OptionInfo::CD) << " " <<
-        "RC:" << flags.get(OptionInfo::RC);
+         "QR:" << flags.get(OptionInfo::QR) <<
+        " OP:" << flags.get(OptionInfo::OP) <<
+        " AA:" << flags.get(OptionInfo::AA) <<
+        " TC:" << flags.get(OptionInfo::TC) <<
+        " RD:" << flags.get(OptionInfo::RD) <<
+        " RA:" << flags.get(OptionInfo::RA) <<
+        " Z:"  << flags.get(OptionInfo::Z)  <<
+        " AD:" << flags.get(OptionInfo::AD) <<
+        " CD:" << flags.get(OptionInfo::CD) <<
+        " RC:" << flags.get(OptionInfo::RC);
 
     // Section count fields
-    os << " ";
 
     inbuf.setPosition(OptionInfo::word(OptionInfo::QC));
-    os << std::hex << std::uppercase <<
-        "QC:" << inbuf.readUint16() << " ";
+    os << std::dec << std::uppercase <<
+        " QC:" << inbuf.readUint16();
 
     inbuf.setPosition(OptionInfo::word(OptionInfo::AC));
-    os << std::hex << std::uppercase <<
-        "AC:" << inbuf.readUint16() << " ";
+    os << std::dec << std::uppercase <<
+        " AC:" << inbuf.readUint16();
 
     inbuf.setPosition(OptionInfo::word(OptionInfo::UC));
-    os << std::hex << std::uppercase <<
-        "UC:" << inbuf.readUint16() << " ";
+    os << std::dec << std::uppercase <<
+        " UC:" << inbuf.readUint16();
 
     inbuf.setPosition(OptionInfo::word(OptionInfo::DC));
-    os << std::hex << std::uppercase <<
-        "DC:" << inbuf.readUint16();
+    os << std::dec << std::uppercase <<
+        " DC:" << inbuf.readUint16();
+
+    // ... and message size
+    os << std::dec << std::uppercase <<
+        " MS:" << msgbuf->getLength();
 
     return (os.str());
 }

+ 73 - 14
tests/tools/badpacket/scan.h

@@ -26,6 +26,7 @@
 #include <dns/buffer.h>
 
 #include "command_options.h"
+#include "header_flags.h"
 
 namespace isc {
 namespace badpacket {
@@ -59,35 +60,93 @@ public:
     virtual void operator()(asiolink::IOFetch::Result result);
 
 private:
-    /// \brief Set Flags Fields Options
+    /// \brief Iterate over flags fields options
     ///
-    /// Iterates through all combinations of the DNS message flags fields specified
-    /// on the command line and calls scanOne for each combination.
+    /// This method relies on the fact that data concerning the settings for
+    /// the fields in the flags word of the DNS message are held at adjacent
+    /// elements in the various data arrays and so can be accessed by a set
+    /// of contiguous index values.
+    ///
+    /// The method is passed an index value and the maximum valid index value.
+    /// If a set of values for the field at the given index was specified on
+    /// the command line, it loops through those values and sets the appropriate
+    /// value in the a copy of the DNS message header flags.  It then calls
+    /// itself with an incremented index.  If the value was not given, it just
+    /// sets a default value calls itself (with the incremented index).  When
+    /// called with an index above the maximum valid index, the header flags
+    /// in the message buffer are set and the next stage of processing called.
+    ///
+    /// In this way, all fields can be cycled through without the need for a
+    /// single function to nest loops very deeply.
     ///
     /// \param msgbuf Message that will be sent to the remote nameserver.  The
     ///        QID given will be ignored - the value used will be determined by
     ///        the sending code
     /// \param options Command-line options (the important ones being address,
     ///        port and timeout).
-    void iterateFlagsFields(isc::dns::OutputBufferPtr& msgbuf,
-                 const CommandOptions& options);
+    /// \param flags Header flags
+    /// \param index Index of the current field to be processed.
+    /// \param maxindex Maximum valid index value
+    void iterateFlags(isc::dns::OutputBufferPtr& msgbuf,
+                 const CommandOptions& options, HeaderFlags& flags,
+                 int index, int maxindex);
+
+    /// \brief Start iterating over flags field options
+    ///
+    /// Kicks off the call to \c iterateFlags by calling it with the initial
+    /// index value.
+    ///
+    /// \param msgbuf Message that will be sent to the remote nameserver.  The
+    ///        QID given will be ignored - the value used will be determined by
+    ///        the sending code
+    /// \param options Command-line options (the important ones being address,
+    ///        port and timeout).
+    void iterateFlagsStart(isc::dns::OutputBufferPtr& msgbuf,
+                           const CommandOptions& options);
+
+    /// \brief Iterate over count fields
+    ///
+    /// In a manner similar to iterateFlags, this iterates over all specified
+    /// values for each count field, recursively calling itself to process the
+    /// next field.  When all fields have been processed, it chains to the
+    /// next stage of packet processing.
+    ///
+    /// \param msgbuf Message that will be sent to the remote nameserver.  The
+    ///        QID given will be ignored - the value used will be determined by
+    ///        the sending code
+    /// \param options Command-line options (the important ones being address,
+    ///        port and timeout).
+    /// \param index Index of the current field to be processed.
+    /// \param maxindex Maximum valid index value
+    void iterateCount(isc::dns::OutputBufferPtr& msgbuf,
+                      const CommandOptions& options, int index, int maxindex);
 
-    /// \brief Set Count Fields Options
+    /// \brief Start iterating over count fields
     ///
-    /// Iterates through all combinations of the count fields specified on the
-    /// command line.
+    /// Kicks off the call to \c iterateCount by calling it with the initial
+    /// index value.
     ///
-    /// The count fields are set by default to question count = 1, all the rest
-    /// zero.  Command-line options allow these values to be altered, although
-    /// the actual contents of the sections are not changed.
+    /// \param msgbuf Message that will be sent to the remote nameserver.  The
+    ///        QID given will be ignored - the value used will be determined by
+    ///        the sending code
+    /// \param options Command-line options (the important ones being address,
+    ///        port and timeout).
+    void iterateCountStart(isc::dns::OutputBufferPtr& msgbuf,
+                           const CommandOptions& options);
+
+    /// \brief Iterate over message sizes
+    ///
+    /// If the message size option is given on the command line, the message
+    /// sent to the remote system is either truncated or extended (with zeroes)
+    /// before being set.
     ///
     /// \param msgbuf Message that will be sent to the remote nameserver.  The
     ///        QID given will be ignored - the value used will be determined by
     ///        the sending code
     /// \param options Command-line options (the important ones being address,
     ///        port and timeout).
-    void iterateCountFields(isc::dns::OutputBufferPtr& msgbuf,
-                 const CommandOptions& options);
+    void sizeMessage(isc::dns::OutputBufferPtr& msgbuf,
+                     const CommandOptions& options);
 
     /// \brief Scan One Value
     ///
@@ -117,7 +176,7 @@ private:
     /// \brief Get Fields
     ///
     /// Interprets the flags fields in a DNS message and converts them to a
-    /// terxtual format.
+    /// textual format.
     ///
     /// \param msg Message for which the header is value
     ///

+ 163 - 347
tests/tools/badpacket/tests/command_options_unittest.cc

@@ -33,47 +33,147 @@ class CommandOptionsTest : public virtual ::testing::Test,
 public:
 
     /// \brief Default Constructor
-    CommandOptionsTest() {}
+    CommandOptionsTest()
+    {}
 
-    /// \brief Checks the minimum and maximum () specified for an option
+    /// \brief Check Non-Limit Options
     ///
-    /// Checks the () for one of the options whose values are stored in the
-    /// class's limits) array.
+    /// Checks that the options that are NOT related to the message are set to
+    /// their default values.
+    void checkDefaultOtherValues() {
+        EXPECT_EQ("127.0.0.1", getAddress());
+        EXPECT_EQ(53, getPort());
+        EXPECT_EQ(500, getTimeout());
+        EXPECT_EQ("www.example.com", getQname());
+    }
+
+    /// \brief Checks the minimum and maximum value specified for an option
+    ///
+    /// Checks the values for one of the options whose values are stored in the
+    /// class's options_) array.
     ///
     /// \param index Index of the option in the limits_ array
     /// \param minval Expected minimum value
     /// \param maxval Expected maximum value
-    void checkValuePair(int index, uint32_t minval = 0, uint32_t maxval = 0)
-    {
+    void checkValuePair(int index, uint32_t minval = 0, uint32_t maxval = 0) {
         EXPECT_EQ(minimum(index), minval);
         EXPECT_EQ(maximum(index), maxval);
     }
 
-    /// \brief Checks that all options giving flags () are zero.
+    /// \brief Checks that all options are at default values
+    ///
+    /// Checks that all options have both their maximum and minimum set
+    /// to the default values.
     ///
-    /// Checks that all options whose () are stored in the class's limits_
-    /// array have both their maximum and minimum () set to zero.
-    void checkDefaultLimitsValues() {
-        checkValuePair(OptionInfo::QR);
-        checkValuePair(OptionInfo::OP);
-        checkValuePair(OptionInfo::AA);
-        checkValuePair(OptionInfo::TC);
-        checkValuePair(OptionInfo::Z);
-        checkValuePair(OptionInfo::AD);
-        checkValuePair(OptionInfo::CD);
-        checkValuePair(OptionInfo::RC);
+    /// \param except Index not to check. (This allows options not being tested
+    ///        to be checked to see that they are at the default value.)  As all
+    ///        index values are positive, a negative value means check
+    ///        everything.
+    void checkDefaultLimitsValues(int except = -1) {
+        for (int i = 0; i < OptionInfo::SIZE; ++i) {
+            if (i != except) {
+                checkValuePair(i, OptionInfo::defval(i),
+                               OptionInfo::defval(i));
+            }
+        }
     }
 
-    /// \brief Check Non-Limit Options
+    /// \brief Check valid command option
     ///
-    /// Checks that the options whose () are NOT stored in the limits_
-    /// array are set to their default ().
-    void
-    checkDefaultOtherValues() {
-        EXPECT_EQ("127.0.0.1", getAddress());
-        EXPECT_EQ(53, getPort());
-        EXPECT_EQ(500, getTimeout());
-        EXPECT_EQ("www.example.com", getQname());
+    /// Checks that the command line specification of one of the options taking
+    /// a value correctly processes the option.
+    ///
+    /// \param index Option index
+    /// \param optflag Option flag (in the form '--option')
+    /// \param optval Value to be passed to the option.
+    /// \param minval Expected minimum value
+    /// \param maxval Expected maximum value
+    void checkCommandValid(int index, const char* optflag, const char* optval,
+                           uint32_t minval, uint32_t maxval) {
+
+        // Set up the command line and parse it.
+        const char* argv[] = {"badpacket", NULL, NULL};
+        argv[1] = optflag;
+        argv[2] = optval;
+        int argc = 3;
+        parse(argc, const_cast<char**>(argv));
+
+        // Check the results.  Everything should be at the defaults except for
+        // the specified option, where the minimum and maximum should be as
+        // given.
+        checkDefaultOtherValues();
+        checkDefaultLimitsValues(index);
+        checkValuePair(index, minval, maxval);
+    }
+
+    /// \brief Check invalid command option
+    ///
+    /// Passed a command with an invalid value, which that the parsing throws
+    /// a BadValue exception.
+    ///
+    /// \param optflag Option flag (in the form '--option')
+    /// \param optval Value to be passed to the option.
+    void checkCommandInvalid(const char* optflag, const char* optval) {
+
+        // Set up the command line and parse it.
+        const char* argv[] = {"badpacket", NULL, NULL};
+        argv[1] = optflag;
+        argv[2] = optval;
+        int argc = 3;
+        EXPECT_THROW(parse(argc, const_cast<char**>(argv)), isc::BadValue);
+    }
+
+    /// \brief Check one-bit field
+    ///
+    /// Explicitly for those fields in the flags word that are one bit wide,
+    /// perform a series of tests to check that they accept valid values and
+    /// reject invalid ones.
+    ///
+    /// \param index Option index
+    /// \param optflag Option flag (in the form '--option')
+    void checkOneBitField(int index, const char* optflag) {
+        checkCommandValid(index, optflag, "0", 0, 0);
+        checkCommandValid(index, optflag, "1", 1, 1);
+        checkCommandValid(index, optflag, "0-1", 0, 1);
+        checkCommandValid(index, optflag, "1-0", 0, 1);
+        checkCommandInvalid(optflag, "0-3");
+        checkCommandInvalid(optflag, "4");
+        checkCommandInvalid(optflag, "xyz");
+    }
+
+    /// \brief Check four-bit field
+    ///
+    /// Explicitly for those fields in the flags word that are four bits wide,
+    /// perform a series of tests to check that they accept valid values and
+    /// reject invalid ones.
+    ///
+    /// \param index Option index
+    /// \param optflag Option flag (in the form '--option')
+    void checkFourBitField(int index, const char* optflag) {
+        checkCommandValid(index, optflag, "0", 0, 0);
+        checkCommandValid(index, optflag, "15", 15, 15);
+        checkCommandValid(index, optflag, "0-15", 0, 15);
+        checkCommandValid(index, optflag, "15-0", 0, 15);
+        checkCommandInvalid(optflag, "0-17");
+        checkCommandInvalid(optflag, "24");
+        checkCommandInvalid(optflag, "xyz");
+    }
+
+    /// \brief Check sixteen-bit field
+    ///
+    /// Explicitly test the parsing of the fields that can take a 16-bit
+    /// value ranging from 0 to 65535.
+    ///
+    /// \param index Option index
+    /// \param optflag Option flag (in the form '--option')
+    void checkSixteenBitField(int index, const char* optflag) {
+        checkCommandValid(index, optflag, "0", 0, 0);
+        checkCommandValid(index, optflag, "65535", 65535, 65535);
+        checkCommandValid(index, optflag, "0-65535", 0, 65535);
+        checkCommandValid(index, optflag, "65535-0", 0, 65535);
+        checkCommandInvalid(optflag, "0-65536");
+        checkCommandInvalid(optflag, "65537");
+        checkCommandInvalid(optflag, "xyz");
     }
 };
 
@@ -133,351 +233,67 @@ TEST_F(CommandOptionsTest, parameter) {
 
 // The various tests of the different flags
 TEST_F(CommandOptionsTest, qr) {
-
-    // Specifying a value of zero, we expect all flag () to be zero
-    const char* argv1[] = {"badpacket",  "--qr", "0"};
-    int argc1 = sizeof(argv1) / sizeof(const char*);
-
-    parse(argc1, const_cast<char**>(argv1));
-    checkDefaultOtherValues();
-    checkDefaultLimitsValues();
-
-    // Check that a value of 1 is accepted
-    const char* argv2[] = {"badpacket",  "--qr", "1"};
-    int argc2 = sizeof(argv2) / sizeof(const char*);
-
-    parse(argc2, const_cast<char**>(argv2));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR, 1, 1);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
-
-    // Check that a range is accepted (in this case, specified backwards)
-    const char* argv3[] = {"badpacket",  "--qr", "1-0"};
-    int argc3 = sizeof(argv3) / sizeof(const char*);
-
-    parse(argc3, const_cast<char**>(argv3));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR, 0, 1);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
+    checkOneBitField(OptionInfo::QR, "--qr");
 }
 
-// The following are cut-and-pasted from the "qr" test.  (It is awkward to put
-// the test into a general function because of differing string () and
-// variables.)
-
 TEST_F(CommandOptionsTest, op) {
-
-    // Specifying a value of zero, we expect all flag () to be zero
-    const char* argv1[] = {"badpacket",  "--op", "0"};
-    int argc1 = sizeof(argv1) / sizeof(const char*);
-
-    parse(argc1, const_cast<char**>(argv1));
-    checkDefaultOtherValues();
-    checkDefaultLimitsValues();
-
-    // Check that a value of 1 is accepted
-    const char* argv2[] = {"badpacket",  "--op", "8"};
-    int argc2 = sizeof(argv2) / sizeof(const char*);
-
-    parse(argc2, const_cast<char**>(argv2));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP, 8, 8);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
-
-    // Check that a range is accepted (in this case, specified backwards)
-    const char* argv3[] = {"badpacket",  "--op", "14-2"};
-    int argc3 = sizeof(argv3) / sizeof(const char*);
-
-    parse(argc3, const_cast<char**>(argv3));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP, 2, 14);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
+    checkFourBitField(OptionInfo::OP, "--op");
 }
 
 TEST_F(CommandOptionsTest, aa) {
-
-    // Specifying a value of zero, we expect all flag () to be zero
-    const char* argv1[] = {"badpacket",  "--aa", "0"};
-    int argc1 = sizeof(argv1) / sizeof(const char*);
-
-    parse(argc1, const_cast<char**>(argv1));
-    checkDefaultOtherValues();
-    checkDefaultLimitsValues();
-
-    // Check that a value of 1 is accepted
-    const char* argv2[] = {"badpacket",  "--aa", "1"};
-    int argc2 = sizeof(argv2) / sizeof(const char*);
-
-    parse(argc2, const_cast<char**>(argv2));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA, 1, 1);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
-
-    // Check that a range is accepted.
-    const char* argv3[] = {"badpacket",  "--aa", "1-0"};
-    int argc3 = sizeof(argv3) / sizeof(const char*);
-
-    parse(argc3, const_cast<char**>(argv3));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA, 0, 1);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
+    checkOneBitField(OptionInfo::AA, "--aa");
 }
 
 TEST_F(CommandOptionsTest, tc) {
-
-    // Specifying a value of zero, we expect all flag () to be zero
-    const char* argv1[] = {"badpacket",  "--tc", "0"};
-    int argc1 = sizeof(argv1) / sizeof(const char*);
-
-    parse(argc1, const_cast<char**>(argv1));
-    checkDefaultOtherValues();
-    checkDefaultLimitsValues();
-
-    // Check that a value of 1 is accepted
-    const char* argv2[] = {"badpacket",  "--tc", "1"};
-    int argc2 = sizeof(argv2) / sizeof(const char*);
-
-    parse(argc2, const_cast<char**>(argv2));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC, 1, 1);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
-
-    // Check that a range is accepted.
-    const char* argv3[] = {"badpacket",  "--tc", "1-0"};
-    int argc3 = sizeof(argv3) / sizeof(const char*);
-
-    parse(argc3, const_cast<char**>(argv3));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC, 0, 1);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
+    checkOneBitField(OptionInfo::TC, "--tc");
 }
 
 TEST_F(CommandOptionsTest, z) {
-
-    // Specifying a value of zero, we expect all flag () to be zero
-    const char* argv1[] = {"badpacket",  "--z", "0"};
-    int argc1 = sizeof(argv1) / sizeof(const char*);
-
-    parse(argc1, const_cast<char**>(argv1));
-    checkDefaultOtherValues();
-    checkDefaultLimitsValues();
-
-    // Check that a value of 1 is accepted
-    const char* argv2[] = {"badpacket",  "--z", "1"};
-    int argc2 = sizeof(argv2) / sizeof(const char*);
-
-    parse(argc2, const_cast<char**>(argv2));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z, 1, 1);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
-
-    // Check that a range is accepted.
-    const char* argv3[] = {"badpacket",  "--z", "1-0"};
-    int argc3 = sizeof(argv3) / sizeof(const char*);
-
-    parse(argc3, const_cast<char**>(argv3));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z, 0, 1);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
+    checkOneBitField(OptionInfo::Z, "--z");
 }
 
 TEST_F(CommandOptionsTest, ad) {
-
-    // Specifying a value of zero, we expect all flag () to be zero
-    const char* argv1[] = {"badpacket",  "--ad", "0"};
-    int argc1 = sizeof(argv1) / sizeof(const char*);
-
-    parse(argc1, const_cast<char**>(argv1));
-    checkDefaultOtherValues();
-    checkDefaultLimitsValues();
-
-    // Check that a value of 1 is accepted
-    const char* argv2[] = {"badpacket",  "--ad", "1"};
-    int argc2 = sizeof(argv2) / sizeof(const char*);
-
-    parse(argc2, const_cast<char**>(argv2));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD, 1, 1);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
-
-    // Check that a range is accepted.
-    const char* argv3[] = {"badpacket",  "--ad", "0-1"};
-    int argc3 = sizeof(argv3) / sizeof(const char*);
-
-    parse(argc3, const_cast<char**>(argv3));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD, 0, 1);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC);
+    checkOneBitField(OptionInfo::AD, "--ad");
 }
 
 TEST_F(CommandOptionsTest, cd) {
+    checkOneBitField(OptionInfo::CD, "--cd");
+}
 
-    // Specifying a value of zero, we expect all flag () to be zero
-    const char* argv1[] = {"badpacket",  "--cd", "0"};
-    int argc1 = sizeof(argv1) / sizeof(const char*);
+TEST_F(CommandOptionsTest, rc) {
+    checkFourBitField(OptionInfo::RC, "--rc");
+}
 
-    parse(argc1, const_cast<char**>(argv1));
-    checkDefaultOtherValues();
-    checkDefaultLimitsValues();
+// Section count options
 
-    // Check that a value of 1 is accepted
-    const char* argv2[] = {"badpacket",  "--cd", "1"};
-    int argc2 = sizeof(argv2) / sizeof(const char*);
-
-    parse(argc2, const_cast<char**>(argv2));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD, 1, 1);
-    checkValuePair(OptionInfo::RC);
-
-    // Check that a range is accepted.
-    const char* argv3[] = {"badpacket",  "--cd", "1-0"};
-    int argc3 = sizeof(argv3) / sizeof(const char*);
-
-    parse(argc3, const_cast<char**>(argv3));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD, 0, 1);
-    checkValuePair(OptionInfo::RC);
+TEST_F(CommandOptionsTest, qc) {
+    checkSixteenBitField(OptionInfo::QC, "--qc");
 }
 
-TEST_F(CommandOptionsTest, rc) {
-
-    // Specifying a value of zero, we expect all flag () to be zero
-    const char* argv1[] = {"badpacket",  "--rc", "0"};
-    int argc1 = sizeof(argv1) / sizeof(const char*);
+TEST_F(CommandOptionsTest, ac) {
+    checkSixteenBitField(OptionInfo::AC, "--ac");
+}
 
-    parse(argc1, const_cast<char**>(argv1));
-    checkDefaultOtherValues();
-    checkDefaultLimitsValues();
+TEST_F(CommandOptionsTest, uc) {
+    checkSixteenBitField(OptionInfo::UC, "--uc");
+}
 
-    // Check that a valid value is accepted.
-    const char* argv2[] = {"badpacket",  "--rc", "15"};
-    int argc2 = sizeof(argv2) / sizeof(const char*);
-
-    parse(argc2, const_cast<char**>(argv2));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC, 15, 15);
-
-    // Check that a range is accepted (in this case, specified backwards and
-    // outside the range - so it should be truncated).
-    const char* argv3[] = {"badpacket",  "--rc", "8-4"};
-    int argc3 = sizeof(argv3) / sizeof(const char*);
-
-    parse(argc3, const_cast<char**>(argv3));
-    checkDefaultOtherValues();
-    checkValuePair(OptionInfo::QR);
-    checkValuePair(OptionInfo::OP);
-    checkValuePair(OptionInfo::AA);
-    checkValuePair(OptionInfo::TC);
-    checkValuePair(OptionInfo::Z);
-    checkValuePair(OptionInfo::AD);
-    checkValuePair(OptionInfo::CD);
-    checkValuePair(OptionInfo::RC, 4, 8);
+TEST_F(CommandOptionsTest, dc) {
+    checkSixteenBitField(OptionInfo::DC, "--dc");
 }
 
-// Check that invalid () are caught.
-TEST_F(CommandOptionsTest, processOptionValue) {
-
-    // Check out of range () cause a BadValue exception
-    EXPECT_THROW(processOptionValue('Q', "2"), isc::BadValue);      // Single value above range
-    EXPECT_THROW(processOptionValue('O', "0-17"), isc::BadValue);   // Range overlapping valid range
-
-    // ... and that any invalid string does the same
-    EXPECT_THROW(processOptionValue('O', ""), isc::BadValue);
-    EXPECT_THROW(processOptionValue('O', " "), isc::BadValue);
-    EXPECT_THROW(processOptionValue('O', "1-2-3"), isc::BadValue);
-    EXPECT_THROW(processOptionValue('O', "abc"), isc::BadValue);
-    EXPECT_THROW(processOptionValue('O', "abc-xyz"), isc::BadValue);
-    EXPECT_THROW(processOptionValue('O', "0.7"), isc::BadValue);
-    EXPECT_THROW(processOptionValue('O', "0.7-2.3"), isc::BadValue);
+// ... and the message size option
+
+TEST_F(CommandOptionsTest, ms) {
+    int index = OptionInfo::MS;
+    const char* optflag = "--ms";
+
+    checkCommandValid(index, optflag, "1", 1, 1);
+    checkCommandValid(index, optflag, "65536", 65536, 65536);
+    checkCommandValid(index, optflag, "1-65536", 1, 65536);
+    checkCommandValid(index, optflag, "65536-1", 1, 65536);
+    checkCommandInvalid(optflag, "0");
+    checkCommandInvalid(optflag, "1-65537");
+    checkCommandInvalid(optflag, "65538");
+    checkCommandInvalid(optflag, "xyz");
 }

+ 7 - 0
tests/tools/badpacket/tests/option_info_unittest.cc

@@ -152,3 +152,10 @@ TEST_F(OptionInfoTest, CountValues) {
     EXPECT_EQ(0,       OptionInfo::minval(OptionInfo::DC));
     EXPECT_EQ(0xFFFF,  OptionInfo::maxval(OptionInfo::DC));
 }
+
+TEST_F(OptionInfoTest, OtherValues) {
+    EXPECT_STREQ("ms", OptionInfo::name(OptionInfo::MS));
+    EXPECT_STREQ("ms", OptionInfo::name(OptionInfo::getIndex('M')));
+    EXPECT_EQ(1,       OptionInfo::minval(OptionInfo::MS));
+    EXPECT_EQ(65536,   OptionInfo::maxval(OptionInfo::MS));
+}