void
OptionDnr6::packAddresses(util::OutputBuffer& buf) const {
- for (auto address = ipv6_addresses_.begin(); address != ipv6_addresses_.end(); ++address) {
- if (!address->isV6()) {
- isc_throw(isc::BadValue, address->toText() << " is not an IPv6 address");
+ for (const auto& address : ipv6_addresses_) {
+ if (!address.isV6()) {
+ isc_throw(isc::BadValue, address.toText() << " is not an IPv6 address");
}
- buf.writeData(&address->toBytes()[0], V6ADDRESS_LEN);
+ buf.writeData(&address.toBytes()[0], V6ADDRESS_LEN);
}
}
void
OptionDnr6::packSvcParams(util::OutputBuffer& buf) const {
if (svc_params_length_ > 0) {
- buf.writeData(&*svc_params_.begin(), svc_params_length_);
+ buf.writeData(&(*svc_params_.begin()), svc_params_length_);
}
}
}
setData(begin, end);
// First two octets of Option data is Service Priority - this is mandatory field.
- service_priority_ = isc::util::readUint16((&*begin), SERVICE_PRIORITY_SIZE);
+ service_priority_ = isc::util::readUint16(&(*begin), SERVICE_PRIORITY_SIZE);
begin += SERVICE_PRIORITY_SIZE;
// Next come two octets of ADN Length plus the ADN data itself (variable length).
}
// Let's try to extract ADN FQDN data.
- isc::util::InputBuffer name_buf(&adn_tuple.getData()[0], adn_length_);
+ isc::util::InputBuffer name_buf(&(*adn_tuple.getData().begin()), adn_length_);
try {
adn_.reset(new isc::dns::Name(name_buf, true));
} catch (const Exception& ex) {
"2 bytes long Addr Length field");
}
// Next come two octets of Addr Length.
- addr_length_ = isc::util::readUint16((&*begin), ADDR_LENGTH_SIZE);
+ addr_length_ = isc::util::readUint16(&(*begin), ADDR_LENGTH_SIZE);
begin += ADDR_LENGTH_SIZE;
// It MUST be a multiple of 16.
if ((addr_length_ % V6ADDRESS_LEN) != 0) {
isc_throw(OutOfRange, "DHCPv6 Encrypted DNS Option (" << type_ << ")"
<< " malformed: Addr Len=" << addr_length_
- << " is not divisible by 16.");
+ << " is not divisible by 16");
+ }
+
+ // As per draft-ietf-add-dnr 3.1.8:
+ // If additional data is supplied (i.e. not ADN only mode),
+ // the option includes at least one valid IP address.
+ if (addr_length_ == 0) {
+ isc_throw(OutOfRange, "DHCPv6 Encrypted DNS Option (" << type_ << ")"
+ << " malformed: Addr Len=" << addr_length_
+ << " is not greater than 0");
+ }
+
+ // Check if IPv6 Address(es) field is not truncated.
+ if (std::distance(begin, end) < addr_length_) {
+ isc_throw(OutOfRange, "DHCPv6 Encrypted DNS Option (" << type_ << ")"
+ << " malformed: Addr Len=" << addr_length_
+ << " but IPv6 address(es) are truncated to len="
+ << std::distance(begin, end));
}
// Let's unpack the ipv6-address(es).
begin += V6ADDRESS_LEN;
}
- // SvcParams (variable length) field is last
+ // SvcParams (variable length) field is last.
svc_params_length_ = std::distance(begin, end);
if (svc_params_length_ > 0) {
- // for now let's keep SvcParams as binary data in buffer
svc_params_.assign(begin, end);
+ checkSvcParams();
}
}
std::string
OptionDnr6::toText(int indent) const {
- return Option::toText(indent);
+ std::ostringstream stream;
+ std::string in(indent, ' '); // base indentation
+ stream << in << "type=" << type_ << "(V6_DNR), "
+ << "len=" << (len() - getHeaderLen()) << ", "
+ << "service_priority=" << service_priority_ << ", "
+ << "adn_length=" << adn_length_ << ", "
+ << "adn='" << getAdn() << "'";
+ if (!adn_only_mode_) {
+ stream << ", addr_length=" << addr_length_
+ << ", address(es):";
+ for (const auto& address : ipv6_addresses_) {
+ stream << " " << address.toText();
+ }
+
+ if (svc_params_length_ > 0) {
+ stream << ", svc_params='" << svc_params_ << "'";
+ }
+ }
+
+ return (stream.str());
}
uint16_t
return ("");
}
+void
+OptionDnr6::checkSvcParams() const {
+ // TODO: check SvcParams and throw in case something is wrong
+}
+
OptionDnr4::OptionDnr4() : Option(V4, DHO_V4_DNR) {
}
EXPECT_EQ(0x8001, option->getServicePriority());
EXPECT_EQ(20, option->getAdnLength());
EXPECT_EQ("myhost.example.com.", option->getAdn());
+
+ // This is ADN only mode, so Addr Length and SvcParams Length
+ // are both expected to be zero.
EXPECT_EQ(0, option->getAddrLength());
EXPECT_EQ(0, option->getSvcParamsLength());
+
+ // BTW let's check if len() works ok.
+ // expected len: 20 (FQDN) + 2 (ADN Len) + 2 (Service priority) + 4 (headers) = 28.
EXPECT_EQ(28, option->len());
+
+ // BTW let's check if toText() works ok.
+ // toText() len does not count in headers len.
+ EXPECT_EQ("type=144(V6_DNR), len=24, "
+ "service_priority=32769, adn_length=20, "
+ "adn='myhost.example.com.'", option->toText());
}
TEST(OptionDnr6Test, constructorDataTruncated) {
};
OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
- // Create option instance. Check that constructor doesn't throw.
+ // Create option instance. Check that constructor throws OutOfRange exception.
scoped_ptr<OptionDnr6> option;
- EXPECT_NO_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())));
- ASSERT_TRUE(option);
-
- // Check if member variables were correctly set by ctor.
- EXPECT_EQ(Option::V6, option->getUniverse());
- EXPECT_EQ(D6O_V6_DNR, option->getType());
-
- // Check if data was unpacked correctly from wire data.
- EXPECT_EQ(0x8001, option->getServicePriority());
- EXPECT_EQ(20, option->getAdnLength());
- EXPECT_EQ("myhost.example.com.", option->getAdn());
- EXPECT_EQ(0, option->getAddrLength());
- EXPECT_EQ(0, option->getSvcParamsLength());
- EXPECT_EQ(30, option->len());
+ EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), OutOfRange);
+ ASSERT_FALSE(option);
}
TEST(OptionDnr6Test, addrLenNot16Modulo) {
EXPECT_EQ("ff02::face:b00c", addresses[1].toText());
EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", addresses[2].toText());
EXPECT_EQ(0, option->getSvcParamsLength());
+
+ // BTW let's check if len() works ok.
+ // expected len: 20 (FQDN) + 2 (ADN Len) + 2 (Service priority) + 4 (headers) = 28
+ // + 48 (3 IP addresses) + 2 (Addr Len) = 78
EXPECT_EQ(78, option->len());
+
+ // BTW let's check if toText() works ok.
+ // toText() len does not count in headers len.
+ EXPECT_EQ("type=144(V6_DNR), len=74, "
+ "service_priority=32769, adn_length=20, "
+ "adn='myhost.example.com.', "
+ "addr_length=48, "
+ "address(es): 2001:db8:1::dead:beef "
+ "ff02::face:b00c "
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", option->toText());
+}
+
+TEST(OptionDnr6Test, truncatedIpV6Addresses) {
+ // Prepare data to decode
+ const uint8_t buf_data[] = {
+ 0x80, 0x01, // Service priority is 32769 dec
+ 0x00, 0x14, // ADN Length is 20 dec
+ 0x06, 0x4D, 0x79, 0x68, 0x6F, 0x73, 0x74, // FQDN: Myhost.
+ 0x07, 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // Example.
+ 0x03, 0x43, 0x6F, 0x6D, 0x00, // Com.
+ 0x00, 0x30, // Addr Len field value = 48 dec
+ 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef
+ 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
+ 0xff, 0x02, 0x00 // IPv6 address truncated
+ };
+
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+ // Create option instance. Check that constructor throws OutOfRange exception.
+ scoped_ptr<OptionDnr6> option;
+ EXPECT_THROW(option.reset(new OptionDnr6(buf.begin(), buf.end())), OutOfRange);
+ ASSERT_FALSE(option);
}
TEST(OptionDnr6Test, svcParamsIncluded) {
0x00, 0x10, // Addr Len field value = 48 dec
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, // 2001:db8:1::dead:beef
0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
- 0xff, 0xff // example SvcParams data
+ 'a', 'b', 'c' // example SvcParams data
};
OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
const OptionDnr6::AddressContainer& addresses = option->getAddresses();
EXPECT_EQ(1, addresses.size());
EXPECT_EQ("2001:db8:1::dead:beef", addresses[0].toText());
- EXPECT_EQ(2, option->getSvcParamsLength());
- EXPECT_EQ(48, option->len());
+ EXPECT_EQ(3, option->getSvcParamsLength());
+
+ // BTW let's check if len() works ok.
+ // expected len: 20 (FQDN) + 2 (ADN Len) + 2 (Service priority) + 4 (headers) = 28
+ // + 16 (IP address) + 2 (Addr Len) + 3 (SvcParams) = 49
+ EXPECT_EQ(49, option->len());
+
+ // BTW let's check if toText() works ok.
+ // toText() len does not count in headers len.
+ EXPECT_EQ("type=144(V6_DNR), len=45, "
+ "service_priority=32769, adn_length=20, "
+ "adn='myhost.example.com.', "
+ "addr_length=16, "
+ "address(es): 2001:db8:1::dead:beef, "
+ "svc_params='abc'", option->toText());
}
} // namespace
\ No newline at end of file