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