xref: /freebsd/lib/libc/tests/resolv/resolv_test.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
1 /*	$NetBSD: resolv.c,v 1.6 2004/05/23 16:59:11 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2004 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Christos Zoulas.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 /* $FreeBSD$ */
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: resolv.c,v 1.6 2004/05/23 16:59:11 christos Exp $");
34 
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <assert.h>
38 #include <errno.h>
39 #include <pthread.h>
40 #include <stdio.h>
41 #include <stdatomic.h>
42 #include <netdb.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <stringlist.h>
47 
48 #include <atf-c.h>
49 
50 #define NTHREADS	10
51 #define NHOSTS		100
52 #define WS		" \t\n\r"
53 
54 enum method {
55 	METHOD_GETADDRINFO,
56 	METHOD_GETHOSTBY,
57 	METHOD_GETIPNODEBY
58 };
59 
60 static StringList *hosts = NULL;
61 static _Atomic(int) *ask = NULL;
62 static _Atomic(int) *got = NULL;
63 static bool debug_output = 0;
64 
65 static void load(const char *);
66 static void resolvone(long, int, enum method);
67 static void *resolvloop(void *);
68 static pthread_t run(int, enum method, long);
69 
70 #define	DBG(...) do {					\
71 	if (debug_output)				\
72 		dprintf(STDOUT_FILENO, __VA_ARGS__);	\
73 	} while (0)
74 
75 static void
76 load(const char *fname)
77 {
78 	FILE *fp;
79 	size_t linecap;
80 	char *line;
81 
82 	fp = fopen(fname, "r");
83 	ATF_REQUIRE(fp != NULL);
84 	line = NULL;
85 	linecap = 0;
86 	while (getline(&line, &linecap, fp) >= 0) {
87 		char *ptr;
88 
89 		for (ptr = strtok(line, WS); ptr; ptr = strtok(NULL, WS)) {
90 			if (ptr[0] == '#')
91 				break;
92 			sl_add(hosts, strdup(ptr));
93 		}
94 	}
95 	free(line);
96 
97 	(void)fclose(fp);
98 }
99 
100 static int
101 resolv_getaddrinfo(long threadnum, char *host, int port, const char **errstr)
102 {
103 	char portstr[6], hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
104 	struct addrinfo hints, *res;
105 	int error;
106 
107 	snprintf(portstr, sizeof(portstr), "%d", port);
108 	memset(&hints, 0, sizeof(hints));
109 	hints.ai_family = AF_UNSPEC;
110 	hints.ai_flags = AI_PASSIVE;
111 	hints.ai_socktype = SOCK_STREAM;
112 	error = getaddrinfo(host, portstr, &hints, &res);
113 	if (error == 0) {
114 		DBG("T%ld: host %s ok\n", threadnum, host);
115 		memset(hbuf, 0, sizeof(hbuf));
116 		memset(pbuf, 0, sizeof(pbuf));
117 		getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
118 		    pbuf, sizeof(pbuf), 0);
119 		DBG("T%ld: reverse %s %s\n", threadnum, hbuf, pbuf);
120 		freeaddrinfo(res);
121 	} else {
122 		*errstr = gai_strerror(error);
123 		DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr);
124 	}
125 	return error;
126 }
127 
128 static int
129 resolv_gethostby(long threadnum, char *host, const char **errstr)
130 {
131 	char buf[1024];
132 	struct hostent *hp, *hp2;
133 
134 	hp = gethostbyname(host);
135 	if (hp) {
136 		DBG("T%ld: host %s ok\n", threadnum, host);
137 		memcpy(buf, hp->h_addr, hp->h_length);
138 		hp2 = gethostbyaddr(buf, hp->h_length, hp->h_addrtype);
139 		if (hp2) {
140 			DBG("T%ld: reverse %s\n", threadnum, hp2->h_name);
141 		}
142 	} else {
143 		*errstr = hstrerror(h_errno);
144 		DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr);
145 	}
146 	return hp ? 0 : h_errno;
147 }
148 
149 static int
150 resolv_getipnodeby(long threadnum, char *host, const char **errstr)
151 {
152 	char buf[1024];
153 	struct hostent *hp, *hp2;
154 	int error = 0;
155 
156 	hp = getipnodebyname(host, AF_INET, 0, &error);
157 	if (hp) {
158 		DBG("T%ld: host %s ok\n", threadnum, host);
159 		memcpy(buf, hp->h_addr, hp->h_length);
160 		hp2 = getipnodebyaddr(buf, hp->h_length, hp->h_addrtype,
161 		    &error);
162 		if (hp2) {
163 			DBG("T%ld: reverse %s\n", threadnum, hp2->h_name);
164 			freehostent(hp2);
165 		}
166 		freehostent(hp);
167 	} else {
168 		*errstr = hstrerror(error);
169 		DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr);
170 	}
171 	return hp ? 0 : error;
172 }
173 
174 static void
175 resolvone(long threadnum, int n, enum method method)
176 {
177 	const char* errstr = NULL;
178 	size_t i = (random() & 0x0fffffff) % hosts->sl_cur;
179 	char *host = hosts->sl_str[i];
180 	int error;
181 
182 	DBG("T%ld: %d resolving %s %zd\n", threadnum, n, host, i);
183 	switch (method) {
184 	case METHOD_GETADDRINFO:
185 		error = resolv_getaddrinfo(threadnum, host, i, &errstr);
186 		break;
187 	case METHOD_GETHOSTBY:
188 		error = resolv_gethostby(threadnum, host, &errstr);
189 		break;
190 	case METHOD_GETIPNODEBY:
191 		error = resolv_getipnodeby(threadnum, host, &errstr);
192 		break;
193 	default:
194 		/* UNREACHABLE */
195 		/* XXX Needs an __assert_unreachable() for userland. */
196 		assert(0 && "Unreachable segment reached");
197 		abort();
198 		break;
199 	}
200 	atomic_fetch_add_explicit(&ask[i], 1, memory_order_relaxed);
201 	if (error == 0)
202 		atomic_fetch_add_explicit(&got[i], 1, memory_order_relaxed);
203 	else if (got[i] != 0)
204 		fprintf(stderr,
205 		    "T%ld ERROR after previous success for %s: %d (%s)\n",
206 		    threadnum, hosts->sl_str[i], error, errstr);
207 }
208 
209 struct resolvloop_args {
210 	int nhosts;
211 	enum method method;
212 	long threadnum;
213 };
214 
215 static void *
216 resolvloop(void *p)
217 {
218 	struct resolvloop_args *args = p;
219 	int nhosts = args->nhosts;
220 
221 	if (nhosts == 0) {
222 		free(args);
223 		return NULL;
224 	}
225 
226 	do {
227 		resolvone(args->threadnum, nhosts, args->method);
228 	} while (--nhosts);
229 	free(args);
230 	return (void *)(uintptr_t)nhosts;
231 }
232 
233 static pthread_t
234 run(int nhosts, enum method method, long i)
235 {
236 	pthread_t t;
237 	int rc;
238 	struct resolvloop_args *args;
239 
240 	/* Created thread is responsible for free(). */
241 	args = malloc(sizeof(*args));
242 	ATF_REQUIRE(args != NULL);
243 
244 	args->nhosts = nhosts;
245 	args->method = method;
246 	args->threadnum = i + 1;
247 	rc = pthread_create(&t, NULL, resolvloop, args);
248 	ATF_REQUIRE_MSG(rc == 0, "pthread_create failed: %s", strerror(rc));
249 	return t;
250 }
251 
252 static int
253 run_tests(const char *hostlist_file, enum method method)
254 {
255 	size_t nthreads = NTHREADS;
256 	pthread_t *threads;
257 	size_t nhosts = NHOSTS;
258 	size_t i;
259 	int c;
260 	hosts = sl_init();
261 
262 	srandom(1234);
263 	debug_output = getenv("DEBUG_OUTPUT") != NULL;
264 
265 	load(hostlist_file);
266 
267 	ATF_REQUIRE_MSG(0 < hosts->sl_cur, "0 hosts in %s", hostlist_file);
268 
269 	ask = calloc(hosts->sl_cur, sizeof(int));
270 	ATF_REQUIRE(ask != NULL);
271 
272 	got = calloc(hosts->sl_cur, sizeof(int));
273 	ATF_REQUIRE(got != NULL);
274 
275 	threads = calloc(nthreads, sizeof(pthread_t));
276 	ATF_REQUIRE(threads != NULL);
277 
278 	for (i = 0; i < nthreads; i++) {
279 		threads[i] = run(nhosts, method, i);
280 	}
281 	/* Wait for all threads to join and check that they checked all hosts */
282 	for (i = 0; i < nthreads; i++) {
283 		size_t remaining;
284 
285 		remaining = (uintptr_t)pthread_join(threads[i], NULL);
286 		ATF_CHECK_EQ_MSG(0, remaining,
287 		    "Thread %zd still had %zd hosts to check!", i, remaining);
288 	}
289 
290 	c = 0;
291 	for (i = 0; i < hosts->sl_cur; i++) {
292 		if (got[i] != 0) {
293 			ATF_CHECK_EQ_MSG(ask[i], got[i],
294 			    "Error: host %s ask %d got %d", hosts->sl_str[i],
295 			    ask[i], got[i]);
296 			c += ask[i] != got[i];
297 		}
298 	}
299 	free(threads);
300 	free(ask);
301 	free(got);
302 	sl_free(hosts, 1);
303 	return c;
304 }
305 
306 #define	HOSTLIST_FILE	"mach"
307 
308 #define	RUN_TESTS(tc, method) \
309 do {									\
310 	char *_hostlist_file;						\
311 	ATF_REQUIRE(0 < asprintf(&_hostlist_file, "%s/%s",		\
312 	    atf_tc_get_config_var(tc, "srcdir"), HOSTLIST_FILE));	\
313 	ATF_REQUIRE(run_tests(_hostlist_file, method) == 0);		\
314 } while(0)
315 
316 ATF_TC(getaddrinfo_test);
317 ATF_TC_HEAD(getaddrinfo_test, tc) {
318 	atf_tc_set_md_var(tc, "timeout", "1200");
319 }
320 ATF_TC_BODY(getaddrinfo_test, tc)
321 {
322 
323 	RUN_TESTS(tc, METHOD_GETADDRINFO);
324 }
325 
326 ATF_TC(gethostby_test);
327 ATF_TC_HEAD(gethostby_test, tc) {
328 	atf_tc_set_md_var(tc, "timeout", "1200");
329 }
330 ATF_TC_BODY(gethostby_test, tc)
331 {
332 
333 	RUN_TESTS(tc, METHOD_GETHOSTBY);
334 }
335 
336 ATF_TC(getipnodeby_test);
337 ATF_TC_HEAD(getipnodeby_test, tc) {
338 
339 	atf_tc_set_md_var(tc, "timeout", "1200");
340 }
341 ATF_TC_BODY(getipnodeby_test, tc)
342 {
343 
344 	RUN_TESTS(tc, METHOD_GETIPNODEBY);
345 }
346 
347 ATF_TP_ADD_TCS(tp)
348 {
349 
350 	ATF_TP_ADD_TC(tp, getaddrinfo_test);
351 	ATF_TP_ADD_TC(tp, gethostby_test);
352 	ATF_TP_ADD_TC(tp, getipnodeby_test);
353 
354 	return (atf_no_error());
355 }
356