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]
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
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>
51 #include <netinet/in.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
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
);
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
));
125 _uncached_getipnodebyaddr(const char *addr
, int length
, int type
,
126 struct hostent
*result
, char *buffer
, int buflen
, int *h_errnop
)
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
));
139 * Given a name, an address family, and a set of flags, return a
140 * bitfield that getipnodebyname() will use.
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
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
) {
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
)) &&
182 * Determine what kinds of addresses the user is interested
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
;
201 ipnode_bits
&= ~IPNODE_IPV4
;
205 if ((flags
& AI_ADDRCONFIG
) && !ipv4configured
)
206 ipnode_bits
&= ~IPNODE_IPV4
;
207 ipnode_bits
&= ~IPNODE_WANTIPV6
;
208 ipnode_bits
&= ~IPNODE_IPV4IFNOIPV6
;
216 * If we're not looking for IPv4 addresses, don't bother looking
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
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
);
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
;
257 if ((nconf
= __rpc_getconfip("udp")) == NULL
&&
258 (nconf
= __rpc_getconfip("tcp")) == NULL
) {
259 *error_num
= NO_RECOVERY
;
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
;
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
;
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
);
292 __IPv6_cleanup(buf6
);
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
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
);
317 if (ipnode_bits
& IPNODE_LOOKUPHOSTS
) {
318 if ((buf4
= __IPv6_alloc(NSS_BUFLEN_HOSTS
)) == NULL
) {
319 *error_num
= NO_RECOVERY
;
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
);
330 __IPv6_cleanup(buf4
);
335 if (buf6
== NULL
&& buf4
== NULL
) {
336 *error_num
= HOST_NOT_FOUND
;
340 /* Extract the appropriate addresses from the returned buffer(s). */
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
353 mergebuf
= __IPv6_alloc(NSS_BUFLEN_IPNODES
);
354 if (mergebuf
== NULL
) {
355 *error_num
= NO_RECOVERY
;
358 hp
= __mapv4tov6(buf4
->result
,
359 ((buf6
!= NULL
) ? buf6
->result
: NULL
),
362 order_haddrlist_af(AF_INET6
, hp
->h_addr_list
);
364 *error_num
= NO_RECOVERY
;
368 if (buf4
== NULL
&& buf6
!= NULL
) {
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
386 if (hp
!= NULL
&& (ipnode_bits
& IPNODE_UNMAP
)) {
388 * Just set hp to a new value, cleanup: will
391 hp
= __mappedtov4(hp
, error_num
);
392 } else if (hp
== NULL
)
393 *error_num
= NO_ADDRESS
;
400 /* We could have results in buf6 or buf4, not both */
403 * Extract the IPv4-mapped addresses from buf6
406 hp
= __mappedtov4(buf6
->result
, error_num
);
408 /* We have what we need in buf4. */
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
;
436 * Free the memory we allocated, but make sure we don't free
437 * the memory we're returning to the caller.
440 if (buf6
->result
== hp
)
442 __IPv6_cleanup(buf6
);
445 if (buf4
->result
== hp
)
447 __IPv6_cleanup(buf4
);
449 (void) freenetconfigent(nconf
);
455 * This is the IPv6 interface for "gethostbyaddr".
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
;
471 if (type
== AF_INET6
) {
472 if ((addr6
= (struct in6_addr
*)src
) == NULL
) {
473 *error_num
= HOST_NOT_FOUND
;
476 } else if (type
== AF_INET
) {
477 if ((addr4
= (struct in_addr
*)src
) == NULL
) {
478 *error_num
= HOST_NOT_FOUND
;
482 *error_num
= HOST_NOT_FOUND
;
486 * Specific case: query for "::"
488 if (type
== AF_INET6
&& IN6_IS_ADDR_UNSPECIFIED(addr6
)) {
489 *error_num
= HOST_NOT_FOUND
;
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
;
502 if ((nconf
= __rpc_getconfip("udp")) == NULL
&&
503 (nconf
= __rpc_getconfip("tcp")) == NULL
) {
504 *error_num
= NO_RECOVERY
;
508 nssin
.op_t
= NSS_HOST6
;
509 if (IN6_IS_ADDR_V4COMPAT(addr6
)) {
510 (void) memcpy(tmpbuf
, addr6
, sizeof (*addr6
));
513 nssin
.arg
.nss
.host
.addr
= (const char *)tmpbuf
;
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
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
)) {
541 /* Found one, now format it into mapped/compat addr */
542 if ((res
= __IPv6_alloc(NSS_BUFLEN_IPNODES
)) == 0) {
544 *error_num
= NO_RECOVERY
;
547 /* Convert IPv4 to mapped/compat address w/name */
549 (void) __mapv4tov6(buf
->result
, 0, res
,
550 IN6_IS_ADDR_V4MAPPED(addr6
));
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.
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;
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
577 if (type
== AF_INET
) {
578 struct in6_addr 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
;
587 if ((buf
= __IPv6_alloc(NSS_BUFLEN_IPNODES
)) == 0) {
588 *error_num
= NO_RECOVERY
;
589 freenetconfigent(nconf
);
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
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 */
613 if (!gethostbyaddr_r(src
, len
, type
, buf
->result
,
614 buf
->buffer
, buf
->buflen
, error_num
)) {
621 if ((hp
= __mappedtov4(buf
->result
, error_num
)) == NULL
) {
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
;
637 if ((buf
= __IPv6_alloc(NSS_BUFLEN_IPNODES
)) == 0) {
638 *error_num
= NO_RECOVERY
;
639 freenetconfigent(nconf
);
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
657 _get_hostserv_inetnetdir_byaddr(nconf
, &nssin
, &nssout
);
659 (void) freenetconfigent(nconf
);
660 if (neterr
!= ND_OK
) {
665 return (nssout
.nss
.host
.hent
);
668 * If we got here, unknown type.
670 *error_num
= HOST_NOT_FOUND
;
675 freehostent(struct hostent
*hent
)
681 __ai_addrconfig(int af
, boolean_t loopback
)
685 struct lifreq
*lifp
, *buf
= NULL
;
688 static hrtime_t then4
, then6
; /* the last time we updated ifnum# */
689 static int ifnum4
= -1, ifnum6
= -1, iflb4
= 0, iflb6
= 0;
691 int nlifr
, count
= 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.
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.
728 if (nss_ioctl(af
, SIOCGLIFNUM
, &lifn
) < 0)
731 if (lifn
.lifn_count
== 0) {
739 * Pad the interface count to detect when additional
740 * interfaces have been configured between SIOCGLIFNUM
743 lifn
.lifn_count
+= 4;
745 bufsize
= lifn
.lifn_count
* sizeof (struct lifreq
);
746 if ((buf
= realloc(buf
, bufsize
)) == NULL
)
749 lifc
.lifc_family
= af
;
751 lifc
.lifc_len
= bufsize
;
752 lifc
.lifc_buf
= (caddr_t
)buf
;
753 if (nss_ioctl(af
, SIOCGLIFCONF
, &lifc
) < 0)
756 nlifr
= lifc
.lifc_len
/ sizeof (struct lifreq
);
757 if (nlifr
>= lifn
.lifn_count
)
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
++) {
769 struct sockaddr_in
*in
;
771 in
= (struct sockaddr_in
*)&lifp
->lifr_addr
;
772 if (ntohl(in
->sin_addr
.s_addr
) ==
779 struct sockaddr_in6
*in6
;
781 in6
= (struct sockaddr_in6
*)&lifp
->lifr_addr
;
782 if (IN6_IS_ADDR_LOOPBACK(&in6
->sin6_addr
))
788 *num
= nlifr
- count
;
793 if (loopback
== B_TRUE
)
800 * If the process is running without the NET_ACCESS basic privilege,
801 * pretend we still have inet/inet6 interfaces.
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.
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
,
833 char *buffer
, *limit
;
834 int buflen
= res
->buflen
;
835 struct in6_addr
*addr6p
;
837 struct hostent
*host
;
838 int count
= 0, len
, i
;
841 if (he4
== NULL
|| res
== 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
) {
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
);
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.
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
));
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
));
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;
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.
896 (void) inet_ntop(AF_INET6
, host
->h_addr_list
[0], tmpstr
,
898 buff_locp
-= (len
= strlen(tmpstr
) + 1);
900 if (buff_locp
<= (char *)(host
->h_aliases
))
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
))
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);
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.
931 host
->h_aliases
[count
] = buff_locp
;
932 bcopy((char *)he4
->h_aliases
[i
], buff_locp
, len
);
935 host
->h_aliases
[count
] = NULL
;
936 host
->h_length
= sizeof (struct in6_addr
);
937 host
->h_addrtype
= AF_INET6
;
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
);
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.
956 host
->h_addr_list
[count
] = buff_locp
;
957 bcopy((char *)he6
->h_addr_list
[i
], buff_locp
,
958 sizeof (struct in6_addr
));
962 * Pass 1 (IPv4 mapped addresses):
964 for (i
= 0; he4
->h_addr_list
[i
] != NULL
; i
++) {
965 buff_locp
-= sizeof (struct in6_addr
);
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.
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
));
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;
993 buff_locp
-= (len
= strlen(he6
->h_name
) + 1);
994 if (buff_locp
<= (char *)(host
->h_aliases
))
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);
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.
1008 host
->h_aliases
[count
] = buff_locp
;
1009 bcopy((char *)he6
->h_aliases
[i
], buff_locp
, len
);
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);
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.
1025 host
->h_aliases
[count
] = buff_locp
;
1026 bcopy((char *)he4
->h_aliases
[i
], buff_locp
, len
);
1029 host
->h_aliases
[count
] = NULL
;
1030 host
->h_length
= sizeof (struct in6_addr
);
1031 host
->h_addrtype
= AF_INET6
;
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!
1049 * he pointer to hostent with mapped v4 addresses
1050 * filter_error pointer to return error code
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|
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
;
1071 struct hostent
*host
;
1072 int count
= 0, len
, i
;
1076 *extract_error
= NO_ADDRESS
;
1079 if ((__find_mapped(he
, 0)) == 0) {
1080 *extract_error
= NO_ADDRESS
;
1083 if ((res
= __IPv6_alloc(NSS_BUFLEN_HOSTS
)) == 0) {
1084 *extract_error
= NO_RECOVERY
;
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
)
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
]))
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.
1114 (char *)&(host
->h_addr_list
[count
+ 1]))
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
);
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;
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
))
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.
1150 (char *)&(host
->h_aliases
[count
+ 1]))
1152 host
->h_aliases
[count
] = buff_locp
;
1153 bcopy((char *)he
->h_aliases
[i
], buff_locp
, len
);
1157 host
->h_aliases
[count
] = NULL
;
1158 host
->h_length
= sizeof (struct in_addr
);
1159 host
->h_addrtype
= AF_INET
;
1163 *extract_error
= NO_RECOVERY
;
1164 (void) __IPv6_cleanup(res
);
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
;
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
;
1201 /* We filtered everything out. */
1204 /* NULL terminate the list and return the hostent */
1205 in6addrlist
[i
] = NULL
;
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
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.
1223 __find_mapped(struct hostent
*he
, int find_both
)
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
])) {
1240 /* save some iterations once both found */
1241 if (mapd_found
&& v6_found
)
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
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
)
1272 if ((bufp
->result
= malloc(ALIGN(sizeof (struct hostent
)) + bufsz
)) ==
1277 bufp
->buffer
= (char *)(bufp
->result
) + sizeof (struct hostent
);
1278 bufp
->buflen
= bufsz
;
1283 * This routine is use only for error return cleanup. This will free the
1284 * hostent pointer, so don't use for successful returns.
1287 __IPv6_cleanup(nss_XbyY_buf_t
*bufp
)