Browse Source

for trac #256: completely revised the base32(hex) implementation by
generalizing the base64 logiic. fully detailed tests are provided which now
pass. documentation was also provided.
using this opportunity I moved non DNS helper classes/functions to a separate
'util' directory to make it clearer which files are for the public API.


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac256@2482 e5f2f494-b856-4b98-b285-d166d9295462

JINMEI Tatuya 15 years ago
parent
commit
96a06a2baf

+ 3 - 3
src/lib/datasrc/data_source.cc

@@ -28,14 +28,14 @@
 #include <datasrc/data_source.h>
 #include <datasrc/query.h>
 
-#include <dns/base32.h>
+#include <dns/util/base32hex.h>
 #include <dns/buffer.h>
 #include <dns/message.h>
 #include <dns/name.h>
 #include <dns/rdataclass.h>
 #include <dns/rrset.h>
 #include <dns/rrsetlist.h>
-#include <dns/sha1.h>
+#include <dns/util/sha1.h>
 
 #include <cc/data.h>
 
@@ -1234,7 +1234,7 @@ Nsec3Param::getHash(const Name& name) const {
         inlength = SHA1_HASHSIZE;
     } while (n++ < iterations_);
 
-    return (encodeBase32(vector<uint8_t>(digest, digest + SHA1_HASHSIZE)));
+    return (encodeBase32Hex(vector<uint8_t>(digest, digest + SHA1_HASHSIZE)));
 }
 
 //

+ 6 - 8
src/lib/dns/Makefile.am

@@ -57,12 +57,13 @@ BUILT_SOURCES = rrclass.h rrtype.h rrparamregistry.cc
 
 lib_LTLIBRARIES = libdns++.la
 
-libdns___la_SOURCES = base32.h base32.cc
-libdns___la_SOURCES += base64.h base64.cc
+libdns___la_SOURCES = util/base32hex.h util/base64.h util/base_n.cc
+libdns___la_SOURCES += util/base32hex_from_binary.h
+libdns___la_SOURCES += util/binary_from_base32hex.h
 libdns___la_SOURCES += buffer.h
 libdns___la_SOURCES += dnssectime.h dnssectime.cc
 libdns___la_SOURCES += exceptions.h exceptions.cc
-libdns___la_SOURCES += hex.h hex.cc
+libdns___la_SOURCES += util/hex.h util/hex.cc
 libdns___la_SOURCES += message.h message.cc
 libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
 libdns___la_SOURCES += name.h name.cc
@@ -74,7 +75,7 @@ libdns___la_SOURCES += rrsetlist.h rrsetlist.cc
 libdns___la_SOURCES += rrttl.h rrttl.cc
 libdns___la_SOURCES += rrtype.cc
 libdns___la_SOURCES += question.h question.cc
-libdns___la_SOURCES += sha1.h sha1.cc
+libdns___la_SOURCES += util/sha1.h util/sha1.cc
 libdns___la_SOURCES += tsig.h tsig.cc
 
 nodist_libdns___la_SOURCES = rdataclass.cc rrclass.h rrtype.h
@@ -105,9 +106,6 @@ libdns++_include_HEADERS = \
 	rrtype.h \
 	tsig.h
 # Purposely not installing these headers:
-# base32.h # used only internally, and not actually DNS specific
-# base64.h # used only internally, and not actually DNS specific
-# hex.h # used only internally, and not actually DNS specific
-# sha1.h # used only internally, and not actually DNS specific
+# util/*.h: used only internally, and not actually DNS specific
 # rrclass-placeholder.h
 # rrtype-placeholder.h

+ 0 - 198
src/lib/dns/base32.cc

@@ -1,198 +0,0 @@
-// 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 <cassert>
-#include <iterator>
-#include <iomanip>
-#include <iostream>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <exceptions/exceptions.h>
-
-#include <ctype.h>
-#include <stdint.h>
-#include <string.h>
-
-#include <dns/base32.h>
-
-using namespace std;
-
-namespace isc {
-namespace dns {
-
-static const char base32hex[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
-
-string
-encodeBase32(const vector<uint8_t>& binary) {
-    ostringstream base32;
-    size_t len = binary.size();
-    size_t pos = 0;
-    while (len > 0) {
-        char buf[9];
-        memset(buf, '=', 8);
-        buf[8] = '\0';
-
-        uint8_t digit = (binary.at(pos) >> 3) & 0x1f;
-        buf[0] = base32hex[digit];
-
-        if (len == 1) {
-            digit = (binary.at(pos) << 2) & 0x1c;
-            buf[1] = base32hex[digit];
-            base32 << buf;
-            break;
-        }
-
-        digit = ((binary.at(pos) << 2) & 0x1c) |
-                ((binary.at(pos + 1) >> 6) & 0x03);
-        buf[1] = base32hex[digit];
-
-        digit = (binary.at(pos + 1) >> 1) & 0x1f;
-        buf[2] = base32hex[digit];
-
-        if (len == 2) {
-            digit = (binary.at(pos + 1) << 4) & 0x10;
-            buf[3] = base32hex[digit];
-            base32 << buf;
-            break;
-        }
-
-        digit = ((binary.at(pos + 1) << 4) & 0x10) |
-                ((binary.at(pos + 2) >> 4) & 0x0f);
-        buf[3] = base32hex[digit];
-        if (len == 3) {
-            digit = (binary.at(pos + 2) << 1) & 0x1e;
-            buf[4] = base32hex[digit];
-            base32 << buf;
-            break;
-        }
-
-        digit = ((binary.at(pos + 2) << 1) & 0x1e) |
-                ((binary.at(pos + 3) >> 7) & 0x01);
-        buf[4] = base32hex[digit];
-
-        digit = (binary.at(pos + 3) >> 2) & 0x1f;
-        buf[5] = base32hex[digit];
-
-        if (len == 4) {
-            digit = (binary.at(pos + 3) << 3) & 0x18;
-            buf[6] = base32hex[digit];
-            base32 << buf;
-            break;
-        }
-
-        digit = ((binary.at(pos + 3) << 3) & 0x18) |
-                ((binary.at(pos + 4) >> 5) & 0x07);
-        buf[6] = base32hex[digit];
-
-        digit = binary.at(pos + 4) & 0x1f;
-        buf[7] = base32hex[digit];
-
-        len -= 5;
-        pos += 5;
-
-        base32 << buf;
-    }
-
-    return (base32.str());
-}
-
-void
-decodeBase32(const string& base32, vector<uint8_t>& result) {
-    ostringstream comp;
-
-    // compress input by removing whitespace
-    const size_t len = base32.length();
-    for (int i = 0; i < len; ++i) {
-        const char c = base32[i];
-        if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
-            continue;
-        }
-        comp << c;
-    }
-
-    // base32 text should be a multiple of 8 bytes long
-    if (comp.str().length() % 8 != 0) {
-        isc_throw(BadBase32String, "Invalid length: " << comp.str().length());
-    }
-
-    istringstream iss(comp.str());
-    result.clear();
-    bool seenpad = false;
-    while (!iss.eof()) {
-        string group;
-
-        iss >> setw(8) >> group;
-        if (iss.bad() || iss.fail()) {
-            isc_throw(BadBase32String,
-                      "Could not parse base32 input: " << base32);
-        }
-
-        uint8_t octet = 0;
-        for (int i = 0; i < 8; ++i) {
-            char c = toupper(group.at(i));
-            int value;
-
-            if (c != '=' && seenpad) {
-                isc_throw(BadBase32String, "Invalid base32 input: " << base32);
-            } else 
-
-            if (c == '=' && !seenpad) {
-                value = 0;
-                seenpad = true;
-            } else {
-                const char* pos = strchr(base32hex, c);
-                if (!pos) {
-                    isc_throw(BadBase32String,
-                              "Invalid base32 input: " << base32);
-                }
-                value = pos - base32hex;
-                assert (value < 32);
-            }
-
-            switch (i) {
-            case 0: octet |= value << 3;
-                    break;
-            case 1: octet |= value >> 2;
-                    result.push_back(octet);
-                    octet = (value & 0x03) << 6;
-                    break;
-            case 2: octet |= value << 1;
-                    break;
-            case 3: octet |= value >> 4;
-                    result.push_back(octet);
-                    octet = (value & 0x0f) << 4;
-                    break;
-            case 4: octet |= value >> 1;
-                    result.push_back(octet);
-                    octet = (value & 0x01) << 7;
-                    break;
-            case 5: octet |= value << 2;
-                    break;
-            case 6: octet |= value >> 3;
-                    result.push_back(octet);
-                    octet = (value & 0x07) << 5;
-                    break;
-            case 7: octet |= value;
-                    result.push_back(octet);
-            }
-        }
-    }
-}
-
-}
-}

