1 // Copyright (C) 2012-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 #include <config.h>
8 #include <dhcp/option_data_types.h>
9 #include <gtest/gtest.h>
10 #include <utility>
11
12 using namespace isc;
13 using namespace isc::asiolink;
14 using namespace isc::dhcp;
15
16 namespace {
17
18 /// @brief Default (zero) prefix tuple.
19 const PrefixTuple
20 ZERO_PREFIX_TUPLE(std::make_pair(PrefixLen(0),
21 IOAddress(IOAddress::IPV6_ZERO_ADDRESS())));
22
23 /// @brief Test class for option data type utilities.
24 class OptionDataTypesTest : public ::testing::Test {
25 public:
26
27 /// @brief Constructor.
OptionDataTypesTest()28 OptionDataTypesTest() { }
29
30 /// @brief Write IP address into a buffer.
31 ///
32 /// @param address address to be written.
33 /// @param [out] buf output buffer.
writeAddress(const asiolink::IOAddress & address,std::vector<uint8_t> & buf)34 void writeAddress(const asiolink::IOAddress& address,
35 std::vector<uint8_t>& buf) {
36 const std::vector<uint8_t>& vec = address.toBytes();
37 buf.insert(buf.end(), vec.begin(), vec.end());
38 }
39
40 /// @brief Write integer (signed or unsigned) into a buffer.
41 ///
42 /// @param value integer value.
43 /// @param [out] buf output buffer.
44 /// @tparam integer type.
45 template<typename T>
writeInt(T value,std::vector<uint8_t> & buf)46 void writeInt(T value, std::vector<uint8_t>& buf) {
47 switch (sizeof(T)) {
48 case 4:
49 buf.push_back((value >> 24) & 0xFF);
50 /* falls through */
51 case 3:
52 buf.push_back((value >> 16) & 0xFF);
53 /* falls through */
54 case 2:
55 buf.push_back((value >> 8) & 0xFF);
56 /* falls through */
57 case 1:
58 buf.push_back(value & 0xFF);
59 break;
60 default:
61 // This loop is incorrectly compiled by some old g++?!
62 for (int i = 0; i < sizeof(T); ++i) {
63 buf.push_back(value >> ((sizeof(T) - i - 1) * 8) & 0xFF);
64 }
65 }
66 }
67
68 /// @brief Write a string into a buffer.
69 ///
70 /// @param value string to be written into a buffer.
71 /// @param buf output buffer.
writeString(const std::string & value,std::vector<uint8_t> & buf)72 void writeString(const std::string& value,
73 std::vector<uint8_t>& buf) {
74 buf.resize(buf.size() + value.size());
75 std::copy_backward(value.c_str(), value.c_str() + value.size(),
76 buf.end());
77 }
78 };
79
80 // The goal of this test is to verify that the getLabelCount returns the
81 // correct number of labels in the domain name specified as a string
82 // parameter.
TEST_F(OptionDataTypesTest,getLabelCount)83 TEST_F(OptionDataTypesTest, getLabelCount) {
84 EXPECT_EQ(0, OptionDataTypeUtil::getLabelCount(""));
85 EXPECT_EQ(1, OptionDataTypeUtil::getLabelCount("."));
86 EXPECT_EQ(2, OptionDataTypeUtil::getLabelCount("example"));
87 EXPECT_EQ(3, OptionDataTypeUtil::getLabelCount("example.com"));
88 EXPECT_EQ(3, OptionDataTypeUtil::getLabelCount("example.com."));
89 EXPECT_EQ(4, OptionDataTypeUtil::getLabelCount("myhost.example.com"));
90 EXPECT_THROW(OptionDataTypeUtil::getLabelCount(".abc."),
91 isc::dhcp::BadDataTypeCast);
92 }
93
94 // The goal of this test is to verify that an IPv4 address being
95 // stored in a buffer (wire format) can be read into IOAddress
96 // object.
TEST_F(OptionDataTypesTest,readAddress)97 TEST_F(OptionDataTypesTest, readAddress) {
98 // Create some IPv4 address.
99 asiolink::IOAddress address("192.168.0.1");
100 // And store it in a buffer in a wire format.
101 std::vector<uint8_t> buf;
102 writeAddress(address, buf);
103
104 // Now, try to read the IP address with a utility function
105 // being under test.
106 asiolink::IOAddress address_out("127.0.0.1");
107 EXPECT_NO_THROW(address_out = OptionDataTypeUtil::readAddress(buf, AF_INET));
108
109 // Check that the read address matches address that
110 // we used as input.
111 EXPECT_EQ(address, address_out);
112
113 // Check that an attempt to read the buffer as IPv6 address
114 // causes an error as the IPv6 address needs at least 16 bytes
115 // long buffer.
116 EXPECT_THROW(
117 OptionDataTypeUtil::readAddress(buf, AF_INET6),
118 isc::dhcp::BadDataTypeCast
119 );
120
121 buf.clear();
122
123 // Do another test like this for IPv6 address.
124 address = asiolink::IOAddress("2001:db8:1:0::1");
125 writeAddress(address, buf);
126 EXPECT_NO_THROW(address_out = OptionDataTypeUtil::readAddress(buf, AF_INET6));
127 EXPECT_EQ(address, address_out);
128
129 // Truncate the buffer and expect an error to be reported when
130 // trying to read it.
131 buf.resize(buf.size() - 1);
132 EXPECT_THROW(
133 OptionDataTypeUtil::readAddress(buf, AF_INET6),
134 isc::dhcp::BadDataTypeCast
135 );
136 }
137
138 // The goal of this test is to verify that an IPv6 address
139 // is properly converted to wire format and stored in a
140 // buffer.
TEST_F(OptionDataTypesTest,writeAddress)141 TEST_F(OptionDataTypesTest, writeAddress) {
142 // Encode an IPv6 address 2001:db8:1::1 in wire format.
143 // This will be used as reference data to validate if
144 // an IPv6 address is stored in a buffer properly.
145 const uint8_t data[] = {
146 0x20, 0x01, 0x0d, 0xb8, 0x0, 0x1, 0x0, 0x0,
147 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1
148 };
149 std::vector<uint8_t> buf_in(data, data + sizeof(data));
150
151 // Create IPv6 address object.
152 asiolink::IOAddress address("2001:db8:1::1");
153 // Define the output buffer to write IP address to.
154 std::vector<uint8_t> buf_out;
155 // Write the address to the buffer.
156 ASSERT_NO_THROW(OptionDataTypeUtil::writeAddress(address, buf_out));
157 // Make sure that input and output buffers have the same size
158 // so we can compare them.
159 ASSERT_EQ(buf_in.size(), buf_out.size());
160 // And finally compare them.
161 EXPECT_TRUE(std::equal(buf_in.begin(), buf_in.end(), buf_out.begin()));
162
163 buf_out.clear();
164
165 // Do similar test for IPv4 address.
166 address = asiolink::IOAddress("192.168.0.1");
167 ASSERT_NO_THROW(OptionDataTypeUtil::writeAddress(address, buf_out));
168 ASSERT_EQ(4, buf_out.size());
169 // Verify that the IP address has been written correctly.
170 EXPECT_EQ(192, buf_out[0]);
171 EXPECT_EQ(168, buf_out[1]);
172 EXPECT_EQ(0, buf_out[2]);
173 EXPECT_EQ(1, buf_out[3]);
174 }
175
176 // The purpose of this test is to verify that binary data represented
177 // as a string of hexadecimal digits can be written to a buffer.
TEST_F(OptionDataTypesTest,writeBinary)178 TEST_F(OptionDataTypesTest, writeBinary) {
179 // Prepare the reference data.
180 const char data[] = {
181 0x0, 0x1, 0x2, 0x3, 0x4, 0x5,
182 0x6, 0x7, 0x8, 0x9, 0xA, 0xB
183 };
184 std::vector<uint8_t> buf_ref(data, data + sizeof(data));
185 // Create empty vector where binary data will be written to.
186 std::vector<uint8_t> buf;
187 ASSERT_NO_THROW(
188 OptionDataTypeUtil::writeBinary("000102030405060708090A0B", buf)
189 );
190 // Verify that the buffer contains valid data.
191 ASSERT_EQ(buf_ref.size(), buf.size());
192 EXPECT_TRUE(std::equal(buf_ref.begin(), buf_ref.end(), buf.begin()));
193 }
194
195 // The purpose of this test is to verify that the tuple value stored
TEST_F(OptionDataTypesTest,readTuple)196 TEST_F(OptionDataTypesTest, readTuple) {
197 // The string
198 std::string value = "hello world";
199 // Create an input buffer.
200 std::vector<uint8_t> buf;
201 // DHCPv4 tuples use 1 byte length
202 writeInt<uint8_t>(static_cast<uint8_t>(value.size()), buf);
203 writeString(value, buf);
204
205 // Read the string from the buffer.
206 std::string result;
207 ASSERT_NO_THROW(
208 result = OptionDataTypeUtil::readTuple(buf, OpaqueDataTuple::LENGTH_1_BYTE);
209 );
210 // Check that it is valid.
211 EXPECT_EQ(value, result);
212
213 // Read the tuple from the buffer.
214 OpaqueDataTuple tuple4(OpaqueDataTuple::LENGTH_1_BYTE);
215 ASSERT_NO_THROW(OptionDataTypeUtil::readTuple(buf, tuple4));
216 // Check that it is valid.
217 EXPECT_EQ(value, tuple4.getText());
218
219 buf.clear();
220
221 // DHCPv6 tuples use 2 byte length
222 writeInt<uint16_t>(static_cast<uint16_t>(value.size()), buf);
223 writeString(value, buf);
224
225 // Read the string from the buffer.
226 ASSERT_NO_THROW(
227 result = OptionDataTypeUtil::readTuple(buf, OpaqueDataTuple::LENGTH_2_BYTES);
228 );
229 // Check that it is valid.
230 EXPECT_EQ(value, result);
231
232 // Read the tuple from the buffer.
233 OpaqueDataTuple tuple6(OpaqueDataTuple::LENGTH_2_BYTES);
234 ASSERT_NO_THROW(OptionDataTypeUtil::readTuple(buf, tuple6));
235 // Check that it is valid.
236 EXPECT_EQ(value, tuple6.getText());
237 }
238
239 // The purpose of this test is to verify that a tuple value
240 // are correctly encoded in a buffer (string version)
TEST_F(OptionDataTypesTest,writeTupleString)241 TEST_F(OptionDataTypesTest, writeTupleString) {
242 // The string
243 std::string value = "hello world";
244 // Create an output buffer.
245 std::vector<uint8_t> buf;
246
247 // Encode it in DHCPv4
248 OptionDataTypeUtil::writeTuple(value, OpaqueDataTuple::LENGTH_1_BYTE, buf);
249
250 // Check that it is valid.
251 ASSERT_EQ(value.size() + 1, buf.size());
252 std::vector<uint8_t> expected;
253 writeInt<uint8_t>(static_cast<uint8_t>(value.size()), expected);
254 writeString(value, expected);
255 EXPECT_EQ(0, std::memcmp(&buf[0], &expected[0], buf.size()));
256
257 buf.clear();
258
259 // Encode it in DHCPv6
260 OptionDataTypeUtil::writeTuple(value, OpaqueDataTuple::LENGTH_2_BYTES, buf);
261
262 // Check that it is valid.
263 ASSERT_EQ(value.size() + 2, buf.size());
264 expected.clear();
265 writeInt<uint16_t>(static_cast<uint16_t>(value.size()), expected);
266 writeString(value, expected);
267 EXPECT_EQ(0, std::memcmp(&buf[0], &expected[0], buf.size()));
268 }
269
270 // The purpose of this test is to verify that a tuple value
271 // are correctly encoded in a buffer (tuple version)
TEST_F(OptionDataTypesTest,writeTuple)272 TEST_F(OptionDataTypesTest, writeTuple) {
273 // The string
274 std::string value = "hello world";
275 // Create a DHCPv4 tuple
276 OpaqueDataTuple tuple4(OpaqueDataTuple::LENGTH_1_BYTE);
277 tuple4.append(value);
278 // Create an output buffer.
279 std::vector<uint8_t> buf;
280
281 // Encode it in DHCPv4
282 OptionDataTypeUtil::writeTuple(tuple4, buf);
283
284 // Check that it is valid.
285 ASSERT_EQ(value.size() + 1, buf.size());
286 std::vector<uint8_t> expected;
287 writeInt<uint8_t>(static_cast<uint8_t>(value.size()), expected);
288 writeString(value, expected);
289 EXPECT_EQ(0, std::memcmp(&buf[0], &expected[0], buf.size()));
290
291 buf.clear();
292
293 // Create a DHCPv6 tuple
294 OpaqueDataTuple tuple6(OpaqueDataTuple::LENGTH_2_BYTES);
295 tuple6.append(value);
296
297 // Encode it in DHCPv6
298 OptionDataTypeUtil::writeTuple(tuple6, buf);
299
300 // Check that it is valid.
301 ASSERT_EQ(value.size() + 2, buf.size());
302 expected.clear();
303 writeInt<uint16_t>(static_cast<uint16_t>(value.size()), expected);
304 writeString(value, expected);
305 EXPECT_EQ(0, std::memcmp(&buf[0], &expected[0], buf.size()));
306 }
307
308 // The purpose of this test is to verify that the boolean value stored
309 // in a buffer is correctly read from this buffer.
TEST_F(OptionDataTypesTest,readBool)310 TEST_F(OptionDataTypesTest, readBool) {
311 // Create an input buffer.
312 std::vector<uint8_t> buf;
313 // 'true' value is encoded as 1 ('false' is encoded as 0)
314 buf.push_back(1);
315
316 // Read the value from the buffer.
317 bool value = false;
318 ASSERT_NO_THROW(
319 value = OptionDataTypeUtil::readBool(buf);
320 );
321 // Verify the value.
322 EXPECT_TRUE(value);
323 // Check if 'false' is read correctly either.
324 buf[0] = 0;
325 ASSERT_NO_THROW(
326 value = OptionDataTypeUtil::readBool(buf);
327 );
328 EXPECT_FALSE(value);
329
330 // Check that invalid value causes exception.
331 buf[0] = 5;
332 ASSERT_THROW(
333 OptionDataTypeUtil::readBool(buf),
334 isc::dhcp::BadDataTypeCast
335 );
336 }
337
338 // The purpose of this test is to verify that boolean values
339 // are correctly encoded in a buffer as '1' for 'true' and
340 // '0' for 'false' values.
TEST_F(OptionDataTypesTest,writeBool)341 TEST_F(OptionDataTypesTest, writeBool) {
342 // Create a buffer we will write to.
343 std::vector<uint8_t> buf;
344 // Write the 'true' value to the buffer.
345 ASSERT_NO_THROW(OptionDataTypeUtil::writeBool(true, buf));
346 // We should now have 'true' value stored in a buffer.
347 ASSERT_EQ(1, buf.size());
348 EXPECT_EQ(buf[0], 1);
349 // Let's append another value to make sure that it is not always
350 // 'true' value being written.
351 ASSERT_NO_THROW(OptionDataTypeUtil::writeBool(false, buf));
352 ASSERT_EQ(2, buf.size());
353 // Check that the first value has not changed.
354 EXPECT_EQ(buf[0], 1);
355 // Check the second value is correct.
356 EXPECT_EQ(buf[1], 0);
357 }
358
359 // The purpose of this test is to verify that the integer values
360 // of different types are correctly read from a buffer.
TEST_F(OptionDataTypesTest,readInt)361 TEST_F(OptionDataTypesTest, readInt) {
362 std::vector<uint8_t> buf;
363
364 // Write an 8-bit unsigned integer value to the buffer.
365 writeInt<uint8_t>(129, buf);
366 uint8_t valueUint8 = 0;
367 // Read the value and check that it is valid.
368 ASSERT_NO_THROW(
369 valueUint8 = OptionDataTypeUtil::readInt<uint8_t>(buf);
370 );
371 EXPECT_EQ(129, valueUint8);
372
373 // Try to read 16-bit value from a buffer holding 8-bit value.
374 // This should result in an exception.
375 EXPECT_THROW(
376 OptionDataTypeUtil::readInt<uint16_t>(buf),
377 isc::dhcp::BadDataTypeCast
378 );
379
380 // Clear the buffer for the next check we are going to do.
381 buf.clear();
382
383 // Test uint16_t value.
384 writeInt<uint16_t>(1234, buf);
385 uint16_t valueUint16 = 0;
386 ASSERT_NO_THROW(
387 valueUint16 = OptionDataTypeUtil::readInt<uint16_t>(buf);
388 );
389 EXPECT_EQ(1234, valueUint16);
390
391 // Try to read 32-bit value from a buffer holding 16-bit value.
392 // This should result in an exception.
393 EXPECT_THROW(
394 OptionDataTypeUtil::readInt<uint32_t>(buf),
395 isc::dhcp::BadDataTypeCast
396 );
397
398 buf.clear();
399
400 // Test uint32_t value.
401 writeInt<uint32_t>(56789, buf);
402 uint32_t valueUint32 = 0;
403 ASSERT_NO_THROW(
404 valueUint32 = OptionDataTypeUtil::readInt<uint32_t>(buf);
405 );
406 EXPECT_EQ(56789, valueUint32);
407 buf.clear();
408
409 // Test int8_t value.
410 writeInt<int8_t>(-65, buf);
411 int8_t valueInt8 = 0;
412 ASSERT_NO_THROW(
413 valueInt8 = OptionDataTypeUtil::readInt<int8_t>(buf);
414 );
415 EXPECT_EQ(-65, valueInt8);
416 buf.clear();
417
418 // Try to read 16-bit value from a buffer holding 8-bit value.
419 // This should result in an exception.
420 EXPECT_THROW(
421 OptionDataTypeUtil::readInt<int16_t>(buf),
422 isc::dhcp::BadDataTypeCast
423 );
424
425 // Test int16_t value.
426 writeInt<int16_t>(2345, buf);
427 int32_t valueInt16 = 0;
428 ASSERT_NO_THROW(
429 valueInt16 = OptionDataTypeUtil::readInt<int16_t>(buf);
430 );
431 EXPECT_EQ(2345, valueInt16);
432 buf.clear();
433
434 // Try to read 32-bit value from a buffer holding 16-bit value.
435 // This should result in an exception.
436 EXPECT_THROW(
437 OptionDataTypeUtil::readInt<int32_t>(buf),
438 isc::dhcp::BadDataTypeCast
439 );
440
441 // Test int32_t value.
442 writeInt<int32_t>(-16543, buf);
443 int32_t valueInt32 = 0;
444 ASSERT_NO_THROW(
445 valueInt32 = OptionDataTypeUtil::readInt<int32_t>(buf);
446 );
447 EXPECT_EQ(-16543, valueInt32);
448
449 buf.clear();
450 }
451
452 // The purpose of this test is to verify that integer values of different
453 // types are correctly written to a buffer.
TEST_F(OptionDataTypesTest,writeInt)454 TEST_F(OptionDataTypesTest, writeInt) {
455 // Prepare the reference buffer.
456 const uint8_t data[] = {
457 0x7F, // 127
458 0x03, 0xFF, // 1023
459 0x00, 0x00, 0x10, 0x00, // 4096
460 0xFF, 0xFF, 0xFC, 0x00, // -1024
461 0x02, 0x00, // 512
462 0x81 // -127
463 };
464 std::vector<uint8_t> buf_ref(data, data + sizeof(data));
465
466 // Fill in the buffer with data. Each write operation appends an
467 // integer value. Eventually the buffer holds all values and should
468 // match with the reference buffer.
469 std::vector<uint8_t> buf;
470 ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<uint8_t>(127, buf));
471 ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<uint16_t>(1023, buf));
472 ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<uint32_t>(4096, buf));
473 ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<int32_t>(-1024, buf));
474 ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<int16_t>(512, buf));
475 ASSERT_NO_THROW(OptionDataTypeUtil::writeInt<int8_t>(-127, buf));
476
477 // Make sure that the buffer has the same size as the reference
478 // buffer.
479 ASSERT_EQ(buf_ref.size(), buf.size());
480 // Compare buffers.
481 EXPECT_TRUE(std::equal(buf_ref.begin(), buf_ref.end(), buf.begin()));
482 }
483
484 // The purpose of this test is to verify that FQDN is read from
485 // a buffer and returned as a text. The representation of the FQDN
486 // in the buffer complies with RFC1035, section 3.1.
487 // This test also checks that if invalid (truncated) FQDN is stored
488 // in a buffer the appropriate exception is returned when trying to
489 // read it as a string.
TEST_F(OptionDataTypesTest,readFqdn)490 TEST_F(OptionDataTypesTest, readFqdn) {
491 // The binary representation of the "mydomain.example.com".
492 // Values: 8, 7, 3 and 0 specify the lengths of subsequent
493 // labels within the FQDN.
494 const char data[] = {
495 8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
496 7, 101, 120, 97, 109, 112, 108, 101, // "example"
497 3, 99, 111, 109, // "com"
498 0
499 };
500
501 // Make a vector out of the data.
502 std::vector<uint8_t> buf(data, data + sizeof(data));
503
504 // Read the buffer as FQDN and verify its correctness.
505 std::string fqdn;
506 EXPECT_NO_THROW(fqdn = OptionDataTypeUtil::readFqdn(buf));
507 EXPECT_EQ("mydomain.example.com.", fqdn);
508
509 // By resizing the buffer we simulate truncation. The first
510 // length field (8) indicate that the first label's size is
511 // 8 but the actual buffer size is 5. Expect that conversion
512 // fails.
513 buf.resize(5);
514 EXPECT_THROW(
515 OptionDataTypeUtil::readFqdn(buf),
516 isc::dhcp::BadDataTypeCast
517 );
518
519 // Another special case: provide an empty buffer.
520 buf.clear();
521 EXPECT_THROW(
522 OptionDataTypeUtil::readFqdn(buf),
523 isc::dhcp::BadDataTypeCast
524 );
525 }
526
527 // The purpose of this test is to verify that FQDN's syntax is validated
528 // and that FQDN is correctly written to a buffer in a format described
529 // in RFC1035 section 3.1.
TEST_F(OptionDataTypesTest,writeFqdn)530 TEST_F(OptionDataTypesTest, writeFqdn) {
531 // Create empty buffer. The FQDN will be written to it.
532 OptionBuffer buf;
533 // Write a domain name into the buffer in the format described
534 // in RFC1035 section 3.1. This function should not throw
535 // exception because domain name is well formed.
536 EXPECT_NO_THROW(
537 OptionDataTypeUtil::writeFqdn("mydomain.example.com", buf)
538 );
539 // The length of the data is 22 (8 bytes for "mydomain" label,
540 // 7 bytes for "example" label, 3 bytes for "com" label and
541 // finally 4 bytes positions between labels where length
542 // information is stored.
543 ASSERT_EQ(22, buf.size());
544
545 // Verify that length fields between labels hold valid values.
546 EXPECT_EQ(8, buf[0]); // length of "mydomain"
547 EXPECT_EQ(7, buf[9]); // length of "example"
548 EXPECT_EQ(3, buf[17]); // length of "com"
549 EXPECT_EQ(0, buf[21]); // zero byte at the end.
550
551 // Verify that labels are valid.
552 std::string label0(buf.begin() + 1, buf.begin() + 9);
553 EXPECT_EQ("mydomain", label0);
554
555 std::string label1(buf.begin() + 10, buf.begin() + 17);
556 EXPECT_EQ("example", label1);
557
558 std::string label2(buf.begin() + 18, buf.begin() + 21);
559 EXPECT_EQ("com", label2);
560
561 // The tested function is supposed to append data to a buffer
562 // so let's check that it is a case by appending another domain.
563 OptionDataTypeUtil::writeFqdn("hello.net", buf);
564
565 // The buffer length should be now longer.
566 ASSERT_EQ(33, buf.size());
567
568 // Check the length fields for new labels being appended.
569 EXPECT_EQ(5, buf[22]);
570 EXPECT_EQ(3, buf[28]);
571
572 // And check that labels are ok.
573 std::string label3(buf.begin() + 23, buf.begin() + 28);
574 EXPECT_EQ("hello", label3);
575
576 std::string label4(buf.begin() + 29, buf.begin() + 32);
577 EXPECT_EQ("net", label4);
578
579 // Check that invalid (empty) FQDN is rejected and expected
580 // exception type is thrown.
581 buf.clear();
582 EXPECT_THROW(
583 OptionDataTypeUtil::writeFqdn("", buf),
584 isc::dhcp::BadDataTypeCast
585 );
586
587 // Check another invalid domain name (with repeated dot).
588 buf.clear();
589 EXPECT_THROW(
590 OptionDataTypeUtil::writeFqdn("example..com", buf),
591 isc::dhcp::BadDataTypeCast
592 );
593 }
594
595 // The purpose of this test is to verify that the variable length prefix
596 // can be read from a buffer correctly.
TEST_F(OptionDataTypesTest,readPrefix)597 TEST_F(OptionDataTypesTest, readPrefix) {
598 std::vector<uint8_t> buf;
599
600 // Prefix 2001:db8::/64
601 writeInt<uint8_t>(64, buf);
602 writeInt<uint32_t>(0x20010db8, buf);
603 writeInt<uint32_t>(0, buf);
604
605 PrefixTuple prefix(ZERO_PREFIX_TUPLE);
606 ASSERT_NO_THROW(prefix = OptionDataTypeUtil::readPrefix(buf));
607 EXPECT_EQ(64, prefix.first.asUnsigned());
608 EXPECT_EQ("2001:db8::", prefix.second.toText());
609
610 buf.clear();
611
612 // Prefix 2001:db8::/63
613 writeInt<uint8_t>(63, buf);
614 writeInt<uint32_t>(0x20010db8, buf);
615 writeInt<uint32_t>(0, buf);
616
617 ASSERT_NO_THROW(prefix = OptionDataTypeUtil::readPrefix(buf));
618 EXPECT_EQ(63, prefix.first.asUnsigned());
619 EXPECT_EQ("2001:db8::", prefix.second.toText());
620
621 buf.clear();
622
623 // Prefix 2001:db8:c0000. Note that the last four bytes are filled with
624 // 0xFF (all bits set). When the prefix is read those non-significant
625 // bits (beyond prefix length) should be ignored (read as 0). Only first
626 // two bits of 0xFFFFFFFF should be read, thus 0xC000, rather than 0xFFFF.
627 writeInt<uint8_t>(34, buf);
628 writeInt<uint32_t>(0x20010db8, buf);
629 writeInt<uint32_t>(0xFFFFFFFF, buf);
630
631 ASSERT_NO_THROW(prefix = OptionDataTypeUtil::readPrefix(buf));
632 EXPECT_EQ(34, prefix.first.asUnsigned());
633 EXPECT_EQ("2001:db8:c000::", prefix.second.toText());
634
635 buf.clear();
636
637 // Prefix having a length of 0.
638 writeInt<uint8_t>(0, buf);
639 writeInt<uint16_t>(0x2001, buf);
640
641 ASSERT_NO_THROW(prefix = OptionDataTypeUtil::readPrefix(buf));
642 EXPECT_EQ(0, prefix.first.asUnsigned());
643 EXPECT_EQ("::", prefix.second.toText());
644
645 buf.clear();
646
647 // Prefix having a maximum length of 128.
648 writeInt<uint8_t>(128, buf);
649 buf.insert(buf.end(), 16, 0x11);
650
651 ASSERT_NO_THROW(prefix = OptionDataTypeUtil::readPrefix(buf));
652 EXPECT_EQ(128, prefix.first.asUnsigned());
653 EXPECT_EQ("1111:1111:1111:1111:1111:1111:1111:1111",
654 prefix.second.toText());
655
656 buf.clear();
657
658 // Prefix length is greater than 128. This should result in an
659 // error.
660 writeInt<uint8_t>(129, buf);
661 writeInt<uint16_t>(0x3000, buf);
662 buf.resize(17);
663
664 EXPECT_THROW(static_cast<void>(OptionDataTypeUtil::readPrefix(buf)),
665 BadDataTypeCast);
666
667 buf.clear();
668
669 // Buffer truncated. Prefix length of 10 requires at least 2 bytes,
670 // but there is only one byte.
671 writeInt<uint8_t>(10, buf);
672 writeInt<uint8_t>(1, buf);
673
674 EXPECT_THROW(static_cast<void>(OptionDataTypeUtil::readPrefix(buf)),
675 BadDataTypeCast);
676 }
677
678 // The purpose of this test is to verify that the variable length prefix
679 // is written to a buffer correctly.
TEST_F(OptionDataTypesTest,writePrefix)680 TEST_F(OptionDataTypesTest, writePrefix) {
681 // Initialize a buffer and store some value in it. We'll want to make
682 // sure that the prefix being written will not override this value, but
683 // will rather be appended.
684 std::vector<uint8_t> buf(1, 1);
685
686 // Prefix 2001:db8:FFFF::/34 is equal to 2001:db8:C000::/34 because
687 // there are only 34 significant bits. All other bits must be zeroed.
688 ASSERT_NO_THROW(OptionDataTypeUtil::writePrefix(PrefixLen(34),
689 IOAddress("2001:db8:FFFF::"),
690 buf));
691 ASSERT_EQ(7, buf.size());
692
693 EXPECT_EQ(1, static_cast<unsigned>(buf[0]));
694 EXPECT_EQ(34, static_cast<unsigned>(buf[1]));
695 EXPECT_EQ(0x20, static_cast<unsigned>(buf[2]));
696 EXPECT_EQ(0x01, static_cast<unsigned>(buf[3]));
697 EXPECT_EQ(0x0D, static_cast<unsigned>(buf[4]));
698 EXPECT_EQ(0xB8, static_cast<unsigned>(buf[5]));
699 EXPECT_EQ(0xC0, static_cast<unsigned>(buf[6]));
700
701 buf.clear();
702
703 // Prefix length is 0. The entire prefix should be ignored.
704 ASSERT_NO_THROW(OptionDataTypeUtil::writePrefix(PrefixLen(0),
705 IOAddress("2001:db8:FFFF::"),
706 buf));
707 ASSERT_EQ(1, buf.size());
708 EXPECT_EQ(0, static_cast<unsigned>(buf[0]));
709
710 buf.clear();
711
712 // Prefix having a maximum length of 128.
713 ASSERT_NO_THROW(OptionDataTypeUtil::writePrefix(PrefixLen(128),
714 IOAddress("2001:db8::FF"),
715 buf));
716
717 // We should now have a 17 bytes long buffer. 1 byte goes for a prefix
718 // length field, the remaining ones hold the prefix.
719 ASSERT_EQ(17, buf.size());
720 // Because the prefix is 16 bytes long, we can simply use the
721 // IOAddress convenience function to read it back and compare
722 // it with the textual representation. This is simpler than
723 // comparing each byte separately.
724 IOAddress prefix_read = IOAddress::fromBytes(AF_INET6, &buf[1]);
725 EXPECT_EQ("2001:db8::ff", prefix_read.toText());
726
727 buf.clear();
728
729 // It is illegal to use IPv4 address as prefix.
730 EXPECT_THROW(OptionDataTypeUtil::writePrefix(PrefixLen(4),
731 IOAddress("10.0.0.1"), buf),
732 BadDataTypeCast);
733 }
734
735 // The purpose of this test is to verify that the
736 // PSID-len/PSID tuple can be read from a buffer.
TEST_F(OptionDataTypesTest,readPsid)737 TEST_F(OptionDataTypesTest, readPsid) {
738 std::vector<uint8_t> buf;
739
740 // PSID length is 6 (bits)
741 writeInt<uint8_t>(6, buf);
742 // 0xA400 is represented as 1010010000000000b, which is equivalent
743 // of portset 0x29 (101001b).
744 writeInt<uint16_t>(0xA400, buf);
745
746 PSIDTuple psid;
747 ASSERT_NO_THROW(psid = OptionDataTypeUtil::readPsid(buf));
748 EXPECT_EQ(6, psid.first.asUnsigned());
749 EXPECT_EQ(0x29, psid.second.asUint16());
750
751 buf.clear();
752
753 // PSID length is 16 (bits)
754 writeInt<uint8_t>(16, buf);
755 // 0xF000 is represented as 1111000000000000b, which is equivalent
756 // of portset 0xF000.
757 writeInt<uint16_t>(0xF000, buf);
758
759 ASSERT_NO_THROW(psid = OptionDataTypeUtil::readPsid(buf));
760 EXPECT_EQ(16, psid.first.asUnsigned());
761 EXPECT_EQ(0xF000, psid.second.asUint16());
762
763 buf.clear();
764
765 // PSID length is 0, in which case PSID should be ignored.
766 writeInt<uint8_t>(0, buf);
767 // Let's put some junk into the PSID field to make sure it will
768 // be ignored.
769 writeInt<uint16_t>(0x1234, buf);
770 ASSERT_NO_THROW(psid = OptionDataTypeUtil::readPsid(buf));
771 EXPECT_EQ(0, psid.first.asUnsigned());
772 EXPECT_EQ(0, psid.second.asUint16());
773
774 buf.clear();
775
776 // PSID length greater than 16 is not allowed.
777 writeInt<uint8_t>(17, buf);
778 writeInt<uint16_t>(0, buf);
779 EXPECT_THROW(static_cast<void>(OptionDataTypeUtil::readPsid(buf)),
780 BadDataTypeCast);
781
782 buf.clear();
783
784 // PSID length is 3 bits, but the PSID value is 11 (1011b), so it
785 // is encoded on 4 bits, rather than 3.
786 writeInt<uint8_t>(3, buf);
787 writeInt<uint16_t>(0xB000, buf);
788 EXPECT_THROW(static_cast<void>(OptionDataTypeUtil::readPsid(buf)),
789 BadDataTypeCast);
790
791 buf.clear();
792
793 // Buffer is truncated - 2 bytes instead of 3.
794 writeInt<uint8_t>(4, buf);
795 writeInt<uint8_t>(0xF0, buf);
796 EXPECT_THROW(static_cast<void>(OptionDataTypeUtil::readPsid(buf)),
797 BadDataTypeCast);
798
799 // Check for out of range values.
800 for (int i = 1; i < 16; ++i) {
801 buf.clear();
802 writeInt<uint8_t>(i, buf);
803 writeInt<uint16_t>(0xFFFF << (15 - i), buf);
804 EXPECT_THROW(static_cast<void>(OptionDataTypeUtil::readPsid(buf)),
805 BadDataTypeCast);
806 }
807
808 }
809
810 // The purpose of this test is to verify that the PSID-len/PSID
811 // tuple is written to a buffer correctly.
TEST_F(OptionDataTypesTest,writePsid)812 TEST_F(OptionDataTypesTest, writePsid) {
813 // Let's create a buffer with some data in it. We want to make
814 // sure that the existing data remain untouched when we write
815 // PSID to the buffer.
816 std::vector<uint8_t> buf(1, 1);
817 // PSID length is 4 (bits), PSID value is 8.
818 ASSERT_NO_THROW(OptionDataTypeUtil::writePsid(PSIDLen(4), PSID(8), buf));
819 ASSERT_EQ(4, buf.size());
820 // The byte which existed in the buffer should still hold the
821 // same value.
822 EXPECT_EQ(1, static_cast<unsigned>(buf[0]));
823 // PSID length should be written as specified in the function call.
824 EXPECT_EQ(4, static_cast<unsigned>(buf[1]));
825 // The PSID structure is as follows:
826 // UUUUPPPPPPPPPPPP, where "U" are useful bits on which we code
827 // the PSID. "P" are zero padded bits. The PSID value 8 is coded
828 // on four useful bits as '1000b'. That means that the PSID value
829 // encoded in the PSID field is: '1000000000000000b', which is
830 // 0x8000. The next two EXPECT_EQ statements verify that.
831 EXPECT_EQ(0x80, static_cast<unsigned>(buf[2]));
832 EXPECT_EQ(0x00, static_cast<unsigned>(buf[3]));
833
834 // Clear the buffer to make sure we don't append to the
835 // existing data.
836 buf.clear();
837
838 // The PSID length of 0 causes the PSID value (of 6) to be ignored.
839 // As a result, the buffer should hold only zeros.
840 ASSERT_NO_THROW(OptionDataTypeUtil::writePsid(PSIDLen(0), PSID(6), buf));
841 ASSERT_EQ(3, buf.size());
842 EXPECT_EQ(0, static_cast<unsigned>(buf[0]));
843 EXPECT_EQ(0, static_cast<unsigned>(buf[1]));
844 EXPECT_EQ(0, static_cast<unsigned>(buf[2]));
845
846 buf.clear();
847
848 // Another test case, to verify that we can use the maximum length
849 // of PSID (16 bits).
850 ASSERT_NO_THROW(OptionDataTypeUtil::writePsid(PSIDLen(16), PSID(5), buf));
851 ASSERT_EQ(3, buf.size());
852 // PSID length should be written with no change.
853 EXPECT_EQ(16, static_cast<unsigned>(buf[0]));
854 // Check PSID value.
855 EXPECT_EQ(0x00, static_cast<unsigned>(buf[1]));
856 EXPECT_EQ(0x05, static_cast<unsigned>(buf[2]));
857
858 // PSID length of 17 exceeds the maximum allowed value of 16.
859 EXPECT_THROW(OptionDataTypeUtil::writePsid(PSIDLen(17), PSID(1), buf),
860 OutOfRange);
861
862 // Check for out of range values.
863 for (int i = 1; i < 16; ++i) {
864 EXPECT_THROW(OptionDataTypeUtil::writePsid(PSIDLen(i), PSID(1 << i), buf),
865 BadDataTypeCast);
866 }
867 }
868
869 // The purpose of this test is to verify that the string
870 // can be read from a buffer correctly.
TEST_F(OptionDataTypesTest,readString)871 TEST_F(OptionDataTypesTest, readString) {
872
873 // Prepare a buffer with some string in it.
874 std::vector<uint8_t> buf;
875 writeString("hello world", buf);
876
877 // Read the string from the buffer.
878 std::string value;
879 ASSERT_NO_THROW(
880 value = OptionDataTypeUtil::readString(buf);
881 );
882 // Check that it is valid.
883 EXPECT_EQ("hello world", value);
884
885 // Only nulls should throw.
886 OptionBuffer buffer = { 0, 0 };
887 ASSERT_THROW(OptionDataTypeUtil::readString(buffer), isc::OutOfRange);
888
889 // One trailing null should trim off.
890 buffer = {'o', 'n', 'e', 0 };
891 ASSERT_NO_THROW(value = OptionDataTypeUtil::readString(buffer));
892 EXPECT_EQ(3, value.length());
893 EXPECT_EQ(value, std::string("one"));
894
895 // More than one trailing null should trim off.
896 buffer = { 't', 'h', 'r', 'e', 'e', 0, 0, 0 };
897 ASSERT_NO_THROW(value = OptionDataTypeUtil::readString(buffer));
898 EXPECT_EQ(5, value.length());
899 EXPECT_EQ(value, std::string("three"));
900
901 // Embedded null should be left in place.
902 buffer = { 'e', 'm', 0, 'b', 'e', 'd' };
903 ASSERT_NO_THROW(value = OptionDataTypeUtil::readString(buffer));
904 EXPECT_EQ(6, value.length());
905 EXPECT_EQ(value, (std::string{"em\0bed", 6}));
906
907 // Leading null should be left in place.
908 buffer = { 0, 'l', 'e', 'a', 'd', 'i', 'n', 'g' };
909 ASSERT_NO_THROW(value = OptionDataTypeUtil::readString(buffer));
910 EXPECT_EQ(8, value.length());
911 EXPECT_EQ(value, (std::string{"\0leading", 8}));
912 }
913
914 // The purpose of this test is to verify that a string can be
915 // stored in a buffer correctly.
TEST_F(OptionDataTypesTest,writeString)916 TEST_F(OptionDataTypesTest, writeString) {
917 // Prepare a buffer with a reference data.
918 std::vector<uint8_t> buf_ref;
919 writeString("hello world!", buf_ref);
920 // Create empty buffer we will write to.
921 std::vector<uint8_t> buf;
922 ASSERT_NO_THROW(OptionDataTypeUtil::writeString("hello world!", buf));
923 // Compare two buffers.
924 ASSERT_EQ(buf_ref.size(), buf.size());
925 EXPECT_TRUE(std::equal(buf_ref.begin(), buf_ref.end(), buf.begin()));
926 }
927
928 } // anonymous namespace
929