]>
Commit | Line | Data |
---|---|---|
0b99828d | 1 | /* Test reverse DNS lookup. |
6d7e8eda | 2 | Copyright (C) 2022-2023 Free Software Foundation, Inc. |
0b99828d 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 | <https://www.gnu.org/licenses/>. */ | |
18 | ||
19 | #include <arpa/inet.h> | |
20 | #include <errno.h> | |
21 | #include <netdb.h> | |
22 | #include <stdbool.h> | |
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
26 | #include <support/check.h> | |
27 | #include <support/check_nss.h> | |
28 | #include <support/next_to_fault.h> | |
29 | #include <support/resolv_test.h> | |
30 | #include <support/support.h> | |
31 | ||
32 | #include "tst-resolv-maybe_insert_sig.h" | |
33 | ||
34 | /* QNAME format: | |
35 | ||
36 | ADDRESSES.CNAMES...(lots of 0s)...8.b.d.0.1.0.0.2.ip6.arpa. | |
37 | CNAMES|ADDRESSES.2.0.192.in-addr-arpa. | |
38 | ||
39 | For the IPv4 reverse lookup, the address count is in the lower | |
40 | bits. | |
41 | ||
42 | CNAMES is the length of the CNAME chain, ADDRESSES is the number of | |
43 | addresses in the response. The special value 15 means that there | |
44 | are no addresses, and the RCODE is NXDOMAIN. */ | |
45 | static void | |
46 | response (const struct resolv_response_context *ctx, | |
47 | struct resolv_response_builder *b, | |
48 | const char *qname, uint16_t qclass, uint16_t qtype) | |
49 | { | |
50 | TEST_COMPARE (qclass, C_IN); | |
51 | TEST_COMPARE (qtype, T_PTR); | |
52 | ||
53 | unsigned int addresses, cnames, bits; | |
54 | char *tail; | |
55 | if (strstr (qname, "ip6.arpa") != NULL | |
56 | && sscanf (qname, "%x.%x.%ms", &addresses, &cnames, &tail) == 3) | |
57 | TEST_COMPARE_STRING (tail, "\ | |
58 | 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"); | |
59 | else if (sscanf (qname, "%u.%ms", &bits, &tail) == 2) | |
60 | { | |
61 | TEST_COMPARE_STRING (tail, "2.0.192.in-addr.arpa"); | |
62 | addresses = bits & 0x0f; | |
63 | cnames = bits >> 4; | |
64 | } | |
65 | else | |
66 | FAIL_EXIT1 ("invalid QNAME: %s", qname); | |
67 | free (tail); | |
68 | ||
69 | int rcode; | |
70 | if (addresses == 15) | |
71 | { | |
72 | /* Special case: Use no addresses with NXDOMAIN response. */ | |
73 | rcode = ns_r_nxdomain; | |
74 | addresses = 0; | |
75 | } | |
76 | else | |
77 | rcode = 0; | |
78 | ||
79 | struct resolv_response_flags flags = { .rcode = rcode }; | |
80 | resolv_response_init (b, flags); | |
81 | resolv_response_add_question (b, qname, qclass, qtype); | |
82 | resolv_response_section (b, ns_s_an); | |
83 | maybe_insert_sig (b, qname); | |
84 | ||
85 | /* Provide the requested number of CNAME records. */ | |
86 | char *previous_name = (char *) qname; | |
87 | for (int unique = 0; unique < cnames; ++unique) | |
88 | { | |
89 | resolv_response_open_record (b, previous_name, qclass, T_CNAME, 60); | |
90 | char *new_name = xasprintf ("%d.alias.example", unique); | |
91 | resolv_response_add_name (b, new_name); | |
92 | resolv_response_close_record (b); | |
93 | ||
94 | maybe_insert_sig (b, qname); | |
95 | ||
96 | if (previous_name != qname) | |
97 | free (previous_name); | |
98 | previous_name = new_name; | |
99 | } | |
100 | ||
101 | for (int unique = 0; unique < addresses; ++unique) | |
102 | { | |
103 | resolv_response_open_record (b, previous_name, qclass, T_PTR, 60); | |
104 | char *ptr = xasprintf ("unique-%d.cnames-%u.addresses-%u.example", | |
105 | unique, cnames, addresses); | |
106 | resolv_response_add_name (b, ptr); | |
107 | free (ptr); | |
108 | resolv_response_close_record (b); | |
109 | } | |
110 | ||
111 | if (previous_name != qname) | |
112 | free (previous_name); | |
113 | } | |
114 | ||
115 | /* Used to check that gethostbyaddr_r does not write past the buffer | |
116 | end. */ | |
117 | static struct support_next_to_fault ntf; | |
118 | ||
119 | /* Perform a gethostbyaddr call and check the result. */ | |
120 | static void | |
121 | check_gethostbyaddr (const char *address, const char *expected) | |
122 | { | |
123 | unsigned char bytes[16]; | |
124 | unsigned int byteslen; | |
125 | int family; | |
126 | if (strchr (address, ':') != NULL) | |
127 | { | |
128 | family = AF_INET6; | |
129 | byteslen = 16; | |
130 | } | |
131 | else | |
132 | { | |
133 | family = AF_INET; | |
134 | byteslen = 4; | |
135 | } | |
136 | TEST_COMPARE (inet_pton (family, address, bytes), 1); | |
137 | ||
138 | struct hostent *e = gethostbyaddr (bytes, byteslen, family); | |
139 | check_hostent (address, e, expected); | |
140 | ||
141 | if (e == NULL) | |
142 | return; | |
143 | ||
144 | /* Try gethostbyaddr_r with increasing sizes until success. First | |
145 | compute a reasonable minimum buffer size, to avoid many pointless | |
146 | attempts. */ | |
147 | size_t minimum_size = strlen (e->h_name); | |
148 | for (int i = 0; e->h_addr_list[i] != NULL; ++i) | |
149 | minimum_size += e->h_length + sizeof (char *); | |
150 | for (int i = 0; e->h_aliases[i] != NULL; ++i) | |
151 | minimum_size += strlen (e->h_aliases[i]) + 1 + sizeof (char *); | |
152 | ||
153 | /* Gradually increase the size until success. */ | |
154 | for (size_t size = minimum_size; size < ntf.length; ++size) | |
155 | { | |
156 | struct hostent result; | |
157 | int herrno; | |
158 | int ret = gethostbyaddr_r (bytes, byteslen, family, &result, | |
159 | ntf.buffer + ntf.length - size, size, | |
160 | &e, &herrno); | |
161 | if (ret == ERANGE) | |
162 | /* Retry with larger size. */ | |
163 | TEST_COMPARE (herrno, NETDB_INTERNAL); | |
164 | else if (ret == 0) | |
165 | { | |
166 | TEST_VERIFY (size > minimum_size); | |
167 | check_hostent (address, e, expected); | |
168 | return; | |
169 | } | |
170 | else | |
171 | FAIL_EXIT1 ("Unexpected gethostbyaddr_r failure: %d", ret); | |
172 | } | |
173 | ||
174 | FAIL_EXIT1 ("gethostbyaddr_r always failed for: %s", address); | |
175 | } | |
176 | ||
177 | /* Perform a getnameinfo call and check the result. */ | |
178 | static void | |
179 | check_getnameinfo (const char *address, const char *expected) | |
180 | { | |
181 | struct sockaddr_in sin = { }; | |
182 | struct sockaddr_in6 sin6 = { }; | |
183 | void *sa; | |
184 | socklen_t salen; | |
185 | if (strchr (address, ':') != NULL) | |
186 | { | |
187 | sin6.sin6_family = AF_INET6; | |
188 | TEST_COMPARE (inet_pton (AF_INET6, address, &sin6.sin6_addr), 1); | |
189 | sin6.sin6_port = htons (80); | |
190 | sa = &sin6; | |
191 | salen = sizeof (sin6); | |
192 | } | |
193 | else | |
194 | { | |
195 | sin.sin_family = AF_INET; | |
196 | TEST_COMPARE (inet_pton (AF_INET, address, &sin.sin_addr), 1); | |
197 | sin.sin_port = htons (80); | |
198 | sa = &sin; | |
199 | salen = sizeof (sin); | |
200 | } | |
201 | ||
202 | char host[64]; | |
203 | char service[64]; | |
204 | int ret = getnameinfo (sa, salen, host, | |
205 | sizeof (host), service, sizeof (service), | |
206 | NI_NAMEREQD | NI_NUMERICSERV); | |
207 | switch (ret) | |
208 | { | |
209 | case 0: | |
210 | TEST_COMPARE_STRING (host, expected); | |
211 | TEST_COMPARE_STRING (service, "80"); | |
212 | break; | |
213 | case EAI_SYSTEM: | |
214 | TEST_COMPARE_STRING (strerror (errno), expected); | |
215 | break; | |
216 | default: | |
217 | TEST_COMPARE_STRING (gai_strerror (ret), expected); | |
218 | } | |
219 | } | |
220 | ||
221 | static int | |
222 | do_test (void) | |
223 | { | |
224 | /* Some reasonably upper bound for the maximum response size. */ | |
225 | ntf = support_next_to_fault_allocate (4096); | |
226 | ||
227 | struct resolv_test *obj = resolv_test_start | |
228 | ((struct resolv_redirect_config) | |
229 | { | |
230 | .response_callback = response | |
231 | }); | |
232 | ||
233 | for (int do_insert_sig = 0; do_insert_sig < 2; ++do_insert_sig) | |
234 | { | |
235 | insert_sig = do_insert_sig; | |
236 | ||
237 | /* No PTR record, RCODE=0. */ | |
238 | check_gethostbyaddr ("192.0.2.0", "error: NO_RECOVERY\n"); | |
239 | check_getnameinfo ("192.0.2.0", "Name or service not known"); | |
240 | check_gethostbyaddr ("192.0.2.16", "error: NO_RECOVERY\n"); | |
241 | check_getnameinfo ("192.0.2.16", "Name or service not known"); | |
242 | check_gethostbyaddr ("192.0.2.32", "error: NO_RECOVERY\n"); | |
243 | check_getnameinfo ("192.0.2.32", "Name or service not known"); | |
244 | check_gethostbyaddr ("2001:db8::", "error: NO_RECOVERY\n"); | |
245 | check_getnameinfo ("2001:db8::", "Name or service not known"); | |
246 | check_gethostbyaddr ("2001:db8::10", "error: NO_RECOVERY\n"); | |
247 | check_getnameinfo ("2001:db8::10", "Name or service not known"); | |
248 | check_gethostbyaddr ("2001:db8::20", "error: NO_RECOVERY\n"); | |
249 | check_getnameinfo ("2001:db8::20", "Name or service not known"); | |
250 | ||
251 | /* No PTR record, NXDOMAIN. */ | |
252 | check_gethostbyaddr ("192.0.2.15", "error: HOST_NOT_FOUND\n"); | |
253 | check_getnameinfo ("192.0.2.15", "Name or service not known"); | |
254 | check_gethostbyaddr ("192.0.2.31", "error: HOST_NOT_FOUND\n"); | |
255 | check_getnameinfo ("192.0.2.31", "Name or service not known"); | |
256 | check_gethostbyaddr ("192.0.2.47", "error: HOST_NOT_FOUND\n"); | |
257 | check_getnameinfo ("192.0.2.47", "Name or service not known"); | |
258 | check_gethostbyaddr ("2001:db8::f", "error: HOST_NOT_FOUND\n"); | |
259 | check_getnameinfo ("2001:db8::f", "Name or service not known"); | |
260 | check_gethostbyaddr ("2001:db8::1f", "error: HOST_NOT_FOUND\n"); | |
261 | check_getnameinfo ("2001:db8::1f", "Name or service not known"); | |
262 | check_gethostbyaddr ("2001:db8::2f", "error: HOST_NOT_FOUND\n"); | |
263 | check_getnameinfo ("2001:db8::2f", "Name or service not known"); | |
264 | ||
265 | /* Actual response data. Only the first PTR record is returned. */ | |
266 | check_gethostbyaddr ("192.0.2.1", | |
267 | "name: unique-0.cnames-0.addresses-1.example\n" | |
268 | "address: 192.0.2.1\n"); | |
269 | check_getnameinfo ("192.0.2.1", | |
270 | "unique-0.cnames-0.addresses-1.example"); | |
271 | check_gethostbyaddr ("192.0.2.17", | |
272 | "name: unique-0.cnames-1.addresses-1.example\n" | |
273 | "address: 192.0.2.17\n"); | |
274 | check_getnameinfo ("192.0.2.17", | |
275 | "unique-0.cnames-1.addresses-1.example"); | |
276 | check_gethostbyaddr ("192.0.2.18", | |
277 | "name: unique-0.cnames-1.addresses-2.example\n" | |
278 | "address: 192.0.2.18\n"); | |
279 | check_getnameinfo ("192.0.2.18", | |
280 | "unique-0.cnames-1.addresses-2.example"); | |
281 | check_gethostbyaddr ("192.0.2.33", | |
282 | "name: unique-0.cnames-2.addresses-1.example\n" | |
283 | "address: 192.0.2.33\n"); | |
284 | check_getnameinfo ("192.0.2.33", | |
285 | "unique-0.cnames-2.addresses-1.example"); | |
286 | check_gethostbyaddr ("192.0.2.34", | |
287 | "name: unique-0.cnames-2.addresses-2.example\n" | |
288 | "address: 192.0.2.34\n"); | |
289 | check_getnameinfo ("192.0.2.34", | |
290 | "unique-0.cnames-2.addresses-2.example"); | |
291 | ||
292 | /* Same for IPv6 addresses. */ | |
293 | check_gethostbyaddr ("2001:db8::1", | |
294 | "name: unique-0.cnames-0.addresses-1.example\n" | |
295 | "address: 2001:db8::1\n"); | |
296 | check_getnameinfo ("2001:db8::1", | |
297 | "unique-0.cnames-0.addresses-1.example"); | |
298 | check_gethostbyaddr ("2001:db8::11", | |
299 | "name: unique-0.cnames-1.addresses-1.example\n" | |
300 | "address: 2001:db8::11\n"); | |
301 | check_getnameinfo ("2001:db8::11", | |
302 | "unique-0.cnames-1.addresses-1.example"); | |
303 | check_gethostbyaddr ("2001:db8::12", | |
304 | "name: unique-0.cnames-1.addresses-2.example\n" | |
305 | "address: 2001:db8::12\n"); | |
306 | check_getnameinfo ("2001:db8::12", | |
307 | "unique-0.cnames-1.addresses-2.example"); | |
308 | check_gethostbyaddr ("2001:db8::21", | |
309 | "name: unique-0.cnames-2.addresses-1.example\n" | |
310 | "address: 2001:db8::21\n"); | |
311 | check_getnameinfo ("2001:db8::21", | |
312 | "unique-0.cnames-2.addresses-1.example"); | |
313 | check_gethostbyaddr ("2001:db8::22", | |
314 | "name: unique-0.cnames-2.addresses-2.example\n" | |
315 | "address: 2001:db8::22\n"); | |
316 | check_getnameinfo ("2001:db8::22", | |
317 | "unique-0.cnames-2.addresses-2.example"); | |
318 | } | |
319 | ||
320 | resolv_test_end (obj); | |
321 | ||
322 | support_next_to_fault_free (&ntf); | |
323 | return 0; | |
324 | } | |
325 | ||
326 | #include <support/test-driver.c> |