Commit ea97070c authored by Paul Selkirk's avatar Paul Selkirk
Browse files

Merge branch 'trac2522'

parents bf2a2aab 64bc0886
......@@ -24,39 +24,6 @@ from os.path import getmtime
import re
import sys
# new_rdata_factory_users[] is a list of tuples of the form (rrtype,
# rrclass). Items in the list use the (new) RdataFactory class, and
# items which are not in the list use OldRdataFactory class.
# Note: rrtype and rrclass must be specified in lowercase in
# new_rdata_factory_users.
#
# Example:
# new_rdata_factory_users = [('a', 'in'), ('a', 'ch'), ('soa', 'generic')]
new_rdata_factory_users = [('a', 'in'),
('aaaa', 'in'),
('afsdb', 'generic'),
('cname', 'generic'),
('dhcid', 'in'),
('dlv', 'generic'),
('dname', 'generic'),
('dnskey', 'generic'),
('ds', 'generic'),
('hinfo', 'generic'),
('naptr', 'generic'),
('mx', 'generic'),
('ns', 'generic'),
('nsec', 'generic'),
('nsec3', 'generic'),
('nsec3param', 'generic'),
('opt', 'generic'),
('ptr', 'generic'),
('rrsig', 'generic'),
('soa', 'generic'),
('spf', 'generic'),
('srv', 'in'),
('txt', 'generic')
]
re_typecode = re.compile('([\da-z\-]+)_(\d+)')
classcode2txt = {}
typecode2txt = {}
......@@ -66,7 +33,7 @@ meta_types = {
# Real meta types. We won't have Rdata implement for them, but we need
# RRType constants.
'251': 'ixfr', '252': 'axfr', '255': 'any',
# Obsolete types. We probalby won't implement Rdata for them, but it's
# Obsolete types. We probably won't implement Rdata for them, but it's
# better to have RRType constants.
'3': 'md', '4': 'mf', '7': 'mb', '8': 'mg', '9': 'mr', '30': 'nxt',
'38': 'a6', '254': 'maila',
......@@ -375,28 +342,15 @@ def generate_rrparam(fileprefix, basemtime):
indent = ' ' * 8
typeandclassparams += indent
# By default, we use OldRdataFactory (see bug #2497). If you
# want to pick RdataFactory for a particular type, add it to
# new_rdata_factory_users. Note that we explicitly generate (for
# optimization) class-independent ("generic") factories for class IN
# for optimization.
if (((type_txt.lower(), class_txt.lower()) in
new_rdata_factory_users) or
((class_txt.lower() == 'in') and
((type_txt.lower(), 'generic') in new_rdata_factory_users))):
rdf_class = 'RdataFactory'
else:
rdf_class = 'OldRdataFactory'
if class_tuple[1] != 'generic':
typeandclassparams += 'add("' + type_utxt + '", '
typeandclassparams += str(type_code) + ', "' + class_utxt
typeandclassparams += '", ' + str(class_code)
typeandclassparams += ', RdataFactoryPtr(new ' + rdf_class + '<'
typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<'
typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
else:
typeandclassparams += 'add("' + type_utxt + '", ' + str(type_code)
typeandclassparams += ', RdataFactoryPtr(new ' + rdf_class + '<'
typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<'
typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
typeandclassparams += indent + '// Meta and non-implemented RR types\n'
......
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2010-2013 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
......@@ -19,26 +19,28 @@
#include <boost/lexical_cast.hpp>
#include <util/buffer.h>
#include <util/strutil.h>
#include <util/encode/base64.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rcode.h>
#include <dns/tsigerror.h>
#include <dns/rdata/generic/detail/lexer_util.h>
using namespace std;
using boost::lexical_cast;
using namespace isc::util;
using namespace isc::util::encode;
using namespace isc::util::str;
using namespace isc::dns;
using isc::dns::rdata::generic::detail::createNameFromLexer;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
/// This is a straightforward representation of TSIG RDATA fields.
struct TSIG::TSIGImpl {
// straightforward representation of TSIG RDATA fields
struct 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) :
......@@ -68,99 +70,184 @@ struct TSIG::TSIGImpl {
const vector<uint8_t> other_data_;
};
// helper function for string and lexer constructors
TSIGImpl*
TSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) {
const Name& algorithm =
createNameFromLexer(lexer, origin ? origin : &Name::ROOT_NAME());
const string& time_txt =
lexer.getNextToken(MasterToken::STRING).getString();
uint64_t time_signed;
try {
time_signed = boost::lexical_cast<uint64_t>(time_txt);
} catch (const boost::bad_lexical_cast&) {
isc_throw(InvalidRdataText, "Invalid TSIG Time");
}
if ((time_signed >> 48) != 0) {
isc_throw(InvalidRdataText, "TSIG Time out of range");
}
const uint32_t fudge = lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (fudge > 0xffff) {
isc_throw(InvalidRdataText, "TSIG Fudge out of range");
}
const uint32_t macsize =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (macsize > 0xffff) {
isc_throw(InvalidRdataText, "TSIG MAC Size out of range");
}
const string& mac_txt = (macsize > 0) ?
lexer.getNextToken(MasterToken::STRING).getString() : "";
vector<uint8_t> mac;
decodeBase64(mac_txt, mac);
if (mac.size() != macsize) {
isc_throw(InvalidRdataText, "TSIG MAC Size and data are inconsistent");
}
const uint32_t orig_id =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (orig_id > 0xffff) {
isc_throw(InvalidRdataText, "TSIG Original ID out of range");
}
const string& error_txt =
lexer.getNextToken(MasterToken::STRING).getString();
uint32_t error = 0;
// XXX: In the initial implementation we hardcode the mnemonics.
// We'll soon generalize this.
if (error_txt == "NOERROR") {
error = Rcode::NOERROR_CODE;
} else if (error_txt == "BADSIG") {
error = TSIGError::BAD_SIG_CODE;
} else if (error_txt == "BADKEY") {
error = TSIGError::BAD_KEY_CODE;
} else if (error_txt == "BADTIME") {
error = TSIGError::BAD_TIME_CODE;
} else {
/// we cast to uint32_t and range-check, because casting directly to
/// uint16_t will convert negative numbers to large positive numbers
try {
error = boost::lexical_cast<uint32_t>(error_txt);
} catch (const boost::bad_lexical_cast&) {
isc_throw(InvalidRdataText, "Invalid TSIG Error");
}
if (error > 0xffff) {
isc_throw(InvalidRdataText, "TSIG Error out of range");
}
}
const uint32_t otherlen =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (otherlen > 0xffff) {
isc_throw(InvalidRdataText, "TSIG Other Len out of range");
}
const string otherdata_txt = (otherlen > 0) ?
lexer.getNextToken(MasterToken::STRING).getString() : "";
vector<uint8_t> other_data;
decodeBase64(otherdata_txt, other_data);
if (other_data.size() != otherlen) {
isc_throw(InvalidRdataText,
"TSIG Other Data length does not match Other Len");
}
// RFC2845 says Other Data is "empty unless Error == BADTIME".
// However, we don't enforce that.
return (new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id,
error, other_data));
}
/// \brief Constructor from string.
///
/// The given string must represent a valid TSIG RDATA. There can be extra
/// space characters at the beginning or end of the text (which are simply
/// ignored), but other extra text, including a new line, will make the
/// construction fail with an exception.
///
/// \c tsig_str must be formatted as follows:
/// \code <Alg> <Time> <Fudge> <MACsize> [<MAC>] <OrigID> <Error> <OtherLen> [<OtherData>]
/// \code <Algorithm Name> <Time Signed> <Fudge> <MAC Size> [<MAC>]
/// <Original ID> <Error> <Other Len> [<Other Data>]
/// \endcode
/// where
/// - &lt;Alg&gt; is a valid textual representation of domain name.
/// - &lt;Time&gt; is an unsigned 48-bit decimal integer.
/// - &lt;MACSize&gt;, &lt;OrigID&gt;, and &lt;OtherLen&gt; are an unsigned
/// 16-bit decimal
/// integer.
/// - &lt;Error&gt; 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.
/// - &lt;MAC&gt; and &lt;OtherData&gt; is a BASE-64 encoded string that does
/// not contain space characters.
/// When &lt;MACSize&gt; and &lt;OtherLen&gt; is 0, &lt;MAC&gt; and
/// &lt;OtherData&gt; must not appear in \c tsig_str, respectively.
/// - The decoded data of &lt;MAC&gt; is &lt;MACSize&gt; bytes of binary
/// stream.
/// - The decoded data of &lt;OtherData&gt; is &lt;OtherLen&gt; bytes of
/// binary stream.
///
/// Note that, since the Algorithm Name field is defined to be "in domain name
/// syntax", but it is not actually a domain name, it does not have to be
/// fully qualified.
///
/// The Error field is an unsigned 16-bit decimal integer or a valid mnemonic
/// as specified in RFC2845. Currently, "NOERROR", "BADSIG", "BADKEY", and
/// "BADTIME" are supported (case sensitive). In future versions other
/// representations that are compatible with the DNS RCODE may be supported.
///
/// The MAC and Other Data fields are base-64 encoded strings that do not
/// contain space characters.
/// If the MAC Size field is 0, the MAC field must not appear in \c tsig_str.
/// If the Other Len field is 0, the Other Data field must not appear in
/// \c tsig_str.
/// The decoded data of the MAC field is MAC Size bytes of binary stream.
/// The decoded data of the Other Data field is Other Len bytes of binary
/// stream.
///
/// An example of valid string is:
/// \code "hmac-sha256. 853804800 300 3 AAAA 2845 0 0" \endcode
/// In this example &lt;OtherData&gt; is missing because &lt;OtherLen&gt; is 0.
/// In this example Other Data is missing because Other Len 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>
/// \throw Others Exception from the Name constructors.
/// \throw InvalidRdataText if any fields are out of their valid range,
/// or are incorrect.
/// \throw BadValue if MAC or Other Data is not validly encoded in base-64.
///
/// If &lt;Alg&gt; is not a valid domain name, a corresponding exception from
/// the \c Name class will be thrown;
/// if &lt;MAC&gt; or &lt;OtherData&gt; 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.
/// \param tsig_str A string containing the RDATA to be created
TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) {
istringstream iss(tsig_str);
// We use auto_ptr here because if there is an exception in this
// constructor, the destructor is not called and there could be a
// leak of the TSIGImpl that constructFromLexer() returns.
std::auto_ptr<TSIGImpl> impl_ptr(NULL);
try {
const Name algorithm(getToken(iss));
const int64_t time_signed = tokenToNum<int64_t, 48>(getToken(iss));
const int32_t fudge = tokenToNum<int32_t, 16>(getToken(iss));
const int32_t macsize = tokenToNum<int32_t, 16>(getToken(iss));
const string mac_txt = (macsize > 0) ? getToken(iss) : "";
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));
const string error_txt = getToken(iss);
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);
}
std::istringstream ss(tsig_str);
MasterLexer lexer;
lexer.pushSource(ss);
const int32_t otherlen = tokenToNum<int32_t, 16>(getToken(iss));
const string otherdata_txt = (otherlen > 0) ? getToken(iss) : "";
vector<uint8_t> other_data;
decodeBase64(otherdata_txt, other_data);
impl_ptr.reset(constructFromLexer(lexer, NULL));
if (!iss.eof()) {
isc_throw(InvalidRdataText, "Unexpected input for TSIG RDATA: " <<
tsig_str);
if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
isc_throw(InvalidRdataText,
"Extra input text for TSIG: " << tsig_str);
}
} catch (const MasterLexer::LexerError& ex) {
isc_throw(InvalidRdataText,
"Failed to construct TSIG from '" << tsig_str << "': "
<< ex.what());
}
impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id,
error, other_data);
impl_ = impl_ptr.release();
}
} catch (const StringTokenError& ste) {
isc_throw(InvalidRdataText, "Invalid TSIG text: " << ste.what() <<
": " << tsig_str);
}
/// \brief Constructor with a context of MasterLexer.
///
/// The \c lexer should point to the beginning of valid textual
/// representation of an TSIG RDATA.
///
/// See \c TSIG::TSIG(const std::string&) for description of the
/// expected RDATA fields.
///
/// \throw MasterLexer::LexerError General parsing error such as
/// missing field.
/// \throw InvalidRdataText if any fields are out of their valid range,
/// or are incorrect.
///
/// \param lexer A \c MasterLexer object parsing a master file for the
/// RDATA to be created
TSIG::TSIG(MasterLexer& lexer, const Name* origin,
MasterLoader::Options, MasterLoaderCallbacks&) :
impl_(constructFromLexer(lexer, origin))
{
}
/// \brief Constructor from wire-format data.
......@@ -183,7 +270,9 @@ TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) {
/// 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) {
TSIG::TSIG(InputBuffer& buffer, size_t) :
impl_(NULL)
{
Name algorithm(buffer);
uint8_t time_signed_buf[6];
......@@ -298,7 +387,7 @@ TSIG::toText() const {
// toWire().
template <typename Output>
void
TSIG::TSIGImpl::toWireCommon(Output& output) const {
TSIGImpl::toWireCommon(Output& output) const {
output.writeUint16(time_signed_ >> 32);
output.writeUint32(time_signed_ & 0xffffffff);
output.writeUint16(fudge_);
......
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2010-2013 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
......@@ -18,14 +18,9 @@
#include <string>
#include <dns/name.h>
#include <dns/rdata.h>
namespace isc {
namespace dns {
class Name;
}
}
// BEGIN_ISC_NAMESPACE
// BEGIN_COMMON_DECLARATIONS
......@@ -33,6 +28,8 @@ class Name;
// BEGIN_RDATA_NAMESPACE
struct TSIGImpl;
/// \brief \c rdata::TSIG class represents the TSIG RDATA as defined %in
/// RFC2845.
///
......@@ -145,7 +142,8 @@ public:
/// This method never throws an exception.
const void* getOtherData() const;
private:
struct TSIGImpl;
TSIGImpl* constructFromLexer(MasterLexer& lexer, const Name* origin);
TSIGImpl* impl_;
};
......
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2010-2013 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
......@@ -31,6 +31,12 @@ A::A(const std::string&) {
// TBD
}
A::A(MasterLexer&, const Name*,
MasterLoader::Options, MasterLoaderCallbacks&)
{
// TBD
}
A::A(InputBuffer&, size_t) {
// TBD
}
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-2013 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
......@@ -21,10 +21,12 @@
#include <dns/name.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rdata/generic/detail/lexer_util.h>
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using isc::dns::rdata::generic::detail::createNameFromLexer;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
......@@ -39,12 +41,10 @@ using namespace isc::util;
/// An example of valid string is:
/// \code "rmail.example.com. email.example.com." \endcode
///
/// <b>Exceptions</b>
///
/// \exception InvalidRdataText The number of RDATA fields (must be 2) is
/// \throw InvalidRdataText The number of RDATA fields (must be 2) is
/// incorrect.
/// \exception std::bad_alloc Memory allocation for names fails.
/// \exception Other The constructor of the \c Name class will throw if the
/// \throw std::bad_alloc Memory allocation for names fails.
/// \throw Other The constructor of the \c Name class will throw if the
/// names in the string is invalid.
MINFO::MINFO(const std::string& minfo_str) :
// We cannot construct both names in the initialization list due to the
......@@ -52,21 +52,44 @@ MINFO::MINFO(const std::string& minfo_str) :
// name and replace them later.
rmailbox_(Name::ROOT_NAME()), emailbox_(Name::ROOT_NAME())
{
istringstream iss(minfo_str);
string rmailbox_str, emailbox_str;
iss >> rmailbox_str >> emailbox_str;
// Validation: A valid MINFO RR must have exactly two fields.
if (iss.bad() || iss.fail()) {
isc_throw(InvalidRdataText, "Invalid MINFO text: " << minfo_str);
}
if (!iss.eof()) {
isc_throw(InvalidRdataText, "Invalid MINFO text (redundant field): "
<< minfo_str);
try {
std::istringstream ss(minfo_str);
MasterLexer lexer;
lexer.pushSource(ss);
rmailbox_ = createNameFromLexer(lexer, NULL);
emailbox_ = createNameFromLexer(lexer, NULL);
if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
isc_throw(InvalidRdataText, "extra input text for MINFO: "
<< minfo_str);
}
} catch (const MasterLexer::LexerError& ex) {
isc_throw(InvalidRdataText, "Failed to construct MINFO from '" <<
minfo_str << "': " << ex.what());
}
}
rmailbox_ = Name(rmailbox_str);
emailbox_ = Name(emailbox_str);
/// \brief Constructor with a context of MasterLexer.
///
/// The \c lexer should point to the beginning of valid textual representation
/// of an MINFO RDATA. The RMAILBOX and EMAILBOX fields can be non-absolute
/// if \c origin is non-NULL, in which case \c origin is used to make them
/// absolute.
///
/// \throw MasterLexer::LexerError General parsing error such as missing field.
/// \throw Other Exceptions from the Name and constructors if construction of
/// textual fields as these objects fail.
///
/// \param lexer A \c MasterLexer object parsing a master file for the
/// RDATA to be created
/// \param origin If non NULL, specifies the origin of SERVER when it
/// is non-absolute.
MINFO::MINFO(MasterLexer& lexer, const Name* origin,
MasterLoader::Options, MasterLoaderCallbacks&) :
rmailbox_(createNameFromLexer(lexer, origin)),
emailbox_(createNameFromLexer(lexer, origin))
{
}
/// \brief Constructor from wire-format data.
......@@ -75,8 +98,8 @@ MINFO::MINFO(const std::string& minfo_str) :
/// length) for parsing.
/// If necessary, the caller will check consistency.
///
/// \exception std::bad_alloc Memory allocation for names fails.
/// \exception Other The constructor of the \c Name class will throw if the
/// \throw std::bad_alloc Memory allocation for names fails.
/// \throw Other The constructor of the \c Name class will throw if the
/// names in the wire is invalid.
MINFO::MINFO(InputBuffer& buffer, size_t) :
rmailbox_(buffer), emailbox_(buffer)
......@@ -84,7 +107,7 @@ MINFO::MINFO(InputBuffer& buffer, size_t) :
/// \brief Copy constructor.
///
/// \exception std::bad_alloc Memory allocation fails in copying internal
/// \throw std::bad_alloc Memory allocation fails in copying internal
/// member variables (this should be very rare).
MINFO::MINFO(const MINFO& other) :
Rdata(), rmailbox_(other.rmailbox_), emailbox_(other.emailbox_)
......@@ -95,7 +118,7 @@ MINFO::MINFO(const MINFO& other) :
/// The output of this method is formatted as described in the "from string"
/// constructor (\c MINFO(const std::string&))).
///
/// \exception std::bad_alloc Internal resource allocation fails.
/// \throw std::bad_alloc Internal resource allocation fails.
///
/// \return A \c string object that represents the \c MINFO object.
std::string
......@@ -105,7 +128,7 @@ MINFO::toText() const {
/// \brief Render the \c MINFO in the wire format without name compression.
///
/// \exception std::bad_alloc Internal resource allocation fails.
/// \throw std::bad_alloc Internal resource allocation fails.
///
/// \param buffer An output buffer to store the wire data.
void
......@@ -128,7 +151,7 @@ MINFO::operator=(const MINFO& source) {
/// As specified in RFC3597, TYPE MINFO is "well-known", the rmailbox and
/// emailbox fields (domain names) will be compressed.
///
/// \exception std::bad_alloc Internal resource allocation fails.
/// \throw std::bad_alloc Internal resource allocation fails.
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-2013 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
......@@ -45,7 +45,7 @@ public:
/// \brief Return the value of the rmailbox field.
///
/// \exception std::bad_alloc If resource allocation for the returned
/// \throw std::bad_alloc If resource allocation for the returned
/// \c Name fails.
///
/// \note
......@@ -64,7 +64,7 @@ public:
/// \brief Return the value of the emailbox field.
///
/// \exception std::bad_alloc If resource allocation for the returned
/// \throw std::bad_alloc If resource allocation for the returned
/// \c Name fails.
Name getEmailbox() const { return (emailbox_); }
......
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-2013 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
......@@ -21,10 +21,12 @@
#include <dns/name.h>