Commit 937635d7 authored by Mukund Sivaraman's avatar Mukund Sivaraman
Browse files

Merge branch 'trac2387'

parents 0a9abd16 1b9e3430
......@@ -37,12 +37,15 @@ new_rdata_factory_users = [('a', 'in'), ('aaaa', 'in'),
('cname', 'generic'),
('dlv', 'generic'),
('dname', 'generic'),
('dnskey', 'generic'),
('ds', 'generic'),
('hinfo', 'generic'),
('naptr', 'generic'),
('mx', 'generic'),
('ns', 'generic'),
('nsec', 'generic'),
('nsec3', 'generic'),
('nsec3param', 'generic'),
('ptr', 'generic'),
('soa', 'generic'),
('spf', 'generic'),
......
......@@ -39,41 +39,33 @@ namespace detail {
namespace nsec3 {
ParseNSEC3ParamResult
parseNSEC3ParamText(const char* const rrtype_name,
const string& rdata_str, istringstream& iss,
vector<uint8_t>& salt)
parseNSEC3ParamFromLexer(const char* const rrtype_name,
MasterLexer& lexer, vector<uint8_t>& salt)
{
unsigned int hashalg, flags, iterations;
string iterations_str, salthex;
iss >> hashalg >> flags >> iterations_str >> salthex;
if (iss.bad() || iss.fail()) {
isc_throw(InvalidRdataText, "Invalid " << rrtype_name <<
" text: " << rdata_str);
}
const uint32_t hashalg =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (hashalg > 0xff) {
isc_throw(InvalidRdataText, rrtype_name <<
" hash algorithm out of range: " << hashalg);
}
const uint32_t flags =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (flags > 0xff) {
isc_throw(InvalidRdataText, rrtype_name << " flags out of range: " <<
flags);
}
// Convert iteration. To reject an invalid case where there's no space
// between iteration and salt, we extract this field as string and convert
// to integer.
try {
iterations = boost::lexical_cast<unsigned int>(iterations_str);
} catch (const boost::bad_lexical_cast&) {
isc_throw(InvalidRdataText, "Bad " << rrtype_name <<
" iteration: " << iterations_str);
}
const uint32_t iterations =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (iterations > 0xffff) {
isc_throw(InvalidRdataText, rrtype_name <<
" iterations out of range: " <<
iterations);
" iterations out of range: " << iterations);
}
const string salthex =
lexer.getNextToken(MasterToken::STRING).getString();
// Salt is up to 255 bytes, and space is not allowed in the HEX encoding,
// so the encoded string cannot be longer than the double of max length
// of the actual salt.
......
......@@ -15,12 +15,11 @@
#ifndef NSEC3PARAM_COMMON_H
#define NSEC3PARAM_COMMON_H 1
#include <dns/master_lexer.h>
#include <util/buffer.h>
#include <stdint.h>
#include <sstream>
#include <string>
#include <vector>
namespace isc {
......@@ -59,7 +58,7 @@ struct ParseNSEC3ParamResult {
/// \brief Convert textual representation of NSEC3 parameters.
///
/// This function takes an input string stream that consists of a complete
/// This function takes an input MasterLexer that points at a complete
/// textual representation of an NSEC3 or NSEC3PARAM RDATA and parses it
/// extracting the hash algorithm, flags, iterations, and salt fields.
///
......@@ -67,28 +66,26 @@ struct ParseNSEC3ParamResult {
/// The salt will be stored in the given vector. The vector is expected
/// to be empty, but if not, the existing content will be overridden.
///
/// On successful return the given input stream will reach the end of the
/// On successful return the given MasterLexer will reach the end of the
/// salt field.
///
/// \exception isc::BadValue The salt is not a valid hex string.
/// \exception InvalidRdataText The given string is otherwise invalid for
/// \exception InvalidRdataText The given RDATA is otherwise invalid for
/// NSEC3 or NSEC3PARAM fields.
/// \exception MasterLexer::LexerError There was a syntax error reading
/// a field from the MasterLexer.
///
/// \param rrtype_name Either "NSEC3" or "NSEC3PARAM"; used as part of
/// exception messages.
/// \param rdata_str A complete textual string of the RDATA; used as part of
/// exception messages.
/// \param iss Input stream that consists of a complete textual string of
/// the RDATA.
/// \param lexer The MasterLexer to read NSEC3 parameter fields from.
/// \param salt A placeholder for the salt field value of the RDATA.
/// Expected to be empty, but it's not checked (and will be overridden).
///
/// \return The hash algorithm, flags, iterations in the form of
/// ParseNSEC3ParamResult.
ParseNSEC3ParamResult parseNSEC3ParamText(const char* const rrtype_name,
const std::string& rdata_str,
std::istringstream& iss,
std::vector<uint8_t>& salt);
ParseNSEC3ParamResult parseNSEC3ParamFromLexer(const char* const rrtype_name,
isc::dns::MasterLexer& lexer,
std::vector<uint8_t>& salt);
/// \brief Extract NSEC3 parameters from wire-format data.
///
......
......@@ -77,69 +77,29 @@ checkRRTypeBitmaps(const char* const rrtype_name,
}
}
void
buildBitmapsFromText(const char* const rrtype_name,
istringstream& iss, vector<uint8_t>& typebits)
{
uint8_t bitmap[8 * 1024]; // 64k bits
memset(bitmap, 0, sizeof(bitmap));
do {
string type;
iss >> type;
if (iss.bad() || iss.fail()) {
isc_throw(InvalidRdataText, "Unexpected input for "
<< rrtype_name << " bitmap");
}
try {
const int code = RRType(type).getCode();
bitmap[code / 8] |= (0x80 >> (code % 8));
} catch (const InvalidRRType&) {
isc_throw(InvalidRdataText, "Invalid RRtype in "
<< rrtype_name << " bitmap: " << type);
}
} while (!iss.eof());
for (int window = 0; window < 256; ++window) {
int octet;
for (octet = 31; octet >= 0; octet--) {
if (bitmap[window * 32 + octet] != 0) {
break;
}
}
if (octet < 0) {
continue;
}
typebits.push_back(window);
typebits.push_back(octet + 1);
for (int i = 0; i <= octet; ++i) {
typebits.push_back(bitmap[window * 32 + i]);
}
}
}
// Note: this function shares common code with buildBitmapsFromText()
// above, but it is expected that buildBitmapsFromText() will be deleted
// entirely once the transition to MasterLexer is done for all dependent
// RR types. So a common method is not made from the two.
void
buildBitmapsFromLexer(const char* const rrtype_name,
MasterLexer& lexer, vector<uint8_t>& typebits)
MasterLexer& lexer, vector<uint8_t>& typebits,
bool allow_empty)
{
uint8_t bitmap[8 * 1024]; // 64k bits
memset(bitmap, 0, sizeof(bitmap));
bool have_rrtypes = false;
std::string type_str;
while (true) {
const MasterToken& token = lexer.getNextToken();
if (token.getType() != MasterToken::STRING) {
const MasterToken& token =
lexer.getNextToken(MasterToken::STRING, true);
if ((token.getType() == MasterToken::END_OF_FILE) ||
(token.getType() == MasterToken::END_OF_LINE)) {
break;
}
// token is now assured to be of type STRING.
have_rrtypes = true;
std::string type_str;
token.getString(type_str);
try {
type_str = token.getString();
const int code = RRType(type_str).getCode();
bitmap[code / 8] |= (0x80 >> (code % 8));
} catch (const InvalidRRType&) {
......@@ -151,8 +111,12 @@ buildBitmapsFromLexer(const char* const rrtype_name,
lexer.ungetToken();
if (!have_rrtypes) {
if (allow_empty) {
return;
}
isc_throw(InvalidRdataText,
rrtype_name << " record does not end with RR type mnemonic");
rrtype_name <<
" record does not end with RR type mnemonic");
}
for (int window = 0; window < 256; ++window) {
......
......@@ -54,33 +54,11 @@ namespace nsec {
void checkRRTypeBitmaps(const char* const rrtype_name,
const std::vector<uint8_t>& typebits);
/// \brief Convert textual sequence of RR types into type bitmaps.
///
/// This function extracts a sequence of strings, converts each sequence
/// into an RR type, and builds NSEC/NSEC3 type bitmaps with the corresponding
/// bits for the extracted types being on. The resulting bitmaps (which are
/// in the wire format according to RFC4034 and RFC5155) are stored in the
/// given vector. This function expects the given string stream ends with
/// the sequence.
///
/// \exception InvalidRdataText The given input stream does not meet the
/// assumption (e.g. including invalid form of RR type, not ending with
/// an RR type string).
///
/// \param rrtype_name Either "NSEC" or "NSEC3"; used as part of exception
/// messages.
/// \param iss Input stream that consists of a complete sequence of textual
/// RR types for which the corresponding bits are set.
/// \param typebits A placeholder for the resulting bitmaps. Expected to be
/// empty, but it's not checked.
void buildBitmapsFromText(const char* const rrtype_name,
std::istringstream& iss,
std::vector<uint8_t>& typebits);
/// \brief Convert textual sequence of RR types read from a lexer into
/// type bitmaps.
///
/// See the other variant above for description.
/// See the other variant above for description. If \c allow_empty is
/// true and there are no mnemonics, \c typebits is left untouched.
///
/// \exception InvalidRdataText Data read from the given lexer does not
/// meet the assumption (e.g. including invalid form of RR type, not
......@@ -93,9 +71,13 @@ void buildBitmapsFromText(const char* const rrtype_name,
/// bits are set.
/// \param typebits A placeholder for the resulting bitmaps. Expected to be
/// empty, but it's not checked.
/// \param allow_empty If true, the function simply returns if no RR
/// type mnemonics are found. Otherwise, it throws an exception if no RR
/// type mnemonics are found.
void buildBitmapsFromLexer(const char* const rrtype_name,
isc::dns::MasterLexer& lexer,
std::vector<uint8_t>& typebits);
std::vector<uint8_t>& typebits,
bool allow_empty = false);
/// \brief Convert type bitmaps to textual sequence of RR types.
///
......
......@@ -27,6 +27,8 @@
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <memory>
#include <stdio.h>
#include <time.h>
......@@ -51,51 +53,157 @@ struct DNSKEYImpl {
const vector<uint8_t> keydata_;
};
/// \brief Constructor from string.
///
/// The given string must represent a valid DNSKEY 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.
///
/// The Protocol and Algorithm fields must be within their valid
/// ranges. The Public Key field must be present and must contain a
/// Base64 encoding of the public key. Whitespace is allowed within the
/// Base64 text.
///
/// It is okay for the key data to be missing. Note: BIND 9 also accepts
/// DNSKEY missing key data. While the RFC is silent in this case, and it
/// may be debatable what an implementation should do, but since this field
/// is algorithm dependent and this implementations doesn't reject unknown
/// algorithms, it's lenient here.
///
/// \throw InvalidRdataText if any fields are out of their valid range,
/// or are incorrect.
///
/// \param dnskey_str A string containing the RDATA to be created
DNSKEY::DNSKEY(const std::string& dnskey_str) :
impl_(NULL)
{
istringstream iss(dnskey_str);
unsigned int flags, protocol, algorithm;
stringbuf keydatabuf;
// 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 DNSKEYImpl that constructFromLexer() returns.
std::auto_ptr<DNSKEYImpl> impl_ptr(NULL);
try {
std::istringstream ss(dnskey_str);
MasterLexer lexer;
lexer.pushSource(ss);
impl_ptr.reset(constructFromLexer(lexer));
if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
isc_throw(InvalidRdataText,
"Extra input text for DNSKEY: " << dnskey_str);
}
} catch (const MasterLexer::LexerError& ex) {
isc_throw(InvalidRdataText,
"Failed to construct DNSKEY from '" << dnskey_str << "': "
<< ex.what());
}
iss >> flags >> protocol >> algorithm >> &keydatabuf;
if (iss.bad() || iss.fail()) {
isc_throw(InvalidRdataText, "Invalid DNSKEY text");
impl_ = impl_ptr.release();
}
/// \brief Constructor from InputBuffer.
///
/// The passed buffer must contain a valid DNSKEY RDATA.
///
/// The Protocol and Algorithm fields are not checked for unknown
/// values. It is okay for the key data to be missing (see the description
/// of the constructor from string).
DNSKEY::DNSKEY(InputBuffer& buffer, size_t rdata_len) :
impl_(NULL)
{
if (rdata_len < 4) {
isc_throw(InvalidRdataLength, "DNSKEY too short: " << rdata_len);
}
const uint16_t flags = buffer.readUint16();
const uint16_t protocol = buffer.readUint8();
const uint16_t algorithm = buffer.readUint8();
rdata_len -= 4;
vector<uint8_t> keydata;
// If key data is missing, it's OK. See the API documentation of the
// constructor.
if (rdata_len > 0) {
keydata.resize(rdata_len);
buffer.readData(&keydata[0], rdata_len);
}
impl_ = new DNSKEYImpl(flags, protocol, algorithm, keydata);
}
/// \brief Constructor with a context of MasterLexer.
///
/// The \c lexer should point to the beginning of valid textual
/// representation of an DNSKEY RDATA.
///
/// See \c DNSKEY::DNSKEY(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
DNSKEY::DNSKEY(MasterLexer& lexer, const Name*,
MasterLoader::Options, MasterLoaderCallbacks&) :
impl_(NULL)
{
impl_ = constructFromLexer(lexer);
}
DNSKEYImpl*
DNSKEY::constructFromLexer(MasterLexer& lexer) {
const uint32_t flags = lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (flags > 0xffff) {
isc_throw(InvalidRdataText, "DNSKEY flags out of range");
isc_throw(InvalidRdataText,
"DNSKEY flags out of range: " << flags);
}
const uint32_t protocol =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (protocol > 0xff) {
isc_throw(InvalidRdataText, "DNSKEY protocol out of range");
isc_throw(InvalidRdataText,
"DNSKEY protocol out of range: " << protocol);
}
const uint32_t algorithm =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (algorithm > 0xff) {
isc_throw(InvalidRdataText, "DNSKEY algorithm out of range");
isc_throw(InvalidRdataText,
"DNSKEY algorithm out of range: " << algorithm);
}
vector<uint8_t> keydata;
decodeBase64(keydatabuf.str(), keydata);
if (algorithm == 1 && keydata.size() < 3) {
isc_throw(InvalidRdataLength, "DNSKEY keydata too short");
}
std::string keydata_str;
std::string keydata_substr;
while (true) {
const MasterToken& token =
lexer.getNextToken(MasterToken::STRING, true);
if ((token.getType() == MasterToken::END_OF_FILE) ||
(token.getType() == MasterToken::END_OF_LINE)) {
break;
}
impl_ = new DNSKEYImpl(flags, protocol, algorithm, keydata);
}
// token is now assured to be of type STRING.
DNSKEY::DNSKEY(InputBuffer& buffer, size_t rdata_len) {
if (rdata_len < 4) {
isc_throw(InvalidRdataLength, "DNSKEY too short: " << rdata_len);
token.getString(keydata_substr);
keydata_str.append(keydata_substr);
}
uint16_t flags = buffer.readUint16();
uint16_t protocol = buffer.readUint8();
uint16_t algorithm = buffer.readUint8();
lexer.ungetToken();
rdata_len -= 4;
vector<uint8_t> keydata(rdata_len);
buffer.readData(&keydata[0], rdata_len);
vector<uint8_t> keydata;
// If key data is missing, it's OK. See the API documentation of the
// constructor.
if (keydata_str.size() > 0) {
decodeBase64(keydata_str, keydata);
}
impl_ = new DNSKEYImpl(flags, protocol, algorithm, keydata);
return (new DNSKEYImpl(flags, protocol, algorithm, keydata));
}
DNSKEY::DNSKEY(const DNSKEY& source) :
......@@ -157,11 +265,11 @@ DNSKEY::compare(const Rdata& other) const {
return (impl_->algorithm_ < other_dnskey.impl_->algorithm_ ? -1 : 1);
}
size_t this_len = impl_->keydata_.size();
size_t other_len = other_dnskey.impl_->keydata_.size();
size_t cmplen = min(this_len, other_len);
int cmp = memcmp(&impl_->keydata_[0], &other_dnskey.impl_->keydata_[0],
cmplen);
const size_t this_len = impl_->keydata_.size();
const size_t other_len = other_dnskey.impl_->keydata_.size();
const size_t cmplen = min(this_len, other_len);
const int cmp = memcmp(&impl_->keydata_[0],
&other_dnskey.impl_->keydata_[0], cmplen);
if (cmp != 0) {
return (cmp);
} else {
......@@ -172,15 +280,24 @@ DNSKEY::compare(const Rdata& other) const {
uint16_t
DNSKEY::getTag() const {
if (impl_->algorithm_ == 1) {
int len = impl_->keydata_.size();
// See RFC 4034 appendix B.1 for why the key data must contain
// at least 4 bytes with RSA/MD5: 3 trailing bytes to extract
// the tag from, and 1 byte of exponent length subfield before
// modulus.
const int len = impl_->keydata_.size();
if (len < 4) {
isc_throw(isc::OutOfRange,
"DNSKEY keydata too short for tag extraction");
}
return ((impl_->keydata_[len - 3] << 8) + impl_->keydata_[len - 2]);
}
uint32_t ac = impl_->flags_;
ac += (impl_->protocol_ << 8);
ac += impl_->algorithm_;
size_t size = impl_->keydata_.size();
const size_t size = impl_->keydata_.size();
for (size_t i = 0; i < size; i ++) {
ac += (i & 1) ? impl_->keydata_[i] : (impl_->keydata_[i] << 8);
}
......
......@@ -20,6 +20,7 @@
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/rdata.h>
#include <dns/master_lexer.h>
// BEGIN_HEADER_GUARD
......@@ -42,11 +43,19 @@ public:
///
/// Specialized methods
///
/// \brief Returns the key tag
///
/// \throw isc::OutOfRange if the key data for RSA/MD5 is too short
/// to support tag extraction.
uint16_t getTag() const;
uint16_t getFlags() const;
uint8_t getAlgorithm() const;
private:
DNSKEYImpl* constructFromLexer(isc::dns::MasterLexer& lexer);
DNSKEYImpl* impl_;
};
......
......@@ -68,15 +68,7 @@ MX::MX(const std::string& mx_str) :
MasterLexer lexer;
lexer.pushSource(ss);
const uint32_t num =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (num > 65535) {
isc_throw(InvalidRdataText, "Invalid MX preference in: "
<< mx_str);
}
preference_ = static_cast<uint16_t>(num);
mxname_ = createNameFromLexer(lexer, NULL);
constructFromLexer(lexer, NULL);
if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
isc_throw(InvalidRdataText, "extra input text for MX: "
......@@ -108,8 +100,13 @@ MX::MX(const std::string& mx_str) :
/// is non-absolute.
MX::MX(MasterLexer& lexer, const Name* origin,
MasterLoader::Options, MasterLoaderCallbacks&) :
preference_(0), mxname_(".")
preference_(0), mxname_(Name::ROOT_NAME())
{
constructFromLexer(lexer, origin);
}
void
MX::constructFromLexer(MasterLexer& lexer, const Name* origin) {
const uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (num > 65535) {
isc_throw(InvalidRdataText, "Invalid MX preference: " << num);
......
......@@ -42,6 +42,9 @@ public:
uint16_t getMXPref() const;
private:
void constructFromLexer(isc::dns::MasterLexer& lexer,
const isc::dns::Name* origin);
/// Note: this is a prototype version; we may reconsider
/// this representation later.
uint16_t preference_;
......
......@@ -35,6 +35,8 @@
#include <dns/rdata/generic/detail/nsec_bitmap.h>
#include <dns/rdata/generic/detail/nsec3param_common.h>
#include <memory>
#include <stdio.h>
#include <time.h>
......@@ -64,24 +66,86 @@ struct NSEC3Impl {
const vector<uint8_t> typebits_;
};
/// \brief Constructor from string.