getaddrinfo_test.c revision 251818
1/*-
2 * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/tools/regression/lib/libc/nss/test-getaddr.c 251818 2013-06-16 19:35:01Z eadler $");
29
30#include <arpa/inet.h>
31#include <sys/socket.h>
32#include <sys/types.h>
33#include <netinet/in.h>
34#include <assert.h>
35#include <errno.h>
36#include <netdb.h>
37#include <resolv.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <stringlist.h>
42#include <unistd.h>
43#include "testutil.h"
44
45enum test_methods {
46	TEST_GETADDRINFO,
47	TEST_BUILD_SNAPSHOT
48};
49
50static int debug = 0;
51static struct addrinfo hints;
52static enum test_methods method = TEST_GETADDRINFO;
53
54DECLARE_TEST_DATA(addrinfo)
55DECLARE_TEST_FILE_SNAPSHOT(addrinfo)
56DECLARE_2PASS_TEST(addrinfo)
57
58static void clone_addrinfo(struct addrinfo *, struct addrinfo const *);
59static int compare_addrinfo(struct addrinfo *, struct addrinfo *, void *);
60static void dump_addrinfo(struct addrinfo *);
61static void free_addrinfo(struct addrinfo *);
62
63static void sdump_addrinfo(struct addrinfo *, char *, size_t);
64
65IMPLEMENT_TEST_DATA(addrinfo)
66IMPLEMENT_TEST_FILE_SNAPSHOT(addrinfo)
67IMPLEMENT_2PASS_TEST(addrinfo)
68
69static void
70clone_addrinfo(struct addrinfo *dest, struct addrinfo const *src)
71{
72	assert(dest != NULL);
73	assert(src != NULL);
74
75	memcpy(dest, src, sizeof(struct addrinfo));
76	if (src->ai_canonname != NULL)
77		dest->ai_canonname = strdup(src->ai_canonname);
78
79	if (src->ai_addr != NULL) {
80		dest->ai_addr = (struct sockaddr *)malloc(src->ai_addrlen);
81		assert(dest->ai_addr != NULL);
82		memcpy(dest->ai_addr, src->ai_addr, src->ai_addrlen);
83	}
84
85	if (src->ai_next != NULL) {
86		dest->ai_next = (struct addrinfo *)malloc(
87			sizeof(struct addrinfo));
88		assert(dest->ai_next != NULL);
89		clone_addrinfo(dest->ai_next, src->ai_next);
90	}
91}
92
93static int
94compare_addrinfo_(struct addrinfo *ai1, struct addrinfo *ai2)
95{
96	if ((ai1 == NULL) || (ai2 == NULL))
97		return (-1);
98
99	if ((ai1->ai_flags != ai2->ai_flags) ||
100	    (ai1->ai_family != ai2->ai_family) ||
101	    (ai1->ai_socktype != ai2->ai_socktype) ||
102	    (ai1->ai_protocol != ai2->ai_protocol) ||
103	    (ai1->ai_addrlen != ai2->ai_addrlen) ||
104	    (((ai1->ai_addr == NULL) || (ai2->ai_addr == NULL)) &&
105	    (ai1->ai_addr != ai2->ai_addr)) ||
106	    (((ai1->ai_canonname == NULL) || (ai2->ai_canonname == NULL)) &&
107	    (ai1->ai_canonname != ai2->ai_canonname)))
108		return (-1);
109
110	if ((ai1->ai_canonname != NULL) &&
111		(strcmp(ai1->ai_canonname, ai2->ai_canonname) != 0))
112		return (-1);
113
114	if ((ai1->ai_addr != NULL) &&
115		(memcmp(ai1->ai_addr, ai2->ai_addr, ai1->ai_addrlen) != 0))
116		return (-1);
117
118	if ((ai1->ai_next == NULL) && (ai2->ai_next == NULL))
119		return (0);
120	else
121		return (compare_addrinfo_(ai1->ai_next, ai2->ai_next));
122}
123
124static int
125compare_addrinfo(struct addrinfo *ai1, struct addrinfo *ai2, void *mdata)
126{
127	int rv;
128
129	if (debug) {
130		printf("testing equality of 2 addrinfo structures\n");
131	}
132
133	rv = compare_addrinfo_(ai1, ai2);
134
135	if (debug) {
136		if (rv == 0)
137			printf("equal\n");
138		else {
139			dump_addrinfo(ai1);
140			dump_addrinfo(ai2);
141			printf("not equal\n");
142		}
143	}
144
145	return (rv);
146}
147
148void
149free_addrinfo(struct addrinfo *ai)
150{
151	if (ai == NULL)
152		return;
153
154	free(ai->ai_addr);
155	free(ai->ai_canonname);
156	free_addrinfo(ai->ai_next);
157}
158
159void
160sdump_addrinfo(struct addrinfo *ai, char *buffer, size_t buflen)
161{
162	int written, i;
163
164	written = snprintf(buffer, buflen, "%d %d %d %d %d ",
165		ai->ai_flags, ai->ai_family, ai->ai_socktype, ai->ai_protocol,
166		ai->ai_addrlen);
167	buffer += written;
168	if (written > buflen)
169		return;
170	buflen -= written;
171
172	written = snprintf(buffer, buflen, "%s ",
173		ai->ai_canonname == NULL ? "(null)" : ai->ai_canonname);
174	buffer += written;
175	if (written > buflen)
176		return;
177	buflen -= written;
178
179	if (ai->ai_addr == NULL) {
180		written = snprintf(buffer, buflen, "(null)");
181		buffer += written;
182		if (written > buflen)
183			return;
184		buflen -= written;
185	} else {
186	    for (i = 0; i < ai->ai_addrlen; ++i ) {
187		written = snprintf(buffer, buflen,
188		    i + 1 != ai->ai_addrlen ? "%d." : "%d",
189				    	((unsigned char *)ai->ai_addr)[i]);
190		    buffer += written;
191		    if (written > buflen)
192			return;
193		    buflen -= written;
194
195		    if (buflen == 0)
196			return;
197	    }
198	}
199
200	if (ai->ai_next != NULL) {
201		written = snprintf(buffer, buflen, ":");
202		buffer += written;
203		if (written > buflen)
204			return;
205		buflen -= written;
206
207		sdump_addrinfo(ai->ai_next, buffer, buflen);
208	}
209}
210
211void
212dump_addrinfo(struct addrinfo *result)
213{
214	if (result != NULL) {
215		char buffer[2048];
216		sdump_addrinfo(result, buffer, sizeof(buffer));
217		printf("%s\n", buffer);
218	} else
219		printf("(null)\n");
220}
221
222static int
223addrinfo_read_snapshot_addr(char *addr, unsigned char *result, size_t len)
224{
225	char *s, *ps, *ts;
226
227	ps = addr;
228	while ( (s = strsep(&ps, ".")) != NULL) {
229		if (len == 0)
230			return (-1);
231
232		*result = (unsigned char)strtol(s, &ts, 10);
233		++result;
234		if (*ts != '\0')
235			return (-1);
236
237		--len;
238	}
239	if (len != 0)
240		return (-1);
241	else
242		return (0);
243}
244
245static int
246addrinfo_read_snapshot_ai(struct addrinfo *ai, char *line)
247{
248	char *s, *ps, *ts;
249	int i, rv, *pi;
250
251	rv = 0;
252	i = 0;
253	ps = line;
254	memset(ai, 0, sizeof(struct addrinfo));
255	while ( (s = strsep(&ps, " ")) != NULL) {
256		switch (i) {
257			case 0:
258			case 1:
259			case 2:
260			case 3:
261				pi = &ai->ai_flags + i;
262				*pi = (int)strtol(s, &ts, 10);
263				if (*ts != '\0')
264					goto fin;
265				break;
266			case 4:
267				ai->ai_addrlen = (socklen_t)strtol(s, &ts, 10);
268				if (*ts != '\0')
269					goto fin;
270				break;
271			case 5:
272				if (strcmp(s, "(null)") != 0) {
273					ai->ai_canonname = strdup(s);
274					assert(ai->ai_canonname != NULL);
275				}
276				break;
277			case 6:
278				if (strcmp(s, "(null)") != 0) {
279				    ai->ai_addr = (struct sockaddr *)malloc(
280					ai->ai_addrlen);
281				    assert(ai->ai_addr != NULL);
282				    memset(ai->ai_addr, 0, ai->ai_addrlen);
283				    rv = addrinfo_read_snapshot_addr(s,
284					(unsigned char *)ai->ai_addr,
285				    	ai->ai_addrlen);
286
287				    if (rv != 0)
288					goto fin;
289				}
290				break;
291			default:
292				/* NOTE: should not be reachable */
293				rv = -1;
294				goto fin;
295		};
296
297		++i;
298	}
299
300fin:
301	if ((i != 7) || (rv != 0)) {
302		free_addrinfo(ai);
303		memset(ai, 0, sizeof(struct addrinfo));
304		return (-1);
305	}
306
307	return (0);
308}
309
310static int
311addrinfo_read_snapshot_func(struct addrinfo *ai, char *line)
312{
313	struct addrinfo *ai2;
314	char *s, *ps;
315	int i, rv;
316
317	if (debug)
318		printf("1 line read from snapshot:\n%s\n", line);
319
320	rv = 0;
321	i = 0;
322	ps = line;
323
324	s = strsep(&ps, ":");
325	if (s == NULL)
326		return (-1);
327
328	rv = addrinfo_read_snapshot_ai(ai, s);
329	if (rv != 0)
330		return (-1);
331
332	ai2 = ai;
333	while ( (s = strsep(&ps, ":")) != NULL) {
334		ai2->ai_next = (struct addrinfo *)malloc(
335			sizeof(struct addrinfo));
336		assert(ai2->ai_next != NULL);
337		memset(ai2->ai_next, 0, sizeof(struct addrinfo));
338
339		rv = addrinfo_read_snapshot_ai(ai2->ai_next, s);
340		if (rv != 0) {
341			free_addrinfo(ai);
342			return (-1);
343		}
344
345		ai2 = ai2->ai_next;
346	}
347
348	return (0);
349}
350
351static int
352addrinfo_test_correctness(struct addrinfo *ai, void *mdata)
353{
354	if (debug) {
355		printf("testing correctness with the following data:\n");
356		dump_addrinfo(ai);
357	}
358
359	if (ai == NULL)
360		goto errfin;
361
362	if (!((ai->ai_family >= 0) && (ai->ai_family < AF_MAX)))
363		goto errfin;
364
365	if ((ai->ai_socktype != 0) && (ai->ai_socktype != SOCK_STREAM) &&
366	    (ai->ai_socktype != SOCK_DGRAM) && (ai->ai_socktype != SOCK_RAW))
367		goto errfin;
368
369	if ((ai->ai_protocol != 0) && (ai->ai_protocol != IPPROTO_UDP) &&
370	    (ai->ai_protocol != IPPROTO_TCP))
371		goto errfin;
372
373	if ((ai->ai_flags & ~(AI_CANONNAME | AI_NUMERICHOST | AI_PASSIVE)) != 0)
374		goto errfin;
375
376	if ((ai->ai_addrlen != ai->ai_addr->sa_len) ||
377	    (ai->ai_family != ai->ai_addr->sa_family))
378		goto errfin;
379
380	if (debug)
381		printf("correct\n");
382
383	return (0);
384errfin:
385	if (debug)
386		printf("incorrect\n");
387
388	return (-1);
389}
390
391static int
392addrinfo_read_hostlist_func(struct addrinfo *ai, char *line)
393{
394	struct addrinfo *result;
395	int rv;
396
397	if (debug)
398		printf("resolving %s: ", line);
399	rv = getaddrinfo(line, NULL, &hints, &result);
400	if (rv == 0) {
401		if (debug)
402			printf("found\n");
403
404		rv = addrinfo_test_correctness(result, NULL);
405		if (rv != 0) {
406			freeaddrinfo(result);
407			return (rv);
408		}
409
410		clone_addrinfo(ai, result);
411		freeaddrinfo(result);
412	} else {
413		if (debug)
414			printf("not found\n");
415
416 		memset(ai, 0, sizeof(struct addrinfo));
417	}
418	return (0);
419}
420
421static void
422usage(void)
423{
424	(void)fprintf(stderr,
425	    "Usage: %s [-d] [-46] [-s <file]> -f <file>\n",
426	    getprogname());
427	exit(1);
428}
429
430int
431main(int argc, char **argv)
432{
433	struct addrinfo_test_data td, td_snap;
434	char *snapshot_file, *hostlist_file;
435	int rv;
436	int c;
437
438	if (argc < 2)
439		usage();
440
441	snapshot_file = NULL;
442	hostlist_file = NULL;
443	memset(&hints, 0, sizeof(struct addrinfo));
444	hints.ai_family = PF_UNSPEC;
445	hints.ai_flags = AI_CANONNAME;
446	while ((c = getopt(argc, argv, "46dns:f:")) != -1)
447		switch (c) {
448		case '4':
449			hints.ai_family = PF_INET;
450		case '6':
451			hints.ai_family = PF_INET6;
452			break;
453		case 'd':
454			debug = 1;
455			break;
456		case 's':
457			snapshot_file = strdup(optarg);
458			method = TEST_BUILD_SNAPSHOT;
459			break;
460		case 'f':
461			hostlist_file = strdup(optarg);
462			break;
463		default:
464			usage();
465		}
466
467	TEST_DATA_INIT(addrinfo, &td, clone_addrinfo, free_addrinfo);
468	TEST_DATA_INIT(addrinfo, &td_snap, clone_addrinfo, free_addrinfo);
469
470	if (hostlist_file == NULL)
471		usage();
472
473	if (access(hostlist_file, R_OK) != 0) {
474		if (debug)
475			printf("can't access the hostlist file %s\n",
476				hostlist_file);
477
478		usage();
479	}
480
481	if (debug)
482		printf("building host lists from %s\n", hostlist_file);
483
484	rv = TEST_SNAPSHOT_FILE_READ(addrinfo, hostlist_file, &td,
485		addrinfo_read_hostlist_func);
486	if (rv != 0)
487		goto fin;
488
489	if (snapshot_file != NULL) {
490		if (access(snapshot_file, W_OK | R_OK) != 0) {
491			if (errno == ENOENT)
492				method = TEST_BUILD_SNAPSHOT;
493			else {
494				if (debug)
495				    printf("can't access the snapshot file %s\n",
496				    snapshot_file);
497
498				rv = -1;
499				goto fin;
500			}
501		} else {
502			rv = TEST_SNAPSHOT_FILE_READ(addrinfo, snapshot_file,
503				&td_snap, addrinfo_read_snapshot_func);
504			if (rv != 0) {
505				if (debug)
506					printf("error reading snapshot file\n");
507				goto fin;
508			}
509		}
510	}
511
512	switch (method) {
513	case TEST_GETADDRINFO:
514		if (snapshot_file != NULL)
515			rv = DO_2PASS_TEST(addrinfo, &td, &td_snap,
516				compare_addrinfo, NULL);
517		break;
518	case TEST_BUILD_SNAPSHOT:
519		if (snapshot_file != NULL) {
520		    rv = TEST_SNAPSHOT_FILE_WRITE(addrinfo, snapshot_file, &td,
521			sdump_addrinfo);
522		}
523		break;
524	default:
525		rv = 0;
526		break;
527	};
528
529fin:
530	TEST_DATA_DESTROY(addrinfo, &td_snap);
531	TEST_DATA_DESTROY(addrinfo, &td);
532	free(hostlist_file);
533	free(snapshot_file);
534	return (rv);
535
536}
537
538