xref: /freebsd/lib/libc/tests/nss/getserv_test.c (revision 559a218c9b257775fb249b67945fe4a05b7a6b9f)
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)
IMPLEMENT_TEST_FILE_SNAPSHOT(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
free_servent(struct servent * serv)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
compare_servent(struct servent * serv1,struct servent * serv2,void * mdata)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
sdump_servent(struct servent * serv,char * buffer,size_t buflen)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
servent_read_snapshot_func(struct servent * serv,char * line)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
dump_servent(struct servent * result)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
servent_fill_test_data(struct servent_test_data * td)283 servent_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 
299 static int
servent_test_correctness(struct servent * serv,void * mdata __unused)300 servent_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);
323 errfin:
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 */
334 static int
servent_check_ambiguity(struct servent_test_data * td,struct servent * serv)335 servent_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 
342 static int
servent_test_getservbyname(struct servent * serv_model,void * mdata)343 servent_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 
375 errfin:
376 	printf("not ok\n");
377 
378 	return (-1);
379 }
380 
381 static int
servent_test_getservbyport(struct servent * serv_model,void * mdata)382 servent_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 
402 static int
servent_test_getservent(struct servent * serv,void * mdata __unused)403 servent_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 
410 static int
run_tests(const char * snapshot_file,enum test_methods method)411 run_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 
486 fin:
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 
495 ATF_TC_WITHOUT_HEAD(build_snapshot);
ATF_TC_BODY(build_snapshot,tc)496 ATF_TC_BODY(build_snapshot, tc)
497 {
498 
499 	ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
500 }
501 
502 ATF_TC_WITHOUT_HEAD(getservbyname);
ATF_TC_BODY(getservbyname,tc)503 ATF_TC_BODY(getservbyname, tc)
504 {
505 
506 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYNAME) == 0);
507 }
508 
509 ATF_TC_WITHOUT_HEAD(getservbyname_with_snapshot);
ATF_TC_BODY(getservbyname_with_snapshot,tc)510 ATF_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 
517 ATF_TC_WITHOUT_HEAD(getservbyport);
ATF_TC_BODY(getservbyport,tc)518 ATF_TC_BODY(getservbyport, tc)
519 {
520 
521 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYPORT) == 0);
522 }
523 
524 ATF_TC_WITHOUT_HEAD(getservbyport_with_snapshot);
ATF_TC_BODY(getservbyport_with_snapshot,tc)525 ATF_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 
532 ATF_TC_WITHOUT_HEAD(getservbyent);
ATF_TC_BODY(getservbyent,tc)533 ATF_TC_BODY(getservbyent, tc)
534 {
535 
536 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT) == 0);
537 }
538 
539 ATF_TC_WITHOUT_HEAD(getservbyent_with_snapshot);
ATF_TC_BODY(getservbyent_with_snapshot,tc)540 ATF_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 
547 ATF_TC_WITHOUT_HEAD(getservbyent_with_two_pass);
ATF_TC_BODY(getservbyent_with_two_pass,tc)548 ATF_TC_BODY(getservbyent_with_two_pass, tc)
549 {
550 
551 	ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT_2PASS) == 0);
552 }
553 
ATF_TP_ADD_TCS(tp)554 ATF_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