getproto_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-getproto.c 251818 2013-06-16 19:35:01Z eadler $");
29
30#include <arpa/inet.h>
31#include <assert.h>
32#include <errno.h>
33#include <netdb.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <stringlist.h>
38#include <unistd.h>
39#include "testutil.h"
40
41enum test_methods {
42	TEST_GETPROTOENT,
43	TEST_GETPROTOBYNAME,
44	TEST_GETPROTOBYNUMBER,
45	TEST_GETPROTOENT_2PASS,
46	TEST_BUILD_SNAPSHOT
47};
48
49static int debug = 0;
50static enum test_methods method = TEST_BUILD_SNAPSHOT;
51
52DECLARE_TEST_DATA(protoent)
53DECLARE_TEST_FILE_SNAPSHOT(protoent)
54DECLARE_1PASS_TEST(protoent)
55DECLARE_2PASS_TEST(protoent)
56
57static void clone_protoent(struct protoent *, struct protoent const *);
58static int compare_protoent(struct protoent *, struct protoent *, void *);
59static void dump_protoent(struct protoent *);
60static void free_protoent(struct protoent *);
61
62static void sdump_protoent(struct protoent *, char *, size_t);
63static int protoent_read_snapshot_func(struct protoent *, char *);
64
65static int protoent_check_ambiguity(struct protoent_test_data *,
66	struct protoent *);
67static int protoent_fill_test_data(struct protoent_test_data *);
68static int protoent_test_correctness(struct protoent *, void *);
69static int protoent_test_getprotobyname(struct protoent *, void *);
70static int protoent_test_getprotobynumber(struct protoent *, void *);
71static int protoent_test_getprotoent(struct protoent *, void *);
72
73static void usage(void)  __attribute__((__noreturn__));
74
75IMPLEMENT_TEST_DATA(protoent)
76IMPLEMENT_TEST_FILE_SNAPSHOT(protoent)
77IMPLEMENT_1PASS_TEST(protoent)
78IMPLEMENT_2PASS_TEST(protoent)
79
80static void
81clone_protoent(struct protoent *dest, struct protoent const *src)
82{
83	assert(dest != NULL);
84	assert(src != NULL);
85
86	char **cp;
87	int aliases_num;
88
89	memset(dest, 0, sizeof(struct protoent));
90
91	if (src->p_name != NULL) {
92		dest->p_name = strdup(src->p_name);
93		assert(dest->p_name != NULL);
94	}
95
96	dest->p_proto = src->p_proto;
97
98	if (src->p_aliases != NULL) {
99		aliases_num = 0;
100		for (cp = src->p_aliases; *cp; ++cp)
101			++aliases_num;
102
103		dest->p_aliases = (char **)malloc((aliases_num+1) * (sizeof(char *)));
104		assert(dest->p_aliases != NULL);
105		memset(dest->p_aliases, 0, (aliases_num+1) * (sizeof(char *)));
106
107		for (cp = src->p_aliases; *cp; ++cp) {
108			dest->p_aliases[cp - src->p_aliases] = strdup(*cp);
109			assert(dest->p_aliases[cp - src->p_aliases] != NULL);
110		}
111	}
112}
113
114static void
115free_protoent(struct protoent *pe)
116{
117	char **cp;
118
119	assert(pe != NULL);
120
121	free(pe->p_name);
122
123	for (cp = pe->p_aliases; *cp; ++cp)
124		free(*cp);
125	free(pe->p_aliases);
126}
127
128static  int
129compare_protoent(struct protoent *pe1, struct protoent *pe2, void *mdata)
130{
131	char **c1, **c2;
132
133	if (pe1 == pe2)
134		return 0;
135
136	if ((pe1 == NULL) || (pe2 == NULL))
137		goto errfin;
138
139	if ((strcmp(pe1->p_name, pe2->p_name) != 0) ||
140		(pe1->p_proto != pe2->p_proto))
141			goto errfin;
142
143	c1 = pe1->p_aliases;
144	c2 = pe2->p_aliases;
145
146	if ((pe1->p_aliases == NULL) || (pe2->p_aliases == NULL))
147		goto errfin;
148
149	for (;*c1 && *c2; ++c1, ++c2)
150		if (strcmp(*c1, *c2) != 0)
151			goto errfin;
152
153	if ((*c1 != '\0') || (*c2 != '\0'))
154		goto errfin;
155
156	return 0;
157
158errfin:
159	if ((debug) && (mdata == NULL)) {
160		printf("following structures are not equal:\n");
161		dump_protoent(pe1);
162		dump_protoent(pe2);
163	}
164
165	return (-1);
166}
167
168static void
169sdump_protoent(struct protoent *pe, char *buffer, size_t buflen)
170{
171	char **cp;
172	int written;
173
174	written = snprintf(buffer, buflen, "%s %d",
175		pe->p_name, pe->p_proto);
176	buffer += written;
177	if (written > buflen)
178		return;
179	buflen -= written;
180
181	if (pe->p_aliases != NULL) {
182		if (*(pe->p_aliases) != '\0') {
183			for (cp = pe->p_aliases; *cp; ++cp) {
184				written = snprintf(buffer, buflen, " %s",*cp);
185				buffer += written;
186				if (written > buflen)
187					return;
188				buflen -= written;
189
190				if (buflen == 0)
191					return;
192			}
193		} else
194			snprintf(buffer, buflen, " noaliases");
195	} else
196		snprintf(buffer, buflen, " (null)");
197}
198
199static int
200protoent_read_snapshot_func(struct protoent *pe, char *line)
201{
202	StringList *sl;
203	char *s, *ps, *ts;
204	int i;
205
206	if (debug)
207		printf("1 line read from snapshot:\n%s\n", line);
208
209	i = 0;
210	sl = NULL;
211	ps = line;
212	memset(pe, 0, sizeof(struct protoent));
213	while ( (s = strsep(&ps, " ")) != NULL) {
214		switch (i) {
215			case 0:
216				pe->p_name = strdup(s);
217				assert(pe->p_name != NULL);
218			break;
219
220			case 1:
221				pe->p_proto = (int)strtol(s, &ts, 10);
222				if (*ts != '\0') {
223					free(pe->p_name);
224					return (-1);
225				}
226			break;
227
228			default:
229				if (sl == NULL) {
230					if (strcmp(s, "(null)") == 0)
231						return (0);
232
233					sl = sl_init();
234					assert(sl != NULL);
235
236					if (strcmp(s, "noaliases") != 0) {
237						ts = strdup(s);
238						assert(ts != NULL);
239						sl_add(sl, ts);
240					}
241				} else {
242					ts = strdup(s);
243					assert(ts != NULL);
244					sl_add(sl, ts);
245				}
246			break;
247		};
248		++i;
249	}
250
251	if (i < 3) {
252		free(pe->p_name);
253		memset(pe, 0, sizeof(struct protoent));
254		return (-1);
255	}
256
257	sl_add(sl, NULL);
258	pe->p_aliases = sl->sl_str;
259
260	/* NOTE: is it a dirty hack or not? */
261	free(sl);
262	return (0);
263}
264
265static void
266dump_protoent(struct protoent *result)
267{
268	if (result != NULL) {
269		char buffer[1024];
270		sdump_protoent(result, buffer, sizeof(buffer));
271		printf("%s\n", buffer);
272	} else
273		printf("(null)\n");
274}
275
276static int
277protoent_fill_test_data(struct protoent_test_data *td)
278{
279	struct protoent *pe;
280
281	setprotoent(1);
282	while ((pe = getprotoent()) != NULL) {
283		if (protoent_test_correctness(pe, NULL) == 0)
284			TEST_DATA_APPEND(protoent, td, pe);
285		else
286			return (-1);
287	}
288	endprotoent();
289
290	return (0);
291}
292
293static int
294protoent_test_correctness(struct protoent *pe, void *mdata)
295{
296	if (debug) {
297		printf("testing correctness with the following data:\n");
298		dump_protoent(pe);
299	}
300
301	if (pe == NULL)
302		goto errfin;
303
304	if (pe->p_name == NULL)
305		goto errfin;
306
307	if (pe->p_proto < 0)
308		goto errfin;
309
310	if (pe->p_aliases == NULL)
311		goto errfin;
312
313	if (debug)
314		printf("correct\n");
315
316	return (0);
317errfin:
318	if (debug)
319		printf("incorrect\n");
320
321	return (-1);
322}
323
324/* protoent_check_ambiguity() is needed when one port+proto is associated with
325 * more than one peice (these cases are usually marked as PROBLEM in
326 * /etc/peices. This functions is needed also when one peice+proto is
327 * associated with several ports. We have to check all the protoent structures
328 * to make sure that pe really exists and correct */
329static int
330protoent_check_ambiguity(struct protoent_test_data *td, struct protoent *pe)
331{
332
333	return (TEST_DATA_FIND(protoent, td, pe, compare_protoent,
334		NULL) != NULL ? 0 : -1);
335}
336
337static int
338protoent_test_getprotobyname(struct protoent *pe_model, void *mdata)
339{
340	char **alias;
341	struct protoent *pe;
342
343	if (debug) {
344		printf("testing getprotobyname() with the following data:\n");
345		dump_protoent(pe_model);
346	}
347
348	pe = getprotobyname(pe_model->p_name);
349	if (protoent_test_correctness(pe, NULL) != 0)
350		goto errfin;
351
352	if ((compare_protoent(pe, pe_model, NULL) != 0) &&
353	    (protoent_check_ambiguity((struct protoent_test_data *)mdata, pe)
354	    !=0))
355	    goto errfin;
356
357	for (alias = pe_model->p_aliases; *alias; ++alias) {
358		pe = getprotobyname(*alias);
359
360		if (protoent_test_correctness(pe, NULL) != 0)
361			goto errfin;
362
363		if ((compare_protoent(pe, pe_model, NULL) != 0) &&
364		    (protoent_check_ambiguity(
365		    (struct protoent_test_data *)mdata, pe) != 0))
366		    goto errfin;
367	}
368
369	if (debug)
370		printf("ok\n");
371	return (0);
372
373errfin:
374	if (debug)
375		printf("not ok\n");
376
377	return (-1);
378}
379
380static int
381protoent_test_getprotobynumber(struct protoent *pe_model, void *mdata)
382{
383	struct protoent *pe;
384
385	if (debug) {
386		printf("testing getprotobyport() with the following data...\n");
387		dump_protoent(pe_model);
388	}
389
390	pe = getprotobynumber(pe_model->p_proto);
391	if ((protoent_test_correctness(pe, NULL) != 0) ||
392	    ((compare_protoent(pe, pe_model, NULL) != 0) &&
393	    (protoent_check_ambiguity((struct protoent_test_data *)mdata, pe)
394	    != 0))) {
395	    if (debug)
396		printf("not ok\n");
397	    return (-1);
398	} else {
399	    if (debug)
400		printf("ok\n");
401	    return (0);
402	}
403}
404
405static int
406protoent_test_getprotoent(struct protoent *pe, void *mdata)
407{
408	/* Only correctness can be checked when doing 1-pass test for
409	 * getprotoent(). */
410	return (protoent_test_correctness(pe, NULL));
411}
412
413static void
414usage(void)
415{
416	(void)fprintf(stderr,
417	    "Usage: %s -nve2 [-d] [-s <file>]\n",
418	    getprogname());
419	exit(1);
420}
421
422int
423main(int argc, char **argv)
424{
425	struct protoent_test_data td, td_snap, td_2pass;
426	char *snapshot_file;
427	int rv;
428	int c;
429
430	if (argc < 2)
431		usage();
432
433	snapshot_file = NULL;
434	while ((c = getopt(argc, argv, "nve2ds:")) != -1)
435		switch (c) {
436		case 'd':
437			debug++;
438			break;
439		case 'n':
440			method = TEST_GETPROTOBYNAME;
441			break;
442		case 'v':
443			method = TEST_GETPROTOBYNUMBER;
444			break;
445		case 'e':
446			method = TEST_GETPROTOENT;
447			break;
448		case '2':
449			method = TEST_GETPROTOENT_2PASS;
450			break;
451		case 's':
452			snapshot_file = strdup(optarg);
453			break;
454		default:
455			usage();
456		}
457
458	TEST_DATA_INIT(protoent, &td, clone_protoent, free_protoent);
459	TEST_DATA_INIT(protoent, &td_snap, clone_protoent, free_protoent);
460	if (snapshot_file != NULL) {
461		if (access(snapshot_file, W_OK | R_OK) != 0) {
462			if (errno == ENOENT)
463				method = TEST_BUILD_SNAPSHOT;
464			else {
465				if (debug)
466					printf("can't access the file %s\n",
467				snapshot_file);
468
469				rv = -1;
470				goto fin;
471			}
472		} else {
473			if (method == TEST_BUILD_SNAPSHOT) {
474				rv = 0;
475				goto fin;
476			}
477
478			TEST_SNAPSHOT_FILE_READ(protoent, snapshot_file,
479				&td_snap, protoent_read_snapshot_func);
480		}
481	}
482
483	rv = protoent_fill_test_data(&td);
484	if (rv == -1)
485		return (-1);
486	switch (method) {
487	case TEST_GETPROTOBYNAME:
488		if (snapshot_file == NULL)
489			rv = DO_1PASS_TEST(protoent, &td,
490				protoent_test_getprotobyname, (void *)&td);
491		else
492			rv = DO_1PASS_TEST(protoent, &td_snap,
493				protoent_test_getprotobyname, (void *)&td_snap);
494		break;
495	case TEST_GETPROTOBYNUMBER:
496		if (snapshot_file == NULL)
497			rv = DO_1PASS_TEST(protoent, &td,
498				protoent_test_getprotobynumber, (void *)&td);
499		else
500			rv = DO_1PASS_TEST(protoent, &td_snap,
501				protoent_test_getprotobynumber, (void *)&td_snap);
502		break;
503	case TEST_GETPROTOENT:
504		if (snapshot_file == NULL)
505			rv = DO_1PASS_TEST(protoent, &td,
506				protoent_test_getprotoent, (void *)&td);
507		else
508			rv = DO_2PASS_TEST(protoent, &td, &td_snap,
509				compare_protoent, NULL);
510		break;
511	case TEST_GETPROTOENT_2PASS:
512			TEST_DATA_INIT(protoent, &td_2pass, clone_protoent,
513				free_protoent);
514			rv = protoent_fill_test_data(&td_2pass);
515			if (rv != -1)
516				rv = DO_2PASS_TEST(protoent, &td, &td_2pass,
517					compare_protoent, NULL);
518			TEST_DATA_DESTROY(protoent, &td_2pass);
519		break;
520	case TEST_BUILD_SNAPSHOT:
521		if (snapshot_file != NULL)
522		    rv = TEST_SNAPSHOT_FILE_WRITE(protoent, snapshot_file, &td,
523			sdump_protoent);
524		break;
525	default:
526		rv = 0;
527		break;
528	};
529
530fin:
531	TEST_DATA_DESTROY(protoent, &td_snap);
532	TEST_DATA_DESTROY(protoent, &td);
533	free(snapshot_file);
534	return (rv);
535}
536