Browse Source

merged trac #372: TSIG RDATA support.

git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@3649 e5f2f494-b856-4b98-b285-d166d9295462
JINMEI Tatuya 14 years ago
parent
commit
2278ce7794
27 changed files with 1355 additions and 26 deletions
  1. 1 0
      src/lib/dns/Makefile.am
  2. 499 0
      src/lib/dns/rdata/any_255/tsig_250.cc
  3. 160 0
      src/lib/dns/rdata/any_255/tsig_250.h
  4. 1 10
      src/lib/dns/rrclass-placeholder.h
  5. 1 0
      src/lib/dns/tests/Makefile.am
  6. 368 0
      src/lib/dns/tests/rdata_tsig_unittest.cc
  7. 16 0
      src/lib/dns/tests/testdata/Makefile.am
  8. 80 16
      src/lib/dns/tests/testdata/gen-wiredata.py.in
  9. 6 0
      src/lib/dns/tests/testdata/rdata_tsig_fromWire1.spec
  10. 8 0
      src/lib/dns/tests/testdata/rdata_tsig_fromWire2.spec
  11. 8 0
      src/lib/dns/tests/testdata/rdata_tsig_fromWire3.spec
  12. 11 0
      src/lib/dns/tests/testdata/rdata_tsig_fromWire4.spec
  13. 7 0
      src/lib/dns/tests/testdata/rdata_tsig_fromWire5.spec
  14. 7 0
      src/lib/dns/tests/testdata/rdata_tsig_fromWire6.spec
  15. 8 0
      src/lib/dns/tests/testdata/rdata_tsig_fromWire7.spec
  16. 8 0
      src/lib/dns/tests/testdata/rdata_tsig_fromWire8.spec
  17. 8 0
      src/lib/dns/tests/testdata/rdata_tsig_fromWire9.spec
  18. 11 0
      src/lib/dns/tests/testdata/rdata_tsig_toWire1.spec
  19. 13 0
      src/lib/dns/tests/testdata/rdata_tsig_toWire2.spec
  20. 15 0
      src/lib/dns/tests/testdata/rdata_tsig_toWire3.spec
  21. 13 0
      src/lib/dns/tests/testdata/rdata_tsig_toWire4.spec
  22. 13 0
      src/lib/dns/tests/testdata/rdata_tsig_toWire5.spec
  23. 5 0
      src/lib/python/isc/utils/Makefile.am
  24. 0 0
      src/lib/python/isc/utils/__init__.py
  25. 37 0
      src/lib/python/isc/utils/process.py
  26. 12 0
      src/lib/python/isc/utils/tests/Makefile.am
  27. 39 0
      src/lib/python/isc/utils/tests/process_test.py

+ 1 - 0
src/lib/dns/Makefile.am

@@ -13,6 +13,7 @@ EXTRA_DIST += rrtype-placeholder.h
 
 # TODO: double-check that this is the only way
 # NOTE: when an rdata file is added, please also add to this list:
+EXTRA_DIST += rdata/any/tsig_250.cc
 EXTRA_DIST += rdata/in_1/aaaa_28.cc
 EXTRA_DIST += rdata/in_1/aaaa_28.h
 EXTRA_DIST += rdata/in_1/a_1.cc

+ 499 - 0
src/lib/dns/rdata/any_255/tsig_250.cc

