1 /*
2 * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 /** @file
27 *
28 * DNS self-tests
29 *
30 */
31
32 /* Forcibly enable assertions */
33 #undef NDEBUG
34
35 #include <string.h>
36 #include <assert.h>
37 #include <ipxe/dns.h>
38 #include <ipxe/test.h>
39
40 /** Define inline data */
41 #define DATA(...) { __VA_ARGS__ }
42
43 /** A DNS encoding test */
44 struct dns_encode_test {
45 /** String */
46 const char *string;
47 /** Encoded string */
48 const void *data;
49 /** Length of encoded string */
50 int len;
51 };
52
53 /**
54 * Define a DNS encoding test
55 *
56 * @v _name Test name
57 * @v _string Test string
58 * @v _data Expected encoded data
59 * @ret test DNS encoding test
60 */
61 #define DNS_ENCODE( _name, _string, _data ) \
62 static const uint8_t _name ## __data[] = _data; \
63 static struct dns_encode_test _name = { \
64 .string = _string, \
65 .data = _name ## __data, \
66 .len = sizeof ( _name ## __data ), \
67 }
68
69 /**
70 * Report DNS encoding test result
71 *
72 * @v test DNS encoding test
73 * @v file Test code file
74 * @v line Test code line
75 */
dns_encode_okx(struct dns_encode_test * test,const char * file,unsigned int line)76 static void dns_encode_okx ( struct dns_encode_test *test, const char *file,
77 unsigned int line ) {
78 uint8_t data[ test->len ];
79 struct dns_name name;
80 int len;
81
82 /* Check ability to determine length with no buffer */
83 memset ( &name, 0, sizeof ( name ) );
84 len = dns_encode ( test->string, &name );
85 okx ( len >= 0, file, line );
86 okx ( len == test->len, file, line );
87
88 /* Check encoded name */
89 name.data = data;
90 name.len = sizeof ( data );
91 len = dns_encode ( test->string, &name );
92 okx ( len >= 0, file, line );
93 if ( len >= 0 ) {
94 okx ( len == test->len, file, line );
95 okx ( memcmp ( data, test->data, test->len ) == 0, file, line );
96 DBGC ( test, "DNS encoded \"%s\" to:\n", test->string );
97 DBGC_HDA ( test, 0, data, len );
98 }
99 }
100 #define dns_encode_ok( test ) dns_encode_okx ( test, __FILE__, __LINE__ )
101
102 /**
103 * Report DNS encoding failure test result
104 *
105 * @v test DNS encoding test
106 * @v file Test code file
107 * @v line Test code line
108 */
dns_encode_fail_okx(struct dns_encode_test * test,const char * file,unsigned int line)109 static void dns_encode_fail_okx ( struct dns_encode_test *test,
110 const char *file, unsigned int line ) {
111 struct dns_name name = { .data = NULL, .len = 0 };
112 int len;
113
114 len = dns_encode ( test->string, &name );
115 okx ( len < 0, file, line );
116 }
117 #define dns_encode_fail_ok( test ) \
118 dns_encode_fail_okx ( test, __FILE__, __LINE__ )
119
120 /** A DNS decoding test */
121 struct dns_decode_test {
122 /** Name */
123 struct dns_name name;
124 /** Expected string */
125 const char *string;
126 };
127
128 /**
129 * Define a DNS decoding test
130 *
131 * @v _name Test name
132 * @v _data RFC1035-encoded data
133 * @v _offset Starting offset within encoded data
134 * @v _string Expected decoded string
135 * @ret test DNS decoding test
136 */
137 #define DNS_DECODE( _name, _data, _offset, _string ) \
138 static uint8_t _name ## __data[] = _data; \
139 static struct dns_decode_test _name = { \
140 .name = { \
141 .data = _name ## __data, \
142 .offset = _offset, \
143 .len = sizeof ( _name ## __data ), \
144 }, \
145 .string = _string, \
146 }
147
148 /**
149 * Report DNS decoding test result
150 *
151 * @v test DNS decoding test
152 * @v file Test code file
153 * @v line Test code line
154 */
dns_decode_okx(struct dns_decode_test * test,const char * file,unsigned int line)155 static void dns_decode_okx ( struct dns_decode_test *test, const char *file,
156 unsigned int line ) {
157 char string[ strlen ( test->string ) + 1 /* NUL */ ];
158 int len;
159
160 /* Check ability to determine length with no buffer */
161 len = dns_decode ( &test->name, NULL, 0 );
162 okx ( len >= 0, file, line );
163 okx ( len == ( ( int ) strlen ( test->string ) ), file, line );
164
165 /* Check decoded string */
166 len = dns_decode ( &test->name, string, sizeof ( string ) );
167 okx ( len >= 0, file, line );
168 if ( len >= 0 ) {
169 okx ( strcmp ( string, test->string ) == 0, file, line );
170 DBGC ( test, "DNS decoded \"%s\" from offset %#zx in:\n",
171 string, test->name.offset );
172 DBGC_HDA ( test, 0, test->name.data, test->name.len );
173 }
174 }
175 #define dns_decode_ok( test ) dns_decode_okx ( test, __FILE__, __LINE__ )
176
177 /**
178 * Report DNS decoding failure test result
179 *
180 * @v test DNS decoding test
181 * @v file Test code file
182 * @v line Test code line
183 */
dns_decode_fail_okx(struct dns_decode_test * test,const char * file,unsigned int line)184 static void dns_decode_fail_okx ( struct dns_decode_test *test,
185 const char *file, unsigned int line ) {
186 int len;
187
188 len = dns_decode ( &test->name, NULL, 0 );
189 okx ( len < 0, file, line );
190 }
191 #define dns_decode_fail_ok( test ) \
192 dns_decode_fail_okx ( test, __FILE__, __LINE__ )
193
194 /** A DNS comparison test */
195 struct dns_compare_test {
196 /** First name */
197 struct dns_name first;
198 /** Second name */
199 struct dns_name second;
200 };
201
202 /**
203 * Define a DNS comparison test
204 *
205 * @v _name Test name
206 * @v _first_data First RFC1035-encoded data
207 * @v _first_offset Starting offset within first encoded data
208 * @v _second_data Second RFC1035-encoded data
209 * @v _second_offset Starting offset within second encoded data
210 * @ret test DNS comparison test
211 */
212 #define DNS_COMPARE( _name, _first_data, _first_offset, _second_data, \
213 _second_offset ) \
214 static uint8_t _name ## __first_data[] = _first_data; \
215 static uint8_t _name ## __second_data[] = _second_data; \
216 static struct dns_compare_test _name = { \
217 .first = { \
218 .data = _name ## __first_data, \
219 .offset = _first_offset, \
220 .len = sizeof ( _name ## __first_data ), \
221 }, \
222 .second = { \
223 .data = _name ## __second_data, \
224 .offset = _second_offset, \
225 .len = sizeof ( _name ## __second_data ), \
226 }, \
227 }
228
229 /**
230 * Report DNS comparison test result
231 *
232 * @v test DNS comparison test
233 * @v file Test code file
234 * @v line Test code line
235 */
dns_compare_okx(struct dns_compare_test * test,const char * file,unsigned int line)236 static void dns_compare_okx ( struct dns_compare_test *test, const char *file,
237 unsigned int line ) {
238
239 okx ( dns_compare ( &test->first, &test->second ) == 0, file, line );
240 }
241 #define dns_compare_ok( test ) dns_compare_okx ( test, __FILE__, __LINE__ )
242
243 /**
244 * Report DNS comparison test failure result
245 *
246 * @v test DNS comparison test
247 * @v file Test code file
248 * @v line Test code line
249 */
dns_compare_fail_okx(struct dns_compare_test * test,const char * file,unsigned int line)250 static void dns_compare_fail_okx ( struct dns_compare_test *test,
251 const char *file, unsigned int line ) {
252
253 okx ( dns_compare ( &test->first, &test->second ) != 0, file, line );
254 }
255 #define dns_compare_fail_ok( test ) \
256 dns_compare_fail_okx ( test, __FILE__, __LINE__ )
257
258 /** A DNS copying test */
259 struct dns_copy_test {
260 /** Source name */
261 struct dns_name src;
262 /** Expected copied name */
263 struct dns_name dst;
264 };
265
266 /**
267 * Define a DNS copying test
268 *
269 * @v _name Test name
270 * @v _src_data Source RFC1035-encoded data
271 * @v _src_offset Starting offset within source encoded data
272 * @v _dst_data Expected copied RFC1035-encoded data
273 * @v _dst_offset Starting offset withint copied encoded data
274 * @ret test DNS copying test
275 */
276 #define DNS_COPY( _name, _src_data, _src_offset, _dst_data, \
277 _dst_offset ) \
278 static uint8_t _name ## __src_data[] = _src_data; \
279 static uint8_t _name ## __dst_data[] = _dst_data; \
280 static struct dns_copy_test _name = { \
281 .src = { \
282 .data = _name ## __src_data, \
283 .offset = _src_offset, \
284 .len = sizeof ( _name ## __src_data ), \
285 }, \
286 .dst = { \
287 .data = _name ## __dst_data, \
288 .offset = _dst_offset, \
289 .len = sizeof ( _name ## __dst_data ), \
290 }, \
291 }
292
293 /**
294 * Report a DNS copying test result
295 *
296 * @v test DNS copying test
297 * @v file Test code file
298 * @v line Test code line
299 */
dns_copy_okx(struct dns_copy_test * test,const char * file,unsigned int line)300 static void dns_copy_okx ( struct dns_copy_test *test,
301 const char *file, unsigned int line ) {
302 uint8_t data[ test->dst.len ];
303 struct dns_name dst;
304 int len;
305
306 /* Check ability to determine length with no buffer */
307 memset ( &dst, 0, sizeof ( dst ) );
308 len = dns_copy ( &test->src, &dst );
309 okx ( len >= 0, file, line );
310 okx ( len == ( ( int ) ( test->dst.len - test->dst.offset ) ),
311 file, line );
312
313 /* Check copied name */
314 dst.data = data;
315 dst.offset = test->dst.offset;
316 dst.len = sizeof ( data );
317 memcpy ( dst.data, test->dst.data, test->dst.offset );
318 len = dns_copy ( &test->src, &dst );
319 okx ( len >= 0, file, line );
320 okx ( len == ( ( int ) ( test->dst.len - test->dst.offset ) ),
321 file, line );
322 okx ( memcmp ( data, test->dst.data, sizeof ( data ) ) == 0,
323 file, line );
324 DBGC ( test, "DNS copied:\n" );
325 DBGC_HDA ( test, 0, test->src.data, test->src.len );
326 DBGC_HDA ( test, 0, data, ( test->dst.offset + len ) );
327 }
328 #define dns_copy_ok( test ) dns_copy_okx ( test, __FILE__, __LINE__ )
329
330 /**
331 * Report a DNS copying failure test result
332 *
333 * @v test DNS copying test
334 * @v file Test code file
335 * @v line Test code line
336 */
dns_copy_fail_okx(struct dns_copy_test * test,const char * file,unsigned int line)337 static void dns_copy_fail_okx ( struct dns_copy_test *test,
338 const char *file, unsigned int line ) {
339 struct dns_name dst;
340 int len;
341
342 memset ( &dst, 0, sizeof ( dst ) );
343 len = dns_copy ( &test->src, &dst );
344 okx ( len < 0, file, line );
345 }
346 #define dns_copy_fail_ok( test ) dns_copy_fail_okx ( test, __FILE__, __LINE__ )
347
348 /** A DNS search list test */
349 struct dns_list_test {
350 /** Search list */
351 struct dns_name list;
352 /** Expected decoded search list */
353 const char **strings;
354 /** Number of expected decoded string */
355 unsigned int count;
356 };
357
358 /**
359 * Define a DNS search list test
360 *
361 * @v _name Test name
362 * @v _list RFC1035-encoded data
363 * @v _strings Expected decoded strings
364 * @ret test DNS search list test
365 */
366 #define DNS_LIST( _name, _list, _strings ) \
367 static uint8_t _name ## __list[] = _list; \
368 static const char * _name ## __strings[] = _strings; \
369 static struct dns_list_test _name = { \
370 .list = { \
371 .data = _name ## __list, \
372 .offset = 0, \
373 .len = sizeof ( _name ## __list ), \
374 }, \
375 .strings = _name ## __strings, \
376 .count = ( sizeof ( _name ## __strings ) / \
377 sizeof ( _name ## __strings[0] ) ), \
378 }
379
380 /**
381 * Report DNS search list test result
382 *
383 * @v test DNS search list test
384 * @v file Test code file
385 * @v line Test code line
386 */
dns_list_okx(struct dns_list_test * test,const char * file,unsigned int line)387 static void dns_list_okx ( struct dns_list_test *test, const char *file,
388 unsigned int line ) {
389 struct dns_name name;
390 unsigned int i;
391
392 DBGC ( test, "DNS search list:\n" );
393 DBGC_HDA ( test, 0, test->list.data, test->list.len );
394 memcpy ( &name, &test->list, sizeof ( name ) );
395 for ( i = 0 ; i < test->count ; i++ ) {
396 char buf[ strlen ( test->strings[i] ) + 1 /* NUL */ ];
397 int len;
398 int offset;
399
400 /* Decode this name */
401 len = dns_decode ( &name, buf, sizeof ( buf ) );
402 okx ( len >= 0, file, line );
403 if ( len >= 0 ) {
404 okx ( len == ( ( int ) strlen ( test->strings[i] ) ),
405 file, line );
406 okx ( strcmp ( buf, test->strings[i] ) == 0,
407 file, line );
408 DBGC ( test, "DNS search list found \"%s\" at offset "
409 "%#zx\n", buf, name.offset );
410 }
411
412 /* Skip to next name */
413 offset = dns_skip ( &name );
414 okx ( offset >= 0, file, line );
415 name.offset = offset;
416 }
417
418 /* Check that we have consumed the whole search list */
419 okx ( name.offset == name.len, file, line );
420 }
421 #define dns_list_ok( test ) dns_list_okx ( test, __FILE__, __LINE__ )
422
423 /* Simple encoding test */
424 DNS_ENCODE ( encode_simple, "ipxe.org",
425 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ) );
426
427 /* Single-word encoding test */
428 DNS_ENCODE ( encode_single, "foo", DATA ( 3, 'f', 'o', 'o', 0 ) );
429
430 /* Absolute encoding test */
431 DNS_ENCODE ( encode_absolute, "git.ipxe.org.",
432 DATA ( 3, 'g', 'i', 't', 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g',
433 0 ) );
434
435 /* Empty string encoding test */
436 DNS_ENCODE ( encode_empty, "", DATA ( 0 ) );
437
438 /* Root domain encoding test */
439 DNS_ENCODE ( encode_root, ".", DATA ( 0 ) );
440
441 /* Invalid initial dot encoding test */
442 DNS_ENCODE ( encode_initial_dot, ".foo", DATA() );
443
444 /* Invalid double dot encoding test */
445 DNS_ENCODE ( encode_double_dot, "ipxe..org", DATA() );
446
447 /* Invalid solo double dot encoding test */
448 DNS_ENCODE ( encode_solo_double_dot, "..", DATA() );
449
450 /* Invalid trailing double dot encoding test */
451 DNS_ENCODE ( encode_trailing_double_dot, "ipxe.org..", DATA() );
452
453 /* Invalid overlength label encoding test */
454 DNS_ENCODE ( encode_overlength,
455 "this-label-is-maliciously-long-in-an-attempt-to-overflow-the-"
456 "length-field-and-generate-a-length-which-looks-like-a-"
457 "compression-pointer", DATA() );
458
459 /* Simple decoding test */
460 DNS_DECODE ( decode_simple,
461 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0,
462 "ipxe.org" );
463
464 /* Compression pointer decoding test */
465 DNS_DECODE ( decode_ptr,
466 DATA ( 3, 'o', 'r', 'g', 0, 3, 'g', 'i', 't', 4, 'i', 'p', 'x',
467 'e', 0xc0, 0x00 ), 5,
468 "git.ipxe.org" );
469
470 /* Root decoding test */
471 DNS_DECODE ( decode_root,
472 DATA ( 0 ), 0, "" );
473
474 /* Incomplete name decoding test */
475 DNS_DECODE ( decode_incomplete_name,
476 DATA ( 4, 'i', 'p', 'x', 'e' ), 0, NULL );
477
478 /* Incomplete label decoding test */
479 DNS_DECODE ( decode_incomplete_label,
480 DATA ( 4, 'i', 'p', 'x' ), 0, NULL );
481
482 /* Incomplete compression pointer decoding test */
483 DNS_DECODE ( decode_incomplete_ptr,
484 DATA ( 3, 'o', 'r', 'g', 0, 4, 'i', 'p', 'x', 'e', 0xc0 ), 5,
485 NULL );
486
487 /* Forward reference decoding test */
488 DNS_DECODE ( decode_forward,
489 DATA ( 0xc0, 0x02, 3, 'f', 'o', 'o', 0 ), 0, NULL );
490
491 /* Infinite loop decoding test */
492 DNS_DECODE ( decode_infinite,
493 DATA ( 4, 'i', 'p', 'x', 'e', 0xc0, 0x00 ), 0, NULL );
494
495 /* Empty decoding test */
496 DNS_DECODE ( decode_empty,
497 DATA (), 0, NULL );
498
499 /* Simple comparison test */
500 DNS_COMPARE ( compare_simple,
501 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0,
502 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0 );
503
504 /* Compression pointer comparison test */
505 DNS_COMPARE ( compare_ptr,
506 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0,
507 DATA ( 3, 'o', 'r', 'g', 0, 4, 'i', 'p', 'x', 'e',
508 0xc0, 0x00 ), 5 );
509
510 /* Case insensitive comparison test */
511 DNS_COMPARE ( compare_case,
512 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0,
513 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'O', 'R', 'G', 0 ), 0 );
514
515 /* Mismatch comparison test */
516 DNS_COMPARE ( compare_mismatch,
517 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0,
518 DATA ( 4, 'g', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0 );
519
520 /* Infinite loop comparison test */
521 DNS_COMPARE ( compare_infinite,
522 DATA ( 3, 'f', 'o', 'o', 0xc0, 0x00 ), 0,
523 DATA ( 3, 'f', 'o', 'o', 0xc0, 0x00 ), 0 );
524
525 /* Simple copying test */
526 DNS_COPY ( copy_simple,
527 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0,
528 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0 );
529
530 /* Simple copying test with offset */
531 DNS_COPY ( copy_offset,
532 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0,
533 DATA ( 'f', 'o', 'o', 0, 4, 'i', 'p', 'x', 'e',
534 3, 'o', 'r', 'g', 0 ), 4 );
535
536 /* Compression pointer copying test */
537 DNS_COPY ( copy_ptr,
538 DATA ( 3, 'o', 'r', 'g', 0, 3, 'g', 'i', 't', 4, 'i', 'p', 'x', 'e',
539 0xc0, 0x00 ), 5,
540 DATA ( 3, 'g', 'i', 't', 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g',
541 0 ), 0 );
542
543 /* Infinite loop copying test */
544 DNS_COPY ( copy_infinite,
545 DATA ( 4, 'l', 'o', 'o', 'p', 7, 'f', 'o', 'r', 'e', 'v', 'e', 'r',
546 0xc0, 0x05 ), 0,
547 DATA (), 0 );
548
549 /* DNS search list test */
550 DNS_LIST ( search,
551 DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0,
552 4, 'b', 'o', 'o', 't', 0xc0, 0x00,
553 3, 'd', 'e', 'v', 0xc0, 0x0a,
554 11, 'n', 'e', 't', 'w', 'o', 'r', 'k', 'b', 'o', 'o', 't',
555 0xc0, 0x05 ),
556 DATA ( "ipxe.org", "boot.ipxe.org", "dev.boot.ipxe.org",
557 "networkboot.org" ) );
558
559 /**
560 * Perform DNS self-test
561 *
562 */
dns_test_exec(void)563 static void dns_test_exec ( void ) {
564
565 /* Encoding tests */
566 dns_encode_ok ( &encode_simple );
567 dns_encode_ok ( &encode_single );
568 dns_encode_ok ( &encode_absolute );
569 dns_encode_ok ( &encode_empty );
570 dns_encode_ok ( &encode_root );
571 dns_encode_fail_ok ( &encode_initial_dot );
572 dns_encode_fail_ok ( &encode_double_dot );
573 dns_encode_fail_ok ( &encode_solo_double_dot );
574 dns_encode_fail_ok ( &encode_trailing_double_dot );
575 dns_encode_fail_ok ( &encode_overlength );
576
577 /* Decoding tests */
578 dns_decode_ok ( &decode_simple );
579 dns_decode_ok ( &decode_ptr );
580 dns_decode_ok ( &decode_root );
581 dns_decode_fail_ok ( &decode_incomplete_name );
582 dns_decode_fail_ok ( &decode_incomplete_label );
583 dns_decode_fail_ok ( &decode_incomplete_ptr );
584 dns_decode_fail_ok ( &decode_forward );
585 dns_decode_fail_ok ( &decode_infinite );
586 dns_decode_fail_ok ( &decode_empty );
587
588 /* Comparison tests */
589 dns_compare_ok ( &compare_simple );
590 dns_compare_ok ( &compare_ptr );
591 dns_compare_ok ( &compare_case );
592 dns_compare_fail_ok ( &compare_mismatch );
593 dns_compare_fail_ok ( &compare_infinite );
594
595 /* Copying tests */
596 dns_copy_ok ( ©_simple );
597 dns_copy_ok ( ©_offset );
598 dns_copy_ok ( ©_ptr );
599 dns_copy_fail_ok ( ©_infinite );
600
601 /* Search list tets */
602 dns_list_ok ( &search );
603 }
604
605 /** DNS self-test */
606 struct self_test dns_test __self_test = {
607 .name = "dns",
608 .exec = dns_test_exec,
609 };
610