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 <sys/cdefs.h> 29 #include <arpa/inet.h> 30 #include <errno.h> 31 #include <netdb.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <stringlist.h> 36 #include <unistd.h> 37 38 #include <atf-c.h> 39 40 #include "testutil.h" 41 42 enum test_methods { 43 TEST_GETSERVENT, 44 TEST_GETSERVBYNAME, 45 TEST_GETSERVBYPORT, 46 TEST_GETSERVENT_2PASS, 47 TEST_BUILD_SNAPSHOT 48 }; 49 50 DECLARE_TEST_DATA(servent) 51 DECLARE_TEST_FILE_SNAPSHOT(servent) 52 DECLARE_1PASS_TEST(servent) 53 DECLARE_2PASS_TEST(servent) 54 55 static void clone_servent(struct servent *, struct servent const *); 56 static int compare_servent(struct servent *, struct servent *, void *); 57 static void dump_servent(struct servent *); 58 static void free_servent(struct servent *); 59 60 static void sdump_servent(struct servent *, char *, size_t); 61 static int servent_read_snapshot_func(struct servent *, char *); 62 63 static int servent_check_ambiguity(struct servent_test_data *, 64 struct servent *); 65 static int servent_fill_test_data(struct servent_test_data *); 66 static int servent_test_correctness(struct servent *, void *); 67 static int servent_test_getservbyname(struct servent *, void *); 68 static int servent_test_getservbyport(struct servent *, void *); 69 static int servent_test_getservent(struct servent *, void *); 70 71 IMPLEMENT_TEST_DATA(servent) 72 IMPLEMENT_TEST_FILE_SNAPSHOT(servent) 73 IMPLEMENT_1PASS_TEST(servent) 74 IMPLEMENT_2PASS_TEST(servent) 75 76 static void 77 clone_servent(struct servent *dest, struct servent const *src) 78 { 79 ATF_REQUIRE(dest != NULL); 80 ATF_REQUIRE(src != NULL); 81 82 char **cp; 83 int aliases_num; 84 85 memset(dest, 0, sizeof(struct servent)); 86 87 if (src->s_name != NULL) { 88 dest->s_name = strdup(src->s_name); 89 ATF_REQUIRE(dest->s_name != NULL); 90 } 91 92 if (src->s_proto != NULL) { 93 dest->s_proto = strdup(src->s_proto); 94 ATF_REQUIRE(dest->s_proto != NULL); 95 } 96 dest->s_port = src->s_port; 97 98 if (src->s_aliases != NULL) { 99 aliases_num = 0; 100 for (cp = src->s_aliases; *cp; ++cp) 101 ++aliases_num; 102 103 dest->s_aliases = calloc(aliases_num + 1, sizeof(char *)); 104 ATF_REQUIRE(dest->s_aliases != NULL); 105 106 for (cp = src->s_aliases; *cp; ++cp) { 107 dest->s_aliases[cp - src->s_aliases] = strdup(*cp); 108 ATF_REQUIRE(dest->s_aliases[cp - src->s_aliases] != NULL); 109 } 110 } 111 } 112 113 static void 114 free_servent(struct servent *serv) 115 { 116 char **cp; 117 118 ATF_REQUIRE(serv != NULL); 119 120 free(serv->s_name); 121 free(serv->s_proto); 122 123 for (cp = serv->s_aliases; *cp; ++cp) 124 free(*cp); 125 free(serv->s_aliases); 126 } 127 128 static int 129 compare_servent(struct servent *serv1, struct servent *serv2, void *mdata) 130 { 131 char **c1, **c2; 132 133 if (serv1 == serv2) 134 return 0; 135 136 if ((serv1 == NULL) || (serv2 == NULL)) 137 goto errfin; 138 139 if ((strcmp(serv1->s_name, serv2->s_name) != 0) || 140 (strcmp(serv1->s_proto, serv2->s_proto) != 0) || 141 (serv1->s_port != serv2->s_port)) 142 goto errfin; 143 144 c1 = serv1->s_aliases; 145 c2 = serv2->s_aliases; 146 147 if ((serv1->s_aliases == NULL) || (serv2->s_aliases == NULL)) 148 goto errfin; 149 150 for (;*c1 && *c2; ++c1, ++c2) 151 if (strcmp(*c1, *c2) != 0) 152 goto errfin; 153 154 if ((*c1 != NULL) || (*c2 != NULL)) 155 goto errfin; 156 157 return 0; 158 159 errfin: 160 if (mdata == NULL) { 161 printf("following structures are not equal:\n"); 162 dump_servent(serv1); 163 dump_servent(serv2); 164 } 165 166 return (-1); 167 } 168 169 static void 170 sdump_servent(struct servent *serv, char *buffer, size_t buflen) 171 { 172 char **cp; 173 int written; 174 175 written = snprintf(buffer, buflen, "%s %d %s", 176 serv->s_name, ntohs(serv->s_port), serv->s_proto); 177 buffer += written; 178 if (written > (int)buflen) 179 return; 180 buflen -= written; 181 182 if (serv->s_aliases != NULL) { 183 if (*(serv->s_aliases) != NULL) { 184 for (cp = serv->s_aliases; *cp; ++cp) { 185 written = snprintf(buffer, buflen, " %s", *cp); 186 buffer += written; 187 if (written > (int)buflen) 188 return; 189 buflen -= written; 190 191 if (buflen == 0) 192 return; 193 } 194 } else 195 snprintf(buffer, buflen, " noaliases"); 196 } else 197 snprintf(buffer, buflen, " (null)"); 198 } 199 200 static int 201 servent_read_snapshot_func(struct servent *serv, char *line) 202 { 203 StringList *sl; 204 char *s, *ps, *ts; 205 int i; 206 207 printf("1 line read from snapshot:\n%s\n", line); 208 209 i = 0; 210 sl = NULL; 211 ps = line; 212 memset(serv, 0, sizeof(struct servent)); 213 while ( (s = strsep(&ps, " ")) != NULL) { 214 switch (i) { 215 case 0: 216 serv->s_name = strdup(s); 217 ATF_REQUIRE(serv->s_name != NULL); 218 break; 219 220 case 1: 221 serv->s_port = htons( 222 (int)strtol(s, &ts, 10)); 223 if (*ts != '\0') { 224 free(serv->s_name); 225 return (-1); 226 } 227 break; 228 229 case 2: 230 serv->s_proto = strdup(s); 231 ATF_REQUIRE(serv->s_proto != NULL); 232 break; 233 234 default: 235 if (sl == NULL) { 236 if (strcmp(s, "(null)") == 0) 237 return (0); 238 239 sl = sl_init(); 240 ATF_REQUIRE(sl != NULL); 241 242 if (strcmp(s, "noaliases") != 0) { 243 ts = strdup(s); 244 ATF_REQUIRE(ts != NULL); 245 sl_add(sl, ts); 246 } 247 } else { 248 ts = strdup(s); 249 ATF_REQUIRE(ts != NULL); 250 sl_add(sl, ts); 251 } 252 break; 253 } 254 ++i; 255 } 256 257 if (i < 3) { 258 free(serv->s_name); 259 free(serv->s_proto); 260 memset(serv, 0, sizeof(struct servent)); 261 return (-1); 262 } 263 264 sl_add(sl, NULL); 265 serv->s_aliases = sl->sl_str; 266 267 /* NOTE: is it a dirty hack or not? */ 268 free(sl); 269 return (0); 270 } 271 272 static void 273 dump_servent(struct servent *result) 274 { 275 if (result != NULL) { 276 char buffer[1024]; 277 sdump_servent(result, buffer, sizeof(buffer)); 278 printf("%s\n", buffer); 279 } else 280 printf("(null)\n"); 281 } 282 283 static int 284 servent_fill_test_data(struct servent_test_data *td) 285 { 286 struct servent *serv; 287 288 setservent(1); 289 while ((serv = getservent()) != NULL) { 290 if (servent_test_correctness(serv, NULL) == 0) 291 TEST_DATA_APPEND(servent, td, serv); 292 else 293 return (-1); 294 } 295 endservent(); 296 297 return (0); 298 } 299 300 static int 301 servent_test_correctness(struct servent *serv, void *mdata __unused) 302 { 303 printf("testing correctness with the following data:\n"); 304 dump_servent(serv); 305 306 if (serv == NULL) 307 goto errfin; 308 309 if (serv->s_name == NULL) 310 goto errfin; 311 312 if (serv->s_proto == NULL) 313 goto errfin; 314 315 if (ntohs(serv->s_port < 0)) 316 goto errfin; 317 318 if (serv->s_aliases == NULL) 319 goto errfin; 320 321 printf("correct\n"); 322 323 return (0); 324 errfin: 325 printf("incorrect\n"); 326 327 return (-1); 328 } 329 330 /* servent_check_ambiguity() is needed when one port+proto is associated with 331 * more than one service (these cases are usually marked as PROBLEM in 332 * /etc/services. This functions is needed also when one service+proto is 333 * associated with several ports. We have to check all the servent structures 334 * to make sure that serv really exists and correct */ 335 static int 336 servent_check_ambiguity(struct servent_test_data *td, struct servent *serv) 337 { 338 339 return (TEST_DATA_FIND(servent, td, serv, compare_servent, 340 NULL) != NULL ? 0 : -1); 341 } 342 343 static int 344 servent_test_getservbyname(struct servent *serv_model, void *mdata) 345 { 346 char **alias; 347 struct servent *serv; 348 349 printf("testing getservbyname() with the following data:\n"); 350 dump_servent(serv_model); 351 352 serv = getservbyname(serv_model->s_name, serv_model->s_proto); 353 if (servent_test_correctness(serv, NULL) != 0) 354 goto errfin; 355 356 if ((compare_servent(serv, serv_model, NULL) != 0) && 357 (servent_check_ambiguity((struct servent_test_data *)mdata, serv) 358 !=0)) 359 goto errfin; 360 361 for (alias = serv_model->s_aliases; *alias; ++alias) { 362 serv = getservbyname(*alias, serv_model->s_proto); 363 364 if (servent_test_correctness(serv, NULL) != 0) 365 goto errfin; 366 367 if ((compare_servent(serv, serv_model, NULL) != 0) && 368 (servent_check_ambiguity( 369 (struct servent_test_data *)mdata, serv) != 0)) 370 goto errfin; 371 } 372 373 printf("ok\n"); 374 return (0); 375 376 errfin: 377 printf("not ok\n"); 378 379 return (-1); 380 } 381 382 static int 383 servent_test_getservbyport(struct servent *serv_model, void *mdata) 384 { 385 struct servent *serv; 386 387 printf("testing getservbyport() with the following data...\n"); 388 dump_servent(serv_model); 389 390 serv = getservbyport(serv_model->s_port, serv_model->s_proto); 391 if ((servent_test_correctness(serv, NULL) != 0) || 392 ((compare_servent(serv, serv_model, NULL) != 0) && 393 (servent_check_ambiguity((struct servent_test_data *)mdata, serv) 394 != 0))) { 395 printf("not ok\n"); 396 return (-1); 397 } else { 398 printf("ok\n"); 399 return (0); 400 } 401 } 402 403 static int 404 servent_test_getservent(struct servent *serv, void *mdata __unused) 405 { 406 /* Only correctness can be checked when doing 1-pass test for 407 * getservent(). */ 408 return (servent_test_correctness(serv, NULL)); 409 } 410 411 static int 412 run_tests(const char *snapshot_file, enum test_methods method) 413 { 414 struct servent_test_data td, td_snap, td_2pass; 415 int rv; 416 417 TEST_DATA_INIT(servent, &td, clone_servent, free_servent); 418 TEST_DATA_INIT(servent, &td_snap, clone_servent, free_servent); 419 if (snapshot_file != NULL) { 420 if (access(snapshot_file, W_OK | R_OK) != 0) { 421 if (errno == ENOENT) 422 method = TEST_BUILD_SNAPSHOT; 423 else { 424 printf("can't access the file %s\n", 425 snapshot_file); 426 427 rv = -1; 428 goto fin; 429 } 430 } else { 431 if (method == TEST_BUILD_SNAPSHOT) { 432 rv = 0; 433 goto fin; 434 } 435 436 TEST_SNAPSHOT_FILE_READ(servent, snapshot_file, 437 &td_snap, servent_read_snapshot_func); 438 } 439 } 440 441 rv = servent_fill_test_data(&td); 442 if (rv == -1) 443 return (-1); 444 switch (method) { 445 case TEST_GETSERVBYNAME: 446 if (snapshot_file == NULL) 447 rv = DO_1PASS_TEST(servent, &td, 448 servent_test_getservbyname, (void *)&td); 449 else 450 rv = DO_1PASS_TEST(servent, &td_snap, 451 servent_test_getservbyname, (void *)&td_snap); 452 break; 453 case TEST_GETSERVBYPORT: 454 if (snapshot_file == NULL) 455 rv = DO_1PASS_TEST(servent, &td, 456 servent_test_getservbyport, (void *)&td); 457 else 458 rv = DO_1PASS_TEST(servent, &td_snap, 459 servent_test_getservbyport, (void *)&td_snap); 460 break; 461 case TEST_GETSERVENT: 462 if (snapshot_file == NULL) 463 rv = DO_1PASS_TEST(servent, &td, servent_test_getservent, 464 (void *)&td); 465 else 466 rv = DO_2PASS_TEST(servent, &td, &td_snap, 467 compare_servent, NULL); 468 break; 469 case TEST_GETSERVENT_2PASS: 470 TEST_DATA_INIT(servent, &td_2pass, clone_servent, free_servent); 471 rv = servent_fill_test_data(&td_2pass); 472 if (rv != -1) 473 rv = DO_2PASS_TEST(servent, &td, &td_2pass, 474 compare_servent, NULL); 475 TEST_DATA_DESTROY(servent, &td_2pass); 476 break; 477 case TEST_BUILD_SNAPSHOT: 478 if (snapshot_file != NULL) 479 rv = TEST_SNAPSHOT_FILE_WRITE(servent, snapshot_file, &td, 480 sdump_servent); 481 break; 482 default: 483 rv = 0; 484 break; 485 } 486 487 fin: 488 TEST_DATA_DESTROY(servent, &td_snap); 489 TEST_DATA_DESTROY(servent, &td); 490 491 return (rv); 492 } 493 494 #define SNAPSHOT_FILE "snapshot_serv" 495 496 ATF_TC_WITHOUT_HEAD(build_snapshot); 497 ATF_TC_BODY(build_snapshot, tc) 498 { 499 500 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); 501 } 502 503 ATF_TC_WITHOUT_HEAD(getservbyname); 504 ATF_TC_BODY(getservbyname, tc) 505 { 506 507 ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYNAME) == 0); 508 } 509 510 ATF_TC_WITHOUT_HEAD(getservbyname_with_snapshot); 511 ATF_TC_BODY(getservbyname_with_snapshot, tc) 512 { 513 514 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); 515 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVBYNAME) == 0); 516 } 517 518 ATF_TC_WITHOUT_HEAD(getservbyport); 519 ATF_TC_BODY(getservbyport, tc) 520 { 521 522 ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYPORT) == 0); 523 } 524 525 ATF_TC_WITHOUT_HEAD(getservbyport_with_snapshot); 526 ATF_TC_BODY(getservbyport_with_snapshot, tc) 527 { 528 529 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); 530 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVBYPORT) == 0); 531 } 532 533 ATF_TC_WITHOUT_HEAD(getservbyent); 534 ATF_TC_BODY(getservbyent, tc) 535 { 536 537 ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT) == 0); 538 } 539 540 ATF_TC_WITHOUT_HEAD(getservbyent_with_snapshot); 541 ATF_TC_BODY(getservbyent_with_snapshot, tc) 542 { 543 544 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); 545 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVENT) == 0); 546 } 547 548 ATF_TC_WITHOUT_HEAD(getservbyent_with_two_pass); 549 ATF_TC_BODY(getservbyent_with_two_pass, tc) 550 { 551 552 ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT_2PASS) == 0); 553 } 554 555 ATF_TP_ADD_TCS(tp) 556 { 557 558 ATF_TP_ADD_TC(tp, build_snapshot); 559 ATF_TP_ADD_TC(tp, getservbyent); 560 ATF_TP_ADD_TC(tp, getservbyent_with_snapshot); 561 ATF_TP_ADD_TC(tp, getservbyent_with_two_pass); 562 ATF_TP_ADD_TC(tp, getservbyname); 563 ATF_TP_ADD_TC(tp, getservbyname_with_snapshot); 564 ATF_TP_ADD_TC(tp, getservbyport); 565 ATF_TP_ADD_TC(tp, getservbyport_with_snapshot); 566 567 return (atf_no_error()); 568 } 569