@@ -0,0 +1,499 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include <boost/lexical_cast.hpp>
+
+#include <dns/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+
+#include <dns/util/base64.h>
+
+using namespace std;
+using namespace boost;
+
+// BEGIN_ISC_NAMESPACE
+// BEGIN_RDATA_NAMESPACE
+
+/// This is a straightforward representation of TSIG RDATA fields.
+struct TSIG::TSIGImpl {
+    TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
+             vector<uint8_t>& mac, uint16_t original_id, uint16_t error,
+             vector<uint8_t>& other_data) :
+        algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge),
+        mac_(mac), original_id_(original_id), error_(error),
+        other_data_(other_data)
+    {}
+    TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
+             size_t macsize, const void* mac, uint16_t original_id,
+             uint16_t error, size_t other_len, const void* other_data) :
+        algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge),
+        mac_(static_cast<const uint8_t*>(mac),
+             static_cast<const uint8_t*>(mac) + macsize),
+        original_id_(original_id), error_(error),
+        other_data_(static_cast<const uint8_t*>(other_data),
+                    static_cast<const uint8_t*>(other_data) + other_len)
+    {}
+
+    const Name algorithm_;
+    const uint64_t time_signed_;
+    const uint16_t fudge_;
+    const vector<uint8_t> mac_;
+    const uint16_t original_id_;
+    const uint16_t error_;
+    const vector<uint8_t> other_data_;
+};
+
+namespace {
+string
+getToken(istringstream& iss, const string& full_input) {
+    string token;
+    iss >> token;
+    if (iss.bad() || iss.fail()) {
+        isc_throw(InvalidRdataText, "Invalid TSIG text: parse error" <<
+                  full_input);
+    }
+    return (token);
+}
+
+// This helper function converts a string token to an *unsigned* integer.
+// NumType is a *signed* integral type (e.g. int32_t) that is sufficiently
+// wide to store resulting integers.
+// BitSize is the maximum number of bits that the resulting integer can take.
+// This function first checks whether the given token can be converted to
+// an integer of NumType type.  It then confirms the conversion result is
+// within the valid range, i.e., [0, 2^NumType - 1].  The second check is
+// necessary because lexical_cast<T> where T is an unsigned integer type
+// doesn't correctly reject negative numbers when compiled with SunStudio.
+template <typename NumType, int BitSize>
+NumType
+tokenToNum(const string& num_token) {
+    NumType num;
+    try {
+        num = lexical_cast<NumType>(num_token);
+    } catch (const boost::bad_lexical_cast& ex) {
+        isc_throw(InvalidRdataText, "Invalid TSIG numeric parameter: " <<
+                  num_token);
+    }
+    if (num < 0 || num >= (static_cast<NumType>(1) << BitSize)) {
+        isc_throw(InvalidRdataText, "Numeric TSIG parameter out of range: " <<
+                  num);
+    }
+    return (num);
+}
+}
+
+/// \brief Constructor from string.
+///
+/// \c tsig_str must be formatted as follows:
+/// \code <Alg> <Time> <Fudge> <MACsize> [<MAC>] <OrigID> <Error> <OtherLen> [<OtherData>]
+/// \endcode
+/// where
+/// - <Alg> is a valid textual representation of domain name.
+/// - <Time> is an unsigned 48-bit decimal integer.
+/// - <MACSize>, <OrigID>, and <OtherLen> are an unsigned 16-bit decimal
+///   integer.
+/// - <Error> is an unsigned 16-bit decimal integer or a valid mnemonic for
+///   the Error field specified in RFC2845.  Currently, "BADSIG", "BADKEY",
+///   and "BADTIME" are supported (case sensitive).  In future versions
+///   other representations that are compatible with the DNS RCODE will be
+///   supported.
+/// - <MAC> and <OtherData> is a BASE-64 encoded string that does not contain
+///   space characters.
+///   When <MACSize> and <OtherLen> is 0, <MAC> and <OtherData> must not
+///   appear in \c tsgi_str, respectively.
+/// - The decoded data of <MAC> is <MACSize> bytes of binary stream.
+/// - The decoded data of <OtherData> is <OtherLen> bytes of binary stream.
+///
+/// An example of valid string is:
+/// \code "hmac-sha256. 853804800 300 3 AAAA 2845 0 0" \endcode
+/// In this example <OtherData> is missing because <OtherLen> is 0.
+///
+/// Note that RFC2845 does not define the standard presentation format
+/// of %TSIG RR, so the above syntax is implementation specific.
+/// This is, however, compatible with the format acceptable to BIND 9's
+/// RDATA parser.
+///
+/// <b>Exceptions</b>
+///
+/// If <Alg> is not a valid domain name, a corresponding exception from
+/// the \c Name class will be thrown;
+/// if <MAC> or <OtherData> is not validly encoded in BASE-64, an exception
+/// of class \c isc::BadValue will be thrown;
+/// if %any of the other bullet points above is not met, an exception of
+/// class \c InvalidRdataText will be thrown.
+/// This constructor internally involves resource allocation, and if it fails
+/// a corresponding standard exception will be thrown.
+TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) {
+    istringstream iss(tsig_str);
+
+    const Name algorithm(getToken(iss, tsig_str));
+    const int64_t time_signed = tokenToNum<int64_t, 48>(getToken(iss,
+                                                                 tsig_str));
+    const int32_t fudge = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
+    const int32_t macsize = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
+
+    const string mac_txt = (macsize > 0) ? getToken(iss, tsig_str) : "";
+    vector<uint8_t> mac;
+    decodeBase64(mac_txt, mac);
+    if (mac.size() != macsize) {
+        isc_throw(InvalidRdataText, "TSIG MAC size and data are inconsistent");
+    }
+
+    const int32_t orig_id = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
+
+    const string error_txt = getToken(iss, tsig_str);
+    int32_t error = 0;
+    // XXX: In the initial implementation we hardcode the mnemonics.
+    // We'll soon generalize this.
+    if (error_txt == "BADSIG") {
+        error = 16;
+    } else if (error_txt == "BADKEY") {
+        error = 17;
+    } else if (error_txt == "BADTIME") {
+        error = 18;
+    } else {
+        error = tokenToNum<int32_t, 16>(error_txt);
+    }
+
+    const int32_t otherlen = tokenToNum<int32_t, 16>(getToken(iss, tsig_str));
+    const string otherdata_txt = (otherlen > 0) ? getToken(iss, tsig_str) : "";
+    vector<uint8_t> other_data;
+    decodeBase64(otherdata_txt, other_data);
+
+    if (!iss.eof()) {
+        isc_throw(InvalidRdataText, "Unexpected input for TSIG RDATA: " <<
+                  tsig_str);
+    }
+
+    impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id,
+                         error, other_data);
+}
+
+/// \brief Constructor from wire-format data.
+///
+/// When a read operation on \c buffer fails (e.g., due to a corrupted
+/// message) a corresponding exception from the \c InputBuffer class will
+/// be thrown.
+/// If the wire-format data does not begin with a valid domain name,
+/// a corresponding exception from the \c Name class will be thrown.
+/// In addition, this constructor internally involves resource allocation,
+/// and if it fails a corresponding standard exception will be thrown.
+///
+/// According to RFC3597, the Algorithm field must be a non compressed form
+/// of domain name.  But this implementation accepts a %TSIG RR even if that
+/// field is compressed.
+///
+/// \param buffer A buffer storing the wire format data.
+/// \param rdata_len The length of the RDATA in bytes, normally expected
+/// to be the value of the RDLENGTH field of the corresponding RR.
+/// But this constructor does not use this parameter; if necessary, the caller
+/// must check consistency between the length parameter and the actual
+/// RDATA length.
+TSIG::TSIG(InputBuffer& buffer, size_t) : impl_(NULL) {
+    Name algorithm(buffer);
+
+    uint8_t time_signed_buf[6];
+    buffer.readData(time_signed_buf, sizeof(time_signed_buf));
+    const uint64_t time_signed =
+        (static_cast<uint64_t>(time_signed_buf[0]) << 40 |
+         static_cast<uint64_t>(time_signed_buf[1]) << 32 |
+         static_cast<uint64_t>(time_signed_buf[2]) << 24 |
+         static_cast<uint64_t>(time_signed_buf[3]) << 16 |
+         static_cast<uint64_t>(time_signed_buf[4]) << 8 |
+         static_cast<uint64_t>(time_signed_buf[5]));
+
+    const uint16_t fudge = buffer.readUint16();
+
+    const uint16_t mac_size = buffer.readUint16();
+    vector<uint8_t> mac(mac_size);
+    if (mac_size > 0) {
+        buffer.readData(&mac[0], mac_size);
+    }
+
+    const uint16_t original_id = buffer.readUint16();
+    const uint16_t error = buffer.readUint16();
+
+    const uint16_t other_len = buffer.readUint16();
+    vector<uint8_t> other_data(other_len);
+    if (other_len > 0) {
+        buffer.readData(&other_data[0], other_len);
+    }
+
+    impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, original_id,
+                         error, other_data);
+}
+
+TSIG::TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
+           uint16_t mac_size, const void* mac, uint16_t original_id,
+           uint16_t error, uint16_t other_len, const void* other_data) :
+    impl_(NULL)
+{
+    // Time Signed is a 48-bit value.
+    if ((time_signed >> 48) != 0) {
+        isc_throw(OutOfRange, "TSIG Time Signed is too large: " <<
+                  time_signed);
+    }
+    if ((mac_size == 0 && mac != NULL) || (mac_size > 0 && mac == NULL)) {
+        isc_throw(InvalidParameter, "TSIG MAC size and data inconsistent");
+    }
+    if ((other_len == 0 && other_data != NULL) ||
+        (other_len > 0 && other_data == NULL)) {
+        isc_throw(InvalidParameter,
+                  "TSIG Other data length and data inconsistent");
+    }
+    impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac_size, mac,
+                         original_id, error, other_len, other_data);
+}
+
+/// \brief The copy constructor.
+///
+/// It internally allocates a resource, and if it fails a corresponding
+/// standard exception will be thrown.
+/// This constructor never throws an exception otherwise.
+TSIG::TSIG(const TSIG& source) : Rdata(), impl_(new TSIGImpl(*source.impl_))
+{}
+
+TSIG&
+TSIG::operator=(const TSIG& source) {
+    if (impl_ == source.impl_) {
+        return (*this);
+    }
+
+    TSIGImpl* newimpl = new TSIGImpl(*source.impl_);
+    delete impl_;
+    impl_ = newimpl;
+
+    return (*this);
+}
+
+TSIG::~TSIG() {
+    delete impl_;
+}
+
+/// \brief Convert the \c TSIG to a string.
+///
+/// The output of this method is formatted as described in the "from string"
+/// constructor (\c TSIG(const std::string&))).
+///
+/// If internal resource allocation fails, a corresponding
+/// standard exception will be thrown.
+///
+/// \return A \c string object that represents the \c TSIG object.
+std::string
+TSIG::toText() const {
+    string result;
+
+    result += impl_->algorithm_.toText() + " " +
+        lexical_cast<string>(impl_->time_signed_) + " " +
+        lexical_cast<string>(impl_->fudge_) + " " +
+        lexical_cast<string>(impl_->mac_.size()) + " ";
+    if (impl_->mac_.size() > 0) {
+        result += encodeBase64(impl_->mac_) + " ";
+    }
+    result += lexical_cast<string>(impl_->original_id_) + " ";
+    if (impl_->error_ == 16) {  // XXX: we'll soon introduce generic converter.
+        result += "BADSIG ";
+    } else if (impl_->error_ == 17) {
+        result += "BADKEY ";
+    } else if (impl_->error_ == 18) {
+        result += "BADTIME ";
+    } else {
+        result += lexical_cast<string>(impl_->error_) + " ";
+    }
+    result += lexical_cast<string>(impl_->other_data_.size());
+    if (impl_->other_data_.size() > 0) {
+        result += " " + encodeBase64(impl_->other_data_);
+    }
+
+    return (result);
+}
+
+// Common sequence of toWire() operations used for the two versions of
+// toWire().
+template <typename Output>
+void
+toWireCommon(const TSIG::TSIGImpl& impl, Output& output) {
+    output.writeUint16(impl.time_signed_ >> 32);
+    output.writeUint32(impl.time_signed_ & 0xffffffff);
+    output.writeUint16(impl.fudge_);
+    const uint16_t mac_size = impl.mac_.size();
+    output.writeUint16(mac_size);
+    if (mac_size > 0) {
+        output.writeData(&impl.mac_[0], mac_size);
+    }
+    output.writeUint16(impl.original_id_);
+    output.writeUint16(impl.error_);
+    const uint16_t other_len = impl.other_data_.size();
+    output.writeUint16(other_len);
+    if (other_len > 0) {
+        output.writeData(&impl.other_data_[0], other_len);
+    }
+}
+
+/// \brief Render the \c TSIG in the wire format without name compression.
+///
+/// If internal resource allocation fails, a corresponding
+/// standard exception will be thrown.
+/// This method never throws an exception otherwise.
+///
+/// \param buffer An output buffer to store the wire data.
+void
+TSIG::toWire(OutputBuffer& buffer) const {
+    impl_->algorithm_.toWire(buffer);
+    toWireCommon<OutputBuffer>(*impl_, buffer);
+}
+
+/// \brief Render the \c TSIG in the wire format with taking into account
+/// compression.
+///
+/// As specified in RFC3597, the Algorithm field (a domain name) will not
+/// be compressed.  However, the domain name could be a target of compression
+/// of other compressible names (though pretty unlikely), the offset
+/// information of the algorithm name may be recorded in \c renderer.
+///
+/// If internal resource allocation fails, a corresponding
+/// standard exception will be thrown.
+/// This method never throws an exception otherwise.
+///
+/// \param renderer DNS message rendering context that encapsulates the
+/// output buffer and name compression information.
+void
+TSIG::toWire(MessageRenderer& renderer) const {
+    renderer.writeName(impl_->algorithm_, false);
+    toWireCommon<MessageRenderer>(*impl_, renderer);
+}
+
+// A helper function commonly used for TSIG::compare().
+int
+vectorComp(const vector<uint8_t>& v1, const vector<uint8_t>& v2) {
+    const size_t this_size = v1.size();
+    const size_t other_size = v2.size();
+    if (this_size != other_size) {
+        return (this_size < other_size ? -1 : 1);
+    }
+    if (this_size > 0) {
+        return (memcmp(&v1[0], &v2[0], this_size));
+    }
+    return (0);
+}
+
+/// \brief Compare two instances of \c TSIG RDATA.
+///
+/// This method compares \c this and the \c other \c TSIG objects
+/// in terms of the DNSSEC sorting order as defined in RFC4034, and returns
+/// the result as an integer.
+///
+/// This method is expected to be used in a polymorphic way, and the
+/// parameter to compare against is therefore of the abstract \c Rdata class.
+/// However, comparing two \c Rdata objects of different RR types
+/// is meaningless, and \c other must point to a \c TSIG object;
+/// otherwise, the standard \c bad_cast exception will be thrown.
+/// This method never throws an exception otherwise.
+///
+/// \param other the right-hand operand to compare against.
+/// \return < 0 if \c this would be sorted before \c other.
+/// \return 0 if \c this is identical to \c other in terms of sorting order.
+/// \return > 0 if \c this would be sorted after \c other.
+int
+TSIG::compare(const Rdata& other) const {
+    const TSIG& other_tsig = dynamic_cast<const TSIG&>(other);
+
+    const int ncmp = compareNames(impl_->algorithm_,
+                                  other_tsig.impl_->algorithm_);
+    if (ncmp != 0) {
+        return (ncmp);
+    }
+
+    if (impl_->time_signed_ != other_tsig.impl_->time_signed_) {
+        return (impl_->time_signed_ < other_tsig.impl_->time_signed_ ? -1 : 1);
+    }
+    if (impl_->fudge_ != other_tsig.impl_->fudge_) {
+        return (impl_->fudge_ < other_tsig.impl_->fudge_ ? -1 : 1);
+    }
+    const int vcmp = vectorComp(impl_->mac_, other_tsig.impl_->mac_);
+    if (vcmp != 0) {
+        return (vcmp);
+    }
+    if (impl_->original_id_ != other_tsig.impl_->original_id_) {
+        return (impl_->original_id_ < other_tsig.impl_->original_id_ ? -1 : 1);
+    }
+    if (impl_->error_ != other_tsig.impl_->error_) {
+        return (impl_->error_ < other_tsig.impl_->error_ ? -1 : 1);
+    }
+    return (vectorComp(impl_->other_data_, other_tsig.impl_->other_data_));
+}
+
+const Name&
+TSIG::getAlgorithm() const {
+    return (impl_->algorithm_);
+}
+
+uint64_t
+TSIG::getTimeSigned() const {
+    return (impl_->time_signed_);
+}
+
+uint16_t
+TSIG::getFudge() const {
+    return (impl_->fudge_);
+}
+
+uint16_t
+TSIG::getMACSize() const {
+    return (impl_->mac_.size());
+}
+
+const void*
+TSIG::getMAC() const {
+    if (impl_->mac_.size() > 0) {
+        return (&impl_->mac_[0]);
+    } else {
+        return (NULL);
+    }
+}
+
+uint16_t
+TSIG::getOriginalID() const {
+    return (impl_->original_id_);
+}
+
+uint16_t
+TSIG::getError() const {
+    return (impl_->error_);
+}
+
+uint16_t
+TSIG::getOtherLen() const {
+    return (impl_->other_data_.size());
+}
+
+const void*
+TSIG::getOtherData() const {
+    if (impl_->other_data_.size() > 0) {
+        return (&impl_->other_data_[0]);
+    } else {
+        return (NULL);
+    }
+}
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE

