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