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
0a4faa07
Commit
0a4faa07
authored
Dec 11, 2012
by
Marcin Siodelski
Browse files
[master] Merge branch 'trac2491'
parents
99b2935b
8627b6eb
Changes
17
Expand all
Hide whitespace changes
Inline
Side-by-side
src/bin/dhcp6/dhcp6_srv.cc
View file @
0a4faa07
...
...
@@ -24,6 +24,7 @@
#include
<dhcp/option6_iaaddr.h>
#include
<dhcp/option6_iaaddr.h>
#include
<dhcp/option6_int_array.h>
#include
<dhcp/option_custom.h>
#include
<dhcp/pkt6.h>
#include
<dhcp6/dhcp6_log.h>
#include
<dhcp6/dhcp6_srv.h>
...
...
@@ -348,13 +349,29 @@ void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer)
}
OptionPtr
Dhcpv6Srv
::
createStatusCode
(
uint16_t
code
,
const
std
::
string
&
text
)
{
// @todo: Implement Option6_StatusCode and rewrite this code here
vector
<
uint8_t
>
data
(
text
.
c_str
(),
text
.
c_str
()
+
text
.
length
());
data
.
insert
(
data
.
begin
(),
static_cast
<
uint8_t
>
(
code
%
256
));
data
.
insert
(
data
.
begin
(),
static_cast
<
uint8_t
>
(
code
>>
8
));
OptionPtr
status
(
new
Option
(
Option
::
V6
,
D6O_STATUS_CODE
,
data
));
return
(
status
);
// @todo This function uses OptionCustom class to manage contents
// of the data fields. Since this this option is frequently used
// it may be good to implement dedicated class to avoid performance
// impact.
// Get the definition of the option holding status code.
OptionDefinitionPtr
status_code_def
=
LibDHCP
::
getOptionDef
(
Option
::
V6
,
D6O_STATUS_CODE
);
// This definition is assumed to be initialized in LibDHCP.
assert
(
status_code_def
);
// As there is no dedicated class to represent Status Code
// the OptionCustom class should be returned here.
boost
::
shared_ptr
<
OptionCustom
>
option_status
=
boost
::
dynamic_pointer_cast
<
OptionCustom
>
(
status_code_def
->
optionFactory
(
Option
::
V6
,
D6O_STATUS_CODE
));
assert
(
option_status
);
// Set status code to 'code' (0 - means data field #0).
option_status
->
writeInteger
(
code
,
0
);
// Set a message (1 - means data field #1).
option_status
->
writeString
(
text
,
1
);
return
(
option_status
);
}
Subnet6Ptr
Dhcpv6Srv
::
selectSubnet
(
const
Pkt6Ptr
&
question
)
{
...
...
@@ -430,6 +447,10 @@ OptionPtr Dhcpv6Srv::handleIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
// but different wording below)
if
(
!
subnet
)
{
// Create empty IA_NA option with IAID matching the request.
// Note that we don't use OptionDefinition class to create this option.
// This is because we prefer using a constructor of Option6IA that
// initializes IAID. Otherwise we would have to use setIAID() after
// creation of the option which has some performance implications.
boost
::
shared_ptr
<
Option6IA
>
ia_rsp
(
new
Option6IA
(
D6O_IA_NA
,
ia
->
getIAID
()));
// Insert status code NoAddrsAvail.
...
...
@@ -471,6 +492,8 @@ OptionPtr Dhcpv6Srv::handleIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
hint
,
fake_allocation
);
// Create IA_NA that we will put in the response.
// Do not use OptionDefinition to create option's instance so
// as we can initialize IAID using a constructor.
boost
::
shared_ptr
<
Option6IA
>
ia_rsp
(
new
Option6IA
(
D6O_IA_NA
,
ia
->
getIAID
()));
if
(
lease
)
{
...
...
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
View file @
0a4faa07
...
...
@@ -801,12 +801,23 @@ TEST_F(Dhcpv6SrvTest, StatusCode) {
ASSERT_NO_THROW
(
srv
.
reset
(
new
NakedDhcpv6Srv
(
0
))
);
// a dummy content for client-id
uint8_t
expected
[]
=
{
0x0
,
0x3
,
0x41
,
0x42
,
0x43
,
0x44
,
0x45
};
OptionBuffer
exp
(
expected
,
expected
+
sizeof
(
expected
));
uint8_t
expected
[]
=
{
0x0
,
0xD
,
// option code = 14
0x0
,
0x7
,
// option length = 7
0x0
,
0x3
,
// status code = 3
0x41
,
0x42
,
0x43
,
0x44
,
0x45
// string value ABCDE
};
// Create the option.
OptionPtr
status
=
srv
->
createStatusCode
(
3
,
"ABCDE"
);
EXPECT_TRUE
(
status
->
getData
()
==
exp
);
// Allocate an output buffer. We will store the option
// in wire format here.
OutputBuffer
buf
(
sizeof
(
expected
));
// Prepare the wire format.
ASSERT_NO_THROW
(
status
->
pack
(
buf
));
// Check that the option buffer has valid length (option header + data).
ASSERT_EQ
(
sizeof
(
expected
),
buf
.
getLength
());
// Verify the contents of the option.
EXPECT_EQ
(
0
,
memcmp
(
expected
,
buf
.
getData
(),
sizeof
(
expected
)));
}
// This test verifies if the selectSubnet() method works as expected.
...
...
src/lib/dhcp/Makefile.am
View file @
0a4faa07
...
...
@@ -33,10 +33,12 @@ libb10_dhcp___la_SOURCES += option6_int_array.h
libb10_dhcp___la_SOURCES
+=
dhcp6.h dhcp4.h
libb10_dhcp___la_SOURCES
+=
pkt6.cc pkt6.h
libb10_dhcp___la_SOURCES
+=
pkt4.cc pkt4.h
libb10_dhcp___la_SOURCES
+=
std_option_defs.h
libb10_dhcp___la_CXXFLAGS
=
$(AM_CXXFLAGS)
libb10_dhcp___la_CPPFLAGS
=
$(AM_CPPFLAGS)
$(LOG4CPLUS_INCLUDES)
libb10_dhcp___la_LIBADD
=
$(top_builddir)
/src/lib/asiolink/libb10-asiolink.la
libb10_dhcp___la_LIBADD
+=
$(top_builddir)
/src/lib/dns/libb10-dns++.la
libb10_dhcp___la_LIBADD
+=
$(top_builddir)
/src/lib/util/libb10-util.la
libb10_dhcp___la_LDFLAGS
=
-no-undefined
-version-info
2:0:0
...
...
src/lib/dhcp/libdhcp++.cc
View file @
0a4faa07
...
...
@@ -22,6 +22,7 @@
#include
<dhcp/option6_iaaddr.h>
#include
<dhcp/option6_int_array.h>
#include
<dhcp/option_definition.h>
#include
<dhcp/std_option_defs.h>
#include
<exceptions/exceptions.h>
#include
<util/buffer.h>
...
...
@@ -45,7 +46,7 @@ OptionDefContainer LibDHCP::v4option_defs_;
OptionDefContainer
LibDHCP
::
v6option_defs_
;
const
OptionDefContainer
&
LibDHCP
::
getOptionDefs
(
Option
::
Universe
u
)
{
LibDHCP
::
getOptionDefs
(
const
Option
::
Universe
u
)
{
switch
(
u
)
{
case
Option
::
V4
:
initStdOptionDefs4
();
...
...
@@ -60,6 +61,17 @@ LibDHCP::getOptionDefs(Option::Universe u) {
}
}
OptionDefinitionPtr
LibDHCP
::
getOptionDef
(
const
Option
::
Universe
u
,
const
uint16_t
code
)
{
const
OptionDefContainer
&
defs
=
getOptionDefs
(
u
);
const
OptionDefContainerTypeIndex
&
idx
=
defs
.
get
<
1
>
();
const
OptionDefContainerTypeRange
&
range
=
idx
.
equal_range
(
code
);
if
(
range
.
first
!=
range
.
second
)
{
return
(
*
range
.
first
);
}
return
(
OptionDefinitionPtr
());
}
OptionPtr
LibDHCP
::
optionFactory
(
Option
::
Universe
u
,
uint16_t
type
,
...
...
@@ -254,52 +266,16 @@ void
LibDHCP
::
initStdOptionDefs6
()
{
v6option_defs_
.
clear
();
struct
OptionParams
{
std
::
string
name
;
uint16_t
code
;
OptionDataType
type
;
bool
array
;
};
OptionParams
params
[]
=
{
{
"CLIENTID"
,
D6O_CLIENTID
,
OPT_BINARY_TYPE
,
false
},
{
"SERVERID"
,
D6O_SERVERID
,
OPT_BINARY_TYPE
,
false
},
{
"IA_NA"
,
D6O_IA_NA
,
OPT_RECORD_TYPE
,
false
},
{
"IAADDR"
,
D6O_IAADDR
,
OPT_RECORD_TYPE
,
false
},
{
"ORO"
,
D6O_ORO
,
OPT_UINT16_TYPE
,
true
},
{
"ELAPSED_TIME"
,
D6O_ELAPSED_TIME
,
OPT_UINT16_TYPE
,
false
},
{
"STATUS_CODE"
,
D6O_STATUS_CODE
,
OPT_RECORD_TYPE
,
false
},
{
"RAPID_COMMIT"
,
D6O_RAPID_COMMIT
,
OPT_EMPTY_TYPE
,
false
},
{
"DNS_SERVERS"
,
D6O_NAME_SERVERS
,
OPT_IPV6_ADDRESS_TYPE
,
true
},
{
"IA_PD"
,
D6O_IA_PD
,
OPT_RECORD_TYPE
,
false
}
};
const
int
params_size
=
sizeof
(
params
)
/
sizeof
(
params
[
0
]);
for
(
int
i
=
0
;
i
<
params_size
;
++
i
)
{
OptionDefinitionPtr
definition
(
new
OptionDefinition
(
params
[
i
].
name
,
params
[
i
].
code
,
params
[
i
].
type
,
params
[
i
].
array
));
switch
(
params
[
i
].
code
)
{
case
D6O_IA_NA
:
case
D6O_IA_PD
:
for
(
int
j
=
0
;
j
<
3
;
++
j
)
{
definition
->
addRecordField
(
OPT_UINT32_TYPE
);
}
break
;
case
D6O_IAADDR
:
definition
->
addRecordField
(
OPT_IPV6_ADDRESS_TYPE
);
definition
->
addRecordField
(
OPT_UINT32_TYPE
);
definition
->
addRecordField
(
OPT_UINT32_TYPE
);
break
;
case
D6O_STATUS_CODE
:
definition
->
addRecordField
(
OPT_UINT16_TYPE
);
definition
->
addRecordField
(
OPT_STRING_TYPE
);
break
;
default:
// The default case is intentionally left empty
// as it does not need any processing.
;
for
(
int
i
=
0
;
i
<
OPTION_DEF_PARAMS_SIZE6
;
++
i
)
{
OptionDefinitionPtr
definition
(
new
OptionDefinition
(
OPTION_DEF_PARAMS6
[
i
].
name
,
OPTION_DEF_PARAMS6
[
i
].
code
,
OPTION_DEF_PARAMS6
[
i
].
type
,
OPTION_DEF_PARAMS6
[
i
].
array
));
for
(
int
rec
=
0
;
rec
<
OPTION_DEF_PARAMS6
[
i
].
records_size
;
++
rec
)
{
definition
->
addRecordField
(
OPTION_DEF_PARAMS6
[
i
].
records
[
rec
]);
}
try
{
definition
->
validate
();
}
catch
(
const
Exception
&
ex
)
{
...
...
src/lib/dhcp/libdhcp++.h
View file @
0a4faa07
...
...
@@ -42,7 +42,18 @@ public:
/// @param u universe of the options (V4 or V6).
///
/// @return collection of option definitions.
static
const
OptionDefContainer
&
getOptionDefs
(
Option
::
Universe
u
);
static
const
OptionDefContainer
&
getOptionDefs
(
const
Option
::
Universe
u
);
/// @brief Return the first option definition matching a
/// particular option code.
///
/// @param u universe (V4 or V6)
/// @param code option code.
///
/// @return reference to an option definition being requested
/// or NULL pointer if option definition has not been found.
static
OptionDefinitionPtr
getOptionDef
(
const
Option
::
Universe
u
,
const
uint16_t
code
);
/// @brief Factory function to create instance of option.
///
...
...
src/lib/dhcp/option_custom.cc
View file @
0a4faa07
...
...
@@ -20,12 +20,29 @@
namespace
isc
{
namespace
dhcp
{
OptionCustom
::
OptionCustom
(
const
OptionDefinition
&
def
,
Universe
u
)
:
Option
(
u
,
def
.
getCode
(),
OptionBuffer
()),
definition_
(
def
)
{
createBuffers
();
}
OptionCustom
::
OptionCustom
(
const
OptionDefinition
&
def
,
Universe
u
,
const
OptionBuffer
&
data
)
:
Option
(
u
,
def
.
getCode
(),
data
.
begin
(),
data
.
end
()),
definition_
(
def
)
{
createBuffers
();
// It is possible that no data is provided if an option
// is being created on a server side. In such case a bunch
// of buffers with default values is first created and then
// the values are replaced using writeXXX functions. Thus
// we need to detect that no data has been specified and
// take a different code path.
if
(
!
data_
.
empty
())
{
createBuffers
(
data_
);
}
else
{
createBuffers
();
}
}
OptionCustom
::
OptionCustom
(
const
OptionDefinition
&
def
,
...
...
@@ -34,25 +51,156 @@ OptionCustom::OptionCustom(const OptionDefinition& def,
OptionBufferConstIter
last
)
:
Option
(
u
,
def
.
getCode
(),
first
,
last
),
definition_
(
def
)
{
createBuffers
();
createBuffers
(
data_
);
}
void
OptionCustom
::
addArrayDataField
(
const
asiolink
::
IOAddress
&
address
)
{
checkArrayType
();
if
((
address
.
getFamily
()
==
AF_INET
&&
definition_
.
getType
()
!=
OPT_IPV4_ADDRESS_TYPE
)
||
(
address
.
getFamily
()
==
AF_INET6
&&
definition_
.
getType
()
!=
OPT_IPV6_ADDRESS_TYPE
))
{
isc_throw
(
BadDataTypeCast
,
"invalid address specified "
<<
address
.
toText
()
<<
". Expected a valid IPv"
<<
(
definition_
.
getType
()
==
OPT_IPV4_ADDRESS_TYPE
?
"4"
:
"6"
)
<<
" address."
);
}
OptionBuffer
buf
;
OptionDataTypeUtil
::
writeAddress
(
address
,
buf
);
buffers_
.
push_back
(
buf
);
}
void
OptionCustom
::
addArrayDataField
(
const
bool
value
)
{
checkArrayType
();
OptionBuffer
buf
;
OptionDataTypeUtil
::
writeBool
(
value
,
buf
);
buffers_
.
push_back
(
buf
);
}
void
OptionCustom
::
checkIndex
(
const
uint32_t
index
)
const
{
if
(
index
>=
buffers_
.
size
())
{
isc_throw
(
isc
::
OutOfRange
,
"specified data field index "
<<
index
<<
" is out of rangex."
);
<<
" is out of range."
);
}
}
template
<
typename
T
>
void
OptionCustom
::
checkDataType
(
const
uint32_t
index
)
const
{
// Check that the requested return type is a supported integer.
if
(
!
OptionDataTypeTraits
<
T
>::
integer_type
)
{
isc_throw
(
isc
::
dhcp
::
InvalidDataType
,
"specified data type"
" is not a supported integer type."
);
}
// Get the option definition type.
OptionDataType
data_type
=
definition_
.
getType
();
if
(
data_type
==
OPT_RECORD_TYPE
)
{
const
OptionDefinition
::
RecordFieldsCollection
&
record_fields
=
definition_
.
getRecordFields
();
// When we initialized buffers we have already checked that
// the number of these buffers is equal to number of option
// fields in the record so the condition below should be met.
assert
(
index
<
record_fields
.
size
());
// Get the data type to be returned.
data_type
=
record_fields
[
index
];
}
if
(
OptionDataTypeTraits
<
T
>::
type
!=
data_type
)
{
isc_throw
(
isc
::
dhcp
::
InvalidDataType
,
"specified data type "
<<
data_type
<<
" does not"
" match the data type in an option definition for field"
" index "
<<
index
);
}
}
void
OptionCustom
::
createBuffers
()
{
definition_
.
validate
();
std
::
vector
<
OptionBuffer
>
buffers
;
OptionDataType
data_type
=
definition_
.
getType
();
// This function is called when an empty data buffer has been
// passed to the constructor. In such cases values for particular
// data fields will be set using modifier functions but for now
// we need to initialize a set of buffers that are specified
// for an option by its definition. Since there is no data yet,
// we are going to fill these buffers with default values.
if
(
data_type
==
OPT_RECORD_TYPE
)
{
// For record types we need to iterate over all data fields
// specified in option definition and create corresponding
// buffers for each of them.
const
OptionDefinition
::
RecordFieldsCollection
fields
=
definition_
.
getRecordFields
();
for
(
OptionDefinition
::
RecordFieldsConstIter
field
=
fields
.
begin
();
field
!=
fields
.
end
();
++
field
)
{
OptionBuffer
buf
;
// For data types that have a fixed size we can use the
// utility function to get the buffer's size.
size_t
data_size
=
OptionDataTypeUtil
::
getDataTypeLen
(
*
field
);
// For variable data sizes the utility function returns zero.
// It is ok for string values because the default string
// is 'empty'. However for FQDN the empty value is not valid
// so we initialize it to '.'.
if
(
data_size
==
0
&&
*
field
==
OPT_FQDN_TYPE
)
{
OptionDataTypeUtil
::
writeFqdn
(
"."
,
buf
);
}
else
{
// At this point we can resize the buffer. Note that
// for string values we are setting the empty buffer
// here.
buf
.
resize
(
data_size
);
}
// We have the buffer with default value prepared so we
// add it to the set of buffers.
buffers
.
push_back
(
buf
);
}
}
else
if
(
!
definition_
.
getArrayType
()
&&
data_type
!=
OPT_EMPTY_TYPE
)
{
// For either 'empty' options we don't have to create any buffers
// for obvious reason. For arrays we also don't create any buffers
// yet because the set of fields that belong to the array is open
// ended so we can't allocate required buffers until we know how
// many of them are needed.
// For non-arrays we have a single value being held by the option
// so we have to allocate exactly one buffer.
OptionBuffer
buf
;
size_t
data_size
=
OptionDataTypeUtil
::
getDataTypeLen
(
data_type
);
if
(
data_size
==
0
&&
data_type
==
OPT_FQDN_TYPE
)
{
OptionDataTypeUtil
::
writeFqdn
(
"."
,
buf
);
}
else
{
// Note that if our option holds a string value then
// we are making empty buffer here.
buf
.
resize
(
data_size
);
}
// Add a buffer that we have created and leave.
buffers
.
push_back
(
buf
);
}
// The 'swap' is used here because we want to make sure that we
// don't touch buffers_ until we successfully allocate all
// buffers to be stored there.
std
::
swap
(
buffers
,
buffers_
);
}
void
OptionCustom
::
createBuffers
(
const
OptionBuffer
&
data_buf
)
{
// Check that the option definition is correct as we are going
// to use it to split the data_ buffer into set of sub buffers.
definition_
.
validate
();
std
::
vector
<
OptionBuffer
>
buffers
;
OptionBuffer
::
iterator
data
=
data_
.
begin
();
OptionBuffer
::
const_
iterator
data
=
data_
buf
.
begin
();
OptionDataType
data_type
=
definition_
.
getType
();
if
(
data_type
==
OPT_RECORD_TYPE
)
{
...
...
@@ -68,17 +216,31 @@ OptionCustom::createBuffers() {
// 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
)
{
std
::
string
fqdn
=
OptionDataTypeUtil
::
readFqdn
(
OptionBuffer
(
data
,
data_buf
.
end
()));
// The size of the buffer holding an FQDN is always
// 1 byte larger than the size of the string
// representation of this FQDN.
data_size
=
fqdn
.
size
()
+
1
;
}
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_buf
.
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
...
...
@@ -90,7 +252,7 @@ OptionCustom::createBuffers() {
}
else
{
// Our data field requires that there is a certain chunk of
// data left in the buffer. If not, option is truncated.
if
(
std
::
distance
(
data
,
data_
.
end
())
<
data_size
)
{
if
(
std
::
distance
(
data
,
data_
buf
.
end
())
<
data_size
)
{
isc_throw
(
OutOfRange
,
"option buffer truncated"
);
}
}
...
...
@@ -105,39 +267,64 @@ OptionCustom::createBuffers() {
// 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.
in
t
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
// if variable length data is being held by the option but
// this will not cause this check to throw exception.
if
(
std
::
distance
(
data
,
data_
.
end
())
<
data_size
)
{
if
(
std
::
distance
(
data
,
data_
buf
.
end
())
<
data_size
)
{
isc_throw
(
OutOfRange
,
"option buffer truncated"
);
}
// 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_buf
.
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
)
{
std
::
string
fqdn
=
OptionDataTypeUtil
::
readFqdn
(
OptionBuffer
(
data
,
data_buf
.
end
()));
// The size of the buffer holding an FQDN is always
// 1 byte larger than the size of the string
// representation of this FQDN.
data_size
=
fqdn
.
size
()
+
1
;
}
// 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_buf
.
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
)
{
std
::
string
fqdn
=
OptionDataTypeUtil
::
readFqdn
(
OptionBuffer
(
data
,
data_buf
.
end
()));
// The size of the buffer holding an FQDN is always
// 1 bytes larger than the size of the string
// representation of this FQDN.
data_size
=
fqdn
.
size
()
+
1
;
}
else
{
data_size
=
std
::
distance
(
data
,
data_buf
.
end
());
}
}
if
(
data_size
>
0
)
{
buffers
.
push_back
(
OptionBuffer
(
data
,
data
+
data_size
));
...
...
@@ -185,6 +372,9 @@ OptionCustom::dataFieldToText(const OptionDataType data_type,
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
;
...
...
@@ -252,8 +442,31 @@ OptionCustom::readAddress(const uint32_t index) const {
return
(
OptionDataTypeUtil
::
readAddress
(
buffers_
[
index
],
AF_INET6
));
}
else
{
isc_throw
(
BadDataTypeCast
,
"unable to read data from the buffer as"
<<
" IP address. Invalid buffer length "
<<
buffers_
[
index
].
size
());
<<
" IP address. Invalid buffer length "
<<
buffers_
[
index
].
size
()
<<
"."
);
}
}
void
OptionCustom
::
writeAddress
(
const
asiolink
::
IOAddress
&
address
,
const
uint32_t
index
)
{
using
namespace
isc
::
asiolink
;
checkIndex
(
index
);
if
((
address
.
getFamily
()
==
AF_INET
&&
buffers_
[
index
].
size
()
!=
V4ADDRESS_LEN
)
||
(
address
.
getFamily
()
==
AF_INET6
&&
buffers_
[
index
].
size
()
!=
V6ADDRESS_LEN
))
{
isc_throw
(
BadDataTypeCast
,
"invalid address specified "
<<
address
.
toText
()
<<
". Expected a valid IPv"
<<
(
buffers_
[
index
].
size
()
==
V4ADDRESS_LEN
?
"4"
:
"6"
)
<<
" address."
);
}
OptionBuffer
buf
;
OptionDataTypeUtil
::
writeAddress
(
address
,
buf
);
std
::
swap
(
buf
,
buffers_
[
index
]);
}
const
OptionBuffer
&
...
...
@@ -262,24 +475,77 @@ OptionCustom::readBinary(const uint32_t index) const {
return
(
buffers_
[
index
]);
}
void
OptionCustom
::
writeBinary
(
const
OptionBuffer
&
buf
,
const
uint32_t
index
)
{
checkIndex
(
index
);
buffers_
[
index
]
=
buf
;
}
bool
OptionCustom
::
readBoolean
(
const
uint32_t
index
)
const
{
checkIndex
(
index
);
return
(
OptionDataTypeUtil
::
readBool
(
buffers_
[
index
]));
}
void
OptionCustom
::
writeBoolean
(
const
bool
value
,
const
uint32_t
index
)
{
checkIndex
(
index
);
buffers_
[
index
].
clear
();
OptionDataTypeUtil
::
writeBool
(
value
,
buffers_
[
index
]);
}