+ 160 - 0
src/lib/dns/rdata/any_255/tsig_250.h

@@ -0,0 +1,160 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+// BEGIN_HEADER_GUARD
+
+#include <stdint.h>
+
+#include <string>
+
+#include <dns/rdata.h>
+
+namespace isc {
+namespace dns {
+class Name;
+}
+}
+
+// BEGIN_ISC_NAMESPACE
+
+// BEGIN_COMMON_DECLARATIONS
+// END_COMMON_DECLARATIONS
+
+// BEGIN_RDATA_NAMESPACE
+
+/// \brief \c rdata::TSIG class represents the TSIG RDATA as defined %in
+/// RFC2845.
+///
+/// This class implements the basic interfaces inherited from the abstract
+/// \c rdata::Rdata class, and provides trivial accessors specific to the
+/// TSIG RDATA.
+class TSIG : public Rdata {
+public:
+    // BEGIN_COMMON_MEMBERS
+    // END_COMMON_MEMBERS
+
+    /// \brief Constructor from RDATA field parameters.
+    ///
+    /// The parameters are a straightforward mapping of %TSIG RDATA
+    /// fields as defined %in RFC2845, but there are some implementation
+    /// specific notes as follows.
+    ///
+    /// \c algorithm is a \c Name object that specifies the algorithm.
+    /// For example, if the algorithm is HMAC-SHA256, \c algorithm would be
+    /// \c Name("hmac-sha256").
+    ///
+    /// \c time_signed corresponds to the Time Signed field, which is of
+    /// 48-bit unsigned integer type, and therefore cannot exceed 2^48-1;
+    /// otherwise, an exception of type \c OutOfRange will be thrown.
+    ///
+    /// \c mac_size and \c mac correspond to the MAC Size and MAC fields,
+    /// respectively.  When the MAC field is empty, \c mac must be NULL.
+    /// \c mac_size and \c mac must be consistent %in that \c mac_size is 0 if
+    /// and only if \c mac is NULL; otherwise an exception of type
+    /// InvalidParameter will be thrown.
+    ///
+    /// The same restriction applies to \c other_len and \c other_data,
+    /// which correspond to the Other Len and Other Data fields, respectively.
+    ///
+    /// This constructor internally involves resource allocation, and if
+    /// it fails, a corresponding standard exception will be thrown.
+    TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
+         uint16_t mac_size, const void* mac, uint16_t original_id,
+         uint16_t error, uint16_t other_len, const void* other_data);
+
+    /// \brief Assignment operator.
+    ///
+    /// It internally allocates a resource, and if it fails a corresponding
+    /// standard exception will be thrown.
+    /// This operator never throws an exception otherwise.
+    ///
+    /// This operator provides the strong exception guarantee: When an
+    /// exception is thrown the content of the assignment target will be
+    /// intact.
+    TSIG& operator=(const TSIG& source);
+
+    /// \brief The destructor.
+    ~TSIG();
+
+    /// \brief Return the algorithm name.
+    ///
+    /// This method never throws an exception.
+    const Name& getAlgorithm() const;
+
+    /// \brief Return the value of the Time Signed field.
+    ///
+    /// The returned value does not exceed 2^48-1.
+    ///
+    /// This method never throws an exception.
+    uint64_t getTimeSigned() const;
+
+    /// \brief Return the value of the Fudge field.
+    ///
+    /// This method never throws an exception.
+    uint16_t getFudge() const;
+
+    /// \brief Return the value of the MAC Size field.
+    ///
+    /// This method never throws an exception.
+    uint16_t getMACSize() const;
+
+    /// \brief Return the value of the MAC field.
+    ///
+    /// If the MAC field is empty, it returns NULL.
+    /// Otherwise, the memory region beginning at the address returned by
+    /// this method is valid up to the bytes specified by the return value
+    /// of \c getMACSize().
+    /// The memory region is only valid while the corresponding \c TSIG
+    /// object is valid.  The caller must hold the \c TSIG object while
+    /// it needs to refer to the region or it must make a local copy of the
+    /// region.
+    ///
+    /// This method never throws an exception.
+    const void* getMAC() const;
+
+    /// \brief Return the value of the Original ID field.
+    ///
+    /// This method never throws an exception.
+    uint16_t getOriginalID() const;
+
+    /// \brief Return the value of the Error field.
+    ///
+    /// This method never throws an exception.
+    uint16_t getError() const;
+
+    /// \brief Return the value of the Other Len field.
+    ///
+    /// This method never throws an exception.
+    uint16_t getOtherLen() const;
+
+    /// \brief Return the value of the Other Data field.
+    ///
+    /// The same note as \c getMAC() applies.
+    ///
+    /// This method never throws an exception.
+    const void* getOtherData() const;
+private:
+    struct TSIGImpl;
+    TSIGImpl* impl_;
+};
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
+// END_HEADER_GUARD
+
+// Local Variables:
+// mode: c++
+// End:

