1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
|
// Copyright (C) 2013-2019,2021 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <dhcp/option_string.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
using namespace isc;
using namespace isc::dhcp;
using namespace isc::util;
namespace {
/// @brief OptionString test class.
class OptionStringTest : public ::testing::Test {
public:
/// @brief Constructor.
///
/// Initializes the test buffer with some data.
OptionStringTest() {
std::string test_string("This is a test string");
buf_.assign(test_string.begin(), test_string.end());
}
OptionBuffer buf_;
};
// This test verifies that the constructor which creates an option instance
// from a string value will create it properly.
TEST_F(OptionStringTest, constructorFromString) {
const std::string optv4_value = "some option";
OptionString optv4(Option::V4, 123, optv4_value);
EXPECT_EQ(Option::V4, optv4.getUniverse());
EXPECT_EQ(123, optv4.getType());
EXPECT_EQ(optv4_value, optv4.getValue());
EXPECT_EQ(Option::OPTION4_HDR_LEN + optv4_value.size(), optv4.len());
// Do another test with the same constructor to make sure that
// different set of parameters would initialize the class members
// to different values.
const std::string optv6_value = "other option";
OptionString optv6(Option::V6, 234, optv6_value);
EXPECT_EQ(Option::V6, optv6.getUniverse());
EXPECT_EQ(234, optv6.getType());
EXPECT_EQ("other option", optv6.getValue());
EXPECT_EQ(Option::OPTION6_HDR_LEN + optv6_value.size(), optv6.len());
// Check that an attempt to use empty string in the constructor
// will result in an exception.
EXPECT_THROW(OptionString(Option::V6, 123, ""), isc::OutOfRange);
// Check that an attempt to use string containing only nulls
// in the constructor will result in an exception.
std::string nulls{"\0\0",2};
EXPECT_THROW(OptionString(Option::V6, 123, nulls), isc::OutOfRange);
}
// This test verifies that the constructor which creates an option instance
// from a buffer, holding option payload, will create it properly.
// This function calls unpack() internally thus test test is considered
// to cover testing of unpack() functionality.
TEST_F(OptionStringTest, constructorFromBuffer) {
// Attempt to create an option using empty buffer should result in
// an exception.
EXPECT_THROW(
OptionString(Option::V4, 234, buf_.begin(), buf_.begin()),
isc::dhcp::SkipThisOptionError
);
// NULLs should result in an exception.
std::vector<uint8_t>nulls = { 0, 0, 0 };
EXPECT_THROW(
OptionString(Option::V4, 234, nulls.begin(), nulls.begin()),
isc::dhcp::SkipThisOptionError
);
// Declare option as a scoped pointer here so as its scope is
// function wide. The initialization (constructor invocation)
// is pushed to the ASSERT_NO_THROW macro below, as it may
// throw exception if buffer is truncated.
boost::scoped_ptr<OptionString> optv4;
ASSERT_NO_THROW(
optv4.reset(new OptionString(Option::V4, 234, buf_.begin(), buf_.end()));
);
// Make sure that it has been initialized to non-NULL value.
ASSERT_TRUE(optv4);
// Test the instance of the created option.
const std::string optv4_value = "This is a test string";
EXPECT_EQ(Option::V4, optv4->getUniverse());
EXPECT_EQ(234, optv4->getType());
EXPECT_EQ(Option::OPTION4_HDR_LEN + buf_.size(), optv4->len());
EXPECT_EQ(optv4_value, optv4->getValue());
// Do the same test for V6 option.
boost::scoped_ptr<OptionString> optv6;
ASSERT_NO_THROW(
// Let's reduce the size of the buffer by one byte and see if our option
// will absorb this little change.
optv6.reset(new OptionString(Option::V6, 123, buf_.begin(), buf_.end() - 1));
);
// Make sure that it has been initialized to non-NULL value.
ASSERT_TRUE(optv6);
// Test the instance of the created option.
const std::string optv6_value = "This is a test strin";
EXPECT_EQ(Option::V6, optv6->getUniverse());
EXPECT_EQ(123, optv6->getType());
EXPECT_EQ(Option::OPTION6_HDR_LEN + buf_.size() - 1, optv6->len());
EXPECT_EQ(optv6_value, optv6->getValue());
}
// This test verifies that the current option value can be overridden
// with new value, using setValue method.
TEST_F(OptionStringTest, setValue) {
// Create an instance of the option and set some initial value.
OptionString optv4(Option::V4, 123, "some option");
EXPECT_EQ("some option", optv4.getValue());
// Replace the value with the new one, and make sure it has
// been successful.
EXPECT_NO_THROW(optv4.setValue("new option value"));
EXPECT_EQ("new option value", optv4.getValue());
// Try to set to an empty string. It should throw exception.
EXPECT_THROW(optv4.setValue(""), isc::OutOfRange);
}
// This test verifies that the pack function encodes the option in
// a on-wire format properly.
TEST_F(OptionStringTest, pack) {
// Create an instance of the option.
std::string option_value("sample option value");
OptionString optv4(Option::V4, 123, option_value);
// Encode the option in on-wire format.
OutputBuffer buf(Option::OPTION4_HDR_LEN);
EXPECT_NO_THROW(optv4.pack(buf));
// Sanity check the length of the buffer.
ASSERT_EQ(Option::OPTION4_HDR_LEN + option_value.length(),
buf.getLength());
// Copy the contents of the OutputBuffer to InputBuffer because
// the latter has API to read data from it.
InputBuffer test_buf(buf.getData(), buf.getLength());
// First byte holds option code.
EXPECT_EQ(123, test_buf.readUint8());
// Second byte holds option length.
EXPECT_EQ(option_value.size(), test_buf.readUint8());
// Read the option data.
std::vector<uint8_t> data;
test_buf.readVector(data, test_buf.getLength() - test_buf.getPosition());
// And create a string from it.
std::string test_string(data.begin(), data.end());
// This string should be equal to the string used to create
// option's instance.
EXPECT_TRUE(option_value == test_string);
}
// This test checks that the DHCP option holding a single string is
// correctly returned in the textual format.
TEST_F(OptionStringTest, toText) {
// V4 option
std::string option_value("lorem ipsum");
OptionString optv4(Option::V4, 122, option_value);
EXPECT_EQ("type=122, len=011: \"lorem ipsum\" (string)", optv4.toText());
// V6 option
option_value = "is a filler text";
OptionString optv6(Option::V6, 512, option_value);
EXPECT_EQ("type=00512, len=00016: \"is a filler text\" (string)", optv6.toText());
}
// This test checks proper handling of trailing and embedded NULLs in
// data use to create or option value.
TEST_F(OptionStringTest, setValueNullsHandling) {
OptionString optv4(Option::V4, 123, "123");
// Only nulls should throw.
ASSERT_THROW(optv4.setValue(std::string{"\0\0", 2}), isc::OutOfRange);
// One trailing null should trim off.
ASSERT_NO_THROW(optv4.setValue(std::string{"one\0", 4}));
EXPECT_EQ(3, optv4.getValue().length());
EXPECT_EQ(optv4.getValue(), std::string("one"));
// More than one trailing null should trim off.
ASSERT_NO_THROW(optv4.setValue(std::string{"three\0\0\0", 8}));
EXPECT_EQ(5, optv4.getValue().length());
EXPECT_EQ(optv4.getValue(), std::string("three"));
// Embedded null should be left in place.
ASSERT_NO_THROW(optv4.setValue(std::string{"em\0bed", 6}));
EXPECT_EQ(6, optv4.getValue().length());
EXPECT_EQ(optv4.getValue(), (std::string{"em\0bed", 6}));
// Leading null should be left in place.
ASSERT_NO_THROW(optv4.setValue(std::string{"\0leading", 8}));
EXPECT_EQ(8, optv4.getValue().length());
EXPECT_EQ(optv4.getValue(), (std::string{"\0leading", 8}));
}
// This test checks proper handling of trailing and embedded NULLs in
// data use to create or option value.
TEST_F(OptionStringTest, unpackNullsHandling) {
OptionString optv4(Option::V4, 123, "123");
// Only nulls should throw.
OptionBuffer buffer = { 0, 0 };
ASSERT_THROW(optv4.unpack(buffer.begin(), buffer.end()), isc::dhcp::SkipThisOptionError);
// One trailing null should trim off.
buffer = {'o', 'n', 'e', 0 };
ASSERT_NO_THROW(optv4.unpack(buffer.begin(), buffer.end()));
EXPECT_EQ(3, optv4.getValue().length());
EXPECT_EQ(optv4.getValue(), std::string("one"));
// More than one trailing null should trim off.
buffer = { 't', 'h', 'r', 'e', 'e', 0, 0, 0 };
ASSERT_NO_THROW(optv4.unpack(buffer.begin(), buffer.end()));
EXPECT_EQ(5, optv4.getValue().length());
EXPECT_EQ(optv4.getValue(), std::string("three"));
// Embedded null should be left in place.
buffer = { 'e', 'm', 0, 'b', 'e', 'd' };
ASSERT_NO_THROW(optv4.unpack(buffer.begin(), buffer.end()));
EXPECT_EQ(6, optv4.getValue().length());
EXPECT_EQ(optv4.getValue(), (std::string{"em\0bed", 6}));
// Leading null should be left in place.
buffer = { 0, 'l', 'e', 'a', 'd', 'i', 'n', 'g' };
ASSERT_NO_THROW(optv4.unpack(buffer.begin(), buffer.end()));
EXPECT_EQ(8, optv4.getValue().length());
EXPECT_EQ(optv4.getValue(), (std::string{"\0leading", 8}));
}
} // anonymous namespace
|