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 231 static files_backend_op_t host_ops[] = { 232 _nss_files_destr, 233 _nss_files_endent, 234 _nss_files_setent, 235 _nss_files_getent_netdb, 236 getbyname, 237 getbyaddr, 238 }; 239 240 /*ARGSUSED*/ 241 nss_backend_t * 242 _nss_files_hosts_constr(dummy1, dummy2, dummy3) 243 const char *dummy1, *dummy2, *dummy3; 244 { 245 return (_nss_files_constr(host_ops, 246 sizeof (host_ops) / sizeof (host_ops[0]), 247 _PATH_HOSTS, 248 NSS_LINELEN_HOSTS, 249 NULL)); 250 } 251 252 253 /* 254 * XXX - this duplicates code from files_common.c because we need to keep 255 * going after we've found a match to satisfy the multihomed host case. 256 */ 257 nss_status_t 258 __nss_files_XY_hostbyname(be, args, filter, type) 259 files_backend_ptr_t be; 260 nss_XbyY_args_t *args; 261 const char *filter; /* hint for name string */ 262 int type; 263 { 264 nss_status_t res; 265 char *abuf = NULL, *abuf_start = NULL, *abuf_end; 266 char *first, *last, *buffer; 267 int parsestat, i, nhosts = 0, buflen; 268 const char *namep; 269 char *h_name; 270 int h_namelen, namelen; 271 struct hostent *hp; 272 in_addr_t *taddr = NULL; 273 struct in6_addr *taddr6 = NULL; 274 size_t ntaddr; 275 void *addrp; 276 char *alias_end = NULL; 277 278 if (be->buf == 0 && (be->buf = malloc(be->minbuf)) == 0) { 279 return (NSS_UNAVAIL); 280 } 281 282 if (be->f == 0) { 283 if ((res = _nss_files_setent(be, 0)) != NSS_SUCCESS) 284 return (res); 285 } 286 287 ntaddr = MAXADDRS; 288 if (type == AF_INET) { 289 taddr = (in_addr_t *)calloc(ntaddr, sizeof (*taddr)); 290 if (taddr == NULL) 291 return (NSS_UNAVAIL); 292 } else { 293 taddr6 = (struct in6_addr *)calloc(ntaddr, sizeof (*taddr6)); 294 if (taddr6 == NULL) 295 return (NSS_UNAVAIL); 296 } 297 298 res = NSS_NOTFOUND; 299 args->returnval = (char *)0; 300 args->returnlen = 0; 301 hp = (struct hostent *)args->buf.result; 302 buffer = args->buf.buffer; 303 buflen = args->buf.buflen; 304 h_namelen = 0; 305 h_name = NULL; 306 307 for (;;) { 308 char *instr = be->buf; 309 int linelen; 310 311 if ((linelen = _nss_files_read_line(be->f, 312 instr, be->minbuf)) < 0) { 313 break; /* EOF */ 314 } 315 316 /* 317 * This check avoids a malloc()/free() for the common 318 * case. Also, if we're trying to match an alias and an 319 * already matched entry doesn't share a canonical name 320 * with the current one, bail. 321 */ 322 if (nhosts == 0 && strcasestr(instr, filter) == 0) { 323 continue; 324 } 325 326 if ((last = strchr(instr, '#')) == 0) 327 last = instr + linelen; 328 *last-- = '\0'; 329 for (first = instr; isspace(*first); first++) 330 ; 331 /* Ignore blank and comment lines */ 332 if (*first == '\0') 333 continue; 334 335 while (isspace(*last)) 336 --last; 337 linelen = last - first + 1; 338 if (first != instr) 339 instr = first; 340 341 /* Bail out if the canonical name does not match */ 342 if (nhosts && strcasestr(instr, h_name) == 0) { 343 continue; 344 } 345 346 /* 347 * Still need to check, strcasestr() above is just a hint. 348 */ 349 addrp = (type == AF_INET)? 350 (void *)&taddr[nhosts]: 351 (void *)&taddr6[nhosts]; 352 353 if (check_name(args, instr, linelen, 354 type, &namep, &namelen, 355 addrp, &i)) { 356 357 /* 358 * If we've already matched once and have a possible 359 * match on this line, copy the aliases where they're 360 * safe from being overwritten when we look at the 361 * next entry. They're saved as a string of blank 362 * separated names for the alias parser. On errors, 363 * we return failure whether or not we have already 364 * obtained a valid address. 365 */ 366 if (nhosts == 1 && hp) { 367 if (h_namelen + 1 > args->buf.buflen) { 368 args->erange = 1; 369 res = NSS_NOTFOUND; 370 break; 371 } 372 abuf = (char *)malloc(args->buf.buflen); 373 if (abuf == NULL) { 374 res = NSS_UNAVAIL; 375 break; 376 } 377 abuf_start = abuf; 378 abuf_end = abuf_start + args->buf.buflen; 379 (void) memcpy(abuf, h_name, h_namelen); 380 abuf += h_namelen; 381 *abuf = '\0'; 382 abuf = do_aliases(hp, abuf, abuf_end); 383 if (abuf == NULL) { 384 args->erange = 1; 385 res = NSS_NOTFOUND; 386 break; 387 } 388 } 389 390 if (hp != NULL) { 391 /* inside the application */ 392 parsestat = (*args->str2ent)(instr, linelen, 393 hp, buffer, buflen); 394 if (parsestat != NSS_STR_PARSE_SUCCESS) { 395 if (parsestat == NSS_STR_PARSE_ERANGE) 396 args->erange = 1; 397 (void) memset(buffer, 0, buflen); 398 continue; 399 } 400 } else { 401 /* inside nscd */ 402 int alen, cplen, erange = 0; 403 char *ap; 404 405 /* Add alias to the first line if any */ 406 if (nhosts > 0) { 407 408 /* get to the start of alias */ 409 ap = (char *)namep + namelen; 410 /* see if there's any alias */ 411 if (ap == instr + linelen) 412 alen = 0; 413 else 414 alen = linelen - (ap - instr); 415 if (alen + 1 >= buflen) 416 erange = 1; 417 if (erange == 0 && alen != 0) { 418 /* make room for the alias */ 419 if (alias_end != NULL) 420 (void) memmove(alias_end + 421 alen, alias_end, buffer - 422 alias_end); 423 /* copy in the alias */ 424 (void) memmove(alias_end, 425 ap, alen); 426 buffer += alen; 427 buflen -= alen; 428 alias_end += alen; 429 } 430 431 /* Add delimiter to the buffer */ 432 *buffer++ = '\n'; 433 buflen--; 434 args->returnlen++; 435 } 436 437 /* copy just the addr if not first one */ 438 if (alias_end == NULL) 439 cplen = linelen; 440 else 441 cplen = namep - instr; 442 443 if (cplen >= buflen || erange == 1) { 444 args->erange = 1; 445 if (nhosts > 0) { 446 *(--buffer) = '\0'; 447 buflen++; 448 args->returnlen--; 449 } 450 continue; 451 } 452 453 (void) memcpy(buffer, instr, cplen); 454 /* Adjust buffer */ 455 buffer += cplen; 456 *buffer = '\0'; 457 buflen -= cplen; 458 if (alias_end == NULL) 459 alias_end = buffer; 460 } 461 462 args->returnlen += linelen; 463 464 /* 465 * If this is the first one, save the canonical 466 * name for future matches and continue. 467 */ 468 if (++nhosts == 1) { 469 h_name = malloc(namelen + 1); 470 if (h_name == NULL) { 471 res = NSS_UNAVAIL; 472 break; 473 } 474 res = NSS_SUCCESS; 475 (void) memcpy(h_name, namep, namelen); 476 h_name[namelen] = '\0'; 477 h_namelen = namelen; 478 if (hp) 479 args->returnval = hp; 480 else 481 args->returnval = args->buf.buffer; 482 continue; 483 } 484 485 486 /* Extend the array */ 487 if (nhosts >= ntaddr) { 488 ntaddr *= 2; 489 if (type == AF_INET) { 490 addrp = realloc(taddr, 491 sizeof (*taddr) * ntaddr); 492 if (addrp == NULL) { 493 res = NSS_UNAVAIL; 494 break; 495 } 496 taddr = (in_addr_t *)addrp; 497 } else { 498 addrp = realloc(taddr6, 499 sizeof (*taddr6) * ntaddr); 500 if (addrp == NULL) { 501 res = NSS_UNAVAIL; 502 break; 503 } 504 taddr6 = (struct in6_addr *)addrp; 505 } 506 } 507 508 /* 509 * For non-nscd, save aliases in a temporary buffer 510 * Don't have to do this for nscd as 'buffer' already 511 * contains the required data in the appropriate 512 * format 513 */ 514 if (hp) { 515 abuf = do_aliases(hp, abuf, abuf_end); 516 if (abuf == NULL) { 517 args->erange = 1; 518 res = NSS_NOTFOUND; 519 break; 520 } 521 } 522 } else if (namep && h_namelen == namelen && 523 strncasecmp(h_name, namep, namelen) == 0) { 524 /* 525 * This line didn't have the requested name but 526 * is part of the same multihomed host (i.e. it 527 * has the same canonical name as the previous 528 * line), so march on... 529 */ 530 continue; 531 } else if (nhosts) { 532 break; 533 } 534 } 535 536 if (abuf && res == NSS_SUCCESS) { 537 538 /* abuf != NULL implies hp and abuf_start != NULL */ 539 540 struct in_addr *addrp; 541 struct in6_addr *addrp6; 542 543 if (type == AF_INET) { 544 addrp = (struct in_addr *)(ROUND_DOWN(args->buf.buffer + 545 args->buf.buflen, sizeof (*addrp))); 546 hp->h_addr_list = (char **)(ROUND_DOWN(addrp - 547 ((nhosts + 1) * sizeof (char *) + 548 (nhosts * sizeof (*addrp))), sizeof (char *))); 549 for (i = 0, --addrp; i < nhosts; i++, --addrp) { 550 (*(in_addr_t *)addrp) = taddr[i]; 551 hp->h_addr_list[i] = (char *)addrp; 552 } 553 } else { 554 addrp6 = (struct in6_addr *) 555 (ROUND_DOWN(args->buf.buffer + args->buf.buflen, 556 sizeof (*addrp6))); 557 hp->h_addr_list = (char **)(ROUND_DOWN(addrp6 - 558 ((nhosts + 1) * sizeof (char *) + 559 (nhosts * sizeof (*addrp6))), sizeof (char *))); 560 for (i = 0, --addrp6; i < nhosts; i++, --addrp6) { 561 (void) memcpy(addrp6, &taddr6[i], 562 sizeof (struct in6_addr)); 563 hp->h_addr_list[i] = (char *)addrp6; 564 } 565 } 566 567 hp->h_addr_list[nhosts] = 0; 568 hp->h_aliases = _nss_netdb_aliases(abuf_start, 569 abuf - abuf_start, args->buf.buffer, 570 (char *)hp->h_addr_list - args->buf.buffer); 571 if (hp->h_aliases == 0) { 572 args->erange = 1; 573 res = NSS_NOTFOUND; 574 } else { 575 hp->h_name = hp->h_aliases[0]; 576 hp->h_aliases++; 577 } 578 } 579 580 /* 581 * stayopen is set to 0 by default in order to close the opened 582 * file. Some applications may break if it is set to 1. 583 */ 584 if (!args->stayopen) 585 (void) _nss_files_endent(be, 0); 586 587 if (taddr) 588 free(taddr); 589 if (taddr6) 590 free(taddr6); 591 if (h_name) 592 free(h_name); 593 if (abuf_start) 594 free(abuf_start); 595 596 return (res); 597 } 598 599 /* 600 * A case-insensitive version of strstr(). 601 */ 602 static char * 603 strcasestr(const char *as1, const char *as2) 604 { 605 int c2; 606 register const char *tptr; 607 register const char *s1, *s2; 608 609 s1 = as1; 610 s2 = as2; 611 612 if (s2 == NULL || *s2 == '\0') 613 return (0); 614 615 while (*s1) { 616 if (tolower(*s1++) == tolower(c2 = *s2)) { 617 tptr = s1; 618 while ((tolower(c2 = *++s2) == 619 tolower(*s1++)) && c2 != 0) 620 ; 621 if (c2 == 0) 622 return ((char *)tptr - 1); 623 s1 = tptr; 624 s2 = as2; 625 } 626 } 627 return (0); 628 } 629 630 631 static char * 632 do_aliases(struct hostent *hp, char *abuf, char *end) 633 { 634 char **cp; 635 size_t len; 636 637 if ((cp = hp->h_aliases) == NULL) 638 return (abuf); 639 640 for (; *cp; cp++) { 641 len = strlen(*cp); 642 if (abuf+len+1 >= end) { 643 return (NULL); 644 } 645 *abuf++ = ' '; 646 (void) memcpy(abuf, *cp, len); 647 abuf += len; 648 } 649 *abuf = '\0'; 650 651 return (abuf); 652 } 653 654 655 /* 656 * This is a copy of a routine in libnsl/nss/netdir_inet.c. It is 657 * here because /etc/lib/nss_files.so.1 cannot call routines 658 * in libnsl. Care should be taken to keep the two copies in sync. 659 */ 660 int 661 __nss_files_2herrno(nsstat) 662 nss_status_t nsstat; 663 { 664 switch (nsstat) { 665 case NSS_SUCCESS: 666 /* no macro-defined success code for h_errno */ 667 return (0); 668 case NSS_NOTFOUND: 669 return (HOST_NOT_FOUND); 670 case NSS_TRYAGAIN: 671 return (TRY_AGAIN); 672 case NSS_UNAVAIL: 673 return (NO_RECOVERY); 674 } 675 /* anything else */ 676 return (NO_RECOVERY); 677 } 678