+ 1 - 10
src/lib/dns/rrclass-placeholder.h

@@ -244,14 +244,12 @@ public:
     // END_WELL_KNOWN_CLASS_DECLARATIONS
     
     static const RRClass& NONE();
-    static const RRClass& ANY();
 
 private:
     // \brief Meta-classes
     enum {
         RRCLASS_RESERVED0 = 0,
-        RRCLASS_NONE = 254,
-        RRCLASS_ANY = 255
+        RRCLASS_NONE = 254
     };
     uint16_t classcode_;
 };
@@ -266,13 +264,6 @@ RRClass::NONE() {
     return (rrclass);
 }
 
-inline const RRClass&
-RRClass::ANY() {
-    static RRClass rrclass(RRCLASS_ANY);
-
-    return (rrclass);
-}
-
 ///
 /// \brief Insert the \c RRClass as a string into stream.
 ///

+ 1 - 0
src/lib/dns/tests/Makefile.am

@@ -38,6 +38,7 @@ run_unittests_SOURCES += rdata_nsec_unittest.cc
 run_unittests_SOURCES += rdata_nsec3_unittest.cc
 run_unittests_SOURCES += rdata_nsec3param_unittest.cc
 run_unittests_SOURCES += rdata_rrsig_unittest.cc
+run_unittests_SOURCES += rdata_tsig_unittest.cc
 run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc
 run_unittests_SOURCES += question_unittest.cc
 run_unittests_SOURCES += rrparamregistry_unittest.cc

+ 368 - 0
src/lib/dns/tests/rdata_tsig_unittest.cc

