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/>. */
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. */
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 addres.
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
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. */
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
),
74 response_data_array
[index
] = data
;
75 xpthread_mutex_unlock (&mutex
);
79 /* Verify the index into the response_data array and return the data
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
);
91 /* Deallocate all response data. */
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
];
105 response_data_array
= NULL
;
106 response_data_count
= 0;
107 xpthread_mutex_unlock (&mutex
);
110 #define EDNS_PROBE_EXAMPLE "edns-probe.example"
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
124 if (strncmp ("formerr.", qname
, strlen ("formerr.")) == 0)
127 qname_compare
= qname
+ strlen ("formerr.");
131 send_formerr
= false;
132 qname_compare
= qname
;
135 /* The "tcp." prefix can be used to request TCP fallback. */
137 if (strncmp ("tcp.", qname_compare
, strlen ("tcp.")) == 0)
140 qname_compare
+= strlen ("tcp.");
145 enum {edns_probe
} requested_qname
;
146 if (strcmp (qname_compare
, EDNS_PROBE_EXAMPLE
) == 0)
147 requested_qname
= edns_probe
;
150 support_record_failure ();
151 printf ("error: unexpected QNAME: %s (reduced: %s)\n",
152 qname
, qname_compare
);
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
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)
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
)
178 unsigned int index
= put_response (ctx
, qname
, qtype
);
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
);
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
);
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
)
212 TEST_VERIFY (memcmp (address
, "\x0a", 1) == 0);
215 TEST_VERIFY (memcmp (address
, "\x20\x01\x0d\xb8", 4) == 0);
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
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
;
242 if (ai
->ai_family
== family
)
244 struct response_data
*new_data
;
249 struct sockaddr_in
*pin
= (struct sockaddr_in
*) ai
->ai_addr
;
250 new_data
= decode_address (&pin
->sin_addr
.s_addr
, 4);
255 struct sockaddr_in6
*pin
= (struct sockaddr_in6
*) ai
->ai_addr
;
256 new_data
= decode_address (&pin
->sin6_addr
.s6_addr
, 16);
260 FAIL_EXIT1 ("invalid address family %d", ai
->ai_family
);
265 /* Check pointer equality because this should be the same
266 response (same index). */
267 TEST_VERIFY (data
== new_data
);
271 TEST_VERIFY_EXIT (data
!= NULL
);
275 /* Updated by the main test loop in accordance with what is set in
277 static bool use_edns
;
278 static bool use_dnssec
;
280 /* Verify the decoded response data against the flags above. */
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
);
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;
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
309 verify_response_data (struct response_data
*data
)
311 verify_response_data_payload (data
, 1200);
315 check_hostent (struct hostent
*e
)
317 TEST_VERIFY_EXIT (e
!= NULL
);
318 verify_response_data (decode_hostent (e
));
324 struct addrinfo hints
= { .ai_family
= family
};
326 int ret
= getaddrinfo (EDNS_PROBE_EXAMPLE
, "80", &hints
, &ai
);
327 TEST_VERIFY_EXIT (ret
== 0);
332 verify_response_data (decode_addrinfo (ai
, family
));
335 verify_response_data (decode_addrinfo (ai
, AF_INET
));
336 verify_response_data (decode_addrinfo (ai
, AF_INET6
));
339 FAIL_EXIT1 ("invalid address family %d", family
);
353 res_op_last
= res_op_nquerydomain
,
357 res_op_string (enum res_op op
)
365 case res_op_querydomain
:
366 return "res_querydomain";
368 return "res_nsearch";
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. */
381 do_res_search (const char *probe_name
, enum res_op op
, size_t size
,
382 size_t expected_buffer_size
)
385 printf ("info: testing %s with buffer size %zu\n",
386 res_op_string (op
), size
);
387 unsigned char *buffer
= xmalloc (size
);
392 ret
= res_search (probe_name
, C_IN
, T_A
, buffer
, size
);
395 ret
= res_query (probe_name
, C_IN
, T_A
, buffer
, size
);
398 ret
= res_nsearch (&_res
, probe_name
, C_IN
, T_A
, buffer
, size
);
401 ret
= res_nquery (&_res
, probe_name
, C_IN
, T_A
, buffer
, size
);
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. */
412 if (op
== res_op_querydomain
)
413 ret
= res_querydomain
414 (example_stripped
, "example", C_IN
, T_A
, buffer
, size
);
416 ret
= res_nquerydomain
417 (&_res
, example_stripped
, "example", C_IN
, T_A
, buffer
, size
);
420 FAIL_EXIT1 ("invalid probe name: %s", probe_name
);
421 free (example_stripped
);
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);
437 TEST_VERIFY_EXIT (end
- p
>= 4);
439 /* Skip over the RNAME and the RR header, but stop at the RDATA
441 ret
= dn_skipname (p
, end
);
442 TEST_VERIFY_EXIT (ret
> 0);
444 TEST_VERIFY_EXIT (end
- p
>= 2 + 2 + 4 + 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. */
451 struct response_data
*data
= decode_address (p
, 4);
453 verify_response_data_payload (data
, expected_buffer_size
);
459 run_test (const char *probe_name
)
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
));
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);
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
,
501 _res
.options
|= RES_USE_EDNS0
;
502 use_dnssec
= do_dnssec
;
504 _res
.options
|= RES_USE_DNSSEC
;
506 char *probe_name
= xstrdup (EDNS_PROBE_EXAMPLE
);
509 char *n
= xasprintf ("tcp.%s", probe_name
);
515 /* Send a garbage query in an attempt to trigger EDNS
517 char *n
= xasprintf ("formerr.%s", probe_name
);
522 run_test (probe_name
);
525 resolv_test_end (aux
);
528 free_response_data ();
532 #include <support/test-driver.c>