Browse Source

[trac703] Bad Packet Tool

Sends packets with a mangled flags field to DNS servers and examines
the responses.
Stephen Morris 14 years ago
parent
commit
f2de716ada

+ 3 - 0
configure.ac

@@ -692,6 +692,9 @@ AC_CONFIG_FILES([Makefile
                  src/lib/server_common/tests/Makefile
                  tests/Makefile
                  tests/system/Makefile
+                 tests/tools/Makefile
+                 tests/tools/badpacket/Makefile
+                 tests/tools/badpacket/tests/Makefile
                ])
 AC_OUTPUT([doc/version.ent
            src/bin/cfgmgr/b10-cfgmgr.py

+ 30 - 8
src/lib/asiolink/io_fetch.cc

@@ -86,6 +86,7 @@ struct IOFetchData {
     size_t                      offset;      ///< Offset to receive data
     bool                        stopped;     ///< Have we stopped running?
     int                         timeout;     ///< Timeout in ms
+    bool                        packet;      ///< true if packet was supplied
 
     // In case we need to log an error, the origin of the last asynchronous
     // I/O is recorded.  To save time and simplify the code, this is recorded
@@ -146,6 +147,7 @@ struct IOFetchData {
         offset(0),
         stopped(false),
         timeout(wait),
+        packet(false),
         origin(ASIO_UNKORIGIN),
         staging(),
         qid(QidGenerator::getInstance().generateQid())
@@ -175,6 +177,18 @@ IOFetch::IOFetch(Protocol protocol, IOService& service,
 {
 }
 
+IOFetch::IOFetch(Protocol protocol, IOService& service,
+    OutputBufferPtr& outpkt, const IOAddress& address, uint16_t port,
+    OutputBufferPtr& buff, Callback* cb, int wait)
+    :
+    data_(new IOFetchData(protocol, service,
+          isc::dns::Question(isc::dns::Name("dummy.example.org"), isc::dns::RRClass::IN(), isc::dns::RRType::A()),
+          address, port, buff, cb, wait))
+{
+    data_->msgbuf = outpkt;
+    data_->packet = true;
+}
+
 // Return protocol in use.
 
 IOFetch::Protocol
@@ -201,14 +215,22 @@ IOFetch::operator()(asio::error_code ec, size_t length) {
         /// This is done in a different scope to allow inline variable
         /// declarations.
         {
-            Message msg(Message::RENDER);
-            msg.setQid(data_->qid);
-            msg.setOpcode(Opcode::QUERY());
-            msg.setRcode(Rcode::NOERROR());
-            msg.setHeaderFlag(Message::HEADERFLAG_RD);
-            msg.addQuestion(data_->question);
-            MessageRenderer renderer(*data_->msgbuf);
-            msg.toWire(renderer);
+            if (data_->packet) {
+                // A packet was given, overwrite the QID (which is in the
+                // first two bytes of the packet).
+                data_->msgbuf->writeUint16At(data_->qid, 0);
+
+            } else {
+                // A question was given, construct the packet
+                Message msg(Message::RENDER);
+                msg.setQid(data_->qid);
+                msg.setOpcode(Opcode::QUERY());
+                msg.setRcode(Rcode::NOERROR());
+                msg.setHeaderFlag(Message::HEADERFLAG_RD);
+                msg.addQuestion(data_->question);
+                MessageRenderer renderer(*data_->msgbuf);
+                msg.toWire(renderer);
+            }
         }
 
         // If we timeout, we stop, which will can cancel outstanding I/Os and

+ 26 - 0
src/lib/asiolink/io_fetch.h

@@ -137,6 +137,32 @@ public:
         uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
         int wait = -1);
 
+    /// \brief Constructor.
+    ///
+    /// Creates the object that will handle the upstream fetch.
+    ///
+    /// TODO: Need to randomise the source port
+    ///
+    /// \param protocol Fetch protocol, either IOFetch::TCP or IOFetch::UDP
+    /// \param service I/O Service object to handle the asynchronous
+    ///     operations.
+    /// \param outpkt Packet to send to upstream server.  Note that the
+    ///     QID (first two bytes of the packet) may be altered in the sending.
+    /// \param buff Output buffer into which the response (in wire format)
+    ///     is written (if a response is received).
+    /// \param cb Callback object containing the callback to be called
+    ///     when we terminate.  The caller is responsible for managing this
+    ///     object and deleting it if necessary.
+    /// \param address IP address of upstream server
+    /// \param port Port to which to connect on the upstream server
+    /// (default = 53)
+    /// \param wait Timeout for the fetch (in ms).  The default value of
+    ///     -1 indicates no timeout.
+    IOFetch(Protocol protocol, IOService& service,
+        isc::dns::OutputBufferPtr& outpkt, const IOAddress& address,
+        uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
+        int wait = -1);
+
     /// \brief Return Current Protocol
     ///
     /// \return Protocol associated with this IOFetch object.

+ 1 - 1
tests/Makefile.am

@@ -1 +1 @@
-SUBDIRS = system
+SUBDIRS = system tools

+ 1 - 0
tests/tools/Makefile.am

@@ -0,0 +1 @@
+SUBDIRS = badpacket

+ 26 - 0
tests/tools/badpacket/Makefile.am

@@ -0,0 +1,26 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+noinst_PROGRAMS  = badpacket
+badpacket_SOURCES  = badpacket.cc
+badpacket_SOURCES += command_options.cc command_options.h
+badpacket_SOURCES += header_flags.h
+badpacket_SOURCES += scan.cc scan.h
+badpacket_SOURCES += version.h
+
+badpacket_LDADD  = $(top_builddir)/src/lib/asiolink/libasiolink.la
+badpacket_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+badpacket_LDADD += $(top_builddir)/src/lib/log/liblog.la
+badpacket_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+

+ 51 - 0
tests/tools/badpacket/README

@@ -0,0 +1,51 @@
+"badpacket" is a tool intended to test that a nameserver can cope with
+incorrectly-formatted DNS messages.
+
+This particular incarnation of the tool allows the flags field of a DNS message
+(the third and fourth bytes) to be set to any bit combination (including ones
+that invalid in a query).  As well as setting the bits to a particular
+combination, it is possible to specify ranges for bit/field values; when this
+is done, the tool will send a set of packets so that each combination of flag
+bits is checked.
+
+To illustrate this, consider the following command:
+
+badpacket --address 192.0.2.21 --port 5301 --aa 0-1 --cd 1
+          --rc 0-2 ftp.example.com
+
+(The command has been split across two lines for clarity.)
+
+The address and port flags are self-evident.  The other flags specify settings
+for the AA bit (0 and 1), CD bit (always 1) and the RCODE field (0, 1, 2). (The
+remaining fields are not specified, so will always be zero.)  There are six
+combinations of these values, so six packets will sent to the remote server with
+the following settings:
+
+AA RCODE  CD Rest
+0    0    1   0
+0    1    1   0
+0    2    1   0
+1    0    1   0
+1    1    1   0
+1    2    1   0
+
+Each packet will cause a line to be output to stdout, which will have the
+following form:
+
+SUCCESS: (QR:0 OP:0 AA:0 TC:0 RD:0 RA:0 Z:0 AD:0 CD:1 RC:0)
+         (qr:1 op:0 aa:0 tc:0 rd:0 ra:1 z:0 ad:0 cd:1 rc:0)
+
+(Again the text has been split across two lines for clarity.)
+
+Each lines contains a status (SUCCESS indicates that a response was received,
+regardless of the contents of the response), the state of the fields in the
+flags word of the packet sent (in upper-case letters) and the state of the
+fields in the flags word of the response (in lower-case letters).
+
+TODO: At the moment the tool is limited to just alerting the flags field.
+Future work should extend the program to other bad packets. Ideas are:
+
+* Flasify the values in the various count fields
+* Add data to sections that should be empty.
+* Deliberately mangle the names placed in the message sections (e.g. by altering
+  the label count fields).

+ 52 - 0
tests/tools/badpacket/badpacket.cc

@@ -0,0 +1,52 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <unistd.h>
+
+#include <config.h>
+
+#include "command_options.h"
+#include "scan.h"
+
+/// \brief Perform Bad Packet Scan
+///
+/// Scans the server by sending a sequence of (potentially) bad packets and
+/// printing the packet settings and the response received.  The principle
+/// raison d'etre for this is to check if a bad packet will cause a crash.
+///
+/// This particular version of the code allows a set of ranges to be set for
+/// each field in the "flags" word (the third and fourth bytes) of a DNS
+/// message. (Most of the flags are single-bit values, so the range is just 0-1.
+/// The OPCODE and RCODE are both four bits wide, so the range is 0-15.)  The
+/// program then sends packets containing each combination of values.
+///
+/// TODO: Extend the program to other bad values.
+/// Examples of this would be to make the count fields invalid, to add data
+/// to sections that should be empty, and to deliberately mangle the names in
+/// these sections.
+
+using namespace isc::badpacket;
+
+/// \brief Main Program
+int main(int argc, char* argv[]) {
+
+    CommandOptions command_line;
+    command_line.parse(argc, argv);
+
+    // Construct the scan object and perform the scan.
+    Scan scanner;
+    scanner.scan(command_line);
+
+    return 0;
+}

+ 221 - 0
tests/tools/badpacket/command_options.cc

@@ -0,0 +1,221 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <boost/lexical_cast.hpp>
+#include <getopt.h>
+
+#include "exceptions/exceptions.h"
+#include "log/strutil.h"
+
+#include "command_options.h"
+#include "version.h"
+
+using namespace std;
+using namespace isc;
+namespace po = boost::program_options;
+
+namespace isc {
+namespace badpacket {
+
+/// Parses the command-line options and returns the results in an Options
+/// structure.  If the 
+void
+CommandOptions::parse(int argc, char* const argv[]) {
+
+    // Set up options for processing
+    const struct option longopts[] = {
+        {"help",    0, NULL, 'h'},  // Print usage message and exit
+        {"version", 0, NULL, 'v'},  // Print program version and exit
+        {"address", 1, NULL, 'a'},  // Specify target server address
+        {"port",    1, NULL, 'p'},  // Specify target port
+        {"timeout", 1, NULL, 't'},  // Time to wait before timing out (ms)
+        {"qr",      1, NULL, 'Q'},  // Query/response flag
+        {"op",      1, NULL, 'O'},  // Opcode
+        {"aa",      1, NULL, 'A'},  // Authoritative answer
+        {"tc",      1, NULL, 'T'},  // Truncated
+        {"rd",      1, NULL, 'D'},  // recursion Desired
+        {"ra",      1, NULL, 'R'},  // Recursion available
+        {"z",       1, NULL, 'Z'},  // Must be Zero (reserved bit)
+        {"ad",      1, NULL, 'U'},  // aUthenticated data
+        {"cd",      1, NULL, 'C'},  // Checking disabled
+        {"rc",      1, NULL, 'E'},  // rEsult code
+        {NULL,      0, NULL, 0  }
+    };
+    const char* shortopts = "hva:p:t:Q:O:A:T:D:R:Z:U:C:E:";
+
+
+    // Set variables to defaults before parsing
+    reset();
+
+    // Process command line
+    int    c;                       // Option being processed
+    optind = 0;                     // Reset parsing
+    while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
+        switch (c) {
+            case 'h':   // --help
+                usage();
+                exit(0);
+
+            case 'v':   // --version
+                version();
+                exit(0);
+
+            case 'a':   // --address
+                address_ = optarg;
+                break;
+
+            case 'p':   // --port
+                port_ = boost::lexical_cast<uint16_t>(string(optarg));
+                break;
+
+            case 't':   // --timeout
+                timeout_ = boost::lexical_cast<int>(string(optarg));
+                break;
+
+            case 'Q':   // --qr (query/response)
+                processOptionValue(optarg, values_.qr, 0, 1);
+                break;
+
+            case 'O':   // --op (operation code)
+                processOptionValue(optarg, values_.op, 0, 15);
+                break;
+
+            case 'A':   // --aa (authoritative answer)
+                processOptionValue(optarg, values_.aa, 0, 1);
+                break;
+
+            case 'T':   // --tc (truncated)
+                processOptionValue(optarg, values_.tc, 0, 1);
+                break;
+
+            case 'D':   // --rd (recursion desired)
+                processOptionValue(optarg, values_.rd, 0, 1);
+                break;
+
+            case 'R':   // --ra (recursion available)
+                processOptionValue(optarg, values_.ra, 0, 1);
+                break;
+
+            case 'Z':   // --z (zero: reserved bit)
+                processOptionValue(optarg, values_.z, 0, 1);
+                break;
+
+            case 'U':   // --ad (authenticated data)
+                processOptionValue(optarg, values_.ad, 0, 1);
+                break;
+
+            case 'C':   // --cd (checking disabled)
+                processOptionValue(optarg, values_.cd, 0, 1);
+                break;
+
+            case 'E':   // --rc (result code)
+                processOptionValue(optarg, values_.rc, 0, 15);
+                break;
+
+            default:
+                isc_throw(isc::InvalidParameter, "Unknown switch");
+        }
+    }
+
+    // Pick up a parameter if there is one (and ignore excess parameters).
+    if (optind < argc) {
+        qname_ = argv[optind++];
+    }
+}
+
+/// \brief Print usage information
+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"
+            "\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"
+            "specifying:\n"
+            "\n"
+            "--tc 0-1 --op 1-4 --aa 1 --rd 0-1\n"
+            "\n"
+            "... would send a total of 16 packets which would have all combinations of the\n"
+            "the tc bit set to 0 and 1, the rd bit set to 0 and 1, and the opcode set to all\n"
+            "values between 1 and 4.  All other flags fields would be zero except for the aa\n"
+            "bit which would always be 1.\n"
+            "\n"
+            "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"
+            "--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"
+            "--port <port>       [-p] Port to which to send query.  Defaults to 53.\n"
+            "--timeout <value>   [-t] Timeout value for the query.  Specified in ms, it\n"
+            "                         defaults to 500ms.\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"
+            "--z <range>         [-Z] Set zero (reserved) bit.  Valid <range> is 0-1\n"
+            "--ad <range>        [-U] Set authentiacted 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"
+            "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"
+            ;
+}
+
+/// \brief Print version information
+void
+CommandOptions::version() {
+    cout << BADPACKET_VERSION << "\n";
+}
+
+// Process single flag
+void
+CommandOptions::processOptionValue(const char* arg, uint32_t* where, uint32_t minval,
+                     uint32_t maxval)
+{
+    // Split the string up into one or two tokens
+    vector<string> values = isc::strutil::tokens(string(arg), "-");
+    if ((values.size() < 1) || (values.size() > 2)) {
+        isc_throw(isc::BadValue, "command argument is '" << arg << "': it must "
+                  "be in the form 'int' or 'int1-int2'");
+    }
+
+    // Convert to uint32.
+    uint32_t start = boost::lexical_cast<uint32_t>(values[0]);
+    uint32_t end = start;
+    if (values.size() == 2) {
+        end = boost::lexical_cast<uint32_t>(values[1]);
+    }
+    if (start > end) {
+        swap(start, end);
+    }
+
+    // Coerce values into the desired range
+    where[0] = max(minval, min(start, maxval));
+    where[1] = min(maxval, max(end, minval));
+}
+
+} // namespace badpacket
+} // namespace isc

+ 183 - 0
tests/tools/badpacket/command_options.h

@@ -0,0 +1,183 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __COMMAND_OPTIONS_H
+#define __COMMAND_OPTIONS_H
+
+#include <cstdlib>
+#include <stdint.h>
+#include <utility>
+
+#include <boost/program_options.hpp>
+
+namespace isc {
+namespace badpacket {
+
+/// \brief Command Options
+///
+/// This class is responsible for parsing the command-line and storing the
+/// specified options.
+///
+/// Each option setting the state of one of the fields in the flags word in the
+/// DNS packet can be specified as either:
+///
+/// - \c --option value
+/// - \c --option value1-value2
+///
+/// Either way, two values are extracted the low value and the high value (in
+/// the former case, bost are the same).  The values are stored in a
+/// "FlagValues" structure, which can be returned on request.
+///
+/// For simplicity, the class also takes care of the --help and --version flags,
+/// each of which will cause a message to be printed to stdout and the program
+/// to terminate.
+
+class CommandOptions {
+public:
+
+    /// \brief Flags Word Values
+    ///
+    /// Structure holding the values for the flag settings.  Each variable in
+    /// the structure corresponds to one of the fields in the flags word.  The
+    /// variables are two-ewlement arrays: element 0 of the array holds the low
+    /// value in the range given, and element 1 the high value.  If only a
+    /// single value is given, both elements hold the same value.
+
+    struct FlagValues {
+        uint32_t qr[2];         // QR bit
+        uint32_t op[2];         // OPCODE field
+        uint32_t aa[2];         // AA bit
+        uint32_t tc[2];         // TC bit
+        uint32_t rd[2];         // RD bit
+        uint32_t ra[2];         // RA bit
+        uint32_t z[2];          // Z bit (reserved bit)
+        uint32_t ad[2];         // AD bit
+        uint32_t cd[2];         // CD bit
+        uint32_t rc[2];         // RCODE field
+
+        /// \brief Default Constructor
+        ///
+        /// Sets everything to zero.
+        FlagValues() {
+            reset();
+        }
+
+        /// \brief Reset All Values to Zero
+        void reset() {
+            qr[0] = qr[1] = 0;
+            op[0] = op[1] = 0;
+            aa[0] = aa[1] = 0;
+            tc[0] = tc[1] = 0;
+            rd[0] = rd[1] = 0;
+            ra[0] = ra[1] = 0;
+            z[0] = z[1] = 0;
+            ad[0] = ad[1] = 0;
+            cd[0] = cd[1] = 0;
+            rc[0] = rc[1] = 0;
+        }
+    };
+
+    /// \brief CommandOptions Constructor
+    ///
+    /// Set values to defaults.
+    CommandOptions() {
+        reset();
+    }
+
+    /// \brief Return Flags Word Values
+    ///
+    /// Returns a copy of the flags word structure for use by the caller.  This
+    /// structure holds the flags field settings specified on the command line.
+    ///
+    /// \return Copy of the values specified on the command line.
+    FlagValues getFlagValues() const {
+        return values_;
+    }
+
+    /// \brief Return Target Address
+    std::string getAddress() const {
+        return address_;
+    }
+
+    /// \brief Return Target Port
+    uint16_t getPort() const {
+        return port_;
+    }
+
+    /// \brief Return Timeout
+    int getTimeout() const {
+        return timeout_;
+    }
+
+    /// \brief Return qname
+    std::string getQname() const {
+        return qname_;
+    }
+
+    /// \brief Reset To Defaults
+    void reset() {
+        values_.reset();
+        address_ = "127.0.0.1";
+        port_ = 53;
+        timeout_ = 500;
+        qname_ = "www.example.com";
+    }
+
+    /// \brief Parse Command Line
+    ///
+    /// Parses the command line and stores the selected options.  The parsing
+    /// also handles the --help and --version commands: both of these will cause
+    /// some text to be printed to stdout, after which exit() is called to
+    /// terminate the program.
+    ///
+    /// \param argc Argument count passed to main().
+    /// \param argv Argument value array passed to main().
+    void parse(int argc, char* const argv[]);
+
+    /// \brief Print Usage Information
+    void usage();
+
+    /// \brief Print Version Information
+    void version();
+
+    // The following are protected to aid testing
+
+protected:
+    /// \brief Process Option Value
+    ///
+    /// Processes a specific command-line option, interpreting the value and
+    /// placing it in the appropriate location.  On error a BadValue exception
+    /// is thrown.
+    ///
+    /// \param arg flag argument read from the command line
+    /// \param where Two-element uint32_t array into which the data is put
+    /// \param minval Minimum allowed value
+    /// \param maxval Maximum allowed value
+    void processOptionValue(const char* arg, uint32_t* where, uint32_t minval,
+                     uint32_t maxval);
+
+    // Member variables
+
+private:
+    FlagValues      values_;        ///< Values read from command line
+    std::string     address_;       ///< Address to where query is sent
+    uint16_t        port_;          ///< Target port
+    int             timeout_;       ///< Timeout for query
+    std::string     qname_;         ///< Query to make
+};
+
+} // namespace badpacket
+} // namespace isc
+
+#endif // __COMMAND_OPTIONS_H

+ 211 - 0
tests/tools/badpacket/header_flags.h

@@ -0,0 +1,211 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __HEADER_FLAGS_H
+#define __HEADER_FLAGS_H
+
+namespace isc {
+namespace badpacket {
+
+/// \brief Header Flags
+///
+/// Simple class providing easy conversion between the header flags in a DNS
+/// message and a 16-bit value.
+
+class HeaderFlags {
+public:
+
+    // The following declaration describes the various fields in the DNS
+    // packet header.
+    struct Flags {
+        unsigned int rc : 4;
+        unsigned int cd : 1;
+        unsigned int ad : 1;
+        unsigned int z  : 1;    // Reserved
+        unsigned int ra : 1;
+        unsigned int rd : 1;
+        unsigned int tc : 1;
+        unsigned int aa : 1;
+        unsigned int op : 4;
+        unsigned int qr : 1;
+    };
+
+    /// \brief Constructor
+    HeaderFlags() {
+        reset();
+    }
+
+    /// \brief Reset Values to Zero
+    ///
+    /// Clears all flags.
+    void reset() {
+        setValue(0);
+    }
+
+    /// \brief Set Header Flags as 16-Bit Value
+    ///
+    /// \param value 16-bit value to put into object as representing the
+    ///        header flags.
+    void setValue(uint16_t value) {
+        flags_.value = value;
+    }
+
+    /// \brief Get Header Flags as 16-bit Value
+    uint16_t getValue() const {
+        return flags_.value;
+    }
+    
+    /// \brief Get QR Bit
+    uint16_t getQR() const {
+        return flags_.fields.qr;
+    }
+
+    /// \brief Set QR Bit
+    ///
+    /// \param value New value of the field, which must be 0 or 1: values
+    ///        outside that range are coerced to the nearest boundary.
+    void setQR(uint16_t value) {
+        flags_.fields.qr = (value > 1) ? 1 : value;
+    }
+    
+    /// \brief Get OP Value
+    uint16_t getOP() const {
+        return flags_.fields.op;
+    }
+
+    /// \brief Set OP Value
+    ///
+    /// \param value New value of the field, which must in the range 0 to 15:
+    ///        values outside that range are coerced to the nearest boundary.
+    void setOP(uint16_t value) {
+        flags_.fields.op = (value > 15) ? 15 : value;
+    }
+    
+    /// \brief Get AA Bit
+    uint16_t getAA() const {
+        return flags_.fields.aa;
+    }
+
+    /// \brief Set AA Bit
+    ///
+    /// \param value New value of the field, which must be 0 or 1: values
+    ///        outside that range are coerced to the nearest boundary.
+    void setAA(uint16_t value) {
+        flags_.fields.aa = (value > 1) ? 1 : value;
+    }
+    
+    /// \brief Get TC Bit
+    uint16_t getTC() const {
+        return flags_.fields.tc;
+    }
+
+    /// \brief Set TC Bit
+    ///
+    /// \param value New value of the field, which must be 0 or 1: values
+    ///        outside that range are coerced to the nearest boundary.
+    void setTC(uint16_t value) {
+        flags_.fields.tc = (value > 1) ? 1 : value;
+    }
+    
+    /// \brief Get RD Bit
+    uint16_t getRD() const {
+        return flags_.fields.rd;
+    }
+
+    /// \brief Set RD Bit
+    ///
+    /// \param value New value of the field, which must be 0 or 1: values
+    ///        outside that range are coerced to the nearest boundary.
+    void setRD(uint16_t value) {
+        flags_.fields.rd = (value > 1) ? 1 : value;
+    }
+    
+    /// \brief Get RA Bit
+    uint16_t getRA() const {
+        return flags_.fields.ra;
+    }
+
+    /// \brief Set RA Bit
+    ///
+    /// \param value New value of the field, which must be 0 or 1: values
+    ///        outside that range are coerced to the nearest boundary.
+    void setRA(uint16_t value) {
+        flags_.fields.ra = (value > 1) ? 1 : value;
+    }
+    
+    /// \brief Get Z Bit
+    uint16_t getZ() const {
+        return flags_.fields.z;
+    }
+
+    /// \brief Set Z Bit
+    ///
+    /// \param value New value of the field, which must be 0 or 1: values
+    ///        outside that range are coerced to the nearest boundary.
+    void setZ(uint16_t value) {
+        flags_.fields.z = (value > 1) ? 1 : value;
+    }
+    
+    /// \brief Get AD Bit
+    uint16_t getAD() const {
+        return flags_.fields.ad;
+    }
+
+    /// \brief Set AD Bit
+    ///
+    /// \param value New value of the field, which must be 0 or 1: values
+    ///        outside that range are coerced to the nearest boundary.
+    void setAD(uint16_t value) {
+        flags_.fields.ad = (value > 1) ? 1 : value;
+    }
+    
+    /// \brief Get CD Bit
+    uint16_t getCD() const {
+        return flags_.fields.cd;
+    }
+
+    /// \brief Set CD Bit
+    ///
+    /// \param value New value of the field, which must be 0 or 1: values
+    ///        outside that range are coerced to the nearest boundary.
+    void setCD(uint16_t value) {
+        flags_.fields.cd = (value > 1) ? 1 : value;
+    }
+    
+    /// \brief Get RC Value
+    uint16_t getRC() const {
+        return flags_.fields.rc;
+    }
+
+    /// \brief Set RC Value
+    ///
+    /// \param value New value of the field, which must be in the range 0 to 15:
+    ///        values outside that range are coerced to the nearest boundary.
+    void setRC(uint16_t value) {
+        flags_.fields.rc = (value > 15) ? 15 : value;
+    }
+
+private:
+
+    // The variable that performs the conversion
+    union {
+        uint16_t        value;
+        Flags           fields;
+    } flags_;
+};
+
+} // namespace badpacket
+} // namespace isc
+
+#endif // __HEADER_FLAGS_H

+ 207 - 0
tests/tools/badpacket/scan.cc

@@ -0,0 +1,207 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <string>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <asio.hpp>
+
+#include <asiolink/io_address.h>
+#include <asiolink/io_fetch.h>
+#include <dns/buffer.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/question.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <log/strutil.h>
+
+#include "command_options.h"
+#include "header_flags.h"
+#include "scan.h"
+
+using namespace std;
+using namespace asiolink;
+using namespace isc::dns;
+using namespace isc::strutil;
+
+namespace isc {
+namespace badpacket {
+
+// Perform the scan from start to end on a single question.
+void
+Scan::scan(const CommandOptions& options) {
+
+    // Create the message buffer to use
+    Message message(Message::RENDER);
+    message.setOpcode(Opcode::QUERY());
+    message.setRcode(Rcode::NOERROR());
+    message.addQuestion(Question(Name(options.getQname()), RRClass::IN(),
+                        RRType::A()));
+
+    OutputBufferPtr msgbuf(new OutputBuffer(512));
+    MessageRenderer renderer(*msgbuf);
+    message.toWire(renderer);
+
+    // Now 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).
+    CommandOptions::FlagValues values = options.getFlagValues();
+    HeaderFlags flags;
+    for (uint16_t qr = values.qr[0]; qr <= values.qr[1]; ++qr) {
+        flags.setQR(qr);
+        for (uint16_t op = values.op[0]; op <= values.op[1]; ++op) {
+            flags.setOP(op);
+            for (uint16_t aa = values.aa[0]; aa <= values.aa[1]; ++aa) {
+                flags.setAA(aa);
+                for (uint16_t tc = values.tc[0]; tc <= values.tc[1]; ++tc) {
+                    flags.setTC(tc);
+                    for (uint16_t rd = values.rd[0]; rd <= values.rd[1]; ++rd) {
+                        flags.setRD(rd);
+                        for (uint16_t ra = values.ra[0]; ra <= values.ra[1]; ++ra) {
+                            flags.setRA(ra);
+                            for (uint16_t z = values.z[0]; z <= values.z[1]; ++z) {
+                                flags.setZ(z);
+                                for (uint16_t ad = values.ad[0]; ad <= values.ad[1]; ++ad) {
+                                    flags.setAD(ad);
+                                    for (uint16_t cd = values.cd[0]; cd <= values.cd[1]; ++cd) {
+                                        flags.setCD(cd);
+                                        for (uint16_t rc = values.rc[0]; rc <= values.rc[1]; ++rc) {
+                                            flags.setRC(rc);
+
+                                            // Set the flags in the message and do the I/O.
+                                            msgbuf->writeUint16At(flags.getValue(), 2);
+                                            scanOne(msgbuf, options);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+// Perform the message exchange for a single combination of flags.
+void
+Scan::scanOne(isc::dns::OutputBufferPtr& msgbuf, const CommandOptions& options) {
+
+    // Store the interpretation of the flags field.
+    string fields = getFields(msgbuf);
+
+    // Do the I/O, waiting for a reply
+    OutputBufferPtr replybuf(new OutputBuffer(512));
+    performIO(msgbuf, replybuf, options);
+
+    string status = "";
+    string returned = "";
+    switch (result_) {
+    case IOFetch::SUCCESS:
+        {
+            status = "SUCCESS";
+
+            // Parse the reply and get the fields
+            returned = getFields(replybuf);
+            lowercase(returned);
+        }
+        break;
+
+    case IOFetch::TIME_OUT:
+        status = "TIMEOUT";
+        break;
+
+    case IOFetch::STOPPED:
+        status = "STOPPED";
+        break;
+
+    default:
+        status = "UNKNOWN";
+    }
+
+    // ... and output the result
+    cout << status << ": (" << fields << ") (" << returned << ")\n";
+}
+
+// Get interpretation of the message fields.
+//
+// This takes the second and third bytes of the passed buffer and interprets
+// the values.  A summary string listing them is returned.
+std::string
+Scan::getFields(isc::dns::OutputBufferPtr& msgbuf) {
+    HeaderFlags flags;
+
+    // Extract the flags field from the buffer
+    InputBuffer inbuf(msgbuf->getData(), msgbuf->getLength());
+    inbuf.setPosition(2);
+    flags.setValue(inbuf.readUint16());
+
+    std::ostringstream os;
+    os << std::hex << std::uppercase <<
+        "QR:" << flags.getQR() << " " <<
+        "OP:" << flags.getOP() << " " <<
+        "AA:" << flags.getAA() << " " <<
+        "TC:" << flags.getTC() << " " <<
+        "RD:" << flags.getRD() << " " <<
+        "RA:" << flags.getRA() << " " <<
+         "Z:" << flags.getZ()  << " " <<
+        "AD:" << flags.getAD() << " " <<
+        "CD:" << flags.getCD() << " " <<
+        "RC:" << flags.getRC();
+    return (os.str());
+}
+
+// Perform the I/O.
+void
+Scan::performIO(OutputBufferPtr& sendbuf, OutputBufferPtr& recvbuf,
+                const CommandOptions& options)
+{
+    // Set up an I/O service for the I/O.  This needs to be initialized before
+    // every call as the callback called when the I/O completes stops it.
+    service_.reset(new IOService());
+
+    // The object that will do the I/O
+    IOFetch fetch(IOFetch::UDP, *service_, sendbuf,
+                  IOAddress(options.getAddress()), options.getPort(), recvbuf,
+                  this, options.getTimeout());
+
+    // Execute the message exhange.  The call to run() will return when a
+    // response is received or when the I/O times out.
+    (service_->get_io_service()).post(fetch); 
+    service_->run();
+}
+
+// I/O Callback.  Called when the message exchange compltes or times out.
+void
+Scan::operator()(IOFetch::Result result) {
+
+    // Record the result.  This is accessed when deciding what was returned
+    // (if a timeout, nothing was returned).
+    result_ = result;
+
+    // Stop the I/O service.  This will cause the call to run() to return.
+    service_->stop();
+}
+
+
+
+} // namespace test
+} // namespace isc

+ 107 - 0
tests/tools/badpacket/scan.h

@@ -0,0 +1,107 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __SCAN_H
+#define __SCAN_H
+
+#include <stdint.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <config.h>
+
+#include <asiolink/io_fetch.h>
+#include <asiolink/io_service.h>
+#include <dns/buffer.h>
+
+#include "command_options.h"
+
+namespace isc {
+namespace badpacket {
+
+/// \brief Field Scan
+///
+/// This class implements a field scan.  Given a CommandOptions object, it
+/// will cycle through combinations of the given options, sending and
+/// receiving packets.  For each packet exchange, a summary is written to
+/// stdout.
+
+class Scan : public asiolink::IOFetch::Callback {
+public:
+
+    /// \brief Constructor
+    Scan() : service_(), result_()
+    {}
+
+    /// \brief Run Scan
+    ///
+    /// \param options Command-line options
+    void scan(const CommandOptions& options);
+    
+    /// \brief Callback
+    ///
+    /// This class is derived from the IOFetch::Callback class as it acts as the
+    /// callback object. When an asynchronous I/I has completed, this method
+    /// will be called.
+    ///
+    /// \param result Result of the asynchronous I/O.  Zero implies success.
+    virtual void operator()(asiolink::IOFetch::Result result);
+
+private:
+    /// \brief Scan One Value
+    ///
+    /// Performs one exchange of packets with the remote nameserver, sending
+    /// the specified message.
+    ///
+    /// \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 scanOne(isc::dns::OutputBufferPtr& msgbuf,
+                 const CommandOptions& options);
+
+    /// \brief Perform I/O
+    ///
+    /// Performs a single query to the nameserver and reads the response.  It
+    /// outputs a summary of the result.
+    ///
+    /// \param sendbuf Buffer sent to the nameserver
+    /// \param recvbuf Buffer to hold reply from the nameserver
+    /// \param options Command-line options
+    void performIO(isc::dns::OutputBufferPtr& sendbuf,
+                   isc::dns::OutputBufferPtr& recvbuf,
+                   const CommandOptions& options);
+
+    /// \brief Get Fields
+    ///
+    /// Interprets the flags fields in a DNS message and converts them to a
+    /// terxtual format.
+    ///
+    /// \param msg Message for which the header is value
+    ///
+    /// \return A string that holds a textual interpretation of all the fields
+    ///         in the header.
+    std::string getFields(isc::dns::OutputBufferPtr& msgbuf);
+
+    // Member variables
+
+    boost::scoped_ptr<asiolink::IOService> service_;///< Service to run the scan
+    asiolink::IOFetch::Result   result_;            ///< Result of the I/O
+};
+
+} // namespace test
+} // namespace isc
+
+#endif // __SCAN_H

+ 29 - 0
tests/tools/badpacket/tests/Makefile.am

@@ -0,0 +1,29 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES  = run_unittests.cc
+run_unittests_SOURCES += command_options_unittest.cc
+run_unittests_SOURCES += header_flags_unittest.cc
+run_unittests_SOURCES += $(top_builddir)/tests/tools/badpacket/command_options.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDFLAGS += $(top_builddir)/src/lib/exceptions/libexceptions.la
+
+run_unittests_LDADD  = $(GTEST_LDADD)
+endif
+
+noinst_PROGRAMS = $(TESTS)

+ 545 - 0
tests/tools/badpacket/tests/command_options_unittest.cc

@@ -0,0 +1,545 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cstddef>
+#include <string>
+#include <gtest/gtest.h>
+
+#include "../command_options.h"
+
+#include "exceptions/exceptions.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::badpacket;
+
+
+// Test fixture class
+
+class CommandOptionsTest : public virtual ::testing::Test,
+                           public virtual CommandOptions
+{
+public:
+    CommandOptionsTest() {}
+};
+
+// Check that the getRange() method works
+
+TEST_F(CommandOptionsTest, processOptionValue) {
+
+    uint32_t    result[2];
+
+    // Check valid data
+    processOptionValue("1", result, 0, 1);
+    EXPECT_EQ(1, result[0]);
+    EXPECT_EQ(1, result[1]);
+
+    processOptionValue("0-2", result, 0, 5);
+    EXPECT_EQ(0, result[0]);
+    EXPECT_EQ(2, result[1]);
+
+    processOptionValue("4-8", result, 0, 10);
+    EXPECT_EQ(4, result[0]);
+    EXPECT_EQ(8, result[1]);
+
+    processOptionValue("172-103", result, 0, 200);
+    EXPECT_EQ(103, result[0]);
+    EXPECT_EQ(172, result[1]);
+
+    // Check coercion is as expected
+    processOptionValue("1", result, 3, 4);    // Single value below range
+    EXPECT_EQ(3, result[0]);
+    EXPECT_EQ(3, result[1]);
+
+    processOptionValue("7", result, 3, 6);    // Single value above range
+    EXPECT_EQ(6, result[0]);
+    EXPECT_EQ(6, result[1]);
+
+    processOptionValue("2-6", result, 5, 10); // Range overlaps valid range on low side
+    EXPECT_EQ(5, result[0]);
+    EXPECT_EQ(6, result[1]);
+
+    processOptionValue("4-7", result, 5, 9);  // Range overlaps valid range on high side
+    EXPECT_EQ(5, result[0]);
+    EXPECT_EQ(7, result[1]);
+
+    processOptionValue("9-1", result, 4, 8);  // Range overlaps valid range
+    EXPECT_EQ(4, result[0]);
+    EXPECT_EQ(8, result[1]);
+
+    processOptionValue("4-8", result, 1, 9);  // Range inside valid range
+    EXPECT_EQ(4, result[0]);
+    EXPECT_EQ(8, result[1]);
+
+    // Now the invalid ones.
+    EXPECT_ANY_THROW(processOptionValue("", result, 0, 1));
+    EXPECT_ANY_THROW(processOptionValue(" ", result, 0, 1));
+    EXPECT_ANY_THROW(processOptionValue("abc", result, 0, 1));
+    EXPECT_ANY_THROW(processOptionValue("xyz-def", result, 0, 1));
+    EXPECT_ANY_THROW(processOptionValue("0.7", result, 0, 1));
+    EXPECT_ANY_THROW(processOptionValue("0.7-2.3", result, 0, 1));
+}
+
+
+// Checks the minimum and maximum values specified for a flag
+void
+checkValuePair(const uint32_t value[2], uint32_t minval = 0,
+               uint32_t maxval = 0)
+{
+    EXPECT_EQ(minval, value[0]);
+    EXPECT_EQ(maxval, value[1]);
+}
+
+// Checks that all flag values in the command values are zero
+void
+checkDefaultFlagValues(const CommandOptions::FlagValues& values) {
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+}
+
+// Checks non-flag options are set to defaults.
+void
+checkDefaultOtherValues(CommandOptions& options) {
+    EXPECT_EQ("127.0.0.1", options.getAddress());
+    EXPECT_EQ(53, options.getPort());
+    EXPECT_EQ(500, options.getTimeout());
+    EXPECT_EQ("www.example.com", options.getQname());
+}
+
+// Check that each of the options will be recognised
+
+TEST_F(CommandOptionsTest, address) {
+    const char* argv[] = {"badpacket",  "--address", "192.0.2.1"};
+    int argc = sizeof(argv) / sizeof(const char*);
+
+    // The conversion is ugly but it simplifies the process of entering the
+    // string constant.  The cast throws away the "const"ness of the pointed-to
+    // strings in order to conform to the function signature; however, the
+    // called functions all treat the strings as const.
+    parse(argc, const_cast<char**>(argv));
+    EXPECT_EQ("192.0.2.1", getAddress());
+    EXPECT_EQ(53, getPort());
+    EXPECT_EQ(500, getTimeout());
+    EXPECT_EQ("www.example.com", getQname());
+    checkDefaultFlagValues(getFlagValues());
+}
+
+TEST_F(CommandOptionsTest, port) {
+    const char* argv[] = {"badpacket",  "--port", "153"};
+    int argc = sizeof(argv) / sizeof(const char*);
+
+    parse(argc, const_cast<char**>(argv));
+    EXPECT_EQ("127.0.0.1", getAddress());
+    EXPECT_EQ(153, getPort());
+    EXPECT_EQ(500, getTimeout());
+    EXPECT_EQ("www.example.com", getQname());
+    checkDefaultFlagValues(getFlagValues());
+}
+
+TEST_F(CommandOptionsTest, timeout) {
+    const char* argv[] = {"badpacket",  "--timeout", "250"};
+    int argc = sizeof(argv) / sizeof(const char*);
+
+    parse(argc, const_cast<char**>(argv));
+    EXPECT_EQ("127.0.0.1", getAddress());
+    EXPECT_EQ(53, getPort());
+    EXPECT_EQ(250, getTimeout());
+    EXPECT_EQ("www.example.com", getQname());
+    checkDefaultFlagValues(getFlagValues());
+}
+
+TEST_F(CommandOptionsTest, parameter) {
+    const char* argv[] = {"badpacket",  "ftp.example.net"};
+    int argc = sizeof(argv) / sizeof(const char*);
+
+    parse(argc, const_cast<char**>(argv));
+    EXPECT_EQ("127.0.0.1", getAddress());
+    EXPECT_EQ(53, getPort());
+    EXPECT_EQ(500, getTimeout());
+    EXPECT_EQ("ftp.example.net", getQname());
+    checkDefaultFlagValues(getFlagValues());
+}
+
+// The various tests of the different flags
+TEST_F(CommandOptionsTest, qr) {
+
+    // Specifying a value of zero, we expect all flag values to be zero
+    const char* argv1[] = {"badpacket",  "--qr", "0"};
+    int argc1 = sizeof(argv1) / sizeof(const char*);
+
+    parse(argc1, const_cast<char**>(argv1));
+    checkDefaultOtherValues(*this);
+    FlagValues values = getFlagValues();
+    checkDefaultFlagValues(values);
+
+    // 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(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr, 1, 1);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.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(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr, 0, 1);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+}
+
+// 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 values and
+// variables.)
+
+TEST_F(CommandOptionsTest, op) {
+
+    // Specifying a value of zero, we expect all flag values to be zero
+    const char* argv1[] = {"badpacket",  "--op", "0"};
+    int argc1 = sizeof(argv1) / sizeof(const char*);
+
+    parse(argc1, const_cast<char**>(argv1));
+    checkDefaultOtherValues(*this);
+    FlagValues values = getFlagValues();
+    checkDefaultFlagValues(values);
+
+    // 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(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op, 8, 8);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+
+    // Check that a range is accepted (in this case, specified backwards and
+    // outside the range - so it should be truncated).
+    const char* argv3[] = {"badpacket",  "--op", "21-0"};
+    int argc3 = sizeof(argv3) / sizeof(const char*);
+
+    parse(argc3, const_cast<char**>(argv3));
+    checkDefaultOtherValues(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op, 0, 15);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+}
+
+TEST_F(CommandOptionsTest, aa) {
+
+    // Specifying a value of zero, we expect all flag values to be zero
+    const char* argv1[] = {"badpacket",  "--aa", "0"};
+    int argc1 = sizeof(argv1) / sizeof(const char*);
+
+    parse(argc1, const_cast<char**>(argv1));
+    checkDefaultOtherValues(*this);
+    FlagValues values = getFlagValues();
+    checkDefaultFlagValues(values);
+
+    // 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(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa, 1, 1);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+
+    // Check that a range is accepted (in this case, specified backwards and
+    // outside the range - so it should be truncated).
+    const char* argv3[] = {"badpacket",  "--aa", "21-0"};
+    int argc3 = sizeof(argv3) / sizeof(const char*);
+
+    parse(argc3, const_cast<char**>(argv3));
+    checkDefaultOtherValues(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa, 0, 1);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+}
+
+TEST_F(CommandOptionsTest, tc) {
+
+    // Specifying a value of zero, we expect all flag values to be zero
+    const char* argv1[] = {"badpacket",  "--tc", "0"};
+    int argc1 = sizeof(argv1) / sizeof(const char*);
+
+    parse(argc1, const_cast<char**>(argv1));
+    checkDefaultOtherValues(*this);
+    FlagValues values = getFlagValues();
+    checkDefaultFlagValues(values);
+
+    // 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(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc, 1, 1);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+
+    // Check that a range is accepted (in this case, specified backwards and
+    // outside the range - so it should be truncated).
+    const char* argv3[] = {"badpacket",  "--tc", "21-0"};
+    int argc3 = sizeof(argv3) / sizeof(const char*);
+
+    parse(argc3, const_cast<char**>(argv3));
+    checkDefaultOtherValues(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc, 0, 1);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+}
+
+TEST_F(CommandOptionsTest, z) {
+
+    // Specifying a value of zero, we expect all flag values to be zero
+    const char* argv1[] = {"badpacket",  "--z", "0"};
+    int argc1 = sizeof(argv1) / sizeof(const char*);
+
+    parse(argc1, const_cast<char**>(argv1));
+    checkDefaultOtherValues(*this);
+    FlagValues values = getFlagValues();
+    checkDefaultFlagValues(values);
+
+    // 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(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z, 1, 1);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+
+    // Check that a range is accepted (in this case, specified backwards and
+    // outside the range - so it should be truncated).
+    const char* argv3[] = {"badpacket",  "--z", "21-0"};
+    int argc3 = sizeof(argv3) / sizeof(const char*);
+
+    parse(argc3, const_cast<char**>(argv3));
+    checkDefaultOtherValues(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z, 0, 1);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+}
+
+TEST_F(CommandOptionsTest, ad) {
+
+    // Specifying a value of zero, we expect all flag values to be zero
+    const char* argv1[] = {"badpacket",  "--ad", "0"};
+    int argc1 = sizeof(argv1) / sizeof(const char*);
+
+    parse(argc1, const_cast<char**>(argv1));
+    checkDefaultOtherValues(*this);
+    FlagValues values = getFlagValues();
+    checkDefaultFlagValues(values);
+
+    // 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(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad, 1, 1);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+
+    // Check that a range is accepted (in this case, specified backwards and
+    // outside the range - so it should be truncated).
+    const char* argv3[] = {"badpacket",  "--ad", "21-0"};
+    int argc3 = sizeof(argv3) / sizeof(const char*);
+
+    parse(argc3, const_cast<char**>(argv3));
+    checkDefaultOtherValues(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad, 0, 1);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc);
+}
+
+TEST_F(CommandOptionsTest, cd) {
+
+    // Specifying a value of zero, we expect all flag values to be zero
+    const char* argv1[] = {"badpacket",  "--cd", "0"};
+    int argc1 = sizeof(argv1) / sizeof(const char*);
+
+    parse(argc1, const_cast<char**>(argv1));
+    checkDefaultOtherValues(*this);
+    FlagValues values = getFlagValues();
+    checkDefaultFlagValues(values);
+
+    // 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(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd, 1, 1);
+    checkValuePair(values.rc);
+
+    // Check that a range is accepted (in this case, specified backwards and
+    // outside the range - so it should be truncated).
+    const char* argv3[] = {"badpacket",  "--cd", "21-0"};
+    int argc3 = sizeof(argv3) / sizeof(const char*);
+
+    parse(argc3, const_cast<char**>(argv3));
+    checkDefaultOtherValues(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd, 0, 1);
+    checkValuePair(values.rc);
+}
+
+TEST_F(CommandOptionsTest, rc) {
+
+    // Specifying a value of zero, we expect all flag values to be zero
+    const char* argv1[] = {"badpacket",  "--rc", "0"};
+    int argc1 = sizeof(argv1) / sizeof(const char*);
+
+    parse(argc1, const_cast<char**>(argv1));
+    checkDefaultOtherValues(*this);
+    FlagValues values = getFlagValues();
+    checkDefaultFlagValues(values);
+
+    // Check that a value of 1 is accepted
+    const char* argv2[] = {"badpacket",  "--rc", "21"};
+    int argc2 = sizeof(argv2) / sizeof(const char*);
+
+    parse(argc2, const_cast<char**>(argv2));
+    checkDefaultOtherValues(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.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", "21-0"};
+    int argc3 = sizeof(argv3) / sizeof(const char*);
+
+    parse(argc3, const_cast<char**>(argv3));
+    checkDefaultOtherValues(*this);
+    values = getFlagValues();
+    checkValuePair(values.qr);
+    checkValuePair(values.op);
+    checkValuePair(values.aa);
+    checkValuePair(values.tc);
+    checkValuePair(values.z);
+    checkValuePair(values.ad);
+    checkValuePair(values.cd);
+    checkValuePair(values.rc, 0, 15);
+}
+
+
+// Check that the other flags work

+ 306 - 0
tests/tools/badpacket/tests/header_flags_unittest.cc

@@ -0,0 +1,306 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cstddef>
+#include <stdint.h>
+#include <gtest/gtest.h>
+
+#include "../header_flags.h"
+
+using namespace isc::badpacket;
+
+
+// Test fixture class
+
+class HeaderFlagsTest : public ::testing::Test {
+public:
+    HeaderFlagsTest() {}
+};
+
+// Convenience function to check that all values are zero
+void
+checkZero(const HeaderFlags& flags) {
+    EXPECT_EQ(0, flags.getQR());
+    EXPECT_EQ(0, flags.getOP());
+    EXPECT_EQ(0, flags.getAA());
+    EXPECT_EQ(0, flags.getTC());
+    EXPECT_EQ(0, flags.getRD());
+    EXPECT_EQ(0, flags.getRA());
+    EXPECT_EQ(0, flags.getZ());
+    EXPECT_EQ(0, flags.getAD());
+    EXPECT_EQ(0, flags.getCD());
+    EXPECT_EQ(0, flags.getRC());
+
+    EXPECT_EQ(0, flags.getValue());
+}
+
+
+// Set of tests to check that setting a bit only sets that bit and nothing
+// else.
+
+TEST_F(HeaderFlagsTest, QRfield) {
+    HeaderFlags flags;
+    checkZero(flags);
+
+    flags.setQR(1);
+    EXPECT_EQ(1, flags.getQR());
+    EXPECT_EQ(0, flags.getOP());
+    EXPECT_EQ(0, flags.getAA());
+    EXPECT_EQ(0, flags.getTC());
+    EXPECT_EQ(0, flags.getRD());
+    EXPECT_EQ(0, flags.getRA());
+    EXPECT_EQ(0, flags.getZ());
+    EXPECT_EQ(0, flags.getAD());
+    EXPECT_EQ(0, flags.getCD());
+    EXPECT_EQ(0, flags.getRC());
+    EXPECT_NE(0, flags.getValue());
+
+    flags.setQR(0);
+    checkZero(flags);
+}
+
+TEST_F(HeaderFlagsTest, OPfield) {
+    HeaderFlags flags;
+    checkZero(flags);
+
+    flags.setOP(15);
+    EXPECT_EQ(0, flags.getQR());
+    EXPECT_EQ(15, flags.getOP());
+    EXPECT_EQ(0, flags.getAA());
+    EXPECT_EQ(0, flags.getTC());
+    EXPECT_EQ(0, flags.getRD());
+    EXPECT_EQ(0, flags.getRA());
+    EXPECT_EQ(0, flags.getZ());
+    EXPECT_EQ(0, flags.getAD());
+    EXPECT_EQ(0, flags.getCD());
+    EXPECT_EQ(0, flags.getRC());
+    EXPECT_NE(0, flags.getValue());
+
+    flags.setOP(0);
+    checkZero(flags);
+}
+
+TEST_F(HeaderFlagsTest, AAfield) {
+    HeaderFlags flags;
+    checkZero(flags);
+
+    flags.setAA(1);
+    EXPECT_EQ(0, flags.getQR());
+    EXPECT_EQ(0, flags.getOP());
+    EXPECT_EQ(1, flags.getAA());
+    EXPECT_EQ(0, flags.getTC());
+    EXPECT_EQ(0, flags.getRD());
+    EXPECT_EQ(0, flags.getRA());
+    EXPECT_EQ(0, flags.getZ());
+    EXPECT_EQ(0, flags.getAD());
+    EXPECT_EQ(0, flags.getCD());
+    EXPECT_EQ(0, flags.getRC());
+    EXPECT_NE(0, flags.getValue());
+
+    flags.setAA(0);
+    checkZero(flags);
+}
+
+TEST_F(HeaderFlagsTest, TCfield) {
+    HeaderFlags flags;
+    checkZero(flags);
+
+    flags.setTC(1);
+    EXPECT_EQ(0, flags.getQR());
+    EXPECT_EQ(0, flags.getOP());
+    EXPECT_EQ(0, flags.getAA());
+    EXPECT_EQ(1, flags.getTC());
+    EXPECT_EQ(0, flags.getRD());
+    EXPECT_EQ(0, flags.getRA());
+    EXPECT_EQ(0, flags.getZ());
+    EXPECT_EQ(0, flags.getAD());
+    EXPECT_EQ(0, flags.getCD());
+    EXPECT_EQ(0, flags.getRC());
+    EXPECT_NE(0, flags.getValue());
+
+    flags.setTC(0);
+    checkZero(flags);
+}
+
+TEST_F(HeaderFlagsTest, RDfield) {
+    HeaderFlags flags;
+    checkZero(flags);
+
+    flags.setRD(1);
+    EXPECT_EQ(0, flags.getQR());
+    EXPECT_EQ(0, flags.getOP());
+    EXPECT_EQ(0, flags.getAA());
+    EXPECT_EQ(0, flags.getTC());
+    EXPECT_EQ(1, flags.getRD());
+    EXPECT_EQ(0, flags.getRA());
+    EXPECT_EQ(0, flags.getZ());
+    EXPECT_EQ(0, flags.getAD());
+    EXPECT_EQ(0, flags.getCD());
+    EXPECT_EQ(0, flags.getRC());
+    EXPECT_NE(0, flags.getValue());
+
+    flags.setRD(0);
+    checkZero(flags);
+}
+
+TEST_F(HeaderFlagsTest, RAfield) {
+    HeaderFlags flags;
+    checkZero(flags);
+
+    flags.setRA(1);
+    EXPECT_EQ(0, flags.getQR());
+    EXPECT_EQ(0, flags.getOP());
+    EXPECT_EQ(0, flags.getAA());
+    EXPECT_EQ(0, flags.getTC());
+    EXPECT_EQ(0, flags.getRD());
+    EXPECT_EQ(1, flags.getRA());
+    EXPECT_EQ(0, flags.getZ());
+    EXPECT_EQ(0, flags.getAD());
+    EXPECT_EQ(0, flags.getCD());
+    EXPECT_EQ(0, flags.getRC());
+    EXPECT_NE(0, flags.getValue());
+
+    flags.setRA(0);
+    checkZero(flags);
+}
+
+TEST_F(HeaderFlagsTest, Zfield) {
+    HeaderFlags flags;
+    checkZero(flags);
+
+    flags.setZ(1);
+    EXPECT_EQ(0, flags.getQR());
+    EXPECT_EQ(0, flags.getOP());
+    EXPECT_EQ(0, flags.getAA());
+    EXPECT_EQ(0, flags.getTC());
+    EXPECT_EQ(0, flags.getRD());
+    EXPECT_EQ(0, flags.getRA());
+    EXPECT_EQ(1, flags.getZ());
+    EXPECT_EQ(0, flags.getAD());
+    EXPECT_EQ(0, flags.getCD());
+    EXPECT_EQ(0, flags.getRC());
+    EXPECT_NE(0, flags.getValue());
+
+    flags.setZ(0);
+    checkZero(flags);
+}
+
+TEST_F(HeaderFlagsTest, ADfield) {
+    HeaderFlags flags;
+    checkZero(flags);
+
+    flags.setAD(1);
+    EXPECT_EQ(0, flags.getQR());
+    EXPECT_EQ(0, flags.getOP());
+    EXPECT_EQ(0, flags.getAA());
+    EXPECT_EQ(0, flags.getTC());
+    EXPECT_EQ(0, flags.getRD());
+    EXPECT_EQ(0, flags.getRA());
+    EXPECT_EQ(0, flags.getZ());
+    EXPECT_EQ(1, flags.getAD());
+    EXPECT_EQ(0, flags.getCD());
+    EXPECT_EQ(0, flags.getRC());
+    EXPECT_NE(0, flags.getValue());
+
+    flags.setAD(0);
+    checkZero(flags);
+}
+
+TEST_F(HeaderFlagsTest, CDfield) {
+    HeaderFlags flags;
+    checkZero(flags);
+
+    flags.setCD(1);
+    EXPECT_EQ(0, flags.getQR());
+    EXPECT_EQ(0, flags.getOP());
+    EXPECT_EQ(0, flags.getAA());
+    EXPECT_EQ(0, flags.getTC());
+    EXPECT_EQ(0, flags.getRD());
+    EXPECT_EQ(0, flags.getRA());
+    EXPECT_EQ(0, flags.getZ());
+    EXPECT_EQ(0, flags.getAD());
+    EXPECT_EQ(1, flags.getCD());
+    EXPECT_EQ(0, flags.getRC());
+    EXPECT_NE(0, flags.getValue());
+
+    flags.setCD(0);
+    checkZero(flags);
+}
+
+TEST_F(HeaderFlagsTest, RCfield) {
+    HeaderFlags flags;
+    checkZero(flags);
+
+    flags.setRC(7);
+    EXPECT_EQ(0, flags.getQR());
+    EXPECT_EQ(0, flags.getOP());
+    EXPECT_EQ(0, flags.getAA());
+    EXPECT_EQ(0, flags.getTC());
+    EXPECT_EQ(0, flags.getRD());
+    EXPECT_EQ(0, flags.getRA());
+    EXPECT_EQ(0, flags.getZ());
+    EXPECT_EQ(0, flags.getAD());
+    EXPECT_EQ(0, flags.getCD());
+    EXPECT_EQ(7, flags.getRC());
+    EXPECT_NE(0, flags.getValue());
+
+    flags.setRC(0);
+    checkZero(flags);
+}
+
+// Check that the correct bits are set
+
+TEST_F(HeaderFlagsTest, bitValues) {
+    HeaderFlags flags;
+    checkZero(flags);
+
+    flags.setQR(1);
+    EXPECT_EQ(0x8000, flags.getValue());
+
+    flags.setQR(0);
+    flags.setOP(15);
+    EXPECT_EQ(0x7800, flags.getValue());
+
+    flags.setOP(0);
+    flags.setAA(1);
+    EXPECT_EQ(0x0400, flags.getValue());
+
+    flags.setAA(0);
+    flags.setTC(1);
+    EXPECT_EQ(0x0200, flags.getValue());
+
+    flags.setTC(0);
+    flags.setRD(1);
+    EXPECT_EQ(0x0100, flags.getValue());
+
+    flags.setRD(0);
+    flags.setRA(1);
+    EXPECT_EQ(0x0080, flags.getValue());
+
+    flags.setRA(0);
+    flags.setZ(1);
+    EXPECT_EQ(0x0040, flags.getValue());
+
+    flags.setZ(0);
+    flags.setAD(1);
+    EXPECT_EQ(0x0020, flags.getValue());
+
+    flags.setAD(0);
+    flags.setCD(1);
+    EXPECT_EQ(0x0010, flags.getValue());
+
+    flags.setCD(0);
+    flags.setRC(15);
+    EXPECT_EQ(0x000F, flags.getValue());
+}

+ 24 - 0
tests/tools/badpacket/tests/run_unittests.cc

@@ -0,0 +1,24 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    return (RUN_ALL_TESTS());
+}

+ 26 - 0
tests/tools/badpacket/version.h

@@ -0,0 +1,26 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __VERSION_H
+#define __VERSION_H
+
+namespace isc {
+namespace badpacket {
+
+static const char* BADPACKET_VERSION = "1.0-1";
+
+} // namespace badpacket
+} // namespace isc
+
+#endif // __VERSION_H