xref: /freebsd/lib/libc/tests/nss/getserv_test.c (revision 35c0a8c449fd2b7f75029ebed5e10852240f0865)
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 
41 enum test_methods {
42 	TEST_GETSERVENT,
43 	TEST_GETSERVBYNAME,
44 	TEST_GETSERVBYPORT,
45 	TEST_GETSERVENT_2PASS,
46 	TEST_BUILD_SNAPSHOT
47 };
48 
49 DECLARE_TEST_DATA(servent)
50 DECLARE_TEST_FILE_SNAPSHOT(servent)
51 DECLARE_1PASS_TEST(servent)
52 DECLARE_2PASS_TEST(servent)
53 
54 static void clone_servent(struct servent *, struct servent const *);
55 static int compare_servent(struct servent *, struct servent *, void *);
56 static void dump_servent(struct servent *);
57 static void free_servent(struct servent *);
58 
59 static void sdump_servent(struct servent *, char *, size_t);
60 static int servent_read_snapshot_func(struct servent *, char *);
61 
62 static int servent_check_ambiguity(struct servent_test_data *,
63 	struct servent *);
64 static int servent_fill_test_data(struct servent_test_data *);
65 static int servent_test_correctness(struct servent *, void *);
66 static int servent_test_getservbyname(struct servent *, void *);
67 static int servent_test_getservbyport(struct servent *, void *);
68 static int servent_test_getservent(struct servent *, void *);
69 
70 IMPLEMENT_TEST_DATA(servent)
71 IMPLEMENT_TEST_FILE_SNAPSHOT(servent)
72 IMPLEMENT_1PASS_TEST(servent)
73 IMPLEMENT_2PASS_TEST(servent)
74 
75 static void
76 clone_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 
112 static void
113 free_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 
127 static  int
128 compare_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 
158 errfin:
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 
168 static void
169 sdump_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 
199 static int
200 servent_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 
271 static void
272 dump_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 
282 static int
283 servent_fill_test_data(struct servent_test_data *td)
284 {
285 	struct servent *serv;
286 	const int limit = 1024;
287 	int count = 0;
288 
289 	setservent(1);
290 	while ((serv = getservent()) != NULL) {
291 		if (servent_test_correctness(serv, NULL) == 0)
292 			TEST_DATA_APPEND(servent, td, serv);
293 		else
294 			return (-1);
295 		if (++count >= limit)
296 			break;
297 	}
298 	endservent();
299 
300 	return (0);
301 }
302 
303 static int
304 servent_test_correctness(struct servent *serv, void *mdata __unused)
305 {
306 	printf("testing correctness with the following data:\n");
307 	dump_servent(serv);
308 
309 	if (serv == NULL)
310 		goto errfin;
311 
312 	if (serv->s_name == NULL)
313 		goto errfin;
314 
315 	if (serv->s_proto == NULL)
316 		goto errfin;
317 
318 	if (ntohs(serv->s_port < 0))
319 		goto errfin;
320 
321 	if (serv->s_aliases == NULL)
322 		goto errfin;
323 
324 	printf("correct\n");
325 
326 	return (0);
327 errfin:
328 	printf("incorrect\n");
329 
330 	return (-1);
331 }
332 
333 /* servent_check_ambiguity() is needed when one port+proto is associated with
334  * more than one service (these cases are usually marked as PROBLEM in
335  * /etc/services. This functions is needed also when one service+proto is
336  * associated with several ports. We have to check all the servent structures
337  * to make sure that serv really exists and correct */
338 static int
339 servent_check_ambiguity(struct servent_test_data *td, struct servent *serv)
340 {
341 
342 	return (TEST_DATA_FIND(servent, td, serv, compare_servent,
343 		NULL) != NULL ? 0 : -1);
344 }
345 
346 static int
347 servent_test_getservbyname(struct servent *serv_model, void *mdata)
348 {
349 	char **alias;
350 	struct servent *serv;
351 
352 	printf("testing getservbyname() with the following data:\n");
353 	dump_servent(serv_model);
354 
355 	serv = getservbyname(serv_model->s_name, serv_model->s_proto);
356 	if (servent_test_correctness(serv, NULL) != 0)
357 		goto errfin;
358 
359 	if ((compare_servent(serv, serv_model, NULL) != 0) &&
360 	    (servent_check_ambiguity((struct servent_test_data *)mdata, serv)
361 	    !=0))
362 		goto errfin;
363 
364 	for (alias = serv_model->s_aliases; *alias; ++alias) {
365 		serv = getservbyname(*alias, serv_model->s_proto);
366 
367 		if (servent_test_correctness(serv, NULL) != 0)
368 			goto errfin;
369 
370 		if ((compare_servent(serv, serv_model, NULL) != 0) &&
371 		    (servent_check_ambiguity(
372 		    (struct servent_test_data *)mdata, serv) != 0))
373 		    goto errfin;
374 	}
375 
376 	printf("ok\n");
377 	return (0);
378 
379 errfin:
380 	printf("not ok\n");
381 
382 	return (-1);
383 }
384 
385 static int
386 servent_test_getservbyport(struct servent *serv_model, void *mdata)
387 {
388 	struct servent *serv;
389 
390 	printf("testing getservbyport() with the following data...\n");
391 	dump_servent(serv_model);
392 
393 	serv = getservbyport(serv_model->s_port, serv_model->s_proto);
394 	if ((servent_test_correctness(serv, NULL) != 0) ||
395 	    ((compare_servent(serv, serv_model, NULL) != 0) &&
396 	    (servent_check_ambiguity((struct servent_test_data *)mdata, serv)
397 	    != 0))) {
398 		printf("not ok\n");
399 		return (-1);
400 	} else {
401 		printf("ok\n");
402 		return (0);
403 	}
404 }
405 
406 static int
407 servent_test_getservent(struct servent *serv, void *mdata __unused)
408 {
409 	/* Only correctness can be checked when doing 1-pass test for
410 	 * getservent(). */
411 	return (servent_test_correctness(serv, NULL));
412 }
413 
414 static int
415 run_tests(const char *snapshot_file, enum test_methods method)
416 {
417 	struct servent_test_data td, td_snap, td_2pass;
418 	int rv;
419 
420 	TEST_DATA_INIT(servent, &td, clone_servent, free_servent);
421 	TEST_DATA_INIT(servent, &td_snap, clone_servent, free_servent);
422 	if (snapshot_file != NULL) {
423 		if (access(snapshot_file, W_OK | R_OK) != 0) {
424 			if (errno == ENOENT)
425 				method = TEST_BUILD_SNAPSHOT;
426 			else {
427 				printf("can't access the file %s\n",
428 				    snapshot_file);
429 
430 				rv = -1;
431 				goto fin;
432 			}
433 		} else {
434 			if (method == TEST_BUILD_SNAPSHOT) {
435 				rv = 0;
436 				goto fin;
437 			}
438 
439 			TEST_SNAPSHOT_FILE_READ(servent, snapshot_file,
440 				&td_snap, servent_read_snapshot_func);
441 		}
442 	}
443 
444 	rv = servent_fill_test_data(&td);
445 	if (rv == -1)
446 		return (-1);
447 	switch (method) {
448 	case TEST_GETSERVBYNAME:
449 		if (snapshot_file == NULL)
450 			rv = DO_1PASS_TEST(servent, &td,
451 				servent_test_getservbyname, (void *)&td);
452 		else
453 			rv = DO_1PASS_TEST(servent, &td_snap,
454 				servent_test_getservbyname, (void *)&td_snap);
455 		break;
456 	case TEST_GETSERVBYPORT:
457 		if (snapshot_file == NULL)
458 			rv = DO_1PASS_TEST(servent, &td,
459 				servent_test_getservbyport, (void *)&td);
460 		else
461 			rv = DO_1PASS_TEST(servent, &td_snap,
462 				servent_test_getservbyport, (void *)&td_snap);
463 		break;
464 	case TEST_GETSERVENT:
465 		if (snapshot_file == NULL)
466 			rv = DO_1PASS_TEST(servent, &td, servent_test_getservent,
467 				(void *)&td);
468 		else
469 			rv = DO_2PASS_TEST(servent, &td, &td_snap,
470 				compare_servent, NULL);
471 		break;
472 	case TEST_GETSERVENT_2PASS:
473 			TEST_DATA_INIT(servent, &td_2pass, clone_servent, free_servent);
474 			rv = servent_fill_test_data(&td_2pass);
475 			if (rv != -1)
476 				rv = DO_2PASS_TEST(servent, &td, &td_2pass,
477 					compare_servent, NULL);
478 			TEST_DATA_DESTROY(servent, &td_2pass);
479 		break;
480 	case TEST_BUILD_SNAPSHOT:
481 		if (snapshot_file != NULL)
482 		    rv = TEST_SNAPSHOT_FILE_WRITE(servent, snapshot_file, &td,
483 			sdump_servent);
484 		break;
485 	default:
486 		rv = 0;
487 		break;
488 	}
489 
490 fin:
491 	TEST_DATA_DESTROY(servent, &td_snap);
492 	TEST_DATA_DESTROY(servent, &td);
493 
494 	return (rv);
495 }
496 
497 #define	SNAPSHOT_FILE	"snapshot_serv"
498 
499 ATF_TC_WITHOUT_HEAD(build_snapshot);
500 ATF_TC_BODY(build_snapshot, tc)
501 {
502 
503 	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
504 }
505 
506 ATF_TC_WITHOUT_HEAD(getservbyname);
507 ATF_TC_BODY(getservbyname, tc)
508 {
509 
510 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYNAME) == 0);
511 }
512 
513 ATF_TC_WITHOUT_HEAD(getservbyname_with_snapshot);
514 ATF_TC_BODY(getservbyname_with_snapshot, tc)
515 {
516 
517 	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
518 	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVBYNAME) == 0);
519 }
520 
521 ATF_TC_WITHOUT_HEAD(getservbyport);
522 ATF_TC_BODY(getservbyport, tc)
523 {
524 
525 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYPORT) == 0);
526 }
527 
528 ATF_TC_WITHOUT_HEAD(getservbyport_with_snapshot);
529 ATF_TC_BODY(getservbyport_with_snapshot, tc)
530 {
531 
532 	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
533 	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVBYPORT) == 0);
534 }
535 
536 ATF_TC_WITHOUT_HEAD(getservbyent);
537 ATF_TC_BODY(getservbyent, tc)
538 {
539 
540 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT) == 0);
541 }
542 
543 ATF_TC_WITHOUT_HEAD(getservbyent_with_snapshot);
544 ATF_TC_BODY(getservbyent_with_snapshot, tc)
545 {
546 
547 	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
548 	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVENT) == 0);
549 }
550 
551 ATF_TC_WITHOUT_HEAD(getservbyent_with_two_pass);
552 ATF_TC_BODY(getservbyent_with_two_pass, tc)
553 {
554 
555 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT_2PASS) == 0);
556 }
557 
558 ATF_TP_ADD_TCS(tp)
559 {
560 
561 	ATF_TP_ADD_TC(tp, build_snapshot);
562 	ATF_TP_ADD_TC(tp, getservbyent);
563 	ATF_TP_ADD_TC(tp, getservbyent_with_snapshot);
564 	ATF_TP_ADD_TC(tp, getservbyent_with_two_pass);
565 	ATF_TP_ADD_TC(tp, getservbyname);
566 	ATF_TP_ADD_TC(tp, getservbyname_with_snapshot);
567 	ATF_TP_ADD_TC(tp, getservbyport);
568 	ATF_TP_ADD_TC(tp, getservbyport_with_snapshot);
569 
570 	return (atf_no_error());
571 }
572