@@ -0,0 +1,368 @@
+// Copyright (C) 2010  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.
+
+// $Id$
+
+#include <string>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/buffer.h>
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace {
+class Rdata_TSIG_Test : public RdataTest {
+protected:
+    vector<uint8_t> expect_data;
+};
+
+const char* const valid_text1 = "hmac-md5.sig-alg.reg.int. 1286779327 300 "
+    "0 16020 BADKEY 0";
+const char* const valid_text2 = "hmac-sha256. 1286779327 300 12 "
+    "FAKEFAKEFAKEFAKE 16020 BADSIG 0";
+
+const char* const valid_text3 = "hmac-sha1. 1286779327 300 12 "
+    "FAKEFAKEFAKEFAKE 16020 BADTIME 6 FAKEFAKE";
+const char* const valid_text4 = "hmac-sha1. 1286779327 300 12 "
+    "FAKEFAKEFAKEFAKE 16020 BADSIG 6 FAKEFAKE";
+const char* const valid_text5 = "hmac-sha256. 1286779327 300 12 "
+    "FAKEFAKEFAKEFAKE 16020 2845 0"; // using numeric error code
+const char* const too_long_label = "012345678901234567890123456789"
+    "0123456789012345678901234567890123";
+
+// commonly used test RDATA
+const any::TSIG rdata_tsig((string(valid_text1)));
+
+TEST_F(Rdata_TSIG_Test, createFromText) {
+    // normal case.  it also tests getter methods.
+    EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), rdata_tsig.getAlgorithm());
+    EXPECT_EQ(1286779327, rdata_tsig.getTimeSigned());
+    EXPECT_EQ(300, rdata_tsig.getFudge());
+    EXPECT_EQ(0, rdata_tsig.getMACSize());
+    EXPECT_EQ(static_cast<void*>(NULL), rdata_tsig.getMAC());
+    EXPECT_EQ(16020, rdata_tsig.getOriginalID());
+    EXPECT_EQ(17, rdata_tsig.getError()); // TODO: use constant
+    EXPECT_EQ(0, rdata_tsig.getOtherLen());
+    EXPECT_EQ(static_cast<void*>(NULL), rdata_tsig.getOtherData());
+
+    any::TSIG tsig2((string(valid_text2)));
+    EXPECT_EQ(12, tsig2.getMACSize());
+    EXPECT_EQ(16, tsig2.getError()); // TODO: use constant
+
+    any::TSIG tsig3((string(valid_text3)));
+    EXPECT_EQ(6, tsig3.getOtherLen());
+
+    // The other data is unusual, but we don't reject it.
+    EXPECT_NO_THROW(any::TSIG(string(valid_text4)));
+
+    // numeric representation of TSIG error
+    any::TSIG tsig5((string(valid_text5)));
+    EXPECT_EQ(2845, tsig5.getError());
+
+    //
+    // invalid cases
+    //
+    // there's a garbage parameter at the end
+    EXPECT_THROW(any::TSIG("foo 0 0 0 0 BADKEY 0 0"), InvalidRdataText);
+    // input is too short
+    EXPECT_THROW(any::TSIG("foo 0 0 0 0 BADKEY"), InvalidRdataText);
+    // bad domain name
+    EXPECT_THROW(any::TSIG(string(too_long_label) + "0 0 0 0 BADKEY 0"),
+                 TooLongLabel);
+    // time is too large (2814...6 is 2^48)
+    EXPECT_THROW(any::TSIG("foo 281474976710656 0 0 0 BADKEY 0"),
+                 InvalidRdataText);
+    // invalid time (negative)
+    EXPECT_THROW(any::TSIG("foo -1 0 0 0 BADKEY 0"), InvalidRdataText);
+    // fudge is too large
+    EXPECT_THROW(any::TSIG("foo 0 65536 0 0 BADKEY 0"), InvalidRdataText);
+    // invalid fudge (negative)
+    EXPECT_THROW(any::TSIG("foo 0 -1 0 0 BADKEY 0"), InvalidRdataText);
+    // MAC size is too large
+    EXPECT_THROW(any::TSIG("foo 0 0 65536 0 BADKEY 0"), InvalidRdataText);
+    // MAC size and MAC mismatch
+    EXPECT_THROW(any::TSIG("foo 0 0 9 FAKE 0 BADKEY 0"), InvalidRdataText);
+    EXPECT_THROW(any::TSIG("foo 0 0 0 FAKE 0 BADKEY 0"), InvalidRdataText);
+    // MAC is bad base64
+    EXPECT_THROW(any::TSIG("foo 0 0 3 FAK= 0 BADKEY 0"), isc::BadValue);
+    // Unknown error code
+    EXPECT_THROW(any::TSIG("foo 0 0 0 0 TEST 0"), InvalidRdataText);
+    // Numeric error code is too large
+    EXPECT_THROW(any::TSIG("foo 0 0 0 0 65536 0"), InvalidRdataText);
+    // Other len is too large
+    EXPECT_THROW(any::TSIG("foo 0 0 0 0 NOERROR 65536 FAKE"), InvalidRdataText);
+    // Other len and data mismatch
+    EXPECT_THROW(any::TSIG("foo 0 0 0 0 NOERROR 9 FAKE"), InvalidRdataText);
+    EXPECT_THROW(any::TSIG("foo 0 0 0 0 NOERROR 0 FAKE"), InvalidRdataText);
+}
+
+void
+fromWireCommonChecks(const any::TSIG& tsig) {
+    EXPECT_EQ(Name("hmac-sha256"), tsig.getAlgorithm());
+    EXPECT_EQ(1286978795, tsig.getTimeSigned());
+    EXPECT_EQ(300, tsig.getFudge());
+
+    vector<uint8_t> expect_mac(32, 'x');
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        &expect_mac[0], expect_mac.size(),
+                        tsig.getMAC(), tsig.getMACSize());
+
+    EXPECT_EQ(2845, tsig.getOriginalID());
+
+    EXPECT_EQ(0, tsig.getOtherLen());
+    EXPECT_EQ(static_cast<const void*>(NULL), tsig.getOtherData());
+}
+
+TEST_F(Rdata_TSIG_Test, createFromWire) {
+    RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(),
+                                        "rdata_tsig_fromWire1.wire"));
+    fromWireCommonChecks(dynamic_cast<any::TSIG&>(*rdata));
+}
+
+TEST_F(Rdata_TSIG_Test, createFromWireWithOtherData) {
+    RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(),
+                                        "rdata_tsig_fromWire2.wire"));
+    const any::TSIG& tsig(dynamic_cast<any::TSIG&>(*rdata));
+
+    EXPECT_EQ(18, tsig.getError());
+    const uint64_t otherdata = 1286978795 + 300 + 1; // time-signed + fudge + 1
+    expect_data.resize(6);
+    expect_data[0] = (otherdata >> 40);
+    expect_data[1] = ((otherdata >> 32) & 0xff);
+    expect_data[2] = ((otherdata >> 24) & 0xff);
+    expect_data[3] = ((otherdata >> 16) & 0xff);
+    expect_data[4] = ((otherdata >> 8) & 0xff);
+    expect_data[5] = (otherdata & 0xff);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        &expect_data[0], expect_data.size(),
+                        tsig.getOtherData(), tsig.getOtherLen());
+}
+
+TEST_F(Rdata_TSIG_Test, createFromWireWithoutMAC) {
+    RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(),
+                                        "rdata_tsig_fromWire3.wire"));
+    const any::TSIG& tsig(dynamic_cast<any::TSIG&>(*rdata));
+    EXPECT_EQ(16, tsig.getError());
+    EXPECT_EQ(0, tsig.getMACSize());
+    EXPECT_EQ(static_cast<const void*>(NULL), tsig.getMAC());
+}
+
+TEST_F(Rdata_TSIG_Test, createFromWireWithCompression) {
+    RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(),
+                                        "rdata_tsig_fromWire4.wire",
+                                        // we need to skip the dummy name:
+                                        Name("hmac-sha256").getLength()));
+    fromWireCommonChecks(dynamic_cast<any::TSIG&>(*rdata));
+}
+
+TEST_F(Rdata_TSIG_Test, badFromWire) {
+    // RDLENGTH is too short:
+    EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(),
+                                      "rdata_tsig_fromWire5.wire"),
+                 InvalidRdataLength);
+    // RDLENGTH is too long:
+    EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(),
+                                      "rdata_tsig_fromWire6.wire"),
+                 InvalidRdataLength);
+    // Algorithm name is broken:
+    EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(),
+                                      "rdata_tsig_fromWire7.wire"),
+                 DNSMessageFORMERR);
+    // MAC size is bogus:
+    EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(),
+                                      "rdata_tsig_fromWire8.wire"),
+                 InvalidBufferPosition);
+    // Other-data length is bogus:
+    EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(),
+                                      "rdata_tsig_fromWire9.wire"),
+                 InvalidBufferPosition);
+}
+
+TEST_F(Rdata_TSIG_Test, copyConstruct) {
+    const any::TSIG copy(rdata_tsig);
+    EXPECT_EQ(0, copy.compare(rdata_tsig));
+
+    // Check the copied data is valid even after the original is deleted
+    any::TSIG* copy2 = new any::TSIG(rdata_tsig);
+    any::TSIG copy3(*copy2);
+    delete copy2;
+    EXPECT_EQ(0, copy3.compare(rdata_tsig));
+}
+
+TEST_F(Rdata_TSIG_Test, createFromParams) {
+    EXPECT_EQ(0, rdata_tsig.compare(any::TSIG(Name("hmac-md5.sig-alg.reg.int"),
+                                              1286779327, 300, 0, NULL, 16020,
+                                              17, 0, NULL)));
+
+    const uint8_t fake_data[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84,
+                                  0x14, 0x02, 0x84, 0x14, 0x02, 0x84 }; 
+    EXPECT_EQ(0, any::TSIG((string(valid_text2))).compare(
+                  any::TSIG(Name("hmac-sha256"), 1286779327, 300, 12,
+                            fake_data, 16020, 16, 0, NULL)));
+
+    const uint8_t fake_data2[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84 };
+    EXPECT_EQ(0, any::TSIG((string(valid_text3))).compare(
+                  any::TSIG(Name("hmac-sha1"), 1286779327, 300, 12,
+                            fake_data, 16020, 18, 6, fake_data2)));
+
+    EXPECT_THROW(any::TSIG(Name("hmac-sha256"), 1LLU << 48, 300, 12,
+                           fake_data, 16020, 18, 6, fake_data2),
+                 isc::OutOfRange);
+    EXPECT_THROW(any::TSIG(Name("hmac-sha256"), 0, 300, 0, fake_data, 16020,
+                           18, 0, NULL),
+                 isc::InvalidParameter);
+    EXPECT_THROW(any::TSIG(Name("hmac-sha256"), 0, 300, 12, NULL, 16020,
+                           18, 0, NULL),
+                 isc::InvalidParameter);
+    EXPECT_THROW(any::TSIG(Name("hmac-sha256"), 0, 300, 0, NULL, 16020,
+                           18, 0, fake_data),
+                 isc::InvalidParameter);
+    EXPECT_THROW(any::TSIG(Name("hmac-sha256"), 0, 300, 0, NULL, 16020,
+                           18, 6, NULL),
+                 isc::InvalidParameter);
+}
+
+TEST_F(Rdata_TSIG_Test, assignment) {
+    any::TSIG copy((string(valid_text2)));
+    copy = rdata_tsig;
+    EXPECT_EQ(0, copy.compare(rdata_tsig));
+
+    // Check if the copied data is valid even after the original is deleted
+    any::TSIG* copy2 = new any::TSIG(rdata_tsig);
+    any::TSIG copy3((string(valid_text2)));
+    copy3 = *copy2;
+    delete copy2;
+    EXPECT_EQ(0, copy3.compare(rdata_tsig));
+
+    // Self assignment
+    copy = copy;
+    EXPECT_EQ(0, copy.compare(rdata_tsig));
+}
+
+template <typename Output>
+void
+toWireCommonChecks(Output& output) {
+    vector<uint8_t> expect_data;
+
+    output.clear();
+    expect_data.clear();
+    rdata_tsig.toWire(output);
+    // read the expected wire format data and trim the RDLEN part.
+    UnitTestUtil::readWireData("rdata_tsig_toWire1.wire", expect_data);
+    expect_data.erase(expect_data.begin(), expect_data.begin() + 2);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        &expect_data[0], expect_data.size(),
+                        output.getData(), output.getLength());
+
+    expect_data.clear();
+    output.clear();
+    any::TSIG(string(valid_text2)).toWire(output);
+    UnitTestUtil::readWireData("rdata_tsig_toWire2.wire", expect_data);
+    expect_data.erase(expect_data.begin(), expect_data.begin() + 2);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        &expect_data[0], expect_data.size(),
+                        output.getData(), output.getLength());
+
+    expect_data.clear();
+    output.clear();
+    any::TSIG(string(valid_text3)).toWire(output);
+    UnitTestUtil::readWireData("rdata_tsig_toWire3.wire", expect_data);
+    expect_data.erase(expect_data.begin(), expect_data.begin() + 2);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        &expect_data[0], expect_data.size(),
+                        output.getData(), output.getLength());
+}
+
+TEST_F(Rdata_TSIG_Test, toWireBuffer) {
+    toWireCommonChecks<OutputBuffer>(obuffer);
+}
+
+TEST_F(Rdata_TSIG_Test, toWireRenderer) {
+    toWireCommonChecks<MessageRenderer>(renderer);
+
+    // check algorithm name won't compressed when it would otherwise.
+    expect_data.clear();
+    renderer.clear();
+    renderer.writeName(Name("hmac-md5.sig-alg.reg.int"));
+    renderer.writeUint16(42); // RDLEN
+    rdata_tsig.toWire(renderer);
+    UnitTestUtil::readWireData("rdata_tsig_toWire4.wire", expect_data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        &expect_data[0], expect_data.size(),
+                        renderer.getData(), renderer.getLength());
+
+    // check algorithm can be used as a compression target.
+    expect_data.clear();
+    renderer.clear();
+    renderer.writeUint16(42);
+    rdata_tsig.toWire(renderer);
+    renderer.writeName(Name("hmac-md5.sig-alg.reg.int"));
+    UnitTestUtil::readWireData("rdata_tsig_toWire5.wire", expect_data);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        &expect_data[0], expect_data.size(),
+                        renderer.getData(), renderer.getLength());
+}
+
+TEST_F(Rdata_TSIG_Test, toText) {
+    EXPECT_EQ(string(valid_text1), rdata_tsig.toText());
+    EXPECT_EQ(string(valid_text2), any::TSIG(string(valid_text2)).toText());
+    EXPECT_EQ(string(valid_text3), any::TSIG(string(valid_text3)).toText());
+    EXPECT_EQ(string(valid_text5), any::TSIG(string(valid_text5)).toText());
+}
+
+TEST_F(Rdata_TSIG_Test, compare) {
+    // test RDATAs, sorted in the ascendent order.
+    // "AAAA" encoded in BASE64 corresponds to 0x000000, so it should be the
+    // smallest data of the same length.
+    vector<any::TSIG> compare_set;
+    compare_set.push_back(any::TSIG("a.example 0 300 0 16020 0 0"));
+    compare_set.push_back(any::TSIG("example 0 300 0 16020 0 0"));
+    compare_set.push_back(any::TSIG("example 1 300 0 16020 0 0"));
+    compare_set.push_back(any::TSIG("example 1 600 0 16020 0 0"));
+    compare_set.push_back(any::TSIG("example 1 600 3 AAAA 16020 0 0"));
+    compare_set.push_back(any::TSIG("example 1 600 3 FAKE 16020 0 0"));
+    compare_set.push_back(any::TSIG("example 1 600 3 FAKE 16021 0 0"));
+    compare_set.push_back(any::TSIG("example 1 600 3 FAKE 16021 1 0"));
+    compare_set.push_back(any::TSIG("example 1 600 3 FAKE 16021 1 3 AAAA"));
+    compare_set.push_back(any::TSIG("example 1 600 3 FAKE 16021 1 3 FAKE"));
+
+    EXPECT_EQ(0, compare_set[0].compare(
+                  any::TSIG("A.EXAMPLE 0 300 0 16020 0 0")));
+
+    vector<any::TSIG>::const_iterator it;
+    vector<any::TSIG>::const_iterator it_end = compare_set.end();
+    for (it = compare_set.begin(); it != it_end - 1; ++it) {
+        EXPECT_GT(0, (*it).compare(*(it + 1)));
+        EXPECT_LT(0, (*(it + 1)).compare(*it));
+    }
+
+    // comparison attempt between incompatible RR types should be rejected
+    EXPECT_THROW(rdata_tsig.compare(*RdataTest::rdata_nomatch), bad_cast);
+}
+}

