2 * Copyright (c) 2003-2020 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>
56 #include <os/overflow.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"
71 #include <nw/private.h>
73 #define DEBUG_REACHABILITY_TYPE_NAME "create w/name"
74 #define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
76 #define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"
77 #define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
79 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"
80 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
82 #define DEBUG_REACHABILITY_TYPE_PTR "create w/ptr"
83 #define DEBUG_REACHABILITY_TYPE_PTR_OPTIONS " + options"
85 static pthread_mutexattr_t lock_attr
;
87 #define MUTEX_INIT(m) { \
88 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \
92 #define MUTEX_LOCK(m) { \
93 int _lock_ = (pthread_mutex_lock(m) == 0); \
97 #define MUTEX_UNLOCK(m) { \
98 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
102 #define MUTEX_ASSERT_HELD(m) { \
103 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \
110 #define REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN "NetworkExtension"
111 #define REACHABILITY_AGENT_DATA_KEY "data"
114 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
115 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
117 static SCNetworkReachabilityFlags
118 __SCNetworkReachabilityGetFlagsFromPath(const char *prefix
,
121 ReachabilityAddressType type
,
122 nw_resolver_status_t resolverStatus
,
123 nw_array_t resolvedEndpoints
,
124 Boolean resolvedEndpointUseFlags
,
125 SCNetworkReachabilityFlags resolvedEndpointFlags
);
128 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
129 dispatch_queue_t queue
);
131 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
134 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
136 "SCNetworkReachability", // className
139 __SCNetworkReachabilityDeallocate
, // dealloc
142 NULL
, // copyFormattingDesc
143 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
147 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
149 static dispatch_queue_t
152 static dispatch_once_t once
;
153 static dispatch_queue_t q
;
155 dispatch_once(&once
, ^{
156 q
= dispatch_queue_create("SCNetworkReachability.callback", NULL
);
163 __log_SCNetworkReachability(void)
165 static os_log_t log
= NULL
;
168 log
= os_log_create("com.apple.SystemConfiguration", "SCNetworkReachability");
176 #pragma mark SCNetworkReachability APIs
179 static __inline__ CFTypeRef
180 isA_SCNetworkReachability(CFTypeRef obj
)
182 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
186 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target
)
188 CFAllocatorRef allocator
= CFGetAllocator(target
);
189 CFMutableStringRef str
;
190 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
192 str
= CFStringCreateMutable(allocator
, 0);
193 switch (targetPrivate
->type
) {
194 case reachabilityTypeAddress
:
195 case reachabilityTypeAddressPair
: {
197 if (targetPrivate
->localAddressEndpoint
!= NULL
) {
198 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->localAddressEndpoint
), buf
, sizeof(buf
));
199 CFStringAppendFormat(str
, NULL
, CFSTR("local address = %s"),
202 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
203 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
204 CFStringAppendFormat(str
, NULL
, CFSTR("%s%saddress = %s"),
205 targetPrivate
->localAddressEndpoint
? ", " : "",
206 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
209 CFStringAppendFormat(str
, NULL
, CFSTR("default path"));
213 case reachabilityTypeName
: {
214 CFStringAppendFormat(str
, NULL
, CFSTR("name = %s"), nw_endpoint_get_hostname(targetPrivate
->hostnameEndpoint
));
217 case reachabilityTypePTR
: {
219 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
220 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
221 CFStringAppendFormat(str
, NULL
, CFSTR("ptr = %s"),
228 if (targetPrivate
->parameters
!= NULL
) {
229 unsigned int if_index
;
231 if_index
= nw_parameters_get_required_interface_index(targetPrivate
->parameters
);
233 CFStringAppendFormat(str
, NULL
, CFSTR(", if_index = %u"), if_index
);
242 __SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target
)
244 CFAllocatorRef allocator
= CFGetAllocator(target
);
245 SCNetworkReachabilityFlags flags
;
246 unsigned int if_index
;
248 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
250 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->log_prefix
,
252 targetPrivate
->lastPath
,
254 targetPrivate
->lastResolverStatus
,
255 targetPrivate
->lastResolvedEndpoints
,
256 targetPrivate
->lastResolvedEndpointHasFlags
,
257 targetPrivate
->lastResolvedEndpointFlags
);
258 if_index
= targetPrivate
->lastResolvedEndpointHasFlags
? targetPrivate
->lastResolvedEndpointInterfaceIndex
259 : nw_path_get_interface_index(targetPrivate
->lastPath
);
260 str
= CFStringCreateWithFormat(allocator
,
262 CFSTR("flags = 0x%08x, if_index = %u"),
270 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
272 CFAllocatorRef allocator
= CFGetAllocator(cf
);
273 CFMutableStringRef result
;
275 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
276 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
278 MUTEX_LOCK(&targetPrivate
->lock
);
280 result
= CFStringCreateMutable(allocator
, 0);
281 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
283 // add target description
284 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
285 CFStringAppend(result
, str
);
288 // add additional "name" info
289 if (isReachabilityTypeName(targetPrivate
->type
)) {
290 if (targetPrivate
->resolver
&& targetPrivate
->lastResolverStatus
== nw_resolver_status_invalid
) {
291 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
292 } else if (targetPrivate
->lastResolverStatus
!= nw_resolver_status_invalid
) {
293 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s"), (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) ? "complete" : "in progress");
294 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
295 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
296 #pragma unused(index)
297 nw_endpoint_t endpoint
= (nw_endpoint_t
)object
;
298 nw_endpoint_type_t endpoint_type
= nw_endpoint_get_type(endpoint
);
299 if (endpoint_type
== nw_endpoint_type_address
) {
301 const struct sockaddr
*sa
= nw_endpoint_get_address(endpoint
);
302 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
303 CFStringAppendFormat(result
, NULL
, CFSTR(", %s"), buf
);
304 } else if (endpoint_type
== nw_endpoint_type_host
) {
305 CFStringAppendFormat(result
, NULL
, CFSTR(", %s"), nw_endpoint_get_hostname(endpoint
));
307 CFStringAppendFormat(result
, NULL
, CFSTR(", unexpected nw_endpoint type: %d"), endpoint_type
);
312 CFStringAppendFormat(result
, NULL
, CFSTR(", no addresses"));
314 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
318 if (targetPrivate
->resolverBypass
) {
319 CFStringAppendFormat(result
, NULL
, CFSTR(", !resolve"));
323 if (targetPrivate
->scheduled
) {
324 str
= __SCNetworkReachabilityCopyTargetFlags(target
);
325 CFStringAppendFormat(result
, NULL
, CFSTR(", %@"), str
);
329 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
331 MUTEX_UNLOCK(&targetPrivate
->lock
);
338 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
340 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
341 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
343 SC_log(LOG_DEBUG
, "%srelease", targetPrivate
->log_prefix
);
345 /* release resources */
346 MUTEX_LOCK(&targetPrivate
->lock
);
347 targetPrivate
->scheduled
= FALSE
;
349 if (targetPrivate
->hostnameEndpoint
) {
350 nw_release(targetPrivate
->hostnameEndpoint
);
351 targetPrivate
->hostnameEndpoint
= NULL
;
353 if (targetPrivate
->localAddressEndpoint
) {
354 nw_release(targetPrivate
->localAddressEndpoint
);
355 targetPrivate
->localAddressEndpoint
= NULL
;
357 if (targetPrivate
->remoteAddressEndpoint
) {
358 nw_release(targetPrivate
->remoteAddressEndpoint
);
359 targetPrivate
->remoteAddressEndpoint
= NULL
;
361 if (targetPrivate
->parameters
) {
362 nw_release(targetPrivate
->parameters
);
363 targetPrivate
->parameters
= NULL
;
365 if (targetPrivate
->lastPath
) {
366 nw_release(targetPrivate
->lastPath
);
367 targetPrivate
->lastPath
= NULL
;
369 if (targetPrivate
->lastPathParameters
) {
370 nw_release(targetPrivate
->lastPathParameters
);
371 targetPrivate
->lastPathParameters
= NULL
;
373 if (targetPrivate
->lastResolvedEndpoints
) {
374 nw_release(targetPrivate
->lastResolvedEndpoints
);
375 targetPrivate
->lastResolvedEndpoints
= NULL
;
378 if (targetPrivate
->rlsContext
.release
!= NULL
) {
379 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
382 MUTEX_UNLOCK(&targetPrivate
->lock
);
384 pthread_mutex_destroy(&targetPrivate
->lock
);
391 __SCNetworkReachabilityInitialize(void)
393 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
395 pthread_mutexattr_init(&lock_attr
);
396 pthread_mutexattr_settype(&lock_attr
, PTHREAD_MUTEX_ERRORCHECK
);
401 static SCNetworkReachabilityPrivateRef
402 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
404 SCNetworkReachabilityPrivateRef targetPrivate
;
407 /* initialize runtime */
408 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
410 /* allocate target */
411 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
412 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
413 __kSCNetworkReachabilityTypeID
,
416 if (targetPrivate
== NULL
) {
420 /* initialize non-zero/NULL members */
421 MUTEX_INIT(&targetPrivate
->lock
);
422 if (_sc_log
> kSCLogDestinationFile
) {
423 snprintf(targetPrivate
->log_prefix
,
424 sizeof(targetPrivate
->log_prefix
),
429 return targetPrivate
;
432 static const struct sockaddr
*
433 is_valid_address(const struct sockaddr
*address
)
435 const struct sockaddr
*valid
= NULL
;
436 static Boolean warned
= FALSE
;
438 if ((address
!= NULL
) &&
439 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
440 switch (address
->sa_family
) {
442 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
444 } else if (!warned
) {
445 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
447 sizeof(struct sockaddr_in
));
452 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
454 } else if (!warned
) {
455 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
457 sizeof(struct sockaddr_in6
));
463 SC_log(LOG_WARNING
, "SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d",
474 __SCNetworkReachabilityAddressIsEmpty(const struct sockaddr
*address
)
476 if (address
== NULL
) {
480 if (address
->sa_family
== AF_INET
) {
481 return (((struct sockaddr_in
*)(void *)address
)->sin_addr
.s_addr
== 0);
482 } else if (address
->sa_family
== AF_INET6
) {
483 return IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6
*)(void *)address
)->sin6_addr
);
489 SCNetworkReachabilityRef
490 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
491 const struct sockaddr
*address
)
493 const struct sockaddr
*targetAddress
;
494 SCNetworkReachabilityPrivateRef targetPrivate
;
496 targetAddress
= is_valid_address(address
);
497 if (targetAddress
== NULL
) {
498 _SCErrorSet(kSCStatusInvalidArgument
);
502 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
503 if (targetPrivate
== NULL
) {
507 targetPrivate
->type
= reachabilityTypeAddress
;
509 if (!__SCNetworkReachabilityAddressIsEmpty(targetAddress
)) {
510 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(targetAddress
);
513 SC_log(LOG_DEBUG
, "%s%s %@",
514 targetPrivate
->log_prefix
,
515 DEBUG_REACHABILITY_TYPE_ADDRESS
,
518 return (SCNetworkReachabilityRef
)targetPrivate
;
522 #if !TARGET_OS_IPHONE
524 is_ipv4_loopback(const struct sockaddr
*sa
)
527 struct sockaddr_in
*sin
= (struct sockaddr_in
*)(void *)sa
;
530 (sa
->sa_len
< sizeof(struct sockaddr_in
)) ||
531 (sa
->sa_family
!= AF_INET
)) {
535 addr
= ntohl(sin
->sin_addr
.s_addr
);
536 return IN_LOOPBACK(addr
) ? TRUE
: FALSE
;
538 #endif // !TARGET_OS_IPHONE
542 is_same_address(const struct sockaddr
*a
, const struct sockaddr
*b
)
550 (a
->sa_family
!= b
->sa_family
) ||
551 (a
->sa_len
!= b
->sa_len
)) {
555 switch (a
->sa_family
) {
557 struct sockaddr_in
*a_sin
= (struct sockaddr_in
*)(void *)a
;
558 struct sockaddr_in
*b_sin
= (struct sockaddr_in
*)(void *)b
;
560 /* ALIGN: assuming a (and b) are aligned, then cast ok. */
561 a_addr
= &a_sin
->sin_addr
;
562 b_addr
= &b_sin
->sin_addr
;
563 len
= sizeof(struct in_addr
);
568 struct sockaddr_in6
*a_sin6
= (struct sockaddr_in6
*)(void *)a
;
569 struct sockaddr_in6
*b_sin6
= (struct sockaddr_in6
*)(void *)b
;
571 if (a_sin6
->sin6_scope_id
!= b_sin6
->sin6_scope_id
) {
575 a_addr
= &a_sin6
->sin6_addr
;
576 b_addr
= &b_sin6
->sin6_addr
;
577 len
= sizeof(struct in6_addr
);
588 return (bcmp(a_addr
, b_addr
, len
) == 0);
592 SCNetworkReachabilityRef
593 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
594 const struct sockaddr
*localAddress
,
595 const struct sockaddr
*remoteAddress
)
597 SCNetworkReachabilityPrivateRef targetPrivate
;
599 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
600 _SCErrorSet(kSCStatusInvalidArgument
);
604 if (localAddress
!= NULL
) {
605 localAddress
= is_valid_address(localAddress
);
606 if (localAddress
== NULL
) {
607 _SCErrorSet(kSCStatusInvalidArgument
);
612 if (remoteAddress
!= NULL
) {
613 remoteAddress
= is_valid_address(remoteAddress
);
614 if (remoteAddress
== NULL
) {
615 _SCErrorSet(kSCStatusInvalidArgument
);
620 #if !TARGET_OS_IPHONE
621 // Check/fix for loopback IP --> remote IP (rdar://26561383)
622 if ((localAddress
!= NULL
) && (remoteAddress
!= NULL
) &&
623 is_ipv4_loopback(localAddress
) && !is_ipv4_loopback(remoteAddress
)) {
624 static Boolean warned
= FALSE
;
627 SC_log(LOG_WARNING
, "BUG: SCNetworkReachabilityCreateWithAddressPair() called with local <loopback>");
628 SC_log(LOG_WARNING
, "address and remote <non-loopback> address. To return the expected (but actually");
629 SC_log(LOG_WARNING
, "incorrect) result, switched to SCNetworkReachabilityCreateWithAddress() with");
630 SC_log(LOG_WARNING
, "the remote address.");
634 return SCNetworkReachabilityCreateWithAddress(allocator
, remoteAddress
);
636 #endif // !TARGET_OS_IPHONE
638 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
639 if (targetPrivate
== NULL
) {
643 targetPrivate
->type
= reachabilityTypeAddressPair
;
645 if (localAddress
!= NULL
) {
646 targetPrivate
->localAddressEndpoint
= nw_endpoint_create_address(localAddress
);
649 if (remoteAddress
!= NULL
) {
650 if (is_same_address(localAddress
, remoteAddress
)) {
651 targetPrivate
->remoteAddressEndpoint
= nw_retain(targetPrivate
->localAddressEndpoint
);
653 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(remoteAddress
);
657 targetPrivate
->parameters
= nw_parameters_create();
658 nw_parameters_set_local_address(targetPrivate
->parameters
, targetPrivate
->localAddressEndpoint
);
660 SC_log(LOG_DEBUG
, "%s%s %@",
661 targetPrivate
->log_prefix
,
662 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR
,
665 return (SCNetworkReachabilityRef
)targetPrivate
;
669 SCNetworkReachabilityRef
670 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
671 const char *nodename
)
675 struct sockaddr_in sin
;
676 struct sockaddr_in6 sin6
;
679 SCNetworkReachabilityPrivateRef targetPrivate
;
681 if (nodename
== NULL
) {
682 _SCErrorSet(kSCStatusInvalidArgument
);
686 nodenameLen
= strlen(nodename
);
687 if (nodenameLen
== 0) {
688 _SCErrorSet(kSCStatusInvalidArgument
);
692 if (_SC_string_to_sockaddr(nodename
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) != NULL
) {
693 /* if this "nodename" is really an IP[v6] address in disguise */
694 return SCNetworkReachabilityCreateWithAddress(allocator
, &addr
.sa
);
697 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
698 if (targetPrivate
== NULL
) {
702 targetPrivate
->type
= reachabilityTypeName
;
704 targetPrivate
->hostnameEndpoint
= nw_endpoint_create_host(nodename
, "0");
706 SC_log(LOG_DEBUG
, "%s%s %@",
707 targetPrivate
->log_prefix
,
708 DEBUG_REACHABILITY_TYPE_NAME
,
711 return (SCNetworkReachabilityRef
)targetPrivate
;
715 static SCNetworkReachabilityRef
716 __SCNetworkReachabilityCreateWithPTR(CFAllocatorRef allocator
,
717 const struct sockaddr
*ptrAddress
)
719 SCNetworkReachabilityPrivateRef targetPrivate
;
721 ptrAddress
= is_valid_address(ptrAddress
);
722 if (ptrAddress
== NULL
) {
723 _SCErrorSet(kSCStatusInvalidArgument
);
727 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
728 if (targetPrivate
== NULL
) {
732 targetPrivate
->type
= reachabilityTypePTR
;
734 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(ptrAddress
);
736 targetPrivate
->parameters
= nw_parameters_create();
737 nw_parameters_set_resolve_ptr(targetPrivate
->parameters
, TRUE
);
739 SC_log(LOG_DEBUG
, "%s%s %@",
740 targetPrivate
->log_prefix
,
741 DEBUG_REACHABILITY_TYPE_PTR
,
744 return (SCNetworkReachabilityRef
)targetPrivate
;
747 SCNetworkReachabilityRef
748 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
749 CFDictionaryRef options
)
751 const struct sockaddr
*addr_l
= NULL
;
752 const struct sockaddr
*addr_p
= NULL
;
753 const struct sockaddr
*addr_r
= NULL
;
755 Boolean haveOpt
= FALSE
;
756 CFStringRef interface
= NULL
;
757 CFStringRef nodename
;
758 CFBooleanRef resolverBypass
;
759 SCNetworkReachabilityRef target
;
760 SCNetworkReachabilityPrivateRef targetPrivate
;
761 unsigned int if_index
= 0;
762 char if_name
[IFNAMSIZ
];
763 CFDataRef sourceAppAuditToken
= NULL
;
764 CFStringRef sourceAppBundleID
= NULL
;
766 if (!isA_CFDictionary(options
)) {
767 _SCErrorSet(kSCStatusInvalidArgument
);
771 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
772 if ((nodename
!= NULL
) &&
773 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
774 _SCErrorSet(kSCStatusInvalidArgument
);
777 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
779 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
780 _SCErrorSet(kSCStatusInvalidArgument
);
783 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
785 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionPTRAddress
);
787 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
788 _SCErrorSet(kSCStatusInvalidArgument
);
791 addr_p
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
793 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
795 if (!isA_CFData(data
) || ((size_t)CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
796 _SCErrorSet(kSCStatusInvalidArgument
);
799 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
801 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
802 if ((interface
!= NULL
) &&
803 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
804 _SCErrorSet(kSCStatusInvalidArgument
);
807 resolverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionResolverBypass
);
808 if ((resolverBypass
!= NULL
) && !isA_CFBoolean(resolverBypass
)) {
809 _SCErrorSet(kSCStatusInvalidArgument
);
812 sourceAppAuditToken
=
813 CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionSourceAppAuditToken
);
814 if ((sourceAppAuditToken
!= NULL
) &&
815 (!isA_CFData(sourceAppAuditToken
) ||
816 (CFDataGetLength(sourceAppAuditToken
) != sizeof(audit_token_t
)))) {
817 _SCErrorSet(kSCStatusInvalidArgument
);
821 CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionSourceAppBundleIdentifier
);
822 if ((sourceAppBundleID
!= NULL
) &&
823 (!isA_CFString(sourceAppBundleID
) ||
824 (CFStringGetLength(sourceAppBundleID
) == 0))) {
825 _SCErrorSet(kSCStatusInvalidArgument
);
829 if (nodename
!= NULL
) {
832 if ((addr_l
!= NULL
) || (addr_r
!= NULL
) || (addr_p
!= NULL
)) {
833 // can't have both a nodename and an address
834 _SCErrorSet(kSCStatusInvalidArgument
);
838 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
839 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
840 CFAllocatorDeallocate(NULL
, (void *)name
);
841 } else if (addr_p
!= NULL
) {
842 if ((addr_l
!= NULL
) || // can't have PTR and target address
844 _SCErrorSet(kSCStatusInvalidArgument
);
848 target
= __SCNetworkReachabilityCreateWithPTR(NULL
, addr_p
);
850 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
851 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
852 } else if (addr_r
!= NULL
) {
853 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
854 } else if (addr_l
!= NULL
) {
855 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, NULL
);
857 _SCErrorSet(kSCStatusInvalidArgument
);
861 if (target
== NULL
) {
865 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
867 if (interface
!= NULL
) {
868 if ((_SC_cfstring_to_cstring(interface
,
871 kCFStringEncodingASCII
) == NULL
) ||
872 ((if_index
= if_nametoindex(if_name
)) == 0)) {
873 CFRelease(targetPrivate
);
874 _SCErrorSet(kSCStatusInvalidArgument
);
879 if (targetPrivate
->parameters
== NULL
) {
880 targetPrivate
->parameters
= nw_parameters_create();
884 nw_interface_t interfaceObject
= nw_interface_create_with_index(if_index
);
885 nw_parameters_require_interface(targetPrivate
->parameters
, interfaceObject
);
886 nw_release(interfaceObject
);
890 if (resolverBypass
!= NULL
) {
891 targetPrivate
->resolverBypass
= CFBooleanGetValue(resolverBypass
);
895 if (sourceAppAuditToken
!= NULL
) {
896 audit_token_t atoken
;
897 CFDataGetBytes(sourceAppAuditToken
,
898 CFRangeMake(0, CFDataGetLength(sourceAppAuditToken
)),
900 nw_parameters_set_source_application(targetPrivate
->parameters
, atoken
);
902 } else if (sourceAppBundleID
!= NULL
) {
903 char *cBundleID
= _SC_cfstring_to_cstring(sourceAppBundleID
,
906 kCFStringEncodingUTF8
);
907 if (cBundleID
!= NULL
) {
908 nw_parameters_set_source_application_by_bundle_id(targetPrivate
->parameters
,
910 CFAllocatorDeallocate(NULL
, (void *)cBundleID
);
912 SC_log(LOG_WARNING
, "failed to convert %@ to a C string", sourceAppBundleID
);
918 const char *opt
= "???";
920 switch (targetPrivate
->type
) {
921 case reachabilityTypeAddress
:
922 opt
= DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS
;
924 case reachabilityTypeAddressPair
:
925 opt
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS
;
927 case reachabilityTypeName
:
928 opt
= DEBUG_REACHABILITY_TYPE_NAME_OPTIONS
;
930 case reachabilityTypePTR
:
931 opt
= DEBUG_REACHABILITY_TYPE_PTR_OPTIONS
;
935 SC_log(LOG_DEBUG
, "%s%s %@",
936 targetPrivate
->log_prefix
,
941 return (SCNetworkReachabilityRef
)targetPrivate
;
946 SCNetworkReachabilityGetTypeID(void)
948 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
949 return __kSCNetworkReachabilityTypeID
;
953 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
954 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
957 CFMutableArrayRef array
= NULL
;
958 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
960 if (!isA_SCNetworkReachability(target
)) {
961 _SCErrorSet(kSCStatusInvalidArgument
);
965 if (!isReachabilityTypeName(targetPrivate
->type
)) {
966 _SCErrorSet(kSCStatusInvalidArgument
);
974 MUTEX_LOCK(&targetPrivate
->lock
);
976 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
977 array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
978 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
979 #pragma unused(index)
980 nw_endpoint_type_t endpoint_type
= nw_endpoint_get_type((nw_endpoint_t
)object
);
981 if (endpoint_type
== nw_endpoint_type_address
) {
982 const struct sockaddr
*address
= nw_endpoint_get_address((nw_endpoint_t
)object
);
983 if (address
== NULL
) {
984 SC_log(LOG_ERR
, "nw_endpoint_type_address w/no address");
988 CFDataRef addressData
= CFDataCreate(kCFAllocatorDefault
, (const uint8_t *)address
, address
->sa_len
);
989 CFArrayAppendValue(array
, addressData
);
990 CFRelease(addressData
);
991 } else if (endpoint_type
== nw_endpoint_type_host
) {
992 const char *host
= nw_endpoint_get_hostname((nw_endpoint_t
)object
);
994 SC_log(LOG_ERR
, "nw_endpoint_type_host w/no host");
998 CFStringRef string
= CFStringCreateWithCString(kCFAllocatorDefault
, host
, kCFStringEncodingASCII
);
999 if (string
== NULL
) {
1000 SC_log(LOG_ERR
, "nw_endpoint_type_host w/non-ASCII host");
1004 if (CFStringHasPrefix(string
, CFSTR(".")) || CFStringHasSuffix(string
, CFSTR("."))) {
1005 CFMutableStringRef mutableString
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, string
);
1007 string
= mutableString
;
1008 CFStringTrim(mutableString
, CFSTR("."));
1010 CFArrayAppendValue(array
, string
);
1013 SC_log(LOG_ERR
, "unexpected nw_endpoint type: %d", endpoint_type
);
1019 MUTEX_UNLOCK(&targetPrivate
->lock
);
1020 _SCErrorSet(kSCStatusOK
);
1025 #pragma mark Reachability Flags
1028 __SCNetworkReachabilityGetAgentVPNFlags(xpc_object_t dictionary
, Boolean
*vpn
, Boolean
*onDemand
)
1030 const struct netagent
*agent
= NULL
;
1031 size_t expected
= 0;
1034 if (dictionary
== NULL
|| vpn
== NULL
|| onDemand
== NULL
) {
1041 agent
= xpc_dictionary_get_data(dictionary
, REACHABILITY_AGENT_DATA_KEY
, &length
);
1042 if ((agent
== NULL
) ||
1043 (length
< sizeof(struct netagent
)) ||
1044 os_add_overflow(sizeof(struct netagent
), agent
->netagent_data_size
, &expected
) ||
1045 (length
!= expected
)) {
1049 if (strncmp(REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN
, agent
->netagent_domain
, NETAGENT_DOMAINSIZE
) == 0) {
1051 if ((agent
->netagent_flags
& NETAGENT_FLAG_VOLUNTARY
) &&
1052 !(agent
->netagent_flags
& NETAGENT_FLAG_ACTIVE
)) {
1059 nw_path_is_linklocal_direct(nw_path_t path
)
1061 bool is_linklocal_direct
= false;
1063 nw_endpoint_t endpoint
= nw_path_copy_endpoint(path
);
1064 if (endpoint
== NULL
) {
1068 if (nw_endpoint_get_type(endpoint
) == nw_endpoint_type_address
) {
1069 const struct sockaddr_in
*sin
;
1071 sin
= (const struct sockaddr_in
*)(void *)nw_endpoint_get_address(endpoint
);;
1072 if ((sin
!= NULL
) &&
1073 (sin
->sin_family
== AF_INET
) &&
1074 IN_LINKLOCAL(ntohl(sin
->sin_addr
.s_addr
))) {
1075 nw_interface_t interface
= nw_path_copy_interface(path
);
1077 if (interface
!= NULL
) {
1078 nw_interface_type_t type
= nw_interface_get_type(interface
);
1079 if ((type
== nw_interface_type_wired
) ||
1080 ((type
== nw_interface_type_wifi
) &&
1081 (nw_interface_get_subtype(interface
) != nw_interface_subtype_wifi_awdl
))) {
1082 is_linklocal_direct
= true;
1085 nw_release(interface
);
1090 nw_release(endpoint
);
1091 return is_linklocal_direct
;
1094 static SCNetworkReachabilityFlags
1095 __SCNetworkReachabilityGetFlagsFromPath(const char *log_prefix
,
1098 ReachabilityAddressType type
,
1099 nw_resolver_status_t resolverStatus
,
1100 nw_array_t resolvedEndpoints
,
1101 Boolean resolvedEndpointUseFlags
,
1102 SCNetworkReachabilityFlags resolvedEndpointFlags
)
1104 __block SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
1105 __block
const char *why
= "???";
1107 nw_path_status_t status
= nw_path_get_status(path
);
1108 if (status
== nw_path_status_satisfied
) {
1109 __block
bool checkDNSFlags
= TRUE
;
1110 flags
= kSCNetworkReachabilityFlagsReachable
;
1111 why
= "nw_path_status_satisfied";
1112 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1113 if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1114 flags
|= (kSCNetworkReachabilityFlagsTransientConnection
| kSCNetworkReachabilityFlagsIsWWAN
);
1115 why
= "nw_path_status_satisfied, cellular";
1117 #endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1118 xpc_object_t agent_dictionary
= nw_path_copy_netagent_dictionary(path
);
1119 if (agent_dictionary
!= NULL
) {
1120 if (xpc_dictionary_get_count(agent_dictionary
) > 0) {
1121 xpc_dictionary_apply(agent_dictionary
, ^bool(const char *key
, xpc_object_t value
) {
1123 Boolean vpn
= FALSE
;
1124 Boolean onDemand
= FALSE
;
1125 __SCNetworkReachabilityGetAgentVPNFlags(value
, &vpn
, &onDemand
);
1127 // VPN flows are transient
1128 flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1129 why
= "nw_path_status_satisfied, VPN";
1132 type
== reachabilityTypeName
&&
1133 resolverStatus
== nw_resolver_status_complete
&&
1134 nw_array_get_count(resolvedEndpoints
) == 0) {
1135 // On Demand by hostname, when no address has been resolved
1136 flags
|= (kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsConnectionOnDemand
);
1137 why
= "nw_path_status_satisfied, OnDemand";
1138 checkDNSFlags
= FALSE
;
1144 xpc_release(agent_dictionary
);
1146 if (isReachabilityTypeName(type
)) {
1147 if (checkDNSFlags
) {
1148 if (resolverStatus
== nw_resolver_status_complete
&&
1149 nw_array_get_count(resolvedEndpoints
) == 0) {
1150 // DNS didn't resolve, as a final answer for now. Not reachable!
1152 why
= "nw_path_status_satisfied, DNS not reachable";
1153 } else if (resolvedEndpointUseFlags
) {
1154 flags
= resolvedEndpointFlags
;
1155 why
= "nw_path_status_satisfied, resolved endpoint flags";
1159 if (nw_path_is_direct(path
) || nw_path_is_linklocal_direct(path
)) {
1160 flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1161 why
= "nw_path_status_satisfied, by address, direct";
1163 if (nw_path_is_local(path
)) {
1164 flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1165 why
= "nw_path_status_satisfied, by address, local";
1168 } else if (status
== nw_path_status_unsatisfied
) {
1170 why
= "nw_path_status_unsatisfied";
1171 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1172 if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1173 flags
|= kSCNetworkReachabilityFlagsIsWWAN
;
1174 why
= "nw_path_status_unsatisfied, WWAN";
1176 #endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1177 } else if (status
== nw_path_status_satisfiable
) {
1178 flags
= (kSCNetworkReachabilityFlagsReachable
| kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsTransientConnection
);
1179 why
= "nw_path_status_satisfiable";
1181 if (nw_path_get_vpn_config_id(path
, &vpn_uuid
)) {
1182 flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
1183 why
= "nw_path_status_satisfiable, OnDemand";
1185 #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1186 else if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1187 flags
|= kSCNetworkReachabilityFlagsIsWWAN
;
1188 why
= "nw_path_status_satisfiable, WWAN";
1190 #endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
1194 SC_log(LOG_DEBUG
, "%s__SCNetworkReachabilityGetFlagsFromPath(%s), flags = 0x%08x, %s",
1200 return (flags
& kSCNetworkReachabilityFlagsMask
);
1203 static nw_endpoint_t
1204 __SCNetworkReachabilityGetPrimaryEndpoint(SCNetworkReachabilityPrivateRef targetPrivate
)
1206 if (targetPrivate
->type
== reachabilityTypeName
) {
1207 return targetPrivate
->hostnameEndpoint
;
1208 } else if (targetPrivate
->type
== reachabilityTypeAddress
||
1209 targetPrivate
->type
== reachabilityTypeAddressPair
||
1210 targetPrivate
->type
== reachabilityTypePTR
) {
1211 return targetPrivate
->remoteAddressEndpoint
;
1217 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target
)
1221 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1222 SCNetworkReachabilityFlags flags
= 0;
1224 if (!isA_SCNetworkReachability(target
)) {
1225 _SCErrorSet(kSCStatusInvalidArgument
);
1229 MUTEX_LOCK(&targetPrivate
->lock
);
1231 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->log_prefix
,
1232 "GetInterfaceIndex",
1233 targetPrivate
->lastPath
,
1234 targetPrivate
->type
,
1235 nw_resolver_status_invalid
,
1237 targetPrivate
->lastResolvedEndpointHasFlags
,
1238 targetPrivate
->lastResolvedEndpointFlags
);
1240 /* Only return the if_index if the connection is reachable not for reachable connection
1241 * required etc ... */
1242 if (ok
&& __SCNetworkReachabilityRank(flags
) == ReachabilityRankReachable
) {
1243 if (targetPrivate
->lastResolvedEndpointHasFlags
) {
1244 if_index
= targetPrivate
->lastResolvedEndpointInterfaceIndex
;
1246 if_index
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1250 MUTEX_UNLOCK(&targetPrivate
->lock
);
1254 // CrazyIvan46 is the feature that allows connections to IPv4 literals on IPv6-only (NAT64+DNS64) networks to work
1255 // This function replaces the path when the initial one isn't satisfied and our target is an IPv4 literal
1256 // It tries IPv6 reachability instead in case we could synthesize another address to connect to
1257 static OS_OBJECT_RETURNS_RETAINED nw_path_t
1258 __SCNetworkReachabilityCreateCrazyIvan46Path(nw_path_t path
, nw_endpoint_t endpoint
,
1259 nw_parameters_t parameters
, Boolean allow_resolution
)
1261 nw_path_t retPath
= NULL
;
1262 const struct sockaddr
*sa
;
1264 if ((path
== NULL
) ||
1265 (nw_path_get_status(path
) != nw_path_status_unsatisfied
) ||
1266 (NULL
== endpoint
) || (nw_endpoint_get_type(endpoint
) != nw_endpoint_type_address
)) {
1270 sa
= nw_endpoint_get_address(endpoint
);
1272 if (sa
->sa_family
!= AF_INET
) {
1276 if (allow_resolution
) {
1277 uint32_t ifIndex
= 0;
1278 int32_t numPrefixes
;
1279 nw_nat64_prefix_t
*prefixes
= NULL
;
1280 struct sockaddr_in sin
;
1282 memcpy(&sin
, sa
, MIN(sizeof(sin
), sa
->sa_len
));
1283 if (NULL
!= parameters
) {
1284 ifIndex
= nw_parameters_get_required_interface_index(parameters
);
1286 numPrefixes
= nw_nat64_copy_prefixes(&ifIndex
, &prefixes
);
1287 if (numPrefixes
> 0) {
1288 struct sockaddr_in6 synthesizedAddress
= {
1289 .sin6_len
= sizeof(struct sockaddr_in6
),
1290 .sin6_family
= AF_INET6
,
1291 .sin6_port
= htons(nw_endpoint_get_port(endpoint
)),
1295 nw_endpoint_t synthesizedEndpoint
;
1296 nw_path_evaluator_t synthesizedEvaluator
;
1297 nw_path_t synthesizedPath
;
1299 nw_nat64_synthesize_v6(&prefixes
[0], &sin
.sin_addr
, &synthesizedAddress
.sin6_addr
);
1300 synthesizedEndpoint
= nw_endpoint_create_address((const struct sockaddr
*)&synthesizedAddress
);
1301 synthesizedEvaluator
= nw_path_create_evaluator_for_endpoint(synthesizedEndpoint
, parameters
);
1302 synthesizedPath
= nw_path_evaluator_copy_path(synthesizedEvaluator
);
1303 if (nw_path_get_status(synthesizedPath
) != nw_path_status_unsatisfied
) {
1304 retPath
= synthesizedPath
;
1305 SC_log(LOG_INFO
, "Using CrazyIvan46 synthesized reachability result");
1307 nw_release(synthesizedPath
);
1309 nw_release(synthesizedEvaluator
);
1310 nw_release(synthesizedEndpoint
);
1313 // Don't synthesize in non-scheduled mode to avoid generating DNS traffic
1315 nw_path_evaluator_t v6PathEvaluator
;
1316 nw_parameters_t v6Parameters
;
1318 if (NULL
!= parameters
) {
1319 v6Parameters
= nw_parameters_copy(parameters
);
1321 v6Parameters
= nw_parameters_create();
1323 nw_parameters_set_required_address_family(v6Parameters
, AF_INET6
);
1324 v6PathEvaluator
= nw_path_create_evaluator_for_endpoint(NULL
, v6Parameters
);
1325 v6Path
= nw_path_evaluator_copy_path(v6PathEvaluator
);
1326 if (nw_path_get_status(v6Path
) != nw_path_status_unsatisfied
) {
1328 SC_log(LOG_INFO
, "Using CrazyIvan46 simple reachability result");
1332 nw_release(v6PathEvaluator
);
1333 nw_release(v6Parameters
);
1339 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
1340 SCNetworkReachabilityFlags
*flags
)
1342 nw_path_t crazyIvanPath
;
1343 nw_endpoint_t endpoint
;
1346 nw_path_evaluator_t pathEvaluator
;
1347 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1349 if (!isA_SCNetworkReachability(target
)) {
1350 _SCErrorSet(kSCStatusInvalidArgument
);
1354 MUTEX_LOCK(&targetPrivate
->lock
);
1356 if (targetPrivate
->scheduled
) {
1357 // if being watched, return the last known (and what should be current) status
1358 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->log_prefix
,
1360 targetPrivate
->lastPath
,
1361 targetPrivate
->type
,
1362 targetPrivate
->lastResolverStatus
,
1363 targetPrivate
->lastResolvedEndpoints
,
1364 targetPrivate
->lastResolvedEndpointHasFlags
,
1365 targetPrivate
->lastResolvedEndpointFlags
);
1366 // because we have synchronously captured the current status, we no longer
1367 // need our by-name required callback
1368 targetPrivate
->sentFirstUpdate
= TRUE
;
1372 // Not being watched, so run a one-shot path evaluator
1373 // We don't care about DNS resolution in this case, since we only need to have the
1374 // DNS resolution to support clients watching reachability to get updates
1375 endpoint
= __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
);
1376 pathEvaluator
= nw_path_create_evaluator_for_endpoint(endpoint
, targetPrivate
->parameters
);
1377 path
= nw_path_evaluator_copy_path(pathEvaluator
);
1379 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1380 crazyIvanPath
= __SCNetworkReachabilityCreateCrazyIvan46Path(path
, endpoint
,
1381 targetPrivate
->parameters
, FALSE
);
1382 if (NULL
!= crazyIvanPath
) {
1384 path
= crazyIvanPath
;
1388 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->log_prefix
,
1392 nw_resolver_status_invalid
,
1397 nw_release(pathEvaluator
);
1401 MUTEX_UNLOCK(&targetPrivate
->lock
);
1407 #pragma mark Notifications
1410 reachPerformAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1413 void (*context_release
)(const void *);
1414 SCNetworkReachabilityCallBack rlsFunction
;
1415 SCNetworkReachabilityFlags flags
= 0;
1417 if (!targetPrivate
->scheduled
) {
1418 // if no longer scheduled
1419 SC_log(LOG_INFO
, "%sskipping SCNetworkReachability callback, no longer scheduled",
1420 targetPrivate
->log_prefix
);
1421 MUTEX_UNLOCK(&targetPrivate
->lock
);
1426 rlsFunction
= targetPrivate
->rlsFunction
;
1427 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
1428 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
1429 context_release
= targetPrivate
->rlsContext
.release
;
1431 context_info
= targetPrivate
->rlsContext
.info
;
1432 context_release
= NULL
;
1435 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->log_prefix
,
1437 targetPrivate
->lastPath
,
1438 targetPrivate
->type
,
1439 targetPrivate
->lastResolverStatus
,
1440 targetPrivate
->lastResolvedEndpoints
,
1441 targetPrivate
->lastResolvedEndpointHasFlags
,
1442 targetPrivate
->lastResolvedEndpointFlags
);
1444 MUTEX_UNLOCK(&targetPrivate
->lock
);
1446 if (rlsFunction
!= NULL
) {
1447 SC_log(LOG_DEBUG
, "%sexec SCNetworkReachability callout w/flags = 0x%08x",
1448 targetPrivate
->log_prefix
,
1450 (*rlsFunction
)((SCNetworkReachabilityRef
)targetPrivate
,
1455 if (context_release
!= NULL
) {
1456 (*context_release
)(context_info
);
1463 reachPerform(void *info
)
1465 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)info
;
1467 MUTEX_LOCK(&targetPrivate
->lock
);
1468 reachPerformAndUnlock(targetPrivate
);
1473 reachUpdateAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1475 targetPrivate
->sentFirstUpdate
= TRUE
;
1476 if (targetPrivate
->rls
!= NULL
) {
1477 if (targetPrivate
->rlList
!= NULL
) {
1478 CFRunLoopSourceSignal(targetPrivate
->rls
);
1479 _SC_signalRunLoop(targetPrivate
, targetPrivate
->rls
, targetPrivate
->rlList
);
1481 MUTEX_UNLOCK(&targetPrivate
->lock
);
1483 reachPerformAndUnlock(targetPrivate
);
1488 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
1489 SCNetworkReachabilityCallBack callout
,
1490 SCNetworkReachabilityContext
*context
)
1492 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1494 MUTEX_LOCK(&targetPrivate
->lock
);
1496 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1497 /* let go of the current context */
1498 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1501 targetPrivate
->rlsFunction
= callout
;
1502 targetPrivate
->rlsContext
.info
= NULL
;
1503 targetPrivate
->rlsContext
.retain
= NULL
;
1504 targetPrivate
->rlsContext
.release
= NULL
;
1505 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1507 memcpy(&targetPrivate
->rlsContext
, context
, sizeof(SCNetworkReachabilityContext
));
1508 if (context
->retain
!= NULL
) {
1509 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
1513 MUTEX_UNLOCK(&targetPrivate
->lock
);
1520 reachRLSCopyDescription(const void *info
)
1522 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1524 return CFStringCreateWithFormat(NULL
,
1526 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
1531 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
1532 CFRunLoopRef runLoop
,
1533 CFStringRef runLoopMode
)
1535 Boolean success
= FALSE
;
1536 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1537 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1538 _SCErrorSet(kSCStatusInvalidArgument
);
1542 MUTEX_LOCK(&targetPrivate
->lock
);
1544 if (targetPrivate
->scheduled
) {
1545 if (targetPrivate
->rls
!= NULL
&& targetPrivate
->rlList
!= NULL
) {
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 MUTEX_UNLOCK(&targetPrivate
->lock
);
1559 MUTEX_UNLOCK(&targetPrivate
->lock
);
1560 _SCErrorSet(kSCStatusInvalidArgument
);
1565 CFRunLoopSourceContext context
= {
1567 , (void *)target
// info
1568 , CFRetain
// retain
1569 , CFRelease
// release
1570 , reachRLSCopyDescription
// copyDescription
1575 , reachPerform
// perform
1578 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
1579 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1581 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1583 * if we do not already have host notifications scheduled with
1584 * this runLoop / runLoopMode
1586 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1589 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
1591 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, _callback_queue());
1593 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1594 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1595 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1596 // if target is no longer scheduled for this runLoop / runLoopMode
1597 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1599 // if *all* notifications have been unscheduled
1600 CFRelease(targetPrivate
->rlList
);
1601 targetPrivate
->rlList
= NULL
;
1602 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1603 CFRelease(targetPrivate
->rls
);
1604 targetPrivate
->rls
= NULL
;
1610 MUTEX_UNLOCK(&targetPrivate
->lock
);
1615 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
1616 CFRunLoopRef runLoop
,
1617 CFStringRef runLoopMode
)
1619 Boolean success
= FALSE
;
1620 Boolean unscheduleDispatchQueue
= FALSE
;
1621 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1623 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1624 _SCErrorSet(kSCStatusInvalidArgument
);
1628 MUTEX_LOCK(&targetPrivate
->lock
);
1630 if (targetPrivate
->rlList
== NULL
|| targetPrivate
->rls
== NULL
|| !targetPrivate
->scheduled
) {
1631 MUTEX_UNLOCK(&targetPrivate
->lock
);
1632 _SCErrorSet(kSCStatusInvalidArgument
);
1636 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1637 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1638 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1639 // if target is no longer scheduled for this runLoop / runLoopMode
1640 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1642 // if *all* notifications have been unscheduled
1643 unscheduleDispatchQueue
= TRUE
;
1644 CFRelease(targetPrivate
->rlList
);
1645 targetPrivate
->rlList
= NULL
;
1646 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1647 CFRelease(targetPrivate
->rls
);
1648 targetPrivate
->rls
= NULL
;
1653 if (unscheduleDispatchQueue
) {
1654 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, NULL
);
1658 MUTEX_UNLOCK(&targetPrivate
->lock
);
1662 static __inline__
void
1663 __SCNetworkReachabilityCopyPathStatus(SCNetworkReachabilityPrivateRef targetPrivate
,
1665 SCNetworkReachabilityFlags
*flags
,
1667 size_t *endpointCount
)
1670 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->log_prefix
,
1672 targetPrivate
->lastPath
,
1673 targetPrivate
->type
,
1674 targetPrivate
->lastResolverStatus
,
1675 targetPrivate
->lastResolvedEndpoints
,
1676 targetPrivate
->lastResolvedEndpointHasFlags
,
1677 targetPrivate
->lastResolvedEndpointFlags
);
1680 *ifIndex
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1682 if (endpointCount
) {
1683 *endpointCount
= nw_array_get_count(targetPrivate
->lastResolvedEndpoints
);
1688 static __inline__ Boolean
1689 __SCNetworkReachabilityShouldUpdateClient(SCNetworkReachabilityPrivateRef targetPrivate
, SCNetworkReachabilityFlags oldFlags
, uint oldIFIndex
, size_t oldEndpointCount
)
1691 SCNetworkReachabilityFlags newFlags
= 0;
1692 uint newIFIndex
= 0;
1693 size_t newEndpointCount
= 0;
1695 __SCNetworkReachabilityCopyPathStatus(targetPrivate
,
1696 "ShouldUpdateClient",
1700 return (!targetPrivate
->sentFirstUpdate
||
1701 oldFlags
!= newFlags
||
1702 oldIFIndex
!= newIFIndex
||
1703 oldEndpointCount
!= newEndpointCount
);
1707 __SCNetworkReachabilityRestartResolver(SCNetworkReachabilityPrivateRef targetPrivate
, const char *caller
)
1709 if (targetPrivate
&&
1710 !targetPrivate
->resolverBypass
&&
1711 isReachabilityTypeName(targetPrivate
->type
)) {
1712 nw_resolver_t resolver
;
1713 CFRetain(targetPrivate
);
1714 if (NULL
!= targetPrivate
->resolver
) {
1715 nw_resolver_cancel(targetPrivate
->resolver
);
1717 if (targetPrivate
->lastPath
!= NULL
) {
1718 resolver
= nw_resolver_create_with_path(targetPrivate
->lastPath
);
1720 resolver
= nw_resolver_create_with_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
), targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
: targetPrivate
->parameters
);
1722 if (resolver
== NULL
) {
1723 SC_log(LOG_ERR
, "%sfailed to create a nw_resolver", targetPrivate
->log_prefix
);
1724 targetPrivate
->resolver
= NULL
;
1725 CFRelease(targetPrivate
);
1728 targetPrivate
->resolver
= resolver
;
1729 nw_resolver_set_cancel_handler(resolver
, ^(void) {
1730 MUTEX_LOCK(&targetPrivate
->lock
);
1731 if (resolver
== targetPrivate
->resolver
) {
1732 targetPrivate
->resolver
= NULL
;
1734 nw_release(resolver
);
1735 MUTEX_UNLOCK(&targetPrivate
->lock
);
1736 CFRelease(targetPrivate
);
1738 if (!nw_resolver_set_update_handler(resolver
, targetPrivate
->dispatchQueue
, ^(nw_resolver_status_t status
, nw_array_t resolved_endpoints
) {
1739 MUTEX_LOCK(&targetPrivate
->lock
);
1740 if (targetPrivate
->scheduled
) {
1741 SCNetworkReachabilityFlags oldFlags
= 0;
1742 uint oldIFIndex
= 0;
1743 size_t oldEndpointCount
= 0;
1745 __SCNetworkReachabilityCopyPathStatus(targetPrivate
,
1751 targetPrivate
->lastResolverStatus
= status
;
1752 nw_release(targetPrivate
->lastResolvedEndpoints
);
1753 targetPrivate
->lastResolvedEndpoints
= nw_retain(resolved_endpoints
);
1755 // Run path evaluation on the resolved endpoints
1756 __block Boolean hasFlags
= FALSE
;
1757 targetPrivate
->lastResolvedEndpointHasFlags
= FALSE
;
1758 targetPrivate
->lastResolvedEndpointFlags
= 0;
1759 targetPrivate
->lastResolvedEndpointInterfaceIndex
= 0;
1760 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
1761 SCNetworkReachabilityFlags flags
= 0;
1762 unsigned int interfaceIndex
= 0;
1763 ReachabilityRankType rank
;
1764 nw_endpoint_t resolvedEndpoint
= (nw_endpoint_t
)object
;
1766 nw_path_evaluator_t pathEvaluator
;
1768 pathEvaluator
= nw_path_create_evaluator_for_endpoint(resolvedEndpoint
,
1769 targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
1770 : targetPrivate
->parameters
);
1771 path
= nw_path_evaluator_copy_path(pathEvaluator
);
1773 char *e_caller
= NULL
;
1774 nw_endpoint_type_t e_type
;
1777 // Update the log prefix to note both the "target" [address] and the "endpoint"
1779 e_type
= nw_endpoint_get_type(resolvedEndpoint
);
1781 case nw_endpoint_type_host
:
1782 case nw_endpoint_type_url
: {
1783 const char *hostname
;
1785 hostname
= nw_endpoint_get_hostname(resolvedEndpoint
);
1786 if (hostname
!= NULL
) {
1787 ret
= asprintf(&e_caller
, "endpoint %zd, %s",
1793 case nw_endpoint_type_address
: {
1794 const struct sockaddr
*addr
;
1796 addr
= nw_endpoint_get_address(resolvedEndpoint
);
1800 _SC_sockaddr_to_string(addr
, buf
, sizeof(buf
));
1801 ret
= asprintf(&e_caller
, "endpoint %zd, %s",
1808 ret
= asprintf(&e_caller
, "endpoint %zd, ?",
1813 if ((ret
< 0) && (e_caller
!= NULL
)) {
1818 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->log_prefix
,
1819 e_caller
!= NULL
? e_caller
: "",
1822 nw_resolver_status_invalid
,
1828 if (e_caller
!= NULL
) {
1832 interfaceIndex
= nw_path_get_interface_index(path
);
1834 nw_release(pathEvaluator
);
1836 rank
= __SCNetworkReachabilityRank(flags
);
1837 if (rank
> __SCNetworkReachabilityRank(targetPrivate
->lastResolvedEndpointFlags
)) {
1838 // Return the best case result
1839 targetPrivate
->lastResolvedEndpointFlags
= flags
;
1840 targetPrivate
->lastResolvedEndpointInterfaceIndex
= interfaceIndex
;
1841 if (rank
== ReachabilityRankReachable
) {
1842 // Can't get any better than REACHABLE
1848 targetPrivate
->lastResolvedEndpointHasFlags
= hasFlags
;
1850 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1851 reachUpdateAndUnlock(targetPrivate
);
1853 MUTEX_UNLOCK(&targetPrivate
->lock
);
1856 MUTEX_UNLOCK(&targetPrivate
->lock
);
1859 nw_release(resolver
);
1860 targetPrivate
->resolver
= NULL
;
1861 CFRelease(targetPrivate
);
1867 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
1868 dispatch_queue_t queue
)
1872 if (queue
!= NULL
) {
1873 nw_path_t crazyIvanPath
;
1874 nw_endpoint_t endpoint
;
1875 nw_path_evaluator_t pathEvaluator
;
1877 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
1878 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
1879 _SCErrorSet(kSCStatusInvalidArgument
);
1883 SC_log(LOG_DEBUG
, "%sscheduled", targetPrivate
->log_prefix
);
1885 // retain dispatch queue
1886 dispatch_retain(queue
);
1887 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1888 endpoint
= __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
);
1889 pathEvaluator
= nw_path_create_evaluator_for_endpoint(endpoint
, targetPrivate
->parameters
);
1890 targetPrivate
->pathEvaluator
= pathEvaluator
;
1891 targetPrivate
->dispatchQueue
= queue
;
1892 targetPrivate
->scheduled
= TRUE
;
1893 if (isReachabilityTypeName(targetPrivate
->type
)) {
1894 // we must have at least one callback for by-name queries
1895 targetPrivate
->sentFirstUpdate
= FALSE
;
1897 targetPrivate
->sentFirstUpdate
= TRUE
;
1900 nw_release(targetPrivate
->lastPath
);
1901 targetPrivate
->lastPath
= nw_path_evaluator_copy_path(pathEvaluator
);
1903 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1904 crazyIvanPath
= __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate
->lastPath
, endpoint
,
1905 targetPrivate
->parameters
, FALSE
);
1906 if (NULL
!= crazyIvanPath
) {
1907 nw_release(targetPrivate
->lastPath
);
1908 targetPrivate
->lastPath
= crazyIvanPath
;
1912 nw_release(targetPrivate
->lastPathParameters
);
1913 targetPrivate
->lastPathParameters
= nw_path_copy_derived_parameters(targetPrivate
->lastPath
);
1915 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1916 nw_release(targetPrivate
->lastResolvedEndpoints
);
1917 targetPrivate
->lastResolvedEndpoints
= NULL
;
1918 __SCNetworkReachabilityRestartResolver(targetPrivate
, "Scheduled, start DNS");
1920 CFRetain(targetPrivate
);
1921 nw_path_evaluator_set_cancel_handler(pathEvaluator
, ^(void) {
1922 MUTEX_LOCK(&targetPrivate
->lock
);
1923 if (pathEvaluator
== targetPrivate
->pathEvaluator
) {
1924 targetPrivate
->pathEvaluator
= NULL
;
1926 nw_release(pathEvaluator
);
1927 MUTEX_UNLOCK(&targetPrivate
->lock
);
1928 CFRelease(targetPrivate
);
1931 if (!nw_path_evaluator_set_update_handler(pathEvaluator
, targetPrivate
->dispatchQueue
, ^(nw_path_t path
) {
1932 MUTEX_LOCK(&targetPrivate
->lock
);
1933 if (targetPrivate
->scheduled
) {
1934 nw_path_t crazyIvanPath
;
1935 SCNetworkReachabilityFlags oldFlags
= 0;
1936 uint oldIFIndex
= 0;
1937 size_t oldEndpointCount
= 0;
1939 __SCNetworkReachabilityCopyPathStatus(targetPrivate
,
1945 nw_release(targetPrivate
->lastPath
);
1946 targetPrivate
->lastPath
= nw_retain(path
);
1948 if (isReachabilityTypeAddress(targetPrivate
->type
)) {
1950 __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate
->lastPath
,
1952 targetPrivate
->parameters
,
1954 if (NULL
!= crazyIvanPath
) {
1955 nw_release(targetPrivate
->lastPath
);
1956 targetPrivate
->lastPath
= crazyIvanPath
;
1960 if (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) {
1961 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1962 __SCNetworkReachabilityRestartResolver(targetPrivate
, "Path updated, restart DNS");
1965 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1966 reachUpdateAndUnlock(targetPrivate
);
1968 MUTEX_UNLOCK(&targetPrivate
->lock
);
1971 MUTEX_UNLOCK(&targetPrivate
->lock
);
1974 targetPrivate
->pathEvaluator
= NULL
;
1975 nw_release(pathEvaluator
);
1976 CFRelease(targetPrivate
);
1979 if (targetPrivate
->dispatchQueue
== NULL
) { // if we should be scheduled on a dispatch queue (but are not)
1980 _SCErrorSet(kSCStatusInvalidArgument
);
1984 if (!targetPrivate
->scheduled
) {
1985 // if not currently scheduled
1986 _SCErrorSet(kSCStatusInvalidArgument
);
1990 targetPrivate
->scheduled
= FALSE
;
1991 targetPrivate
->sentFirstUpdate
= FALSE
;
1992 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1993 targetPrivate
->pathEvaluator
= NULL
;
1994 nw_release(targetPrivate
->lastPath
);
1995 targetPrivate
->lastPath
= NULL
;
1996 nw_release(targetPrivate
->lastPathParameters
);
1997 targetPrivate
->lastPathParameters
= NULL
;
1998 nw_release(targetPrivate
->lastResolvedEndpoints
);
1999 targetPrivate
->lastResolvedEndpoints
= NULL
;
2000 if (NULL
!= targetPrivate
->resolver
) {
2001 nw_resolver_cancel(targetPrivate
->resolver
);
2002 targetPrivate
->resolver
= NULL
;
2004 if (targetPrivate
->dispatchQueue
!= NULL
) {
2005 dispatch_release(targetPrivate
->dispatchQueue
);
2006 targetPrivate
->dispatchQueue
= NULL
;
2009 SC_log(LOG_DEBUG
, "%sunscheduled", targetPrivate
->log_prefix
);
2017 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
2018 dispatch_queue_t queue
)
2020 if (!isA_SCNetworkReachability(target
)) {
2021 _SCErrorSet(kSCStatusInvalidArgument
);
2025 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2026 MUTEX_LOCK(&targetPrivate
->lock
);
2027 Boolean success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, queue
);
2028 MUTEX_UNLOCK(&targetPrivate
->lock
);
2033 * _SC_checkResolverReachabilityByAddress()
2035 * Given an IP address, determine whether a reverse DNS query can be issued
2036 * using the current network configuration.
2039 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
2040 SCNetworkReachabilityFlags
*flags
,
2042 struct sockaddr
*sa
)
2044 #pragma unused(storeP)
2046 nw_path_evaluator_t evaluator
= nw_path_create_default_evaluator();
2047 nw_path_t path
= nw_path_evaluator_copy_path(evaluator
);
2048 if (nw_path_get_status(path
) == nw_path_status_unsatisfied_network
) {
2057 *flags
= kSCNetworkReachabilityFlagsReachable
;
2063 nw_release(evaluator
);