stdlib: Remove use of mergesort on qsort (BZ 21719)
[glibc.git] / resolv / tst-resolv-edns.c
blob6e0072b5412ef76a8341c413ab1dbe8fe82c32ef
1 /* Test EDNS handling in the stub resolver.
2 Copyright (C) 2016-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 #include <errno.h>
20 #include <netdb.h>
21 #include <resolv.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <support/check.h>
26 #include <support/resolv_test.h>
27 #include <support/support.h>
28 #include <support/test-driver.h>
29 #include <support/xthread.h>
31 /* Data produced by a test query. */
32 struct response_data
34 char *qname;
35 uint16_t qtype;
36 struct resolv_edns_info edns;
39 /* Global array used by put_response and get_response to record
40 response data. The test DNS server returns the index of the array
41 element which contains the actual response data. This enables the
42 test case to return arbitrary amounts of data with the limited
43 number of bits which fit into an IP address.
45 The volatile specifier is needed because the test case accesses
46 these variables from a callback function called from a function
47 which is marked as __THROW (i.e., a leaf function which actually is
48 not). */
49 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
50 static struct response_data ** volatile response_data_array;
51 volatile static size_t response_data_count;
53 /* Extract information from the query, store it in a struct
54 response_data object, and return its index in the
55 response_data_array. */
56 static unsigned int
57 put_response (const struct resolv_response_context *ctx,
58 const char *qname, uint16_t qtype)
60 xpthread_mutex_lock (&mutex);
61 ++response_data_count;
62 /* We only can represent 2**24 indexes in 10.0.0.0/8. */
63 TEST_VERIFY (response_data_count < (1 << 24));
64 response_data_array = xrealloc
65 (response_data_array, sizeof (*response_data_array) * response_data_count);
66 unsigned int index = response_data_count - 1;
67 struct response_data *data = xmalloc (sizeof (*data));
68 *data = (struct response_data)
70 .qname = xstrdup (qname),
71 .qtype = qtype,
72 .edns = ctx->edns,
74 response_data_array[index] = data;
75 xpthread_mutex_unlock (&mutex);
76 return index;
79 /* Verify the index into the response_data array and return the data
80 at it. */
81 static struct response_data *
82 get_response (unsigned int index)
84 xpthread_mutex_lock (&mutex);
85 TEST_VERIFY_EXIT (index < response_data_count);
86 struct response_data *result = response_data_array[index];
87 xpthread_mutex_unlock (&mutex);
88 return result;
91 /* Deallocate all response data. */
92 static void
93 free_response_data (void)
95 xpthread_mutex_lock (&mutex);
96 size_t count = response_data_count;
97 struct response_data **array = response_data_array;
98 for (unsigned int i = 0; i < count; ++i)
100 struct response_data *data = array[i];
101 free (data->qname);
102 free (data);
104 free (array);
105 response_data_array = NULL;
106 response_data_count = 0;
107 xpthread_mutex_unlock (&mutex);
110 #define EDNS_PROBE_EXAMPLE "edns-probe.example"
112 static void
113 response (const struct resolv_response_context *ctx,
114 struct resolv_response_builder *b,
115 const char *qname, uint16_t qclass, uint16_t qtype)
117 TEST_VERIFY_EXIT (qname != NULL);
119 const char *qname_compare = qname;
121 /* The "formerr." prefix can be used to request a FORMERR response on the
122 first server. */
123 bool send_formerr;
124 if (strncmp ("formerr.", qname, strlen ("formerr.")) == 0)
126 send_formerr = true;
127 qname_compare = qname + strlen ("formerr.");
129 else
131 send_formerr = false;
132 qname_compare = qname;
135 /* The "tcp." prefix can be used to request TCP fallback. */
136 bool force_tcp;
137 if (strncmp ("tcp.", qname_compare, strlen ("tcp.")) == 0)
139 force_tcp = true;
140 qname_compare += strlen ("tcp.");
142 else
143 force_tcp = false;
145 enum {edns_probe} requested_qname;
146 if (strcmp (qname_compare, EDNS_PROBE_EXAMPLE) == 0)
147 requested_qname = edns_probe;
148 else
150 support_record_failure ();
151 printf ("error: unexpected QNAME: %s (reduced: %s)\n",
152 qname, qname_compare);
153 return;
155 TEST_VERIFY_EXIT (qclass == C_IN);
156 struct resolv_response_flags flags = { };
157 flags.tc = force_tcp && !ctx->tcp;
158 if (!flags.tc && send_formerr && ctx->server_index == 0)
159 /* Send a FORMERR for the first full response from the first
160 server. */
161 flags.rcode = 1; /* FORMERR */
162 resolv_response_init (b, flags);
163 resolv_response_add_question (b, qname, qclass, qtype);
164 if (flags.tc || flags.rcode != 0)
165 return;
167 if (test_verbose)
168 printf ("info: edns=%d payload_size=%d\n",
169 ctx->edns.active, ctx->edns.payload_size);
171 /* Encode the response_data object in multiple address records.
172 Each record carries two bytes of payload data, and an index. */
173 resolv_response_section (b, ns_s_an);
174 switch (requested_qname)
176 case edns_probe:
178 unsigned int index = put_response (ctx, qname, qtype);
179 switch (qtype)
181 case T_A:
183 uint32_t addr = htonl (0x0a000000 | index);
184 resolv_response_open_record (b, qname, qclass, qtype, 0);
185 resolv_response_add_data (b, &addr, sizeof (addr));
186 resolv_response_close_record (b);
188 break;
189 case T_AAAA:
191 char addr[16]
192 = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0,
193 index >> 16, index >> 8, index};
194 resolv_response_open_record (b, qname, qclass, qtype, 0);
195 resolv_response_add_data (b, &addr, sizeof (addr));
196 resolv_response_close_record (b);
200 break;
204 /* Update *DATA with data from ADDRESS of SIZE. Set the corresponding
205 flag in SHADOW for each byte written. */
206 static struct response_data *
207 decode_address (const void *address, size_t size)
209 switch (size)
211 case 4:
212 TEST_VERIFY (memcmp (address, "\x0a", 1) == 0);
213 break;
214 case 16:
215 TEST_VERIFY (memcmp (address, "\x20\x01\x0d\xb8", 4) == 0);
216 break;
217 default:
218 FAIL_EXIT1 ("unexpected address size %zu", size);
220 const unsigned char *addr = address;
221 unsigned int index = addr[size - 3] * 256 * 256
222 + addr[size - 2] * 256
223 + addr[size - 1];
224 return get_response (index);
227 static struct response_data *
228 decode_hostent (struct hostent *e)
230 TEST_VERIFY_EXIT (e != NULL);
231 TEST_VERIFY_EXIT (e->h_addr_list[0] != NULL);
232 TEST_VERIFY (e->h_addr_list[1] == NULL);
233 return decode_address (e->h_addr_list[0], e->h_length);
236 static struct response_data *
237 decode_addrinfo (struct addrinfo *ai, int family)
239 struct response_data *data = NULL;
240 while (ai != NULL)
242 if (ai->ai_family == family)
244 struct response_data *new_data;
245 switch (family)
247 case AF_INET:
249 struct sockaddr_in *pin = (struct sockaddr_in *) ai->ai_addr;
250 new_data = decode_address (&pin->sin_addr.s_addr, 4);
252 break;
253 case AF_INET6:
255 struct sockaddr_in6 *pin = (struct sockaddr_in6 *) ai->ai_addr;
256 new_data = decode_address (&pin->sin6_addr.s6_addr, 16);
258 break;
259 default:
260 FAIL_EXIT1 ("invalid address family %d", ai->ai_family);
262 if (data == NULL)
263 data = new_data;
264 else
265 /* Check pointer equality because this should be the same
266 response (same index). */
267 TEST_VERIFY (data == new_data);
269 ai = ai->ai_next;
271 TEST_VERIFY_EXIT (data != NULL);
272 return data;
275 /* Updated by the main test loop in accordance with what is set in
276 _res.options. */
277 static bool use_edns;
278 static bool use_dnssec;
280 /* Verify the decoded response data against the flags above. */
281 static void
282 verify_response_data_payload (struct response_data *data,
283 size_t expected_payload)
285 bool edns = use_edns || use_dnssec;
286 TEST_VERIFY (data->edns.active == edns);
287 if (!edns)
288 expected_payload = 0;
289 if (data->edns.payload_size != expected_payload)
291 support_record_failure ();
292 printf ("error: unexpected payload size %d (edns=%d)\n",
293 (int) data->edns.payload_size, edns);
295 uint16_t expected_flags = 0;
296 if (use_dnssec)
297 expected_flags |= 0x8000; /* DO flag. */
298 if (data->edns.flags != expected_flags)
300 support_record_failure ();
301 printf ("error: unexpected EDNS flags 0x%04x (edns=%d)\n",
302 (int) data->edns.flags, edns);
306 /* Same as verify_response_data_payload, but use the default
307 payload. */
308 static void
309 verify_response_data (struct response_data *data)
311 verify_response_data_payload (data, 1200);
314 static void
315 check_hostent (struct hostent *e)
317 TEST_VERIFY_EXIT (e != NULL);
318 verify_response_data (decode_hostent (e));
321 static void
322 do_ai (int family)
324 struct addrinfo hints = { .ai_family = family };
325 struct addrinfo *ai;
326 int ret = getaddrinfo (EDNS_PROBE_EXAMPLE, "80", &hints, &ai);
327 TEST_VERIFY_EXIT (ret == 0);
328 switch (family)
330 case AF_INET:
331 case AF_INET6:
332 verify_response_data (decode_addrinfo (ai, family));
333 break;
334 case AF_UNSPEC:
335 verify_response_data (decode_addrinfo (ai, AF_INET));
336 verify_response_data (decode_addrinfo (ai, AF_INET6));
337 break;
338 default:
339 FAIL_EXIT1 ("invalid address family %d", family);
341 freeaddrinfo (ai);
344 enum res_op
346 res_op_search,
347 res_op_query,
348 res_op_querydomain,
349 res_op_nsearch,
350 res_op_nquery,
351 res_op_nquerydomain,
353 res_op_last = res_op_nquerydomain,
356 static const char *
357 res_op_string (enum res_op op)
359 switch (op)
361 case res_op_search:
362 return "res_search";
363 case res_op_query:
364 return "res_query";
365 case res_op_querydomain:
366 return "res_querydomain";
367 case res_op_nsearch:
368 return "res_nsearch";
369 case res_op_nquery:
370 return "res_nquery";
371 case res_op_nquerydomain:
372 return "res_nquerydomain";
374 FAIL_EXIT1 ("invalid res_op value %d", (int) op);
377 /* Call libresolv function OP to look up PROBE_NAME, with an answer
378 buffer of SIZE bytes. Check that the advertised UDP buffer size is
379 in fact EXPECTED_BUFFER_SIZE. */
380 static void
381 do_res_search (const char *probe_name, enum res_op op, size_t size,
382 size_t expected_buffer_size)
384 if (test_verbose)
385 printf ("info: testing %s with buffer size %zu\n",
386 res_op_string (op), size);
387 unsigned char *buffer = xmalloc (size);
388 int ret = -1;
389 switch (op)
391 case res_op_search:
392 ret = res_search (probe_name, C_IN, T_A, buffer, size);
393 break;
394 case res_op_query:
395 ret = res_query (probe_name, C_IN, T_A, buffer, size);
396 break;
397 case res_op_nsearch:
398 ret = res_nsearch (&_res, probe_name, C_IN, T_A, buffer, size);
399 break;
400 case res_op_nquery:
401 ret = res_nquery (&_res, probe_name, C_IN, T_A, buffer, size);
402 break;
403 case res_op_querydomain:
404 case res_op_nquerydomain:
406 char *example_stripped = xstrdup (probe_name);
407 char *dot_example = strstr (example_stripped, ".example");
408 if (dot_example != NULL && strcmp (dot_example, ".example") == 0)
410 /* Truncate the domain name. */
411 *dot_example = '\0';
412 if (op == res_op_querydomain)
413 ret = res_querydomain
414 (example_stripped, "example", C_IN, T_A, buffer, size);
415 else
416 ret = res_nquerydomain
417 (&_res, example_stripped, "example", C_IN, T_A, buffer, size);
419 else
420 FAIL_EXIT1 ("invalid probe name: %s", probe_name);
421 free (example_stripped);
423 break;
425 TEST_VERIFY_EXIT (ret > 12);
426 unsigned char *end = buffer + ret;
428 HEADER *hd = (HEADER *) buffer;
429 TEST_VERIFY (ntohs (hd->qdcount) == 1);
430 TEST_VERIFY (ntohs (hd->ancount) == 1);
431 /* Skip over the header. */
432 unsigned char *p = buffer + sizeof (*hd);
433 /* Skip over the question. */
434 ret = dn_skipname (p, end);
435 TEST_VERIFY_EXIT (ret > 0);
436 p += ret;
437 TEST_VERIFY_EXIT (end - p >= 4);
438 p += 4;
439 /* Skip over the RNAME and the RR header, but stop at the RDATA
440 length. */
441 ret = dn_skipname (p, end);
442 TEST_VERIFY_EXIT (ret > 0);
443 p += ret;
444 TEST_VERIFY_EXIT (end - p >= 2 + 2 + 4 + 2 + 4);
445 p += 2 + 2 + 4;
446 /* The IP address should be 4 bytes long. */
447 TEST_VERIFY_EXIT (p[0] == 0);
448 TEST_VERIFY_EXIT (p[1] == 4);
449 /* Extract the address information. */
450 p += 2;
451 struct response_data *data = decode_address (p, 4);
453 verify_response_data_payload (data, expected_buffer_size);
455 free (buffer);
458 static void
459 run_test (const char *probe_name)
461 if (test_verbose)
462 printf ("\ninfo: * use_edns=%d use_dnssec=%d\n",
463 use_edns, use_dnssec);
464 check_hostent (gethostbyname (probe_name));
465 check_hostent (gethostbyname2 (probe_name, AF_INET));
466 check_hostent (gethostbyname2 (probe_name, AF_INET6));
467 do_ai (AF_UNSPEC);
468 do_ai (AF_INET);
469 do_ai (AF_INET6);
471 for (int op = 0; op <= res_op_last; ++op)
473 do_res_search (probe_name, op, 301, 512);
474 do_res_search (probe_name, op, 511, 512);
475 do_res_search (probe_name, op, 512, 512);
476 do_res_search (probe_name, op, 513, 513);
477 do_res_search (probe_name, op, 657, 657);
478 do_res_search (probe_name, op, 1199, 1199);
479 do_res_search (probe_name, op, 1200, 1200);
480 do_res_search (probe_name, op, 1201, 1200);
481 do_res_search (probe_name, op, 65535, 1200);
485 static int
486 do_test (void)
488 for (int do_edns = 0; do_edns < 2; ++do_edns)
489 for (int do_dnssec = 0; do_dnssec < 2; ++do_dnssec)
490 for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
491 for (int do_formerr = 0; do_formerr < 2; ++do_formerr)
493 struct resolv_test *aux = resolv_test_start
494 ((struct resolv_redirect_config)
496 .response_callback = response,
499 use_edns = do_edns;
500 if (do_edns)
501 _res.options |= RES_USE_EDNS0;
502 use_dnssec = do_dnssec;
503 if (do_dnssec)
504 _res.options |= RES_USE_DNSSEC;
506 char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE);
507 if (do_tcp)
509 char *n = xasprintf ("tcp.%s", probe_name);
510 free (probe_name);
511 probe_name = n;
513 if (do_formerr)
515 /* Send a garbage query in an attempt to trigger EDNS
516 fallback. */
517 char *n = xasprintf ("formerr.%s", probe_name);
518 gethostbyname (n);
519 free (n);
522 run_test (probe_name);
524 free (probe_name);
525 resolv_test_end (aux);
528 free_response_data ();
529 return 0;
532 #include <support/test-driver.c>