]>
Commit | Line | Data |
---|---|---|
5b757a51 | 1 | /* Check that RES_ROTATE works with few nameserver entries (bug 13028). |
04277e02 | 2 | Copyright (C) 2017-2019 Free Software Foundation, Inc. |
5b757a51 FW |
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 | <http://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 | |
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 | |
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 | |
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 | |
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> |