+ 0 - 54
src/lib/dns/base32.h

@@ -1,54 +0,0 @@
-// 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$
-
-#ifndef __BASE32_H
-#define __BASE32_H 1
-
-#include <stdint.h>
-#include <string>
-#include <vector>
-
-#include <exceptions/exceptions.h>
-
-//
-// Note: this helper module isn't specific to the DNS protocol per se.
-// We should probably move this to somewhere else, possibly in some common
-// utility area.
-//
-
-namespace isc {
-namespace dns {
-
-///
-/// \brief A standard DNS (or ISC) module exception that is thrown if a
-/// base32 decoder encounters an invalid input.
-///
-class BadBase32String : public Exception {
-public:
-    BadBase32String(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-std::string encodeBase32(const std::vector<uint8_t>& binary);
-void decodeBase32(const std::string& hex, std::vector<uint8_t>& result);
-}
-}
-
-#endif  // __BASE32_H
-
-// Local Variables: 
-// mode: c++
-// End: 

+ 0 - 189
src/lib/dns/base64.cc

@@ -1,189 +0,0 @@
-// 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 <stdint.h>
-#include <cassert>
-#include <iterator>
-#include <string>
-#include <vector>
-
-#include <boost/archive/iterators/base64_from_binary.hpp>
-#include <boost/archive/iterators/binary_from_base64.hpp>
-#include <boost/archive/iterators/transform_width.hpp>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/base64.h>
-
-using namespace std;
-using namespace boost::archive::iterators;
-
-namespace isc {
-namespace dns {
-
-namespace {
-const char BASE64_PADDING_CHAR = '=';
-const uint8_t BINARY_ZERO_CODE = 0;
-  
-class BinaryNormalizer : public iterator<input_iterator_tag, uint8_t> {
-public:
-    BinaryNormalizer(const vector<uint8_t>::const_iterator& base,
-                     const vector<uint8_t>::const_iterator& base_end) :
-        base_(base), base_end_(base_end), in_pad_(false)
-    {}
-    BinaryNormalizer& operator++()
-    {
-        if (!in_pad_) {
-            ++base_;
-        }
-        if (base_ == base_end_) {
-            in_pad_ = true;
-        }
-        return (*this);
-    }
-    const uint8_t& operator*() const {
-        if (in_pad_) {
-            return (BINARY_ZERO_CODE);
-        } else {
-            return (*base_);
-        }
-    }
-    bool operator==(const BinaryNormalizer& other) const
-    {
-        return (base_ == other.base_);
-    }
-private:
-    vector<uint8_t>::const_iterator base_;
-    const vector<uint8_t>::const_iterator base_end_;
-    bool in_pad_;
-};
-
-typedef
-base64_from_binary<transform_width<BinaryNormalizer, 6, 8> > base64_encoder;
-} // end of anonymous namespace
-
-string
-encodeBase64(const vector<uint8_t>& binary)
-{
-    // calculate the resulting length.  it's the smallest multiple of 4
-    // equal to or larger than 4/3 * original data length.
-    size_t len = ((binary.size() * 4 / 3) + 3) & ~3;
-
-    string base64;
-    base64.reserve(len);
-    base64.assign(base64_encoder(BinaryNormalizer(binary.begin(),
-                                                  binary.end())),
-                  base64_encoder(BinaryNormalizer(binary.end(), binary.end())));
-    assert(len >= base64.length());
-    base64.append(len - base64.length(), BASE64_PADDING_CHAR);
-    return (base64);
-}
-
-namespace {
-const size_t BASE64_MAX_PADDING_CHARS = 2;
-const char BASE64_ZERO_CODE = 'A'; // correspond to 000000(2)
-
-class Base64Normalizer : public iterator<input_iterator_tag, char> {
-public:
-    Base64Normalizer(const string::const_iterator& base,
-                     const string::const_iterator& base_beginpad,
-                     const string::const_iterator& base_end) :
-        base_(base), base_beginpad_(base_beginpad), base_end_(base_end),
-        in_pad_(false)
-    {}
-    Base64Normalizer& operator++()
-    {
-        ++base_;
-        while (base_ != base_end_ && isspace(*base_)) {
-            ++base_;
-        }
-        if (base_ == base_beginpad_) {
-            in_pad_ = true;
-        }
-        return (*this);
-    }
-    const char& operator*() const {
-        if (in_pad_ && *base_ == BASE64_PADDING_CHAR) {
-            return (BASE64_ZERO_CODE);
-        } else {
-            return (*base_);
-        }
-    }
-    bool operator==(const Base64Normalizer& other) const
-    {
-        return (base_ == other.base_);
-    }
-private:
-    string::const_iterator base_;
-    const string::const_iterator base_beginpad_;
-    const string::const_iterator base_end_;
-    bool in_pad_;
-};
-
-typedef
-transform_width<binary_from_base64<Base64Normalizer, char>, 8, 6, char>
-base64_decoder;
-} // end of anonymous namespace
-
-void
-decodeBase64(const string& base64, vector<uint8_t>& result)
-{
-    // enumerate the number of trailing padding characters (=), ignoring
-    // white spaces.  since base64_from_binary doesn't accept padding,
-    // we handle it explicitly.
-    size_t padlen = 0;
-    string::const_reverse_iterator srit = base64.rbegin();
-    string::const_reverse_iterator srit_end = base64.rend();
-    while (srit != srit_end) {
-        char ch = *srit;
-        if (ch == BASE64_PADDING_CHAR) {
-            if (++padlen > BASE64_MAX_PADDING_CHARS) {
-                isc_throw(BadBase64String,
-                          "Too many Base64 padding characters");
-            }
-        } else if (!isspace(ch)) {
-            break;
-        }
-        ++srit;
-    }
-
-    try {
-        result.assign(base64_decoder(Base64Normalizer(base64.begin(),
-                                                      srit.base(),
-                                                      base64.end())),
-                      base64_decoder(Base64Normalizer(base64.end(),
-                                                      base64.end(),
-                                                      base64.end())));
-    } catch (dataflow_exception& ex) {
-        isc_throw(BadBase64String, ex.what());
-    }
-
-    // Confirm the original base64 text is the canonical encoding of the
-    // data.
-    assert(result.size() >= padlen);
-    vector<uint8_t>::const_reverse_iterator rit = result.rbegin();
-    for (int i = 0; i < padlen; ++i, ++rit) {
-        if (*rit != 0) {
-            isc_throw(BadBase64String, "Non 0 bits included in padding");
-        }
-    }
-
-    // strip the padded zero-bit fields
-    result.resize(result.size() - padlen);
-}
-
-}
-}

+ 1 - 1
src/lib/dns/rdata/generic/dnskey_48.cc

@@ -22,7 +22,7 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/foreach.hpp>
 
-#include <dns/base64.h>
+#include <dns/util/base64.h>
 #include <dns/buffer.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>

+ 1 - 1
src/lib/dns/rdata/generic/ds_43.cc

@@ -22,7 +22,7 @@
 #include <boost/lexical_cast.hpp>
 
 #include <dns/buffer.h>
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/rdata.h>

+ 4 - 4
src/lib/dns/rdata/generic/nsec3_50.cc

@@ -22,10 +22,10 @@
 
 #include <boost/lexical_cast.hpp>
 
-#include <dns/base32.h>
+#include <dns/util/base32hex.h>
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/rrtype.h>
@@ -88,7 +88,7 @@ NSEC3::NSEC3(const string& nsec3_str) :
     if (iss.bad() || iss.fail()) {
         isc_throw(InvalidRdataText, "Invalid NSEC3 hash algorithm");
     }
-    decodeBase32(nextstr, next);
+    decodeBase32Hex(nextstr, next);
 
     uint8_t bitmap[8 * 1024];       // 64k bits
     vector<uint8_t> typebits;
@@ -237,7 +237,7 @@ NSEC3::toText() const
         " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) +
         " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) +
         " " + encodeHex(impl_->salt_) +
-        " " + encodeBase32(impl_->next_) + s.str());
+        " " + encodeBase32Hex(impl_->next_) + s.str());
 }
 
 void

