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