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