// For fixed-size data type such as boolean, integer, even
// IP address we can use the utility function to get the required
// buffer size.
- int data_size = OptionDataTypeUtil::getDataTypeLen(*field);
-
- // For variable size types (such as string) the function above
- // will return 0 so we need to do a runtime check. Since variable
- // length data fields may be laid only at the end of an option we
- // consume the rest of this option. Note that validate() function
- // in OptionDefinition object should have checked whether the
- // data fields layout is correct (that the variable string fields
- // are laid at the end).
+ size_t data_size = OptionDataTypeUtil::getDataTypeLen(*field);
+
+ // For variable size types (e.g. string) the function above will
+ // return 0 so we need to do a runtime check of the length.
if (data_size == 0) {
- data_size = std::distance(data, data_.end());
+ // FQDN is a special data type as it stores variable length data
+ // but the data length is encoded in the buffer. The easiest way
+ // to obtain the length of the data is to read the FQDN. The
+ // utility function will return the size of the buffer on success.
+ if (*field == OPT_FQDN_TYPE) {
+ OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_.end()),
+ data_size);
+ } else {
+ // In other case we are dealing with string or binary value
+ // which size can't be determined. Thus we consume the
+ // remaining part of the buffer for it. Note that variable
+ // size data can be laid at the end of the option only and
+ // that the validate() function in OptionDefinition object
+ // should have checked wheter it is a case for this option.
+ data_size = std::distance(data, data_.end());
+ }
if (data_size == 0) {
// If we reached the end of buffer we assume that this option is
// truncated because there is no remaining data to initialize
// data fields of the same type. The type of those fields
// is held in the data_type variable so let's use it to determine
// a size of buffers.
- int data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
+ size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
// The check below will fail if the input buffer is too short
// for the data size being held by this option.
// Note that data_size returned by getDataTypeLen may be zero
// For an array of values we are taking different path because
// we have to handle multiple buffers.
if (definition_.getArrayType()) {
- // We don't perform other checks for data types that can't be
- // used together with array indicator such as strings, empty field
- // etc. This is because OptionDefinition::validate function should
- // have checked this already. Thus data_size must be greater than
- // zero.
- assert(data_size > 0);
- // Get equal chunks of data and store as collection of buffers.
- // Truncate any remaining part which length is not divisible by
- // data_size. Note that it is ok to truncate the data if and only
- // if the data buffer is long enough to keep at least one value.
- // This has been checked above already.
- do {
+ while (data != data_.end()) {
+ // FQDN is a special case because it is of a variable length.
+ // The actual length for a particular FQDN is encoded within
+ // a buffer so we have to actually read the FQDN from a buffer
+ // to get it.
+ if (data_type == OPT_FQDN_TYPE) {
+ OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_.end()),
+ data_size);
+ }
+ // We don't perform other checks for data types that can't be
+ // used together with array indicator such as strings, empty field
+ // etc. This is because OptionDefinition::validate function should
+ // have checked this already. Thus data_size must be greater than
+ // zero.
+ assert(data_size > 0);
+ // Get chunks of data and store as a collection of buffers.
+ // Truncate any remaining part which length is not divisible by
+ // data_size. Note that it is ok to truncate the data if and only
+ // if the data buffer is long enough to keep at least one value.
+ // This has been checked above already.
+ if (std::distance(data, data_.end()) < data_size) {
+ break;
+ }
buffers.push_back(OptionBuffer(data, data + data_size));
data += data_size;
- } while (std::distance(data, data_.end()) >= data_size);
+ }
} else {
// For non-arrays the data_size can be zero because
// getDataTypeLen returns zero for variable size data types
// such as strings. Simply take whole buffer.
if (data_size == 0) {
- data_size = std::distance(data, data_.end());
+ // For FQDN we get the size by actually reading the FQDN.
+ if (data_type == OPT_FQDN_TYPE) {
+ OptionDataTypeUtil::readFqdn(OptionBuffer(data, data_.end()),
+ data_size);
+ } else {
+ data_size = std::distance(data, data_.end());
+ }
}
if (data_size > 0) {
buffers.push_back(OptionBuffer(data, data + data_size));
case OPT_IPV6_ADDRESS_TYPE:
text << readAddress(index).toText();
break;
+ case OPT_FQDN_TYPE:
+ text << readFqdn(index);
+ break;
case OPT_STRING_TYPE:
text << readString(index);
break;
return (OptionDataTypeUtil::readBool(buffers_[index]));
}
+std::string
+OptionCustom::readFqdn(const uint32_t index) const {
+ checkIndex(index);
+ try {
+ size_t len = 0;
+ return (OptionDataTypeUtil::readFqdn(buffers_[index], len));
+ } catch (const Exception& ex) {
+ isc_throw(BadDataTypeCast, ex.what());
+ }
+}
+
std::string
OptionCustom::readString(const uint32_t index) const {
checkIndex(index);
);
}
+// The purpose of this test is to verify that the data from a buffer
+// can be read as FQDN.
+TEST_F(OptionCustomTest, fqdnData) {
+ OptionDefinition opt_def("OPTION_FOO", 1000, "fqdn");
+
+ const char data[] = {
+ 8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
+ 7, 101, 120, 97, 109, 112, 108, 101, // "example"
+ 3, 99, 111, 109, // "com"
+ 0,
+ };
+
+ std::vector<uint8_t> buf(data, data + sizeof(data));
+
+ boost::scoped_ptr<OptionCustom> option;
+ ASSERT_NO_THROW(
+ option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
+ );
+ ASSERT_TRUE(option);
+
+ ASSERT_EQ(1, option->getDataFieldsNum());
+
+ std::string domain0 = option->readFqdn(0);
+ EXPECT_EQ("mydomain.example.com.", domain0);
+
+ // Check that the option with truncated data can't be created.
+ EXPECT_THROW(
+ option.reset(new OptionCustom(opt_def, Option::V6,
+ buf.begin(), buf.begin() + 4)),
+ isc::dhcp::BadDataTypeCast
+ );
+}
+
// The purpose of this test is to verify that the option definition comprising
// 16-bit signed integer value can be used to create an instance of custom option.
TEST_F(OptionCustomTest, int16Data) {
);
}
+
// The purpose of this test is to verify that the option definition comprising
// string value can be used to create an instance of custom option.
TEST_F(OptionCustomTest, stringData) {
);
}
+// The purpose of this test is to verify that the option comprising
+// an array of FQDN values can be created from a buffer which holds
+// multiple FQDN values encoded as described in the RFC1035, section
+// 3.1
+TEST_F(OptionCustomTest, fqdnDataArray) {
+ OptionDefinition opt_def("OPTION_FOO", 1000, "fqdn", true);
+
+ const char data[] = {
+ 8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
+ 7, 101, 120, 97, 109, 112, 108, 101, // "example"
+ 3, 99, 111, 109, // "com"
+ 0,
+ 7, 101, 120, 97, 109, 112, 108, 101, // "example"
+ 3, 99, 111, 109, // "com"
+ 0
+ };
+
+ // Create a buffer that holds two FQDNs.
+ std::vector<uint8_t> buf(data, data + sizeof(data));
+
+ // Create an option from using a buffer.
+ boost::scoped_ptr<OptionCustom> option;
+ ASSERT_NO_THROW(
+ option.reset(new OptionCustom(opt_def, Option::V6, buf));
+ );
+ ASSERT_TRUE(option);
+
+ // We expect that two FQDN values have been extracted
+ // from a buffer.
+ ASSERT_EQ(2, option->getDataFieldsNum());
+
+ // Validate both values.
+ std::string domain0 = option->readFqdn(0);
+ EXPECT_EQ("mydomain.example.com.", domain0);
+
+ std::string domain1 = option->readFqdn(1);
+ EXPECT_EQ("example.com.", domain1);
+}
+
// The purpose of this test is to verify that the option definition comprising
// a record of various data fields can be used to create an instance of
// custom option.
OptionDefinition opt_def("OPTION_FOO", 1000, "record");
ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
ASSERT_NO_THROW(opt_def.addRecordField("boolean"));
+ ASSERT_NO_THROW(opt_def.addRecordField("fqdn"));
ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
ASSERT_NO_THROW(opt_def.addRecordField("string"));
+ const char fqdn_data[] = {
+ 8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
+ 7, 101, 120, 97, 109, 112, 108, 101, // "example"
+ 3, 99, 111, 109, // "com"
+ 0,
+ };
+
OptionBuffer buf;
// Initialize field 0.
writeInt<uint16_t>(8712, buf);
// Initialize field 1 to 'true'
buf.push_back(static_cast<unsigned short>(1));
- // Initialize field 2 to IPv4 address.
+ // Initialize field 2.
+ buf.insert(buf.end(), fqdn_data, fqdn_data + sizeof(fqdn_data));
+ // Initialize field 3 to IPv4 address.
writeAddress(IOAddress("192.168.0.1"), buf);
- // Initialize field 3 to IPv6 address.
+ // Initialize field 4 to IPv6 address.
writeAddress(IOAddress("2001:db8:1::1"), buf);
- // Initialize field 4 to string value.
+ // Initialize field 5 to string value.
writeString("ABCD", buf);
boost::scoped_ptr<OptionCustom> option;
+ try {
+ option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
+ } catch (const Exception& ex) {
+ std::cout << ex.what() << std::endl;
+ }
+
ASSERT_NO_THROW(
option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
);
ASSERT_TRUE(option);
// We should have 5 data fields.
- ASSERT_EQ(5, option->getDataFieldsNum());
+ ASSERT_EQ(6, option->getDataFieldsNum());
// Verify value in the field 0.
uint16_t value0 = 0;
EXPECT_TRUE(value1);
// Verify value in the field 2.
- IOAddress value2("127.0.0.1");
- ASSERT_NO_THROW(value2 = option->readAddress(2));
- EXPECT_EQ("192.168.0.1", value2.toText());
+ std::string value2 = "";
+ ASSERT_NO_THROW(value2 = option->readFqdn(2));
+ EXPECT_EQ("mydomain.example.com.", value2);
// Verify value in the field 3.
- IOAddress value3("::1");
+ IOAddress value3("127.0.0.1");
ASSERT_NO_THROW(value3 = option->readAddress(3));
- EXPECT_EQ("2001:db8:1::1", value3.toText());
+ EXPECT_EQ("192.168.0.1", value3.toText());
// Verify value in the field 4.
- std::string value4;
- ASSERT_NO_THROW(value4 = option->readString(4));
- EXPECT_EQ("ABCD", value4);
+ IOAddress value4("::1");
+ ASSERT_NO_THROW(value4 = option->readAddress(4));
+ EXPECT_EQ("2001:db8:1::1", value4.toText());
+
+ // Verify value in the field 4.
+ std::string value5;
+ ASSERT_NO_THROW(value5 = option->readString(5));
+ EXPECT_EQ("ABCD", value5);
}
// The purpose of this test is to verify that truncated buffer
EXPECT_THROW(option->readInteger<uint32_t>(11), isc::OutOfRange);
}
-
-
} // anonymous namespace