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