1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2021, BELLSOFT. All rights reserved.
4 * Copyright (c) 2015 Fujitsu Ltd.
5 * Copyright (c) International Business Machines Corp., 2001
6 *
7 * Author: David L Stevens
8 */
9
10 /*\
11 * [Description]
12 *
13 * Basic getaddrinfo() tests.
14 *
15 * The test adds LTP specific addresses and names to /etc/hosts to avoid
16 * DNS, hostname setup issues and conflicts with existing configuration.
17 */
18
19 #include <unistd.h>
20 #include <errno.h>
21 #include <stdlib.h>
22
23 #include <sys/socket.h>
24 #include <netdb.h>
25 #include <arpa/inet.h>
26 #include <sys/param.h>
27
28 #include "tst_safe_stdio.h"
29 #include "tst_test.h"
30 #include "tst_safe_net.h"
31
32 #ifndef AI_V4MAPPED
33 # define AI_V4MAPPED 0x0008 /* IPv4 mapped addresses are acceptable. */
34 #endif
35
36 static const char *const host_file = "/etc/hosts";
37 static const char *hostname;
38 static const char *shortname;
39 static sa_family_t family;
40 static int host_file_changed;
41
verify_res(struct addrinfo * res,int sock_type,in_port_t servnum,int (* test_cb)(struct addrinfo *))42 static void verify_res(struct addrinfo *res, int sock_type, in_port_t servnum,
43 int (*test_cb)(struct addrinfo *))
44 {
45 sa_family_t sin_family = 0;
46 in_port_t sin_port = 0;
47 struct addrinfo *p = res;
48 int got_tcp = 0;
49 int got_udp = 0;
50 int ret = 0;
51
52 size_t exp_addrlen = (family == AF_INET) ? sizeof(struct sockaddr_in) :
53 sizeof(struct sockaddr_in6);
54
55 for (; p; p = p->ai_next) {
56 ret |= p->ai_family != family;
57 ret |= p->ai_addrlen != exp_addrlen;
58 ret |= p->ai_addr == 0;
59 got_tcp |= p->ai_socktype == SOCK_STREAM;
60 got_udp |= p->ai_socktype == SOCK_DGRAM;
61
62 if (p->ai_addr) {
63
64 if (test_cb)
65 ret |= test_cb(p);
66
67 if (p->ai_family == AF_INET) {
68 struct sockaddr_in *psin;
69
70 psin = (struct sockaddr_in *)p->ai_addr;
71 sin_family = psin->sin_family;
72 sin_port = psin->sin_port;
73 } else {
74 struct sockaddr_in6 *psin6;
75
76 psin6 = (struct sockaddr_in6 *)p->ai_addr;
77 sin_family = psin6->sin6_family;
78 sin_port = psin6->sin6_port;
79 }
80
81 ret |= sin_family != family;
82 ret |= sin_port != htons(servnum);
83 }
84
85 if (ret)
86 break;
87 }
88
89 if (!sock_type && (!got_tcp || !got_udp)) {
90 tst_brk(TFAIL, "socktype 0,%d TCP %d UDP %d",
91 htons(sin_port), got_tcp, got_udp);
92 }
93
94 if (ret) {
95 tst_brk(TFAIL, "family %d alen %d sin family %d port %d",
96 p->ai_family, p->ai_addrlen, sin_family,
97 htons(sin_port));
98 }
99 }
100
print_test_family(const char * name)101 static void print_test_family(const char *name)
102 {
103 tst_res(TINFO, "test %s: %s", (family == AF_INET) ? "IPv4" : "IPv6",
104 name);
105 }
106
check_addrinfo(int safe,const char * name,const char * host,in_port_t servnum,const char * service,int flags,int type,int proto,int (* test_cb)(struct addrinfo *))107 static void check_addrinfo(int safe, const char *name, const char *host,
108 in_port_t servnum, const char *service,
109 int flags, int type, int proto,
110 int (*test_cb)(struct addrinfo *))
111 {
112 struct addrinfo *res = NULL;
113 struct addrinfo hints;
114
115 print_test_family(name);
116
117 memset(&hints, 0, sizeof(hints));
118 hints.ai_family = family;
119 hints.ai_flags = flags;
120 hints.ai_socktype = type;
121 hints.ai_protocol = proto;
122
123 if (safe)
124 SAFE_GETADDRINFO(host, service, &hints, &res);
125 else
126 TEST(getaddrinfo(host, service, &hints, &res));
127
128 if (res) {
129 verify_res(res, type, servnum, test_cb);
130 freeaddrinfo(res);
131 tst_res(TPASS, "%s", name);
132 }
133 }
134
check_addrinfo_name(const char * name)135 static void check_addrinfo_name(const char *name)
136 {
137 struct addrinfo *p, *res;
138 struct addrinfo hints;
139
140 print_test_family(name);
141
142 memset(&hints, 0, sizeof(hints));
143 hints.ai_family = family;
144 hints.ai_flags = AI_CANONNAME;
145
146 SAFE_GETADDRINFO(shortname, 0, &hints, &res);
147
148 for (p = res; p; p = p->ai_next) {
149 if (p->ai_canonname)
150 break;
151 }
152 if (!p)
153 tst_brk(TFAIL, "%s: no entries with canonical name set", name);
154 else if (strcasecmp(hostname, p->ai_canonname))
155 tst_brk(TFAIL, "%s: ai_canonname '%s' doesn't match hostname '%s'",
156 name, p->ai_canonname, hostname);
157
158 tst_res(TPASS, "%s: ai_canonname '%s'", name, p->ai_canonname);
159 freeaddrinfo(res);
160 }
161
check_addrinfo_badflags(const char * name)162 static void check_addrinfo_badflags(const char *name)
163 {
164 if (TST_RET == EAI_BADFLAGS) {
165 tst_res(TPASS, "%s returns %ld '%s'", name,
166 TST_RET, gai_strerror(TST_RET));
167 } else if (TST_RET) {
168 tst_brk(TFAIL, "%s returns %ld '%s'", name,
169 TST_RET, gai_strerror(TST_RET));
170 }
171 }
172
test_loopback(struct addrinfo * p)173 static int test_loopback(struct addrinfo *p)
174 {
175 /* hostname not set; addr should be loopback */
176 if (family == AF_INET) {
177 struct sockaddr_in *psin = (struct sockaddr_in *)p->ai_addr;
178
179 return psin->sin_addr.s_addr != htonl(INADDR_LOOPBACK);
180 } else {
181 struct sockaddr_in6 *psin6 = (struct sockaddr_in6 *)p->ai_addr;
182
183 return memcmp(&psin6->sin6_addr, &in6addr_loopback,
184 sizeof(struct in6_addr)) != 0;
185 }
186 }
187
test_passive(struct addrinfo * p)188 static int test_passive(struct addrinfo *p)
189 {
190 if (family == AF_INET) {
191 struct sockaddr_in *psin = (struct sockaddr_in *)p->ai_addr;
192
193 return psin->sin_addr.s_addr == 0;
194 } else {
195 struct sockaddr_in6 *psin6 = (struct sockaddr_in6 *)p->ai_addr;
196
197 return memcmp(&psin6->sin6_addr, &in6addr_any,
198 sizeof(struct in6_addr)) == 0;
199 }
200 }
201
test_passive_no_host(struct addrinfo * p)202 static int test_passive_no_host(struct addrinfo *p)
203 {
204 if (family == AF_INET) {
205 struct sockaddr_in *psin = (struct sockaddr_in *)p->ai_addr;
206
207 return psin->sin_addr.s_addr != 0;
208 } else {
209 struct sockaddr_in6 *psin6 = (struct sockaddr_in6 *)p->ai_addr;
210
211 return memcmp(&psin6->sin6_addr, &in6addr_any,
212 sizeof(struct in6_addr));
213 }
214 }
215
gaiv(void)216 static void gaiv(void)
217 {
218 check_addrinfo(1, "basic lookup", hostname, 0, NULL, 0, 0, 0, NULL);
219 check_addrinfo_name("canonical name");
220
221 /*
222 * These are hard-coded for echo/7 to avoid using getservbyname(),
223 * since it isn't thread-safe and these tests may be re-used
224 * multithreaded. Sigh.
225 */
226 check_addrinfo(1, "host+service", hostname, 7, "echo", 0, 0, 0, NULL);
227
228 check_addrinfo(1, "host+service, AI_PASSIVE", hostname, 9462, "9462",
229 AI_PASSIVE, SOCK_STREAM, 0, test_passive);
230
231 check_addrinfo(0, "host+service, AI_NUMERICHOST", hostname, 7, "echo",
232 AI_NUMERICHOST, SOCK_STREAM, 0, NULL);
233 if (TST_RET != EAI_NONAME)
234 tst_brk(TFAIL, "AI_NUMERICHOST: ret %ld exp %d (EAI_NONAME)",
235 TST_RET, EAI_NONAME);
236 tst_res(TPASS, "AI_NUMERICHOST: expected %ld (EAI_NONAME)", TST_RET);
237
238 check_addrinfo(1, "0+service, AI_PASSIVE", NULL, 9462, "9462",
239 AI_PASSIVE, SOCK_STREAM, 0, test_passive_no_host);
240
241 check_addrinfo(0, "0+service", NULL, 9462, "9462",
242 0, SOCK_STREAM, 0, test_loopback);
243 check_addrinfo_badflags("0+service ('', '9462')");
244
245 #ifdef AI_NUMERICSERV
246 check_addrinfo(0, "host+service, AI_NUMERICSERV", hostname, 7, "echo",
247 AI_NUMERICSERV, 0, 0, NULL);
248 if (TST_RET != EAI_NONAME)
249 tst_brk(TFAIL, "AI_NUMERICSERV: returns %ld '%s', expected %d (EAI_NONAME)",
250 TST_RET, gai_strerror(TST_RET), EAI_NONAME);
251 tst_res(TPASS, "AI_NUMERICSERV: returns %ld (EAI_NONAME)", TST_RET);
252 #else
253 tst_res(TCONF, "AI_NUMERICSERV: flag not implemented");
254 #endif
255
256 check_addrinfo(0, "SOCK_STREAM/IPPROTO_UDP", NULL, 0, NULL, 0,
257 SOCK_STREAM, IPPROTO_UDP, NULL);
258 if (!TST_RET)
259 tst_brk(TFAIL, "SOCK_STREAM/IPPROTO_UDP: unexpected pass");
260 tst_res(TPASS, "SOCK_STREAM/IPPROTO_UDP: failed as expected");
261
262 check_addrinfo(0, "socktype 0,513", NULL, 513, "513", 0, 0, 0, NULL);
263 check_addrinfo_badflags("socktype 0,513");
264
265 check_addrinfo(1, "AI_V4MAPPED", NULL, 513, "513",
266 AI_V4MAPPED, 0, 0, NULL);
267 }
268
269 static struct tcase {
270 sa_family_t family;
271 const char *const addr;
272 const char *const name;
273 const char *const alias;
274 } tcases[] = {
275 { AF_INET, "127.0.127.1", "getaddrinfo01.ltp", "getaddrinfo01-ipv4" },
276 { AF_INET6, "::127", "getaddrinfo01.ipv6.ltp", "getaddrinfo01-ipv6" }
277 };
278
setup(void)279 static void setup(void)
280 {
281 unsigned int i;
282 int fd;
283
284 if (access(host_file, W_OK))
285 tst_brk(TCONF | TERRNO, "%s file not available", host_file);
286
287 SAFE_CP(host_file, "hosts");
288
289 host_file_changed = 1;
290 fd = SAFE_OPEN(host_file, O_WRONLY|O_APPEND);
291
292 for (i = 0; i < ARRAY_SIZE(tcases); ++i) {
293 char *entry;
294
295 SAFE_ASPRINTF(&entry, "%s %s %s\n",
296 tcases[i].addr, tcases[i].name, tcases[i].alias);
297 SAFE_WRITE(0, fd, entry, strlen(entry));
298 free(entry);
299 }
300 SAFE_CLOSE(fd);
301 }
302
cleanup(void)303 static void cleanup(void)
304 {
305 if (host_file_changed)
306 SAFE_CP("hosts", host_file);
307 }
308
do_test(unsigned int i)309 static void do_test(unsigned int i)
310 {
311 family = tcases[i].family;
312 hostname = tcases[i].name;
313 shortname = tcases[i].alias;
314 gaiv();
315 }
316
317 static struct tst_test test = {
318 .needs_root = 1,
319 .needs_tmpdir = 1,
320 .setup = setup,
321 .cleanup = cleanup,
322 .tcnt = ARRAY_SIZE(tcases),
323 .test = do_test,
324 };
325