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