/* * Copyright (C) 2014 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * * DNS self-tests * */ /* Forcibly enable assertions */ #undef NDEBUG #include #include #include #include /** Define inline data */ #define DATA(...) { __VA_ARGS__ } /** A DNS encoding test */ struct dns_encode_test { /** String */ const char *string; /** Encoded string */ const void *data; /** Length of encoded string */ int len; }; /** * Define a DNS encoding test * * @v _name Test name * @v _string Test string * @v _data Expected encoded data * @ret test DNS encoding test */ #define DNS_ENCODE( _name, _string, _data ) \ static const uint8_t _name ## __data[] = _data; \ static struct dns_encode_test _name = { \ .string = _string, \ .data = _name ## __data, \ .len = sizeof ( _name ## __data ), \ } /** * Report DNS encoding test result * * @v test DNS encoding test * @v file Test code file * @v line Test code line */ static void dns_encode_okx ( struct dns_encode_test *test, const char *file, unsigned int line ) { uint8_t data[ test->len ]; struct dns_name name; int len; /* Check ability to determine length with no buffer */ memset ( &name, 0, sizeof ( name ) ); len = dns_encode ( test->string, &name ); okx ( len >= 0, file, line ); okx ( len == test->len, file, line ); /* Check encoded name */ name.data = data; name.len = sizeof ( data ); len = dns_encode ( test->string, &name ); okx ( len >= 0, file, line ); if ( len >= 0 ) { okx ( len == test->len, file, line ); okx ( memcmp ( data, test->data, test->len ) == 0, file, line ); DBGC ( test, "DNS encoded \"%s\" to:\n", test->string ); DBGC_HDA ( test, 0, data, len ); } } #define dns_encode_ok( test ) dns_encode_okx ( test, __FILE__, __LINE__ ) /** * Report DNS encoding failure test result * * @v test DNS encoding test * @v file Test code file * @v line Test code line */ static void dns_encode_fail_okx ( struct dns_encode_test *test, const char *file, unsigned int line ) { struct dns_name name = { .data = NULL, .len = 0 }; int len; len = dns_encode ( test->string, &name ); okx ( len < 0, file, line ); } #define dns_encode_fail_ok( test ) \ dns_encode_fail_okx ( test, __FILE__, __LINE__ ) /** A DNS decoding test */ struct dns_decode_test { /** Name */ struct dns_name name; /** Expected string */ const char *string; }; /** * Define a DNS decoding test * * @v _name Test name * @v _data RFC1035-encoded data * @v _offset Starting offset within encoded data * @v _string Expected decoded string * @ret test DNS decoding test */ #define DNS_DECODE( _name, _data, _offset, _string ) \ static uint8_t _name ## __data[] = _data; \ static struct dns_decode_test _name = { \ .name = { \ .data = _name ## __data, \ .offset = _offset, \ .len = sizeof ( _name ## __data ), \ }, \ .string = _string, \ } /** * Report DNS decoding test result * * @v test DNS decoding test * @v file Test code file * @v line Test code line */ static void dns_decode_okx ( struct dns_decode_test *test, const char *file, unsigned int line ) { char string[ strlen ( test->string ) + 1 /* NUL */ ]; int len; /* Check ability to determine length with no buffer */ len = dns_decode ( &test->name, NULL, 0 ); okx ( len >= 0, file, line ); okx ( len == ( ( int ) strlen ( test->string ) ), file, line ); /* Check decoded string */ len = dns_decode ( &test->name, string, sizeof ( string ) ); okx ( len >= 0, file, line ); if ( len >= 0 ) { okx ( strcmp ( string, test->string ) == 0, file, line ); DBGC ( test, "DNS decoded \"%s\" from offset %#zx in:\n", string, test->name.offset ); DBGC_HDA ( test, 0, test->name.data, test->name.len ); } } #define dns_decode_ok( test ) dns_decode_okx ( test, __FILE__, __LINE__ ) /** * Report DNS decoding failure test result * * @v test DNS decoding test * @v file Test code file * @v line Test code line */ static void dns_decode_fail_okx ( struct dns_decode_test *test, const char *file, unsigned int line ) { int len; len = dns_decode ( &test->name, NULL, 0 ); okx ( len < 0, file, line ); } #define dns_decode_fail_ok( test ) \ dns_decode_fail_okx ( test, __FILE__, __LINE__ ) /** A DNS comparison test */ struct dns_compare_test { /** First name */ struct dns_name first; /** Second name */ struct dns_name second; }; /** * Define a DNS comparison test * * @v _name Test name * @v _first_data First RFC1035-encoded data * @v _first_offset Starting offset within first encoded data * @v _second_data Second RFC1035-encoded data * @v _second_offset Starting offset within second encoded data * @ret test DNS comparison test */ #define DNS_COMPARE( _name, _first_data, _first_offset, _second_data, \ _second_offset ) \ static uint8_t _name ## __first_data[] = _first_data; \ static uint8_t _name ## __second_data[] = _second_data; \ static struct dns_compare_test _name = { \ .first = { \ .data = _name ## __first_data, \ .offset = _first_offset, \ .len = sizeof ( _name ## __first_data ), \ }, \ .second = { \ .data = _name ## __second_data, \ .offset = _second_offset, \ .len = sizeof ( _name ## __second_data ), \ }, \ } /** * Report DNS comparison test result * * @v test DNS comparison test * @v file Test code file * @v line Test code line */ static void dns_compare_okx ( struct dns_compare_test *test, const char *file, unsigned int line ) { okx ( dns_compare ( &test->first, &test->second ) == 0, file, line ); } #define dns_compare_ok( test ) dns_compare_okx ( test, __FILE__, __LINE__ ) /** * Report DNS comparison test failure result * * @v test DNS comparison test * @v file Test code file * @v line Test code line */ static void dns_compare_fail_okx ( struct dns_compare_test *test, const char *file, unsigned int line ) { okx ( dns_compare ( &test->first, &test->second ) != 0, file, line ); } #define dns_compare_fail_ok( test ) \ dns_compare_fail_okx ( test, __FILE__, __LINE__ ) /** A DNS copying test */ struct dns_copy_test { /** Source name */ struct dns_name src; /** Expected copied name */ struct dns_name dst; }; /** * Define a DNS copying test * * @v _name Test name * @v _src_data Source RFC1035-encoded data * @v _src_offset Starting offset within source encoded data * @v _dst_data Expected copied RFC1035-encoded data * @v _dst_offset Starting offset withint copied encoded data * @ret test DNS copying test */ #define DNS_COPY( _name, _src_data, _src_offset, _dst_data, \ _dst_offset ) \ static uint8_t _name ## __src_data[] = _src_data; \ static uint8_t _name ## __dst_data[] = _dst_data; \ static struct dns_copy_test _name = { \ .src = { \ .data = _name ## __src_data, \ .offset = _src_offset, \ .len = sizeof ( _name ## __src_data ), \ }, \ .dst = { \ .data = _name ## __dst_data, \ .offset = _dst_offset, \ .len = sizeof ( _name ## __dst_data ), \ }, \ } /** * Report a DNS copying test result * * @v test DNS copying test * @v file Test code file * @v line Test code line */ static void dns_copy_okx ( struct dns_copy_test *test, const char *file, unsigned int line ) { uint8_t data[ test->dst.len ]; struct dns_name dst; int len; /* Check ability to determine length with no buffer */ memset ( &dst, 0, sizeof ( dst ) ); len = dns_copy ( &test->src, &dst ); okx ( len >= 0, file, line ); okx ( len == ( ( int ) ( test->dst.len - test->dst.offset ) ), file, line ); /* Check copied name */ dst.data = data; dst.offset = test->dst.offset; dst.len = sizeof ( data ); memcpy ( dst.data, test->dst.data, test->dst.offset ); len = dns_copy ( &test->src, &dst ); okx ( len >= 0, file, line ); okx ( len == ( ( int ) ( test->dst.len - test->dst.offset ) ), file, line ); okx ( memcmp ( data, test->dst.data, sizeof ( data ) ) == 0, file, line ); DBGC ( test, "DNS copied:\n" ); DBGC_HDA ( test, 0, test->src.data, test->src.len ); DBGC_HDA ( test, 0, data, ( test->dst.offset + len ) ); } #define dns_copy_ok( test ) dns_copy_okx ( test, __FILE__, __LINE__ ) /** * Report a DNS copying failure test result * * @v test DNS copying test * @v file Test code file * @v line Test code line */ static void dns_copy_fail_okx ( struct dns_copy_test *test, const char *file, unsigned int line ) { struct dns_name dst; int len; memset ( &dst, 0, sizeof ( dst ) ); len = dns_copy ( &test->src, &dst ); okx ( len < 0, file, line ); } #define dns_copy_fail_ok( test ) dns_copy_fail_okx ( test, __FILE__, __LINE__ ) /** A DNS search list test */ struct dns_list_test { /** Search list */ struct dns_name list; /** Expected decoded search list */ const char **strings; /** Number of expected decoded string */ unsigned int count; }; /** * Define a DNS search list test * * @v _name Test name * @v _list RFC1035-encoded data * @v _strings Expected decoded strings * @ret test DNS search list test */ #define DNS_LIST( _name, _list, _strings ) \ static uint8_t _name ## __list[] = _list; \ static const char * _name ## __strings[] = _strings; \ static struct dns_list_test _name = { \ .list = { \ .data = _name ## __list, \ .offset = 0, \ .len = sizeof ( _name ## __list ), \ }, \ .strings = _name ## __strings, \ .count = ( sizeof ( _name ## __strings ) / \ sizeof ( _name ## __strings[0] ) ), \ } /** * Report DNS search list test result * * @v test DNS search list test * @v file Test code file * @v line Test code line */ static void dns_list_okx ( struct dns_list_test *test, const char *file, unsigned int line ) { struct dns_name name; unsigned int i; DBGC ( test, "DNS search list:\n" ); DBGC_HDA ( test, 0, test->list.data, test->list.len ); memcpy ( &name, &test->list, sizeof ( name ) ); for ( i = 0 ; i < test->count ; i++ ) { char buf[ strlen ( test->strings[i] ) + 1 /* NUL */ ]; int len; int offset; /* Decode this name */ len = dns_decode ( &name, buf, sizeof ( buf ) ); okx ( len >= 0, file, line ); if ( len >= 0 ) { okx ( len == ( ( int ) strlen ( test->strings[i] ) ), file, line ); okx ( strcmp ( buf, test->strings[i] ) == 0, file, line ); DBGC ( test, "DNS search list found \"%s\" at offset " "%#zx\n", buf, name.offset ); } /* Skip to next name */ offset = dns_skip ( &name ); okx ( offset >= 0, file, line ); name.offset = offset; } /* Check that we have consumed the whole search list */ okx ( name.offset == name.len, file, line ); } #define dns_list_ok( test ) dns_list_okx ( test, __FILE__, __LINE__ ) /* Simple encoding test */ DNS_ENCODE ( encode_simple, "ipxe.org", DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ) ); /* Single-word encoding test */ DNS_ENCODE ( encode_single, "foo", DATA ( 3, 'f', 'o', 'o', 0 ) ); /* Absolute encoding test */ DNS_ENCODE ( encode_absolute, "git.ipxe.org.", DATA ( 3, 'g', 'i', 't', 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ) ); /* Empty string encoding test */ DNS_ENCODE ( encode_empty, "", DATA ( 0 ) ); /* Root domain encoding test */ DNS_ENCODE ( encode_root, ".", DATA ( 0 ) ); /* Invalid initial dot encoding test */ DNS_ENCODE ( encode_initial_dot, ".foo", DATA() ); /* Invalid double dot encoding test */ DNS_ENCODE ( encode_double_dot, "ipxe..org", DATA() ); /* Invalid solo double dot encoding test */ DNS_ENCODE ( encode_solo_double_dot, "..", DATA() ); /* Invalid trailing double dot encoding test */ DNS_ENCODE ( encode_trailing_double_dot, "ipxe.org..", DATA() ); /* Invalid overlength label encoding test */ DNS_ENCODE ( encode_overlength, "this-label-is-maliciously-long-in-an-attempt-to-overflow-the-" "length-field-and-generate-a-length-which-looks-like-a-" "compression-pointer", DATA() ); /* Simple decoding test */ DNS_DECODE ( decode_simple, DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0, "ipxe.org" ); /* Compression pointer decoding test */ DNS_DECODE ( decode_ptr, DATA ( 3, 'o', 'r', 'g', 0, 3, 'g', 'i', 't', 4, 'i', 'p', 'x', 'e', 0xc0, 0x00 ), 5, "git.ipxe.org" ); /* Root decoding test */ DNS_DECODE ( decode_root, DATA ( 0 ), 0, "" ); /* Incomplete name decoding test */ DNS_DECODE ( decode_incomplete_name, DATA ( 4, 'i', 'p', 'x', 'e' ), 0, NULL ); /* Incomplete label decoding test */ DNS_DECODE ( decode_incomplete_label, DATA ( 4, 'i', 'p', 'x' ), 0, NULL ); /* Incomplete compression pointer decoding test */ DNS_DECODE ( decode_incomplete_ptr, DATA ( 3, 'o', 'r', 'g', 0, 4, 'i', 'p', 'x', 'e', 0xc0 ), 5, NULL ); /* Forward reference decoding test */ DNS_DECODE ( decode_forward, DATA ( 0xc0, 0x02, 3, 'f', 'o', 'o', 0 ), 0, NULL ); /* Infinite loop decoding test */ DNS_DECODE ( decode_infinite, DATA ( 4, 'i', 'p', 'x', 'e', 0xc0, 0x00 ), 0, NULL ); /* Empty decoding test */ DNS_DECODE ( decode_empty, DATA (), 0, NULL ); /* Simple comparison test */ DNS_COMPARE ( compare_simple, DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0, DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0 ); /* Compression pointer comparison test */ DNS_COMPARE ( compare_ptr, DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0, DATA ( 3, 'o', 'r', 'g', 0, 4, 'i', 'p', 'x', 'e', 0xc0, 0x00 ), 5 ); /* Case insensitive comparison test */ DNS_COMPARE ( compare_case, DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0, DATA ( 4, 'i', 'p', 'x', 'e', 3, 'O', 'R', 'G', 0 ), 0 ); /* Mismatch comparison test */ DNS_COMPARE ( compare_mismatch, DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0, DATA ( 4, 'g', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0 ); /* Infinite loop comparison test */ DNS_COMPARE ( compare_infinite, DATA ( 3, 'f', 'o', 'o', 0xc0, 0x00 ), 0, DATA ( 3, 'f', 'o', 'o', 0xc0, 0x00 ), 0 ); /* Simple copying test */ DNS_COPY ( copy_simple, DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0, DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0 ); /* Simple copying test with offset */ DNS_COPY ( copy_offset, DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0, DATA ( 'f', 'o', 'o', 0, 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 4 ); /* Compression pointer copying test */ DNS_COPY ( copy_ptr, DATA ( 3, 'o', 'r', 'g', 0, 3, 'g', 'i', 't', 4, 'i', 'p', 'x', 'e', 0xc0, 0x00 ), 5, DATA ( 3, 'g', 'i', 't', 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0 ), 0 ); /* Infinite loop copying test */ DNS_COPY ( copy_infinite, DATA ( 4, 'l', 'o', 'o', 'p', 7, 'f', 'o', 'r', 'e', 'v', 'e', 'r', 0xc0, 0x05 ), 0, DATA (), 0 ); /* DNS search list test */ DNS_LIST ( search, DATA ( 4, 'i', 'p', 'x', 'e', 3, 'o', 'r', 'g', 0, 4, 'b', 'o', 'o', 't', 0xc0, 0x00, 3, 'd', 'e', 'v', 0xc0, 0x0a, 11, 'n', 'e', 't', 'w', 'o', 'r', 'k', 'b', 'o', 'o', 't', 0xc0, 0x05 ), DATA ( "ipxe.org", "boot.ipxe.org", "dev.boot.ipxe.org", "networkboot.org" ) ); /** * Perform DNS self-test * */ static void dns_test_exec ( void ) { /* Encoding tests */ dns_encode_ok ( &encode_simple ); dns_encode_ok ( &encode_single ); dns_encode_ok ( &encode_absolute ); dns_encode_ok ( &encode_empty ); dns_encode_ok ( &encode_root ); dns_encode_fail_ok ( &encode_initial_dot ); dns_encode_fail_ok ( &encode_double_dot ); dns_encode_fail_ok ( &encode_solo_double_dot ); dns_encode_fail_ok ( &encode_trailing_double_dot ); dns_encode_fail_ok ( &encode_overlength ); /* Decoding tests */ dns_decode_ok ( &decode_simple ); dns_decode_ok ( &decode_ptr ); dns_decode_ok ( &decode_root ); dns_decode_fail_ok ( &decode_incomplete_name ); dns_decode_fail_ok ( &decode_incomplete_label ); dns_decode_fail_ok ( &decode_incomplete_ptr ); dns_decode_fail_ok ( &decode_forward ); dns_decode_fail_ok ( &decode_infinite ); dns_decode_fail_ok ( &decode_empty ); /* Comparison tests */ dns_compare_ok ( &compare_simple ); dns_compare_ok ( &compare_ptr ); dns_compare_ok ( &compare_case ); dns_compare_fail_ok ( &compare_mismatch ); dns_compare_fail_ok ( &compare_infinite ); /* Copying tests */ dns_copy_ok ( ©_simple ); dns_copy_ok ( ©_offset ); dns_copy_ok ( ©_ptr ); dns_copy_fail_ok ( ©_infinite ); /* Search list tets */ dns_list_ok ( &search ); } /** DNS self-test */ struct self_test dns_test __self_test = { .name = "dns", .exec = dns_test_exec, };