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