+ 1 - 1
src/lib/dns/rdata/generic/nsec3param_51.cc

@@ -22,7 +22,7 @@
 #include <boost/lexical_cast.hpp>
 
 #include <dns/buffer.h>
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/rdata.h>

+ 1 - 1
src/lib/dns/rdata/generic/nsec_47.cc

@@ -19,7 +19,7 @@
 #include <sstream>
 #include <vector>
 
-#include <dns/base64.h>
+#include <dns/util/base64.h>
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>

+ 1 - 1
src/lib/dns/rdata/generic/rrsig_46.cc

@@ -22,7 +22,7 @@
 
 #include <boost/lexical_cast.hpp>
 
-#include <dns/base64.h>
+#include <dns/util/base64.h>
 #include <dns/buffer.h>
 #include <dns/dnssectime.h>
 #include <dns/messagerenderer.h>

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

@@ -31,7 +31,7 @@ run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc
 run_unittests_SOURCES += question_unittest.cc
 run_unittests_SOURCES += rrparamregistry_unittest.cc
 run_unittests_SOURCES += message_unittest.cc
-run_unittests_SOURCES += base32_unittest.cc
+run_unittests_SOURCES += base32hex_unittest.cc
 run_unittests_SOURCES += base64_unittest.cc
 run_unittests_SOURCES += hex_unittest.cc
 run_unittests_SOURCES += sha1_unittest.cc

+ 0 - 107
src/lib/dns/tests/base32_unittest.cc

