2 * Copyright (c) 2003-2018 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 <os/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 #define SC_LOG_HANDLE_TYPE static
63 #include <SystemConfiguration/SystemConfiguration.h>
64 #include <SystemConfiguration/SCValidation.h>
65 #include <SystemConfiguration/SCPrivate.h>
67 #include "SCNetworkReachabilityInternal.h"
72 #if __has_include(<nw/private.h>)
73 #include <nw/private.h>
74 #else // __has_include(<nw/private.h>)
75 #include <network/private.h>
76 #endif // __has_include(<nw/private.h>)
78 #define DEBUG_REACHABILITY_TYPE_NAME "create w/name"
79 #define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
81 #define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"
82 #define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
84 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"
85 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
87 #define DEBUG_REACHABILITY_TYPE_PTR "create w/ptr"
88 #define DEBUG_REACHABILITY_TYPE_PTR_OPTIONS " + options"
90 static pthread_mutexattr_t lock_attr
;
92 #define MUTEX_INIT(m) { \
93 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \
97 #define MUTEX_LOCK(m) { \
98 int _lock_ = (pthread_mutex_lock(m) == 0); \
102 #define MUTEX_UNLOCK(m) { \
103 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
107 #define MUTEX_ASSERT_HELD(m) { \
108 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \
115 #define REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN "NetworkExtension"
116 #define REACHABILITY_AGENT_DATA_KEY "data"
119 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
120 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
122 static SCNetworkReachabilityFlags
123 __SCNetworkReachabilityGetFlagsFromPath(nw_path_t path
,
124 ReachabilityAddressType type
,
125 nw_resolver_status_t resolverStatus
,
126 nw_array_t resolvedEndpoints
,
127 Boolean resolvedEndpointUseFlags
,
128 SCNetworkReachabilityFlags resolvedEndpointFlags
);
131 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
132 dispatch_queue_t queue
);
134 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
137 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
139 "SCNetworkReachability", // className
142 __SCNetworkReachabilityDeallocate
, // dealloc
145 NULL
, // copyFormattingDesc
146 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
150 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
152 static dispatch_queue_t
155 static dispatch_once_t once
;
156 static dispatch_queue_t q
;
158 dispatch_once(&once
, ^{
159 q
= dispatch_queue_create("SCNetworkReachability.callback", NULL
);
166 __log_SCNetworkReachability(void)
168 static os_log_t log
= NULL
;
171 log
= os_log_create("com.apple.SystemConfiguration", "SCNetworkReachability");
179 #pragma mark SCNetworkReachability APIs
182 static __inline__ CFTypeRef
183 isA_SCNetworkReachability(CFTypeRef obj
)
185 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
189 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target
)
191 CFAllocatorRef allocator
= CFGetAllocator(target
);
192 CFMutableStringRef str
;
193 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
195 str
= CFStringCreateMutable(allocator
, 0);
196 switch (targetPrivate
->type
) {
197 case reachabilityTypeAddress
:
198 case reachabilityTypeAddressPair
: {
200 if (targetPrivate
->localAddressEndpoint
!= NULL
) {
201 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->localAddressEndpoint
), buf
, sizeof(buf
));
202 CFStringAppendFormat(str
, NULL
, CFSTR("local address = %s"),
205 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
206 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
207 CFStringAppendFormat(str
, NULL
, CFSTR("%s%saddress = %s"),
208 targetPrivate
->localAddressEndpoint
? ", " : "",
209 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
212 CFStringAppendFormat(str
, NULL
, CFSTR("default path"));
216 case reachabilityTypeName
: {
217 CFStringAppendFormat(str
, NULL
, CFSTR("name = %s"), nw_endpoint_get_hostname(targetPrivate
->hostnameEndpoint
));
220 case reachabilityTypePTR
: {
222 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
223 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
224 CFStringAppendFormat(str
, NULL
, CFSTR("ptr = %s"),
231 if (targetPrivate
->parameters
!= NULL
) {
232 unsigned int if_index
;
234 if_index
= nw_parameters_get_required_interface_index(targetPrivate
->parameters
);
236 CFStringAppendFormat(str
, NULL
, CFSTR(", if_index = %u"), if_index
);
245 __SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target
)
247 CFAllocatorRef allocator
= CFGetAllocator(target
);
249 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
251 str
= CFStringCreateWithFormat(allocator
,
253 CFSTR("flags = 0x%08x, if_index = %u"),
254 __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
256 targetPrivate
->lastResolverStatus
,
257 targetPrivate
->lastResolvedEndpoints
,
258 targetPrivate
->lastResolvedEndpointHasFlags
,
259 targetPrivate
->lastResolvedEndpointFlags
),
260 targetPrivate
->lastResolvedEndpointHasFlags
? targetPrivate
->lastResolvedEndpointInterfaceIndex
261 : nw_path_get_interface_index(targetPrivate
->lastPath
));
267 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
269 CFAllocatorRef allocator
= CFGetAllocator(cf
);
270 CFMutableStringRef result
;
272 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
273 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
275 MUTEX_LOCK(&targetPrivate
->lock
);
277 result
= CFStringCreateMutable(allocator
, 0);
278 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
280 // add target description
281 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
282 CFStringAppend(result
, str
);
285 // add additional "name" info
286 if (isReachabilityTypeName(targetPrivate
->type
)) {
287 if (targetPrivate
->resolver
&& targetPrivate
->lastResolverStatus
== nw_resolver_status_invalid
) {
288 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
289 } else if (targetPrivate
->lastResolverStatus
!= nw_resolver_status_invalid
) {
290 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s"), (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) ? "complete" : "in progress");
291 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
292 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
293 #pragma unused(index)
294 nw_endpoint_t endpoint
= (nw_endpoint_t
)object
;
295 nw_endpoint_type_t endpoint_type
= nw_endpoint_get_type(endpoint
);
296 if (endpoint_type
== nw_endpoint_type_address
) {
298 const struct sockaddr
*sa
= nw_endpoint_get_address(endpoint
);
299 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
300 CFStringAppendFormat(result
, NULL
, CFSTR(", %s"), buf
);
301 } else if (endpoint_type
== nw_endpoint_type_host
) {
302 CFStringAppendFormat(result
, NULL
, CFSTR(", %s"), nw_endpoint_get_hostname(endpoint
));
304 CFStringAppendFormat(result
, NULL
, CFSTR(", unexpected nw_endpoint type: %d"), endpoint_type
);
309 CFStringAppendFormat(result
, NULL
, CFSTR(", no addresses"));
311 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
315 if (targetPrivate
->resolverBypass
) {
316 CFStringAppendFormat(result
, NULL
, CFSTR(", !resolve"));
320 if (targetPrivate
->scheduled
) {
321 str
= __SCNetworkReachabilityCopyTargetFlags(target
);
322 CFStringAppendFormat(result
, NULL
, CFSTR(", %@"), str
);
326 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
328 MUTEX_UNLOCK(&targetPrivate
->lock
);
335 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
337 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
338 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
340 SC_log(LOG_DEBUG
, "%srelease", targetPrivate
->log_prefix
);
342 /* release resources */
343 MUTEX_LOCK(&targetPrivate
->lock
);
344 targetPrivate
->scheduled
= FALSE
;
346 if (targetPrivate
->hostnameEndpoint
) {
347 network_release(targetPrivate
->hostnameEndpoint
);
348 targetPrivate
->hostnameEndpoint
= NULL
;
350 if (targetPrivate
->localAddressEndpoint
) {
351 network_release(targetPrivate
->localAddressEndpoint
);
352 targetPrivate
->localAddressEndpoint
= NULL
;
354 if (targetPrivate
->remoteAddressEndpoint
) {
355 network_release(targetPrivate
->remoteAddressEndpoint
);
356 targetPrivate
->remoteAddressEndpoint
= NULL
;
358 if (targetPrivate
->parameters
) {
359 network_release(targetPrivate
->parameters
);
360 targetPrivate
->parameters
= NULL
;
362 if (targetPrivate
->lastPath
) {
363 network_release(targetPrivate
->lastPath
);
364 targetPrivate
->lastPath
= NULL
;
366 if (targetPrivate
->lastPathParameters
) {
367 network_release(targetPrivate
->lastPathParameters
);
368 targetPrivate
->lastPathParameters
= NULL
;
370 if (targetPrivate
->lastResolvedEndpoints
) {
371 network_release(targetPrivate
->lastResolvedEndpoints
);
372 targetPrivate
->lastResolvedEndpoints
= NULL
;
375 if (targetPrivate
->rlsContext
.release
!= NULL
) {
376 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
379 MUTEX_UNLOCK(&targetPrivate
->lock
);
381 pthread_mutex_destroy(&targetPrivate
->lock
);
388 __SCNetworkReachabilityInitialize(void)
390 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
392 pthread_mutexattr_init(&lock_attr
);
393 pthread_mutexattr_settype(&lock_attr
, PTHREAD_MUTEX_ERRORCHECK
);
398 static SCNetworkReachabilityPrivateRef
399 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
401 SCNetworkReachabilityPrivateRef targetPrivate
;
404 /* initialize runtime */
405 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
407 /* allocate target */
408 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
409 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
410 __kSCNetworkReachabilityTypeID
,
413 if (targetPrivate
== NULL
) {
417 /* initialize non-zero/NULL members */
418 MUTEX_INIT(&targetPrivate
->lock
);
420 snprintf(targetPrivate
->log_prefix
,
421 sizeof(targetPrivate
->log_prefix
),
426 return targetPrivate
;
430 static const struct sockaddr
*
431 is_valid_address(const struct sockaddr
*address
)
433 const struct sockaddr
*valid
= NULL
;
434 static Boolean warned
= FALSE
;
436 if ((address
!= NULL
) &&
437 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
438 switch (address
->sa_family
) {
440 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
444 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
446 sizeof(struct sockaddr_in
));
452 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
454 } else if (!warned
) {
455 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
457 sizeof(struct sockaddr_in6
));
463 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d",
474 __SCNetworkReachabilityAddressIsEmpty(const struct sockaddr
*address
)
476 if (address
== NULL
) {
480 if (address
->sa_family
== AF_INET
) {
481 return (((struct sockaddr_in
*)(void *)address
)->sin_addr
.s_addr
== 0);
482 } else if (address
->sa_family
== AF_INET6
) {
483 return IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6
*)(void *)address
)->sin6_addr
);
489 SCNetworkReachabilityRef
490 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
491 const struct sockaddr
*address
)
493 const struct sockaddr
*targetAddress
;
494 SCNetworkReachabilityPrivateRef targetPrivate
;
496 targetAddress
= is_valid_address(address
);
497 if (targetAddress
== NULL
) {
498 _SCErrorSet(kSCStatusInvalidArgument
);
502 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
503 if (targetPrivate
== NULL
) {
507 targetPrivate
->type
= reachabilityTypeAddress
;
509 if (!__SCNetworkReachabilityAddressIsEmpty(targetAddress
)) {
510 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(targetAddress
);
513 SC_log(LOG_DEBUG
, "%s%s %@",
514 targetPrivate
->log_prefix
,
515 DEBUG_REACHABILITY_TYPE_ADDRESS
,
518 return (SCNetworkReachabilityRef
)targetPrivate
;
522 #if !TARGET_OS_IPHONE
524 is_ipv4_loopback(const struct sockaddr
*sa
)
527 struct sockaddr_in
*sin
= (struct sockaddr_in
*)(void *)sa
;
530 (sa
->sa_len
< sizeof(struct sockaddr_in
)) ||
531 (sa
->sa_family
!= AF_INET
)) {
535 addr
= ntohl(sin
->sin_addr
.s_addr
);
536 return IN_LOOPBACK(addr
) ? TRUE
: FALSE
;
538 #endif // !TARGET_OS_IPHONE
542 is_same_address(const struct sockaddr
*a
, const struct sockaddr
*b
)
550 (a
->sa_family
!= b
->sa_family
) ||
551 (a
->sa_len
!= b
->sa_len
)) {
555 switch (a
->sa_family
) {
557 struct sockaddr_in
*a_sin
= (struct sockaddr_in
*)(void *)a
;
558 struct sockaddr_in
*b_sin
= (struct sockaddr_in
*)(void *)b
;
560 /* ALIGN: assuming a (and b) are aligned, then cast ok. */
561 a_addr
= &a_sin
->sin_addr
;
562 b_addr
= &b_sin
->sin_addr
;
563 len
= sizeof(struct in_addr
);
568 struct sockaddr_in6
*a_sin6
= (struct sockaddr_in6
*)(void *)a
;
569 struct sockaddr_in6
*b_sin6
= (struct sockaddr_in6
*)(void *)b
;
571 if (a_sin6
->sin6_scope_id
!= b_sin6
->sin6_scope_id
) {
575 a_addr
= &a_sin6
->sin6_addr
;
576 b_addr
= &b_sin6
->sin6_addr
;
577 len
= sizeof(struct in6_addr
);
588 return (bcmp(a_addr
, b_addr
, len
) == 0);
592 SCNetworkReachabilityRef
593 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
594 const struct sockaddr
*localAddress
,
595 const struct sockaddr
*remoteAddress
)
597 SCNetworkReachabilityPrivateRef targetPrivate
;
599 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
600 _SCErrorSet(kSCStatusInvalidArgument
);
604 if (localAddress
!= NULL
) {
605 localAddress
= is_valid_address(localAddress
);
606 if (localAddress
== NULL
) {
607 _SCErrorSet(kSCStatusInvalidArgument
);
612 if (remoteAddress
!= NULL
) {
613 remoteAddress
= is_valid_address(remoteAddress
);
614 if (remoteAddress
== NULL
) {
615 _SCErrorSet(kSCStatusInvalidArgument
);
620 #if !TARGET_OS_IPHONE
621 // Check/fix for loopback IP --> remote IP (rdar://26561383)
622 if ((localAddress
!= NULL
) && (remoteAddress
!= NULL
) &&
623 is_ipv4_loopback(localAddress
) && !is_ipv4_loopback(remoteAddress
)) {
624 static Boolean warned
= FALSE
;
627 SC_log(LOG_WARNING
, "BUG: SCNetworkReachabilityCreateWithAddressPair() called with local <loopback>");
628 SC_log(LOG_WARNING
, "address and remote <non-loopback> address. To return the expected (but actually");
629 SC_log(LOG_WARNING
, "incorrect) result, switched to SCNetworkReachabilityCreateWithAddress() with");
630 SC_log(LOG_WARNING
, "the remote address.");
634 return SCNetworkReachabilityCreateWithAddress(allocator
, remoteAddress
);
636 #endif // !TARGET_OS_IPHONE
638 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
639 if (targetPrivate
== NULL
) {
643 targetPrivate
->type
= reachabilityTypeAddressPair
;
645 if (localAddress
!= NULL
) {
646 targetPrivate
->localAddressEndpoint
= nw_endpoint_create_address(localAddress
);
649 if (remoteAddress
!= NULL
) {
650 if (is_same_address(localAddress
, remoteAddress
)) {
651 targetPrivate
->remoteAddressEndpoint
= network_retain(targetPrivate
->localAddressEndpoint
);
653 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(remoteAddress
);
657 targetPrivate
->parameters
= nw_parameters_create();
658 nw_parameters_set_local_address(targetPrivate
->parameters
, targetPrivate
->localAddressEndpoint
);
660 SC_log(LOG_DEBUG
, "%s%s %@",
661 targetPrivate
->log_prefix
,
662 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR
,
665 return (SCNetworkReachabilityRef
)targetPrivate
;
669 SCNetworkReachabilityRef
670 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
671 const char *nodename
)
675 struct sockaddr_in sin
;
676 struct sockaddr_in6 sin6
;
679 SCNetworkReachabilityPrivateRef targetPrivate
;
681 if (nodename
== NULL
) {
682 _SCErrorSet(kSCStatusInvalidArgument
);
686 nodenameLen
= strlen(nodename
);
687 if (nodenameLen
== 0) {
688 _SCErrorSet(kSCStatusInvalidArgument
);
692 if (_SC_string_to_sockaddr(nodename
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) != NULL
) {
693 /* if this "nodename" is really an IP[v6] address in disguise */
694 return SCNetworkReachabilityCreateWithAddress(allocator
, &addr
.sa
);
697 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
698 if (targetPrivate
== NULL
) {
702 targetPrivate
->type
= reachabilityTypeName
;
704 targetPrivate
->hostnameEndpoint
= nw_endpoint_create_host(nodename
, "0");
706 SC_log(LOG_DEBUG
, "%s%s %@",
707 targetPrivate
->log_prefix
,
708 DEBUG_REACHABILITY_TYPE_NAME
,
711 return (SCNetworkReachabilityRef
)targetPrivate
;
715 static SCNetworkReachabilityRef
716 __SCNetworkReachabilityCreateWithPTR(CFAllocatorRef allocator
,
717 const struct sockaddr
*ptrAddress
)
719 SCNetworkReachabilityPrivateRef targetPrivate
;
721 ptrAddress
= is_valid_address(ptrAddress
);
722 if (ptrAddress
== NULL
) {
723 _SCErrorSet(kSCStatusInvalidArgument
);
727 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
728 if (targetPrivate
== NULL
) {
732 targetPrivate
->type
= reachabilityTypePTR
;
734 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(ptrAddress
);
736 targetPrivate
->parameters
= nw_parameters_create();
737 nw_parameters_set_resolve_ptr(targetPrivate
->parameters
, TRUE
);
739 SC_log(LOG_DEBUG
, "%s%s %@",
740 targetPrivate
->log_prefix
,
741 DEBUG_REACHABILITY_TYPE_PTR
,
744 return (SCNetworkReachabilityRef
)targetPrivate
;
747 SCNetworkReachabilityRef
748 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
749 CFDictionaryRef options
)
751 const struct sockaddr
*addr_l
= NULL
;
752 const struct sockaddr
*addr_p
= NULL
;
753 const struct sockaddr
*addr_r
= NULL
;
755 Boolean haveOpt
= FALSE
;
756 CFStringRef interface
= NULL
;
757 CFStringRef nodename
;
758 CFBooleanRef resolverBypass
;
759 SCNetworkReachabilityRef target
;
760 SCNetworkReachabilityPrivateRef targetPrivate
;
761 unsigned int if_index
= 0;
762 char if_name
[IFNAMSIZ
];
763 CFDataRef sourceAppAuditToken
= NULL
;
764 CFStringRef sourceAppBundleID
= NULL
;
766 if (!isA_CFDictionary(options
)) {
767 _SCErrorSet(kSCStatusInvalidArgument
);
771 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
772 if ((nodename
!= NULL
) &&
773 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
774 _SCErrorSet(kSCStatusInvalidArgument
);
777 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
779 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
780 _SCErrorSet(kSCStatusInvalidArgument
);
783 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
785 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionPTRAddress
);
787 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
788 _SCErrorSet(kSCStatusInvalidArgument
);
791 addr_p
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
793 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
795 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
796 _SCErrorSet(kSCStatusInvalidArgument
);
799 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
801 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
802 if ((interface
!= NULL
) &&
803 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
804 _SCErrorSet(kSCStatusInvalidArgument
);
807 resolverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionResolverBypass
);
808 if ((resolverBypass
!= NULL
) && !isA_CFBoolean(resolverBypass
)) {
809 _SCErrorSet(kSCStatusInvalidArgument
);
812 sourceAppAuditToken
=
813 CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionSourceAppAuditToken
);
814 if ((sourceAppAuditToken
!= NULL
) &&
815 (!isA_CFData(sourceAppAuditToken
) ||
816 (CFDataGetLength(sourceAppAuditToken
) != sizeof(audit_token_t
)))) {
817 _SCErrorSet(kSCStatusInvalidArgument
);
821 CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionSourceAppBundleIdentifier
);
822 if ((sourceAppBundleID
!= NULL
) &&
823 (!isA_CFString(sourceAppBundleID
) ||
824 (CFStringGetLength(sourceAppBundleID
) == 0))) {
825 _SCErrorSet(kSCStatusInvalidArgument
);
830 if (nodename
!= NULL
) {
833 if ((addr_l
!= NULL
) || (addr_r
!= NULL
) || (addr_p
!= NULL
)) {
834 // can't have both a nodename and an address
835 _SCErrorSet(kSCStatusInvalidArgument
);
839 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
840 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
841 CFAllocatorDeallocate(NULL
, (void *)name
);
842 } else if (addr_p
!= NULL
) {
843 if ((addr_l
!= NULL
) || // can't have PTR and target address
845 _SCErrorSet(kSCStatusInvalidArgument
);
849 target
= __SCNetworkReachabilityCreateWithPTR(NULL
, addr_p
);
851 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
852 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
853 } else if (addr_r
!= NULL
) {
854 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
855 } else if (addr_l
!= NULL
) {
856 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, NULL
);
858 _SCErrorSet(kSCStatusInvalidArgument
);
862 if (target
== NULL
) {
866 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
868 if (interface
!= NULL
) {
869 if ((_SC_cfstring_to_cstring(interface
,
872 kCFStringEncodingASCII
) == NULL
) ||
873 ((if_index
= if_nametoindex(if_name
)) == 0)) {
874 CFRelease(targetPrivate
);
875 _SCErrorSet(kSCStatusInvalidArgument
);
880 if (targetPrivate
->parameters
== NULL
) {
881 targetPrivate
->parameters
= nw_parameters_create();
885 nw_interface_t interfaceObject
= nw_interface_create_with_index(if_index
);
886 nw_parameters_require_interface(targetPrivate
->parameters
, interfaceObject
);
887 network_release(interfaceObject
);
891 if (resolverBypass
!= NULL
) {
892 targetPrivate
->resolverBypass
= CFBooleanGetValue(resolverBypass
);
896 if (sourceAppAuditToken
!= NULL
) {
897 audit_token_t atoken
;
898 CFDataGetBytes(sourceAppAuditToken
,
899 CFRangeMake(0, CFDataGetLength(sourceAppAuditToken
)),
901 nw_parameters_set_source_application(targetPrivate
->parameters
, atoken
);
903 } else if (sourceAppBundleID
!= NULL
) {
904 char *cBundleID
= _SC_cfstring_to_cstring(sourceAppBundleID
,
907 kCFStringEncodingUTF8
);
908 if (cBundleID
!= NULL
) {
909 nw_parameters_set_source_application_by_bundle_id(targetPrivate
->parameters
,
911 CFAllocatorDeallocate(NULL
, (void *)cBundleID
);
913 SC_log(LOG_WARNING
, "failed to convert %@ to a C string", sourceAppBundleID
);
919 const char *opt
= "???";
921 switch (targetPrivate
->type
) {
922 case reachabilityTypeAddress
:
923 opt
= DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS
;
925 case reachabilityTypeAddressPair
:
926 opt
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS
;
928 case reachabilityTypeName
:
929 opt
= DEBUG_REACHABILITY_TYPE_NAME_OPTIONS
;
931 case reachabilityTypePTR
:
932 opt
= DEBUG_REACHABILITY_TYPE_PTR_OPTIONS
;
936 SC_log(LOG_DEBUG
, "%s%s %@",
937 targetPrivate
->log_prefix
,
942 return (SCNetworkReachabilityRef
)targetPrivate
;
947 SCNetworkReachabilityGetTypeID(void)
949 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
950 return __kSCNetworkReachabilityTypeID
;
954 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
955 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
958 CFMutableArrayRef array
= NULL
;
959 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
961 if (!isA_SCNetworkReachability(target
)) {
962 _SCErrorSet(kSCStatusInvalidArgument
);
966 if (!isReachabilityTypeName(targetPrivate
->type
)) {
967 _SCErrorSet(kSCStatusInvalidArgument
);
975 MUTEX_LOCK(&targetPrivate
->lock
);
977 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
978 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
979 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
980 #pragma unused(index)
981 nw_endpoint_type_t endpoint_type
= nw_endpoint_get_type((nw_endpoint_t
)object
);
982 if (endpoint_type
== nw_endpoint_type_address
) {
983 const struct sockaddr
*address
= nw_endpoint_get_address((nw_endpoint_t
)object
);
984 if (address
== NULL
) {
985 SC_log(LOG_ERR
, "nw_endpoint_type_address w/no address");
989 CFDataRef addressData
= CFDataCreate(kCFAllocatorDefault
, (const uint8_t *)address
, address
->sa_len
);
990 CFArrayAppendValue(array
, addressData
);
991 CFRelease(addressData
);
992 } else if (endpoint_type
== nw_endpoint_type_host
) {
993 const char *host
= nw_endpoint_get_hostname((nw_endpoint_t
)object
);
995 SC_log(LOG_ERR
, "nw_endpoint_type_host w/no host");
999 CFStringRef string
= CFStringCreateWithCString(kCFAllocatorDefault
, host
, kCFStringEncodingASCII
);
1000 if (string
== NULL
) {
1001 SC_log(LOG_ERR
, "nw_endpoint_type_host w/non-ASCII host");
1005 if (CFStringHasPrefix(string
, CFSTR(".")) || CFStringHasSuffix(string
, CFSTR("."))) {
1006 CFMutableStringRef mutableString
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, string
);
1008 string
= mutableString
;
1009 CFStringTrim(mutableString
, CFSTR("."));
1011 CFArrayAppendValue(array
, string
);
1014 SC_log(LOG_ERR
, "unexpected nw_endpoint type: %d", endpoint_type
);
1020 MUTEX_UNLOCK(&targetPrivate
->lock
);
1021 _SCErrorSet(kSCStatusOK
);
1026 #pragma mark Reachability Flags
1029 __SCNetworkReachabilityGetAgentVPNFlags(xpc_object_t dictionary
, Boolean
*vpn
, Boolean
*onDemand
)
1031 const struct netagent
*agent
= NULL
;
1034 if (dictionary
== NULL
|| vpn
== NULL
|| onDemand
== NULL
) {
1041 agent
= xpc_dictionary_get_data(dictionary
, REACHABILITY_AGENT_DATA_KEY
, &length
);
1042 if (agent
== NULL
|| length
< sizeof(struct netagent
) || length
!= (sizeof(struct netagent
) + agent
->netagent_data_size
)) {
1046 if (strncmp(REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN
, agent
->netagent_domain
, NETAGENT_DOMAINSIZE
) == 0) {
1048 if ((agent
->netagent_flags
& NETAGENT_FLAG_VOLUNTARY
) &&
1049 !(agent
->netagent_flags
& NETAGENT_FLAG_ACTIVE
)) {
1056 nw_path_is_linklocal_direct(nw_path_t path
)
1058 bool is_linklocal_direct
= false;
1060 nw_endpoint_t endpoint
= nw_path_copy_endpoint(path
);
1061 if (endpoint
== NULL
) {
1065 if (nw_endpoint_get_type(endpoint
) == nw_endpoint_type_address
) {
1066 const struct sockaddr_in
*sin
;
1068 sin
= (const struct sockaddr_in
*)(void *)nw_endpoint_get_address(endpoint
);;
1069 if ((sin
!= NULL
) &&
1070 (sin
->sin_family
== AF_INET
) &&
1071 IN_LINKLOCAL(ntohl(sin
->sin_addr
.s_addr
))) {
1072 nw_interface_t interface
= nw_path_copy_interface(path
);
1074 if (interface
!= NULL
) {
1075 nw_interface_type_t type
= nw_interface_get_type(interface
);
1076 if ((type
== nw_interface_type_wired
) ||
1077 ((type
== nw_interface_type_wifi
) &&
1078 (nw_interface_get_subtype(interface
) != nw_interface_subtype_wifi_awdl
))) {
1079 is_linklocal_direct
= true;
1082 network_release(interface
);
1087 network_release(endpoint
);
1088 return is_linklocal_direct
;
1091 static SCNetworkReachabilityFlags
1092 __SCNetworkReachabilityGetFlagsFromPath(nw_path_t path
,
1093 ReachabilityAddressType type
,
1094 nw_resolver_status_t resolverStatus
,
1095 nw_array_t resolvedEndpoints
,
1096 Boolean resolvedEndpointUseFlags
,
1097 SCNetworkReachabilityFlags resolvedEndpointFlags
)
1099 __block SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
1100 __block
const char *why
= "???";
1102 nw_path_status_t status
= nw_path_get_status(path
);
1103 if (status
== nw_path_status_satisfied
) {
1104 __block
bool checkDNSFlags
= TRUE
;
1105 flags
= kSCNetworkReachabilityFlagsReachable
;
1106 why
= "nw_path_status_satisfied";
1107 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1108 if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1109 flags
|= (kSCNetworkReachabilityFlagsTransientConnection
| kSCNetworkReachabilityFlagsIsWWAN
);
1110 why
= "nw_path_status_satisfied, cellular";
1112 #endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1113 xpc_object_t agent_dictionary
= nw_path_copy_netagent_dictionary(path
);
1114 if (agent_dictionary
!= NULL
) {
1115 if (xpc_dictionary_get_count(agent_dictionary
) > 0) {
1116 xpc_dictionary_apply(agent_dictionary
, ^bool(const char *key
, xpc_object_t value
) {
1118 Boolean vpn
= FALSE
;
1119 Boolean onDemand
= FALSE
;
1120 __SCNetworkReachabilityGetAgentVPNFlags(value
, &vpn
, &onDemand
);
1122 // VPN flows are transient
1123 flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1124 why
= "nw_path_status_satisfied, VPN";
1127 type
== reachabilityTypeName
&&
1128 resolverStatus
== nw_resolver_status_complete
&&
1129 nw_array_get_count(resolvedEndpoints
) == 0) {
1130 // On Demand by hostname, when no address has been resolved
1131 flags
|= (kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsConnectionOnDemand
);
1132 why
= "nw_path_status_satisfied, OnDemand";
1133 checkDNSFlags
= FALSE
;
1139 xpc_release(agent_dictionary
);
1141 if (isReachabilityTypeName(type
)) {
1142 if (checkDNSFlags
) {
1143 if (resolverStatus
== nw_resolver_status_complete
&&
1144 nw_array_get_count(resolvedEndpoints
) == 0) {
1145 // DNS didn't resolve, as a final answer for now. Not reachable!
1147 why
= "nw_path_status_satisfied, DNS not reachable";
1148 } else if (resolvedEndpointUseFlags
) {
1149 flags
= resolvedEndpointFlags
;
1150 why
= "nw_path_status_satisfied, resolved endpoint flags";
1154 if (nw_path_is_direct(path
) || nw_path_is_linklocal_direct(path
)) {
1155 flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1156 why
= "nw_path_status_satisfied, by address, direct";
1158 if (nw_path_is_local(path
)) {
1159 flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1160 why
= "nw_path_status_satisfied, by address, local";
1163 } else if (status
== nw_path_status_unsatisfied
) {
1165 why
= "nw_path_status_unsatisfied";
1166 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1167 if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1168 flags
|= kSCNetworkReachabilityFlagsIsWWAN
;
1169 why
= "nw_path_status_unsatisfied, WWAN";
1171 #endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1172 } else if (status
== nw_path_status_satisfiable
) {
1173 flags
= (kSCNetworkReachabilityFlagsReachable
| kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsTransientConnection
);
1174 why
= "nw_path_status_satisfiable";
1176 if (nw_path_get_vpn_config_id(path
, &vpn_uuid
)) {
1177 flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
1178 why
= "nw_path_status_satisfiable, OnDemand";
1180 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1181 else if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1182 flags
|= kSCNetworkReachabilityFlagsIsWWAN
;
1183 why
= "nw_path_status_satisfiable, WWAN";
1185 #endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1188 SC_log(LOG_DEBUG
, "__SCNetworkReachabilityGetFlagsFromPath, flags = 0x%08x, %s", flags
, why
);
1189 return (flags
& kSCNetworkReachabilityFlagsMask
);
1192 static nw_endpoint_t
1193 __SCNetworkReachabilityGetPrimaryEndpoint(SCNetworkReachabilityPrivateRef targetPrivate
)
1195 if (targetPrivate
->type
== reachabilityTypeName
) {
1196 return targetPrivate
->hostnameEndpoint
;
1197 } else if (targetPrivate
->type
== reachabilityTypeAddress
||
1198 targetPrivate
->type
== reachabilityTypeAddressPair
||
1199 targetPrivate
->type
== reachabilityTypePTR
) {
1200 return targetPrivate
->remoteAddressEndpoint
;
1206 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target
)
1210 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1211 SCNetworkReachabilityFlags flags
= 0;
1213 if (!isA_SCNetworkReachability(target
)) {
1214 _SCErrorSet(kSCStatusInvalidArgument
);
1218 MUTEX_LOCK(&targetPrivate
->lock
);
1220 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1221 targetPrivate
->type
,
1222 nw_resolver_status_invalid
,
1224 targetPrivate
->lastResolvedEndpointHasFlags
,
1225 targetPrivate
->lastResolvedEndpointFlags
);
1227 /* Only return the if_index if the connection is reachable not for reachable connection
1228 * required etc ... */
1229 if (ok
&& __SCNetworkReachabilityRank(flags
) == ReachabilityRankReachable
) {
1230 if (targetPrivate
->lastResolvedEndpointHasFlags
) {
1231 if_index
= targetPrivate
->lastResolvedEndpointInterfaceIndex
;
1233 if_index
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1237 MUTEX_UNLOCK(&targetPrivate
->lock
);
1241 // CrazyIvan46 is the feature that allows connections to IPv4 literals on IPv6-only (NAT64+DNS64) networks to work
1242 // This function replaces the path when the initial one isn't satisfied and our target is an IPv4 literal
1243 // It tries IPv6 reachability instead in case we could synthesize another address to connect to
1244 static OS_OBJECT_RETURNS_RETAINED nw_path_t
1245 __SCNetworkReachabilityCreateCrazyIvan46Path(nw_path_t path
, nw_endpoint_t endpoint
,
1246 nw_parameters_t parameters
, Boolean allow_resolution
)
1248 nw_path_t retPath
= NULL
;
1249 const struct sockaddr
*sa
;
1251 if ((nw_path_get_status(path
) != nw_path_status_unsatisfied
) ||
1252 (NULL
== endpoint
) || (nw_endpoint_get_type(endpoint
) != nw_endpoint_type_address
)) {
1256 sa
= nw_endpoint_get_address(endpoint
);
1258 if (sa
->sa_family
!= AF_INET
) {
1262 if (allow_resolution
) {
1263 uint32_t ifIndex
= 0;
1264 int32_t numPrefixes
;
1265 nw_nat64_prefix_t
*prefixes
= NULL
;
1266 struct sockaddr_in sin
;
1268 memcpy(&sin
, sa
, MIN(sizeof(sin
), sa
->sa_len
));
1269 if (NULL
!= parameters
) {
1270 ifIndex
= nw_parameters_get_required_interface_index(parameters
);
1272 numPrefixes
= nw_nat64_copy_prefixes(&ifIndex
, &prefixes
);
1273 if (numPrefixes
> 0) {
1274 struct sockaddr_in6 synthesizedAddress
= {
1275 .sin6_len
= sizeof(struct sockaddr_in6
),
1276 .sin6_family
= AF_INET6
,
1277 #if defined(NW_PORT_HOST_BYTE_ORDER) && NW_PORT_HOST_BYTE_ORDER
1278 .sin6_port
= htons(nw_endpoint_get_port(endpoint
)),
1280 .sin6_port
= nw_endpoint_get_port(endpoint
),
1285 nw_endpoint_t synthesizedEndpoint
;
1286 nw_path_evaluator_t synthesizedEvaluator
;
1287 nw_path_t synthesizedPath
;
1289 nw_nat64_synthesize_v6(&prefixes
[0], &sin
.sin_addr
, &synthesizedAddress
.sin6_addr
);
1290 synthesizedEndpoint
= nw_endpoint_create_address((const struct sockaddr
*)&synthesizedAddress
);
1291 synthesizedEvaluator
= nw_path_create_evaluator_for_endpoint(synthesizedEndpoint
, parameters
);
1292 synthesizedPath
= nw_path_evaluator_copy_path(synthesizedEvaluator
);
1293 if (nw_path_get_status(synthesizedPath
) != nw_path_status_unsatisfied
) {
1294 retPath
= synthesizedPath
;
1295 SC_log(LOG_INFO
, "Using CrazyIvan46 synthesized reachability result");
1297 network_release(synthesizedPath
);
1299 network_release(synthesizedEvaluator
);
1300 network_release(synthesizedEndpoint
);
1303 // Don't synthesize in non-scheduled mode to avoid generating DNS traffic
1305 nw_path_evaluator_t v6PathEvaluator
;
1306 nw_parameters_t v6Parameters
;
1308 if (NULL
!= parameters
) {
1309 v6Parameters
= nw_parameters_copy(parameters
);
1311 v6Parameters
= nw_parameters_create();
1313 nw_parameters_set_required_address_family(v6Parameters
, AF_INET6
);
1314 v6PathEvaluator
= nw_path_create_evaluator_for_endpoint(NULL
, v6Parameters
);
1315 v6Path
= nw_path_evaluator_copy_path(v6PathEvaluator
);
1316 if (nw_path_get_status(v6Path
) != nw_path_status_unsatisfied
) {
1318 SC_log(LOG_INFO
, "Using CrazyIvan46 simple reachability result");
1320 network_release(v6Path
);
1322 network_release(v6PathEvaluator
);
1323 network_release(v6Parameters
);
1329 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
1330 SCNetworkReachabilityFlags
*flags
)
1332 nw_path_t crazyIvanPath
;
1333 nw_endpoint_t endpoint
;
1336 nw_path_evaluator_t pathEvaluator
;
1337 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1339 if (!isA_SCNetworkReachability(target
)) {
1340 _SCErrorSet(kSCStatusInvalidArgument
);
1344 MUTEX_LOCK(&targetPrivate
->lock
);
1346 if (targetPrivate
->scheduled
) {
1347 // if being watched, return the last known (and what should be current) status
1348 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1349 targetPrivate
->type
,
1350 targetPrivate
->lastResolverStatus
,
1351 targetPrivate
->lastResolvedEndpoints
,
1352 targetPrivate
->lastResolvedEndpointHasFlags
,
1353 targetPrivate
->lastResolvedEndpointFlags
);
1354 // because we have synchronously captured the current status, we no longer
1355 // need our by-name required callback
1356 targetPrivate
->sentFirstUpdate
= TRUE
;
1360 // Not being watched, so run a one-shot path evaluator
1361 // We don't care about DNS resolution in this case, since we only need to have the
1362 // DNS resolution to support clients watching reachability to get updates
1363 endpoint
= __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
);
1364 pathEvaluator
= nw_path_create_evaluator_for_endpoint(endpoint
, targetPrivate
->parameters
);
1365 path
= nw_path_evaluator_copy_path(pathEvaluator
);
1367 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1368 crazyIvanPath
= __SCNetworkReachabilityCreateCrazyIvan46Path(path
, endpoint
,
1369 targetPrivate
->parameters
, FALSE
);
1370 if (NULL
!= crazyIvanPath
) {
1371 network_release(path
);
1372 path
= crazyIvanPath
;
1376 *flags
= __SCNetworkReachabilityGetFlagsFromPath(path
, 0, nw_resolver_status_invalid
, NULL
, FALSE
, 0);
1377 network_release(path
);
1378 network_release(pathEvaluator
);
1382 MUTEX_UNLOCK(&targetPrivate
->lock
);
1388 #pragma mark Notifications
1391 reachPerformAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1393 os_activity_t activity
;
1395 void (*context_release
)(const void *);
1396 SCNetworkReachabilityCallBack rlsFunction
;
1397 SCNetworkReachabilityFlags flags
= 0;
1399 activity
= os_activity_create("processing SCNetworkReachability notification",
1400 OS_ACTIVITY_CURRENT
,
1401 OS_ACTIVITY_FLAG_DEFAULT
);
1402 os_activity_scope(activity
);
1404 if (!targetPrivate
->scheduled
) {
1405 // if no longer scheduled
1406 SC_log(LOG_INFO
, "%sskipping SCNetworkReachability callback, no longer scheduled",
1407 targetPrivate
->log_prefix
);
1408 MUTEX_UNLOCK(&targetPrivate
->lock
);
1413 rlsFunction
= targetPrivate
->rlsFunction
;
1414 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
1415 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
1416 context_release
= targetPrivate
->rlsContext
.release
;
1418 context_info
= targetPrivate
->rlsContext
.info
;
1419 context_release
= NULL
;
1422 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1423 targetPrivate
->type
,
1424 targetPrivate
->lastResolverStatus
,
1425 targetPrivate
->lastResolvedEndpoints
,
1426 targetPrivate
->lastResolvedEndpointHasFlags
,
1427 targetPrivate
->lastResolvedEndpointFlags
);
1429 MUTEX_UNLOCK(&targetPrivate
->lock
);
1431 if (rlsFunction
!= NULL
) {
1432 SC_log(LOG_DEBUG
, "%sexec SCNetworkReachability callout w/flags = 0x%08x",
1433 targetPrivate
->log_prefix
,
1435 (*rlsFunction
)((SCNetworkReachabilityRef
)targetPrivate
,
1440 if (context_release
!= NULL
) {
1441 (*context_release
)(context_info
);
1446 os_release(activity
);
1452 reachPerform(void *info
)
1454 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)info
;
1456 MUTEX_LOCK(&targetPrivate
->lock
);
1457 reachPerformAndUnlock(targetPrivate
);
1462 reachUpdateAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1464 targetPrivate
->sentFirstUpdate
= TRUE
;
1465 if (targetPrivate
->rls
!= NULL
) {
1466 if (targetPrivate
->rlList
!= NULL
) {
1467 CFRunLoopSourceSignal(targetPrivate
->rls
);
1468 _SC_signalRunLoop(targetPrivate
, targetPrivate
->rls
, targetPrivate
->rlList
);
1470 MUTEX_UNLOCK(&targetPrivate
->lock
);
1472 reachPerformAndUnlock(targetPrivate
);
1477 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
1478 SCNetworkReachabilityCallBack callout
,
1479 SCNetworkReachabilityContext
*context
)
1481 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1483 MUTEX_LOCK(&targetPrivate
->lock
);
1485 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1486 /* let go of the current context */
1487 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1490 targetPrivate
->rlsFunction
= callout
;
1491 targetPrivate
->rlsContext
.info
= NULL
;
1492 targetPrivate
->rlsContext
.retain
= NULL
;
1493 targetPrivate
->rlsContext
.release
= NULL
;
1494 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1496 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
1497 if (context
->retain
!= NULL
) {
1498 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
1502 MUTEX_UNLOCK(&targetPrivate
->lock
);
1509 reachRLSCopyDescription(const void *info
)
1511 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1513 return CFStringCreateWithFormat(NULL
,
1515 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
1520 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
1521 CFRunLoopRef runLoop
,
1522 CFStringRef runLoopMode
)
1524 Boolean success
= FALSE
;
1525 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1526 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1527 _SCErrorSet(kSCStatusInvalidArgument
);
1531 MUTEX_LOCK(&targetPrivate
->lock
);
1533 if (targetPrivate
->scheduled
) {
1534 if (targetPrivate
->rls
!= NULL
&& targetPrivate
->rlList
!= NULL
) {
1535 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1537 * if we do not already have host notifications scheduled with
1538 * this runLoop / runLoopMode
1540 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1543 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
1545 MUTEX_UNLOCK(&targetPrivate
->lock
);
1548 MUTEX_UNLOCK(&targetPrivate
->lock
);
1549 _SCErrorSet(kSCStatusInvalidArgument
);
1554 CFRunLoopSourceContext context
= {
1556 , (void *)target
// info
1557 , CFRetain
// retain
1558 , CFRelease
// release
1559 , reachRLSCopyDescription
// copyDescription
1564 , reachPerform
// perform
1567 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
1568 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1570 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1572 * if we do not already have host notifications scheduled with
1573 * this runLoop / runLoopMode
1575 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1578 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
1580 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, _callback_queue());
1582 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1583 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1584 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1585 // if target is no longer scheduled for this runLoop / runLoopMode
1586 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1588 // if *all* notifications have been unscheduled
1589 CFRelease(targetPrivate
->rlList
);
1590 targetPrivate
->rlList
= NULL
;
1591 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1592 CFRelease(targetPrivate
->rls
);
1593 targetPrivate
->rls
= NULL
;
1599 MUTEX_UNLOCK(&targetPrivate
->lock
);
1604 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
1605 CFRunLoopRef runLoop
,
1606 CFStringRef runLoopMode
)
1608 Boolean success
= FALSE
;
1609 Boolean unscheduleDispatchQueue
= FALSE
;
1610 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1612 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1613 _SCErrorSet(kSCStatusInvalidArgument
);
1617 MUTEX_LOCK(&targetPrivate
->lock
);
1619 if (targetPrivate
->rlList
== NULL
|| targetPrivate
->rls
== NULL
|| !targetPrivate
->scheduled
) {
1620 MUTEX_UNLOCK(&targetPrivate
->lock
);
1621 _SCErrorSet(kSCStatusInvalidArgument
);
1625 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1626 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1627 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1628 // if target is no longer scheduled for this runLoop / runLoopMode
1629 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1631 // if *all* notifications have been unscheduled
1632 unscheduleDispatchQueue
= TRUE
;
1633 CFRelease(targetPrivate
->rlList
);
1634 targetPrivate
->rlList
= NULL
;
1635 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1636 CFRelease(targetPrivate
->rls
);
1637 targetPrivate
->rls
= NULL
;
1642 if (unscheduleDispatchQueue
) {
1643 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, NULL
);
1647 MUTEX_UNLOCK(&targetPrivate
->lock
);
1651 static __inline__
void
1652 __SCNetworkReachabilityCopyPathStatus(SCNetworkReachabilityPrivateRef targetPrivate
, SCNetworkReachabilityFlags
*flags
, uint
*ifIndex
, size_t *endpointCount
)
1655 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1656 targetPrivate
->type
,
1657 targetPrivate
->lastResolverStatus
,
1658 targetPrivate
->lastResolvedEndpoints
,
1659 targetPrivate
->lastResolvedEndpointHasFlags
,
1660 targetPrivate
->lastResolvedEndpointFlags
);
1663 *ifIndex
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1665 if (endpointCount
) {
1666 *endpointCount
= nw_array_get_count(targetPrivate
->lastResolvedEndpoints
);
1671 static __inline__ Boolean
1672 __SCNetworkReachabilityShouldUpdateClient(SCNetworkReachabilityPrivateRef targetPrivate
, SCNetworkReachabilityFlags oldFlags
, uint oldIFIndex
, size_t oldEndpointCount
)
1674 SCNetworkReachabilityFlags newFlags
= 0;
1675 uint newIFIndex
= 0;
1676 size_t newEndpointCount
= 0;
1677 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &newFlags
, &newIFIndex
, &newEndpointCount
);
1678 return (!targetPrivate
->sentFirstUpdate
||
1679 oldFlags
!= newFlags
||
1680 oldIFIndex
!= newIFIndex
||
1681 oldEndpointCount
!= newEndpointCount
);
1685 __SCNetworkReachabilityRestartResolver(SCNetworkReachabilityPrivateRef targetPrivate
)
1687 if (targetPrivate
&&
1688 !targetPrivate
->resolverBypass
&&
1689 isReachabilityTypeName(targetPrivate
->type
)) {
1690 nw_resolver_t resolver
;
1691 CFRetain(targetPrivate
);
1692 if (NULL
!= targetPrivate
->resolver
) {
1693 nw_resolver_cancel(targetPrivate
->resolver
);
1695 if (targetPrivate
->lastPath
!= NULL
) {
1696 resolver
= nw_resolver_create_with_path(targetPrivate
->lastPath
);
1698 resolver
= nw_resolver_create_with_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
), targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
: targetPrivate
->parameters
);
1700 if (resolver
== NULL
) {
1701 SC_log(LOG_ERR
, "%sfailed to create a nw_resolver", targetPrivate
->log_prefix
);
1702 targetPrivate
->resolver
= NULL
;
1703 CFRelease(targetPrivate
);
1706 targetPrivate
->resolver
= resolver
;
1707 nw_resolver_set_cancel_handler(resolver
, ^(void) {
1708 MUTEX_LOCK(&targetPrivate
->lock
);
1709 if (resolver
== targetPrivate
->resolver
) {
1710 targetPrivate
->resolver
= NULL
;
1712 network_release(resolver
);
1713 MUTEX_UNLOCK(&targetPrivate
->lock
);
1714 CFRelease(targetPrivate
);
1716 if (!nw_resolver_set_update_handler(resolver
, targetPrivate
->dispatchQueue
, ^(nw_resolver_status_t status
, nw_array_t resolved_endpoints
) {
1717 MUTEX_LOCK(&targetPrivate
->lock
);
1718 if (targetPrivate
->scheduled
) {
1719 SCNetworkReachabilityFlags oldFlags
= 0;
1720 uint oldIFIndex
= 0;
1721 size_t oldEndpointCount
= 0;
1722 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &oldFlags
, &oldIFIndex
, &oldEndpointCount
);
1724 targetPrivate
->lastResolverStatus
= status
;
1725 network_release(targetPrivate
->lastResolvedEndpoints
);
1726 targetPrivate
->lastResolvedEndpoints
= network_retain(resolved_endpoints
);
1728 // Run path evaluation on the resolved endpoints
1729 __block Boolean hasFlags
= FALSE
;
1730 targetPrivate
->lastResolvedEndpointHasFlags
= FALSE
;
1731 targetPrivate
->lastResolvedEndpointFlags
= 0;
1732 targetPrivate
->lastResolvedEndpointInterfaceIndex
= 0;
1733 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
1734 #pragma unused(index)
1735 SCNetworkReachabilityFlags flags
= 0;
1736 uint interfaceIndex
= 0;
1737 ReachabilityRankType rank
;
1738 nw_endpoint_t resolvedEndpoint
= (nw_endpoint_t
)object
;
1739 nw_path_evaluator_t pathEvaluator
= nw_path_create_evaluator_for_endpoint(resolvedEndpoint
, targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
: targetPrivate
->parameters
);
1740 nw_path_t path
= nw_path_evaluator_copy_path(pathEvaluator
);
1742 flags
= __SCNetworkReachabilityGetFlagsFromPath(path
, 0, nw_resolver_status_invalid
, NULL
, FALSE
, 0);
1745 interfaceIndex
= nw_path_get_interface_index(path
);
1746 network_release(path
);
1747 network_release(pathEvaluator
);
1749 rank
= __SCNetworkReachabilityRank(flags
);
1750 if (rank
> __SCNetworkReachabilityRank(targetPrivate
->lastResolvedEndpointFlags
)) {
1751 // Return the best case result
1752 targetPrivate
->lastResolvedEndpointFlags
= flags
;
1753 targetPrivate
->lastResolvedEndpointInterfaceIndex
= interfaceIndex
;
1754 if (rank
== ReachabilityRankReachable
) {
1755 // Can't get any better than REACHABLE
1761 targetPrivate
->lastResolvedEndpointHasFlags
= hasFlags
;
1763 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1764 reachUpdateAndUnlock(targetPrivate
);
1766 MUTEX_UNLOCK(&targetPrivate
->lock
);
1769 MUTEX_UNLOCK(&targetPrivate
->lock
);
1772 network_release(resolver
);
1773 targetPrivate
->resolver
= NULL
;
1774 CFRelease(targetPrivate
);
1780 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
1781 dispatch_queue_t queue
)
1785 if (queue
!= NULL
) {
1786 nw_path_t crazyIvanPath
;
1787 nw_endpoint_t endpoint
;
1788 nw_path_evaluator_t pathEvaluator
;
1790 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
1791 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
1792 _SCErrorSet(kSCStatusInvalidArgument
);
1796 SC_log(LOG_DEBUG
, "%sscheduled", targetPrivate
->log_prefix
);
1798 // retain dispatch queue
1799 dispatch_retain(queue
);
1800 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1801 endpoint
= __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
);
1802 pathEvaluator
= nw_path_create_evaluator_for_endpoint(endpoint
, targetPrivate
->parameters
);
1803 targetPrivate
->pathEvaluator
= pathEvaluator
;
1804 targetPrivate
->dispatchQueue
= queue
;
1805 targetPrivate
->scheduled
= TRUE
;
1806 if (isReachabilityTypeName(targetPrivate
->type
)) {
1807 // we must have at least one callback for by-name queries
1808 targetPrivate
->sentFirstUpdate
= FALSE
;
1810 targetPrivate
->sentFirstUpdate
= TRUE
;
1813 network_release(targetPrivate
->lastPath
);
1814 targetPrivate
->lastPath
= nw_path_evaluator_copy_path(pathEvaluator
);
1816 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1817 crazyIvanPath
= __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate
->lastPath
, endpoint
,
1818 targetPrivate
->parameters
, FALSE
);
1819 if (NULL
!= crazyIvanPath
) {
1820 network_release(targetPrivate
->lastPath
);
1821 targetPrivate
->lastPath
= crazyIvanPath
;
1825 network_release(targetPrivate
->lastPathParameters
);
1826 targetPrivate
->lastPathParameters
= nw_path_copy_derived_parameters(targetPrivate
->lastPath
);
1828 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1829 network_release(targetPrivate
->lastResolvedEndpoints
);
1830 targetPrivate
->lastResolvedEndpoints
= NULL
;
1831 __SCNetworkReachabilityRestartResolver(targetPrivate
);
1833 CFRetain(targetPrivate
);
1834 nw_path_evaluator_set_cancel_handler(pathEvaluator
, ^(void) {
1835 MUTEX_LOCK(&targetPrivate
->lock
);
1836 if (pathEvaluator
== targetPrivate
->pathEvaluator
) {
1837 targetPrivate
->pathEvaluator
= NULL
;
1839 network_release(pathEvaluator
);
1840 MUTEX_UNLOCK(&targetPrivate
->lock
);
1841 CFRelease(targetPrivate
);
1844 if (!nw_path_evaluator_set_update_handler(pathEvaluator
, targetPrivate
->dispatchQueue
, ^(nw_path_t path
) {
1845 MUTEX_LOCK(&targetPrivate
->lock
);
1846 if (targetPrivate
->scheduled
) {
1847 nw_path_t crazyIvanPath
;
1848 SCNetworkReachabilityFlags oldFlags
= 0;
1849 uint oldIFIndex
= 0;
1850 size_t oldEndpointCount
= 0;
1851 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &oldFlags
, &oldIFIndex
, &oldEndpointCount
);
1853 network_release(targetPrivate
->lastPath
);
1854 targetPrivate
->lastPath
= network_retain(path
);
1856 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1858 __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate
->lastPath
,
1860 targetPrivate
->parameters
,
1862 if (NULL
!= crazyIvanPath
) {
1863 network_release(targetPrivate
->lastPath
);
1864 targetPrivate
->lastPath
= crazyIvanPath
;
1868 if (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) {
1869 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1870 __SCNetworkReachabilityRestartResolver(targetPrivate
);
1873 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1874 reachUpdateAndUnlock(targetPrivate
);
1876 MUTEX_UNLOCK(&targetPrivate
->lock
);
1879 MUTEX_UNLOCK(&targetPrivate
->lock
);
1882 targetPrivate
->pathEvaluator
= NULL
;
1883 network_release(pathEvaluator
);
1884 CFRelease(targetPrivate
);
1887 if (targetPrivate
->dispatchQueue
== NULL
) { // if we should be scheduled on a dispatch queue (but are not)
1888 _SCErrorSet(kSCStatusInvalidArgument
);
1892 if (!targetPrivate
->scheduled
) {
1893 // if not currently scheduled
1894 _SCErrorSet(kSCStatusInvalidArgument
);
1898 targetPrivate
->scheduled
= FALSE
;
1899 targetPrivate
->sentFirstUpdate
= FALSE
;
1900 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1901 targetPrivate
->pathEvaluator
= NULL
;
1902 network_release(targetPrivate
->lastPath
);
1903 targetPrivate
->lastPath
= NULL
;
1904 network_release(targetPrivate
->lastPathParameters
);
1905 targetPrivate
->lastPathParameters
= NULL
;
1906 network_release(targetPrivate
->lastResolvedEndpoints
);
1907 targetPrivate
->lastResolvedEndpoints
= NULL
;
1908 if (NULL
!= targetPrivate
->resolver
) {
1909 nw_resolver_cancel(targetPrivate
->resolver
);
1910 targetPrivate
->resolver
= NULL
;
1912 if (targetPrivate
->dispatchQueue
!= NULL
) {
1913 dispatch_release(targetPrivate
->dispatchQueue
);
1914 targetPrivate
->dispatchQueue
= NULL
;
1917 SC_log(LOG_DEBUG
, "%sunscheduled", targetPrivate
->log_prefix
);
1925 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
1926 dispatch_queue_t queue
)
1928 if (!isA_SCNetworkReachability(target
)) {
1929 _SCErrorSet(kSCStatusInvalidArgument
);
1933 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1934 MUTEX_LOCK(&targetPrivate
->lock
);
1935 Boolean success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, queue
);
1936 MUTEX_UNLOCK(&targetPrivate
->lock
);
1941 * _SC_checkResolverReachabilityByAddress()
1943 * Given an IP address, determine whether a reverse DNS query can be issued
1944 * using the current network configuration.
1947 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
1948 SCNetworkReachabilityFlags
*flags
,
1950 struct sockaddr
*sa
)
1952 #pragma unused(storeP)
1954 nw_path_evaluator_t evaluator
= nw_path_create_default_evaluator();
1955 nw_path_t path
= nw_path_evaluator_copy_path(evaluator
);
1956 if (nw_path_get_status(path
) == nw_path_status_unsatisfied_network
) {
1965 *flags
= kSCNetworkReachabilityFlagsReachable
;
1971 network_release(evaluator
);
1972 network_release(path
);