2 * Copyright (c) 2003-2016 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 * Modification History
27 * April 12, 2011 Allan Nathanson <ajn@apple.com>
28 * - add SCNetworkReachability "server"
30 * March 31, 2004 Allan Nathanson <ajn@apple.com>
31 * - use [SC] DNS configuration information
33 * January 19, 2003 Allan Nathanson <ajn@apple.com>
34 * - add advanced reachability APIs
37 #include <Availability.h>
38 #include <TargetConditionals.h>
39 #include <sys/cdefs.h>
40 #include <dispatch/dispatch.h>
41 #include <dispatch/private.h>
43 #include <libkern/OSAtomic.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
51 #include <sys/ioctl.h>
52 #include <sys/socket.h>
54 #include <net/if_dl.h>
55 #include <net/if_types.h>
56 #include <net/network_agent.h>
58 #include <CoreFoundation/CoreFoundation.h>
59 #include <CoreFoundation/CFRuntime.h>
61 #define SC_LOG_HANDLE __log_SCNetworkReachability()
62 #include <SystemConfiguration/SystemConfiguration.h>
63 #include <SystemConfiguration/SCValidation.h>
64 #include <SystemConfiguration/SCPrivate.h>
66 #include "SCNetworkReachabilityInternal.h"
71 #include <network/private.h>
73 #define DEBUG_REACHABILITY_TYPE_NAME "create w/name"
74 #define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
76 #define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"
77 #define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
79 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"
80 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
82 #define DEBUG_REACHABILITY_TYPE_PTR "create w/ptr"
83 #define DEBUG_REACHABILITY_TYPE_PTR_OPTIONS " + options"
85 static pthread_mutexattr_t lock_attr
;
87 #define MUTEX_INIT(m) { \
88 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \
92 #define MUTEX_LOCK(m) { \
93 int _lock_ = (pthread_mutex_lock(m) == 0); \
97 #define MUTEX_UNLOCK(m) { \
98 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
102 #define MUTEX_ASSERT_HELD(m) { \
103 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \
110 #define REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN "NetworkExtension"
111 #define REACHABILITY_AGENT_DATA_KEY "data"
114 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
115 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
117 static SCNetworkReachabilityFlags
118 __SCNetworkReachabilityGetFlagsFromPath(nw_path_t path
,
119 ReachabilityAddressType type
,
120 nw_resolver_status_t resolverStatus
,
121 nw_array_t resolvedEndpoints
,
122 Boolean resolvedEndpointUseFlags
,
123 SCNetworkReachabilityFlags resolvedEndpointFlags
);
126 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
127 dispatch_queue_t queue
);
129 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
132 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
134 "SCNetworkReachability", // className
137 __SCNetworkReachabilityDeallocate
, // dealloc
140 NULL
, // copyFormattingDesc
141 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
145 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
147 static dispatch_queue_t
150 static dispatch_once_t once
;
151 static dispatch_queue_t q
;
153 dispatch_once(&once
, ^{
154 q
= dispatch_queue_create("SCNetworkReachability.callback", NULL
);
161 __log_SCNetworkReachability()
163 static os_log_t log
= NULL
;
166 log
= os_log_create("com.apple.SystemConfiguration", "SCNetworkReachability");
174 #pragma mark SCNetworkReachability APIs
177 static __inline__ CFTypeRef
178 isA_SCNetworkReachability(CFTypeRef obj
)
180 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
184 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target
)
186 CFAllocatorRef allocator
= CFGetAllocator(target
);
187 CFMutableStringRef str
;
188 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
190 str
= CFStringCreateMutable(allocator
, 0);
191 switch (targetPrivate
->type
) {
192 case reachabilityTypeAddress
:
193 case reachabilityTypeAddressPair
: {
195 if (targetPrivate
->localAddressEndpoint
!= NULL
) {
196 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->localAddressEndpoint
), buf
, sizeof(buf
));
197 CFStringAppendFormat(str
, NULL
, CFSTR("local address = %s"),
200 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
201 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
202 CFStringAppendFormat(str
, NULL
, CFSTR("%s%saddress = %s"),
203 targetPrivate
->localAddressEndpoint
? ", " : "",
204 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
207 CFStringAppendFormat(str
, NULL
, CFSTR("default path"));
211 case reachabilityTypeName
: {
212 CFStringAppendFormat(str
, NULL
, CFSTR("name = %s"), nw_endpoint_get_hostname(targetPrivate
->hostnameEndpoint
));
215 case reachabilityTypePTR
: {
217 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
218 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
219 CFStringAppendFormat(str
, NULL
, CFSTR("ptr = %s"),
226 if (targetPrivate
->parameters
!= NULL
) {
227 unsigned int if_index
;
229 if_index
= nw_parameters_get_required_interface_index(targetPrivate
->parameters
);
231 CFStringAppendFormat(str
, NULL
, CFSTR(", if_index = %u"), if_index
);
240 _SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target
)
242 CFAllocatorRef allocator
= CFGetAllocator(target
);
244 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
246 str
= CFStringCreateWithFormat(allocator
,
248 CFSTR("flags = 0x%08x, if_index = %u"),
249 __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
251 targetPrivate
->lastResolverStatus
,
252 targetPrivate
->lastResolvedEndpoints
,
253 targetPrivate
->lastResolvedEndpointHasFlags
,
254 targetPrivate
->lastResolvedEndpointFlags
),
255 targetPrivate
->lastResolvedEndpointHasFlags
? targetPrivate
->lastResolvedEndpointInterfaceIndex
256 : nw_path_get_interface_index(targetPrivate
->lastPath
));
262 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
264 CFAllocatorRef allocator
= CFGetAllocator(cf
);
265 CFMutableStringRef result
;
267 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
268 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
270 MUTEX_LOCK(&targetPrivate
->lock
);
272 result
= CFStringCreateMutable(allocator
, 0);
273 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
275 // add target description
276 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
277 CFStringAppend(result
, str
);
280 // add additional "name" info
281 if (isReachabilityTypeName(targetPrivate
->type
)) {
282 if (targetPrivate
->resolver
&& targetPrivate
->lastResolverStatus
== nw_resolver_status_invalid
) {
283 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
284 } else if (targetPrivate
->lastResolverStatus
!= nw_resolver_status_invalid
) {
285 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s"), (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) ? "complete" : "in progress");
286 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
287 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
288 nw_endpoint_t endpoint
= (nw_endpoint_t
)object
;
289 nw_endpoint_type_t endpoint_type
= nw_endpoint_get_type(endpoint
);
290 if (endpoint_type
== nw_endpoint_type_address
) {
292 const struct sockaddr
*sa
= nw_endpoint_get_address(endpoint
);
293 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
294 CFStringAppendFormat(result
, NULL
, CFSTR(", %s"), buf
);
295 } else if (endpoint_type
== nw_endpoint_type_host
) {
296 CFStringAppendFormat(result
, NULL
, CFSTR(", %s"), nw_endpoint_get_hostname(endpoint
));
298 CFStringAppendFormat(result
, NULL
, CFSTR(", unexpected nw_endpoint type: %d"), endpoint_type
);
303 CFStringAppendFormat(result
, NULL
, CFSTR(", no addresses"));
305 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
309 if (targetPrivate
->resolverBypass
) {
310 CFStringAppendFormat(result
, NULL
, CFSTR(", !resolve"));
314 if (targetPrivate
->scheduled
) {
315 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
316 CFStringAppendFormat(result
, NULL
, CFSTR(", %@"), str
);
320 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
322 MUTEX_UNLOCK(&targetPrivate
->lock
);
329 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
331 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
332 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
334 SC_log(LOG_DEBUG
, "%srelease", targetPrivate
->log_prefix
);
336 /* release resources */
337 MUTEX_LOCK(&targetPrivate
->lock
);
338 targetPrivate
->scheduled
= FALSE
;
340 if (targetPrivate
->hostnameEndpoint
) {
341 network_release(targetPrivate
->hostnameEndpoint
);
342 targetPrivate
->hostnameEndpoint
= NULL
;
344 if (targetPrivate
->localAddressEndpoint
) {
345 network_release(targetPrivate
->localAddressEndpoint
);
346 targetPrivate
->localAddressEndpoint
= NULL
;
348 if (targetPrivate
->remoteAddressEndpoint
) {
349 network_release(targetPrivate
->remoteAddressEndpoint
);
350 targetPrivate
->remoteAddressEndpoint
= NULL
;
352 if (targetPrivate
->parameters
) {
353 network_release(targetPrivate
->parameters
);
354 targetPrivate
->parameters
= NULL
;
356 if (targetPrivate
->lastPath
) {
357 network_release(targetPrivate
->lastPath
);
358 targetPrivate
->lastPath
= NULL
;
360 if (targetPrivate
->lastPathParameters
) {
361 network_release(targetPrivate
->lastPathParameters
);
362 targetPrivate
->lastPathParameters
= NULL
;
364 if (targetPrivate
->lastResolvedEndpoints
) {
365 network_release(targetPrivate
->lastResolvedEndpoints
);
366 targetPrivate
->lastResolvedEndpoints
= NULL
;
369 if (targetPrivate
->rlsContext
.release
!= NULL
) {
370 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
373 MUTEX_UNLOCK(&targetPrivate
->lock
);
375 pthread_mutex_destroy(&targetPrivate
->lock
);
382 __SCNetworkReachabilityInitialize(void)
384 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
386 pthread_mutexattr_init(&lock_attr
);
387 pthread_mutexattr_settype(&lock_attr
, PTHREAD_MUTEX_ERRORCHECK
);
392 static SCNetworkReachabilityPrivateRef
393 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
395 SCNetworkReachabilityPrivateRef targetPrivate
;
398 /* initialize runtime */
399 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
401 /* allocate target */
402 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
403 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
404 __kSCNetworkReachabilityTypeID
,
407 if (targetPrivate
== NULL
) {
411 /* initialize non-zero/NULL members */
412 MUTEX_INIT(&targetPrivate
->lock
);
414 snprintf(targetPrivate
->log_prefix
,
415 sizeof(targetPrivate
->log_prefix
),
420 return targetPrivate
;
424 static const struct sockaddr
*
425 is_valid_address(const struct sockaddr
*address
)
427 const struct sockaddr
*valid
= NULL
;
428 static Boolean warned
= FALSE
;
430 if ((address
!= NULL
) &&
431 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
432 switch (address
->sa_family
) {
434 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
438 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
440 sizeof(struct sockaddr_in
));
446 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
448 } else if (!warned
) {
449 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
451 sizeof(struct sockaddr_in6
));
457 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d",
468 __SCNetworkReachabilityAddressIsEmpty(const struct sockaddr
*address
)
470 if (address
== NULL
) {
474 if (address
->sa_family
== AF_INET
) {
475 return (((struct sockaddr_in
*)(void *)address
)->sin_addr
.s_addr
== 0);
476 } else if (address
->sa_family
== AF_INET6
) {
477 return IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6
*)(void *)address
)->sin6_addr
);
483 SCNetworkReachabilityRef
484 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
485 const struct sockaddr
*address
)
487 const struct sockaddr
*targetAddress
;
488 SCNetworkReachabilityPrivateRef targetPrivate
;
490 targetAddress
= is_valid_address(address
);
491 if (targetAddress
== NULL
) {
492 _SCErrorSet(kSCStatusInvalidArgument
);
496 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
497 if (targetPrivate
== NULL
) {
501 targetPrivate
->type
= reachabilityTypeAddress
;
503 if (!__SCNetworkReachabilityAddressIsEmpty(targetAddress
)) {
504 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(targetAddress
);
507 SC_log(LOG_DEBUG
, "%s%s %@",
508 targetPrivate
->log_prefix
,
509 DEBUG_REACHABILITY_TYPE_ADDRESS
,
512 return (SCNetworkReachabilityRef
)targetPrivate
;
517 is_ipv4_loopback(const struct sockaddr
*sa
)
520 struct sockaddr_in
*sin
= (struct sockaddr_in
*)(void *)sa
;
523 (sa
->sa_len
< sizeof(struct sockaddr_in
)) ||
524 (sa
->sa_family
!= AF_INET
)) {
528 addr
= ntohl(sin
->sin_addr
.s_addr
);
529 return IN_LOOPBACK(addr
) ? TRUE
: FALSE
;
534 is_same_address(const struct sockaddr
*a
, const struct sockaddr
*b
)
542 (a
->sa_family
!= b
->sa_family
) ||
543 (a
->sa_len
!= b
->sa_len
)) {
547 switch (a
->sa_family
) {
549 struct sockaddr_in
*a_sin
= (struct sockaddr_in
*)(void *)a
;
550 struct sockaddr_in
*b_sin
= (struct sockaddr_in
*)(void *)b
;
552 /* ALIGN: assuming a (and b) are aligned, then cast ok. */
553 a_addr
= &a_sin
->sin_addr
;
554 b_addr
= &b_sin
->sin_addr
;
555 len
= sizeof(struct in_addr
);
560 struct sockaddr_in6
*a_sin6
= (struct sockaddr_in6
*)(void *)a
;
561 struct sockaddr_in6
*b_sin6
= (struct sockaddr_in6
*)(void *)b
;
563 if (a_sin6
->sin6_scope_id
!= b_sin6
->sin6_scope_id
) {
567 a_addr
= &a_sin6
->sin6_addr
;
568 b_addr
= &b_sin6
->sin6_addr
;
569 len
= sizeof(struct in6_addr
);
580 return (bcmp(a_addr
, b_addr
, len
) == 0);
584 SCNetworkReachabilityRef
585 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
586 const struct sockaddr
*localAddress
,
587 const struct sockaddr
*remoteAddress
)
589 SCNetworkReachabilityPrivateRef targetPrivate
;
591 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
592 _SCErrorSet(kSCStatusInvalidArgument
);
596 if (localAddress
!= NULL
) {
597 localAddress
= is_valid_address(localAddress
);
598 if (localAddress
== NULL
) {
599 _SCErrorSet(kSCStatusInvalidArgument
);
604 if (remoteAddress
!= NULL
) {
605 remoteAddress
= is_valid_address(remoteAddress
);
606 if (remoteAddress
== NULL
) {
607 _SCErrorSet(kSCStatusInvalidArgument
);
612 #if !TARGET_OS_IPHONE
613 // Check/fix for loopback IP --> remote IP (rdar://26561383)
614 if ((localAddress
!= NULL
) && (remoteAddress
!= NULL
) &&
615 is_ipv4_loopback(localAddress
) && !is_ipv4_loopback(remoteAddress
)) {
616 static Boolean warned
= FALSE
;
619 SC_log(LOG_WARNING
, "BUG: SCNetworkReachabilityCreateWithAddressPair() called with local <loopback>");
620 SC_log(LOG_WARNING
, "address and remote <non-loopback> address. To return the expected (but actually");
621 SC_log(LOG_WARNING
, "incorrect) result, switched to SCNetworkReachabilityCreateWithAddress() with");
622 SC_log(LOG_WARNING
, "the remote address.");
626 return SCNetworkReachabilityCreateWithAddress(allocator
, remoteAddress
);
628 #endif // !TARGET_OS_IPHONE
630 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
631 if (targetPrivate
== NULL
) {
635 targetPrivate
->type
= reachabilityTypeAddressPair
;
637 if (localAddress
!= NULL
) {
638 targetPrivate
->localAddressEndpoint
= nw_endpoint_create_address(localAddress
);
641 if (remoteAddress
!= NULL
) {
642 if (is_same_address(localAddress
, remoteAddress
)) {
643 targetPrivate
->remoteAddressEndpoint
= network_retain(targetPrivate
->localAddressEndpoint
);
645 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(remoteAddress
);
649 targetPrivate
->parameters
= nw_parameters_create();
650 nw_parameters_set_local_address(targetPrivate
->parameters
, targetPrivate
->localAddressEndpoint
);
652 SC_log(LOG_DEBUG
, "%s%s %@",
653 targetPrivate
->log_prefix
,
654 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR
,
657 return (SCNetworkReachabilityRef
)targetPrivate
;
661 SCNetworkReachabilityRef
662 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
663 const char *nodename
)
667 struct sockaddr_in sin
;
668 struct sockaddr_in6 sin6
;
671 SCNetworkReachabilityPrivateRef targetPrivate
;
673 if (nodename
== NULL
) {
674 _SCErrorSet(kSCStatusInvalidArgument
);
678 nodenameLen
= strlen(nodename
);
679 if (nodenameLen
== 0) {
680 _SCErrorSet(kSCStatusInvalidArgument
);
684 if (_SC_string_to_sockaddr(nodename
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) != NULL
) {
685 /* if this "nodename" is really an IP[v6] address in disguise */
686 return SCNetworkReachabilityCreateWithAddress(allocator
, &addr
.sa
);
689 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
690 if (targetPrivate
== NULL
) {
694 targetPrivate
->type
= reachabilityTypeName
;
696 targetPrivate
->hostnameEndpoint
= nw_endpoint_create_host(nodename
, "0");
698 SC_log(LOG_DEBUG
, "%s%s %@",
699 targetPrivate
->log_prefix
,
700 DEBUG_REACHABILITY_TYPE_NAME
,
703 return (SCNetworkReachabilityRef
)targetPrivate
;
707 static SCNetworkReachabilityRef
708 __SCNetworkReachabilityCreateWithPTR(CFAllocatorRef allocator
,
709 const struct sockaddr
*ptrAddress
)
711 SCNetworkReachabilityPrivateRef targetPrivate
;
713 ptrAddress
= is_valid_address(ptrAddress
);
714 if (ptrAddress
== NULL
) {
715 _SCErrorSet(kSCStatusInvalidArgument
);
719 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
720 if (targetPrivate
== NULL
) {
724 targetPrivate
->type
= reachabilityTypePTR
;
726 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(ptrAddress
);
728 targetPrivate
->parameters
= nw_parameters_create();
729 nw_parameters_set_resolve_ptr(targetPrivate
->parameters
, TRUE
);
731 SC_log(LOG_DEBUG
, "%s%s %@",
732 targetPrivate
->log_prefix
,
733 DEBUG_REACHABILITY_TYPE_PTR
,
736 return (SCNetworkReachabilityRef
)targetPrivate
;
739 SCNetworkReachabilityRef
740 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
741 CFDictionaryRef options
)
743 const struct sockaddr
*addr_l
= NULL
;
744 const struct sockaddr
*addr_p
= NULL
;
745 const struct sockaddr
*addr_r
= NULL
;
747 Boolean haveOpt
= FALSE
;
748 CFStringRef interface
= NULL
;
749 CFStringRef nodename
;
750 CFBooleanRef resolverBypass
;
751 SCNetworkReachabilityRef target
;
752 SCNetworkReachabilityPrivateRef targetPrivate
;
753 unsigned int if_index
= 0;
754 char if_name
[IFNAMSIZ
];
756 if (!isA_CFDictionary(options
)) {
757 _SCErrorSet(kSCStatusInvalidArgument
);
761 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
762 if ((nodename
!= NULL
) &&
763 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
764 _SCErrorSet(kSCStatusInvalidArgument
);
767 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
769 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
770 _SCErrorSet(kSCStatusInvalidArgument
);
773 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
775 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionPTRAddress
);
777 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
778 _SCErrorSet(kSCStatusInvalidArgument
);
781 addr_p
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
783 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
785 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
786 _SCErrorSet(kSCStatusInvalidArgument
);
789 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
791 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
792 if ((interface
!= NULL
) &&
793 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
794 _SCErrorSet(kSCStatusInvalidArgument
);
797 resolverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionResolverBypass
);
798 if ((resolverBypass
!= NULL
) && !isA_CFBoolean(resolverBypass
)) {
799 _SCErrorSet(kSCStatusInvalidArgument
);
803 if (nodename
!= NULL
) {
806 if ((addr_l
!= NULL
) || (addr_r
!= NULL
) || (addr_p
!= NULL
)) {
807 // can't have both a nodename and an address
808 _SCErrorSet(kSCStatusInvalidArgument
);
812 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
813 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
814 CFAllocatorDeallocate(NULL
, (void *)name
);
815 } else if (addr_p
!= NULL
) {
816 if ((addr_l
!= NULL
) || // can't have PTR and target address
818 _SCErrorSet(kSCStatusInvalidArgument
);
822 target
= __SCNetworkReachabilityCreateWithPTR(NULL
, addr_p
);
824 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
825 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
826 } else if (addr_r
!= NULL
) {
827 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
828 } else if (addr_l
!= NULL
) {
829 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, NULL
);
831 _SCErrorSet(kSCStatusInvalidArgument
);
835 if (target
== NULL
) {
839 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
841 if (interface
!= NULL
) {
842 if ((_SC_cfstring_to_cstring(interface
,
845 kCFStringEncodingASCII
) == NULL
) ||
846 ((if_index
= if_nametoindex(if_name
)) == 0)) {
847 CFRelease(targetPrivate
);
848 _SCErrorSet(kSCStatusInvalidArgument
);
853 if (targetPrivate
->parameters
== NULL
) {
854 targetPrivate
->parameters
= nw_parameters_create();
858 nw_interface_t interfaceObject
= nw_interface_create_with_index(if_index
);
859 nw_parameters_require_interface(targetPrivate
->parameters
, interfaceObject
);
860 network_release(interfaceObject
);
864 if (resolverBypass
!= NULL
) {
865 targetPrivate
->resolverBypass
= CFBooleanGetValue(resolverBypass
);
870 const char *opt
= "???";
872 switch (targetPrivate
->type
) {
873 case reachabilityTypeAddress
:
874 opt
= DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS
;
876 case reachabilityTypeAddressPair
:
877 opt
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS
;
879 case reachabilityTypeName
:
880 opt
= DEBUG_REACHABILITY_TYPE_NAME_OPTIONS
;
882 case reachabilityTypePTR
:
883 opt
= DEBUG_REACHABILITY_TYPE_PTR_OPTIONS
;
887 SC_log(LOG_DEBUG
, "%s%s %@",
888 targetPrivate
->log_prefix
,
893 return (SCNetworkReachabilityRef
)targetPrivate
;
898 SCNetworkReachabilityGetTypeID(void)
900 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
901 return __kSCNetworkReachabilityTypeID
;
905 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
906 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
909 CFMutableArrayRef array
= NULL
;
910 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
912 if (!isA_SCNetworkReachability(target
)) {
913 _SCErrorSet(kSCStatusInvalidArgument
);
917 if (!isReachabilityTypeName(targetPrivate
->type
)) {
918 _SCErrorSet(kSCStatusInvalidArgument
);
926 MUTEX_LOCK(&targetPrivate
->lock
);
928 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
929 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
930 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
931 nw_endpoint_type_t endpoint_type
= nw_endpoint_get_type((nw_endpoint_t
)object
);
932 if (endpoint_type
== nw_endpoint_type_address
) {
933 const struct sockaddr
*address
= nw_endpoint_get_address((nw_endpoint_t
)object
);
934 if (address
== NULL
) {
935 SC_log(LOG_ERR
, "nw_endpoint_type_address w/no address");
939 CFDataRef addressData
= CFDataCreate(kCFAllocatorDefault
, (const uint8_t *)address
, address
->sa_len
);
940 CFArrayAppendValue(array
, addressData
);
941 CFRelease(addressData
);
942 } else if (endpoint_type
== nw_endpoint_type_host
) {
943 const char *host
= nw_endpoint_get_hostname((nw_endpoint_t
)object
);
945 SC_log(LOG_ERR
, "nw_endpoint_type_host w/no host");
949 CFStringRef string
= CFStringCreateWithCString(kCFAllocatorDefault
, host
, kCFStringEncodingASCII
);
950 if (string
== NULL
) {
951 SC_log(LOG_ERR
, "nw_endpoint_type_host w/non-ASCII host");
955 if (CFStringHasPrefix(string
, CFSTR(".")) || CFStringHasSuffix(string
, CFSTR("."))) {
956 CFMutableStringRef mutableString
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, string
);
958 string
= mutableString
;
959 CFStringTrim(mutableString
, CFSTR("."));
961 CFArrayAppendValue(array
, string
);
964 SC_log(LOG_ERR
, "unexpected nw_endpoint type: %d", endpoint_type
);
970 MUTEX_UNLOCK(&targetPrivate
->lock
);
971 _SCErrorSet(kSCStatusOK
);
976 #pragma mark Reachability Flags
979 __SCNetworkReachabilityGetAgentVPNFlags(xpc_object_t dictionary
, Boolean
*vpn
, Boolean
*onDemand
)
981 const struct netagent
*agent
= NULL
;
984 if (dictionary
== NULL
|| vpn
== NULL
|| onDemand
== NULL
) {
991 agent
= xpc_dictionary_get_data(dictionary
, REACHABILITY_AGENT_DATA_KEY
, &length
);
992 if (agent
== NULL
|| length
< sizeof(struct netagent
) || length
!= (sizeof(struct netagent
) + agent
->netagent_data_size
)) {
996 if (strncmp(REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN
, agent
->netagent_domain
, NETAGENT_DOMAINSIZE
) == 0) {
998 if ((agent
->netagent_flags
& NETAGENT_FLAG_VOLUNTARY
) &&
999 !(agent
->netagent_flags
& NETAGENT_FLAG_ACTIVE
)) {
1006 nw_path_is_linklocal_direct(nw_path_t path
)
1008 bool is_linklocal_direct
= false;
1010 nw_endpoint_t endpoint
= nw_path_copy_endpoint(path
);
1011 if (endpoint
== NULL
) {
1015 if (nw_endpoint_get_type(endpoint
) == nw_endpoint_type_address
) {
1016 const struct sockaddr_in
*sin
;
1018 sin
= (const struct sockaddr_in
*)(void *)nw_endpoint_get_address(endpoint
);;
1019 if ((sin
!= NULL
) &&
1020 (sin
->sin_family
== AF_INET
) &&
1021 IN_LINKLOCAL(ntohl(sin
->sin_addr
.s_addr
))) {
1022 nw_interface_t interface
= nw_path_copy_interface(path
);
1024 if (interface
!= NULL
) {
1025 nw_interface_type_t type
= nw_interface_get_type(interface
);
1026 if ((type
== nw_interface_type_wired
) ||
1027 ((type
== nw_interface_type_wifi
) &&
1028 (nw_interface_get_subtype(interface
) != nw_interface_subtype_wifi_awdl
))) {
1029 is_linklocal_direct
= true;
1032 network_release(interface
);
1037 network_release(endpoint
);
1038 return is_linklocal_direct
;
1041 static SCNetworkReachabilityFlags
1042 __SCNetworkReachabilityGetFlagsFromPath(nw_path_t path
,
1043 ReachabilityAddressType type
,
1044 nw_resolver_status_t resolverStatus
,
1045 nw_array_t resolvedEndpoints
,
1046 Boolean resolvedEndpointUseFlags
,
1047 SCNetworkReachabilityFlags resolvedEndpointFlags
)
1049 __block SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
1050 __block
const char *why
= "???";
1052 nw_path_status_t status
= nw_path_get_status(path
);
1053 if (status
== nw_path_status_satisfied
) {
1054 __block
bool checkDNSFlags
= TRUE
;
1055 flags
= kSCNetworkReachabilityFlagsReachable
;
1056 why
= "nw_path_status_satisfied";
1057 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1058 if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1059 flags
|= (kSCNetworkReachabilityFlagsTransientConnection
| kSCNetworkReachabilityFlagsIsWWAN
);
1060 why
= "nw_path_status_satisfied, cellular";
1063 xpc_object_t agent_dictionary
= nw_path_copy_netagent_dictionary(path
);
1064 if (agent_dictionary
!= NULL
) {
1065 if (xpc_dictionary_get_count(agent_dictionary
) > 0) {
1066 xpc_dictionary_apply(agent_dictionary
, ^bool(__unused
const char *key
, xpc_object_t value
) {
1067 Boolean vpn
= FALSE
;
1068 Boolean onDemand
= FALSE
;
1069 __SCNetworkReachabilityGetAgentVPNFlags(value
, &vpn
, &onDemand
);
1071 // VPN flows are transient
1072 flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1073 why
= "nw_path_status_satisfied, VPN";
1076 type
== reachabilityTypeName
&&
1077 resolverStatus
== nw_resolver_status_complete
&&
1078 nw_array_get_count(resolvedEndpoints
) == 0) {
1079 // On Demand by hostname, when no address has been resolved
1080 flags
|= (kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsConnectionOnDemand
);
1081 why
= "nw_path_status_satisfied, OnDemand";
1082 checkDNSFlags
= FALSE
;
1088 xpc_release(agent_dictionary
);
1090 if (isReachabilityTypeName(type
)) {
1091 if (checkDNSFlags
) {
1092 if (resolverStatus
== nw_resolver_status_complete
&&
1093 nw_array_get_count(resolvedEndpoints
) == 0) {
1094 // DNS didn't resolve, as a final answer for now. Not reachable!
1096 why
= "nw_path_status_satisfied, DNS not reachable";
1097 } else if (resolvedEndpointUseFlags
) {
1098 flags
= resolvedEndpointFlags
;
1099 why
= "nw_path_status_satisfied, resolved endpoint flags";
1103 if (nw_path_is_direct(path
) || nw_path_is_linklocal_direct(path
)) {
1104 flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1105 why
= "nw_path_status_satisfied, by address, direct";
1107 if (nw_path_is_local(path
)) {
1108 flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1109 why
= "nw_path_status_satisfied, by address, local";
1112 } else if (status
== nw_path_status_unsatisfied
) {
1114 why
= "nw_path_status_unsatisfied";
1115 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1116 if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1117 flags
|= kSCNetworkReachabilityFlagsIsWWAN
;
1118 why
= "nw_path_status_unsatisfied, WWAN";
1121 } else if (status
== nw_path_status_satisfiable
) {
1122 flags
= (kSCNetworkReachabilityFlagsReachable
| kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsTransientConnection
);
1123 why
= "nw_path_status_satisfiable";
1125 if (nw_path_get_vpn_config_id(path
, &vpn_uuid
)) {
1126 flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
1127 why
= "nw_path_status_satisfiable, OnDemand";
1129 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1130 else if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1131 flags
|= kSCNetworkReachabilityFlagsIsWWAN
;
1132 why
= "nw_path_status_satisfiable, WWAN";
1137 SC_log(LOG_DEBUG
, "__SCNetworkReachabilityGetFlagsFromPath, flags = 0x%08x, %s", flags
, why
);
1138 return (flags
& kSCNetworkReachabilityFlagsMask
);
1141 static nw_endpoint_t
1142 __SCNetworkReachabilityGetPrimaryEndpoint(SCNetworkReachabilityPrivateRef targetPrivate
)
1144 if (targetPrivate
->type
== reachabilityTypeName
) {
1145 return targetPrivate
->hostnameEndpoint
;
1146 } else if (targetPrivate
->type
== reachabilityTypeAddress
||
1147 targetPrivate
->type
== reachabilityTypeAddressPair
||
1148 targetPrivate
->type
== reachabilityTypePTR
) {
1149 return targetPrivate
->remoteAddressEndpoint
;
1155 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target
)
1159 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1160 SCNetworkReachabilityFlags flags
= 0;
1162 if (!isA_SCNetworkReachability(target
)) {
1163 _SCErrorSet(kSCStatusInvalidArgument
);
1167 MUTEX_LOCK(&targetPrivate
->lock
);
1169 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1170 targetPrivate
->type
,
1171 nw_resolver_status_invalid
,
1173 targetPrivate
->lastResolvedEndpointHasFlags
,
1174 targetPrivate
->lastResolvedEndpointFlags
);
1176 /* Only return the if_index if the connection is reachable not for reachable connection
1177 * required etc ... */
1178 if (ok
&& __SCNetworkReachabilityRank(flags
) == ReachabilityRankReachable
) {
1179 if (targetPrivate
->lastResolvedEndpointHasFlags
) {
1180 if_index
= targetPrivate
->lastResolvedEndpointInterfaceIndex
;
1182 if_index
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1186 MUTEX_UNLOCK(&targetPrivate
->lock
);
1190 // CrazyIvan46 is the feature that allows connections to IPv4 literals on IPv6-only (NAT64+DNS64) networks to work
1191 // This function replaces the path when the initial one isn't satisfied and our target is an IPv4 literal
1192 // It tries IPv6 reachability instead in case we could synthesize another address to connect to
1193 static OS_OBJECT_RETURNS_RETAINED nw_path_t
1194 __SCNetworkReachabilityCreateCrazyIvan46Path(nw_path_t path
, nw_endpoint_t endpoint
,
1195 nw_parameters_t parameters
, Boolean allow_resolution
)
1197 nw_path_t retPath
= NULL
;
1198 const struct sockaddr
*sa
;
1200 if ((nw_path_get_status(path
) != nw_path_status_unsatisfied
) ||
1201 (NULL
== endpoint
) || (nw_endpoint_get_type(endpoint
) != nw_endpoint_type_address
)) {
1205 sa
= nw_endpoint_get_address(endpoint
);
1207 if (sa
->sa_family
!= AF_INET
) {
1211 if (allow_resolution
) {
1212 uint32_t ifIndex
= 0;
1213 int32_t numPrefixes
;
1214 nw_nat64_prefix_t
*prefixes
= NULL
;
1215 struct sockaddr_in sin
;
1217 memcpy(&sin
, sa
, MIN(sizeof(sin
), sa
->sa_len
));
1218 if (NULL
!= parameters
) {
1219 ifIndex
= nw_parameters_get_required_interface_index(parameters
);
1221 numPrefixes
= nw_nat64_copy_prefixes(&ifIndex
, &prefixes
);
1222 if (numPrefixes
> 0) {
1223 struct sockaddr_in6 synthesizedAddress
= {
1224 .sin6_len
= sizeof(struct sockaddr_in6
),
1225 .sin6_family
= AF_INET6
,
1226 .sin6_port
= nw_endpoint_get_port(endpoint
),
1230 nw_endpoint_t synthesizedEndpoint
;
1231 nw_path_evaluator_t synthesizedEvaluator
;
1232 nw_path_t synthesizedPath
;
1234 nw_nat64_synthesize_v6(&prefixes
[0], &sin
.sin_addr
, &synthesizedAddress
.sin6_addr
);
1235 synthesizedEndpoint
= nw_endpoint_create_address((const struct sockaddr
*)&synthesizedAddress
);
1236 synthesizedEvaluator
= nw_path_create_evaluator_for_endpoint(synthesizedEndpoint
, parameters
);
1237 synthesizedPath
= nw_path_evaluator_copy_path(synthesizedEvaluator
);
1238 if (nw_path_get_status(synthesizedPath
) != nw_path_status_unsatisfied
) {
1239 retPath
= synthesizedPath
;
1240 SC_log(LOG_INFO
, "Using CrazyIvan46 synthesized reachability result");
1242 network_release(synthesizedPath
);
1244 network_release(synthesizedEvaluator
);
1245 network_release(synthesizedEndpoint
);
1248 // Don't synthesize in non-scheduled mode to avoid generating DNS traffic
1250 nw_path_evaluator_t v6PathEvaluator
;
1251 nw_parameters_t v6Parameters
;
1253 if (NULL
!= parameters
) {
1254 v6Parameters
= nw_parameters_copy(parameters
);
1256 v6Parameters
= nw_parameters_create();
1258 nw_parameters_set_required_address_family(v6Parameters
, AF_INET6
);
1259 v6PathEvaluator
= nw_path_create_evaluator_for_endpoint(NULL
, v6Parameters
);
1260 v6Path
= nw_path_evaluator_copy_path(v6PathEvaluator
);
1261 if (nw_path_get_status(v6Path
) != nw_path_status_unsatisfied
) {
1263 SC_log(LOG_INFO
, "Using CrazyIvan46 simple reachability result");
1265 network_release(v6Path
);
1267 network_release(v6PathEvaluator
);
1268 network_release(v6Parameters
);
1274 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
1275 SCNetworkReachabilityFlags
*flags
)
1277 nw_path_t crazyIvanPath
;
1278 nw_endpoint_t endpoint
;
1281 nw_path_evaluator_t pathEvaluator
;
1282 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1284 if (!isA_SCNetworkReachability(target
)) {
1285 _SCErrorSet(kSCStatusInvalidArgument
);
1289 MUTEX_LOCK(&targetPrivate
->lock
);
1291 if (targetPrivate
->scheduled
) {
1292 // if being watched, return the last known (and what should be current) status
1293 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1294 targetPrivate
->type
,
1295 targetPrivate
->lastResolverStatus
,
1296 targetPrivate
->lastResolvedEndpoints
,
1297 targetPrivate
->lastResolvedEndpointHasFlags
,
1298 targetPrivate
->lastResolvedEndpointFlags
);
1299 // because we have synchronously captured the current status, we no longer
1300 // need our by-name required callback
1301 targetPrivate
->sentFirstUpdate
= TRUE
;
1305 // Not being watched, so run a one-shot path evaluator
1306 // We don't care about DNS resolution in this case, since we only need to have the
1307 // DNS resolution to support clients watching reachability to get updates
1308 endpoint
= __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
);
1309 pathEvaluator
= nw_path_create_evaluator_for_endpoint(endpoint
, targetPrivate
->parameters
);
1310 path
= nw_path_evaluator_copy_path(pathEvaluator
);
1312 crazyIvanPath
= __SCNetworkReachabilityCreateCrazyIvan46Path(path
, endpoint
, targetPrivate
->parameters
, FALSE
);
1313 if (NULL
!= crazyIvanPath
) {
1314 network_release(path
);
1315 path
= crazyIvanPath
;
1318 *flags
= __SCNetworkReachabilityGetFlagsFromPath(path
, 0, nw_resolver_status_invalid
, NULL
, FALSE
, 0);
1319 network_release(path
);
1320 network_release(pathEvaluator
);
1324 MUTEX_UNLOCK(&targetPrivate
->lock
);
1330 #pragma mark Notifications
1333 reachPerformAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1335 os_activity_t activity
;
1337 void (*context_release
)(const void *);
1338 SCNetworkReachabilityCallBack rlsFunction
;
1339 SCNetworkReachabilityFlags flags
= 0;
1341 activity
= os_activity_create("processing SCNetworkReachability notification",
1342 OS_ACTIVITY_CURRENT
,
1343 OS_ACTIVITY_FLAG_DEFAULT
);
1344 os_activity_scope(activity
);
1346 if (!targetPrivate
->scheduled
) {
1347 // if no longer scheduled
1348 SC_log(LOG_INFO
, "%sskipping SCNetworkReachability callback, no longer scheduled",
1349 targetPrivate
->log_prefix
);
1350 MUTEX_UNLOCK(&targetPrivate
->lock
);
1355 rlsFunction
= targetPrivate
->rlsFunction
;
1356 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
1357 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
1358 context_release
= targetPrivate
->rlsContext
.release
;
1360 context_info
= targetPrivate
->rlsContext
.info
;
1361 context_release
= NULL
;
1364 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1365 targetPrivate
->type
,
1366 targetPrivate
->lastResolverStatus
,
1367 targetPrivate
->lastResolvedEndpoints
,
1368 targetPrivate
->lastResolvedEndpointHasFlags
,
1369 targetPrivate
->lastResolvedEndpointFlags
);
1371 MUTEX_UNLOCK(&targetPrivate
->lock
);
1373 if (rlsFunction
!= NULL
) {
1374 SC_log(LOG_DEBUG
, "%sexec SCNetworkReachability callout w/flags = 0x%08x",
1375 targetPrivate
->log_prefix
,
1377 (*rlsFunction
)((SCNetworkReachabilityRef
)targetPrivate
,
1382 if (context_release
!= NULL
) {
1383 (*context_release
)(context_info
);
1388 os_release(activity
);
1394 reachPerform(void *info
)
1396 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)info
;
1398 MUTEX_LOCK(&targetPrivate
->lock
);
1399 reachPerformAndUnlock(targetPrivate
);
1404 reachUpdateAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1406 targetPrivate
->sentFirstUpdate
= TRUE
;
1407 if (targetPrivate
->rls
!= NULL
) {
1408 if (targetPrivate
->rlList
!= NULL
) {
1409 CFRunLoopSourceSignal(targetPrivate
->rls
);
1410 _SC_signalRunLoop(targetPrivate
, targetPrivate
->rls
, targetPrivate
->rlList
);
1412 MUTEX_UNLOCK(&targetPrivate
->lock
);
1414 reachPerformAndUnlock(targetPrivate
);
1419 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
1420 SCNetworkReachabilityCallBack callout
,
1421 SCNetworkReachabilityContext
*context
)
1423 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1425 MUTEX_LOCK(&targetPrivate
->lock
);
1427 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1428 /* let go of the current context */
1429 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1432 targetPrivate
->rlsFunction
= callout
;
1433 targetPrivate
->rlsContext
.info
= NULL
;
1434 targetPrivate
->rlsContext
.retain
= NULL
;
1435 targetPrivate
->rlsContext
.release
= NULL
;
1436 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1438 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
1439 if (context
->retain
!= NULL
) {
1440 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
1444 MUTEX_UNLOCK(&targetPrivate
->lock
);
1451 reachRLSCopyDescription(const void *info
)
1453 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1455 return CFStringCreateWithFormat(NULL
,
1457 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
1462 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
1463 CFRunLoopRef runLoop
,
1464 CFStringRef runLoopMode
)
1466 Boolean success
= FALSE
;
1467 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1468 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1469 _SCErrorSet(kSCStatusInvalidArgument
);
1473 MUTEX_LOCK(&targetPrivate
->lock
);
1475 if (targetPrivate
->scheduled
) {
1476 if (targetPrivate
->rls
!= NULL
&& targetPrivate
->rlList
!= NULL
) {
1477 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1479 * if we do not already have host notifications scheduled with
1480 * this runLoop / runLoopMode
1482 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1485 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
1487 MUTEX_UNLOCK(&targetPrivate
->lock
);
1490 MUTEX_UNLOCK(&targetPrivate
->lock
);
1491 _SCErrorSet(kSCStatusInvalidArgument
);
1496 CFRunLoopSourceContext context
= {
1498 , (void *)target
// info
1499 , CFRetain
// retain
1500 , CFRelease
// release
1501 , reachRLSCopyDescription
// copyDescription
1506 , reachPerform
// perform
1509 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
1510 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1512 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1514 * if we do not already have host notifications scheduled with
1515 * this runLoop / runLoopMode
1517 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1520 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
1522 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, _callback_queue());
1524 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1525 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1526 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1527 // if target is no longer scheduled for this runLoop / runLoopMode
1528 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1530 // if *all* notifications have been unscheduled
1531 CFRelease(targetPrivate
->rlList
);
1532 targetPrivate
->rlList
= NULL
;
1533 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1534 CFRelease(targetPrivate
->rls
);
1535 targetPrivate
->rls
= NULL
;
1541 MUTEX_UNLOCK(&targetPrivate
->lock
);
1546 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
1547 CFRunLoopRef runLoop
,
1548 CFStringRef runLoopMode
)
1550 Boolean success
= FALSE
;
1551 Boolean unscheduleDispatchQueue
= FALSE
;
1552 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1554 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1555 _SCErrorSet(kSCStatusInvalidArgument
);
1559 MUTEX_LOCK(&targetPrivate
->lock
);
1561 if (targetPrivate
->rlList
== NULL
|| targetPrivate
->rls
== NULL
|| !targetPrivate
->scheduled
) {
1562 MUTEX_UNLOCK(&targetPrivate
->lock
);
1563 _SCErrorSet(kSCStatusInvalidArgument
);
1567 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1568 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1569 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1570 // if target is no longer scheduled for this runLoop / runLoopMode
1571 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1573 // if *all* notifications have been unscheduled
1574 unscheduleDispatchQueue
= TRUE
;
1575 CFRelease(targetPrivate
->rlList
);
1576 targetPrivate
->rlList
= NULL
;
1577 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1578 CFRelease(targetPrivate
->rls
);
1579 targetPrivate
->rls
= NULL
;
1584 if (unscheduleDispatchQueue
) {
1585 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, NULL
);
1589 MUTEX_UNLOCK(&targetPrivate
->lock
);
1593 static __inline__
void
1594 __SCNetworkReachabilityCopyPathStatus(SCNetworkReachabilityPrivateRef targetPrivate
, SCNetworkReachabilityFlags
*flags
, uint
*ifIndex
, size_t *endpointCount
)
1597 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1598 targetPrivate
->type
,
1599 targetPrivate
->lastResolverStatus
,
1600 targetPrivate
->lastResolvedEndpoints
,
1601 targetPrivate
->lastResolvedEndpointHasFlags
,
1602 targetPrivate
->lastResolvedEndpointFlags
);
1605 *ifIndex
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1607 if (endpointCount
) {
1608 *endpointCount
= nw_array_get_count(targetPrivate
->lastResolvedEndpoints
);
1613 static __inline__ Boolean
1614 __SCNetworkReachabilityShouldUpdateClient(SCNetworkReachabilityPrivateRef targetPrivate
, SCNetworkReachabilityFlags oldFlags
, uint oldIFIndex
, size_t oldEndpointCount
)
1616 SCNetworkReachabilityFlags newFlags
= 0;
1617 uint newIFIndex
= 0;
1618 size_t newEndpointCount
= 0;
1619 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &newFlags
, &newIFIndex
, &newEndpointCount
);
1620 return (!targetPrivate
->sentFirstUpdate
||
1621 oldFlags
!= newFlags
||
1622 oldIFIndex
!= newIFIndex
||
1623 oldEndpointCount
!= newEndpointCount
);
1627 __SCNetworkReachabilityRestartResolver(SCNetworkReachabilityPrivateRef targetPrivate
)
1629 if (targetPrivate
&&
1630 !targetPrivate
->resolverBypass
&&
1631 isReachabilityTypeName(targetPrivate
->type
)) {
1632 CFRetain(targetPrivate
);
1633 if (NULL
!= targetPrivate
->resolver
) {
1634 nw_resolver_cancel(targetPrivate
->resolver
);
1636 nw_resolver_t resolver
= nw_resolver_create_with_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
), targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
: targetPrivate
->parameters
);
1637 targetPrivate
->resolver
= resolver
;
1638 nw_resolver_set_cancel_handler(resolver
, ^(void) {
1639 MUTEX_LOCK(&targetPrivate
->lock
);
1640 if (resolver
== targetPrivate
->resolver
) {
1641 targetPrivate
->resolver
= NULL
;
1643 network_release(resolver
);
1644 MUTEX_UNLOCK(&targetPrivate
->lock
);
1645 CFRelease(targetPrivate
);
1647 if (!nw_resolver_set_update_handler(resolver
, targetPrivate
->dispatchQueue
, ^(nw_resolver_status_t status
, nw_array_t resolved_endpoints
) {
1648 MUTEX_LOCK(&targetPrivate
->lock
);
1649 if (targetPrivate
->scheduled
) {
1650 SCNetworkReachabilityFlags oldFlags
= 0;
1651 uint oldIFIndex
= 0;
1652 size_t oldEndpointCount
= 0;
1653 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &oldFlags
, &oldIFIndex
, &oldEndpointCount
);
1655 targetPrivate
->lastResolverStatus
= status
;
1656 network_release(targetPrivate
->lastResolvedEndpoints
);
1657 targetPrivate
->lastResolvedEndpoints
= network_retain(resolved_endpoints
);
1659 // Run path evaluation on the resolved endpoints
1660 __block Boolean hasFlags
= FALSE
;
1661 targetPrivate
->lastResolvedEndpointHasFlags
= FALSE
;
1662 targetPrivate
->lastResolvedEndpointFlags
= 0;
1663 targetPrivate
->lastResolvedEndpointInterfaceIndex
= 0;
1664 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
1665 SCNetworkReachabilityFlags flags
= 0;
1666 uint interfaceIndex
= 0;
1667 ReachabilityRankType rank
;
1668 nw_endpoint_t resolvedEndpoint
= (nw_endpoint_t
)object
;
1669 nw_path_evaluator_t pathEvaluator
= nw_path_create_evaluator_for_endpoint(resolvedEndpoint
, targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
: targetPrivate
->parameters
);
1670 nw_path_t path
= nw_path_evaluator_copy_path(pathEvaluator
);
1672 flags
= __SCNetworkReachabilityGetFlagsFromPath(path
, 0, nw_resolver_status_invalid
, NULL
, FALSE
, 0);
1675 interfaceIndex
= nw_path_get_interface_index(path
);
1676 network_release(path
);
1677 network_release(pathEvaluator
);
1679 rank
= __SCNetworkReachabilityRank(flags
);
1680 if (rank
> __SCNetworkReachabilityRank(targetPrivate
->lastResolvedEndpointFlags
)) {
1681 // Return the best case result
1682 targetPrivate
->lastResolvedEndpointFlags
= flags
;
1683 targetPrivate
->lastResolvedEndpointInterfaceIndex
= interfaceIndex
;
1684 if (rank
== ReachabilityRankReachable
) {
1685 // Can't get any better than REACHABLE
1691 targetPrivate
->lastResolvedEndpointHasFlags
= hasFlags
;
1693 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1694 reachUpdateAndUnlock(targetPrivate
);
1696 MUTEX_UNLOCK(&targetPrivate
->lock
);
1699 MUTEX_UNLOCK(&targetPrivate
->lock
);
1702 network_release(resolver
);
1703 targetPrivate
->resolver
= NULL
;
1704 CFRelease(targetPrivate
);
1710 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
1711 dispatch_queue_t queue
)
1715 if (queue
!= NULL
) {
1716 nw_path_t crazyIvanPath
;
1717 nw_endpoint_t endpoint
;
1718 nw_path_evaluator_t pathEvaluator
;
1720 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
1721 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
1722 _SCErrorSet(kSCStatusInvalidArgument
);
1726 SC_log(LOG_DEBUG
, "%sscheduled", targetPrivate
->log_prefix
);
1728 // retain dispatch queue
1729 dispatch_retain(queue
);
1730 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1731 endpoint
= __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
);
1732 pathEvaluator
= nw_path_create_evaluator_for_endpoint(endpoint
, targetPrivate
->parameters
);
1733 targetPrivate
->pathEvaluator
= pathEvaluator
;
1734 targetPrivate
->dispatchQueue
= queue
;
1735 targetPrivate
->scheduled
= TRUE
;
1736 if (isReachabilityTypeName(targetPrivate
->type
)) {
1737 // we must have at least one callback for by-name queries
1738 targetPrivate
->sentFirstUpdate
= FALSE
;
1740 targetPrivate
->sentFirstUpdate
= TRUE
;
1743 network_release(targetPrivate
->lastPath
);
1744 targetPrivate
->lastPath
= nw_path_evaluator_copy_path(pathEvaluator
);
1746 crazyIvanPath
= __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate
->lastPath
, endpoint
,
1747 targetPrivate
->parameters
, FALSE
);
1748 if (NULL
!= crazyIvanPath
) {
1749 network_release(targetPrivate
->lastPath
);
1750 targetPrivate
->lastPath
= crazyIvanPath
;
1753 network_release(targetPrivate
->lastPathParameters
);
1754 targetPrivate
->lastPathParameters
= nw_path_copy_derived_parameters(targetPrivate
->lastPath
);
1756 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1757 network_release(targetPrivate
->lastResolvedEndpoints
);
1758 targetPrivate
->lastResolvedEndpoints
= NULL
;
1759 __SCNetworkReachabilityRestartResolver(targetPrivate
);
1761 CFRetain(targetPrivate
);
1762 nw_path_evaluator_set_cancel_handler(pathEvaluator
, ^(void) {
1763 MUTEX_LOCK(&targetPrivate
->lock
);
1764 if (pathEvaluator
== targetPrivate
->pathEvaluator
) {
1765 targetPrivate
->pathEvaluator
= NULL
;
1767 network_release(pathEvaluator
);
1768 MUTEX_UNLOCK(&targetPrivate
->lock
);
1769 CFRelease(targetPrivate
);
1772 if (!nw_path_evaluator_set_update_handler(pathEvaluator
, targetPrivate
->dispatchQueue
, ^(nw_path_t path
) {
1773 MUTEX_LOCK(&targetPrivate
->lock
);
1774 if (targetPrivate
->scheduled
) {
1775 nw_path_t crazyIvanPath
;
1776 SCNetworkReachabilityFlags oldFlags
= 0;
1777 uint oldIFIndex
= 0;
1778 size_t oldEndpointCount
= 0;
1779 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &oldFlags
, &oldIFIndex
, &oldEndpointCount
);
1781 network_release(targetPrivate
->lastPath
);
1782 targetPrivate
->lastPath
= network_retain(path
);
1784 crazyIvanPath
= __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate
->lastPath
,
1786 targetPrivate
->parameters
,
1788 if (NULL
!= crazyIvanPath
) {
1789 network_release(targetPrivate
->lastPath
);
1790 targetPrivate
->lastPath
= crazyIvanPath
;
1793 if (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) {
1794 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1795 __SCNetworkReachabilityRestartResolver(targetPrivate
);
1798 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1799 reachUpdateAndUnlock(targetPrivate
);
1801 MUTEX_UNLOCK(&targetPrivate
->lock
);
1804 MUTEX_UNLOCK(&targetPrivate
->lock
);
1807 targetPrivate
->pathEvaluator
= NULL
;
1808 network_release(pathEvaluator
);
1809 CFRelease(targetPrivate
);
1812 if (targetPrivate
->dispatchQueue
== NULL
) { // if we should be scheduled on a dispatch queue (but are not)
1813 _SCErrorSet(kSCStatusInvalidArgument
);
1817 if (!targetPrivate
->scheduled
) {
1818 // if not currently scheduled
1819 _SCErrorSet(kSCStatusInvalidArgument
);
1823 targetPrivate
->scheduled
= FALSE
;
1824 targetPrivate
->sentFirstUpdate
= FALSE
;
1825 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1826 targetPrivate
->pathEvaluator
= NULL
;
1827 network_release(targetPrivate
->lastPath
);
1828 targetPrivate
->lastPath
= NULL
;
1829 network_release(targetPrivate
->lastPathParameters
);
1830 targetPrivate
->lastPathParameters
= NULL
;
1831 network_release(targetPrivate
->lastResolvedEndpoints
);
1832 targetPrivate
->lastResolvedEndpoints
= NULL
;
1833 if (NULL
!= targetPrivate
->resolver
) {
1834 nw_resolver_cancel(targetPrivate
->resolver
);
1835 targetPrivate
->resolver
= NULL
;
1837 if (targetPrivate
->dispatchQueue
!= NULL
) {
1838 dispatch_release(targetPrivate
->dispatchQueue
);
1839 targetPrivate
->dispatchQueue
= NULL
;
1842 SC_log(LOG_DEBUG
, "%sunscheduled", targetPrivate
->log_prefix
);
1850 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
1851 dispatch_queue_t queue
)
1853 if (!isA_SCNetworkReachability(target
)) {
1854 _SCErrorSet(kSCStatusInvalidArgument
);
1858 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1859 MUTEX_LOCK(&targetPrivate
->lock
);
1860 Boolean success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, queue
);
1861 MUTEX_UNLOCK(&targetPrivate
->lock
);
1866 * _SC_checkResolverReachabilityByAddress()
1868 * Given an IP address, determine whether a reverse DNS query can be issued
1869 * using the current network configuration.
1872 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
1873 SCNetworkReachabilityFlags
*flags
,
1875 struct sockaddr
*sa
)
1877 nw_path_evaluator_t evaluator
= nw_path_create_default_evaluator();
1878 nw_path_t path
= nw_path_evaluator_copy_path(evaluator
);
1879 if (nw_path_get_status(path
) == nw_path_status_unsatisfied_network
) {
1888 *flags
= kSCNetworkReachabilityFlagsReachable
;
1894 network_release(evaluator
);
1895 network_release(path
);