1 // Copyright (C) 2013-2020 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
9 #include <dhcp_ddns/ncr_io.h>
10 #include <dhcp/duid.h>
11 #include <dhcp/hwaddr.h>
12 #include <util/time_utilities.h>
13
14 #include <testutils/gtest_utils.h>
15 #include <gtest/gtest.h>
16 #include <algorithm>
17
18 using namespace std;
19 using namespace isc;
20 using namespace isc::dhcp_ddns;
21 using namespace isc::dhcp;
22
23 namespace {
24
25 /// @brief Defines a list of valid JSON NameChangeRequest renditions.
26 /// They are used as input to test conversion from JSON.
27 const char *valid_msgs[] =
28 {
29 // Valid Add.
30 "{"
31 " \"change-type\" : 0 , "
32 " \"forward-change\" : true , "
33 " \"reverse-change\" : false , "
34 " \"fqdn\" : \"walah.walah.com\" , "
35 " \"ip-address\" : \"192.168.2.1\" , "
36 " \"dhcid\" : \"010203040A7F8E3D\" , "
37 " \"lease-expires-on\" : \"20130121132405\" , "
38 " \"lease-length\" : 1300, "
39 " \"use-conflict-resolution\": true"
40 "}",
41 // Valid Remove.
42 "{"
43 " \"change-type\" : 1 , "
44 " \"forward-change\" : true , "
45 " \"reverse-change\" : false , "
46 " \"fqdn\" : \"walah.walah.com\" , "
47 " \"ip-address\" : \"192.168.2.1\" , "
48 " \"dhcid\" : \"010203040A7F8E3D\" , "
49 " \"lease-expires-on\" : \"20130121132405\" , "
50 " \"lease-length\" : 1300, "
51 " \"use-conflict-resolution\": true"
52 "}",
53 // Valid Add with IPv6 address
54 "{"
55 " \"change-type\" : 0 , "
56 " \"forward-change\" : true , "
57 " \"reverse-change\" : false , "
58 " \"fqdn\" : \"walah.walah.com\" , "
59 " \"ip-address\" : \"fe80::2acf:e9ff:fe12:e56f\" , "
60 " \"dhcid\" : \"010203040A7F8E3D\" , "
61 " \"lease-expires-on\" : \"20130121132405\" , "
62 " \"lease-length\" : 1300, "
63 " \"use-conflict-resolution\": true"
64 "}",
65 // Missing use-conflict-resolution
66 "{"
67 " \"change-type\" : 0 , "
68 " \"forward-change\" : true , "
69 " \"reverse-change\" : false , "
70 " \"fqdn\" : \"walah.walah.com\" , "
71 " \"ip-address\" : \"192.168.2.1\" , "
72 " \"dhcid\" : \"010203040A7F8E3D\" , "
73 " \"lease-expires-on\" : \"20130121132405\" , "
74 " \"lease-length\" : 1300 "
75 "}"
76 };
77
78 /// @brief Defines a list of invalid JSON NameChangeRequest renditions.
79 /// They are used as input to test conversion from JSON.
80 const char *invalid_msgs[] =
81 {
82 // Invalid change type.
83 "{"
84 " \"change-type\" : 7 , "
85 " \"forward-change\" : true , "
86 " \"reverse-change\" : false , "
87 " \"fqdn\" : \"walah.walah.com\" , "
88 " \"ip-address\" : \"192.168.2.1\" , "
89 " \"dhcid\" : \"010203040A7F8E3D\" , "
90 " \"lease-expires-on\" : \"20130121132405\" , "
91 " \"lease-length\" : 1300, "
92 " \"use-conflict-resolution\": true"
93 "}",
94 // Invalid forward change.
95 "{"
96 " \"change-type\" : 0 , "
97 " \"forward-change\" : \"bogus\" , "
98 " \"reverse-change\" : false , "
99 " \"fqdn\" : \"walah.walah.com\" , "
100 " \"ip-address\" : \"192.168.2.1\" , "
101 " \"dhcid\" : \"010203040A7F8E3D\" , "
102 " \"lease-expires-on\" : \"20130121132405\" , "
103 " \"lease-length\" : 1300, "
104 " \"use-conflict-resolution\": true"
105 "}",
106 // Invalid reverse change.
107 "{"
108 " \"change-type\" : 0 , "
109 " \"forward-change\" : true , "
110 " \"reverse-change\" : 500 , "
111 " \"fqdn\" : \"walah.walah.com\" , "
112 " \"ip-address\" : \"192.168.2.1\" , "
113 " \"dhcid\" : \"010203040A7F8E3D\" , "
114 " \"lease-expires-on\" : \"20130121132405\" , "
115 " \"lease-length\" : 1300, "
116 " \"use-conflict-resolution\": true"
117 "}",
118 // Forward and reverse change both false.
119 "{"
120 " \"change-type\" : 0 , "
121 " \"forward-change\" : false , "
122 " \"reverse-change\" : false , "
123 " \"fqdn\" : \"walah.walah.com\" , "
124 " \"ip-address\" : \"192.168.2.1\" , "
125 " \"dhcid\" : \"010203040A7F8E3D\" , "
126 " \"lease-expires-on\" : \"20130121132405\" , "
127 " \"lease-length\" : 1300, "
128 " \"use-conflict-resolution\": true"
129 "}",
130 // Blank FQDN
131 "{"
132 " \"change-type\" : 0 , "
133 " \"forward-change\" : true , "
134 " \"reverse-change\" : false , "
135 " \"fqdn\" : \"\" , "
136 " \"ip-address\" : \"192.168.2.1\" , "
137 " \"dhcid\" : \"010203040A7F8E3D\" , "
138 " \"lease-expires-on\" : \"20130121132405\" , "
139 " \"lease-length\" : 1300, "
140 " \"use-conflict-resolution\": true"
141 "}",
142 // Malformed FQDN
143 "{"
144 " \"change-type\" : 0 , "
145 " \"forward-change\" : true , "
146 " \"reverse-change\" : false , "
147 " \"fqdn\" : \".bad_name\" , "
148 " \"ip-address\" : \"192.168.2.1\" , "
149 " \"dhcid\" : \"010203040A7F8E3D\" , "
150 " \"lease-expires-on\" : \"20130121132405\" , "
151 " \"lease-length\" : 1300, "
152 " \"use-conflict-resolution\": true"
153 "}",
154 // Bad IP address
155 "{"
156 " \"change-type\" : 0 , "
157 " \"forward-change\" : true , "
158 " \"reverse-change\" : false , "
159 " \"fqdn\" : \"walah.walah.com\" , "
160 " \"ip-address\" : \"xxxxxx\" , "
161 " \"dhcid\" : \"010203040A7F8E3D\" , "
162 " \"lease-expires-on\" : \"20130121132405\" , "
163 " \"lease-length\" : 1300 "
164 " \"use-conflict-resolution\": true"
165 "}",
166 // Blank DHCID
167 "{"
168 " \"change-type\" : 0 , "
169 " \"forward-change\" : true , "
170 " \"reverse-change\" : false , "
171 " \"fqdn\" : \"walah.walah.com\" , "
172 " \"ip-address\" : \"192.168.2.1\" , "
173 " \"dhcid\" : \"\" , "
174 " \"lease-expires-on\" : \"20130121132405\" , "
175 " \"lease-length\" : 1300, "
176 " \"use-conflict-resolution\": true"
177 "}",
178 // Odd number of digits in DHCID
179 "{"
180 " \"change-type\" : 0 , "
181 " \"forward-change\" : true , "
182 " \"reverse-change\" : false , "
183 " \"fqdn\" : \"walah.walah.com\" , "
184 " \"ip-address\" : \"192.168.2.1\" , "
185 " \"dhcid\" : \"010203040A7F8E3\" , "
186 " \"lease-expires-on\" : \"20130121132405\" , "
187 " \"lease-length\" : 1300, "
188 " \"use-conflict-resolution\": true"
189 "}",
190 // Text in DHCID
191 "{"
192 " \"change-type\" : 0 , "
193 " \"forward-change\" : true , "
194 " \"reverse-change\" : false , "
195 " \"fqdn\" : \"walah.walah.com\" , "
196 " \"ip-address\" : \"192.168.2.1\" , "
197 " \"dhcid\" : \"THIS IS BOGUS!!!\" , "
198 " \"lease-expires-on\" : \"20130121132405\" , "
199 " \"lease-length\" : 1300, "
200 " \"use-conflict-resolution\": true"
201 "}",
202 // Invalid lease expiration string
203 "{"
204 " \"change-type\" : 0 , "
205 " \"forward-change\" : true , "
206 " \"reverse-change\" : false , "
207 " \"fqdn\" : \"walah.walah.com\" , "
208 " \"ip-address\" : \"192.168.2.1\" , "
209 " \"dhcid\" : \"010203040A7F8E3D\" , "
210 " \"lease-expires-on\" : \"Wed Jun 26 13:46:46 EDT 2013\" , "
211 " \"lease-length\" : 1300, "
212 " \"use-conflict-resolution\": true"
213 "}",
214 // Non-integer for lease length.
215 "{"
216 " \"change-type\" : 0 , "
217 " \"forward-change\" : true , "
218 " \"reverse-change\" : false , "
219 " \"fqdn\" : \"walah.walah.com\" , "
220 " \"ip-address\" : \"192.168.2.1\" , "
221 " \"dhcid\" : \"010203040A7F8E3D\" , "
222 " \"lease-expires-on\" : \"20130121132405\" , "
223 " \"lease-length\" : \"BOGUS\", "
224 " \"use-conflict-resolution\": true"
225 "}",
226 // Invalid use-conflict-resolution
227 "{"
228 " \"change-type\" : 0 , "
229 " \"forward-change\" : true , "
230 " \"reverse-change\" : false , "
231 " \"fqdn\" : \"walah.walah.com\" , "
232 " \"ip-address\" : \"192.168.2.1\" , "
233 " \"dhcid\" : \"010203040A7F8E3D\" , "
234 " \"lease-expires-on\" : \"20130121132405\" , "
235 " \"lease-length\" : 1300, "
236 " \"use-conflict-resolution\": 777"
237 "}"
238 };
239
240 /// @brief Tests the NameChangeRequest constructors.
241 /// This test verifies that:
242 /// 1. Default constructor works.
243 /// 2. "Full" constructor, when given valid parameter values, works.
244 /// 3. "Full" constructor, given a blank FQDN fails
245 /// 4. "Full" constructor, given an invalid IP Address FQDN fails
246 /// 5. "Full" constructor, given a blank DHCID fails
247 /// 6. "Full" constructor, given false for both forward and reverse fails
TEST(NameChangeRequestTest,constructionTests)248 TEST(NameChangeRequestTest, constructionTests) {
249 // Verify the default constructor works.
250 NameChangeRequestPtr ncr;
251 EXPECT_NO_THROW(ncr.reset(new NameChangeRequest()));
252 EXPECT_TRUE(ncr);
253
254 // Verify that full constructor works.
255 uint64_t expiry = isc::util::detail::gettimeWrapper();
256 D2Dhcid dhcid("010203040A7F8E3D");
257
258 EXPECT_NO_THROW(ncr.reset(new NameChangeRequest(
259 CHG_ADD, true, true, "walah.walah.com",
260 "192.168.1.101", dhcid, expiry, 1300)));
261 EXPECT_TRUE(ncr);
262 ncr.reset();
263
264 // Verify blank FQDN is detected.
265 EXPECT_THROW(NameChangeRequest(CHG_ADD, true, true, "",
266 "192.168.1.101", dhcid, expiry, 1300), NcrMessageError);
267
268 // Verify that an invalid IP address is detected.
269 EXPECT_THROW(NameChangeRequest(CHG_ADD, true, true, "valid.fqdn",
270 "xxx.168.1.101", dhcid, expiry, 1300), NcrMessageError);
271
272 // Verify that a blank DHCID is detected.
273 D2Dhcid blank_dhcid;
274 EXPECT_THROW(NameChangeRequest(CHG_ADD, true, true, "walah.walah.com",
275 "192.168.1.101", blank_dhcid, expiry, 1300), NcrMessageError);
276
277 // Verify that one or both of direction flags must be true.
278 EXPECT_THROW(NameChangeRequest(CHG_ADD, false, false, "valid.fqdn",
279 "192.168.1.101", dhcid, expiry, 1300), NcrMessageError);
280
281 }
282
283 /// @brief Tests the basic workings of D2Dhcid to and from string conversions.
284 /// It verifies that:
285 /// 1. DHCID input strings must contain an even number of characters
286 /// 2. DHCID input strings must contain only hexadecimal character digits
287 /// 3. A valid DHCID string converts correctly.
288 /// 4. Converting a D2Dhcid to a string works correctly.
289 /// 5. Equality, inequality, and less-than-equal operators work.
TEST(NameChangeRequestTest,dhcidTest)290 TEST(NameChangeRequestTest, dhcidTest) {
291 D2Dhcid dhcid;
292
293 // Odd number of digits should be rejected.
294 std::string test_str = "010203040A7F8E3";
295 EXPECT_THROW(dhcid.fromStr(test_str), NcrMessageError);
296
297 // Non digit content should be rejected.
298 test_str = "0102BOGUSA7F8E3D";
299 EXPECT_THROW(dhcid.fromStr(test_str), NcrMessageError);
300
301 // Verify that valid input converts into a proper byte array.
302 test_str = "010203040A7F8E3D";
303 ASSERT_NO_THROW(dhcid.fromStr(test_str));
304
305 // Create a test vector of expected byte contents.
306 const uint8_t bytes[] = { 0x1, 0x2, 0x3, 0x4, 0xA, 0x7F, 0x8E, 0x3D };
307 std::vector<uint8_t> expected_bytes(bytes, bytes + sizeof(bytes));
308
309 // Fetch the byte vector from the dhcid and verify if equals the expected
310 // content.
311 const std::vector<uint8_t>& converted_bytes = dhcid.getBytes();
312 EXPECT_EQ(expected_bytes.size(), converted_bytes.size());
313 EXPECT_TRUE (std::equal(expected_bytes.begin(),
314 expected_bytes.begin()+expected_bytes.size(),
315 converted_bytes.begin()));
316
317 // Convert the new dhcid back to string and verify it matches the original
318 // DHCID input string.
319 std::string next_str = dhcid.toStr();
320 EXPECT_EQ(test_str, next_str);
321
322 // Test equality, inequality, and less-than-equal operators
323 test_str="AABBCCDD";
324 EXPECT_NO_THROW(dhcid.fromStr(test_str));
325
326 D2Dhcid other_dhcid;
327 EXPECT_NO_THROW(other_dhcid.fromStr(test_str));
328
329 EXPECT_TRUE(dhcid == other_dhcid);
330 EXPECT_FALSE(dhcid != other_dhcid);
331
332 EXPECT_NO_THROW(other_dhcid.fromStr("BBCCDDEE"));
333 EXPECT_TRUE(dhcid < other_dhcid);
334
335 }
336
337 /// @brief Test fixture class for testing DHCID creation.
338 class DhcidTest : public ::testing::Test {
339 public:
340 /// @brief Constructor
DhcidTest()341 DhcidTest() {
342 const uint8_t fqdn_data[] = {
343 6, 109, 121, 104, 111, 115, 116, // myhost.
344 7, 101, 120, 97, 109, 112, 108, 101, // example.
345 3, 99, 111, 109, 0 // com.
346 };
347 wire_fqdn_.assign(fqdn_data, fqdn_data + sizeof(fqdn_data));
348 }
349
350 /// @brief Destructor
~DhcidTest()351 virtual ~DhcidTest() {
352 }
353
354 std::vector<uint8_t> wire_fqdn_;
355 };
356
357 /// Tests that DHCID is correctly created from a DUID and FQDN. The final format
358 /// of the DHCID is as follows:
359 /// <identifier-type> <digest-type-code> <digest>
360 /// where:
361 /// - identifier-type (2 octets) is 0x0002.
362 /// - digest-type-code (1 octet) indicates SHA-256 hashing and is equal 0x1.
363 /// - digest = SHA-256(<DUID> <FQDN>)
364 /// Note: FQDN is given in the on-wire canonical format.
TEST_F(DhcidTest,fromDUID)365 TEST_F(DhcidTest, fromDUID) {
366 D2Dhcid dhcid;
367
368 // Create DUID.
369 uint8_t duid_data[] = { 0, 1, 2, 3, 4, 5, 6 };
370 DUID duid(duid_data, sizeof(duid_data));
371
372 // Create DHCID.
373 ASSERT_NO_THROW(dhcid.fromDUID(duid, wire_fqdn_));
374
375 // The reference DHCID (represented as string of hexadecimal digits)
376 // has been calculated using one of the online calculators.
377 std::string dhcid_ref = "0002012191B7B21AF97E0E656DF887C5E2D"
378 "EF30E7758A207EDF4CCB2DE8CA37066021C";
379
380 // Make sure that the DHCID is valid.
381 EXPECT_EQ(dhcid_ref, dhcid.toStr());
382 }
383
384 // Test that DHCID is correctly created when the DUID has minimal length (1).
TEST_F(DhcidTest,fromMinDUID)385 TEST_F(DhcidTest, fromMinDUID) {
386 D2Dhcid dhcid;
387
388 // Create DUID.
389 uint8_t duid_data[] = { 1 };
390 DUID duid(duid_data, sizeof(duid_data));
391
392 // Create DHCID.
393 ASSERT_NO_THROW(dhcid.fromDUID(duid, wire_fqdn_));
394
395 // The reference DHCID (represented as string of hexadecimal digits)
396 // has been calculated using one of the online calculators.
397 std::string dhcid_ref = "000201F89004F73E60CAEDFF514E11CB91D"
398 "1F45C8F0A55D4BC4C688484A819F8EA4074";
399
400 // Make sure that the DHCID is valid.
401 EXPECT_EQ(dhcid_ref, dhcid.toStr());
402 }
403
404 // Test that DHCID is correctly created when the DUID has maximal length (128).
TEST_F(DhcidTest,fromMaxDUID)405 TEST_F(DhcidTest, fromMaxDUID) {
406 D2Dhcid dhcid;
407
408 // Create DUID.
409 std::vector<uint8_t> duid_data(128, 1);
410 DUID duid(&duid_data[0], duid_data.size());
411
412 // Create DHCID.
413 ASSERT_NO_THROW(dhcid.fromDUID(duid, wire_fqdn_));
414
415 // The reference DHCID (represented as string of hexadecimal digits)
416 // has been calculated using one of the online calculators.
417 std::string dhcid_ref = "00020137D8FBDC0585B44DFA03FAD2E36C6"
418 "159737D545A12EFB40B0D88D110A5748234";
419
420 // Make sure that the DHCID is valid.
421 EXPECT_EQ(dhcid_ref, dhcid.toStr());
422 }
423
424 // This test verifies that DHCID is properly computed from a buffer holding
425 // client identifier data.
TEST_F(DhcidTest,fromClientId)426 TEST_F(DhcidTest, fromClientId) {
427 D2Dhcid dhcid;
428
429 // Create a buffer holding client id..
430 uint8_t clientid_data[] = { 0, 1, 2, 3, 4, 5, 6 };
431 std::vector<uint8_t> clientid(clientid_data,
432 clientid_data + sizeof(clientid_data));
433
434 // Create DHCID.
435 ASSERT_NO_THROW(dhcid.fromClientId(clientid, wire_fqdn_));
436
437 // The reference DHCID (represented as string of hexadecimal digits)
438 // has been calculated using one of the online calculators.
439 std::string dhcid_ref = "0001012191B7B21AF97E0E656DF887C5E2D"
440 "EF30E7758A207EDF4CCB2DE8CA37066021C";
441
442 // Make sure that the DHCID is valid.
443 EXPECT_EQ(dhcid_ref, dhcid.toStr());
444
445 // Make sure that the empty FQDN is not accepted.
446 std::vector<uint8_t> empty_wire_fqdn;
447 EXPECT_THROW(dhcid.fromClientId(clientid, empty_wire_fqdn),
448 isc::dhcp_ddns::DhcidRdataComputeError);
449
450 // Make sure that the empty client identifier is not accepted.
451 clientid.clear();
452 EXPECT_THROW(dhcid.fromClientId(clientid, wire_fqdn_),
453 isc::dhcp_ddns::DhcidRdataComputeError);
454
455
456 }
457
458 // This test verifies that DHCID is properly computed from a HW address.
TEST_F(DhcidTest,fromHWAddr)459 TEST_F(DhcidTest, fromHWAddr) {
460 D2Dhcid dhcid;
461
462 // Create a buffer holding client id..
463 uint8_t hwaddr_data[] = { 0, 1, 2, 3, 4, 5, 6 };
464 HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data),
465 HTYPE_ETHER));
466
467 // Create DHCID.
468 ASSERT_NO_THROW(dhcid.fromHWAddr(hwaddr, wire_fqdn_));
469
470 // The reference DHCID (represented as string of hexadecimal digits)
471 // has been calculated using one of the online calculators.
472 std::string dhcid_ref = "0000012247F6DC4423C3E8627434A9D686860"
473 "9D88948F78018B215EDCAA30C0C135035";
474
475 // Make sure that the DHCID is valid.
476 EXPECT_EQ(dhcid_ref, dhcid.toStr());
477
478 // Make sure that the empty FQDN is not accepted.
479 std::vector<uint8_t> empty_wire_fqdn;
480 EXPECT_THROW(dhcid.fromHWAddr(hwaddr, empty_wire_fqdn),
481 isc::dhcp_ddns::DhcidRdataComputeError);
482
483 // Make sure that the NULL HW address is not accepted.
484 hwaddr.reset();
485 EXPECT_THROW(dhcid.fromHWAddr(hwaddr, wire_fqdn_),
486 isc::dhcp_ddns::DhcidRdataComputeError);
487 }
488
489 // test operator<< on D2Dhcid
TEST(NameChangeRequestTest,leftShiftOperation)490 TEST(NameChangeRequestTest, leftShiftOperation) {
491 const D2Dhcid dhcid("010203040A7F8E3D");
492
493 ostringstream oss;
494 oss << dhcid;
495 EXPECT_EQ(dhcid.toStr(), oss.str());
496 }
497
498 /// @brief Verifies the fundamentals of converting from and to JSON.
499 /// It verifies that:
500 /// 1. A NameChangeRequest can be created from a valid JSON string.
501 /// 2. A valid JSON string can be created from a NameChangeRequest
TEST(NameChangeRequestTest,basicJsonTest)502 TEST(NameChangeRequestTest, basicJsonTest) {
503 // Define valid JSON rendition of a request.
504 std::string msg_str = "{"
505 "\"change-type\":1,"
506 "\"forward-change\":true,"
507 "\"reverse-change\":false,"
508 "\"fqdn\":\"walah.walah.com.\","
509 "\"ip-address\":\"192.168.2.1\","
510 "\"dhcid\":\"010203040A7F8E3D\","
511 "\"lease-expires-on\":\"20130121132405\","
512 "\"lease-length\":1300,"
513 "\"use-conflict-resolution\":true"
514 "}";
515
516 // Verify that a NameChangeRequests can be instantiated from the
517 // a valid JSON rendition.
518 NameChangeRequestPtr ncr;
519 ASSERT_NO_THROW_LOG(ncr = NameChangeRequest::fromJSON(msg_str));
520 ASSERT_TRUE(ncr);
521
522 // Verify that the JSON string created by the new request equals the
523 // original input string.
524 std::string json_str = ncr->toJSON();
525 EXPECT_EQ(msg_str, json_str);
526
527 // Verify that the request ID matches the string from the DHCID.
528 std::string dhcid_str = "010203040A7F8E3D";
529 EXPECT_EQ(dhcid_str, ncr->getRequestId());
530 }
531
532 /// @brief Tests a variety of invalid JSON message strings.
533 /// This test iterates over a list of JSON messages, each containing a single
534 /// content error. The list of messages is defined by the global array,
535 /// invalid_messages. Currently that list contains the following invalid
536 /// conditions:
537 /// 1. Invalid change type
538 /// 2. Invalid forward change
539 /// 3. Invalid reverse change
540 /// 4. Forward and reverse change both false
541 /// 5. Invalid forward change
542 /// 6. Blank FQDN
543 /// 7. Bad IP address
544 /// 8. Blank DHCID
545 /// 9. Odd number of digits in DHCID
546 /// 10. Text in DHCID
547 /// 11. Invalid lease expiration string
548 /// 12. Non-integer for lease length.
549 /// If more permutations arise they can easily be added to the list.
TEST(NameChangeRequestTest,invalidMsgChecks)550 TEST(NameChangeRequestTest, invalidMsgChecks) {
551 // Iterate over the list of JSON strings, attempting to create a
552 // NameChangeRequest. The attempt should throw a NcrMessageError.
553 int num_msgs = sizeof(invalid_msgs)/sizeof(char*);
554 for (int i = 0; i < num_msgs; i++) {
555 EXPECT_THROW(NameChangeRequest::fromJSON(invalid_msgs[i]),
556 NcrMessageError) << "Invalid message not caught idx: "
557 << i << std::endl << " text:[" << invalid_msgs[i] << "]"
558 << std::endl;
559 }
560 }
561
562 /// @brief Tests a variety of valid JSON message strings.
563 /// This test iterates over a list of JSON messages, each containing a single
564 /// valid request rendition. The list of messages is defined by the global
565 /// array, valid_messages. Currently that list contains the following valid
566 /// messages:
567 /// 1. Valid, IPv4 Add
568 /// 2. Valid, IPv4 Remove
569 /// 3. Valid, IPv6 Add
570 /// If more permutations arise they can easily be added to the list.
TEST(NameChangeRequestTest,validMsgChecks)571 TEST(NameChangeRequestTest, validMsgChecks) {
572 // Iterate over the list of JSON strings, attempting to create a
573 // NameChangeRequest. The attempt should succeed.
574 int num_msgs = sizeof(valid_msgs)/sizeof(char*);
575 for (int i = 0; i < num_msgs; i++) {
576 EXPECT_NO_THROW(NameChangeRequest::fromJSON(valid_msgs[i]))
577 << "Valid message failed, message idx: " << i
578 << std::endl << " text:[" << valid_msgs[i] << "]"
579 << std::endl;
580 }
581 }
582
583 /// @brief Tests converting to and from JSON via isc::util buffer classes.
584 /// This test verifies that:
585 /// 1. A NameChangeRequest can be rendered in JSON written to an OutputBuffer
586 /// 2. A InputBuffer containing a valid JSON request rendition can be used
587 /// to create a NameChangeRequest.
TEST(NameChangeRequestTest,toFromBufferTest)588 TEST(NameChangeRequestTest, toFromBufferTest) {
589 // Define a string containing a valid JSON NameChangeRequest rendition.
590 std::string msg_str = "{"
591 "\"change-type\":1,"
592 "\"forward-change\":true,"
593 "\"reverse-change\":false,"
594 "\"fqdn\":\"walah.walah.com.\","
595 "\"ip-address\":\"192.168.2.1\","
596 "\"dhcid\":\"010203040A7F8E3D\","
597 "\"lease-expires-on\":\"20130121132405\","
598 "\"lease-length\":1300,"
599 "\"use-conflict-resolution\":true"
600 "}";
601
602 // Create a request from JSON directly.
603 NameChangeRequestPtr ncr;
604 ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(msg_str));
605
606 // Verify that we output the request as JSON text to a buffer
607 // without error.
608 isc::util::OutputBuffer output_buffer(1024);
609 ASSERT_NO_THROW(ncr->toFormat(FMT_JSON, output_buffer));
610
611 // Make an InputBuffer from the OutputBuffer.
612 isc::util::InputBuffer input_buffer(output_buffer.getData(),
613 output_buffer.getLength());
614
615 // Verify that we can create a new request from the InputBuffer.
616 NameChangeRequestPtr ncr2;
617 ASSERT_NO_THROW(ncr2 =
618 NameChangeRequest::fromFormat(FMT_JSON, input_buffer));
619
620 // Convert the new request to JSON directly.
621 std::string final_str = ncr2->toJSON();
622
623 // Verify that the final string matches the original.
624 ASSERT_EQ(final_str, msg_str);
625 }
626
627 /// @brief Tests ip address modification and validation
TEST(NameChangeRequestTest,ipAddresses)628 TEST(NameChangeRequestTest, ipAddresses) {
629 NameChangeRequest ncr;
630
631 // Verify that a valid IPv4 address works.
632 ASSERT_NO_THROW(ncr.setIpAddress("192.168.1.1"));
633 const asiolink::IOAddress& io_addr4 = ncr.getIpIoAddress();
634 EXPECT_EQ(ncr.getIpAddress(), io_addr4.toText());
635 EXPECT_TRUE(ncr.isV4());
636 EXPECT_FALSE(ncr.isV6());
637
638 // Verify that a valid IPv6 address works.
639 ASSERT_NO_THROW(ncr.setIpAddress("2001:1::f3"));
640 const asiolink::IOAddress& io_addr6 = ncr.getIpIoAddress();
641 EXPECT_EQ(ncr.getIpAddress(), io_addr6.toText());
642 EXPECT_FALSE(ncr.isV4());
643 EXPECT_TRUE(ncr.isV6());
644
645 // Verify that an invalid address fails.
646 ASSERT_THROW(ncr.setIpAddress("x001:1::f3"),NcrMessageError);
647 }
648
649 /// @brief Tests conversion of NameChangeFormat between enum and strings.
TEST(NameChangeFormatTest,formatEnumConversion)650 TEST(NameChangeFormatTest, formatEnumConversion){
651 ASSERT_EQ(stringToNcrFormat("JSON"), dhcp_ddns::FMT_JSON);
652 ASSERT_EQ(stringToNcrFormat("jSoN"), dhcp_ddns::FMT_JSON);
653 ASSERT_THROW(stringToNcrFormat("bogus"), isc::BadValue);
654
655 ASSERT_EQ(ncrFormatToString(dhcp_ddns::FMT_JSON), "JSON");
656 }
657
658 /// @brief Tests conversion of NameChangeProtocol between enum and strings.
TEST(NameChangeProtocolTest,protocolEnumConversion)659 TEST(NameChangeProtocolTest, protocolEnumConversion){
660 ASSERT_EQ(stringToNcrProtocol("UDP"), dhcp_ddns::NCR_UDP);
661 ASSERT_EQ(stringToNcrProtocol("udP"), dhcp_ddns::NCR_UDP);
662 ASSERT_EQ(stringToNcrProtocol("TCP"), dhcp_ddns::NCR_TCP);
663 ASSERT_EQ(stringToNcrProtocol("Tcp"), dhcp_ddns::NCR_TCP);
664 ASSERT_THROW(stringToNcrProtocol("bogus"), isc::BadValue);
665
666 ASSERT_EQ(ncrProtocolToString(dhcp_ddns::NCR_UDP), "UDP");
667 ASSERT_EQ(ncrProtocolToString(dhcp_ddns::NCR_TCP), "TCP");
668 }
669
TEST(NameChangeRequestTest,useConflictResolutionParsing)670 TEST(NameChangeRequestTest, useConflictResolutionParsing) {
671 std::string base_json =
672 "{"
673 " \"change-type\" : 0 , "
674 " \"forward-change\" : true , "
675 " \"reverse-change\" : false , "
676 " \"fqdn\" : \"walah.walah.com\" , "
677 " \"ip-address\" : \"192.168.2.1\" , "
678 " \"dhcid\" : \"010203040A7F8E3D\" , "
679 " \"lease-expires-on\" : \"20130121132405\" , "
680 " \"lease-length\" : 1300 ";
681
682 std::string its_true(base_json + ",\"use-conflict-resolution\": true}");
683 NameChangeRequestPtr ncr;
684 ASSERT_NO_THROW_LOG(ncr = NameChangeRequest::fromJSON(its_true));
685 ASSERT_TRUE(ncr);
686 EXPECT_TRUE(ncr->useConflictResolution());
687
688 std::string its_false(base_json + ",\"use-conflict-resolution\": false}");
689 ASSERT_NO_THROW_LOG(ncr = NameChangeRequest::fromJSON(its_false));
690 ASSERT_TRUE(ncr);
691 EXPECT_FALSE(ncr->useConflictResolution());
692
693 std::string its_missing(base_json + "}");
694 ASSERT_NO_THROW_LOG(ncr = NameChangeRequest::fromJSON(its_true));
695 ASSERT_TRUE(ncr);
696 EXPECT_TRUE(ncr->useConflictResolution());
697 }
698
699 } // end of anonymous namespace
700
701