2 * Copyright (c) 2003-2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * April 12, 2011 Allan Nathanson <ajn@apple.com>
28 * - add SCNetworkReachability "server"
30 * March 31, 2004 Allan Nathanson <ajn@apple.com>
31 * - use [SC] DNS configuration information
33 * January 19, 2003 Allan Nathanson <ajn@apple.com>
34 * - add advanced reachability APIs
37 #include <Availability.h>
38 #include <TargetConditionals.h>
39 #include <sys/cdefs.h>
40 #include <dispatch/dispatch.h>
41 #include <dispatch/private.h>
43 #include <libkern/OSAtomic.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
51 #include <sys/ioctl.h>
52 #include <sys/socket.h>
54 #include <net/if_dl.h>
55 #include <net/if_types.h>
56 #include <net/network_agent.h>
58 #include <CoreFoundation/CoreFoundation.h>
59 #include <CoreFoundation/CFRuntime.h>
61 #define SC_LOG_HANDLE __log_SCNetworkReachability()
62 #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 #include <network/private.h>
74 #define DEBUG_REACHABILITY_TYPE_NAME "create w/name"
75 #define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
77 #define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"
78 #define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
80 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"
81 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
83 #define DEBUG_REACHABILITY_TYPE_PTR "create w/ptr"
84 #define DEBUG_REACHABILITY_TYPE_PTR_OPTIONS " + options"
86 static pthread_mutexattr_t lock_attr
;
88 #define MUTEX_INIT(m) { \
89 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \
93 #define MUTEX_LOCK(m) { \
94 int _lock_ = (pthread_mutex_lock(m) == 0); \
98 #define MUTEX_UNLOCK(m) { \
99 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
103 #define MUTEX_ASSERT_HELD(m) { \
104 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \
111 #define REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN "NetworkExtension"
112 #define REACHABILITY_AGENT_DATA_KEY "data"
115 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
116 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
118 static SCNetworkReachabilityFlags
119 __SCNetworkReachabilityGetFlagsFromPath(nw_path_t path
,
120 ReachabilityAddressType type
,
121 nw_resolver_status_t resolverStatus
,
122 nw_array_t resolvedEndpoints
,
123 Boolean resolvedEndpointUseFlags
,
124 SCNetworkReachabilityFlags resolvedEndpointFlags
);
127 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
128 dispatch_queue_t queue
);
130 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
133 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
135 "SCNetworkReachability", // className
138 __SCNetworkReachabilityDeallocate
, // dealloc
141 NULL
, // copyFormattingDesc
142 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
146 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
148 static dispatch_queue_t
151 static dispatch_once_t once
;
152 static dispatch_queue_t q
;
154 dispatch_once(&once
, ^{
155 q
= dispatch_queue_create("SCNetworkReachability.callback", NULL
);
162 __log_SCNetworkReachability()
164 static os_log_t log
= NULL
;
167 log
= os_log_create("com.apple.SystemConfiguration", "SCNetworkReachability");
175 #pragma mark SCNetworkReachability APIs
178 static __inline__ CFTypeRef
179 isA_SCNetworkReachability(CFTypeRef obj
)
181 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
185 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target
)
187 CFAllocatorRef allocator
= CFGetAllocator(target
);
188 CFMutableStringRef str
;
189 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
191 str
= CFStringCreateMutable(allocator
, 0);
192 switch (targetPrivate
->type
) {
193 case reachabilityTypeAddress
:
194 case reachabilityTypeAddressPair
: {
196 if (targetPrivate
->localAddressEndpoint
!= NULL
) {
197 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->localAddressEndpoint
), buf
, sizeof(buf
));
198 CFStringAppendFormat(str
, NULL
, CFSTR("local address = %s"),
201 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
202 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
203 CFStringAppendFormat(str
, NULL
, CFSTR("%s%saddress = %s"),
204 targetPrivate
->localAddressEndpoint
? ", " : "",
205 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
208 CFStringAppendFormat(str
, NULL
, CFSTR("default path"));
212 case reachabilityTypeName
: {
213 CFStringAppendFormat(str
, NULL
, CFSTR("name = %s"), nw_endpoint_get_hostname(targetPrivate
->hostnameEndpoint
));
216 case reachabilityTypePTR
: {
218 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
219 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
220 CFStringAppendFormat(str
, NULL
, CFSTR("ptr = %s"),
227 if (targetPrivate
->parameters
!= NULL
) {
228 unsigned int if_index
;
230 if_index
= nw_parameters_get_required_interface_index(targetPrivate
->parameters
);
232 CFStringAppendFormat(str
, NULL
, CFSTR(", if_index = %u"), if_index
);
241 _SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target
)
243 CFAllocatorRef allocator
= CFGetAllocator(target
);
245 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
247 str
= CFStringCreateWithFormat(allocator
,
249 CFSTR("flags = 0x%08x, if_index = %u"),
250 __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
252 targetPrivate
->lastResolverStatus
,
253 targetPrivate
->lastResolvedEndpoints
,
254 targetPrivate
->lastResolvedEndpointHasFlags
,
255 targetPrivate
->lastResolvedEndpointFlags
),
256 targetPrivate
->lastResolvedEndpointHasFlags
? targetPrivate
->lastResolvedEndpointInterfaceIndex
257 : nw_path_get_interface_index(targetPrivate
->lastPath
));
263 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
265 CFAllocatorRef allocator
= CFGetAllocator(cf
);
266 CFMutableStringRef result
;
268 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
269 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
271 MUTEX_LOCK(&targetPrivate
->lock
);
273 result
= CFStringCreateMutable(allocator
, 0);
274 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
276 // add target description
277 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
278 CFStringAppend(result
, str
);
281 // add additional "name" info
282 if (isReachabilityTypeName(targetPrivate
->type
)) {
283 if (targetPrivate
->resolver
&& targetPrivate
->lastResolverStatus
== nw_resolver_status_invalid
) {
284 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
285 } else if (targetPrivate
->lastResolverStatus
!= nw_resolver_status_invalid
) {
286 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s"), (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) ? "complete" : "in progress");
287 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
288 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
289 #pragma unused(index)
290 nw_endpoint_t endpoint
= (nw_endpoint_t
)object
;
291 nw_endpoint_type_t endpoint_type
= nw_endpoint_get_type(endpoint
);
292 if (endpoint_type
== nw_endpoint_type_address
) {
294 const struct sockaddr
*sa
= nw_endpoint_get_address(endpoint
);
295 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
296 CFStringAppendFormat(result
, NULL
, CFSTR(", %s"), buf
);
297 } else if (endpoint_type
== nw_endpoint_type_host
) {
298 CFStringAppendFormat(result
, NULL
, CFSTR(", %s"), nw_endpoint_get_hostname(endpoint
));
300 CFStringAppendFormat(result
, NULL
, CFSTR(", unexpected nw_endpoint type: %d"), endpoint_type
);
305 CFStringAppendFormat(result
, NULL
, CFSTR(", no addresses"));
307 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
311 if (targetPrivate
->resolverBypass
) {
312 CFStringAppendFormat(result
, NULL
, CFSTR(", !resolve"));
316 if (targetPrivate
->scheduled
) {
317 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
318 CFStringAppendFormat(result
, NULL
, CFSTR(", %@"), str
);
322 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
324 MUTEX_UNLOCK(&targetPrivate
->lock
);
331 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
333 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
334 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
336 SC_log(LOG_DEBUG
, "%srelease", targetPrivate
->log_prefix
);
338 /* release resources */
339 MUTEX_LOCK(&targetPrivate
->lock
);
340 targetPrivate
->scheduled
= FALSE
;
342 if (targetPrivate
->hostnameEndpoint
) {
343 network_release(targetPrivate
->hostnameEndpoint
);
344 targetPrivate
->hostnameEndpoint
= NULL
;
346 if (targetPrivate
->localAddressEndpoint
) {
347 network_release(targetPrivate
->localAddressEndpoint
);
348 targetPrivate
->localAddressEndpoint
= NULL
;
350 if (targetPrivate
->remoteAddressEndpoint
) {
351 network_release(targetPrivate
->remoteAddressEndpoint
);
352 targetPrivate
->remoteAddressEndpoint
= NULL
;
354 if (targetPrivate
->parameters
) {
355 network_release(targetPrivate
->parameters
);
356 targetPrivate
->parameters
= NULL
;
358 if (targetPrivate
->lastPath
) {
359 network_release(targetPrivate
->lastPath
);
360 targetPrivate
->lastPath
= NULL
;
362 if (targetPrivate
->lastPathParameters
) {
363 network_release(targetPrivate
->lastPathParameters
);
364 targetPrivate
->lastPathParameters
= NULL
;
366 if (targetPrivate
->lastResolvedEndpoints
) {
367 network_release(targetPrivate
->lastResolvedEndpoints
);
368 targetPrivate
->lastResolvedEndpoints
= NULL
;
371 if (targetPrivate
->rlsContext
.release
!= NULL
) {
372 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
375 MUTEX_UNLOCK(&targetPrivate
->lock
);
377 pthread_mutex_destroy(&targetPrivate
->lock
);
384 __SCNetworkReachabilityInitialize(void)
386 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
388 pthread_mutexattr_init(&lock_attr
);
389 pthread_mutexattr_settype(&lock_attr
, PTHREAD_MUTEX_ERRORCHECK
);
394 static SCNetworkReachabilityPrivateRef
395 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
397 SCNetworkReachabilityPrivateRef targetPrivate
;
400 /* initialize runtime */
401 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
403 /* allocate target */
404 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
405 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
406 __kSCNetworkReachabilityTypeID
,
409 if (targetPrivate
== NULL
) {
413 /* initialize non-zero/NULL members */
414 MUTEX_INIT(&targetPrivate
->lock
);
416 snprintf(targetPrivate
->log_prefix
,
417 sizeof(targetPrivate
->log_prefix
),
422 return targetPrivate
;
426 static const struct sockaddr
*
427 is_valid_address(const struct sockaddr
*address
)
429 const struct sockaddr
*valid
= NULL
;
430 static Boolean warned
= FALSE
;
432 if ((address
!= NULL
) &&
433 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
434 switch (address
->sa_family
) {
436 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
440 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
442 sizeof(struct sockaddr_in
));
448 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
450 } else if (!warned
) {
451 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
453 sizeof(struct sockaddr_in6
));
459 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d",
470 __SCNetworkReachabilityAddressIsEmpty(const struct sockaddr
*address
)
472 if (address
== NULL
) {
476 if (address
->sa_family
== AF_INET
) {
477 return (((struct sockaddr_in
*)(void *)address
)->sin_addr
.s_addr
== 0);
478 } else if (address
->sa_family
== AF_INET6
) {
479 return IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6
*)(void *)address
)->sin6_addr
);
485 SCNetworkReachabilityRef
486 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
487 const struct sockaddr
*address
)
489 const struct sockaddr
*targetAddress
;
490 SCNetworkReachabilityPrivateRef targetPrivate
;
492 targetAddress
= is_valid_address(address
);
493 if (targetAddress
== NULL
) {
494 _SCErrorSet(kSCStatusInvalidArgument
);
498 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
499 if (targetPrivate
== NULL
) {
503 targetPrivate
->type
= reachabilityTypeAddress
;
505 if (!__SCNetworkReachabilityAddressIsEmpty(targetAddress
)) {
506 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(targetAddress
);
509 SC_log(LOG_DEBUG
, "%s%s %@",
510 targetPrivate
->log_prefix
,
511 DEBUG_REACHABILITY_TYPE_ADDRESS
,
514 return (SCNetworkReachabilityRef
)targetPrivate
;
518 #if !TARGET_OS_IPHONE
520 is_ipv4_loopback(const struct sockaddr
*sa
)
523 struct sockaddr_in
*sin
= (struct sockaddr_in
*)(void *)sa
;
526 (sa
->sa_len
< sizeof(struct sockaddr_in
)) ||
527 (sa
->sa_family
!= AF_INET
)) {
531 addr
= ntohl(sin
->sin_addr
.s_addr
);
532 return IN_LOOPBACK(addr
) ? TRUE
: FALSE
;
534 #endif // !TARGET_OS_IPHONE
538 is_same_address(const struct sockaddr
*a
, const struct sockaddr
*b
)
546 (a
->sa_family
!= b
->sa_family
) ||
547 (a
->sa_len
!= b
->sa_len
)) {
551 switch (a
->sa_family
) {
553 struct sockaddr_in
*a_sin
= (struct sockaddr_in
*)(void *)a
;
554 struct sockaddr_in
*b_sin
= (struct sockaddr_in
*)(void *)b
;
556 /* ALIGN: assuming a (and b) are aligned, then cast ok. */
557 a_addr
= &a_sin
->sin_addr
;
558 b_addr
= &b_sin
->sin_addr
;
559 len
= sizeof(struct in_addr
);
564 struct sockaddr_in6
*a_sin6
= (struct sockaddr_in6
*)(void *)a
;
565 struct sockaddr_in6
*b_sin6
= (struct sockaddr_in6
*)(void *)b
;
567 if (a_sin6
->sin6_scope_id
!= b_sin6
->sin6_scope_id
) {
571 a_addr
= &a_sin6
->sin6_addr
;
572 b_addr
= &b_sin6
->sin6_addr
;
573 len
= sizeof(struct in6_addr
);
584 return (bcmp(a_addr
, b_addr
, len
) == 0);
588 SCNetworkReachabilityRef
589 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
590 const struct sockaddr
*localAddress
,
591 const struct sockaddr
*remoteAddress
)
593 SCNetworkReachabilityPrivateRef targetPrivate
;
595 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
596 _SCErrorSet(kSCStatusInvalidArgument
);
600 if (localAddress
!= NULL
) {
601 localAddress
= is_valid_address(localAddress
);
602 if (localAddress
== NULL
) {
603 _SCErrorSet(kSCStatusInvalidArgument
);
608 if (remoteAddress
!= NULL
) {
609 remoteAddress
= is_valid_address(remoteAddress
);
610 if (remoteAddress
== NULL
) {
611 _SCErrorSet(kSCStatusInvalidArgument
);
616 #if !TARGET_OS_IPHONE
617 // Check/fix for loopback IP --> remote IP (rdar://26561383)
618 if ((localAddress
!= NULL
) && (remoteAddress
!= NULL
) &&
619 is_ipv4_loopback(localAddress
) && !is_ipv4_loopback(remoteAddress
)) {
620 static Boolean warned
= FALSE
;
623 SC_log(LOG_WARNING
, "BUG: SCNetworkReachabilityCreateWithAddressPair() called with local <loopback>");
624 SC_log(LOG_WARNING
, "address and remote <non-loopback> address. To return the expected (but actually");
625 SC_log(LOG_WARNING
, "incorrect) result, switched to SCNetworkReachabilityCreateWithAddress() with");
626 SC_log(LOG_WARNING
, "the remote address.");
630 return SCNetworkReachabilityCreateWithAddress(allocator
, remoteAddress
);
632 #endif // !TARGET_OS_IPHONE
634 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
635 if (targetPrivate
== NULL
) {
639 targetPrivate
->type
= reachabilityTypeAddressPair
;
641 if (localAddress
!= NULL
) {
642 targetPrivate
->localAddressEndpoint
= nw_endpoint_create_address(localAddress
);
645 if (remoteAddress
!= NULL
) {
646 if (is_same_address(localAddress
, remoteAddress
)) {
647 targetPrivate
->remoteAddressEndpoint
= network_retain(targetPrivate
->localAddressEndpoint
);
649 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(remoteAddress
);
653 targetPrivate
->parameters
= nw_parameters_create();
654 nw_parameters_set_local_address(targetPrivate
->parameters
, targetPrivate
->localAddressEndpoint
);
656 SC_log(LOG_DEBUG
, "%s%s %@",
657 targetPrivate
->log_prefix
,
658 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR
,
661 return (SCNetworkReachabilityRef
)targetPrivate
;
665 SCNetworkReachabilityRef
666 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
667 const char *nodename
)
671 struct sockaddr_in sin
;
672 struct sockaddr_in6 sin6
;
675 SCNetworkReachabilityPrivateRef targetPrivate
;
677 if (nodename
== NULL
) {
678 _SCErrorSet(kSCStatusInvalidArgument
);
682 nodenameLen
= strlen(nodename
);
683 if (nodenameLen
== 0) {
684 _SCErrorSet(kSCStatusInvalidArgument
);
688 if (_SC_string_to_sockaddr(nodename
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) != NULL
) {
689 /* if this "nodename" is really an IP[v6] address in disguise */
690 return SCNetworkReachabilityCreateWithAddress(allocator
, &addr
.sa
);
693 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
694 if (targetPrivate
== NULL
) {
698 targetPrivate
->type
= reachabilityTypeName
;
700 targetPrivate
->hostnameEndpoint
= nw_endpoint_create_host(nodename
, "0");
702 SC_log(LOG_DEBUG
, "%s%s %@",
703 targetPrivate
->log_prefix
,
704 DEBUG_REACHABILITY_TYPE_NAME
,
707 return (SCNetworkReachabilityRef
)targetPrivate
;
711 static SCNetworkReachabilityRef
712 __SCNetworkReachabilityCreateWithPTR(CFAllocatorRef allocator
,
713 const struct sockaddr
*ptrAddress
)
715 SCNetworkReachabilityPrivateRef targetPrivate
;
717 ptrAddress
= is_valid_address(ptrAddress
);
718 if (ptrAddress
== NULL
) {
719 _SCErrorSet(kSCStatusInvalidArgument
);
723 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
724 if (targetPrivate
== NULL
) {
728 targetPrivate
->type
= reachabilityTypePTR
;
730 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(ptrAddress
);
732 targetPrivate
->parameters
= nw_parameters_create();
733 nw_parameters_set_resolve_ptr(targetPrivate
->parameters
, TRUE
);
735 SC_log(LOG_DEBUG
, "%s%s %@",
736 targetPrivate
->log_prefix
,
737 DEBUG_REACHABILITY_TYPE_PTR
,
740 return (SCNetworkReachabilityRef
)targetPrivate
;
743 SCNetworkReachabilityRef
744 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
745 CFDictionaryRef options
)
747 const struct sockaddr
*addr_l
= NULL
;
748 const struct sockaddr
*addr_p
= NULL
;
749 const struct sockaddr
*addr_r
= NULL
;
751 Boolean haveOpt
= FALSE
;
752 CFStringRef interface
= NULL
;
753 CFStringRef nodename
;
754 CFBooleanRef resolverBypass
;
755 SCNetworkReachabilityRef target
;
756 SCNetworkReachabilityPrivateRef targetPrivate
;
757 unsigned int if_index
= 0;
758 char if_name
[IFNAMSIZ
];
759 CFDataRef sourceAppAuditToken
= NULL
;
760 CFStringRef sourceAppBundleID
= NULL
;
762 if (!isA_CFDictionary(options
)) {
763 _SCErrorSet(kSCStatusInvalidArgument
);
767 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
768 if ((nodename
!= NULL
) &&
769 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
770 _SCErrorSet(kSCStatusInvalidArgument
);
773 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
775 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
776 _SCErrorSet(kSCStatusInvalidArgument
);
779 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
781 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionPTRAddress
);
783 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
784 _SCErrorSet(kSCStatusInvalidArgument
);
787 addr_p
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
789 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
791 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
792 _SCErrorSet(kSCStatusInvalidArgument
);
795 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
797 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
798 if ((interface
!= NULL
) &&
799 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
800 _SCErrorSet(kSCStatusInvalidArgument
);
803 resolverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionResolverBypass
);
804 if ((resolverBypass
!= NULL
) && !isA_CFBoolean(resolverBypass
)) {
805 _SCErrorSet(kSCStatusInvalidArgument
);
808 sourceAppAuditToken
=
809 CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionSourceAppAuditToken
);
810 if ((sourceAppAuditToken
!= NULL
) &&
811 (!isA_CFData(sourceAppAuditToken
) ||
812 (CFDataGetLength(sourceAppAuditToken
) != sizeof(audit_token_t
)))) {
813 _SCErrorSet(kSCStatusInvalidArgument
);
817 CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionSourceAppBundleIdentifier
);
818 if ((sourceAppBundleID
!= NULL
) &&
819 (!isA_CFString(sourceAppBundleID
) ||
820 (CFStringGetLength(sourceAppBundleID
) == 0))) {
821 _SCErrorSet(kSCStatusInvalidArgument
);
826 if (nodename
!= NULL
) {
829 if ((addr_l
!= NULL
) || (addr_r
!= NULL
) || (addr_p
!= NULL
)) {
830 // can't have both a nodename and an address
831 _SCErrorSet(kSCStatusInvalidArgument
);
835 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
836 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
837 CFAllocatorDeallocate(NULL
, (void *)name
);
838 } else if (addr_p
!= NULL
) {
839 if ((addr_l
!= NULL
) || // can't have PTR and target address
841 _SCErrorSet(kSCStatusInvalidArgument
);
845 target
= __SCNetworkReachabilityCreateWithPTR(NULL
, addr_p
);
847 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
848 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
849 } else if (addr_r
!= NULL
) {
850 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
851 } else if (addr_l
!= NULL
) {
852 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, NULL
);
854 _SCErrorSet(kSCStatusInvalidArgument
);
858 if (target
== NULL
) {
862 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
864 if (interface
!= NULL
) {
865 if ((_SC_cfstring_to_cstring(interface
,
868 kCFStringEncodingASCII
) == NULL
) ||
869 ((if_index
= if_nametoindex(if_name
)) == 0)) {
870 CFRelease(targetPrivate
);
871 _SCErrorSet(kSCStatusInvalidArgument
);
876 if (targetPrivate
->parameters
== NULL
) {
877 targetPrivate
->parameters
= nw_parameters_create();
881 nw_interface_t interfaceObject
= nw_interface_create_with_index(if_index
);
882 nw_parameters_require_interface(targetPrivate
->parameters
, interfaceObject
);
883 network_release(interfaceObject
);
887 if (resolverBypass
!= NULL
) {
888 targetPrivate
->resolverBypass
= CFBooleanGetValue(resolverBypass
);
892 if (sourceAppAuditToken
!= NULL
) {
893 audit_token_t atoken
;
894 CFDataGetBytes(sourceAppAuditToken
,
895 CFRangeMake(0, CFDataGetLength(sourceAppAuditToken
)),
897 nw_parameters_set_source_application(targetPrivate
->parameters
, atoken
);
899 } else if (sourceAppBundleID
!= NULL
) {
900 char *cBundleID
= _SC_cfstring_to_cstring(sourceAppBundleID
,
903 kCFStringEncodingUTF8
);
904 if (cBundleID
!= NULL
) {
905 nw_parameters_set_source_application_by_bundle_id(targetPrivate
->parameters
,
907 CFAllocatorDeallocate(NULL
, (void *)cBundleID
);
909 SC_log(LOG_WARNING
, "failed to convert %@ to a C string", sourceAppBundleID
);
915 const char *opt
= "???";
917 switch (targetPrivate
->type
) {
918 case reachabilityTypeAddress
:
919 opt
= DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS
;
921 case reachabilityTypeAddressPair
:
922 opt
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS
;
924 case reachabilityTypeName
:
925 opt
= DEBUG_REACHABILITY_TYPE_NAME_OPTIONS
;
927 case reachabilityTypePTR
:
928 opt
= DEBUG_REACHABILITY_TYPE_PTR_OPTIONS
;
932 SC_log(LOG_DEBUG
, "%s%s %@",
933 targetPrivate
->log_prefix
,
938 return (SCNetworkReachabilityRef
)targetPrivate
;
943 SCNetworkReachabilityGetTypeID(void)
945 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
946 return __kSCNetworkReachabilityTypeID
;
950 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
951 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
954 CFMutableArrayRef array
= NULL
;
955 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
957 if (!isA_SCNetworkReachability(target
)) {
958 _SCErrorSet(kSCStatusInvalidArgument
);
962 if (!isReachabilityTypeName(targetPrivate
->type
)) {
963 _SCErrorSet(kSCStatusInvalidArgument
);
971 MUTEX_LOCK(&targetPrivate
->lock
);
973 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
974 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
975 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
976 #pragma unused(index)
977 nw_endpoint_type_t endpoint_type
= nw_endpoint_get_type((nw_endpoint_t
)object
);
978 if (endpoint_type
== nw_endpoint_type_address
) {
979 const struct sockaddr
*address
= nw_endpoint_get_address((nw_endpoint_t
)object
);
980 if (address
== NULL
) {
981 SC_log(LOG_ERR
, "nw_endpoint_type_address w/no address");
985 CFDataRef addressData
= CFDataCreate(kCFAllocatorDefault
, (const uint8_t *)address
, address
->sa_len
);
986 CFArrayAppendValue(array
, addressData
);
987 CFRelease(addressData
);
988 } else if (endpoint_type
== nw_endpoint_type_host
) {
989 const char *host
= nw_endpoint_get_hostname((nw_endpoint_t
)object
);
991 SC_log(LOG_ERR
, "nw_endpoint_type_host w/no host");
995 CFStringRef string
= CFStringCreateWithCString(kCFAllocatorDefault
, host
, kCFStringEncodingASCII
);
996 if (string
== NULL
) {
997 SC_log(LOG_ERR
, "nw_endpoint_type_host w/non-ASCII host");
1001 if (CFStringHasPrefix(string
, CFSTR(".")) || CFStringHasSuffix(string
, CFSTR("."))) {
1002 CFMutableStringRef mutableString
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, string
);
1004 string
= mutableString
;
1005 CFStringTrim(mutableString
, CFSTR("."));
1007 CFArrayAppendValue(array
, string
);
1010 SC_log(LOG_ERR
, "unexpected nw_endpoint type: %d", endpoint_type
);
1016 MUTEX_UNLOCK(&targetPrivate
->lock
);
1017 _SCErrorSet(kSCStatusOK
);
1022 #pragma mark Reachability Flags
1025 __SCNetworkReachabilityGetAgentVPNFlags(xpc_object_t dictionary
, Boolean
*vpn
, Boolean
*onDemand
)
1027 const struct netagent
*agent
= NULL
;
1030 if (dictionary
== NULL
|| vpn
== NULL
|| onDemand
== NULL
) {
1037 agent
= xpc_dictionary_get_data(dictionary
, REACHABILITY_AGENT_DATA_KEY
, &length
);
1038 if (agent
== NULL
|| length
< sizeof(struct netagent
) || length
!= (sizeof(struct netagent
) + agent
->netagent_data_size
)) {
1042 if (strncmp(REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN
, agent
->netagent_domain
, NETAGENT_DOMAINSIZE
) == 0) {
1044 if ((agent
->netagent_flags
& NETAGENT_FLAG_VOLUNTARY
) &&
1045 !(agent
->netagent_flags
& NETAGENT_FLAG_ACTIVE
)) {
1052 nw_path_is_linklocal_direct(nw_path_t path
)
1054 bool is_linklocal_direct
= false;
1056 nw_endpoint_t endpoint
= nw_path_copy_endpoint(path
);
1057 if (endpoint
== NULL
) {
1061 if (nw_endpoint_get_type(endpoint
) == nw_endpoint_type_address
) {
1062 const struct sockaddr_in
*sin
;
1064 sin
= (const struct sockaddr_in
*)(void *)nw_endpoint_get_address(endpoint
);;
1065 if ((sin
!= NULL
) &&
1066 (sin
->sin_family
== AF_INET
) &&
1067 IN_LINKLOCAL(ntohl(sin
->sin_addr
.s_addr
))) {
1068 nw_interface_t interface
= nw_path_copy_interface(path
);
1070 if (interface
!= NULL
) {
1071 nw_interface_type_t type
= nw_interface_get_type(interface
);
1072 if ((type
== nw_interface_type_wired
) ||
1073 ((type
== nw_interface_type_wifi
) &&
1074 (nw_interface_get_subtype(interface
) != nw_interface_subtype_wifi_awdl
))) {
1075 is_linklocal_direct
= true;
1078 network_release(interface
);
1083 network_release(endpoint
);
1084 return is_linklocal_direct
;
1087 static SCNetworkReachabilityFlags
1088 __SCNetworkReachabilityGetFlagsFromPath(nw_path_t path
,
1089 ReachabilityAddressType type
,
1090 nw_resolver_status_t resolverStatus
,
1091 nw_array_t resolvedEndpoints
,
1092 Boolean resolvedEndpointUseFlags
,
1093 SCNetworkReachabilityFlags resolvedEndpointFlags
)
1095 __block SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
1096 __block
const char *why
= "???";
1098 nw_path_status_t status
= nw_path_get_status(path
);
1099 if (status
== nw_path_status_satisfied
) {
1100 __block
bool checkDNSFlags
= TRUE
;
1101 flags
= kSCNetworkReachabilityFlagsReachable
;
1102 why
= "nw_path_status_satisfied";
1103 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1104 if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1105 flags
|= (kSCNetworkReachabilityFlagsTransientConnection
| kSCNetworkReachabilityFlagsIsWWAN
);
1106 why
= "nw_path_status_satisfied, cellular";
1109 xpc_object_t agent_dictionary
= nw_path_copy_netagent_dictionary(path
);
1110 if (agent_dictionary
!= NULL
) {
1111 if (xpc_dictionary_get_count(agent_dictionary
) > 0) {
1112 xpc_dictionary_apply(agent_dictionary
, ^bool(const char *key
, xpc_object_t value
) {
1114 Boolean vpn
= FALSE
;
1115 Boolean onDemand
= FALSE
;
1116 __SCNetworkReachabilityGetAgentVPNFlags(value
, &vpn
, &onDemand
);
1118 // VPN flows are transient
1119 flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1120 why
= "nw_path_status_satisfied, VPN";
1123 type
== reachabilityTypeName
&&
1124 resolverStatus
== nw_resolver_status_complete
&&
1125 nw_array_get_count(resolvedEndpoints
) == 0) {
1126 // On Demand by hostname, when no address has been resolved
1127 flags
|= (kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsConnectionOnDemand
);
1128 why
= "nw_path_status_satisfied, OnDemand";
1129 checkDNSFlags
= FALSE
;
1135 xpc_release(agent_dictionary
);
1137 if (isReachabilityTypeName(type
)) {
1138 if (checkDNSFlags
) {
1139 if (resolverStatus
== nw_resolver_status_complete
&&
1140 nw_array_get_count(resolvedEndpoints
) == 0) {
1141 // DNS didn't resolve, as a final answer for now. Not reachable!
1143 why
= "nw_path_status_satisfied, DNS not reachable";
1144 } else if (resolvedEndpointUseFlags
) {
1145 flags
= resolvedEndpointFlags
;
1146 why
= "nw_path_status_satisfied, resolved endpoint flags";
1150 if (nw_path_is_direct(path
) || nw_path_is_linklocal_direct(path
)) {
1151 flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1152 why
= "nw_path_status_satisfied, by address, direct";
1154 if (nw_path_is_local(path
)) {
1155 flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1156 why
= "nw_path_status_satisfied, by address, local";
1159 } else if (status
== nw_path_status_unsatisfied
) {
1161 why
= "nw_path_status_unsatisfied";
1162 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1163 if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1164 flags
|= kSCNetworkReachabilityFlagsIsWWAN
;
1165 why
= "nw_path_status_unsatisfied, WWAN";
1168 } else if (status
== nw_path_status_satisfiable
) {
1169 flags
= (kSCNetworkReachabilityFlagsReachable
| kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsTransientConnection
);
1170 why
= "nw_path_status_satisfiable";
1172 if (nw_path_get_vpn_config_id(path
, &vpn_uuid
)) {
1173 flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
1174 why
= "nw_path_status_satisfiable, OnDemand";
1176 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1177 else if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1178 flags
|= kSCNetworkReachabilityFlagsIsWWAN
;
1179 why
= "nw_path_status_satisfiable, WWAN";
1184 SC_log(LOG_DEBUG
, "__SCNetworkReachabilityGetFlagsFromPath, flags = 0x%08x, %s", flags
, why
);
1185 return (flags
& kSCNetworkReachabilityFlagsMask
);
1188 static nw_endpoint_t
1189 __SCNetworkReachabilityGetPrimaryEndpoint(SCNetworkReachabilityPrivateRef targetPrivate
)
1191 if (targetPrivate
->type
== reachabilityTypeName
) {
1192 return targetPrivate
->hostnameEndpoint
;
1193 } else if (targetPrivate
->type
== reachabilityTypeAddress
||
1194 targetPrivate
->type
== reachabilityTypeAddressPair
||
1195 targetPrivate
->type
== reachabilityTypePTR
) {
1196 return targetPrivate
->remoteAddressEndpoint
;
1202 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target
)
1206 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1207 SCNetworkReachabilityFlags flags
= 0;
1209 if (!isA_SCNetworkReachability(target
)) {
1210 _SCErrorSet(kSCStatusInvalidArgument
);
1214 MUTEX_LOCK(&targetPrivate
->lock
);
1216 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1217 targetPrivate
->type
,
1218 nw_resolver_status_invalid
,
1220 targetPrivate
->lastResolvedEndpointHasFlags
,
1221 targetPrivate
->lastResolvedEndpointFlags
);
1223 /* Only return the if_index if the connection is reachable not for reachable connection
1224 * required etc ... */
1225 if (ok
&& __SCNetworkReachabilityRank(flags
) == ReachabilityRankReachable
) {
1226 if (targetPrivate
->lastResolvedEndpointHasFlags
) {
1227 if_index
= targetPrivate
->lastResolvedEndpointInterfaceIndex
;
1229 if_index
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1233 MUTEX_UNLOCK(&targetPrivate
->lock
);
1237 // CrazyIvan46 is the feature that allows connections to IPv4 literals on IPv6-only (NAT64+DNS64) networks to work
1238 // This function replaces the path when the initial one isn't satisfied and our target is an IPv4 literal
1239 // It tries IPv6 reachability instead in case we could synthesize another address to connect to
1240 static OS_OBJECT_RETURNS_RETAINED nw_path_t
1241 __SCNetworkReachabilityCreateCrazyIvan46Path(nw_path_t path
, nw_endpoint_t endpoint
,
1242 nw_parameters_t parameters
, Boolean allow_resolution
)
1244 nw_path_t retPath
= NULL
;
1245 const struct sockaddr
*sa
;
1247 if ((nw_path_get_status(path
) != nw_path_status_unsatisfied
) ||
1248 (NULL
== endpoint
) || (nw_endpoint_get_type(endpoint
) != nw_endpoint_type_address
)) {
1252 sa
= nw_endpoint_get_address(endpoint
);
1254 if (sa
->sa_family
!= AF_INET
) {
1258 if (allow_resolution
) {
1259 uint32_t ifIndex
= 0;
1260 int32_t numPrefixes
;
1261 nw_nat64_prefix_t
*prefixes
= NULL
;
1262 struct sockaddr_in sin
;
1264 memcpy(&sin
, sa
, MIN(sizeof(sin
), sa
->sa_len
));
1265 if (NULL
!= parameters
) {
1266 ifIndex
= nw_parameters_get_required_interface_index(parameters
);
1268 numPrefixes
= nw_nat64_copy_prefixes(&ifIndex
, &prefixes
);
1269 if (numPrefixes
> 0) {
1270 struct sockaddr_in6 synthesizedAddress
= {
1271 .sin6_len
= sizeof(struct sockaddr_in6
),
1272 .sin6_family
= AF_INET6
,
1273 .sin6_port
= nw_endpoint_get_port(endpoint
),
1277 nw_endpoint_t synthesizedEndpoint
;
1278 nw_path_evaluator_t synthesizedEvaluator
;
1279 nw_path_t synthesizedPath
;
1281 nw_nat64_synthesize_v6(&prefixes
[0], &sin
.sin_addr
, &synthesizedAddress
.sin6_addr
);
1282 synthesizedEndpoint
= nw_endpoint_create_address((const struct sockaddr
*)&synthesizedAddress
);
1283 synthesizedEvaluator
= nw_path_create_evaluator_for_endpoint(synthesizedEndpoint
, parameters
);
1284 synthesizedPath
= nw_path_evaluator_copy_path(synthesizedEvaluator
);
1285 if (nw_path_get_status(synthesizedPath
) != nw_path_status_unsatisfied
) {
1286 retPath
= synthesizedPath
;
1287 SC_log(LOG_INFO
, "Using CrazyIvan46 synthesized reachability result");
1289 network_release(synthesizedPath
);
1291 network_release(synthesizedEvaluator
);
1292 network_release(synthesizedEndpoint
);
1295 // Don't synthesize in non-scheduled mode to avoid generating DNS traffic
1297 nw_path_evaluator_t v6PathEvaluator
;
1298 nw_parameters_t v6Parameters
;
1300 if (NULL
!= parameters
) {
1301 v6Parameters
= nw_parameters_copy(parameters
);
1303 v6Parameters
= nw_parameters_create();
1305 nw_parameters_set_required_address_family(v6Parameters
, AF_INET6
);
1306 v6PathEvaluator
= nw_path_create_evaluator_for_endpoint(NULL
, v6Parameters
);
1307 v6Path
= nw_path_evaluator_copy_path(v6PathEvaluator
);
1308 if (nw_path_get_status(v6Path
) != nw_path_status_unsatisfied
) {
1310 SC_log(LOG_INFO
, "Using CrazyIvan46 simple reachability result");
1312 network_release(v6Path
);
1314 network_release(v6PathEvaluator
);
1315 network_release(v6Parameters
);
1321 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
1322 SCNetworkReachabilityFlags
*flags
)
1324 nw_path_t crazyIvanPath
;
1325 nw_endpoint_t endpoint
;
1328 nw_path_evaluator_t pathEvaluator
;
1329 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1331 if (!isA_SCNetworkReachability(target
)) {
1332 _SCErrorSet(kSCStatusInvalidArgument
);
1336 MUTEX_LOCK(&targetPrivate
->lock
);
1338 if (targetPrivate
->scheduled
) {
1339 // if being watched, return the last known (and what should be current) status
1340 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1341 targetPrivate
->type
,
1342 targetPrivate
->lastResolverStatus
,
1343 targetPrivate
->lastResolvedEndpoints
,
1344 targetPrivate
->lastResolvedEndpointHasFlags
,
1345 targetPrivate
->lastResolvedEndpointFlags
);
1346 // because we have synchronously captured the current status, we no longer
1347 // need our by-name required callback
1348 targetPrivate
->sentFirstUpdate
= TRUE
;
1352 // Not being watched, so run a one-shot path evaluator
1353 // We don't care about DNS resolution in this case, since we only need to have the
1354 // DNS resolution to support clients watching reachability to get updates
1355 endpoint
= __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
);
1356 pathEvaluator
= nw_path_create_evaluator_for_endpoint(endpoint
, targetPrivate
->parameters
);
1357 path
= nw_path_evaluator_copy_path(pathEvaluator
);
1359 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1360 crazyIvanPath
= __SCNetworkReachabilityCreateCrazyIvan46Path(path
, endpoint
,
1361 targetPrivate
->parameters
, FALSE
);
1362 if (NULL
!= crazyIvanPath
) {
1363 network_release(path
);
1364 path
= crazyIvanPath
;
1368 *flags
= __SCNetworkReachabilityGetFlagsFromPath(path
, 0, nw_resolver_status_invalid
, NULL
, FALSE
, 0);
1369 network_release(path
);
1370 network_release(pathEvaluator
);
1374 MUTEX_UNLOCK(&targetPrivate
->lock
);
1380 #pragma mark Notifications
1383 reachPerformAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1385 os_activity_t activity
;
1387 void (*context_release
)(const void *);
1388 SCNetworkReachabilityCallBack rlsFunction
;
1389 SCNetworkReachabilityFlags flags
= 0;
1391 activity
= os_activity_create("processing SCNetworkReachability notification",
1392 OS_ACTIVITY_CURRENT
,
1393 OS_ACTIVITY_FLAG_DEFAULT
);
1394 os_activity_scope(activity
);
1396 if (!targetPrivate
->scheduled
) {
1397 // if no longer scheduled
1398 SC_log(LOG_INFO
, "%sskipping SCNetworkReachability callback, no longer scheduled",
1399 targetPrivate
->log_prefix
);
1400 MUTEX_UNLOCK(&targetPrivate
->lock
);
1405 rlsFunction
= targetPrivate
->rlsFunction
;
1406 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
1407 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
1408 context_release
= targetPrivate
->rlsContext
.release
;
1410 context_info
= targetPrivate
->rlsContext
.info
;
1411 context_release
= NULL
;
1414 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1415 targetPrivate
->type
,
1416 targetPrivate
->lastResolverStatus
,
1417 targetPrivate
->lastResolvedEndpoints
,
1418 targetPrivate
->lastResolvedEndpointHasFlags
,
1419 targetPrivate
->lastResolvedEndpointFlags
);
1421 MUTEX_UNLOCK(&targetPrivate
->lock
);
1423 if (rlsFunction
!= NULL
) {
1424 SC_log(LOG_DEBUG
, "%sexec SCNetworkReachability callout w/flags = 0x%08x",
1425 targetPrivate
->log_prefix
,
1427 (*rlsFunction
)((SCNetworkReachabilityRef
)targetPrivate
,
1432 if (context_release
!= NULL
) {
1433 (*context_release
)(context_info
);
1438 os_release(activity
);
1444 reachPerform(void *info
)
1446 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)info
;
1448 MUTEX_LOCK(&targetPrivate
->lock
);
1449 reachPerformAndUnlock(targetPrivate
);
1454 reachUpdateAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1456 targetPrivate
->sentFirstUpdate
= TRUE
;
1457 if (targetPrivate
->rls
!= NULL
) {
1458 if (targetPrivate
->rlList
!= NULL
) {
1459 CFRunLoopSourceSignal(targetPrivate
->rls
);
1460 _SC_signalRunLoop(targetPrivate
, targetPrivate
->rls
, targetPrivate
->rlList
);
1462 MUTEX_UNLOCK(&targetPrivate
->lock
);
1464 reachPerformAndUnlock(targetPrivate
);
1469 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
1470 SCNetworkReachabilityCallBack callout
,
1471 SCNetworkReachabilityContext
*context
)
1473 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1475 MUTEX_LOCK(&targetPrivate
->lock
);
1477 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1478 /* let go of the current context */
1479 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1482 targetPrivate
->rlsFunction
= callout
;
1483 targetPrivate
->rlsContext
.info
= NULL
;
1484 targetPrivate
->rlsContext
.retain
= NULL
;
1485 targetPrivate
->rlsContext
.release
= NULL
;
1486 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1488 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
1489 if (context
->retain
!= NULL
) {
1490 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
1494 MUTEX_UNLOCK(&targetPrivate
->lock
);
1501 reachRLSCopyDescription(const void *info
)
1503 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1505 return CFStringCreateWithFormat(NULL
,
1507 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
1512 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
1513 CFRunLoopRef runLoop
,
1514 CFStringRef runLoopMode
)
1516 Boolean success
= FALSE
;
1517 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1518 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1519 _SCErrorSet(kSCStatusInvalidArgument
);
1523 MUTEX_LOCK(&targetPrivate
->lock
);
1525 if (targetPrivate
->scheduled
) {
1526 if (targetPrivate
->rls
!= NULL
&& targetPrivate
->rlList
!= NULL
) {
1527 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1529 * if we do not already have host notifications scheduled with
1530 * this runLoop / runLoopMode
1532 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1535 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
1537 MUTEX_UNLOCK(&targetPrivate
->lock
);
1540 MUTEX_UNLOCK(&targetPrivate
->lock
);
1541 _SCErrorSet(kSCStatusInvalidArgument
);
1546 CFRunLoopSourceContext context
= {
1548 , (void *)target
// info
1549 , CFRetain
// retain
1550 , CFRelease
// release
1551 , reachRLSCopyDescription
// copyDescription
1556 , reachPerform
// perform
1559 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
1560 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1562 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1564 * if we do not already have host notifications scheduled with
1565 * this runLoop / runLoopMode
1567 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1570 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
1572 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, _callback_queue());
1574 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1575 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1576 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1577 // if target is no longer scheduled for this runLoop / runLoopMode
1578 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1580 // if *all* notifications have been unscheduled
1581 CFRelease(targetPrivate
->rlList
);
1582 targetPrivate
->rlList
= NULL
;
1583 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1584 CFRelease(targetPrivate
->rls
);
1585 targetPrivate
->rls
= NULL
;
1591 MUTEX_UNLOCK(&targetPrivate
->lock
);
1596 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
1597 CFRunLoopRef runLoop
,
1598 CFStringRef runLoopMode
)
1600 Boolean success
= FALSE
;
1601 Boolean unscheduleDispatchQueue
= FALSE
;
1602 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1604 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1605 _SCErrorSet(kSCStatusInvalidArgument
);
1609 MUTEX_LOCK(&targetPrivate
->lock
);
1611 if (targetPrivate
->rlList
== NULL
|| targetPrivate
->rls
== NULL
|| !targetPrivate
->scheduled
) {
1612 MUTEX_UNLOCK(&targetPrivate
->lock
);
1613 _SCErrorSet(kSCStatusInvalidArgument
);
1617 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1618 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1619 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1620 // if target is no longer scheduled for this runLoop / runLoopMode
1621 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1623 // if *all* notifications have been unscheduled
1624 unscheduleDispatchQueue
= TRUE
;
1625 CFRelease(targetPrivate
->rlList
);
1626 targetPrivate
->rlList
= NULL
;
1627 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1628 CFRelease(targetPrivate
->rls
);
1629 targetPrivate
->rls
= NULL
;
1634 if (unscheduleDispatchQueue
) {
1635 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, NULL
);
1639 MUTEX_UNLOCK(&targetPrivate
->lock
);
1643 static __inline__
void
1644 __SCNetworkReachabilityCopyPathStatus(SCNetworkReachabilityPrivateRef targetPrivate
, SCNetworkReachabilityFlags
*flags
, uint
*ifIndex
, size_t *endpointCount
)
1647 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
,
1648 targetPrivate
->type
,
1649 targetPrivate
->lastResolverStatus
,
1650 targetPrivate
->lastResolvedEndpoints
,
1651 targetPrivate
->lastResolvedEndpointHasFlags
,
1652 targetPrivate
->lastResolvedEndpointFlags
);
1655 *ifIndex
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1657 if (endpointCount
) {
1658 *endpointCount
= nw_array_get_count(targetPrivate
->lastResolvedEndpoints
);
1663 static __inline__ Boolean
1664 __SCNetworkReachabilityShouldUpdateClient(SCNetworkReachabilityPrivateRef targetPrivate
, SCNetworkReachabilityFlags oldFlags
, uint oldIFIndex
, size_t oldEndpointCount
)
1666 SCNetworkReachabilityFlags newFlags
= 0;
1667 uint newIFIndex
= 0;
1668 size_t newEndpointCount
= 0;
1669 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &newFlags
, &newIFIndex
, &newEndpointCount
);
1670 return (!targetPrivate
->sentFirstUpdate
||
1671 oldFlags
!= newFlags
||
1672 oldIFIndex
!= newIFIndex
||
1673 oldEndpointCount
!= newEndpointCount
);
1677 __SCNetworkReachabilityRestartResolver(SCNetworkReachabilityPrivateRef targetPrivate
)
1679 if (targetPrivate
&&
1680 !targetPrivate
->resolverBypass
&&
1681 isReachabilityTypeName(targetPrivate
->type
)) {
1682 nw_resolver_t resolver
;
1683 CFRetain(targetPrivate
);
1684 if (NULL
!= targetPrivate
->resolver
) {
1685 nw_resolver_cancel(targetPrivate
->resolver
);
1687 if (targetPrivate
->lastPath
!= NULL
) {
1688 resolver
= nw_resolver_create_with_path(targetPrivate
->lastPath
);
1690 resolver
= nw_resolver_create_with_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
), targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
: targetPrivate
->parameters
);
1692 if (resolver
== NULL
) {
1693 SC_log(LOG_ERR
, "%sfailed to create a nw_resolver", targetPrivate
->log_prefix
);
1694 targetPrivate
->resolver
= NULL
;
1695 CFRelease(targetPrivate
);
1698 targetPrivate
->resolver
= resolver
;
1699 nw_resolver_set_cancel_handler(resolver
, ^(void) {
1700 MUTEX_LOCK(&targetPrivate
->lock
);
1701 if (resolver
== targetPrivate
->resolver
) {
1702 targetPrivate
->resolver
= NULL
;
1704 network_release(resolver
);
1705 MUTEX_UNLOCK(&targetPrivate
->lock
);
1706 CFRelease(targetPrivate
);
1708 if (!nw_resolver_set_update_handler(resolver
, targetPrivate
->dispatchQueue
, ^(nw_resolver_status_t status
, nw_array_t resolved_endpoints
) {
1709 MUTEX_LOCK(&targetPrivate
->lock
);
1710 if (targetPrivate
->scheduled
) {
1711 SCNetworkReachabilityFlags oldFlags
= 0;
1712 uint oldIFIndex
= 0;
1713 size_t oldEndpointCount
= 0;
1714 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &oldFlags
, &oldIFIndex
, &oldEndpointCount
);
1716 targetPrivate
->lastResolverStatus
= status
;
1717 network_release(targetPrivate
->lastResolvedEndpoints
);
1718 targetPrivate
->lastResolvedEndpoints
= network_retain(resolved_endpoints
);
1720 // Run path evaluation on the resolved endpoints
1721 __block Boolean hasFlags
= FALSE
;
1722 targetPrivate
->lastResolvedEndpointHasFlags
= FALSE
;
1723 targetPrivate
->lastResolvedEndpointFlags
= 0;
1724 targetPrivate
->lastResolvedEndpointInterfaceIndex
= 0;
1725 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
1726 #pragma unused(index)
1727 SCNetworkReachabilityFlags flags
= 0;
1728 uint interfaceIndex
= 0;
1729 ReachabilityRankType rank
;
1730 nw_endpoint_t resolvedEndpoint
= (nw_endpoint_t
)object
;
1731 nw_path_evaluator_t pathEvaluator
= nw_path_create_evaluator_for_endpoint(resolvedEndpoint
, targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
: targetPrivate
->parameters
);
1732 nw_path_t path
= nw_path_evaluator_copy_path(pathEvaluator
);
1734 flags
= __SCNetworkReachabilityGetFlagsFromPath(path
, 0, nw_resolver_status_invalid
, NULL
, FALSE
, 0);
1737 interfaceIndex
= nw_path_get_interface_index(path
);
1738 network_release(path
);
1739 network_release(pathEvaluator
);
1741 rank
= __SCNetworkReachabilityRank(flags
);
1742 if (rank
> __SCNetworkReachabilityRank(targetPrivate
->lastResolvedEndpointFlags
)) {
1743 // Return the best case result
1744 targetPrivate
->lastResolvedEndpointFlags
= flags
;
1745 targetPrivate
->lastResolvedEndpointInterfaceIndex
= interfaceIndex
;
1746 if (rank
== ReachabilityRankReachable
) {
1747 // Can't get any better than REACHABLE
1753 targetPrivate
->lastResolvedEndpointHasFlags
= hasFlags
;
1755 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1756 reachUpdateAndUnlock(targetPrivate
);
1758 MUTEX_UNLOCK(&targetPrivate
->lock
);
1761 MUTEX_UNLOCK(&targetPrivate
->lock
);
1764 network_release(resolver
);
1765 targetPrivate
->resolver
= NULL
;
1766 CFRelease(targetPrivate
);
1772 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
1773 dispatch_queue_t queue
)
1777 if (queue
!= NULL
) {
1778 nw_path_t crazyIvanPath
;
1779 nw_endpoint_t endpoint
;
1780 nw_path_evaluator_t pathEvaluator
;
1782 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
1783 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
1784 _SCErrorSet(kSCStatusInvalidArgument
);
1788 SC_log(LOG_DEBUG
, "%sscheduled", targetPrivate
->log_prefix
);
1790 // retain dispatch queue
1791 dispatch_retain(queue
);
1792 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1793 endpoint
= __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
);
1794 pathEvaluator
= nw_path_create_evaluator_for_endpoint(endpoint
, targetPrivate
->parameters
);
1795 targetPrivate
->pathEvaluator
= pathEvaluator
;
1796 targetPrivate
->dispatchQueue
= queue
;
1797 targetPrivate
->scheduled
= TRUE
;
1798 if (isReachabilityTypeName(targetPrivate
->type
)) {
1799 // we must have at least one callback for by-name queries
1800 targetPrivate
->sentFirstUpdate
= FALSE
;
1802 targetPrivate
->sentFirstUpdate
= TRUE
;
1805 network_release(targetPrivate
->lastPath
);
1806 targetPrivate
->lastPath
= nw_path_evaluator_copy_path(pathEvaluator
);
1808 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1809 crazyIvanPath
= __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate
->lastPath
, endpoint
,
1810 targetPrivate
->parameters
, FALSE
);
1811 if (NULL
!= crazyIvanPath
) {
1812 network_release(targetPrivate
->lastPath
);
1813 targetPrivate
->lastPath
= crazyIvanPath
;
1817 network_release(targetPrivate
->lastPathParameters
);
1818 targetPrivate
->lastPathParameters
= nw_path_copy_derived_parameters(targetPrivate
->lastPath
);
1820 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1821 network_release(targetPrivate
->lastResolvedEndpoints
);
1822 targetPrivate
->lastResolvedEndpoints
= NULL
;
1823 __SCNetworkReachabilityRestartResolver(targetPrivate
);
1825 CFRetain(targetPrivate
);
1826 nw_path_evaluator_set_cancel_handler(pathEvaluator
, ^(void) {
1827 MUTEX_LOCK(&targetPrivate
->lock
);
1828 if (pathEvaluator
== targetPrivate
->pathEvaluator
) {
1829 targetPrivate
->pathEvaluator
= NULL
;
1831 network_release(pathEvaluator
);
1832 MUTEX_UNLOCK(&targetPrivate
->lock
);
1833 CFRelease(targetPrivate
);
1836 if (!nw_path_evaluator_set_update_handler(pathEvaluator
, targetPrivate
->dispatchQueue
, ^(nw_path_t path
) {
1837 MUTEX_LOCK(&targetPrivate
->lock
);
1838 if (targetPrivate
->scheduled
) {
1839 nw_path_t crazyIvanPath
;
1840 SCNetworkReachabilityFlags oldFlags
= 0;
1841 uint oldIFIndex
= 0;
1842 size_t oldEndpointCount
= 0;
1843 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &oldFlags
, &oldIFIndex
, &oldEndpointCount
);
1845 network_release(targetPrivate
->lastPath
);
1846 targetPrivate
->lastPath
= network_retain(path
);
1848 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1850 __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate
->lastPath
,
1852 targetPrivate
->parameters
,
1854 if (NULL
!= crazyIvanPath
) {
1855 network_release(targetPrivate
->lastPath
);
1856 targetPrivate
->lastPath
= crazyIvanPath
;
1860 if (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) {
1861 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1862 __SCNetworkReachabilityRestartResolver(targetPrivate
);
1865 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1866 reachUpdateAndUnlock(targetPrivate
);
1868 MUTEX_UNLOCK(&targetPrivate
->lock
);
1871 MUTEX_UNLOCK(&targetPrivate
->lock
);
1874 targetPrivate
->pathEvaluator
= NULL
;
1875 network_release(pathEvaluator
);
1876 CFRelease(targetPrivate
);
1879 if (targetPrivate
->dispatchQueue
== NULL
) { // if we should be scheduled on a dispatch queue (but are not)
1880 _SCErrorSet(kSCStatusInvalidArgument
);
1884 if (!targetPrivate
->scheduled
) {
1885 // if not currently scheduled
1886 _SCErrorSet(kSCStatusInvalidArgument
);
1890 targetPrivate
->scheduled
= FALSE
;
1891 targetPrivate
->sentFirstUpdate
= FALSE
;
1892 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1893 targetPrivate
->pathEvaluator
= NULL
;
1894 network_release(targetPrivate
->lastPath
);
1895 targetPrivate
->lastPath
= NULL
;
1896 network_release(targetPrivate
->lastPathParameters
);
1897 targetPrivate
->lastPathParameters
= NULL
;
1898 network_release(targetPrivate
->lastResolvedEndpoints
);
1899 targetPrivate
->lastResolvedEndpoints
= NULL
;
1900 if (NULL
!= targetPrivate
->resolver
) {
1901 nw_resolver_cancel(targetPrivate
->resolver
);
1902 targetPrivate
->resolver
= NULL
;
1904 if (targetPrivate
->dispatchQueue
!= NULL
) {
1905 dispatch_release(targetPrivate
->dispatchQueue
);
1906 targetPrivate
->dispatchQueue
= NULL
;
1909 SC_log(LOG_DEBUG
, "%sunscheduled", targetPrivate
->log_prefix
);
1917 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
1918 dispatch_queue_t queue
)
1920 if (!isA_SCNetworkReachability(target
)) {
1921 _SCErrorSet(kSCStatusInvalidArgument
);
1925 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1926 MUTEX_LOCK(&targetPrivate
->lock
);
1927 Boolean success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, queue
);
1928 MUTEX_UNLOCK(&targetPrivate
->lock
);
1933 * _SC_checkResolverReachabilityByAddress()
1935 * Given an IP address, determine whether a reverse DNS query can be issued
1936 * using the current network configuration.
1939 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
1940 SCNetworkReachabilityFlags
*flags
,
1942 struct sockaddr
*sa
)
1944 #pragma unused(storeP)
1946 nw_path_evaluator_t evaluator
= nw_path_create_default_evaluator();
1947 nw_path_t path
= nw_path_evaluator_copy_path(evaluator
);
1948 if (nw_path_get_status(path
) == nw_path_status_unsatisfied_network
) {
1957 *flags
= kSCNetworkReachabilityFlagsReachable
;
1963 network_release(evaluator
);
1964 network_release(path
);