Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ISC Open Source Projects
Kea
Commits
f82a806d
Commit
f82a806d
authored
Apr 18, 2017
by
Francis Dupont
Browse files
[5087] Done: now DHCPv4 domain-search option takes a (possibly compressed) FQDN list
parent
4cbf341f
Changes
6
Hide whitespace changes
Inline
Side-by-side
doc/guide/dhcp4-srv.xml
View file @
f82a806d
...
...
@@ -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>
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>
domain-search
</entry><entry>
119
</entry><entry>
binary
</entry><entry>
fals
e
</entry><entry>
false
</entry></row>
<row><entry>
domain-search
</entry><entry>
119
</entry><entry>
fqdn
</entry><entry>
tru
e
</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>
</tbody>
...
...
src/bin/dhcp4/tests/config_parser_unittest.cc
View file @
f82a806d
...
...
@@ -2930,6 +2930,22 @@ TEST_F(Dhcp4ParserTest, DISABLED_Uint32Parser) {
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
// be configured to encapsulate multiple other options.
TEST_F
(
Dhcp4ParserTest
,
stdOptionDataEncapsulate
)
{
...
...
@@ -4748,7 +4764,7 @@ TEST_F(Dhcp4ParserTest, invalidPoolRange) {
EXPECT_EQ
(
1
,
rcode
);
string
expected
=
"Failed to create pool defined by: "
"192.0.2.1-19.2.0.200 (<string>:6:26)"
;
"192.0.2.1-19.2.0.200 (<string>:6:26)"
;
EXPECT_EQ
(
expected
,
text
);
}
...
...
@@ -4778,9 +4794,9 @@ TEST_F(Dhcp4ParserTest, outsideSubnetPool) {
EXPECT_EQ
(
1
,
rcode
);
string
expected
=
"subnet configuration failed: "
"a pool of type V4, with the following address range: "
"192.0.2.1-192.0.2.100 does not match the prefix of a subnet: "
"10.0.2.0/24 to which it is being added (<string>:5:14)"
;
"a pool of type V4, with the following address range: "
"192.0.2.1-192.0.2.100 does not match the prefix of a subnet: "
"10.0.2.0/24 to which it is being added (<string>:5:14)"
;
EXPECT_EQ
(
expected
,
text
);
}
...
...
src/lib/dhcp/option_definition.cc
View file @
f82a806d
...
...
@@ -25,6 +25,8 @@
#include
<dhcp/option_vendor.h>
#include
<dhcp/option_vendor_class.h>
#include
<util/encode/hex.h>
#include
<dns/labelsequence.h>
#include
<dns/name.h>
#include
<util/strutil.h>
#include
<boost/algorithm/string/classification.hpp>
#include
<boost/algorithm/string/predicate.hpp>
...
...
@@ -444,6 +446,11 @@ OptionDefinition::haveOpaqueDataTuplesFormat() const {
return
(
haveType
(
OPT_TUPLE_TYPE
)
&&
getArrayType
());
}
bool
OptionDefinition
::
haveCompressedFqdnListFormat
()
const
{
return
(
haveType
(
OPT_FQDN_TYPE
)
&&
getArrayType
());
}
bool
OptionDefinition
::
convertToBool
(
const
std
::
string
&
value_str
)
const
{
// Case-insensitive check that the input is one of: "true" or "false".
...
...
@@ -773,6 +780,34 @@ OptionDefinition::factoryOpaqueDataTuples(Option::Universe u,
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
OptionDefinition
::
factorySpecialFormatOption
(
Option
::
Universe
u
,
OptionBufferConstIter
begin
,
...
...
@@ -820,6 +855,9 @@ OptionDefinition::factorySpecialFormatOption(Option::Universe u,
}
else
{
if
((
getCode
()
==
DHO_FQDN
)
&&
haveFqdn4Format
())
{
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
)
&&
haveVendorClass4Format
())
{
// V-I Vendor Class (option code 124).
...
...
src/lib/dhcp/option_definition.h
View file @
f82a806d
...
...
@@ -374,6 +374,9 @@ public:
/// @return true if option has the format of OpaqueDataTuples type options.
bool
haveOpaqueDataTuplesFormat
()
const
;
/// @brief Check if the option has format of CompressedFqdnList options.
bool
haveCompressedFqdnListFormat
()
const
;
/// @brief Option factory.
///
/// This function creates an instance of DHCP option using
...
...
@@ -578,6 +581,19 @@ public:
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.
///
/// The option with special formats are encapsulated by the dedicated
...
...
src/lib/dhcp/std_option_defs.h
View file @
f82a806d
...
...
@@ -192,12 +192,7 @@ const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = {
{
"uuid-guid"
,
DHO_UUID_GUID
,
OPT_RECORD_TYPE
,
false
,
RECORD_DEF
(
UUID_GUID_RECORDS
),
""
},
{
"subnet-selection"
,
DHO_SUBNET_SELECTION
,
OPT_IPV4_ADDRESS_TYPE
,
false
,
NO_RECORD_DEF
,
""
},
// The following options need a special encoding of data
// 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
,
""
},
{
"domain-search"
,
DHO_DOMAIN_SEARCH
,
OPT_FQDN_TYPE
,
true
,
NO_RECORD_DEF
,
""
},
{
"vivco-suboptions"
,
DHO_VIVCO_SUBOPTIONS
,
OPT_RECORD_TYPE
,
false
,
RECORD_DEF
(
VIVCO_RECORDS
),
""
},
// Vendor-Identifying Vendor Specific Information option payload begins with a
...
...
src/lib/dhcp/tests/libdhcp++_unittest.cc
View file @
f82a806d
...
...
@@ -1319,8 +1319,21 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
LibDhcpTest
::
testStdOptionDefs4
(
DHO_UUID_GUID
,
begin
,
begin
+
17
,
typeid
(
OptionCustom
));
LibDhcpTest
::
testStdOptionDefs4
(
DHO_DOMAIN_SEARCH
,
begin
,
end
,
typeid
(
Option
));
// Prepare buffer holding an array of FQDNs.
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.
const
char
vivco_data
[]
=
{
...
...
@@ -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.
TEST_F
(
LibDhcpTest
,
vendorClass6
)
{
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment