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