1 /* Check that RES_ROTATE works with few nameserver entries (bug 13028).
2    Copyright (C) 2017-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 <netdb.h>
20 #include <resolv.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <support/check.h>
24 #include <support/check_nss.h>
25 #include <support/resolv_test.h>
26 #include <support/test-driver.h>
27 
28 static volatile int drop_server = -1;
29 static volatile unsigned int query_counts[resolv_max_test_servers];
30 
31 static const char address_ipv4[4] = {192, 0, 2, 1};
32 static const char address_ipv6[16]
33   = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
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   if (ctx->server_index == drop_server)
41     {
42       resolv_response_drop (b);
43       resolv_response_close (b);
44       return;
45     }
46 
47   bool force_tcp = strncmp (qname, "2.", 2) == 0;
48   struct resolv_response_flags flags = {.tc = force_tcp && !ctx->tcp};
49   resolv_response_init (b, flags);
50   resolv_response_add_question (b, qname, qclass, qtype);
51   if (flags.tc)
52     return;
53 
54   TEST_VERIFY_EXIT (ctx->server_index < resolv_max_test_servers);
55   ++query_counts[ctx->server_index];
56 
57   resolv_response_section (b, ns_s_an);
58   resolv_response_open_record (b, qname, qclass, qtype, 0);
59   switch (qtype)
60     {
61     case T_A:
62       {
63         char addr[sizeof (address_ipv4)];
64         memcpy (addr, address_ipv4, sizeof (address_ipv4));
65         addr[3] = 1 + ctx->tcp;
66         resolv_response_add_data (b, addr, sizeof (addr));
67       }
68       break;
69     case T_AAAA:
70       {
71         char addr[sizeof (address_ipv6)];
72         memcpy (addr, address_ipv6, sizeof (address_ipv6));
73         addr[15] = 1 + ctx->tcp;
74         resolv_response_add_data (b, addr, sizeof (addr));
75       }
76       break;
77     case T_PTR:
78       if (force_tcp)
79         resolv_response_add_name (b, "2.host.example");
80       else
81         resolv_response_add_name (b, "host.example");
82       break;
83     default:
84       FAIL_EXIT1 ("unexpected QTYPE: %s/%u/%u", qname, qclass, qtype);
85     }
86   resolv_response_close_record (b);
87 }
88 
89 static void
check_forward_1(const char * name,int family)90 check_forward_1 (const char *name, int family)
91 {
92   unsigned char lsb;
93   if (strncmp (name, "2.", 2) == 0)
94     lsb = 2;
95   else
96     lsb = 1;
97 
98   char expected_hostent_v4[200];
99   snprintf (expected_hostent_v4, sizeof (expected_hostent_v4),
100             "name: %s\naddress: 192.0.2.%d\n", name, lsb);
101   char expected_hostent_v6[200];
102   snprintf (expected_hostent_v6, sizeof (expected_hostent_v6),
103             "name: %s\naddress: 2001:db8::%d\n", name, lsb);
104   char expected_ai[200];
105 
106   unsigned char address[16];
107   size_t address_length;
108 
109   char *expected_hostent;
110   switch (family)
111     {
112     case AF_INET:
113       expected_hostent = expected_hostent_v4;
114       snprintf (expected_ai, sizeof (expected_ai),
115                 "address: STREAM/TCP 192.0.2.%d 80\n", lsb);
116       TEST_VERIFY_EXIT (sizeof (address_ipv4) == sizeof (struct in_addr));
117       memcpy (address, address_ipv4, sizeof (address_ipv4));
118       address_length = sizeof (address_ipv4);
119       break;
120     case AF_INET6:
121       expected_hostent = expected_hostent_v6;
122       snprintf (expected_ai, sizeof (expected_ai),
123                 "address: STREAM/TCP 2001:db8::%d 80\n", lsb);
124       TEST_VERIFY_EXIT (sizeof (address_ipv6) == sizeof (struct in6_addr));
125       memcpy (address, address_ipv6, sizeof (address_ipv6));
126       address_length = sizeof (address_ipv6);
127       break;
128     case AF_UNSPEC:
129       expected_hostent = NULL;
130       snprintf (expected_ai, sizeof (expected_ai),
131                 "address: STREAM/TCP 192.0.2.%d 80\n"
132                 "address: STREAM/TCP 2001:db8::%d 80\n",
133                 lsb, lsb);
134       address_length = 0;
135       break;
136     default:
137       FAIL_EXIT1 ("unknown address family %d", family);
138     }
139 
140 
141   if (family == AF_INET)
142     {
143       struct hostent *e = gethostbyname (name);
144       check_hostent (name, e, expected_hostent_v4);
145     }
146 
147   if (family != AF_UNSPEC)
148     {
149       struct hostent *e = gethostbyname2 (name, family);
150       check_hostent (name, e, expected_hostent);
151     }
152 
153   if (address_length > 0)
154     {
155       address[address_length - 1] = lsb;
156       struct hostent *e = gethostbyaddr (address, address_length, family);
157       check_hostent (name, e, expected_hostent);
158     }
159 
160   struct addrinfo hints =
161     {
162       .ai_family = family,
163       .ai_socktype = SOCK_STREAM,
164       .ai_protocol = IPPROTO_TCP,
165     };
166   struct addrinfo *ai;
167   int ret = getaddrinfo (name, "80", &hints, &ai);
168   check_addrinfo (name, ai, ret, expected_ai);
169   if (ret == 0)
170     {
171       for (struct addrinfo *p = ai; p != NULL; p = p->ai_next)
172         {
173           char host[200];
174           ret = getnameinfo (p->ai_addr, p->ai_addrlen,
175                              host, sizeof (host),
176                              NULL, 0, /* service */
177                              0);
178           if (ret != 0)
179             {
180               support_record_failure ();
181               printf ("error: getnameinfo: %d\n", ret);
182             }
183           else
184             {
185               if (lsb == 1)
186                 TEST_VERIFY (strcmp (host, "host.example") == 0);
187               else
188                 TEST_VERIFY (strcmp (host, "2.host.example") == 0);
189             }
190         }
191       freeaddrinfo (ai);
192     }
193 }
194 
195 static void
check_forward(int family)196 check_forward (int family)
197 {
198   check_forward_1 ("host.example", family);
199   check_forward_1 ("2.host.example", family);
200 }
201 
202 static int
do_test(void)203 do_test (void)
204 {
205   for (int force_tcp = 0; force_tcp < 2; ++force_tcp)
206     for (int nscount = 1; nscount <= 3; ++nscount)
207       for (int disable_server = -1; disable_server < nscount; ++disable_server)
208         for (drop_server = -1; drop_server < nscount; ++drop_server)
209           {
210             /* A disabled server will never receive queries and
211                therefore cannot drop them.  */
212             if (drop_server >= 0 && drop_server == disable_server)
213               continue;
214             /* No servers remaining to query, all queries are expected
215                to fail.  */
216             int broken_servers = (disable_server >= 0) + (drop_server >= 0);
217             if (nscount <= broken_servers)
218               continue;
219 
220             if (test_verbose > 0)
221               printf ("info: tcp=%d nscount=%d disable=%d drop=%d\n",
222                       force_tcp, nscount, disable_server, drop_server);
223             struct resolv_redirect_config config =
224               {
225                 .response_callback = response,
226                 .nscount = nscount
227               };
228             if (disable_server >= 0)
229               {
230                 config.servers[disable_server].disable_udp = true;
231                 config.servers[disable_server].disable_tcp = true;
232               }
233 
234             struct resolv_test *aux = resolv_test_start (config);
235             _res.options |= RES_ROTATE;
236 
237             /* Run a few queries to make sure that all of them
238                succeed.  We always perform more than nscount queries,
239                so we cover all active servers due to RES_ROTATE.  */
240             for (size_t i = 0; i < resolv_max_test_servers; ++i)
241               query_counts[i] = 0;
242             check_forward (AF_INET);
243             check_forward (AF_INET6);
244             check_forward (AF_UNSPEC);
245 
246             for (int i = 0; i < nscount; ++i)
247               {
248                 if (i != disable_server && i != drop_server
249                     && query_counts[i] == 0)
250                   {
251                     support_record_failure ();
252                     printf ("error: nscount=%d, but no query to server %d\n",
253                             nscount, i);
254                   }
255               }
256 
257             resolv_test_end (aux);
258           }
259   return 0;
260 }
261 
262 #define TIMEOUT 300
263 #include <support/test-driver.c>
264