xref: /illumos-gate/usr/src/lib/libnsl/nss/getipnodeby.c (revision 41e0a469c3dbc14deb2b200f6ca6f6e00b5865d0)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright 2016 Joyent, Inc.
27  *
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).
31  *
32  * lib/libnsl/nss/getipnodeby.c
33  */
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 <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"
58 
59 #define	IPV6_LITERAL_CHAR	':'
60 
61 /*
62  * The number of nanoseconds getipnodebyname() waits before getting
63  * fresh interface count information with SIOCGLIFNUM.  The default is
64  * five minutes.
65  */
66 #define	IFNUM_TIMEOUT	((hrtime_t)300 * NANOSEC)
67 
68 /*
69  * Bits in the bitfield returned by getipnodebyname_processflags().
70  *
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.
80  */
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)
89 
90 /*
91  * The private flag between libsocket and libnsl. See
92  * lib/libsocket/inet/getaddrinfo.c for more information.
93  */
94 #define	AI_ADDRINFO	0x8000
95 
96 /*
97  * The default set of bits corresponding to a getipnodebyname() flags
98  * argument of AI_DEFAULT.
99  */
100 #define	IPNODE_DEFAULT (IPNODE_WANTIPV6 | IPNODE_IPV4 | \
101 	IPNODE_LOOKUPIPNODES | IPNODE_LOOKUPHOSTS)
102 
103 extern struct netconfig *__rpc_getconfip(char *);
104 
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);
113 
114 
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)
119 {
120 	return (_switch_getipnodebyname_r(nam, result, buffer, buflen,
121 	    af_family, flags, h_errnop));
122 }
123 
124 struct hostent *
125 _uncached_getipnodebyaddr(const char *addr, int length, int type,
126 	struct hostent *result, char *buffer, int buflen, int *h_errnop)
127 {
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);
135 }
136 #endif
137 
138 /*
139  * Given a name, an address family, and a set of flags, return a
140  * bitfield that getipnodebyname() will use.
141  */
142 static uint_t
143 getipnodebyname_processflags(const char *name, int af, int flags)
144 {
145 	uint_t		ipnode_bits = IPNODE_DEFAULT;
146 	boolean_t	ipv6configured = B_FALSE;
147 	boolean_t	ipv4configured = B_FALSE;
148 
149 	/*
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.
153 	 *
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.
164 	 */
165 	if (flags & AI_ADDRCONFIG) {
166 		boolean_t hv4, hv6;
167 
168 		hv4 = __ai_addrconfig(AF_INET, B_FALSE) > 0;
169 		hv6 = __ai_addrconfig(AF_INET6, B_FALSE) > 0;
170 
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;
174 		}
175 
176 		ipv6configured = (af == AF_INET6 && hv6);
177 		ipv4configured = (af == AF_INET || (flags & AI_V4MAPPED)) &&
178 		    hv4;
179 	}
180 
181 	/*
182 	 * Determine what kinds of addresses the user is interested
183 	 * in getting back.
184 	 */
185 	switch (af) {
186 	case AF_INET6:
187 		if ((flags & AI_ADDRCONFIG) && !ipv6configured)
188 			ipnode_bits &= ~IPNODE_WANTIPV6;
189 
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;
195 			}
196 			if ((flags & AI_ADDRCONFIG) && !ipv6configured &&
197 			    (flags & AI_ADDRINFO)) {
198 				ipnode_bits |= IPNODE_UNMAP;
199 			}
200 		} else {
201 			ipnode_bits &= ~IPNODE_IPV4;
202 		}
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;
213 	}
214 
215 	/*
216 	 * If we're not looking for IPv4 addresses, don't bother looking
217 	 * in hosts.
218 	 */
219 	if (!(ipnode_bits & IPNODE_WANTIPV4))
220 		ipnode_bits &= ~IPNODE_LOOKUPHOSTS;
221 
222 	/*
223 	 * Determine if name is a literal IP address.  This will
224 	 * further narrow down what type of lookup we're going to do.
225 	 */
226 	if (strchr(name, IPV6_LITERAL_CHAR) != NULL) {
227 		/* Literal IPv6 address */
228 		ipnode_bits |= IPNODE_LITERAL;
229 		/*
230 		 * In s9 we accepted the literal without filtering independent
231 		 * of what family was passed in hints.  We continue to do
232 		 * this.
233 		 */
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;
241 	}
242 	return (ipnode_bits);
243 }
244 
245 struct hostent *
246 getipnodebyname(const char *name, int af, int flags, int *error_num)
247 {
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;
256 
257 	if ((nconf = __rpc_getconfip("udp")) == NULL &&
258 	    (nconf = __rpc_getconfip("tcp")) == NULL) {
259 		*error_num = NO_RECOVERY;
260 		return (NULL);
261 	}
262 
263 	ipnode_bits = getipnodebyname_processflags(name, af, flags);
264 
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;
269 	}
270 
271 	/*
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.
276 	 */
277 	if (ipnode_bits & IPNODE_LOOKUPIPNODES) {
278 		if ((buf6 = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == NULL) {
279 			*error_num = NO_RECOVERY;
280 			goto cleanup;
281 		}
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) {
295 			/*
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.
302 			 */
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)) {
307 				/*
308 				 * If all we're looking for are IPv4
309 				 * addresses and there are none in
310 				 * buf6 then buf6 is now useless.
311 				 */
312 				__IPv6_cleanup(buf6);
313 				buf6 = NULL;
314 			}
315 		}
316 	}
317 	if (ipnode_bits & IPNODE_LOOKUPHOSTS) {
318 		if ((buf4 = __IPv6_alloc(NSS_BUFLEN_HOSTS)) == NULL) {
319 			*error_num = NO_RECOVERY;
320 			goto cleanup;
321 		}
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;
332 		}
333 	}
334 
335 	if (buf6 == NULL && buf4 == NULL) {
336 		*error_num = HOST_NOT_FOUND;
337 		goto cleanup;
338 	}
339 
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;
345 
346 			/*
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.
352 			 */
353 			mergebuf = __IPv6_alloc(NSS_BUFLEN_IPNODES);
354 			if (mergebuf == NULL) {
355 				*error_num = NO_RECOVERY;
356 				goto cleanup;
357 			}
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);
366 		}
367 
368 		if (buf4 == NULL && buf6 != NULL) {
369 			hp = buf6->result;
370 
371 			/*
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.
375 			 */
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);
380 
381 			/*
382 			 * We've been asked to unmap v4 addresses. This
383 			 * situation implies IPNODE_WANTIPV4 and
384 			 * !IPNODE_WANTIPV6.
385 			 */
386 			if (hp != NULL && (ipnode_bits & IPNODE_UNMAP)) {
387 				/*
388 				 * Just set hp to a new value, cleanup: will
389 				 * free the old one
390 				 */
391 				hp = __mappedtov4(hp, error_num);
392 			} else if (hp == NULL)
393 				*error_num = NO_ADDRESS;
394 		}
395 
396 		break;
397 	}
398 
399 	case AF_INET:
400 		/* We could have results in buf6 or buf4, not both */
401 		if (buf6 != NULL) {
402 			/*
403 			 * Extract the IPv4-mapped addresses from buf6
404 			 * into hp.
405 			 */
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) {
411 				/*
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.
423 				 */
424 				hp->h_aliases = NULL;
425 			}
426 		}
427 
428 		break;
429 
430 	default:
431 		break;
432 	}
433 
434 cleanup:
435 	/*
436 	 * Free the memory we allocated, but make sure we don't free
437 	 * the memory we're returning to the caller.
438 	 */
439 	if (buf6 != NULL) {
440 		if (buf6->result == hp)
441 			buf6->result = NULL;
442 		__IPv6_cleanup(buf6);
443 	}
444 	if (buf4 != NULL) {
445 		if (buf4->result == hp)
446 			buf4->result = NULL;
447 		__IPv6_cleanup(buf4);
448 	}
449 	(void) freenetconfigent(nconf);
450 
451 	return (hp);
452 }
453 
454 /*
455  * This is the IPv6 interface for "gethostbyaddr".
456  */
457 struct hostent *
458 getipnodebyaddr(const void *src, size_t len, int type, int *error_num)
459 {
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];
470 
471 	if (type == AF_INET6) {
472 		if ((addr6 = (struct in6_addr *)src) == NULL) {
473 			*error_num = HOST_NOT_FOUND;
474 			return (NULL);
475 		}
476 	} else if (type == AF_INET) {
477 		if ((addr4 = (struct in_addr *)src) == NULL) {
478 			*error_num = HOST_NOT_FOUND;
479 			return (NULL);
480 		}
481 	} else {
482 		*error_num = HOST_NOT_FOUND;
483 		return (NULL);
484 	}
485 	/*
486 	 * Specific case: query for "::"
487 	 */
488 	if (type == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(addr6)) {
489 		*error_num = HOST_NOT_FOUND;
490 		return (NULL);
491 	}
492 	/*
493 	 * Step 1: IPv4-mapped address  or IPv4 Compat
494 	 */
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);
501 		}
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);
507 		}
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;
516 		}
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;
521 
522 		nssout.nss.host.hent = buf->result;
523 		nssout.nss.host.herrno_p = error_num;
524 		/*
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.
528 		 */
529 		neterr =
530 		    _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout);
531 
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);
540 			}
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);
546 			}
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);
554 		}
555 		/*
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.
559 		 */
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;
566 		}
567 		free(buf);
568 		return (hp);
569 	}
570 	/*
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(8) caches v4 from ipnodes as mapped v6's. The
574 	 * switch backend knows to lookup v4's (not v4mapped) from the
575 	 * name services.
576 	 */
577 	if (type == AF_INET) {
578 		struct in6_addr v4mapbuf;
579 		addr6 = &v4mapbuf;
580 
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);
586 		}
587 		if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) {
588 			*error_num = NO_RECOVERY;
589 			freenetconfigent(nconf);
590 			return (NULL);
591 		}
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;
598 
599 		nssout.nss.host.hent = buf->result;
600 		nssout.nss.host.herrno_p = error_num;
601 		/*
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.
605 		 */
606 		neterr =
607 		    _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout);
608 
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);
617 			}
618 			free(buf);
619 			return (hp);
620 		}
621 		if ((hp = __mappedtov4(buf->result, error_num)) == NULL) {
622 			__IPv6_cleanup(buf);
623 			return (NULL);
624 		}
625 		__IPv6_cleanup(buf);
626 		return (hp);
627 	}
628 	/*
629 	 * Step 3: AF_INET6, plain vanilla v6 getipnodebyaddr() call.
630 	 */
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);
636 		}
637 		if ((buf = __IPv6_alloc(NSS_BUFLEN_IPNODES)) == 0) {
638 			*error_num = NO_RECOVERY;
639 			freenetconfigent(nconf);
640 			return (NULL);
641 		}
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;
648 
649 		nssout.nss.host.hent = buf->result;
650 		nssout.nss.host.herrno_p = error_num;
651 		/*
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.
655 		 */
656 		neterr =
657 		    _get_hostserv_inetnetdir_byaddr(nconf, &nssin, &nssout);
658 
659 		(void) freenetconfigent(nconf);
660 		if (neterr != ND_OK) {
661 			__IPv6_cleanup(buf);
662 			return (NULL);
663 		}
664 		free(buf);
665 		return (nssout.nss.host.hent);
666 	}
667 	/*
668 	 * If we got here, unknown type.
669 	 */
670 	*error_num = HOST_NOT_FOUND;
671 	return (NULL);
672 }
673 
674 void
675 freehostent(struct hostent *hent)
676 {
677 	free(hent);
678 }
679 
680 static int
681 __ai_addrconfig(int af, boolean_t loopback)
682 {
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;
692 
693 
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);
707 	}
708 
709 	/*
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.
712 	 *
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.
716 	 */
717 	now = gethrtime();
718 	if (*num == -1 || ((now - *then) >= IFNUM_TIMEOUT)) {
719 		lifn.lifn_family = af;
720 		/*
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.
725 		 */
726 		lifn.lifn_flags = 0;
727 again:
728 		if (nss_ioctl(af, SIOCGLIFNUM, &lifn) < 0)
729 			goto fail;
730 
731 		if (lifn.lifn_count == 0) {
732 			*lb = 0;
733 			*num = 0;
734 			*then = now;
735 			return (*num);
736 		}
737 
738 		/*
739 		 * Pad the interface count to detect when additional
740 		 * interfaces have been configured between SIOCGLIFNUM
741 		 * and SIOCGLIFCONF.
742 		 */
743 		lifn.lifn_count += 4;
744 
745 		bufsize = lifn.lifn_count * sizeof (struct lifreq);
746 		if ((buf = realloc(buf, bufsize)) == NULL)
747 			goto fail;
748 
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;
755 
756 		nlifr = lifc.lifc_len / sizeof (struct lifreq);
757 		if (nlifr >= lifn.lifn_count)
758 			goto again;
759 		/*
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.
765 		 */
766 		for (lifp = buf; lifp < buf + nlifr; lifp++) {
767 			switch (af) {
768 			case AF_INET: {
769 				struct sockaddr_in *in;
770 
771 				in = (struct sockaddr_in *)&lifp->lifr_addr;
772 				if (ntohl(in->sin_addr.s_addr) ==
773 				    INADDR_LOOPBACK) {
774 					count++;
775 				}
776 				break;
777 			}
778 			case AF_INET6: {
779 				struct sockaddr_in6 *in6;
780 
781 				in6 = (struct sockaddr_in6 *)&lifp->lifr_addr;
782 				if (IN6_IS_ADDR_LOOPBACK(&in6->sin6_addr))
783 					count++;
784 				break;
785 			}
786 			}
787 		}
788 		*num = nlifr - count;
789 		*lb = count;
790 		*then = now;
791 		free(buf);
792 	}
793 	if (loopback == B_TRUE)
794 		return (*num + *lb);
795 	else
796 		return (*num);
797 fail:
798 	free(buf);
799 	/*
800 	 * If the process is running without the NET_ACCESS basic privilege,
801 	 * pretend we still have inet/inet6 interfaces.
802 	 */
803 	if (errno == EACCES)
804 		return (1);
805 	return (-1);
806 }
807 
808 /*
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
819  *
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|
828  */
829 static struct hostent *
830 __mapv4tov6(struct hostent *he4, struct hostent *he6, nss_XbyY_buf_t *res,
831 		int mapped)
832 {
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;
840 
841 	if (he4 == NULL || res == NULL) {
842 		return (NULL);
843 	}
844 	limit = res->buffer + buflen;
845 	host = (struct hostent *)res->result;
846 	buffer = res->buffer;
847 
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);
853 	}
854 	if (he6 == NULL) {
855 		/*
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.
861 		 */
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])) {
866 			/*
867 			 * Has to be room for the pointer to the address we're
868 			 * about to add, as well as the final NULL ptr.
869 			 */
870 				return (NULL);
871 			}
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;
879 			}
880 			bcopy((char *)he4->h_addr_list[i],
881 			    &addr6p->s6_addr[12], sizeof (struct in_addr));
882 			++count;
883 		}
884 		/*
885 		 * Set last array element to NULL and add cname as first alias
886 		 */
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) {
891 		/*
892 		 * Literal address string, since we're mapping, we need the IPv6
893 		 * V4 mapped literal address string for h_name.
894 		 */
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 */
908 		}
909 		/*
910 		 * Not a literal address string, so just copy h_name.
911 		 */
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;
918 		/*
919 		 * Pass 2 (IPv4 aliases):
920 		 */
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])) {
925 			/*
926 			 * Has to be room for the pointer to the address we're
927 			 * about to add, as well as the final NULL ptr.
928 			 */
929 				return (NULL);
930 			}
931 			host->h_aliases[count] = buff_locp;
932 			bcopy((char *)he4->h_aliases[i], buff_locp, len);
933 			++count;
934 		}
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 {
940 		/*
941 		 * Merge IPv4 mapped addresses with IPv6 addresses. The
942 		 * IPv6 address will go in first, followed by the v4 mapped.
943 		 *
944 		 * Pass 1 (IPv6 addresses):
945 		 */
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])) {
950 			/*
951 			 * Has to be room for the pointer to the address we're
952 			 * about to add, as well as the final NULL ptr.
953 			 */
954 				return (NULL);
955 			}
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;
960 		}
961 		/*
962 		 * Pass 1 (IPv4 mapped addresses):
963 		 */
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])) {
968 			/*
969 			 * Has to be room for the pointer to the address we're
970 			 * about to add, as well as the final NULL ptr.
971 			 */
972 				return (NULL);
973 			}
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;
983 		}
984 		/*
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.
989 		 */
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])) {
1002 			/*
1003 			 * Has to be room for the pointer to the address we're
1004 			 * about to add, as well as the final NULL ptr.
1005 			 */
1006 				return (NULL);
1007 			}
1008 			host->h_aliases[count] = buff_locp;
1009 			bcopy((char *)he6->h_aliases[i], buff_locp, len);
1010 			++count;
1011 		}
1012 		/*
1013 		 * Pass 2 (IPv4 aliases):
1014 		 */
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])) {
1019 			/*
1020 			 * Has to be room for the pointer to the address we're
1021 			 * about to add, as well as the final NULL ptr.
1022 			 */
1023 				return (NULL);
1024 			}
1025 			host->h_aliases[count] = buff_locp;
1026 			bcopy((char *)he4->h_aliases[i], buff_locp, len);
1027 			++count;
1028 		}
1029 		host->h_aliases[count] = NULL;
1030 		host->h_length = sizeof (struct in6_addr);
1031 		host->h_addrtype = AF_INET6;
1032 		return (host);
1033 	}
1034 }
1035 
1036 /*
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:
1044  *
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!
1047  *
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.
1053  *
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|
1062  */
1063 struct hostent *
1064 __mappedtov4(struct hostent *he, int *extract_error)
1065 {
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;
1074 
1075 	if (he == NULL) {
1076 		*extract_error = NO_ADDRESS;
1077 		return (NULL);
1078 	}
1079 	if ((__find_mapped(he, 0)) == 0) {
1080 		*extract_error = NO_ADDRESS;
1081 		return (NULL);
1082 	}
1083 	if ((res = __IPv6_alloc(NSS_BUFLEN_HOSTS)) == 0) {
1084 		*extract_error = NO_RECOVERY;
1085 		return (NULL);
1086 	}
1087 	limit = res->buffer + buflen;
1088 	host = (struct hostent *)res->result;
1089 	buffer = res->buffer;
1090 
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;
1096 	/*
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.
1102 	 */
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);
1109 		/*
1110 		 * Has to be room for the pointer to the address we're
1111 		 * about to add, as well as the final NULL ptr.
1112 		 */
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;
1125 	}
1126 	/*
1127 	 * Set last array element to NULL and add cname as first alias
1128 	 */
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;
1139 	/*
1140 	 * Pass 2 (IPv4 aliases):
1141 	 */
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);
1145 			/*
1146 			 * Has to be room for the pointer to the address we're
1147 			 * about to add, as well as the final NULL ptr.
1148 			 */
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;
1155 		}
1156 	}
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);
1166 }
1167 
1168 /*
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.
1177  */
1178 static struct hostent *
1179 __filter_addresses(int af, struct hostent *he)
1180 {
1181 	struct in6_addr	**in6addrlist, **in6addr;
1182 	boolean_t	isipv4mapped;
1183 	int		i = 0;
1184 
1185 	if (he == NULL)
1186 		return (NULL);
1187 
1188 	in6addrlist = (struct in6_addr **)he->h_addr_list;
1189 	for (in6addr = in6addrlist; *in6addr != NULL; in6addr++) {
1190 		isipv4mapped = IN6_IS_ADDR_V4MAPPED(*in6addr);
1191 
1192 		if ((af == AF_INET && !isipv4mapped) ||
1193 		    (af == AF_INET6 && isipv4mapped)) {
1194 			if (in6addrlist[i] != *in6addr)
1195 				in6addrlist[i] = *in6addr;
1196 			i++;
1197 		}
1198 	}
1199 
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);
1207 	}
1208 }
1209 
1210 /*
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
1218  *
1219  * If hostent passed in with no addresses, zero will be returned.
1220  */
1221 
1222 static int
1223 __find_mapped(struct hostent *he, int find_both)
1224 {
1225 	int i;
1226 	int mapd_found = 0;
1227 	int v6_found = 0;
1228 
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;
1239 		}
1240 		/* save some iterations once both found */
1241 		if (mapd_found && v6_found)
1242 			return (2);
1243 	}
1244 	return (mapd_found);
1245 }
1246 
1247 /*
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.
1252  *
1253  *   bufp->result    bufp->buffer
1254  *		|		|
1255  *		V		V
1256  *		------------------------------------------------...--
1257  *		|struct hostent	|addresses		     aliases |
1258  *		------------------------------------------------...--
1259  *		|               |<--------bufp->buflen-------------->|
1260  */
1261 
1262 #define	ALIGN(x) ((((long)(x)) + sizeof (long) - 1) & ~(sizeof (long) - 1))
1263 
1264 static nss_XbyY_buf_t *
1265 __IPv6_alloc(int bufsz)
1266 {
1267 	nss_XbyY_buf_t *bufp;
1268 
1269 	if ((bufp = malloc(sizeof (nss_XbyY_buf_t))) == NULL)
1270 		return (NULL);
1271 
1272 	if ((bufp->result = malloc(ALIGN(sizeof (struct hostent)) + bufsz)) ==
1273 	    NULL) {
1274 		free(bufp);
1275 		return (NULL);
1276 	}
1277 	bufp->buffer = (char *)(bufp->result) + sizeof (struct hostent);
1278 	bufp->buflen = bufsz;
1279 	return (bufp);
1280 }
1281 
1282 /*
1283  * This routine is use only for error return cleanup. This will free the
1284  * hostent pointer, so don't use for successful returns.
1285  */
1286 static void
1287 __IPv6_cleanup(nss_XbyY_buf_t *bufp)
1288 {
1289 	if (bufp == NULL)
1290 		return;
1291 	if (bufp->result != NULL)
1292 		free(bufp->result);
1293 	free(bufp);
1294 }
1295