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