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