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