2 * Copyright (c) 2008-2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 #include <sys/types.h>
29 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <network/sa_compare.h>
33 #include <arpa/inet.h>
37 #include <sys/param.h>
39 #include <notify_keys.h>
41 #include <TargetConditionals.h>
42 #include "netdb_async.h"
43 #include "si_module.h"
46 #define IPPROTO_UNSPEC 0
48 #define IPV6_ADDR_LEN 16
49 #define IPV4_ADDR_LEN 4
51 #define WANT_NOTHING 0
52 #define WANT_A4_ONLY 1
53 #define WANT_A6_ONLY 2
54 #define WANT_A6_PLUS_MAPPED_A4 3
55 #define WANT_A6_OR_MAPPED_A4_IF_NO_A6 4
57 #define V6TO4_PREFIX_16 0x2002
59 static int net_config_token
= -1;
60 static uint32_t net_v4_count
= 0;
61 static uint32_t net_v6_count
= 0; // includes 6to4 addresses
62 static pthread_mutex_t net_config_mutex
= PTHREAD_MUTEX_INITIALIZER
;
65 int _inet_aton_check(const char *cp
, struct in_addr
*addr
, int strict
);
74 __private_extern__
int
75 si_inet_config(uint32_t *inet4
, uint32_t *inet6
)
78 struct ifaddrs
*ifa
, *ifap
;
80 pthread_mutex_lock(&net_config_mutex
);
84 if (net_config_token
< 0)
86 status
= notify_register_check(kNotifySCNetworkChange
, &net_config_token
);
87 if (status
!= 0) net_config_token
= -1;
90 if (net_config_token
>= 0)
92 status
= notify_check(net_config_token
, &checkit
);
93 if (status
!= 0) checkit
= 1;
100 if (getifaddrs(&ifa
) < 0)
109 for (ifap
= ifa
; ifap
!= NULL
; ifap
= ifap
->ifa_next
)
111 if (ifap
->ifa_addr
== NULL
) continue;
112 if ((ifap
->ifa_flags
& IFF_UP
) == 0) continue;
114 if (ifap
->ifa_addr
->sa_family
== AF_INET
)
118 else if (ifap
->ifa_addr
->sa_family
== AF_INET6
)
128 if (inet4
!= NULL
) *inet4
= net_v4_count
;
129 if (inet6
!= NULL
) *inet6
= net_v6_count
;
131 pthread_mutex_unlock(&net_config_mutex
);
137 freeaddrinfo(struct addrinfo
*a
)
139 struct addrinfo
*next
;
144 if (a
->ai_addr
!= NULL
) free(a
->ai_addr
);
145 if (a
->ai_canonname
!= NULL
) free(a
->ai_canonname
);
152 gai_strerror(int32_t err
)
156 case EAI_ADDRFAMILY
: return "Address family for nodename not supported";
157 case EAI_AGAIN
: return "Temporary failure in name resolution";
158 case EAI_BADFLAGS
: return "Invalid value for ai_flags";
159 case EAI_FAIL
: return "Non-recoverable failure in name resolution";
160 case EAI_FAMILY
: return "ai_family not supported";
161 case EAI_MEMORY
: return "Memory allocation failure";
162 case EAI_NODATA
: return "No address associated with nodename";
163 case EAI_NONAME
: return "nodename nor servname provided, or not known";
164 case EAI_SERVICE
: return "servname not supported for ai_socktype";
165 case EAI_SOCKTYPE
: return "ai_socktype not supported";
166 case EAI_SYSTEM
: return "System error";
167 case EAI_BADHINTS
: return "Bad hints";
168 case EAI_PROTOCOL
: return "ai_protocol not supported";
169 case EAI_OVERFLOW
: return "argument buffer overflow";
172 return "Unknown error";
178 * We handle some "trival" cases locally. If the caller passes
179 * NI_NUMERICHOST (only), then this call turns into a getservbyport
180 * to get the service name + inet_pton() to create a host string.
181 * If the caller passes NI_NUMERICSERV (only), then we zero out the port
182 * number, complete the getnameinfo, and use printf() to create a service
183 * string. If the caller specifies both NI_NUMERICHOST and NI_NUMERICSERV,
184 * we inet_ntop() and printf() and return the results.
187 si_nameinfo(si_mod_t
*si
, const struct sockaddr
*sa
, int flags
, const char *interface
, uint32_t *err
)
189 si_item_t
*out
= NULL
;
190 const struct sockaddr
*lookup_sa
;
191 struct sockaddr_in s4
;
194 const uint64_t unused
= 0;
201 int do_host_lookup
= ((flags
& NI_NUMERICHOST
) == 0);
202 int do_serv_lookup
= ((flags
& NI_NUMERICSERV
) == 0);
205 if ((si
== NULL
) || (sa
== NULL
))
207 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAIL
;
211 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
215 if (sa
->sa_family
== AF_INET
)
217 struct sockaddr_in
*s4
= (struct sockaddr_in
*)sa
;
218 memcpy(&a4
, &s4
->sin_addr
, sizeof(a4
));
222 else if (sa
->sa_family
== AF_INET6
)
224 struct sockaddr_in6
*s6
= (struct sockaddr_in6
*)sa
;
225 memcpy(&a6
, &s6
->sin6_addr
, sizeof(a6
));
226 port
= s6
->sin6_port
;
228 /* Look for scope id in IPv6 Link Local, Multicast Node Local, and Multicast Link Local */
229 if (IN6_IS_ADDR_LINKLOCAL(&s6
->sin6_addr
) || IN6_IS_ADDR_MC_NODELOCAL(&s6
->sin6_addr
) || IN6_IS_ADDR_MC_LINKLOCAL(&s6
->sin6_addr
))
231 ifnum
= ntohs(a6
.__u6_addr
.__u6_addr16
[1]);
234 ifnum
= s6
->sin6_scope_id
;
235 a6
.__u6_addr
.__u6_addr16
[1] = htons(ifnum
);
238 if ((ifnum
!= s6
->sin6_scope_id
) && (s6
->sin6_scope_id
!= 0))
240 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAIL
;
245 /* v4 mapped and compat addresses are converted to plain v4 */
246 if (IN6_IS_ADDR_V4MAPPED(&s6
->sin6_addr
) || IN6_IS_ADDR_V4COMPAT(&s6
->sin6_addr
))
248 memcpy(&a4
, &s6
->sin6_addr
.s6_addr
[12], sizeof(a4
));
250 memset(&s4
, 0, sizeof(s4
));
251 s4
.sin_len
= sizeof(s4
);
252 s4
.sin_family
= AF_INET
;
254 memcpy(&s4
.sin_addr
, &a4
, sizeof(s4
.sin_addr
));
255 lookup_sa
= (const struct sockaddr
*)&s4
;
264 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAMILY
;
268 if (do_host_lookup
== 1)
270 si_item_t
*item
= si_host_byaddr(si
, addr
, lookup_sa
->sa_family
, interface
, NULL
);
274 h
= (struct hostent
*)((uintptr_t)item
+ sizeof(si_item_t
));
275 if (h
->h_name
== NULL
)
277 si_item_release(item
);
278 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAIL
;
282 host
= strdup(h
->h_name
);
283 si_item_release(item
);
286 if (err
!= NULL
) *err
= SI_STATUS_EAI_MEMORY
;
292 if ((do_serv_lookup
== 1) && (port
!= 0))
294 si_item_t
*item
= si_service_byport(si
, port
, NULL
);
298 s
= (struct servent
*)((uintptr_t)item
+ sizeof(si_item_t
));
299 if (s
->s_name
== NULL
)
301 si_item_release(item
);
303 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAIL
;
307 serv
= strdup(s
->s_name
);
308 si_item_release(item
);
312 if (err
!= NULL
) *err
= SI_STATUS_EAI_MEMORY
;
319 * Return numeric host name for NI_NUMERICHOST or if lookup failed, but not
320 * if NI_NAMEREQD is specified (so that we later fail with EAI_NONAME).
322 if ((host
== NULL
) && ((flags
& NI_NAMEREQD
) == 0))
324 char tmp
[INET6_ADDRSTRLEN
+ 1 + IF_NAMESIZE
+ 1];
326 if (sa
->sa_family
== AF_INET
)
328 char buf
[INET_ADDRSTRLEN
];
329 if (inet_ntop(AF_INET
, &a4
, buf
, sizeof(buf
)) != 0)
334 else if (sa
->sa_family
== AF_INET6
)
336 char buf
[INET6_ADDRSTRLEN
];
338 /* zero the embedded scope ID */
341 a6
.__u6_addr
.__u6_addr16
[1] = 0;
344 if (inet_ntop(AF_INET6
, &a6
, buf
, sizeof(buf
)) != 0)
348 char ifname
[IF_NAMESIZE
];
349 if (if_indextoname(ifnum
, ifname
) != NULL
)
351 asprintf(&host
, "%s%%%s", buf
, ifname
);
356 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAIL
;
368 /* Return numeric service name for NI_NUMERICSERV or if lookup failed. */
371 asprintf(&serv
, "%hu", ntohs(port
));
374 if ((host
== NULL
) || (serv
== NULL
))
378 if ((flags
& NI_NAMEREQD
) != 0)
380 *err
= SI_STATUS_EAI_NONAME
;
384 *err
= SI_STATUS_EAI_MEMORY
;
390 out
= (si_item_t
*)LI_ils_create("L4488ss", (unsigned long)si
, CATEGORY_NAMEINFO
, 1, unused
, unused
, host
, serv
);
399 _gai_numericserv(const char *serv
, uint16_t *port
)
414 num
= strtol(serv
, &endptr
, 10);
415 if ((serv
[0] != '\0') && (*endptr
== '\0') && (num
>= 0) && (num
<= UINT16_MAX
))
418 if (port
!= NULL
) *port
= (uint16_t)num
;
426 _gai_serv_to_port(const char *serv
, uint32_t proto
, uint16_t *port
)
430 const char *protoname
= NULL
;
432 if (_gai_numericserv(serv
, port
)) return 0;
434 if (proto
== IPPROTO_UDP
) protoname
= "udp";
435 if (proto
== IPPROTO_TCP
) protoname
= "tcp";
437 item
= si_service_byname(si_search(), serv
, protoname
);
438 if (item
== NULL
) return -1;
440 s
= (struct servent
*)((uintptr_t)item
+ sizeof(si_item_t
));
441 if (port
) *port
= ntohs(s
->s_port
);
442 si_item_release(item
);
448 si_addrinfo_v4(si_mod_t
*si
, int32_t flags
, int32_t sock
, int32_t proto
, uint16_t port
, struct in_addr
*addr
, uint16_t iface
, const char *cname
)
450 socket_data_t sockdata
;
451 struct sockaddr_in
*sa
;
456 len
= sizeof(struct sockaddr_in
);
457 memset(&sockdata
, 0, sizeof(socket_data_t
));
458 sa
= (struct sockaddr_in
*)&sockdata
;
461 sa
->sin_family
= AF_INET
;
462 sa
->sin_port
= htons(port
);
463 memcpy(&sa
->sin_addr
, addr
, sizeof(sa
->sin_addr
));
465 /* Kludge: Jam the interface number into sin_zero (4 bytes). */
467 memmove(sa
->sin_zero
, &v32
, sizeof(uint32_t));
469 return (si_item_t
*)LI_ils_create("L448844444Ss", (unsigned long)si
, CATEGORY_ADDRINFO
, 1, unused
, unused
, flags
, AF_INET
, sock
, proto
, len
, sockdata
, cname
);
473 si_addrinfo_v4_mapped(si_mod_t
*si
, int32_t flags
, int32_t sock
, int32_t proto
, uint16_t port
, struct in_addr
*addr
, uint16_t iface
, const char *cname
)
475 socket_data_t sockdata
;
476 struct sockaddr_in6
*sa
;
481 len
= sizeof(struct sockaddr_in6
);
482 memset(&sockdata
, 0, sizeof(socket_data_t
));
483 sa
= (struct sockaddr_in6
*)&sockdata
;
486 sa
->sin6_family
= AF_INET6
;
487 sa
->sin6_port
= htons(port
);
488 memset(&(sa
->sin6_addr
.__u6_addr
.__u6_addr8
[10]), 0xff, 2);
489 memcpy(&(sa
->sin6_addr
.__u6_addr
.__u6_addr8
[12]), addr
, sizeof(struct in_addr
));
491 /* sin6_scope_id is in host byte order */
492 sa
->sin6_scope_id
= iface
;
494 return (si_item_t
*)LI_ils_create("L448844444Ss", (unsigned long)si
, CATEGORY_ADDRINFO
, 1, unused
, unused
, flags
, AF_INET6
, sock
, proto
, len
, sockdata
, cname
);
499 si_addrinfo_v6(si_mod_t
*si
, int32_t flags
, int32_t sock
, int32_t proto
, uint16_t port
, struct in6_addr
*addr
, uint16_t iface
, const char *cname
)
501 socket_data_t sockdata
;
502 struct sockaddr_in6
*sa
;
507 len
= sizeof(struct sockaddr_in6
);
508 memset(&sockdata
, 0, sizeof(socket_data_t
));
509 sa
= (struct sockaddr_in6
*)&sockdata
;
512 sa
->sin6_family
= AF_INET6
;
513 sa
->sin6_port
= htons(port
);
514 memcpy(&sa
->sin6_addr
, addr
, sizeof(sa
->sin6_addr
));
516 /* sin6_scope_id is in host byte order */
517 sa
->sin6_scope_id
= iface
;
519 if (IN6_IS_ADDR_LINKLOCAL(&sa
->sin6_addr
))
521 /* check for embedded scopeid */
522 uint16_t esid
= ntohs(sa
->sin6_addr
.__u6_addr
.__u6_addr16
[1]);
525 sa
->sin6_addr
.__u6_addr
.__u6_addr16
[1] = 0;
526 if (iface
== 0) sa
->sin6_scope_id
= esid
;
530 return (si_item_t
*)LI_ils_create("L448844444Ss", (unsigned long)si
, CATEGORY_ADDRINFO
, 1, unused
, unused
, flags
, AF_INET6
, sock
, proto
, len
, sockdata
, cname
);
534 si_addrinfo_list(si_mod_t
*si
, uint32_t flags
, int socktype
, int proto
, struct in_addr
*a4
, struct in6_addr
*a6
, int port
, int scopeid
, const char *cname4
, const char *cname6
)
537 si_item_t
*item
= NULL
;
538 si_list_t
*out4
= NULL
, *out6
= NULL
;
540 if ((flags
& AI_V4MAPPED
) && ((flags
& AI_ALL
) || (a6
== NULL
))) do_map
= 1;
544 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_UDP
))
546 item
= si_addrinfo_v6(si
, 0, SOCK_DGRAM
, IPPROTO_UDP
, port
, a6
, scopeid
, cname6
);
547 out6
= si_list_add(out6
, item
);
548 si_item_release(item
);
551 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_TCP
))
553 item
= si_addrinfo_v6(si
, 0, SOCK_STREAM
, IPPROTO_TCP
, port
, a6
, scopeid
, cname6
);
554 out6
= si_list_add(out6
, item
);
555 si_item_release(item
);
558 if (proto
== IPPROTO_ICMPV6
)
560 item
= si_addrinfo_v6(si
, 0, SOCK_RAW
, IPPROTO_ICMPV6
, port
, a6
, scopeid
, cname6
);
561 out6
= si_list_add(out6
, item
);
562 si_item_release(item
);
568 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_UDP
))
572 item
= si_addrinfo_v4(si
, 0, SOCK_DGRAM
, IPPROTO_UDP
, port
, a4
, 0, cname4
);
573 out4
= si_list_add(out4
, item
);
577 item
= si_addrinfo_v4_mapped(si
, 0, SOCK_DGRAM
, IPPROTO_UDP
, port
, a4
, 0, cname4
);
578 out6
= si_list_add(out6
, item
);
581 si_item_release(item
);
584 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_TCP
))
588 item
= si_addrinfo_v4(si
, 0, SOCK_STREAM
, IPPROTO_TCP
, port
, a4
, 0, cname4
);
589 out4
= si_list_add(out4
, item
);
593 item
= si_addrinfo_v4_mapped(si
, 0, SOCK_STREAM
, IPPROTO_TCP
, port
, a4
, 0, cname4
);
594 out6
= si_list_add(out6
, item
);
597 si_item_release(item
);
600 if (proto
== IPPROTO_ICMP
)
604 item
= si_addrinfo_v4(si
, 0, SOCK_RAW
, IPPROTO_ICMP
, port
, a4
, 0, cname4
);
605 out4
= si_list_add(out4
, item
);
609 item
= si_addrinfo_v4_mapped(si
, 0, SOCK_RAW
, IPPROTO_ICMP
, port
, a4
, 0, cname4
);
610 out6
= si_list_add(out6
, item
);
613 si_item_release(item
);
617 out6
= si_list_concat(out6
, out4
);
618 si_list_release(out4
);
625 * Determines whether the given host name is a numeric IPv4 or IPv6 address,
626 * based on the address family input value. If the input addres family is
627 * unspecified, a more specific value will be provided on output if possible.
628 * Returns 1 if host name is numeric or 0 if not, or -1 on error.
631 _gai_numerichost(const char* nodename
, uint32_t *family
, int flags
, struct in_addr
*a4
, struct in6_addr
*a6
, int *scope
)
633 int numerichost
, passive
;
637 if (nodename
== NULL
)
639 /* return loopback or passive addresses */
640 passive
= (flags
& AI_PASSIVE
);
642 if (((*family
== AF_UNSPEC
) || (*family
== AF_INET
)) || ((*family
== AF_INET6
) && (flags
& AI_V4MAPPED
) && (flags
& AI_ALL
)))
644 if (passive
) a4
->s_addr
= 0;
645 else a4
->s_addr
= htonl(INADDR_LOOPBACK
);
648 if ((*family
== AF_UNSPEC
) || (*family
== AF_INET6
))
650 memset(a6
, 0, sizeof(*a6
));
651 if (!passive
) a6
->__u6_addr
.__u6_addr32
[3] = htonl(1);
659 * numeric IPv4 host valid for AF_UNSPEC and AF_INET
660 * also valid for AF_INET6 with AI_V4MAPPED
662 numerichost
= inet_pton(AF_INET
, nodename
, a4
);
663 if (numerichost
== 0)
665 /* inet_pton doesn't allow "a", "a.b", or "a.b.c" forms, so we re-check */
666 numerichost
= _inet_aton_check(nodename
, a4
, 1);
669 if (numerichost
== 1)
671 if (*family
== AF_UNSPEC
)
675 else if (*family
== AF_INET6
)
677 if (flags
& AI_V4MAPPED
)
679 memset(a6
, 0, sizeof(struct in6_addr
));
680 memset(&(a6
->__u6_addr
.__u6_addr8
[10]), 0xff, 2);
681 memcpy(&(a6
->__u6_addr
.__u6_addr8
[12]), a4
, sizeof(struct in_addr
));
692 /* numeric IPv6 host valid for AF_UNSPEC and AF_INET6 */
693 numerichost
= inet_pton(AF_INET6
, nodename
, a6
);
694 if (numerichost
== 1)
696 /* check for scope/zone id */
697 char *p
= strrchr(nodename
, SCOPE_DELIMITER
);
705 for (x
= p
; (*x
!= '\0') && (d
== 1); x
++)
711 if (d
== 1) *scope
= atoi(p
);
712 else *scope
= if_nametoindex(p
);
715 if (*family
== AF_UNSPEC
) *family
= AF_INET6
;
716 else if (*family
== AF_INET
) numerichost
= -1;
725 /* si_addrinfo_list_from_hostent
726 * Returns an addrinfo list from IPv4 and IPv6 hostent entries
729 si_addrinfo_list_from_hostent(si_mod_t
*si
, uint32_t flags
, uint32_t socktype
, uint32_t proto
, uint16_t port
, uint16_t scope
, const struct hostent
*h4
, const struct hostent
*h6
)
732 si_list_t
*out
= NULL
;
735 if ((h6
!= NULL
) && (h6
->h_addr_list
!= NULL
))
737 for (i
= 0; h6
->h_addr_list
[i
] != NULL
; i
++)
740 memcpy(&a6
, h6
->h_addr_list
[i
], h6
->h_length
);
741 list
= si_addrinfo_list(si
, flags
, socktype
, proto
, NULL
, &a6
, port
, scope
, NULL
, h6
->h_name
);
742 out
= si_list_concat(out
, list
);
743 si_list_release(list
);
747 if ((h4
!= NULL
) && (h4
->h_addr_list
!= NULL
))
749 for (i
= 0; h4
->h_addr_list
[i
] != NULL
; i
++)
752 memcpy(&a4
, h4
->h_addr_list
[i
], h4
->h_length
);
753 list
= si_addrinfo_list(si
, flags
, socktype
, proto
, &a4
, NULL
, port
, 0, h4
->h_name
, NULL
);
754 out
= si_list_concat(out
, list
);
755 si_list_release(list
);
763 _gai_addr_sort(const void *a
, const void *b
)
765 si_item_t
**item_a
, **item_b
;
766 si_addrinfo_t
*p
, *q
;
767 struct sockaddr
*sp
, *sq
;
769 item_a
= (si_item_t
**)a
;
770 item_b
= (si_item_t
**)b
;
772 p
= (si_addrinfo_t
*)((uintptr_t)*item_a
+ sizeof(si_item_t
));
773 q
= (si_addrinfo_t
*)((uintptr_t)*item_b
+ sizeof(si_item_t
));
775 sp
= (struct sockaddr
*)p
->ai_addr
.x
;
776 sq
= (struct sockaddr
*)q
->ai_addr
.x
;
779 * sa_dst_compare(A,B) returns -1 if A is less desirable than B,
780 * 0 if they are equally desirable, and 1 if A is more desirable.
781 * qsort() expects the inverse, so we swap sp and sq.
783 return sa_dst_compare(sq
, sp
, 0);
787 _gai_sort_list(si_list_t
*in
, uint32_t flags
)
792 uint32_t v4mapped_count
= 0;
793 uint32_t v6_count
= 0;
796 if (in
== NULL
) return NULL
;
798 for (i
= 0; i
< in
->count
; i
++)
800 a
= (si_addrinfo_t
*)((uintptr_t)in
->entry
[i
] + sizeof(si_item_t
));
801 if (a
->ai_family
== AF_INET6
)
803 struct sockaddr_in6
*s6
= (struct sockaddr_in6
*)a
->ai_addr
.x
;
804 if (IN6_IS_ADDR_V4MAPPED(&(s6
->sin6_addr
))) v4mapped_count
++;
810 if ((flags
& AI_V4MAPPED
) && ((v6_count
== 0) || (flags
& AI_ALL
))) filter_mapped
= 0;
814 if ((filter_mapped
== 1) && (v4mapped_count
> 0))
816 i
= in
->count
- v4mapped_count
;
817 if (i
== 0) return NULL
;
819 out
= (si_list_t
*)calloc(1, sizeof(si_list_t
));
820 if (out
== NULL
) return in
;
823 out
->refcount
= in
->refcount
;
825 out
->entry
= (si_item_t
**)calloc(out
->count
, sizeof(si_item_t
*));
826 if (out
->entry
== NULL
)
834 for (i
= 0; i
< in
->count
; i
++)
836 a
= (si_addrinfo_t
*)((uintptr_t)in
->entry
[i
] + sizeof(si_item_t
));
837 if (a
->ai_family
== AF_INET6
)
839 struct sockaddr_in6
*s6
= (struct sockaddr_in6
*)a
->ai_addr
.x
;
840 if (IN6_IS_ADDR_V4MAPPED(&(s6
->sin6_addr
)))
842 si_item_release(in
->entry
[i
]);
847 out
->entry
[out
->curr
++] = in
->entry
[i
];
856 qsort(&out
->entry
[0], out
->count
, sizeof(si_item_t
*), _gai_addr_sort
);
861 * Simple lookup via gethostbyname2(3) mechanism.
864 _gai_simple(si_mod_t
*si
, const void *nodeptr
, const void *servptr
, uint32_t family
, uint32_t socktype
, uint32_t proto
, uint32_t flags
, const char *interface
, uint32_t *err
)
866 si_item_t
*h4_item
= NULL
, *h6_item
= NULL
;
867 struct hostent
*h4
= NULL
, *h6
= NULL
;
868 si_list_t
*out
= NULL
;
871 if ((flags
& AI_NUMERICSERV
) != 0)
873 port
= *(uint16_t*)servptr
;
877 if (_gai_serv_to_port(servptr
, proto
, &port
) != 0)
879 if (err
) *err
= SI_STATUS_EAI_NONAME
;
884 if ((flags
& AI_NUMERICHOST
) != 0)
886 if (family
== AF_INET
)
888 h4_item
= si_host_byaddr(si
, nodeptr
, AF_INET
, interface
, NULL
);
890 else if (family
== AF_INET6
)
892 h6_item
= si_host_byaddr(si
, nodeptr
, AF_INET6
, interface
, NULL
);
897 if ((family
== AF_INET
) || (family
== AF_UNSPEC
))
899 h4_item
= si_host_byname(si
, nodeptr
, AF_INET
, interface
, NULL
);
902 if ((family
== AF_INET6
) || (family
== AF_UNSPEC
))
904 h6_item
= si_host_byname(si
, nodeptr
, AF_INET6
, interface
, NULL
);
910 h4
= (struct hostent
*)((uintptr_t)h4_item
+ sizeof(si_item_t
));
915 h6
= (struct hostent
*)((uintptr_t)h6_item
+ sizeof(si_item_t
));
918 out
= si_addrinfo_list_from_hostent(si
, flags
, socktype
, proto
, port
, 0, h4
, h6
);
919 si_item_release(h4_item
);
920 si_item_release(h6_item
);
922 return _gai_sort_list(out
, flags
);
926 si_srv_byname(si_mod_t
*si
, const char *qname
, const char *interface
, uint32_t *err
)
928 if (si
== NULL
) return 0;
929 if (si
->vtable
->sim_srv_byname
== NULL
) return 0;
931 return si
->vtable
->sim_srv_byname(si
, qname
, interface
, err
);
935 si_wants_addrinfo(si_mod_t
*si
)
937 if (si
== NULL
) return 0;
938 if (si
->vtable
->sim_addrinfo
== NULL
) return 0;
939 if (si
->vtable
->sim_wants_addrinfo
== NULL
) return 0;
941 return si
->vtable
->sim_wants_addrinfo(si
);
945 _gai_srv(si_mod_t
*si
, const char *node
, const char *serv
, uint32_t family
, uint32_t socktype
, uint32_t proto
, uint32_t flags
, const char *interface
, uint32_t *err
)
952 si_list_t
*list
= NULL
;
953 si_list_t
*result
= NULL
;
955 /* Minimum SRV priority is zero. Start below that. */
959 if (node
== NULL
|| serv
== NULL
) return NULL
;
961 asprintf(&qname
, "%s.%s", serv
, node
);
962 list
= si_srv_byname(si
, qname
, interface
, err
);
965 /* Iterate the SRV records starting at lowest priority and attempt to
966 * lookup the target host name. Returns the first successful lookup.
967 * It's an O(n^2) algorithm but data sets are small (less than 100) and
968 * sorting overhead is dwarfed by network I/O for each element.
970 while (list
!= NULL
&& result
== NULL
)
972 /* Find the next lowest priority level. */
973 /* Maximum SRV priority is UINT16_MAX. Start above that. */
976 for (i
= 0; i
< list
->count
; ++i
)
978 item
= list
->entry
[i
];
979 srv
= (si_srv_t
*)((uintptr_t)item
+ sizeof(si_item_t
));
981 if (srv
->priority
> lastprio
&& srv
->priority
< currprio
)
983 currprio
= srv
->priority
;
987 if (currprio
== INT_MAX
)
989 /* All priorities have been evaluated. Done. */
997 /* Lookup hosts at the current priority level. Return first match. */
998 for (i
= 0; i
< list
->count
; ++i
)
1000 item
= list
->entry
[i
];
1001 srv
= (si_srv_t
*)((uintptr_t)item
+ sizeof(si_item_t
));
1003 if (srv
->priority
== currprio
)
1005 /* So that _gai_simple expects an integer service. */
1006 flags
|= AI_NUMERICSERV
;
1008 result
= _gai_simple(si
, srv
->target
, &srv
->port
, family
, socktype
, proto
, flags
, interface
, err
);
1019 si_list_release(list
);
1026 si_addrinfo(si_mod_t
*si
, const char *node
, const char *serv
, uint32_t family
, uint32_t socktype
, uint32_t proto
, uint32_t flags
, const char *interface
, uint32_t *err
)
1028 int numerichost
, numericserv
= 0;
1030 const void *nodeptr
= NULL
, *servptr
= NULL
;
1032 struct in_addr a4
, *p4
;
1033 struct in6_addr a6
, *p6
;
1037 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
1041 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAIL
;
1045 /* treat empty strings as NULL */
1046 if ((node
!= NULL
) && (node
[0] == '\0')) node
= NULL
;
1047 if ((serv
!= NULL
) && (serv
[0] == '\0')) serv
= NULL
;
1049 /* make sure we have a query */
1050 if ((node
== NULL
) && (serv
== NULL
))
1052 if (err
!= NULL
) *err
= SI_STATUS_EAI_NONAME
;
1056 /* verify family is supported */
1064 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAMILY
;
1068 /* verify socket type is supported */
1077 if (err
!= NULL
) *err
= SI_STATUS_EAI_BADHINTS
;
1081 /* verify protocol is supported */
1084 case IPPROTO_UNSPEC
:
1088 case IPPROTO_ICMPV6
:
1091 if (err
!= NULL
) *err
= SI_STATUS_EAI_BADHINTS
;
1095 /* verify socket type compatible with protocol */
1096 if (((socktype
== SOCK_DGRAM
) && (proto
!= IPPROTO_UNSPEC
) && (proto
!= IPPROTO_UDP
)) || ((socktype
== SOCK_STREAM
) && (proto
!= IPPROTO_UNSPEC
) && (proto
!= IPPROTO_TCP
)))
1098 if (err
!= NULL
) *err
= SI_STATUS_EAI_BADHINTS
;
1102 /* check AI_V4MAPPED and AI_ALL */
1103 if (family
!= AF_INET6
)
1105 /* unset AI_V4MAPPED and AI_ALL unless family is AF_INET6 */
1106 flags
&= ~(AI_V4MAPPED
| AI_ALL
);
1108 else if ((flags
& AI_V4MAPPED
) == 0)
1110 /* unset AI_ALL unless family is AF_INET6 and AI_V4MAPPED is set */
1114 memset(&a4
, 0, sizeof(struct in_addr
));
1115 memset(&a6
, 0, sizeof(struct in6_addr
));
1117 /* determine the protocol if possible */
1118 if ((proto
== IPPROTO_UNSPEC
) && (socktype
== SOCK_DGRAM
)) proto
= IPPROTO_UDP
;
1119 if ((proto
== IPPROTO_UNSPEC
) && (socktype
== SOCK_STREAM
)) proto
= IPPROTO_TCP
;
1123 if ((flags
& AI_SRV
) != 0)
1126 out
= _gai_srv(si
, node
, serv
, family
, socktype
, proto
, flags
, interface
, err
);
1127 return _gai_sort_list(out
, flags
);
1131 /* Usual service lookup */
1132 numericserv
= _gai_numericserv(serv
, &port
);
1135 if ((flags
& AI_NUMERICSERV
) && (numericserv
== 0))
1137 /* FreeBSD returns SI_STATUS_EAI_SERVICE */
1138 if (err
!= NULL
) *err
= SI_STATUS_EAI_NONAME
;
1142 if ((serv
!= NULL
) && (strcmp(serv
, "0") != 0))
1144 if (numericserv
== 1)
1146 flags
|= AI_NUMERICSERV
;
1155 numerichost
= _gai_numerichost(node
, &family
, flags
, &a4
, &a6
, &scope
);
1156 if ((numerichost
== -1) || ((flags
& AI_NUMERICHOST
) && (numerichost
== 0)))
1158 if (err
!= NULL
) *err
= SI_STATUS_EAI_NONAME
;
1162 if (numerichost
== 1)
1164 flags
|= AI_NUMERICHOST
;
1165 if (family
== AF_INET
)
1169 else if (family
== AF_INET6
)
1179 if ((numerichost
== 1) && (numericserv
== 0))
1181 /* only service lookup needed. convert to port and perform a trivial getaddrinfo */
1182 if (_gai_serv_to_port(serv
, proto
, &port
) != 0)
1184 if (err
!= NULL
) *err
= SI_STATUS_EAI_NONAME
;
1189 flags
|= AI_NUMERICSERV
;
1195 if ((numerichost
== 1) && (numericserv
== 1))
1201 if (family
== AF_INET
) p6
= NULL
;
1202 if (family
== AF_INET6
) p4
= NULL
;
1203 if (node
== NULL
) cname
= "localhost";
1205 /* allow nodename to be a mapped IPv4 address, e.g. "::ffff:10.0.0.1" */
1206 if (p6
!= NULL
) flags
|= AI_V4MAPPED
;
1208 /* handle trivial questions */
1209 out
= si_addrinfo_list(si
, flags
, socktype
, proto
, p4
, p6
, port
, scope
, cname
, cname
);
1210 return _gai_sort_list(out
, flags
);
1212 else if (si_wants_addrinfo(si
))
1214 /* or let the current module handle the host lookups intelligently */
1215 out
= si
->vtable
->sim_addrinfo(si
, nodeptr
, servptr
, family
, socktype
, proto
, flags
, interface
, err
);
1216 return _gai_sort_list(out
, flags
);
1219 /* fall back to a default path */
1220 out
= _gai_simple(si
, nodeptr
, servptr
, family
, socktype
, proto
, flags
, interface
, err
);
1221 return _gai_sort_list(out
, flags
);
1224 static struct addrinfo
*
1225 si_item_to_addrinfo(si_item_t
*item
)
1228 struct addrinfo
*out
;
1230 if (item
== NULL
) return NULL
;
1232 a
= (si_addrinfo_t
*)((uintptr_t)item
+ sizeof(si_item_t
));
1234 out
= (struct addrinfo
*)calloc(1, sizeof(struct addrinfo
));
1235 if (out
== NULL
) return NULL
;
1237 out
->ai_flags
= a
->ai_flags
;
1238 out
->ai_family
= a
->ai_family
;
1239 out
->ai_socktype
= a
->ai_socktype
;
1240 out
->ai_protocol
= a
->ai_protocol
;
1241 out
->ai_addrlen
= a
->ai_addrlen
;
1243 out
->ai_addr
= (struct sockaddr
*)calloc(1, out
->ai_addrlen
);
1244 if (out
->ai_addr
== NULL
)
1250 memcpy(out
->ai_addr
, a
->ai_addr
.x
, out
->ai_addrlen
);
1252 if (a
->ai_canonname
!= NULL
)
1254 out
->ai_canonname
= strdup(a
->ai_canonname
);
1255 if (out
->ai_canonname
== NULL
)
1266 si_list_to_addrinfo(si_list_t
*list
)
1268 struct addrinfo
*tail
, *out
;
1271 if (list
== NULL
) return NULL
;
1272 if (list
->count
== 0) return NULL
;
1274 i
= list
->count
- 1;
1276 out
= si_item_to_addrinfo(list
->entry
[i
]);
1279 for (i
--; i
>= 0; i
--)
1281 out
= si_item_to_addrinfo(list
->entry
[i
]);
1288 out
->ai_next
= tail
;
1295 /* getipnodebyname */
1298 make_hostent(si_mod_t
*si
, const char *name
, struct in_addr addr
)
1304 if (name
== NULL
) return NULL
;
1308 addrs
[0] = (char *)&(addr
.s_addr
);
1312 return (si_item_t
*)LI_ils_create("L4488s*44a", (unsigned long)si
, CATEGORY_HOST_IPV4
, 1, unused
, unused
, name
, aliases
, AF_INET
, IPV4_ADDR_LEN
, addrs
);
1316 make_hostent6(si_mod_t
*si
, const char *name
, struct in6_addr addr
)
1322 if (name
== NULL
) return NULL
;
1326 addrs
[0] = (char *)&(addr
.__u6_addr
.__u6_addr32
[0]);
1330 return (si_item_t
*)LI_ils_create("L4488s*44c", (unsigned long)si
, CATEGORY_HOST_IPV6
, 1, unused
, unused
, name
, aliases
, AF_INET6
, IPV6_ADDR_LEN
, addrs
);
1334 lower_case(const char *s
)
1339 if (s
== NULL
) return NULL
;
1341 t
= malloc(strlen(s
) + 1);
1342 if (t
== NULL
) return NULL
;
1344 for (i
= 0; s
[i
] != '\0'; i
++)
1346 if ((s
[i
] >= 'A') && (s
[i
] <= 'Z')) t
[i
] = s
[i
] + 32;
1356 merge_alias(const char *name
, build_hostent_t
*h
)
1360 if (name
== NULL
) return 0;
1361 if (h
== NULL
) return 0;
1362 if (h
->host
.h_name
== NULL
) return 0;
1364 if ((h
->host
.h_name
!= NULL
) && (string_equal(name
, h
->host
.h_name
))) return 0;
1366 for (i
= 0; i
< h
->alias_count
; i
++)
1368 if (string_equal(name
, h
->host
.h_aliases
[i
])) return 0;
1371 if (h
->alias_count
== 0) h
->host
.h_aliases
= (char **)calloc(2, sizeof(char *));
1372 else h
->host
.h_aliases
= (char **)reallocf(h
->host
.h_aliases
, (h
->alias_count
+ 2) * sizeof(char *));
1374 if (h
->host
.h_aliases
== NULL
)
1380 h
->host
.h_aliases
[h
->alias_count
] = lower_case(name
);
1382 h
->host
.h_aliases
[h
->alias_count
] = NULL
;
1388 append_addr(const char *addr
, uint32_t len
, build_hostent_t
*h
)
1390 if (addr
== NULL
) return 0;
1391 if (h
== NULL
) return 0;
1393 if (h
->addr_count
== 0) h
->host
.h_addr_list
= (char **)calloc(2, sizeof(char *));
1394 else h
->host
.h_addr_list
= (char **)reallocf(h
->host
.h_addr_list
, (h
->addr_count
+ 2) * sizeof(char *));
1396 if (h
->host
.h_addr_list
== NULL
)
1402 h
->host
.h_addr_list
[h
->addr_count
] = malloc(len
);
1403 if (h
->host
.h_addr_list
[h
->addr_count
] == NULL
) return -1;
1405 memcpy(h
->host
.h_addr_list
[h
->addr_count
], addr
, len
);
1407 h
->host
.h_addr_list
[h
->addr_count
] = NULL
;
1413 free_build_hostent(build_hostent_t
*h
)
1418 if (h
== NULL
) return;
1420 if (h
->host
.h_name
!= NULL
) free(h
->host
.h_name
);
1421 h
->host
.h_name
= NULL
;
1423 aliases
= h
->host
.h_aliases
;
1424 if (aliases
!= NULL
)
1426 while (*aliases
!= NULL
) free(*aliases
++);
1427 free(h
->host
.h_aliases
);
1430 h
->host
.h_aliases
= NULL
;
1432 if (h
->host
.h_addr_list
!= NULL
)
1434 for (i
= 0; h
->host
.h_addr_list
[i
] != NULL
; i
++) free(h
->host
.h_addr_list
[i
]);
1435 free(h
->host
.h_addr_list
);
1438 h
->host
.h_addr_list
= NULL
;
1443 si_ipnode_byname(si_mod_t
*si
, const char *name
, int family
, int flags
, const char *interface
, uint32_t *err
)
1445 int i
, status
, want
;
1447 struct in_addr addr4
;
1448 struct in6_addr addr6
;
1449 si_item_t
*item4
, *item6
;
1450 build_hostent_t
*out
;
1454 memset(&addr4
, 0, sizeof(struct in_addr
));
1455 memset(&addr6
, 0, sizeof(struct in6_addr
));
1457 if (err
!= NULL
) *err
= 0;
1459 if (family
== AF_INET
)
1461 status
= inet_aton(name
, &addr4
);
1464 /* create a host entry */
1465 item4
= make_hostent(si
, name
, addr4
);
1468 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1475 else if (family
== AF_INET6
)
1477 status
= inet_pton(family
, name
, &addr6
);
1480 /* create a host entry */
1481 item6
= make_hostent6(si
, name
, addr6
);
1484 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1491 status
= inet_aton(name
, &addr4
);
1494 if (!(flags
& (AI_V4MAPPED
| AI_V4MAPPED_CFG
)))
1496 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
1500 addr6
.__u6_addr
.__u6_addr32
[0] = 0x00000000;
1501 addr6
.__u6_addr
.__u6_addr32
[1] = 0x00000000;
1502 addr6
.__u6_addr
.__u6_addr32
[2] = htonl(0x0000ffff);
1503 memmove(&(addr6
.__u6_addr
.__u6_addr32
[3]), &(addr4
.s_addr
), IPV4_ADDR_LEN
);
1505 /* create a host entry */
1506 item6
= make_hostent6(si
, name
, addr6
);
1509 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1518 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1523 * IF AI_ADDRCONFIG is set, we need to know what interface flavors we really have.
1529 if (flags
& AI_ADDRCONFIG
)
1531 if (si_inet_config(&if4
, &if6
) < 0)
1533 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1537 /* Bail out if there are no interfaces */
1538 if ((if4
== 0) && (if6
== 0))
1540 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1546 * Figure out what we want.
1547 * If user asked for AF_INET, we only want V4 addresses.
1549 want
= WANT_A4_ONLY
;
1551 if (family
== AF_INET
)
1553 if ((flags
& AI_ADDRCONFIG
) && (if4
== 0))
1555 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1559 else if (family
== AF_INET6
)
1561 /* family == AF_INET6 */
1562 want
= WANT_A6_ONLY
;
1564 if (flags
& (AI_V4MAPPED
| AI_V4MAPPED_CFG
))
1568 want
= WANT_A6_PLUS_MAPPED_A4
;
1572 want
= WANT_A6_OR_MAPPED_A4_IF_NO_A6
;
1577 if ((flags
& AI_ADDRCONFIG
) && (if6
== 0))
1579 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1586 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1593 /* fetch IPv6 data if required */
1594 if ((want
== WANT_A6_ONLY
) || (want
== WANT_A6_OR_MAPPED_A4_IF_NO_A6
) || (want
== WANT_A6_PLUS_MAPPED_A4
))
1596 item6
= si_host_byname(si
, name
, AF_INET6
, interface
, (uint32_t *)err
);
1599 /* fetch IPv4 data if required */
1600 if ((want
== WANT_A4_ONLY
) || (want
== WANT_A6_PLUS_MAPPED_A4
) || ((want
== WANT_A6_OR_MAPPED_A4_IF_NO_A6
) && (item6
== NULL
)))
1602 item4
= si_host_byname(si
, name
, AF_INET
, interface
, (uint32_t *)err
);
1605 if (want
== WANT_A4_ONLY
)
1607 si_item_release(item6
);
1608 if ((item4
== NULL
) && (err
!= NULL
)) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
1612 if (want
== WANT_A6_ONLY
)
1614 si_item_release(item4
);
1615 if ((item6
== NULL
) && (err
!= NULL
)) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
1619 if ((item6
== NULL
) && (item4
== NULL
))
1621 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
1625 /* output item will have IPv6 + mapped IPv4 addresses */
1627 out
= (build_hostent_t
*)calloc(1, sizeof(build_hostent_t
));
1630 si_item_release(item4
);
1631 si_item_release(item6
);
1632 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1638 h
= (struct hostent
*)((uintptr_t)item4
+ sizeof(si_item_t
));
1640 out
->host
.h_name
= lower_case(h
->h_name
);
1642 if (h
->h_aliases
!= NULL
)
1644 for (i
= 0; h
->h_aliases
[i
] != NULL
; i
++) merge_alias(h
->h_aliases
[i
], out
);
1647 for (i
= 0; h
->h_addr_list
[i
] != 0; i
++)
1649 addr6
.__u6_addr
.__u6_addr32
[0] = 0x00000000;
1650 addr6
.__u6_addr
.__u6_addr32
[1] = 0x00000000;
1651 addr6
.__u6_addr
.__u6_addr32
[2] = htonl(0x0000ffff);
1652 memmove(&(addr6
.__u6_addr
.__u6_addr32
[3]), h
->h_addr_list
[i
], IPV4_ADDR_LEN
);
1653 append_addr((const char *)&addr6
, IPV6_ADDR_LEN
, out
);
1659 h
= (struct hostent
*)((uintptr_t)item6
+ sizeof(si_item_t
));
1661 if (out
->host
.h_name
== NULL
) out
->host
.h_name
= lower_case(h
->h_name
);
1663 if (h
->h_aliases
!= NULL
)
1665 for (i
= 0; h
->h_aliases
[i
] != NULL
; i
++) merge_alias(h
->h_aliases
[i
], out
);
1668 for (i
= 0; h
->h_addr_list
[i
] != 0; i
++) append_addr(h
->h_addr_list
[i
], IPV6_ADDR_LEN
, out
);
1671 si_item_release(item4
);
1672 si_item_release(item6
);
1676 item6
= (si_item_t
*)LI_ils_create("L4488s*44c", (unsigned long)si
, CATEGORY_HOST_IPV6
, 1, unused
, unused
, out
->host
.h_name
, out
->host
.h_aliases
, AF_INET6
, IPV6_ADDR_LEN
, out
->host
.h_addr_list
);
1678 free_build_hostent(out
);