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