+ 16 - 0
src/lib/dns/tests/testdata/Makefile.am

@@ -12,6 +12,14 @@ BUILT_SOURCES += rdata_rrsig_fromWire2.wire
 BUILT_SOURCES += rdata_soa_toWireUncompressed.wire
 BUILT_SOURCES +=  rdata_txt_fromWire2.wire rdata_txt_fromWire3.wire
 BUILT_SOURCES += rdata_txt_fromWire4.wire rdata_txt_fromWire5.wire
+BUILT_SOURCES += rdata_tsig_fromWire1.wire rdata_tsig_fromWire2.wire
+BUILT_SOURCES += rdata_tsig_fromWire3.wire rdata_tsig_fromWire4.wire
+BUILT_SOURCES += rdata_tsig_fromWire5.wire rdata_tsig_fromWire6.wire
+BUILT_SOURCES += rdata_tsig_fromWire7.wire rdata_tsig_fromWire8.wire
+BUILT_SOURCES += rdata_tsig_fromWire9.wire
+BUILT_SOURCES += rdata_tsig_toWire1.wire rdata_tsig_toWire2.wire
+BUILT_SOURCES += rdata_tsig_toWire3.wire rdata_tsig_toWire4.wire
+BUILT_SOURCES += rdata_tsig_toWire5.wire
 
 # NOTE: keep this in sync with real file listing
 # so is included in tarball
@@ -51,6 +59,14 @@ EXTRA_DIST += rdata_txt_fromWire5.spec rdata_unknown_fromWire
 EXTRA_DIST += rrcode16_fromWire1 rrcode16_fromWire2
 EXTRA_DIST += rrcode32_fromWire1 rrcode32_fromWire2
 EXTRA_DIST += rrset_toWire1 rrset_toWire2
+EXTRA_DIST += rdata_tsig_fromWire1.spec rdata_tsig_fromWire2.spec
+EXTRA_DIST += rdata_tsig_fromWire3.spec rdata_tsig_fromWire4.spec
+EXTRA_DIST += rdata_tsig_fromWire5.spec rdata_tsig_fromWire6.spec
+EXTRA_DIST += rdata_tsig_fromWire7.spec rdata_tsig_fromWire8.spec
+EXTRA_DIST += rdata_tsig_fromWire9.spec
+EXTRA_DIST += rdata_tsig_toWire1.spec rdata_tsig_toWire2.spec
+EXTRA_DIST += rdata_tsig_toWire3.spec rdata_tsig_toWire4.spec
+EXTRA_DIST += rdata_tsig_toWire5.spec
 
 .spec.wire:
 	./gen-wiredata.py -o $@ $<

+ 80 - 16
src/lib/dns/tests/testdata/gen-wiredata.py.in

