1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * files/gethostent.c -- "files" backend for nsswitch "hosts" database 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <netdb.h> 31 #include "files_common.h" 32 #include <string.h> 33 #include <strings.h> 34 #include <stddef.h> 35 #include <stdlib.h> 36 #include <sys/types.h> 37 #include <sys/socket.h> 38 #include <netinet/in.h> 39 #include <arpa/nameser.h> 40 #include <arpa/inet.h> 41 #include <ctype.h> 42 43 static int check_name(nss_XbyY_args_t *, const char *, int, 44 int, const char **, int *, void *, int *); 45 static char *do_aliases(); 46 static char *strcasestr(const char *as1, const char *as2); 47 nss_status_t __nss_files_XY_hostbyname(); 48 int __nss_files_2herrno(); 49 static int __nss_files_get_addr(int, const char *, int, 50 void *, int, int *); 51 52 static int 53 check_name(nss_XbyY_args_t *argp, const char *line, int linelen, 54 int type, const char **namep, int *namelen, 55 void *addrp, int *addrsize) 56 { 57 const char *limit, *linep, *keyp, *addrstart; 58 int v6flag = 0, addrlen; 59 60 linep = line; 61 limit = line + linelen; 62 63 /* Address */ 64 addrstart = linep; 65 while (linep < limit && !isspace(*linep)) { 66 if (*linep == ':') 67 v6flag++; 68 linep++; 69 } 70 addrlen = linep - addrstart; 71 72 /* skip the delimiting spaces */ 73 while (linep < limit && isspace(*linep)) 74 linep++; 75 76 /* Canonical name */ 77 keyp = argp->key.name; 78 *namep = linep; 79 while (*keyp && linep < limit && !isspace(*linep) && 80 tolower(*keyp) == tolower(*linep)) { 81 keyp++; 82 linep++; 83 } 84 if (*keyp == '\0' && (linep == limit || isspace(*linep))) { 85 if (__nss_files_get_addr(type, addrstart, addrlen, 86 addrp, v6flag, addrsize)) { 87 *namelen = linep - *namep; 88 return (1); 89 } 90 } 91 while (linep < limit && !isspace(*linep)) 92 linep++; 93 *namelen = linep - *namep; 94 95 /* Aliases */ 96 while (linep < limit) { 97 /* skip the delimiting spaces */ 98 while (linep < limit && isspace(*linep)) 99 linep++; 100 101 /* compare name (case insensitive) */ 102 keyp = argp->key.name; 103 while (*keyp && linep < limit && !isspace(*linep) && 104 tolower(*keyp) == tolower(*linep)) { 105 keyp++; 106 linep++; 107 } 108 if (*keyp == '\0' && (linep == limit || isspace(*linep))) 109 return (__nss_files_get_addr(type, addrstart, addrlen, 110 addrp, v6flag, addrsize)); 111 112 /* skip remainder of alias, if any */ 113 while (linep < limit && !isspace(*linep)) 114 linep++; 115 } 116 return (0); 117 118 } 119 120 static nss_status_t 121 getbyname(be, a) 122 files_backend_ptr_t be; 123 void *a; 124 { 125 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 126 nss_status_t res; 127 128 res = __nss_files_XY_hostbyname(be, argp, argp->key.name, AF_INET); 129 if (res != NSS_SUCCESS) 130 argp->h_errno = __nss_files_2herrno(res); 131 return (res); 132 } 133 134 static int 135 __nss_files_get_addr(int af, const char *addrstart, int addrlen, 136 void *addrp, int v6flag, int *h_length) 137 { 138 struct in_addr addr_ipv4; 139 struct in6_addr *addrpv6; 140 in_addr_t *addrpv4; 141 char addrbuf[INET6_ADDRSTRLEN + 1]; 142 143 if (addrlen >= sizeof (addrbuf)) 144 return (0); 145 (void) memcpy(addrbuf, addrstart, addrlen); 146 addrbuf[addrlen] = '\0'; 147 148 if (af == AF_INET) { 149 addrpv4 = (in_addr_t *)addrp; 150 if ((*addrpv4 = inet_addr(addrbuf)) == 0xffffffffU) 151 return (0); 152 *h_length = sizeof (in_addr_t); 153 } else if (af == AF_INET6) { 154 addrpv6 = (struct in6_addr *)addrp; 155 if (v6flag) { 156 if (inet_pton(af, addrbuf, addrpv6) != 1) 157 return (0); 158 } else { 159 if ((addr_ipv4.s_addr = inet_addr(addrbuf)) == 160 0xffffffffU) 161 return (0); 162 IN6_INADDR_TO_V4MAPPED(&addr_ipv4, addrpv6); 163 } 164 *h_length = sizeof (struct in6_addr); 165 } else { 166 return (0); 167 } 168 return (1); 169 } 170 171 172 int 173 __nss_files_check_addr(int af, nss_XbyY_args_t *argp, const char *line, 174 int linelen) 175 { 176 const char *limit, *linep, *addrstart; 177 int v6flag = 0, addrlen, h_length; 178 in_addr_t addr_ipv4; 179 struct in6_addr addr_ipv6; 180 char *h_addrp; 181 182 /* Compare the address type */ 183 if (argp->key.hostaddr.type != af) 184 return (0); 185 186 /* Retrieve the address */ 187 if (af == AF_INET) 188 h_addrp = (char *)&addr_ipv4; 189 else 190 h_addrp = (char *)&addr_ipv6; 191 linep = line; 192 limit = line + linelen; 193 addrstart = linep; 194 while (linep < limit && !isspace(*linep)) { 195 if (*linep == ':') 196 v6flag++; 197 linep++; 198 } 199 addrlen = linep - addrstart; 200 if (__nss_files_get_addr(af, addrstart, addrlen, h_addrp, 201 v6flag, &h_length) == 0) 202 return (0); 203 204 /* Compare the address */ 205 return (h_length == argp->key.hostaddr.len && 206 memcmp(h_addrp, argp->key.hostaddr.addr, 207 argp->key.hostaddr.len) == 0); 208 } 209 210 static int 211 check_addr(nss_XbyY_args_t *argp, const char *line, int linelen) 212 { 213 return (__nss_files_check_addr(AF_INET, argp, line, linelen)); 214 } 215 216 static nss_status_t 217 getbyaddr(be, a) 218 files_backend_ptr_t be; 219 void *a; 220 { 221 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 222 nss_status_t res; 223 224 res = _nss_files_XY_all(be, argp, 1, 0, check_addr); 225 if (res != NSS_SUCCESS) 226 argp->h_errno = __nss_files_2herrno(res); 227 return (res); 228 } 229 230 int _inet_aton(const char *cp, struct in_addr *addr); 231 /* 232 * filter_ipv6 233 * 234 * Return - NSS_STR_PARSE_SUCCESS: An IPv4 address 235 * NSS_STR_PARSE_PARSE: An IPv6 address or other errors 236 */ 237 static int 238 filter_ipv6(char *instr, int lenstr) { 239 char *p, *addrstart, *limit, c; 240 int rc; 241 struct in_addr addr; 242 243 p = instr; 244 limit = p + lenstr; 245 246 addrstart = p; 247 248 /* parse IP address */ 249 while (p < limit && !isspace(*p)) { 250 if (*p == ':') 251 /* IPv6 */ 252 return (NSS_STR_PARSE_PARSE); 253 else 254 p++; 255 } 256 257 if (p >= limit) 258 /* invalid IP */ 259 return (NSS_STR_PARSE_PARSE); 260 261 /* extract IP address */ 262 c = *p; 263 *p = '\0'; 264 rc = _inet_aton(addrstart, &addr); 265 *p = c; 266 267 if (rc == 0) 268 /* invalid IP */ 269 return (NSS_STR_PARSE_PARSE); 270 else 271 /* IPv4 */ 272 return (NSS_STR_PARSE_SUCCESS); 273 274 275 } 276 static nss_status_t 277 getent_hosts(files_backend_ptr_t be, void *a) 278 { 279 nss_XbyY_args_t *args = (nss_XbyY_args_t *)a; 280 nss_status_t rc = NSS_SUCCESS; 281 282 if (args->buf.result != NULL) { 283 return (_nss_files_XY_all(be, args, 1, 0, 0)); 284 } else { 285 /* 286 * Called by nscd 287 */ 288 /*CONSTCOND*/ 289 while (1) { 290 rc = _nss_files_XY_all(be, args, 1, 0, 0); 291 /* 292 * NSS_NOTFOUND, end of file or other errors. 293 */ 294 if (rc != NSS_SUCCESS) 295 break; 296 /* 297 * /etc/hosts and /etc/ipnodes are merged and 298 * /etc/hosts can contain IPv6 addresses. 299 * These addresses have to be filtered. 300 */ 301 if (filter_ipv6(args->returnval, args->returnlen) 302 == NSS_STR_PARSE_SUCCESS) 303 break; 304 /* 305 * The entry is an IPv6 address or other errors. 306 * Skip it and continue to find next one. 307 */ 308 args->returnval = NULL; 309 args->returnlen = 0; 310 311 } 312 return (rc); 313 } 314 315 } 316 317 static files_backend_op_t host_ops[] = { 318 _nss_files_destr, 319 _nss_files_endent, 320 _nss_files_setent, 321 getent_hosts, 322 getbyname, 323 getbyaddr, 324 }; 325 326 /*ARGSUSED*/ 327 nss_backend_t * 328 _nss_files_hosts_constr(dummy1, dummy2, dummy3) 329 const char *dummy1, *dummy2, *dummy3; 330 { 331 return (_nss_files_constr(host_ops, 332 sizeof (host_ops) / sizeof (host_ops[0]), 333 _PATH_HOSTS, 334 NSS_LINELEN_HOSTS, 335 NULL)); 336 } 337 338 339 /* 340 * XXX - this duplicates code from files_common.c because we need to keep 341 * going after we've found a match to satisfy the multihomed host case. 342 */ 343 nss_status_t 344 __nss_files_XY_hostbyname(be, args, filter, type) 345 files_backend_ptr_t be; 346 nss_XbyY_args_t *args; 347 const char *filter; /* hint for name string */ 348 int type; 349 { 350 nss_status_t res; 351 char *abuf = NULL, *abuf_start = NULL, *abuf_end; 352 char *first, *last, *buffer; 353 int parsestat, i, nhosts = 0, buflen; 354 const char *namep; 355 char *h_name; 356 int h_namelen, namelen; 357 struct hostent *hp; 358 in_addr_t *taddr = NULL; 359 struct in6_addr *taddr6 = NULL; 360 size_t ntaddr; 361 void *addrp; 362 char *alias_end = NULL; 363 364 if (be->buf == 0 && (be->buf = malloc(be->minbuf)) == 0) { 365 return (NSS_UNAVAIL); 366 } 367 368 if (be->f == 0) { 369 if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS) 370 return (res); 371 } 372 373 ntaddr = MAXADDRS; 374 if (type == AF_INET) { 375 taddr = (in_addr_t *)calloc(ntaddr, sizeof (*taddr)); 376 if (taddr == NULL) 377 return (NSS_UNAVAIL); 378 } else { 379 taddr6 = (struct in6_addr *)calloc(ntaddr, sizeof (*taddr6)); 380 if (taddr6 == NULL) 381 return (NSS_UNAVAIL); 382 } 383 384 res = NSS_NOTFOUND; 385 args->returnval = (char *)0; 386 args->returnlen = 0; 387 hp = (struct hostent *)args->buf.result; 388 buffer = args->buf.buffer; 389 buflen = args->buf.buflen; 390 h_namelen = 0; 391 h_name = NULL; 392 393 for (;;) { 394 char *instr = be->buf; 395 int linelen; 396 397 if ((linelen = _nss_files_read_line(be->f, 398 instr, be->minbuf)) < 0) { 399 break; /* EOF */ 400 } 401 402 /* 403 * This check avoids a malloc()/free() for the common 404 * case. Also, if we're trying to match an alias and an 405 * already matched entry doesn't share a canonical name 406 * with the current one, bail. 407 */ 408 if (nhosts == 0 && strcasestr(instr, filter) == 0) { 409 continue; 410 } 411 412 if ((last = strchr(instr, '#')) == 0) 413 last = instr + linelen; 414 *last-- = '\0'; 415 for (first = instr; isspace(*first); first++) 416 ; 417 /* Ignore blank and comment lines */ 418 if (*first == '\0') 419 continue; 420 421 while (isspace(*last)) 422 --last; 423 linelen = last - first + 1; 424 if (first != instr) 425 instr = first; 426 427 /* Bail out if the canonical name does not match */ 428 if (nhosts && strcasestr(instr, h_name) == 0) { 429 continue; 430 } 431 432 /* 433 * Still need to check, strcasestr() above is just a hint. 434 */ 435 addrp = (type == AF_INET)? 436 (void *)&taddr[nhosts]: 437 (void *)&taddr6[nhosts]; 438 439 if (check_name(args, instr, linelen, 440 type, &namep, &namelen, 441 addrp, &i)) { 442 443 /* 444 * If we've already matched once and have a possible 445 * match on this line, copy the aliases where they're 446 * safe from being overwritten when we look at the 447 * next entry. They're saved as a string of blank 448 * separated names for the alias parser. On errors, 449 * we return failure whether or not we have already 450 * obtained a valid address. 451 */ 452 if (nhosts == 1 && hp) { 453 if (h_namelen + 1 > args->buf.buflen) { 454 args->erange = 1; 455 res = NSS_NOTFOUND; 456 break; 457 } 458 abuf = (char *)malloc(args->buf.buflen); 459 if (abuf == NULL) { 460 res = NSS_UNAVAIL; 461 break; 462 } 463 abuf_start = abuf; 464 abuf_end = abuf_start + args->buf.buflen; 465 (void) memcpy(abuf, h_name, h_namelen); 466 abuf += h_namelen; 467 *abuf = '\0'; 468 abuf = do_aliases(hp, abuf, abuf_end); 469 if (abuf == NULL) { 470 args->erange = 1; 471 res = NSS_NOTFOUND; 472 break; 473 } 474 } 475 476 if (hp != NULL) { 477 /* inside the application */ 478 parsestat = (*args->str2ent)(instr, linelen, 479 hp, buffer, buflen); 480 if (parsestat != NSS_STR_PARSE_SUCCESS) { 481 if (parsestat == NSS_STR_PARSE_ERANGE) 482 args->erange = 1; 483 (void) memset(buffer, 0, buflen); 484 continue; 485 } 486 } else { 487 /* inside nscd */ 488 int alen, cplen, erange = 0; 489 char *ap; 490 491 /* Add alias to the first line if any */ 492 if (nhosts > 0) { 493 494 /* get to the start of alias */ 495 ap = (char *)namep + namelen; 496 /* see if there's any alias */ 497 if (ap == instr + linelen) 498 alen = 0; 499 else 500 alen = linelen - (ap - instr); 501 if (alen + 1 >= buflen) 502 erange = 1; 503 if (erange == 0 && alen != 0) { 504 /* make room for the alias */ 505 if (alias_end != NULL) 506 (void) memmove(alias_end + 507 alen, alias_end, buffer - 508 alias_end); 509 /* copy in the alias */ 510 (void) memmove(alias_end, 511 ap, alen); 512 buffer += alen; 513 buflen -= alen; 514 args->returnlen += alen; 515 alias_end += alen; 516 } 517 518 /* Add delimiter to the buffer */ 519 *buffer++ = '\n'; 520 buflen--; 521 args->returnlen++; 522 } 523 524 /* copy just the addr if not first one */ 525 if (alias_end == NULL) 526 cplen = linelen; 527 else 528 cplen = namep - instr; 529 530 if (cplen >= buflen || erange == 1) { 531 args->erange = 1; 532 if (nhosts > 0) { 533 *(--buffer) = '\0'; 534 buflen++; 535 args->returnlen--; 536 } 537 continue; 538 } 539 540 (void) memcpy(buffer, instr, cplen); 541 /* Adjust buffer */ 542 buffer += cplen; 543 *buffer = '\0'; 544 buflen -= cplen; 545 args->returnlen += cplen; 546 if (alias_end == NULL) 547 alias_end = buffer; 548 } 549 550 /* 551 * If this is the first one, save the canonical 552 * name for future matches and continue. 553 */ 554 if (++nhosts == 1) { 555 h_name = malloc(namelen + 1); 556 if (h_name == NULL) { 557 res = NSS_UNAVAIL; 558 break; 559 } 560 res = NSS_SUCCESS; 561 (void) memcpy(h_name, namep, namelen); 562 h_name[namelen] = '\0'; 563 h_namelen = namelen; 564 if (hp) 565 args->returnval = hp; 566 else 567 args->returnval = args->buf.buffer; 568 continue; 569 } 570 571 572 /* Extend the array */ 573 if (nhosts >= ntaddr) { 574 ntaddr *= 2; 575 if (type == AF_INET) { 576 addrp = realloc(taddr, 577 sizeof (*taddr) * ntaddr); 578 if (addrp == NULL) { 579 res = NSS_UNAVAIL; 580 break; 581 } 582 taddr = (in_addr_t *)addrp; 583 } else { 584 addrp = realloc(taddr6, 585 sizeof (*taddr6) * ntaddr); 586 if (addrp == NULL) { 587 res = NSS_UNAVAIL; 588 break; 589 } 590 taddr6 = (struct in6_addr *)addrp; 591 } 592 } 593 594 /* 595 * For non-nscd, save aliases in a temporary buffer 596 * Don't have to do this for nscd as 'buffer' already 597 * contains the required data in the appropriate 598 * format 599 */ 600 if (hp) { 601 abuf = do_aliases(hp, abuf, abuf_end); 602 if (abuf == NULL) { 603 args->erange = 1; 604 res = NSS_NOTFOUND; 605 break; 606 } 607 } 608 } else if (namep && h_namelen == namelen && 609 strncasecmp(h_name, namep, namelen) == 0) { 610 /* 611 * This line didn't have the requested name but 612 * is part of the same multihomed host (i.e. it 613 * has the same canonical name as the previous 614 * line), so march on... 615 */ 616 continue; 617 } else if (nhosts) { 618 break; 619 } 620 } 621 622 if (abuf && res == NSS_SUCCESS) { 623 624 /* abuf != NULL implies hp and abuf_start != NULL */ 625 626 struct in_addr *addrp; 627 struct in6_addr *addrp6; 628 629 if (type == AF_INET) { 630 addrp = (struct in_addr *)(ROUND_DOWN(args->buf.buffer + 631 args->buf.buflen, sizeof (*addrp))); 632 hp->h_addr_list = (char **)(ROUND_DOWN(addrp - 633 ((nhosts + 1) * sizeof (char *) + 634 (nhosts * sizeof (*addrp))), sizeof (char *))); 635 for (i = 0, --addrp; i < nhosts; i++, --addrp) { 636 (*(in_addr_t *)addrp) = taddr[i]; 637 hp->h_addr_list[i] = (char *)addrp; 638 } 639 } else { 640 addrp6 = (struct in6_addr *) 641 (ROUND_DOWN(args->buf.buffer + args->buf.buflen, 642 sizeof (*addrp6))); 643 hp->h_addr_list = (char **)(ROUND_DOWN(addrp6 - 644 ((nhosts + 1) * sizeof (char *) + 645 (nhosts * sizeof (*addrp6))), sizeof (char *))); 646 for (i = 0, --addrp6; i < nhosts; i++, --addrp6) { 647 (void) memcpy(addrp6, &taddr6[i], 648 sizeof (struct in6_addr)); 649 hp->h_addr_list[i] = (char *)addrp6; 650 } 651 } 652 653 hp->h_addr_list[nhosts] = 0; 654 hp->h_aliases = _nss_netdb_aliases(abuf_start, 655 abuf - abuf_start, args->buf.buffer, 656 (char *)hp->h_addr_list - args->buf.buffer); 657 if (hp->h_aliases == 0) { 658 args->erange = 1; 659 res = NSS_NOTFOUND; 660 } else { 661 hp->h_name = hp->h_aliases[0]; 662 hp->h_aliases++; 663 } 664 } 665 666 /* 667 * stayopen is set to 0 by default in order to close the opened 668 * file. Some applications may break if it is set to 1. 669 */ 670 if (!args->stayopen) 671 (void) _nss_files_endent(be, 0); 672 673 if (taddr) 674 free(taddr); 675 if (taddr6) 676 free(taddr6); 677 if (h_name) 678 free(h_name); 679 if (abuf_start) 680 free(abuf_start); 681 682 return (res); 683 } 684 685 /* 686 * A case-insensitive version of strstr(). 687 */ 688 static char * 689 strcasestr(const char *as1, const char *as2) 690 { 691 int c2; 692 register const char *tptr; 693 register const char *s1, *s2; 694 695 s1 = as1; 696 s2 = as2; 697 698 if (s2 == NULL || *s2 == '\0') 699 return (0); 700 701 while (*s1) { 702 if (tolower(*s1++) == tolower(c2 = *s2)) { 703 tptr = s1; 704 while ((tolower(c2 = *++s2) == 705 tolower(*s1++)) && c2 != 0) 706 ; 707 if (c2 == 0) 708 return ((char *)tptr - 1); 709 s1 = tptr; 710 s2 = as2; 711 } 712 } 713 return (0); 714 } 715 716 717 static char * 718 do_aliases(struct hostent *hp, char *abuf, char *end) 719 { 720 char **cp; 721 size_t len; 722 723 if ((cp = hp->h_aliases) == NULL) 724 return (abuf); 725 726 for (; *cp; cp++) { 727 len = strlen(*cp); 728 if (abuf+len+1 >= end) { 729 return (NULL); 730 } 731 *abuf++ = ' '; 732 (void) memcpy(abuf, *cp, len); 733 abuf += len; 734 } 735 *abuf = '\0'; 736 737 return (abuf); 738 } 739 740 741 /* 742 * This is a copy of a routine in libnsl/nss/netdir_inet.c. It is 743 * here because /etc/lib/nss_files.so.1 cannot call routines 744 * in libnsl. Care should be taken to keep the two copies in sync. 745 */ 746 int 747 __nss_files_2herrno(nsstat) 748 nss_status_t nsstat; 749 { 750 switch (nsstat) { 751 case NSS_SUCCESS: 752 /* no macro-defined success code for h_errno */ 753 return (0); 754 case NSS_NOTFOUND: 755 return (HOST_NOT_FOUND); 756 case NSS_TRYAGAIN: 757 return (TRY_AGAIN); 758 case NSS_UNAVAIL: 759 return (NO_RECOVERY); 760 } 761 /* anything else */ 762 return (NO_RECOVERY); 763 } 764