1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/socket.h> 34 #include <arpa/inet.h> 35 #include <db.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <limits.h> 39 #include <netdb.h> 40 #include <nsswitch.h> 41 #include <stdio.h> 42 #include <string.h> 43 #include <stdlib.h> 44 #include <unistd.h> 45 #ifdef YP 46 #include <rpc/rpc.h> 47 #include <rpcsvc/yp_prot.h> 48 #include <rpcsvc/ypclnt.h> 49 #endif 50 #include "namespace.h" 51 #include "reentrant.h" 52 #include "un-namespace.h" 53 #include "netdb_private.h" 54 #ifdef NS_CACHING 55 #include "nscache.h" 56 #endif 57 #include "nss_tls.h" 58 59 enum constants 60 { 61 SETSERVENT = 1, 62 ENDSERVENT = 2, 63 SERVENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */ 64 SERVENT_STORAGE_MAX = 1 << 20, /* 1 MByte */ 65 }; 66 67 struct servent_mdata 68 { 69 enum nss_lookup_type how; 70 int compat_mode; 71 }; 72 73 static const ns_src defaultsrc[] = { 74 { NSSRC_COMPAT, NS_SUCCESS }, 75 { NULL, 0 } 76 }; 77 78 static int servent_unpack(char *, struct servent *, char **, size_t, int *); 79 80 /* files backend declarations */ 81 struct files_state 82 { 83 FILE *fp; 84 int stayopen; 85 86 int compat_mode_active; 87 }; 88 static void files_endstate(void *); 89 NSS_TLS_HANDLING(files); 90 91 static int files_servent(void *, void *, va_list); 92 static int files_setservent(void *, void *, va_list); 93 94 /* db backend declarations */ 95 struct db_state 96 { 97 DB *db; 98 int stayopen; 99 int keynum; 100 }; 101 static void db_endstate(void *); 102 NSS_TLS_HANDLING(db); 103 104 static int db_servent(void *, void *, va_list); 105 static int db_setservent(void *, void *, va_list); 106 107 #ifdef YP 108 /* nis backend declarations */ 109 static int nis_servent(void *, void *, va_list); 110 static int nis_setservent(void *, void *, va_list); 111 112 struct nis_state 113 { 114 int yp_stepping; 115 char yp_domain[MAXHOSTNAMELEN]; 116 char *yp_key; 117 int yp_keylen; 118 }; 119 static void nis_endstate(void *); 120 NSS_TLS_HANDLING(nis); 121 122 static int nis_servent(void *, void *, va_list); 123 static int nis_setservent(void *, void *, va_list); 124 #endif 125 126 /* compat backend declarations */ 127 static int compat_setservent(void *, void *, va_list); 128 129 /* get** wrappers for get**_r functions declarations */ 130 struct servent_state { 131 struct servent serv; 132 char *buffer; 133 size_t bufsize; 134 }; 135 static void servent_endstate(void *); 136 NSS_TLS_HANDLING(servent); 137 138 struct key { 139 const char *proto; 140 union { 141 const char *name; 142 int port; 143 }; 144 }; 145 146 static int wrap_getservbyname_r(struct key, struct servent *, char *, size_t, 147 struct servent **); 148 static int wrap_getservbyport_r(struct key, struct servent *, char *, size_t, 149 struct servent **); 150 static int wrap_getservent_r(struct key, struct servent *, char *, size_t, 151 struct servent **); 152 static struct servent *getserv(int (*fn)(struct key, struct servent *, char *, 153 size_t, struct servent **), struct key); 154 155 #ifdef NS_CACHING 156 static int serv_id_func(char *, size_t *, va_list, void *); 157 static int serv_marshal_func(char *, size_t *, void *, va_list, void *); 158 static int serv_unmarshal_func(char *, size_t, void *, va_list, void *); 159 #endif 160 161 static int 162 servent_unpack(char *p, struct servent *serv, char **aliases, 163 size_t aliases_size, int *errnop) 164 { 165 char *cp, **q, *endp; 166 long l; 167 168 if (*p == '#') 169 return -1; 170 171 memset(serv, 0, sizeof(struct servent)); 172 173 cp = strpbrk(p, "#\n"); 174 if (cp != NULL) 175 *cp = '\0'; 176 serv->s_name = p; 177 178 p = strpbrk(p, " \t"); 179 if (p == NULL) 180 return -1; 181 *p++ = '\0'; 182 while (*p == ' ' || *p == '\t') 183 p++; 184 cp = strpbrk(p, ",/"); 185 if (cp == NULL) 186 return -1; 187 188 *cp++ = '\0'; 189 l = strtol(p, &endp, 10); 190 if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX) 191 return -1; 192 serv->s_port = htons((in_port_t)l); 193 serv->s_proto = cp; 194 195 q = serv->s_aliases = aliases; 196 cp = strpbrk(cp, " \t"); 197 if (cp != NULL) 198 *cp++ = '\0'; 199 while (cp && *cp) { 200 if (*cp == ' ' || *cp == '\t') { 201 cp++; 202 continue; 203 } 204 if (q < &aliases[aliases_size - 1]) { 205 *q++ = cp; 206 } else { 207 *q = NULL; 208 *errnop = ERANGE; 209 return -1; 210 } 211 cp = strpbrk(cp, " \t"); 212 if (cp != NULL) 213 *cp++ = '\0'; 214 } 215 *q = NULL; 216 217 return 0; 218 } 219 220 static int 221 parse_result(struct servent *serv, char *buffer, size_t bufsize, 222 char *resultbuf, size_t resultbuflen, int *errnop) 223 { 224 char **aliases; 225 int aliases_size; 226 227 if (bufsize <= resultbuflen + _ALIGNBYTES + sizeof(char *)) { 228 *errnop = ERANGE; 229 return (NS_RETURN); 230 } 231 aliases = (char **)_ALIGN(&buffer[resultbuflen + 1]); 232 aliases_size = (buffer + bufsize - (char *)aliases) / sizeof(char *); 233 if (aliases_size < 1) { 234 *errnop = ERANGE; 235 return (NS_RETURN); 236 } 237 238 memcpy(buffer, resultbuf, resultbuflen); 239 buffer[resultbuflen] = '\0'; 240 241 if (servent_unpack(buffer, serv, aliases, aliases_size, errnop) != 0) 242 return ((*errnop == 0) ? NS_NOTFOUND : NS_RETURN); 243 return (NS_SUCCESS); 244 } 245 246 /* files backend implementation */ 247 static void 248 files_endstate(void *p) 249 { 250 FILE * f; 251 252 if (p == NULL) 253 return; 254 255 f = ((struct files_state *)p)->fp; 256 if (f != NULL) 257 fclose(f); 258 259 free(p); 260 } 261 262 /* 263 * compat structures. compat and files sources functionalities are almost 264 * equal, so they all are managed by files_servent function 265 */ 266 static int 267 files_servent(void *retval, void *mdata, va_list ap) 268 { 269 static const ns_src compat_src[] = { 270 #ifdef YP 271 { NSSRC_NIS, NS_SUCCESS }, 272 #endif 273 { NULL, 0 } 274 }; 275 ns_dtab compat_dtab[] = { 276 { NSSRC_DB, db_servent, 277 (void *)((struct servent_mdata *)mdata)->how }, 278 #ifdef YP 279 { NSSRC_NIS, nis_servent, 280 (void *)((struct servent_mdata *)mdata)->how }, 281 #endif 282 { NULL, NULL, NULL } 283 }; 284 285 struct files_state *st; 286 int rv; 287 int stayopen; 288 289 struct servent_mdata *serv_mdata; 290 char *name; 291 char *proto; 292 int port; 293 294 struct servent *serv; 295 char *buffer; 296 size_t bufsize; 297 int *errnop; 298 299 size_t linesize; 300 char *line; 301 char **cp; 302 303 name = NULL; 304 proto = NULL; 305 serv_mdata = (struct servent_mdata *)mdata; 306 switch (serv_mdata->how) { 307 case nss_lt_name: 308 name = va_arg(ap, char *); 309 proto = va_arg(ap, char *); 310 break; 311 case nss_lt_id: 312 port = va_arg(ap, int); 313 proto = va_arg(ap, char *); 314 break; 315 case nss_lt_all: 316 break; 317 default: 318 return NS_NOTFOUND; 319 } 320 321 serv = va_arg(ap, struct servent *); 322 buffer = va_arg(ap, char *); 323 bufsize = va_arg(ap, size_t); 324 errnop = va_arg(ap,int *); 325 326 *errnop = files_getstate(&st); 327 if (*errnop != 0) 328 return (NS_UNAVAIL); 329 330 if (st->fp == NULL) 331 st->compat_mode_active = 0; 332 333 if (st->fp == NULL && (st->fp = fopen(_PATH_SERVICES, "re")) == NULL) { 334 *errnop = errno; 335 return (NS_UNAVAIL); 336 } 337 338 if (serv_mdata->how == nss_lt_all) 339 stayopen = 1; 340 else { 341 rewind(st->fp); 342 stayopen = st->stayopen; 343 } 344 345 rv = NS_NOTFOUND; 346 do { 347 if (!st->compat_mode_active) { 348 if ((line = fgetln(st->fp, &linesize)) == NULL) { 349 *errnop = errno; 350 rv = NS_RETURN; 351 break; 352 } 353 354 if (*line=='+' && serv_mdata->compat_mode != 0) 355 st->compat_mode_active = 1; 356 } 357 358 if (st->compat_mode_active != 0) { 359 switch (serv_mdata->how) { 360 case nss_lt_name: 361 rv = nsdispatch(retval, compat_dtab, 362 NSDB_SERVICES_COMPAT, "getservbyname_r", 363 compat_src, name, proto, serv, buffer, 364 bufsize, errnop); 365 break; 366 case nss_lt_id: 367 rv = nsdispatch(retval, compat_dtab, 368 NSDB_SERVICES_COMPAT, "getservbyport_r", 369 compat_src, port, proto, serv, buffer, 370 bufsize, errnop); 371 break; 372 case nss_lt_all: 373 rv = nsdispatch(retval, compat_dtab, 374 NSDB_SERVICES_COMPAT, "getservent_r", 375 compat_src, serv, buffer, bufsize, errnop); 376 break; 377 } 378 379 if (!(rv & NS_TERMINATE) || 380 serv_mdata->how != nss_lt_all) 381 st->compat_mode_active = 0; 382 383 continue; 384 } 385 386 rv = parse_result(serv, buffer, bufsize, line, linesize, 387 errnop); 388 if (rv == NS_NOTFOUND) 389 continue; 390 if (rv == NS_RETURN) 391 break; 392 393 rv = NS_NOTFOUND; 394 switch (serv_mdata->how) { 395 case nss_lt_name: 396 if (strcmp(name, serv->s_name) == 0) 397 goto gotname; 398 for (cp = serv->s_aliases; *cp; cp++) 399 if (strcmp(name, *cp) == 0) 400 goto gotname; 401 402 continue; 403 gotname: 404 if (proto == NULL || strcmp(serv->s_proto, proto) == 0) 405 rv = NS_SUCCESS; 406 break; 407 case nss_lt_id: 408 if (port != serv->s_port) 409 continue; 410 411 if (proto == NULL || strcmp(serv->s_proto, proto) == 0) 412 rv = NS_SUCCESS; 413 break; 414 case nss_lt_all: 415 rv = NS_SUCCESS; 416 break; 417 } 418 419 } while (!(rv & NS_TERMINATE)); 420 421 if (!stayopen && st->fp != NULL) { 422 fclose(st->fp); 423 st->fp = NULL; 424 } 425 426 if ((rv == NS_SUCCESS) && (retval != NULL)) 427 *(struct servent **)retval=serv; 428 429 return (rv); 430 } 431 432 static int 433 files_setservent(void *retval, void *mdata, va_list ap) 434 { 435 struct files_state *st; 436 int rv; 437 int f; 438 439 rv = files_getstate(&st); 440 if (rv != 0) 441 return (NS_UNAVAIL); 442 443 switch ((enum constants)(uintptr_t)mdata) { 444 case SETSERVENT: 445 f = va_arg(ap,int); 446 if (st->fp == NULL) 447 st->fp = fopen(_PATH_SERVICES, "re"); 448 else 449 rewind(st->fp); 450 st->stayopen |= f; 451 break; 452 case ENDSERVENT: 453 if (st->fp != NULL) { 454 fclose(st->fp); 455 st->fp = NULL; 456 } 457 st->stayopen = 0; 458 break; 459 default: 460 break; 461 } 462 463 st->compat_mode_active = 0; 464 return (NS_UNAVAIL); 465 } 466 467 /* db backend implementation */ 468 static void 469 db_endstate(void *p) 470 { 471 DB *db; 472 473 if (p == NULL) 474 return; 475 476 db = ((struct db_state *)p)->db; 477 if (db != NULL) 478 db->close(db); 479 480 free(p); 481 } 482 483 static int 484 db_servent(void *retval, void *mdata, va_list ap) 485 { 486 char buf[BUFSIZ]; 487 DBT key, data, *result; 488 DB *db; 489 490 struct db_state *st; 491 int rv; 492 int stayopen; 493 494 enum nss_lookup_type how; 495 char *name; 496 char *proto; 497 int port; 498 499 struct servent *serv; 500 char *buffer; 501 size_t bufsize; 502 int *errnop; 503 504 name = NULL; 505 proto = NULL; 506 how = (enum nss_lookup_type)(uintptr_t)mdata; 507 switch (how) { 508 case nss_lt_name: 509 name = va_arg(ap, char *); 510 proto = va_arg(ap, char *); 511 break; 512 case nss_lt_id: 513 port = va_arg(ap, int); 514 proto = va_arg(ap, char *); 515 break; 516 case nss_lt_all: 517 break; 518 default: 519 return NS_NOTFOUND; 520 } 521 522 serv = va_arg(ap, struct servent *); 523 buffer = va_arg(ap, char *); 524 bufsize = va_arg(ap, size_t); 525 errnop = va_arg(ap,int *); 526 527 *errnop = db_getstate(&st); 528 if (*errnop != 0) 529 return (NS_UNAVAIL); 530 531 if (how == nss_lt_all && st->keynum < 0) 532 return (NS_NOTFOUND); 533 534 if (st->db == NULL) { 535 st->db = dbopen(_PATH_SERVICES_DB, O_RDONLY, 0, DB_HASH, NULL); 536 if (st->db == NULL) { 537 *errnop = errno; 538 return (NS_UNAVAIL); 539 } 540 } 541 542 stayopen = (how == nss_lt_all) ? 1 : st->stayopen; 543 db = st->db; 544 545 do { 546 switch (how) { 547 case nss_lt_name: 548 key.data = buf; 549 if (proto == NULL) 550 key.size = snprintf(buf, sizeof(buf), 551 "\376%s", name); 552 else 553 key.size = snprintf(buf, sizeof(buf), 554 "\376%s/%s", name, proto); 555 key.size++; 556 if (db->get(db, &key, &data, 0) != 0 || 557 db->get(db, &data, &key, 0) != 0) { 558 rv = NS_NOTFOUND; 559 goto db_fin; 560 } 561 result = &key; 562 break; 563 case nss_lt_id: 564 key.data = buf; 565 port = htons(port); 566 if (proto == NULL) 567 key.size = snprintf(buf, sizeof(buf), 568 "\377%d", port); 569 else 570 key.size = snprintf(buf, sizeof(buf), 571 "\377%d/%s", port, proto); 572 key.size++; 573 if (db->get(db, &key, &data, 0) != 0 || 574 db->get(db, &data, &key, 0) != 0) { 575 rv = NS_NOTFOUND; 576 goto db_fin; 577 } 578 result = &key; 579 break; 580 case nss_lt_all: 581 key.data = buf; 582 key.size = snprintf(buf, sizeof(buf), "%d", 583 st->keynum++); 584 key.size++; 585 if (db->get(db, &key, &data, 0) != 0) { 586 st->keynum = -1; 587 rv = NS_NOTFOUND; 588 goto db_fin; 589 } 590 result = &data; 591 break; 592 } 593 594 rv = parse_result(serv, buffer, bufsize, result->data, 595 result->size - 1, errnop); 596 597 } while (!(rv & NS_TERMINATE) && how == nss_lt_all); 598 599 db_fin: 600 if (!stayopen && st->db != NULL) { 601 db->close(db); 602 st->db = NULL; 603 } 604 605 if (rv == NS_SUCCESS && retval != NULL) 606 *(struct servent **)retval = serv; 607 608 return (rv); 609 } 610 611 static int 612 db_setservent(void *retval, void *mdata, va_list ap) 613 { 614 DB *db; 615 struct db_state *st; 616 int rv; 617 int f; 618 619 rv = db_getstate(&st); 620 if (rv != 0) 621 return (NS_UNAVAIL); 622 623 switch ((enum constants)(uintptr_t)mdata) { 624 case SETSERVENT: 625 f = va_arg(ap, int); 626 st->stayopen |= f; 627 st->keynum = 0; 628 break; 629 case ENDSERVENT: 630 db = st->db; 631 if (db != NULL) { 632 db->close(db); 633 st->db = NULL; 634 } 635 st->stayopen = 0; 636 break; 637 default: 638 break; 639 } 640 641 return (NS_UNAVAIL); 642 } 643 644 /* nis backend implementation */ 645 #ifdef YP 646 static void 647 nis_endstate(void *p) 648 { 649 if (p == NULL) 650 return; 651 652 free(((struct nis_state *)p)->yp_key); 653 free(p); 654 } 655 656 static int 657 nis_servent(void *retval, void *mdata, va_list ap) 658 { 659 char *resultbuf, *lastkey; 660 int resultbuflen; 661 char *buf; 662 663 struct nis_state *st; 664 int rv; 665 666 enum nss_lookup_type how; 667 char *name; 668 char *proto; 669 int port; 670 671 struct servent *serv; 672 char *buffer; 673 size_t bufsize; 674 int *errnop; 675 676 name = NULL; 677 proto = NULL; 678 buf = NULL; 679 how = (enum nss_lookup_type)(uintptr_t)mdata; 680 switch (how) { 681 case nss_lt_name: 682 name = va_arg(ap, char *); 683 proto = va_arg(ap, char *); 684 break; 685 case nss_lt_id: 686 port = va_arg(ap, int); 687 proto = va_arg(ap, char *); 688 break; 689 case nss_lt_all: 690 break; 691 default: 692 return NS_NOTFOUND; 693 } 694 695 serv = va_arg(ap, struct servent *); 696 buffer = va_arg(ap, char *); 697 bufsize = va_arg(ap, size_t); 698 errnop = va_arg(ap, int *); 699 700 *errnop = nis_getstate(&st); 701 if (*errnop != 0) 702 return (NS_UNAVAIL); 703 704 if (st->yp_domain[0] == '\0') { 705 if (getdomainname(st->yp_domain, sizeof st->yp_domain)) { 706 *errnop = errno; 707 return (NS_UNAVAIL); 708 } 709 } 710 711 do { 712 switch (how) { 713 case nss_lt_name: 714 free(buf); 715 asprintf(&buf, "%s/%s", name, proto); 716 if (buf == NULL) 717 return (NS_TRYAGAIN); 718 if (yp_match(st->yp_domain, "services.byname", buf, 719 strlen(buf), &resultbuf, &resultbuflen)) { 720 rv = NS_NOTFOUND; 721 goto fin; 722 } 723 break; 724 case nss_lt_id: 725 free(buf); 726 asprintf(&buf, "%d/%s", ntohs(port), proto); 727 if (buf == NULL) 728 return (NS_TRYAGAIN); 729 730 /* 731 * We have to be a little flexible 732 * here. Ideally you're supposed to have both 733 * a services.byname and a services.byport 734 * map, but some systems have only 735 * services.byname. FreeBSD cheats a little by 736 * putting the services.byport information in 737 * the same map as services.byname so that 738 * either case will work. We allow for both 739 * possibilities here: if there is no 740 * services.byport map, we try services.byname 741 * instead. 742 */ 743 rv = yp_match(st->yp_domain, "services.byport", buf, 744 strlen(buf), &resultbuf, &resultbuflen); 745 if (rv) { 746 if (rv == YPERR_MAP) { 747 if (yp_match(st->yp_domain, 748 "services.byname", buf, 749 strlen(buf), &resultbuf, 750 &resultbuflen)) { 751 rv = NS_NOTFOUND; 752 goto fin; 753 } 754 } else { 755 rv = NS_NOTFOUND; 756 goto fin; 757 } 758 } 759 760 break; 761 case nss_lt_all: 762 if (!st->yp_stepping) { 763 free(st->yp_key); 764 rv = yp_first(st->yp_domain, "services.byname", 765 &st->yp_key, &st->yp_keylen, &resultbuf, 766 &resultbuflen); 767 if (rv) { 768 rv = NS_NOTFOUND; 769 goto fin; 770 } 771 st->yp_stepping = 1; 772 } else { 773 lastkey = st->yp_key; 774 rv = yp_next(st->yp_domain, "services.byname", 775 st->yp_key, st->yp_keylen, &st->yp_key, 776 &st->yp_keylen, &resultbuf, &resultbuflen); 777 free(lastkey); 778 if (rv) { 779 st->yp_stepping = 0; 780 rv = NS_NOTFOUND; 781 goto fin; 782 } 783 } 784 break; 785 } 786 787 rv = parse_result(serv, buffer, bufsize, resultbuf, 788 resultbuflen, errnop); 789 free(resultbuf); 790 791 } while (!(rv & NS_TERMINATE) && how == nss_lt_all); 792 793 fin: 794 free(buf); 795 if (rv == NS_SUCCESS && retval != NULL) 796 *(struct servent **)retval = serv; 797 798 return (rv); 799 } 800 801 static int 802 nis_setservent(void *result, void *mdata, va_list ap) 803 { 804 struct nis_state *st; 805 int rv; 806 807 rv = nis_getstate(&st); 808 if (rv != 0) 809 return (NS_UNAVAIL); 810 811 switch ((enum constants)(uintptr_t)mdata) { 812 case SETSERVENT: 813 case ENDSERVENT: 814 free(st->yp_key); 815 st->yp_key = NULL; 816 st->yp_stepping = 0; 817 break; 818 default: 819 break; 820 } 821 822 return (NS_UNAVAIL); 823 } 824 #endif 825 826 /* compat backend implementation */ 827 static int 828 compat_setservent(void *retval, void *mdata, va_list ap) 829 { 830 static const ns_src compat_src[] = { 831 #ifdef YP 832 { NSSRC_NIS, NS_SUCCESS }, 833 #endif 834 { NULL, 0 } 835 }; 836 ns_dtab compat_dtab[] = { 837 { NSSRC_DB, db_setservent, mdata }, 838 #ifdef YP 839 { NSSRC_NIS, nis_setservent, mdata }, 840 #endif 841 { NULL, NULL, NULL } 842 }; 843 int f; 844 845 (void)files_setservent(retval, mdata, ap); 846 847 switch ((enum constants)(uintptr_t)mdata) { 848 case SETSERVENT: 849 f = va_arg(ap,int); 850 (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, 851 "setservent", compat_src, f); 852 break; 853 case ENDSERVENT: 854 (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT, 855 "endservent", compat_src); 856 break; 857 default: 858 break; 859 } 860 861 return (NS_UNAVAIL); 862 } 863 864 #ifdef NS_CACHING 865 static int 866 serv_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata) 867 { 868 char *name; 869 char *proto; 870 int port; 871 872 size_t desired_size, size, size2; 873 enum nss_lookup_type lookup_type; 874 int res = NS_UNAVAIL; 875 876 lookup_type = (enum nss_lookup_type)(uintptr_t)cache_mdata; 877 switch (lookup_type) { 878 case nss_lt_name: 879 name = va_arg(ap, char *); 880 proto = va_arg(ap, char *); 881 882 size = strlen(name); 883 desired_size = sizeof(enum nss_lookup_type) + size + 1; 884 if (proto != NULL) { 885 size2 = strlen(proto); 886 desired_size += size2 + 1; 887 } else 888 size2 = 0; 889 890 if (desired_size > *buffer_size) { 891 res = NS_RETURN; 892 goto fin; 893 } 894 895 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); 896 memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1); 897 898 if (proto != NULL) 899 memcpy(buffer + sizeof(enum nss_lookup_type) + size + 1, 900 proto, size2 + 1); 901 902 res = NS_SUCCESS; 903 break; 904 case nss_lt_id: 905 port = va_arg(ap, int); 906 proto = va_arg(ap, char *); 907 908 desired_size = sizeof(enum nss_lookup_type) + sizeof(int); 909 if (proto != NULL) { 910 size = strlen(proto); 911 desired_size += size + 1; 912 } else 913 size = 0; 914 915 if (desired_size > *buffer_size) { 916 res = NS_RETURN; 917 goto fin; 918 } 919 920 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type)); 921 memcpy(buffer + sizeof(enum nss_lookup_type), &port, 922 sizeof(int)); 923 924 if (proto != NULL) 925 memcpy(buffer + sizeof(enum nss_lookup_type) + 926 sizeof(int), proto, size + 1); 927 928 res = NS_SUCCESS; 929 break; 930 default: 931 /* should be unreachable */ 932 return (NS_UNAVAIL); 933 } 934 935 fin: 936 *buffer_size = desired_size; 937 return (res); 938 } 939 940 static int 941 serv_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap, 942 void *cache_mdata) 943 { 944 char *name __unused; 945 char *proto __unused; 946 int port __unused; 947 struct servent *serv; 948 char *orig_buf __unused; 949 size_t orig_buf_size __unused; 950 951 struct servent new_serv; 952 size_t desired_size; 953 char **alias; 954 char *p; 955 size_t size; 956 size_t aliases_size; 957 958 switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { 959 case nss_lt_name: 960 name = va_arg(ap, char *); 961 proto = va_arg(ap, char *); 962 break; 963 case nss_lt_id: 964 port = va_arg(ap, int); 965 proto = va_arg(ap, char *); 966 break; 967 case nss_lt_all: 968 break; 969 default: 970 /* should be unreachable */ 971 return (NS_UNAVAIL); 972 } 973 974 serv = va_arg(ap, struct servent *); 975 orig_buf = va_arg(ap, char *); 976 orig_buf_size = va_arg(ap, size_t); 977 978 desired_size = _ALIGNBYTES + sizeof(struct servent) + sizeof(char *); 979 if (serv->s_name != NULL) 980 desired_size += strlen(serv->s_name) + 1; 981 if (serv->s_proto != NULL) 982 desired_size += strlen(serv->s_proto) + 1; 983 984 aliases_size = 0; 985 if (serv->s_aliases != NULL) { 986 for (alias = serv->s_aliases; *alias; ++alias) { 987 desired_size += strlen(*alias) + 1; 988 ++aliases_size; 989 } 990 991 desired_size += _ALIGNBYTES + 992 sizeof(char *) * (aliases_size + 1); 993 } 994 995 if (*buffer_size < desired_size) { 996 /* this assignment is here for future use */ 997 *buffer_size = desired_size; 998 return (NS_RETURN); 999 } 1000 1001 memcpy(&new_serv, serv, sizeof(struct servent)); 1002 memset(buffer, 0, desired_size); 1003 1004 *buffer_size = desired_size; 1005 p = buffer + sizeof(struct servent) + sizeof(char *); 1006 memcpy(buffer + sizeof(struct servent), &p, sizeof(char *)); 1007 p = (char *)_ALIGN(p); 1008 1009 if (new_serv.s_name != NULL) { 1010 size = strlen(new_serv.s_name); 1011 memcpy(p, new_serv.s_name, size); 1012 new_serv.s_name = p; 1013 p += size + 1; 1014 } 1015 1016 if (new_serv.s_proto != NULL) { 1017 size = strlen(new_serv.s_proto); 1018 memcpy(p, new_serv.s_proto, size); 1019 new_serv.s_proto = p; 1020 p += size + 1; 1021 } 1022 1023 if (new_serv.s_aliases != NULL) { 1024 p = (char *)_ALIGN(p); 1025 memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size); 1026 new_serv.s_aliases = (char **)p; 1027 p += sizeof(char *) * (aliases_size + 1); 1028 1029 for (alias = new_serv.s_aliases; *alias; ++alias) { 1030 size = strlen(*alias); 1031 memcpy(p, *alias, size); 1032 *alias = p; 1033 p += size + 1; 1034 } 1035 } 1036 1037 memcpy(buffer, &new_serv, sizeof(struct servent)); 1038 return (NS_SUCCESS); 1039 } 1040 1041 static int 1042 serv_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap, 1043 void *cache_mdata) 1044 { 1045 char *name __unused; 1046 char *proto __unused; 1047 int port __unused; 1048 struct servent *serv; 1049 char *orig_buf; 1050 char *p; 1051 char **alias; 1052 size_t orig_buf_size; 1053 int *ret_errno; 1054 1055 switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) { 1056 case nss_lt_name: 1057 name = va_arg(ap, char *); 1058 proto = va_arg(ap, char *); 1059 break; 1060 case nss_lt_id: 1061 port = va_arg(ap, int); 1062 proto = va_arg(ap, char *); 1063 break; 1064 case nss_lt_all: 1065 break; 1066 default: 1067 /* should be unreachable */ 1068 return (NS_UNAVAIL); 1069 } 1070 1071 serv = va_arg(ap, struct servent *); 1072 orig_buf = va_arg(ap, char *); 1073 orig_buf_size = va_arg(ap, size_t); 1074 ret_errno = va_arg(ap, int *); 1075 1076 if (orig_buf_size < 1077 buffer_size - sizeof(struct servent) - sizeof(char *)) { 1078 *ret_errno = ERANGE; 1079 return (NS_RETURN); 1080 } 1081 1082 memcpy(serv, buffer, sizeof(struct servent)); 1083 memcpy(&p, buffer + sizeof(struct servent), sizeof(char *)); 1084 1085 orig_buf = (char *)_ALIGN(orig_buf); 1086 memcpy(orig_buf, buffer + sizeof(struct servent) + sizeof(char *) + 1087 (_ALIGN(p) - (size_t)p), 1088 buffer_size - sizeof(struct servent) - sizeof(char *) - 1089 (_ALIGN(p) - (size_t)p)); 1090 p = (char *)_ALIGN(p); 1091 1092 NS_APPLY_OFFSET(serv->s_name, orig_buf, p, char *); 1093 NS_APPLY_OFFSET(serv->s_proto, orig_buf, p, char *); 1094 if (serv->s_aliases != NULL) { 1095 NS_APPLY_OFFSET(serv->s_aliases, orig_buf, p, char **); 1096 1097 for (alias = serv->s_aliases; *alias; ++alias) 1098 NS_APPLY_OFFSET(*alias, orig_buf, p, char *); 1099 } 1100 1101 if (retval != NULL) 1102 *((struct servent **)retval) = serv; 1103 return (NS_SUCCESS); 1104 } 1105 1106 NSS_MP_CACHE_HANDLING(services); 1107 #endif /* NS_CACHING */ 1108 1109 /* get**_r functions implementation */ 1110 int 1111 getservbyname_r(const char *name, const char *proto, struct servent *serv, 1112 char *buffer, size_t bufsize, struct servent **result) 1113 { 1114 static const struct servent_mdata mdata = { nss_lt_name, 0 }; 1115 static const struct servent_mdata compat_mdata = { nss_lt_name, 1 }; 1116 #ifdef NS_CACHING 1117 static const nss_cache_info cache_info = 1118 NS_COMMON_CACHE_INFO_INITIALIZER( 1119 services, (void *)nss_lt_name, 1120 serv_id_func, serv_marshal_func, serv_unmarshal_func); 1121 #endif /* NS_CACHING */ 1122 static const ns_dtab dtab[] = { 1123 { NSSRC_FILES, files_servent, (void *)&mdata }, 1124 { NSSRC_DB, db_servent, (void *)nss_lt_name }, 1125 #ifdef YP 1126 { NSSRC_NIS, nis_servent, (void *)nss_lt_name }, 1127 #endif 1128 { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, 1129 #ifdef NS_CACHING 1130 NS_CACHE_CB(&cache_info) 1131 #endif 1132 { NULL, NULL, NULL } 1133 }; 1134 int rv, ret_errno; 1135 1136 ret_errno = 0; 1137 *result = NULL; 1138 rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyname_r", 1139 defaultsrc, name, proto, serv, buffer, bufsize, &ret_errno); 1140 1141 if (rv == NS_SUCCESS) 1142 return (0); 1143 else 1144 return (ret_errno); 1145 } 1146 1147 int 1148 getservbyport_r(int port, const char *proto, struct servent *serv, 1149 char *buffer, size_t bufsize, struct servent **result) 1150 { 1151 static const struct servent_mdata mdata = { nss_lt_id, 0 }; 1152 static const struct servent_mdata compat_mdata = { nss_lt_id, 1 }; 1153 #ifdef NS_CACHING 1154 static const nss_cache_info cache_info = 1155 NS_COMMON_CACHE_INFO_INITIALIZER( 1156 services, (void *)nss_lt_id, 1157 serv_id_func, serv_marshal_func, serv_unmarshal_func); 1158 #endif 1159 static const ns_dtab dtab[] = { 1160 { NSSRC_FILES, files_servent, (void *)&mdata }, 1161 { NSSRC_DB, db_servent, (void *)nss_lt_id }, 1162 #ifdef YP 1163 { NSSRC_NIS, nis_servent, (void *)nss_lt_id }, 1164 #endif 1165 { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, 1166 #ifdef NS_CACHING 1167 NS_CACHE_CB(&cache_info) 1168 #endif 1169 { NULL, NULL, NULL } 1170 }; 1171 int rv, ret_errno; 1172 1173 ret_errno = 0; 1174 *result = NULL; 1175 rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyport_r", 1176 defaultsrc, port, proto, serv, buffer, bufsize, &ret_errno); 1177 1178 if (rv == NS_SUCCESS) 1179 return (0); 1180 else 1181 return (ret_errno); 1182 } 1183 1184 int 1185 getservent_r(struct servent *serv, char *buffer, size_t bufsize, 1186 struct servent **result) 1187 { 1188 static const struct servent_mdata mdata = { nss_lt_all, 0 }; 1189 static const struct servent_mdata compat_mdata = { nss_lt_all, 1 }; 1190 #ifdef NS_CACHING 1191 static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( 1192 services, (void *)nss_lt_all, 1193 serv_marshal_func, serv_unmarshal_func); 1194 #endif 1195 static const ns_dtab dtab[] = { 1196 { NSSRC_FILES, files_servent, (void *)&mdata }, 1197 { NSSRC_DB, db_servent, (void *)nss_lt_all }, 1198 #ifdef YP 1199 { NSSRC_NIS, nis_servent, (void *)nss_lt_all }, 1200 #endif 1201 { NSSRC_COMPAT, files_servent, (void *)&compat_mdata }, 1202 #ifdef NS_CACHING 1203 NS_CACHE_CB(&cache_info) 1204 #endif 1205 { NULL, NULL, NULL } 1206 }; 1207 int rv, ret_errno; 1208 1209 ret_errno = 0; 1210 *result = NULL; 1211 rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservent_r", 1212 defaultsrc, serv, buffer, bufsize, &ret_errno); 1213 1214 if (rv == NS_SUCCESS) 1215 return (0); 1216 else 1217 return (ret_errno); 1218 } 1219 1220 void 1221 setservent(int stayopen) 1222 { 1223 #ifdef NS_CACHING 1224 static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( 1225 services, (void *)nss_lt_all, 1226 NULL, NULL); 1227 #endif 1228 static const ns_dtab dtab[] = { 1229 { NSSRC_FILES, files_setservent, (void *)SETSERVENT }, 1230 { NSSRC_DB, db_setservent, (void *)SETSERVENT }, 1231 #ifdef YP 1232 { NSSRC_NIS, nis_setservent, (void *)SETSERVENT }, 1233 #endif 1234 { NSSRC_COMPAT, compat_setservent, (void *)SETSERVENT }, 1235 #ifdef NS_CACHING 1236 NS_CACHE_CB(&cache_info) 1237 #endif 1238 { NULL, NULL, NULL } 1239 }; 1240 1241 (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "setservent", defaultsrc, 1242 stayopen); 1243 } 1244 1245 void 1246 endservent(void) 1247 { 1248 #ifdef NS_CACHING 1249 static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER( 1250 services, (void *)nss_lt_all, 1251 NULL, NULL); 1252 #endif 1253 static const ns_dtab dtab[] = { 1254 { NSSRC_FILES, files_setservent, (void *)ENDSERVENT }, 1255 { NSSRC_DB, db_setservent, (void *)ENDSERVENT }, 1256 #ifdef YP 1257 { NSSRC_NIS, nis_setservent, (void *)ENDSERVENT }, 1258 #endif 1259 { NSSRC_COMPAT, compat_setservent, (void *)ENDSERVENT }, 1260 #ifdef NS_CACHING 1261 NS_CACHE_CB(&cache_info) 1262 #endif 1263 { NULL, NULL, NULL } 1264 }; 1265 1266 (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "endservent", defaultsrc); 1267 } 1268 1269 /* get** wrappers for get**_r functions implementation */ 1270 static void 1271 servent_endstate(void *p) 1272 { 1273 if (p == NULL) 1274 return; 1275 1276 free(((struct servent_state *)p)->buffer); 1277 free(p); 1278 } 1279 1280 static int 1281 wrap_getservbyname_r(struct key key, struct servent *serv, char *buffer, 1282 size_t bufsize, struct servent **res) 1283 { 1284 return (getservbyname_r(key.name, key.proto, serv, buffer, bufsize, 1285 res)); 1286 } 1287 1288 static int 1289 wrap_getservbyport_r(struct key key, struct servent *serv, char *buffer, 1290 size_t bufsize, struct servent **res) 1291 { 1292 return (getservbyport_r(key.port, key.proto, serv, buffer, bufsize, 1293 res)); 1294 } 1295 1296 static int 1297 wrap_getservent_r(struct key key, struct servent *serv, char *buffer, 1298 size_t bufsize, struct servent **res) 1299 { 1300 return (getservent_r(serv, buffer, bufsize, res)); 1301 } 1302 1303 static struct servent * 1304 getserv(int (*fn)(struct key, struct servent *, char *, size_t, 1305 struct servent **), struct key key) 1306 { 1307 int rv; 1308 struct servent *res; 1309 struct servent_state * st; 1310 1311 rv = servent_getstate(&st); 1312 if (rv != 0) { 1313 errno = rv; 1314 return NULL; 1315 } 1316 1317 if (st->buffer == NULL) { 1318 st->buffer = malloc(SERVENT_STORAGE_INITIAL); 1319 if (st->buffer == NULL) 1320 return (NULL); 1321 st->bufsize = SERVENT_STORAGE_INITIAL; 1322 } 1323 do { 1324 rv = fn(key, &st->serv, st->buffer, st->bufsize, &res); 1325 if (res == NULL && rv == ERANGE) { 1326 free(st->buffer); 1327 if ((st->bufsize << 1) > SERVENT_STORAGE_MAX) { 1328 st->buffer = NULL; 1329 errno = ERANGE; 1330 return (NULL); 1331 } 1332 st->bufsize <<= 1; 1333 st->buffer = malloc(st->bufsize); 1334 if (st->buffer == NULL) 1335 return (NULL); 1336 } 1337 } while (res == NULL && rv == ERANGE); 1338 if (rv != 0) 1339 errno = rv; 1340 1341 return (res); 1342 } 1343 1344 struct servent * 1345 getservbyname(const char *name, const char *proto) 1346 { 1347 struct key key; 1348 1349 key.name = name; 1350 key.proto = proto; 1351 1352 return (getserv(wrap_getservbyname_r, key)); 1353 } 1354 1355 struct servent * 1356 getservbyport(int port, const char *proto) 1357 { 1358 struct key key; 1359 1360 key.port = port; 1361 key.proto = proto; 1362 1363 return (getserv(wrap_getservbyport_r, key)); 1364 } 1365 1366 struct servent * 1367 getservent(void) 1368 { 1369 struct key key; 1370 1371 key.proto = NULL; 1372 key.port = 0; 1373 1374 return (getserv(wrap_getservent_r, key)); 1375 } 1376