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
36static const char *const host_file = "/etc/hosts";
37static const char *hostname;
38static const char *shortname;
39static sa_family_t family;
40static int host_file_changed;
41
42static 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
101static void print_test_family(const char *name)
102{
103	tst_res(TINFO, "test %s: %s", (family == AF_INET) ? "IPv4" : "IPv6",
104		name);
105}
106
107static 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
135static 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
162static 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
173static 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
188static 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
202static 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
216static 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
269static 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
279static 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(SAFE_WRITE_ANY, fd, entry, strlen(entry));
298		free(entry);
299	}
300	SAFE_CLOSE(fd);
301}
302
303static void cleanup(void)
304{
305	if (host_file_changed)
306		SAFE_CP("hosts", host_file);
307}
308
309static 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
317static 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