1 /* Test basic nss_dns functionality and the resolver test harness itself.
2    Copyright (C) 2016-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 <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <support/check.h>
24 #include <support/check_nss.h>
25 #include <support/format_nss.h>
26 #include <support/resolv_test.h>
27 #include <support/support.h>
28 
29 #define LONG_NAME                                                       \
30   "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax."    \
31   "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay."    \
32   "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz."    \
33   "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat"
34 
35 static void
response(const struct resolv_response_context * ctx,struct resolv_response_builder * b,const char * qname,uint16_t qclass,uint16_t qtype)36 response (const struct resolv_response_context *ctx,
37           struct resolv_response_builder *b,
38           const char *qname, uint16_t qclass, uint16_t qtype)
39 {
40   TEST_VERIFY_EXIT (qname != NULL);
41 
42   /* The "t." prefix can be used to request TCP fallback.  */
43   bool force_tcp;
44   if (strncmp ("t.", qname, 2) == 0)
45     force_tcp = true;
46   else
47     force_tcp = false;
48   const char *qname_compare;
49   if (force_tcp)
50     qname_compare = qname + 2;
51   else
52     qname_compare = qname;
53   enum {www, alias, nxdomain, long_name, nodata} requested_qname;
54   if (strcmp (qname_compare, "www.example") == 0)
55     requested_qname = www;
56   else if (strcmp (qname_compare, "alias.example") == 0)
57     requested_qname = alias;
58   else if (strcmp (qname_compare, "nxdomain.example") == 0)
59     requested_qname = nxdomain;
60   else if (strcmp (qname_compare, LONG_NAME) == 0)
61     requested_qname = long_name;
62   else if (strcmp (qname_compare, "nodata.example") == 0)
63     requested_qname = nodata;
64   else
65     {
66       support_record_failure ();
67       printf ("error: unexpected QNAME: %s\n", qname);
68       return;
69     }
70   TEST_VERIFY_EXIT (qclass == C_IN);
71   struct resolv_response_flags flags = {.tc = force_tcp && !ctx->tcp};
72   if (requested_qname == nxdomain)
73     flags.rcode = 3;            /* NXDOMAIN */
74   resolv_response_init (b, flags);
75   resolv_response_add_question (b, qname, qclass, qtype);
76   if (requested_qname == nxdomain || flags.tc)
77     return;
78 
79   resolv_response_section (b, ns_s_an);
80   switch (requested_qname)
81     {
82     case www:
83     case long_name:
84       resolv_response_open_record (b, qname, qclass, qtype, 0);
85       break;
86     case alias:
87       resolv_response_open_record (b, qname, qclass, T_CNAME, 0);
88       resolv_response_add_name (b, "www.example");
89       resolv_response_close_record (b);
90       resolv_response_open_record (b, "www.example", qclass, qtype, 0);
91       break;
92     case nodata:
93       return;
94     case nxdomain:
95       FAIL_EXIT1 ("unreachable");
96     }
97   switch (qtype)
98     {
99     case T_A:
100       {
101         char ipv4[4] = {192, 0, 2, 17};
102         ipv4[3] += requested_qname + 2 * ctx->tcp + 4 * ctx->server_index;
103         resolv_response_add_data (b, &ipv4, sizeof (ipv4));
104       }
105       break;
106     case T_AAAA:
107       {
108         char ipv6[16]
109           = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
110         ipv6[15] += requested_qname + 2 * ctx->tcp + 4 * ctx->server_index;
111         resolv_response_add_data (b, &ipv6, sizeof (ipv6));
112       }
113       break;
114     default:
115       support_record_failure ();
116       printf ("error: unexpected QTYPE: %s/%u/%u\n",
117               qname, qclass, qtype);
118     }
119   resolv_response_close_record (b);
120 }
121 
122 static void
check_h(const char * name,int family,const char * expected)123 check_h (const char *name, int family, const char *expected)
124 {
125   if (family == AF_INET)
126     {
127       char *query = xasprintf ("gethostbyname (\"%s\")", name);
128       check_hostent (query, gethostbyname (name), expected);
129       free (query);
130     }
131   {
132     char *query = xasprintf ("gethostbyname2 (\"%s\", %d)", name, family);
133     check_hostent (query, gethostbyname2 (name, family), expected);
134     free (query);
135   }
136 
137   bool too_small = true;
138   for (unsigned int offset = 0; offset < 8; ++offset)
139     for (unsigned int size = 1; too_small; ++size)
140       {
141         char *buf = xmalloc (offset + size);
142         too_small = false;
143 
144         struct hostent hostbuf;
145         struct hostent *result;
146         int herror;
147         if (family == AF_INET)
148           {
149             char *query = xasprintf ("gethostbyname (\"%s\") %u/%u",
150                                      name, offset, size);
151             int ret = gethostbyname_r
152               (name, &hostbuf, buf + offset, size, &result, &herror);
153             if (ret == 0)
154               {
155                 h_errno = herror;
156                 check_hostent (query, result, expected);
157               }
158             else if (ret == ERANGE)
159               too_small = true;
160             else
161               {
162                 errno = ret;
163                 FAIL_EXIT1 ("gethostbyname_r: %m");
164               }
165             free (query);
166             memset (buf, 0, offset + size);
167           }
168         char *query = xasprintf ("gethostbyname2 (\"%s\", %d) %u/%u",
169                                  name, family, offset, size);
170         int ret = gethostbyname2_r
171           (name, family, &hostbuf, buf + offset, size, &result, &herror);
172         if (ret == 0)
173           {
174             h_errno = herror;
175             check_hostent (query, result, expected);
176           }
177         else if (ret == ERANGE)
178           too_small = true;
179         else
180           {
181             errno = ret;
182             FAIL_EXIT1 ("gethostbyname_r: %m");
183           }
184         free (buf);
185         free (query);
186       }
187 }
188 
189 static void
check_ai_hints(const char * name,const char * service,struct addrinfo hints,const char * expected)190 check_ai_hints (const char *name, const char *service,
191                 struct addrinfo hints, const char *expected)
192 {
193   struct addrinfo *ai;
194   char *query = xasprintf ("%s:%s [%d]/0x%x", name, service,
195                            hints.ai_family, hints.ai_flags);
196   int ret = getaddrinfo (name, service, &hints, &ai);
197   check_addrinfo (query, ai, ret, expected);
198   if (ret == 0)
199     freeaddrinfo (ai);
200   free (query);
201 }
202 
203 static void
check_ai(const char * name,const char * service,int family,const char * expected)204 check_ai (const char *name, const char *service,
205           int family, const char *expected)
206 {
207   return check_ai_hints (name, service,
208                          (struct addrinfo) { .ai_family = family, },
209                          expected);
210 }
211 
212 /* Test for bug 21295: getaddrinfo used to discard address information
213    instead of merging it.  */
214 static void
test_bug_21295(void)215 test_bug_21295 (void)
216 {
217   /* The address order is unpredictable.  There are two factors which
218      contribute to that: The stub resolver does not perform proper
219      response matching for A/AAAA queries (an A response could be
220      associated with an AAAA query and vice versa), and without
221      namespaces, system configuration could affect address
222      ordering.  */
223   for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
224     {
225       const struct addrinfo hints =
226         {
227           .ai_family = AF_INET6,
228           .ai_socktype = SOCK_STREAM,
229           .ai_flags = AI_V4MAPPED | AI_ALL,
230         };
231       const char *qname;
232       if (do_tcp)
233         qname = "t.www.example";
234       else
235         qname = "www.example";
236       struct addrinfo *ai = NULL;
237       int ret = getaddrinfo (qname, "80", &hints, &ai);
238       TEST_VERIFY_EXIT (ret == 0);
239 
240       const char *expected_a;
241       const char *expected_b;
242       if (do_tcp)
243         {
244           expected_a = "flags: AI_V4MAPPED AI_ALL\n"
245             "address: STREAM/TCP 2001:db8::3 80\n"
246             "address: STREAM/TCP ::ffff:192.0.2.19 80\n";
247           expected_b = "flags: AI_V4MAPPED AI_ALL\n"
248             "address: STREAM/TCP ::ffff:192.0.2.19 80\n"
249             "address: STREAM/TCP 2001:db8::3 80\n";
250         }
251       else
252         {
253           expected_a = "flags: AI_V4MAPPED AI_ALL\n"
254             "address: STREAM/TCP 2001:db8::1 80\n"
255             "address: STREAM/TCP ::ffff:192.0.2.17 80\n";
256           expected_b = "flags: AI_V4MAPPED AI_ALL\n"
257             "address: STREAM/TCP ::ffff:192.0.2.17 80\n"
258             "address: STREAM/TCP 2001:db8::1 80\n";
259         }
260 
261       char *actual = support_format_addrinfo (ai, ret);
262       if (!(strcmp (actual, expected_a) == 0
263             || strcmp (actual, expected_b) == 0))
264         {
265           support_record_failure ();
266           printf ("error: %s: unexpected response (TCP: %d):\n%s\n",
267                   __func__, do_tcp, actual);
268         }
269       free (actual);
270       freeaddrinfo (ai);
271     }
272 }
273 
274 /* Run tests which do not expect any data.  */
275 static void
test_nodata_nxdomain(void)276 test_nodata_nxdomain (void)
277 {
278   /* Iterate through different address families.  */
279   int families[] = { AF_UNSPEC, AF_INET, AF_INET6, -1 };
280   for (int i = 0; families[i] >= 0; ++i)
281     /* If do_tcp, prepend "t." to the name to trigger TCP
282        fallback.  */
283     for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
284       /* If do_nxdomain, trigger an NXDOMAIN error (DNS failure),
285          otherwise use a NODATA response (empty but successful
286          answer).  */
287       for (int do_nxdomain = 0; do_nxdomain < 2; ++do_nxdomain)
288         {
289           int family = families[i];
290           char *name = xasprintf ("%s%s.example",
291                                   do_tcp ? "t." : "",
292                                   do_nxdomain ? "nxdomain" : "nodata");
293 
294           if (family != AF_UNSPEC)
295             {
296               if (do_nxdomain)
297                 check_h (name, family, "error: HOST_NOT_FOUND\n");
298               else
299                 check_h (name, family, "error: NO_ADDRESS\n");
300             }
301 
302           const char *expected;
303           if (do_nxdomain)
304             expected = "error: Name or service not known\n";
305           else
306             expected = "error: No address associated with hostname\n";
307 
308           check_ai (name, "80", family, expected);
309 
310           struct addrinfo hints =
311             {
312               .ai_family = family,
313               .ai_flags = AI_V4MAPPED | AI_ALL,
314             };
315           check_ai_hints (name, "80", hints, expected);
316           hints.ai_flags |= AI_CANONNAME;
317           check_ai_hints (name, "80", hints, expected);
318 
319           free (name);
320         }
321 }
322 
323 static int
do_test(void)324 do_test (void)
325 {
326   struct resolv_test *aux = resolv_test_start
327     ((struct resolv_redirect_config)
328      {
329        .response_callback = response,
330      });
331 
332   check_h ("www.example", AF_INET,
333            "name: www.example\n"
334            "address: 192.0.2.17\n");
335   check_h ("alias.example", AF_INET,
336            "name: www.example\n"
337            "alias: alias.example\n"
338            "address: 192.0.2.18\n");
339   check_h ("www.example", AF_INET6,
340            "name: www.example\n"
341            "address: 2001:db8::1\n");
342   check_h ("alias.example", AF_INET6,
343            "name: www.example\n"
344            "alias: alias.example\n"
345            "address: 2001:db8::2\n");
346   check_h (LONG_NAME, AF_INET,
347            "name: " LONG_NAME "\n"
348            "address: 192.0.2.20\n");
349 
350   check_ai ("www.example", "80", AF_UNSPEC,
351             "address: STREAM/TCP 192.0.2.17 80\n"
352             "address: DGRAM/UDP 192.0.2.17 80\n"
353             "address: RAW/IP 192.0.2.17 80\n"
354             "address: STREAM/TCP 2001:db8::1 80\n"
355             "address: DGRAM/UDP 2001:db8::1 80\n"
356             "address: RAW/IP 2001:db8::1 80\n");
357   check_ai_hints ("www.example", "80",
358                   (struct addrinfo) { .ai_family = AF_UNSPEC,
359                       .ai_flags = AI_CANONNAME, },
360                   "flags: AI_CANONNAME\n"
361                   "canonname: www.example\n"
362                   "address: STREAM/TCP 192.0.2.17 80\n"
363                   "address: DGRAM/UDP 192.0.2.17 80\n"
364                   "address: RAW/IP 192.0.2.17 80\n"
365                   "address: STREAM/TCP 2001:db8::1 80\n"
366                   "address: DGRAM/UDP 2001:db8::1 80\n"
367                   "address: RAW/IP 2001:db8::1 80\n");
368   check_ai ("alias.example", "80", AF_UNSPEC,
369             "address: STREAM/TCP 192.0.2.18 80\n"
370             "address: DGRAM/UDP 192.0.2.18 80\n"
371             "address: RAW/IP 192.0.2.18 80\n"
372             "address: STREAM/TCP 2001:db8::2 80\n"
373             "address: DGRAM/UDP 2001:db8::2 80\n"
374             "address: RAW/IP 2001:db8::2 80\n");
375   check_ai_hints ("alias.example", "80",
376                   (struct addrinfo) { .ai_family = AF_UNSPEC,
377                       .ai_flags = AI_CANONNAME, },
378                   "flags: AI_CANONNAME\n"
379                   "canonname: www.example\n"
380                   "address: STREAM/TCP 192.0.2.18 80\n"
381                   "address: DGRAM/UDP 192.0.2.18 80\n"
382                   "address: RAW/IP 192.0.2.18 80\n"
383                   "address: STREAM/TCP 2001:db8::2 80\n"
384                   "address: DGRAM/UDP 2001:db8::2 80\n"
385                   "address: RAW/IP 2001:db8::2 80\n");
386   check_ai (LONG_NAME, "80", AF_UNSPEC,
387             "address: STREAM/TCP 192.0.2.20 80\n"
388             "address: DGRAM/UDP 192.0.2.20 80\n"
389             "address: RAW/IP 192.0.2.20 80\n"
390             "address: STREAM/TCP 2001:db8::4 80\n"
391             "address: DGRAM/UDP 2001:db8::4 80\n"
392             "address: RAW/IP 2001:db8::4 80\n");
393   check_ai ("www.example", "80", AF_INET,
394             "address: STREAM/TCP 192.0.2.17 80\n"
395             "address: DGRAM/UDP 192.0.2.17 80\n"
396             "address: RAW/IP 192.0.2.17 80\n");
397   check_ai_hints ("www.example", "80",
398                   (struct addrinfo) { .ai_family = AF_INET,
399                       .ai_flags = AI_CANONNAME, },
400                   "flags: AI_CANONNAME\n"
401                   "canonname: www.example\n"
402                   "address: STREAM/TCP 192.0.2.17 80\n"
403                   "address: DGRAM/UDP 192.0.2.17 80\n"
404                   "address: RAW/IP 192.0.2.17 80\n");
405   check_ai ("alias.example", "80", AF_INET,
406             "address: STREAM/TCP 192.0.2.18 80\n"
407             "address: DGRAM/UDP 192.0.2.18 80\n"
408             "address: RAW/IP 192.0.2.18 80\n");
409   check_ai_hints ("alias.example", "80",
410                   (struct addrinfo) { .ai_family = AF_INET,
411                       .ai_flags = AI_CANONNAME, },
412                   "flags: AI_CANONNAME\n"
413                   "canonname: www.example\n"
414                   "address: STREAM/TCP 192.0.2.18 80\n"
415                   "address: DGRAM/UDP 192.0.2.18 80\n"
416                   "address: RAW/IP 192.0.2.18 80\n");
417   check_ai (LONG_NAME, "80", AF_INET,
418             "address: STREAM/TCP 192.0.2.20 80\n"
419             "address: DGRAM/UDP 192.0.2.20 80\n"
420             "address: RAW/IP 192.0.2.20 80\n");
421   check_ai ("www.example", "80", AF_INET6,
422             "address: STREAM/TCP 2001:db8::1 80\n"
423             "address: DGRAM/UDP 2001:db8::1 80\n"
424             "address: RAW/IP 2001:db8::1 80\n");
425   check_ai_hints ("www.example", "80",
426                   (struct addrinfo) { .ai_family = AF_INET6,
427                       .ai_flags = AI_CANONNAME, },
428                   "flags: AI_CANONNAME\n"
429                   "canonname: www.example\n"
430                   "address: STREAM/TCP 2001:db8::1 80\n"
431                   "address: DGRAM/UDP 2001:db8::1 80\n"
432                   "address: RAW/IP 2001:db8::1 80\n");
433   check_ai ("alias.example", "80", AF_INET6,
434             "address: STREAM/TCP 2001:db8::2 80\n"
435             "address: DGRAM/UDP 2001:db8::2 80\n"
436             "address: RAW/IP 2001:db8::2 80\n");
437   check_ai_hints ("alias.example", "80",
438                   (struct addrinfo) { .ai_family = AF_INET6,
439                       .ai_flags = AI_CANONNAME, },
440                   "flags: AI_CANONNAME\n"
441                   "canonname: www.example\n"
442                   "address: STREAM/TCP 2001:db8::2 80\n"
443                   "address: DGRAM/UDP 2001:db8::2 80\n"
444                   "address: RAW/IP 2001:db8::2 80\n");
445   check_ai (LONG_NAME, "80", AF_INET6,
446             "address: STREAM/TCP 2001:db8::4 80\n"
447             "address: DGRAM/UDP 2001:db8::4 80\n"
448             "address: RAW/IP 2001:db8::4 80\n");
449 
450   check_h ("t.www.example", AF_INET,
451            "name: t.www.example\n"
452            "address: 192.0.2.19\n");
453   check_h ("t.alias.example", AF_INET,
454            "name: www.example\n"
455            "alias: t.alias.example\n"
456            "address: 192.0.2.20\n");
457   check_h ("t.www.example", AF_INET6,
458            "name: t.www.example\n"
459            "address: 2001:db8::3\n");
460   check_h ("t.alias.example", AF_INET6,
461            "name: www.example\n"
462            "alias: t.alias.example\n"
463            "address: 2001:db8::4\n");
464   check_ai ("t.www.example", "80", AF_UNSPEC,
465             "address: STREAM/TCP 192.0.2.19 80\n"
466             "address: DGRAM/UDP 192.0.2.19 80\n"
467             "address: RAW/IP 192.0.2.19 80\n"
468             "address: STREAM/TCP 2001:db8::3 80\n"
469             "address: DGRAM/UDP 2001:db8::3 80\n"
470             "address: RAW/IP 2001:db8::3 80\n");
471   check_ai ("t.alias.example", "80", AF_UNSPEC,
472             "address: STREAM/TCP 192.0.2.20 80\n"
473             "address: DGRAM/UDP 192.0.2.20 80\n"
474             "address: RAW/IP 192.0.2.20 80\n"
475             "address: STREAM/TCP 2001:db8::4 80\n"
476             "address: DGRAM/UDP 2001:db8::4 80\n"
477             "address: RAW/IP 2001:db8::4 80\n");
478   check_ai ("t.www.example", "80", AF_INET,
479             "address: STREAM/TCP 192.0.2.19 80\n"
480             "address: DGRAM/UDP 192.0.2.19 80\n"
481             "address: RAW/IP 192.0.2.19 80\n");
482   check_ai ("t.alias.example", "80", AF_INET,
483             "address: STREAM/TCP 192.0.2.20 80\n"
484             "address: DGRAM/UDP 192.0.2.20 80\n"
485             "address: RAW/IP 192.0.2.20 80\n");
486   check_ai ("t.www.example", "80", AF_INET6,
487             "address: STREAM/TCP 2001:db8::3 80\n"
488             "address: DGRAM/UDP 2001:db8::3 80\n"
489             "address: RAW/IP 2001:db8::3 80\n");
490   check_ai ("t.alias.example", "80", AF_INET6,
491             "address: STREAM/TCP 2001:db8::4 80\n"
492             "address: DGRAM/UDP 2001:db8::4 80\n"
493             "address: RAW/IP 2001:db8::4 80\n");
494 
495   test_bug_21295 ();
496   test_nodata_nxdomain ();
497 
498   resolv_test_end (aux);
499 
500   return 0;
501 }
502 
503 #include <support/test-driver.c>
504