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