@@ -19,8 +19,8 @@ import configparser, re, time, sys
 from datetime import datetime
 from optparse import OptionParser
 
-re_hex = re.compile(r'0x[0-9a-fA-F]+')
-re_decimal = re.compile(r'\d+$')
+re_hex = re.compile(r'^0x[0-9a-fA-F]+')
+re_decimal = re.compile(r'^\d+$')
 re_string = re.compile(r"\'(.*)\'$")
 
 dnssec_timefmt = '%Y%m%d%H%M%S'
@@ -48,9 +48,12 @@ dict_rrtype = { 'none' : 0, 'a' : 1, 'ns' : 2, 'md' : 3, 'mf' : 4, 'cname' : 5,
                 'maila' : 254, 'any' : 255 }
 rdict_rrtype = dict([(dict_rrtype[k], k.upper()) for k in dict_rrtype.keys()])
 dict_rrclass = { 'in' : 1, 'ch' : 3, 'hs' : 4, 'any' : 255 }
-rdict_rrclass = dict([(dict_rrclass[k], k.upper()) for k in dict_rrclass.keys()])
-dict_algorithm = { 'rsamd5' : 1, 'dh' : 2, 'dsa' : 3, 'ecc' : 4, 'rsasha1' : 5 }
-rdict_algorithm = dict([(dict_algorithm[k], k.upper()) for k in dict_algorithm.keys()])
+rdict_rrclass = dict([(dict_rrclass[k], k.upper()) for k in \
+                          dict_rrclass.keys()])
+dict_algorithm = { 'rsamd5' : 1, 'dh' : 2, 'dsa' : 3, 'ecc' : 4,
+                   'rsasha1' : 5 }
+rdict_algorithm = dict([(dict_algorithm[k], k.upper()) for k in \
+                            dict_algorithm.keys()])
 
 header_xtables = { 'qr' : dict_qr, 'opcode' : dict_opcode,
                    'rcode' : dict_rcode }
@@ -75,13 +78,17 @@ def code_totext(code, dict):
         return dict[code] + '(' + str(code) + ')'
     return str(code)
 
-def encode_name(name, absolute = True):
+def encode_name(name, absolute=True):
     # make sure the name is dot-terminated.  duplicate dots will be ignored
     # below.
     name += '.'
     labels = name.split('.')
     wire = ''
     for l in labels:
+        if len(l) > 4 and l[0:4] == 'ptr=':
+            # special meta-syntax for compression pointer
+            wire += ' %04x' % (0xc000 | int(l[4:]))
+            break
         if absolute or len(l) > 0:
             wire += '%02x' % len(l)
             wire += ''.join(['%02x' % ord(ch) for ch in l])
@@ -89,7 +96,9 @@ def encode_name(name, absolute = True):
             break
     return wire
 
-def encode_string(name):
+def encode_string(name, len=None):
+    if type(name) is int and len is not None:
+        return '%0.*x' % (len * 2, name)
     return ''.join(['%02x' % ord(ch) for ch in name])
 
 def count_namelabels(name):
@@ -121,17 +130,19 @@ def print_header(f, input_file):
 
 class Name:
     name = 'example.com'
-    pointer = -1                # no compression by default
+    pointer = None                # no compression by default
     def dump(self, f):
-        name_wire = encode_name(self.name,
-                                True if self.pointer == -1 else False)
+        name = self.name
+        if self.pointer is not None:
+            if len(name) > 0 and name[-1] != '.':
+                name += '.'
+            name += 'ptr=%d' % self.pointer
+        name_wire = encode_name(name)
         f.write('\n# DNS Name: %s' % self.name)
-        if self.pointer >= 0:
+        if self.pointer is not None:
             f.write(' + compression pointer: %d' % self.pointer)
         f.write('\n')
         f.write('%s' % name_wire)
-        if self.pointer >= 0:
-            f.write(' %04x' % (0xc000 | self.pointer))
         f.write('\n')
 
 class DNSHeader:
@@ -338,20 +349,73 @@ class RRSIG:
                 (code_totext(self.covered, rdict_rrtype),
                  code_totext(self.algorithm, rdict_algorithm), labels,
                  self.originalttl))
-        f.write('%04x %02x %02x %08x\n' % (self.covered, self.algorithm, labels,
-                                           self.originalttl))
+        f.write('%04x %02x %02x %08x\n' % (self.covered, self.algorithm,
+                                           labels, self.originalttl))
         f.write('# Expiration=%s, Inception=%s\n' %
                 (str(self.expiration), str(self.inception)))
         f.write('%08x %08x\n' % (self.expiration, self.inception))
         f.write('# Tag=%d Signer=%s and Signature\n' % (self.tag, self.signer))
         f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire))
 
+class TSIG:
+    rdlen = None                # auto-calculate
+    algorithm = 'hmac-sha256'
+    time_signed = 1286978795    # arbitrarily chosen default
+    fudge = 300
+    mac_size = None             # use a common value for the algorithm
+    mac = None                  # use 'x' * mac_size
+    original_id = 2845          # arbitrarily chosen default
+    error = 0
+    other_len = None         # 6 if error is BADTIME; otherwise 0
+    other_data = None        # use time_signed + fudge + 1 for BADTIME
+    dict_macsize = { 'hmac-md5' : 16, 'hmac-sha1' : 20, 'hmac-sha256' : 32 }
+    def dump(self, f):
+        if str(self.algorithm) == 'hmac-md5':
+            name_wire = encode_name('hmac-md5.sig-alg.reg.int')
+        else:
+            name_wire = encode_name(self.algorithm)
+        rdlen = self.rdlen
+        mac_size = self.mac_size
+        if mac_size is None:
+            if self.algorithm in self.dict_macsize.keys():
+                mac_size = self.dict_macsize[self.algorithm]
+            else:
+                raise RuntimeError('TSIG Mac Size cannot be determined')
+        mac = encode_string('x' * mac_size) if self.mac is None else \
+            encode_string(self.mac, mac_size)
+        other_len = self.other_len
+        if other_len is None:
+            # 18 = BADTIME
+            other_len = 6 if self.error == 18 else 0
+        other_data = self.other_data
+        if other_data is None:
+            other_data = '%012x' % (self.time_signed + self.fudge + 1) \
+                if self.error == 18 else ''
+        else:
+            other_data = encode_string(self.other_data, other_len)
+        if rdlen is None:
+            rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \
+                            len(other_data) / 2)
+        f.write('\n# TSIG RDATA (RDLEN=%d)\n' % rdlen)
+        f.write('%04x\n' % rdlen);
+        f.write('# Algorithm=%s Time-Signed=%d Fudge=%d\n' %
+                (self.algorithm, self.time_signed, self.fudge))
+        f.write('%s %012x %04x\n' % (name_wire, self.time_signed, self.fudge))
+        f.write('# MAC Size=%d MAC=(see hex)\n' % mac_size)
+        f.write('%04x%s\n' % (mac_size, ' ' + mac if len(mac) > 0 else ''))
+        f.write('# Original-ID=%d Error=%d\n' % (self.original_id, self.error))
+        f.write('%04x %04x\n' %  (self.original_id, self.error))
+        f.write('# Other-Len=%d Other-Data=(see hex)\n' % other_len)
+        f.write('%04x%s\n' % (other_len,
+                              ' ' + other_data if len(other_data) > 0 else ''))
+
 def get_config_param(section):
     config_param = {'name' : (Name, {}),
                     'header' : (DNSHeader, header_xtables),
                     'question' : (DNSQuestion, question_xtables),
                     'edns' : (EDNS, {}), 'soa' : (SOA, {}), 'txt' : (TXT, {}),
-                    'rrsig' : (RRSIG, {}), 'nsec' : (NSEC, {})}
+                    'rrsig' : (RRSIG, {}), 'nsec' : (NSEC, {}),
+                    'tsig' : (TSIG, {}) }
     s = section
     m = re.match('^([^:]+)/\d+$', section)
     if m:

