summaryrefslogtreecommitdiffstats
path: root/src/tests/dns_test.c
diff options
context:
space:
mode:
authorMichael Brown2014-01-31 19:16:42 +0100
committerMichael Brown2014-02-05 15:56:49 +0100
commitd4c0226a6ce888218f258df409bb6a955e727d81 (patch)
tree365925bb9a14bdfb25763088ee0f53944181500b /src/tests/dns_test.c
parent[intel] Add some missing PCI IDs (diff)
downloadipxe-d4c0226a6ce888218f258df409bb6a955e727d81.tar.gz
ipxe-d4c0226a6ce888218f258df409bb6a955e727d81.tar.xz
ipxe-d4c0226a6ce888218f258df409bb6a955e727d81.zip
[dns] Support DNS search lists
Update the DNS resolver to support DNS search lists (as provided by DHCP option 119, DHCPv6 option 24, or NDP option 31). Add validation code to ensure that parsing of DNS packets does not overrun the input, get stuck in infinite loops, or (worse) write beyond the end of allocated buffers. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/tests/dns_test.c')
-rw-r--r--src/tests/dns_test.c605
1 files changed, 605 insertions, 0 deletions
diff --git a/src/tests/dns_test.c b/src/tests/dns_test.c
new file mode 100644
index 00000000..52f5f19f
--- /dev/null
+++ b/src/tests/dns_test.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/** @file
+ *
+ * DNS self-tests
+ *
+ */
+
+/* Forcibly enable assertions */
+#undef NDEBUG
+
+#include <string.h>
+#include <assert.h>
+#include <ipxe/dns.h>
+#include <ipxe/test.h>
+
+/** 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 ( &copy_simple );
+ dns_copy_ok ( &copy_offset );
+ dns_copy_ok ( &copy_ptr );
+ dns_copy_fail_ok ( &copy_infinite );
+
+ /* Search list tets */
+ dns_list_ok ( &search );
+}
+
+/** DNS self-test */
+struct self_test dns_test __self_test = {
+ .name = "dns",
+ .exec = dns_test_exec,
+};