Commit 019ca218 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac2500'

parents 6278daf2 a095d57b
$ORIGIN include. ; initialize origin
$TTL 300
; this needs #2500
;@ IN SOA ns hostmaster (
@ IN SOA ns.include. hostmaster.include. (
@ IN SOA ns hostmaster (
1 ; serial
3600
1800
......
$ORIGIN mix1.
; this needs #2500
;@ IN SOA ns hostmaster (
@ IN SOA ns.mix1. hostmaster.mix1. (
@ IN SOA ns hostmaster (
1 ; serial
3600
1800
......
$ORIGIN mix2.
; this needs #2500
;@ 1 IN SOA ns hostmaster (
@ 1 IN SOA ns.mix2. hostmaster.mix2. (
@ 1 IN SOA ns hostmaster (
1 ; serial
3600
1800
......
$ORIGIN ttl1.
; this needs #2500
;@ IN SOA ns hostmaster (
@ IN SOA ns.ttl1. hostmaster.ttl1. (
@ IN SOA ns hostmaster (
1 ; serial
3600
1800
......
$ORIGIN ttl2.
; this needs #2500
;@ 1 IN SOA ns hostmaster (
@ 1 IN SOA ns.ttl2. hostmaster.ttl2 (
@ 1 IN SOA ns hostmaster (
1 ; serial
3600
1800
......
$ORIGIN ttlext.
; this needs #2500
;@ IN SOA ns hostmaster (
@ IN SOA ns.ttlext. hostmaster.ttlext. (
@ IN SOA ns hostmaster (
1 ; serial
3600
1800
......
......@@ -60,7 +60,7 @@ TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
# SOA intended to be used for the new SOA as a result of transfer.
soa_rdata = Rdata(RRType.SOA(), TEST_RRCLASS,
'master.example.com. admin.example.com ' +
'master.example.com. admin.example.com. ' +
'1234 3600 1800 2419200 7200')
soa_rrset = RRset(TEST_ZONE_NAME, TEST_RRCLASS, RRType.SOA(), RRTTL(3600))
soa_rrset.add_rdata(soa_rdata)
......@@ -68,7 +68,7 @@ soa_rrset.add_rdata(soa_rdata)
# SOA intended to be used for the current SOA at the secondary side.
# Note that its serial is smaller than that of soa_rdata.
begin_soa_rdata = Rdata(RRType.SOA(), TEST_RRCLASS,
'master.example.com. admin.example.com ' +
'master.example.com. admin.example.com. ' +
'1230 3600 1800 2419200 7200')
begin_soa_rrset = RRset(TEST_ZONE_NAME, TEST_RRCLASS, RRType.SOA(), RRTTL(3600))
begin_soa_rrset.add_rdata(begin_soa_rdata)
......
......@@ -249,7 +249,7 @@ class TestXfroutSessionBase(unittest.TestCase):
# In the RDATA only the serial matters.
for i in range(0, num_soa):
soa.add_rdata(Rdata(RRType.SOA(), soa_class,
'm r ' + str(ixfr) + ' 1 1 1 1'))
'm. r. ' + str(ixfr) + ' 1 1 1 1'))
msg.add_rrset(Message.SECTION_AUTHORITY, soa)
renderer = MessageRenderer()
......
......@@ -71,20 +71,20 @@ struct TestRdata {
// unusual and corner cases).
const TestRdata test_rdata_list[] = {
{"IN", "A", "192.0.2.1", 0},
{"IN", "NS", "ns.example.com", 0},
{"IN", "CNAME", "cname.example.com", 0},
{"IN", "SOA", "ns.example.com root.example.com 0 0 0 0 0", 0},
{"IN", "PTR", "reverse.example.com", 0},
{"IN", "NS", "ns.example.com.", 0},
{"IN", "CNAME", "cname.example.com.", 0},
{"IN", "SOA", "ns.example.com. root.example.com. 0 0 0 0 0", 0},
{"IN", "PTR", "reverse.example.com.", 0},
{"IN", "HINFO", "\"cpu-info\" \"OS-info\"", 1},
{"IN", "MINFO", "root.example.com mbox.example.com", 0},
{"IN", "MX", "10 mx.example.com", 0},
{"IN", "MINFO", "root.example.com. mbox.example.com.", 0},
{"IN", "MX", "10 mx.example.com.", 0},
{"IN", "TXT", "\"test1\" \"test 2\"", 1},
{"IN", "RP", "root.example.com. rp-text.example.com", 0},
{"IN", "AFSDB", "1 afsdb.example.com", 0},
{"IN", "RP", "root.example.com. rp-text.example.com.", 0},
{"IN", "AFSDB", "1 afsdb.example.com.", 0},
{"IN", "AAAA", "2001:db8::1", 0},
{"IN", "SRV", "1 0 10 target.example.com", 0},
{"IN", "NAPTR", "100 50 \"s\" \"http\" \"\" _http._tcp.example.com", 1},
{"IN", "DNAME", "dname.example.com", 0},
{"IN", "SRV", "1 0 10 target.example.com.", 0},
{"IN", "NAPTR", "100 50 \"s\" \"http\" \"\" _http._tcp.example.com.", 1},
{"IN", "DNAME", "dname.example.com.", 0},
{"IN", "DS", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
{"IN", "SSHFP", "1 1 dd465c09cfa51fb45020cc83316fff", 1},
// We handle RRSIG separately, so it's excluded from the list
......@@ -98,7 +98,7 @@ const TestRdata test_rdata_list[] = {
{"IN", "TYPE65000", "\\# 3 010203", 1}, // some "custom" type
{"IN", "TYPE65535", "\\# 0", 1}, // max RR type, 0-length RDATA
{"CH", "A", "\\# 2 0102", 1}, // A RR for non-IN class; varlen data
{"CH", "NS", "ns.example.com", 0}, // class CH, generic data
{"CH", "NS", "ns.example.com.", 0}, // class CH, generic data
{"CH", "TXT", "BIND10", 1}, // ditto
{"HS", "A", "\\# 5 0102030405", 1}, // A RR for non-IN class; varlen data
{NULL, NULL, NULL, 0}
......
......@@ -23,6 +23,7 @@ EXTRA_DIST += rdata/generic/cname_5.cc
EXTRA_DIST += rdata/generic/cname_5.h
EXTRA_DIST += rdata/generic/detail/char_string.cc
EXTRA_DIST += rdata/generic/detail/char_string.h
EXTRA_DIST += rdata/generic/detail/lexer_util.h
EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc
EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h
EXTRA_DIST += rdata/generic/detail/nsec3param_common.cc
......
......@@ -32,8 +32,8 @@ import sys
#
# Example:
# new_rdata_factory_users = [('a', 'in'), ('a', 'ch'), ('soa', 'generic')]
new_rdata_factory_users = [('aaaa', 'in'), ('txt', 'generic'),
('spf', 'generic')]
new_rdata_factory_users = [('soa', 'generic'), ('txt', 'generic'),
('aaaa', 'in'), ('spf', 'generic')]
re_typecode = re.compile('([\da-z]+)_(\d+)')
classcode2txt = {}
......
// Copyright (C) 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
// 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.
#ifndef DNS_RDATA_LEXER_UTIL_H
#define DNS_RDATA_LEXER_UTIL_H 1
#include <dns/name.h>
#include <dns/master_lexer.h>
/// \file lexer_util.h
/// \brief Utilities for extracting RDATA fields from lexer.
///
/// This file intends to define convenient small routines that can be
/// commonly used in the RDATA implementation to build RDATA fields from
/// a \c MasterLexer.
namespace isc {
namespace dns {
namespace rdata {
namespace generic {
namespace detail {
/// \brief Construct a Name object using a master lexer and optional origin.
///
/// This is a convenient shortcut of commonly used code pattern that would
/// be used to build RDATA that contain a domain name field.
///
/// Note that this function throws an exception against invalid input.
/// The (direct or indirect) caller's responsibility needs to expect and
/// handle exceptions appropriately.
///
/// \throw MasterLexer::LexerError The next token from lexer is not string.
/// \throw Other Exceptions from the \c Name class constructor if the next
/// string token from the lexer does not represent a valid name.
///
/// \param lexer A \c MasterLexer object. Its next token is expected to be
/// a string that represent a domain name.
/// \param origin If non NULL, specifies the origin of the name to be
/// constructed.
///
/// \return A new Name object that corresponds to the next string token of
/// the \c lexer.
inline Name
createNameFromLexer(MasterLexer& lexer, const Name* origin) {
const MasterToken::StringRegion& str_region =
lexer.getNextToken(MasterToken::STRING).getStringRegion();
return (Name(str_region.beg, str_region.len, origin));
}
} // namespace detail
} // namespace generic
} // namespace rdata
} // namespace dns
} // namespace isc
#endif // DNS_RDATA_LEXER_UTIL_H
// Local Variables:
// mode: c++
// End:
......@@ -14,22 +14,29 @@
#include <config.h>
#include <string>
#include <boost/static_assert.hpp>
#include <boost/lexical_cast.hpp>
#include <exceptions/exceptions.h>
#include <util/buffer.h>
#include <dns/name.h>
#include <dns/master_lexer.h>
#include <dns/master_loader.h>
#include <dns/master_loader_callbacks.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rdata/generic/detail/lexer_util.h>
#include <boost/static_assert.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
#include <sstream>
using namespace std;
using boost::lexical_cast;
using namespace isc::util;
using isc::dns::rdata::generic::detail::createNameFromLexer;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
......@@ -42,35 +49,85 @@ SOA::SOA(InputBuffer& buffer, size_t) :
buffer.readData(numdata_, sizeof(numdata_));
}
namespace {
void
fillParameters(MasterLexer& lexer, uint8_t numdata[20]) {
// Copy serial, refresh, retry, expire, minimum. We accept the extended
// TTL-compatible style for the latter four.
OutputBuffer buffer(20);
buffer.writeUint32(lexer.getNextToken(MasterToken::NUMBER).getNumber());
for (int i = 0; i < 4; ++i) {
buffer.writeUint32(RRTTL(lexer.getNextToken(MasterToken::STRING).
getString()).getValue());
}
memcpy(numdata, buffer.getData(), buffer.getLength());
}
}
/// \brief Constructor from string.
///
/// The given string must represent a valid SOA 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 MNAME and RNAME must be absolute since there's no parameter that
/// specifies the origin name; if these are not absolute, \c MissingNameOrigin
/// exception will be thrown. These must not be represented as a quoted
/// string.
///
/// See the construction that takes \c MasterLexer for other fields.
///
/// \throw Others Exception from the Name and RRTTL constructors.
/// \throw InvalidRdataText Other general syntax errors.
SOA::SOA(const std::string& soastr) :
mname_("."), rname_(".") // quick hack workaround
// Fill in dummy name and replace them soon below.
mname_(Name::ROOT_NAME()), rname_(Name::ROOT_NAME())
{
istringstream iss(soastr);
string token;
iss >> token;
if (iss.bad() || iss.fail()) {
isc_throw(InvalidRdataText, "Invalid SOA MNAME");
try {
std::istringstream ss(soastr);
MasterLexer lexer;
lexer.pushSource(ss);
mname_ = createNameFromLexer(lexer, NULL);
rname_ = createNameFromLexer(lexer, NULL);
fillParameters(lexer, numdata_);
if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
isc_throw(InvalidRdataText, "extra input text for SOA: "
<< soastr);
}
} catch (const MasterLexer::LexerError& ex) {
isc_throw(InvalidRdataText, "Failed to construct SOA from '" <<
soastr << "': " << ex.what());
}
mname_ = Name(token);
iss >> token;
if (iss.bad() || iss.fail()) {
isc_throw(InvalidRdataText, "Invalid SOA RNAME");
}
rname_ = Name(token);
}
uint32_t serial, refresh, retry, expire, minimum;
iss >> serial >> refresh >> retry >> expire >> minimum;
if (iss.rdstate() != ios::eofbit) {
isc_throw(InvalidRdataText, "Invalid SOA format");
}
OutputBuffer buffer(20);
buffer.writeUint32(serial);
buffer.writeUint32(refresh);
buffer.writeUint32(retry);
buffer.writeUint32(expire);
buffer.writeUint32(minimum);
memcpy(numdata_, buffer.getData(), buffer.getLength());
/// \brief Constructor with a context of MasterLexer.
///
/// The \c lexer should point to the beginning of valid textual representation
/// of an SOA RDATA. The MNAME and RNAME fields can be non absolute if
/// \c origin is non NULL, in which case \c origin is used to make them
/// absolute. These must not be represented as a quoted string.
///
/// The REFRESH, RETRY, EXPIRE, and MINIMUM fields can be either a valid
/// decimal representation of an unsigned 32-bit integer or other
/// valid textual representation of \c RRTTL such as "1H" (which means 3600).
///
/// \throw MasterLexer::LexerError General parsing error such as missing field.
/// \throw Other Exceptions from the Name and RRTTL 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 MNAME and RNAME when
/// they are non absolute.
SOA::SOA(MasterLexer& lexer, const Name* origin,
MasterLoader::Options, MasterLoaderCallbacks&) :
mname_(createNameFromLexer(lexer, origin)),
rname_(createNameFromLexer(lexer, origin))
{
fillParameters(lexer, numdata_);
}
SOA::SOA(const Name& mname, const Name& rname, uint32_t serial,
......
......@@ -33,15 +33,129 @@ using namespace isc::dns::rdata;
namespace {
class Rdata_SOA_Test : public RdataTest {
protected:
Rdata_SOA_Test() : rdata_soa(Name("ns.example.com"),
Name("root.example.com"),
2010012601, 3600, 300, 3600000, 1200)
Rdata_SOA_Test() :
rdata_soa(Name("ns.example.com"),
Name("root.example.com"),
2010012601, 3600, 300, 3600000, 1200)
{}
// Common check to see if the given text can be used to construct SOA
// Rdata that is identical rdata_soa.
void checkFromText(const char* soa_txt, const Name* origin = NULL) {
std::stringstream ss(soa_txt);
MasterLexer lexer;
lexer.pushSource(ss);
if (origin == NULL) {
// from-string constructor works correctly only when origin
// is NULL (by its nature).
EXPECT_EQ(0, generic::SOA(soa_txt).compare(rdata_soa));
}
EXPECT_EQ(0, generic::SOA(lexer, origin, MasterLoader::DEFAULT,
loader_cb).compare(rdata_soa));
}
// Common check if given text (which is invalid as SOA RDATA) is rejected
// with the specified type of exception: ExForString is the expected
// exception for the "from string" constructor; ExForLexer is for the
// constructor with master lexer.
template <typename ExForString, typename ExForLexer>
void checkFromBadTexxt(const char* soa_txt, const Name* origin = NULL) {
EXPECT_THROW(generic::SOA soa(soa_txt), ExForString);
std::stringstream ss(soa_txt);
MasterLexer lexer;
lexer.pushSource(ss);
EXPECT_THROW(generic::SOA soa(lexer, origin, MasterLoader::DEFAULT,
loader_cb), ExForLexer);
}
const generic::SOA rdata_soa;
};
TEST_F(Rdata_SOA_Test, createFromText) {
//TBD
// A simple case.
checkFromText("ns.example.com. root.example.com. "
"2010012601 3600 300 3600000 1200");
// Beginning and trailing space are ignored.
checkFromText(" ns.example.com. root.example.com. "
"2010012601 3600 300 3600000 1200 ");
// using extended TTL-like form for some parameters.
checkFromText("ns.example.com. root.example.com. "
"2010012601 1H 5M 1000H 20M");
// multi-line.
checkFromText("ns.example.com. (root.example.com.\n"
"2010012601 1H 5M 1000H) 20M");
// relative names for MNAME and RNAME with a separate origin (lexer
// version only)
const Name origin("example.com");
checkFromText("ns root 2010012601 1H 5M 1000H 20M", &origin);
// with the '@' notation with a separate origin (lexer version only)
const Name full_mname("ns.example.com");
checkFromText("@ root.example.com. 2010012601 1H 5M 1000H 20M",
&full_mname);
// bad MNAME/RNAMEs
checkFromBadTexxt<EmptyLabel, EmptyLabel>(
"bad..example. . 2010012601 1H 5M 1000H 20M");
checkFromBadTexxt<EmptyLabel, EmptyLabel>(
". bad..example. 2010012601 1H 5M 1000H 20M");
// Names shouldn't be quoted. (Note: on completion of #2534, the resulting
// exception will be different).
checkFromBadTexxt<MissingNameOrigin, MissingNameOrigin>(
"\".\" . 0 0 0 0 0");
checkFromBadTexxt<MissingNameOrigin, MissingNameOrigin>(
". \".\" 0 0 0 0 0");
// Missing MAME or RNAME: for the string version, the serial would be
// tried as RNAME and result in "not absolute". For the lexer version,
// it reaches the end-of-line, missing min TTL.
checkFromBadTexxt<MissingNameOrigin, MasterLexer::LexerError>(
". 2010012601 0 0 0 0", &Name::ROOT_NAME());
// bad serial. the string version converts lexer error to
// InvalidRdataText.
checkFromBadTexxt<InvalidRdataText, MasterLexer::LexerError>(
". . bad 0 0 0 0");
// bad serial; exceeding the uint32_t range (4294967296 = 2^32)
checkFromBadTexxt<InvalidRdataText, MasterLexer::LexerError>(
". . 4294967296 0 0 0 0");
// Bad format for other numeric parameters. These will be tried as a TTL,
// and result in an exception there.
checkFromBadTexxt<InvalidRRTTL, InvalidRRTTL>(". . 2010012601 bad 0 0 0");
checkFromBadTexxt<InvalidRRTTL, InvalidRRTTL>(
". . 2010012601 4294967296 0 0 0");
checkFromBadTexxt<InvalidRRTTL, InvalidRRTTL>(". . 2010012601 0 bad 0 0");
checkFromBadTexxt<InvalidRRTTL, InvalidRRTTL>(
". . 2010012601 0 4294967296 0 0");
checkFromBadTexxt<InvalidRRTTL, InvalidRRTTL>(". . 2010012601 0 0 bad 0");
checkFromBadTexxt<InvalidRRTTL, InvalidRRTTL>(
". . 2010012601 0 0 4294967296 0");
checkFromBadTexxt<InvalidRRTTL, InvalidRRTTL>(". . 2010012601 0 0 0 bad");
checkFromBadTexxt<InvalidRRTTL, InvalidRRTTL>(
". . 2010012601 0 0 0 4294967296");
// No space between RNAME and serial. This case is the same as missing
// M/RNAME.
checkFromBadTexxt<MissingNameOrigin, MasterLexer::LexerError>(
". example.0 0 0 0 0", &Name::ROOT_NAME());
// Extra parameter. string version immediately detects the error.
EXPECT_THROW(generic::SOA soa(". . 0 0 0 0 0 extra"), InvalidRdataText);
// Likewise. Redundant newline is also considered an error.
EXPECT_THROW(generic::SOA soa(". . 0 0 0 0 0\n"), InvalidRdataText);
EXPECT_THROW(generic::SOA soa("\n. . 0 0 0 0 0"), InvalidRdataText);
// lexer version defers the check to the upper layer (we pass origin
// to skip the check with the string version).
checkFromText("ns root 2010012601 1H 5M 1000H 20M extra", &origin);
}
TEST_F(Rdata_SOA_Test, createFromWire) {
......@@ -97,4 +211,42 @@ TEST_F(Rdata_SOA_Test, getMinimum) {
0, 0, 0, 0, 0x80706050).getMinimum());
}
void
compareCheck(const generic::SOA& small, const generic::SOA& large) {
EXPECT_GT(0, small.compare(large));
EXPECT_LT(0, large.compare(small));
}
TEST_F(Rdata_SOA_Test, compare) {
// Check simple equivalence
EXPECT_EQ(0, rdata_soa.compare(generic::SOA(
"ns.example.com. root.example.com. "
"2010012601 3600 300 3600000 1200")));
// Check name comparison is case insensitive
EXPECT_EQ(0, rdata_soa.compare(generic::SOA(
"NS.example.com. root.EXAMPLE.com. "
"2010012601 3600 300 3600000 1200")));
// Check names are compared in the RDATA comparison semantics (different
// from DNSSEC ordering for owner names)
compareCheck(generic::SOA("a.example. . 0 0 0 0 0"),
generic::SOA("example. . 0 0 0 0 0"));
compareCheck(generic::SOA(". a.example. 0 0 0 0 0"),
generic::SOA(". example. 0 0 0 0 0"));
// Compare other numeric fields: 1076895760 = 0x40302010,
// 270544960 = 0x10203040. These are chosen to make sure that machine
// endian doesn't confuse the comparison results.
compareCheck(generic::SOA(". . 270544960 0 0 0 0"),
generic::SOA(". . 1076895760 0 0 0 0"));
compareCheck(generic::SOA(". . 0 270544960 0 0 0"),
generic::SOA(". . 0 1076895760 0 0 0"));
compareCheck(generic::SOA(". . 0 0 270544960 0 0"),
generic::SOA(". . 0 0 1076895760 0 0"));
compareCheck(generic::SOA(". . 0 0 0 270544960 0"),
generic::SOA(". . 0 0 0 1076895760 0"));
compareCheck(generic::SOA(". . 0 0 0 0 270544960"),
generic::SOA(". . 0 0 0 0 1076895760"));
}
}
......@@ -57,7 +57,6 @@ template<class TXT_LIKE>
class Rdata_TXT_LIKE_Test : public RdataTest {
protected:
Rdata_TXT_LIKE_Test() :
loader_cb(MasterLoaderCallbacks::getNullCallbacks()),
wiredata_longesttxt(256, 'a'),
rdata_txt_like("Test-String"),
rdata_txt_like_empty("\"\""),
......@@ -67,7 +66,6 @@ protected:
}
protected:
MasterLoaderCallbacks loader_cb;
vector<uint8_t> wiredata_longesttxt;
const TXT_LIKE rdata_txt_like;
const TXT_LIKE rdata_txt_like_empty;
......
......@@ -41,7 +41,8 @@ namespace isc {
namespace dns {
namespace rdata {
RdataTest::RdataTest() :
obuffer(0), rdata_nomatch(createRdata(RRType(0), RRClass(1), "\\# 0"))
obuffer(0), rdata_nomatch(createRdata(RRType(0), RRClass(1), "\\# 0")),
loader_cb(MasterLoaderCallbacks::getNullCallbacks())
{}
RdataPtr
......
......@@ -42,6 +42,7 @@ protected:
/// used to test the compare() method against a well-known RR type.
RdataPtr rdata_nomatch;
MasterLexer lexer;
MasterLoaderCallbacks loader_cb;
};
namespace test {
......
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