@@ -1,107 +0,0 @@
-// 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 <stdint.h>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <dns/base32.h>
-
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc::dns;
-
-namespace {
-
-typedef pair<string, string> StringPair;
-
-class Base32Test : public ::testing::Test {
-protected:
-    Base32Test() {
-        // test vectors from RFC4648
-#if 0   // the current implementation doesn't seem to handle '=' correctly
-        test_sequence.push_back(StringPair("", ""));
-        test_sequence.push_back(StringPair("f", "CO======"));
-        test_sequence.push_back(StringPair("fo", "CPNG===="));
-        test_sequence.push_back(StringPair("foo", "CPNMU==="));
-        test_sequence.push_back(StringPair("foob", "CPNMUOG="));
-#endif
-        test_sequence.push_back(StringPair("fooba", "CPNMUOJ1"));
-#if 0                           // this fails
-        test_sequence.push_back(StringPair("foobar", "CPNMUOJ1E8======"));
-#endif
-    }
-    vector<StringPair> test_sequence;
-    vector<uint8_t> decoded_data;
-};
-
-void
-decodeCheck(const string& input_string, vector<uint8_t>& output,
-            const string& expected)
-{
-    decodeBase32(input_string, output);
-    EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
-}
-
-
-TEST_F(Base32Test, reversibility) {
-    vector<uint8_t> result;
-//    const string input("H9RSFB7FPF2L8HG35CMPC765TDK23RP6");
-    const string input("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
-    decodeBase32(input, result);
-    string output = encodeBase32(result);
-    EXPECT_EQ(input, output);
-}
-
-TEST_F(Base32Test, decode0) {
-    for (vector<StringPair>::const_iterator it = test_sequence.begin();
-         it != test_sequence.end();
-         ++it) {
-        decodeCheck((*it).second, decoded_data, (*it).first);
-    }
-}
-
-TEST_F(Base32Test, decode1) {
-    vector<uint8_t> result;
-    const std::string input("000G40O40K30E209185GO38E1S8124GJ");
-    decodeBase32(input, result);
-    EXPECT_EQ(20, result.size());
-    for (uint8_t i = 0; i < 20; i++) {
-        EXPECT_EQ((int) i, (int) result[i]);
-    }
-}
-
-TEST_F(Base32Test, encode0) {
-    for (vector<StringPair>::const_iterator it = test_sequence.begin();
-         it != test_sequence.end();
-         ++it) {
-        decoded_data.assign((*it).first.begin(), (*it).first.end());
-        EXPECT_EQ((*it).second, encodeBase32(decoded_data));
-    }
-}
-
-TEST_F(Base32Test, encode1) {
-    const std::string expect("000G40O40K30E209185GO38E1S8124GJ");
-    vector<uint8_t> binary;
-    for (uint8_t i = 0; i < 20; i++) {
-        binary.push_back(i);
-    }
-    string base32 = encodeBase32(binary);
-    EXPECT_EQ(expect, base32);
-}
-}

+ 148 - 0
src/lib/dns/tests/base32hex_unittest.cc

@@ -0,0 +1,148 @@
+// 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 <stdint.h>
+
+#include <cctype>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/util/base32hex.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dns;
+
+namespace {
+
+typedef pair<string, string> StringPair;
+
+class Base32HexTest : public ::testing::Test {
+protected:
+    Base32HexTest() : encoding_chars("0123456789ABCDEFGHIJKLMNOPQRSTUV=") {
+        // test vectors from RFC4648
+        test_sequence.push_back(StringPair("", ""));
+        test_sequence.push_back(StringPair("f", "CO======"));
+        test_sequence.push_back(StringPair("fo", "CPNG===="));
+        test_sequence.push_back(StringPair("foo", "CPNMU==="));
+        test_sequence.push_back(StringPair("foob", "CPNMUOG="));
+        test_sequence.push_back(StringPair("fooba", "CPNMUOJ1"));
+        test_sequence.push_back(StringPair("foobar", "CPNMUOJ1E8======"));
+
+        // the same data, encoded using lower case chars (testable only
+        // for the decode side)
+        test_sequence_lower.push_back(StringPair("f", "co======"));
+        test_sequence_lower.push_back(StringPair("fo", "cpng===="));
+        test_sequence_lower.push_back(StringPair("foo", "cpnmu==="));
+        test_sequence_lower.push_back(StringPair("foob", "cpnmuog="));
+        test_sequence_lower.push_back(StringPair("fooba", "cpnmuoj1"));
+        test_sequence_lower.push_back(StringPair("foobar",
+                                                 "cpnmuoj1e8======"));
+    }
+    vector<StringPair> test_sequence;
+    vector<StringPair> test_sequence_lower;
+    vector<uint8_t> decoded_data;
+    const string encoding_chars;
+};
+
+void
+decodeCheck(const string& input_string, vector<uint8_t>& output,
+            const string& expected)
+{
+    decodeBase32Hex(input_string, output);
+    EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
+}
+
+TEST_F(Base32HexTest, decode) {
+    for (vector<StringPair>::const_iterator it = test_sequence.begin();
+         it != test_sequence.end();
+         ++it) {
+        decodeCheck((*it).second, decoded_data, (*it).first);
+    }
+
+    // whitespace should be allowed
+    decodeCheck("CP NM\tUOG=", decoded_data, "foob");
+    decodeCheck("CPNMU===\n", decoded_data, "foo");
+
+    // invalid number of padding characters
+    EXPECT_THROW(decodeBase32Hex("CPNMU0==", decoded_data), BadValue);
+    EXPECT_THROW(decodeBase32Hex("CO0=====", decoded_data), BadValue);
+    EXPECT_THROW(decodeBase32Hex("CO=======", decoded_data), // too many ='s
+                 BadValue);
+
+    // intermediate padding isn't allowed
+    EXPECT_THROW(decodeBase32Hex("CPNMUOG=CPNMUOG=", decoded_data), BadValue);
+
+    // Non canonical form isn't allowed.
+    // P => 25(11001), so the padding byte would be 01000000
+    EXPECT_THROW(decodeBase32Hex("0P======", decoded_data), BadValue);
+}
+
+TEST_F(Base32HexTest, decodeLower) {
+    for (vector<StringPair>::const_iterator it = test_sequence_lower.begin();
+         it != test_sequence_lower.end();
+         ++it) {
+        decodeCheck((*it).second, decoded_data, (*it).first);
+    }
+}
+
+TEST_F(Base32HexTest, encode) {
+    for (vector<StringPair>::const_iterator it = test_sequence.begin();
+         it != test_sequence.end();
+         ++it) {
+        decoded_data.assign((*it).first.begin(), (*it).first.end());
+        EXPECT_EQ((*it).second, encodeBase32Hex(decoded_data));
+    }
+}
+
+// For Base32Hex we use handmade mappings, so it's prudent to test the
+// entire mapping table explicitly.
+TEST_F(Base32HexTest, decodeMap) {
+    string input(8, '0');       // input placeholder
+
+    for (int i = 0; i < 256; ++i) {
+        input[7] = i;
+
+        const char ch = toupper(i);
+        const size_t pos = encoding_chars.find(ch);
+        if (pos == string::npos) {
+            EXPECT_THROW(decodeBase32Hex(input, decoded_data), BadValue);
+        } else {
+            decodeBase32Hex(input, decoded_data);
+            if (ch == '=') {
+                EXPECT_EQ(4, decoded_data.size());
+            } else {
+                EXPECT_EQ(5, decoded_data.size());
+                EXPECT_EQ(pos, decoded_data[4]);
+            }
+        }
+    }
+}
+
+TEST_F(Base32HexTest, encodeMap) {
+    for (int i = 0; i < 32; ++i) {
+        decoded_data.assign(4, 0);
+        decoded_data.push_back(i);
+        EXPECT_EQ(encoding_chars[i], encodeBase32Hex(decoded_data)[7]);
+    }
+}
+
+}

+ 9 - 6
src/lib/dns/tests/base64_unittest.cc

@@ -18,11 +18,14 @@
 #include <utility>
 #include <vector>
 
-#include <dns/base64.h>
+#include <exceptions/exceptions.h>
+
+#include <dns/util/base64.h>
 
 #include <gtest/gtest.h>
 
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 
 namespace {
@@ -67,18 +70,18 @@ TEST_F(Base64Test, decode) {
     decodeCheck("Zm9vYmE=\n", decoded_data, "fooba");
 
     // only up to 2 padding characters are allowed
-    EXPECT_THROW(decodeBase64("A===", decoded_data), BadBase64String);
-    EXPECT_THROW(decodeBase64("A= ==", decoded_data), BadBase64String);
+    EXPECT_THROW(decodeBase64("A===", decoded_data), BadValue);
+    EXPECT_THROW(decodeBase64("A= ==", decoded_data), BadValue);
 
     // intermediate padding isn't allowed
-    EXPECT_THROW(decodeBase64("YmE=YmE=", decoded_data), BadBase64String);
+    EXPECT_THROW(decodeBase64("YmE=YmE=", decoded_data), BadValue);
 
     // Non canonical form isn't allowed.
     // Z => 25(011001), m => 38(100110), 9 => 60(111101), so the padding
     // byte would be 0100 0000.
-    EXPECT_THROW(decodeBase64("Zm9=", decoded_data), BadBase64String);
+    EXPECT_THROW(decodeBase64("Zm9=", decoded_data), BadValue);
     // Same for the 1st padding byte.  This would make it 01100000.
-    EXPECT_THROW(decodeBase64("Zm==", decoded_data), BadBase64String);
+    EXPECT_THROW(decodeBase64("Zm==", decoded_data), BadValue);
 }
 
 TEST_F(Base64Test, encode) {

+ 1 - 1
src/lib/dns/tests/hex_unittest.cc

@@ -19,7 +19,7 @@
 #include <vector>
 #include <string>
 
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 
 #include <gtest/gtest.h>
 

+ 4 - 3
src/lib/dns/tests/rdata_dnskey_unittest.cc

@@ -16,7 +16,8 @@
 
 #include <string>
 
-#include <dns/base64.h>
+#include <exceptions/exceptions.h>
+
 #include <dns/buffer.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
@@ -31,6 +32,7 @@
 
 using isc::UnitTestUtil;
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 
@@ -71,8 +73,7 @@ TEST_F(Rdata_DNSKEY_Test, badText)
                  InvalidRdataText);
     EXPECT_THROW(generic::DNSKEY("257 3 500 BAAAAAAAAAAAD"),
                  InvalidRdataText);
-    EXPECT_THROW(generic::DNSKEY("257 3 5 BAAAAAAAAAAAD"),
-                 BadBase64String);
+    EXPECT_THROW(generic::DNSKEY("257 3 5 BAAAAAAAAAAAD"), BadValue);
 #if 0
     // Should this be allowed?  Probably not.  But the test currently fails.
     EXPECT_THROW(generic::DNSKEY("257 3 5BEAAEFTd"),

+ 13 - 10
src/lib/dns/tests/rdata_nsec3_unittest.cc

@@ -16,10 +16,11 @@
 
 #include <string>
 
-#include <dns/base32.h>
+#include <exceptions/exceptions.h>
+
 #include <dns/buffer.h>
 #include <dns/exceptions.h>
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
@@ -33,16 +34,19 @@
 
 using isc::UnitTestUtil;
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 
 namespace {
 class Rdata_NSEC3_Test : public RdataTest {
     // there's nothing to specialize
+public:
+    Rdata_NSEC3_Test() :
+        nsec3_txt("1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
+                  "NS SOA RRSIG DNSKEY NSEC3PARAM") {}
+    string nsec3_txt;
 };
-string nsec3_txt("1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
-                 "NS SOA RRSIG DNSKEY NSEC3PARAM");
-
 
 TEST_F(Rdata_NSEC3_Test, toText)
 {
@@ -50,8 +54,7 @@ TEST_F(Rdata_NSEC3_Test, toText)
     EXPECT_EQ(nsec3_txt, rdata_nsec3.toText());
 }
 
-TEST_F(Rdata_NSEC3_Test, badText)
-{
+TEST_F(Rdata_NSEC3_Test, badText) {
     EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1 ADDAFEE "
                                             "0123456789ABCDEFGHIJKLMNOPQRSTUV "
                                             "BIFF POW SPOON"),
@@ -59,7 +62,7 @@ TEST_F(Rdata_NSEC3_Test, badText)
     EXPECT_THROW(generic::NSEC3 rdata_nsec3("1 1 1 ADDAFEE "
                                             "WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW "
                                             "A NS SOA"),
-                 BadBase32String);
+                 BadValue);     // bad base32hex
     EXPECT_THROW(generic::NSEC3 rdata_nsec3("1000000 1 1 ADDAFEE "
                                             "0123456789ABCDEFGHIJKLMNOPQRSTUV "
                                             "A NS SOA"),
@@ -72,12 +75,12 @@ TEST_F(Rdata_NSEC3_Test, badText)
                                             "0123456789ABCDEFGHIJKLMNOPQRSTUV "
                                             "A NS SOA"),
                  InvalidRdataText);
+}
 
-#if 0 // this currently fails
+TEST_F(Rdata_NSEC3_Test, DISABLED_badText) { // this currently fails
     EXPECT_THROW(generic::NSEC3(
                      "1 1 1D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
                      "NS SOA RRSIG DNSKEY NSEC3PARAM"), InvalidRdataText);
-#endif
 }
 
 TEST_F(Rdata_NSEC3_Test, createFromWire)

+ 2 - 2
src/lib/dns/tests/rdata_nsec3param_unittest.cc

@@ -16,9 +16,9 @@
 
 #include <string>
 
-#include <dns/base32.h>
+#include <dns/util/base32hex.h>
 #include <dns/buffer.h>
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>

+ 9 - 7
src/lib/dns/tests/rdata_rrsig_unittest.cc

@@ -14,7 +14,8 @@
 
 // $Id$
 
-#include <dns/base64.h>
+#include <exceptions/exceptions.h>
+
 #include <dns/buffer.h>
 #include <dns/dnssectime.h>
 #include <dns/messagerenderer.h>
@@ -30,6 +31,7 @@
 
 using isc::UnitTestUtil;
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 
@@ -50,8 +52,7 @@ TEST_F(Rdata_RRSIG_Test, fromText)
 
 }
 
-TEST_F(Rdata_RRSIG_Test, badText)
-{
+TEST_F(Rdata_RRSIG_Test, badText) {
     EXPECT_THROW(const generic::RRSIG sig("SPORK"), InvalidRdataText);
     EXPECT_THROW(const generic::RRSIG sig("A 555 4 43200 "
                      "20100223214617 20100222214617 8496 isc.org. "
@@ -83,16 +84,17 @@ TEST_F(Rdata_RRSIG_Test, badText)
                      "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
                      "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU"
                      "f49t+sXKPzbipN9g+s1ZPiIyofc="), InvalidRdataText);
-    EXPECT_THROW(const generic::RRSIG sig("A 5 4 43200 "
+    EXPECT_THROW(const generic::RRSIG sig(
+                     "A 5 4 43200 "
                      "20100223214617 20100222214617 8496 isc.org. "
                      "EEeeeeeeEEEeeeeeeGaaahAAAAAAAAHHHHHHHHHHH!="),
-                     BadBase64String);
+                 BadValue);     // bad base64 input
+}
 
-#if 0                           // this currently fails
+TEST_F(Rdata_RRSIG_Test, DISABLED_badText) { // this currently fails
     // no space between the tag and signer
     EXPECT_THROW(generic::RRSIG("A 5 4 43200 20100223214617 20100222214617 "
                                 "8496isc.org. ofc="), InvalidRdataText);
-#endif
 }
 
 TEST_F(Rdata_RRSIG_Test, toWireRenderer)

+ 1 - 1
src/lib/dns/tests/sha1_unittest.cc

@@ -17,7 +17,7 @@
 #include <stdint.h>
 #include <string>
 
-#include <dns/sha1.h>
+#include <dns/util/sha1.h>
 
 #include <gtest/gtest.h>
 

+ 26 - 17
src/lib/dns/base64.h

@@ -14,15 +14,13 @@
 
 // $Id$
 
-#ifndef __BASE64_H
-#define __BASE64_H 1
+#ifndef __BASE32HEX_H
+#define __BASE32HEX_H 1
 
 #include <stdint.h>
 #include <string>
 #include <vector>
 
-#include <exceptions/exceptions.h>
-
 //
 // Note: this helper module isn't specific to the DNS protocol per se.
 // We should probably move this to somewhere else, possibly in some common
@@ -32,23 +30,34 @@
 namespace isc {
 namespace dns {
 
+/// \brief Encode binary data in the base32hex format.
+///
+/// The underlying implementation is shared with \c encodeBase64, and all
+/// description except the format (base32hex) equally applies.
+///
+/// Note: the encoding format is base32hex, not base32.
+///
+/// \param binary A vector object storing the data to be encoded. 
+/// \return A newly created string that stores base32hex encoded value for
+/// binary.
+std::string encodeBase32Hex(const std::vector<uint8_t>& binary);
+
+/// \brief Decode a text encoded in the base32hex format into the
+/// original %data.
+///
+/// The underlying implementation is shared with \c decodeBase64, and all
+/// description except the format (base32hex) equally applies.
 ///
-/// \brief A standard DNS (or ISC) module exception that is thrown a Base64
-/// decoder encounters an invalid input.
+/// Note: the encoding format is base32hex, not base32.
 ///
-class BadBase64String : public Exception {
-public:
-    BadBase64String(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-std::string encodeBase64(const std::vector<uint8_t>& binary);
-void decodeBase64(const std::string& base64, std::vector<uint8_t>& result);
+/// \param input A text encoded in the base32hex format.
+/// \param result A vector in which the decoded %data is to be stored.
+void decodeBase32Hex(const std::string& input, std::vector<uint8_t>& result);
 }
 }
 
-#endif  // __BASE64_H
+#endif  // __BASE32HEX_H
 
-// Local Variables: 
+// Local Variables:
 // mode: c++
-// End: 
+// End:

+ 110 - 0
src/lib/dns/util/base32hex_from_binary.h

@@ -0,0 +1,110 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
+#define BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// base32hex_from_binary.h (derived from boost base64_from_binary.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . 
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+//  See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+#include <cstddef> // size_t
+#include <boost/config.hpp> // for BOOST_DEDUCED_TYPENAME
+#if defined(BOOST_NO_STDC_NAMESPACE)
+namespace std{ 
+    using ::size_t; 
+} // namespace std
+#endif
+
+#include <boost/serialization/pfto.hpp>
+
+#include <boost/iterator/transform_iterator.hpp>
+#include <boost/archive/iterators/dataflow_exception.hpp>
+
+namespace boost { 
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert binary integers to base32hex characters
+
+namespace detail {
+
+template<class CharType>
+struct from_5_bit {
+    typedef CharType result_type;
+    CharType operator()(CharType t) const{
+        const char * lookup_table = 
+            "0123456789"
+            "ABCDEFGHIJKLMNOPQRSTUV";
+        assert(t < 32);
+        return lookup_table[static_cast<size_t>(t)];
+    }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+//  typedef transform_iterator<
+//      from_5_bit<CharType>,
+//      transform_width<Base, 5, sizeof(Base::value_type) * 8, CharType>
+//  > base32hex_from_binary;
+// but C++ won't accept this.  Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor.  This makes it incompatible with the dataflow
+// ideal.  This is also addressed here.
+
+//template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+template<
+    class Base, 
+    class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class base32hex_from_binary : 
+    public transform_iterator<
+        detail::from_5_bit<CharType>,
+        Base
+    >
+{
+    friend class boost::iterator_core_access;
+    typedef transform_iterator<
+        BOOST_DEDUCED_TYPENAME detail::from_5_bit<CharType>,
+        Base
+    > super_t;
+
+public:
+    // make composible buy using templated constructor
+    template<class T>
+    base32hex_from_binary(BOOST_PFTO_WRAPPER(T) start) :
+        super_t(
+            Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
+            detail::from_5_bit<CharType>()
+        )
+    {}
+    // intel 7.1 doesn't like default copy constructor
+    base32hex_from_binary(const base32hex_from_binary & rhs) : 
+        super_t(
+            Base(rhs.base_reference()),
+            detail::from_5_bit<CharType>()
+        )
+    {}
+//    base32hex_from_binary(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP

+ 78 - 0
src/lib/dns/util/base64.h

@@ -0,0 +1,78 @@
+// 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.
+
+// $Id$
+
+#ifndef __BASE64_H
+#define __BASE64_H 1
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace dns {
+
+/// \brief Encode binary data in the base64 format.
+///
+/// This function returns a new \c std::string object that stores a text
+/// encoded in the base64 format for the given \c binary %data.
+/// The resulting string will be a valid, canonical form of base64
+/// representation as specified in RFC4648.
+///
+/// If memory allocation for the returned string fails, a corresponding
+/// standard exception will be thrown.  This function never throws exceptions
+/// otherwise.
+///
+/// \param binary A vector object storing the data to be encoded. 
+/// \return A newly created string that stores base64 encoded value for binary.
+std::string encodeBase64(const std::vector<uint8_t>& binary);
+
+/// \brief Decode a text encoded in the base64 format into the original %data.
+///
+/// The \c input argument must be a valid string represented in the base64
+/// format as specified in RFC4648.  Space characters (spaces, tabs, newlines)
+/// can be included in \c input and will be ignored.  Without spaces, the
+/// length of string must be a multiple of 4 bytes with necessary paddings.
+/// Also it must be encoded using the canonical encoding (see RFC4648).
+/// If any of these conditions is not met, an exception of class
+/// \c isc::BadValue will be thrown.
+///
+/// If \c result doesn't have sufficient capacity to store all decoded %data
+/// and memory allocation fails, a corresponding standard exception will be
+/// thrown.  If the caller knows the necessary length (which can in theory
+/// be calculated from the input string), this situation can be avoided by
+/// reserving sufficient space for \c result beforehand.
+///
+/// Any existing %data in \c result will be removed.  This is the case in some
+/// of the cases where an exception is thrown; that is, this function only
+/// provides the basic exception guarantee.
+///
+/// \param input A text encoded in the base64 format.
+/// \param result A vector in which the decoded %data is to be stored.
+void decodeBase64(const std::string& input, std::vector<uint8_t>& result);
+}
+}
+
+#endif  // __BASE64_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 308 - 0
src/lib/dns/util/base_n.cc

@@ -0,0 +1,308 @@
+// 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 <stdint.h>
+#include <cassert>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include <boost/archive/iterators/base64_from_binary.hpp>
+#include <boost/archive/iterators/binary_from_base64.hpp>
+#include <boost/archive/iterators/transform_width.hpp>
+#include <boost/math/common_factor.hpp>
+
+#include <dns/util/base32hex_from_binary.h>
+#include <dns/util/binary_from_base32hex.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/util/base32hex.h>
+#include <dns/util/base64.h>
+
+using namespace std;
+using namespace boost::archive::iterators;
+
+namespace isc {
+namespace dns {
+
+// In the following anonymous namespace, we provide a generic framework
+// to encode/decode baseN format.  We use the following tools:
+// - boost base64_from_binary/binary_from_base64: provide mapping table for
+//   base64
+// - base32hex_from_binary/binary_from_base32hex: provide mapping table for
+//   base32hex.  A straightforward variation of their base64 counterparts.
+// - EncodeNormalizer/DecodeNormalizer: supplemental filter handling baseN
+//   padding characters (=)
+// - boost transform_width: an iterator framework for handling data stream
+//   per bit-group
+//
+// A conceptual description of how the encoding and decoding work is as
+// follows:
+// Encoding:
+//   input binary data => Normalizer (append sufficient number of 0 bits)
+//                     => transform_width (extract bit groups from the original
+//                                         stream)
+//                     => baseXX_from_binary (convert each bit group to an
+//                                            encoded byte using the mapping)
+// Decoding:
+//   input baseXX text => Normalizer (convert '=' to 0 bits)
+//                     => binary_from_baseXX (convert each encoded byte into
+//                                            the original group bit)
+//                     => transform_width (build original byte stream by
+//                                         concatenating the decoded bit
+//                                         stream)
+//
+// Below, we define a set of templated classes to handle different parameters
+// for different encoding algorithms.
+namespace {
+// Common constants used for all baseN encoding.
+const char BASE_PADDING_CHAR = '=';
+const uint8_t BINARY_ZERO_CODE = 0;
+  
+class EncodeNormalizer : public iterator<input_iterator_tag, uint8_t> {
+public:
+    EncodeNormalizer(const vector<uint8_t>::const_iterator& base,
+                     const vector<uint8_t>::const_iterator& base_end) :
+        base_(base), base_end_(base_end), in_pad_(false)
+    {}
+    EncodeNormalizer& operator++() {
+        if (!in_pad_) {
+            ++base_;
+        }
+        if (base_ == base_end_) {
+            in_pad_ = true;
+        }
+        return (*this);
+    }
+    const uint8_t& operator*() const {
+        if (in_pad_) {
+            return (BINARY_ZERO_CODE);
+        } else {
+            return (*base_);
+        }
+    }
+    bool operator==(const EncodeNormalizer& other) const {
+        return (base_ == other.base_);
+    }
+private:
+    vector<uint8_t>::const_iterator base_;
+    const vector<uint8_t>::const_iterator base_end_;
+    bool in_pad_;
+};
+
+// BaseZeroCode is the byte character that represents a value of '0' in
+// the corresponding encoding.  For example, BaseZeroCode is 'A' for base64.
+template <char BaseZeroCode>
+class DecodeNormalizer : public iterator<input_iterator_tag, char> {
+public:
+    DecodeNormalizer(const string::const_iterator& base,
+                     const string::const_iterator& base_beginpad,
+                     const string::const_iterator& base_end) :
+        base_(base), base_beginpad_(base_beginpad), base_end_(base_end),
+        in_pad_(false)
+    {}
+    DecodeNormalizer& operator++() {
+        ++base_;
+        while (base_ != base_end_ && isspace(*base_)) {
+            ++base_;
+        }
+        if (base_ == base_beginpad_) {
+            in_pad_ = true;
+        }
+        return (*this);
+    }
+    const char& operator*() const {
+        if (in_pad_ && *base_ == BASE_PADDING_CHAR) {
+            return (BASE_ZERO_CODE);
+        } else {
+            return (*base_);
+        }
+    }
+    bool operator==(const DecodeNormalizer& other) const {
+        return (base_ == other.base_);
+    }
+private:
+    string::const_iterator base_;
+    const string::const_iterator base_beginpad_;
+    const string::const_iterator base_end_;
+    bool in_pad_;
+    static const char BASE_ZERO_CODE = BaseZeroCode;
+};
+
+// BitsPerChunk: number of bits to be converted using the baseN mapping table.
+//               e.g. 6 for base64.
+// BaseZeroCode: the byte character that represents a value of 0 in
+//               the corresponding encoding.  e.g. 'A' for base64.
+// Encoder: baseX_from_binary<transform_width<EncodeNormalizer,
+//                                            BitsPerChunk, 8> >
+// Decoder: transform_width<binary_from_baseX<DecodeNormalizer<BaseZeroCode>,
+//                                           char>, 8, BitsPerChunk, char>
+template <int BitsPerChunk, char BaseZeroCode,
+          typename Encoder, typename Decoder>
+struct BaseNTransformer {
+    static string encode(const vector<uint8_t>& binary);
+    static void decode(const char* algorithm,
+                       const string& base64, vector<uint8_t>& result);
+
+    // BITS_PER_GROUP is the number of bits for the smallest possible (non
+    // empty) bit string that can be converted to a valid baseN encoded text
+    // without padding.  It's the least common multiple of 8 and BitsPerChunk,
+    // e.g. 24 for base64.
+    static const int BITS_PER_GROUP =
+        boost::math::static_lcm<BitsPerChunk, 8>::value;
+
+    // MAX_PADDING_CHARS is the maximum number of padding characters
+    // that can appear in a valid baseN encoded text.
+    // It's group_len - chars_for_byte, where group_len is the number of
+    // encoded characters to represent BITS_PER_GROUP bits, and
+    // chars_for_byte is the number of encoded character that is needed to
+    // represent a single byte, which is ceil(8 / BitsPerChunk).
+    // For example, for base64 we need two encoded characters to represent a
+    // byte, and each group consists of 4 encoded characters, so
+    // MAX_PADDING_CHARS is 4 - 2 = 2.
+    static const int MAX_PADDING_CHARS =
+        BITS_PER_GROUP / BitsPerChunk -
+        (8 / BitsPerChunk + ((8 % BitsPerChunk) == 0 ? 0 : 1));
+}; 
+
+template <int BitsPerChunk, char BaseZeroCode,
+          typename Encoder, typename Decoder>
+string
+BaseNTransformer<BitsPerChunk, BaseZeroCode,Encoder, Decoder>::encode(
+    const vector<uint8_t>& binary)
+{
+    // calculate the resulting length.
+    size_t bits = binary.size() * 8;
+    if (bits % BITS_PER_GROUP > 0) {
+        bits += (BITS_PER_GROUP - (bits % BITS_PER_GROUP));
+    }
+    const size_t len = bits / BitsPerChunk;
+
+    string result;
+    result.reserve(len);
+    result.assign(Encoder(EncodeNormalizer(binary.begin(), binary.end())),
+                  Encoder(EncodeNormalizer(binary.end(), binary.end())));
+    assert(len >= result.length());
+    result.append(len - result.length(), BASE_PADDING_CHAR);
+    return (result);
+}
+
+template <int BitsPerChunk, char BaseZeroCode,
+          typename Encoder, typename Decoder>
+void
+BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::decode(
+    const char* const algorithm,
+    const string& input,
+    vector<uint8_t>& result)
+{
+    // enumerate the number of trailing padding characters (=), ignoring
+    // white spaces.  since baseN_from_binary doesn't accept padding,
+    // we handle it explicitly.
+    size_t padchars = 0;
+    string::const_reverse_iterator srit = input.rbegin();
+    string::const_reverse_iterator srit_end = input.rend();
+    while (srit != srit_end) {
+        char ch = *srit;
+        if (ch == BASE_PADDING_CHAR) {
+            if (++padchars > MAX_PADDING_CHARS) {
+                isc_throw(BadValue, "Too many " << algorithm
+                          << " padding characters: " << input);
+            }
+        } else if (!isspace(ch)) {
+            break;
+        }
+        ++srit;
+    }
+    const size_t padbits = (padchars * BitsPerChunk + 7) & ~7;
+    if (padbits > BitsPerChunk * (padchars + 1)) {
+        isc_throw(BadValue, "Invalid " << algorithm << "padding: " << input);
+    }
+    const size_t padbytes = padbits / 8;
+
+    try {
+        result.assign(Decoder(DecodeNormalizer<BaseZeroCode>(input.begin(),
+                                                             srit.base(),
+                                                             input.end())),
+                      Decoder(DecodeNormalizer<BaseZeroCode>(input.end(),
+                                                             input.end(),
+                                                             input.end())));
+    } catch (const dataflow_exception& ex) {
+        // convert any boost exceptions into our local one.
+        isc_throw(BadValue, ex.what());
+    }
+
+    // Confirm the original BaseX text is the canonical encoding of the
+    // data.
+    assert(result.size() >= padbytes);
+    vector<uint8_t>::const_reverse_iterator rit = result.rbegin();
+    for (int i = 0; i < padbytes; ++i, ++rit) {
+        if (*rit != 0) {
+            isc_throw(BadValue, "Non 0 bits included in " << algorithm
+                      << " padding: " << input);
+        }
+    }
+
+    // strip the padded zero-bit fields
+    result.resize(result.size() - padbytes);
+}
+
+//
+// Instantiation for BASE-64
+//
+typedef
+base64_from_binary<transform_width<EncodeNormalizer, 6, 8> > base64_encoder;
+typedef
+transform_width<binary_from_base64<DecodeNormalizer<'A'>, char>, 8, 6, char>
+base64_decoder;
+typedef BaseNTransformer<6, 'A', base64_encoder, base64_decoder>
+Base64Transformer;
+
+//
+// Instantiation for BASE-32HEX
+//
+typedef
+base32hex_from_binary<transform_width<EncodeNormalizer, 5, 8> >
+base32hex_encoder;
+typedef
+transform_width<binary_from_base32hex<DecodeNormalizer<'0'>, char>, 8, 5, char>
+base32hex_decoder;
+typedef BaseNTransformer<5, '0', base32hex_encoder, base32hex_decoder>
+Base32HexTransformer;
+}
+
+string
+encodeBase64(const vector<uint8_t>& binary) {
+    return (Base64Transformer::encode(binary));
+}
+
+void
+decodeBase64(const string& input, vector<uint8_t>& result) {
+    Base64Transformer::decode("base64", input, result);
+}
+
+string
+encodeBase32Hex(const vector<uint8_t>& binary) {
+    return (Base32HexTransformer::encode(binary));
+}
+
+void
+decodeBase32Hex(const string& input, vector<uint8_t>& result) {
+    Base32HexTransformer::decode("base32hex", input, result);
+}
+
+}
+}

+ 126 - 0
src/lib/dns/util/binary_from_base32hex.h

@@ -0,0 +1,126 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+#define BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// binary_from_base32hex.h (derived from boost binary_from_base64.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . 
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+//  See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+#include <boost/config.hpp> // for BOOST_DEDUCED_TYPENAME
+#include <boost/serialization/throw_exception.hpp>
+#include <boost/serialization/pfto.hpp>
+#include <boost/static_assert.hpp>
+
+#include <boost/iterator/transform_iterator.hpp>
+#include <boost/archive/iterators/dataflow_exception.hpp>
+
+#include <exceptions/exceptions.h>
+
+namespace boost { 
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert base32hex characters to binary data
+
+namespace detail {
+
+template<class CharType>
+struct to_5_bit {
+    typedef CharType result_type;
+    CharType operator()(CharType t) const{
+        const char lookup_table[] = {
+            -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 00-0f
+            -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 10-1f
+            -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 20-2f
+             0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 30-3f
+            -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, // 40-4f
+            25,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 50-5f
+            -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, // 60-6f
+            25,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,-1  // 70-7f
+        };
+        // metrowerks trips this assertion - how come?
+        #if ! defined(__MWERKS__)
+        BOOST_STATIC_ASSERT(128 == sizeof(lookup_table));
+        #endif
+        signed char value = -1;
+        if((unsigned)t <= 127)
+            value = lookup_table[(unsigned)t];
+        if(-1 == value) { 
+            isc_throw(isc::BadValue,
+                      "attempt to decode a value not in base32hex char set");
+        }
+        return value;
+    }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+//  typedef transform_iterator<
+//      from_5_bit<CharType>,
+//      transform_width<Base, 5, sizeof(Base::value_type) * 8, CharType>
+//  > base32hex_from_binary;
+// but C++ won't accept this.  Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor.  This makes it incompatible with the dataflow
+// ideal.  This is also addressed here.
+
+template<
+    class Base, 
+    class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class binary_from_base32hex : public
+    transform_iterator<
+        detail::to_5_bit<CharType>,
+        Base
+    >
+{
+    friend class boost::iterator_core_access;
+    typedef transform_iterator<
+        detail::to_5_bit<CharType>,
+        Base
+    > super_t;
+public:
+    // make composible buy using templated constructor
+    template<class T>
+    binary_from_base32hex(BOOST_PFTO_WRAPPER(T)  start) :
+        super_t(
+            Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))), 
+            detail::to_5_bit<CharType>()
+        )
+    {}
+    // intel 7.1 doesn't like default copy constructor
+    binary_from_base32hex(const binary_from_base32hex & rhs) : 
+        super_t(
+            Base(rhs.base_reference()),
+            detail::to_5_bit<CharType>()
+        )
+    {}
+//    binary_from_base32hex(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 1 - 1
src/lib/dns/hex.cc

@@ -28,7 +28,7 @@
 #include <ctype.h>
 #include <stdint.h>
 
-#include <dns/hex.h>
+#include <dns/util/hex.h>
 
 using namespace std;
 

src/lib/dns/hex.h → src/lib/dns/util/hex.h


+ 1 - 1
src/lib/dns/sha1.cc

@@ -50,7 +50,7 @@
  *      without express or implied warranty of any kind.
  *      
  */
-#include <dns/sha1.h>
+#include <dns/util/sha1.h>
 
 /* Local Function Prototyptes */
 static void SHA1Finalize(SHA1Context *, uint8_t Pad_Byte);

src/lib/dns/sha1.h → src/lib/dns/util/sha1.h


+ 12 - 2
src/lib/exceptions/exceptions.h

@@ -104,7 +104,7 @@ private:
 };
 
 ///
-/// \brief A standard DNS module exception that is thrown if a parameter give
+/// \brief A generic exception that is thrown if a parameter given
 /// to a method would refer to or modify out-of-range data.
 ///
 class OutOfRange : public Exception {
@@ -114,7 +114,17 @@ public:
 };
 
 ///
-/// \brief A standard DNS module exception that is thrown when an unexpected
+/// \brief A generic exception that is thrown if a parameter given
+/// to a method is considered invalid in that context.
+///
+class BadValue : public Exception {
+public:
+    BadValue(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+///
+/// \brief A generic exception that is thrown when an unexpected
 /// error condition occurs.
 ///
 class Unexpected : public Exception {