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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
|
// Copyright (C) 2013-2014 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 <d2/d2_config.h>
#include <d2/d2_update_message.h>
#include <d2/d2_zone.h>
#include <dns/messagerenderer.h>
#include <dns/rdataclass.h>
#include <dns/rdata.h>
#include <dns/rrttl.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
using namespace std;
using namespace isc;
using namespace isc::d2;
using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::util;
namespace {
// @brief Test fixture class for testing D2UpdateMessage object.
class D2UpdateMessageTest : public ::testing::Test {
public:
// @brief Constructor.
//
// Does nothing.
D2UpdateMessageTest() { }
// @brief Destructor.
//
// Does nothing.
~D2UpdateMessageTest() { };
// @brief Return string representation of the name encoded in wire format.
//
// This function reads the number of bytes specified in the second
// argument from the buffer. It doesn't check if buffer has sufficient
// length for reading given number of bytes. Caller should verify it
// prior to calling this function.
//
// @param buf input buffer, its internal pointer will be moved to
// the position after a name being read from it.
// @param name_length length of the name stored in the buffer
// @param no_zero_byte if true it indicates that the given buffer does not
// comprise the zero byte, which signals end of the name. This is
// the case, when dealing with compressed messages which don't have
// this byte.
//
// @return string representation of the name.
std::string readNameFromWire(InputBuffer& buf, size_t name_length,
const bool no_zero_byte = false) {
std::vector<uint8_t> name_data;
// Create another InputBuffer which holds only the name in the wire
// format.
buf.readVector(name_data, name_length);
if (no_zero_byte) {
++name_length;
name_data.push_back(0);
}
InputBuffer name_buf(&name_data[0], name_length);
// Parse the name and return its textual representation.
Name name(name_buf);
return (name.toText());
}
};
// This test verifies that DNS Update message ID can be set using
// setId function.
TEST_F(D2UpdateMessageTest, setId) {
// Message ID is initialized to 0.
D2UpdateMessage msg;
EXPECT_EQ(0, msg.getId());
// Override the default value and verify that it has been set.
msg.setId(0x1234);
EXPECT_EQ(0x1234, msg.getId());
}
// This test verifies that the DNS Update message RCODE can be set
// using setRcode function.
TEST_F(D2UpdateMessageTest, setRcode) {
D2UpdateMessage msg;
// Rcode must be explicitly set before it is accessed.
msg.setRcode(Rcode::NOERROR());
EXPECT_EQ(Rcode::NOERROR().getCode(), msg.getRcode().getCode());
// Let's override current value to make sure that getter does
// not return fixed value.
msg.setRcode(Rcode::NOTIMP());
EXPECT_EQ(Rcode::NOTIMP().getCode(), msg.getRcode().getCode());
}
// This test verifies that the Zone section in the DNS Update message
// can be set.
TEST_F(D2UpdateMessageTest, setZone) {
D2UpdateMessage msg;
// The zone pointer is initialized to NULL.
D2ZonePtr zone = msg.getZone();
EXPECT_FALSE(zone);
// Let's create a new Zone and check that it is returned
// via getter.
msg.setZone(Name("example.com"), RRClass::ANY());
zone = msg.getZone();
EXPECT_TRUE(zone);
EXPECT_EQ("example.com.", zone->getName().toText());
EXPECT_EQ(RRClass::ANY().getCode(), zone->getClass().getCode());
// Now, let's check that the existing Zone object can be
// overriden with a new one.
msg.setZone(Name("foo.example.com"), RRClass::NONE());
zone = msg.getZone();
EXPECT_TRUE(zone);
EXPECT_EQ("foo.example.com.", zone->getName().toText());
EXPECT_EQ(RRClass::NONE().getCode(), zone->getClass().getCode());
}
// This test verifies that the DNS message is properly decoded from the
// wire format.
TEST_F(D2UpdateMessageTest, fromWire) {
// The following table holds the DNS response in on-wire format.
// This message comprises the following sections:
// - HEADER
// - PREREQUISITE section with one RR
// - UPDATE section with 1 RR.
// Such a response may be generated by the DNS server as a result
// of copying the contents of the REQUEST message sent by DDNS client.
const uint8_t bin_msg[] = {
// HEADER section starts here (see RFC 2136, section 2).
0x05, 0xAF, // ID=0x05AF
0xA8, 0x6, // QR=1, Opcode=6, RCODE=YXDOMAIN
0x0, 0x1, // ZOCOUNT=1
0x0, 0x2, // PRCOUNT=2
0x0, 0x1, // UPCOUNT=1
0x0, 0x0, // ADCOUNT=0
// Zone section starts here. The The first field comprises
// the Zone name encoded as a set of labels, each preceded
// by a length of the following label. The whole Zone name is
// terminated with a NULL char.
// For Zone section format see (RFC 2136, section 2.3).
0x7, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example (7 is length)
0x3, 0x63, 0x6F, 0x6D, //.com. (0x3 is a length)
0x0, // NULL character terminates the Zone name.
0x0, 0x6, // ZTYPE='SOA'
0x0, 0x1, // ZCLASS='IN'
// Prerequisite section starts here. This section comprises two
// prerequisites:
// - 'Name is not in use'
// - 'Name is in use'
// See RFC 2136, section 2.4 for the format of Prerequisite section.
// Each prerequisite RR starts with its name. It is expressed in the
// compressed format as described in RFC 1035, section 4.1.4. The first
// label is expressed as in case of non-compressed name. It is preceded
// by the length value. The following two bytes are the pointer to the
// offset in the message where 'example.com' was used. That is, in the
// Zone name at offset 12. Pointer starts with two bits set - they
// mark start of the pointer.
// First prerequisite. NONE class indicates that the update requires
// that the name 'foo.example.com' is not use/
0x03, 0x66, 0x6F, 0x6F, // foo.
0xC0, 0x0C, // pointer to example.com.
0x0, 0x1C, // TYPE=AAAA
0x0, 0xFE, // CLASS=NONE
0x0, 0x0, 0x0, 0x0, // TTL=0
0x0, 0x0, // RDLENGTH=0
// Second prerequisite. ANY class indicates tha the update requires
// that the name 'bar.example.com' exists.
0x03, 0x62, 0x61, 0x72, // bar.
0xC0, 0x0C, // pointer to example.com.
0x0, 0x1C, // TYPE=AAAA
0x0, 0xFF, // CLASS=ANY
0x0, 0x0, 0x0, 0x0, // TTL=0
0x0, 0x0, // RDLENGTH=0
// Update section starts here. The format of this section conforms to
// RFC 2136, section 2.5. The name of the RR is again expressed in
// compressed format. The two pointer bytes point to the offset in the
// message where 'foo.example.com' was used already - 29.
0xC0, 0x1D, // pointer to foo.example.com.
0x0, 0x1C, // TYPE=AAAA
0x0, 0x1, // CLASS=IN
0xAA, 0xBB, 0xCC, 0xDD, // TTL=0xAABBCCDD
0x0, 0x10, // RDLENGTH=16
// The following 16 bytes of RDATA hold IPv6 address: 2001:db8:1::1.
0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
};
// Create an object to be used to decode the message from the wire format.
D2UpdateMessage msg(D2UpdateMessage::INBOUND);
// Decode the message.
ASSERT_NO_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)));
// Check that the message header is valid.
EXPECT_EQ(0x05AF, msg.getId());
EXPECT_EQ(D2UpdateMessage::RESPONSE, msg.getQRFlag());
EXPECT_EQ(Rcode::YXDOMAIN_CODE, msg.getRcode().getCode());
// The ZOCOUNT must contain exactly one zone. If it does, we should get
// the name, class and type of the zone and verify they are valid.
ASSERT_EQ(1, msg.getRRCount(D2UpdateMessage::SECTION_ZONE));
D2ZonePtr zone = msg.getZone();
ASSERT_TRUE(zone);
EXPECT_EQ("example.com.", zone->getName().toText());
EXPECT_EQ(RRClass::IN().getCode(), zone->getClass().getCode());
// Check the Prerequisite section. It should contain two records.
ASSERT_EQ(2, msg.getRRCount(D2UpdateMessage::SECTION_PREREQUISITE));
// Proceed to the first prerequisite.
RRsetIterator rrset_it =
msg.beginSection(D2UpdateMessage::SECTION_PREREQUISITE);
RRsetPtr prereq1 = *rrset_it;
ASSERT_TRUE(prereq1);
// Check record fields.
EXPECT_EQ("foo.example.com.", prereq1->getName().toText()); // NAME
EXPECT_EQ(RRType::AAAA().getCode(), prereq1->getType().getCode()); // TYPE
EXPECT_EQ(RRClass::NONE().getCode(),
prereq1->getClass().getCode()); // CLASS
EXPECT_EQ(0, prereq1->getTTL().getValue()); // TTL
EXPECT_EQ(0, prereq1->getRdataCount()); // RDLENGTH
// Move to next prerequisite section.
++rrset_it;
RRsetPtr prereq2 = *rrset_it;
ASSERT_TRUE(prereq2);
// Check record fields.
EXPECT_EQ("bar.example.com.", prereq2->getName().toText()); // NAME
EXPECT_EQ(RRType::AAAA().getCode(), prereq2->getType().getCode()); // TYPE
EXPECT_EQ(RRClass::ANY().getCode(), prereq2->getClass().getCode()); // CLASS
EXPECT_EQ(0, prereq2->getTTL().getValue()); // TTL
EXPECT_EQ(0, prereq2->getRdataCount()); // RDLENGTH
// Check the Update section. There is only one record, so beginSection()
// should return the pointer to this sole record.
ASSERT_EQ(1, msg.getRRCount(D2UpdateMessage::SECTION_UPDATE));
rrset_it = msg.beginSection(D2UpdateMessage::SECTION_UPDATE);
RRsetPtr update = *rrset_it;
ASSERT_TRUE(update);
// Check the record fields.
EXPECT_EQ("foo.example.com.", update->getName().toText()); // NAME
EXPECT_EQ(RRType::AAAA().getCode(), update->getType().getCode()); // TYPE
EXPECT_EQ(RRClass::IN().getCode(), update->getClass().getCode()); // CLASS
EXPECT_EQ(0xAABBCCDD, update->getTTL().getValue()); // TTL
// There should be exactly one record holding the IPv6 address.
// This record can be accessed using RdataIterator. This record
// can be compared with the reference record, holding expected IPv6
// address using compare function.
ASSERT_EQ(1, update->getRdataCount());
RdataIteratorPtr rdata_it = update->getRdataIterator();
ASSERT_TRUE(rdata_it);
in::AAAA rdata_ref("2001:db8:1::1");
EXPECT_EQ(0, rdata_ref.compare(rdata_it->getCurrent()));
// @todo: at this point we don't test Additional Data records. We may
// consider implementing tests for it in the future.
}
// This test verifies that the fromWire function throws appropriate exception
// if the message being parsed comprises invalid Opcode (is not a DNS Update).
TEST_F(D2UpdateMessageTest, fromWireInvalidOpcode) {
// This is a binary representation of the DNS message.
// It comprises invalid Opcode=3, expected value is 6
// (Update).
const uint8_t bin_msg[] = {
0x05, 0xAF, // ID=0x05AF
0x98, 0x6, // QR=1, Opcode=3, RCODE=YXDOMAIN
0x0, 0x0, // ZOCOUNT=0
0x0, 0x0, // PRCOUNT=0
0x0, 0x0, // UPCOUNT=0
0x0, 0x0 // ADCOUNT=0
};
// The 'true' argument passed to the constructor turns the
// message into the parse mode in which the fromWire function
// can be used to decode the binary mesasage data.
D2UpdateMessage msg(D2UpdateMessage::INBOUND);
// When using invalid Opcode, the fromWire function should
// throw NotUpdateMessage exception.
EXPECT_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)),
isc::d2::NotUpdateMessage);
}
// This test verifies that the fromWire function throws appropriate exception
// if the message being parsed comprises invalid QR flag. The QR bit is
// expected to be set to indicate that the message is a RESPONSE.
TEST_F(D2UpdateMessageTest, fromWireInvalidQRFlag) {
// This is a binary representation of the DNS message.
// It comprises invalid QR flag = 0.
const uint8_t bin_msg[] = {
0x05, 0xAF, // ID=0x05AF
0x28, 0x6, // QR=0, Opcode=6, RCODE=YXDOMAIN
0x0, 0x0, // ZOCOUNT=0
0x0, 0x0, // PRCOUNT=0
0x0, 0x0, // UPCOUNT=0
0x0, 0x0 // ADCOUNT=0
};
// The 'true' argument passed to the constructor turns the
// message into the parse mode in which the fromWire function
// can be used to decode the binary mesasage data.
D2UpdateMessage msg(D2UpdateMessage::INBOUND);
// When using invalid QR flag, the fromWire function should
// throw InvalidQRFlag exception.
EXPECT_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)),
isc::d2::InvalidQRFlag);
}
// This test verifies that the fromWire function throws appropriate exception
// if the message being parsed comprises more than one (two in this case)
// Zone records.
TEST_F(D2UpdateMessageTest, fromWireTooManyZones) {
// This is a binary representation of the DNS message. This message
// comprises two Zone records.
const uint8_t bin_msg[] = {
0x05, 0xAF, // ID=0x05AF
0xA8, 0x6, // QR=1, Opcode=6, RCODE=YXDOMAIN
0x0, 0x2, // ZOCOUNT=2
0x0, 0x0, // PRCOUNT=0
0x0, 0x0, // UPCOUNT=0
0x0, 0x0, // ADCOUNT=0
// Start first Zone record.
0x7, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example (7 is length)
0x3, 0x63, 0x6F, 0x6D, //.com. (0x3 is a length)
0x0, // NULL character terminates the Zone name.
0x0, 0x6, // ZTYPE='SOA'
0x0, 0x1, // ZCLASS='IN'
// Start second Zone record. Presence of this record should result
// in error when parsing this message.
0x3, 0x63, 0x6F, 0x6D, // com. (0x3 is a length)
0x0, // NULL character terminates the Zone name.
0x0, 0x6, // ZTYPE='SOA'
0x0, 0x1 // ZCLASS='IN'
};
// The 'true' argument passed to the constructor turns the
// message into the parse mode in which the fromWire function
// can be used to decode the binary mesasage data.
D2UpdateMessage msg(D2UpdateMessage::INBOUND);
// When parsing a message with more than one Zone record,
// exception should be thrown.
EXPECT_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)),
isc::d2::InvalidZoneSection);
}
// This test verifies that the wire format of the message is produced
// in the render mode.
TEST_F(D2UpdateMessageTest, toWire) {
D2UpdateMessage msg;
// Set message ID.
msg.setId(0x1234);
// Rcode to NOERROR.
msg.setRcode(Rcode(Rcode::NOERROR_CODE));
// Set Zone section. This section must comprise exactly
// one Zone. toWire function would fail if Zone is not set.
msg.setZone(Name("example.com"), RRClass::IN());
// Set prerequisities.
// 'Name Is Not In Use' prerequisite (RFC 2136, section 2.4.5)
RRsetPtr prereq1(new RRset(Name("foo.example.com"), RRClass::NONE(),
RRType::ANY(), RRTTL(0)));
msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq1);
// 'Name is In Use' prerequisite (RFC 2136, section 2.4.4)
RRsetPtr prereq2(new RRset(Name("bar.example.com"), RRClass::ANY(),
RRType::ANY(), RRTTL(0)));
msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq2);
// Set Update Section.
// Create RR holding a name being added. This RR is constructed
// in conformance to RFC 2136, section 2.5.1.
RRsetPtr updaterr1(new RRset(Name("foo.example.com"), RRClass::IN(),
RRType::A(), RRTTL(10)));
// RR record is of the type A, thus RDATA holds 4 octet Internet
// address. This address is 10.10.1.1.
char rdata1[] = {
0xA, 0xA , 0x1, 0x1
};
InputBuffer buf_rdata1(rdata1, 4);
updaterr1->addRdata(createRdata(RRType::A(), RRClass::IN(), buf_rdata1,
buf_rdata1.getLength()));
// Add the RR to the message.
msg.addRRset(D2UpdateMessage::SECTION_UPDATE, updaterr1);
// Render message into the wire format.
MessageRenderer renderer;
ASSERT_NO_THROW(msg.toWire(renderer));
// Make sure that created packet is not truncated.
ASSERT_EQ(77, renderer.getLength());
// Create input buffer from the rendered data. InputBuffer
// is handy to validate the byte contents of the rendered
// message.
InputBuffer buf(renderer.getData(), renderer.getLength());
// Start validating the message header.
// Verify message ID.
EXPECT_EQ(0x1234, buf.readUint16());
// The 2-bytes following message ID comprise the following fields:
// - QR - 1 bit indicating that it is REQUEST. Should be 0.
// - Opcode - 4 bits which should hold value of 5 indicating this is
// an Update message. Binary form is "0101".
// - Z - These bits are unused for Update Message and should be 0.
// - RCODE - Response code, set to NOERROR for REQUEST. It is 0.
//8706391835
// The binary value is:
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | QR| Opcode | Z | RCODE |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | 0 | 0 1 0 1 | 0 0 0 0 0 0 0 | 0 0 0 0 |
// +---+---+---+-------+---+---+---+---+---+---+---+---+---+---+---+
// and the hexadecimal representation is 0x2800.
EXPECT_EQ(0x2800, buf.readUint16());
// ZOCOUNT - holds the number of zones for the update. For Request
// message it must be exactly one record (RFC2136, section 2.3).
EXPECT_EQ(1, buf.readUint16());
// PRCOUNT - holds the number of prerequisites. Earlier we have added
// two prerequisites. Thus, expect that this conter is 2.
EXPECT_EQ(2, buf.readUint16());
// UPCOUNT - holds the number of RRs in the Update Section. We have
// added 1 RR, which adds the name foo.example.com to the Zone.
EXPECT_EQ(1, buf.readUint16());
// ADCOUNT - holds the number of RRs in the Additional Data Section.
EXPECT_EQ(0, buf.readUint16());
// Start validating the Zone section. This section comprises the
// following data:
// - ZNAME
// - ZTYPE
// - ZCLASS
// ZNAME holds 'example.com.' encoded as set of labels. Each label
// is preceded by its length. The name is ended with the byte holding
// zero value. This yields the total size of the name in wire format
// of 13 bytes.
// The simplest way to convert the name from wire format to a string
// is to use dns::Name class. It should be ok to rely on the Name class
// to decode the name, because it is unit tested elswhere.
std::string zone_name = readNameFromWire(buf, 13);
EXPECT_EQ("example.com.", zone_name);
// ZTYPE of the Zone section must be SOA according to RFC 2136,
// section 2.3.
EXPECT_EQ(RRType::SOA().getCode(), buf.readUint16());
// ZCLASS of the Zone section is IN.
EXPECT_EQ(RRClass::IN().getCode(), buf.readUint16());
// Start checks on Prerequisite section. Each prerequisite comprises
// the following fields:
// - NAME - name of the RR in wire format
// - TYPE - two octets with one of the RR TYPE codes
// - CLASS - two octets with one of the RR CLASS codes
// - TTL - a 32-bit signed integer specifying Time-To-Live
// - RDLENGTH - length of the RDATA field
// - RDATA - a variable length string of octets containing
// resource data.
// In case of this message, we expect to have two prerequisite RRs.
// Their structure is checked below.
// First prerequisite should comprise the 'Name is not in use prerequisite'
// for 'foo.example.com'.
// Check the name first. Message renderer is using compression for domain
// names as described in RFC 1035, section 4.1.4. The name in this RR is
// foo.example.com. The name of the zone is example.com and it has occured
// in this message already at offset 12 (the size of the header is 12).
// Therefore, name of this RR is encoded as 'foo', followed by a pointer
// to offset in this message where the remainder of this name was used.
// This pointer has the following format:
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | 1 1| OFFSET |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | 1 1| 0 0 0 0 0 0 0 0 0 0 1 1 0 0|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// which has a following hexadecimal representation: 0xC00C
// Let's read the non-compressed part first - 'foo.'
std::string name_prereq1 = readNameFromWire(buf, 4, true);
EXPECT_EQ("foo.", name_prereq1);
// The remaining two bytes hold the pointer to 'example.com'.
EXPECT_EQ(0xC00C, buf.readUint16());
// TYPE is ANY
EXPECT_EQ(RRType::ANY().getCode(), buf.readUint16());
// CLASS is NONE
EXPECT_EQ(RRClass::NONE().getCode(), buf.readUint16());
// TTL is a 32-but value, expecting 0
EXPECT_EQ(0, buf.readUint32());
// There is no RDATA, so RDLENGTH is 0
EXPECT_EQ(0, buf.readUint16());
// Start checking second prerequisite.
std::string name_prereq2 = readNameFromWire(buf, 4, true);
EXPECT_EQ("bar.", name_prereq2);
// The remaining two bytes hold the pointer to 'example.com'.
EXPECT_EQ(0xC00C, buf.readUint16());
// TYPE is ANY
EXPECT_EQ(RRType::ANY().getCode(), buf.readUint16());
// CLASS is ANY
EXPECT_EQ(RRClass::ANY().getCode(), buf.readUint16());
// TTL is a 32-but value, expecting 0
EXPECT_EQ(0, buf.readUint32());
// There is no RDATA, so RDLENGTH is 0
EXPECT_EQ(0, buf.readUint16());
// Start checking Update section. This section contains RRset with
// one A RR.
// The name of the RR is 'foo.example.com'. It is encoded in the
// compressed format - as a pointer to the name of prerequisite 1.
// This name is in offset 0x1D in this message.
EXPECT_EQ(0xC01D, buf.readUint16());
// TYPE is A
EXPECT_EQ(RRType::A().getCode(), buf.readUint16());
// CLASS is IN (same as zone class)
EXPECT_EQ(RRClass::IN().getCode(), buf.readUint16());
// TTL is a 32-but value, set here to 10.
EXPECT_EQ(10, buf.readUint32());
// For A records, the RDATA comprises the 4-byte Internet address.
// So, RDLENGTH is 4.
EXPECT_EQ(4, buf.readUint16());
// We have stored the following address in RDATA field: 10.10.1.1
// (which is 0A 0A 01 01) in hexadecimal format.
EXPECT_EQ(0x0A0A0101, buf.readUint32());
// @todo: consider extending this test to verify Additional Data
// section.
}
// This test verifies that an attempt to call toWire function on the
// received message will result in an exception.
TEST_F(D2UpdateMessageTest, toWireInvalidQRFlag) {
// This is a binary representation of the DNS message.
// This message is valid and should be parsed with no
// error.
const uint8_t bin_msg[] = {
0x05, 0xAF, // ID=0x05AF
0xA8, 0x6, // QR=1, Opcode=6, RCODE=YXDOMAIN
0x0, 0x0, // ZOCOUNT=0
0x0, 0x0, // PRCOUNT=0
0x0, 0x0, // UPCOUNT=0
0x0, 0x0 // ADCOUNT=0
};
// The 'true' argument passed to the constructor turns the
// message into the parse mode in which the fromWire function
// can be used to decode the binary mesasage data.
D2UpdateMessage msg(D2UpdateMessage::INBOUND);
ASSERT_NO_THROW(msg.fromWire(bin_msg, sizeof(bin_msg)));
// The message is parsed. The QR Flag should now indicate that
// it is a Response message.
ASSERT_EQ(D2UpdateMessage::RESPONSE, msg.getQRFlag());
// An attempt to call toWire on the Response message should
// result in the InvalidQRFlag exception.
MessageRenderer renderer;
EXPECT_THROW(msg.toWire(renderer), isc::d2::InvalidQRFlag);
}
// TSIG test
TEST_F(D2UpdateMessageTest, validTSIG) {
// Create a TSIG Key and context
std::string secret("this key will match");
TSIGKeyPtr right_key;
ASSERT_NO_THROW(right_key.reset(new
TSIGKey(Name("right.com"),
TSIGKey::HMACMD5_NAME(),
secret.c_str(), secret.size())));
TSIGKeyPtr wrong_key;
secret = "this key will not match";
ASSERT_NO_THROW(wrong_key.reset(new
TSIGKey(Name("wrong.com"),
TSIGKey::HMACMD5_NAME(),
secret.c_str(), secret.size())));
// Build a request message
D2UpdateMessage msg;
msg.setId(0x1234);
msg.setRcode(Rcode(Rcode::NOERROR_CODE));
msg.setZone(Name("example.com"), RRClass::IN());
RRsetPtr prereq1(new RRset(Name("foo.example.com"), RRClass::NONE(),
RRType::ANY(), RRTTL(0)));
msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq1);
RRsetPtr prereq2(new RRset(Name("bar.example.com"), RRClass::ANY(),
RRType::ANY(), RRTTL(0)));
msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq2);
RRsetPtr updaterr1(new RRset(Name("foo.example.com"), RRClass::IN(),
RRType::A(), RRTTL(10)));
char rdata1[] = {
0xA, 0xA , 0x1, 0x1
};
InputBuffer buf_rdata1(rdata1, 4);
updaterr1->addRdata(createRdata(RRType::A(), RRClass::IN(), buf_rdata1,
buf_rdata1.getLength()));
msg.addRRset(D2UpdateMessage::SECTION_UPDATE, updaterr1);
// Make a context to send the message with and use it to render
// the message into the wire format.
TSIGContextPtr context;
ASSERT_NO_THROW(context.reset(new TSIGContext(*right_key)));
MessageRenderer renderer;
ASSERT_NO_THROW(msg.toWire(renderer, context.get()));
// Grab the wire data from the signed message.
const void* wire_data = renderer.getData();
const size_t wire_size = renderer.getLength();
// Make a context with the wrong key and use it to convert the wired data.
// Verification should fail.
D2UpdateMessage msg2(D2UpdateMessage::INBOUND);
ASSERT_NO_THROW(context.reset(new TSIGContext(*wrong_key)));
ASSERT_THROW(msg2.fromWire(wire_data, wire_size, context.get()),
TSIGVerifyError);
// Now make a context with the correct key and try again.
// If the message passes TSIG verification, then the QR Flag test in
// the subsequent call to D2UpdateMessage::validateResponse should
// fail because this isn't really received message.
ASSERT_NO_THROW(context.reset(new TSIGContext(*right_key)));
ASSERT_THROW(msg2.fromWire(wire_data, wire_size, context.get()),
InvalidQRFlag);
}
// Tests message signing and verification for all supported algorithms.
TEST_F(D2UpdateMessageTest, allValidTSIG) {
std::vector<std::string>algorithms;
algorithms.push_back(TSIGKeyInfo::HMAC_MD5_STR);
algorithms.push_back(TSIGKeyInfo::HMAC_SHA1_STR);
algorithms.push_back(TSIGKeyInfo::HMAC_SHA224_STR);
algorithms.push_back(TSIGKeyInfo::HMAC_SHA256_STR);
algorithms.push_back(TSIGKeyInfo::HMAC_SHA384_STR);
algorithms.push_back(TSIGKeyInfo::HMAC_SHA512_STR);
dns::Name key_name("test_key");
std::string secret("random text for secret");
for (int i = 0; i < algorithms.size(); ++i) {
dns::TSIGKey key(key_name,
TSIGKeyInfo::stringToAlgorithmName(algorithms[i]),
secret.c_str(), secret.size());
// Build a request message
D2UpdateMessage msg;
msg.setId(0x1234);
msg.setRcode(Rcode(Rcode::NOERROR_CODE));
msg.setZone(Name("example.com"), RRClass::IN());
// Make a context to send the message with and use it to render
// the message into the wire format.
TSIGContextPtr context;
ASSERT_NO_THROW(context.reset(new TSIGContext(key)));
MessageRenderer renderer;
ASSERT_NO_THROW(msg.toWire(renderer, context.get()));
// Grab the wire data from the signed message.
const void* wire_data = renderer.getData();
const size_t wire_size = renderer.getLength();
// Create a fresh context to "receive" the message. (We can't use the
// one we signed it with, as its expecting a signed response to its
// request. Here we are acting like the server).
// If the message passes TSIG verification, then the QR Flag test in
// the subsequent call to D2UpdateMessage::validateResponse should
// fail because this isn't really received message.
ASSERT_NO_THROW(context.reset(new TSIGContext(key)));
D2UpdateMessage msg2(D2UpdateMessage::INBOUND);
ASSERT_THROW(msg2.fromWire(wire_data, wire_size, context.get()),
InvalidQRFlag);
}
}
} // End of anonymous namespace
|