xref: /freebsd/lib/libc/tests/nss/getserv_test.c (revision 81ad6265)
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 <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <arpa/inet.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 
40 #include <atf-c.h>
41 
42 #include "testutil.h"
43 
44 enum test_methods {
45 	TEST_GETSERVENT,
46 	TEST_GETSERVBYNAME,
47 	TEST_GETSERVBYPORT,
48 	TEST_GETSERVENT_2PASS,
49 	TEST_BUILD_SNAPSHOT
50 };
51 
52 DECLARE_TEST_DATA(servent)
53 DECLARE_TEST_FILE_SNAPSHOT(servent)
54 DECLARE_1PASS_TEST(servent)
55 DECLARE_2PASS_TEST(servent)
56 
57 static void clone_servent(struct servent *, struct servent const *);
58 static int compare_servent(struct servent *, struct servent *, void *);
59 static void dump_servent(struct servent *);
60 static void free_servent(struct servent *);
61 
62 static void sdump_servent(struct servent *, char *, size_t);
63 static int servent_read_snapshot_func(struct servent *, char *);
64 
65 static int servent_check_ambiguity(struct servent_test_data *,
66 	struct servent *);
67 static int servent_fill_test_data(struct servent_test_data *);
68 static int servent_test_correctness(struct servent *, void *);
69 static int servent_test_getservbyname(struct servent *, void *);
70 static int servent_test_getservbyport(struct servent *, void *);
71 static int servent_test_getservent(struct servent *, void *);
72 
73 IMPLEMENT_TEST_DATA(servent)
74 IMPLEMENT_TEST_FILE_SNAPSHOT(servent)
75 IMPLEMENT_1PASS_TEST(servent)
76 IMPLEMENT_2PASS_TEST(servent)
77 
78 static void
79 clone_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 
115 static void
116 free_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 
130 static  int
131 compare_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 != NULL) || (*c2 != NULL))
157 		goto errfin;
158 
159 	return 0;
160 
161 errfin:
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 
171 static void
172 sdump_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) != NULL) {
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 
202 static int
203 servent_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 
274 static void
275 dump_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 
285 static int
286 servent_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 
302 static int
303 servent_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);
326 errfin:
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 */
337 static int
338 servent_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 
345 static int
346 servent_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 
378 errfin:
379 	printf("not ok\n");
380 
381 	return (-1);
382 }
383 
384 static int
385 servent_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 
405 static int
406 servent_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 
413 static int
414 run_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 
489 fin:
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 
498 ATF_TC_WITHOUT_HEAD(build_snapshot);
499 ATF_TC_BODY(build_snapshot, tc)
500 {
501 
502 	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
503 }
504 
505 ATF_TC_WITHOUT_HEAD(getservbyname);
506 ATF_TC_BODY(getservbyname, tc)
507 {
508 
509 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYNAME) == 0);
510 }
511 
512 ATF_TC_WITHOUT_HEAD(getservbyname_with_snapshot);
513 ATF_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 
520 ATF_TC_WITHOUT_HEAD(getservbyport);
521 ATF_TC_BODY(getservbyport, tc)
522 {
523 
524 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYPORT) == 0);
525 }
526 
527 ATF_TC_WITHOUT_HEAD(getservbyport_with_snapshot);
528 ATF_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 
535 ATF_TC_WITHOUT_HEAD(getservbyent);
536 ATF_TC_BODY(getservbyent, tc)
537 {
538 
539 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT) == 0);
540 }
541 
542 ATF_TC_WITHOUT_HEAD(getservbyent_with_snapshot);
543 ATF_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 
550 ATF_TC_WITHOUT_HEAD(getservbyent_with_two_pass);
551 ATF_TC_BODY(getservbyent_with_two_pass, tc)
552 {
553 
554 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT_2PASS) == 0);
555 }
556 
557 ATF_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