1/* Test reverse DNS lookup.
2 Copyright (C) 2022-2024 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 <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. */
45static void
46response (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 (haystack: qname, needle: "ip6.arpa") != NULL
56 && sscanf (s: qname, format: "%x.%x.%ms", &addresses, &cnames, &tail) == 3)
57 TEST_COMPARE_STRING (tail, "\
580.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 (s: qname, format: "%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 (ptr: 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, name: qname, class: qclass, type: qtype);
82 resolv_response_section (b, ns_s_an);
83 maybe_insert_sig (b, owner: 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, name: previous_name, class: qclass, T_CNAME, ttl: 60);
90 char *new_name = xasprintf (format: "%d.alias.example", unique);
91 resolv_response_add_name (b, name: new_name);
92 resolv_response_close_record (b);
93
94 maybe_insert_sig (b, owner: qname);
95
96 if (previous_name != qname)
97 free (ptr: previous_name);
98 previous_name = new_name;
99 }
100
101 for (int unique = 0; unique < addresses; ++unique)
102 {
103 resolv_response_open_record (b, name: previous_name, class: qclass, T_PTR, ttl: 60);
104 char *ptr = xasprintf (format: "unique-%d.cnames-%u.addresses-%u.example",
105 unique, cnames, addresses);
106 resolv_response_add_name (b, name: ptr);
107 free (ptr: ptr);
108 resolv_response_close_record (b);
109 }
110
111 if (previous_name != qname)
112 free (ptr: previous_name);
113}
114
115/* Used to check that gethostbyaddr_r does not write past the buffer
116 end. */
117static struct support_next_to_fault ntf;
118
119/* Perform a gethostbyaddr call and check the result. */
120static void
121check_gethostbyaddr (const char *address, const char *expected)
122{
123 unsigned char bytes[16];
124 unsigned int byteslen;
125 int family;
126 if (strchr (s: address, c: ':') != 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 (addr: bytes, len: byteslen, type: family);
139 check_hostent (query_description: 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 (s: 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 (s: 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 (addr: bytes, len: byteslen, type: family, result_buf: &result,
159 buf: ntf.buffer + ntf.length - size, buflen: size,
160 result: &e, h_errnop: &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 (query_description: 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. */
178static void
179check_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 (s: address, c: ':') != 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: sa, salen: salen, host: host,
205 hostlen: sizeof (host), serv: service, servlen: 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
221static int
222do_test (void)
223{
224 /* Some reasonably upper bound for the maximum response size. */
225 ntf = support_next_to_fault_allocate (size: 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 (address: "192.0.2.0", expected: "error: NO_RECOVERY\n");
239 check_getnameinfo (address: "192.0.2.0", expected: "Name or service not known");
240 check_gethostbyaddr (address: "192.0.2.16", expected: "error: NO_RECOVERY\n");
241 check_getnameinfo (address: "192.0.2.16", expected: "Name or service not known");
242 check_gethostbyaddr (address: "192.0.2.32", expected: "error: NO_RECOVERY\n");
243 check_getnameinfo (address: "192.0.2.32", expected: "Name or service not known");
244 check_gethostbyaddr (address: "2001:db8::", expected: "error: NO_RECOVERY\n");
245 check_getnameinfo (address: "2001:db8::", expected: "Name or service not known");
246 check_gethostbyaddr (address: "2001:db8::10", expected: "error: NO_RECOVERY\n");
247 check_getnameinfo (address: "2001:db8::10", expected: "Name or service not known");
248 check_gethostbyaddr (address: "2001:db8::20", expected: "error: NO_RECOVERY\n");
249 check_getnameinfo (address: "2001:db8::20", expected: "Name or service not known");
250
251 /* No PTR record, NXDOMAIN. */
252 check_gethostbyaddr (address: "192.0.2.15", expected: "error: HOST_NOT_FOUND\n");
253 check_getnameinfo (address: "192.0.2.15", expected: "Name or service not known");
254 check_gethostbyaddr (address: "192.0.2.31", expected: "error: HOST_NOT_FOUND\n");
255 check_getnameinfo (address: "192.0.2.31", expected: "Name or service not known");
256 check_gethostbyaddr (address: "192.0.2.47", expected: "error: HOST_NOT_FOUND\n");
257 check_getnameinfo (address: "192.0.2.47", expected: "Name or service not known");
258 check_gethostbyaddr (address: "2001:db8::f", expected: "error: HOST_NOT_FOUND\n");
259 check_getnameinfo (address: "2001:db8::f", expected: "Name or service not known");
260 check_gethostbyaddr (address: "2001:db8::1f", expected: "error: HOST_NOT_FOUND\n");
261 check_getnameinfo (address: "2001:db8::1f", expected: "Name or service not known");
262 check_gethostbyaddr (address: "2001:db8::2f", expected: "error: HOST_NOT_FOUND\n");
263 check_getnameinfo (address: "2001:db8::2f", expected: "Name or service not known");
264
265 /* Actual response data. Only the first PTR record is returned. */
266 check_gethostbyaddr (address: "192.0.2.1",
267 expected: "name: unique-0.cnames-0.addresses-1.example\n"
268 "address: 192.0.2.1\n");
269 check_getnameinfo (address: "192.0.2.1",
270 expected: "unique-0.cnames-0.addresses-1.example");
271 check_gethostbyaddr (address: "192.0.2.17",
272 expected: "name: unique-0.cnames-1.addresses-1.example\n"
273 "address: 192.0.2.17\n");
274 check_getnameinfo (address: "192.0.2.17",
275 expected: "unique-0.cnames-1.addresses-1.example");
276 check_gethostbyaddr (address: "192.0.2.18",
277 expected: "name: unique-0.cnames-1.addresses-2.example\n"
278 "address: 192.0.2.18\n");
279 check_getnameinfo (address: "192.0.2.18",
280 expected: "unique-0.cnames-1.addresses-2.example");
281 check_gethostbyaddr (address: "192.0.2.33",
282 expected: "name: unique-0.cnames-2.addresses-1.example\n"
283 "address: 192.0.2.33\n");
284 check_getnameinfo (address: "192.0.2.33",
285 expected: "unique-0.cnames-2.addresses-1.example");
286 check_gethostbyaddr (address: "192.0.2.34",
287 expected: "name: unique-0.cnames-2.addresses-2.example\n"
288 "address: 192.0.2.34\n");
289 check_getnameinfo (address: "192.0.2.34",
290 expected: "unique-0.cnames-2.addresses-2.example");
291
292 /* Same for IPv6 addresses. */
293 check_gethostbyaddr (address: "2001:db8::1",
294 expected: "name: unique-0.cnames-0.addresses-1.example\n"
295 "address: 2001:db8::1\n");
296 check_getnameinfo (address: "2001:db8::1",
297 expected: "unique-0.cnames-0.addresses-1.example");
298 check_gethostbyaddr (address: "2001:db8::11",
299 expected: "name: unique-0.cnames-1.addresses-1.example\n"
300 "address: 2001:db8::11\n");
301 check_getnameinfo (address: "2001:db8::11",
302 expected: "unique-0.cnames-1.addresses-1.example");
303 check_gethostbyaddr (address: "2001:db8::12",
304 expected: "name: unique-0.cnames-1.addresses-2.example\n"
305 "address: 2001:db8::12\n");
306 check_getnameinfo (address: "2001:db8::12",
307 expected: "unique-0.cnames-1.addresses-2.example");
308 check_gethostbyaddr (address: "2001:db8::21",
309 expected: "name: unique-0.cnames-2.addresses-1.example\n"
310 "address: 2001:db8::21\n");
311 check_getnameinfo (address: "2001:db8::21",
312 expected: "unique-0.cnames-2.addresses-1.example");
313 check_gethostbyaddr (address: "2001:db8::22",
314 expected: "name: unique-0.cnames-2.addresses-2.example\n"
315 "address: 2001:db8::22\n");
316 check_getnameinfo (address: "2001:db8::22",
317 expected: "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>
327

source code of glibc/resolv/tst-resolv-byaddr.c