1 /* Test the RES_NOAAAA resolver option.
2 Copyright (C) 2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
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.
9
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.
14
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/>. */
18
19 #include <errno.h>
20 #include <netdb.h>
21 #include <resolv.h>
22 #include <stdlib.h>
23 #include <support/check.h>
24 #include <support/check_nss.h>
25 #include <support/resolv_test.h>
26 #include <support/support.h>
27
28 /* Used to keep track of the number of queries. */
29 static volatile unsigned int queries;
30
31 static void
response(const struct resolv_response_context * ctx,struct resolv_response_builder * b,const char * qname,uint16_t qclass,uint16_t qtype)32 response (const struct resolv_response_context *ctx,
33 struct resolv_response_builder *b,
34 const char *qname, uint16_t qclass, uint16_t qtype)
35 {
36 /* Each test should only send one query. */
37 ++queries;
38 TEST_COMPARE (queries, 1);
39
40 /* AAAA queries are supposed to be disabled. */
41 TEST_VERIFY (qtype != T_AAAA);
42 TEST_COMPARE (qclass, C_IN);
43
44 /* The only other query type besides A is PTR. */
45 if (qtype != T_A)
46 TEST_COMPARE (qtype, T_PTR);
47
48 int an, ns, ar;
49 char *tail;
50 if (sscanf (qname, "an%d.ns%d.ar%d.%ms", &an, &ns, &ar, &tail) != 4)
51 FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
52 TEST_COMPARE_STRING (tail, "example");
53 free (tail);
54
55 if (an < 0 || ns < 0 || ar < 0)
56 {
57 struct resolv_response_flags flags = { .rcode = NXDOMAIN, };
58 resolv_response_init (b, flags);
59 resolv_response_add_question (b, qname, qclass, qtype);
60 return;
61 }
62
63 struct resolv_response_flags flags = {};
64 resolv_response_init (b, flags);
65 resolv_response_add_question (b, qname, qclass, qtype);
66
67 resolv_response_section (b, ns_s_an);
68 for (int i = 0; i < an; ++i)
69 {
70 resolv_response_open_record (b, qname, qclass, qtype, 60);
71 switch (qtype)
72 {
73 case T_A:
74 {
75 char ipv4[4] = {192, 0, 2, i + 1};
76 resolv_response_add_data (b, &ipv4, sizeof (ipv4));
77 }
78 break;
79
80 case T_PTR:
81 {
82 char *name = xasprintf ("ptr-%d", i);
83 resolv_response_add_name (b, name);
84 free (name);
85 }
86 break;
87 }
88 resolv_response_close_record (b);
89 }
90
91 resolv_response_section (b, ns_s_ns);
92 for (int i = 0; i < ns; ++i)
93 {
94 resolv_response_open_record (b, qname, qclass, T_NS, 60);
95 char *name = xasprintf ("ns%d.example.net", i);
96 resolv_response_add_name (b, name);
97 free (name);
98 resolv_response_close_record (b);
99 }
100
101 resolv_response_section (b, ns_s_ar);
102 int addr = 1;
103 for (int i = 0; i < ns; ++i)
104 {
105 char *name = xasprintf ("ns%d.example.net", i);
106 for (int j = 0; j < ar; ++j)
107 {
108 resolv_response_open_record (b, name, qclass, T_A, 60);
109 char ipv4[4] = {192, 0, 2, addr};
110 resolv_response_add_data (b, &ipv4, sizeof (ipv4));
111 resolv_response_close_record (b);
112
113 resolv_response_open_record (b, name, qclass, T_AAAA, 60);
114 char ipv6[16]
115 = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, addr};
116 resolv_response_add_data (b, &ipv6, sizeof (ipv6));
117 resolv_response_close_record (b);
118
119 ++addr;
120 }
121 free (name);
122 }
123 }
124
125 /* Number of modes. Lowest bit encodes *n* function vs implicit _res
126 argument. The mode numbers themselves are arbitrary. */
127 enum { mode_count = 8 };
128
129 /* res_send-like modes do not perform error translation. */
130 enum { first_send_mode = 6 };
131
132 static int
libresolv_query(unsigned int mode,const char * qname,uint16_t qtype,unsigned char * buf,size_t buflen)133 libresolv_query (unsigned int mode, const char *qname, uint16_t qtype,
134 unsigned char *buf, size_t buflen)
135 {
136 int saved_errno = errno;
137
138 TEST_VERIFY_EXIT (mode < mode_count);
139
140 switch (mode)
141 {
142 case 0:
143 return res_query (qname, C_IN, qtype, buf, buflen);
144 case 1:
145 return res_nquery (&_res, qname, C_IN, qtype, buf, buflen);
146 case 2:
147 return res_search (qname, C_IN, qtype, buf, buflen);
148 case 3:
149 return res_nsearch (&_res, qname, C_IN, qtype, buf, buflen);
150 case 4:
151 return res_querydomain (qname, "", C_IN, qtype, buf, buflen);
152 case 5:
153 return res_nquerydomain (&_res, qname, "", C_IN, qtype, buf, buflen);
154 case 6:
155 {
156 unsigned char querybuf[512];
157 int ret = res_mkquery (QUERY, qname, C_IN, qtype,
158 NULL, 0, NULL, querybuf, sizeof (querybuf));
159 TEST_VERIFY_EXIT (ret > 0);
160 errno = saved_errno;
161 return res_send (querybuf, ret, buf, buflen);
162 }
163 case 7:
164 {
165 unsigned char querybuf[512];
166 int ret = res_nmkquery (&_res, QUERY, qname, C_IN, qtype,
167 NULL, 0, NULL, querybuf, sizeof (querybuf));
168 TEST_VERIFY_EXIT (ret > 0);
169 errno = saved_errno;
170 return res_nsend (&_res, querybuf, ret, buf, buflen);
171 }
172 }
173 __builtin_unreachable ();
174 }
175
176 static int
do_test(void)177 do_test (void)
178 {
179 struct resolv_test *obj = resolv_test_start
180 ((struct resolv_redirect_config)
181 {
182 .response_callback = response
183 });
184
185 _res.options |= RES_NOAAAA;
186
187 check_hostent ("an1.ns2.ar1.example",
188 gethostbyname ("an1.ns2.ar1.example"),
189 "name: an1.ns2.ar1.example\n"
190 "address: 192.0.2.1\n");
191 queries = 0;
192 check_hostent ("an0.ns2.ar1.example",
193 gethostbyname ("an0.ns2.ar1.example"),
194 "error: NO_ADDRESS\n");
195 queries = 0;
196 check_hostent ("an-1.ns2.ar1.example",
197 gethostbyname ("an-1.ns2.ar1.example"),
198 "error: HOST_NOT_FOUND\n");
199 queries = 0;
200
201 check_hostent ("an1.ns2.ar1.example AF_INET",
202 gethostbyname2 ("an1.ns2.ar1.example", AF_INET),
203 "name: an1.ns2.ar1.example\n"
204 "address: 192.0.2.1\n");
205 queries = 0;
206 check_hostent ("an0.ns2.ar1.example AF_INET",
207 gethostbyname2 ("an0.ns2.ar1.example", AF_INET),
208 "error: NO_ADDRESS\n");
209 queries = 0;
210 check_hostent ("an-1.ns2.ar1.example AF_INET",
211 gethostbyname2 ("an-1.ns2.ar1.example", AF_INET),
212 "error: HOST_NOT_FOUND\n");
213 queries = 0;
214
215 check_hostent ("an1.ns2.ar1.example AF_INET6",
216 gethostbyname2 ("an1.ns2.ar1.example", AF_INET6),
217 "error: NO_ADDRESS\n");
218 queries = 0;
219 check_hostent ("an0.ns2.ar1.example AF_INET6",
220 gethostbyname2 ("an0.ns2.ar1.example", AF_INET6),
221 "error: NO_ADDRESS\n");
222 queries = 0;
223 check_hostent ("an-1.ns2.ar1.example AF_INET6",
224 gethostbyname2 ("an-1.ns2.ar1.example", AF_INET6),
225 "error: HOST_NOT_FOUND\n");
226 queries = 0;
227
228 /* Multiple addresses. */
229 check_hostent ("an2.ns0.ar0.example",
230 gethostbyname ("an2.ns0.ar0.example"),
231 "name: an2.ns0.ar0.example\n"
232 "address: 192.0.2.1\n"
233 "address: 192.0.2.2\n");
234 queries = 0;
235 check_hostent ("an2.ns0.ar0.example AF_INET6",
236 gethostbyname2 ("an2.ns0.ar0.example", AF_INET6),
237 "error: NO_ADDRESS\n");
238 queries = 0;
239
240 /* getaddrinfo checks with one address. */
241 struct addrinfo *ai;
242 int ret;
243 ret = getaddrinfo ("an1.ns2.ar1.example", "80",
244 &(struct addrinfo)
245 {
246 .ai_family = AF_INET,
247 .ai_socktype = SOCK_STREAM,
248 }, &ai);
249 check_addrinfo ("an1.ns2.ar1.example (AF_INET)", ai, ret,
250 "address: STREAM/TCP 192.0.2.1 80\n");
251 freeaddrinfo (ai);
252 queries = 0;
253 ret = getaddrinfo ("an1.ns2.ar1.example", "80",
254 &(struct addrinfo)
255 {
256 .ai_family = AF_INET6,
257 .ai_socktype = SOCK_STREAM,
258 }, &ai);
259 check_addrinfo ("an1.ns2.ar1.example (AF_INET6)", ai, ret,
260 "error: No address associated with hostname\n");
261 queries = 0;
262 ret = getaddrinfo ("an1.ns2.ar1.example", "80",
263 &(struct addrinfo)
264 {
265 .ai_family = AF_UNSPEC,
266 .ai_socktype = SOCK_STREAM,
267 }, &ai);
268 check_addrinfo ("an1.ns2.ar1.example (AF_UNSPEC)", ai, ret,
269 "address: STREAM/TCP 192.0.2.1 80\n");
270 freeaddrinfo (ai);
271 queries = 0;
272
273 /* getaddrinfo checks with three addresses. */
274 ret = getaddrinfo ("an3.ns2.ar1.example", "80",
275 &(struct addrinfo)
276 {
277 .ai_family = AF_INET,
278 .ai_socktype = SOCK_STREAM,
279 }, &ai);
280 check_addrinfo ("an3.ns2.ar1.example (AF_INET)", ai, ret,
281 "address: STREAM/TCP 192.0.2.1 80\n"
282 "address: STREAM/TCP 192.0.2.2 80\n"
283 "address: STREAM/TCP 192.0.2.3 80\n");
284 freeaddrinfo (ai);
285 queries = 0;
286 ret = getaddrinfo ("an3.ns2.ar1.example", "80",
287 &(struct addrinfo)
288 {
289 .ai_family = AF_INET6,
290 .ai_socktype = SOCK_STREAM,
291 }, &ai);
292 check_addrinfo ("an3.ns2.ar1.example (AF_INET6)", ai, ret,
293 "error: No address associated with hostname\n");
294 queries = 0;
295 ret = getaddrinfo ("an3.ns2.ar1.example", "80",
296 &(struct addrinfo)
297 {
298 .ai_family = AF_UNSPEC,
299 .ai_socktype = SOCK_STREAM,
300 }, &ai);
301 check_addrinfo ("an3.ns2.ar1.example (AF_UNSPEC)", ai, ret,
302 "address: STREAM/TCP 192.0.2.1 80\n"
303 "address: STREAM/TCP 192.0.2.2 80\n"
304 "address: STREAM/TCP 192.0.2.3 80\n");
305 freeaddrinfo (ai);
306 queries = 0;
307
308 /* getaddrinfo checks with no address. */
309 ret = getaddrinfo ("an0.ns2.ar1.example", "80",
310 &(struct addrinfo)
311 {
312 .ai_family = AF_INET,
313 .ai_socktype = SOCK_STREAM,
314 }, &ai);
315 check_addrinfo ("an0.ns2.ar1.example (AF_INET)", ai, ret,
316 "error: No address associated with hostname\n");
317 queries = 0;
318 ret = getaddrinfo ("an0.ns2.ar1.example", "80",
319 &(struct addrinfo)
320 {
321 .ai_family = AF_INET6,
322 .ai_socktype = SOCK_STREAM,
323 }, &ai);
324 check_addrinfo ("an0.ns2.ar1.example (AF_INET6)", ai, ret,
325 "error: No address associated with hostname\n");
326 queries = 0;
327 ret = getaddrinfo ("an0.ns2.ar1.example", "80",
328 &(struct addrinfo)
329 {
330 .ai_family = AF_UNSPEC,
331 .ai_socktype = SOCK_STREAM,
332 }, &ai);
333 check_addrinfo ("an-1.ns2.ar1.example (AF_UNSPEC)", ai, ret,
334 "error: No address associated with hostname\n");
335 queries = 0;
336
337 /* getaddrinfo checks with NXDOMAIN. */
338 ret = getaddrinfo ("an-1.ns2.ar1.example", "80",
339 &(struct addrinfo)
340 {
341 .ai_family = AF_INET,
342 .ai_socktype = SOCK_STREAM,
343 }, &ai);
344 check_addrinfo ("an-1.ns2.ar1.example (AF_INET)", ai, ret,
345 "error: Name or service not known\n");
346 queries = 0;
347 ret = getaddrinfo ("an-1.ns2.ar1.example", "80",
348 &(struct addrinfo)
349 {
350 .ai_family = AF_INET6,
351 .ai_socktype = SOCK_STREAM,
352 }, &ai);
353 check_addrinfo ("an-1.ns2.ar1.example (AF_INET6)", ai, ret,
354 "error: Name or service not known\n");
355 queries = 0;
356 ret = getaddrinfo ("an-1.ns2.ar1.example", "80",
357 &(struct addrinfo)
358 {
359 .ai_family = AF_UNSPEC,
360 .ai_socktype = SOCK_STREAM,
361 }, &ai);
362 check_addrinfo ("an-1.ns2.ar1.example (AF_UNSPEC)", ai, ret,
363 "error: Name or service not known\n");
364 queries = 0;
365
366 for (unsigned int mode = 0; mode < mode_count; ++mode)
367 {
368 unsigned char *buf;
369 int ret;
370
371 /* Response for A. */
372 buf = malloc (512);
373 ret = libresolv_query (mode, "an1.ns2.ar1.example", T_A, buf, 512);
374 TEST_VERIFY_EXIT (ret > 0);
375 check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
376 "name: an1.ns2.ar1.example\n"
377 "address: 192.0.2.1\n");
378 free (buf);
379 queries = 0;
380
381 /* NODATA response for A. */
382 buf = malloc (512);
383 errno = 0;
384 ret = libresolv_query (mode, "an0.ns2.ar1.example", T_A, buf, 512);
385 if (mode < first_send_mode)
386 {
387 TEST_COMPARE (ret, -1);
388 TEST_COMPARE (errno, 0);
389 TEST_COMPARE (h_errno, NO_ADDRESS);
390 }
391 else
392 {
393 TEST_VERIFY_EXIT (ret > 0);
394 TEST_COMPARE (((HEADER *)buf)->rcode, 0);
395 check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
396 "name: an0.ns2.ar1.example\n");
397 }
398 free (buf);
399 queries = 0;
400
401 /* NXDOMAIN response for A. */
402 buf = malloc (512);
403 errno = 0;
404 ret = libresolv_query (mode, "an-1.ns2.ar1.example", T_A, buf, 512);
405 if (mode < first_send_mode)
406 {
407 TEST_COMPARE (ret, -1);
408 TEST_COMPARE (errno, 0);
409 TEST_COMPARE (h_errno, HOST_NOT_FOUND);
410 }
411 else
412 {
413 TEST_VERIFY_EXIT (ret > 0);
414 TEST_COMPARE (((HEADER *)buf)->rcode, NXDOMAIN);
415 check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
416 "name: an-1.ns2.ar1.example\n");
417 }
418 free (buf);
419 queries = 0;
420
421 /* Response for PTR. */
422 buf = malloc (512);
423 ret = libresolv_query (mode, "an1.ns2.ar1.example", T_PTR, buf, 512);
424 TEST_VERIFY_EXIT (ret > 0);
425 check_dns_packet ("an1.ns2.ar1.example PTR", buf, ret,
426 "name: an1.ns2.ar1.example\n"
427 "data: an1.ns2.ar1.example PTR ptr-0\n");
428 free (buf);
429 queries = 0;
430
431 /* NODATA response for PTR. */
432 buf = malloc (512);
433 errno = 0;
434 ret = libresolv_query (mode, "an0.ns2.ar1.example", T_PTR, buf, 512);
435 if (mode < first_send_mode)
436 {
437 TEST_COMPARE (ret, -1);
438 TEST_COMPARE (errno, 0);
439 TEST_COMPARE (h_errno, NO_ADDRESS);
440 }
441 else
442 {
443 TEST_VERIFY_EXIT (ret > 0);
444 TEST_COMPARE (((HEADER *)buf)->rcode, 0);
445 check_dns_packet ("an1.ns2.ar1.example PTR", buf, ret,
446 "name: an0.ns2.ar1.example\n");
447 }
448 free (buf);
449 queries = 0;
450
451 /* NXDOMAIN response for PTR. */
452 buf = malloc (512);
453 errno = 0;
454 ret = libresolv_query (mode, "an-1.ns2.ar1.example", T_PTR, buf, 512);
455 if (mode < first_send_mode)
456 {
457 TEST_COMPARE (ret, -1);
458 TEST_COMPARE (errno, 0);
459 TEST_COMPARE (h_errno, HOST_NOT_FOUND);
460 }
461 else
462 {
463 TEST_VERIFY_EXIT (ret > 0);
464 TEST_COMPARE (((HEADER *)buf)->rcode, NXDOMAIN);
465 check_dns_packet ("an1.ns2.ar1.example PTR", buf, ret,
466 "name: an-1.ns2.ar1.example\n");
467 }
468 free (buf);
469 queries = 0;
470
471 /* NODATA response for AAAA. */
472 buf = malloc (512);
473 errno = 0;
474 ret = libresolv_query (mode, "an1.ns2.ar1.example", T_AAAA, buf, 512);
475 if (mode < first_send_mode)
476 {
477 TEST_COMPARE (ret, -1);
478 TEST_COMPARE (errno, 0);
479 TEST_COMPARE (h_errno, NO_ADDRESS);
480 }
481 else
482 {
483 TEST_VERIFY_EXIT (ret > 0);
484 TEST_COMPARE (((HEADER *)buf)->rcode, 0);
485 check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
486 "name: an1.ns2.ar1.example\n");
487 }
488 free (buf);
489 queries = 0;
490
491 /* NODATA response for AAAA (original is already NODATA). */
492 buf = malloc (512);
493 errno = 0;
494 ret = libresolv_query (mode, "an0.ns2.ar1.example", T_AAAA, buf, 512);
495 if (mode < first_send_mode)
496 {
497 TEST_COMPARE (ret, -1);
498 TEST_COMPARE (errno, 0);
499 TEST_COMPARE (h_errno, NO_ADDRESS);
500 }
501 else
502 {
503 TEST_VERIFY_EXIT (ret > 0);
504 TEST_COMPARE (((HEADER *)buf)->rcode, 0);
505 check_dns_packet ("an0.ns2.ar1.example A", buf, ret,
506 "name: an0.ns2.ar1.example\n");
507 }
508 free (buf);
509 queries = 0;
510
511 /* NXDOMAIN response. */
512 buf = malloc (512);
513 errno = 0;
514 ret = libresolv_query (mode, "an-1.ns2.ar1.example", T_AAAA, buf, 512);
515 if (mode < first_send_mode)
516 {
517 TEST_COMPARE (ret, -1);
518 TEST_COMPARE (errno, 0);
519 TEST_COMPARE (h_errno, HOST_NOT_FOUND);
520 }
521 else
522 {
523 TEST_VERIFY_EXIT (ret > 0);
524 TEST_COMPARE (((HEADER *)buf)->rcode, NXDOMAIN);
525 check_dns_packet ("an-1.ns2.ar1.example A", buf, ret,
526 "name: an-1.ns2.ar1.example\n");
527 }
528 free (buf);
529 queries = 0;
530 }
531
532 resolv_test_end (obj);
533
534 return 0;
535 }
536
537 #include <support/test-driver.c>
538