1 /*-
2 * Copyright (c) 2006 Michael Bushkov <[email protected]>
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)
IMPLEMENT_TEST_FILE_SNAPSHOT(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
free_servent(struct servent * serv)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
compare_servent(struct servent * serv1,struct servent * serv2,void * mdata)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 != '\0') || (*c2 != '\0'))
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
sdump_servent(struct servent * serv,char * buffer,size_t buflen)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) != '\0') {
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
servent_read_snapshot_func(struct servent * serv,char * line)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
dump_servent(struct servent * result)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
servent_fill_test_data(struct servent_test_data * td)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
servent_test_correctness(struct servent * serv,void * mdata __unused)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
servent_check_ambiguity(struct servent_test_data * td,struct servent * serv)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
servent_test_getservbyname(struct servent * serv_model,void * mdata)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
servent_test_getservbyport(struct servent * serv_model,void * mdata)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
servent_test_getservent(struct servent * serv,void * mdata __unused)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
run_tests(const char * snapshot_file,enum test_methods method)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);
ATF_TC_BODY(build_snapshot,tc)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);
ATF_TC_BODY(getservbyname,tc)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);
ATF_TC_BODY(getservbyname_with_snapshot,tc)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);
ATF_TC_BODY(getservbyport,tc)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);
ATF_TC_BODY(getservbyport_with_snapshot,tc)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);
ATF_TC_BODY(getservbyent,tc)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);
ATF_TC_BODY(getservbyent_with_snapshot,tc)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);
ATF_TC_BODY(getservbyent_with_two_pass,tc)551 ATF_TC_BODY(getservbyent_with_two_pass, tc)
552 {
553
554 ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT_2PASS) == 0);
555 }
556
ATF_TP_ADD_TCS(tp)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