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