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