2 * Copyright (c) 2003-2019 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 <TargetConditionals.h>
38 #include <sys/cdefs.h>
39 #include <dispatch/dispatch.h>
40 #include <dispatch/private.h>
42 #include <libkern/OSAtomic.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
50 #include <sys/ioctl.h>
51 #include <sys/socket.h>
53 #include <net/if_dl.h>
54 #include <net/if_types.h>
55 #include <net/network_agent.h>
57 #include <CoreFoundation/CoreFoundation.h>
58 #include <CoreFoundation/CFRuntime.h>
60 #define SC_LOG_HANDLE __log_SCNetworkReachability
61 #define SC_LOG_HANDLE_TYPE static
62 #include <SystemConfiguration/SystemConfiguration.h>
63 #include <SystemConfiguration/SCValidation.h>
64 #include <SystemConfiguration/SCPrivate.h>
66 #include "SCNetworkReachabilityInternal.h"
70 #include <nw/private.h>
72 #define DEBUG_REACHABILITY_TYPE_NAME "create w/name"
73 #define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
75 #define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"
76 #define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
78 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"
79 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
81 #define DEBUG_REACHABILITY_TYPE_PTR "create w/ptr"
82 #define DEBUG_REACHABILITY_TYPE_PTR_OPTIONS " + options"
84 static pthread_mutexattr_t lock_attr
;
86 #define MUTEX_INIT(m) { \
87 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \
91 #define MUTEX_LOCK(m) { \
92 int _lock_ = (pthread_mutex_lock(m) == 0); \
96 #define MUTEX_UNLOCK(m) { \
97 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
101 #define MUTEX_ASSERT_HELD(m) { \
102 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \
109 #define REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN "NetworkExtension"
110 #define REACHABILITY_AGENT_DATA_KEY "data"
113 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
114 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
116 static SCNetworkReachabilityFlags
117 __SCNetworkReachabilityGetFlagsFromPath(nw_path_t path
,
118 ReachabilityAddressType type
,
119 nw_resolver_status_t resolverStatus
,
120 nw_array_t resolvedEndpoints
,
121 Boolean resolvedEndpointUseFlags
,
122 SCNetworkReachabilityFlags resolvedEndpointFlags
);
125 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
126 dispatch_queue_t queue
);
128 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
131 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
133 "SCNetworkReachability", // className
136 __SCNetworkReachabilityDeallocate
, // dealloc
139 NULL
, // copyFormattingDesc
140 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
144 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
146 static dispatch_queue_t
149 static dispatch_once_t once
;
150 static dispatch_queue_t q
;
152 dispatch_once(&once
, ^{
153 q
= dispatch_queue_create("SCNetworkReachability.callback", NULL
);
160 __log_SCNetworkReachability(void)
162 static os_log_t log
= NULL
;
165 log
= os_log_create("com.apple.SystemConfiguration", "SCNetworkReachability");
173 #pragma mark SCNetworkReachability APIs
176 static __inline__ CFTypeRef
177 isA_SCNetworkReachability(CFTypeRef obj
)
179 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
183 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target
)
185 CFAllocatorRef allocator
= CFGetAllocator(target
);
186 CFMutableStringRef str
;
187 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
189 str
= CFStringCreateMutable(allocator
, 0);
190 switch (targetPrivate
->type
) {
191 case reachabilityTypeAddress
:
192 case reachabilityTypeAddressPair
: {
194 if (targetPrivate
->localAddressEndpoint
!= NULL
) {
195 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->localAddressEndpoint
), buf
, sizeof(buf
));
196 CFStringAppendFormat(str
, NULL
, CFSTR("local address = %s"),
199 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
200 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
201 CFStringAppendFormat(str
, NULL
, CFSTR("%s%saddress = %s"),
202 targetPrivate
->localAddressEndpoint
? ", " : "",
203 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
206 CFStringAppendFormat(str
, NULL
, CFSTR("default path"));
210 case reachabilityTypeName
: {
211 CFStringAppendFormat(str
, NULL
, CFSTR("name = %s"), nw_endpoint_get_hostname(targetPrivate
->hostnameEndpoint
));
214 case reachabilityTypePTR
: {
216 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
217 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
218 CFStringAppendFormat(str
, NULL
, CFSTR("ptr = %s"),
225 if (targetPrivate
->parameters
!= NULL
) {
226 unsigned int if_index
;
228 if_index
= nw_parameters_get_required_interface_index(targetPrivate
->parameters
);
230 CFStringAppendFormat(str
, NULL
, CFSTR(", if_index = %u"), if_index
);
239 __SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target
)
241 CFAllocatorRef allocator
= CFGetAllocator(target
);
243 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
245 str
= CFStringCreateWithFormat(allocator
,
247 CFSTR("flags = 0x%08x, if_index = %u"),
248 __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
250 targetPrivate
->lastResolverStatus
,
251 targetPrivate
->lastResolvedEndpoints
,
252 targetPrivate
->lastResolvedEndpointHasFlags
,
253 targetPrivate
->lastResolvedEndpointFlags
),
254 targetPrivate
->lastResolvedEndpointHasFlags
? targetPrivate
->lastResolvedEndpointInterfaceIndex
255 : nw_path_get_interface_index(targetPrivate
->lastPath
));
261 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
263 CFAllocatorRef allocator
= CFGetAllocator(cf
);
264 CFMutableStringRef result
;
266 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
267 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
269 MUTEX_LOCK(&targetPrivate
->lock
);
271 result
= CFStringCreateMutable(allocator
, 0);
272 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
274 // add target description
275 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
276 CFStringAppend(result
, str
);
279 // add additional "name" info
280 if (isReachabilityTypeName(targetPrivate
->type
)) {
281 if (targetPrivate
->resolver
&& targetPrivate
->lastResolverStatus
== nw_resolver_status_invalid
) {
282 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
283 } else if (targetPrivate
->lastResolverStatus
!= nw_resolver_status_invalid
) {
284 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s"), (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) ? "complete" : "in progress");
285 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
286 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
287 #pragma unused(index)
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 nw_release(targetPrivate
->hostnameEndpoint
);
342 targetPrivate
->hostnameEndpoint
= NULL
;
344 if (targetPrivate
->localAddressEndpoint
) {
345 nw_release(targetPrivate
->localAddressEndpoint
);
346 targetPrivate
->localAddressEndpoint
= NULL
;
348 if (targetPrivate
->remoteAddressEndpoint
) {
349 nw_release(targetPrivate
->remoteAddressEndpoint
);
350 targetPrivate
->remoteAddressEndpoint
= NULL
;
352 if (targetPrivate
->parameters
) {
353 nw_release(targetPrivate
->parameters
);
354 targetPrivate
->parameters
= NULL
;
356 if (targetPrivate
->lastPath
) {
357 nw_release(targetPrivate
->lastPath
);
358 targetPrivate
->lastPath
= NULL
;
360 if (targetPrivate
->lastPathParameters
) {
361 nw_release(targetPrivate
->lastPathParameters
);
362 targetPrivate
->lastPathParameters
= NULL
;
364 if (targetPrivate
->lastResolvedEndpoints
) {
365 nw_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
);
413 if (_sc_log
> kSCLogDestinationFile
) {
414 snprintf(targetPrivate
->log_prefix
,
415 sizeof(targetPrivate
->log_prefix
),
420 return targetPrivate
;
423 static const struct sockaddr
*
424 is_valid_address(const struct sockaddr
*address
)
426 const struct sockaddr
*valid
= NULL
;
427 static Boolean warned
= FALSE
;
429 if ((address
!= NULL
) &&
430 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
431 switch (address
->sa_family
) {
433 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
435 } else if (!warned
) {
436 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
438 sizeof(struct sockaddr_in
));
443 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
445 } else if (!warned
) {
446 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
448 sizeof(struct sockaddr_in6
));
454 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d",
465 __SCNetworkReachabilityAddressIsEmpty(const struct sockaddr
*address
)
467 if (address
== NULL
) {
471 if (address
->sa_family
== AF_INET
) {
472 return (((struct sockaddr_in
*)(void *)address
)->sin_addr
.s_addr
== 0);
473 } else if (address
->sa_family
== AF_INET6
) {
474 return IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6
*)(void *)address
)->sin6_addr
);
480 SCNetworkReachabilityRef
481 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
482 const struct sockaddr
*address
)
484 const struct sockaddr
*targetAddress
;
485 SCNetworkReachabilityPrivateRef targetPrivate
;
487 targetAddress
= is_valid_address(address
);
488 if (targetAddress
== NULL
) {
489 _SCErrorSet(kSCStatusInvalidArgument
);
493 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
494 if (targetPrivate
== NULL
) {
498 targetPrivate
->type
= reachabilityTypeAddress
;
500 if (!__SCNetworkReachabilityAddressIsEmpty(targetAddress
)) {
501 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(targetAddress
);
504 SC_log(LOG_DEBUG
, "%s%s %@",
505 targetPrivate
->log_prefix
,
506 DEBUG_REACHABILITY_TYPE_ADDRESS
,
509 return (SCNetworkReachabilityRef
)targetPrivate
;
513 #if !TARGET_OS_IPHONE
515 is_ipv4_loopback(const struct sockaddr
*sa
)
518 struct sockaddr_in
*sin
= (struct sockaddr_in
*)(void *)sa
;
521 (sa
->sa_len
< sizeof(struct sockaddr_in
)) ||
522 (sa
->sa_family
!= AF_INET
)) {
526 addr
= ntohl(sin
->sin_addr
.s_addr
);
527 return IN_LOOPBACK(addr
) ? TRUE
: FALSE
;
529 #endif // !TARGET_OS_IPHONE
533 is_same_address(const struct sockaddr
*a
, const struct sockaddr
*b
)
541 (a
->sa_family
!= b
->sa_family
) ||
542 (a
->sa_len
!= b
->sa_len
)) {
546 switch (a
->sa_family
) {
548 struct sockaddr_in
*a_sin
= (struct sockaddr_in
*)(void *)a
;
549 struct sockaddr_in
*b_sin
= (struct sockaddr_in
*)(void *)b
;
551 /* ALIGN: assuming a (and b) are aligned, then cast ok. */
552 a_addr
= &a_sin
->sin_addr
;
553 b_addr
= &b_sin
->sin_addr
;
554 len
= sizeof(struct in_addr
);
559 struct sockaddr_in6
*a_sin6
= (struct sockaddr_in6
*)(void *)a
;
560 struct sockaddr_in6
*b_sin6
= (struct sockaddr_in6
*)(void *)b
;
562 if (a_sin6
->sin6_scope_id
!= b_sin6
->sin6_scope_id
) {
566 a_addr
= &a_sin6
->sin6_addr
;
567 b_addr
= &b_sin6
->sin6_addr
;
568 len
= sizeof(struct in6_addr
);
579 return (bcmp(a_addr
, b_addr
, len
) == 0);
583 SCNetworkReachabilityRef
584 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
585 const struct sockaddr
*localAddress
,
586 const struct sockaddr
*remoteAddress
)
588 SCNetworkReachabilityPrivateRef targetPrivate
;
590 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
591 _SCErrorSet(kSCStatusInvalidArgument
);
595 if (localAddress
!= NULL
) {
596 localAddress
= is_valid_address(localAddress
);
597 if (localAddress
== NULL
) {
598 _SCErrorSet(kSCStatusInvalidArgument
);
603 if (remoteAddress
!= NULL
) {
604 remoteAddress
= is_valid_address(remoteAddress
);
605 if (remoteAddress
== NULL
) {
606 _SCErrorSet(kSCStatusInvalidArgument
);
611 #if !TARGET_OS_IPHONE
612 // Check/fix for loopback IP --> remote IP (rdar://26561383)
613 if ((localAddress
!= NULL
) && (remoteAddress
!= NULL
) &&
614 is_ipv4_loopback(localAddress
) && !is_ipv4_loopback(remoteAddress
)) {
615 static Boolean warned
= FALSE
;
618 SC_log(LOG_WARNING
, "BUG: SCNetworkReachabilityCreateWithAddressPair() called with local <loopback>");
619 SC_log(LOG_WARNING
, "address and remote <non-loopback> address. To return the expected (but actually");
620 SC_log(LOG_WARNING
, "incorrect) result, switched to SCNetworkReachabilityCreateWithAddress() with");
621 SC_log(LOG_WARNING
, "the remote address.");
625 return SCNetworkReachabilityCreateWithAddress(allocator
, remoteAddress
);
627 #endif // !TARGET_OS_IPHONE
629 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
630 if (targetPrivate
== NULL
) {
634 targetPrivate
->type
= reachabilityTypeAddressPair
;
636 if (localAddress
!= NULL
) {
637 targetPrivate
->localAddressEndpoint
= nw_endpoint_create_address(localAddress
);
640 if (remoteAddress
!= NULL
) {
641 if (is_same_address(localAddress
, remoteAddress
)) {
642 targetPrivate
->remoteAddressEndpoint
= nw_retain(targetPrivate
->localAddressEndpoint
);
644 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(remoteAddress
);
648 targetPrivate
->parameters
= nw_parameters_create();
649 nw_parameters_set_local_address(targetPrivate
->parameters
, targetPrivate
->localAddressEndpoint
);
651 SC_log(LOG_DEBUG
, "%s%s %@",
652 targetPrivate
->log_prefix
,
653 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR
,
656 return (SCNetworkReachabilityRef
)targetPrivate
;
660 SCNetworkReachabilityRef
661 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
662 const char *nodename
)
666 struct sockaddr_in sin
;
667 struct sockaddr_in6 sin6
;
670 SCNetworkReachabilityPrivateRef targetPrivate
;
672 if (nodename
== NULL
) {
673 _SCErrorSet(kSCStatusInvalidArgument
);
677 nodenameLen
= strlen(nodename
);
678 if (nodenameLen
== 0) {
679 _SCErrorSet(kSCStatusInvalidArgument
);
683 if (_SC_string_to_sockaddr(nodename
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) != NULL
) {
684 /* if this "nodename" is really an IP[v6] address in disguise */
685 return SCNetworkReachabilityCreateWithAddress(allocator
, &addr
.sa
);
688 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
689 if (targetPrivate
== NULL
) {
693 targetPrivate
->type
= reachabilityTypeName
;
695 targetPrivate
->hostnameEndpoint
= nw_endpoint_create_host(nodename
, "0");
697 SC_log(LOG_DEBUG
, "%s%s %@",
698 targetPrivate
->log_prefix
,
699 DEBUG_REACHABILITY_TYPE_NAME
,
702 return (SCNetworkReachabilityRef
)targetPrivate
;
706 static SCNetworkReachabilityRef
707 __SCNetworkReachabilityCreateWithPTR(CFAllocatorRef allocator
,
708 const struct sockaddr
*ptrAddress
)
710 SCNetworkReachabilityPrivateRef targetPrivate
;
712 ptrAddress
= is_valid_address(ptrAddress
);
713 if (ptrAddress
== NULL
) {
714 _SCErrorSet(kSCStatusInvalidArgument
);
718 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
719 if (targetPrivate
== NULL
) {
723 targetPrivate
->type
= reachabilityTypePTR
;
725 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(ptrAddress
);
727 targetPrivate
->parameters
= nw_parameters_create();
728 nw_parameters_set_resolve_ptr(targetPrivate
->parameters
, TRUE
);
730 SC_log(LOG_DEBUG
, "%s%s %@",
731 targetPrivate
->log_prefix
,
732 DEBUG_REACHABILITY_TYPE_PTR
,
735 return (SCNetworkReachabilityRef
)targetPrivate
;
738 SCNetworkReachabilityRef
739 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
740 CFDictionaryRef options
)
742 const struct sockaddr
*addr_l
= NULL
;
743 const struct sockaddr
*addr_p
= NULL
;
744 const struct sockaddr
*addr_r
= NULL
;
746 Boolean haveOpt
= FALSE
;
747 CFStringRef interface
= NULL
;
748 CFStringRef nodename
;
749 CFBooleanRef resolverBypass
;
750 SCNetworkReachabilityRef target
;
751 SCNetworkReachabilityPrivateRef targetPrivate
;
752 unsigned int if_index
= 0;
753 char if_name
[IFNAMSIZ
];
754 CFDataRef sourceAppAuditToken
= NULL
;
755 CFStringRef sourceAppBundleID
= NULL
;
757 if (!isA_CFDictionary(options
)) {
758 _SCErrorSet(kSCStatusInvalidArgument
);
762 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
763 if ((nodename
!= NULL
) &&
764 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
765 _SCErrorSet(kSCStatusInvalidArgument
);
768 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
770 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
771 _SCErrorSet(kSCStatusInvalidArgument
);
774 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
776 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionPTRAddress
);
778 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
779 _SCErrorSet(kSCStatusInvalidArgument
);
782 addr_p
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
784 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
786 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
787 _SCErrorSet(kSCStatusInvalidArgument
);
790 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
792 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
793 if ((interface
!= NULL
) &&
794 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
795 _SCErrorSet(kSCStatusInvalidArgument
);
798 resolverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionResolverBypass
);
799 if ((resolverBypass
!= NULL
) && !isA_CFBoolean(resolverBypass
)) {
800 _SCErrorSet(kSCStatusInvalidArgument
);
803 sourceAppAuditToken
=
804 CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionSourceAppAuditToken
);
805 if ((sourceAppAuditToken
!= NULL
) &&
806 (!isA_CFData(sourceAppAuditToken
) ||
807 (CFDataGetLength(sourceAppAuditToken
) != sizeof(audit_token_t
)))) {
808 _SCErrorSet(kSCStatusInvalidArgument
);
812 CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionSourceAppBundleIdentifier
);
813 if ((sourceAppBundleID
!= NULL
) &&
814 (!isA_CFString(sourceAppBundleID
) ||
815 (CFStringGetLength(sourceAppBundleID
) == 0))) {
816 _SCErrorSet(kSCStatusInvalidArgument
);
820 if (nodename
!= NULL
) {
823 if ((addr_l
!= NULL
) || (addr_r
!= NULL
) || (addr_p
!= NULL
)) {
824 // can't have both a nodename and an address
825 _SCErrorSet(kSCStatusInvalidArgument
);
829 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
830 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
831 CFAllocatorDeallocate(NULL
, (void *)name
);
832 } else if (addr_p
!= NULL
) {
833 if ((addr_l
!= NULL
) || // can't have PTR and target address
835 _SCErrorSet(kSCStatusInvalidArgument
);
839 target
= __SCNetworkReachabilityCreateWithPTR(NULL
, addr_p
);
841 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
842 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
843 } else if (addr_r
!= NULL
) {
844 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
845 } else if (addr_l
!= NULL
) {
846 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, NULL
);
848 _SCErrorSet(kSCStatusInvalidArgument
);
852 if (target
== NULL
) {
856 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
858 if (interface
!= NULL
) {
859 if ((_SC_cfstring_to_cstring(interface
,
862 kCFStringEncodingASCII
) == NULL
) ||
863 ((if_index
= if_nametoindex(if_name
)) == 0)) {
864 CFRelease(targetPrivate
);
865 _SCErrorSet(kSCStatusInvalidArgument
);
870 if (targetPrivate
->parameters
== NULL
) {
871 targetPrivate
->parameters
= nw_parameters_create();
875 nw_interface_t interfaceObject
= nw_interface_create_with_index(if_index
);
876 nw_parameters_require_interface(targetPrivate
->parameters
, interfaceObject
);
877 nw_release(interfaceObject
);
881 if (resolverBypass
!= NULL
) {
882 targetPrivate
->resolverBypass
= CFBooleanGetValue(resolverBypass
);
886 if (sourceAppAuditToken
!= NULL
) {
887 audit_token_t atoken
;
888 CFDataGetBytes(sourceAppAuditToken
,
889 CFRangeMake(0, CFDataGetLength(sourceAppAuditToken
)),
891 nw_parameters_set_source_application(targetPrivate
->parameters
, atoken
);
893 } else if (sourceAppBundleID
!= NULL
) {
894 char *cBundleID
= _SC_cfstring_to_cstring(sourceAppBundleID
,
897 kCFStringEncodingUTF8
);
898 if (cBundleID
!= NULL
) {
899 nw_parameters_set_source_application_by_bundle_id(targetPrivate
->parameters
,
901 CFAllocatorDeallocate(NULL
, (void *)cBundleID
);
903 SC_log(LOG_WARNING
, "failed to convert %@ to a C string", sourceAppBundleID
);
909 const char *opt
= "???";
911 switch (targetPrivate
->type
) {
912 case reachabilityTypeAddress
:
913 opt
= DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS
;
915 case reachabilityTypeAddressPair
:
916 opt
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS
;
918 case reachabilityTypeName
:
919 opt
= DEBUG_REACHABILITY_TYPE_NAME_OPTIONS
;
921 case reachabilityTypePTR
:
922 opt
= DEBUG_REACHABILITY_TYPE_PTR_OPTIONS
;
926 SC_log(LOG_DEBUG
, "%s%s %@",
927 targetPrivate
->log_prefix
,
932 return (SCNetworkReachabilityRef
)targetPrivate
;
937 SCNetworkReachabilityGetTypeID(void)
939 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
940 return __kSCNetworkReachabilityTypeID
;
944 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
945 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
948 CFMutableArrayRef array
= NULL
;
949 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
951 if (!isA_SCNetworkReachability(target
)) {
952 _SCErrorSet(kSCStatusInvalidArgument
);
956 if (!isReachabilityTypeName(targetPrivate
->type
)) {
957 _SCErrorSet(kSCStatusInvalidArgument
);
965 MUTEX_LOCK(&targetPrivate
->lock
);
967 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
968 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
969 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
970 #pragma unused(index)
971 nw_endpoint_type_t endpoint_type
= nw_endpoint_get_type((nw_endpoint_t
)object
);
972 if (endpoint_type
== nw_endpoint_type_address
) {
973 const struct sockaddr
*address
= nw_endpoint_get_address((nw_endpoint_t
)object
);
974 if (address
== NULL
) {
975 SC_log(LOG_ERR
, "nw_endpoint_type_address w/no address");
979 CFDataRef addressData
= CFDataCreate(kCFAllocatorDefault
, (const uint8_t *)address
, address
->sa_len
);
980 CFArrayAppendValue(array
, addressData
);
981 CFRelease(addressData
);
982 } else if (endpoint_type
== nw_endpoint_type_host
) {
983 const char *host
= nw_endpoint_get_hostname((nw_endpoint_t
)object
);
985 SC_log(LOG_ERR
, "nw_endpoint_type_host w/no host");
989 CFStringRef string
= CFStringCreateWithCString(kCFAllocatorDefault
, host
, kCFStringEncodingASCII
);
990 if (string
== NULL
) {
991 SC_log(LOG_ERR
, "nw_endpoint_type_host w/non-ASCII host");
995 if (CFStringHasPrefix(string
, CFSTR(".")) || CFStringHasSuffix(string
, CFSTR("."))) {
996 CFMutableStringRef mutableString
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, string
);
998 string
= mutableString
;
999 CFStringTrim(mutableString
, CFSTR("."));
1001 CFArrayAppendValue(array
, string
);
1004 SC_log(LOG_ERR
, "unexpected nw_endpoint type: %d", endpoint_type
);
1010 MUTEX_UNLOCK(&targetPrivate
->lock
);
1011 _SCErrorSet(kSCStatusOK
);
1016 #pragma mark Reachability Flags
1019 __SCNetworkReachabilityGetAgentVPNFlags(xpc_object_t dictionary
, Boolean
*vpn
, Boolean
*onDemand
)
1021 const struct netagent
*agent
= NULL
;
1024 if (dictionary
== NULL
|| vpn
== NULL
|| onDemand
== NULL
) {
1031 agent
= xpc_dictionary_get_data(dictionary
, REACHABILITY_AGENT_DATA_KEY
, &length
);
1032 if (agent
== NULL
|| length
< sizeof(struct netagent
) || length
!= (sizeof(struct netagent
) + agent
->netagent_data_size
)) {
1036 if (strncmp(REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN
, agent
->netagent_domain
, NETAGENT_DOMAINSIZE
) == 0) {
1038 if ((agent
->netagent_flags
& NETAGENT_FLAG_VOLUNTARY
) &&
1039 !(agent
->netagent_flags
& NETAGENT_FLAG_ACTIVE
)) {
1046 nw_path_is_linklocal_direct(nw_path_t path
)
1048 bool is_linklocal_direct
= false;
1050 nw_endpoint_t endpoint
= nw_path_copy_endpoint(path
);
1051 if (endpoint
== NULL
) {
1055 if (nw_endpoint_get_type(endpoint
) == nw_endpoint_type_address
) {
1056 const struct sockaddr_in
*sin
;
1058 sin
= (const struct sockaddr_in
*)(void *)nw_endpoint_get_address(endpoint
);;
1059 if ((sin
!= NULL
) &&
1060 (sin
->sin_family
== AF_INET
) &&
1061 IN_LINKLOCAL(ntohl(sin
->sin_addr
.s_addr
))) {
1062 nw_interface_t interface
= nw_path_copy_interface(path
);
1064 if (interface
!= NULL
) {
1065 nw_interface_type_t type
= nw_interface_get_type(interface
);
1066 if ((type
== nw_interface_type_wired
) ||
1067 ((type
== nw_interface_type_wifi
) &&
1068 (nw_interface_get_subtype(interface
) != nw_interface_subtype_wifi_awdl
))) {
1069 is_linklocal_direct
= true;
1072 nw_release(interface
);
1077 nw_release(endpoint
);
1078 return is_linklocal_direct
;
1081 static SCNetworkReachabilityFlags
1082 __SCNetworkReachabilityGetFlagsFromPath(nw_path_t path
,
1083 ReachabilityAddressType type
,
1084 nw_resolver_status_t resolverStatus
,
1085 nw_array_t resolvedEndpoints
,
1086 Boolean resolvedEndpointUseFlags
,
1087 SCNetworkReachabilityFlags resolvedEndpointFlags
)
1089 __block SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
1090 __block
const char *why
= "???";
1092 nw_path_status_t status
= nw_path_get_status(path
);
1093 if (status
== nw_path_status_satisfied
) {
1094 __block
bool checkDNSFlags
= TRUE
;
1095 flags
= kSCNetworkReachabilityFlagsReachable
;
1096 why
= "nw_path_status_satisfied";
1097 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1098 if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1099 flags
|= (kSCNetworkReachabilityFlagsTransientConnection
| kSCNetworkReachabilityFlagsIsWWAN
);
1100 why
= "nw_path_status_satisfied, cellular";
1102 #endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1103 xpc_object_t agent_dictionary
= nw_path_copy_netagent_dictionary(path
);
1104 if (agent_dictionary
!= NULL
) {
1105 if (xpc_dictionary_get_count(agent_dictionary
) > 0) {
1106 xpc_dictionary_apply(agent_dictionary
, ^bool(const char *key
, xpc_object_t value
) {
1108 Boolean vpn
= FALSE
;
1109 Boolean onDemand
= FALSE
;
1110 __SCNetworkReachabilityGetAgentVPNFlags(value
, &vpn
, &onDemand
);
1112 // VPN flows are transient
1113 flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1114 why
= "nw_path_status_satisfied, VPN";
1117 type
== reachabilityTypeName
&&
1118 resolverStatus
== nw_resolver_status_complete
&&
1119 nw_array_get_count(resolvedEndpoints
) == 0) {
1120 // On Demand by hostname, when no address has been resolved
1121 flags
|= (kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsConnectionOnDemand
);
1122 why
= "nw_path_status_satisfied, OnDemand";
1123 checkDNSFlags
= FALSE
;
1129 xpc_release(agent_dictionary
);
1131 if (isReachabilityTypeName(type
)) {
1132 if (checkDNSFlags
) {
1133 if (resolverStatus
== nw_resolver_status_complete
&&
1134 nw_array_get_count(resolvedEndpoints
) == 0) {
1135 // DNS didn't resolve, as a final answer for now. Not reachable!
1137 why
= "nw_path_status_satisfied, DNS not reachable";
1138 } else if (resolvedEndpointUseFlags
) {
1139 flags
= resolvedEndpointFlags
;
1140 why
= "nw_path_status_satisfied, resolved endpoint flags";
1144 if (nw_path_is_direct(path
) || nw_path_is_linklocal_direct(path
)) {
1145 flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1146 why
= "nw_path_status_satisfied, by address, direct";
1148 if (nw_path_is_local(path
)) {
1149 flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1150 why
= "nw_path_status_satisfied, by address, local";
1153 } else if (status
== nw_path_status_unsatisfied
) {
1155 why
= "nw_path_status_unsatisfied";
1156 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1157 if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1158 flags
|= kSCNetworkReachabilityFlagsIsWWAN
;
1159 why
= "nw_path_status_unsatisfied, WWAN";
1161 #endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1162 } else if (status
== nw_path_status_satisfiable
) {
1163 flags
= (kSCNetworkReachabilityFlagsReachable
| kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsTransientConnection
);
1164 why
= "nw_path_status_satisfiable";
1166 if (nw_path_get_vpn_config_id(path
, &vpn_uuid
)) {
1167 flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
1168 why
= "nw_path_status_satisfiable, OnDemand";
1170 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1171 else if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1172 flags
|= kSCNetworkReachabilityFlagsIsWWAN
;
1173 why
= "nw_path_status_satisfiable, WWAN";
1175 #endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1178 SC_log(LOG_DEBUG
, "__SCNetworkReachabilityGetFlagsFromPath, flags = 0x%08x, %s", flags
, why
);
1179 return (flags
& kSCNetworkReachabilityFlagsMask
);
1182 static nw_endpoint_t
1183 __SCNetworkReachabilityGetPrimaryEndpoint(SCNetworkReachabilityPrivateRef targetPrivate
)
1185 if (targetPrivate
->type
== reachabilityTypeName
) {
1186 return targetPrivate
->hostnameEndpoint
;
1187 } else if (targetPrivate
->type
== reachabilityTypeAddress
||
1188 targetPrivate
->type
== reachabilityTypeAddressPair
||
1189 targetPrivate
->type
== reachabilityTypePTR
) {
1190 return targetPrivate
->remoteAddressEndpoint
;
1196 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target
)
1200 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1201 SCNetworkReachabilityFlags flags
= 0;
1203 if (!isA_SCNetworkReachability(target
)) {
1204 _SCErrorSet(kSCStatusInvalidArgument
);
1208 MUTEX_LOCK(&targetPrivate
->lock
);
1210 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1211 targetPrivate
->type
,
1212 nw_resolver_status_invalid
,
1214 targetPrivate
->lastResolvedEndpointHasFlags
,
1215 targetPrivate
->lastResolvedEndpointFlags
);
1217 /* Only return the if_index if the connection is reachable not for reachable connection
1218 * required etc ... */
1219 if (ok
&& __SCNetworkReachabilityRank(flags
) == ReachabilityRankReachable
) {
1220 if (targetPrivate
->lastResolvedEndpointHasFlags
) {
1221 if_index
= targetPrivate
->lastResolvedEndpointInterfaceIndex
;
1223 if_index
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1227 MUTEX_UNLOCK(&targetPrivate
->lock
);
1231 // CrazyIvan46 is the feature that allows connections to IPv4 literals on IPv6-only (NAT64+DNS64) networks to work
1232 // This function replaces the path when the initial one isn't satisfied and our target is an IPv4 literal
1233 // It tries IPv6 reachability instead in case we could synthesize another address to connect to
1234 static OS_OBJECT_RETURNS_RETAINED nw_path_t
1235 __SCNetworkReachabilityCreateCrazyIvan46Path(nw_path_t path
, nw_endpoint_t endpoint
,
1236 nw_parameters_t parameters
, Boolean allow_resolution
)
1238 nw_path_t retPath
= NULL
;
1239 const struct sockaddr
*sa
;
1241 if ((nw_path_get_status(path
) != nw_path_status_unsatisfied
) ||
1242 (NULL
== endpoint
) || (nw_endpoint_get_type(endpoint
) != nw_endpoint_type_address
)) {
1246 sa
= nw_endpoint_get_address(endpoint
);
1248 if (sa
->sa_family
!= AF_INET
) {
1252 if (allow_resolution
) {
1253 uint32_t ifIndex
= 0;
1254 int32_t numPrefixes
;
1255 nw_nat64_prefix_t
*prefixes
= NULL
;
1256 struct sockaddr_in sin
;
1258 memcpy(&sin
, sa
, MIN(sizeof(sin
), sa
->sa_len
));
1259 if (NULL
!= parameters
) {
1260 ifIndex
= nw_parameters_get_required_interface_index(parameters
);
1262 numPrefixes
= nw_nat64_copy_prefixes(&ifIndex
, &prefixes
);
1263 if (numPrefixes
> 0) {
1264 struct sockaddr_in6 synthesizedAddress
= {
1265 .sin6_len
= sizeof(struct sockaddr_in6
),
1266 .sin6_family
= AF_INET6
,
1267 .sin6_port
= htons(nw_endpoint_get_port(endpoint
)),
1271 nw_endpoint_t synthesizedEndpoint
;
1272 nw_path_evaluator_t synthesizedEvaluator
;
1273 nw_path_t synthesizedPath
;
1275 nw_nat64_synthesize_v6(&prefixes
[0], &sin
.sin_addr
, &synthesizedAddress
.sin6_addr
);
1276 synthesizedEndpoint
= nw_endpoint_create_address((const struct sockaddr
*)&synthesizedAddress
);
1277 synthesizedEvaluator
= nw_path_create_evaluator_for_endpoint(synthesizedEndpoint
, parameters
);
1278 synthesizedPath
= nw_path_evaluator_copy_path(synthesizedEvaluator
);
1279 if (nw_path_get_status(synthesizedPath
) != nw_path_status_unsatisfied
) {
1280 retPath
= synthesizedPath
;
1281 SC_log(LOG_INFO
, "Using CrazyIvan46 synthesized reachability result");
1283 nw_release(synthesizedPath
);
1285 nw_release(synthesizedEvaluator
);
1286 nw_release(synthesizedEndpoint
);
1289 // Don't synthesize in non-scheduled mode to avoid generating DNS traffic
1291 nw_path_evaluator_t v6PathEvaluator
;
1292 nw_parameters_t v6Parameters
;
1294 if (NULL
!= parameters
) {
1295 v6Parameters
= nw_parameters_copy(parameters
);
1297 v6Parameters
= nw_parameters_create();
1299 nw_parameters_set_required_address_family(v6Parameters
, AF_INET6
);
1300 v6PathEvaluator
= nw_path_create_evaluator_for_endpoint(NULL
, v6Parameters
);
1301 v6Path
= nw_path_evaluator_copy_path(v6PathEvaluator
);
1302 if (nw_path_get_status(v6Path
) != nw_path_status_unsatisfied
) {
1304 SC_log(LOG_INFO
, "Using CrazyIvan46 simple reachability result");
1308 nw_release(v6PathEvaluator
);
1309 nw_release(v6Parameters
);
1315 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
1316 SCNetworkReachabilityFlags
*flags
)
1318 nw_path_t crazyIvanPath
;
1319 nw_endpoint_t endpoint
;
1322 nw_path_evaluator_t pathEvaluator
;
1323 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1325 if (!isA_SCNetworkReachability(target
)) {
1326 _SCErrorSet(kSCStatusInvalidArgument
);
1330 MUTEX_LOCK(&targetPrivate
->lock
);
1332 if (targetPrivate
->scheduled
) {
1333 // if being watched, return the last known (and what should be current) status
1334 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1335 targetPrivate
->type
,
1336 targetPrivate
->lastResolverStatus
,
1337 targetPrivate
->lastResolvedEndpoints
,
1338 targetPrivate
->lastResolvedEndpointHasFlags
,
1339 targetPrivate
->lastResolvedEndpointFlags
);
1340 // because we have synchronously captured the current status, we no longer
1341 // need our by-name required callback
1342 targetPrivate
->sentFirstUpdate
= TRUE
;
1346 // Not being watched, so run a one-shot path evaluator
1347 // We don't care about DNS resolution in this case, since we only need to have the
1348 // DNS resolution to support clients watching reachability to get updates
1349 endpoint
= __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
);
1350 pathEvaluator
= nw_path_create_evaluator_for_endpoint(endpoint
, targetPrivate
->parameters
);
1351 path
= nw_path_evaluator_copy_path(pathEvaluator
);
1353 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1354 crazyIvanPath
= __SCNetworkReachabilityCreateCrazyIvan46Path(path
, endpoint
,
1355 targetPrivate
->parameters
, FALSE
);
1356 if (NULL
!= crazyIvanPath
) {
1358 path
= crazyIvanPath
;
1362 *flags
= __SCNetworkReachabilityGetFlagsFromPath(path
, 0, nw_resolver_status_invalid
, NULL
, FALSE
, 0);
1364 nw_release(pathEvaluator
);
1368 MUTEX_UNLOCK(&targetPrivate
->lock
);
1374 #pragma mark Notifications
1377 reachPerformAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1380 void (*context_release
)(const void *);
1381 SCNetworkReachabilityCallBack rlsFunction
;
1382 SCNetworkReachabilityFlags flags
= 0;
1384 if (!targetPrivate
->scheduled
) {
1385 // if no longer scheduled
1386 SC_log(LOG_INFO
, "%sskipping SCNetworkReachability callback, no longer scheduled",
1387 targetPrivate
->log_prefix
);
1388 MUTEX_UNLOCK(&targetPrivate
->lock
);
1393 rlsFunction
= targetPrivate
->rlsFunction
;
1394 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
1395 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
1396 context_release
= targetPrivate
->rlsContext
.release
;
1398 context_info
= targetPrivate
->rlsContext
.info
;
1399 context_release
= NULL
;
1402 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1403 targetPrivate
->type
,
1404 targetPrivate
->lastResolverStatus
,
1405 targetPrivate
->lastResolvedEndpoints
,
1406 targetPrivate
->lastResolvedEndpointHasFlags
,
1407 targetPrivate
->lastResolvedEndpointFlags
);
1409 MUTEX_UNLOCK(&targetPrivate
->lock
);
1411 if (rlsFunction
!= NULL
) {
1412 SC_log(LOG_DEBUG
, "%sexec SCNetworkReachability callout w/flags = 0x%08x",
1413 targetPrivate
->log_prefix
,
1415 (*rlsFunction
)((SCNetworkReachabilityRef
)targetPrivate
,
1420 if (context_release
!= NULL
) {
1421 (*context_release
)(context_info
);
1428 reachPerform(void *info
)
1430 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)info
;
1432 MUTEX_LOCK(&targetPrivate
->lock
);
1433 reachPerformAndUnlock(targetPrivate
);
1438 reachUpdateAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1440 targetPrivate
->sentFirstUpdate
= TRUE
;
1441 if (targetPrivate
->rls
!= NULL
) {
1442 if (targetPrivate
->rlList
!= NULL
) {
1443 CFRunLoopSourceSignal(targetPrivate
->rls
);
1444 _SC_signalRunLoop(targetPrivate
, targetPrivate
->rls
, targetPrivate
->rlList
);
1446 MUTEX_UNLOCK(&targetPrivate
->lock
);
1448 reachPerformAndUnlock(targetPrivate
);
1453 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
1454 SCNetworkReachabilityCallBack callout
,
1455 SCNetworkReachabilityContext
*context
)
1457 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1459 MUTEX_LOCK(&targetPrivate
->lock
);
1461 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1462 /* let go of the current context */
1463 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1466 targetPrivate
->rlsFunction
= callout
;
1467 targetPrivate
->rlsContext
.info
= NULL
;
1468 targetPrivate
->rlsContext
.retain
= NULL
;
1469 targetPrivate
->rlsContext
.release
= NULL
;
1470 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1472 memcpy(&targetPrivate
->rlsContext
, context
, sizeof(SCNetworkReachabilityContext
));
1473 if (context
->retain
!= NULL
) {
1474 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
1478 MUTEX_UNLOCK(&targetPrivate
->lock
);
1485 reachRLSCopyDescription(const void *info
)
1487 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1489 return CFStringCreateWithFormat(NULL
,
1491 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
1496 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
1497 CFRunLoopRef runLoop
,
1498 CFStringRef runLoopMode
)
1500 Boolean success
= FALSE
;
1501 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1502 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1503 _SCErrorSet(kSCStatusInvalidArgument
);
1507 MUTEX_LOCK(&targetPrivate
->lock
);
1509 if (targetPrivate
->scheduled
) {
1510 if (targetPrivate
->rls
!= NULL
&& targetPrivate
->rlList
!= NULL
) {
1511 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1513 * if we do not already have host notifications scheduled with
1514 * this runLoop / runLoopMode
1516 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1519 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
1521 MUTEX_UNLOCK(&targetPrivate
->lock
);
1524 MUTEX_UNLOCK(&targetPrivate
->lock
);
1525 _SCErrorSet(kSCStatusInvalidArgument
);
1530 CFRunLoopSourceContext context
= {
1532 , (void *)target
// info
1533 , CFRetain
// retain
1534 , CFRelease
// release
1535 , reachRLSCopyDescription
// copyDescription
1540 , reachPerform
// perform
1543 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
1544 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1546 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1548 * if we do not already have host notifications scheduled with
1549 * this runLoop / runLoopMode
1551 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1554 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
1556 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, _callback_queue());
1558 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1559 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1560 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1561 // if target is no longer scheduled for this runLoop / runLoopMode
1562 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1564 // if *all* notifications have been unscheduled
1565 CFRelease(targetPrivate
->rlList
);
1566 targetPrivate
->rlList
= NULL
;
1567 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1568 CFRelease(targetPrivate
->rls
);
1569 targetPrivate
->rls
= NULL
;
1575 MUTEX_UNLOCK(&targetPrivate
->lock
);
1580 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
1581 CFRunLoopRef runLoop
,
1582 CFStringRef runLoopMode
)
1584 Boolean success
= FALSE
;
1585 Boolean unscheduleDispatchQueue
= FALSE
;
1586 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1588 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1589 _SCErrorSet(kSCStatusInvalidArgument
);
1593 MUTEX_LOCK(&targetPrivate
->lock
);
1595 if (targetPrivate
->rlList
== NULL
|| targetPrivate
->rls
== NULL
|| !targetPrivate
->scheduled
) {
1596 MUTEX_UNLOCK(&targetPrivate
->lock
);
1597 _SCErrorSet(kSCStatusInvalidArgument
);
1601 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1602 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1603 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1604 // if target is no longer scheduled for this runLoop / runLoopMode
1605 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1607 // if *all* notifications have been unscheduled
1608 unscheduleDispatchQueue
= TRUE
;
1609 CFRelease(targetPrivate
->rlList
);
1610 targetPrivate
->rlList
= NULL
;
1611 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1612 CFRelease(targetPrivate
->rls
);
1613 targetPrivate
->rls
= NULL
;
1618 if (unscheduleDispatchQueue
) {
1619 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, NULL
);
1623 MUTEX_UNLOCK(&targetPrivate
->lock
);
1627 static __inline__
void
1628 __SCNetworkReachabilityCopyPathStatus(SCNetworkReachabilityPrivateRef targetPrivate
, SCNetworkReachabilityFlags
*flags
, uint
*ifIndex
, size_t *endpointCount
)
1631 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1632 targetPrivate
->type
,
1633 targetPrivate
->lastResolverStatus
,
1634 targetPrivate
->lastResolvedEndpoints
,
1635 targetPrivate
->lastResolvedEndpointHasFlags
,
1636 targetPrivate
->lastResolvedEndpointFlags
);
1639 *ifIndex
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1641 if (endpointCount
) {
1642 *endpointCount
= nw_array_get_count(targetPrivate
->lastResolvedEndpoints
);
1647 static __inline__ Boolean
1648 __SCNetworkReachabilityShouldUpdateClient(SCNetworkReachabilityPrivateRef targetPrivate
, SCNetworkReachabilityFlags oldFlags
, uint oldIFIndex
, size_t oldEndpointCount
)
1650 SCNetworkReachabilityFlags newFlags
= 0;
1651 uint newIFIndex
= 0;
1652 size_t newEndpointCount
= 0;
1653 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &newFlags
, &newIFIndex
, &newEndpointCount
);
1654 return (!targetPrivate
->sentFirstUpdate
||
1655 oldFlags
!= newFlags
||
1656 oldIFIndex
!= newIFIndex
||
1657 oldEndpointCount
!= newEndpointCount
);
1661 __SCNetworkReachabilityRestartResolver(SCNetworkReachabilityPrivateRef targetPrivate
)
1663 if (targetPrivate
&&
1664 !targetPrivate
->resolverBypass
&&
1665 isReachabilityTypeName(targetPrivate
->type
)) {
1666 nw_resolver_t resolver
;
1667 CFRetain(targetPrivate
);
1668 if (NULL
!= targetPrivate
->resolver
) {
1669 nw_resolver_cancel(targetPrivate
->resolver
);
1671 if (targetPrivate
->lastPath
!= NULL
) {
1672 resolver
= nw_resolver_create_with_path(targetPrivate
->lastPath
);
1674 resolver
= nw_resolver_create_with_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
), targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
: targetPrivate
->parameters
);
1676 if (resolver
== NULL
) {
1677 SC_log(LOG_ERR
, "%sfailed to create a nw_resolver", targetPrivate
->log_prefix
);
1678 targetPrivate
->resolver
= NULL
;
1679 CFRelease(targetPrivate
);
1682 targetPrivate
->resolver
= resolver
;
1683 nw_resolver_set_cancel_handler(resolver
, ^(void) {
1684 MUTEX_LOCK(&targetPrivate
->lock
);
1685 if (resolver
== targetPrivate
->resolver
) {
1686 targetPrivate
->resolver
= NULL
;
1688 nw_release(resolver
);
1689 MUTEX_UNLOCK(&targetPrivate
->lock
);
1690 CFRelease(targetPrivate
);
1692 if (!nw_resolver_set_update_handler(resolver
, targetPrivate
->dispatchQueue
, ^(nw_resolver_status_t status
, nw_array_t resolved_endpoints
) {
1693 MUTEX_LOCK(&targetPrivate
->lock
);
1694 if (targetPrivate
->scheduled
) {
1695 SCNetworkReachabilityFlags oldFlags
= 0;
1696 uint oldIFIndex
= 0;
1697 size_t oldEndpointCount
= 0;
1698 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &oldFlags
, &oldIFIndex
, &oldEndpointCount
);
1700 targetPrivate
->lastResolverStatus
= status
;
1701 nw_release(targetPrivate
->lastResolvedEndpoints
);
1702 targetPrivate
->lastResolvedEndpoints
= nw_retain(resolved_endpoints
);
1704 // Run path evaluation on the resolved endpoints
1705 __block Boolean hasFlags
= FALSE
;
1706 targetPrivate
->lastResolvedEndpointHasFlags
= FALSE
;
1707 targetPrivate
->lastResolvedEndpointFlags
= 0;
1708 targetPrivate
->lastResolvedEndpointInterfaceIndex
= 0;
1709 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
1710 #pragma unused(index)
1711 SCNetworkReachabilityFlags flags
= 0;
1712 uint interfaceIndex
= 0;
1713 ReachabilityRankType rank
;
1714 nw_endpoint_t resolvedEndpoint
= (nw_endpoint_t
)object
;
1715 nw_path_evaluator_t pathEvaluator
= nw_path_create_evaluator_for_endpoint(resolvedEndpoint
, targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
: targetPrivate
->parameters
);
1716 nw_path_t path
= nw_path_evaluator_copy_path(pathEvaluator
);
1718 flags
= __SCNetworkReachabilityGetFlagsFromPath(path
, 0, nw_resolver_status_invalid
, NULL
, FALSE
, 0);
1721 interfaceIndex
= nw_path_get_interface_index(path
);
1723 nw_release(pathEvaluator
);
1725 rank
= __SCNetworkReachabilityRank(flags
);
1726 if (rank
> __SCNetworkReachabilityRank(targetPrivate
->lastResolvedEndpointFlags
)) {
1727 // Return the best case result
1728 targetPrivate
->lastResolvedEndpointFlags
= flags
;
1729 targetPrivate
->lastResolvedEndpointInterfaceIndex
= interfaceIndex
;
1730 if (rank
== ReachabilityRankReachable
) {
1731 // Can't get any better than REACHABLE
1737 targetPrivate
->lastResolvedEndpointHasFlags
= hasFlags
;
1739 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1740 reachUpdateAndUnlock(targetPrivate
);
1742 MUTEX_UNLOCK(&targetPrivate
->lock
);
1745 MUTEX_UNLOCK(&targetPrivate
->lock
);
1748 nw_release(resolver
);
1749 targetPrivate
->resolver
= NULL
;
1750 CFRelease(targetPrivate
);
1756 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
1757 dispatch_queue_t queue
)
1761 if (queue
!= NULL
) {
1762 nw_path_t crazyIvanPath
;
1763 nw_endpoint_t endpoint
;
1764 nw_path_evaluator_t pathEvaluator
;
1766 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
1767 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
1768 _SCErrorSet(kSCStatusInvalidArgument
);
1772 SC_log(LOG_DEBUG
, "%sscheduled", targetPrivate
->log_prefix
);
1774 // retain dispatch queue
1775 dispatch_retain(queue
);
1776 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1777 endpoint
= __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
);
1778 pathEvaluator
= nw_path_create_evaluator_for_endpoint(endpoint
, targetPrivate
->parameters
);
1779 targetPrivate
->pathEvaluator
= pathEvaluator
;
1780 targetPrivate
->dispatchQueue
= queue
;
1781 targetPrivate
->scheduled
= TRUE
;
1782 if (isReachabilityTypeName(targetPrivate
->type
)) {
1783 // we must have at least one callback for by-name queries
1784 targetPrivate
->sentFirstUpdate
= FALSE
;
1786 targetPrivate
->sentFirstUpdate
= TRUE
;
1789 nw_release(targetPrivate
->lastPath
);
1790 targetPrivate
->lastPath
= nw_path_evaluator_copy_path(pathEvaluator
);
1792 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1793 crazyIvanPath
= __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate
->lastPath
, endpoint
,
1794 targetPrivate
->parameters
, FALSE
);
1795 if (NULL
!= crazyIvanPath
) {
1796 nw_release(targetPrivate
->lastPath
);
1797 targetPrivate
->lastPath
= crazyIvanPath
;
1801 nw_release(targetPrivate
->lastPathParameters
);
1802 targetPrivate
->lastPathParameters
= nw_path_copy_derived_parameters(targetPrivate
->lastPath
);
1804 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1805 nw_release(targetPrivate
->lastResolvedEndpoints
);
1806 targetPrivate
->lastResolvedEndpoints
= NULL
;
1807 __SCNetworkReachabilityRestartResolver(targetPrivate
);
1809 CFRetain(targetPrivate
);
1810 nw_path_evaluator_set_cancel_handler(pathEvaluator
, ^(void) {
1811 MUTEX_LOCK(&targetPrivate
->lock
);
1812 if (pathEvaluator
== targetPrivate
->pathEvaluator
) {
1813 targetPrivate
->pathEvaluator
= NULL
;
1815 nw_release(pathEvaluator
);
1816 MUTEX_UNLOCK(&targetPrivate
->lock
);
1817 CFRelease(targetPrivate
);
1820 if (!nw_path_evaluator_set_update_handler(pathEvaluator
, targetPrivate
->dispatchQueue
, ^(nw_path_t path
) {
1821 MUTEX_LOCK(&targetPrivate
->lock
);
1822 if (targetPrivate
->scheduled
) {
1823 nw_path_t crazyIvanPath
;
1824 SCNetworkReachabilityFlags oldFlags
= 0;
1825 uint oldIFIndex
= 0;
1826 size_t oldEndpointCount
= 0;
1827 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &oldFlags
, &oldIFIndex
, &oldEndpointCount
);
1829 nw_release(targetPrivate
->lastPath
);
1830 targetPrivate
->lastPath
= nw_retain(path
);
1832 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1834 __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate
->lastPath
,
1836 targetPrivate
->parameters
,
1838 if (NULL
!= crazyIvanPath
) {
1839 nw_release(targetPrivate
->lastPath
);
1840 targetPrivate
->lastPath
= crazyIvanPath
;
1844 if (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) {
1845 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1846 __SCNetworkReachabilityRestartResolver(targetPrivate
);
1849 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1850 reachUpdateAndUnlock(targetPrivate
);
1852 MUTEX_UNLOCK(&targetPrivate
->lock
);
1855 MUTEX_UNLOCK(&targetPrivate
->lock
);
1858 targetPrivate
->pathEvaluator
= NULL
;
1859 nw_release(pathEvaluator
);
1860 CFRelease(targetPrivate
);
1863 if (targetPrivate
->dispatchQueue
== NULL
) { // if we should be scheduled on a dispatch queue (but are not)
1864 _SCErrorSet(kSCStatusInvalidArgument
);
1868 if (!targetPrivate
->scheduled
) {
1869 // if not currently scheduled
1870 _SCErrorSet(kSCStatusInvalidArgument
);
1874 targetPrivate
->scheduled
= FALSE
;
1875 targetPrivate
->sentFirstUpdate
= FALSE
;
1876 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1877 targetPrivate
->pathEvaluator
= NULL
;
1878 nw_release(targetPrivate
->lastPath
);
1879 targetPrivate
->lastPath
= NULL
;
1880 nw_release(targetPrivate
->lastPathParameters
);
1881 targetPrivate
->lastPathParameters
= NULL
;
1882 nw_release(targetPrivate
->lastResolvedEndpoints
);
1883 targetPrivate
->lastResolvedEndpoints
= NULL
;
1884 if (NULL
!= targetPrivate
->resolver
) {
1885 nw_resolver_cancel(targetPrivate
->resolver
);
1886 targetPrivate
->resolver
= NULL
;
1888 if (targetPrivate
->dispatchQueue
!= NULL
) {
1889 dispatch_release(targetPrivate
->dispatchQueue
);
1890 targetPrivate
->dispatchQueue
= NULL
;
1893 SC_log(LOG_DEBUG
, "%sunscheduled", targetPrivate
->log_prefix
);
1901 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
1902 dispatch_queue_t queue
)
1904 if (!isA_SCNetworkReachability(target
)) {
1905 _SCErrorSet(kSCStatusInvalidArgument
);
1909 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1910 MUTEX_LOCK(&targetPrivate
->lock
);
1911 Boolean success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, queue
);
1912 MUTEX_UNLOCK(&targetPrivate
->lock
);
1917 * _SC_checkResolverReachabilityByAddress()
1919 * Given an IP address, determine whether a reverse DNS query can be issued
1920 * using the current network configuration.
1923 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
1924 SCNetworkReachabilityFlags
*flags
,
1926 struct sockaddr
*sa
)
1928 #pragma unused(storeP)
1930 nw_path_evaluator_t evaluator
= nw_path_create_default_evaluator();
1931 nw_path_t path
= nw_path_evaluator_copy_path(evaluator
);
1932 if (nw_path_get_status(path
) == nw_path_status_unsatisfied_network
) {
1941 *flags
= kSCNetworkReachabilityFlagsReachable
;
1947 nw_release(evaluator
);