Commit dc0f34af authored by Mukund Sivaraman's avatar Mukund Sivaraman
Browse files

Merge branch 'trac2386'

parents 07ecaaf4 d217a093
......@@ -34,11 +34,14 @@ import sys
# new_rdata_factory_users = [('a', 'in'), ('a', 'ch'), ('soa', 'generic')]
new_rdata_factory_users = [('a', 'in'), ('aaaa', 'in'),
('cname', 'generic'),
('dlv', 'generic'),
('dname', 'generic'),
('ds', 'generic'),
('hinfo', 'generic'),
('naptr', 'generic'),
('mx', 'generic'),
('ns', 'generic'),
('nsec', 'generic'),
('ptr', 'generic'),
('soa', 'generic'),
('spf', 'generic'),
......
......@@ -58,49 +58,109 @@ template <class Type, uint16_t typeCode> class DSLikeImpl {
public:
/// \brief Constructor from string.
///
/// <b>Exceptions</b>
/// The given string must represent a valid DS-like 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 tag field must be a valid decimal representation of an
/// unsigned 16-bit integer. The protocol and algorithm fields must
/// be valid decimal representations of unsigned 8-bit integers
/// respectively. The digest field may contain whitespace.
///
/// \throw InvalidRdataText if any fields are out of their valid range.
///
/// \c InvalidRdataText is thrown if the method cannot process the
/// parameter data for any of the number of reasons.
/// \param ds_str A string containing the RDATA to be created
DSLikeImpl(const std::string& ds_str) {
std::istringstream iss(ds_str);
// peekc should be of iss's char_type for isspace to work
std::istringstream::char_type peekc;
std::stringbuf digestbuf;
uint32_t tag, algorithm, digest_type;
iss >> tag >> algorithm >> digest_type;
if (iss.bad() || iss.fail()) {
try {
std::istringstream ss(ds_str);
MasterLexer lexer;
lexer.pushSource(ss);
constructFromLexer(lexer);
if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
isc_throw(InvalidRdataText,
"Extra input text for " << RRType(typeCode) << ": "
<< ds_str);
}
} catch (const MasterLexer::LexerError& ex) {
isc_throw(InvalidRdataText,
"Invalid " << RRType(typeCode) << " text");
"Failed to construct " << RRType(typeCode) << " from '" <<
ds_str << "': " << ex.what());
}
}
/// \brief Constructor with a context of MasterLexer.
///
/// The \c lexer should point to the beginning of valid textual
/// representation of a DS-like RDATA.
///
/// The tag field must be a valid decimal representation of an
/// unsigned 16-bit integer. The protocol and algorithm fields must
/// be valid decimal representations of unsigned 8-bit integers
/// respectively.
///
/// \throw MasterLexer::LexerError General parsing error such as
/// missing field.
/// \throw InvalidRdataText if any fields are out of their valid range.
///
/// \param lexer A \c MasterLexer object parsing a master file for the
/// RDATA to be created
DSLikeImpl(MasterLexer& lexer, const Name*, MasterLoader::Options,
MasterLoaderCallbacks&)
{
constructFromLexer(lexer);
}
private:
void constructFromLexer(MasterLexer& lexer) {
const uint32_t tag =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (tag > 0xffff) {
isc_throw(InvalidRdataText,
RRType(typeCode) << " tag out of range");
"Invalid " << RRType(typeCode) << " tag: " << tag);
}
const uint32_t algorithm =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (algorithm > 0xff) {
isc_throw(InvalidRdataText,
RRType(typeCode) << " algorithm out of range");
"Invalid " << RRType(typeCode) << " algorithm: "
<< algorithm);
}
const uint32_t digest_type =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (digest_type > 0xff) {
isc_throw(InvalidRdataText,
RRType(typeCode) << " digest type out of range");
"Invalid " << RRType(typeCode) << " digest type: "
<< digest_type);
}
iss.read(&peekc, 1);
if (!iss.good() || !isspace(peekc, iss.getloc())) {
isc_throw(InvalidRdataText,
RRType(typeCode) << " presentation format error");
std::string digest;
while (true) {
const MasterToken& token = lexer.getNextToken();
if (token.getType() != MasterToken::STRING) {
break;
}
digest.append(token.getString());
}
iss >> &digestbuf;
lexer.ungetToken();
if (digest.size() == 0) {
isc_throw(InvalidRdataText,
"Missing " << RRType(typeCode) << " digest");
}
tag_ = tag;
algorithm_ = algorithm;
digest_type_ = digest_type;
decodeHex(digestbuf.str(), digest_);
decodeHex(digest, digest_);
}
public:
/// \brief Constructor from wire-format data.
///
/// \param buffer A buffer storing the wire format data.
......
......@@ -118,6 +118,61 @@ buildBitmapsFromText(const char* const rrtype_name,
}
}
// 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)
{
uint8_t bitmap[8 * 1024]; // 64k bits
memset(bitmap, 0, sizeof(bitmap));
bool have_rrtypes = false;
while (true) {
const MasterToken& token = lexer.getNextToken();
if (token.getType() != MasterToken::STRING) {
break;
}
have_rrtypes = true;
std::string type_str;
try {
type_str = token.getString();
const int code = RRType(type_str).getCode();
bitmap[code / 8] |= (0x80 >> (code % 8));
} catch (const InvalidRRType&) {
isc_throw(InvalidRdataText, "Invalid RRtype in "
<< rrtype_name << " bitmap: " << type_str);
}
}
lexer.ungetToken();
if (!have_rrtypes) {
isc_throw(InvalidRdataText,
rrtype_name << " record does not end with RR type mnemonic");
}
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]);
}
}
}
void
bitmapsToText(const vector<uint8_t>& typebits, ostringstream& oss) {
// In the following loop we use string::at() rather than operator[].
......
......@@ -15,6 +15,8 @@
#ifndef NSECBITMAP_H
#define NSECBITMAP_H 1
#include <dns/master_lexer.h>
#include <stdint.h>
#include <sstream>
......@@ -75,6 +77,26 @@ 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.
///
/// \exception InvalidRdataText Data read from the given lexer 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 lexer MasterLexer that provides consists of a complete
/// sequence of textual lexemes of 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 buildBitmapsFromLexer(const char* const rrtype_name,
isc::dns::MasterLexer& lexer,
std::vector<uint8_t>& typebits);
/// \brief Convert type bitmaps to textual sequence of RR types.
///
/// This function converts wire-format data of type bitmaps for NSEC/NSEC3
......
......@@ -45,6 +45,11 @@ DLV::DLV(InputBuffer& buffer, size_t rdata_len) :
impl_(new DLVImpl(buffer, rdata_len))
{}
DLV::DLV(MasterLexer& lexer, const Name* origin, MasterLoader::Options options,
MasterLoaderCallbacks& callbacks) :
impl_(new DLVImpl(lexer, origin, options, callbacks))
{}
/// \brief Copy constructor
///
/// A copy of the implementation object is allocated and constructed.
......
......@@ -39,6 +39,11 @@ DS::DS(InputBuffer& buffer, size_t rdata_len) :
impl_(new DSImpl(buffer, rdata_len))
{}
DS::DS(MasterLexer& lexer, const Name* origin, MasterLoader::Options options,
MasterLoaderCallbacks& callbacks) :
impl_(new DSImpl(lexer, origin, options, callbacks))
{}
DS::DS(const DS& source) :
Rdata(), impl_(new DSImpl(*source.impl_))
{}
......
......@@ -27,6 +27,7 @@
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rdata/generic/detail/nsec_bitmap.h>
#include <dns/rdata/generic/detail/lexer_util.h>
#include <stdio.h>
#include <time.h>
......@@ -35,6 +36,7 @@ using namespace std;
using namespace isc::util;
using namespace isc::util::encode;
using namespace isc::dns::rdata::generic::detail::nsec;
using isc::dns::rdata::generic::detail::createNameFromLexer;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
......@@ -49,24 +51,50 @@ struct NSECImpl {
vector<uint8_t> typebits_;
};
/// \brief Constructor from string.
///
/// The given string must represent a valid NSEC 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 Next Domain Name field must be absolute since there's no
/// parameter that specifies the origin name; if it is not absolute,
/// \c MissingNameOrigin exception will be thrown. This must not be
/// represented as a quoted string.
///
/// The type mnemonics must be valid, and separated by whitespace. If
/// any invalid mnemonics are found, InvalidRdataText exception is
/// thrown.
///
/// \throw MissingNameOrigin Thrown when the Next Domain Name is not absolute.
/// \throw InvalidRdataText if any fields are out of their valid range.
///
/// \param nsec_str A string containing the RDATA to be created
NSEC::NSEC(const std::string& nsec_str) :
impl_(NULL)
{
istringstream iss(nsec_str);
string nextname;
iss >> nextname;
if (iss.bad() || iss.fail()) {
isc_throw(InvalidRdataText, "Invalid NSEC name");
}
if (iss.eof()) {
isc_throw(InvalidRdataText, "NSEC bitmap is missing");
try {
std::istringstream ss(nsec_str);
MasterLexer lexer;
lexer.pushSource(ss);
const Name origin_name(createNameFromLexer(lexer, NULL));
vector<uint8_t> typebits;
buildBitmapsFromLexer("NSEC", lexer, typebits);
impl_ = new NSECImpl(origin_name, typebits);
if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
isc_throw(InvalidRdataText,
"Extra input text for NSEC: " << nsec_str);
}
} catch (const MasterLexer::LexerError& ex) {
isc_throw(InvalidRdataText,
"Failed to construct NSEC from '" << nsec_str << "': "
<< ex.what());
}
vector<uint8_t> typebits;
buildBitmapsFromText("NSEC", iss, typebits);
impl_ = new NSECImpl(Name(nextname), typebits);
}
NSEC::NSEC(InputBuffer& buffer, size_t rdata_len) {
......@@ -87,6 +115,38 @@ NSEC::NSEC(InputBuffer& buffer, size_t rdata_len) {
impl_ = new NSECImpl(nextname, typebits);
}
/// \brief Constructor with a context of MasterLexer.
///
/// The \c lexer should point to the beginning of valid textual
/// representation of an NSEC RDATA.
///
/// The Next Domain Name field can be non-absolute if \c origin is
/// non-NULL, in which case \c origin is used to make it absolute. It
/// must not be represented as a quoted string.
///
/// The type mnemonics must be valid, and separated by whitespace. If
/// any invalid mnemonics are found, InvalidRdataText exception is
/// thrown.
///
/// \throw MasterLexer::LexerError General parsing error such as
/// missing field.
/// \throw MissingNameOrigin Thrown when the Next Domain Name is not
/// absolute and \c origin is NULL.
/// \throw InvalidRdataText if any fields are out of their valid range.
///
/// \param lexer A \c MasterLexer object parsing a master file for the
/// RDATA to be created
NSEC::NSEC(MasterLexer& lexer, const Name* origin, MasterLoader::Options,
MasterLoaderCallbacks&)
{
const Name origin_name(createNameFromLexer(lexer, origin));
vector<uint8_t> typebits;
buildBitmapsFromLexer("NSEC", lexer, typebits);
impl_ = new NSECImpl(origin_name, typebits);
}
NSEC::NSEC(const NSEC& source) :
Rdata(), impl_(new NSECImpl(*source.impl_))
{}
......
......@@ -61,6 +61,14 @@ typedef testing::Types<generic::DS, generic::DLV> Implementations;
TYPED_TEST_CASE(Rdata_DS_LIKE_Test, Implementations);
TYPED_TEST(Rdata_DS_LIKE_Test, createFromText) {
// It's valid for the digest's presentation format to contain
// spaces. See RFC4034 section 5.3.
EXPECT_EQ(0, this->rdata_ds_like.compare(
TypeParam("12892 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D5F0EB5"
"C777 58 6DE18 \t DA6B5")));
}
TYPED_TEST(Rdata_DS_LIKE_Test, toText_DS_LIKE) {
EXPECT_EQ(ds_like_txt, this->rdata_ds_like.toText());
}
......@@ -90,9 +98,26 @@ TYPED_TEST(Rdata_DS_LIKE_Test, createFromLexer_DS_LIKE) {
*test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
ds_like_txt)));
// Whitespace is okay
EXPECT_EQ(0, this->rdata_ds_like.compare(
*test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
"12892 5 2 F1E184C0E1D615D20EB3C223ACED3B0"
"3C773DD952D5F0EB5C777 58 6DE18 \t DA6B5"
)));
// Exceptions cause NULL to be returned.
// Bad tag
EXPECT_FALSE(test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
"65536 5 2 BEEF"));
// Bad algorithm
EXPECT_FALSE(test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
"1024 256 2 BEEF"));
// Bad digest type
EXPECT_FALSE(test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
"99999 5 2 BEEF"));
"2048 2 256 BEEF"));
}
TYPED_TEST(Rdata_DS_LIKE_Test, assignment_DS_LIKE) {
......
......@@ -72,6 +72,12 @@ TEST_F(Rdata_NSEC_Test, createFromLexer_NSEC) {
*test::createRdataUsingLexer(RRType::NSEC(), RRClass::IN(),
nsec_txt)));
// test::createRdataUsingLexer() constructs relative to
// "example.org." origin.
EXPECT_EQ(0, generic::NSEC("www2.example.org. CNAME RRSIG NSEC").compare(
*test::createRdataUsingLexer(RRType::NSEC(), RRClass::IN(),
"www2 CNAME RRSIG NSEC")));
// Exceptions cause NULL to be returned.
EXPECT_FALSE(test::createRdataUsingLexer(RRType::NSEC(), RRClass::IN(),
"www.isc.org."));
......@@ -108,9 +114,9 @@ TEST_F(Rdata_NSEC_Test, getNextName) {
TEST_F(Rdata_NSEC_Test, compare) {
// trivial case: self equivalence
EXPECT_EQ(0, generic::NSEC("example A").
EXPECT_EQ(0, generic::NSEC("example. A").
compare(generic::NSEC("example. A")));
EXPECT_EQ(0, generic::NSEC("EXAMPLE A"). // should be case insensitive
EXPECT_EQ(0, generic::NSEC("EXAMPLE. A"). // should be case insensitive
compare(generic::NSEC("example. A")));
// comparison attempt between incompatible RR types should be rejected
......
......@@ -168,12 +168,6 @@ TYPED_TEST(NSECLikeBitmapTest, createFromWire) {
DNSMessageFORMERR);
}
TYPED_TEST(NSECLikeBitmapTest, badText) {
// redundant space after the sequence
EXPECT_THROW(this->fromText(this->getCommonText() + "A "),
InvalidRdataText);
}
// This tests the result of toText() with various kinds of NSEC/NSEC3 bitmaps.
// It also tests the "from text" constructor as a result.
TYPED_TEST(NSECLikeBitmapTest, toText) {
......@@ -230,7 +224,7 @@ TYPED_TEST(NSECLikeBitmapTest, compare) {
// NSEC bitmaps must not be empty
TEST_F(NSECBitmapTest, emptyMap) {
EXPECT_THROW(this->fromText("next.example").toText(), InvalidRdataText);
EXPECT_THROW(this->fromText("next.example.").toText(), InvalidRdataText);
EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
(this->getWireFilePrefix() +
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment