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.
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.
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.
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/>. */
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>
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.
36 0: No special leading label.
37 1: Starting with "*.".
38 2: Starting with "-x.".
39 3: Starting with "star.*.".
41 The first CNAME in the response using the two least significant
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
48 COUNT.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.
50 where BITS and COUNT are hexadecimal. */
53 response (const struct resolv_response_context
*ctx
,
54 struct resolv_response_builder
*b
,
55 const char *qname
, uint16_t qclass
, uint16_t qtype
)
57 TEST_COMPARE (qclass
, C_IN
);
59 /* The only other query type besides A is PTR. */
60 if (qtype
!= T_A
&& qtype
!= T_AAAA
)
61 TEST_COMPARE (qtype
, T_PTR
);
63 unsigned int bits
, bits1
, count
;
65 if (sscanf (qname
, "bits%u.count%u.%ms", &bits
, &count
, &tail
) == 3)
66 TEST_COMPARE_STRING (tail
, "example");
67 else if (strstr (qname
, "in-addr.arpa") != NULL
68 && sscanf (qname
, "%u.%u.%ms", &bits
, &count
, &tail
) == 3)
69 TEST_COMPARE_STRING (tail
, "168.192.in-addr.arpa");
70 else if (sscanf (qname
, "%x.%x.%x.%ms", &bits
, &bits1
, &count
, &tail
) == 4)
72 TEST_COMPARE_STRING (tail
, "\
73 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");
77 FAIL_EXIT1 ("invalid QNAME: %s\n", qname
);
80 struct resolv_response_flags flags
= {};
81 resolv_response_init (b
, flags
);
82 resolv_response_add_question (b
, qname
, qclass
, qtype
);
83 resolv_response_section (b
, ns_s_an
);
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
)
90 resolv_response_open_record (b
, previous_name
, qclass
, T_CNAME
, 60);
92 static const char bits_to_prefix
[4][8] = { "", "*.", "-x.", "star.*." };
93 char *new_name
= xasprintf ("%sunique%d.example",
94 bits_to_prefix
[bits
& 3], unique
);
96 resolv_response_add_name (b
, new_name
);
97 resolv_response_close_record (b
);
99 if (previous_name
!= qname
)
100 free (previous_name
);
101 previous_name
= new_name
;
104 /* Actual answer record. */
105 resolv_response_open_record (b
, previous_name
, qclass
, qtype
, 60);
110 char ipv4
[4] = {192, 168, count
, original_bits
};
111 resolv_response_add_data (b
, &ipv4
, sizeof (ipv4
));
118 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 resolv_response_add_data (b
, &ipv6
, sizeof (ipv6
));
127 char *name
= xasprintf ("bits%u.count%u.example",
128 original_bits
, count
);
129 resolv_response_add_name (b
, name
);
134 resolv_response_close_record (b
);
136 if (previous_name
!= qname
)
137 free (previous_name
);
140 /* Controls which name resolution function is invoked. */
143 byname
, /* gethostbyname. */
144 byname2
, /* gethostbyname2. */
145 gai
, /* getaddrinfo without AI_CANONNAME. */
146 gai_canon
, /* getaddrinfo with AI_CANONNAME. */
148 test_mode_num
/* Number of enum values. */
152 test_mode_to_string (enum test_mode mode
)
165 break; /* Report error below. */
167 FAIL_EXIT1 ("invalid test_mode: %d", mode
);
170 /* Append the name and aliases to OUT. */
172 append_names (FILE *out
, const char *qname
, int bits
, int count
,
175 /* Largest valid index which has a corresponding zero in bits
176 (meaning a syntactically valid CNAME). */
177 int last_valid_cname
= -1;
179 for (int i
= 0; i
< count
; ++i
)
180 if ((bits
& (3 << (i
* 2))) == 0)
181 last_valid_cname
= i
;
186 if (mode
== gai_canon
)
190 if (last_valid_cname
>= 0)
191 fprintf (out
, "%s: unique%d.example\n", label
, last_valid_cname
);
193 fprintf (out
, "%s: %s\n", label
, qname
);
196 if (mode
== byname
|| mode
== byname2
)
198 if (last_valid_cname
>= 0)
199 fprintf (out
, "alias: %s\n", qname
);
200 for (int i
= 0; i
< count
; ++i
)
202 if ((bits
& (3 << (i
* 2))) == 0 && i
!= last_valid_cname
)
203 fprintf (out
, "alias: unique%d.example\n", i
);
208 /* Append the address information to OUT. */
210 append_addresses (FILE *out
, int af
, int bits
, int count
, enum test_mode mode
)
212 int last
= count
* 256 + bits
;
213 if (mode
== gai
|| mode
== gai_canon
)
215 if (af
== AF_INET
|| af
== AF_UNSPEC
)
216 fprintf (out
, "address: STREAM/TCP 192.168.%d.%d 80\n", count
, bits
);
217 if (af
== AF_INET6
|| af
== AF_UNSPEC
)
220 fprintf (out
, "address: STREAM/TCP 2001:db8:: 80\n");
222 fprintf (out
, "address: STREAM/TCP 2001:db8::%x 80\n", last
);
227 TEST_VERIFY (af
!= AF_UNSPEC
);
229 fprintf (out
, "address: 192.168.%d.%d\n", count
, bits
);
233 fprintf (out
, "address: 2001:db8::\n");
235 fprintf (out
, "address: 2001:db8::%x\n", last
);
240 /* Perform one test using a forward lookup. */
242 check_forward (int af
, int bits
, int count
, enum test_mode mode
)
244 char *qname
= xasprintf ("bits%d.count%d.example", bits
, count
);
245 char *label
= xasprintf ("af=%d bits=%d count=%d mode=%s qname=%s",
246 af
, bits
, count
, test_mode_to_string (mode
), qname
);
248 struct xmemstream expected
;
249 xopen_memstream (&expected
);
250 if (mode
== gai_canon
)
251 fprintf (expected
.out
, "flags: AI_CANONNAME\n");
252 append_names (expected
.out
, qname
, bits
, count
, mode
);
253 append_addresses (expected
.out
, af
, bits
, count
, mode
);
254 xfclose_memstream (&expected
);
256 if (mode
== gai
|| mode
== gai_canon
)
259 struct addrinfo hints
=
262 .ai_socktype
= SOCK_STREAM
,
264 if (mode
== gai_canon
)
265 hints
.ai_flags
|= AI_CANONNAME
;
266 int ret
= getaddrinfo (qname
, "80", &hints
, &ai
);
267 check_addrinfo (label
, ai
, ret
, expected
.buffer
);
276 TEST_COMPARE (af
, AF_INET
);
277 e
= gethostbyname (qname
);
282 TEST_COMPARE (af
, AF_INET6
);
283 e
= gethostbyname2 (qname
, af
);
285 check_hostent (label
, e
, expected
.buffer
);
288 free (expected
.buffer
);
293 /* Perform one check using a reverse lookup. */
296 check_reverse (int af
, int bits
, int count
)
298 TEST_VERIFY (af
== AF_INET
|| af
== AF_INET6
);
300 char *label
= xasprintf ("af=%d bits=%d count=%d", af
, bits
, count
);
301 char *fqdn
= xasprintf ("bits%d.count%d.example", bits
, count
);
303 struct xmemstream expected
;
304 xopen_memstream (&expected
);
305 fprintf (expected
.out
, "name: %s\n", fqdn
);
306 append_addresses (expected
.out
, af
, bits
, count
, byname
);
307 xfclose_memstream (&expected
);
309 char addr
[16] = { 0 };
330 struct hostent
*e
= gethostbyaddr (addr
, addrlen
, af
);
331 check_hostent (label
, e
, expected
.buffer
);
333 /* getnameinfo check is different. There is no generic check_*
336 struct sockaddr_in sin
= { };
337 struct sockaddr_in6 sin6
= { };
342 sin
.sin_family
= AF_INET
;
343 memcpy (&sin
.sin_addr
, addr
, addrlen
);
344 sin
.sin_port
= htons (80);
346 salen
= sizeof (sin
);
350 sin6
.sin6_family
= AF_INET6
;
351 memcpy (&sin6
.sin6_addr
, addr
, addrlen
);
352 sin6
.sin6_port
= htons (80);
354 salen
= sizeof (sin6
);
359 int ret
= getnameinfo (sa
, salen
, host
,
360 sizeof (host
), service
, sizeof (service
),
361 NI_NAMEREQD
| NI_NUMERICSERV
);
362 TEST_COMPARE (ret
, 0);
363 TEST_COMPARE_STRING (host
, fqdn
);
364 TEST_COMPARE_STRING (service
, "80");
367 free (expected
.buffer
);
375 struct resolv_test
*obj
= resolv_test_start
376 ((struct resolv_redirect_config
)
378 .response_callback
= response
381 for (int count
= 0; count
<= 3; ++count
)
382 for (int bits
= 0; bits
<= 1 << (count
* 2); ++bits
)
384 if (count
> 0 && bits
== count
)
385 /* The last bits value is only checked if count == 0. */
388 for (enum test_mode mode
= 0; mode
< test_mode_num
; ++mode
)
390 check_forward (AF_INET
, bits
, count
, mode
);
392 check_forward (AF_INET6
, bits
, count
, mode
);
393 if (mode
== gai
|| mode
== gai_canon
)
394 check_forward (AF_UNSPEC
, bits
, count
, mode
);
397 check_reverse (AF_INET
, bits
, count
);
398 check_reverse (AF_INET6
, bits
, count
);
401 resolv_test_end (obj
);
406 #include <support/test-driver.c>