2 * Copyright (c) 2008-2009 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 <arpa/inet.h>
36 #include <sys/param.h>
38 #include <notify_keys.h>
40 #include <TargetConditionals.h>
41 #include "netdb_async.h"
42 #include "si_module.h"
45 #define IPPROTO_UNSPEC 0
47 #define IPV6_ADDR_LEN 16
48 #define IPV4_ADDR_LEN 4
50 #define WANT_NOTHING 0
51 #define WANT_A4_ONLY 1
52 #define WANT_A6_ONLY 2
53 #define WANT_A6_PLUS_MAPPED_A4 3
54 #define WANT_A6_OR_MAPPED_A4_IF_NO_A6 4
56 #define TEREDO_PREFIX_32 0x20010000
57 #define V6TO4_PREFIX_16 0x2002
58 #define ULA_PREFIX_8 0xfc
60 #define NET_TYPE_UNKNOWN 0x00000000
61 #define NET_TYPE_V4 0x00000001
62 #define NET_TYPE_V6 0x00000002
63 #define NET_TYPE_TEREDO 0x00000012
64 #define NET_TYPE_6TO4 0x00000022
65 #define NET_TYPE_LINKLOCAL 0x00000042
66 #define NET_TYPE_SITELOCAL 0x00000082
67 #define NET_TYPE_ULA 0x00000102
68 #define NET_MASK_T6LSU 0x000001f0
82 static int net_config_token
= -1;
83 static config_stats_t config_stats_global
;
84 static pthread_mutex_t net_config_mutex
= PTHREAD_MUTEX_INITIALIZER
;
94 _si_net_addr_type(const struct sockaddr
*s
)
96 const struct sockaddr_in6
*sa6
= (const struct sockaddr_in6
*)s
;
98 if (s
== NULL
) return NET_TYPE_UNKNOWN
;
99 else if (s
->sa_family
== AF_INET
) return NET_TYPE_V4
;
100 else if (s
->sa_family
!= AF_INET6
) return NET_TYPE_UNKNOWN
;
101 else if (sa6
->sin6_addr
.__u6_addr
.__u6_addr32
[0] == ntohs(TEREDO_PREFIX_32
)) return NET_TYPE_TEREDO
;
102 else if (sa6
->sin6_addr
.__u6_addr
.__u6_addr16
[0] == ntohs(V6TO4_PREFIX_16
)) return NET_TYPE_6TO4
;
103 else if (IN6_IS_ADDR_LINKLOCAL(&(sa6
->sin6_addr
))) return NET_TYPE_LINKLOCAL
;
104 else if (IN6_IS_ADDR_SITELOCAL(&(sa6
->sin6_addr
))) return NET_TYPE_SITELOCAL
;
105 else if ((sa6
->sin6_addr
.__u6_addr
.__u6_addr8
[0] & 0xfe) == ULA_PREFIX_8
) return NET_TYPE_ULA
;
111 _si_net_type_is_IPv6_globably_reachable_non_transitional(int t
)
115 if (t
& NET_MASK_T6LSU
) return 0;
123 _si_netconfig(config_stats_t
*stats
)
125 int status
, checkit
, net_type
;
126 struct ifaddrs
*ifa
, *ifap
;
128 if (stats
== NULL
) return 0;
130 pthread_mutex_lock(&net_config_mutex
);
135 if (net_config_token
< 0)
137 status
= notify_register_check(kNotifySCNetworkChange
, &net_config_token
);
138 if (status
!= 0) net_config_token
= -1;
141 if (net_config_token
>= 0)
143 status
= notify_check(net_config_token
, &checkit
);
144 if (status
!= 0) checkit
= 1;
149 if (getifaddrs(&ifa
) < 0)
155 memset(&config_stats_global
, 0, sizeof(config_stats_t
));
157 for (ifap
= ifa
; ifap
!= NULL
; ifap
= ifap
->ifa_next
)
159 if (ifap
->ifa_addr
== NULL
) continue;
160 if ((ifap
->ifa_flags
& IFF_UP
) == 0) continue;
162 net_type
= _si_net_addr_type(ifap
->ifa_addr
);
164 if (net_type
== NET_TYPE_UNKNOWN
)
167 else if (net_type
== NET_TYPE_V4
)
169 config_stats_global
.v4_count
++;
173 config_stats_global
.v6_count
++;
175 if (net_type
== NET_TYPE_LINKLOCAL
)
177 config_stats_global
.ll_count
++;
180 else if (net_type
== NET_TYPE_ULA
)
182 config_stats_global
.ula_count
++;
183 if (!strncmp(ifap
->ifa_name
, "utun", 4)) continue;
185 if (net_type
== NET_TYPE_TEREDO
)
187 config_stats_global
.teredo_count
++;
189 else if (net_type
== NET_TYPE_6TO4
)
191 config_stats_global
.v6to4_count
++;
193 else if (net_type
== NET_TYPE_SITELOCAL
)
195 config_stats_global
.sl_count
++;
198 if (_si_net_type_is_IPv6_globably_reachable_non_transitional(net_type
))
200 config_stats_global
.gai_v6_preferred
= 1;
209 *stats
= config_stats_global
;
211 pthread_mutex_unlock(&net_config_mutex
);
216 __private_extern__
int
217 si_inet_config(uint32_t *inet4
, uint32_t *inet6
)
221 if (_si_netconfig(&x
) < 0) return -1;
222 if (inet4
!= NULL
) *inet4
= x
.v4_count
;
223 if (inet6
!= NULL
) *inet6
= x
.v6_count
;
228 freeaddrinfo(struct addrinfo
*a
)
230 struct addrinfo
*next
;
235 if (a
->ai_addr
!= NULL
) free(a
->ai_addr
);
236 if (a
->ai_canonname
!= NULL
) free(a
->ai_canonname
);
243 gai_strerror(int32_t err
)
247 case EAI_ADDRFAMILY
: return "Address family for nodename not supported";
248 case EAI_AGAIN
: return "Temporary failure in name resolution";
249 case EAI_BADFLAGS
: return "Invalid value for ai_flags";
250 case EAI_FAIL
: return "Non-recoverable failure in name resolution";
251 case EAI_FAMILY
: return "ai_family not supported";
252 case EAI_MEMORY
: return "Memory allocation failure";
253 case EAI_NODATA
: return "No address associated with nodename";
254 case EAI_NONAME
: return "nodename nor servname provided, or not known";
255 case EAI_SERVICE
: return "servname not supported for ai_socktype";
256 case EAI_SOCKTYPE
: return "ai_socktype not supported";
257 case EAI_SYSTEM
: return "System error";
258 case EAI_BADHINTS
: return "Bad hints";
259 case EAI_PROTOCOL
: return "ai_protocol not supported";
260 case EAI_OVERFLOW
: return "argument buffer overflow";
263 return "Unknown error";
269 * We handle some "trival" cases locally. If the caller passes
270 * NI_NUMERICHOST (only), then this call turns into a getservbyport
271 * to get the service name + inet_pton() to create a host string.
272 * If the caller passes NI_NUMERICSERV (only), then we zero out the port
273 * number, complete the getnameinfo, and use printf() to create a service
274 * string. If the caller specifies both NI_NUMERICHOST and NI_NUMERICSERV,
275 * we inet_ntop() and printf() and return the results.
277 __private_extern__ si_item_t
*
278 si_nameinfo(si_mod_t
*si
, const struct sockaddr
*sa
, int flags
, const char *interface
, uint32_t *err
)
280 si_item_t
*out
= NULL
;
281 const struct sockaddr
*lookup_sa
;
282 struct sockaddr_in s4
;
285 const uint64_t unused
= 0;
292 int do_host_lookup
= ((flags
& NI_NUMERICHOST
) == 0);
293 int do_serv_lookup
= ((flags
& NI_NUMERICSERV
) == 0);
296 if ((si
== NULL
) || (sa
== NULL
))
298 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAIL
;
302 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
306 if (sa
->sa_family
== AF_INET
)
308 struct sockaddr_in
*s4
= (struct sockaddr_in
*)sa
;
309 memcpy(&a4
, &s4
->sin_addr
, sizeof(a4
));
313 else if (sa
->sa_family
== AF_INET6
)
315 struct sockaddr_in6
*s6
= (struct sockaddr_in6
*)sa
;
316 memcpy(&a6
, &s6
->sin6_addr
, sizeof(a6
));
317 port
= s6
->sin6_port
;
319 /* Look for link-local IPv6 scope id */
320 if (IN6_IS_ADDR_LINKLOCAL(&s6
->sin6_addr
))
322 ifnum
= ntohs(a6
.__u6_addr
.__u6_addr16
[1]);
325 ifnum
= s6
->sin6_scope_id
;
326 a6
.__u6_addr
.__u6_addr16
[1] = htons(ifnum
);
329 if (ifnum
!= s6
->sin6_scope_id
&&
330 s6
->sin6_scope_id
!= 0)
332 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAIL
;
337 /* v4 mapped and compat addresses are converted to plain v4 */
338 if (IN6_IS_ADDR_V4MAPPED(&s6
->sin6_addr
) || IN6_IS_ADDR_V4COMPAT(&s6
->sin6_addr
))
340 memcpy(&a4
, &s6
->sin6_addr
.s6_addr
[12], sizeof(a4
));
342 memset(&s4
, 0, sizeof(s4
));
343 s4
.sin_len
= sizeof(s4
);
344 s4
.sin_family
= AF_INET
;
346 memcpy(&s4
.sin_addr
, &a4
, sizeof(s4
.sin_addr
));
347 lookup_sa
= (const struct sockaddr
*)&s4
;
356 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAMILY
;
360 if (do_host_lookup
== 1)
363 if ((do_serv_lookup
== 1) && (si
->sim_nameinfo
!= NULL
))
365 return si
->sim_nameinfo(si
, lookup_sa
, flags
, interface
, err
);
368 si_item_t
*item
= si_host_byaddr(si
, addr
, lookup_sa
->sa_family
, interface
, NULL
);
372 h
= (struct hostent
*)((uintptr_t)item
+ sizeof(si_item_t
));
373 host
= strdup(h
->h_name
);
374 si_item_release(item
);
377 if (err
!= NULL
) *err
= SI_STATUS_EAI_MEMORY
;
383 if ((do_serv_lookup
== 1) && (port
!= 0))
385 si_item_t
*item
= si_service_byport(si
, port
, NULL
);
389 s
= (struct servent
*)((uintptr_t)item
+ sizeof(si_item_t
));
390 serv
= strdup(s
->s_name
);
391 si_item_release(item
);
395 if (err
!= NULL
) *err
= SI_STATUS_EAI_MEMORY
;
402 * Return numeric host name for NI_NUMERICHOST or if lookup failed, but not
403 * if NI_NAMEREQD is specified (so that we later fail with EAI_NONAME).
405 if (host
== NULL
&& (flags
& NI_NAMEREQD
) == 0)
407 char tmp
[INET6_ADDRSTRLEN
+ 1 + IF_NAMESIZE
+ 1];
409 if (sa
->sa_family
== AF_INET
)
411 char buf
[INET_ADDRSTRLEN
];
412 if (inet_ntop(AF_INET
, &a4
, buf
, sizeof(buf
)) != 0)
417 else if (sa
->sa_family
== AF_INET6
)
419 char buf
[INET6_ADDRSTRLEN
];
421 /* zero the embedded scope ID */
424 a6
.__u6_addr
.__u6_addr16
[1] = 0;
427 if (inet_ntop(AF_INET6
, &a6
, buf
, sizeof(buf
)) != 0)
431 char ifname
[IF_NAMESIZE
];
432 if (if_indextoname(ifnum
, ifname
) != NULL
)
434 asprintf(&host
, "%s%%%s", buf
, ifname
);
439 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAIL
;
451 /* Return numeric service name for NI_NUMERICSERV or if lookup failed. */
454 asprintf(&serv
, "%hu", ntohs(port
));
457 if ((host
== NULL
) || (serv
== NULL
))
461 if ((flags
& NI_NAMEREQD
) != 0)
463 *err
= SI_STATUS_EAI_NONAME
;
467 *err
= SI_STATUS_EAI_MEMORY
;
473 out
= (si_item_t
*)LI_ils_create("L4488ss", (unsigned long)si
, CATEGORY_NAMEINFO
, 1, unused
, unused
, host
, serv
);
482 _gai_numericserv(const char *serv
, uint16_t *port
)
497 num
= strtol(serv
, &endptr
, 10);
498 if ((serv
[0] != '\0') && (*endptr
== '\0') && (num
>= 0) && (num
<= UINT16_MAX
))
501 if (port
!= NULL
) *port
= (uint16_t)num
;
508 __private_extern__
int
509 _gai_serv_to_port(const char *serv
, uint32_t proto
, uint16_t *port
)
513 const char *protoname
= NULL
;
515 if (_gai_numericserv(serv
, port
)) return 0;
517 if (proto
== IPPROTO_UDP
) protoname
= "udp";
518 if (proto
== IPPROTO_TCP
) protoname
= "tcp";
520 item
= si_service_byname(si_search(), serv
, protoname
);
521 if (item
== NULL
) return -1;
523 s
= (struct servent
*)((uintptr_t)item
+ sizeof(si_item_t
));
524 if (port
) *port
= ntohs(s
->s_port
);
525 si_item_release(item
);
530 __private_extern__ si_item_t
*
531 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
)
533 socket_data_t sockdata
;
534 struct sockaddr_in
*sa
;
539 len
= sizeof(struct sockaddr_in
);
540 memset(&sockdata
, 0, sizeof(socket_data_t
));
541 sa
= (struct sockaddr_in
*)&sockdata
;
544 sa
->sin_family
= AF_INET
;
545 sa
->sin_port
= htons(port
);
546 memcpy(&sa
->sin_addr
, addr
, sizeof(sa
->sin_addr
));
548 /* Kludge: Jam the interface number into sin_zero (4 bytes). */
550 memmove(sa
->sin_zero
, &v32
, sizeof(uint32_t));
552 return (si_item_t
*)LI_ils_create("L448844444Ss", (unsigned long)si
, CATEGORY_ADDRINFO
, 1, unused
, unused
, flags
, AF_INET
, sock
, proto
, len
, sockdata
, cname
);
555 __private_extern__ si_item_t
*
556 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
)
558 socket_data_t sockdata
;
559 struct sockaddr_in6
*sa
;
564 len
= sizeof(struct sockaddr_in6
);
565 memset(&sockdata
, 0, sizeof(socket_data_t
));
566 sa
= (struct sockaddr_in6
*)&sockdata
;
569 sa
->sin6_family
= AF_INET6
;
570 sa
->sin6_port
= htons(port
);
571 memcpy(&sa
->sin6_addr
, addr
, sizeof(sa
->sin6_addr
));
573 /* sin6_scope_id is in host byte order */
574 sa
->sin6_scope_id
= iface
;
576 if (IN6_IS_ADDR_LINKLOCAL(&sa
->sin6_addr
))
578 /* check for embedded scopeid */
579 uint16_t esid
= ntohs(sa
->sin6_addr
.__u6_addr
.__u6_addr16
[1]);
582 sa
->sin6_addr
.__u6_addr
.__u6_addr16
[1] = 0;
583 if (iface
== 0) sa
->sin6_scope_id
= esid
;
587 return (si_item_t
*)LI_ils_create("L448844444Ss", (unsigned long)si
, CATEGORY_ADDRINFO
, 1, unused
, unused
, flags
, AF_INET6
, sock
, proto
, len
, sockdata
, cname
);
590 __private_extern__ si_list_t
*
591 si_addrinfo_list(si_mod_t
*si
, int socktype
, int proto
, struct in_addr
*a4
, struct in6_addr
*a6
, int port
, int scopeid
, const char *cname4
, const char *cname6
)
593 si_item_t
*item
= NULL
;
594 si_list_t
*out4
= NULL
, *out6
= NULL
;
597 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_UDP
))
599 item
= si_addrinfo_v4(si
, 0, SOCK_DGRAM
, IPPROTO_UDP
, port
, a4
, 0, cname4
);
600 out4
= si_list_add(out4
, item
);
601 si_item_release(item
);
604 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_TCP
))
606 item
= si_addrinfo_v4(si
, 0, SOCK_STREAM
, IPPROTO_TCP
, port
, a4
, 0, cname4
);
607 out4
= si_list_add(out4
, item
);
608 si_item_release(item
);
611 if (proto
== IPPROTO_ICMP
)
613 item
= si_addrinfo_v4(si
, 0, SOCK_RAW
, IPPROTO_ICMP
, port
, a4
, 0, cname4
);
614 out4
= si_list_add(out4
, item
);
615 si_item_release(item
);
621 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_UDP
))
623 item
= si_addrinfo_v6(si
, 0, SOCK_DGRAM
, IPPROTO_UDP
, port
, a6
, scopeid
, cname6
);
624 out6
= si_list_add(out6
, item
);
625 si_item_release(item
);
628 if ((proto
== IPPROTO_UNSPEC
) || (proto
== IPPROTO_TCP
))
630 item
= si_addrinfo_v6(si
, 0, SOCK_STREAM
, IPPROTO_TCP
, port
, a6
, scopeid
, cname6
);
631 out6
= si_list_add(out6
, item
);
632 si_item_release(item
);
635 if (proto
== IPPROTO_ICMPV6
)
637 item
= si_addrinfo_v6(si
, 0, SOCK_RAW
, IPPROTO_ICMPV6
, port
, a6
, scopeid
, cname6
);
638 out6
= si_list_add(out6
, item
);
639 si_item_release(item
);
643 out6
= si_list_concat(out6
, out4
);
644 si_list_release(out4
);
651 * Determines whether the given host name is a numeric IPv4 or IPv6 address,
652 * based on the address family input value. If the input addres family is
653 * unspecified, a more specific value will be provided on output if possible.
654 * Returns 1 if host name is numeric or 0 if not, or -1 on error.
657 _gai_numerichost(const char* nodename
, uint32_t *family
, int flags
, struct in_addr
*a4
, struct in6_addr
*a6
)
659 int numerichost
, passive
;
663 if (nodename
== NULL
)
665 /* return loopback or passive addresses */
666 passive
= (flags
& AI_PASSIVE
);
668 if ((*family
== AF_UNSPEC
) || (*family
== AF_INET
))
670 if (passive
) a4
->s_addr
= 0;
671 else a4
->s_addr
= htonl(INADDR_LOOPBACK
);
674 if ((*family
== AF_UNSPEC
) || (*family
== AF_INET6
))
676 memset(a6
, 0, sizeof(*a6
));
677 if (!passive
) a6
->__u6_addr
.__u6_addr32
[3] = htonl(1);
684 /* numeric IPv4 host valid for AF_UNSPEC and AF_INET */
685 numerichost
= inet_pton(AF_INET
, nodename
, a4
);
686 if (numerichost
== 1)
688 if (*family
== AF_UNSPEC
) *family
= AF_INET
;
689 else if (*family
== AF_INET6
) numerichost
= -1;
693 /* numeric IPv6 host valid for AF_UNSPEC and AF_INET6 */
694 numerichost
= inet_pton(AF_INET6
, nodename
, a6
);
695 if (numerichost
== 1)
697 if (*family
== AF_UNSPEC
) *family
= AF_INET6
;
698 else if (*family
== AF_INET
) numerichost
= -1;
706 /* si_addrinfo_list_from_hostent
707 * Returns an addrinfo list from IPv4 and IPv6 hostent entries
709 __private_extern__ si_list_t
*
710 si_addrinfo_list_from_hostent(si_mod_t
*si
, uint32_t socktype
, uint32_t proto
, uint16_t port
, uint16_t scope
, const struct hostent
*h4
, const struct hostent
*h6
)
713 si_list_t
*out
= NULL
;
716 if ((h6
!= NULL
) && (h6
->h_addr_list
!= NULL
))
718 for (i
= 0; h6
->h_addr_list
[i
] != NULL
; i
++)
721 memcpy(&a6
, h6
->h_addr_list
[i
], h6
->h_length
);
722 list
= si_addrinfo_list(si
, socktype
, proto
, NULL
, &a6
, port
, scope
, NULL
, h6
->h_name
);
723 out
= si_list_concat(out
, list
);
724 si_list_release(list
);
728 if ((h4
!= NULL
) && (h4
->h_addr_list
!= NULL
))
730 for (i
= 0; h4
->h_addr_list
[i
] != NULL
; i
++)
733 memcpy(&a4
, h4
->h_addr_list
[i
], h4
->h_length
);
734 list
= si_addrinfo_list(si
, socktype
, proto
, &a4
, NULL
, port
, 0, h4
->h_name
, NULL
);
735 out
= si_list_concat(out
, list
);
736 si_list_release(list
);
744 _gai_sa_dst_compare(const struct sockaddr
*a
, const struct sockaddr
*b
, int unused
)
746 int ta
, tb
, posn_a
, posn_b
;
748 ta
= _si_net_addr_type(a
);
749 tb
= _si_net_addr_type(b
);
754 if (ta
& NET_TYPE_V4
) posn_a
= 1;
755 if (tb
& NET_TYPE_V4
) posn_b
= 1;
757 if (_si_net_type_is_IPv6_globably_reachable_non_transitional(ta
)) posn_a
= 0;
758 if (_si_net_type_is_IPv6_globably_reachable_non_transitional(tb
)) posn_b
= 0;
760 return (posn_a
- posn_b
);
764 _gai_addr_sort(void *thunk
, const void *a
, const void *b
)
766 si_item_t
**ia
, **ib
;
767 si_addrinfo_t
*p
, *q
;
768 struct sockaddr
*sp
, *sq
;
769 uint32_t *v4first
= (uint32_t *)thunk
;
771 ia
= (si_item_t
**)a
;
772 ib
= (si_item_t
**)b
;
774 p
= (si_addrinfo_t
*)((uintptr_t)*ia
+ sizeof(si_item_t
));
775 q
= (si_addrinfo_t
*)((uintptr_t)*ib
+ sizeof(si_item_t
));
777 sp
= (struct sockaddr
*)p
->ai_addr
.x
;
778 sq
= (struct sockaddr
*)q
->ai_addr
.x
;
782 if (sp
->sa_family
== sq
->sa_family
) return 0;
783 if (sp
->sa_family
== AF_INET
) return -1;
787 return _gai_sa_dst_compare(sp
, sq
, 0);
791 _gai_sort_list(si_list_t
*in
)
796 if (in
== NULL
) return NULL
;
798 memset(&x
, 0, sizeof(config_stats_t
));
799 if (_si_netconfig(&x
) < 0) return in
;
802 if (x
.gai_v6_preferred
!= 0) v4first
= 0;
804 qsort_r(&in
->entry
[0], in
->count
, sizeof(si_item_t
*), (void *)&v4first
, _gai_addr_sort
);
809 * Simple lookup via gethostbyname2(3) mechanism.
811 __private_extern__ si_list_t
*
812 _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
)
814 si_item_t
*h4_item
= NULL
, *h6_item
= NULL
;
815 struct hostent
*h4
= NULL
, *h6
= NULL
;
816 si_list_t
*out
= NULL
;
819 if ((flags
& AI_NUMERICSERV
) != 0)
821 port
= *(uint16_t*)servptr
;
825 if (_gai_serv_to_port(servptr
, proto
, &port
) != 0)
827 if (err
) *err
= SI_STATUS_EAI_NONAME
;
832 if ((flags
& AI_NUMERICHOST
) != 0)
834 if (family
== AF_INET
)
836 h4_item
= si_host_byaddr(si
, nodeptr
, AF_INET
, interface
, NULL
);
838 else if (family
== AF_INET6
)
840 h6_item
= si_host_byaddr(si
, nodeptr
, AF_INET6
, interface
, NULL
);
845 if ((family
== AF_INET
) || (family
== AF_UNSPEC
))
847 h4_item
= si_host_byname(si
, nodeptr
, AF_INET
, interface
, NULL
);
850 if ((family
== AF_INET6
) || (family
== AF_UNSPEC
))
852 h6_item
= si_host_byname(si
, nodeptr
, AF_INET6
, interface
, NULL
);
858 h4
= (struct hostent
*)((uintptr_t)h4_item
+ sizeof(si_item_t
));
863 h6
= (struct hostent
*)((uintptr_t)h6_item
+ sizeof(si_item_t
));
866 out
= si_addrinfo_list_from_hostent(si
, socktype
, proto
, port
, 0, h4
, h6
);
867 si_item_release(h4_item
);
868 si_item_release(h6_item
);
870 return _gai_sort_list(out
);
873 __private_extern__ si_list_t
*
874 si_srv_byname(si_mod_t
*si
, const char *qname
, const char *interface
, uint32_t *err
)
876 if (si
== NULL
) return 0;
877 if (si
->sim_srv_byname
== NULL
) return 0;
879 return si
->sim_srv_byname(si
, qname
, interface
, err
);
882 __private_extern__
int
883 si_wants_addrinfo(si_mod_t
*si
)
885 if (si
== NULL
) return 0;
886 if (si
->sim_addrinfo
== NULL
) return 0;
887 if (si
->sim_wants_addrinfo
== NULL
) return 0;
889 return si
->sim_wants_addrinfo(si
);
893 _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
)
900 si_list_t
*list
= NULL
;
901 si_list_t
*result
= NULL
;
903 /* Minimum SRV priority is zero. Start below that. */
907 if (node
== NULL
|| serv
== NULL
) return NULL
;
909 asprintf(&qname
, "%s.%s", serv
, node
);
910 list
= si_srv_byname(si
, qname
, interface
, err
);
913 /* Iterate the SRV records starting at lowest priority and attempt to
914 * lookup the target host name. Returns the first successful lookup.
915 * It's an O(n^2) algorithm but data sets are small (less than 100) and
916 * sorting overhead is dwarfed by network I/O for each element.
918 while (list
!= NULL
&& result
== NULL
)
920 /* Find the next lowest priority level. */
921 /* Maximum SRV priority is UINT16_MAX. Start above that. */
924 for (i
= 0; i
< list
->count
; ++i
)
926 item
= list
->entry
[i
];
927 srv
= (si_srv_t
*)((uintptr_t)item
+ sizeof(si_item_t
));
929 if (srv
->priority
> lastprio
&& srv
->priority
< currprio
)
931 currprio
= srv
->priority
;
935 if (currprio
== INT_MAX
)
937 /* All priorities have been evaluated. Done. */
945 /* Lookup hosts at the current priority level. Return first match. */
946 for (i
= 0; i
< list
->count
; ++i
)
948 item
= list
->entry
[i
];
949 srv
= (si_srv_t
*)((uintptr_t)item
+ sizeof(si_item_t
));
951 if (srv
->priority
== currprio
)
953 /* So that _gai_simple expects an integer service. */
954 flags
|= AI_NUMERICSERV
;
956 result
= _gai_simple(si
, srv
->target
, &srv
->port
, family
, socktype
, proto
, flags
, interface
, err
);
967 si_list_release(list
);
973 __private_extern__ si_list_t
*
974 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
)
976 int numerichost
, numericserv
= 0;
977 const void *nodeptr
= NULL
, *servptr
= NULL
;
979 struct in_addr a4
, *p4
;
980 struct in6_addr a6
, *p6
;
984 if (err
!= NULL
) *err
= SI_STATUS_NO_ERROR
;
988 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAIL
;
992 /* treat empty strings as NULL */
993 if ((node
!= NULL
) && (node
[0] == '\0')) node
= NULL
;
994 if ((serv
!= NULL
) && (serv
[0] == '\0')) serv
= NULL
;
996 /* make sure we have a query */
997 if ((node
== NULL
) && (serv
== NULL
))
999 if (err
!= NULL
) *err
= SI_STATUS_EAI_NONAME
;
1003 /* verify family is supported */
1011 if (err
!= NULL
) *err
= SI_STATUS_EAI_FAMILY
;
1015 /* verify socket type is supported */
1024 if (err
!= NULL
) *err
= SI_STATUS_EAI_BADHINTS
;
1028 /* verify protocol is supported */
1031 case IPPROTO_UNSPEC
:
1035 case IPPROTO_ICMPV6
:
1038 if (err
!= NULL
) *err
= SI_STATUS_EAI_BADHINTS
;
1042 /* verify socket type compatible with protocol */
1043 if (((socktype
== SOCK_DGRAM
) && (proto
!= IPPROTO_UNSPEC
) && (proto
!= IPPROTO_UDP
)) || ((socktype
== SOCK_STREAM
) && (proto
!= IPPROTO_UNSPEC
) && (proto
!= IPPROTO_TCP
)))
1045 if (err
!= NULL
) *err
= SI_STATUS_EAI_BADHINTS
;
1049 memset(&a4
, 0, sizeof(struct in_addr
));
1050 memset(&a6
, 0, sizeof(struct in6_addr
));
1052 /* determine the protocol if possible */
1053 if ((proto
== IPPROTO_UNSPEC
) && (socktype
== SOCK_DGRAM
)) proto
= IPPROTO_UDP
;
1054 if ((proto
== IPPROTO_UNSPEC
) && (socktype
== SOCK_STREAM
)) proto
= IPPROTO_TCP
;
1058 if ((flags
& AI_SRV
) != 0)
1061 out
= _gai_srv(si
, node
, serv
, family
, socktype
, proto
, flags
, interface
, err
);
1062 return _gai_sort_list(out
);
1066 /* Usual service lookup */
1067 numericserv
= _gai_numericserv(serv
, &port
);
1070 if ((flags
& AI_NUMERICSERV
) && (numericserv
== 0))
1072 /* FreeBSD returns SI_STATUS_EAI_SERVICE */
1073 if (err
!= NULL
) *err
= SI_STATUS_EAI_NONAME
;
1079 if (numericserv
== 1)
1081 flags
|= AI_NUMERICSERV
;
1090 numerichost
= _gai_numerichost(node
, &family
, flags
, &a4
, &a6
);
1091 if ((numerichost
== -1) || ((flags
& AI_NUMERICHOST
) && (numerichost
== 0)))
1093 if (err
!= NULL
) *err
= SI_STATUS_EAI_NONAME
;
1097 if (numerichost
== 1)
1099 flags
|= AI_NUMERICHOST
;
1100 if (family
== AF_INET
)
1104 else if (family
== AF_INET6
)
1114 if ((numerichost
== 1) && (numericserv
== 0))
1116 /* only service lookup needed. convert to port and perform a trivial getaddrinfo */
1117 if (_gai_serv_to_port(serv
, proto
, &port
) != 0)
1119 if (err
!= NULL
) *err
= SI_STATUS_EAI_NONAME
;
1124 flags
|= AI_NUMERICSERV
;
1130 if ((numerichost
== 1) && (numericserv
== 1))
1136 if (family
== AF_INET
) p6
= NULL
;
1137 if (family
== AF_INET6
) p4
= NULL
;
1138 if (node
== NULL
) cname
= "localhost";
1140 /* handle trivial questions */
1141 out
= si_addrinfo_list(si
, socktype
, proto
, p4
, p6
, port
, 0, cname
, cname
);
1142 return _gai_sort_list(out
);
1144 else if ((si
->sim_wants_addrinfo
!= NULL
) && si
->sim_wants_addrinfo(si
))
1146 /* or let the current module handle the host lookups intelligently */
1147 out
= si
->sim_addrinfo(si
, nodeptr
, servptr
, family
, socktype
, proto
, flags
, interface
, err
);
1148 return _gai_sort_list(out
);
1151 /* fall back to a default path */
1152 out
= _gai_simple(si
, nodeptr
, servptr
, family
, socktype
, proto
, flags
, interface
, err
);
1153 return _gai_sort_list(out
);
1156 static struct addrinfo
*
1157 si_item_to_addrinfo(si_item_t
*item
)
1160 struct addrinfo
*out
;
1162 if (item
== NULL
) return NULL
;
1164 a
= (si_addrinfo_t
*)((uintptr_t)item
+ sizeof(si_item_t
));
1166 out
= (struct addrinfo
*)calloc(1, sizeof(struct addrinfo
));
1167 if (out
== NULL
) return NULL
;
1169 out
->ai_flags
= a
->ai_flags
;
1170 out
->ai_family
= a
->ai_family
;
1171 out
->ai_socktype
= a
->ai_socktype
;
1172 out
->ai_protocol
= a
->ai_protocol
;
1173 out
->ai_addrlen
= a
->ai_addrlen
;
1175 out
->ai_addr
= (struct sockaddr
*)calloc(1, out
->ai_addrlen
);
1176 if (out
->ai_addr
== NULL
)
1182 memcpy(out
->ai_addr
, a
->ai_addr
.x
, out
->ai_addrlen
);
1184 if (a
->ai_canonname
!= NULL
)
1186 out
->ai_canonname
= strdup(a
->ai_canonname
);
1187 if (out
->ai_canonname
== NULL
)
1197 __private_extern__
struct addrinfo
*
1198 si_list_to_addrinfo(si_list_t
*list
)
1200 struct addrinfo
*tail
, *out
;
1203 if (list
== NULL
) return NULL
;
1204 if (list
->count
== 0) return NULL
;
1206 i
= list
->count
- 1;
1208 out
= si_item_to_addrinfo(list
->entry
[i
]);
1211 for (i
--; i
>= 0; i
--)
1213 out
= si_item_to_addrinfo(list
->entry
[i
]);
1220 out
->ai_next
= tail
;
1227 /* getipnodebyname */
1230 make_hostent(si_mod_t
*si
, const char *name
, struct in_addr addr
)
1236 if (name
== NULL
) return NULL
;
1240 addrs
[0] = (char *)&(addr
.s_addr
);
1244 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
);
1248 make_hostent6(si_mod_t
*si
, const char *name
, struct in6_addr addr
)
1254 if (name
== NULL
) return NULL
;
1258 addrs
[0] = (char *)&(addr
.__u6_addr
.__u6_addr32
[0]);
1262 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
);
1266 lower_case(const char *s
)
1271 if (s
== NULL
) return NULL
;
1273 t
= malloc(strlen(s
) + 1);
1275 for (i
= 0; s
[i
] != '\0'; i
++)
1277 if ((s
[i
] >= 'A') && (s
[i
] <= 'Z')) t
[i
] = s
[i
] + 32;
1287 merge_alias(const char *name
, build_hostent_t
*h
)
1291 if (name
== NULL
) return 0;
1292 if (h
== NULL
) return 0;
1294 if ((h
->host
.h_name
!= NULL
) && (string_equal(name
, h
->host
.h_name
))) return 0;
1296 for (i
= 0; i
< h
->alias_count
; i
++)
1298 if (string_equal(name
, h
->host
.h_aliases
[i
])) return 0;
1301 if (h
->alias_count
== 0) h
->host
.h_aliases
= (char **)calloc(2, sizeof(char *));
1302 else h
->host
.h_aliases
= (char **)reallocf(h
->host
.h_aliases
, (h
->alias_count
+ 2) * sizeof(char *));
1304 if (h
->host
.h_aliases
== NULL
)
1310 h
->host
.h_aliases
[h
->alias_count
] = lower_case(name
);
1312 h
->host
.h_aliases
[h
->alias_count
] = NULL
;
1318 append_addr(const char *addr
, uint32_t len
, build_hostent_t
*h
)
1320 if (addr
== NULL
) return 0;
1321 if (h
== NULL
) return 0;
1323 if (h
->addr_count
== 0) h
->host
.h_addr_list
= (char **)calloc(2, sizeof(char *));
1324 else h
->host
.h_addr_list
= (char **)reallocf(h
->host
.h_addr_list
, (h
->addr_count
+ 2) * sizeof(char *));
1326 if (h
->host
.h_addr_list
== NULL
)
1332 h
->host
.h_addr_list
[h
->addr_count
] = malloc(len
);
1333 if (h
->host
.h_addr_list
[h
->addr_count
] == NULL
) return -1;
1335 memcpy(h
->host
.h_addr_list
[h
->addr_count
], addr
, len
);
1337 h
->host
.h_addr_list
[h
->addr_count
] = NULL
;
1343 free_build_hostent(build_hostent_t
*h
)
1348 if (h
== NULL
) return;
1350 if (h
->host
.h_name
!= NULL
) free(h
->host
.h_name
);
1351 h
->host
.h_name
= NULL
;
1353 aliases
= h
->host
.h_aliases
;
1354 if (aliases
!= NULL
)
1356 while (*aliases
!= NULL
) free(*aliases
++);
1357 free(h
->host
.h_aliases
);
1360 h
->host
.h_aliases
= NULL
;
1362 if (h
->host
.h_addr_list
!= NULL
)
1364 for (i
= 0; h
->host
.h_addr_list
[i
] != NULL
; i
++) free(h
->host
.h_addr_list
[i
]);
1365 free(h
->host
.h_addr_list
);
1368 h
->host
.h_addr_list
= NULL
;
1372 __private_extern__ si_item_t
*
1373 si_ipnode_byname(si_mod_t
*si
, const char *name
, int family
, int flags
, const char *interface
, uint32_t *err
)
1375 int i
, status
, want
;
1376 struct in_addr addr4
;
1377 struct in6_addr addr6
;
1378 si_item_t
*item4
, *item6
;
1379 build_hostent_t
*out
;
1382 config_stats_t ifstats
;
1384 memset(&addr4
, 0, sizeof(struct in_addr
));
1385 memset(&addr6
, 0, sizeof(struct in6_addr
));
1387 if (err
!= NULL
) *err
= 0;
1389 if (family
== AF_INET
)
1391 status
= inet_aton(name
, &addr4
);
1394 /* create a host entry */
1395 item4
= make_hostent(si
, name
, addr4
);
1398 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1405 else if (family
== AF_INET6
)
1407 status
= inet_pton(family
, name
, &addr6
);
1410 /* create a host entry */
1411 item6
= make_hostent6(si
, name
, addr6
);
1414 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1421 status
= inet_aton(name
, &addr4
);
1424 if (!(flags
& (AI_V4MAPPED
| AI_V4MAPPED_CFG
)))
1426 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
1430 addr6
.__u6_addr
.__u6_addr32
[0] = 0x00000000;
1431 addr6
.__u6_addr
.__u6_addr32
[1] = 0x00000000;
1432 addr6
.__u6_addr
.__u6_addr32
[2] = htonl(0x0000ffff);
1433 memmove(&(addr6
.__u6_addr
.__u6_addr32
[3]), &(addr4
.s_addr
), IPV4_ADDR_LEN
);
1435 /* create a host entry */
1436 item6
= make_hostent6(si
, name
, addr6
);
1439 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1448 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1453 * IF AI_ADDRCONFIG is set, we need to know what interface flavors we really have.
1456 memset(&ifstats
, 0, sizeof(config_stats_t
));
1458 if (flags
& AI_ADDRCONFIG
)
1460 if (_si_netconfig(&ifstats
) < 0)
1462 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1466 /* Bail out if there are no interfaces */
1467 if ((ifstats
.v4_count
== 0) && (ifstats
.v6_count
== 0))
1469 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1475 * Figure out what we want.
1476 * If user asked for AF_INET, we only want V4 addresses.
1478 want
= WANT_A4_ONLY
;
1480 if (family
== AF_INET
)
1482 if ((flags
& AI_ADDRCONFIG
) && (ifstats
.v4_count
== 0))
1484 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1488 else if (family
== AF_INET6
)
1490 /* family == AF_INET6 */
1491 want
= WANT_A6_ONLY
;
1493 if (flags
& (AI_V4MAPPED
| AI_V4MAPPED_CFG
))
1497 want
= WANT_A6_PLUS_MAPPED_A4
;
1501 want
= WANT_A6_OR_MAPPED_A4_IF_NO_A6
;
1506 if ((flags
& AI_ADDRCONFIG
) && (ifstats
.v6_count
== 0))
1508 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1515 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1522 /* fetch IPv6 data if required */
1523 if ((want
== WANT_A6_ONLY
) || (want
== WANT_A6_OR_MAPPED_A4_IF_NO_A6
) || (want
== WANT_A6_PLUS_MAPPED_A4
))
1525 item6
= si_host_byname(si
, name
, AF_INET6
, interface
, (uint32_t *)err
);
1528 /* fetch IPv4 data if required */
1529 if ((want
== WANT_A4_ONLY
) || (want
== WANT_A6_PLUS_MAPPED_A4
) || ((want
== WANT_A6_OR_MAPPED_A4_IF_NO_A6
) && (item6
== NULL
)))
1531 item4
= si_host_byname(si
, name
, AF_INET
, interface
, (uint32_t *)err
);
1534 if (want
== WANT_A4_ONLY
)
1536 si_item_release(item6
);
1537 if ((item4
== NULL
) && (err
!= NULL
)) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
1541 if (want
== WANT_A6_ONLY
)
1543 si_item_release(item4
);
1544 if ((item6
== NULL
) && (err
!= NULL
)) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
1548 if ((item6
== NULL
) && (item4
== NULL
))
1550 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_HOST_NOT_FOUND
;
1554 /* output item will have IPv6 + mapped IPv4 addresses */
1556 out
= (build_hostent_t
*)calloc(1, sizeof(build_hostent_t
));
1559 si_item_release(item4
);
1560 si_item_release(item6
);
1561 if (err
!= NULL
) *err
= SI_STATUS_H_ERRNO_NO_RECOVERY
;
1567 h
= (struct hostent
*)((uintptr_t)item4
+ sizeof(si_item_t
));
1569 out
->host
.h_name
= lower_case(h
->h_name
);
1571 if (h
->h_aliases
!= NULL
)
1573 for (i
= 0; h
->h_aliases
[i
] != NULL
; i
++) merge_alias(h
->h_aliases
[i
], out
);
1576 for (i
= 0; h
->h_addr_list
[i
] != 0; i
++)
1578 addr6
.__u6_addr
.__u6_addr32
[0] = 0x00000000;
1579 addr6
.__u6_addr
.__u6_addr32
[1] = 0x00000000;
1580 addr6
.__u6_addr
.__u6_addr32
[2] = htonl(0x0000ffff);
1581 memmove(&(addr6
.__u6_addr
.__u6_addr32
[3]), h
->h_addr_list
[i
], IPV4_ADDR_LEN
);
1582 append_addr((const char *)&addr6
, IPV6_ADDR_LEN
, out
);
1588 h
= (struct hostent
*)((uintptr_t)item6
+ sizeof(si_item_t
));
1590 if (out
->host
.h_name
== NULL
) out
->host
.h_name
= lower_case(h
->h_name
);
1592 if (h
->h_aliases
!= NULL
)
1594 for (i
= 0; h
->h_aliases
[i
] != NULL
; i
++) merge_alias(h
->h_aliases
[i
], out
);
1597 for (i
= 0; h
->h_addr_list
[i
] != 0; i
++) append_addr(h
->h_addr_list
[i
], IPV6_ADDR_LEN
, out
);
1600 si_item_release(item4
);
1601 si_item_release(item6
);
1605 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
);
1607 free_build_hostent(out
);