namespace memory {
namespace {
+/// Specification of a single RDATA field in terms of internal encoding.
struct RdataFieldSpec {
enum FieldType {
- FIXEDLEN_DATA = 0,
- VARLEN_DATA,
- DOMAIN_NAME
+ FIXEDLEN_DATA = 0, // fixed-length data field
+ VARLEN_DATA, // variable-length data field
+ DOMAIN_NAME // domain name
};
- FieldType type;
+ const FieldType type; // field type
+
+ // type specific data. We use a union so it'll be clear only one of them
+ // (determined by the type) is valid. Since we want to make it as
+ // lightweight as possible, we use a relatively lower-level primitives
+ // here.
union {
- uint16_t fixeddata_len;
- RdataNameAttributes name_attributes;
+ // The length of fixed-length data field. Only valid for FIXEDLEN_DATA
+ const uint16_t fixeddata_len;
+
+ // Attributes of the name. Only valid for DOMAIN_NAME.
+ const RdataNameAttributes name_attributes;
};
};
+/// Specification of RDATA in terms of internal encoding.
struct RdataEncodeSpec {
- uint16_t field_count;
- uint16_t name_count;
- uint16_t varlen_count;
- const RdataFieldSpec* fields;
+ const uint16_t field_count; // total number of fields (# of fields member)
+ const uint16_t name_count; // number of domain name fields
+ const uint16_t varlen_count; // number of variable-length data fields
+ const RdataFieldSpec* const fields; // list of field specs
};
+// These constants are convenient shortcut to initialize the name_attributes
+// member of RdataFieldSpec (since it's a union, we can only directly
+// initialize fixeddata_len member, so we need to convert it to its type).
+// These are essentially small integers, so the cast should be safe.
const uint16_t NAMEATTR_NOATTRIBUTE_INITIALIZER = static_cast<uint16_t>(0);
const uint16_t NAMEATTR_COMPRESSIBLE_INITIALIZER =
static_cast<uint16_t>(NAMEATTR_COMPRESSIBLE);
const uint16_t NAMEATTR_COMPADDITIONAL_INITIALIZER =
static_cast<uint16_t>(NAMEATTR_COMPRESSIBLE | NAMEATTR_ADDITIONAL);
-// TBD
+// Many types of RDATA can be treated as a single-field, variable length
+// field (in terms of our encoding). The following define such most general
+// form of field spec.
const RdataFieldSpec generic_data_fields[] = {
{RdataFieldSpec::VARLEN_DATA, {0}}
};
const uint16_t n_generic_data_fields =
sizeof(generic_data_fields) / sizeof(RdataFieldSpec);
-
-// Most general form of field spec: that consisting of single variable-len
-// data field.
const RdataEncodeSpec generic_data_spec = {
n_generic_data_fields, 0, 1, generic_data_fields
};
-// TBD
+// RDATA consist of a single IPv4 address field.
const RdataFieldSpec single_ipv4_fields[] = {
{RdataFieldSpec::FIXEDLEN_DATA, {sizeof(uint32_t)}}
};
const uint16_t n_ipv4_fields =
sizeof(single_ipv4_fields) / sizeof(RdataFieldSpec);
+// RDATA consist of a single IPv6 address field.
const RdataFieldSpec single_ipv6_fields[] = {
{RdataFieldSpec::FIXEDLEN_DATA, {16}} // 128bits = 16 bytes
};
const uint16_t n_ipv6_fields =
sizeof(single_ipv6_fields) / sizeof(RdataFieldSpec);
-// TBD
+// There are several RR types that consist of a single domain name.
const RdataFieldSpec single_noattr_name_fields[] = {
{RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_NOATTRIBUTE_INITIALIZER}}
};
const uint16_t n_double_name_fields =
sizeof(double_compressible_name_fields) / sizeof(RdataFieldSpec);
-
-// SOA specific: two compressible names + 20-byte data
+// SOA specific: two compressible names + 5*32-bit data
const RdataFieldSpec soa_fields[] = {
{RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_COMPRESSIBLE_INITIALIZER}},
{RdataFieldSpec::DOMAIN_NAME, {NAMEATTR_COMPRESSIBLE_INITIALIZER}},
};
const uint16_t n_nsec_fields = sizeof(nsec_fields) / sizeof(RdataFieldSpec);
-// Class IN encode specs
+// Class IN encode specs. This gives a shortcut to the encode spec for
+// some well-known types of RDATA specific to class IN (most of which are
+// generic and can be used for other classes). The array index is the
+// RR type code.
const RdataEncodeSpec encode_spec_list_in[] = {
generic_data_spec, // #0: (NONE)
{n_ipv4_fields, 0, 0, single_ipv4_fields}, // #1: A
// least for currently supported RR types.
};
+// # of entries in encode_spec_list_in
const size_t encode_spec_list_in_size =
sizeof(encode_spec_list_in) / sizeof(encode_spec_list_in[0]);
BOOST_STATIC_ASSERT(encode_spec_list_in_size == 48);
inline
const RdataEncodeSpec&
getRdataEncodeSpec(RRClass rrclass, RRType rrtype) {
+ // Special case: for classes other than IN, we treat RDATA of RR types
+ // that are class-IN specific as generic opaque data.
if (rrclass != RRClass::IN() &&
(rrtype == RRType::A() || rrtype == RRType::AAAA() ||
rrtype == RRType::SRV())) {
return (generic_data_spec);
}
+ // Otherwise, if the type is in the pre-defined range, we use the defined
+ // spec; otherwise we treat it as opaque data.
const uint16_t typecode = rrtype.getCode();
if (typecode < encode_spec_list_in_size) {
return (encode_spec_list_in[rrtype.getCode()]);
}
} // end of unnamed namespace
+namespace testing {
void
encodeRdata(const rdata::Rdata& rdata, RRClass rrclass, RRType rrtype,
vector<uint8_t>& data_result, vector<uint16_t>& len_result)
assert(name_count == encode_spec.name_count);
assert(varlen_count == encode_spec.varlen_count);
}
+} // namespace testing
} // namespace memory
} // namespace datasrc
namespace datasrc {
namespace memory {
+/// \brief Attributes of domain name fields of encoded RDATA.
+///
+/// The enum values define special traits of the name that can affect how
+/// it should be handled in rendering or query processing.
enum RdataNameAttributes {
- NAMEATTR_COMPRESSIBLE = 1,
- NAMEATTR_ADDITIONAL = (NAMEATTR_COMPRESSIBLE << 1)
+ NAMEATTR_COMPRESSIBLE = 1, ///< Name should be compressed when rendered
+ NAMEATTR_ADDITIONAL = (NAMEATTR_COMPRESSIBLE << 1) ///< Name requires
+ ///< Additional section
+ ///< handling
};
+// We use the following quick-hack version of encoder and "foreach"
+// operator until we implement the complete versions. The plan is to
+// update the test cases that use these functions with the complete
+// functions/classes, and then remove the entire namespace.
+namespace testing {
+// "Encode" given RDATA of given RR class and type.
+//
+// Fixed/variable-length data fields are encoded in their wire-format;
+// domain names are encoded in the form of:
+// - nlen: name data length (1 byte)
+// - olen: offset data length (1 byte)
+// - name data (nlen bytes)
+// - offset data (olen bytes)
+//
+// The encoded results are appended to data_result.
+// If the RDATA contain variable-length data fields, the lengths of the
+// these fields will be appended in len_result, in the order of appearance.
void encodeRdata(const dns::rdata::Rdata& rdata, dns::RRClass rrclass,
dns::RRType rrtype, std::vector<uint8_t>& data_result,
std::vector<uint16_t>& len_result);
+// Callbacks used in foreachRdataField.
typedef boost::function<void(const dns::LabelSequence&,
RdataNameAttributes)> NameCallback;
typedef boost::function<void(const uint8_t*, size_t)> DataCallback;
+// Iterate over each RDATA field (in terms of the internal encoding) stored
+// in encoded_data, and call the given callback for each data (for
+// domain name fields, name_callback will be called; for normal data fields
+// data_callback will be called). If the encoded data contain variable-length
+// data fields, varlen_list should store a sequence of their lengths, in the
+// of the appearance.
void foreachRdataField(dns::RRClass rrclass, dns::RRType rrtype,
const std::vector<uint8_t>& encoded_data,
const std::vector<uint16_t>& varlen_list,
NameCallback name_callback, DataCallback data_callback);
+}
} // namespace memory
} // namespace datasrc
using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::testing;
using isc::util::unittests::matchWireData;
using std::string;
using std::vector;
namespace {
+// This defines a tuple of test data used in test_rdata_list below.
struct TestRdata {
- const char* const rrclass;
- const char* const rrtype;
- const char* const rdata;
+ const char* const rrclass; // RR class, textual form
+ const char* const rrtype; // RR type, textual form
+ const char* const rdata; // textual RDATA
const size_t n_varlen_fields; // expected # of variable-len fields
};
+
+// This test data consist of all supported types of RDATA (+ some
+// unusual and corner cases). We'll construct corresponding Rdata
+// object from this, and compare its wire format data both generated
+// by normal libdns++ interface and via encoding conversion.
const TestRdata test_rdata_list[] = {
{"IN", "A", "192.0.2.1", 0},
{"IN", "NS", "ns.example.com", 0},
{NULL, NULL, NULL, 0}
};
+// The following two functions will be used to generate wire format data
+// from encoded representation of each RDATA.
void
renderNameField(MessageRenderer* renderer, bool additional_required,
const LabelSequence& labels, RdataNameAttributes attributes)
}
TEST(RdataFieldSpec, checkData) {
+ // These two names will be rendered before and after the test RDATA,
+ // to check in case the RDATA contain a domain name whether it's
+ // compressed or not correctly. The names in the RDATA should basically
+ // a subdomain of example.com, so it can be compressed due to dummy_name.
+ // Likewise, dummy_name2 should be able to be fully compressed due to
+ // the name in the RDATA.
const Name dummy_name("com");
const Name dummy_name2("example.com");
+
MessageRenderer expected_renderer, actual_renderer;
vector<uint8_t> encoded_data;
vector<uint16_t> varlen_list;
+ // The set of RR types that require additional section processing.
+ // We'll pass it to renderNameField to check the stored attribute matches
+ // our expectation.
std::set<RRType> need_additionals;
need_additionals.insert(RRType::NS());
need_additionals.insert(RRType::MX());