aboutsummaryrefslogtreecommitdiff
path: root/resolv/tst-resolv-edns.c
diff options
context:
space:
mode:
Diffstat (limited to 'resolv/tst-resolv-edns.c')
-rw-r--r--resolv/tst-resolv-edns.c532
1 files changed, 0 insertions, 532 deletions
diff --git a/resolv/tst-resolv-edns.c b/resolv/tst-resolv-edns.c
deleted file mode 100644
index 8945d79d09..0000000000
--- a/resolv/tst-resolv-edns.c
+++ /dev/null
@@ -1,532 +0,0 @@
-/* Test EDNS handling in the stub resolver.
- Copyright (C) 2016-2017 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 <stdlib.h>
-#include <string.h>
-#include <support/check.h>
-#include <support/resolv_test.h>
-#include <support/support.h>
-#include <support/test-driver.h>
-#include <support/xthread.h>
-
-/* Data produced by a test query. */
-struct response_data
-{
- char *qname;
- uint16_t qtype;
- struct resolv_edns_info edns;
-};
-
-/* Global array used by put_response and get_response to record
- response data. The test DNS server returns the index of the array
- element which contains the actual response data. This enables the
- test case to return arbitrary amounts of data with the limited
- number of bits which fit into an IP addres.
-
- The volatile specifier is needed because the test case accesses
- these variables from a callback function called from a function
- which is marked as __THROW (i.e., a leaf function which actually is
- not). */
-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-static struct response_data ** volatile response_data_array;
-volatile static size_t response_data_count;
-
-/* Extract information from the query, store it in a struct
- response_data object, and return its index in the
- response_data_array. */
-static unsigned int
-put_response (const struct resolv_response_context *ctx,
- const char *qname, uint16_t qtype)
-{
- xpthread_mutex_lock (&mutex);
- ++response_data_count;
- /* We only can represent 2**24 indexes in 10.0.0.0/8. */
- TEST_VERIFY (response_data_count < (1 << 24));
- response_data_array = xrealloc
- (response_data_array, sizeof (*response_data_array) * response_data_count);
- unsigned int index = response_data_count - 1;
- struct response_data *data = xmalloc (sizeof (*data));
- *data = (struct response_data)
- {
- .qname = xstrdup (qname),
- .qtype = qtype,
- .edns = ctx->edns,
- };
- response_data_array[index] = data;
- xpthread_mutex_unlock (&mutex);
- return index;
-}
-
-/* Verify the index into the response_data array and return the data
- at it. */
-static struct response_data *
-get_response (unsigned int index)
-{
- xpthread_mutex_lock (&mutex);
- TEST_VERIFY_EXIT (index < response_data_count);
- struct response_data *result = response_data_array[index];
- xpthread_mutex_unlock (&mutex);
- return result;
-}
-
-/* Deallocate all response data. */
-static void
-free_response_data (void)
-{
- xpthread_mutex_lock (&mutex);
- size_t count = response_data_count;
- struct response_data **array = response_data_array;
- for (unsigned int i = 0; i < count; ++i)
- {
- struct response_data *data = array[i];
- free (data->qname);
- free (data);
- }
- free (array);
- response_data_array = NULL;
- response_data_count = 0;
- xpthread_mutex_unlock (&mutex);
-}
-
-#define EDNS_PROBE_EXAMPLE "edns-probe.example"
-
-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);
-
- const char *qname_compare = qname;
-
- /* The "formerr." prefix can be used to request a FORMERR response on the
- first server. */
- bool send_formerr;
- if (strncmp ("formerr.", qname, strlen ("formerr.")) == 0)
- {
- send_formerr = true;
- qname_compare = qname + strlen ("formerr.");
- }
- else
- {
- send_formerr = false;
- qname_compare = qname;
- }
-
- /* The "tcp." prefix can be used to request TCP fallback. */
- bool force_tcp;
- if (strncmp ("tcp.", qname_compare, strlen ("tcp.")) == 0)
- {
- force_tcp = true;
- qname_compare += strlen ("tcp.");
- }
- else
- force_tcp = false;
-
- enum {edns_probe} requested_qname;
- if (strcmp (qname_compare, EDNS_PROBE_EXAMPLE) == 0)
- requested_qname = edns_probe;
- else
- {
- support_record_failure ();
- printf ("error: unexpected QNAME: %s (reduced: %s)\n",
- qname, qname_compare);
- return;
- }
- TEST_VERIFY_EXIT (qclass == C_IN);
- struct resolv_response_flags flags = { };
- flags.tc = force_tcp && !ctx->tcp;
- if (!flags.tc && send_formerr && ctx->server_index == 0)
- /* Send a FORMERR for the first full response from the first
- server. */
- flags.rcode = 1; /* FORMERR */
- resolv_response_init (b, flags);
- resolv_response_add_question (b, qname, qclass, qtype);
- if (flags.tc || flags.rcode != 0)
- return;
-
- if (test_verbose)
- printf ("info: edns=%d payload_size=%d\n",
- ctx->edns.active, ctx->edns.payload_size);
-
- /* Encode the response_data object in multiple address records.
- Each record carries two bytes of payload data, and an index. */
- resolv_response_section (b, ns_s_an);
- switch (requested_qname)
- {
- case edns_probe:
- {
- unsigned int index = put_response (ctx, qname, qtype);
- switch (qtype)
- {
- case T_A:
- {
- uint32_t addr = htonl (0x0a000000 | index);
- resolv_response_open_record (b, qname, qclass, qtype, 0);
- resolv_response_add_data (b, &addr, sizeof (addr));
- resolv_response_close_record (b);
- }
- break;
- case T_AAAA:
- {
- char addr[16]
- = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- index >> 16, index >> 8, index};
- resolv_response_open_record (b, qname, qclass, qtype, 0);
- resolv_response_add_data (b, &addr, sizeof (addr));
- resolv_response_close_record (b);
- }
- }
- }
- break;
- }
-}
-
-/* Update *DATA with data from ADDRESS of SIZE. Set the corresponding
- flag in SHADOW for each byte written. */
-static struct response_data *
-decode_address (const void *address, size_t size)
-{
- switch (size)
- {
- case 4:
- TEST_VERIFY (memcmp (address, "\x0a", 1) == 0);
- break;
- case 16:
- TEST_VERIFY (memcmp (address, "\x20\x01\x0d\xb8", 4) == 0);
- break;
- default:
- FAIL_EXIT1 ("unexpected address size %zu", size);
- }
- const unsigned char *addr = address;
- unsigned int index = addr[size - 3] * 256 * 256
- + addr[size - 2] * 256
- + addr[size - 1];
- return get_response (index);
-}
-
-static struct response_data *
-decode_hostent (struct hostent *e)
-{
- TEST_VERIFY_EXIT (e != NULL);
- TEST_VERIFY_EXIT (e->h_addr_list[0] != NULL);
- TEST_VERIFY (e->h_addr_list[1] == NULL);
- return decode_address (e->h_addr_list[0], e->h_length);
-}
-
-static struct response_data *
-decode_addrinfo (struct addrinfo *ai, int family)
-{
- struct response_data *data = NULL;
- while (ai != NULL)
- {
- if (ai->ai_family == family)
- {
- struct response_data *new_data;
- switch (family)
- {
- case AF_INET:
- {
- struct sockaddr_in *pin = (struct sockaddr_in *) ai->ai_addr;
- new_data = decode_address (&pin->sin_addr.s_addr, 4);
- }
- break;
- case AF_INET6:
- {
- struct sockaddr_in6 *pin = (struct sockaddr_in6 *) ai->ai_addr;
- new_data = decode_address (&pin->sin6_addr.s6_addr, 16);
- }
- break;
- default:
- FAIL_EXIT1 ("invalid address family %d", ai->ai_family);
- }
- if (data == NULL)
- data = new_data;
- else
- /* Check pointer equality because this should be the same
- response (same index). */
- TEST_VERIFY (data == new_data);
- }
- ai = ai->ai_next;
- }
- TEST_VERIFY_EXIT (data != NULL);
- return data;
-}
-
-/* Updated by the main test loop in accordance with what is set in
- _res.options. */
-static bool use_edns;
-static bool use_dnssec;
-
-/* Verify the decoded response data against the flags above. */
-static void
-verify_response_data_payload (struct response_data *data,
- size_t expected_payload)
-{
- bool edns = use_edns || use_dnssec;
- TEST_VERIFY (data->edns.active == edns);
- if (!edns)
- expected_payload = 0;
- if (data->edns.payload_size != expected_payload)
- {
- support_record_failure ();
- printf ("error: unexpected payload size %d (edns=%d)\n",
- (int) data->edns.payload_size, edns);
- }
- uint16_t expected_flags = 0;
- if (use_dnssec)
- expected_flags |= 0x8000; /* DO flag. */
- if (data->edns.flags != expected_flags)
- {
- support_record_failure ();
- printf ("error: unexpected EDNS flags 0x%04x (edns=%d)\n",
- (int) data->edns.flags, edns);
- }
-}
-
-/* Same as verify_response_data_payload, but use the default
- payload. */
-static void
-verify_response_data (struct response_data *data)
-{
- verify_response_data_payload (data, 1200);
-}
-
-static void
-check_hostent (struct hostent *e)
-{
- TEST_VERIFY_EXIT (e != NULL);
- verify_response_data (decode_hostent (e));
-}
-
-static void
-do_ai (int family)
-{
- struct addrinfo hints = { .ai_family = family };
- struct addrinfo *ai;
- int ret = getaddrinfo (EDNS_PROBE_EXAMPLE, "80", &hints, &ai);
- TEST_VERIFY_EXIT (ret == 0);
- switch (family)
- {
- case AF_INET:
- case AF_INET6:
- verify_response_data (decode_addrinfo (ai, family));
- break;
- case AF_UNSPEC:
- verify_response_data (decode_addrinfo (ai, AF_INET));
- verify_response_data (decode_addrinfo (ai, AF_INET6));
- break;
- default:
- FAIL_EXIT1 ("invalid address family %d", family);
- }
- freeaddrinfo (ai);
-}
-
-enum res_op
-{
- res_op_search,
- res_op_query,
- res_op_querydomain,
- res_op_nsearch,
- res_op_nquery,
- res_op_nquerydomain,
-
- res_op_last = res_op_nquerydomain,
-};
-
-static const char *
-res_op_string (enum res_op op)
-{
- switch (op)
- {
- case res_op_search:
- return "res_search";
- case res_op_query:
- return "res_query";
- case res_op_querydomain:
- return "res_querydomain";
- case res_op_nsearch:
- return "res_nsearch";
- case res_op_nquery:
- return "res_nquery";
- case res_op_nquerydomain:
- return "res_nquerydomain";
- }
- FAIL_EXIT1 ("invalid res_op value %d", (int) op);
-}
-
-/* Call libresolv function OP to look up PROBE_NAME, with an answer
- buffer of SIZE bytes. Check that the advertised UDP buffer size is
- in fact EXPECTED_BUFFER_SIZE. */
-static void
-do_res_search (const char *probe_name, enum res_op op, size_t size,
- size_t expected_buffer_size)
-{
- if (test_verbose)
- printf ("info: testing %s with buffer size %zu\n",
- res_op_string (op), size);
- unsigned char *buffer = xmalloc (size);
- int ret = -1;
- switch (op)
- {
- case res_op_search:
- ret = res_search (probe_name, C_IN, T_A, buffer, size);
- break;
- case res_op_query:
- ret = res_query (probe_name, C_IN, T_A, buffer, size);
- break;
- case res_op_nsearch:
- ret = res_nsearch (&_res, probe_name, C_IN, T_A, buffer, size);
- break;
- case res_op_nquery:
- ret = res_nquery (&_res, probe_name, C_IN, T_A, buffer, size);
- break;
- case res_op_querydomain:
- case res_op_nquerydomain:
- {
- char *example_stripped = xstrdup (probe_name);
- char *dot_example = strstr (example_stripped, ".example");
- if (dot_example != NULL && strcmp (dot_example, ".example") == 0)
- {
- /* Truncate the domain name. */
- *dot_example = '\0';
- if (op == res_op_querydomain)
- ret = res_querydomain
- (example_stripped, "example", C_IN, T_A, buffer, size);
- else
- ret = res_nquerydomain
- (&_res, example_stripped, "example", C_IN, T_A, buffer, size);
- }
- else
- FAIL_EXIT1 ("invalid probe name: %s", probe_name);
- free (example_stripped);
- }
- break;
- }
- TEST_VERIFY_EXIT (ret > 12);
- unsigned char *end = buffer + ret;
-
- HEADER *hd = (HEADER *) buffer;
- TEST_VERIFY (ntohs (hd->qdcount) == 1);
- TEST_VERIFY (ntohs (hd->ancount) == 1);
- /* Skip over the header. */
- unsigned char *p = buffer + sizeof (*hd);
- /* Skip over the question. */
- ret = dn_skipname (p, end);
- TEST_VERIFY_EXIT (ret > 0);
- p += ret;
- TEST_VERIFY_EXIT (end - p >= 4);
- p += 4;
- /* Skip over the RNAME and the RR header, but stop at the RDATA
- length. */
- ret = dn_skipname (p, end);
- TEST_VERIFY_EXIT (ret > 0);
- p += ret;
- TEST_VERIFY_EXIT (end - p >= 2 + 2 + 4 + 2 + 4);
- p += 2 + 2 + 4;
- /* The IP address should be 4 bytes long. */
- TEST_VERIFY_EXIT (p[0] == 0);
- TEST_VERIFY_EXIT (p[1] == 4);
- /* Extract the address information. */
- p += 2;
- struct response_data *data = decode_address (p, 4);
-
- verify_response_data_payload (data, expected_buffer_size);
-
- free (buffer);
-}
-
-static void
-run_test (const char *probe_name)
-{
- if (test_verbose)
- printf ("\ninfo: * use_edns=%d use_dnssec=%d\n",
- use_edns, use_dnssec);
- check_hostent (gethostbyname (probe_name));
- check_hostent (gethostbyname2 (probe_name, AF_INET));
- check_hostent (gethostbyname2 (probe_name, AF_INET6));
- do_ai (AF_UNSPEC);
- do_ai (AF_INET);
- do_ai (AF_INET6);
-
- for (int op = 0; op <= res_op_last; ++op)
- {
- do_res_search (probe_name, op, 301, 512);
- do_res_search (probe_name, op, 511, 512);
- do_res_search (probe_name, op, 512, 512);
- do_res_search (probe_name, op, 513, 513);
- do_res_search (probe_name, op, 657, 657);
- do_res_search (probe_name, op, 1199, 1199);
- do_res_search (probe_name, op, 1200, 1200);
- do_res_search (probe_name, op, 1201, 1200);
- do_res_search (probe_name, op, 65535, 1200);
- }
-}
-
-static int
-do_test (void)
-{
- for (int do_edns = 0; do_edns < 2; ++do_edns)
- for (int do_dnssec = 0; do_dnssec < 2; ++do_dnssec)
- for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
- for (int do_formerr = 0; do_formerr < 2; ++do_formerr)
- {
- struct resolv_test *aux = resolv_test_start
- ((struct resolv_redirect_config)
- {
- .response_callback = response,
- });
-
- use_edns = do_edns;
- if (do_edns)
- _res.options |= RES_USE_EDNS0;
- use_dnssec = do_dnssec;
- if (do_dnssec)
- _res.options |= RES_USE_DNSSEC;
-
- char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE);
- if (do_tcp)
- {
- char *n = xasprintf ("tcp.%s", probe_name);
- free (probe_name);
- probe_name = n;
- }
- if (do_formerr)
- {
- /* Send a garbage query in an attempt to trigger EDNS
- fallback. */
- char *n = xasprintf ("formerr.%s", probe_name);
- gethostbyname (n);
- free (n);
- }
-
- run_test (probe_name);
-
- free (probe_name);
- resolv_test_end (aux);
- }
-
- free_response_data ();
- return 0;
-}
-
-#include <support/test-driver.c>