import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / getipnodeby.c
blob33101f0a27a1415f79bc6c6d0e9ded1f54493996
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
26 * Copyright 2016 Joyent, Inc.
28 * This file defines and implements the re-entrant getipnodebyname(),
29 * getipnodebyaddr(), and freehostent() routines for IPv6. These routines
30 * follow use the netdir_getbyYY() (see netdir_inet.c).
32 * lib/libnsl/nss/getipnodeby.c
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 <netinet/in.h>
47 #include <sys/socket.h>
48 #include <sys/sockio.h>
49 #include <nss_netdir.h>
50 #include <net/if.h>
51 #include <netinet/in.h>
52 #include <netdir.h>
53 #include <thread.h>
54 #include <synch.h>
55 #include <fcntl.h>
56 #include <sys/time.h>
57 #include "nss.h"
59 #define IPV6_LITERAL_CHAR ':'
62 * The number of nanoseconds getipnodebyname() waits before getting
63 * fresh interface count information with SIOCGLIFNUM. The default is
64 * five minutes.
66 #define IFNUM_TIMEOUT ((hrtime_t)300 * NANOSEC)
69 * Bits in the bitfield returned by getipnodebyname_processflags().
71 * IPNODE_WANTIPV6 The user wants IPv6 addresses returned.
72 * IPNODE_WANTIPV4 The user wants IPv4 addresses returned.
73 * IPNODE_IPV4IFNOIPV6 The user only wants IPv4 addresses returned if no IPv6
74 * addresses are returned.
75 * IPNODE_LOOKUPIPNODES getipnodebyname() needs to lookup the name in ipnodes.
76 * IPNODE_LOOKUPHOSTS getipnodebyname() needs to lookup the name in hosts.
77 * IPNODE_ISLITERAL The name supplied is a literal address string.
78 * IPNODE_UNMAP The user doesn't want v4 mapped addresses if no IPv6
79 * interfaces are plumbed on the system.
81 #define IPNODE_WANTIPV6 0x00000001u
82 #define IPNODE_WANTIPV4 0x00000002u
83 #define IPNODE_IPV4IFNOIPV6 0x00000004u
84 #define IPNODE_LOOKUPIPNODES 0x00000008u
85 #define IPNODE_LOOKUPHOSTS 0x00000010u
86 #define IPNODE_LITERAL 0x00000020u
87 #define IPNODE_UNMAP 0x00000040u
88 #define IPNODE_IPV4 (IPNODE_WANTIPV4 | IPNODE_IPV4IFNOIPV6)
91 * The private flag between libsocket and libnsl. See
92 * lib/libsocket/inet/getaddrinfo.c for more information.
94 #define AI_ADDRINFO 0x8000
97 * The default set of bits corresponding to a getipnodebyname() flags
98 * argument of AI_DEFAULT.
100 #define IPNODE_DEFAULT (IPNODE_WANTIPV6 | IPNODE_IPV4 | \
101 IPNODE_LOOKUPIPNODES | IPNODE_LOOKUPHOSTS)
103 extern struct netconfig *__rpc_getconfip(char *);
105 static struct hostent *__mapv4tov6(struct hostent *, struct hostent *,
106 nss_XbyY_buf_t *, int);
107 struct hostent *__mappedtov4(struct hostent *, int *);
108 static struct hostent *__filter_addresses(int, struct hostent *);
109 static int __find_mapped(struct hostent *, int);
110 static nss_XbyY_buf_t *__IPv6_alloc(int);
111 static void __IPv6_cleanup(nss_XbyY_buf_t *);
112 static int __ai_addrconfig(int, boolean_t);
115 #ifdef PIC
116 struct hostent *
117 _uncached_getipnodebyname(const char *nam, struct hostent *result,
118 char *buffer, int buflen, int af_family, int flags, int *h_errnop)
120 return (_switch_getipnodebyname_r(nam, result, buffer, buflen,
121 af_family, flags, h_errnop));
124 struct hostent *
125 _uncached_getipnodebyaddr(const char *addr, int length, int type,
126 struct hostent *result, char *buffer, int buflen, int *h_errnop)
128 if (type == AF_INET)
129 return (_switch_gethostbyaddr_r(addr, length, type,
130 result, buffer, buflen, h_errnop));
131 else if (type == AF_INET6)
132 return (_switch_getipnodebyaddr_r(addr, length, type,
133 result, buffer, buflen, h_errnop));
134 return (NULL);
136 #endif
139 * Given a name, an address family, and a set of flags, return a
140 * bitfield that getipnodebyname() will use.
142 static uint_t
143 getipnodebyname_processflags(const char *name, int af, int flags)
145 uint_t ipnode_bits = IPNODE_DEFAULT;
146 boolean_t ipv6configured = B_FALSE;
147 boolean_t ipv4configured = B_FALSE;
150 * If AI_ADDRCONFIG is specified, we need to determine the number of
151 * addresses of each address family configured on the system as
152 * appropriate.
154 * When trying to determine which addresses should be used for
155 * addrconfig, we first ignore loopback devices. This generally makes
156 * sense as policy, as most of these queries will be trying to go
157 * off-box and one should not have an IPv6 loopback address suggest that
158 * we can now send IPv6 traffic off the box or the equivalent with IPv4.
159 * However, it's possible that no non-loopback interfaces are up on the
160 * box. In those cases, we then check which interfaces are up and
161 * consider loopback devices. While this isn't to the letter of RFC 3493
162 * (which itself is a bit vague in this case, as is SUS), it matches
163 * expected user behavior in these situations.
165 if (flags & AI_ADDRCONFIG) {
166 boolean_t hv4, hv6;
168 hv4 = __ai_addrconfig(AF_INET, B_FALSE) > 0;
169 hv6 = __ai_addrconfig(AF_INET6, B_FALSE) > 0;
171 if (hv4 == B_FALSE && hv6 == B_FALSE) {
172 hv4 = __ai_addrconfig(AF_INET, B_TRUE) > 0;
173 hv6 = __ai_addrconfig(AF_INET6, B_TRUE) > 0;
176 ipv6configured = (af == AF_INET6 && hv6);
177 ipv4configured = (af == AF_INET || (flags & AI_V4MAPPED)) &&
178 hv4;
182 * Determine what kinds of addresses the user is interested
183 * in getting back.
185 switch (af) {
186 case AF_INET6:
187 if ((flags & AI_ADDRCONFIG) && !ipv6configured)
188 ipnode_bits &= ~IPNODE_WANTIPV6;
190 if (flags & AI_V4MAPPED) {
191 if ((flags & AI_ADDRCONFIG) && !ipv4configured) {
192 ipnode_bits &= ~IPNODE_IPV4;
193 } else if (flags & AI_ALL) {
194 ipnode_bits &= ~IPNODE_IPV4IFNOIPV6;
196 if ((flags & AI_ADDRCONFIG) && !ipv6configured &&
197 (flags & AI_ADDRINFO)) {
198 ipnode_bits |= IPNODE_UNMAP;
200 } else {
201 ipnode_bits &= ~IPNODE_IPV4;
203 break;
204 case AF_INET:
205 if ((flags & AI_ADDRCONFIG) && !ipv4configured)
206 ipnode_bits &= ~IPNODE_IPV4;
207 ipnode_bits &= ~IPNODE_WANTIPV6;
208 ipnode_bits &= ~IPNODE_IPV4IFNOIPV6;
209 break;
210 default:
211 ipnode_bits = 0;
212 break;
216 * If we're not looking for IPv4 addresses, don't bother looking
217 * in hosts.
219 if (!(ipnode_bits & IPNODE_WANTIPV4))
220 ipnode_bits &= ~IPNODE_LOOKUPHOSTS;
223 * Determine if name is a literal IP address. This will
224 * further narrow down what type of lookup we're going to do.
226 if (strchr(name, IPV6_LITERAL_CHAR) != NULL) {
227 /* Literal IPv6 address */
228 ipnode_bits |= IPNODE_LITERAL;
230 * In s9 we accepted the literal without filtering independent
231 * of what family was passed in hints. We continue to do
232 * this.
234 ipnode_bits |= (IPNODE_WANTIPV6 | IPNODE_WANTIPV4);
235 ipnode_bits &= ~IPNODE_LOOKUPHOSTS;
236 } else if (inet_addr(name) != 0xffffffffU) {
237 /* Literal IPv4 address */
238 ipnode_bits |= (IPNODE_LITERAL | IPNODE_WANTIPV4);
239 ipnode_bits &= ~IPNODE_WANTIPV6;
240 ipnode_bits &= ~IPNODE_LOOKUPIPNODES;
242 return (ipnode_bits);
245 struct hostent *
246 getipnodebyname(const char *name, int af, int flags, int *error_num)
248 struct hostent *hp = NULL;
249 nss_XbyY_buf_t *buf4 = NULL;
250 nss_XbyY_buf_t *buf6 = NULL;
251 struct netconfig *nconf;
252 struct nss_netdirbyname_in nssin;
253 union nss_netdirbyname_out nssout;
254 int ret;
255 uint_t ipnode_bits;
257 if ((nconf = __rpc_getconfip("udp")) == NULL &&
258 (nconf = __rpc_getconfip("tcp")) == NULL) {
259 *error_num = NO_RECOVERY;
260 return (NULL);
263 ipnode_bits = getipnodebyname_processflags(name, af, flags);
265 /* Make sure we have something to look up. */
266 if (!(ipnode_bits & (IPNODE_WANTIPV6 | IPNODE_WANTIPV4))) {
267 *error_num = HOST_NOT_FOUND;
268 goto cleanup;
272 * Perform the requested lookups. We always look through
273 * ipnodes first for both IPv4 and IPv6 addresses. Depending
274 * on what was returned and what was needed, we either filter
275 * out the garbage, or ask for more using hosts.
277 if (ipnode_bits & IPNODE_LOOKUPIPNODES) {
278 if ((buf6 = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == NULL) {
279 *error_num = NO_RECOVERY;
280 goto cleanup;
282 nssin.op_t = NSS_HOST6;
283 nssin.arg.nss.host6.name = name;
284 nssin.arg.nss.host6.buf = buf6->buffer;
285 nssin.arg.nss.host6.buflen = buf6->buflen;
286 nssin.arg.nss.host6.af_family = af;
287 nssin.arg.nss.host6.flags = flags;
288 nssout.nss.host.hent = buf6->result;
289 nssout.nss.host.herrno_p = error_num;
290 ret = _get_hostserv_inetnetdir_byname(nconf, &nssin, &nssout);
291 if (ret != ND_OK) {
292 __IPv6_cleanup(buf6);
293 buf6 = NULL;
294 } else if (ipnode_bits & IPNODE_WANTIPV4) {
296 * buf6 may have all that we need if we either
297 * only wanted IPv4 addresses if there were no
298 * IPv6 addresses returned, or if there are
299 * IPv4-mapped addresses in buf6. If either
300 * of these are true, then there's no need to
301 * look in hosts.
303 if (ipnode_bits & IPNODE_IPV4IFNOIPV6 ||
304 __find_mapped(buf6->result, 0) != 0) {
305 ipnode_bits &= ~IPNODE_LOOKUPHOSTS;
306 } else if (!(ipnode_bits & IPNODE_WANTIPV6)) {
308 * If all we're looking for are IPv4
309 * addresses and there are none in
310 * buf6 then buf6 is now useless.
312 __IPv6_cleanup(buf6);
313 buf6 = NULL;
317 if (ipnode_bits & IPNODE_LOOKUPHOSTS) {
318 if ((buf4 = __IPv6_alloc(NSS_BUFLEN_HOSTS)) == NULL) {
319 *error_num = NO_RECOVERY;
320 goto cleanup;
322 nssin.op_t = NSS_HOST;
323 nssin.arg.nss.host.name = name;
324 nssin.arg.nss.host.buf = buf4->buffer;
325 nssin.arg.nss.host.buflen = buf4->buflen;
326 nssout.nss.host.hent = buf4->result;
327 nssout.nss.host.herrno_p = error_num;
328 ret = _get_hostserv_inetnetdir_byname(nconf, &nssin, &nssout);
329 if (ret != ND_OK) {
330 __IPv6_cleanup(buf4);
331 buf4 = NULL;
335 if (buf6 == NULL && buf4 == NULL) {
336 *error_num = HOST_NOT_FOUND;
337 goto cleanup;
340 /* Extract the appropriate addresses from the returned buffer(s). */
341 switch (af) {
342 case AF_INET6: {
343 if (buf4 != NULL) {
344 nss_XbyY_buf_t *mergebuf;
347 * The IPv4 results we have need to be
348 * converted to IPv4-mapped addresses,
349 * conditionally merged with the IPv6
350 * results, and the end result needs to be
351 * re-ordered.
353 mergebuf = __IPv6_alloc(NSS_BUFLEN_IPNODES);
354 if (mergebuf == NULL) {
355 *error_num = NO_RECOVERY;
356 goto cleanup;
358 hp = __mapv4tov6(buf4->result,
359 ((buf6 != NULL) ? buf6->result : NULL),
360 mergebuf, 1);
361 if (hp != NULL)
362 order_haddrlist_af(AF_INET6, hp->h_addr_list);
363 else
364 *error_num = NO_RECOVERY;
365 free(mergebuf);
368 if (buf4 == NULL && buf6 != NULL) {
369 hp = buf6->result;
372 * We have what we need in buf6, but we may need
373 * to filter out some addresses depending on what
374 * is being asked for.
376 if (!(ipnode_bits & IPNODE_WANTIPV4))
377 hp = __filter_addresses(AF_INET, buf6->result);
378 else if (!(ipnode_bits & IPNODE_WANTIPV6))
379 hp = __filter_addresses(AF_INET6, buf6->result);
382 * We've been asked to unmap v4 addresses. This
383 * situation implies IPNODE_WANTIPV4 and
384 * !IPNODE_WANTIPV6.
386 if (hp != NULL && (ipnode_bits & IPNODE_UNMAP)) {
388 * Just set hp to a new value, cleanup: will
389 * free the old one
391 hp = __mappedtov4(hp, error_num);
392 } else if (hp == NULL)
393 *error_num = NO_ADDRESS;
396 break;
399 case AF_INET:
400 /* We could have results in buf6 or buf4, not both */
401 if (buf6 != NULL) {
403 * Extract the IPv4-mapped addresses from buf6
404 * into hp.
406 hp = __mappedtov4(buf6->result, error_num);
407 } else {
408 /* We have what we need in buf4. */
409 hp = buf4->result;
410 if (ipnode_bits & IPNODE_LITERAL) {
412 * There is a special case here for literal
413 * IPv4 address strings. The hosts
414 * front-end sets h_aliases to a one
415 * element array containing a single NULL
416 * pointer (in ndaddr2hent()), while
417 * getipnodebyname() requires h_aliases to
418 * be a NULL pointer itself. We're not
419 * going to change the front-end since it
420 * needs to remain backward compatible for
421 * gethostbyname() and friends. Just set
422 * h_aliases to NULL here instead.
424 hp->h_aliases = NULL;
428 break;
430 default:
431 break;
434 cleanup:
436 * Free the memory we allocated, but make sure we don't free
437 * the memory we're returning to the caller.
439 if (buf6 != NULL) {
440 if (buf6->result == hp)
441 buf6->result = NULL;
442 __IPv6_cleanup(buf6);
444 if (buf4 != NULL) {
445 if (buf4->result == hp)
446 buf4->result = NULL;
447 __IPv6_cleanup(buf4);
449 (void) freenetconfigent(nconf);
451 return (hp);
455 * This is the IPv6 interface for "gethostbyaddr".
457 struct hostent *
458 getipnodebyaddr(const void *src, size_t len, int type, int *error_num)
460 struct in6_addr *addr6 = 0;
461 struct in_addr *addr4 = 0;
462 nss_XbyY_buf_t *buf = 0;
463 nss_XbyY_buf_t *res = 0;
464 struct netconfig *nconf;
465 struct hostent *hp = 0;
466 struct nss_netdirbyaddr_in nssin;
467 union nss_netdirbyaddr_out nssout;
468 int neterr;
469 char tmpbuf[64];
471 if (type == AF_INET6) {
472 if ((addr6 = (struct in6_addr *)src) == NULL) {
473 *error_num = HOST_NOT_FOUND;
474 return (NULL);
476 } else if (type == AF_INET) {
477 if ((addr4 = (struct in_addr *)src) == NULL) {
478 *error_num = HOST_NOT_FOUND;
479 return (NULL);
481 } else {
482 *error_num = HOST_NOT_FOUND;
483 return (NULL);
486 * Specific case: query for "::"
488 if (type == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(addr6)) {
489 *error_num = HOST_NOT_FOUND;
490 return (NULL);
493 * Step 1: IPv4-mapped address or IPv4 Compat
495 if ((type == AF_INET6 && len == 16) &&
496 ((IN6_IS_ADDR_V4MAPPED(addr6)) ||
497 (IN6_IS_ADDR_V4COMPAT(addr6)))) {
498 if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) {
499 *error_num = NO_RECOVERY;
500 return (NULL);
502 if ((nconf = __rpc_getconfip("udp")) == NULL &&
503 (nconf = __rpc_getconfip("tcp")) == NULL) {
504 *error_num = NO_RECOVERY;
505 __IPv6_cleanup(buf);
506 return (NULL);
508 nssin.op_t = NSS_HOST6;
509 if (IN6_IS_ADDR_V4COMPAT(addr6)) {
510 (void) memcpy(tmpbuf, addr6, sizeof (*addr6));
511 tmpbuf[10] = 0xffU;
512 tmpbuf[11] = 0xffU;
513 nssin.arg.nss.host.addr = (const char *)tmpbuf;
514 } else {
515 nssin.arg.nss.host.addr = (const char *)addr6;
517 nssin.arg.nss.host.len = sizeof (struct in6_addr);
518 nssin.arg.nss.host.type = AF_INET6;
519 nssin.arg.nss.host.buf = buf->buffer;
520 nssin.arg.nss.host.buflen = buf->buflen;
522 nssout.nss.host.hent = buf->result;
523 nssout.nss.host.herrno_p = error_num;
525 * We pass in nconf and let the implementation of the
526 * long-named func decide whether to use the switch based on
527 * nc_nlookups.
529 neterr =
530 _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout);
532 (void) freenetconfigent(nconf);
533 if (neterr != ND_OK) {
534 /* Failover case, try hosts db for v4 address */
535 if (!gethostbyaddr_r(((char *)addr6) + 12,
536 sizeof (in_addr_t), AF_INET, buf->result,
537 buf->buffer, buf->buflen, error_num)) {
538 __IPv6_cleanup(buf);
539 return (NULL);
541 /* Found one, now format it into mapped/compat addr */
542 if ((res = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) {
543 __IPv6_cleanup(buf);
544 *error_num = NO_RECOVERY;
545 return (NULL);
547 /* Convert IPv4 to mapped/compat address w/name */
548 hp = res->result;
549 (void) __mapv4tov6(buf->result, 0, res,
550 IN6_IS_ADDR_V4MAPPED(addr6));
551 __IPv6_cleanup(buf);
552 free(res);
553 return (hp);
556 * At this point, we'll have a v4mapped hostent. If that's
557 * what was passed in, just return. If the request was a compat,
558 * twiggle the two bytes to make the mapped address a compat.
560 hp = buf->result;
561 if (IN6_IS_ADDR_V4COMPAT(addr6)) {
562 /* LINTED pointer cast */
563 addr6 = (struct in6_addr *)hp->h_addr_list[0];
564 addr6->s6_addr[10] = 0;
565 addr6->s6_addr[11] = 0;
567 free(buf);
568 return (hp);
571 * Step 2: AF_INET, v4 lookup. Since we're going to search the
572 * ipnodes (v6) path first, we need to treat this as a v4mapped
573 * address. nscd(1m) caches v4 from ipnodes as mapped v6's. The
574 * switch backend knows to lookup v4's (not v4mapped) from the
575 * name services.
577 if (type == AF_INET) {
578 struct in6_addr v4mapbuf;
579 addr6 = &v4mapbuf;
581 IN6_INADDR_TO_V4MAPPED(addr4, addr6);
582 if ((nconf = __rpc_getconfip("udp")) == NULL &&
583 (nconf = __rpc_getconfip("tcp")) == NULL) {
584 *error_num = NO_RECOVERY;
585 return (NULL);
587 if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) {
588 *error_num = NO_RECOVERY;
589 freenetconfigent(nconf);
590 return (NULL);
592 nssin.op_t = NSS_HOST6;
593 nssin.arg.nss.host.addr = (const char *)addr6;
594 nssin.arg.nss.host.len = sizeof (struct in6_addr);
595 nssin.arg.nss.host.type = AF_INET6;
596 nssin.arg.nss.host.buf = buf->buffer;
597 nssin.arg.nss.host.buflen = buf->buflen;
599 nssout.nss.host.hent = buf->result;
600 nssout.nss.host.herrno_p = error_num;
602 * We pass in nconf and let the implementation of the
603 * long-named func decide whether to use the switch based on
604 * nc_nlookups.
606 neterr =
607 _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout);
609 (void) freenetconfigent(nconf);
610 if (neterr != ND_OK) {
611 /* Failover case, try hosts db for v4 address */
612 hp = buf->result;
613 if (!gethostbyaddr_r(src, len, type, buf->result,
614 buf->buffer, buf->buflen, error_num)) {
615 __IPv6_cleanup(buf);
616 return (NULL);
618 free(buf);
619 return (hp);
621 if ((hp = __mappedtov4(buf->result, error_num)) == NULL) {
622 __IPv6_cleanup(buf);
623 return (NULL);
625 __IPv6_cleanup(buf);
626 return (hp);
629 * Step 3: AF_INET6, plain vanilla v6 getipnodebyaddr() call.
631 if (type == AF_INET6) {
632 if ((nconf = __rpc_getconfip("udp")) == NULL &&
633 (nconf = __rpc_getconfip("tcp")) == NULL) {
634 *error_num = NO_RECOVERY;
635 return (NULL);
637 if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) {
638 *error_num = NO_RECOVERY;
639 freenetconfigent(nconf);
640 return (NULL);
642 nssin.op_t = NSS_HOST6;
643 nssin.arg.nss.host.addr = (const char *)addr6;
644 nssin.arg.nss.host.len = len;
645 nssin.arg.nss.host.type = type;
646 nssin.arg.nss.host.buf = buf->buffer;
647 nssin.arg.nss.host.buflen = buf->buflen;
649 nssout.nss.host.hent = buf->result;
650 nssout.nss.host.herrno_p = error_num;
652 * We pass in nconf and let the implementation of the
653 * long-named func decide whether to use the switch based on
654 * nc_nlookups.
656 neterr =
657 _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout);
659 (void) freenetconfigent(nconf);
660 if (neterr != ND_OK) {
661 __IPv6_cleanup(buf);
662 return (NULL);
664 free(buf);
665 return (nssout.nss.host.hent);
668 * If we got here, unknown type.
670 *error_num = HOST_NOT_FOUND;
671 return (NULL);
674 void
675 freehostent(struct hostent *hent)
677 free(hent);
680 static int
681 __ai_addrconfig(int af, boolean_t loopback)
683 struct lifnum lifn;
684 struct lifconf lifc;
685 struct lifreq *lifp, *buf = NULL;
686 size_t bufsize;
687 hrtime_t now, *then;
688 static hrtime_t then4, then6; /* the last time we updated ifnum# */
689 static int ifnum4 = -1, ifnum6 = -1, iflb4 = 0, iflb6 = 0;
690 int *num, *lb;
691 int nlifr, count = 0;
694 switch (af) {
695 case AF_INET:
696 num = &ifnum4;
697 then = &then4;
698 lb = &iflb4;
699 break;
700 case AF_INET6:
701 num = &ifnum6;
702 then = &then6;
703 lb = &iflb6;
704 break;
705 default:
706 return (0);
710 * We don't need to check this every time someone does a name
711 * lookup. Do it every IFNUM_TIMEOUT for each address family.
713 * There's no need to protect all of this with a lock. The
714 * worst that can happen is that we update the interface count
715 * twice instead of once. That's no big deal.
717 now = gethrtime();
718 if (*num == -1 || ((now - *then) >= IFNUM_TIMEOUT)) {
719 lifn.lifn_family = af;
721 * We want to determine if this machine knows anything
722 * at all about the address family; the status of the
723 * interface is less important. Hence, set
724 * 'lifn_flags' to zero.
726 lifn.lifn_flags = 0;
727 again:
728 if (nss_ioctl(af, SIOCGLIFNUM, &lifn) < 0)
729 goto fail;
731 if (lifn.lifn_count == 0) {
732 *lb = 0;
733 *num = 0;
734 *then = now;
735 return (*num);
739 * Pad the interface count to detect when additional
740 * interfaces have been configured between SIOCGLIFNUM
741 * and SIOCGLIFCONF.
743 lifn.lifn_count += 4;
745 bufsize = lifn.lifn_count * sizeof (struct lifreq);
746 if ((buf = realloc(buf, bufsize)) == NULL)
747 goto fail;
749 lifc.lifc_family = af;
750 lifc.lifc_flags = 0;
751 lifc.lifc_len = bufsize;
752 lifc.lifc_buf = (caddr_t)buf;
753 if (nss_ioctl(af, SIOCGLIFCONF, &lifc) < 0)
754 goto fail;
756 nlifr = lifc.lifc_len / sizeof (struct lifreq);
757 if (nlifr >= lifn.lifn_count)
758 goto again;
760 * Do not include any loopback addresses, 127.0.0.1 for AF_INET
761 * and ::1 for AF_INET6, while counting the number of available
762 * IPv4 or IPv6 addresses. (RFC 3493 requires this, whenever
763 * AI_ADDRCONFIG flag is set) However, if the loopback flag is
764 * set to true we'll include it in the output.
766 for (lifp = buf; lifp < buf + nlifr; lifp++) {
767 switch (af) {
768 case AF_INET: {
769 struct sockaddr_in *in;
771 in = (struct sockaddr_in *)&lifp->lifr_addr;
772 if (ntohl(in->sin_addr.s_addr) ==
773 INADDR_LOOPBACK) {
774 count++;
776 break;
778 case AF_INET6: {
779 struct sockaddr_in6 *in6;
781 in6 = (struct sockaddr_in6 *)&lifp->lifr_addr;
782 if (IN6_IS_ADDR_LOOPBACK(&in6->sin6_addr))
783 count++;
784 break;
788 *num = nlifr - count;
789 *lb = count;
790 *then = now;
791 free(buf);
793 if (loopback == B_TRUE)
794 return (*num + *lb);
795 else
796 return (*num);
797 fail:
798 free(buf);
800 * If the process is running without the NET_ACCESS basic privilege,
801 * pretend we still have inet/inet6 interfaces.
803 if (errno == EACCES)
804 return (1);
805 return (-1);
809 * This routine will either convert an IPv4 address to a mapped or compat
810 * IPv6 (if he6 == NULL) or merge IPv6 (he6) addresses with mapped
811 * v4 (he4) addresses. In either case, the results are returned in res.
812 * Caller must provide all buffers.
813 * Inputs:
814 * he4 pointer to IPv4 buffer
815 * he6 pointer to IPv6 buffer (NULL if not merging v4/v6
816 * res pointer to results buffer
817 * mapped mapped == 1, map IPv4 : mapped == 0, compat IPv4
818 * mapped flag is ignored if he6 != NULL
820 * The results are packed into the res->buffer as follows:
821 * <--------------- buffer + buflen -------------------------------------->
822 * |-----------------|-----------------|----------------|----------------|
823 * | pointers vector | pointers vector | aliases grow | addresses grow |
824 * | for addresses | for aliases | | |
825 * | this way -> | this way -> | <- this way |<- this way |
826 * |-----------------|-----------------|----------------|----------------|
827 * | grows in PASS 1 | grows in PASS2 | grows in PASS2 | grows in PASS 1|
829 static struct hostent *
830 __mapv4tov6(struct hostent *he4, struct hostent *he6, nss_XbyY_buf_t *res,
831 int mapped)
833 char *buffer, *limit;
834 int buflen = res->buflen;
835 struct in6_addr *addr6p;
836 char *buff_locp;
837 struct hostent *host;
838 int count = 0, len, i;
839 char *h_namep;
841 if (he4 == NULL || res == NULL) {
842 return (NULL);
844 limit = res->buffer + buflen;
845 host = (struct hostent *)res->result;
846 buffer = res->buffer;
848 buff_locp = (char *)ROUND_DOWN(limit, sizeof (struct in6_addr));
849 host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **));
850 if ((char *)host->h_addr_list >= limit ||
851 buff_locp <= (char *)host->h_addr_list) {
852 return (NULL);
854 if (he6 == NULL) {
856 * If he6==NULL, map the v4 address into the v6 address format.
857 * This is used for getipnodebyaddr() (single address, mapped or
858 * compatible) or for v4 mapped for getipnodebyname(), which
859 * could be multiple addresses. This could also be a literal
860 * address string, which is why there is a inet_addr() call.
862 for (i = 0; he4->h_addr_list[i] != NULL; i++) {
863 buff_locp -= sizeof (struct in6_addr);
864 if (buff_locp <=
865 (char *)&(host->h_addr_list[count + 1])) {
867 * Has to be room for the pointer to the address we're
868 * about to add, as well as the final NULL ptr.
870 return (NULL);
872 /* LINTED pointer cast */
873 addr6p = (struct in6_addr *)buff_locp;
874 host->h_addr_list[count] = (char *)addr6p;
875 bzero(addr6p->s6_addr, sizeof (struct in6_addr));
876 if (mapped) {
877 addr6p->s6_addr[10] = 0xff;
878 addr6p->s6_addr[11] = 0xff;
880 bcopy((char *)he4->h_addr_list[i],
881 &addr6p->s6_addr[12], sizeof (struct in_addr));
882 ++count;
885 * Set last array element to NULL and add cname as first alias
887 host->h_addr_list[count] = NULL;
888 host->h_aliases = host->h_addr_list + count + 1;
889 count = 0;
890 if ((int)(inet_addr(he4->h_name)) != -1) {
892 * Literal address string, since we're mapping, we need the IPv6
893 * V4 mapped literal address string for h_name.
895 char tmpstr[128];
896 (void) inet_ntop(AF_INET6, host->h_addr_list[0], tmpstr,
897 sizeof (tmpstr));
898 buff_locp -= (len = strlen(tmpstr) + 1);
899 h_namep = tmpstr;
900 if (buff_locp <= (char *)(host->h_aliases))
901 return (NULL);
902 bcopy(h_namep, buff_locp, len);
903 host->h_name = buff_locp;
904 host->h_aliases = NULL; /* no aliases for literal */
905 host->h_length = sizeof (struct in6_addr);
906 host->h_addrtype = AF_INET6;
907 return (host); /* we're done, return result */
910 * Not a literal address string, so just copy h_name.
912 buff_locp -= (len = strlen(he4->h_name) + 1);
913 h_namep = he4->h_name;
914 if (buff_locp <= (char *)(host->h_aliases))
915 return (NULL);
916 bcopy(h_namep, buff_locp, len);
917 host->h_name = buff_locp;
919 * Pass 2 (IPv4 aliases):
921 for (i = 0; he4->h_aliases[i] != NULL; i++) {
922 buff_locp -= (len = strlen(he4->h_aliases[i]) + 1);
923 if (buff_locp <=
924 (char *)&(host->h_aliases[count + 1])) {
926 * Has to be room for the pointer to the address we're
927 * about to add, as well as the final NULL ptr.
929 return (NULL);
931 host->h_aliases[count] = buff_locp;
932 bcopy((char *)he4->h_aliases[i], buff_locp, len);
933 ++count;
935 host->h_aliases[count] = NULL;
936 host->h_length = sizeof (struct in6_addr);
937 host->h_addrtype = AF_INET6;
938 return (host);
939 } else {
941 * Merge IPv4 mapped addresses with IPv6 addresses. The
942 * IPv6 address will go in first, followed by the v4 mapped.
944 * Pass 1 (IPv6 addresses):
946 for (i = 0; he6->h_addr_list[i] != NULL; i++) {
947 buff_locp -= sizeof (struct in6_addr);
948 if (buff_locp <=
949 (char *)&(host->h_addr_list[count + 1])) {
951 * Has to be room for the pointer to the address we're
952 * about to add, as well as the final NULL ptr.
954 return (NULL);
956 host->h_addr_list[count] = buff_locp;
957 bcopy((char *)he6->h_addr_list[i], buff_locp,
958 sizeof (struct in6_addr));
959 ++count;
962 * Pass 1 (IPv4 mapped addresses):
964 for (i = 0; he4->h_addr_list[i] != NULL; i++) {
965 buff_locp -= sizeof (struct in6_addr);
966 if (buff_locp <=
967 (char *)&(host->h_addr_list[count + 1])) {
969 * Has to be room for the pointer to the address we're
970 * about to add, as well as the final NULL ptr.
972 return (NULL);
974 /* LINTED pointer cast */
975 addr6p = (struct in6_addr *)buff_locp;
976 host->h_addr_list[count] = (char *)addr6p;
977 bzero(addr6p->s6_addr, sizeof (struct in6_addr));
978 addr6p->s6_addr[10] = 0xff;
979 addr6p->s6_addr[11] = 0xff;
980 bcopy(he4->h_addr_list[i], &addr6p->s6_addr[12],
981 sizeof (struct in_addr));
982 ++count;
985 * Pass 2 (IPv6 aliases, host name first). We start h_aliases
986 * one after where h_addr_list array ended. This is where cname
987 * is put, followed by all aliases. Reset count to 0, for index
988 * in the h_aliases array.
990 host->h_addr_list[count] = NULL;
991 host->h_aliases = host->h_addr_list + count + 1;
992 count = 0;
993 buff_locp -= (len = strlen(he6->h_name) + 1);
994 if (buff_locp <= (char *)(host->h_aliases))
995 return (NULL);
996 bcopy(he6->h_name, buff_locp, len);
997 host->h_name = buff_locp;
998 for (i = 0; he6->h_aliases[i] != NULL; i++) {
999 buff_locp -= (len = strlen(he6->h_aliases[i]) + 1);
1000 if (buff_locp <=
1001 (char *)&(host->h_aliases[count + 1])) {
1003 * Has to be room for the pointer to the address we're
1004 * about to add, as well as the final NULL ptr.
1006 return (NULL);
1008 host->h_aliases[count] = buff_locp;
1009 bcopy((char *)he6->h_aliases[i], buff_locp, len);
1010 ++count;
1013 * Pass 2 (IPv4 aliases):
1015 for (i = 0; he4->h_aliases[i] != NULL; i++) {
1016 buff_locp -= (len = strlen(he4->h_aliases[i]) + 1);
1017 if (buff_locp <=
1018 (char *)&(host->h_aliases[count + 1])) {
1020 * Has to be room for the pointer to the address we're
1021 * about to add, as well as the final NULL ptr.
1023 return (NULL);
1025 host->h_aliases[count] = buff_locp;
1026 bcopy((char *)he4->h_aliases[i], buff_locp, len);
1027 ++count;
1029 host->h_aliases[count] = NULL;
1030 host->h_length = sizeof (struct in6_addr);
1031 host->h_addrtype = AF_INET6;
1032 return (host);
1037 * This routine will convert a mapped v4 hostent (AF_INET6) to a
1038 * AF_INET hostent. If no mapped addrs found, then a NULL is returned.
1039 * If mapped addrs found, then a new buffer is alloc'd and all the v4 mapped
1040 * addresses are extracted and copied to it. On sucess, a pointer to a new
1041 * hostent is returned.
1042 * There are two possible errors in which case a NULL is returned.
1043 * One of two error codes are returned:
1045 * NO_RECOVERY - a malloc failed or the like for which there's no recovery.
1046 * NO_ADDRESS - after filtering all the v4, there was nothing left!
1048 * Inputs:
1049 * he pointer to hostent with mapped v4 addresses
1050 * filter_error pointer to return error code
1051 * Return:
1052 * pointer to a malloc'd hostent with v4 addresses.
1054 * The results are packed into the res->buffer as follows:
1055 * <--------------- buffer + buflen -------------------------------------->
1056 * |-----------------|-----------------|----------------|----------------|
1057 * | pointers vector | pointers vector | aliases grow | addresses grow |
1058 * | for addresses | for aliases | | |
1059 * | this way -> | this way -> | <- this way |<- this way |
1060 * |-----------------|-----------------|----------------|----------------|
1061 * | grows in PASS 1 | grows in PASS2 | grows in PASS2 | grows in PASS 1|
1063 struct hostent *
1064 __mappedtov4(struct hostent *he, int *extract_error)
1066 char *buffer, *limit;
1067 nss_XbyY_buf_t *res;
1068 int buflen = NSS_BUFLEN_HOSTS;
1069 struct in_addr *addr4p;
1070 char *buff_locp;
1071 struct hostent *host;
1072 int count = 0, len, i;
1073 char *h_namep;
1075 if (he == NULL) {
1076 *extract_error = NO_ADDRESS;
1077 return (NULL);
1079 if ((__find_mapped(he, 0)) == 0) {
1080 *extract_error = NO_ADDRESS;
1081 return (NULL);
1083 if ((res = __IPv6_alloc(NSS_BUFLEN_HOSTS)) == 0) {
1084 *extract_error = NO_RECOVERY;
1085 return (NULL);
1087 limit = res->buffer + buflen;
1088 host = (struct hostent *)res->result;
1089 buffer = res->buffer;
1091 buff_locp = (char *)ROUND_DOWN(limit, sizeof (struct in_addr));
1092 host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **));
1093 if ((char *)host->h_addr_list >= limit ||
1094 buff_locp <= (char *)host->h_addr_list)
1095 goto cleanup;
1097 * "Unmap" the v4 mapped address(es) into a v4 hostent format.
1098 * This is used for getipnodebyaddr() (single address) or for
1099 * v4 mapped for getipnodebyname(), which could be multiple
1100 * addresses. This could also be a literal address string,
1101 * which is why there is a inet_addr() call.
1103 for (i = 0; he->h_addr_list[i] != NULL; i++) {
1104 /* LINTED pointer cast */
1105 if (!IN6_IS_ADDR_V4MAPPED((struct in6_addr *)
1106 he->h_addr_list[i]))
1107 continue;
1108 buff_locp -= sizeof (struct in6_addr);
1110 * Has to be room for the pointer to the address we're
1111 * about to add, as well as the final NULL ptr.
1113 if (buff_locp <=
1114 (char *)&(host->h_addr_list[count + 1]))
1115 goto cleanup;
1116 /* LINTED pointer cast */
1117 addr4p = (struct in_addr *)buff_locp;
1118 host->h_addr_list[count] = (char *)addr4p;
1119 bzero((char *)&addr4p->s_addr,
1120 sizeof (struct in_addr));
1121 /* LINTED pointer cast */
1122 IN6_V4MAPPED_TO_INADDR(
1123 (struct in6_addr *)he->h_addr_list[i], addr4p);
1124 ++count;
1127 * Set last array element to NULL and add cname as first alias
1129 host->h_addr_list[count] = NULL;
1130 host->h_aliases = host->h_addr_list + count + 1;
1131 count = 0;
1132 /* Copy official host name */
1133 buff_locp -= (len = strlen(he->h_name) + 1);
1134 h_namep = he->h_name;
1135 if (buff_locp <= (char *)(host->h_aliases))
1136 goto cleanup;
1137 bcopy(h_namep, buff_locp, len);
1138 host->h_name = buff_locp;
1140 * Pass 2 (IPv4 aliases):
1142 if (he->h_aliases != NULL) {
1143 for (i = 0; he->h_aliases[i] != NULL; i++) {
1144 buff_locp -= (len = strlen(he->h_aliases[i]) + 1);
1146 * Has to be room for the pointer to the address we're
1147 * about to add, as well as the final NULL ptr.
1149 if (buff_locp <=
1150 (char *)&(host->h_aliases[count + 1]))
1151 goto cleanup;
1152 host->h_aliases[count] = buff_locp;
1153 bcopy((char *)he->h_aliases[i], buff_locp, len);
1154 ++count;
1157 host->h_aliases[count] = NULL;
1158 host->h_length = sizeof (struct in_addr);
1159 host->h_addrtype = AF_INET;
1160 free(res);
1161 return (host);
1162 cleanup:
1163 *extract_error = NO_RECOVERY;
1164 (void) __IPv6_cleanup(res);
1165 return (NULL);
1169 * This routine takes as input a pointer to a hostent and filters out
1170 * the type of addresses specified by the af argument. AF_INET
1171 * indicates that the caller wishes to filter out IPv4-mapped
1172 * addresses, and AF_INET6 indicates that the caller wishes to filter
1173 * out IPv6 addresses which aren't IPv4-mapped. If filtering would
1174 * result in all addresses being filtered out, a NULL pointer is returned.
1175 * Otherwise, the he pointer passed in is returned, even if no addresses
1176 * were filtered out.
1178 static struct hostent *
1179 __filter_addresses(int af, struct hostent *he)
1181 struct in6_addr **in6addrlist, **in6addr;
1182 boolean_t isipv4mapped;
1183 int i = 0;
1185 if (he == NULL)
1186 return (NULL);
1188 in6addrlist = (struct in6_addr **)he->h_addr_list;
1189 for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
1190 isipv4mapped = IN6_IS_ADDR_V4MAPPED(*in6addr);
1192 if ((af == AF_INET && !isipv4mapped) ||
1193 (af == AF_INET6 && isipv4mapped)) {
1194 if (in6addrlist[i] != *in6addr)
1195 in6addrlist[i] = *in6addr;
1196 i++;
1200 if (i == 0) {
1201 /* We filtered everything out. */
1202 return (NULL);
1203 } else {
1204 /* NULL terminate the list and return the hostent */
1205 in6addrlist[i] = NULL;
1206 return (he);
1211 * This routine searches a hostent for v4 mapped IPv6 addresses.
1212 * he hostent structure to seach
1213 * find_both flag indicating if only want mapped or both map'd and v6
1214 * return values:
1215 * 0 = No mapped addresses
1216 * 1 = Mapped v4 address found (returns on first one found)
1217 * 2 = Both v6 and v4 mapped are present
1219 * If hostent passed in with no addresses, zero will be returned.
1222 static int
1223 __find_mapped(struct hostent *he, int find_both)
1225 int i;
1226 int mapd_found = 0;
1227 int v6_found = 0;
1229 for (i = 0; he->h_addr_list[i] != NULL; i++) {
1230 /* LINTED pointer cast */
1231 if (IN6_IS_ADDR_V4MAPPED(
1232 (struct in6_addr *)he->h_addr_list[i])) {
1233 if (find_both)
1234 mapd_found = 1;
1235 else
1236 return (1);
1237 } else {
1238 v6_found = 1;
1240 /* save some iterations once both found */
1241 if (mapd_found && v6_found)
1242 return (2);
1244 return (mapd_found);
1248 * This routine was added specifically for the IPv6 getipnodeby*() APIs. This
1249 * separates the result pointer (ptr to hostent+data buf) from the
1250 * nss_XbyY_buf_t ptr (required for nsswitch API). The returned hostent ptr
1251 * can be passed to freehostent() and freed independently.
1253 * bufp->result bufp->buffer
1254 * | |
1255 * V V
1256 * ------------------------------------------------...--
1257 * |struct hostent |addresses aliases |
1258 * ------------------------------------------------...--
1259 * | |<--------bufp->buflen-------------->|
1262 #define ALIGN(x) ((((long)(x)) + sizeof (long) - 1) & ~(sizeof (long) - 1))
1264 static nss_XbyY_buf_t *
1265 __IPv6_alloc(int bufsz)
1267 nss_XbyY_buf_t *bufp;
1269 if ((bufp = malloc(sizeof (nss_XbyY_buf_t))) == NULL)
1270 return (NULL);
1272 if ((bufp->result = malloc(ALIGN(sizeof (struct hostent)) + bufsz)) ==
1273 NULL) {
1274 free(bufp);
1275 return (NULL);
1277 bufp->buffer = (char *)(bufp->result) + sizeof (struct hostent);
1278 bufp->buflen = bufsz;
1279 return (bufp);
1283 * This routine is use only for error return cleanup. This will free the
1284 * hostent pointer, so don't use for successful returns.
1286 static void
1287 __IPv6_cleanup(nss_XbyY_buf_t *bufp)
1289 if (bufp == NULL)
1290 return;
1291 free(bufp->result);
1292 free(bufp);