+ 6 - 0
src/lib/dns/tests/testdata/rdata_tsig_fromWire1.spec

@@ -0,0 +1,6 @@
+#
+# A simplest form of TSIG: all default parameters
+#
+[custom]
+sections: tsig
+[tsig]

+ 8 - 0
src/lib/dns/tests/testdata/rdata_tsig_fromWire2.spec

@@ -0,0 +1,8 @@
+#
+# TSIG with other data (error = BADTIME(18))
+#
+[custom]
+sections: tsig
+[tsig]
+mac_size: 0
+error: 18

+ 8 - 0
src/lib/dns/tests/testdata/rdata_tsig_fromWire3.spec

@@ -0,0 +1,8 @@
+#
+# TSIG without MAC (error = BADSIG(16))
+#
+[custom]
+sections: tsig
+[tsig]
+mac_size: 0
+error: 16

+ 11 - 0
src/lib/dns/tests/testdata/rdata_tsig_fromWire4.spec

@@ -0,0 +1,11 @@
+#
+# A simplest form of TSIG, but the algorithm name is compressed (quite
+# pathological, but we accept it)
+#
+[custom]
+sections: name:tsig
+[name]
+name: hmac-sha256
+[tsig]
+algorithm: ptr=0
+mac_size: 32

+ 7 - 0
src/lib/dns/tests/testdata/rdata_tsig_fromWire5.spec

@@ -0,0 +1,7 @@
+#
+# TSIG-like RDATA but RDLEN is too short.
+#
+[custom]
+sections: tsig
+[tsig]
+rdlen: 60

+ 7 - 0
src/lib/dns/tests/testdata/rdata_tsig_fromWire6.spec

@@ -0,0 +1,7 @@
+#
+# TSIG-like RDATA but RDLEN is too long.
+#
+[custom]
+sections: tsig
+[tsig]
+rdlen: 63

+ 8 - 0
src/lib/dns/tests/testdata/rdata_tsig_fromWire7.spec

@@ -0,0 +1,8 @@
+#
+# TSIG-like RDATA but algorithm name is broken.
+#
+[custom]
+sections: tsig
+[tsig]
+algorithm: "01234567890123456789012345678901234567890123456789012345678901234"
+mac_size: 32

+ 8 - 0
src/lib/dns/tests/testdata/rdata_tsig_fromWire8.spec

@@ -0,0 +1,8 @@
+#
+# TSIG-like RDATA but MAC size is bogus
+#
+[custom]
+sections: tsig
+[tsig]
+mac_size: 65535
+mac: "dummy data"

+ 8 - 0
src/lib/dns/tests/testdata/rdata_tsig_fromWire9.spec

@@ -0,0 +1,8 @@
+#
+# TSIG-like RDATA but Other-Data length is bogus
+#
+[custom]
+sections: tsig
+[tsig]
+other_len: 65535
+otherdata: "dummy data"

+ 11 - 0
src/lib/dns/tests/testdata/rdata_tsig_toWire1.spec

@@ -0,0 +1,11 @@
+#
+# An artificial TSIG RDATA for toWire test.
+#
+[custom]
+sections: tsig
+[tsig]
+algorithm: hmac-md5
+time_signed: 1286779327
+mac_size: 0
+original_id: 16020
+error: 17

+ 13 - 0
src/lib/dns/tests/testdata/rdata_tsig_toWire2.spec

@@ -0,0 +1,13 @@
+#
+# An artificial TSIG RDATA for toWire test.
+#
+[custom]
+sections: tsig
+[tsig]
+algorithm: hmac-sha256
+time_signed: 1286779327
+mac_size: 12
+# 0x1402... would be FAKEFAKE... if encoded in BASE64
+mac: 0x140284140284140284140284
+original_id: 16020
+error: 16

+ 15 - 0
src/lib/dns/tests/testdata/rdata_tsig_toWire3.spec

@@ -0,0 +1,15 @@
+#
+# An artificial TSIG RDATA for toWire test.
+#
+[custom]
+sections: tsig
+[tsig]
+algorithm: hmac-sha1
+time_signed: 1286779327
+mac_size: 12
+# 0x1402... would be FAKEFAKE... if encoded in BASE64
+mac: 0x140284140284140284140284
+original_id: 16020
+error: 18
+other_len: 6
+other_data: 0x140284140284

+ 13 - 0
src/lib/dns/tests/testdata/rdata_tsig_toWire4.spec

@@ -0,0 +1,13 @@
+#
+# An artificial TSIG RDATA for toWire test.
+#
+[custom]
+sections: name:tsig
+[name]
+name: hmac-md5.sig-alg.reg.int.
+[tsig]
+algorithm: hmac-md5
+time_signed: 1286779327
+mac_size: 0
+original_id: 16020
+error: 17

+ 13 - 0
src/lib/dns/tests/testdata/rdata_tsig_toWire5.spec

@@ -0,0 +1,13 @@
+#
+# An artificial TSIG RDATA for toWire test.
+#
+[custom]
+sections: tsig:name
+[tsig]
+algorithm: hmac-md5
+time_signed: 1286779327
+mac_size: 0
+original_id: 16020
+error: 17
+[name]
+name: ptr=2

+ 5 - 0
src/lib/python/isc/utils/Makefile.am

@@ -0,0 +1,5 @@
+SUBDIRS = tests
+
+python_PYTHON = __init__.py process.py
+
+pythondir = $(pyexecdir)/isc/utils

+ 0 - 0
src/lib/python/isc/utils/__init__.py


+ 37 - 0
src/lib/python/isc/utils/process.py

@@ -0,0 +1,37 @@
+# Copyright (C) 2010  CZ NIC
+#
+# Permission to use, copy, modify, and 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 INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM 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.
+
+"""
+Module to manipulate the python processes.
+
+It contains only function to rename the process, which is currently
+wrapper around setproctitle library. Does not fail if the setproctitle
+module is missing, but does nothing in that case.
+"""
+try:
+    from setproctitle import setproctitle
+except ImportError:
+    def setproctitle(_): pass
+import sys
+import os.path
+
+"""
+Rename the current process to given name (so it can be found in ps).
+If name is None, use zero'th command line argument.
+"""
+def rename(name=None):
+    if name is None:
+        name = os.path.basename(sys.argv[0])
+    setproctitle(name)

+ 12 - 0
src/lib/python/isc/utils/tests/Makefile.am

@@ -0,0 +1,12 @@
+PYTESTS = process_test.py
+EXTRA_DIST = $(PYTESTS)
+
+# later will have configure option to choose this, like: coverage run --branch
+PYCOVERAGE = $(PYTHON)
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+	for pytest in $(PYTESTS) ; do \
+	echo Running test: $$pytest ; \
+	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+	$(PYCOVERAGE) $(abs_srcdir)/$$pytest || exit ; \
+	done

+ 39 - 0
src/lib/python/isc/utils/tests/process_test.py

@@ -0,0 +1,39 @@
+# Copyright (C) 2010  CZ NIC
+#
+# Permission to use, copy, modify, and 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 INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM 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.
+
+"""Tests for isc.utils.process."""
+import unittest
+import isc.utils.process
+run_tests = True
+try:
+    import setproctitle
+except ImportError:
+    run_tests = False
+
+class TestRename(unittest.TestCase):
+    """Testcase for isc.process.rename."""
+    def __get_self_name(self):
+        return setproctitle.getproctitle()
+
+    @unittest.skipIf(not run_tests, "Setproctitle not installed, not testing")
+    def test_rename(self):
+        """Test if the renaming function works."""
+        isc.utils.process.rename("rename-test")
+        self.assertEqual("rename-test", self.__get_self_name())
+        isc.utils.process.rename()
+        self.assertEqual("process_test.py", self.__get_self_name())
+
+if __name__ == "__main__":
+    unittest.main()