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