xref: /titanic_44/usr/src/lib/libsocket/inet/getaddrinfo.c (revision b6c3f7863936abeae522e48a13887dddeb691a45)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 
30 #include <netdb.h>
31 #include <arpa/inet.h>
32 #include <nss_dbdefs.h>
33 #include <netinet/in.h>
34 #include <sys/socket.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <stdio.h>
38 #include <ctype.h>
39 #include <sys/types.h>
40 #include <stdlib.h>
41 #include <net/if.h>
42 
43 extern char *_dgettext(const char *, const char *);
44 
45 #define	ai2sin(x)	((struct sockaddr_in *)((x)->ai_addr))
46 #define	ai2sin6(x)	((struct sockaddr_in6 *)((x)->ai_addr))
47 
48 #define	HOST_BROADCAST	"255.255.255.255"
49 
50 /*
51  * getaddrinfo() returns EAI_NONAME in some cases, however
52  * since EAI_NONAME is not part of SUSv3 it needed to be
53  * masked in the standards compliant environment.
54  * GAIV_DEFAULT and GAIV_XPG6 accomplish this.
55  */
56 #define	GAIV_DEFAULT	0
57 #define	GAIV_XPG6	1
58 
59 /*
60  * Storage allocation for global variables in6addr_any and
61  * in6addr_loopback.  The extern declarations for these
62  * variables are defined in <netinet/in.h>.  These two
63  * variables could have been defined in any of the "C" files
64  * in libsocket. They are defined here with other IPv6
65  * related interfaces.
66  */
67 const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
68 const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
69 
70 /* AI_MASK:  all valid flags for addrinfo */
71 #define	AI_MASK		(AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST \
72 	| AI_ADDRCONFIG | AI_NUMERICSERV | AI_V4MAPPED | AI_ALL)
73 #define	ANY		0
74 /* function prototypes for used by getaddrinfo() routine */
75 static int get_addr(int family, const char *hostname, struct addrinfo *aip,
76 	struct addrinfo *cur, ushort_t port, int version);
77 static uint_t getscopeidfromzone(const struct sockaddr_in6 *sa,
78     const char *zone, uint32_t *sin6_scope_id);
79 static boolean_t str_isnumber(const char *p);
80 
81 /*
82  * getaddrinfo:
83  *
84  * Purpose:
85  *   Routine for performing Address-to-nodename in a
86  *   protocol-independent fashion.
87  * Description and history of the routine:
88  *   Nodename-to-address translation is done in a protocol-
89  *   independent fashion using the getaddrinfo() function
90  *   that is taken from the IEEE POSIX 1003.1g.
91  *
92  *   The official specification for this function will be the
93  *   final POSIX standard, with the following additional
94  *   requirements:
95  *
96  *   - getaddrinfo() must be thread safe
97  *   - The AI_NUMERICHOST is new.
98  *   - All fields in socket address structures returned by
99  *
100  *  getaddrinfo() that are not filled in through an explicit
101  *  argument (e.g., sin6_flowinfo and sin_zero) must be set to 0.
102  *  (This makes it easier to compare socket address structures).
103  *
104  * Input Parameters:
105  *  nodename  - pointer to null-terminated strings that represents
106  *              a hostname or literal ip address (IPv4/IPv6) or this
107  *              pointer can be NULL.
108  *  servname  - pointer to null-terminated strings that represents
109  *              a servicename or literal port number or this
110  *              pointer can be NULL.
111  *  hints     - optional argument that points to an addrinfo structure
112  *              to provide hints on the type of socket that the caller
113  *              supports.
114  *   Possible setting of the ai_flags member of the hints structure:
115  *   AI_PASSIVE -     If set, the caller plans to use the returned socket
116  *                    address in a call to bind().  In this case, it the
117  *                    nodename argument is NULL, then the IP address portion
118  *                    of the socket address structure will be set to
119  *                    INADDR_ANY for IPv4 or IN6ADDR_ANY_INIT for IPv6.
120  *   AI_PASSIVE -     If not set, then the returned socket address will be
121  *                    ready for a call to connect() (for conn-oriented) or
122  *                    connect(), sendto(), or sendmsg() (for connectionless).
123  *                    In this case, if nodename is NULL, then the IP address
124  *                    portion of the socket address structure will be set to
125  *                    the loopback address.
126  *   AI_CANONNAME -   If set, then upon successful return the ai_canonname
127  *                    field of the first addrinfo structure in the linked
128  *                    list will point to a NULL-terminated string
129  *                    containing the canonical name of the specified nodename.
130  *   AI_NUMERICHOST - If set, then a non-NULL nodename string must be a numeric
131  *                    host address string.  Otherwise an error of EAI_NONAME
132  *                    is returned.  This flag prevents any type of name
133  *                    resolution service from being called.
134  *   AI_NUMERICSERV - If set, then a non-null servname string supplied shall
135  *                    be a numeric port string. Otherwise, an [EAI_NONAME]
136  *                    error shall be returned. This flag shall prevent any
137  *                    type of name resolution service from being invoked.
138  *   AI_V4MAPPED -    If set, along with an ai_family of AF_INET6, then
139  *                    getaddrinfo() shall return IPv4-mapped IPv6 addresses
140  *                    on finding no matching IPv6 addresses ( ai_addrlen shall
141  *                    be 16). The AI_V4MAPPED flag shall be ignored unless
142  *                    ai_family equals AF_INET6.
143  *   AI_ALL -	      If the AI_ALL flag is used with the AI_V4MAPPED flag,
144  *		      then getaddrinfo() shall return all matching IPv6 and
145  *		      IPv4 addresses. The AI_ALL flag without the AI_V4MAPPED
146  *		      flag is ignored.
147  * Output Parameters:
148  *  res       - upon successful return a pointer to a linked list of one
149  *              or more addrinfo structures is returned through this
150  *              argument.  The caller can process each addrinfo structures
151  *              in this list by following the ai_next pointer, until a
152  *              NULL pointer is encountered.  In each returned addrinfo
153  *              structure the three members ai_family, ai_socktype, and
154  *              ai_protocol are corresponding arguments for a call to the
155  *              socket() function.  In each addrinfo structure the ai_addr
156  *              field points to filled-in socket address structure whose
157  *              length is specified by the ai_addrlen member.
158  *
159  * Return Value:
160  *  This function returns 0 upon success or a nonzero error code.  The
161  *  following names are nonzero error codes from getaddrinfo(), and are
162  *  defined in <netdb.h>.
163  *  EAI_ADDRFAMILY - address family not supported
164  *  EAI_AGAIN      - DNS temporary failure
165  *  EAI_BADFLAGS   - invalid ai_flags
166  *  EAI_FAIL       - DNS non-recoverable failure
167  *  EAI_FAMILY     - ai_family not supported
168  *  EAI_MEMORY     - memory allocation failure
169  *  EAI_NODATA     - no address associated with nodename
170  *  EAI_NONAME     - host/servname not known
171  *  EAI_SERVICE    - servname not supported for ai_socktype
172  *  EAI_SOCKTYPE   - ai_socktype not supported
173  *  EAI_SYSTEM     - system error in errno
174  *
175  * Memory Allocation:
176  *  All of the information returned by getaddrinfo() is dynamically
177  *  allocated:  the addrinfo structures, and the socket address
178  *  structures and canonical node name strings pointed to by the
179  *  addrinfo structures.
180  */
181 
182 
183 static int
184 _getaddrinfo(const char *hostname, const char *servname,
185 	const struct addrinfo *hints, struct addrinfo **res, int version)
186 {
187 	struct addrinfo *cur;
188 	struct addrinfo *aip;
189 	struct addrinfo ai;
190 	int		error;
191 	ushort_t	port;
192 
193 	cur = &ai;
194 	aip = &ai;
195 
196 	aip->ai_flags = 0;
197 	aip->ai_family = PF_UNSPEC;
198 	aip->ai_socktype = 0;
199 	aip->ai_protocol = 0;
200 #ifdef __sparcv9
201 	/*
202 	 * We need to clear _ai_pad to preserve binary
203 	 * compatibility with previously compiled 64-bit
204 	 * applications by guaranteeing the upper 32-bits
205 	 * are empty.
206 	 */
207 	aip->_ai_pad = 0;
208 #endif /* __sparcv9 */
209 	aip->ai_addrlen = 0;
210 	aip->ai_canonname = NULL;
211 	aip->ai_addr = NULL;
212 	aip->ai_next = NULL;
213 	port = 0;
214 
215 	/* if nodename nor servname provided */
216 	if (hostname == NULL && servname == NULL) {
217 		*res = NULL;
218 		return (EAI_NONAME);
219 	}
220 	if (hints != NULL) {
221 		/* check for bad flags in hints */
222 		if ((hints->ai_flags != 0) && (hints->ai_flags & ~AI_MASK)) {
223 			*res = NULL;
224 			return (EAI_BADFLAGS);
225 		}
226 		if ((hostname == NULL || *hostname == '\0') &&
227 		    (hints->ai_flags & AI_CANONNAME)) {
228 				*res = NULL;
229 				return (EAI_BADFLAGS);
230 		}
231 		if (hints->ai_family != PF_UNSPEC &&
232 		    hints->ai_family != PF_INET &&
233 		    hints->ai_family != PF_INET6) {
234 			*res = NULL;
235 			return (EAI_FAMILY);
236 		}
237 
238 		(void) memcpy(aip, hints, sizeof (*aip));
239 #ifdef __sparcv9
240 		/*
241 		 * We need to clear _ai_pad to preserve binary
242 		 * compatibility.  See prior comment.
243 		 */
244 		aip->_ai_pad = 0;
245 #endif /* __sparcv9 */
246 		switch (aip->ai_socktype) {
247 		case ANY:
248 			switch (aip->ai_protocol) {
249 			case ANY:
250 				break;
251 			case IPPROTO_UDP:
252 				aip->ai_socktype = SOCK_DGRAM;
253 				break;
254 			case IPPROTO_TCP:
255 			case IPPROTO_SCTP:
256 				aip->ai_socktype = SOCK_STREAM;
257 				break;
258 			default:
259 				aip->ai_socktype = SOCK_RAW;
260 				break;
261 			}
262 			break;
263 		case SOCK_RAW:
264 			break;
265 		case SOCK_SEQPACKET:
266 			/*
267 			 * If the hint does not have a preference on the
268 			 * protocol, use SCTP as the default for
269 			 * SOCK_SEQPACKET.
270 			 */
271 			if (aip->ai_protocol == ANY)
272 				aip->ai_protocol = IPPROTO_SCTP;
273 			break;
274 		case SOCK_DGRAM:
275 			aip->ai_protocol = IPPROTO_UDP;
276 			break;
277 		case SOCK_STREAM:
278 			/*
279 			 * If the hint does not have a preference on the
280 			 * protocol, use TCP as the default for SOCK_STREAM.
281 			 */
282 			if (aip->ai_protocol == ANY)
283 				aip->ai_protocol = IPPROTO_TCP;
284 			break;
285 		default:
286 			*res = NULL;
287 			return (EAI_SOCKTYPE);
288 		}
289 	}
290 
291 	/*
292 	 *  Get the service.
293 	 */
294 
295 	if (servname != NULL) {
296 		struct servent result;
297 		int bufsize = 128;
298 		char *buf = NULL;
299 		struct servent *sp;
300 		char *proto = NULL;
301 
302 		switch (aip->ai_socktype) {
303 		case ANY:
304 			proto = NULL;
305 			break;
306 		case SOCK_DGRAM:
307 			proto = "udp";
308 			break;
309 		case SOCK_STREAM:
310 			/*
311 			 * If there is no hint given, use TCP as the default
312 			 * protocol.
313 			 */
314 			switch (aip->ai_protocol) {
315 			case ANY:
316 			case IPPROTO_TCP:
317 			default:
318 				proto = "tcp";
319 				break;
320 			case IPPROTO_SCTP:
321 				proto = "sctp";
322 				break;
323 			}
324 			break;
325 		case SOCK_SEQPACKET:
326 			/* Default to SCTP if no hint given. */
327 			switch (aip->ai_protocol) {
328 			case ANY:
329 			default:
330 				proto = "sctp";
331 				break;
332 			}
333 			break;
334 		}
335 		/*
336 		 * Servname string can be a decimal port number.
337 		 * If we already know the socket type there is no need
338 		 * to call getservbyport.
339 		 */
340 		if (aip->ai_flags & AI_NUMERICSERV) {
341 			if (!str_isnumber(servname)) {
342 				return (EAI_NONAME);
343 			}
344 			port = htons(atoi(servname));
345 		} else if (str_isnumber(servname)) {
346 			port = htons(atoi(servname));
347 			if (aip->ai_socktype == ANY) {
348 				do {
349 					if (buf != NULL)
350 						free(buf);
351 					bufsize *= 2;
352 					buf = malloc(bufsize);
353 					if (buf == NULL) {
354 						*res = NULL;
355 						return (EAI_MEMORY);
356 					}
357 
358 					sp = getservbyport_r(port, proto,
359 					    &result, buf, bufsize);
360 					if (sp == NULL && errno != ERANGE) {
361 						free(buf);
362 						*res = NULL;
363 						return (EAI_SERVICE);
364 					}
365 				/*
366 				 * errno == ERANGE so our scratch buffer space
367 				 * wasn't big enough.  Double it and try
368 				 * again.
369 				 */
370 				} while (sp == NULL);
371 			}
372 		} else {
373 			do {
374 				if (buf != NULL)
375 					free(buf);
376 				bufsize *= 2;
377 				buf = malloc(bufsize);
378 				if (buf == NULL) {
379 					*res = NULL;
380 					return (EAI_MEMORY);
381 				}
382 
383 				sp = getservbyname_r(servname, proto, &result,
384 				    buf, bufsize);
385 				if (sp == NULL && errno != ERANGE) {
386 					free(buf);
387 					*res = NULL;
388 					return (EAI_SERVICE);
389 				}
390 			/*
391 			 * errno == ERANGE so our scratch buffer space wasn't
392 			 * big enough.  Double it and try again.
393 			 */
394 			} while (sp == NULL);
395 
396 			port = sp->s_port;
397 		}
398 		if (aip->ai_socktype == ANY) {
399 			if (aip->ai_flags & AI_NUMERICSERV) {
400 				/*
401 				 * RFC 2553bis doesn't allow us to use the
402 				 * any resolver to find out if there is a
403 				 * match.  We could walk the service file
404 				 * with *servent().  Given the commonality of
405 				 * calling getaddrinfo() with a number and
406 				 * ANY protocol we won't add that at this time.
407 				 */
408 				return (EAI_NONAME);
409 			}
410 
411 			if (strcmp(sp->s_proto, "udp") == 0) {
412 				aip->ai_socktype = SOCK_DGRAM;
413 				aip->ai_protocol = IPPROTO_UDP;
414 			} else if (strcmp(sp->s_proto, "tcp") == 0) {
415 				aip->ai_socktype = SOCK_STREAM;
416 				aip->ai_protocol = IPPROTO_TCP;
417 			} else if (strcmp(sp->s_proto, "sctp") == 0) {
418 				aip->ai_socktype = SOCK_STREAM;
419 				aip->ai_protocol = IPPROTO_SCTP;
420 			} else {
421 				if (buf != NULL)
422 					free(buf);
423 
424 				*res = NULL;
425 				errno = EPROTONOSUPPORT;
426 				return (EAI_SYSTEM);
427 			}
428 		}
429 
430 		if (buf != NULL)
431 			free(buf);
432 	}
433 
434 	/*
435 	 * hostname is NULL
436 	 * case 1: AI_PASSIVE bit set : anyaddr 0.0.0.0 or ::
437 	 * case 2: AI_PASSIVE bit not set : localhost 127.0.0.1 or ::1
438 	 */
439 
440 	if (hostname == NULL) {
441 		struct addrinfo *nai;
442 		socklen_t addrlen;
443 		char *canonname;
444 
445 		if (aip->ai_family == PF_INET)
446 			goto v4only;
447 		/* create IPv6 addrinfo */
448 		nai = malloc(sizeof (struct addrinfo));
449 		if (nai == NULL)
450 			goto nomem;
451 		*nai = *aip;
452 		addrlen = sizeof (struct sockaddr_in6);
453 		nai->ai_addr = malloc(addrlen);
454 		if (nai->ai_addr == NULL) {
455 			freeaddrinfo(nai);
456 			goto nomem;
457 		}
458 		bzero(nai->ai_addr, addrlen);
459 		nai->ai_addrlen = addrlen;
460 		nai->ai_family = PF_INET6;
461 		nai->ai_protocol = 0;
462 		nai->ai_canonname = NULL;
463 		if (nai->ai_flags & AI_PASSIVE) {
464 			ai2sin6(nai)->sin6_addr = in6addr_any;
465 		} else {
466 			ai2sin6(nai)->sin6_addr = in6addr_loopback;
467 			if (nai->ai_flags & AI_CANONNAME) {
468 				canonname = strdup("loopback");
469 				if (canonname == NULL) {
470 					freeaddrinfo(nai);
471 					goto nomem;
472 				}
473 				nai->ai_canonname = canonname;
474 			}
475 		}
476 		ai2sin6(nai)->sin6_family = PF_INET6;
477 		ai2sin6(nai)->sin6_port = port;
478 		cur->ai_next = nai;
479 		cur = nai;
480 		if (aip->ai_family == PF_INET6) {
481 			cur->ai_next = NULL;
482 			goto success;
483 		}
484 		/* If address family is PF_UNSPEC or PF_INET */
485 v4only:
486 		/* create IPv4 addrinfo */
487 		nai = malloc(sizeof (struct addrinfo));
488 		if (nai == NULL)
489 			goto nomem;
490 		*nai = *aip;
491 		addrlen = sizeof (struct sockaddr_in);
492 		nai->ai_addr = malloc(addrlen);
493 		if (nai->ai_addr == NULL) {
494 			freeaddrinfo(nai);
495 			goto nomem;
496 		}
497 		bzero(nai->ai_addr, addrlen);
498 		nai->ai_addrlen = addrlen;
499 		nai->ai_family = PF_INET;
500 		nai->ai_protocol = 0;
501 		nai->ai_canonname = NULL;
502 		if (nai->ai_flags & AI_PASSIVE) {
503 			ai2sin(nai)->sin_addr.s_addr = INADDR_ANY;
504 		} else {
505 			ai2sin(nai)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
506 			if (nai->ai_flags & AI_CANONNAME &&
507 			    nai->ai_family != PF_UNSPEC) {
508 				canonname = strdup("loopback");
509 				if (canonname == NULL) {
510 					freeaddrinfo(nai);
511 					goto nomem;
512 				}
513 				nai->ai_canonname = canonname;
514 			}
515 		}
516 		ai2sin(nai)->sin_family = PF_INET;
517 		ai2sin(nai)->sin_port = port;
518 		cur->ai_next = nai;
519 		cur = nai;
520 		cur->ai_next = NULL;
521 		goto success;
522 	}
523 
524 	/* hostname string is a literal address or an alphabetical name */
525 	error = get_addr(aip->ai_family, hostname, aip, cur, port, version);
526 	if (error) {
527 		*res = NULL;
528 		return (error);
529 	}
530 
531 success:
532 	*res = aip->ai_next;
533 	return (0);
534 
535 nomem:
536 	return (EAI_MEMORY);
537 }
538 
539 int
540 getaddrinfo(const char *hostname, const char *servname,
541 	const struct addrinfo *hints, struct addrinfo **res)
542 {
543 	return (_getaddrinfo(hostname, servname, hints, res, GAIV_DEFAULT));
544 }
545 
546 int
547 __xnet_getaddrinfo(const char *hostname, const char *servname,
548 	const struct addrinfo *hints, struct addrinfo **res)
549 {
550 	return (_getaddrinfo(hostname, servname, hints, res, GAIV_XPG6));
551 }
552 
553 static int
554 get_addr(int family, const char *hostname, struct addrinfo *aip, struct
555 	addrinfo *cur, ushort_t port, int version)
556 {
557 	struct hostent		*hp;
558 	char			_hostname[MAXHOSTNAMELEN];
559 	int			i, errnum;
560 	struct addrinfo		*nai;
561 	int			addrlen;
562 	char			*canonname;
563 	boolean_t		firsttime = B_TRUE;
564 	boolean_t		create_v6_addrinfo;
565 	struct in_addr		v4addr;
566 	struct in6_addr		v6addr;
567 	struct in6_addr		*v6addrp;
568 	char			*zonestr = NULL;
569 
570 	/*
571 	 * Check for existence of address-zoneid delimiter '%'
572 	 * If the delimiter exists, parse the zoneid portion of
573 	 * <addr>%<zone_id>
574 	 */
575 	if ((zonestr = strchr(hostname, '%')) != NULL) {
576 		/* make sure we have room for <addr> portion of hostname */
577 		if (((zonestr - hostname) + 1) > sizeof (_hostname)) {
578 			return (EAI_MEMORY);
579 		}
580 
581 		/* chop off and save <zone_id> portion */
582 		(void) strlcpy(_hostname, hostname, (zonestr - hostname) + 1);
583 		++zonestr;	/* make zonestr point at start of <zone-id> */
584 		/* ensure zone is valid */
585 		if ((*zonestr == '\0') || (strlen(zonestr) > LIFNAMSIZ))  {
586 			return (EAI_NONAME);
587 		}
588 	} else {
589 		size_t hlen = sizeof (_hostname);
590 
591 		if (strlcpy(_hostname, hostname, hlen) >= hlen) {
592 			return (EAI_MEMORY);
593 		}
594 	}
595 
596 	/* Check to see if AI_NUMERICHOST bit is set */
597 	if (aip->ai_flags & AI_NUMERICHOST) {
598 		/* check to see if _hostname points to a literal IP address */
599 		if (!((inet_addr(_hostname) != ((in_addr_t)-1)) ||
600 		    (strcmp(_hostname, HOST_BROADCAST) == 0) ||
601 		    (inet_pton(AF_INET6, _hostname, &v6addr) > 0))) {
602 			return (EAI_NONAME);
603 		}
604 	}
605 
606 	/* if hostname argument is literal, name service doesn't get called */
607 	if (family == PF_UNSPEC) {
608 		hp = getipnodebyname(_hostname, AF_INET6, AI_ALL |
609 		    aip->ai_flags | AI_V4MAPPED, &errnum);
610 	} else {
611 		hp = getipnodebyname(_hostname, family, aip->ai_flags, &errnum);
612 	}
613 
614 	if (hp == NULL) {
615 		switch (errnum) {
616 		case HOST_NOT_FOUND:
617 			return (EAI_NONAME);
618 		case TRY_AGAIN:
619 			return (EAI_AGAIN);
620 		case NO_RECOVERY:
621 			return (EAI_FAIL);
622 		case NO_ADDRESS:
623 			if (version == GAIV_XPG6)
624 				return (EAI_NONAME);
625 			return (EAI_NODATA);
626 		default:
627 		return (EAI_SYSTEM);
628 		}
629 	}
630 
631 	for (i = 0; hp->h_addr_list[i]; i++) {
632 		/* Determine if an IPv6 addrinfo structure should be created */
633 		create_v6_addrinfo = B_TRUE;
634 		if (hp->h_addrtype == AF_INET6) {
635 			v6addrp = (struct in6_addr *)hp->h_addr_list[i];
636 			if (!(aip->ai_flags & AI_V4MAPPED) &&
637 			    IN6_IS_ADDR_V4MAPPED(v6addrp)) {
638 				create_v6_addrinfo = B_FALSE;
639 				IN6_V4MAPPED_TO_INADDR(v6addrp, &v4addr);
640 			}
641 		} else	if (hp->h_addrtype == AF_INET) {
642 			create_v6_addrinfo = B_FALSE;
643 			(void) memcpy(&v4addr, hp->h_addr_list[i],
644 			    sizeof (struct in_addr));
645 		} else {
646 			return (EAI_SYSTEM);
647 		}
648 
649 		if (create_v6_addrinfo) {
650 			/* create IPv6 addrinfo */
651 			nai = malloc(sizeof (struct addrinfo));
652 			if (nai == NULL)
653 				goto nomem;
654 			*nai = *aip;
655 			addrlen = sizeof (struct sockaddr_in6);
656 			nai->ai_addr = malloc(addrlen);
657 			if (nai->ai_addr == NULL) {
658 				freeaddrinfo(nai);
659 				goto nomem;
660 			}
661 			bzero(nai->ai_addr, addrlen);
662 			nai->ai_addrlen = addrlen;
663 			nai->ai_family = PF_INET6;
664 			nai->ai_protocol = 0;
665 
666 			(void) memcpy(ai2sin6(nai)->sin6_addr.s6_addr,
667 			    hp->h_addr_list[i], sizeof (struct in6_addr));
668 			nai->ai_canonname = NULL;
669 			if ((nai->ai_flags & AI_CANONNAME) && firsttime) {
670 				canonname = strdup(hp->h_name);
671 				if (canonname == NULL) {
672 					freeaddrinfo(nai);
673 					goto nomem;
674 				}
675 				nai->ai_canonname = canonname;
676 				firsttime = B_FALSE;
677 			}
678 			ai2sin6(nai)->sin6_family = PF_INET6;
679 			ai2sin6(nai)->sin6_port = port;
680 			/* set sin6_scope_id */
681 			if (zonestr != NULL) {
682 				/*
683 				 * Translate 'zonestr' into a valid
684 				 * sin6_scope_id.
685 				 */
686 				if ((errnum =
687 				    getscopeidfromzone(ai2sin6(nai), zonestr,
688 				    &ai2sin6(nai)->sin6_scope_id)) != 0) {
689 					return (errnum);
690 				}
691 			} else {
692 				ai2sin6(nai)->sin6_scope_id = 0;
693 			}
694 		} else {
695 			/* create IPv4 addrinfo */
696 			nai = malloc(sizeof (struct addrinfo));
697 			if (nai == NULL)
698 				goto nomem;
699 			*nai = *aip;
700 			addrlen = sizeof (struct sockaddr_in);
701 			nai->ai_addr = malloc(addrlen);
702 			if (nai->ai_addr == NULL) {
703 				freeaddrinfo(nai);
704 				goto nomem;
705 			}
706 			bzero(nai->ai_addr, addrlen);
707 			nai->ai_addrlen = addrlen;
708 			nai->ai_family = PF_INET;
709 			nai->ai_protocol = 0;
710 			(void) memcpy(&(ai2sin(nai)->sin_addr.s_addr),
711 			    &v4addr, sizeof (struct in_addr));
712 			nai->ai_canonname = NULL;
713 			if (nai->ai_flags & AI_CANONNAME && firsttime) {
714 				canonname = strdup(hp->h_name);
715 				if (canonname == NULL) {
716 					freeaddrinfo(nai);
717 					goto nomem;
718 				}
719 				nai->ai_canonname = canonname;
720 				firsttime = B_FALSE;
721 			}
722 			ai2sin(nai)->sin_family = PF_INET;
723 			ai2sin(nai)->sin_port = port;
724 		}
725 
726 		cur->ai_next = nai;
727 		cur = nai;
728 	}
729 	cur->ai_next = NULL;
730 	freehostent(hp);
731 	return (0);
732 
733 nomem:
734 	freehostent(hp);
735 	return (EAI_MEMORY);
736 
737 }
738 
739 /*
740  * getscopeidfromzone(sa, zone, sin6_scope_id)
741  *
742  * Converts the string pointed to by 'zone' into a sin6_scope_id.
743  * 'zone' will either be a pointer to an interface name or will
744  * be a literal sin6_scope_id.
745  *
746  * 0 is returned on success and the output parameter 'sin6_scope_id' will
747  *   be set to a valid sin6_scope_id.
748  * EAI_NONAME is returned for either of two reasons:
749  *	1.  The IPv6 address pointed to by sa->sin6_addr is not
750  *	    part of the 'link scope' (ie link local, nodelocal multicast or
751  *	    linklocal multicast address)
752  *	2.  The string pointed to by 'zone' can not be translate to a valid
753  *	    sin6_scope_id.
754  */
755 static uint_t
756 getscopeidfromzone(const struct sockaddr_in6 *sa, const char *zone,
757     uint32_t *sin6_scope_id) {
758 	const in6_addr_t *addr = &sa->sin6_addr;
759 	ulong_t ul_scope_id;
760 	char *endp;
761 
762 	if (IN6_IS_ADDR_LINKSCOPE(addr)) {
763 		/*
764 		 * Look up interface index associated with interface name
765 		 * pointed to by 'zone'.  Since the address is part of the link
766 		 * scope, there is a one-to-one relationship between interface
767 		 * index and sin6_scope_id.
768 		 * If an interface index can not be found for 'zone', then
769 		 * treat 'zone' as a literal sin6_scope_id value.
770 		 */
771 		if ((*sin6_scope_id = if_nametoindex(zone)) != 0) {
772 			return (0);
773 		} else {
774 			if ((ul_scope_id = strtoul(zone, &endp, 10)) != 0) {
775 				/* check that entire string was read */
776 				if (*endp != '\0') {
777 					return (EAI_NONAME);
778 				}
779 				*sin6_scope_id =
780 				    (uint32_t)(ul_scope_id & 0xffffffffUL);
781 			} else {
782 				return (EAI_NONAME);
783 			}
784 		}
785 	} else {
786 		return (EAI_NONAME);
787 	}
788 	return (0);
789 }
790 
791 
792 void
793 freeaddrinfo(struct addrinfo *ai)
794 {
795 	struct addrinfo *next;
796 
797 	do {
798 		next = ai->ai_next;
799 		if (ai->ai_canonname)
800 			free(ai->ai_canonname);
801 		if (ai->ai_addr)
802 			free(ai->ai_addr);
803 		free(ai);
804 		ai = next;
805 	} while (ai != NULL);
806 }
807 
808 static boolean_t
809 str_isnumber(const char *p)
810 {
811 	char *q = (char *)p;
812 	while (*q) {
813 		if (!isdigit(*q))
814 			return (B_FALSE);
815 		q++;
816 	}
817 	return (B_TRUE);
818 }
819 static const char *gai_errlist[] = {
820 	"name translation error 0 (no error)",		/* 0 */
821 	"specified address family not supported",	/* 1 EAI_ADDRFAMILY */
822 	"temporary name resolution failure",		/* 2 EAI_AGAIN */
823 	"invalid flags",				/* 3 EAI_BADFLAGS */
824 	"non-recoverable name resolution failure",	/* 4 EAI_FAIL */
825 	"specified address family not supported",	/* 5 EAI_FAMILY */
826 	"memory allocation failure",			/* 6 EAI_MEMORY */
827 	"no address for the specified node name",	/* 7 EAI_NODATA */
828 	"node name or service name not known",		/* 8 EAI_NONAME */
829 	"service name not available for the specified socket type",
830 							/* 9 EAI_SERVICE */
831 	"specified socket type not supported",		/* 10 EAI_SOCKTYPE */
832 	"system error",					/* 11 EAI_SYSTEM */
833 };
834 static int gai_nerr = { sizeof (gai_errlist)/sizeof (gai_errlist[0]) };
835 
836 const char *
837 gai_strerror(int ecode)
838 {
839 	if (ecode < 0)
840 		return (_dgettext(TEXT_DOMAIN,
841 		    "name translation internal error"));
842 	else if (ecode < gai_nerr)
843 		return (_dgettext(TEXT_DOMAIN, gai_errlist[ecode]));
844 	return (_dgettext(TEXT_DOMAIN, "unknown name translation error"));
845 }
846