Commit f82a806d authored by Francis Dupont's avatar Francis Dupont
Browse files

[5087] Done: now DHCPv4 domain-search option takes a (possibly compressed) FQDN list

parent 4cbf341f
...@@ -1245,7 +1245,7 @@ It is merely echoed by the server ...@@ -1245,7 +1245,7 @@ It is merely echoed by the server
<row><entry>client-ndi</entry><entry>94</entry><entry>record (uint8, uint8, uint8)</entry><entry>false</entry><entry>false</entry></row> <row><entry>client-ndi</entry><entry>94</entry><entry>record (uint8, uint8, uint8)</entry><entry>false</entry><entry>false</entry></row>
<row><entry>uuid-guid</entry><entry>97</entry><entry>record (uint8, binary)</entry><entry>false</entry><entry>false</entry></row> <row><entry>uuid-guid</entry><entry>97</entry><entry>record (uint8, binary)</entry><entry>false</entry><entry>false</entry></row>
<row><entry>subnet-selection</entry><entry>118</entry><entry>ipv4-address</entry><entry>false</entry><entry>false</entry></row> <row><entry>subnet-selection</entry><entry>118</entry><entry>ipv4-address</entry><entry>false</entry><entry>false</entry></row>
<row><entry>domain-search</entry><entry>119</entry><entry>binary</entry><entry>false</entry><entry>false</entry></row> <row><entry>domain-search</entry><entry>119</entry><entry>fqdn</entry><entry>true</entry><entry>false</entry></row>
<row><entry>vivco-suboptions</entry><entry>124</entry><entry>binary</entry><entry>false</entry><entry>false</entry></row> <row><entry>vivco-suboptions</entry><entry>124</entry><entry>binary</entry><entry>false</entry><entry>false</entry></row>
<row><entry>vivso-suboptions</entry><entry>125</entry><entry>binary</entry><entry>false</entry><entry>false</entry></row> <row><entry>vivso-suboptions</entry><entry>125</entry><entry>binary</entry><entry>false</entry><entry>false</entry></row>
</tbody> </tbody>
......
...@@ -2930,6 +2930,22 @@ TEST_F(Dhcp4ParserTest, DISABLED_Uint32Parser) { ...@@ -2930,6 +2930,22 @@ TEST_F(Dhcp4ParserTest, DISABLED_Uint32Parser) {
EXPECT_TRUE(errorContainsPosition(status, "<string>")); EXPECT_TRUE(errorContainsPosition(status, "<string>"));
} }
// The goal of this test is to verify that the domain-search option
// can be set using domain names
TEST_F(Dhcp4ParserTest, domainSearchOption) {
// Create configuration.
std::map<std::string, std::string> params;
params["name"] = "domain-search";
params["space"] = DHCP4_OPTION_SPACE;
params["code"] = "119"; // DHO_DOMAIN_SEARCH
params["data"] = "mydomain.example.com, example.com";
params["csv-format"] = "true";
std::string config = createConfigWithOption(params);
EXPECT_TRUE(executeConfiguration(config, "parse configuration with a"
" domain-search option"));
}
// The goal of this test is to verify that the standard option can // The goal of this test is to verify that the standard option can
// be configured to encapsulate multiple other options. // be configured to encapsulate multiple other options.
TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) { TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#include <dhcp/option_vendor.h> #include <dhcp/option_vendor.h>
#include <dhcp/option_vendor_class.h> #include <dhcp/option_vendor_class.h>
#include <util/encode/hex.h> #include <util/encode/hex.h>
#include <dns/labelsequence.h>
#include <dns/name.h>
#include <util/strutil.h> #include <util/strutil.h>
#include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
...@@ -444,6 +446,11 @@ OptionDefinition::haveOpaqueDataTuplesFormat() const { ...@@ -444,6 +446,11 @@ OptionDefinition::haveOpaqueDataTuplesFormat() const {
return (haveType(OPT_TUPLE_TYPE) && getArrayType()); return (haveType(OPT_TUPLE_TYPE) && getArrayType());
} }
bool
OptionDefinition::haveCompressedFqdnListFormat() const {
return (haveType(OPT_FQDN_TYPE) && getArrayType());
}
bool bool
OptionDefinition::convertToBool(const std::string& value_str) const { OptionDefinition::convertToBool(const std::string& value_str) const {
// Case-insensitive check that the input is one of: "true" or "false". // Case-insensitive check that the input is one of: "true" or "false".
...@@ -773,6 +780,34 @@ OptionDefinition::factoryOpaqueDataTuples(Option::Universe u, ...@@ -773,6 +780,34 @@ OptionDefinition::factoryOpaqueDataTuples(Option::Universe u,
return (option); return (option);
} }
OptionPtr
OptionDefinition::factoryFqdnList(Option::Universe u,
OptionBufferConstIter begin,
OptionBufferConstIter end) const {
const std::vector<uint8_t> data(begin, end);
InputBuffer in_buf(static_cast<const void*>(&data[0]), data.size());
std::vector<uint8_t> out_buf;
out_buf.reserve(data.size());
while (in_buf.getPosition() < in_buf.getLength()) {
// Reuse readFqdn and writeFqdn code but on the whole buffer
// so the DNS name code handles compression for us.
try {
isc::dns::Name name(in_buf);
isc::dns::LabelSequence labels(name);
if (labels.getDataLength() > 0) {
size_t read_len = 0;
const uint8_t* label = labels.getData(&read_len);
out_buf.insert(out_buf.end(), label, label + read_len);
}
} catch (const isc::Exception& ex) {
isc_throw(InvalidOptionValue, ex.what());
}
}
return OptionPtr(new OptionCustom(*this, u,
out_buf.begin(), out_buf.end()));
}
OptionPtr OptionPtr
OptionDefinition::factorySpecialFormatOption(Option::Universe u, OptionDefinition::factorySpecialFormatOption(Option::Universe u,
OptionBufferConstIter begin, OptionBufferConstIter begin,
...@@ -820,6 +855,9 @@ OptionDefinition::factorySpecialFormatOption(Option::Universe u, ...@@ -820,6 +855,9 @@ OptionDefinition::factorySpecialFormatOption(Option::Universe u,
} else { } else {
if ((getCode() == DHO_FQDN) && haveFqdn4Format()) { if ((getCode() == DHO_FQDN) && haveFqdn4Format()) {
return (OptionPtr(new Option4ClientFqdn(begin, end))); return (OptionPtr(new Option4ClientFqdn(begin, end)));
} else if ((getCode() == DHO_DOMAIN_SEARCH) &&
haveCompressedFqdnListFormat()) {
return (factoryFqdnList(Option::V4, begin, end));
} else if ((getCode() == DHO_VIVCO_SUBOPTIONS) && } else if ((getCode() == DHO_VIVCO_SUBOPTIONS) &&
haveVendorClass4Format()) { haveVendorClass4Format()) {
// V-I Vendor Class (option code 124). // V-I Vendor Class (option code 124).
......
...@@ -374,6 +374,9 @@ public: ...@@ -374,6 +374,9 @@ public:
/// @return true if option has the format of OpaqueDataTuples type options. /// @return true if option has the format of OpaqueDataTuples type options.
bool haveOpaqueDataTuplesFormat() const; bool haveOpaqueDataTuplesFormat() const;
/// @brief Check if the option has format of CompressedFqdnList options.
bool haveCompressedFqdnListFormat() const;
/// @brief Option factory. /// @brief Option factory.
/// ///
/// This function creates an instance of DHCP option using /// This function creates an instance of DHCP option using
...@@ -578,6 +581,19 @@ public: ...@@ -578,6 +581,19 @@ public:
private: private:
/// @brief Factory function to create option with a compressed FQDN list.
///
/// @param u universe (V4 or V6).
/// @param type option type.
/// @param begin iterator pointing to the beginning of the buffer.
/// @param end iterator pointing to the end of the buffer.
///
/// @return instance of the DHCP option where FQDNs are uncompressed.
/// @throw InvalidOptionValue if data for the option is invalid.
OptionPtr factoryFqdnList(Option::Universe u,
OptionBufferConstIter begin,
OptionBufferConstIter end) const;
/// @brief Creates an instance of an option having special format. /// @brief Creates an instance of an option having special format.
/// ///
/// The option with special formats are encapsulated by the dedicated /// The option with special formats are encapsulated by the dedicated
......
...@@ -192,12 +192,7 @@ const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = { ...@@ -192,12 +192,7 @@ const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = {
{ "uuid-guid", DHO_UUID_GUID, OPT_RECORD_TYPE, false, RECORD_DEF(UUID_GUID_RECORDS), "" }, { "uuid-guid", DHO_UUID_GUID, OPT_RECORD_TYPE, false, RECORD_DEF(UUID_GUID_RECORDS), "" },
{ "subnet-selection", DHO_SUBNET_SELECTION, { "subnet-selection", DHO_SUBNET_SELECTION,
OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF, "" }, OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
// The following options need a special encoding of data { "domain-search", DHO_DOMAIN_SEARCH, OPT_FQDN_TYPE, true, NO_RECORD_DEF, "" },
// being carried by them. Therefore, there is no way they can
// be handled by OptionCustom. We may need to implement
// dedicated classes to handle them. Until that happens
// let's treat them as 'binary' options.
{ "domain-search", DHO_DOMAIN_SEARCH, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
{ "vivco-suboptions", DHO_VIVCO_SUBOPTIONS, OPT_RECORD_TYPE, { "vivco-suboptions", DHO_VIVCO_SUBOPTIONS, OPT_RECORD_TYPE,
false, RECORD_DEF(VIVCO_RECORDS), "" }, false, RECORD_DEF(VIVCO_RECORDS), "" },
// Vendor-Identifying Vendor Specific Information option payload begins with a // Vendor-Identifying Vendor Specific Information option payload begins with a
......
...@@ -1319,8 +1319,21 @@ TEST_F(LibDhcpTest, stdOptionDefs4) { ...@@ -1319,8 +1319,21 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
LibDhcpTest::testStdOptionDefs4(DHO_UUID_GUID, begin, begin + 17, LibDhcpTest::testStdOptionDefs4(DHO_UUID_GUID, begin, begin + 17,
typeid(OptionCustom)); typeid(OptionCustom));
LibDhcpTest::testStdOptionDefs4(DHO_DOMAIN_SEARCH, begin, end, // Prepare buffer holding an array of FQDNs.
typeid(Option)); 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,
7, 101, 120, 97, 109, 112, 108, 101, // "example"
3, 99, 111, 109, // "com"
0
};
// Initialize a vector with the FQDN data.
std::vector<uint8_t> fqdn_buf(fqdn_data, fqdn_data + sizeof(fqdn_data));
LibDhcpTest::testStdOptionDefs4(DHO_DOMAIN_SEARCH, fqdn_buf.begin(),
fqdn_buf.end(), typeid(OptionCustom));
// V-I Vendor option requires specially crafted data. // V-I Vendor option requires specially crafted data.
const char vivco_data[] = { const char vivco_data[] = {
...@@ -1654,6 +1667,85 @@ TEST_F(LibDhcpTest, getVendorOptionDefByName4) { ...@@ -1654,6 +1667,85 @@ TEST_F(LibDhcpTest, getVendorOptionDefByName4) {
} }
} }
// This test checks handling of compressed FQDN list.
TEST_F(LibDhcpTest, fqdnList) {
OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
DHO_DOMAIN_SEARCH);
ASSERT_TRUE(def);
// Prepare buffer holding an array of FQDNs.
const uint8_t fqdn[] = {
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,
3, 99, 111, 109, // "com"
0
};
// Initialize a vector with the FQDN data.
std::vector<uint8_t> fqdn_buf(fqdn, fqdn + sizeof(fqdn));
OptionPtr option;
ASSERT_NO_THROW(option = def->optionFactory(Option::V4,
DHO_DOMAIN_SEARCH,
fqdn_buf.begin(),
fqdn_buf.end()));
ASSERT_TRUE(option);
OptionCustomPtr names = boost::dynamic_pointer_cast<OptionCustom>(option);
ASSERT_TRUE(names);
EXPECT_EQ(sizeof(fqdn), names->len() - names->getHeaderLen());
ASSERT_EQ(3, names->getDataFieldsNum());
EXPECT_EQ("mydomain.example.com.", names->readFqdn(0));
EXPECT_EQ("example.com.", names->readFqdn(1));
EXPECT_EQ("com.", names->readFqdn(2));
LibDhcpTest::testStdOptionDefs4(DHO_DOMAIN_SEARCH, fqdn_buf.begin(),
fqdn_buf.end(), typeid(OptionCustom));
const uint8_t compressed[] = {
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,
192, 9, // pointer to example.com
192, 17 // pointer to com
};
std::vector<uint8_t> compressed_buf(compressed,
compressed + sizeof(compressed));
ASSERT_NO_THROW(option = def->optionFactory(Option::V4,
DHO_DOMAIN_SEARCH,
compressed_buf.begin(),
compressed_buf.end()));
ASSERT_TRUE(option);
names = boost::dynamic_pointer_cast<OptionCustom>(option);
ASSERT_TRUE(names);
EXPECT_EQ(sizeof(fqdn), names->len() - names->getHeaderLen());
ASSERT_EQ(3, names->getDataFieldsNum());
EXPECT_EQ("mydomain.example.com.", names->readFqdn(0));
EXPECT_EQ("example.com.", names->readFqdn(1));
EXPECT_EQ("com.", names->readFqdn(2));
const uint8_t bad[] = {
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,
192, 80, // too big/forward pointer
192, 11 // pointer to com
};
std::vector<uint8_t> bad_buf(bad, bad + sizeof(bad));
EXPECT_THROW(option = def->optionFactory(Option::V4,
DHO_DOMAIN_SEARCH,
bad_buf.begin(),
bad_buf.end()),
InvalidOptionValue);
}
// tests whether v6 vendor-class option can be parsed properly. // tests whether v6 vendor-class option can be parsed properly.
TEST_F(LibDhcpTest, vendorClass6) { TEST_F(LibDhcpTest, vendorClass6) {
......
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