diff options
author | Florian Weimer <fweimer@redhat.com> | 2016-12-31 14:06:16 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2016-12-31 18:52:32 +0100 |
commit | 5840c75c2d6a9b980d6789f2ca7d47a9fa067263 (patch) | |
tree | 3eb4c28827ba0901f9ae7396d841c396c393805e /resolv | |
parent | f47ae5186624e5bb3a2d2b25be742b90a1eee3cd (diff) | |
download | glibc-5840c75c2d6a9b980d6789f2ca7d47a9fa067263.tar.gz |
resolv: Add beginnings of a libresolv test suite
Diffstat (limited to 'resolv')
-rw-r--r-- | resolv/Makefile | 18 | ||||
-rw-r--r-- | resolv/tst-bug18665-tcp.c | 229 | ||||
-rw-r--r-- | resolv/tst-bug18665.c | 138 | ||||
-rw-r--r-- | resolv/tst-res_use_inet6.c | 201 | ||||
-rw-r--r-- | resolv/tst-resolv-basic.c | 315 | ||||
-rw-r--r-- | resolv/tst-resolv-network.c | 299 | ||||
-rw-r--r-- | resolv/tst-resolv-search.c | 343 |
7 files changed, 1542 insertions, 1 deletions
diff --git a/resolv/Makefile b/resolv/Makefile index bd086e0e7d..5eb10e38af 100644 --- a/resolv/Makefile +++ b/resolv/Makefile @@ -39,7 +39,16 @@ extra-libs := libresolv libnss_dns ifeq ($(have-thread-library),yes) extra-libs += libanl routines += gai_sigqueue -tests += tst-res_hconf_reorder + +tests += \ + tst-bug18665 \ + tst-bug18665-tcp \ + tst-res_hconf_reorder \ + tst-res_use_inet6 \ + tst-resolv-basic \ + tst-resolv-network \ + tst-resolv-search \ + endif extra-libs-others = $(extra-libs) libresolv-routines := res_comp res_debug \ @@ -108,3 +117,10 @@ tst-leaks2-ENV = MALLOC_TRACE=$(objpfx)tst-leaks2.mtrace $(objpfx)mtrace-tst-leaks2.out: $(objpfx)tst-leaks2.out $(common-objpfx)malloc/mtrace $(objpfx)tst-leaks2.mtrace > $@; \ $(evaluate-test) + +$(objpfx)tst-bug18665-tcp: $(objpfx)libresolv.so $(shared-thread-library) +$(objpfx)tst-bug18665: $(objpfx)libresolv.so $(shared-thread-library) +$(objpfx)tst-res_use_inet6: $(objpfx)libresolv.so $(shared-thread-library) +$(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library) +$(objpfx)tst-resolv-network: $(objpfx)libresolv.so $(shared-thread-library) +$(objpfx)tst-resolv-search: $(objpfx)libresolv.so $(shared-thread-library) diff --git a/resolv/tst-bug18665-tcp.c b/resolv/tst-bug18665-tcp.c new file mode 100644 index 0000000000..fcd3bafdc0 --- /dev/null +++ b/resolv/tst-bug18665-tcp.c @@ -0,0 +1,229 @@ +/* Test __libc_res_nsend buffer mismanagement, basic TCP coverage. + Copyright (C) 2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <netdb.h> +#include <resolv.h> +#include <stdio.h> +#include <string.h> +#include <support/check.h> +#include <support/check_nss.h> +#include <support/resolv_test.h> +#include <support/xthread.h> +#include <support/xmemstream.h> + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +static int initial_address_count = 1; +static int subsequent_address_count = 2000; +static int response_number = 0; + +static void +response (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype) +{ + TEST_VERIFY_EXIT (qname != NULL); + + /* If not using TCP, just force its use. */ + if (!ctx->tcp) + { + struct resolv_response_flags flags = {.tc = true}; + resolv_response_init (b, flags); + resolv_response_add_question (b, qname, qclass, qtype); + return; + } + + struct resolv_response_flags flags = {}; + resolv_response_init (b, flags); + resolv_response_add_question (b, qname, qclass, qtype); + + resolv_response_section (b, ns_s_an); + + /* The number of addresses (in the additional section) for the name + server record (in the authoritative section). */ + int address_count; + xpthread_mutex_lock (&lock); + ++response_number; + if (response_number == 1) + address_count = initial_address_count; + else if (response_number == 2) + { + address_count = 0; + resolv_response_drop (b); + resolv_response_close (b); + } + else + address_count = subsequent_address_count; + xpthread_mutex_unlock (&lock); + + /* Only add the address record to the answer section if we requested + any name server addresses. */ + if (address_count > 0) + { + resolv_response_open_record (b, qname, qclass, qtype, 0); + switch (qtype) + { + case T_A: + { + char ipv4[4] = {10, response_number >> 8, response_number, 0}; + ipv4[3] = 2 * ctx->tcp + 4 * ctx->server_index; + resolv_response_add_data (b, &ipv4, sizeof (ipv4)); + } + break; + case T_AAAA: + { + char ipv6[16] + = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, + response_number >> 8, response_number, 0, 0}; + ipv6[15] = 2 * ctx->tcp + 4 * ctx->server_index; + resolv_response_add_data (b, &ipv6, sizeof (ipv6)); + } + break; + default: + support_record_failure (); + printf ("error: unexpected QTYPE: %s/%u/%u\n", + qname, qclass, qtype); + } + resolv_response_close_record (b); + + /* Add the name server record. */ + resolv_response_section (b, ns_s_ns); + resolv_response_open_record (b, "example", C_IN, T_NS, 0); + resolv_response_add_name (b, "ns.example"); + resolv_response_close_record (b); + + /* Increase the response size with name server addresses. These + addresses are not copied out of nss_dns, and thus do not + trigger getaddrinfo retries with a larger buffer, making + testing more predictable. */ + resolv_response_section (b, ns_s_ar); + for (int i = 1; i <= address_count; ++i) + { + resolv_response_open_record (b, "ns.example", qclass, qtype, 0); + switch (qtype) + { + case T_A: + { + char ipv4[4] = {response_number, i >> 8, i, 0}; + ipv4[3] = 2 * ctx->tcp + 4 * ctx->server_index; + resolv_response_add_data (b, &ipv4, sizeof (ipv4)); + } + break; + case T_AAAA: + { + char ipv6[16] + = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, + response_number >> 8, response_number, + i >> 8, i, 0, 0}; + ipv6[15] = 2 * ctx->tcp + 4 * ctx->server_index; + resolv_response_add_data (b, &ipv6, sizeof (ipv6)); + } + break; + default: + support_record_failure (); + printf ("error: unexpected QTYPE: %s/%u/%u\n", + qname, qclass, qtype); + } + resolv_response_close_record (b); + } + } +} + +static char * +expected_result (unsigned port, unsigned response_number) +{ + struct xmemstream mem; + xopen_memstream (&mem); + /* We fail the second TCP query to the first server by closing the + connection immediately, without returning any data. This should + cause failover to the second server. */ + int server_index = 1; + fprintf (mem.out, "address: STREAM/TCP 10.%u.%u.%u %u\n", + (response_number >> 8) & 0xff, response_number & 0xff, + 2 + 4 * server_index, port); + fprintf (mem.out, "address: STREAM/TCP 2001:db8::%x:%x %u\n", + (response_number + 1) & 0xffff, + 2 + 4 * server_index, port); + xfclose_memstream (&mem); + return mem.buffer; +} + +static void +test_different_sizes (void) +{ + struct addrinfo hints = + { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP, + }; + struct addrinfo *ai; + char *expected; + int ret; + + /* This magic number produces a response size close to 2048 + bytes. */ + initial_address_count = 124; + response_number = 0; + + ret = getaddrinfo ("www.example", "80", &hints, &ai); + expected = expected_result (80, 3); + check_addrinfo ("www.example:80", ai, ret, expected); + if (ret == 0) + freeaddrinfo (ai); + free (expected); + + response_number = 0; + ret = getaddrinfo ("www123.example", "80", &hints, &ai); + if (ret == 0) + freeaddrinfo (ai); + + response_number = 0; + ret = getaddrinfo ("www1234.example", "80", &hints, &ai); + if (ret == 0) + freeaddrinfo (ai); + + response_number = 0; + ret = getaddrinfo ("www12345.example", "80", &hints, &ai); + if (ret == 0) + freeaddrinfo (ai); +} + +static int +do_test (void) +{ + struct resolv_test *obj = resolv_test_start + ((struct resolv_redirect_config) + { + .response_callback = response + }); + + test_different_sizes (); + + _res.options |= RES_SNGLKUP; + test_different_sizes (); + + _res.options |= RES_SNGLKUPREOP; + test_different_sizes (); + + resolv_test_end (obj); + return 0; +} + +#include <support/test-driver.c> diff --git a/resolv/tst-bug18665.c b/resolv/tst-bug18665.c new file mode 100644 index 0000000000..2d0cdb2a96 --- /dev/null +++ b/resolv/tst-bug18665.c @@ -0,0 +1,138 @@ +/* Test for __libc_res_nsend buffer mismanagent (bug 18665), UDP case. + Copyright (C) 2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <netdb.h> +#include <resolv.h> +#include <stdio.h> +#include <string.h> +#include <support/check.h> +#include <support/resolv_test.h> +#include <support/xthread.h> + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +static int initial_address_count; +static int response_count; + +static void +response (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype) +{ + TEST_VERIFY_EXIT (qname != NULL); + struct resolv_response_flags flags = {}; + resolv_response_init (b, flags); + resolv_response_add_question (b, qname, qclass, qtype); + + resolv_response_section (b, ns_s_an); + + /* Add many A/AAAA records to the second response. */ + int address_count; + xpthread_mutex_lock (&lock); + if (response_count == 0) + address_count = initial_address_count; + else + address_count = 2000; + ++response_count; + xpthread_mutex_unlock (&lock); + + for (int i = 0; i < address_count; ++i) + { + resolv_response_open_record (b, qname, qclass, qtype, 0); + switch (qtype) + { + case T_A: + { + char ipv4[4] = {10, i >> 8, i, 0}; + ipv4[3] = 2 * ctx->tcp + 4 * ctx->server_index; + resolv_response_add_data (b, &ipv4, sizeof (ipv4)); + } + break; + case T_AAAA: + { + char ipv6[16] + = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + i >> 8, i, 0}; + ipv6[15] = 2 * ctx->tcp + 4 * ctx->server_index; + resolv_response_add_data (b, &ipv6, sizeof (ipv6)); + } + break; + default: + support_record_failure (); + printf ("error: unexpected QTYPE: %s/%u/%u\n", + qname, qclass, qtype); + } + resolv_response_close_record (b); + } +} + +static void +test_different_sizes (void) +{ + struct addrinfo hints = { .ai_family = AF_UNSPEC, }; + struct addrinfo *ai; + int ret; + + /* This magic number produces a response size close to 2048 + bytes. */ + initial_address_count = 126; + response_count = 0; + + ret = getaddrinfo ("www.example", "80", &hints, &ai); + if (ret == 0) + freeaddrinfo (ai); + + response_count = 0; + ret = getaddrinfo ("www123.example", "80", &hints, &ai); + if (ret == 0) + freeaddrinfo (ai); + + response_count = 0; + ret = getaddrinfo ("www1234.example", "80", &hints, &ai); + if (ret == 0) + freeaddrinfo (ai); + + response_count = 0; + ret = getaddrinfo ("www12345.example", "80", &hints, &ai); + if (ret == 0) + freeaddrinfo (ai); +} + +static int +do_test (void) +{ + struct resolv_test *obj = resolv_test_start + ((struct resolv_redirect_config) + { + .response_callback = response + }); + + test_different_sizes (); + + _res.options |= RES_SNGLKUP; + test_different_sizes (); + + _res.options |= RES_SNGLKUPREOP; + test_different_sizes (); + + resolv_test_end (obj); + return 0; +} + +#include <support/test-driver.c> diff --git a/resolv/tst-res_use_inet6.c b/resolv/tst-res_use_inet6.c new file mode 100644 index 0000000000..5ed0ad0dc4 --- /dev/null +++ b/resolv/tst-res_use_inet6.c @@ -0,0 +1,201 @@ +/* Basic functionality tests for inet6 option processing. + Copyright (C) 2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <netdb.h> +#include <resolv.h> +#include <string.h> +#include <support/check_nss.h> +#include <support/resolv_test.h> +#include <support/xthread.h> + +static void +response (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype) +{ + bool include_both = strcmp (qname, "both.example") == 0; + bool include_a = qtype == T_A || include_both; + bool include_aaaa = qtype == T_AAAA || include_both; + + resolv_response_init (b, (struct resolv_response_flags) {}); + resolv_response_add_question (b, qname, qclass, qtype); + resolv_response_section (b, ns_s_an); + if (include_a) + { + char ipv4[4] = {192, 0, 2, 17}; + resolv_response_open_record (b, qname, qclass, T_A, 0); + resolv_response_add_data (b, &ipv4, sizeof (ipv4)); + resolv_response_close_record (b); + } + if (include_aaaa) + { + char ipv6[16] + = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; + resolv_response_open_record (b, qname, qclass, T_AAAA, 0); + resolv_response_add_data (b, &ipv6, sizeof (ipv6)); + resolv_response_close_record (b); + } +} + +/* Test that getaddrinfo is not influenced by RES_USE_INET6. */ +static void +test_gai (void) +{ + { + struct addrinfo hints = + { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP, + }; + struct addrinfo *ai; + int ret = getaddrinfo ("www1.example", "80", &hints, &ai); + check_addrinfo ("getaddrinfo AF_UNSPEC www1.example", ai, ret, + "address: STREAM/TCP 192.0.2.17 80\n" + "address: STREAM/TCP 2001:db8::1 80\n"); + if (ret == 0) + freeaddrinfo (ai); + ret = getaddrinfo ("both.example", "80", &hints, &ai); + /* Combined A/AAAA responses currently result in address + duplication. */ + check_addrinfo ("getaddrinfo AF_UNSPEC both.example", ai, ret, + "address: STREAM/TCP 192.0.2.17 80\n" + "address: STREAM/TCP 192.0.2.17 80\n" + "address: STREAM/TCP 2001:db8::1 80\n" + "address: STREAM/TCP 2001:db8::1 80\n"); + if (ret == 0) + freeaddrinfo (ai); + } + { + struct addrinfo hints = + { + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP, + }; + struct addrinfo *ai; + int ret = getaddrinfo ("www1.example", "80", &hints, &ai); + check_addrinfo ("getaddrinfo AF_INET www1.example", ai, ret, + "address: STREAM/TCP 192.0.2.17 80\n"); + if (ret == 0) + freeaddrinfo (ai); + ret = getaddrinfo ("both.example", "80", &hints, &ai); + check_addrinfo ("getaddrinfo AF_INET both.example", ai, ret, + "address: STREAM/TCP 192.0.2.17 80\n"); + if (ret == 0) + freeaddrinfo (ai); + } + { + struct addrinfo hints = + { + .ai_family = AF_INET6, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP, + }; + struct addrinfo *ai; + int ret = getaddrinfo ("www1.example", "80", &hints, &ai); + check_addrinfo ("getaddrinfo (AF_INET6)", ai, ret, + "address: STREAM/TCP 2001:db8::1 80\n"); + if (ret == 0) + freeaddrinfo (ai); + ret = getaddrinfo ("both.example", "80", &hints, &ai); + check_addrinfo ("getaddrinfo AF_INET6 both.example", ai, ret, + "address: STREAM/TCP 2001:db8::1 80\n"); + if (ret == 0) + freeaddrinfo (ai); + } +} + +/* Test that gethostbyname2 is not influenced by RES_USE_INET6. */ +static void +test_get2 (void) +{ + check_hostent ("gethostbyname2 AF_INET www1.example", + gethostbyname2 ("www1.example", AF_INET), + "name: www1.example\n" + "address: 192.0.2.17\n"); + check_hostent ("gethostbyname2 AF_INET both.example", + gethostbyname2 ("both.example", AF_INET), + "name: both.example\n" + "address: 192.0.2.17\n"); + + check_hostent ("gethostbyname2 AF_INET6 www1.example", + gethostbyname2 ("www1.example", AF_INET6), + "name: www1.example\n" + "address: 2001:db8::1\n"); + check_hostent ("gethostbyname2 AF_INET6 both.example", + gethostbyname2 ("both.example", AF_INET6), + "name: both.example\n" + "address: 2001:db8::1\n"); +} + +static void * +threadfunc (void *ignored) +{ + struct resolv_test *obj = resolv_test_start + ((struct resolv_redirect_config) + { + .response_callback = response + }); + + check_hostent ("gethostbyname (\"www1.example\")", + gethostbyname ("www1.example"), + "name: www1.example\n" + "address: 192.0.2.17\n"); + check_hostent ("gethostbyname (\"both.example\")", + gethostbyname ("both.example"), + "name: both.example\n" + "address: 192.0.2.17\n"); + test_get2 (); + test_gai (); + + _res.options |= RES_USE_INET6; + check_hostent ("gethostbyname (\"www1.example\")", + gethostbyname ("www1.example"), + "name: www1.example\n" + "address: 2001:db8::1\n"); + check_hostent ("gethostbyname (\"both.example\")", + gethostbyname ("both.example"), + "name: both.example\n" + "address: 2001:db8::1\n"); + test_get2 (); + test_gai (); + + resolv_test_end (obj); + + return NULL; +} + +static int +do_test (void) +{ + resolv_test_init (); + + /* Attempt to run on a non-main thread first. */ + { + pthread_t thr = xpthread_create (NULL, threadfunc, NULL); + xpthread_join (thr); + } + + /* Try the main thread next. */ + threadfunc (NULL); + + return 0; +} + +#include <support/test-driver.c> diff --git a/resolv/tst-resolv-basic.c b/resolv/tst-resolv-basic.c new file mode 100644 index 0000000000..78c581df1d --- /dev/null +++ b/resolv/tst-resolv-basic.c @@ -0,0 +1,315 @@ +/* Test basic nss_dns functionality and the resolver test harness itself. + Copyright (C) 2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <support/check.h> +#include <support/check_nss.h> +#include <support/resolv_test.h> +#include <support/support.h> + +static void +response (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype) +{ + TEST_VERIFY_EXIT (qname != NULL); + + /* The "t." prefix can be used to request TCP fallback. */ + bool force_tcp; + if (strncmp ("t.", qname, 2) == 0) + force_tcp = true; + else + force_tcp = false; + const char *qname_compare; + if (force_tcp) + qname_compare = qname + 2; + else + qname_compare = qname; + enum {www, alias, nxdomain} requested_qname; + if (strcmp (qname_compare, "www.example") == 0) + requested_qname = www; + else if (strcmp (qname_compare, "alias.example") == 0) + requested_qname = alias; + else if (strcmp (qname_compare, "nxdomain.example") == 0) + requested_qname = nxdomain; + else + { + support_record_failure (); + printf ("error: unexpected QNAME: %s\n", qname); + return; + } + TEST_VERIFY_EXIT (qclass == C_IN); + struct resolv_response_flags flags = {.tc = force_tcp && !ctx->tcp}; + if (requested_qname == nxdomain) + flags.rcode = 3; /* NXDOMAIN */ + resolv_response_init (b, flags); + resolv_response_add_question (b, qname, qclass, qtype); + if (requested_qname == nxdomain || flags.tc) + return; + + resolv_response_section (b, ns_s_an); + switch (requested_qname) + { + case www: + resolv_response_open_record (b, qname, qclass, qtype, 0); + break; + case alias: + resolv_response_open_record (b, qname, qclass, T_CNAME, 0); + resolv_response_add_name (b, "www.example"); + resolv_response_close_record (b); + resolv_response_open_record (b, "www.example", qclass, qtype, 0); + break; + case nxdomain: + FAIL_EXIT1 ("unreachable"); + } + switch (qtype) + { + case T_A: + { + char ipv4[4] = {192, 0, 2, 17}; + ipv4[3] += requested_qname + 2 * ctx->tcp + 4 * ctx->server_index; + resolv_response_add_data (b, &ipv4, sizeof (ipv4)); + } + break; + case T_AAAA: + { + char ipv6[16] + = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; + ipv6[15] += requested_qname + 2 * ctx->tcp + 4 * ctx->server_index; + resolv_response_add_data (b, &ipv6, sizeof (ipv6)); + } + break; + default: + support_record_failure (); + printf ("error: unexpected QTYPE: %s/%u/%u\n", + qname, qclass, qtype); + } + resolv_response_close_record (b); +} + +static void +check_h (const char *name, int family, const char *expected) +{ + if (family == AF_INET) + { + char *query = xasprintf ("gethostbyname (\"%s\")", name); + check_hostent (query, gethostbyname (name), expected); + free (query); + } + { + char *query = xasprintf ("gethostbyname2 (\"%s\", %d)", name, family); + check_hostent (query, gethostbyname2 (name, family), expected); + free (query); + } + + bool too_small = true; + for (unsigned int offset = 0; offset < 8; ++offset) + for (unsigned int size = 1; too_small; ++size) + { + char *buf = xmalloc (offset + size); + too_small = false; + + struct hostent hostbuf; + struct hostent *result; + int herror; + if (family == AF_INET) + { + char *query = xasprintf ("gethostbyname (\"%s\") %u/%u", + name, offset, size); + int ret = gethostbyname_r + (name, &hostbuf, buf + offset, size, &result, &herror); + if (ret == 0) + { + h_errno = herror; + check_hostent (query, result, expected); + } + else if (ret == ERANGE) + too_small = true; + else + { + errno = ret; + FAIL_EXIT1 ("gethostbyname_r: %m"); + } + free (query); + memset (buf, 0, offset + size); + } + char *query = xasprintf ("gethostbyname2 (\"%s\", %d) %u/%u", + name, family, offset, size); + int ret = gethostbyname2_r + (name, family, &hostbuf, buf + offset, size, &result, &herror); + if (ret == 0) + { + h_errno = herror; + check_hostent (query, result, expected); + } + else if (ret == ERANGE) + too_small = true; + else + { + errno = ret; + FAIL_EXIT1 ("gethostbyname_r: %m"); + } + free (buf); + free (query); + } +} + +static void +check_ai (const char *name, const char *service, + int family, const char *expected) +{ + struct addrinfo hints = {.ai_family = family}; + struct addrinfo *ai; + char *query = xasprintf ("%s:%s [%d]", name, service, family); + int ret = getaddrinfo (name, service, &hints, &ai); + check_addrinfo (query, ai, ret, expected); + if (ret == 0) + freeaddrinfo (ai); + free (query); +} + +static int +do_test (void) +{ + struct resolv_test *aux = resolv_test_start + ((struct resolv_redirect_config) + { + .response_callback = response, + }); + + check_h ("www.example", AF_INET, + "name: www.example\n" + "address: 192.0.2.17\n"); + check_h ("alias.example", AF_INET, + "name: www.example\n" + "alias: alias.example\n" + "address: 192.0.2.18\n"); + check_h ("www.example", AF_INET6, + "name: www.example\n" + "address: 2001:db8::1\n"); + check_h ("alias.example", AF_INET6, + "name: www.example\n" + "alias: alias.example\n" + "address: 2001:db8::2\n"); + check_ai ("www.example", "80", AF_UNSPEC, + "address: STREAM/TCP 192.0.2.17 80\n" + "address: DGRAM/UDP 192.0.2.17 80\n" + "address: RAW/IP 192.0.2.17 80\n" + "address: STREAM/TCP 2001:db8::1 80\n" + "address: DGRAM/UDP 2001:db8::1 80\n" + "address: RAW/IP 2001:db8::1 80\n"); + check_ai ("alias.example", "80", AF_UNSPEC, + "address: STREAM/TCP 192.0.2.18 80\n" + "address: DGRAM/UDP 192.0.2.18 80\n" + "address: RAW/IP 192.0.2.18 80\n" + "address: STREAM/TCP 2001:db8::2 80\n" + "address: DGRAM/UDP 2001:db8::2 80\n" + "address: RAW/IP 2001:db8::2 80\n"); + check_ai ("www.example", "80", AF_INET, + "address: STREAM/TCP 192.0.2.17 80\n" + "address: DGRAM/UDP 192.0.2.17 80\n" + "address: RAW/IP 192.0.2.17 80\n"); + check_ai ("alias.example", "80", AF_INET, + "address: STREAM/TCP 192.0.2.18 80\n" + "address: DGRAM/UDP 192.0.2.18 80\n" + "address: RAW/IP 192.0.2.18 80\n"); + check_ai ("www.example", "80", AF_INET6, + "address: STREAM/TCP 2001:db8::1 80\n" + "address: DGRAM/UDP 2001:db8::1 80\n" + "address: RAW/IP 2001:db8::1 80\n"); + check_ai ("alias.example", "80", AF_INET6, + "address: STREAM/TCP 2001:db8::2 80\n" + "address: DGRAM/UDP 2001:db8::2 80\n" + "address: RAW/IP 2001:db8::2 80\n"); + + check_h ("t.www.example", AF_INET, + "name: t.www.example\n" + "address: 192.0.2.19\n"); + check_h ("t.alias.example", AF_INET, + "name: www.example\n" + "alias: t.alias.example\n" + "address: 192.0.2.20\n"); + check_h ("t.www.example", AF_INET6, + "name: t.www.example\n" + "address: 2001:db8::3\n"); + check_h ("t.alias.example", AF_INET6, + "name: www.example\n" + "alias: t.alias.example\n" + "address: 2001:db8::4\n"); + check_ai ("t.www.example", "80", AF_UNSPEC, + "address: STREAM/TCP 192.0.2.19 80\n" + "address: DGRAM/UDP 192.0.2.19 80\n" + "address: RAW/IP 192.0.2.19 80\n" + "address: STREAM/TCP 2001:db8::3 80\n" + "address: DGRAM/UDP 2001:db8::3 80\n" + "address: RAW/IP 2001:db8::3 80\n"); + check_ai ("t.alias.example", "80", AF_UNSPEC, + "address: STREAM/TCP 192.0.2.20 80\n" + "address: DGRAM/UDP 192.0.2.20 80\n" + "address: RAW/IP 192.0.2.20 80\n" + "address: STREAM/TCP 2001:db8::4 80\n" + "address: DGRAM/UDP 2001:db8::4 80\n" + "address: RAW/IP 2001:db8::4 80\n"); + check_ai ("t.www.example", "80", AF_INET, + "address: STREAM/TCP 192.0.2.19 80\n" + "address: DGRAM/UDP 192.0.2.19 80\n" + "address: RAW/IP 192.0.2.19 80\n"); + check_ai ("t.alias.example", "80", AF_INET, + "address: STREAM/TCP 192.0.2.20 80\n" + "address: DGRAM/UDP 192.0.2.20 80\n" + "address: RAW/IP 192.0.2.20 80\n"); + check_ai ("t.www.example", "80", AF_INET6, + "address: STREAM/TCP 2001:db8::3 80\n" + "address: DGRAM/UDP 2001:db8::3 80\n" + "address: RAW/IP 2001:db8::3 80\n"); + check_ai ("t.alias.example", "80", AF_INET6, + "address: STREAM/TCP 2001:db8::4 80\n" + "address: DGRAM/UDP 2001:db8::4 80\n" + "address: RAW/IP 2001:db8::4 80\n"); + + check_h ("nxdomain.example", AF_INET, + "error: HOST_NOT_FOUND\n"); + check_h ("nxdomain.example", AF_INET6, + "error: HOST_NOT_FOUND\n"); + check_ai ("nxdomain.example", "80", AF_UNSPEC, + "error: Name or service not known\n"); + check_ai ("nxdomain.example", "80", AF_INET, + "error: Name or service not known\n"); + check_ai ("nxdomain.example", "80", AF_INET6, + "error: Name or service not known\n"); + + check_h ("t.nxdomain.example", AF_INET, + "error: HOST_NOT_FOUND\n"); + check_h ("t.nxdomain.example", AF_INET6, + "error: HOST_NOT_FOUND\n"); + check_ai ("t.nxdomain.example", "80", AF_UNSPEC, + "error: Name or service not known\n"); + check_ai ("t.nxdomain.example", "80", AF_INET, + "error: Name or service not known\n"); + check_ai ("t.nxdomain.example", "80", AF_INET6, + "error: Name or service not known\n"); + + resolv_test_end (aux); + + return 0; +} + +#include <support/test-driver.c> diff --git a/resolv/tst-resolv-network.c b/resolv/tst-resolv-network.c new file mode 100644 index 0000000000..f2485277be --- /dev/null +++ b/resolv/tst-resolv-network.c @@ -0,0 +1,299 @@ +/* Test getnetbyname and getnetbyaddr. + Copyright (C) 2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <netdb.h> +#include <stdlib.h> +#include <string.h> +#include <support/check.h> +#include <support/check_nss.h> +#include <support/resolv_test.h> +#include <support/support.h> +#include <support/xmemstream.h> + +static void +send_ptr (struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype, + const char *alias) +{ + resolv_response_init (b, (struct resolv_response_flags) {}); + resolv_response_add_question (b, qname, qclass, qtype); + resolv_response_section (b, ns_s_an); + resolv_response_open_record (b, qname, qclass, T_PTR, 0); + resolv_response_add_name (b, alias); + resolv_response_close_record (b); +} + +static void +handle_code (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype, + int code) +{ + switch (code) + { + case 1: + send_ptr (b, qname, qclass, qtype, "1.in-addr.arpa"); + break; + case 2: + send_ptr (b, qname, qclass, qtype, "2.1.in-addr.arpa"); + break; + case 3: + send_ptr (b, qname, qclass, qtype, "3.2.1.in-addr.arpa"); + break; + case 4: + send_ptr (b, qname, qclass, qtype, "4.3.2.1.in-addr.arpa"); + break; + case 5: + /* Test multiple PTR records. */ + resolv_response_init (b, (struct resolv_response_flags) {}); + resolv_response_add_question (b, qname, qclass, qtype); + resolv_response_section (b, ns_s_an); + resolv_response_open_record (b, qname, qclass, T_PTR, 0); + resolv_response_add_name (b, "127.in-addr.arpa"); + resolv_response_close_record (b); + resolv_response_open_record (b, qname, qclass, T_PTR, 0); + resolv_response_add_name (b, "0.in-addr.arpa"); + resolv_response_close_record (b); + break; + case 6: + /* Test skipping of RRSIG record. */ + resolv_response_init (b, (struct resolv_response_flags) { }); + resolv_response_add_question (b, qname, qclass, qtype); + resolv_response_section (b, ns_s_an); + + resolv_response_open_record (b, qname, qclass, T_PTR, 0); + resolv_response_add_name (b, "127.in-addr.arpa"); + resolv_response_close_record (b); + + resolv_response_open_record (b, qname, qclass, 46 /* RRSIG */, 0); + { + char buf[500]; + memset (buf, 0x3f, sizeof (buf)); + resolv_response_add_data (b, buf, sizeof (buf)); + } + resolv_response_close_record (b); + + resolv_response_open_record (b, qname, qclass, T_PTR, 0); + resolv_response_add_name (b, "0.in-addr.arpa"); + resolv_response_close_record (b); + break; + case 7: + /* Test CNAME handling. */ + resolv_response_init (b, (struct resolv_response_flags) { }); + resolv_response_add_question (b, qname, qclass, qtype); + resolv_response_section (b, ns_s_an); + resolv_response_open_record (b, qname, qclass, T_CNAME, 0); + resolv_response_add_name (b, "cname.example"); + resolv_response_close_record (b); + resolv_response_open_record (b, "cname.example", qclass, T_PTR, 0); + resolv_response_add_name (b, "4.3.2.1.in-addr.arpa"); + resolv_response_close_record (b); + break; + + case 100: + resolv_response_init (b, (struct resolv_response_flags) { .rcode = 0, }); + resolv_response_add_question (b, qname, qclass, qtype); + break; + case 101: + resolv_response_init (b, (struct resolv_response_flags) + { .rcode = NXDOMAIN, }); + resolv_response_add_question (b, qname, qclass, qtype); + break; + case 102: + resolv_response_init (b, (struct resolv_response_flags) {.rcode = SERVFAIL}); + resolv_response_add_question (b, qname, qclass, qtype); + break; + case 103: + /* Check response length matching. */ + if (!ctx->tcp) + { + resolv_response_init (b, (struct resolv_response_flags) {.tc = true}); + resolv_response_add_question (b, qname, qclass, qtype); + } + else + { + resolv_response_init (b, (struct resolv_response_flags) {.ancount = 1}); + resolv_response_add_question (b, qname, qclass, qtype); + resolv_response_section (b, ns_s_an); + resolv_response_open_record (b, qname, qclass, T_PTR, 0); + resolv_response_add_name (b, "127.in-addr.arpa"); + resolv_response_close_record (b); + resolv_response_open_record (b, qname, qclass, T_PTR, 0); + resolv_response_add_name (b, "example"); + resolv_response_close_record (b); + + resolv_response_open_record (b, qname, qclass, T_PTR, 0); + size_t to_fill = 65535 - resolv_response_length (b) + - 2 /* length, "n" */ - 2 /* compression reference */ + - 2 /* RR type */; + for (size_t i = 0; i < to_fill; ++i) + resolv_response_add_data (b, "", 1); + resolv_response_close_record (b); + resolv_response_add_name (b, "n.example"); + uint16_t rrtype = htons (T_PTR); + resolv_response_add_data (b, &rrtype, sizeof (rrtype)); + } + break; + default: + FAIL_EXIT1 ("invalid QNAME: %s (code %d)", qname, code); + } +} + +static void +response (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype) +{ + int code; + if (strstr (qname, "in-addr.arpa") == NULL) + { + char *tail; + if (sscanf (qname, "code%d.%ms", &code, &tail) != 2 + || strcmp (tail, "example") != 0) + FAIL_EXIT1 ("invalid QNAME: %s", qname); + free (tail); + handle_code (ctx, b, qname, qclass, qtype, code); + } + else + { + /* Reverse lookup. */ + int components[4]; + char *tail; + if (sscanf (qname, "%d.%d.%d.%d.%ms", + components, components + 1, components + 2, components + 3, + &tail) != 5 + || strcmp (tail, "in-addr.arpa") != 0) + FAIL_EXIT1 ("invalid QNAME: %s", qname); + free (tail); + handle_code (ctx, b, qname, qclass, qtype, components[3]); + } +} + +static void +check_reverse (int code, const char *expected) +{ + char *query = xasprintf ("code=%d", code); + check_netent (query, getnetbyaddr (code, AF_INET), expected); + free (query); +} + +/* Test for CVE-2016-3075. */ +static void +check_long_name (void) +{ + struct xmemstream mem; + xopen_memstream (&mem); + + char label[65]; + memset (label, 'x', 63); + label[63] = '.'; + label[64] = '\0'; + for (unsigned i = 0; i < 64 * 1024 * 1024 / strlen (label); ++i) + fprintf (mem.out, "%s", label); + + xfclose_memstream (&mem); + + check_netent ("long name", getnetbyname (mem.buffer), + "error: NO_RECOVERY\n"); + + free (mem.buffer); +} + +int +main (void) +{ + struct resolv_test *obj = resolv_test_start + ((struct resolv_redirect_config) + { + .response_callback = response + }); + + /* Lookup by name, success cases. */ + check_netent ("code1.example", getnetbyname ("code1.example"), + "alias: 1.in-addr.arpa\n" + "net: 0x00000001\n"); + check_netent ("code2.example", getnetbyname ("code2.example"), + "alias: 2.1.in-addr.arpa\n" + "net: 0x00000102\n"); + check_netent ("code3.example", getnetbyname ("code3.example"), + "alias: 3.2.1.in-addr.arpa\n" + "net: 0x00010203\n"); + check_netent ("code4.example", getnetbyname ("code4.example"), + "alias: 4.3.2.1.in-addr.arpa\n" + "net: 0x01020304\n"); + check_netent ("code5.example", getnetbyname ("code5.example"), + "alias: 127.in-addr.arpa\n" + "alias: 0.in-addr.arpa\n" + "net: 0x0000007f\n"); + check_netent ("code6.example", getnetbyname ("code6.example"), + "alias: 127.in-addr.arpa\n" + "alias: 0.in-addr.arpa\n" + "net: 0x0000007f\n"); + check_netent ("code7.example", getnetbyname ("code7.example"), + "alias: 4.3.2.1.in-addr.arpa\n" + "net: 0x01020304\n"); + + /* Lookup by name, failure cases. */ + check_netent ("code100.example", getnetbyname ("code100.example"), + "error: NO_ADDRESS\n"); + check_netent ("code101.example", getnetbyname ("code101.example"), + "error: HOST_NOT_FOUND\n"); + check_netent ("code102.example", getnetbyname ("code102.example"), + "error: TRY_AGAIN\n"); + check_netent ("code103.example", getnetbyname ("code103.example"), + "error: NO_RECOVERY\n"); + + /* Lookup by address, success cases. */ + check_reverse (1, + "name: 1.in-addr.arpa\n" + "net: 0x00000001\n"); + check_reverse (2, + "name: 2.1.in-addr.arpa\n" + "net: 0x00000002\n"); + check_reverse (3, + "name: 3.2.1.in-addr.arpa\n" + "net: 0x00000003\n"); + check_reverse (4, + "name: 4.3.2.1.in-addr.arpa\n" + "net: 0x00000004\n"); + check_reverse (5, + "name: 127.in-addr.arpa\n" + "alias: 0.in-addr.arpa\n" + "net: 0x00000005\n"); + check_reverse (6, + "name: 127.in-addr.arpa\n" + "alias: 0.in-addr.arpa\n" + "net: 0x00000006\n"); + check_reverse (7, + "name: 4.3.2.1.in-addr.arpa\n" + "net: 0x00000007\n"); + + /* Lookup by address, failure cases. */ + check_reverse (100, + "error: NO_ADDRESS\n"); + check_reverse (101, + "error: HOST_NOT_FOUND\n"); + check_reverse (102, + "error: TRY_AGAIN\n"); + check_reverse (103, + "error: NO_RECOVERY\n"); + + check_long_name (); + + resolv_test_end (obj); +} diff --git a/resolv/tst-resolv-search.c b/resolv/tst-resolv-search.c new file mode 100644 index 0000000000..d95f9d9601 --- /dev/null +++ b/resolv/tst-resolv-search.c @@ -0,0 +1,343 @@ +/* Test search/default domain name behavior. + Copyright (C) 2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <resolv.h> +#include <string.h> +#include <support/check.h> +#include <support/check_nss.h> +#include <support/resolv_test.h> +#include <support/support.h> +#include <support/xmemstream.h> + +struct item +{ + const char *name; + int response; +}; + +const struct item items[] = + { + {"hostname.usersys.example.com", 1}, + {"hostname.corp.example.com", 1}, + {"hostname.example.com", 1}, + + {"mail.corp.example.com", 1}, + {"mail.example.com", 1}, + + {"file.corp.example.com", 2}, + {"file.corp", 1}, + {"file.example.com", 1}, + {"servfail-usersys.usersys.example.com", -ns_r_servfail}, + {"servfail-usersys.corp.example.com", 1}, + {"servfail-usersys.example.com", 1}, + {"servfail-corp.usersys.example.com", 1}, + {"servfail-corp.corp.example.com", -ns_r_servfail}, + {"servfail-corp.example.com", 1}, + {"www.example.com", 1}, + {"large.example.com", 200}, + + /* Test query amplification with a SERVFAIL response combined with + a large RRset. */ + {"large-servfail.usersys.example.com", -ns_r_servfail}, + {"large-servfail.example.com", 2000}, + {} + }; + +enum + { + name_not_found = -1, + name_no_data = -2 + }; + +static int +find_name (const char *name) +{ + for (int i = 0; items[i].name != NULL; ++i) + { + if (strcmp (name, items[i].name) == 0) + return i; + } + if (strcmp (name, "example.com") == 0 + || strcmp (name, "usersys.example.com") == 0 + || strcmp (name, "corp.example.com") == 0) + return name_no_data; + return name_not_found; +} + +static int rcode_override_server_index = -1; +static int rcode_override; + +static void +response (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype) +{ + if (ctx->server_index == rcode_override_server_index) + { + struct resolv_response_flags flags = {.rcode = rcode_override}; + resolv_response_init (b, flags); + resolv_response_add_question (b, qname, qclass, qtype); + return; + } + + int index = find_name (qname); + struct resolv_response_flags flags = {}; + if (index == name_not_found) + flags.rcode = ns_r_nxdomain; + else if (index >= 0 && items[index].response < 0) + flags.rcode = -items[index].response; + else if (index >= 0 && items[index].response > 5 && !ctx->tcp) + /* Force TCP if more than 5 addresses where requested. */ + flags.tc = true; + resolv_response_init (b, flags); + resolv_response_add_question (b, qname, qclass, qtype); + + if (flags.tc || index < 0 || items[index].response < 0) + return; + + resolv_response_section (b, ns_s_an); + + for (int i = 0; i < items[index].response; ++i) + { + resolv_response_open_record (b, qname, qclass, qtype, 0); + + switch (qtype) + { + case T_A: + { + char addr[4] = {10, index, i >> 8, i}; + resolv_response_add_data (b, addr, sizeof (addr)); + } + break; + case T_AAAA: + { + char addr[16] + = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, + 0, index + 1, (i + 1) >> 8, i + 1}; + resolv_response_add_data (b, addr, sizeof (addr)); + } + break; + default: + support_record_failure (); + printf ("error: unexpected QTYPE: %s/%u/%u\n", + qname, qclass, qtype); + } + resolv_response_close_record (b); + } +} + +enum output_format + { + format_get, format_gai + }; + +static void +format_expected_1 (FILE *out, int family, enum output_format format, int index) +{ + for (int i = 0; i < items[index].response; ++i) + { + char address[200]; + switch (family) + { + case AF_INET: + snprintf (address, sizeof (address), "10.%d.%d.%d", + index, (i >> 8) & 0xff, i & 0xff); + break; + case AF_INET6: + snprintf (address, sizeof (address), "2001:db8::%x:%x", + index + 1, i + 1); + break; + default: + FAIL_EXIT1 ("unreachable"); + } + + switch (format) + { + case format_get: + fprintf (out, "address: %s\n", address); + break; + case format_gai: + fprintf (out, "address: STREAM/TCP %s 80\n", address); + } + } +} + +static char * +format_expected (const char *fqdn, int family, enum output_format format) +{ + int index = find_name (fqdn); + TEST_VERIFY_EXIT (index >= 0); + struct xmemstream stream; + xopen_memstream (&stream); + + TEST_VERIFY_EXIT (items[index].response >= 0); + if (format == format_get) + fprintf (stream.out, "name: %s\n", items[index].name); + if (family == AF_INET || family == AF_UNSPEC) + format_expected_1 (stream.out, AF_INET, format, index); + if (family == AF_INET6 || family == AF_UNSPEC) + format_expected_1 (stream.out, AF_INET6, format, index); + + xfclose_memstream (&stream); + return stream.buffer; +} + +static void +do_get (const char *name, const char *fqdn, int family) +{ + char *expected = format_expected (fqdn, family, format_get); + if (family == AF_INET) + { + char *query = xasprintf ("gethostbyname (\"%s\")", name); + check_hostent (query, gethostbyname (name), expected); + free (query); + } + char *query = xasprintf ("gethostbyname2 (\"%s\", %d)", name, family); + check_hostent (query, gethostbyname2 (name, family), expected); + + /* Test res_search. */ + int qtype; + switch (family) + { + case AF_INET: + qtype = T_A; + break; + case AF_INET6: + qtype = T_AAAA; + break; + default: + qtype = -1; + } + if (qtype >= 0) + { + int sz = 512; + unsigned char *response = xmalloc (sz); + int ret = res_search (name, C_IN, qtype, response, sz); + TEST_VERIFY_EXIT (ret >= 0); + if (ret > sz) + { + /* Truncation. Retry with a larger buffer. */ + sz = 65535; + unsigned char *newptr = xrealloc (response, sz); + response = newptr; + + ret = res_search (name, C_IN, qtype, response, sz); + TEST_VERIFY_EXIT (ret >= 0); + TEST_VERIFY_EXIT (ret < sz); + } + check_dns_packet (query, response, ret, expected); + free (response); + } + + free (query); + free (expected); +} + +static void +do_gai (const char *name, const char *fqdn, int family) +{ + struct addrinfo hints = + { + .ai_family = family, + .ai_protocol = IPPROTO_TCP, + .ai_socktype = SOCK_STREAM + }; + struct addrinfo *ai; + char *query = xasprintf ("%s:80 [%d]", name, family); + int ret = getaddrinfo (name, "80", &hints, &ai); + char *expected = format_expected (fqdn, family, format_gai); + check_addrinfo (query, ai, ret, expected); + if (ret == 0) + freeaddrinfo (ai); + free (expected); + free (query); +} + +static void +do_both (const char *name, const char *fqdn) +{ + do_get (name, fqdn, AF_INET); + do_get (name, fqdn, AF_INET6); + do_gai (name, fqdn, AF_INET); + do_gai (name, fqdn, AF_INET6); + do_gai (name, fqdn, AF_UNSPEC); +} + +static void +do_test_all (bool unconnectable_server) +{ + struct resolv_redirect_config config = + { + .response_callback = response, + .search = {"usersys.example.com", "corp.example.com", "example.com"}, + }; + struct resolv_test *obj = resolv_test_start (config); + + if (unconnectable_server) + { + /* 255.255.255.255 results in an immediate connect failure. The + next server will supply the answer instead. This is a + triggering condition for bug 19791. */ + _res.nsaddr_list[0].sin_addr.s_addr = -1; + _res.nsaddr_list[0].sin_port = htons (53); + } + + do_both ("file", "file.corp.example.com"); + do_both ("www", "www.example.com"); + do_both ("servfail-usersys", "servfail-usersys.corp.example.com"); + do_both ("servfail-corp", "servfail-corp.usersys.example.com"); + do_both ("large", "large.example.com"); + do_both ("large-servfail", "large-servfail.example.com"); + do_both ("file.corp", "file.corp"); + + /* Check that SERVFAIL and REFUSED responses do not alter the search + path resolution. */ + rcode_override_server_index = 0; + rcode_override = ns_r_servfail; + do_both ("hostname", "hostname.usersys.example.com"); + do_both ("large", "large.example.com"); + do_both ("large-servfail", "large-servfail.example.com"); + rcode_override = ns_r_refused; + do_both ("hostname", "hostname.usersys.example.com"); + do_both ("large", "large.example.com"); + do_both ("large-servfail", "large-servfail.example.com"); + /* Likewise, but with an NXDOMAIN for the first search path + entry. */ + rcode_override = ns_r_servfail; + do_both ("mail", "mail.corp.example.com"); + rcode_override = ns_r_refused; + do_both ("mail", "mail.corp.example.com"); + /* Likewise, but with ndots handling. */ + rcode_override = ns_r_servfail; + do_both ("file.corp", "file.corp"); + rcode_override = ns_r_refused; + do_both ("file.corp", "file.corp"); + + resolv_test_end (obj); +} + +static int +do_test (void) +{ + for (int unconnectable_server = 0; unconnectable_server < 2; + ++unconnectable_server) + do_test_all (unconnectable_server); + return 0; +} + +#include <support/test-driver.c> |