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 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * This file defines and implements the re-entrant getipnodebyname(), 27 * getipnodebyaddr(), and freehostent() routines for IPv6. These routines 28 * follow use the netdir_getbyYY() (see netdir_inet.c). 29 * 30 * lib/libnsl/nss/getipnodeby.c 31 */ 32 33 #include "mt.h" 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <stropts.h> 37 #include <ctype.h> 38 #include <string.h> 39 #include <strings.h> 40 #include <netdb.h> 41 #include <stdio.h> 42 #include <arpa/inet.h> 43 #include <nss_dbdefs.h> 44 #include <netinet/in.h> 45 #include <sys/socket.h> 46 #include <sys/sockio.h> 47 #include <nss_netdir.h> 48 #include <net/if.h> 49 #include <netinet/in.h> 50 #include <netdir.h> 51 #include <thread.h> 52 #include <synch.h> 53 #include <fcntl.h> 54 #include <sys/time.h> 55 #include "nss.h" 56 57 #define IPV6_LITERAL_CHAR ':' 58 59 /* 60 * The number of nanoseconds getipnodebyname() waits before getting 61 * fresh interface count information with SIOCGLIFNUM. The default is 62 * five minutes. 63 */ 64 #define IFNUM_TIMEOUT ((hrtime_t)300 * NANOSEC) 65 66 /* 67 * Bits in the bitfield returned by getipnodebyname_processflags(). 68 * 69 * IPNODE_WANTIPV6 The user wants IPv6 addresses returned. 70 * IPNODE_WANTIPV4 The user wants IPv4 addresses returned. 71 * IPNODE_IPV4IFNOIPV6 The user only wants IPv4 addresses returned if no IPv6 72 * addresses are returned. 73 * IPNODE_LOOKUPIPNODES getipnodebyname() needs to lookup the name in ipnodes. 74 * IPNODE_LOOKUPHOSTS getipnodebyname() needs to lookup the name in hosts. 75 * IPNODE_ISLITERAL The name supplied is a literal address string. 76 * IPNODE_UNMAP The user doesn't want v4 mapped addresses if no IPv6 77 * interfaces are plumbed on the system. 78 */ 79 #define IPNODE_WANTIPV6 0x00000001u 80 #define IPNODE_WANTIPV4 0x00000002u 81 #define IPNODE_IPV4IFNOIPV6 0x00000004u 82 #define IPNODE_LOOKUPIPNODES 0x00000008u 83 #define IPNODE_LOOKUPHOSTS 0x00000010u 84 #define IPNODE_LITERAL 0x00000020u 85 #define IPNODE_UNMAP 0x00000040u 86 #define IPNODE_IPV4 (IPNODE_WANTIPV4 | IPNODE_IPV4IFNOIPV6) 87 88 /* 89 * The private flag between libsocket and libnsl. See 90 * lib/libsocket/inet/getaddrinfo.c for more information. 91 */ 92 #define AI_ADDRINFO 0x8000 93 94 /* 95 * The default set of bits corresponding to a getipnodebyname() flags 96 * argument of AI_DEFAULT. 97 */ 98 #define IPNODE_DEFAULT (IPNODE_WANTIPV6 | IPNODE_IPV4 | \ 99 IPNODE_LOOKUPIPNODES | IPNODE_LOOKUPHOSTS) 100 101 extern struct netconfig *__rpc_getconfip(char *); 102 103 static struct hostent *__mapv4tov6(struct hostent *, struct hostent *, 104 nss_XbyY_buf_t *, int); 105 struct hostent *__mappedtov4(struct hostent *, int *); 106 static struct hostent *__filter_addresses(int, struct hostent *); 107 static int __find_mapped(struct hostent *, int); 108 static nss_XbyY_buf_t *__IPv6_alloc(int); 109 static void __IPv6_cleanup(nss_XbyY_buf_t *); 110 static int __ai_addrconfig(int); 111 112 113 #ifdef PIC 114 struct hostent * 115 _uncached_getipnodebyname(const char *nam, struct hostent *result, 116 char *buffer, int buflen, int af_family, int flags, int *h_errnop) 117 { 118 return (_switch_getipnodebyname_r(nam, result, buffer, buflen, 119 af_family, flags, h_errnop)); 120 } 121 122 struct hostent * 123 _uncached_getipnodebyaddr(const char *addr, int length, int type, 124 struct hostent *result, char *buffer, int buflen, int *h_errnop) 125 { 126 if (type == AF_INET) 127 return (_switch_gethostbyaddr_r(addr, length, type, 128 result, buffer, buflen, h_errnop)); 129 else if (type == AF_INET6) 130 return (_switch_getipnodebyaddr_r(addr, length, type, 131 result, buffer, buflen, h_errnop)); 132 return (NULL); 133 } 134 #endif 135 136 /* 137 * Given a name, an address family, and a set of flags, return a 138 * bitfield that getipnodebyname() will use. 139 */ 140 static uint_t 141 getipnodebyname_processflags(const char *name, int af, int flags) 142 { 143 uint_t ipnode_bits = IPNODE_DEFAULT; 144 boolean_t ipv6configured = B_FALSE; 145 boolean_t ipv4configured = B_FALSE; 146 147 /* 148 * If AI_ADDRCONFIG is specified, we need to determine the number 149 * of addresses of each address family configured on the system as 150 * appropriate. 151 */ 152 if (flags & AI_ADDRCONFIG) { 153 ipv6configured = (af == AF_INET6 && 154 __ai_addrconfig(AF_INET6) > 0); 155 ipv4configured = ((af == AF_INET || (flags & AI_V4MAPPED)) && 156 __ai_addrconfig(AF_INET) > 0); 157 } 158 159 /* 160 * Determine what kinds of addresses the user is interested 161 * in getting back. 162 */ 163 switch (af) { 164 case AF_INET6: 165 if ((flags & AI_ADDRCONFIG) && !ipv6configured) 166 ipnode_bits &= ~IPNODE_WANTIPV6; 167 168 if (flags & AI_V4MAPPED) { 169 if ((flags & AI_ADDRCONFIG) && !ipv4configured) { 170 ipnode_bits &= ~IPNODE_IPV4; 171 } else if (flags & AI_ALL) { 172 ipnode_bits &= ~IPNODE_IPV4IFNOIPV6; 173 } 174 if ((flags & AI_ADDRCONFIG) && !ipv6configured && 175 (flags & AI_ADDRINFO)) { 176 ipnode_bits |= IPNODE_UNMAP; 177 } 178 } else { 179 ipnode_bits &= ~IPNODE_IPV4; 180 } 181 break; 182 case AF_INET: 183 if ((flags & AI_ADDRCONFIG) && !ipv4configured) 184 ipnode_bits &= ~IPNODE_IPV4; 185 ipnode_bits &= ~IPNODE_WANTIPV6; 186 ipnode_bits &= ~IPNODE_IPV4IFNOIPV6; 187 break; 188 default: 189 ipnode_bits = 0; 190 break; 191 } 192 193 /* 194 * If we're not looking for IPv4 addresses, don't bother looking 195 * in hosts. 196 */ 197 if (!(ipnode_bits & IPNODE_WANTIPV4)) 198 ipnode_bits &= ~IPNODE_LOOKUPHOSTS; 199 200 /* 201 * Determine if name is a literal IP address. This will 202 * further narrow down what type of lookup we're going to do. 203 */ 204 if (strchr(name, IPV6_LITERAL_CHAR) != NULL) { 205 /* Literal IPv6 address */ 206 ipnode_bits |= IPNODE_LITERAL; 207 /* 208 * In s9 we accepted the literal without filtering independent 209 * of what family was passed in hints. We continue to do 210 * this. 211 */ 212 ipnode_bits |= (IPNODE_WANTIPV6 | IPNODE_WANTIPV4); 213 ipnode_bits &= ~IPNODE_LOOKUPHOSTS; 214 } else if (inet_addr(name) != 0xffffffffU) { 215 /* Literal IPv4 address */ 216 ipnode_bits |= (IPNODE_LITERAL | IPNODE_WANTIPV4); 217 ipnode_bits &= ~IPNODE_WANTIPV6; 218 ipnode_bits &= ~IPNODE_LOOKUPIPNODES; 219 } 220 return (ipnode_bits); 221 } 222 223 struct hostent * 224 getipnodebyname(const char *name, int af, int flags, int *error_num) 225 { 226 struct hostent *hp = NULL; 227 nss_XbyY_buf_t *buf4 = NULL; 228 nss_XbyY_buf_t *buf6 = NULL; 229 struct netconfig *nconf; 230 struct nss_netdirbyname_in nssin; 231 union nss_netdirbyname_out nssout; 232 int ret; 233 uint_t ipnode_bits; 234 235 if ((nconf = __rpc_getconfip("udp")) == NULL && 236 (nconf = __rpc_getconfip("tcp")) == NULL) { 237 *error_num = NO_RECOVERY; 238 return (NULL); 239 } 240 241 ipnode_bits = getipnodebyname_processflags(name, af, flags); 242 243 /* Make sure we have something to look up. */ 244 if (!(ipnode_bits & (IPNODE_WANTIPV6 | IPNODE_WANTIPV4))) { 245 *error_num = HOST_NOT_FOUND; 246 goto cleanup; 247 } 248 249 /* 250 * Perform the requested lookups. We always look through 251 * ipnodes first for both IPv4 and IPv6 addresses. Depending 252 * on what was returned and what was needed, we either filter 253 * out the garbage, or ask for more using hosts. 254 */ 255 if (ipnode_bits & IPNODE_LOOKUPIPNODES) { 256 if ((buf6 = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == NULL) { 257 *error_num = NO_RECOVERY; 258 goto cleanup; 259 } 260 nssin.op_t = NSS_HOST6; 261 nssin.arg.nss.host6.name = name; 262 nssin.arg.nss.host6.buf = buf6->buffer; 263 nssin.arg.nss.host6.buflen = buf6->buflen; 264 nssin.arg.nss.host6.af_family = af; 265 nssin.arg.nss.host6.flags = flags; 266 nssout.nss.host.hent = buf6->result; 267 nssout.nss.host.herrno_p = error_num; 268 ret = _get_hostserv_inetnetdir_byname(nconf, &nssin, &nssout); 269 if (ret != ND_OK) { 270 __IPv6_cleanup(buf6); 271 buf6 = NULL; 272 } else if (ipnode_bits & IPNODE_WANTIPV4) { 273 /* 274 * buf6 may have all that we need if we either 275 * only wanted IPv4 addresses if there were no 276 * IPv6 addresses returned, or if there are 277 * IPv4-mapped addresses in buf6. If either 278 * of these are true, then there's no need to 279 * look in hosts. 280 */ 281 if (ipnode_bits & IPNODE_IPV4IFNOIPV6 || 282 __find_mapped(buf6->result, 0) != 0) { 283 ipnode_bits &= ~IPNODE_LOOKUPHOSTS; 284 } else if (!(ipnode_bits & IPNODE_WANTIPV6)) { 285 /* 286 * If all we're looking for are IPv4 287 * addresses and there are none in 288 * buf6 then buf6 is now useless. 289 */ 290 __IPv6_cleanup(buf6); 291 buf6 = NULL; 292 } 293 } 294 } 295 if (ipnode_bits & IPNODE_LOOKUPHOSTS) { 296 if ((buf4 = __IPv6_alloc(NSS_BUFLEN_HOSTS)) == NULL) { 297 *error_num = NO_RECOVERY; 298 goto cleanup; 299 } 300 nssin.op_t = NSS_HOST; 301 nssin.arg.nss.host.name = name; 302 nssin.arg.nss.host.buf = buf4->buffer; 303 nssin.arg.nss.host.buflen = buf4->buflen; 304 nssout.nss.host.hent = buf4->result; 305 nssout.nss.host.herrno_p = error_num; 306 ret = _get_hostserv_inetnetdir_byname(nconf, &nssin, &nssout); 307 if (ret != ND_OK) { 308 __IPv6_cleanup(buf4); 309 buf4 = NULL; 310 } 311 } 312 313 if (buf6 == NULL && buf4 == NULL) { 314 *error_num = HOST_NOT_FOUND; 315 goto cleanup; 316 } 317 318 /* Extract the appropriate addresses from the returned buffer(s). */ 319 switch (af) { 320 case AF_INET6: { 321 if (buf4 != NULL) { 322 nss_XbyY_buf_t *mergebuf; 323 324 /* 325 * The IPv4 results we have need to be 326 * converted to IPv4-mapped addresses, 327 * conditionally merged with the IPv6 328 * results, and the end result needs to be 329 * re-ordered. 330 */ 331 mergebuf = __IPv6_alloc(NSS_BUFLEN_IPNODES); 332 if (mergebuf == NULL) { 333 *error_num = NO_RECOVERY; 334 goto cleanup; 335 } 336 hp = __mapv4tov6(buf4->result, 337 ((buf6 != NULL) ? buf6->result : NULL), 338 mergebuf, 1); 339 if (hp != NULL) 340 order_haddrlist_af(AF_INET6, hp->h_addr_list); 341 else 342 *error_num = NO_RECOVERY; 343 free(mergebuf); 344 } 345 346 if (buf4 == NULL && buf6 != NULL) { 347 hp = buf6->result; 348 349 /* 350 * We have what we need in buf6, but we may need 351 * to filter out some addresses depending on what 352 * is being asked for. 353 */ 354 if (!(ipnode_bits & IPNODE_WANTIPV4)) 355 hp = __filter_addresses(AF_INET, buf6->result); 356 else if (!(ipnode_bits & IPNODE_WANTIPV6)) 357 hp = __filter_addresses(AF_INET6, buf6->result); 358 359 /* 360 * We've been asked to unmap v4 addresses. This 361 * situation implies IPNODE_WANTIPV4 and 362 * !IPNODE_WANTIPV6. 363 */ 364 if (hp != NULL && (ipnode_bits & IPNODE_UNMAP)) { 365 /* 366 * Just set hp to a new value, cleanup: will 367 * free the old one 368 */ 369 hp = __mappedtov4(hp, error_num); 370 } else if (hp == NULL) 371 *error_num = NO_ADDRESS; 372 } 373 374 break; 375 } 376 377 case AF_INET: 378 /* We could have results in buf6 or buf4, not both */ 379 if (buf6 != NULL) { 380 /* 381 * Extract the IPv4-mapped addresses from buf6 382 * into hp. 383 */ 384 hp = __mappedtov4(buf6->result, error_num); 385 } else { 386 /* We have what we need in buf4. */ 387 hp = buf4->result; 388 if (ipnode_bits & IPNODE_LITERAL) { 389 /* 390 * There is a special case here for literal 391 * IPv4 address strings. The hosts 392 * front-end sets h_aliases to a one 393 * element array containing a single NULL 394 * pointer (in ndaddr2hent()), while 395 * getipnodebyname() requires h_aliases to 396 * be a NULL pointer itself. We're not 397 * going to change the front-end since it 398 * needs to remain backward compatible for 399 * gethostbyname() and friends. Just set 400 * h_aliases to NULL here instead. 401 */ 402 hp->h_aliases = NULL; 403 } 404 } 405 406 break; 407 408 default: 409 break; 410 } 411 412 cleanup: 413 /* 414 * Free the memory we allocated, but make sure we don't free 415 * the memory we're returning to the caller. 416 */ 417 if (buf6 != NULL) { 418 if (buf6->result == hp) 419 buf6->result = NULL; 420 __IPv6_cleanup(buf6); 421 } 422 if (buf4 != NULL) { 423 if (buf4->result == hp) 424 buf4->result = NULL; 425 __IPv6_cleanup(buf4); 426 } 427 (void) freenetconfigent(nconf); 428 429 return (hp); 430 } 431 432 /* 433 * This is the IPv6 interface for "gethostbyaddr". 434 */ 435 struct hostent * 436 getipnodebyaddr(const void *src, size_t len, int type, int *error_num) 437 { 438 struct in6_addr *addr6 = 0; 439 struct in_addr *addr4 = 0; 440 nss_XbyY_buf_t *buf = 0; 441 nss_XbyY_buf_t *res = 0; 442 struct netconfig *nconf; 443 struct hostent *hp = 0; 444 struct nss_netdirbyaddr_in nssin; 445 union nss_netdirbyaddr_out nssout; 446 int neterr; 447 char tmpbuf[64]; 448 449 if (type == AF_INET6) { 450 if ((addr6 = (struct in6_addr *)src) == NULL) { 451 *error_num = HOST_NOT_FOUND; 452 return (NULL); 453 } 454 } else if (type == AF_INET) { 455 if ((addr4 = (struct in_addr *)src) == NULL) { 456 *error_num = HOST_NOT_FOUND; 457 return (NULL); 458 } 459 } else { 460 *error_num = HOST_NOT_FOUND; 461 return (NULL); 462 } 463 /* 464 * Specific case: query for "::" 465 */ 466 if (type == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(addr6)) { 467 *error_num = HOST_NOT_FOUND; 468 return (NULL); 469 } 470 /* 471 * Step 1: IPv4-mapped address or IPv4 Compat 472 */ 473 if ((type == AF_INET6 && len == 16) && 474 ((IN6_IS_ADDR_V4MAPPED(addr6)) || 475 (IN6_IS_ADDR_V4COMPAT(addr6)))) { 476 if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { 477 *error_num = NO_RECOVERY; 478 return (NULL); 479 } 480 if ((nconf = __rpc_getconfip("udp")) == NULL && 481 (nconf = __rpc_getconfip("tcp")) == NULL) { 482 *error_num = NO_RECOVERY; 483 __IPv6_cleanup(buf); 484 return (NULL); 485 } 486 nssin.op_t = NSS_HOST6; 487 if (IN6_IS_ADDR_V4COMPAT(addr6)) { 488 (void) memcpy(tmpbuf, addr6, sizeof (*addr6)); 489 tmpbuf[10] = 0xffU; 490 tmpbuf[11] = 0xffU; 491 nssin.arg.nss.host.addr = (const char *)tmpbuf; 492 } else { 493 nssin.arg.nss.host.addr = (const char *)addr6; 494 } 495 nssin.arg.nss.host.len = sizeof (struct in6_addr); 496 nssin.arg.nss.host.type = AF_INET6; 497 nssin.arg.nss.host.buf = buf->buffer; 498 nssin.arg.nss.host.buflen = buf->buflen; 499 500 nssout.nss.host.hent = buf->result; 501 nssout.nss.host.herrno_p = error_num; 502 /* 503 * We pass in nconf and let the implementation of the 504 * long-named func decide whether to use the switch based on 505 * nc_nlookups. 506 */ 507 neterr = 508 _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout); 509 510 (void) freenetconfigent(nconf); 511 if (neterr != ND_OK) { 512 /* Failover case, try hosts db for v4 address */ 513 if (!gethostbyaddr_r(((char *)addr6) + 12, 514 sizeof (in_addr_t), AF_INET, buf->result, 515 buf->buffer, buf->buflen, error_num)) { 516 __IPv6_cleanup(buf); 517 return (NULL); 518 } 519 /* Found one, now format it into mapped/compat addr */ 520 if ((res = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { 521 __IPv6_cleanup(buf); 522 *error_num = NO_RECOVERY; 523 return (NULL); 524 } 525 /* Convert IPv4 to mapped/compat address w/name */ 526 hp = res->result; 527 (void) __mapv4tov6(buf->result, 0, res, 528 IN6_IS_ADDR_V4MAPPED(addr6)); 529 __IPv6_cleanup(buf); 530 free(res); 531 return (hp); 532 } 533 /* 534 * At this point, we'll have a v4mapped hostent. If that's 535 * what was passed in, just return. If the request was a compat, 536 * twiggle the two bytes to make the mapped address a compat. 537 */ 538 hp = buf->result; 539 if (IN6_IS_ADDR_V4COMPAT(addr6)) { 540 /* LINTED pointer cast */ 541 addr6 = (struct in6_addr *)hp->h_addr_list[0]; 542 addr6->s6_addr[10] = 0; 543 addr6->s6_addr[11] = 0; 544 } 545 free(buf); 546 return (hp); 547 } 548 /* 549 * Step 2: AF_INET, v4 lookup. Since we're going to search the 550 * ipnodes (v6) path first, we need to treat this as a v4mapped 551 * address. nscd(1m) caches v4 from ipnodes as mapped v6's. The 552 * switch backend knows to lookup v4's (not v4mapped) from the 553 * name services. 554 */ 555 if (type == AF_INET) { 556 struct in6_addr v4mapbuf; 557 addr6 = &v4mapbuf; 558 559 IN6_INADDR_TO_V4MAPPED(addr4, addr6); 560 if ((nconf = __rpc_getconfip("udp")) == NULL && 561 (nconf = __rpc_getconfip("tcp")) == NULL) { 562 *error_num = NO_RECOVERY; 563 return (NULL); 564 } 565 if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { 566 *error_num = NO_RECOVERY; 567 freenetconfigent(nconf); 568 return (NULL); 569 } 570 nssin.op_t = NSS_HOST6; 571 nssin.arg.nss.host.addr = (const char *)addr6; 572 nssin.arg.nss.host.len = sizeof (struct in6_addr); 573 nssin.arg.nss.host.type = AF_INET6; 574 nssin.arg.nss.host.buf = buf->buffer; 575 nssin.arg.nss.host.buflen = buf->buflen; 576 577 nssout.nss.host.hent = buf->result; 578 nssout.nss.host.herrno_p = error_num; 579 /* 580 * We pass in nconf and let the implementation of the 581 * long-named func decide whether to use the switch based on 582 * nc_nlookups. 583 */ 584 neterr = 585 _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout); 586 587 (void) freenetconfigent(nconf); 588 if (neterr != ND_OK) { 589 /* Failover case, try hosts db for v4 address */ 590 hp = buf->result; 591 if (!gethostbyaddr_r(src, len, type, buf->result, 592 buf->buffer, buf->buflen, error_num)) { 593 __IPv6_cleanup(buf); 594 return (NULL); 595 } 596 free(buf); 597 return (hp); 598 } 599 if ((hp = __mappedtov4(buf->result, error_num)) == NULL) { 600 __IPv6_cleanup(buf); 601 return (NULL); 602 } 603 __IPv6_cleanup(buf); 604 return (hp); 605 } 606 /* 607 * Step 3: AF_INET6, plain vanilla v6 getipnodebyaddr() call. 608 */ 609 if (type == AF_INET6) { 610 if ((nconf = __rpc_getconfip("udp")) == NULL && 611 (nconf = __rpc_getconfip("tcp")) == NULL) { 612 *error_num = NO_RECOVERY; 613 return (NULL); 614 } 615 if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) { 616 *error_num = NO_RECOVERY; 617 freenetconfigent(nconf); 618 return (NULL); 619 } 620 nssin.op_t = NSS_HOST6; 621 nssin.arg.nss.host.addr = (const char *)addr6; 622 nssin.arg.nss.host.len = len; 623 nssin.arg.nss.host.type = type; 624 nssin.arg.nss.host.buf = buf->buffer; 625 nssin.arg.nss.host.buflen = buf->buflen; 626 627 nssout.nss.host.hent = buf->result; 628 nssout.nss.host.herrno_p = error_num; 629 /* 630 * We pass in nconf and let the implementation of the 631 * long-named func decide whether to use the switch based on 632 * nc_nlookups. 633 */ 634 neterr = 635 _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout); 636 637 (void) freenetconfigent(nconf); 638 if (neterr != ND_OK) { 639 __IPv6_cleanup(buf); 640 return (NULL); 641 } 642 free(buf); 643 return (nssout.nss.host.hent); 644 } 645 /* 646 * If we got here, unknown type. 647 */ 648 *error_num = HOST_NOT_FOUND; 649 return (NULL); 650 } 651 652 void 653 freehostent(struct hostent *hent) 654 { 655 free(hent); 656 } 657 658 static int 659 __ai_addrconfig(int af) 660 { 661 struct lifnum lifn; 662 struct lifconf lifc; 663 struct lifreq *lifp, *buf = NULL; 664 size_t bufsize; 665 hrtime_t now, *then; 666 static hrtime_t then4, then6; /* the last time we updated ifnum# */ 667 static int ifnum4 = -1, ifnum6 = -1; 668 int *num; 669 int nlifr, count = 0; 670 671 672 switch (af) { 673 case AF_INET: 674 num = &ifnum4; 675 then = &then4; 676 break; 677 case AF_INET6: 678 num = &ifnum6; 679 then = &then6; 680 break; 681 default: 682 return (0); 683 } 684 685 /* 686 * We don't need to check this every time someone does a name 687 * lookup. Do it every IFNUM_TIMEOUT for each address family. 688 * 689 * There's no need to protect all of this with a lock. The 690 * worst that can happen is that we update the interface count 691 * twice instead of once. That's no big deal. 692 */ 693 now = gethrtime(); 694 if (*num == -1 || ((now - *then) >= IFNUM_TIMEOUT)) { 695 lifn.lifn_family = af; 696 /* 697 * We want to determine if this machine knows anything 698 * at all about the address family; the status of the 699 * interface is less important. Hence, set 700 * 'lifn_flags' to zero. 701 */ 702 lifn.lifn_flags = 0; 703 again: 704 if (nss_ioctl(af, SIOCGLIFNUM, &lifn) < 0) 705 goto fail; 706 707 if (lifn.lifn_count == 0) { 708 *num = 0; 709 *then = now; 710 return (*num); 711 } 712 713 /* 714 * Pad the interface count to detect when additional 715 * interfaces have been configured between SIOCGLIFNUM 716 * and SIOCGLIFCONF. 717 */ 718 lifn.lifn_count += 4; 719 720 bufsize = lifn.lifn_count * sizeof (struct lifreq); 721 if ((buf = realloc(buf, bufsize)) == NULL) 722 goto fail; 723 724 lifc.lifc_family = af; 725 lifc.lifc_flags = 0; 726 lifc.lifc_len = bufsize; 727 lifc.lifc_buf = (caddr_t)buf; 728 if (nss_ioctl(af, SIOCGLIFCONF, &lifc) < 0) 729 goto fail; 730 731 nlifr = lifc.lifc_len / sizeof (struct lifreq); 732 if (nlifr >= lifn.lifn_count) 733 goto again; 734 /* 735 * Do not include any loopback addresses, 127.0.0.1 for AF_INET 736 * and ::1 for AF_INET6, while counting the number of available 737 * IPv4 or IPv6 addresses. (RFC 3493 requires this, whenever 738 * AI_ADDRCONFIG flag is set) 739 */ 740 for (lifp = buf; lifp < buf + nlifr; lifp++) { 741 switch (af) { 742 case AF_INET: { 743 struct sockaddr_in *in; 744 745 in = (struct sockaddr_in *)&lifp->lifr_addr; 746 if (ntohl(in->sin_addr.s_addr) == 747 INADDR_LOOPBACK) { 748 count++; 749 } 750 break; 751 } 752 case AF_INET6: { 753 struct sockaddr_in6 *in6; 754 755 in6 = (struct sockaddr_in6 *)&lifp->lifr_addr; 756 if (IN6_IS_ADDR_LOOPBACK(&in6->sin6_addr)) 757 count++; 758 break; 759 } 760 } 761 } 762 *num = nlifr - count; 763 *then = now; 764 free(buf); 765 } 766 return (*num); 767 fail: 768 free(buf); 769 /* 770 * If the process is running without the NET_ACCESS basic privilege, 771 * pretend we still have inet/inet6 interfaces. 772 */ 773 if (errno == EACCES) 774 return (1); 775 return (-1); 776 } 777 778 /* 779 * This routine will either convert an IPv4 address to a mapped or compat 780 * IPv6 (if he6 == NULL) or merge IPv6 (he6) addresses with mapped 781 * v4 (he4) addresses. In either case, the results are returned in res. 782 * Caller must provide all buffers. 783 * Inputs: 784 * he4 pointer to IPv4 buffer 785 * he6 pointer to IPv6 buffer (NULL if not merging v4/v6 786 * res pointer to results buffer 787 * mapped mapped == 1, map IPv4 : mapped == 0, compat IPv4 788 * mapped flag is ignored if he6 != NULL 789 * 790 * The results are packed into the res->buffer as follows: 791 * <--------------- buffer + buflen --------------------------------------> 792 * |-----------------|-----------------|----------------|----------------| 793 * | pointers vector | pointers vector | aliases grow | addresses grow | 794 * | for addresses | for aliases | | | 795 * | this way -> | this way -> | <- this way |<- this way | 796 * |-----------------|-----------------|----------------|----------------| 797 * | grows in PASS 1 | grows in PASS2 | grows in PASS2 | grows in PASS 1| 798 */ 799 static struct hostent * 800 __mapv4tov6(struct hostent *he4, struct hostent *he6, nss_XbyY_buf_t *res, 801 int mapped) 802 { 803 char *buffer, *limit; 804 int buflen = res->buflen; 805 struct in6_addr *addr6p; 806 char *buff_locp; 807 struct hostent *host; 808 int count = 0, len, i; 809 char *h_namep; 810 811 if (he4 == NULL || res == NULL) { 812 return (NULL); 813 } 814 limit = res->buffer + buflen; 815 host = (struct hostent *)res->result; 816 buffer = res->buffer; 817 818 buff_locp = (char *)ROUND_DOWN(limit, sizeof (struct in6_addr)); 819 host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **)); 820 if ((char *)host->h_addr_list >= limit || 821 buff_locp <= (char *)host->h_addr_list) { 822 return (NULL); 823 } 824 if (he6 == NULL) { 825 /* 826 * If he6==NULL, map the v4 address into the v6 address format. 827 * This is used for getipnodebyaddr() (single address, mapped or 828 * compatible) or for v4 mapped for getipnodebyname(), which 829 * could be multiple addresses. This could also be a literal 830 * address string, which is why there is a inet_addr() call. 831 */ 832 for (i = 0; he4->h_addr_list[i] != NULL; i++) { 833 buff_locp -= sizeof (struct in6_addr); 834 if (buff_locp <= 835 (char *)&(host->h_addr_list[count + 1])) { 836 /* 837 * Has to be room for the pointer to the address we're 838 * about to add, as well as the final NULL ptr. 839 */ 840 return (NULL); 841 } 842 /* LINTED pointer cast */ 843 addr6p = (struct in6_addr *)buff_locp; 844 host->h_addr_list[count] = (char *)addr6p; 845 bzero(addr6p->s6_addr, sizeof (struct in6_addr)); 846 if (mapped) { 847 addr6p->s6_addr[10] = 0xff; 848 addr6p->s6_addr[11] = 0xff; 849 } 850 bcopy((char *)he4->h_addr_list[i], 851 &addr6p->s6_addr[12], sizeof (struct in_addr)); 852 ++count; 853 } 854 /* 855 * Set last array element to NULL and add cname as first alias 856 */ 857 host->h_addr_list[count] = NULL; 858 host->h_aliases = host->h_addr_list + count + 1; 859 count = 0; 860 if ((int)(inet_addr(he4->h_name)) != -1) { 861 /* 862 * Literal address string, since we're mapping, we need the IPv6 863 * V4 mapped literal address string for h_name. 864 */ 865 char tmpstr[128]; 866 (void) inet_ntop(AF_INET6, host->h_addr_list[0], tmpstr, 867 sizeof (tmpstr)); 868 buff_locp -= (len = strlen(tmpstr) + 1); 869 h_namep = tmpstr; 870 if (buff_locp <= (char *)(host->h_aliases)) 871 return (NULL); 872 bcopy(h_namep, buff_locp, len); 873 host->h_name = buff_locp; 874 host->h_aliases = NULL; /* no aliases for literal */ 875 host->h_length = sizeof (struct in6_addr); 876 host->h_addrtype = AF_INET6; 877 return (host); /* we're done, return result */ 878 } 879 /* 880 * Not a literal address string, so just copy h_name. 881 */ 882 buff_locp -= (len = strlen(he4->h_name) + 1); 883 h_namep = he4->h_name; 884 if (buff_locp <= (char *)(host->h_aliases)) 885 return (NULL); 886 bcopy(h_namep, buff_locp, len); 887 host->h_name = buff_locp; 888 /* 889 * Pass 2 (IPv4 aliases): 890 */ 891 for (i = 0; he4->h_aliases[i] != NULL; i++) { 892 buff_locp -= (len = strlen(he4->h_aliases[i]) + 1); 893 if (buff_locp <= 894 (char *)&(host->h_aliases[count + 1])) { 895 /* 896 * Has to be room for the pointer to the address we're 897 * about to add, as well as the final NULL ptr. 898 */ 899 return (NULL); 900 } 901 host->h_aliases[count] = buff_locp; 902 bcopy((char *)he4->h_aliases[i], buff_locp, len); 903 ++count; 904 } 905 host->h_aliases[count] = NULL; 906 host->h_length = sizeof (struct in6_addr); 907 host->h_addrtype = AF_INET6; 908 return (host); 909 } else { 910 /* 911 * Merge IPv4 mapped addresses with IPv6 addresses. The 912 * IPv6 address will go in first, followed by the v4 mapped. 913 * 914 * Pass 1 (IPv6 addresses): 915 */ 916 for (i = 0; he6->h_addr_list[i] != NULL; i++) { 917 buff_locp -= sizeof (struct in6_addr); 918 if (buff_locp <= 919 (char *)&(host->h_addr_list[count + 1])) { 920 /* 921 * Has to be room for the pointer to the address we're 922 * about to add, as well as the final NULL ptr. 923 */ 924 return (NULL); 925 } 926 host->h_addr_list[count] = buff_locp; 927 bcopy((char *)he6->h_addr_list[i], buff_locp, 928 sizeof (struct in6_addr)); 929 ++count; 930 } 931 /* 932 * Pass 1 (IPv4 mapped addresses): 933 */ 934 for (i = 0; he4->h_addr_list[i] != NULL; i++) { 935 buff_locp -= sizeof (struct in6_addr); 936 if (buff_locp <= 937 (char *)&(host->h_addr_list[count + 1])) { 938 /* 939 * Has to be room for the pointer to the address we're 940 * about to add, as well as the final NULL ptr. 941 */ 942 return (NULL); 943 } 944 /* LINTED pointer cast */ 945 addr6p = (struct in6_addr *)buff_locp; 946 host->h_addr_list[count] = (char *)addr6p; 947 bzero(addr6p->s6_addr, sizeof (struct in6_addr)); 948 addr6p->s6_addr[10] = 0xff; 949 addr6p->s6_addr[11] = 0xff; 950 bcopy(he4->h_addr_list[i], &addr6p->s6_addr[12], 951 sizeof (struct in_addr)); 952 ++count; 953 } 954 /* 955 * Pass 2 (IPv6 aliases, host name first). We start h_aliases 956 * one after where h_addr_list array ended. This is where cname 957 * is put, followed by all aliases. Reset count to 0, for index 958 * in the h_aliases array. 959 */ 960 host->h_addr_list[count] = NULL; 961 host->h_aliases = host->h_addr_list + count + 1; 962 count = 0; 963 buff_locp -= (len = strlen(he6->h_name) + 1); 964 if (buff_locp <= (char *)(host->h_aliases)) 965 return (NULL); 966 bcopy(he6->h_name, buff_locp, len); 967 host->h_name = buff_locp; 968 for (i = 0; he6->h_aliases[i] != NULL; i++) { 969 buff_locp -= (len = strlen(he6->h_aliases[i]) + 1); 970 if (buff_locp <= 971 (char *)&(host->h_aliases[count + 1])) { 972 /* 973 * Has to be room for the pointer to the address we're 974 * about to add, as well as the final NULL ptr. 975 */ 976 return (NULL); 977 } 978 host->h_aliases[count] = buff_locp; 979 bcopy((char *)he6->h_aliases[i], buff_locp, len); 980 ++count; 981 } 982 /* 983 * Pass 2 (IPv4 aliases): 984 */ 985 for (i = 0; he4->h_aliases[i] != NULL; i++) { 986 buff_locp -= (len = strlen(he4->h_aliases[i]) + 1); 987 if (buff_locp <= 988 (char *)&(host->h_aliases[count + 1])) { 989 /* 990 * Has to be room for the pointer to the address we're 991 * about to add, as well as the final NULL ptr. 992 */ 993 return (NULL); 994 } 995 host->h_aliases[count] = buff_locp; 996 bcopy((char *)he4->h_aliases[i], buff_locp, len); 997 ++count; 998 } 999 host->h_aliases[count] = NULL; 1000 host->h_length = sizeof (struct in6_addr); 1001 host->h_addrtype = AF_INET6; 1002 return (host); 1003 } 1004 } 1005 1006 /* 1007 * This routine will convert a mapped v4 hostent (AF_INET6) to a 1008 * AF_INET hostent. If no mapped addrs found, then a NULL is returned. 1009 * If mapped addrs found, then a new buffer is alloc'd and all the v4 mapped 1010 * addresses are extracted and copied to it. On sucess, a pointer to a new 1011 * hostent is returned. 1012 * There are two possible errors in which case a NULL is returned. 1013 * One of two error codes are returned: 1014 * 1015 * NO_RECOVERY - a malloc failed or the like for which there's no recovery. 1016 * NO_ADDRESS - after filtering all the v4, there was nothing left! 1017 * 1018 * Inputs: 1019 * he pointer to hostent with mapped v4 addresses 1020 * filter_error pointer to return error code 1021 * Return: 1022 * pointer to a malloc'd hostent with v4 addresses. 1023 * 1024 * The results are packed into the res->buffer as follows: 1025 * <--------------- buffer + buflen --------------------------------------> 1026 * |-----------------|-----------------|----------------|----------------| 1027 * | pointers vector | pointers vector | aliases grow | addresses grow | 1028 * | for addresses | for aliases | | | 1029 * | this way -> | this way -> | <- this way |<- this way | 1030 * |-----------------|-----------------|----------------|----------------| 1031 * | grows in PASS 1 | grows in PASS2 | grows in PASS2 | grows in PASS 1| 1032 */ 1033 struct hostent * 1034 __mappedtov4(struct hostent *he, int *extract_error) 1035 { 1036 char *buffer, *limit; 1037 nss_XbyY_buf_t *res; 1038 int buflen = NSS_BUFLEN_HOSTS; 1039 struct in_addr *addr4p; 1040 char *buff_locp; 1041 struct hostent *host; 1042 int count = 0, len, i; 1043 char *h_namep; 1044 1045 if (he == NULL) { 1046 *extract_error = NO_ADDRESS; 1047 return (NULL); 1048 } 1049 if ((__find_mapped(he, 0)) == 0) { 1050 *extract_error = NO_ADDRESS; 1051 return (NULL); 1052 } 1053 if ((res = __IPv6_alloc(NSS_BUFLEN_HOSTS)) == 0) { 1054 *extract_error = NO_RECOVERY; 1055 return (NULL); 1056 } 1057 limit = res->buffer + buflen; 1058 host = (struct hostent *)res->result; 1059 buffer = res->buffer; 1060 1061 buff_locp = (char *)ROUND_DOWN(limit, sizeof (struct in_addr)); 1062 host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **)); 1063 if ((char *)host->h_addr_list >= limit || 1064 buff_locp <= (char *)host->h_addr_list) 1065 goto cleanup; 1066 /* 1067 * "Unmap" the v4 mapped address(es) into a v4 hostent format. 1068 * This is used for getipnodebyaddr() (single address) or for 1069 * v4 mapped for getipnodebyname(), which could be multiple 1070 * addresses. This could also be a literal address string, 1071 * which is why there is a inet_addr() call. 1072 */ 1073 for (i = 0; he->h_addr_list[i] != NULL; i++) { 1074 /* LINTED pointer cast */ 1075 if (!IN6_IS_ADDR_V4MAPPED((struct in6_addr *) 1076 he->h_addr_list[i])) 1077 continue; 1078 buff_locp -= sizeof (struct in6_addr); 1079 /* 1080 * Has to be room for the pointer to the address we're 1081 * about to add, as well as the final NULL ptr. 1082 */ 1083 if (buff_locp <= 1084 (char *)&(host->h_addr_list[count + 1])) 1085 goto cleanup; 1086 /* LINTED pointer cast */ 1087 addr4p = (struct in_addr *)buff_locp; 1088 host->h_addr_list[count] = (char *)addr4p; 1089 bzero((char *)&addr4p->s_addr, 1090 sizeof (struct in_addr)); 1091 /* LINTED pointer cast */ 1092 IN6_V4MAPPED_TO_INADDR( 1093 (struct in6_addr *)he->h_addr_list[i], addr4p); 1094 ++count; 1095 } 1096 /* 1097 * Set last array element to NULL and add cname as first alias 1098 */ 1099 host->h_addr_list[count] = NULL; 1100 host->h_aliases = host->h_addr_list + count + 1; 1101 count = 0; 1102 /* Copy official host name */ 1103 buff_locp -= (len = strlen(he->h_name) + 1); 1104 h_namep = he->h_name; 1105 if (buff_locp <= (char *)(host->h_aliases)) 1106 goto cleanup; 1107 bcopy(h_namep, buff_locp, len); 1108 host->h_name = buff_locp; 1109 /* 1110 * Pass 2 (IPv4 aliases): 1111 */ 1112 if (he->h_aliases != NULL) { 1113 for (i = 0; he->h_aliases[i] != NULL; i++) { 1114 buff_locp -= (len = strlen(he->h_aliases[i]) + 1); 1115 /* 1116 * Has to be room for the pointer to the address we're 1117 * about to add, as well as the final NULL ptr. 1118 */ 1119 if (buff_locp <= 1120 (char *)&(host->h_aliases[count + 1])) 1121 goto cleanup; 1122 host->h_aliases[count] = buff_locp; 1123 bcopy((char *)he->h_aliases[i], buff_locp, len); 1124 ++count; 1125 } 1126 } 1127 host->h_aliases[count] = NULL; 1128 host->h_length = sizeof (struct in_addr); 1129 host->h_addrtype = AF_INET; 1130 free(res); 1131 return (host); 1132 cleanup: 1133 *extract_error = NO_RECOVERY; 1134 (void) __IPv6_cleanup(res); 1135 return (NULL); 1136 } 1137 1138 /* 1139 * This routine takes as input a pointer to a hostent and filters out 1140 * the type of addresses specified by the af argument. AF_INET 1141 * indicates that the caller wishes to filter out IPv4-mapped 1142 * addresses, and AF_INET6 indicates that the caller wishes to filter 1143 * out IPv6 addresses which aren't IPv4-mapped. If filtering would 1144 * result in all addresses being filtered out, a NULL pointer is returned. 1145 * Otherwise, the he pointer passed in is returned, even if no addresses 1146 * were filtered out. 1147 */ 1148 static struct hostent * 1149 __filter_addresses(int af, struct hostent *he) 1150 { 1151 struct in6_addr **in6addrlist, **in6addr; 1152 boolean_t isipv4mapped; 1153 int i = 0; 1154 1155 if (he == NULL) 1156 return (NULL); 1157 1158 in6addrlist = (struct in6_addr **)he->h_addr_list; 1159 for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) { 1160 isipv4mapped = IN6_IS_ADDR_V4MAPPED(*in6addr); 1161 1162 if ((af == AF_INET && !isipv4mapped) || 1163 (af == AF_INET6 && isipv4mapped)) { 1164 if (in6addrlist[i] != *in6addr) 1165 in6addrlist[i] = *in6addr; 1166 i++; 1167 } 1168 } 1169 1170 if (i == 0) { 1171 /* We filtered everything out. */ 1172 return (NULL); 1173 } else { 1174 /* NULL terminate the list and return the hostent */ 1175 in6addrlist[i] = NULL; 1176 return (he); 1177 } 1178 } 1179 1180 /* 1181 * This routine searches a hostent for v4 mapped IPv6 addresses. 1182 * he hostent structure to seach 1183 * find_both flag indicating if only want mapped or both map'd and v6 1184 * return values: 1185 * 0 = No mapped addresses 1186 * 1 = Mapped v4 address found (returns on first one found) 1187 * 2 = Both v6 and v4 mapped are present 1188 * 1189 * If hostent passed in with no addresses, zero will be returned. 1190 */ 1191 1192 static int 1193 __find_mapped(struct hostent *he, int find_both) 1194 { 1195 int i; 1196 int mapd_found = 0; 1197 int v6_found = 0; 1198 1199 for (i = 0; he->h_addr_list[i] != NULL; i++) { 1200 /* LINTED pointer cast */ 1201 if (IN6_IS_ADDR_V4MAPPED( 1202 (struct in6_addr *)he->h_addr_list[i])) { 1203 if (find_both) 1204 mapd_found = 1; 1205 else 1206 return (1); 1207 } else { 1208 v6_found = 1; 1209 } 1210 /* save some iterations once both found */ 1211 if (mapd_found && v6_found) 1212 return (2); 1213 } 1214 return (mapd_found); 1215 } 1216 1217 /* 1218 * This routine was added specifically for the IPv6 getipnodeby*() APIs. This 1219 * separates the result pointer (ptr to hostent+data buf) from the 1220 * nss_XbyY_buf_t ptr (required for nsswitch API). The returned hostent ptr 1221 * can be passed to freehostent() and freed independently. 1222 * 1223 * bufp->result bufp->buffer 1224 * | | 1225 * V V 1226 * ------------------------------------------------...-- 1227 * |struct hostent |addresses aliases | 1228 * ------------------------------------------------...-- 1229 * | |<--------bufp->buflen-------------->| 1230 */ 1231 1232 #define ALIGN(x) ((((long)(x)) + sizeof (long) - 1) & ~(sizeof (long) - 1)) 1233 1234 static nss_XbyY_buf_t * 1235 __IPv6_alloc(int bufsz) 1236 { 1237 nss_XbyY_buf_t *bufp; 1238 1239 if ((bufp = malloc(sizeof (nss_XbyY_buf_t))) == NULL) 1240 return (NULL); 1241 1242 if ((bufp->result = malloc(ALIGN(sizeof (struct hostent)) + bufsz)) == 1243 NULL) { 1244 free(bufp); 1245 return (NULL); 1246 } 1247 bufp->buffer = (char *)(bufp->result) + sizeof (struct hostent); 1248 bufp->buflen = bufsz; 1249 return (bufp); 1250 } 1251 1252 /* 1253 * This routine is use only for error return cleanup. This will free the 1254 * hostent pointer, so don't use for successful returns. 1255 */ 1256 static void 1257 __IPv6_cleanup(nss_XbyY_buf_t *bufp) 1258 { 1259 if (bufp == NULL) 1260 return; 1261 if (bufp->result != NULL) 1262 free(bufp->result); 1263 free(bufp); 1264 } 1265