1/* Tests for __inet6_scopeid_pton and IPv6 scopes in getaddrinfo.
2 Copyright (C) 2016-2022 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 <inttypes.h>
21#include <net-internal.h>
22#include <net/if.h>
23#include <netdb.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <support/check.h>
28#include <support/support.h>
29#include <support/test-driver.h>
30
31/* An interface which is known to the system. */
32static const char *interface_name;
33static uint32_t interface_index;
34
35/* Initiale the variables above. */
36static void
37setup_interface (void)
38{
39 struct if_nameindex *list = if_nameindex ();
40 if (list != NULL && list[0].if_index != 0 && list[0].if_name[0] != '\0')
41 {
42 interface_name = list[0].if_name;
43 interface_index = list[0].if_index;
44 }
45}
46
47/* Convert ADDRESS to struct in6_addr. */
48static struct in6_addr
49from_string (const char *address)
50{
51 struct in6_addr addr;
52 if (inet_pton (AF_INET6, address, &addr) != 1)
53 FAIL_EXIT1 ("inet_pton (\"%s\")", address);
54 return addr;
55}
56
57/* Invoke getaddrinfo to parse ADDRESS%SCOPE. Return true if
58 getaddrinfo was successful. */
59static bool
60call_gai (int family, const char *address, const char *scope,
61 struct sockaddr_in6 *result)
62{
63 struct addrinfo hints =
64 {
65 .ai_family = family,
66 .ai_flags = AI_NUMERICHOST,
67 .ai_socktype = SOCK_DGRAM,
68 .ai_protocol = IPPROTO_UDP,
69 };
70 char *fulladdr = xasprintf (format: "%s%%%s", address, scope);
71 struct addrinfo *ai = NULL;
72 int ret = getaddrinfo (fulladdr, NULL, &hints, &ai);
73 if (ret == EAI_ADDRFAMILY || ret == EAI_NONAME)
74 {
75 if (test_verbose > 0)
76 printf (format: "info: getaddrinfo (\"%s\"): %s (%d)\n",
77 fulladdr, gai_strerror (ret), ret);
78 free (ptr: fulladdr);
79 return false;
80 }
81 if (ret != 0)
82 FAIL_EXIT1 ("getaddrinfo (\"%s\"): %s (%d)\n",
83 fulladdr, gai_strerror (ret), ret);
84 TEST_VERIFY_EXIT (ai != NULL);
85 TEST_VERIFY_EXIT (ai->ai_addrlen == sizeof (*result));
86 TEST_VERIFY (ai->ai_family == AF_INET6);
87 TEST_VERIFY (ai->ai_next == NULL);
88 memcpy (result, ai->ai_addr, sizeof (*result));
89 free (ptr: fulladdr);
90 freeaddrinfo (ai);
91 return true;
92}
93
94/* Verify that a successful call to getaddrinfo returned the expected
95 scope data. */
96static void
97check_ai (const char *what, const char *addr_string, const char *scope_string,
98 const struct sockaddr_in6 *sa,
99 const struct in6_addr *addr, uint32_t scope)
100{
101 if (memcmp (addr, &sa->sin6_addr, sizeof (*addr)) != 0)
102 {
103 support_record_failure ();
104 printf (format: "error: getaddrinfo %s address mismatch for %s%%%s\n",
105 what, addr_string, scope_string);
106 }
107 if (sa->sin6_scope_id != scope)
108 {
109 support_record_failure ();
110 printf (format: "error: getaddrinfo %s scope mismatch for %s%%%s\n"
111 " expected: %" PRIu32 "\n"
112 " actual: %" PRIu32 "\n",
113 what, addr_string, scope_string, scope, sa->sin6_scope_id);
114 }
115}
116
117/* Check a single address were we expected a failure. */
118static void
119expect_failure (const char *address, const char *scope)
120{
121 if (test_verbose > 0)
122 printf (format: "info: expecting failure for %s%%%s\n", address, scope);
123 struct in6_addr addr = from_string (address);
124 uint32_t result = 1234;
125 if (__inet6_scopeid_pton (&addr, scope, &result) == 0)
126 {
127 support_record_failure ();
128 printf (format: "error: unexpected success for %s%%%s\n",
129 address, scope);
130 }
131 if (result != 1234)
132 {
133 support_record_failure ();
134 printf (format: "error: unexpected result update for %s%%%s\n",
135 address, scope);
136 }
137
138 struct sockaddr_in6 sa;
139 if (call_gai (AF_UNSPEC, address, scope, result: &sa))
140 {
141 support_record_failure ();
142 printf (format: "error: unexpected getaddrinfo success for %s%%%s (AF_UNSPEC)\n",
143 address, scope);
144 }
145 if (call_gai (AF_INET6, address, scope, result: &sa))
146 {
147 support_record_failure ();
148 printf (format: "error: unexpected getaddrinfo success for %s%%%s (AF_INET6)\n",
149 address, scope);
150 }
151}
152
153/* Check a single address were we expected a success. */
154static void
155expect_success (const char *address, const char *scope, uint32_t expected)
156{
157 if (test_verbose > 0)
158 printf (format: "info: expecting success for %s%%%s\n", address, scope);
159 struct in6_addr addr = from_string (address);
160 uint32_t actual = expected + 1;
161 if (__inet6_scopeid_pton (&addr, scope, &actual) != 0)
162 {
163 support_record_failure ();
164 printf (format: "error: unexpected failure for %s%%%s\n",
165 address, scope);
166 }
167 if (actual != expected)
168 {
169 support_record_failure ();
170 printf (format: "error: unexpected result for for %s%%%s\n",
171 address, scope);
172 printf (format: " expected: %" PRIu32 "\n", expected);
173 printf (format: " actual: %" PRIu32 "\n", actual);
174 }
175
176 struct sockaddr_in6 sa;
177 memset (&sa, 0xc0, sizeof (sa));
178 if (call_gai (AF_UNSPEC, address, scope, result: &sa))
179 check_ai (what: "AF_UNSPEC", addr_string: address, scope_string: scope, sa: &sa, addr: &addr, scope: expected);
180 else
181 {
182 support_record_failure ();
183 printf (format: "error: unexpected getaddrinfo failure for %s%%%s (AF_UNSPEC)\n",
184 address, scope);
185 }
186 memset (&sa, 0xc0, sizeof (sa));
187 if (call_gai (AF_INET6, address, scope, result: &sa))
188 check_ai (what: "AF_INET6", addr_string: address, scope_string: scope, sa: &sa, addr: &addr, scope: expected);
189 else
190 {
191 support_record_failure ();
192 printf (format: "error: unexpected getaddrinfo failure for %s%%%s (AF_INET6)\n",
193 address, scope);
194 }
195}
196
197static int
198do_test (void)
199{
200 setup_interface ();
201
202 static const char *test_addresses[]
203 = { "::", "::1", "2001:db8::1", NULL };
204 for (int i = 0; test_addresses[i] != NULL; ++i)
205 {
206 expect_success (address: test_addresses[i], scope: "0", expected: 0);
207 expect_success (address: test_addresses[i], scope: "5555", expected: 5555);
208
209 expect_failure (address: test_addresses[i], scope: "");
210 expect_failure (address: test_addresses[i], scope: "-1");
211 expect_failure (address: test_addresses[i], scope: "-99");
212 expect_failure (address: test_addresses[i], scope: "037777777777");
213 expect_failure (address: test_addresses[i], scope: "0x");
214 expect_failure (address: test_addresses[i], scope: "0x1");
215 }
216
217 if (interface_name != NULL)
218 {
219 expect_success (address: "fe80::1", scope: interface_name, expected: interface_index);
220 expect_success (address: "ff02::1", scope: interface_name, expected: interface_index);
221 expect_success (address: "ff01::1", scope: interface_name, expected: interface_index);
222 expect_failure (address: "::", scope: interface_name);
223 expect_failure (address: "::1", scope: interface_name);
224 expect_failure (address: "2001:db8::1", scope: interface_name);
225 }
226
227 return 0;
228}
229
230#include <support/test-driver.c>
231

source code of glibc/inet/tst-inet6_scopeid_pton.c