1 /* Tests for __inet6_scopeid_pton and IPv6 scopes in getaddrinfo.
2    Copyright (C) 2016-2021 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.  */
32 static const char *interface_name;
33 static uint32_t interface_index;
34 
35 /* Initiale the variables above.  */
36 static void
setup_interface(void)37 setup_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.  */
48 static struct in6_addr
from_string(const char * address)49 from_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.  */
59 static bool
call_gai(int family,const char * address,const char * scope,struct sockaddr_in6 * result)60 call_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 ("%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 ("info: getaddrinfo (\"%s\"): %s (%d)\n",
77                 fulladdr, gai_strerror (ret), ret);
78       free (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 (fulladdr);
90   freeaddrinfo (ai);
91   return true;
92 }
93 
94 /* Verify that a successful call to getaddrinfo returned the expected
95    scope data.  */
96 static void
check_ai(const char * what,const char * addr_string,const char * scope_string,const struct sockaddr_in6 * sa,const struct in6_addr * addr,uint32_t scope)97 check_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 ("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 ("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.  */
118 static void
expect_failure(const char * address,const char * scope)119 expect_failure (const char *address, const char *scope)
120 {
121   if (test_verbose > 0)
122     printf ("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 ("error: unexpected success for %s%%%s\n",
129               address, scope);
130     }
131   if (result != 1234)
132     {
133       support_record_failure ();
134       printf ("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, &sa))
140     {
141       support_record_failure ();
142       printf ("error: unexpected getaddrinfo success for %s%%%s (AF_UNSPEC)\n",
143               address, scope);
144     }
145   if (call_gai (AF_INET6, address, scope, &sa))
146     {
147       support_record_failure ();
148       printf ("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.  */
154 static void
expect_success(const char * address,const char * scope,uint32_t expected)155 expect_success (const char *address, const char *scope, uint32_t expected)
156 {
157   if (test_verbose > 0)
158     printf ("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 ("error: unexpected failure for %s%%%s\n",
165               address, scope);
166     }
167   if (actual != expected)
168     {
169       support_record_failure ();
170       printf ("error: unexpected result for for %s%%%s\n",
171               address, scope);
172       printf ("  expected: %" PRIu32 "\n", expected);
173       printf ("  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, &sa))
179     check_ai ("AF_UNSPEC", address, scope, &sa, &addr, expected);
180   else
181     {
182       support_record_failure ();
183       printf ("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, &sa))
188     check_ai ("AF_INET6", address, scope, &sa, &addr, expected);
189   else
190     {
191       support_record_failure ();
192       printf ("error: unexpected getaddrinfo failure for %s%%%s (AF_INET6)\n",
193               address, scope);
194     }
195 }
196 
197 static int
do_test(void)198 do_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 (test_addresses[i], "0", 0);
207       expect_success (test_addresses[i], "5555", 5555);
208 
209       expect_failure (test_addresses[i], "");
210       expect_failure (test_addresses[i], "-1");
211       expect_failure (test_addresses[i], "-99");
212       expect_failure (test_addresses[i], "037777777777");
213       expect_failure (test_addresses[i], "0x");
214       expect_failure (test_addresses[i], "0x1");
215     }
216 
217   if (interface_name != NULL)
218     {
219       expect_success ("fe80::1", interface_name, interface_index);
220       expect_success ("ff02::1", interface_name, interface_index);
221       expect_success ("ff01::1", interface_name, interface_index);
222       expect_failure ("::", interface_name);
223       expect_failure ("::1", interface_name);
224       expect_failure ("2001:db8::1", interface_name);
225     }
226 
227   return 0;
228 }
229 
230 #include <support/test-driver.c>
231