2 * Copyright (c) 2003-2015 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>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include <CoreFoundation/CFRuntime.h>
44 #include <SystemConfiguration/SystemConfiguration.h>
45 #include <SystemConfiguration/SCValidation.h>
46 #include <SystemConfiguration/SCPrivate.h>
48 #include <libkern/OSAtomic.h>
52 #include <netinet/in.h>
53 #include <arpa/inet.h>
57 #include <sys/ioctl.h>
58 #include <sys/socket.h>
60 #include <net/if_dl.h>
61 #include <net/if_types.h>
62 #include <net/network_agent.h>
64 #include "SCNetworkReachabilityInternal.h"
66 #include <network_information.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
);
125 static Boolean
__SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
126 dispatch_queue_t queue
);
128 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
131 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
133 "SCNetworkReachability", // className
136 __SCNetworkReachabilityDeallocate
, // dealloc
139 NULL
, // copyFormattingDesc
140 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
144 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
146 static dispatch_queue_t
149 static dispatch_once_t once
;
150 static dispatch_queue_t q
;
152 dispatch_once(&once
, ^{
153 q
= dispatch_queue_create("SCNetworkReachability.callback", NULL
);
160 #pragma mark SCNetworkReachability APIs
163 static __inline__ CFTypeRef
164 isA_SCNetworkReachability(CFTypeRef obj
)
166 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
170 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target
)
172 CFAllocatorRef allocator
= CFGetAllocator(target
);
173 CFMutableStringRef str
;
174 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
176 str
= CFStringCreateMutable(allocator
, 0);
177 switch (targetPrivate
->type
) {
178 case reachabilityTypeAddress
:
179 case reachabilityTypeAddressPair
: {
181 if (targetPrivate
->localAddressEndpoint
!= NULL
) {
182 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->localAddressEndpoint
), buf
, sizeof(buf
));
183 CFStringAppendFormat(str
, NULL
, CFSTR("local address = %s"),
186 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
187 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
188 CFStringAppendFormat(str
, NULL
, CFSTR("%s%saddress = %s"),
189 targetPrivate
->localAddressEndpoint
? ", " : "",
190 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
193 CFStringAppendFormat(str
, NULL
, CFSTR("default path"));
197 case reachabilityTypeName
: {
198 CFStringAppendFormat(str
, NULL
, CFSTR("name = %s"), nw_endpoint_get_hostname(targetPrivate
->hostnameEndpoint
));
201 case reachabilityTypePTR
: {
203 if (targetPrivate
->remoteAddressEndpoint
!= NULL
) {
204 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate
->remoteAddressEndpoint
), buf
, sizeof(buf
));
205 CFStringAppendFormat(str
, NULL
, CFSTR("ptr = %s"),
217 _SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target
)
219 CFAllocatorRef allocator
= CFGetAllocator(target
);
221 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
223 str
= CFStringCreateWithFormat(allocator
,
225 CFSTR("flags = 0x%08x, if_index = %u"),
226 __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
, targetPrivate
->type
, targetPrivate
->lastResolverStatus
, targetPrivate
->lastResolvedEndpoints
, targetPrivate
->lastResolvedEndpointHasFlags
, targetPrivate
->lastResolvedEndpointFlags
),
227 targetPrivate
->lastResolvedEndpointHasFlags
? targetPrivate
->lastResolvedEndpointInterfaceIndex
: nw_path_get_interface_index(targetPrivate
->lastPath
));
233 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
235 CFAllocatorRef allocator
= CFGetAllocator(cf
);
236 CFMutableStringRef result
;
238 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
239 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
241 MUTEX_LOCK(&targetPrivate
->lock
);
243 result
= CFStringCreateMutable(allocator
, 0);
244 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
246 // add target description
247 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
248 CFStringAppend(result
, str
);
251 // add additional "name" info
252 if (isReachabilityTypeName(targetPrivate
->type
)) {
253 if (targetPrivate
->resolver
&& targetPrivate
->lastResolverStatus
== nw_resolver_status_invalid
) {
254 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
255 } else if (targetPrivate
->lastResolverStatus
!= nw_resolver_status_invalid
) {
256 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s"), (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) ? "complete" : "in progress");
257 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
258 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
259 nw_endpoint_t endpoint
= (nw_endpoint_t
)object
;
260 if (nw_endpoint_get_type(endpoint
) == nw_endpoint_type_address
) {
262 const struct sockaddr
*sa
= nw_endpoint_get_address(endpoint
);
263 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
264 CFStringAppendFormat(result
, NULL
, CFSTR(", %s"), buf
);
265 } else if (nw_endpoint_get_type(endpoint
) == nw_endpoint_type_host
) {
266 CFStringAppendFormat(result
, NULL
, CFSTR(", %s"), nw_endpoint_get_hostname(endpoint
));
271 CFStringAppendFormat(result
, NULL
, CFSTR(", no addresses"));
273 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
277 if (targetPrivate
->resolverBypass
) {
278 CFStringAppendFormat(result
, NULL
, CFSTR(", !resolve"));
282 if (targetPrivate
->scheduled
) {
283 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
284 CFStringAppendFormat(result
, NULL
, CFSTR(", %@"), str
);
288 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
290 MUTEX_UNLOCK(&targetPrivate
->lock
);
297 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
299 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
300 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
302 if (_sc_debug
&& (_sc_log
> 0)) {
303 SC_log(LOG_INFO
, "%srelease", targetPrivate
->log_prefix
);
306 /* release resources */
307 MUTEX_LOCK(&targetPrivate
->lock
);
308 targetPrivate
->scheduled
= FALSE
;
310 if (targetPrivate
->hostnameEndpoint
) {
311 network_release(targetPrivate
->hostnameEndpoint
);
312 targetPrivate
->hostnameEndpoint
= NULL
;
314 if (targetPrivate
->localAddressEndpoint
) {
315 network_release(targetPrivate
->localAddressEndpoint
);
316 targetPrivate
->localAddressEndpoint
= NULL
;
318 if (targetPrivate
->remoteAddressEndpoint
) {
319 network_release(targetPrivate
->remoteAddressEndpoint
);
320 targetPrivate
->remoteAddressEndpoint
= NULL
;
322 if (targetPrivate
->parameters
) {
323 network_release(targetPrivate
->parameters
);
324 targetPrivate
->parameters
= NULL
;
326 if (targetPrivate
->lastPath
) {
327 network_release(targetPrivate
->lastPath
);
328 targetPrivate
->lastPath
= NULL
;
330 if (targetPrivate
->lastPathParameters
) {
331 network_release(targetPrivate
->lastPathParameters
);
332 targetPrivate
->lastPathParameters
= NULL
;
334 if (targetPrivate
->lastResolvedEndpoints
) {
335 network_release(targetPrivate
->lastResolvedEndpoints
);
336 targetPrivate
->lastResolvedEndpoints
= NULL
;
339 if (targetPrivate
->rlsContext
.release
!= NULL
) {
340 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
343 MUTEX_UNLOCK(&targetPrivate
->lock
);
345 pthread_mutex_destroy(&targetPrivate
->lock
);
352 __SCNetworkReachabilityInitialize(void)
354 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
356 // provide a way to enable SCNetworkReachability logging without
357 // having to set _sc_debug=1.
358 if ((getenv("REACH_LOGGING") != NULL
) ||
359 (CFPreferencesGetAppBooleanValue(CFSTR("com.apple.SCNetworkReachability.debug"),
360 kCFPreferencesCurrentApplication
,
365 pthread_mutexattr_init(&lock_attr
);
366 pthread_mutexattr_settype(&lock_attr
, PTHREAD_MUTEX_ERRORCHECK
);
371 static SCNetworkReachabilityPrivateRef
372 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
374 SCNetworkReachabilityPrivateRef targetPrivate
;
377 /* initialize runtime */
378 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
380 /* allocate target */
381 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
382 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
383 __kSCNetworkReachabilityTypeID
,
386 if (targetPrivate
== NULL
) {
390 bzero((void *)targetPrivate
+ sizeof(CFRuntimeBase
), size
);
392 MUTEX_INIT(&targetPrivate
->lock
);
394 targetPrivate
->pathEvaluator
= NULL
;
395 targetPrivate
->resolver
= NULL
;
397 targetPrivate
->log_prefix
[0] = '\0';
399 snprintf(targetPrivate
->log_prefix
,
400 sizeof(targetPrivate
->log_prefix
),
405 return targetPrivate
;
409 static const struct sockaddr
*
410 is_valid_address(const struct sockaddr
*address
)
412 const struct sockaddr
*valid
= NULL
;
413 static Boolean warned
= FALSE
;
415 if ((address
!= NULL
) &&
416 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
417 switch (address
->sa_family
) {
419 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
423 SC_log(LOG_NOTICE
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
425 sizeof(struct sockaddr_in
));
431 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
433 } else if (!warned
) {
434 SC_log(LOG_NOTICE
, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu",
436 sizeof(struct sockaddr_in6
));
442 SC_log(LOG_NOTICE
, "SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d",
453 __SCNetworkReachabilityAddressIsEmpty(const struct sockaddr
*address
)
455 if (address
== NULL
) {
459 if (address
->sa_family
== AF_INET
) {
460 return (((struct sockaddr_in
*)(void *)address
)->sin_addr
.s_addr
== 0);
461 } else if (address
->sa_family
== AF_INET6
) {
462 return IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6
*)(void *)address
)->sin6_addr
);
468 SCNetworkReachabilityRef
469 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
470 const struct sockaddr
*address
)
472 SCNetworkReachabilityPrivateRef targetPrivate
;
474 address
= is_valid_address(address
);
475 if (address
== NULL
) {
476 _SCErrorSet(kSCStatusInvalidArgument
);
480 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
481 if (targetPrivate
== NULL
) {
485 targetPrivate
->type
= reachabilityTypeAddress
;
487 if (!__SCNetworkReachabilityAddressIsEmpty(address
)) {
488 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(address
);
491 if (_sc_debug
&& (_sc_log
> 0)) {
492 SC_log(LOG_INFO
, "%s%s %@",
493 targetPrivate
->log_prefix
,
494 DEBUG_REACHABILITY_TYPE_ADDRESS
,
498 return (SCNetworkReachabilityRef
)targetPrivate
;
503 is_same_address(const struct sockaddr
*a
, const struct sockaddr
*b
)
511 (a
->sa_family
!= b
->sa_family
) ||
512 (a
->sa_len
!= b
->sa_len
)) {
516 switch (a
->sa_family
) {
518 struct sockaddr_in
*a_sin
= (struct sockaddr_in
*)(void *)a
;
519 struct sockaddr_in
*b_sin
= (struct sockaddr_in
*)(void *)b
;
521 /* ALIGN: assuming a (and b) are aligned, then cast ok. */
522 a_addr
= &a_sin
->sin_addr
;
523 b_addr
= &b_sin
->sin_addr
;
524 len
= sizeof(struct in_addr
);
529 struct sockaddr_in6
*a_sin6
= (struct sockaddr_in6
*)(void *)a
;
530 struct sockaddr_in6
*b_sin6
= (struct sockaddr_in6
*)(void *)b
;
532 if (a_sin6
->sin6_scope_id
!= b_sin6
->sin6_scope_id
) {
536 a_addr
= &a_sin6
->sin6_addr
;
537 b_addr
= &b_sin6
->sin6_addr
;
538 len
= sizeof(struct in6_addr
);
549 return (bcmp(a_addr
, b_addr
, len
) == 0);
553 SCNetworkReachabilityRef
554 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
555 const struct sockaddr
*localAddress
,
556 const struct sockaddr
*remoteAddress
)
558 SCNetworkReachabilityPrivateRef targetPrivate
;
560 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
561 _SCErrorSet(kSCStatusInvalidArgument
);
565 if (localAddress
!= NULL
) {
566 localAddress
= is_valid_address(localAddress
);
567 if (localAddress
== NULL
) {
568 _SCErrorSet(kSCStatusInvalidArgument
);
573 if (remoteAddress
!= NULL
) {
574 remoteAddress
= is_valid_address(remoteAddress
);
575 if (remoteAddress
== NULL
) {
576 _SCErrorSet(kSCStatusInvalidArgument
);
581 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
582 if (targetPrivate
== NULL
) {
586 targetPrivate
->type
= reachabilityTypeAddressPair
;
588 if (localAddress
!= NULL
) {
589 targetPrivate
->localAddressEndpoint
= nw_endpoint_create_address(localAddress
);
592 if (remoteAddress
!= NULL
) {
593 if (is_same_address(localAddress
, remoteAddress
)) {
594 targetPrivate
->remoteAddressEndpoint
= network_retain(targetPrivate
->localAddressEndpoint
);
596 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(remoteAddress
);
600 targetPrivate
->parameters
= nw_parameters_create();
601 nw_parameters_set_local_address(targetPrivate
->parameters
, targetPrivate
->localAddressEndpoint
);
603 if (_sc_debug
&& (_sc_log
> 0)) {
604 SC_log(LOG_INFO
, "%s%s %@",
605 targetPrivate
->log_prefix
,
606 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR
,
610 return (SCNetworkReachabilityRef
)targetPrivate
;
614 SCNetworkReachabilityRef
615 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
616 const char *nodename
)
620 struct sockaddr_in sin
;
621 struct sockaddr_in6 sin6
;
624 SCNetworkReachabilityPrivateRef targetPrivate
;
626 if (nodename
== NULL
) {
627 _SCErrorSet(kSCStatusInvalidArgument
);
631 nodenameLen
= strlen(nodename
);
632 if (nodenameLen
== 0) {
633 _SCErrorSet(kSCStatusInvalidArgument
);
637 if (_SC_string_to_sockaddr(nodename
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) != NULL
) {
638 /* if this "nodename" is really an IP[v6] address in disguise */
639 return SCNetworkReachabilityCreateWithAddress(allocator
, &addr
.sa
);
642 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
643 if (targetPrivate
== NULL
) {
647 targetPrivate
->type
= reachabilityTypeName
;
649 targetPrivate
->hostnameEndpoint
= nw_endpoint_create_host(nodename
, "0");
651 if (_sc_debug
&& (_sc_log
> 0)) {
652 SC_log(LOG_INFO
, "%s%s %@",
653 targetPrivate
->log_prefix
,
654 DEBUG_REACHABILITY_TYPE_NAME
,
658 return (SCNetworkReachabilityRef
)targetPrivate
;
662 static SCNetworkReachabilityRef
663 __SCNetworkReachabilityCreateWithPTR(CFAllocatorRef allocator
,
664 const struct sockaddr
*ptrAddress
)
666 SCNetworkReachabilityPrivateRef targetPrivate
;
668 ptrAddress
= is_valid_address(ptrAddress
);
669 if (ptrAddress
== NULL
) {
670 _SCErrorSet(kSCStatusInvalidArgument
);
674 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
675 if (targetPrivate
== NULL
) {
679 targetPrivate
->type
= reachabilityTypePTR
;
681 targetPrivate
->remoteAddressEndpoint
= nw_endpoint_create_address(ptrAddress
);
683 targetPrivate
->parameters
= nw_parameters_create();
684 nw_parameters_set_resolve_ptr(targetPrivate
->parameters
, TRUE
);
686 if (_sc_debug
&& (_sc_log
> 0)) {
687 SC_log(LOG_INFO
, "%s%s %@",
688 targetPrivate
->log_prefix
,
689 DEBUG_REACHABILITY_TYPE_PTR
,
693 return (SCNetworkReachabilityRef
)targetPrivate
;
696 SCNetworkReachabilityRef
697 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
698 CFDictionaryRef options
)
700 const struct sockaddr
*addr_l
= NULL
;
701 const struct sockaddr
*addr_p
= NULL
;
702 const struct sockaddr
*addr_r
= NULL
;
704 CFStringRef interface
= NULL
;
705 CFStringRef nodename
;
706 CFBooleanRef resolverBypass
;
707 SCNetworkReachabilityRef target
;
708 SCNetworkReachabilityPrivateRef targetPrivate
;
709 unsigned int if_index
= 0;
710 char if_name
[IFNAMSIZ
];
712 if (!isA_CFDictionary(options
)) {
713 _SCErrorSet(kSCStatusInvalidArgument
);
717 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
718 if ((nodename
!= NULL
) &&
719 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
720 _SCErrorSet(kSCStatusInvalidArgument
);
723 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
725 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
726 _SCErrorSet(kSCStatusInvalidArgument
);
729 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
731 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionPTRAddress
);
733 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
734 _SCErrorSet(kSCStatusInvalidArgument
);
737 addr_p
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
739 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
741 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
742 _SCErrorSet(kSCStatusInvalidArgument
);
745 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
747 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
748 if ((interface
!= NULL
) &&
749 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
750 _SCErrorSet(kSCStatusInvalidArgument
);
753 resolverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionResolverBypass
);
754 if ((resolverBypass
!= NULL
) && !isA_CFBoolean(resolverBypass
)) {
755 _SCErrorSet(kSCStatusInvalidArgument
);
759 if (nodename
!= NULL
) {
762 if ((addr_l
!= NULL
) || (addr_r
!= NULL
) || (addr_p
!= NULL
)) {
763 // can't have both a nodename and an address
764 _SCErrorSet(kSCStatusInvalidArgument
);
768 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
769 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
770 CFAllocatorDeallocate(NULL
, (void *)name
);
771 } else if (addr_p
!= NULL
) {
772 if ((addr_l
!= NULL
) || // can't have PTR and target address
774 _SCErrorSet(kSCStatusInvalidArgument
);
778 target
= __SCNetworkReachabilityCreateWithPTR(NULL
, addr_p
);
780 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
781 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
782 } else if (addr_r
!= NULL
) {
783 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
784 } else if (addr_l
!= NULL
) {
785 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, NULL
);
787 _SCErrorSet(kSCStatusInvalidArgument
);
791 if (target
== NULL
) {
795 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
797 if (interface
!= NULL
) {
798 if ((_SC_cfstring_to_cstring(interface
,
801 kCFStringEncodingASCII
) == NULL
) ||
802 ((if_index
= if_nametoindex(if_name
)) == 0)) {
803 CFRelease(targetPrivate
);
804 _SCErrorSet(kSCStatusInvalidArgument
);
809 if (targetPrivate
->parameters
== NULL
) {
810 targetPrivate
->parameters
= nw_parameters_create();
814 nw_interface_t interfaceObject
= nw_interface_create_with_index(if_index
);
815 nw_parameters_require_interface(targetPrivate
->parameters
, interfaceObject
);
816 network_release(interfaceObject
);
819 if (resolverBypass
!= NULL
) {
820 targetPrivate
->resolverBypass
= CFBooleanGetValue(resolverBypass
);
823 if (_sc_debug
&& (_sc_log
> 0)) {
824 const char *opt
= "???";
826 switch (targetPrivate
->type
) {
827 case reachabilityTypeAddress
:
828 opt
= DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS
;
830 case reachabilityTypeAddressPair
:
831 opt
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS
;
833 case reachabilityTypeName
:
834 opt
= DEBUG_REACHABILITY_TYPE_NAME_OPTIONS
;
836 case reachabilityTypePTR
:
837 opt
= DEBUG_REACHABILITY_TYPE_PTR_OPTIONS
;
841 SC_log(LOG_INFO
, "%s%s %@",
842 targetPrivate
->log_prefix
,
847 return (SCNetworkReachabilityRef
)targetPrivate
;
852 SCNetworkReachabilityGetTypeID(void)
854 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
855 return __kSCNetworkReachabilityTypeID
;
859 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
860 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
863 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
865 if (!isA_SCNetworkReachability(target
)) {
866 _SCErrorSet(kSCStatusInvalidArgument
);
870 if (!isReachabilityTypeName(targetPrivate
->type
)) {
871 _SCErrorSet(kSCStatusInvalidArgument
);
879 MUTEX_LOCK(&targetPrivate
->lock
);
881 if (nw_array_get_count(targetPrivate
->lastResolvedEndpoints
) > 0) {
882 CFMutableArrayRef array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
884 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
885 if (nw_endpoint_get_type((nw_endpoint_t
)object
) == nw_endpoint_type_address
) {
886 const struct sockaddr
*address
= nw_endpoint_get_address((nw_endpoint_t
)object
);
887 if (address
!= NULL
) {
888 CFDataRef addressData
= CFDataCreate(kCFAllocatorDefault
, (const uint8_t *)address
, address
->sa_len
);
889 if (addressData
!= NULL
) {
890 CFArrayAppendValue(array
, addressData
);
891 CFRelease(addressData
);
894 } else if (nw_endpoint_get_type((nw_endpoint_t
)object
) == nw_endpoint_type_host
) {
895 CFStringRef string
= CFStringCreateWithCString(kCFAllocatorDefault
, nw_endpoint_get_hostname((nw_endpoint_t
)object
), kCFStringEncodingASCII
);
896 if (string
!= NULL
) {
897 if (CFStringHasPrefix(string
, CFSTR(".")) || CFStringHasSuffix(string
, CFSTR("."))) {
898 CFMutableStringRef mutableString
= CFStringCreateMutableCopy(kCFAllocatorDefault
, 0, string
);
899 if (mutableString
!= NULL
) {
901 string
= mutableString
;
902 CFStringTrim(mutableString
, CFSTR("."));
905 CFArrayAppendValue(array
, string
);
911 MUTEX_UNLOCK(&targetPrivate
->lock
);
916 MUTEX_UNLOCK(&targetPrivate
->lock
);
918 _SCErrorSet(kSCStatusOK
);
925 * Connection Required == 1
929 rankReachability(SCNetworkReachabilityFlags flags
)
933 if (flags
& kSCNetworkReachabilityFlagsReachable
) rank
= 2;
934 if (flags
& kSCNetworkReachabilityFlagsConnectionRequired
) rank
= 1;
939 #pragma mark Reachability Flags
942 __SCNetworkReachabilityGetAgentVPNFlags(xpc_object_t dictionary
, Boolean
*vpn
, Boolean
*onDemand
)
944 const struct netagent
*agent
= NULL
;
947 if (dictionary
== NULL
|| vpn
== NULL
|| onDemand
== NULL
) {
954 agent
= xpc_dictionary_get_data(dictionary
, REACHABILITY_AGENT_DATA_KEY
, &length
);
955 if (agent
== NULL
|| length
< sizeof(struct netagent
) || length
!= (sizeof(struct netagent
) + agent
->netagent_data_size
)) {
959 if (strncmp(REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN
, agent
->netagent_domain
, NETAGENT_DOMAINSIZE
) == 0) {
961 if ((agent
->netagent_flags
& NETAGENT_FLAG_VOLUNTARY
) &&
962 !(agent
->netagent_flags
& NETAGENT_FLAG_ACTIVE
)) {
968 static SCNetworkReachabilityFlags
969 __SCNetworkReachabilityGetFlagsFromPath(nw_path_t path
,
970 ReachabilityAddressType type
,
971 nw_resolver_status_t resolverStatus
,
972 nw_array_t resolvedEndpoints
,
973 Boolean resolvedEndpointUseFlags
,
974 SCNetworkReachabilityFlags resolvedEndpointFlags
)
976 __block SCNetworkReachabilityFlags flags
= kSCNetworkReachabilityFlagsReachable
;
978 nw_path_status_t status
= nw_path_get_status(path
);
979 if (status
== nw_path_status_satisfied
) {
980 __block
bool checkDNSFlags
= TRUE
;
981 flags
= kSCNetworkReachabilityFlagsReachable
;
982 #if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
983 if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
984 flags
|= (kSCNetworkReachabilityFlagsTransientConnection
| kSCNetworkReachabilityFlagsIsWWAN
);
987 xpc_object_t agent_dictionary
= nw_path_copy_netagent_dictionary(path
);
988 if (agent_dictionary
!= NULL
) {
989 if (xpc_dictionary_get_count(agent_dictionary
) > 0) {
990 xpc_dictionary_apply(agent_dictionary
, ^bool(__unused
const char *key
, xpc_object_t value
) {
992 Boolean onDemand
= FALSE
;
993 __SCNetworkReachabilityGetAgentVPNFlags(value
, &vpn
, &onDemand
);
995 // VPN flows are transient
996 flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
999 type
== reachabilityTypeName
&&
1000 resolverStatus
== nw_resolver_status_complete
&&
1001 nw_array_get_count(resolvedEndpoints
) == 0) {
1002 // On Demand by hostname, when no address has been resolved
1003 flags
|= (kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsConnectionOnDemand
);
1004 checkDNSFlags
= FALSE
;
1010 xpc_release(agent_dictionary
);
1012 if (isReachabilityTypeName(type
)) {
1013 if (checkDNSFlags
) {
1014 if (resolverStatus
== nw_resolver_status_complete
&&
1015 nw_array_get_count(resolvedEndpoints
) == 0) {
1016 // DNS didn't resolve, as a final answer for now. Not reachable!
1018 } else if (resolvedEndpointUseFlags
) {
1019 flags
= resolvedEndpointFlags
;
1023 if (nw_path_is_direct(path
)) {
1024 flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1026 if (nw_path_is_local(path
)) {
1027 flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1030 } else if (status
== nw_path_status_unsatisfied
) {
1032 } else if (status
== nw_path_status_satisfiable
) {
1033 flags
= (kSCNetworkReachabilityFlagsReachable
| kSCNetworkReachabilityFlagsConnectionRequired
| kSCNetworkReachabilityFlagsTransientConnection
);
1035 if (nw_path_get_vpn_config_id(path
, &vpn_uuid
)) {
1036 flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
1038 #if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
1039 else if (nw_path_uses_interface_type(path
, nw_interface_type_cellular
)) {
1040 flags
|= (kSCNetworkReachabilityFlagsIsWWAN
);
1045 return (flags
& kSCNetworkReachabilityFlagsMask
);
1048 static nw_endpoint_t
1049 __SCNetworkReachabilityGetPrimaryEndpoint(SCNetworkReachabilityPrivateRef targetPrivate
)
1051 if (targetPrivate
->type
== reachabilityTypeName
) {
1052 return targetPrivate
->hostnameEndpoint
;
1053 } else if (targetPrivate
->type
== reachabilityTypeAddress
||
1054 targetPrivate
->type
== reachabilityTypeAddressPair
||
1055 targetPrivate
->type
== reachabilityTypePTR
) {
1056 return targetPrivate
->remoteAddressEndpoint
;
1062 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target
)
1066 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1067 SCNetworkReachabilityFlags flags
= 0;
1069 if (!isA_SCNetworkReachability(target
)) {
1070 _SCErrorSet(kSCStatusInvalidArgument
);
1074 MUTEX_LOCK(&targetPrivate
->lock
);
1076 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
, targetPrivate
->type
, nw_resolver_status_invalid
, NULL
, targetPrivate
->lastResolvedEndpointHasFlags
, targetPrivate
->lastResolvedEndpointFlags
);
1078 /* Only return the if_index if the connection is reachable not for reachable connection
1079 * required etc ... */
1080 if (ok
&& rankReachability(flags
) == 2) {
1081 if (targetPrivate
->lastResolvedEndpointHasFlags
) {
1082 if_index
= targetPrivate
->lastResolvedEndpointInterfaceIndex
;
1084 if_index
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1088 MUTEX_UNLOCK(&targetPrivate
->lock
);
1093 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
1094 SCNetworkReachabilityFlags
*flags
)
1097 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1099 if (!isA_SCNetworkReachability(target
)) {
1100 _SCErrorSet(kSCStatusInvalidArgument
);
1104 MUTEX_LOCK(&targetPrivate
->lock
);
1106 if (targetPrivate
->scheduled
) {
1107 // if being watched, return the last known (and what should be current) status
1108 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
, targetPrivate
->type
, targetPrivate
->lastResolverStatus
, targetPrivate
->lastResolvedEndpoints
, targetPrivate
->lastResolvedEndpointHasFlags
, targetPrivate
->lastResolvedEndpointFlags
);
1109 // because we have synchronously captured the current status, we no longer
1110 // need our by-name required callback
1111 targetPrivate
->sentFirstUpdate
= TRUE
;
1116 // Not being watched, so run a one-shot path evaluator
1117 // We don't care about DNS resolution in this case, since we only need to have the DNS resolution to support clients watching reachability to get updates
1118 nw_path_evaluator_t pathEvaluator
= nw_path_create_evaluator_for_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
), targetPrivate
->parameters
);
1119 nw_path_t path
= nw_path_evaluator_copy_path(pathEvaluator
);
1120 *flags
= __SCNetworkReachabilityGetFlagsFromPath(path
, 0, nw_resolver_status_invalid
, NULL
, FALSE
, 0);
1121 network_release(path
);
1122 network_release(pathEvaluator
);
1126 MUTEX_UNLOCK(&targetPrivate
->lock
);
1132 #pragma mark Notifications
1135 reachPerformAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1137 os_activity_t activity_id
;
1139 void (*context_release
)(const void *);
1140 SCNetworkReachabilityCallBack rlsFunction
;
1141 SCNetworkReachabilityFlags flags
= 0;
1143 activity_id
= os_activity_start("processing SCNetworkReachability notification",
1144 OS_ACTIVITY_FLAG_DEFAULT
);
1146 if (!targetPrivate
->scheduled
) {
1147 // if no longer scheduled
1148 SC_log(LOG_INFO
, "%sskipping SCNetworkReachability callback, no longer scheduled",
1149 targetPrivate
->log_prefix
);
1150 MUTEX_UNLOCK(&targetPrivate
->lock
);
1155 rlsFunction
= targetPrivate
->rlsFunction
;
1156 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
1157 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
1158 context_release
= targetPrivate
->rlsContext
.release
;
1160 context_info
= targetPrivate
->rlsContext
.info
;
1161 context_release
= NULL
;
1164 flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
, targetPrivate
->type
, targetPrivate
->lastResolverStatus
, targetPrivate
->lastResolvedEndpoints
, targetPrivate
->lastResolvedEndpointHasFlags
, targetPrivate
->lastResolvedEndpointFlags
);
1166 MUTEX_UNLOCK(&targetPrivate
->lock
);
1168 if (rlsFunction
!= NULL
) {
1169 (*rlsFunction
)((SCNetworkReachabilityRef
)targetPrivate
,
1174 if (context_release
!= NULL
) {
1175 (*context_release
)(context_info
);
1180 os_activity_end(activity_id
);
1186 reachPerform(void *info
)
1188 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)info
;
1190 MUTEX_LOCK(&targetPrivate
->lock
);
1191 reachPerformAndUnlock(targetPrivate
);
1196 reachUpdateAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate
)
1198 targetPrivate
->sentFirstUpdate
= TRUE
;
1199 if (targetPrivate
->rls
!= NULL
) {
1200 if (targetPrivate
->rlList
!= NULL
) {
1201 CFRunLoopSourceSignal(targetPrivate
->rls
);
1202 _SC_signalRunLoop(targetPrivate
, targetPrivate
->rls
, targetPrivate
->rlList
);
1204 MUTEX_UNLOCK(&targetPrivate
->lock
);
1206 reachPerformAndUnlock(targetPrivate
);
1211 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
1212 SCNetworkReachabilityCallBack callout
,
1213 SCNetworkReachabilityContext
*context
)
1215 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1217 MUTEX_LOCK(&targetPrivate
->lock
);
1219 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1220 /* let go of the current context */
1221 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1224 targetPrivate
->rlsFunction
= callout
;
1225 targetPrivate
->rlsContext
.info
= NULL
;
1226 targetPrivate
->rlsContext
.retain
= NULL
;
1227 targetPrivate
->rlsContext
.release
= NULL
;
1228 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1230 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
1231 if (context
->retain
!= NULL
) {
1232 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
1236 MUTEX_UNLOCK(&targetPrivate
->lock
);
1243 reachRLSCopyDescription(const void *info
)
1245 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1247 return CFStringCreateWithFormat(NULL
,
1249 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
1254 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
1255 CFRunLoopRef runLoop
,
1256 CFStringRef runLoopMode
)
1258 Boolean success
= FALSE
;
1259 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1260 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1261 _SCErrorSet(kSCStatusInvalidArgument
);
1265 MUTEX_LOCK(&targetPrivate
->lock
);
1267 if (targetPrivate
->scheduled
) {
1268 if (targetPrivate
->rls
!= NULL
&& targetPrivate
->rlList
!= NULL
) {
1269 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1271 * if we do not already have host notifications scheduled with
1272 * this runLoop / runLoopMode
1274 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1277 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
1279 MUTEX_UNLOCK(&targetPrivate
->lock
);
1282 MUTEX_UNLOCK(&targetPrivate
->lock
);
1283 _SCErrorSet(kSCStatusInvalidArgument
);
1288 CFRunLoopSourceContext context
= {
1290 , (void *)target
// info
1291 , CFRetain
// retain
1292 , CFRelease
// release
1293 , reachRLSCopyDescription
// copyDescription
1298 , reachPerform
// perform
1301 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
1302 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1304 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1306 * if we do not already have host notifications scheduled with
1307 * this runLoop / runLoopMode
1309 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1312 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
1314 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, _callback_queue());
1316 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1317 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1318 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1319 // if target is no longer scheduled for this runLoop / runLoopMode
1320 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1322 // if *all* notifications have been unscheduled
1323 CFRelease(targetPrivate
->rlList
);
1324 targetPrivate
->rlList
= NULL
;
1325 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1326 CFRelease(targetPrivate
->rls
);
1327 targetPrivate
->rls
= NULL
;
1333 MUTEX_UNLOCK(&targetPrivate
->lock
);
1338 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
1339 CFRunLoopRef runLoop
,
1340 CFStringRef runLoopMode
)
1342 Boolean success
= FALSE
;
1343 Boolean unscheduleDispatchQueue
= FALSE
;
1344 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1345 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
1346 _SCErrorSet(kSCStatusInvalidArgument
);
1350 MUTEX_LOCK(&targetPrivate
->lock
);
1352 if (targetPrivate
->rlList
== NULL
|| targetPrivate
->rls
== NULL
|| !targetPrivate
->scheduled
) {
1353 MUTEX_UNLOCK(&targetPrivate
->lock
);
1354 _SCErrorSet(kSCStatusInvalidArgument
);
1358 if (_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
1359 CFIndex n
= CFArrayGetCount(targetPrivate
->rlList
);
1360 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
1361 // if target is no longer scheduled for this runLoop / runLoopMode
1362 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
1364 // if *all* notifications have been unscheduled
1365 unscheduleDispatchQueue
= TRUE
;
1366 CFRelease(targetPrivate
->rlList
);
1367 targetPrivate
->rlList
= NULL
;
1368 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
1369 CFRelease(targetPrivate
->rls
);
1370 targetPrivate
->rls
= NULL
;
1375 if (unscheduleDispatchQueue
) {
1376 success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, NULL
);
1380 MUTEX_UNLOCK(&targetPrivate
->lock
);
1384 static __inline__
void
1385 __SCNetworkReachabilityCopyPathStatus(SCNetworkReachabilityPrivateRef targetPrivate
, SCNetworkReachabilityFlags
*flags
, uint
*ifIndex
, size_t *endpointCount
)
1388 *flags
= __SCNetworkReachabilityGetFlagsFromPath(targetPrivate
->lastPath
, targetPrivate
->type
, targetPrivate
->lastResolverStatus
, targetPrivate
->lastResolvedEndpoints
, targetPrivate
->lastResolvedEndpointHasFlags
, targetPrivate
->lastResolvedEndpointFlags
);
1391 *ifIndex
= nw_path_get_interface_index(targetPrivate
->lastPath
);
1393 if (endpointCount
) {
1394 *endpointCount
= nw_array_get_count(targetPrivate
->lastResolvedEndpoints
);
1399 static __inline__ Boolean
1400 __SCNetworkReachabilityShouldUpdateClient(SCNetworkReachabilityPrivateRef targetPrivate
, SCNetworkReachabilityFlags oldFlags
, uint oldIFIndex
, size_t oldEndpointCount
)
1402 SCNetworkReachabilityFlags newFlags
= 0;
1403 uint newIFIndex
= 0;
1404 size_t newEndpointCount
= 0;
1405 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &newFlags
, &newIFIndex
, &newEndpointCount
);
1406 return (!targetPrivate
->sentFirstUpdate
||
1407 oldFlags
!= newFlags
||
1408 oldIFIndex
!= newIFIndex
||
1409 oldEndpointCount
!= newEndpointCount
);
1413 __SCNetworkReachabilityRestartResolver(SCNetworkReachabilityPrivateRef targetPrivate
)
1415 if (targetPrivate
&&
1416 !targetPrivate
->resolverBypass
&&
1417 isReachabilityTypeName(targetPrivate
->type
)) {
1418 targetPrivate
= (SCNetworkReachabilityPrivateRef
)CFRetain(targetPrivate
);
1419 nw_resolver_cancel(targetPrivate
->resolver
);
1420 nw_resolver_t resolver
= nw_resolver_create_with_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
), targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
: targetPrivate
->parameters
);
1421 targetPrivate
->resolver
= resolver
;
1422 nw_resolver_set_cancel_handler(resolver
, ^(void) {
1423 MUTEX_LOCK(&targetPrivate
->lock
);
1424 if (resolver
== targetPrivate
->resolver
) {
1425 targetPrivate
->resolver
= NULL
;
1427 network_release(resolver
);
1428 MUTEX_UNLOCK(&targetPrivate
->lock
);
1429 CFRelease(targetPrivate
);
1431 if (!nw_resolver_set_update_handler(resolver
, targetPrivate
->dispatchQueue
, ^(nw_resolver_status_t status
, nw_array_t resolved_endpoints
) {
1432 MUTEX_LOCK(&targetPrivate
->lock
);
1433 if (targetPrivate
->scheduled
) {
1434 SCNetworkReachabilityFlags oldFlags
= 0;
1435 uint oldIFIndex
= 0;
1436 size_t oldEndpointCount
= 0;
1437 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &oldFlags
, &oldIFIndex
, &oldEndpointCount
);
1439 targetPrivate
->lastResolverStatus
= status
;
1440 network_release(targetPrivate
->lastResolvedEndpoints
);
1441 targetPrivate
->lastResolvedEndpoints
= network_retain(resolved_endpoints
);
1443 // Run path evaluation on the resolved endpoints
1444 __block Boolean hasFlags
= FALSE
;
1445 targetPrivate
->lastResolvedEndpointHasFlags
= FALSE
;
1446 targetPrivate
->lastResolvedEndpointFlags
= 0;
1447 targetPrivate
->lastResolvedEndpointInterfaceIndex
= 0;
1448 nw_array_apply(targetPrivate
->lastResolvedEndpoints
, ^bool(size_t index
, nw_object_t object
) {
1449 SCNetworkReachabilityFlags flags
= 0;
1450 uint interfaceIndex
= 0;
1451 nw_endpoint_t resolvedEndpoint
= (nw_endpoint_t
)object
;
1452 nw_path_evaluator_t pathEvaluator
= nw_path_create_evaluator_for_endpoint(resolvedEndpoint
, targetPrivate
->lastPathParameters
? targetPrivate
->lastPathParameters
: targetPrivate
->parameters
);
1453 nw_path_t path
= nw_path_evaluator_copy_path(pathEvaluator
);
1455 flags
= __SCNetworkReachabilityGetFlagsFromPath(path
, 0, nw_resolver_status_invalid
, NULL
, FALSE
, 0);
1458 interfaceIndex
= nw_path_get_interface_index(path
);
1459 network_release(path
);
1460 network_release(pathEvaluator
);
1462 if (rankReachability(flags
) > rankReachability(targetPrivate
->lastResolvedEndpointFlags
)) {
1463 // Return the best case result
1464 targetPrivate
->lastResolvedEndpointFlags
= flags
;
1465 targetPrivate
->lastResolvedEndpointInterfaceIndex
= interfaceIndex
;
1466 if (rankReachability(flags
) == 2) {
1467 // Can't get any better than REACHABLE
1473 targetPrivate
->lastResolvedEndpointHasFlags
= hasFlags
;
1475 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1476 reachUpdateAndUnlock(targetPrivate
);
1478 MUTEX_UNLOCK(&targetPrivate
->lock
);
1481 MUTEX_UNLOCK(&targetPrivate
->lock
);
1484 network_release(resolver
);
1485 targetPrivate
->resolver
= NULL
;
1486 CFRelease(targetPrivate
);
1492 __SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate
,
1493 dispatch_queue_t queue
)
1497 if (queue
!= NULL
) {
1498 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
1499 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
1500 _SCErrorSet(kSCStatusInvalidArgument
);
1504 // retain dispatch queue
1505 dispatch_retain(queue
);
1506 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1507 nw_path_evaluator_t pathEvaluator
= nw_path_create_evaluator_for_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate
), targetPrivate
->parameters
);
1508 targetPrivate
->pathEvaluator
= pathEvaluator
;
1509 targetPrivate
->dispatchQueue
= queue
;
1510 targetPrivate
->scheduled
= TRUE
;
1511 if (isReachabilityTypeName(targetPrivate
->type
)) {
1512 // we must have at least one callback for by-name queries
1513 targetPrivate
->sentFirstUpdate
= FALSE
;
1515 targetPrivate
->sentFirstUpdate
= TRUE
;
1518 network_release(targetPrivate
->lastPath
);
1519 targetPrivate
->lastPath
= nw_path_evaluator_copy_path(pathEvaluator
);
1521 network_release(targetPrivate
->lastPathParameters
);
1522 targetPrivate
->lastPathParameters
= nw_path_copy_derived_parameters(targetPrivate
->lastPath
);
1524 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1525 network_release(targetPrivate
->lastResolvedEndpoints
);
1526 targetPrivate
->lastResolvedEndpoints
= NULL
;
1527 __SCNetworkReachabilityRestartResolver(targetPrivate
);
1529 targetPrivate
= (SCNetworkReachabilityPrivateRef
)CFRetain(targetPrivate
);
1530 nw_path_evaluator_set_cancel_handler(pathEvaluator
, ^(void) {
1531 MUTEX_LOCK(&targetPrivate
->lock
);
1532 if (pathEvaluator
== targetPrivate
->pathEvaluator
) {
1533 targetPrivate
->pathEvaluator
= NULL
;
1535 network_release(pathEvaluator
);
1536 MUTEX_UNLOCK(&targetPrivate
->lock
);
1537 CFRelease(targetPrivate
);
1540 if (!nw_path_evaluator_set_update_handler(pathEvaluator
, targetPrivate
->dispatchQueue
, ^(nw_path_t path
) {
1541 MUTEX_LOCK(&targetPrivate
->lock
);
1542 if (targetPrivate
->scheduled
) {
1543 SCNetworkReachabilityFlags oldFlags
= 0;
1544 uint oldIFIndex
= 0;
1545 size_t oldEndpointCount
= 0;
1546 __SCNetworkReachabilityCopyPathStatus(targetPrivate
, &oldFlags
, &oldIFIndex
, &oldEndpointCount
);
1548 network_release(targetPrivate
->lastPath
);
1549 targetPrivate
->lastPath
= network_retain(path
);
1550 if (targetPrivate
->lastResolverStatus
== nw_resolver_status_complete
) {
1551 targetPrivate
->lastResolverStatus
= nw_resolver_status_invalid
;
1552 __SCNetworkReachabilityRestartResolver(targetPrivate
);
1555 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate
, oldFlags
, oldIFIndex
, oldEndpointCount
)) {
1556 reachUpdateAndUnlock(targetPrivate
);
1558 MUTEX_UNLOCK(&targetPrivate
->lock
);
1561 MUTEX_UNLOCK(&targetPrivate
->lock
);
1564 targetPrivate
->pathEvaluator
= NULL
;
1565 network_release(pathEvaluator
);
1566 CFRelease(targetPrivate
);
1569 if (targetPrivate
->dispatchQueue
== NULL
) { // if we should be scheduled on a dispatch queue (but are not)
1570 _SCErrorSet(kSCStatusInvalidArgument
);
1574 if (!targetPrivate
->scheduled
) {
1575 // if not currently scheduled
1576 _SCErrorSet(kSCStatusInvalidArgument
);
1581 targetPrivate
->scheduled
= FALSE
;
1582 targetPrivate
->sentFirstUpdate
= FALSE
;
1583 nw_path_evaluator_cancel(targetPrivate
->pathEvaluator
);
1584 targetPrivate
->pathEvaluator
= NULL
;
1585 network_release(targetPrivate
->lastPath
);
1586 targetPrivate
->lastPath
= NULL
;
1587 network_release(targetPrivate
->lastPathParameters
);
1588 targetPrivate
->lastPathParameters
= NULL
;
1589 network_release(targetPrivate
->lastResolvedEndpoints
);
1590 targetPrivate
->lastResolvedEndpoints
= NULL
;
1591 nw_resolver_cancel(targetPrivate
->resolver
);
1592 targetPrivate
->resolver
= NULL
;
1593 if (targetPrivate
->dispatchQueue
!= NULL
) {
1594 dispatch_release(targetPrivate
->dispatchQueue
);
1595 targetPrivate
->dispatchQueue
= NULL
;
1604 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
1605 dispatch_queue_t queue
)
1607 if (!isA_SCNetworkReachability(target
)) {
1608 _SCErrorSet(kSCStatusInvalidArgument
);
1612 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1613 MUTEX_LOCK(&targetPrivate
->lock
);
1614 Boolean success
= __SCNetworkReachabilitySetDispatchQueue(targetPrivate
, queue
);
1615 MUTEX_UNLOCK(&targetPrivate
->lock
);
1620 * _SC_checkResolverReachabilityByAddress()
1622 * Given an IP address, determine whether a reverse DNS query can be issued
1623 * using the current network configuration.
1626 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
1627 SCNetworkReachabilityFlags
*flags
,
1629 struct sockaddr
*sa
)
1631 nw_path_evaluator_t evaluator
= nw_path_create_default_evaluator();
1632 nw_path_t path
= nw_path_evaluator_copy_path(evaluator
);
1633 if (nw_path_get_status(path
) == nw_path_status_unsatisfied_network
) {
1642 *flags
= kSCNetworkReachabilityFlagsReachable
;
1648 network_release(evaluator
);
1649 network_release(path
);