• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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