1/* Test handling of CNAMEs with non-host domain names (bug 12154).
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 <errno.h>
20#include <netdb.h>
21#include <resolv.h>
22#include <stdlib.h>
23#include <string.h>
24#include <support/check.h>
25#include <support/check_nss.h>
26#include <support/resolv_test.h>
27#include <support/support.h>
28#include <support/xmemstream.h>
29
30/* Query strings describe the CNAME chain in the response. They have
31 the format "bitsBITS.countCOUNT.example.", where BITS and COUNT are
32 replaced by unsigned decimal numbers. COUNT is the number of CNAME
33 records in the response. BITS has two bits for each CNAME record,
34 describing a special prefix that is added to that CNAME.
35
36 0: No special leading label.
37 1: Starting with "*.".
38 2: Starting with "-x.".
39 3: Starting with "star.*.".
40
41 The first CNAME in the response using the two least significant
42 bits.
43
44 For PTR queries, the QNAME format is different, it is either
45 COUNT.BITS.168.192.in-addr.arpa. (with BITS and COUNT still
46 decimal), or:
47
48COUNT.BITS0.BITS1.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.
49
50 where BITS and COUNT are hexadecimal. */
51
52static void
53response (const struct resolv_response_context *ctx,
54 struct resolv_response_builder *b,
55 const char *qname, uint16_t qclass, uint16_t qtype)
56{
57 TEST_COMPARE (qclass, C_IN);
58
59 /* The only other query type besides A is PTR. */
60 if (qtype != T_A && qtype != T_AAAA)
61 TEST_COMPARE (qtype, T_PTR);
62
63 unsigned int bits, bits1, count;
64 char *tail = NULL;
65 if (sscanf (s: qname, format: "bits%u.count%u.%ms", &bits, &count, &tail) == 3)
66 TEST_COMPARE_STRING (tail, "example");
67 else if (strstr (haystack: qname, needle: "in-addr.arpa") != NULL
68 && sscanf (s: qname, format: "%u.%u.%ms", &bits, &count, &tail) == 3)
69 TEST_COMPARE_STRING (tail, "168.192.in-addr.arpa");
70 else if (sscanf (s: qname, format: "%x.%x.%x.%ms", &bits, &bits1, &count, &tail) == 4)
71 {
72 TEST_COMPARE_STRING (tail, "\
730.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");
74 bits |= bits1 << 4;
75 }
76 else
77 FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
78 free (ptr: tail);
79
80 struct resolv_response_flags flags = {};
81 resolv_response_init (b, flags);
82 resolv_response_add_question (b, name: qname, class: qclass, type: qtype);
83 resolv_response_section (b, ns_s_an);
84
85 /* Provide the requested number of CNAME records. */
86 char *previous_name = (char *) qname;
87 unsigned int original_bits = bits;
88 for (int unique = 0; unique < count; ++unique)
89 {
90 resolv_response_open_record (b, name: previous_name, class: qclass, T_CNAME, ttl: 60);
91
92 static const char bits_to_prefix[4][8] = { "", "*.", "-x.", "star.*." };
93 char *new_name = xasprintf (format: "%sunique%d.example",
94 bits_to_prefix[bits & 3], unique);
95 bits >>= 2;
96 resolv_response_add_name (b, name: new_name);
97 resolv_response_close_record (b);
98
99 if (previous_name != qname)
100 free (ptr: previous_name);
101 previous_name = new_name;
102 }
103
104 /* Actual answer record. */
105 resolv_response_open_record (b, name: previous_name, class: qclass, type: qtype, ttl: 60);
106 switch (qtype)
107 {
108 case T_A:
109 {
110 char ipv4[4] = {192, 168, count, original_bits};
111 resolv_response_add_data (b, &ipv4, sizeof (ipv4));
112 }
113 break;
114 case T_AAAA:
115 {
116 char ipv6[16] =
117 {
118 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 count, original_bits
120 };
121 resolv_response_add_data (b, &ipv6, sizeof (ipv6));
122 }
123 break;
124
125 case T_PTR:
126 {
127 char *name = xasprintf (format: "bits%u.count%u.example",
128 original_bits, count);
129 resolv_response_add_name (b, name);
130 free (ptr: name);
131 }
132 break;
133 }
134 resolv_response_close_record (b);
135
136 if (previous_name != qname)
137 free (ptr: previous_name);
138}
139
140/* Controls which name resolution function is invoked. */
141enum test_mode
142 {
143 byname, /* gethostbyname. */
144 byname2, /* gethostbyname2. */
145 gai, /* getaddrinfo without AI_CANONNAME. */
146 gai_canon, /* getaddrinfo with AI_CANONNAME. */
147
148 test_mode_num /* Number of enum values. */
149 };
150
151static const char *
152test_mode_to_string (enum test_mode mode)
153{
154 switch (mode)
155 {
156 case byname:
157 return "byname";
158 case byname2:
159 return "byname2";
160 case gai:
161 return "gai";
162 case gai_canon:
163 return "gai_canon";
164 case test_mode_num:
165 break; /* Report error below. */
166 }
167 FAIL_EXIT1 ("invalid test_mode: %d", mode);
168}
169
170/* Append the name and aliases to OUT. */
171static void
172append_names (FILE *out, const char *qname, int bits, int count,
173 enum test_mode mode)
174{
175 /* Largest valid index which has a corresponding zero in bits
176 (meaning a syntactically valid CNAME). */
177 int last_valid_cname = -1;
178
179 for (int i = 0; i < count; ++i)
180 if ((bits & (3 << (i * 2))) == 0)
181 last_valid_cname = i;
182
183 if (mode != gai)
184 {
185 const char *label;
186 if (mode == gai_canon)
187 label = "canonname";
188 else
189 label = "name";
190 if (last_valid_cname >= 0)
191 fprintf (stream: out, format: "%s: unique%d.example\n", label, last_valid_cname);
192 else
193 fprintf (stream: out, format: "%s: %s\n", label, qname);
194 }
195
196 if (mode == byname || mode == byname2)
197 {
198 if (last_valid_cname >= 0)
199 fprintf (stream: out, format: "alias: %s\n", qname);
200 for (int i = 0; i < count; ++i)
201 {
202 if ((bits & (3 << (i * 2))) == 0 && i != last_valid_cname)
203 fprintf (stream: out, format: "alias: unique%d.example\n", i);
204 }
205 }
206}
207
208/* Append the address information to OUT. */
209static void
210append_addresses (FILE *out, int af, int bits, int count, enum test_mode mode)
211{
212 int last = count * 256 + bits;
213 if (mode == gai || mode == gai_canon)
214 {
215 if (af == AF_INET || af == AF_UNSPEC)
216 fprintf (stream: out, format: "address: STREAM/TCP 192.168.%d.%d 80\n", count, bits);
217 if (af == AF_INET6 || af == AF_UNSPEC)
218 {
219 if (last == 0)
220 fprintf (stream: out, format: "address: STREAM/TCP 2001:db8:: 80\n");
221 else
222 fprintf (stream: out, format: "address: STREAM/TCP 2001:db8::%x 80\n", last);
223 }
224 }
225 else
226 {
227 TEST_VERIFY (af != AF_UNSPEC);
228 if (af == AF_INET)
229 fprintf (stream: out, format: "address: 192.168.%d.%d\n", count, bits);
230 if (af == AF_INET6)
231 {
232 if (last == 0)
233 fprintf (stream: out, format: "address: 2001:db8::\n");
234 else
235 fprintf (stream: out, format: "address: 2001:db8::%x\n", last);
236 }
237 }
238}
239
240/* Perform one test using a forward lookup. */
241static void
242check_forward (int af, int bits, int count, enum test_mode mode)
243{
244 char *qname = xasprintf (format: "bits%d.count%d.example", bits, count);
245 char *label = xasprintf (format: "af=%d bits=%d count=%d mode=%s qname=%s",
246 af, bits, count, test_mode_to_string (mode), qname);
247
248 struct xmemstream expected;
249 xopen_memstream (stream: &expected);
250 if (mode == gai_canon)
251 fprintf (stream: expected.out, format: "flags: AI_CANONNAME\n");
252 append_names (out: expected.out, qname, bits, count, mode);
253 append_addresses (out: expected.out, af, bits, count, mode);
254 xfclose_memstream (stream: &expected);
255
256 if (mode == gai || mode == gai_canon)
257 {
258 struct addrinfo *ai;
259 struct addrinfo hints =
260 {
261 .ai_family = af,
262 .ai_socktype = SOCK_STREAM,
263 };
264 if (mode == gai_canon)
265 hints.ai_flags |= AI_CANONNAME;
266 int ret = getaddrinfo (name: qname, service: "80", req: &hints, pai: &ai);
267 check_addrinfo (query_description: label, ai, ret, expected: expected.buffer);
268 if (ret == 0)
269 freeaddrinfo (ai: ai);
270 }
271 else
272 {
273 struct hostent *e;
274 if (mode == gai)
275 {
276 TEST_COMPARE (af, AF_INET);
277 e = gethostbyname (name: qname);
278 }
279 else
280 {
281 if (af != AF_INET)
282 TEST_COMPARE (af, AF_INET6);
283 e = gethostbyname2 (name: qname, af: af);
284 }
285 check_hostent (query_description: label, e, expected: expected.buffer);
286 }
287
288 free (ptr: expected.buffer);
289 free (ptr: label);
290 free (ptr: qname);
291}
292
293/* Perform one check using a reverse lookup. */
294
295static void
296check_reverse (int af, int bits, int count)
297{
298 TEST_VERIFY (af == AF_INET || af == AF_INET6);
299
300 char *label = xasprintf (format: "af=%d bits=%d count=%d", af, bits, count);
301 char *fqdn = xasprintf (format: "bits%d.count%d.example", bits, count);
302
303 struct xmemstream expected;
304 xopen_memstream (stream: &expected);
305 fprintf (stream: expected.out, format: "name: %s\n", fqdn);
306 append_addresses (out: expected.out, af, bits, count, mode: byname);
307 xfclose_memstream (stream: &expected);
308
309 char addr[16] = { 0 };
310 socklen_t addrlen;
311 if (af == AF_INET)
312 {
313 addr[0] = 192;
314 addr[1] = 168;
315 addr[2] = count;
316 addr[3] = bits;
317 addrlen = 4;
318 }
319 else
320 {
321 addr[0] = 0x20;
322 addr[1] = 0x01;
323 addr[2] = 0x0d;
324 addr[3] = 0xb8;
325 addr[14] = count;
326 addr[15] = bits;
327 addrlen = 16;
328 }
329
330 struct hostent *e = gethostbyaddr (addr: addr, len: addrlen, type: af);
331 check_hostent (query_description: label, e, expected: expected.buffer);
332
333 /* getnameinfo check is different. There is no generic check_*
334 function for it. */
335 {
336 struct sockaddr_in sin = { };
337 struct sockaddr_in6 sin6 = { };
338 void *sa;
339 socklen_t salen;
340 if (af == AF_INET)
341 {
342 sin.sin_family = AF_INET;
343 memcpy (dest: &sin.sin_addr, src: addr, n: addrlen);
344 sin.sin_port = htons (80);
345 sa = &sin;
346 salen = sizeof (sin);
347 }
348 else
349 {
350 sin6.sin6_family = AF_INET6;
351 memcpy (dest: &sin6.sin6_addr, src: addr, n: addrlen);
352 sin6.sin6_port = htons (80);
353 sa = &sin6;
354 salen = sizeof (sin6);
355 }
356
357 char host[64];
358 char service[64];
359 int ret = getnameinfo (sa: sa, salen: salen, host: host,
360 hostlen: sizeof (host), serv: service, servlen: sizeof (service),
361 NI_NAMEREQD | NI_NUMERICSERV);
362 TEST_COMPARE (ret, 0);
363 TEST_COMPARE_STRING (host, fqdn);
364 TEST_COMPARE_STRING (service, "80");
365 }
366
367 free (ptr: expected.buffer);
368 free (ptr: fqdn);
369 free (ptr: label);
370}
371
372static int
373do_test (void)
374{
375 struct resolv_test *obj = resolv_test_start
376 ((struct resolv_redirect_config)
377 {
378 .response_callback = response
379 });
380
381 for (int count = 0; count <= 3; ++count)
382 for (int bits = 0; bits <= 1 << (count * 2); ++bits)
383 {
384 if (count > 0 && bits == count)
385 /* The last bits value is only checked if count == 0. */
386 continue;
387
388 for (enum test_mode mode = 0; mode < test_mode_num; ++mode)
389 {
390 check_forward (AF_INET, bits, count, mode);
391 if (mode != byname)
392 check_forward (AF_INET6, bits, count, mode);
393 if (mode == gai || mode == gai_canon)
394 check_forward (AF_UNSPEC, bits, count, mode);
395 }
396
397 check_reverse (AF_INET, bits, count);
398 check_reverse (AF_INET6, bits, count);
399 }
400
401 resolv_test_end (obj);
402
403 return 0;
404}
405
406#include <support/test-driver.c>
407

source code of glibc/resolv/tst-resolv-invalid-cname.c