1/*-
2 * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
3 * All rights reserved.
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 <arpa/inet.h>
29#include <errno.h>
30#include <netdb.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <stringlist.h>
35#include <unistd.h>
36
37#include <atf-c.h>
38
39#include "testutil.h"
40
41enum test_methods {
42	TEST_GETSERVENT,
43	TEST_GETSERVBYNAME,
44	TEST_GETSERVBYPORT,
45	TEST_GETSERVENT_2PASS,
46	TEST_BUILD_SNAPSHOT
47};
48
49DECLARE_TEST_DATA(servent)
50DECLARE_TEST_FILE_SNAPSHOT(servent)
51DECLARE_1PASS_TEST(servent)
52DECLARE_2PASS_TEST(servent)
53
54static void clone_servent(struct servent *, struct servent const *);
55static int compare_servent(struct servent *, struct servent *, void *);
56static void dump_servent(struct servent *);
57static void free_servent(struct servent *);
58
59static void sdump_servent(struct servent *, char *, size_t);
60static int servent_read_snapshot_func(struct servent *, char *);
61
62static int servent_check_ambiguity(struct servent_test_data *,
63	struct servent *);
64static int servent_fill_test_data(struct servent_test_data *);
65static int servent_test_correctness(struct servent *, void *);
66static int servent_test_getservbyname(struct servent *, void *);
67static int servent_test_getservbyport(struct servent *, void *);
68static int servent_test_getservent(struct servent *, void *);
69
70IMPLEMENT_TEST_DATA(servent)
71IMPLEMENT_TEST_FILE_SNAPSHOT(servent)
72IMPLEMENT_1PASS_TEST(servent)
73IMPLEMENT_2PASS_TEST(servent)
74
75static void
76clone_servent(struct servent *dest, struct servent const *src)
77{
78	ATF_REQUIRE(dest != NULL);
79	ATF_REQUIRE(src != NULL);
80
81	char **cp;
82	int aliases_num;
83
84	memset(dest, 0, sizeof(struct servent));
85
86	if (src->s_name != NULL) {
87		dest->s_name = strdup(src->s_name);
88		ATF_REQUIRE(dest->s_name != NULL);
89	}
90
91	if (src->s_proto != NULL) {
92		dest->s_proto = strdup(src->s_proto);
93		ATF_REQUIRE(dest->s_proto != NULL);
94	}
95	dest->s_port = src->s_port;
96
97	if (src->s_aliases != NULL) {
98		aliases_num = 0;
99		for (cp = src->s_aliases; *cp; ++cp)
100			++aliases_num;
101
102		dest->s_aliases = calloc(aliases_num + 1, sizeof(char *));
103		ATF_REQUIRE(dest->s_aliases != NULL);
104
105		for (cp = src->s_aliases; *cp; ++cp) {
106			dest->s_aliases[cp - src->s_aliases] = strdup(*cp);
107			ATF_REQUIRE(dest->s_aliases[cp - src->s_aliases] != NULL);
108		}
109	}
110}
111
112static void
113free_servent(struct servent *serv)
114{
115	char **cp;
116
117	ATF_REQUIRE(serv != NULL);
118
119	free(serv->s_name);
120	free(serv->s_proto);
121
122	for (cp = serv->s_aliases; *cp; ++cp)
123		free(*cp);
124	free(serv->s_aliases);
125}
126
127static  int
128compare_servent(struct servent *serv1, struct servent *serv2, void *mdata)
129{
130	char **c1, **c2;
131
132	if (serv1 == serv2)
133		return 0;
134
135	if ((serv1 == NULL) || (serv2 == NULL))
136		goto errfin;
137
138	if ((strcmp(serv1->s_name, serv2->s_name) != 0) ||
139		(strcmp(serv1->s_proto, serv2->s_proto) != 0) ||
140		(serv1->s_port != serv2->s_port))
141			goto errfin;
142
143	c1 = serv1->s_aliases;
144	c2 = serv2->s_aliases;
145
146	if ((serv1->s_aliases == NULL) || (serv2->s_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 != NULL) || (*c2 != NULL))
154		goto errfin;
155
156	return 0;
157
158errfin:
159	if (mdata == NULL) {
160		printf("following structures are not equal:\n");
161		dump_servent(serv1);
162		dump_servent(serv2);
163	}
164
165	return (-1);
166}
167
168static void
169sdump_servent(struct servent *serv, char *buffer, size_t buflen)
170{
171	char **cp;
172	int written;
173
174	written = snprintf(buffer, buflen, "%s %d %s",
175		serv->s_name, ntohs(serv->s_port), serv->s_proto);
176	buffer += written;
177	if (written > (int)buflen)
178		return;
179	buflen -= written;
180
181	if (serv->s_aliases != NULL) {
182		if (*(serv->s_aliases) != NULL) {
183			for (cp = serv->s_aliases; *cp; ++cp) {
184				written = snprintf(buffer, buflen, " %s", *cp);
185				buffer += written;
186				if (written > (int)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
200servent_read_snapshot_func(struct servent *serv, char *line)
201{
202	StringList *sl;
203	char *s, *ps, *ts;
204	int i;
205
206	printf("1 line read from snapshot:\n%s\n", line);
207
208	i = 0;
209	sl = NULL;
210	ps = line;
211	memset(serv, 0, sizeof(struct servent));
212	while ( (s = strsep(&ps, " ")) != NULL) {
213		switch (i) {
214			case 0:
215				serv->s_name = strdup(s);
216				ATF_REQUIRE(serv->s_name != NULL);
217			break;
218
219			case 1:
220				serv->s_port = htons(
221					(int)strtol(s, &ts, 10));
222				if (*ts != '\0') {
223					free(serv->s_name);
224					return (-1);
225				}
226			break;
227
228			case 2:
229				serv->s_proto = strdup(s);
230				ATF_REQUIRE(serv->s_proto != NULL);
231			break;
232
233			default:
234				if (sl == NULL) {
235					if (strcmp(s, "(null)") == 0)
236						return (0);
237
238					sl = sl_init();
239					ATF_REQUIRE(sl != NULL);
240
241					if (strcmp(s, "noaliases") != 0) {
242						ts = strdup(s);
243						ATF_REQUIRE(ts != NULL);
244						sl_add(sl, ts);
245					}
246				} else {
247					ts = strdup(s);
248					ATF_REQUIRE(ts != NULL);
249					sl_add(sl, ts);
250				}
251			break;
252		}
253		++i;
254	}
255
256	if (i < 3) {
257		free(serv->s_name);
258		free(serv->s_proto);
259		memset(serv, 0, sizeof(struct servent));
260		return (-1);
261	}
262
263	sl_add(sl, NULL);
264	serv->s_aliases = sl->sl_str;
265
266	/* NOTE: is it a dirty hack or not? */
267	free(sl);
268	return (0);
269}
270
271static void
272dump_servent(struct servent *result)
273{
274	if (result != NULL) {
275		char buffer[1024];
276		sdump_servent(result, buffer, sizeof(buffer));
277		printf("%s\n", buffer);
278	} else
279		printf("(null)\n");
280}
281
282static int
283servent_fill_test_data(struct servent_test_data *td)
284{
285	struct servent *serv;
286
287	setservent(1);
288	while ((serv = getservent()) != NULL) {
289		if (servent_test_correctness(serv, NULL) == 0)
290			TEST_DATA_APPEND(servent, td, serv);
291		else
292			return (-1);
293	}
294	endservent();
295
296	return (0);
297}
298
299static int
300servent_test_correctness(struct servent *serv, void *mdata __unused)
301{
302	printf("testing correctness with the following data:\n");
303	dump_servent(serv);
304
305	if (serv == NULL)
306		goto errfin;
307
308	if (serv->s_name == NULL)
309		goto errfin;
310
311	if (serv->s_proto == NULL)
312		goto errfin;
313
314	if (ntohs(serv->s_port < 0))
315		goto errfin;
316
317	if (serv->s_aliases == NULL)
318		goto errfin;
319
320	printf("correct\n");
321
322	return (0);
323errfin:
324	printf("incorrect\n");
325
326	return (-1);
327}
328
329/* servent_check_ambiguity() is needed when one port+proto is associated with
330 * more than one service (these cases are usually marked as PROBLEM in
331 * /etc/services. This functions is needed also when one service+proto is
332 * associated with several ports. We have to check all the servent structures
333 * to make sure that serv really exists and correct */
334static int
335servent_check_ambiguity(struct servent_test_data *td, struct servent *serv)
336{
337
338	return (TEST_DATA_FIND(servent, td, serv, compare_servent,
339		NULL) != NULL ? 0 : -1);
340}
341
342static int
343servent_test_getservbyname(struct servent *serv_model, void *mdata)
344{
345	char **alias;
346	struct servent *serv;
347
348	printf("testing getservbyname() with the following data:\n");
349	dump_servent(serv_model);
350
351	serv = getservbyname(serv_model->s_name, serv_model->s_proto);
352	if (servent_test_correctness(serv, NULL) != 0)
353		goto errfin;
354
355	if ((compare_servent(serv, serv_model, NULL) != 0) &&
356	    (servent_check_ambiguity((struct servent_test_data *)mdata, serv)
357	    !=0))
358		goto errfin;
359
360	for (alias = serv_model->s_aliases; *alias; ++alias) {
361		serv = getservbyname(*alias, serv_model->s_proto);
362
363		if (servent_test_correctness(serv, NULL) != 0)
364			goto errfin;
365
366		if ((compare_servent(serv, serv_model, NULL) != 0) &&
367		    (servent_check_ambiguity(
368		    (struct servent_test_data *)mdata, serv) != 0))
369		    goto errfin;
370	}
371
372	printf("ok\n");
373	return (0);
374
375errfin:
376	printf("not ok\n");
377
378	return (-1);
379}
380
381static int
382servent_test_getservbyport(struct servent *serv_model, void *mdata)
383{
384	struct servent *serv;
385
386	printf("testing getservbyport() with the following data...\n");
387	dump_servent(serv_model);
388
389	serv = getservbyport(serv_model->s_port, serv_model->s_proto);
390	if ((servent_test_correctness(serv, NULL) != 0) ||
391	    ((compare_servent(serv, serv_model, NULL) != 0) &&
392	    (servent_check_ambiguity((struct servent_test_data *)mdata, serv)
393	    != 0))) {
394		printf("not ok\n");
395		return (-1);
396	} else {
397		printf("ok\n");
398		return (0);
399	}
400}
401
402static int
403servent_test_getservent(struct servent *serv, void *mdata __unused)
404{
405	/* Only correctness can be checked when doing 1-pass test for
406	 * getservent(). */
407	return (servent_test_correctness(serv, NULL));
408}
409
410static int
411run_tests(const char *snapshot_file, enum test_methods method)
412{
413	struct servent_test_data td, td_snap, td_2pass;
414	int rv;
415
416	TEST_DATA_INIT(servent, &td, clone_servent, free_servent);
417	TEST_DATA_INIT(servent, &td_snap, clone_servent, free_servent);
418	if (snapshot_file != NULL) {
419		if (access(snapshot_file, W_OK | R_OK) != 0) {
420			if (errno == ENOENT)
421				method = TEST_BUILD_SNAPSHOT;
422			else {
423				printf("can't access the file %s\n",
424				    snapshot_file);
425
426				rv = -1;
427				goto fin;
428			}
429		} else {
430			if (method == TEST_BUILD_SNAPSHOT) {
431				rv = 0;
432				goto fin;
433			}
434
435			TEST_SNAPSHOT_FILE_READ(servent, snapshot_file,
436				&td_snap, servent_read_snapshot_func);
437		}
438	}
439
440	rv = servent_fill_test_data(&td);
441	if (rv == -1)
442		return (-1);
443	switch (method) {
444	case TEST_GETSERVBYNAME:
445		if (snapshot_file == NULL)
446			rv = DO_1PASS_TEST(servent, &td,
447				servent_test_getservbyname, (void *)&td);
448		else
449			rv = DO_1PASS_TEST(servent, &td_snap,
450				servent_test_getservbyname, (void *)&td_snap);
451		break;
452	case TEST_GETSERVBYPORT:
453		if (snapshot_file == NULL)
454			rv = DO_1PASS_TEST(servent, &td,
455				servent_test_getservbyport, (void *)&td);
456		else
457			rv = DO_1PASS_TEST(servent, &td_snap,
458				servent_test_getservbyport, (void *)&td_snap);
459		break;
460	case TEST_GETSERVENT:
461		if (snapshot_file == NULL)
462			rv = DO_1PASS_TEST(servent, &td, servent_test_getservent,
463				(void *)&td);
464		else
465			rv = DO_2PASS_TEST(servent, &td, &td_snap,
466				compare_servent, NULL);
467		break;
468	case TEST_GETSERVENT_2PASS:
469			TEST_DATA_INIT(servent, &td_2pass, clone_servent, free_servent);
470			rv = servent_fill_test_data(&td_2pass);
471			if (rv != -1)
472				rv = DO_2PASS_TEST(servent, &td, &td_2pass,
473					compare_servent, NULL);
474			TEST_DATA_DESTROY(servent, &td_2pass);
475		break;
476	case TEST_BUILD_SNAPSHOT:
477		if (snapshot_file != NULL)
478		    rv = TEST_SNAPSHOT_FILE_WRITE(servent, snapshot_file, &td,
479			sdump_servent);
480		break;
481	default:
482		rv = 0;
483		break;
484	}
485
486fin:
487	TEST_DATA_DESTROY(servent, &td_snap);
488	TEST_DATA_DESTROY(servent, &td);
489
490	return (rv);
491}
492
493#define	SNAPSHOT_FILE	"snapshot_serv"
494
495ATF_TC_WITHOUT_HEAD(build_snapshot);
496ATF_TC_BODY(build_snapshot, tc)
497{
498
499	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
500}
501
502ATF_TC_WITHOUT_HEAD(getservbyname);
503ATF_TC_BODY(getservbyname, tc)
504{
505
506	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYNAME) == 0);
507}
508
509ATF_TC_WITHOUT_HEAD(getservbyname_with_snapshot);
510ATF_TC_BODY(getservbyname_with_snapshot, tc)
511{
512
513	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
514	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVBYNAME) == 0);
515}
516
517ATF_TC_WITHOUT_HEAD(getservbyport);
518ATF_TC_BODY(getservbyport, tc)
519{
520
521	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYPORT) == 0);
522}
523
524ATF_TC_WITHOUT_HEAD(getservbyport_with_snapshot);
525ATF_TC_BODY(getservbyport_with_snapshot, tc)
526{
527
528	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
529	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVBYPORT) == 0);
530}
531
532ATF_TC_WITHOUT_HEAD(getservbyent);
533ATF_TC_BODY(getservbyent, tc)
534{
535
536	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT) == 0);
537}
538
539ATF_TC_WITHOUT_HEAD(getservbyent_with_snapshot);
540ATF_TC_BODY(getservbyent_with_snapshot, tc)
541{
542
543	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
544	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVENT) == 0);
545}
546
547ATF_TC_WITHOUT_HEAD(getservbyent_with_two_pass);
548ATF_TC_BODY(getservbyent_with_two_pass, tc)
549{
550
551	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT_2PASS) == 0);
552}
553
554ATF_TP_ADD_TCS(tp)
555{
556
557	ATF_TP_ADD_TC(tp, build_snapshot);
558	ATF_TP_ADD_TC(tp, getservbyent);
559	ATF_TP_ADD_TC(tp, getservbyent_with_snapshot);
560	ATF_TP_ADD_TC(tp, getservbyent_with_two_pass);
561	ATF_TP_ADD_TC(tp, getservbyname);
562	ATF_TP_ADD_TC(tp, getservbyname_with_snapshot);
563	ATF_TP_ADD_TC(tp, getservbyport);
564	ATF_TP_ADD_TC(tp, getservbyport_with_snapshot);
565
566	return (atf_no_error());
567}
568