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