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 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
servent_test_correctness(struct servent * serv,void * mdata __unused)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
servent_check_ambiguity(struct servent_test_data * td,struct servent * serv)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
servent_test_getservbyname(struct servent * serv_model,void * mdata)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
servent_test_getservbyport(struct servent * serv_model,void * mdata)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
servent_test_getservent(struct servent * serv,void * mdata __unused)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
run_tests(const char * snapshot_file,enum test_methods method)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);
ATF_TC_BODY(build_snapshot,tc)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);
ATF_TC_BODY(getservbyname,tc)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);
ATF_TC_BODY(getservbyname_with_snapshot,tc)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);
ATF_TC_BODY(getservbyport,tc)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);
ATF_TC_BODY(getservbyport_with_snapshot,tc)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);
ATF_TC_BODY(getservbyent,tc)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);
ATF_TC_BODY(getservbyent_with_snapshot,tc)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);
ATF_TC_BODY(getservbyent_with_two_pass,tc)552 ATF_TC_BODY(getservbyent_with_two_pass, tc)
553 {
554
555 ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT_2PASS) == 0);
556 }
557
ATF_TP_ADD_TCS(tp)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