2 * Copyright (c) 2003-2012 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>
51 #include <IOKit/pwr_mgt/IOPMLibPrivate.h>
52 #endif // !TARGET_OS_IPHONE
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
59 #include <netdb_async.h>
62 #include <sys/ioctl.h>
63 #include <sys/socket.h>
65 #include <net/if_dl.h>
66 #include <net/if_types.h>
67 #define KERNEL_PRIVATE
68 #include <net/route.h>
72 #define s6_addr16 __u6_addr.__u6_addr16
75 #include "SCNetworkReachabilityInternal.h"
77 #include <ppp/ppp_msg.h>
79 #if !TARGET_IPHONE_SIMULATOR
80 #include <ppp/PPPControllerPriv.h>
81 #endif // !TARGET_IPHONE_SIMULATOR
85 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR
86 #define HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
87 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR
89 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_EMBEDDED_OTHER
90 #define HAVE_IPSEC_STATUS
91 #define HAVE_VPN_STATUS
92 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_EMBEDDED_OTHER
97 #define DEBUG_REACHABILITY_TYPE_NAME "create w/name"
98 #define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
100 #define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"
101 #define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
103 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"
104 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
107 static pthread_mutexattr_t lock_attr
;
109 #define MUTEX_INIT(m) { \
110 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \
114 #define MUTEX_LOCK(m) { \
115 int _lock_ = (pthread_mutex_lock(m) == 0); \
119 #define MUTEX_UNLOCK(m) { \
120 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
124 #define MUTEX_ASSERT_HELD(m) { \
125 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \
130 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
133 _getaddrinfo_interface_async_call(const char *nodename
,
134 const char *servname
,
135 const struct addrinfo
*hints
,
136 const char *interface
,
137 getaddrinfo_async_callback callback
,
139 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
142 #define SCNETWORKREACHABILITY_TRIGGER_KEY CFSTR("com.apple.SCNetworkReachability:FORCE-CHANGE")
145 // how long (minimum time, us) to wait before retrying DNS query after EAI_NONAME
146 #define EAI_NONAME_RETRY_DELAY_USEC 250000 // 250ms
148 // how long (maximum time, us) after DNS configuration change we accept EAI_NONAME
150 #define EAI_NONAME_RETRY_LIMIT_USEC 2500000 // 2.5s
153 // how long (maximum time, ns) to wait for a long-lived-query callback before
154 // we assume EAI_NONAME.
155 #define LLQ_TIMEOUT_NSEC 30 * NSEC_PER_SEC // 30s
161 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
162 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
163 static void reachPerform (void *info
);
167 __SCNetworkReachabilityScheduleWithRunLoop (SCNetworkReachabilityRef target
,
168 CFRunLoopRef runLoop
,
169 CFStringRef runLoopMode
,
170 dispatch_queue_t queue
,
174 __SCNetworkReachabilityUnscheduleFromRunLoop (SCNetworkReachabilityRef target
,
175 CFRunLoopRef runLoop
,
176 CFStringRef runLoopMode
,
180 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
183 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
185 "SCNetworkReachability", // className
188 __SCNetworkReachabilityDeallocate
, // dealloc
191 NULL
, // copyFormattingDesc
192 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
196 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
197 static const ReachabilityInfo NOT_REACHABLE
= { 0, 0, 0, { 0 }, FALSE
};
198 static const ReachabilityInfo NOT_REPORTED
= { 0, 0xFFFFFFFF, 0, { 0 }, FALSE
};
199 static int rtm_seq
= 0;
202 static const struct addrinfo HINTS_DEFAULT
= {
204 .ai_flags
= AI_ADDRCONFIG
| AI_PARALLEL
,
206 .ai_flags
= AI_ADDRCONFIG
,
207 #endif // AI_PARALLEL
211 static const struct timeval TIME_ZERO
= { 0, 0 };
214 static Boolean D_llqBypass
= FALSE
;
215 static int llqCount
= 0;
216 static DNSServiceRef llqMain
= NULL
;
217 static CFMutableSetRef llqUpdated
= NULL
;
220 #ifdef HAVE_REACHABILITY_SERVER
221 static Boolean D_serverBypass
= FALSE
;
222 #endif // HAVE_REACHABILITY_SERVER
225 #if !TARGET_OS_IPHONE
227 * Power capabilities (sleep/wake)
229 static IOPMSystemPowerStateCapabilities power_capabilities
= kIOPMSytemPowerStateCapabilitiesMask
;
230 #endif // !TARGET_OS_IPHONE
234 * host "something has changed" notifications
237 static SCDynamicStoreRef hn_store
= NULL
;
238 static dispatch_queue_t hn_dispatchQueue
= NULL
;
239 static CFMutableSetRef hn_targets
= NULL
;
247 dns_config_t
*config
;
249 } dns_configuration_t
;
252 static pthread_mutex_t dns_lock
= PTHREAD_MUTEX_INITIALIZER
;
253 static dns_configuration_t
*dns_configuration
= NULL
;
254 static int dns_token
;
255 static Boolean dns_token_valid
= FALSE
;
266 __dns_query_start(struct timeval
*dnsQueryStart
,
267 struct timeval
*dnsQueryEnd
)
269 (void) gettimeofday(dnsQueryStart
, NULL
);
270 *dnsQueryEnd
= TIME_ZERO
;
277 __dns_query_end(SCNetworkReachabilityRef target
,
279 dns_query_type query_type
,
280 struct timeval
*dnsQueryStart
,
281 struct timeval
*dnsQueryEnd
)
283 struct timeval dnsQueryElapsed
;
285 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
287 // report initial or updated query time
288 firstQuery
= !timerisset(dnsQueryEnd
);
290 (void) gettimeofday(dnsQueryEnd
, NULL
);
296 if (!timerisset(dnsQueryStart
)) {
300 timersub(dnsQueryEnd
, dnsQueryStart
, &dnsQueryElapsed
);
301 switch (query_type
) {
303 // #define QUERY_TIME__FMT "%d.%3.3d"
304 // #define QUERY_TIME__DIV 1000
306 #define QUERY_TIME__FMT "%d.%6.6d"
307 #define QUERY_TIME__DIV 1
309 case dns_query_sync
:
310 SCLog(TRUE
, LOG_INFO
,
311 CFSTR("%ssync DNS complete%s (query time = " QUERY_TIME__FMT
")"),
312 targetPrivate
->log_prefix
,
313 found
? "" : ", host not found",
314 dnsQueryElapsed
.tv_sec
,
315 dnsQueryElapsed
.tv_usec
/ QUERY_TIME__DIV
);
317 case dns_query_async
:
318 SCLog(TRUE
, LOG_INFO
,
319 CFSTR("%sasync DNS complete%s (query time = " QUERY_TIME__FMT
")"),
320 targetPrivate
->log_prefix
,
321 found
? "" : ", host not found",
322 dnsQueryElapsed
.tv_sec
,
323 dnsQueryElapsed
.tv_usec
/ QUERY_TIME__DIV
);
326 SCLog(TRUE
, LOG_INFO
,
327 CFSTR("%sDNS updated%s (%s = " QUERY_TIME__FMT
")"),
328 targetPrivate
->log_prefix
,
329 found
? "" : ", host not found",
330 firstQuery
? "query time" : "updated after",
331 dnsQueryElapsed
.tv_sec
,
332 dnsQueryElapsed
.tv_usec
/ QUERY_TIME__DIV
);
340 static __inline__ Boolean
341 __reach_changed(ReachabilityInfo
*r1
, ReachabilityInfo
*r2
)
343 if (r1
->flags
!= r2
->flags
) {
344 // if the reachability flags changed
348 if (r1
->if_index
!= r2
->if_index
) {
349 // if the target interface changed
353 if ((r1
->sleeping
!= r2
->sleeping
) && !r2
->sleeping
) {
354 // if our sleep/wake status changed and if we
355 // are no longer sleeping
363 static __inline__
void
364 _reach_set(ReachabilityInfo
*dst
, const ReachabilityInfo
*src
, uint64_t cycle
)
366 memcpy(dst
, src
, sizeof(ReachabilityInfo
));
374 #pragma mark SCDynamicStore info
378 SCDynamicStoreRef store
;
380 CFDictionaryRef dict
;
383 const void * keys_q
[N_QUICK
];
384 const void ** values
;
385 const void * values_q
[N_QUICK
];
386 } ReachabilityStoreInfo
, *ReachabilityStoreInfoRef
;
389 static ReachabilityStoreInfo S_storeInfo
= { 0 };
390 static Boolean S_storeInfoActive
= FALSE
;
393 static dispatch_queue_t
396 static dispatch_once_t once
;
397 static dispatch_queue_t q
;
399 dispatch_once(&once
, ^{
400 q
= dispatch_queue_create("SCNetworkReachabilty.storeInfo", NULL
);
408 ReachabilityStoreInfo_copy(ReachabilityStoreInfoRef src
,
409 ReachabilityStoreInfoRef dst
)
411 if (src
->dict
!= NULL
) {
412 dst
->store
= src
->store
;
413 CFRetain(dst
->store
);
415 dst
->dict
= src
->dict
;
420 if (dst
->n
<= (CFIndex
)(sizeof(dst
->keys_q
) / sizeof(CFTypeRef
))) {
421 dst
->keys
= dst
->keys_q
;
422 dst
->values
= dst
->values_q
;
424 dst
->keys
= CFAllocatorAllocate(NULL
, dst
->n
* sizeof(CFTypeRef
), 0);
425 dst
->values
= CFAllocatorAllocate(NULL
, dst
->n
* sizeof(CFTypeRef
), 0);
427 memcpy(dst
->keys
, src
->keys
, dst
->n
* sizeof(CFTypeRef
));
428 memcpy(dst
->values
, src
->values
, dst
->n
* sizeof(CFTypeRef
));
437 ReachabilityStoreInfo_enable(Boolean enable
)
439 dispatch_sync(_storeInfo_queue(), ^{
440 S_storeInfoActive
= enable
;
448 ReachabilityStoreInfo_free(ReachabilityStoreInfoRef store_info
)
450 if ((store_info
->n
> 0) && (store_info
->keys
!= store_info
->keys_q
)) {
451 CFAllocatorDeallocate(NULL
, store_info
->keys
);
452 store_info
->keys
= NULL
;
454 CFAllocatorDeallocate(NULL
, store_info
->values
);
455 store_info
->values
= NULL
;
459 if (store_info
->dict
!= NULL
) {
460 CFRelease(store_info
->dict
);
461 store_info
->dict
= NULL
;
464 if (store_info
->store
!= NULL
) {
465 CFRelease(store_info
->store
);
466 store_info
->store
= NULL
;
474 ReachabilityStoreInfo_init(ReachabilityStoreInfoRef store_info
)
476 dispatch_sync(_storeInfo_queue(), ^{
477 bzero(store_info
, sizeof(ReachabilityStoreInfo
));
479 if (S_storeInfoActive
&& (S_storeInfo
.dict
!= NULL
)) {
480 ReachabilityStoreInfo_copy(&S_storeInfo
, store_info
);
489 ReachabilityStoreInfo_save(ReachabilityStoreInfoRef store_info
)
491 dispatch_sync(_storeInfo_queue(), ^{
492 if ((store_info
== NULL
) ||
493 !_SC_CFEqual(store_info
->dict
, S_storeInfo
.dict
)) {
495 ReachabilityStoreInfo_free(&S_storeInfo
);
498 if (S_storeInfoActive
&&
499 (store_info
!= NULL
) &&
500 (store_info
->dict
!= NULL
)) {
501 ReachabilityStoreInfo_copy(store_info
, &S_storeInfo
);
511 ReachabilityStoreInfo_fill(ReachabilityStoreInfoRef store_info
)
514 CFMutableArrayRef patterns
;
516 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
518 // get info for IPv4 services
519 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
520 kSCDynamicStoreDomainSetup
,
523 CFArrayAppendValue(patterns
, pattern
);
525 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
526 kSCDynamicStoreDomainState
,
529 CFArrayAppendValue(patterns
, pattern
);
532 // get info for IPv6 services
533 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
534 kSCDynamicStoreDomainSetup
,
537 CFArrayAppendValue(patterns
, pattern
);
539 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
540 kSCDynamicStoreDomainState
,
543 CFArrayAppendValue(patterns
, pattern
);
546 // get info for PPP services
547 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
548 kSCDynamicStoreDomainSetup
,
551 CFArrayAppendValue(patterns
, pattern
);
553 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
554 kSCDynamicStoreDomainState
,
557 CFArrayAppendValue(patterns
, pattern
);
560 #if !TARGET_IPHONE_SIMULATOR
561 // get info for VPN services
562 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
563 kSCDynamicStoreDomainSetup
,
566 CFArrayAppendValue(patterns
, pattern
);
568 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
569 kSCDynamicStoreDomainState
,
572 CFArrayAppendValue(patterns
, pattern
);
574 #endif // !TARGET_IPHONE_SIMULATOR
576 // get info for IPSec services
577 // pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
578 // kSCDynamicStoreDomainSetup,
581 // CFArrayAppendValue(patterns, pattern);
582 // CFRelease(pattern);
583 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
584 kSCDynamicStoreDomainState
,
587 CFArrayAppendValue(patterns
, pattern
);
590 // get info to identify "available" services
591 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
592 kSCDynamicStoreDomainSetup
,
595 CFArrayAppendValue(patterns
, pattern
);
599 // get the SCDynamicStore info
600 store_info
->dict
= SCDynamicStoreCopyMultiple(store_info
->store
, NULL
, patterns
);
602 if (store_info
->dict
== NULL
) {
606 // and extract the keys/values for post-processing
607 store_info
->n
= CFDictionaryGetCount(store_info
->dict
);
608 if (store_info
->n
> 0) {
609 if (store_info
->n
<= (CFIndex
)(sizeof(store_info
->keys_q
) / sizeof(CFTypeRef
))) {
610 store_info
->keys
= store_info
->keys_q
;
611 store_info
->values
= store_info
->values_q
;
613 store_info
->keys
= CFAllocatorAllocate(NULL
, store_info
->n
* sizeof(CFTypeRef
), 0);
614 store_info
->values
= CFAllocatorAllocate(NULL
, store_info
->n
* sizeof(CFTypeRef
), 0);
616 CFDictionaryGetKeysAndValues(store_info
->dict
,
626 ReachabilityStoreInfo_update(ReachabilityStoreInfoRef store_info
,
627 SCDynamicStoreRef
*storeP
,
628 sa_family_t sa_family
)
630 __block Boolean ok
= TRUE
;
634 store_info
->entity
= NULL
;
637 store_info
->entity
= kSCEntNetIPv4
;
640 store_info
->entity
= kSCEntNetIPv6
;
646 if (store_info
->dict
!= NULL
) {
647 // if info already available
651 dispatch_sync(_storeInfo_queue(), ^{
652 if (S_storeInfoActive
&& (S_storeInfo
.dict
!= NULL
)) {
654 ReachabilityStoreInfo_free(store_info
);
656 // copy the shared/available info
657 ReachabilityStoreInfo_copy(&S_storeInfo
, store_info
);
660 if (store_info
->store
== NULL
) {
661 store_info
->store
= (storeP
!= NULL
) ? *storeP
: NULL
;
662 if (store_info
->store
!= NULL
) {
663 // keep a reference to the passed in SCDynamicStore
664 CFRetain(store_info
->store
);
666 store_info
->store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
667 if (store_info
->store
== NULL
) {
668 SCLog(TRUE
, LOG_ERR
, CFSTR("ReachabilityStoreInfo_update SCDynamicStoreCreate() failed"));
672 if (storeP
!= NULL
) {
673 // and pass back a reference
674 *storeP
= store_info
->store
;
680 if (sa_family
== AF_UNSPEC
) {
681 // if the address family was not specified than
682 // all we wanted, for now, was to establish the
683 // SCDynamicStore session
687 if (store_info
->dict
!= NULL
) {
688 // or we have picked up the shared info
692 ok
= ReachabilityStoreInfo_fill(store_info
);
697 if (!_SC_CFEqual(store_info
->dict
, S_storeInfo
.dict
)) {
699 ReachabilityStoreInfo_free(&S_storeInfo
);
702 if (S_storeInfoActive
&&
703 (store_info
->dict
!= NULL
)) {
704 ReachabilityStoreInfo_copy(store_info
, &S_storeInfo
);
714 #pragma mark PPP info
718 updatePPPStatus(ReachabilityStoreInfoRef store_info
,
719 const struct sockaddr
*sa
,
721 SCNetworkReachabilityFlags
*flags
,
722 CFStringRef
*ppp_server
,
723 const char *log_prefix
)
727 int sc_status
= kSCStatusNoKey
;
729 if (!ReachabilityStoreInfo_update(store_info
, NULL
, sa
->sa_family
)) {
730 return kSCStatusReachabilityUnknown
;
733 if (store_info
->n
<= 0) {
735 return kSCStatusNoKey
;
738 // look for the [PPP] service which matches the provided interface
740 ppp_if
= CFStringCreateWithCStringNoCopy(NULL
,
742 kCFStringEncodingASCII
,
745 for (i
=0; i
< store_info
->n
; i
++) {
746 CFArrayRef components
;
749 CFDictionaryRef p_setup
;
750 CFDictionaryRef p_state
;
753 CFStringRef service
= NULL
;
754 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
755 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
758 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
762 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
763 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
764 continue; // if not an active IPv4 or IPv6 entity
767 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
768 if (!isA_CFString(s_if
)) {
769 continue; // if no interface
772 if (!CFEqual(ppp_if
, s_if
)) {
773 continue; // if not this interface
776 // extract the service ID, get the PPP "state" entity for
777 // the "Status", and get the PPP "setup" entity for the
778 // the "DialOnDemand" flag
779 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
780 if (CFArrayGetCount(components
) != 5) {
781 CFRelease(components
);
784 service
= CFArrayGetValueAtIndex(components
, 3);
785 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
786 kSCDynamicStoreDomainState
,
789 p_state
= CFDictionaryGetValue(store_info
->dict
, key
);
791 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
792 kSCDynamicStoreDomainSetup
,
795 p_setup
= CFDictionaryGetValue(store_info
->dict
, key
);
797 CFRelease(components
);
799 // ensure that this is a PPP service
800 if (!isA_CFDictionary(p_state
)) {
804 sc_status
= kSCStatusOK
;
806 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
809 if (ppp_server
!= NULL
) {
810 *ppp_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
811 *ppp_server
= isA_CFString(*ppp_server
);
812 if (*ppp_server
!= NULL
) {
813 CFRetain(*ppp_server
);
818 if (!CFDictionaryGetValueIfPresent(p_state
,
820 (const void **)&num
) ||
821 !isA_CFNumber(num
) ||
822 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) {
825 switch (ppp_status
) {
827 // if we're really UP and RUNNING
830 // if we're effectively UP and RUNNING
833 // if we're not connected at all
834 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link idle"),
836 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
838 case PPP_STATERESERVED
:
839 // if we're not connected at all
840 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link idle, dial-on-traffic to connect"),
842 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
845 // if we're in the process of [dis]connecting
846 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link, connection in progress"),
848 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
852 // get PPP dial-on-traffic status
853 if (isA_CFDictionary(p_setup
) &&
854 CFDictionaryGetValueIfPresent(p_setup
,
855 kSCPropNetPPPDialOnDemand
,
856 (const void **)&num
) &&
858 CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
) &&
860 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
861 if (ppp_status
== PPP_IDLE
) {
862 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
876 updatePPPAvailable(ReachabilityStoreInfoRef store_info
,
877 const struct sockaddr
*sa
,
878 SCNetworkReachabilityFlags
*flags
,
879 const char *log_prefix
)
882 int sc_status
= kSCStatusNoKey
;
884 if (!ReachabilityStoreInfo_update(store_info
,
886 (sa
!= NULL
) ? sa
->sa_family
: AF_INET
)) {
887 return kSCStatusReachabilityUnknown
;
890 if (store_info
->n
<= 0) {
892 return kSCStatusNoKey
;
895 // look for an available service which will provide connectivity
896 // for the requested address family.
898 for (i
= 0; i
< store_info
->n
; i
++) {
899 CFArrayRef components
;
900 Boolean found
= FALSE
;
902 CFDictionaryRef i_dict
;
904 CFDictionaryRef p_dict
;
906 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
907 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
909 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
913 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
914 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainSetup
)) {
915 continue; // if not an IPv4 or IPv6 entity
918 // extract service ID
919 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
920 if (CFArrayGetCount(components
) != 5) {
921 CFRelease(components
);
924 service
= CFArrayGetValueAtIndex(components
, 3);
926 // check for [non-VPN] PPP entity
927 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
928 kSCDynamicStoreDomainSetup
,
931 p_dict
= CFDictionaryGetValue(store_info
->dict
, p_key
);
934 i_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
935 kSCDynamicStoreDomainSetup
,
938 i_dict
= CFDictionaryGetValue(store_info
->dict
, i_key
);
941 if (isA_CFDictionary(p_dict
) &&
942 isA_CFDictionary(i_dict
) &&
943 CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) {
946 // we have a PPP service for this address family
949 *flags
|= kSCNetworkReachabilityFlagsReachable
;
950 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
951 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
953 // get PPP dial-on-traffic status
954 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
955 if (isA_CFNumber(num
)) {
958 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
960 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
966 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after connect)"),
968 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service = %@"),
975 CFRelease(components
);
978 sc_status
= kSCStatusOK
;
988 #pragma mark VPN info
991 #if !TARGET_IPHONE_SIMULATOR
993 updateVPNStatus(ReachabilityStoreInfoRef store_info
,
994 const struct sockaddr
*sa
,
996 SCNetworkReachabilityFlags
*flags
,
997 CFStringRef
*vpn_server
,
998 const char *log_prefix
)
1002 int sc_status
= kSCStatusNoKey
;
1004 if (!ReachabilityStoreInfo_update(store_info
, NULL
, sa
->sa_family
)) {
1005 return kSCStatusReachabilityUnknown
;
1008 if (store_info
->n
<= 0) {
1010 return kSCStatusNoKey
;
1013 // look for the [VPN] service which matches the provided interface
1015 vpn_if
= CFStringCreateWithCStringNoCopy(NULL
,
1017 kCFStringEncodingASCII
,
1020 for (i
=0; i
< store_info
->n
; i
++) {
1021 CFArrayRef components
;
1024 CFDictionaryRef p_state
;
1026 CFStringRef service
= NULL
;
1027 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
1028 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
1031 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
1035 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
1036 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
1037 continue; // if not an active IPv4 or IPv6 entity
1040 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
1041 if (!isA_CFString(s_if
)) {
1042 continue; // if no interface
1045 if (!CFEqual(vpn_if
, s_if
)) {
1046 continue; // if not this interface
1049 // extract the service ID and get the VPN "state" entity for
1051 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
1052 if (CFArrayGetCount(components
) != 5) {
1053 CFRelease(components
);
1056 service
= CFArrayGetValueAtIndex(components
, 3);
1057 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1058 kSCDynamicStoreDomainState
,
1061 p_state
= CFDictionaryGetValue(store_info
->dict
, key
);
1063 CFRelease(components
);
1065 // ensure that this is a VPN service
1066 if (!isA_CFDictionary(p_state
)) {
1070 sc_status
= kSCStatusOK
;
1072 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1075 if (vpn_server
!= NULL
) {
1076 *vpn_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
1077 *vpn_server
= isA_CFString(*vpn_server
);
1078 if (*vpn_server
!= NULL
) {
1079 CFRetain(*vpn_server
);
1084 if (!CFDictionaryGetValueIfPresent(p_state
,
1085 kSCPropNetVPNStatus
,
1086 (const void **)&num
) ||
1087 !isA_CFNumber(num
) ||
1088 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &vpn_status
)) {
1091 #ifdef HAVE_VPN_STATUS
1092 switch (vpn_status
) {
1094 // if we're really UP and RUNNING
1099 case VPN_UNLOADING
:
1100 // if we're not connected at all
1101 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s VPN link idle"),
1103 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1106 // if we're in the process of [dis]connecting
1107 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s VPN link, connection in progress"),
1109 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1112 #endif // HAVE_VPN_STATUS
1124 updateVPNAvailable(ReachabilityStoreInfoRef store_info
,
1125 const struct sockaddr
*sa
,
1126 SCNetworkReachabilityFlags
*flags
,
1127 const char *log_prefix
)
1130 int sc_status
= kSCStatusNoKey
;
1132 if (!ReachabilityStoreInfo_update(store_info
,
1134 (sa
!= NULL
) ? sa
->sa_family
: AF_INET
)) {
1135 return kSCStatusReachabilityUnknown
;
1138 if (store_info
->n
<= 0) {
1140 return kSCStatusNoKey
;
1143 // look for an available service which will provide connectivity
1144 // for the requested address family.
1146 for (i
= 0; i
< store_info
->n
; i
++) {
1147 CFArrayRef components
;
1148 Boolean found
= FALSE
;
1150 CFDictionaryRef i_dict
;
1152 CFDictionaryRef p_dict
;
1153 CFStringRef service
;
1154 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
1155 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
1157 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
1161 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
1162 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainSetup
)) {
1163 continue; // if not an IPv4 or IPv6 entity
1166 // extract service ID
1167 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
1168 if (CFArrayGetCount(components
) != 5) {
1169 CFRelease(components
);
1172 service
= CFArrayGetValueAtIndex(components
, 3);
1174 // check for VPN entity
1175 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1176 kSCDynamicStoreDomainSetup
,
1179 p_dict
= CFDictionaryGetValue(store_info
->dict
, p_key
);
1182 i_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1183 kSCDynamicStoreDomainSetup
,
1185 kSCEntNetInterface
);
1186 i_dict
= CFDictionaryGetValue(store_info
->dict
, i_key
);
1189 if (isA_CFDictionary(p_dict
) &&
1190 isA_CFDictionary(i_dict
) &&
1191 CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) {
1192 // we have a VPN service for this address family
1195 *flags
|= kSCNetworkReachabilityFlagsReachable
;
1196 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1197 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1200 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after connect)"),
1202 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service = %@"),
1209 CFRelease(components
);
1212 sc_status
= kSCStatusOK
;
1219 #endif // !TARGET_IPHONE_SIMULATOR
1223 #pragma mark IPSec info
1227 updateIPSecStatus(ReachabilityStoreInfoRef store_info
,
1228 const struct sockaddr
*sa
,
1229 const char *if_name
,
1230 SCNetworkReachabilityFlags
*flags
,
1231 CFStringRef
*ipsec_server
,
1232 const char *log_prefix
)
1235 CFStringRef ipsec_if
;
1236 int sc_status
= kSCStatusNoKey
;
1238 if (!ReachabilityStoreInfo_update(store_info
, NULL
, sa
->sa_family
)) {
1239 return kSCStatusReachabilityUnknown
;
1242 if (store_info
->n
<= 0) {
1244 return kSCStatusNoKey
;
1247 // look for the [IPSec] service that matches the provided interface
1249 ipsec_if
= CFStringCreateWithCStringNoCopy(NULL
,
1251 kCFStringEncodingASCII
,
1254 for (i
=0; i
< store_info
->n
; i
++) {
1255 CFArrayRef components
;
1257 CFDictionaryRef i_state
;
1258 int32_t ipsec_status
;
1260 CFStringRef service
= NULL
;
1261 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
1262 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
1265 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
1269 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
1270 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
1271 continue; // if not an IPv4 or IPv6 entity
1274 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
1275 if (!isA_CFString(s_if
)) {
1276 continue; // if no interface
1279 if (!CFEqual(ipsec_if
, s_if
)) {
1280 continue; // if not this interface
1283 // extract the service ID, get the IPSec "state" entity for
1284 // the "Status", and get the IPSec "setup" entity to confirm
1285 // that we're looking at what we're expecting
1286 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
1287 if (CFArrayGetCount(components
) != 5) {
1288 CFRelease(components
);
1291 service
= CFArrayGetValueAtIndex(components
, 3);
1292 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1293 kSCDynamicStoreDomainState
,
1296 i_state
= CFDictionaryGetValue(store_info
->dict
, key
);
1298 CFRelease(components
);
1300 // ensure that this is an IPSec service
1301 if (!isA_CFDictionary(i_state
)) {
1305 sc_status
= kSCStatusOK
;
1307 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1310 if (ipsec_server
!= NULL
) {
1311 *ipsec_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
1312 *ipsec_server
= isA_CFString(*ipsec_server
);
1313 if (*ipsec_server
!= NULL
) {
1314 CFRetain(*ipsec_server
);
1319 if (!CFDictionaryGetValueIfPresent(i_state
,
1320 kSCPropNetIPSecStatus
,
1321 (const void **)&num
) ||
1322 !isA_CFNumber(num
) ||
1323 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ipsec_status
)) {
1326 #ifdef HAVE_IPSEC_STATUS
1327 switch (ipsec_status
) {
1328 case IPSEC_RUNNING
:
1329 // if we're really UP and RUNNING
1332 // if we're not connected at all
1333 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s IPSec link idle"),
1335 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1338 // if we're in the process of [dis]connecting
1339 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s IPSec link, connection in progress"),
1341 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1344 #endif // HAVE_IPSEC_STATUS
1349 CFRelease(ipsec_if
);
1358 #pragma mark Reachability engine
1361 #define ROUNDUP(a, size) \
1362 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
1364 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
1365 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
1366 sizeof(uint32_t)) :\
1370 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
1374 for (i
= 0; i
< RTAX_MAX
; i
++) {
1375 if (addrs
& (1 << i
)) {
1384 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
1390 struct rt_msghdr rtm
;
1393 struct sockaddr
*rti_info
[RTAX_MAX
];
1394 struct rt_msghdr
*rtm
;
1395 struct sockaddr_dl
*sdl
;
1396 } route_info
, *route_info_p
;
1401 * returns zero if route exists an data returned, EHOSTUNREACH
1402 * if no route, or errno for any other error.
1405 route_get(const struct sockaddr
*address
,
1406 unsigned int if_index
,
1411 pid_t pid
= getpid();
1413 struct sockaddr
*sa
;
1414 int32_t seq
= OSAtomicIncrement32Barrier(&rtm_seq
);
1415 #ifndef RTM_GET_SILENT
1416 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
1417 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
1418 int sosize
= 48 * 1024;
1421 bzero(info
, sizeof(*info
));
1423 info
->rtm
= &info
->buf
.rtm
;
1424 info
->rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
1425 info
->rtm
->rtm_version
= RTM_VERSION
;
1426 #ifdef RTM_GET_SILENT
1427 info
->rtm
->rtm_type
= RTM_GET_SILENT
;
1429 info
->rtm
->rtm_type
= RTM_GET
;
1431 info
->rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
1432 info
->rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
1433 info
->rtm
->rtm_pid
= pid
;
1434 info
->rtm
->rtm_seq
= seq
;
1436 if (if_index
!= 0) {
1437 info
->rtm
->rtm_flags
|= RTF_IFSCOPE
;
1438 info
->rtm
->rtm_index
= if_index
;
1441 switch (address
->sa_family
) {
1443 struct sockaddr_in6
*sin6
;
1445 /* ALIGN: caller ensures that the address is aligned */
1446 sin6
= (struct sockaddr_in6
*)(void *)address
;
1447 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
1448 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
1449 (sin6
->sin6_scope_id
!= 0)) {
1450 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
1451 sin6
->sin6_scope_id
= 0;
1457 sa
= (struct sockaddr
*) (info
->rtm
+ 1);
1458 bcopy(address
, sa
, address
->sa_len
);
1459 n
= ROUNDUP(sa
->sa_len
, sizeof(uint32_t));
1460 info
->rtm
->rtm_msglen
+= n
;
1462 info
->sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
1463 info
->sdl
->sdl_family
= AF_LINK
;
1464 info
->sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
1465 n
= ROUNDUP(info
->sdl
->sdl_len
, sizeof(uint32_t));
1466 info
->rtm
->rtm_msglen
+= n
;
1468 #ifndef RTM_GET_SILENT
1469 pthread_mutex_lock(&lock
);
1471 rsock
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
);
1475 #ifndef RTM_GET_SILENT
1476 pthread_mutex_unlock(&lock
);
1478 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error
));
1482 if (ioctl(rsock
, FIONBIO
, &opt
) < 0) {
1486 #ifndef RTM_GET_SILENT
1487 pthread_mutex_unlock(&lock
);
1489 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl(FIONBIO) failed: %s"), strerror(error
));
1493 #ifndef RTM_GET_SILENT
1494 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
1498 pthread_mutex_unlock(&lock
);
1499 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error
));
1504 if (write(rsock
, &info
->buf
, info
->rtm
->rtm_msglen
) == -1) {
1508 #ifndef RTM_GET_SILENT
1509 pthread_mutex_unlock(&lock
);
1511 if (error
!= ESRCH
) {
1512 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(error
));
1515 return EHOSTUNREACH
;
1519 * Type, seq, pid identify our response.
1520 * Routing sockets are broadcasters on input.
1525 n
= read(rsock
, &info
->buf
, sizeof(info
->buf
));
1529 if (error
== EINTR
) {
1533 #ifndef RTM_GET_SILENT
1534 pthread_mutex_unlock(&lock
);
1536 SCLog(TRUE
, LOG_ERR
,
1537 CFSTR("SCNetworkReachability: routing socket"
1538 " read() failed: %s"), strerror(error
));
1541 if ((info
->rtm
->rtm_type
== RTM_GET
) &&
1542 (info
->rtm
->rtm_seq
== seq
) &&
1543 (info
->rtm
->rtm_pid
== pid
)) {
1549 #ifndef RTM_GET_SILENT
1550 pthread_mutex_unlock(&lock
);
1553 get_rtaddrs(info
->rtm
->rtm_addrs
, sa
, info
->rti_info
);
1555 //#define LOG_RTADDRS
1560 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), info
->rtm
->rtm_flags
);
1562 if ((info
->rti_info
[RTAX_NETMASK
] != NULL
) && (info
->rti_info
[RTAX_DST
] != NULL
)) {
1563 info
->rti_info
[RTAX_NETMASK
]->sa_family
= info
->rti_info
[RTAX_DST
]->sa_family
;
1566 for (i
= 0; i
< RTAX_MAX
; i
++) {
1567 if (info
->rti_info
[i
] != NULL
) {
1570 _SC_sockaddr_to_string(info
->rti_info
[i
], addr
, sizeof(addr
));
1571 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, addr
);
1575 #endif /* LOG_RTADDRS */
1577 if ((info
->rti_info
[RTAX_IFP
] == NULL
) ||
1578 (info
->rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
1579 /* no interface info */
1580 SCLog(TRUE
, LOG_DEBUG
, CFSTR("route_get() no interface info"));
1584 /* ALIGN: accessors are retrieving byte values, cast ok. */
1585 info
->sdl
= (struct sockaddr_dl
*)(void *) info
->rti_info
[RTAX_IFP
];
1586 if ((info
->sdl
->sdl_nlen
== 0) || (info
->sdl
->sdl_nlen
> IFNAMSIZ
)) {
1587 /* no interface name */
1588 return EHOSTUNREACH
;
1596 checkAddress(ReachabilityStoreInfoRef store_info
,
1597 const struct sockaddr
*address
,
1598 unsigned int if_index
,
1599 ReachabilityInfo
*reach_info
,
1600 const char *log_prefix
)
1604 char if_name
[IFNAMSIZ
];
1607 int sc_status
= kSCStatusReachabilityUnknown
;
1608 CFStringRef server
= NULL
;
1609 char *statusMessage
= NULL
;
1610 struct sockaddr_in v4mapped
;
1612 _reach_set(reach_info
, &NOT_REACHABLE
, reach_info
->cycle
);
1614 if (address
== NULL
) {
1615 /* special case: check only for available paths off the system */
1616 goto checkAvailable
;
1619 switch (address
->sa_family
) {
1624 char if_name
[IFNAMSIZ
+ 1];
1626 _SC_sockaddr_to_string(address
, addr
, sizeof(addr
));
1628 if ((if_index
!= 0) &&
1629 (if_indextoname(if_index
, &if_name
[1]) != NULL
)) {
1635 SCLog(TRUE
, LOG_INFO
, CFSTR("%scheckAddress(%s%s)"),
1643 * if no code for this address family (yet)
1645 SCLog(TRUE
, LOG_INFO
,
1646 CFSTR("checkAddress(): unexpected address family %d"),
1647 address
->sa_family
);
1648 sc_status
= kSCStatusInvalidArgument
;
1652 if (address
->sa_family
== AF_INET6
) {
1653 /* ALIGN: sin6_addr accessed aligned, cast ok. */
1654 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)(void *)address
;
1656 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) {
1657 bzero(&v4mapped
, sizeof(v4mapped
));
1658 v4mapped
.sin_len
= sizeof(v4mapped
);
1659 v4mapped
.sin_family
= AF_INET
;
1660 v4mapped
.sin_port
= sin6
->sin6_port
;
1661 v4mapped
.sin_addr
.s_addr
= sin6
->sin6_addr
.__u6_addr
.__u6_addr32
[3];
1662 address
= (struct sockaddr
*)&v4mapped
;
1666 ret
= route_get(address
, if_index
, &info
);
1672 goto checkAvailable
;
1679 /* get the interface flags */
1681 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
1683 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
1687 bzero(&ifr
, sizeof(ifr
));
1688 bcopy(info
.sdl
->sdl_data
, ifr
.ifr_name
, info
.sdl
->sdl_nlen
);
1690 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) == -1) {
1691 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl() failed: %s"), strerror(errno
));
1695 if (!(ifr
.ifr_flags
& IFF_UP
)) {
1696 goto checkAvailable
;
1699 statusMessage
= "isReachable";
1700 reach_info
->flags
|= kSCNetworkReachabilityFlagsReachable
;
1702 if (info
.rtm
->rtm_flags
& RTF_LOCAL
) {
1703 statusMessage
= "isReachable (is a local address)";
1704 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1705 } else if (ifr
.ifr_flags
& IFF_LOOPBACK
) {
1706 statusMessage
= "isReachable (is loopback network)";
1707 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1708 } else if ((info
.rti_info
[RTAX_IFA
] != NULL
) &&
1709 (info
.rti_info
[RTAX_IFA
]->sa_family
!= AF_LINK
)) {
1710 void *addr1
= (void *)address
;
1711 void *addr2
= (void *)info
.rti_info
[RTAX_IFA
];
1712 size_t len
= address
->sa_len
;
1714 if ((address
->sa_family
!= info
.rti_info
[RTAX_IFA
]->sa_family
) &&
1715 (address
->sa_len
!= info
.rti_info
[RTAX_IFA
]->sa_len
)) {
1716 SCLog(TRUE
, LOG_NOTICE
,
1717 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
1720 info
.rti_info
[RTAX_IFA
]->sa_family
,
1721 info
.rti_info
[RTAX_IFA
]->sa_len
);
1725 switch (address
->sa_family
) {
1727 /* ALIGN: cast ok, because only bcmp is used. */
1728 addr1
= &((struct sockaddr_in
*)(void *)address
)->sin_addr
;
1729 addr2
= &((struct sockaddr_in
*)(void *)info
.rti_info
[RTAX_IFA
])->sin_addr
;
1730 len
= sizeof(struct in_addr
);
1735 /* ALIGN: sin_addr should be aligned, cast ok. */
1736 if (((struct sockaddr_in
*)(void *)address
)->sin_addr
.s_addr
== 0) {
1737 statusMessage
= "isReachable (this host)";
1738 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1742 /* ALIGN: cast ok, because only bcmp is used. */
1743 addr1
= &((struct sockaddr_in6
*)(void *)address
)->sin6_addr
;
1744 addr2
= &((struct sockaddr_in6
*)(void *)info
.rti_info
[RTAX_IFA
])->sin6_addr
;
1745 len
= sizeof(struct in6_addr
);
1751 if (bcmp(addr1
, addr2
, len
) == 0) {
1752 statusMessage
= "isReachable (is interface address)";
1753 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1757 if (!(info
.rtm
->rtm_flags
& RTF_GATEWAY
) &&
1758 (info
.rti_info
[RTAX_GATEWAY
] != NULL
) &&
1759 (info
.rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
) &&
1760 !(ifr
.ifr_flags
& IFF_POINTOPOINT
)) {
1761 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1764 bzero(&if_name
, sizeof(if_name
));
1765 bcopy(info
.sdl
->sdl_data
,
1767 (info
.sdl
->sdl_nlen
<= IFNAMSIZ
) ? info
.sdl
->sdl_nlen
: IFNAMSIZ
);
1769 strlcpy(reach_info
->if_name
, if_name
, sizeof(reach_info
->if_name
));
1770 reach_info
->if_index
= info
.sdl
->sdl_index
;
1773 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = %s"), log_prefix
, statusMessage
);
1774 SCLog(TRUE
, LOG_INFO
, CFSTR("%s device = %s (%hu)"), log_prefix
, if_name
, info
.sdl
->sdl_index
);
1775 SCLog(TRUE
, LOG_INFO
, CFSTR("%s sdl_type = 0x%x"), log_prefix
, info
.sdl
->sdl_type
);
1776 SCLog(TRUE
, LOG_INFO
, CFSTR("%s ifr_flags = 0x%04hx"), log_prefix
, ifr
.ifr_flags
);
1777 SCLog(TRUE
, LOG_INFO
, CFSTR("%s rtm_flags = 0x%08x"), log_prefix
, info
.rtm
->rtm_flags
);
1780 sc_status
= kSCStatusOK
;
1782 if (ifr
.ifr_flags
& IFF_POINTOPOINT
) {
1783 reach_info
->flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1786 if (info
.sdl
->sdl_type
== IFT_PPP
) {
1788 * 1. check if PPP service
1789 * 2. check for dial-on-demand PPP link that is not yet connected
1790 * 3. get PPP server address
1792 sc_status
= updatePPPStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1793 } else if (info
.sdl
->sdl_type
== IFT_OTHER
) {
1795 * 1. check if IPSec service
1796 * 2. get IPSec server address
1798 sc_status
= updateIPSecStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1800 #if !TARGET_IPHONE_SIMULATOR
1801 if (sc_status
== kSCStatusNoKey
) {
1803 * 1. check if VPN service
1804 * 2. get VPN server address
1806 sc_status
= updateVPNStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1808 #endif // !TARGET_IPHONE_SIMULATOR
1817 sc_status
= updatePPPAvailable(store_info
, address
, &reach_info
->flags
, log_prefix
);
1818 if ((sc_status
== kSCStatusOK
) && (reach_info
->flags
!= 0)) {
1822 #if !TARGET_IPHONE_SIMULATOR
1823 sc_status
= updateVPNAvailable(store_info
, address
, &reach_info
->flags
, log_prefix
);
1824 if ((sc_status
== kSCStatusOK
) && (reach_info
->flags
!= 0)) {
1827 #endif // !TARGET_IPHONE_SIMULATOR
1831 if (reach_info
->flags
== 0) {
1832 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s cannot be reached"), log_prefix
);
1835 if (isock
!= -1) (void)close(isock
);
1836 if (server
!= NULL
) CFRelease(server
);
1837 if ((sc_status
!= kSCStatusOK
) && (sc_status
!= kSCStatusNoKey
)) {
1838 _SCErrorSet(sc_status
);
1847 #pragma mark SCNetworkReachability APIs
1850 static __inline__ CFTypeRef
1851 isA_SCNetworkReachability(CFTypeRef obj
)
1853 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
1858 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target
)
1860 CFAllocatorRef allocator
= CFGetAllocator(target
);
1861 CFMutableStringRef str
;
1862 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1864 str
= CFStringCreateMutable(allocator
, 0);
1865 switch (targetPrivate
->type
) {
1866 case reachabilityTypeAddress
:
1867 case reachabilityTypeAddressPair
: {
1870 if (targetPrivate
->localAddress
!= NULL
) {
1871 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
1872 CFStringAppendFormat(str
, NULL
, CFSTR("local address = %s"),
1876 if (targetPrivate
->remoteAddress
!= NULL
) {
1877 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
1878 CFStringAppendFormat(str
, NULL
, CFSTR("%s%saddress = %s"),
1879 targetPrivate
->localAddress
? ", " : "",
1880 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
1885 case reachabilityTypeName
: {
1886 if ((targetPrivate
->name
!= NULL
)) {
1887 CFStringAppendFormat(str
, NULL
, CFSTR("name = %s"), targetPrivate
->name
);
1889 if ((targetPrivate
->serv
!= NULL
)) {
1890 CFStringAppendFormat(str
, NULL
, CFSTR("%sserv = %s"),
1891 targetPrivate
->name
!= NULL
? ", " : "",
1892 targetPrivate
->serv
);
1903 _SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target
)
1905 CFAllocatorRef allocator
= CFGetAllocator(target
);
1907 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1909 str
= CFStringCreateWithFormat(allocator
,
1911 CFSTR("flags = 0x%08x, if_index = %hu%s"),
1912 targetPrivate
->info
.flags
,
1913 targetPrivate
->info
.if_index
,
1914 targetPrivate
->info
.sleeping
? ", z" : "");
1920 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
1922 CFAllocatorRef allocator
= CFGetAllocator(cf
);
1923 CFMutableStringRef result
;
1925 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
1926 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1928 result
= CFStringCreateMutable(allocator
, 0);
1929 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
1931 // add target description
1932 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
1933 CFStringAppend(result
, str
);
1936 // add additional "name" info
1937 if (targetPrivate
->type
== reachabilityTypeName
) {
1938 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
1939 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
1940 } else if (targetPrivate
->dnsRetry
!= NULL
) {
1941 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS retry queued)"));
1942 } else if ((targetPrivate
->resolvedAddress
!= NULL
) || (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1943 if (targetPrivate
->resolvedAddress
!= NULL
) {
1944 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
1946 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddress
);
1948 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
1949 for (i
= 0; i
< n
; i
++) {
1952 struct sockaddr
*sa
;
1954 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
);
1955 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
1956 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
1957 CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"),
1961 } else if (CFEqual(targetPrivate
->resolvedAddress
, kCFNull
)) {
1962 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s"),
1963 gai_strerror(targetPrivate
->resolvedAddressError
));
1965 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses"));
1968 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s"),
1969 gai_strerror(targetPrivate
->resolvedAddressError
));
1971 if (targetPrivate
->llqActive
) {
1972 CFStringAppendFormat(result
, NULL
, CFSTR("), DNS llq active"));
1974 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
1976 } else if (targetPrivate
->llqActive
) {
1977 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS llq active)"));
1983 if (targetPrivate
->scheduled
) {
1984 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1985 CFStringAppendFormat(result
, NULL
, CFSTR(", %@"), str
);
1989 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
1996 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1998 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
1999 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2001 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%srelease"),
2002 targetPrivate
->log_prefix
);
2004 #ifdef HAVE_REACHABILITY_SERVER
2005 /* disconnect from the reachability server */
2007 if (targetPrivate
->serverActive
) {
2008 __SCNetworkReachabilityServer_targetRemove(target
);
2010 #endif // HAVE_REACHABILITY_SERVER
2012 /* release resources */
2014 pthread_mutex_destroy(&targetPrivate
->lock
);
2016 if (targetPrivate
->name
!= NULL
)
2017 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
2019 if (targetPrivate
->serv
!= NULL
)
2020 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->serv
);
2022 if (targetPrivate
->resolvedAddress
!= NULL
)
2023 CFRelease(targetPrivate
->resolvedAddress
);
2025 if (targetPrivate
->localAddress
!= NULL
)
2026 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
2028 if (targetPrivate
->remoteAddress
!= NULL
)
2029 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
2031 if (targetPrivate
->rlsContext
.release
!= NULL
) {
2032 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
2035 if (targetPrivate
->onDemandName
!= NULL
) {
2036 CFRelease(targetPrivate
->onDemandName
);
2039 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
2040 CFRelease(targetPrivate
->onDemandRemoteAddress
);
2043 if (targetPrivate
->onDemandServer
!= NULL
) {
2044 CFRelease(targetPrivate
->onDemandServer
);
2047 if (targetPrivate
->onDemandServiceID
!= NULL
) {
2048 CFRelease(targetPrivate
->onDemandServiceID
);
2051 #ifdef HAVE_REACHABILITY_SERVER
2052 if (targetPrivate
->serverDigest
!= NULL
) {
2053 CFRelease(targetPrivate
->serverDigest
);
2056 if (targetPrivate
->serverGroup
!= NULL
) {
2057 dispatch_release(targetPrivate
->serverGroup
);
2060 if (targetPrivate
->serverQueue
!= NULL
) {
2061 dispatch_release(targetPrivate
->serverQueue
);
2064 if (targetPrivate
->serverWatchers
!= NULL
) {
2065 CFRelease(targetPrivate
->serverWatchers
);
2067 #endif // HAVE_REACHABILITY_SERVER
2074 __SCNetworkReachabilityInitialize(void)
2076 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
2078 // provide a way to enable SCNetworkReachability logging without
2079 // having to set _sc_debug=1.
2080 if (getenv("REACH_LOGGING") != NULL
) {
2084 // set per-process "bypass" of the SCNetworkReachability server
2085 if (getenv("LONG_LIVED_QUERY_BYPASS") != NULL
) {
2089 #ifdef HAVE_REACHABILITY_SERVER
2090 // set per-process "bypass" of the SCNetworkReachability server
2091 if (getenv("REACH_SERVER_BYPASS") != NULL
) {
2092 D_serverBypass
= TRUE
;
2094 #endif // HAVE_REACHABILITY_SERVER
2096 pthread_mutexattr_init(&lock_attr
);
2097 pthread_mutexattr_settype(&lock_attr
, PTHREAD_MUTEX_ERRORCHECK
);
2105 __SCNetworkReachability_concurrent_queue()
2107 static dispatch_once_t once
;
2108 static dispatch_queue_t q
;
2110 dispatch_once(&once
, ^{
2111 q
= dispatch_queue_create("SCNetworkReachabilty.concurrent",
2112 DISPATCH_QUEUE_CONCURRENT
);
2113 dispatch_queue_set_width(q
, 32);
2121 * __SCNetworkReachabilityPerformInlineNoLock
2123 * Calls reachPerform()
2124 * - caller must be holding a reference to the target
2125 * - caller must *not* be holding the target lock
2126 * - caller must be running on the __SCNetworkReachability_concurrent_queue()
2128 static __inline__
void
2129 __SCNetworkReachabilityPerformInlineNoLock(SCNetworkReachabilityRef target
, Boolean needResolve
)
2131 dispatch_queue_t queue
;
2132 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2134 MUTEX_LOCK(&targetPrivate
->lock
);
2137 // allow the DNS query to be [re-]started
2138 targetPrivate
->needResolve
= TRUE
;
2141 queue
= targetPrivate
->dispatchQueue
;
2142 if (queue
!= NULL
) {
2143 dispatch_group_t group
;
2145 dispatch_retain(queue
);
2147 group
= targetPrivate
->dispatchGroup
;
2148 dispatch_group_enter(group
);
2150 MUTEX_UNLOCK(&targetPrivate
->lock
);
2152 dispatch_sync(queue
, ^{
2153 reachPerform((void *)target
);
2154 dispatch_group_leave(group
);
2156 dispatch_release(queue
);
2158 if (targetPrivate
->rls
!= NULL
) {
2159 CFRunLoopSourceSignal(targetPrivate
->rls
);
2160 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2163 MUTEX_UNLOCK(&targetPrivate
->lock
);
2170 #ifdef HAVE_REACHABILITY_SERVER
2172 * __SCNetworkReachabilityPerformNoLock
2174 * Calls reachPerform()
2175 * - caller must *not* be holding the target lock
2176 * - caller must *not* running on the __SCNetworkReachability_concurrent_queue()
2180 __SCNetworkReachabilityPerformNoLock(SCNetworkReachabilityRef target
)
2183 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
2184 __SCNetworkReachabilityPerformInlineNoLock(target
, FALSE
);
2190 #endif // HAVE_REACHABILITY_SERVER
2194 * __SCNetworkReachabilityPerformConcurrent
2196 * Calls reachPerform()
2197 * - caller must be holding the target lock
2198 * - caller running on the __SCNetworkReachability_concurrent_queue()
2200 static __inline__
void
2201 __SCNetworkReachabilityPerformConcurrent(SCNetworkReachabilityRef target
)
2203 dispatch_queue_t queue
;
2204 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2206 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2208 queue
= targetPrivate
->dispatchQueue
;
2209 if (queue
!= NULL
) {
2211 dispatch_group_async(targetPrivate
->dispatchGroup
, queue
, ^{
2212 reachPerform((void *)target
);
2216 if (targetPrivate
->rls
!= NULL
) {
2217 CFRunLoopSourceSignal(targetPrivate
->rls
);
2218 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2227 * __SCNetworkReachabilityPerform
2229 * Calls reachPerform()
2230 * - caller must be holding the target lock
2231 * - caller not running on the __SCNetworkReachability_concurrent_queue()
2234 __SCNetworkReachabilityPerform(SCNetworkReachabilityRef target
)
2236 dispatch_queue_t queue
;
2237 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2239 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2241 queue
= targetPrivate
->dispatchQueue
;
2242 if (queue
!= NULL
) {
2243 dispatch_retain(queue
);
2245 dispatch_group_async(targetPrivate
->dispatchGroup
, __SCNetworkReachability_concurrent_queue(), ^{
2246 dispatch_sync(queue
, ^{
2247 reachPerform((void *)target
);
2250 dispatch_release(queue
);
2252 } else if (targetPrivate
->rls
!= NULL
) {
2253 CFRunLoopSourceSignal(targetPrivate
->rls
);
2254 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2261 static SCNetworkReachabilityPrivateRef
2262 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
2264 SCNetworkReachabilityPrivateRef targetPrivate
;
2267 /* initialize runtime */
2268 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
2270 /* allocate target */
2271 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
2272 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
2273 __kSCNetworkReachabilityTypeID
,
2276 if (targetPrivate
== NULL
) {
2280 MUTEX_INIT(&targetPrivate
->lock
);
2282 targetPrivate
->name
= NULL
;
2283 targetPrivate
->serv
= NULL
;
2284 targetPrivate
->hints
= HINTS_DEFAULT
;
2285 targetPrivate
->needResolve
= FALSE
;
2286 targetPrivate
->resolvedAddress
= NULL
;
2287 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
2289 targetPrivate
->if_index
= 0;
2291 targetPrivate
->localAddress
= NULL
;
2292 targetPrivate
->remoteAddress
= NULL
;
2294 targetPrivate
->cycle
= 1;
2295 targetPrivate
->info
= NOT_REACHABLE
;
2296 targetPrivate
->last_notify
= NOT_REPORTED
;
2298 targetPrivate
->scheduled
= FALSE
;
2299 targetPrivate
->rls
= NULL
;
2300 targetPrivate
->rlsFunction
= NULL
;
2301 targetPrivate
->rlsContext
.info
= NULL
;
2302 targetPrivate
->rlsContext
.retain
= NULL
;
2303 targetPrivate
->rlsContext
.release
= NULL
;
2304 targetPrivate
->rlsContext
.copyDescription
= NULL
;
2305 targetPrivate
->rlList
= NULL
;
2307 targetPrivate
->haveDNS
= FALSE
;
2308 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
2309 targetPrivate
->dnsPort
= NULL
;
2310 targetPrivate
->dnsRLS
= NULL
;
2311 targetPrivate
->dnsSource
= NULL
;
2312 targetPrivate
->dnsQueryStart
= TIME_ZERO
;
2313 targetPrivate
->dnsQueryEnd
= TIME_ZERO
;
2314 targetPrivate
->dnsRetry
= NULL
;
2315 targetPrivate
->dnsRetryCount
= 0;
2317 targetPrivate
->last_dns
= TIME_ZERO
;
2318 targetPrivate
->last_network
= TIME_ZERO
;
2319 #if !TARGET_OS_IPHONE
2320 targetPrivate
->last_power
= TIME_ZERO
;
2321 #endif // !TARGET_OS_IPHONE
2322 targetPrivate
->last_push
= TIME_ZERO
;
2324 targetPrivate
->onDemandBypass
= FALSE
;
2325 targetPrivate
->onDemandName
= NULL
;
2326 targetPrivate
->onDemandRemoteAddress
= NULL
;
2327 targetPrivate
->onDemandServer
= NULL
;
2328 targetPrivate
->onDemandServiceID
= NULL
;
2330 #ifdef HAVE_REACHABILITY_SERVER
2331 targetPrivate
->serverBypass
= D_serverBypass
;
2332 #endif // HAVE_REACHABILITY_SERVER
2335 targetPrivate
->llqActive
= FALSE
;
2336 targetPrivate
->llqBypass
= D_llqBypass
;
2337 targetPrivate
->llqTarget
= NULL
;
2338 targetPrivate
->llqTimer
= NULL
;
2340 #ifdef HAVE_REACHABILITY_SERVER
2341 targetPrivate
->serverActive
= FALSE
;
2342 targetPrivate
->serverScheduled
= FALSE
;
2343 targetPrivate
->serverInfo
= NOT_REACHABLE
;
2345 targetPrivate
->serverDigest
= NULL
;
2346 targetPrivate
->serverGroup
= NULL
;
2347 targetPrivate
->serverInfoValid
= FALSE
;
2348 targetPrivate
->serverQueryActive
= 0;
2349 targetPrivate
->serverQueue
= NULL
;
2350 targetPrivate
->serverReferences
= 0;
2351 targetPrivate
->serverWatchers
= NULL
;
2352 #endif // HAVE_REACHABILITY_SERVER
2354 targetPrivate
->log_prefix
[0] = '\0';
2356 snprintf(targetPrivate
->log_prefix
,
2357 sizeof(targetPrivate
->log_prefix
),
2362 return targetPrivate
;
2368 static const struct sockaddr
*
2369 is_valid_address(const struct sockaddr
*address
)
2371 const struct sockaddr
*valid
= NULL
;
2372 static Boolean warned
= FALSE
;
2374 if ((address
!= NULL
) &&
2375 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
2376 switch (address
->sa_family
) {
2378 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
2382 SCLog(TRUE
, LOG_ERR
,
2383 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
2385 sizeof(struct sockaddr_in
));
2391 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
2393 } else if (!warned
) {
2394 SCLog(TRUE
, LOG_ERR
,
2395 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
2397 sizeof(struct sockaddr_in6
));
2403 SCLog(TRUE
, LOG_ERR
,
2404 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"),
2405 address
->sa_family
);
2415 SCNetworkReachabilityRef
2416 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
2417 const struct sockaddr
*address
)
2419 SCNetworkReachabilityPrivateRef targetPrivate
;
2421 address
= is_valid_address(address
);
2422 if (address
== NULL
) {
2423 _SCErrorSet(kSCStatusInvalidArgument
);
2427 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2428 if (targetPrivate
== NULL
) {
2432 targetPrivate
->type
= reachabilityTypeAddress
;
2433 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
2434 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
2436 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
2437 targetPrivate
->log_prefix
,
2438 DEBUG_REACHABILITY_TYPE_ADDRESS
,
2441 return (SCNetworkReachabilityRef
)targetPrivate
;
2445 SCNetworkReachabilityRef
2446 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
2447 const struct sockaddr
*localAddress
,
2448 const struct sockaddr
*remoteAddress
)
2450 SCNetworkReachabilityPrivateRef targetPrivate
;
2452 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
2453 _SCErrorSet(kSCStatusInvalidArgument
);
2457 if (localAddress
!= NULL
) {
2458 localAddress
= is_valid_address(localAddress
);
2459 if (localAddress
== NULL
) {
2460 _SCErrorSet(kSCStatusInvalidArgument
);
2465 if (remoteAddress
!= NULL
) {
2466 remoteAddress
= is_valid_address(remoteAddress
);
2467 if (remoteAddress
== NULL
) {
2468 _SCErrorSet(kSCStatusInvalidArgument
);
2473 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2474 if (targetPrivate
== NULL
) {
2478 targetPrivate
->type
= reachabilityTypeAddressPair
;
2480 if (localAddress
!= NULL
) {
2481 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
2482 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
2485 if (remoteAddress
!= NULL
) {
2486 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
2487 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
2490 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
2491 targetPrivate
->log_prefix
,
2492 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR
,
2495 return (SCNetworkReachabilityRef
)targetPrivate
;
2499 SCNetworkReachabilityRef
2500 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
2501 const char *nodename
)
2505 struct sockaddr_in sin
;
2506 struct sockaddr_in6 sin6
;
2509 SCNetworkReachabilityPrivateRef targetPrivate
;
2511 if (nodename
== NULL
) {
2512 _SCErrorSet(kSCStatusInvalidArgument
);
2516 nodenameLen
= strlen(nodename
);
2517 if (nodenameLen
== 0) {
2518 _SCErrorSet(kSCStatusInvalidArgument
);
2522 if (_SC_string_to_sockaddr(nodename
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) != NULL
) {
2523 /* if this "nodename" is really an IP[v6] address in disguise */
2524 return SCNetworkReachabilityCreateWithAddress(allocator
, &addr
.sa
);
2527 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2528 if (targetPrivate
== NULL
) {
2532 targetPrivate
->type
= reachabilityTypeName
;
2534 targetPrivate
->name
= CFAllocatorAllocate(NULL
, nodenameLen
+ 1, 0);
2535 strlcpy((char *)targetPrivate
->name
, nodename
, nodenameLen
+ 1);
2537 targetPrivate
->needResolve
= TRUE
;
2538 targetPrivate
->info
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
2539 #ifdef HAVE_REACHABILITY_SERVER
2540 targetPrivate
->serverInfo
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
2541 #endif // HAVE_REACHABILITY_SERVER
2543 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
2544 targetPrivate
->log_prefix
,
2545 DEBUG_REACHABILITY_TYPE_NAME
,
2548 return (SCNetworkReachabilityRef
)targetPrivate
;
2554 SCNetworkReachabilityRef
2555 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
2556 CFDictionaryRef options
)
2558 const struct sockaddr
*addr_l
= NULL
;
2559 const struct sockaddr
*addr_r
= NULL
;
2561 struct addrinfo
*hints
= NULL
;
2562 CFStringRef interface
= NULL
;
2563 CFBooleanRef llqBypass
;
2564 CFStringRef nodename
;
2565 CFBooleanRef onDemandBypass
;
2566 CFBooleanRef resolverBypass
;
2567 #ifdef HAVE_REACHABILITY_SERVER
2568 CFBooleanRef serverBypass
;
2569 #endif // HAVE_REACHABILITY_SERVER
2570 CFStringRef servname
;
2571 SCNetworkReachabilityRef target
;
2572 SCNetworkReachabilityPrivateRef targetPrivate
;
2574 if (!isA_CFDictionary(options
)) {
2575 _SCErrorSet(kSCStatusInvalidArgument
);
2579 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
2580 if ((nodename
!= NULL
) &&
2581 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
2582 _SCErrorSet(kSCStatusInvalidArgument
);
2585 servname
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServName
);
2586 if ((servname
!= NULL
) &&
2587 (!isA_CFString(servname
) || (CFStringGetLength(servname
) == 0))) {
2588 _SCErrorSet(kSCStatusInvalidArgument
);
2591 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
2593 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2594 _SCErrorSet(kSCStatusInvalidArgument
);
2597 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2599 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
2601 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2602 _SCErrorSet(kSCStatusInvalidArgument
);
2605 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2607 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionHints
);
2609 if (!isA_CFData(data
) || (CFDataGetLength(data
) != sizeof(targetPrivate
->hints
))) {
2610 _SCErrorSet(kSCStatusInvalidArgument
);
2614 /* ALIGN: CF aligns to >8 byte boundries */
2615 hints
= (struct addrinfo
*)(void *)CFDataGetBytePtr(data
);
2616 if ((hints
->ai_addrlen
!= 0) ||
2617 (hints
->ai_addr
!= NULL
) ||
2618 (hints
->ai_canonname
!= NULL
) ||
2619 (hints
->ai_next
!= NULL
)) {
2620 _SCErrorSet(kSCStatusInvalidArgument
);
2624 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
2625 if ((interface
!= NULL
) &&
2626 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
2627 _SCErrorSet(kSCStatusInvalidArgument
);
2630 onDemandBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandBypass
);
2631 if ((onDemandBypass
!= NULL
) && !isA_CFBoolean(onDemandBypass
)) {
2632 _SCErrorSet(kSCStatusInvalidArgument
);
2635 resolverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionResolverBypass
);
2636 if ((resolverBypass
!= NULL
) && !isA_CFBoolean(resolverBypass
)) {
2637 _SCErrorSet(kSCStatusInvalidArgument
);
2642 llqBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLongLivedQueryBypass
);
2643 if ((llqBypass
!= NULL
) && !isA_CFBoolean(llqBypass
)) {
2644 _SCErrorSet(kSCStatusInvalidArgument
);
2648 #ifdef HAVE_REACHABILITY_SERVER
2649 serverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServerBypass
);
2650 if ((serverBypass
!= NULL
) && !isA_CFBoolean(serverBypass
)) {
2651 _SCErrorSet(kSCStatusInvalidArgument
);
2654 #endif // HAVE_REACHABILITY_SERVER
2656 if ((nodename
!= NULL
) || (servname
!= NULL
)) {
2659 if ((addr_l
!= NULL
) || (addr_r
!= NULL
)) {
2660 // can't have both a name/serv and an address
2661 _SCErrorSet(kSCStatusInvalidArgument
);
2665 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
2666 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
2667 CFAllocatorDeallocate(NULL
, (void *)name
);
2669 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
2670 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
2671 } else if (addr_r
!= NULL
) {
2672 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
2673 } else if (addr_l
!= NULL
) {
2674 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_l
);
2676 _SCErrorSet(kSCStatusInvalidArgument
);
2680 if (target
== NULL
) {
2684 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2685 if (targetPrivate
->type
== reachabilityTypeName
) {
2686 if (servname
!= NULL
) {
2687 targetPrivate
->serv
= _SC_cfstring_to_cstring(servname
, NULL
, 0, kCFStringEncodingUTF8
);
2689 if (hints
!= NULL
) {
2690 bcopy(hints
, &targetPrivate
->hints
, sizeof(targetPrivate
->hints
));
2694 if (interface
!= NULL
) {
2695 if ((_SC_cfstring_to_cstring(interface
,
2696 targetPrivate
->if_name
,
2697 sizeof(targetPrivate
->if_name
),
2698 kCFStringEncodingASCII
) == NULL
) ||
2699 ((targetPrivate
->if_index
= if_nametoindex(targetPrivate
->if_name
)) == 0)) {
2700 CFRelease(targetPrivate
);
2701 _SCErrorSet(kSCStatusInvalidArgument
);
2707 if (llqBypass
!= NULL
) {
2708 targetPrivate
->llqBypass
= CFBooleanGetValue(llqBypass
);
2711 if (onDemandBypass
!= NULL
) {
2712 targetPrivate
->onDemandBypass
= CFBooleanGetValue(onDemandBypass
);
2715 if (resolverBypass
!= NULL
) {
2716 targetPrivate
->resolverBypass
= CFBooleanGetValue(resolverBypass
);
2719 #ifdef HAVE_REACHABILITY_SERVER
2720 if (serverBypass
!= NULL
) {
2721 targetPrivate
->serverBypass
= CFBooleanGetValue(serverBypass
);
2723 #endif // HAVE_REACHABILITY_SERVER
2725 if (_sc_debug
&& (_sc_log
> 0)) {
2728 switch (targetPrivate
->type
) {
2729 case reachabilityTypeName
:
2730 opt
= DEBUG_REACHABILITY_TYPE_NAME_OPTIONS
;
2732 case reachabilityTypeAddress
:
2733 opt
= DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS
;
2735 case reachabilityTypeAddressPair
:
2736 opt
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS
;
2743 SCLog(TRUE
, LOG_INFO
, CFSTR("%s%s %@"),
2744 targetPrivate
->log_prefix
,
2749 return (SCNetworkReachabilityRef
)targetPrivate
;
2754 SCNetworkReachabilityGetTypeID(void)
2756 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
2757 return __kSCNetworkReachabilityTypeID
;
2761 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
2762 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
2765 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2767 if (!isA_SCNetworkReachability(target
)) {
2768 _SCErrorSet(kSCStatusInvalidArgument
);
2772 if (targetPrivate
->type
!= reachabilityTypeName
) {
2773 _SCErrorSet(kSCStatusInvalidArgument
);
2778 *error_num
= targetPrivate
->resolvedAddressError
;
2781 if (targetPrivate
->resolvedAddress
!= NULL
) {
2782 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
2783 return CFRetain(targetPrivate
->resolvedAddress
);
2785 /* if status is known but no resolved addresses to return */
2786 _SCErrorSet(kSCStatusOK
);
2791 _SCErrorSet(kSCStatusReachabilityUnknown
);
2797 __SCNetworkReachabilitySetResolvedAddress(int32_t status
,
2798 struct addrinfo
*res
,
2799 SCNetworkReachabilityRef target
)
2801 struct addrinfo
*resP
;
2802 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2804 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2806 if (targetPrivate
->resolvedAddress
!= NULL
) {
2807 CFRelease(targetPrivate
->resolvedAddress
);
2808 targetPrivate
->resolvedAddress
= NULL
;
2811 if ((status
== 0) && (res
!= NULL
)) {
2812 CFMutableArrayRef addresses
;
2814 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2816 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
2818 CFDataRef newAddress
;
2820 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
2821 n
= CFArrayGetCount(addresses
);
2823 !CFArrayContainsValue(addresses
, CFRangeMake(0, n
), newAddress
)) {
2824 CFArrayAppendValue(addresses
, newAddress
);
2826 CFRelease(newAddress
);
2829 /* save the resolved address[es] */
2830 targetPrivate
->resolvedAddress
= addresses
;
2831 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
2833 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sgetaddrinfo() failed: %s"),
2834 targetPrivate
->log_prefix
,
2835 gai_strerror(status
));
2837 /* save the error associated with the attempt to resolve the name */
2838 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
2839 targetPrivate
->resolvedAddressError
= status
;
2841 targetPrivate
->needResolve
= FALSE
;
2843 if (res
!= NULL
) freeaddrinfo(res
);
2845 if (targetPrivate
->scheduled
) {
2846 __SCNetworkReachabilityPerform(target
);
2854 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo
*res
, void *context
)
2856 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
2857 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2859 __dns_query_end(target
,
2860 ((status
== 0) && (res
!= NULL
)), // if successful query
2861 dns_query_async
, // async
2862 &targetPrivate
->dnsQueryStart
, // start time
2863 &targetPrivate
->dnsQueryEnd
); // end time
2865 __SCNetworkReachabilitySetResolvedAddress(status
, res
, target
);
2871 * rankReachability()
2872 * Not reachable == 0
2873 * Connection Required == 1
2877 rankReachability(SCNetworkReachabilityFlags flags
)
2881 if (flags
& kSCNetworkReachabilityFlagsReachable
) rank
= 2;
2882 if (flags
& kSCNetworkReachabilityFlagsConnectionRequired
) rank
= 1;
2888 #pragma mark DNS name resolution
2892 replyMPCopyDescription(const void *info
)
2894 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2895 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2897 return CFStringCreateWithFormat(NULL
,
2899 CFSTR("<getaddrinfo_async_start reply MP> {%s%s%s%s%s, target = %p}"),
2900 targetPrivate
->name
!= NULL
? "name = " : "",
2901 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
2902 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
2903 targetPrivate
->serv
!= NULL
? "serv = " : "",
2904 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "",
2910 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
);
2914 enqueueAsyncDNSQuery_dispatch(SCNetworkReachabilityRef target
)
2917 dispatch_source_t source
;
2918 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2920 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2922 mp
= targetPrivate
->dnsMP
;
2924 // mach_port context <-- NULL (no msg received)
2925 mach_port_set_context(mach_task_self(), mp
, (mach_vm_address_t
)(uintptr_t)NULL
);
2927 // create dispatch source to handle DNS reply
2928 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
,
2931 __SCNetworkReachability_concurrent_queue());
2932 if (source
== NULL
) {
2933 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_source_create() failed"));
2938 // We created the dispatch_source to listen for (and process) the mach IPC
2939 // reply to our async DNS query. Because the source handler runs asychronously
2940 // we need to ensure that we're holding a reference to the target. Here, we take
2941 // a reference and setup the dispatch_source finalizer to drop it.
2944 dispatch_set_context(source
, (void *)target
);
2945 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
2947 dispatch_source_set_event_handler(source
, ^{
2948 mach_msg_size_t msg_size
= 8192;
2949 const mach_msg_options_t options
= MACH_RCV_MSG
2951 | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX
)
2952 | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
);
2956 mach_msg_header_t
*msg
= (mach_msg_header_t
*)malloc(msg_size
);
2958 kr
= mach_msg(msg
, /* msg */
2959 options
, /* options */
2961 msg_size
, /* rcv_size */
2963 MACH_MSG_TIMEOUT_NONE
, /* timeout */
2964 MACH_PORT_NULL
); /* notify */
2965 if (kr
== KERN_SUCCESS
) {
2966 // mach_port context <-- msg
2967 mach_port_set_context(mach_task_self(),
2969 (mach_vm_address_t
)(uintptr_t)msg
);
2970 } else if (kr
== MACH_RCV_TOO_LARGE
) {
2975 SCLog(TRUE
, LOG_ERR
,
2976 CFSTR("SCNetworkReachability async DNS handler, kr=0x%x"),
2983 dispatch_source_cancel(source
);
2986 dispatch_source_set_cancel_handler(source
, ^{
2987 #if !TARGET_OS_EMBEDDED
2988 mach_vm_address_t context
;
2989 #else // !TARGET_OS_EMBEDDED
2990 mach_port_context_t context
;
2991 #endif // !TARGET_OS_EMBEDDED
2995 // get the [async DNS query] mach port
2996 mp
= (mach_port_t
)dispatch_source_get_handle(source
);
2998 // check if we have a received message
2999 kr
= mach_port_get_context(mach_task_self(), mp
, &context
);
3000 if (kr
== KERN_SUCCESS
) {
3003 msg
= (void *)(uintptr_t)context
;
3005 MUTEX_LOCK(&targetPrivate
->lock
);
3006 getaddrinfo_async_handle_reply(msg
);
3007 targetPrivate
->dnsSource
= NULL
;
3008 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
3009 MUTEX_UNLOCK(&targetPrivate
->lock
);
3012 getaddrinfo_async_cancel(mp
);
3016 dispatch_release(source
);
3019 targetPrivate
->dnsSource
= source
;
3020 dispatch_resume(source
);
3027 enqueueAsyncDNSQuery_CF(SCNetworkReachabilityRef target
)
3029 CFMachPortContext context
= { 0
3033 , replyMPCopyDescription
3038 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3040 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3042 mp
= targetPrivate
->dnsMP
;
3044 targetPrivate
->dnsPort
= _SC_CFMachPortCreateWithPort("SCNetworkReachability",
3046 getaddrinfo_async_handleCFReply
,
3048 if (targetPrivate
->dnsPort
== NULL
) {
3049 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability CFMachPortCreateWithPort() failed"));
3053 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
3054 if (targetPrivate
->dnsRLS
== NULL
) {
3055 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability CFMachPortCreateRunLoopSource() failed"));
3059 n
= CFArrayGetCount(targetPrivate
->rlList
);
3060 for (i
= 0; i
< n
; i
+= 3) {
3061 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
3062 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
3064 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
3071 if (targetPrivate
->dnsRLS
!= NULL
) {
3072 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
3073 CFRelease(targetPrivate
->dnsRLS
);
3074 targetPrivate
->dnsRLS
= NULL
;
3076 if (targetPrivate
->dnsPort
!= NULL
) {
3077 CFMachPortInvalidate(targetPrivate
->dnsPort
);
3078 CFRelease(targetPrivate
->dnsPort
);
3079 targetPrivate
->dnsPort
= NULL
;
3087 enqueueAsyncDNSQuery(SCNetworkReachabilityRef target
, mach_port_t mp
)
3090 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3092 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3094 targetPrivate
->dnsMP
= mp
;
3096 if (targetPrivate
->dispatchQueue
!= NULL
) {
3097 ok
= enqueueAsyncDNSQuery_dispatch(target
);
3098 } else if (targetPrivate
->rls
!= NULL
) {
3099 ok
= enqueueAsyncDNSQuery_CF(target
);
3103 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
3104 _SCErrorSet(kSCStatusFailed
);
3113 dequeueAsyncDNSQuery(SCNetworkReachabilityRef target
, Boolean cancel
)
3115 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3117 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3119 if (targetPrivate
->dnsPort
!= NULL
) {
3120 CFMachPortInvalidate(targetPrivate
->dnsPort
);
3121 CFRelease(targetPrivate
->dnsPort
);
3122 targetPrivate
->dnsPort
= NULL
;
3125 if (targetPrivate
->dnsRLS
!= NULL
) {
3126 CFRelease(targetPrivate
->dnsRLS
);
3127 targetPrivate
->dnsRLS
= NULL
;
3130 if (targetPrivate
->dnsSource
!= NULL
) {
3131 dispatch_source_cancel(targetPrivate
->dnsSource
);
3132 targetPrivate
->dnsSource
= NULL
;
3133 cancel
= FALSE
; // the cancellation handler does the work
3136 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
3138 getaddrinfo_async_cancel(targetPrivate
->dnsMP
);
3140 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
3148 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
3150 mach_port_t mp
= CFMachPortGetPort(port
);
3152 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
3153 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3155 MUTEX_LOCK(&targetPrivate
->lock
);
3157 if (mp
!= targetPrivate
->dnsMP
) {
3158 // we've received a callback on the async DNS port but since the
3159 // associated CFMachPort doesn't match than the request must have
3160 // already been cancelled.
3161 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP"));
3162 MUTEX_UNLOCK(&targetPrivate
->lock
);
3166 dequeueAsyncDNSQuery(target
, FALSE
);
3167 status
= getaddrinfo_async_handle_reply(msg
);
3168 if ((status
== 0) &&
3169 (targetPrivate
->resolvedAddress
== NULL
) && (targetPrivate
->resolvedAddressError
== NETDB_SUCCESS
)) {
3172 // if the request is not complete and needs to be re-queued
3173 ok
= enqueueAsyncDNSQuery(target
, mp
);
3175 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply enqueueAsyncDNSQuery() failed"));
3179 MUTEX_UNLOCK(&targetPrivate
->lock
);
3186 check_resolver_reachability(ReachabilityStoreInfoRef store_info
,
3187 dns_resolver_t
*resolver
,
3188 SCNetworkReachabilityFlags
*flags
,
3190 uint32_t *resolver_if_index
,
3191 const char *log_prefix
)
3195 if (resolver_if_index
) *resolver_if_index
= 0;
3197 if (resolver
->n_nameserver
> 0) {
3198 #if !TARGET_IPHONE_SIMULATOR
3199 *flags
= (SCNetworkReachabilityFlags
)resolver
->reach_flags
;
3200 if (resolver_if_index
!= NULL
) {
3201 *resolver_if_index
= resolver
->if_index
;
3203 #else // !TARGET_IPHONE_SIMULATOR
3206 *flags
= kSCNetworkReachabilityFlagsReachable
;
3208 for (i
= 0; i
< resolver
->n_nameserver
; i
++) {
3209 struct sockaddr
*address
= resolver
->nameserver
[i
];
3210 ReachabilityInfo ns_info
;
3212 ok
= checkAddress(store_info
, address
, resolver
->if_index
, &ns_info
, log_prefix
);
3219 (rankReachability(ns_info
.flags
) < rankReachability(*flags
))) {
3220 /* return the worst case result */
3221 *flags
= ns_info
.flags
;
3222 if (resolver_if_index
!= NULL
) {
3223 *resolver_if_index
= ns_info
.if_index
;
3227 #endif // !TARGET_IPHONE_SIMULATOR
3230 *flags
= kSCNetworkReachabilityFlagsReachable
;
3239 check_matching_resolvers(ReachabilityStoreInfoRef store_info
,
3240 dns_config_t
*dns_config
,
3242 unsigned int if_index
,
3243 SCNetworkReachabilityFlags
*flags
,
3245 uint32_t *resolver_if_index
,
3246 int *dns_config_index
,
3247 const char *log_prefix
)
3250 Boolean matched
= FALSE
;
3251 const char *name
= fqdn
;
3252 int32_t n_resolvers
;
3253 dns_resolver_t
**resolvers
;
3255 if (if_index
== 0) {
3256 n_resolvers
= dns_config
->n_resolver
;
3257 resolvers
= dns_config
->resolver
;
3259 n_resolvers
= dns_config
->n_scoped_resolver
;
3260 resolvers
= dns_config
->scoped_resolver
;
3263 /* In case we couldn't find a match, setting an index of -1
3264 and resolver_if_index 0 */
3265 if (dns_config_index
!= NULL
) *dns_config_index
= -1;
3266 if (resolver_if_index
!= NULL
) *resolver_if_index
= 0;
3268 while (!matched
&& (name
!= NULL
)) {
3272 * check if the provided name (or sub-component)
3273 * matches one of our resolver configurations.
3276 for (i
= 0; i
< n_resolvers
; i
++) {
3278 dns_resolver_t
*resolver
;
3280 resolver
= resolvers
[i
];
3281 if ((if_index
!= 0) && (if_index
!= resolver
->if_index
)) {
3285 domain
= resolver
->domain
;
3286 if (domain
!= NULL
&& (len
== strlen(domain
))) {
3287 if (strcasecmp(name
, domain
) == 0) {
3291 * if name matches domain
3294 ok
= check_resolver_reachability(store_info
, resolver
, flags
, haveDNS
,
3295 resolver_if_index
, log_prefix
);
3300 if (dns_config_index
!= NULL
) *dns_config_index
= i
;
3307 * we have not found a matching resolver, try
3308 * a less qualified domain
3310 name
= strchr(name
, '.');
3311 if ((name
!= NULL
) && (*name
!= '\0')) {
3323 static dns_resolver_t
*
3324 get_default_resolver(dns_config_t
*dns_config
, unsigned int if_index
)
3327 int32_t n_resolvers
;
3328 dns_resolver_t
*resolver
= NULL
;
3329 dns_resolver_t
**resolvers
;
3331 if (if_index
== 0) {
3332 n_resolvers
= dns_config
->n_resolver
;
3333 resolvers
= dns_config
->resolver
;
3335 n_resolvers
= dns_config
->n_scoped_resolver
;
3336 resolvers
= dns_config
->scoped_resolver
;
3339 for (i
= 0; i
< n_resolvers
; i
++) {
3340 if ((if_index
!= 0) && (if_index
!= resolvers
[i
]->if_index
)) {
3344 if (((if_index
== 0) && (i
== 0)) ||
3345 ((if_index
!= 0) && (resolver
== NULL
))) {
3346 // if this is the first (aka default) resolver
3347 resolver
= resolvers
[i
];
3348 } else if ((resolvers
[i
]->domain
== NULL
) &&
3349 (resolvers
[i
]->search_order
< resolver
->search_order
)) {
3350 // if this is a default resolver with a lower search order
3351 resolver
= resolvers
[i
];
3359 static dns_configuration_t
*
3360 dns_configuration_retain()
3362 pthread_mutex_lock(&dns_lock
);
3364 if ((dns_configuration
!= NULL
) && dns_token_valid
) {
3369 * check if the global [DNS] configuration snapshot needs
3372 status
= notify_check(dns_token
, &check
);
3373 if (status
!= NOTIFY_STATUS_OK
) {
3374 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
3377 if ((status
!= NOTIFY_STATUS_OK
) || (check
!= 0)) {
3379 * if the snapshot needs to be refreshed
3381 if (dns_configuration
->refs
== 0) {
3382 dns_configuration_free(dns_configuration
->config
);
3383 CFAllocatorDeallocate(NULL
, dns_configuration
);
3385 dns_configuration
= NULL
;
3389 if (dns_configuration
== NULL
) {
3390 dns_config_t
*new_config
;
3392 new_config
= dns_configuration_copy();
3393 if (new_config
!= NULL
) {
3394 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
3395 dns_configuration
->config
= new_config
;
3396 dns_configuration
->refs
= 0;
3400 if (dns_configuration
!= NULL
) {
3401 dns_configuration
->refs
++;
3404 pthread_mutex_unlock(&dns_lock
);
3405 return dns_configuration
;
3410 dns_configuration_release(dns_configuration_t
*config
)
3412 pthread_mutex_lock(&dns_lock
);
3415 if (config
->refs
== 0) {
3416 if ((dns_configuration
!= config
)) {
3417 dns_configuration_free(config
->config
);
3418 CFAllocatorDeallocate(NULL
, config
);
3422 pthread_mutex_unlock(&dns_lock
);
3428 dns_configuration_watch()
3431 const char *dns_key
;
3435 pthread_mutex_lock(&dns_lock
);
3437 dns_key
= dns_configuration_notify_key();
3438 if (dns_key
== NULL
) {
3439 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
3443 status
= notify_register_check(dns_key
, &dns_token
);
3444 if (status
== NOTIFY_STATUS_OK
) {
3445 dns_token_valid
= TRUE
;
3447 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
);
3451 status
= notify_check(dns_token
, &dns_check
);
3452 if (status
!= NOTIFY_STATUS_OK
) {
3453 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
3454 (void)notify_cancel(dns_token
);
3455 dns_token_valid
= FALSE
;
3463 pthread_mutex_unlock(&dns_lock
);
3469 dns_configuration_unwatch()
3471 pthread_mutex_lock(&dns_lock
);
3473 (void)notify_cancel(dns_token
);
3474 dns_token_valid
= FALSE
;
3476 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
3477 dns_configuration_free(dns_configuration
->config
);
3478 CFAllocatorDeallocate(NULL
, dns_configuration
);
3479 dns_configuration
= NULL
;
3482 pthread_mutex_unlock(&dns_lock
);
3488 _SC_R_checkResolverReachability(ReachabilityStoreInfoRef store_info
,
3489 SCNetworkReachabilityFlags
*flags
,
3491 const char *nodename
,
3492 const char *servname
,
3493 unsigned int if_index
,
3494 uint32_t *resolver_if_index
,
3495 int *dns_config_index
,
3496 const char *log_prefix
3499 dns_resolver_t
*default_resolver
;
3500 dns_configuration_t
*dns
;
3501 Boolean found
= FALSE
;
3502 char *fqdn
= (char *)nodename
;
3504 Boolean isFQDN
= FALSE
;
3508 Boolean useDefault
= FALSE
;
3510 if (resolver_if_index
) *resolver_if_index
= 0;
3511 if (dns_config_index
) *dns_config_index
= -1;
3514 * We first assume that all of the configured DNS servers
3515 * are available. Since we don't know which name server will
3516 * be consulted to resolve the specified nodename we need to
3517 * check the availability of ALL name servers. We can only
3518 * proceed if we know that our query can be answered.
3521 *flags
= kSCNetworkReachabilityFlagsReachable
;
3524 len
= (nodename
!= NULL
) ? strlen(nodename
) : 0;
3526 if ((servname
== NULL
) || (strlen(servname
) == 0)) {
3527 // if no nodename or servname, return not reachable
3533 dns
= dns_configuration_retain();
3536 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no configuration"), log_prefix
);
3540 if (dns
->config
->n_resolver
== 0) {
3541 // if no resolver configuration
3542 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no resolvers"), log_prefix
);
3546 if (fqdn
[len
- 1] == '.') {
3549 // trim trailing '.''s
3550 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
3551 if (fqdn
== nodename
) {
3552 fqdn
= strdup(nodename
);
3558 default_resolver
= get_default_resolver(dns
->config
, if_index
);
3561 * check if the provided name matches a supplemental domain
3563 found
= check_matching_resolvers(store_info
, dns
->config
, fqdn
, if_index
,
3564 flags
, haveDNS
, resolver_if_index
,
3565 dns_config_index
, log_prefix
);
3567 if (!found
&& !isFQDN
) {
3569 * if we did not match a supplemental domain name and if the
3570 * provided name has enough "."s then the first query will be
3571 * directed to the default resolver.
3576 #define NDOTS_OPT "ndots="
3577 #define NDOTS_OPT_LEN (sizeof("ndots=") - 1)
3579 if (default_resolver
->options
!= NULL
) {
3580 cp
= strstr(default_resolver
->options
, NDOTS_OPT
);
3582 ((cp
== default_resolver
->options
) || isspace(cp
[-1])) &&
3583 ((cp
[NDOTS_OPT_LEN
] != '\0') && isdigit(cp
[NDOTS_OPT_LEN
]))) {
3587 cp
+= NDOTS_OPT_LEN
;
3589 val
= strtol(cp
, &end
, 10);
3590 if ((*cp
!= '\0') && (cp
!= end
) && (errno
== 0) &&
3591 ((*end
== '\0') || isspace(*end
))) {
3598 for (cp
= fqdn
; *cp
!= '\0'; cp
++) {
3599 if (*cp
== '.') dots
++;
3607 if (!found
&& !isFQDN
&& !useDefault
&& (dns
->config
->n_resolver
> 1)) {
3609 * FQDN not specified, try matching w/search domains
3611 if (default_resolver
->n_search
> 0) {
3612 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
3614 char *search_fqdn
= NULL
;
3616 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
3621 // try the provided name with the search domain appended
3622 found
= check_matching_resolvers(store_info
,
3633 } else if (default_resolver
->domain
!= NULL
) {
3635 int domain_parts
= 0;
3637 // count domain parts
3638 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
3644 // remove trailing dots
3645 for (dp
--; (dp
>= default_resolver
->domain
) && (*dp
== '.'); dp
--) {
3650 if (dp
>= default_resolver
->domain
) {
3651 // dots are separators, bump # of components
3655 dp
= default_resolver
->domain
;
3656 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= (domain_parts
- ndots
)); i
++) {
3658 char *search_fqdn
= NULL
;
3660 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
3665 // try the provided name with the [default] domain appended
3666 found
= check_matching_resolvers(store_info
,
3677 // move to the next component of the [default] domain
3678 dp
= strchr(dp
, '.') + 1;
3685 * check the reachability of the default resolver
3687 ok
= check_resolver_reachability(store_info
, default_resolver
, flags
, haveDNS
,
3688 resolver_if_index
, log_prefix
);
3689 if (ok
&& dns_config_index
!= NULL
) *dns_config_index
= 0;
3692 if (fqdn
!= nodename
) free(fqdn
);
3697 dns_configuration_release(dns
);
3705 _SC_checkResolverReachability(SCDynamicStoreRef
*storeP
,
3706 SCNetworkReachabilityFlags
*flags
,
3708 const char *nodename
,
3709 const char *servname
)
3712 ReachabilityStoreInfo store_info
;
3714 ReachabilityStoreInfo_init(&store_info
);
3715 ok
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
);
3720 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, nodename
,
3721 servname
, 0, NULL
, NULL
, "");
3725 ReachabilityStoreInfo_free(&store_info
);
3730 __SC_checkResolverReachabilityInternal(SCDynamicStoreRef
*storeP
,
3731 SCNetworkReachabilityFlags
*flags
,
3733 const char *nodename
,
3734 const char *servname
,
3735 uint32_t *resolver_if_index
,
3736 int *dns_config_index
)
3739 ReachabilityStoreInfo store_info
;
3741 ReachabilityStoreInfo_init(&store_info
);
3742 ok
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
);
3747 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, nodename
,
3748 servname
, 0, resolver_if_index
, dns_config_index
, "");
3752 ReachabilityStoreInfo_free(&store_info
);
3757 * _SC_checkResolverReachabilityByAddress()
3759 * Given an IP address, determine whether a reverse DNS query can be issued
3760 * using the current network configuration.
3763 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
3764 SCNetworkReachabilityFlags
*flags
,
3766 struct sockaddr
*sa
)
3771 ReachabilityStoreInfo store_info
;
3773 ReachabilityStoreInfo_init(&store_info
);
3774 ok
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
);
3780 * Ideally, we would have an API that given a local IP
3781 * address would return the DNS server(s) that would field
3782 * a given PTR query. Fortunately, we do have an SPI which
3783 * which will provide this information given a "name" so we
3784 * take the address, convert it into the inverse query name,
3785 * and find out which servers should be consulted.
3788 switch (sa
->sa_family
) {
3794 /* ALIGN: assuming sa is aligned, then cast ok. */
3795 struct sockaddr_in
*sin
= (struct sockaddr_in
*)(void *)sa
;
3798 * build "PTR" query name
3799 * NNN.NNN.NNN.NNN.in-addr.arpa.
3801 rev
.s_addr
= sin
->sin_addr
.s_addr
;
3802 (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.",
3813 /* ALIGN: assume sa is aligned, cast ok. */
3814 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)(void *)sa
;
3815 int x
= sizeof(ptr_name
);
3819 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
3820 * N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.ip6.arpa.
3822 for (i
= sizeof(sin6
->sin6_addr
) - 1; i
>= 0; i
--) {
3823 n
= snprintf(&ptr_name
[s
], x
, "%x.%x.",
3824 ( sin6
->sin6_addr
.s6_addr
[i
] & 0xf),
3825 ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf));
3826 if ((n
== -1) || (n
>= x
)) {
3834 n
= snprintf(&ptr_name
[s
], x
, "ip6.arpa.");
3835 if ((n
== -1) || (n
>= x
)) {
3846 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, ptr_name
, NULL
, 0, NULL
, NULL
, "");
3850 ReachabilityStoreInfo_free(&store_info
);
3856 startAsyncDNSQuery(SCNetworkReachabilityRef target
)
3859 mach_port_t mp
= MACH_PORT_NULL
;
3861 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3863 __dns_query_start(&targetPrivate
->dnsQueryStart
, &targetPrivate
->dnsQueryEnd
);
3865 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3866 if (targetPrivate
->if_index
== 0) {
3867 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3868 error
= getaddrinfo_async_start(&mp
,
3869 targetPrivate
->name
,
3870 targetPrivate
->serv
,
3871 &targetPrivate
->hints
,
3872 __SCNetworkReachabilityCallbackSetResolvedAddress
,
3874 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3876 mp
= _getaddrinfo_interface_async_call(targetPrivate
->name
,
3877 targetPrivate
->serv
,
3878 &targetPrivate
->hints
,
3879 targetPrivate
->if_name
,
3880 __SCNetworkReachabilityCallbackSetResolvedAddress
,
3882 if (mp
== MACH_PORT_NULL
) {
3886 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3888 /* save the error associated with the attempt to resolve the name */
3889 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
);
3893 ok
= enqueueAsyncDNSQuery(target
, mp
);
3902 enqueueAsyncDNSRetry(SCNetworkReachabilityRef target
)
3905 dispatch_source_t source
;
3906 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3908 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3910 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
3913 __SCNetworkReachability_concurrent_queue());
3914 if (source
== NULL
) {
3915 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability retry dispatch_source_create() failed"));
3919 // retain the target ... and release it when the [timer] source is released
3921 dispatch_set_context(source
, (void *)target
);
3922 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
3924 dispatch_source_set_event_handler(source
, ^(void) {
3925 __SCNetworkReachabilityPerformInlineNoLock(target
, TRUE
);
3928 // start a one-shot timer
3929 delay
= targetPrivate
->dnsRetryCount
* EAI_NONAME_RETRY_DELAY_USEC
* NSEC_PER_USEC
;
3930 dispatch_source_set_timer(source
,
3931 dispatch_time(DISPATCH_TIME_NOW
, delay
), // start
3933 10 * NSEC_PER_MSEC
); // leeway
3935 targetPrivate
->dnsRetry
= source
;
3936 dispatch_resume(source
);
3943 dequeueAsyncDNSRetry(SCNetworkReachabilityRef target
)
3945 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3947 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3949 if (targetPrivate
->dnsRetry
!= NULL
) {
3950 dispatch_source_cancel(targetPrivate
->dnsRetry
);
3951 dispatch_release(targetPrivate
->dnsRetry
);
3952 targetPrivate
->dnsRetry
= NULL
;
3962 static dispatch_queue_t
3965 static dispatch_once_t once
;
3966 static dispatch_queue_t q
;
3968 dispatch_once(&once
, ^{
3969 q
= dispatch_queue_create("SCNetworkReachabilty.longLivedQueries", NULL
);
3979 * Called to push out a target's DNS changes
3980 * - caller must be running on the _llq_queue()
3983 _llq_notify(const void *value
, void *context
)
3985 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
3986 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3988 MUTEX_LOCK(&targetPrivate
->lock
);
3990 __dns_query_end(target
,
3991 (targetPrivate
->resolvedAddressError
== NETDB_SUCCESS
), // if successful query
3992 dns_query_llq
, // long-lived-query
3993 &targetPrivate
->dnsQueryStart
, // start time
3994 &targetPrivate
->dnsQueryEnd
); // end time
3996 if (targetPrivate
->scheduled
) {
3997 __SCNetworkReachabilityPerform(target
);
4000 // last long-lived-query end time is new start time
4001 targetPrivate
->dnsQueryStart
= targetPrivate
->dnsQueryEnd
;
4003 MUTEX_UNLOCK(&targetPrivate
->lock
);
4011 * Called to process mDNSResponder long-lived-query updates
4012 * - caller must be running on the _llq_queue()
4015 _llq_callback(DNSServiceRef sdRef
,
4016 DNSServiceFlags flags
,
4017 uint32_t interfaceIndex
,
4018 DNSServiceErrorType errorCode
,
4019 const char *hostname
,
4020 const struct sockaddr
*address
,
4024 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
4025 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4027 MUTEX_LOCK(&targetPrivate
->lock
);
4029 if (targetPrivate
->llqTimer
!= NULL
) {
4030 dispatch_source_cancel(targetPrivate
->llqTimer
);
4031 dispatch_release(targetPrivate
->llqTimer
);
4032 targetPrivate
->llqTimer
= NULL
;
4035 switch (errorCode
) {
4036 case kDNSServiceErr_NoError
:
4037 if (address
!= NULL
) {
4038 CFMutableArrayRef addresses
;
4039 CFDataRef llqAddress
;
4041 if (targetPrivate
->resolvedAddress
!= NULL
) {
4042 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
4043 addresses
= CFArrayCreateMutableCopy(NULL
, 0, targetPrivate
->resolvedAddress
);
4045 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4048 CFRelease(targetPrivate
->resolvedAddress
);
4049 targetPrivate
->resolvedAddress
= NULL
;
4051 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4054 llqAddress
= CFDataCreate(NULL
, (void *)address
, address
->sa_len
);
4055 if (flags
& kDNSServiceFlagsAdd
) {
4057 CFArrayAppendValue(addresses
, llqAddress
);
4062 i
= CFArrayGetFirstIndexOfValue(addresses
,
4063 CFRangeMake(0, CFArrayGetCount(addresses
)),
4065 if (i
!= kCFNotFound
) {
4066 CFArrayRemoveValueAtIndex(addresses
, i
);
4069 CFRelease(llqAddress
);
4071 if (CFArrayGetCount(addresses
) > 0) {
4072 targetPrivate
->resolvedAddress
= addresses
;
4073 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
4075 // if host not found
4076 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
4077 targetPrivate
->resolvedAddressError
= EAI_NONAME
;
4078 CFRelease(addresses
);
4081 targetPrivate
->needResolve
= FALSE
;
4084 case kDNSServiceErr_NoSuchRecord
:
4085 if (address
!= NULL
) {
4086 // no IPv4/IPv6 address for name (NXDOMAIN)
4087 if (targetPrivate
->resolvedAddress
== NULL
) {
4088 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
4089 targetPrivate
->resolvedAddressError
= EAI_NONAME
;
4091 targetPrivate
->needResolve
= FALSE
;
4094 case kDNSServiceErr_Timeout
:
4095 if (targetPrivate
->resolvedAddress
== NULL
) {
4096 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
4097 targetPrivate
->resolvedAddressError
= EAI_NONAME
;
4099 targetPrivate
->needResolve
= FALSE
;
4102 SCLog(TRUE
, LOG_ERR
,
4103 CFSTR("%sSCNetworkReachability _llq_callback w/error=%d"),
4104 targetPrivate
->log_prefix
,
4109 MUTEX_UNLOCK(&targetPrivate
->lock
);
4111 // the "more coming" flag applies to DNSService callouts for any/all
4112 // hosts that are being watched so we need to keep track of the targets
4113 // we have updated. When we [finally] have the last callout then we
4114 // push our notifications for all of the updated targets.
4116 if (llqUpdated
== NULL
) {
4117 llqUpdated
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
4119 CFSetAddValue(llqUpdated
, target
);
4121 if (!(flags
& kDNSServiceFlagsMoreComing
)) {
4122 CFSetApplyFunction(llqUpdated
, _llq_notify
, NULL
);
4123 CFRelease(llqUpdated
);
4132 enqueueLongLivedQuery(SCNetworkReachabilityRef target
)
4134 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4136 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4138 if (targetPrivate
->serv
!= NULL
) {
4139 // if "serv" provided, can't use DNSServiceGetAddrInfo
4143 if (memcmp(&targetPrivate
->hints
, &HINTS_DEFAULT
, sizeof(targetPrivate
->hints
)) != 0) {
4144 // non-default "hints" provided, can't use DNSServiceGetAddrInfo
4148 // mark the long lived query active
4149 targetPrivate
->llqActive
= TRUE
;
4151 // track the DNS resolution time
4152 __dns_query_start(&targetPrivate
->dnsQueryStart
, &targetPrivate
->dnsQueryEnd
);
4155 dispatch_async(_llq_queue(), ^{
4156 DNSServiceErrorType err
;
4157 dispatch_source_t source
;
4159 MUTEX_LOCK(&targetPrivate
->lock
);
4161 if (targetPrivate
->llqTarget
!= NULL
) {
4162 // if already running
4163 MUTEX_UNLOCK(&targetPrivate
->lock
);
4168 // if needed, start interacting with mDNSResponder
4170 if (llqMain
== NULL
) {
4171 err
= DNSServiceCreateConnection(&llqMain
);
4172 if (err
!= kDNSServiceErr_NoError
) {
4173 SCLog(TRUE
, LOG_ERR
,
4174 CFSTR("DNSServiceCreateConnection(&llqMain) failed, error = %d"),
4177 targetPrivate
->llqActive
= FALSE
;
4179 MUTEX_UNLOCK(&targetPrivate
->lock
);
4184 err
= DNSServiceSetDispatchQueue(llqMain
, _llq_queue());
4185 if (err
!= kDNSServiceErr_NoError
) {
4186 SCLog(TRUE
, LOG_ERR
,
4187 CFSTR("DNSServiceSetDispatchQueue() failed, error = %d"),
4189 DNSServiceRefDeallocate(llqMain
);
4192 targetPrivate
->llqActive
= FALSE
;
4194 MUTEX_UNLOCK(&targetPrivate
->lock
);
4200 // start a long-lived-query for this target
4202 targetPrivate
->llqTarget
= llqMain
;
4203 err
= DNSServiceGetAddrInfo(&targetPrivate
->llqTarget
, // sdRef
4204 kDNSServiceFlagsReturnIntermediates
// flags
4205 | kDNSServiceFlagsShareConnection
,
4206 targetPrivate
->if_index
, // interfaceIndex
4208 targetPrivate
->name
, // hostname
4209 _llq_callback
, // callback
4210 (void *)target
); // context
4211 if (err
!= kDNSServiceErr_NoError
) {
4212 SCLog(TRUE
, LOG_ERR
,
4213 CFSTR("DNSServiceGetAddrInfo() failed, error = %d"),
4215 targetPrivate
->llqTarget
= NULL
;
4216 if (llqCount
== 0) {
4217 // if this was the first request
4218 DNSServiceRefDeallocate(llqMain
);
4222 targetPrivate
->llqActive
= FALSE
;
4224 MUTEX_UNLOCK(&targetPrivate
->lock
);
4231 // if case we don't get any callbacks from our long-lived-query (this
4232 // could happen if the DNS servers do not respond), we start a timer
4233 // to ensure that we fire off at least one reachability callback.
4235 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
4239 if (source
!= NULL
) {
4240 // retain the target ... and release it when the [timer] source is released
4242 dispatch_set_context(source
, (void *)target
);
4243 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
4245 dispatch_source_set_event_handler(source
, ^(void) {
4246 _llq_callback(NULL
, // sdRef
4248 0, // interfaceIndex
4249 kDNSServiceErr_Timeout
, // errorCode
4253 (void *)target
); // context
4256 dispatch_source_set_timer(source
,
4257 dispatch_time(DISPATCH_TIME_NOW
,
4258 LLQ_TIMEOUT_NSEC
), // start
4260 10 * NSEC_PER_MSEC
); // leeway
4262 targetPrivate
->llqTimer
= source
;
4263 dispatch_resume(source
);
4265 SCLog(TRUE
, LOG_ERR
,
4266 CFSTR("SCNetworkReachability llq dispatch_source_create(no-reply) failed"));
4269 MUTEX_UNLOCK(&targetPrivate
->lock
);
4278 dequeueLongLivedQuery(SCNetworkReachabilityRef target
)
4280 DNSServiceRef sdRef
;
4281 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4283 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4285 // terminate the [target] llq timer
4286 if (targetPrivate
->llqTimer
!= NULL
) {
4287 dispatch_source_cancel(targetPrivate
->llqTimer
);
4288 dispatch_release(targetPrivate
->llqTimer
);
4289 targetPrivate
->llqTimer
= NULL
;
4292 // terminate the [target] long lived query
4293 sdRef
= targetPrivate
->llqTarget
;
4294 targetPrivate
->llqTarget
= NULL
;
4296 // mark the long lived query NOT active
4297 targetPrivate
->llqActive
= FALSE
;
4299 if (sdRef
!= NULL
) {
4300 dispatch_async(_llq_queue(), ^{
4301 DNSServiceRefDeallocate(sdRef
);
4305 if (llqCount
== 0) {
4306 // if no more queries active
4307 DNSServiceRefDeallocate(llqMain
);
4318 #pragma mark OnDemand
4322 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target
,
4323 CFDictionaryRef
*userOptions
)
4325 SCNetworkServiceRef service
= NULL
;
4326 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4328 if (!isA_SCNetworkReachability(target
)) {
4329 _SCErrorSet(kSCStatusInvalidArgument
);
4333 if (targetPrivate
->onDemandServiceID
!= NULL
) {
4334 service
= _SCNetworkServiceCopyActive(NULL
, targetPrivate
->onDemandServiceID
);
4337 if (userOptions
!= NULL
) {
4338 if (targetPrivate
->onDemandName
!= NULL
) {
4339 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4340 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
);
4342 *userOptions
= NULL
;
4351 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer
,
4352 SCNetworkReachabilityFlags onDemandFlags
,
4355 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
4356 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4358 MUTEX_LOCK(&targetPrivate
->lock
);
4360 if (!targetPrivate
->scheduled
) {
4361 // if not currently scheduled
4362 MUTEX_UNLOCK(&targetPrivate
->lock
);
4366 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sOnDemand \"server\" status changed"),
4367 targetPrivate
->log_prefix
);
4368 __SCNetworkReachabilityPerform(target
);
4370 MUTEX_UNLOCK(&targetPrivate
->lock
);
4377 __SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef store_info
,
4378 SCNetworkReachabilityRef target
,
4379 Boolean onDemandRetry
,
4380 SCNetworkReachabilityFlags
*flags
)
4383 Boolean onDemand
= FALSE
;
4384 CFStringRef onDemandRemoteAddress
= NULL
;
4385 CFStringRef onDemandServiceID
= NULL
;
4386 SCNetworkConnectionStatus onDemandStatus
;
4387 SCDynamicStoreRef store
;
4388 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4390 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4392 // SCLog(_sc_debug, LOG_INFO,
4393 // CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"),
4394 // targetPrivate->log_prefix,
4395 // onDemandRetry ? "after" : "before");
4397 if (targetPrivate
->onDemandName
== NULL
) {
4398 targetPrivate
->onDemandName
= CFStringCreateWithCString(NULL
, targetPrivate
->name
, kCFStringEncodingUTF8
);
4402 * check if an OnDemand VPN configuration matches the name.
4404 store
= store_info
->store
;
4405 ok
= __SCNetworkConnectionCopyOnDemandInfoWithName(&store
,
4406 targetPrivate
->onDemandName
,
4410 &onDemandRemoteAddress
);
4411 if ((store_info
->store
== NULL
) && (store
!= NULL
)) {
4412 // if an SCDynamicStore session was added, keep it
4413 store_info
->store
= store
;
4415 if (!_SC_CFEqual(targetPrivate
->onDemandRemoteAddress
, onDemandRemoteAddress
) ||
4416 !_SC_CFEqual(targetPrivate
->onDemandServiceID
, onDemandServiceID
)) {
4417 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
4418 CFRelease(targetPrivate
->onDemandRemoteAddress
);
4419 targetPrivate
->onDemandRemoteAddress
= NULL
;
4422 if (targetPrivate
->onDemandServer
!= NULL
) {
4423 if (targetPrivate
->dispatchQueue
!= NULL
) {
4425 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
4426 } else if (targetPrivate
->rls
!= NULL
) {
4431 n
= CFArrayGetCount(targetPrivate
->rlList
);
4432 for (i
= 0; i
< n
; i
+= 3) {
4433 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
4434 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
4436 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, TRUE
);
4440 CFRelease(targetPrivate
->onDemandServer
);
4441 targetPrivate
->onDemandServer
= NULL
;
4444 if (targetPrivate
->onDemandServiceID
!= NULL
) {
4445 CFRelease(targetPrivate
->onDemandServiceID
);
4446 targetPrivate
->onDemandServiceID
= NULL
;
4450 if (onDemandStatus
!= kSCNetworkConnectionConnected
) {
4452 * if we have a VPN configuration matching the name *and* we need to
4453 * bring the VPN up. Combine our flags with those of the VPN server.
4455 if (targetPrivate
->onDemandServer
== NULL
) {
4456 CFMutableDictionaryRef options
;
4458 options
= CFDictionaryCreateMutable(NULL
,
4460 &kCFTypeDictionaryKeyCallBacks
,
4461 &kCFTypeDictionaryValueCallBacks
);
4462 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, onDemandRemoteAddress
);
4463 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandBypass
, kCFBooleanTrue
);
4464 #ifdef HAVE_REACHABILITY_SERVER
4465 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionServerBypass
, kCFBooleanTrue
);
4466 #endif // HAVE_REACHABILITY_SERVER
4467 targetPrivate
->onDemandServer
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
4470 if (targetPrivate
->scheduled
) {
4471 SCNetworkReachabilityContext context
= { 0, NULL
, CFRetain
, CFRelease
, CFCopyDescription
};
4473 context
.info
= (void *)target
;
4474 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
,
4475 __SCNetworkReachabilityOnDemandCheckCallback
,
4478 // schedule server reachability to match that of the target
4479 if (targetPrivate
->dispatchQueue
!= NULL
) {
4480 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, targetPrivate
->dispatchQueue
, TRUE
);
4485 n
= CFArrayGetCount(targetPrivate
->rlList
);
4486 for (i
= 0; i
< n
; i
+= 3) {
4487 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
4488 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
4490 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, NULL
, TRUE
);
4496 ok
= SCNetworkReachabilityGetFlags(targetPrivate
->onDemandServer
, flags
);
4497 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status * = 0x%08x"),
4498 targetPrivate
->log_prefix
,
4500 if (ok
&& (*flags
& kSCNetworkReachabilityFlagsReachable
)) {
4501 if (!(*flags
& kSCNetworkReachabilityFlagsTransientConnection
)) {
4502 // start clean if not already layered on a transient network
4505 *flags
|= kSCNetworkReachabilityFlagsReachable
;
4506 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
4507 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4508 *flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
4511 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service * = %@"),
4512 targetPrivate
->log_prefix
,
4514 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after OnDemand connect)"),
4515 targetPrivate
->log_prefix
);
4522 if (onDemandRemoteAddress
!= NULL
) {
4523 if (targetPrivate
->onDemandRemoteAddress
== NULL
) {
4524 targetPrivate
->onDemandRemoteAddress
= onDemandRemoteAddress
;
4526 CFRelease(onDemandRemoteAddress
);
4530 if (onDemandServiceID
!= NULL
) {
4531 if (targetPrivate
->onDemandServiceID
== NULL
) {
4532 targetPrivate
->onDemandServiceID
= onDemandServiceID
;
4534 CFRelease(onDemandServiceID
);
4544 #pragma mark Reachability Flags
4547 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
4550 struct addrinfo
*res
;
4555 reply_callback(int32_t status
, struct addrinfo
*res
, void *context
)
4557 reply_info
*reply
= (reply_info
*)context
;
4559 reply
->status
= status
;
4566 getaddrinfo_interface_sync(const char *nodename
,
4567 const char *servname
,
4568 const struct addrinfo
*hints
,
4569 const char *interface
,
4570 struct addrinfo
**res
)
4573 reply_info reply
= { NETDB_SUCCESS
, NULL
};
4575 mp
= _getaddrinfo_interface_async_call(nodename
,
4581 if (mp
== MACH_PORT_NULL
) {
4589 mach_msg_empty_rcv_t msg
;
4591 kern_return_t m_status
;
4593 m_status
= mach_msg(&m_reply
.msg
.header
, /* msg */
4594 MACH_RCV_MSG
, /* options */
4596 sizeof(m_reply
), /* rcv_size */
4598 MACH_MSG_TIMEOUT_NONE
, /* timeout */
4599 MACH_PORT_NULL
); /* notify */
4600 if (m_status
!= KERN_SUCCESS
) {
4604 g_status
= getaddrinfo_async_handle_reply((void *)m_reply
.buf
);
4605 if (g_status
!= 0) {
4606 if (reply
.res
!= NULL
) {
4607 freeaddrinfo(reply
.res
);
4613 if ((reply
.res
!= NULL
) || (reply
.status
!= NETDB_SUCCESS
)) {
4614 // if we have a reply or an error
4618 // if the request is not complete and needs to be re-queued
4622 return reply
.status
;
4624 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
4628 __SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef store_info
,
4629 SCNetworkReachabilityRef target
,
4630 ReachabilityInfo
*reach_info
,
4633 CFMutableArrayRef addresses
= NULL
;
4634 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4635 ReachabilityInfo my_info
= NOT_REACHABLE
;
4638 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4640 _reach_set(reach_info
, &NOT_REACHABLE
, reach_info
->cycle
);
4642 if (!isA_SCNetworkReachability(target
)) {
4643 _SCErrorSet(kSCStatusInvalidArgument
);
4647 #ifdef HAVE_REACHABILITY_SERVER
4648 if (!targetPrivate
->serverBypass
) {
4649 if (!targetPrivate
->serverActive
) {
4650 ok
= __SCNetworkReachabilityServer_targetAdd(target
);
4652 targetPrivate
->serverBypass
= TRUE
;
4656 if (targetPrivate
->serverActive
) {
4657 ok
= __SCNetworkReachabilityServer_targetStatus(target
);
4659 SCLog(TRUE
, LOG_DEBUG
,
4660 CFSTR("__SCNetworkReachabilityGetFlags _targetStatus() failed"));
4661 _SCErrorSet(kSCStatusFailed
);
4665 targetPrivate
->cycle
= targetPrivate
->serverInfo
.cycle
;
4666 _reach_set(&my_info
, &targetPrivate
->serverInfo
, targetPrivate
->cycle
);
4670 #endif // HAVE_REACHABILITY_SERVER
4672 switch (targetPrivate
->type
) {
4673 case reachabilityTypeAddress
:
4674 case reachabilityTypeAddressPair
: {
4676 * Check "local" address
4678 if (targetPrivate
->localAddress
!= NULL
) {
4680 * Check "local" address
4682 ok
= checkAddress(store_info
,
4683 targetPrivate
->localAddress
,
4684 targetPrivate
->if_index
,
4686 targetPrivate
->log_prefix
);
4688 goto error
; /* not today */
4691 if (!(my_info
.flags
& kSCNetworkReachabilityFlagsIsLocalAddress
)) {
4692 goto error
; /* not reachable, non-"local" address */
4697 * Check "remote" address
4699 if (targetPrivate
->remoteAddress
!= NULL
) {
4701 * in cases where we have "local" and "remote" addresses
4702 * we need to re-initialize the to-be-returned flags.
4704 my_info
= NOT_REACHABLE
;
4707 * Check "remote" address
4709 ok
= checkAddress(store_info
,
4710 targetPrivate
->remoteAddress
,
4711 targetPrivate
->if_index
,
4713 targetPrivate
->log_prefix
);
4715 goto error
; /* not today */
4723 case reachabilityTypeName
: {
4724 struct timeval dnsQueryStart
;
4725 struct timeval dnsQueryEnd
;
4727 SCNetworkReachabilityFlags ns_flags
;
4728 uint32_t ns_if_index
;
4729 struct addrinfo
*res
;
4731 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
4732 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
4733 /* if resolved or an error had been detected */
4735 /* if not an async request */
4736 goto checkResolvedAddress
;
4737 } else if (targetPrivate
->llqActive
) {
4738 /* if long-lived-query active */
4739 goto checkResolvedAddress
;
4740 } else if ((targetPrivate
->dnsMP
== MACH_PORT_NULL
) && !targetPrivate
->needResolve
) {
4741 struct timeval elapsed
;
4742 const struct timeval retry_limit
= { EAI_NONAME_RETRY_LIMIT_USEC
/ USEC_PER_SEC
,
4743 EAI_NONAME_RETRY_LIMIT_USEC
% USEC_PER_SEC
};
4746 * if this is an async request (i.e. someone is watching the reachability
4747 * of this target), if no query active, and if no query is needed
4750 if ((error
!= EAI_NONAME
)
4751 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
4752 && (error
!= EAI_NODATA
)
4755 /* if not "host not found" */
4756 goto checkResolvedAddress
;
4760 * if our last DNS query returned EAI_NONAME then we
4761 * "may" want to retry.
4763 * Specifically, if the [DNS] configuration was updated a while
4764 * back then we'll trust the EAI_NONAME reply. Otherwise, we
4765 * want to try again to ensure that we didn't get caught in a
4766 * race between the time when the configuration was changed and
4767 * when mDNSResponder is really ready to handle the query.
4769 * Retry handling details :
4771 * Compare the time when the DNS configuration was last changed and
4772 * when our DNS reply was started (->last_dns vs ->dnsQueryStart).
4774 * Expected: 0 < last_dns (t1) < dnsQueryStart (t2)
4776 * last start end description action
4777 * ==== ===== ==== ================================= ========
4778 * 0 N/A N/A no change, query error no retry
4779 * 0 N/A N/A no change, query complete no retry
4780 * N/A N/A 0 changed, query in-flight or error no retry
4781 * t1 > t2 query started, then [DNS] changed no retry
4782 * t1 == t2 changed & query started together no retry
4783 * t1 < t2 changed, then query started retry
4786 if (!timerisset(&targetPrivate
->last_dns
)) {
4788 * if we have not yet seen a DNS configuration
4791 goto checkResolvedAddress
;
4794 if (!timerisset(&targetPrivate
->dnsQueryEnd
)) {
4796 * if no query end time (new request in flight)
4798 goto checkResolvedAddress
;
4801 if (timercmp(&targetPrivate
->last_dns
,
4802 &targetPrivate
->dnsQueryStart
,
4805 * if our DNS query started and then, a
4806 * short time later, the DNS configuration
4807 * was changed we don't need to retry
4808 * because we will be re-issuing (and not
4809 * retrying) the query.
4811 goto checkResolvedAddress
;
4814 timersub(&targetPrivate
->dnsQueryStart
,
4815 &targetPrivate
->last_dns
,
4817 if (timercmp(&elapsed
, &retry_limit
, >)) {
4819 * if the DNS query started after mDNSResponder
4820 * had a chance to apply the last configuration
4821 * then we should trust the EAI_NONAME reply.
4823 goto checkResolvedAddress
;
4826 /* retry the DNS query */
4828 if (targetPrivate
->dnsRetry
!= NULL
) {
4829 // no need to schedule if we already have a
4830 // retry query in flight
4834 targetPrivate
->dnsRetryCount
++;
4836 SCLog(_sc_debug
, LOG_INFO
,
4837 CFSTR("%sretry [%d] DNS query for %s%s%s%s%s"),
4838 targetPrivate
->log_prefix
,
4839 targetPrivate
->dnsRetryCount
,
4840 targetPrivate
->name
!= NULL
? "name = " : "",
4841 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
4842 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
4843 targetPrivate
->serv
!= NULL
? "serv = " : "",
4844 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
4846 enqueueAsyncDNSRetry(target
);
4851 if (!targetPrivate
->onDemandBypass
) {
4855 * before we attempt our initial DNS query, check if there is
4856 * an OnDemand configuration that we should be using.
4858 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, FALSE
, &my_info
.flags
);
4860 /* if OnDemand connection is needed */
4865 /* check the reachability of the DNS servers */
4866 ok
= _SC_R_checkResolverReachability(store_info
,
4868 &targetPrivate
->haveDNS
,
4869 targetPrivate
->name
,
4870 targetPrivate
->serv
,
4871 targetPrivate
->if_index
,
4874 targetPrivate
->log_prefix
);
4876 /* if we could not get DNS server info */
4877 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
4878 targetPrivate
->log_prefix
);
4880 } else if (rankReachability(ns_flags
) < 2) {
4882 * if DNS servers are not (or are no longer) reachable, set
4883 * flags based on the availability of configured (but not
4887 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
4888 targetPrivate
->log_prefix
);
4890 ok
= checkAddress(store_info
,
4892 targetPrivate
->if_index
,
4894 targetPrivate
->log_prefix
);
4896 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sNo available networks"),
4897 targetPrivate
->log_prefix
);
4901 if (async
&& targetPrivate
->scheduled
) {
4903 * return "host not found", set flags appropriately,
4904 * and schedule notification.
4906 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME
,
4909 my_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
4911 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sno DNS servers are reachable"),
4912 targetPrivate
->log_prefix
);
4913 __SCNetworkReachabilityPerform(target
);
4918 if (targetPrivate
->resolverBypass
) {
4919 /* if we are not resolving the name,
4920 * set the flags of the resolvers */
4921 my_info
.flags
= ns_flags
;
4922 my_info
.if_index
= ns_if_index
;
4927 /* for async requests we return the last known status */
4928 my_info
= targetPrivate
->info
;
4930 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
4931 /* if request already in progress */
4932 SCLog(_sc_debug
, LOG_INFO
,
4933 CFSTR("%swaiting for DNS reply"),
4934 targetPrivate
->log_prefix
);
4935 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
4936 /* updated reachability based on the previous reply */
4937 goto checkResolvedAddress
;
4942 if (targetPrivate
->dnsRetry
!= NULL
) {
4943 /* if we already have a "retry" queued */
4947 if (targetPrivate
->llqActive
) {
4948 /* if long-lived-query active */
4949 SCLog(_sc_debug
, LOG_INFO
,
4950 CFSTR("%swaiting for DNS updates"),
4951 targetPrivate
->log_prefix
);
4952 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
4953 /* updated reachability based on the previous reply */
4954 goto checkResolvedAddress
;
4959 if (!targetPrivate
->llqBypass
) {
4960 SCLog(_sc_debug
, LOG_INFO
,
4961 CFSTR("%sstart long-lived DNS query for %s%s%s%s%s"),
4962 targetPrivate
->log_prefix
,
4963 targetPrivate
->name
!= NULL
? "name = " : "",
4964 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
4965 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
4966 targetPrivate
->serv
!= NULL
? "serv = " : "",
4967 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
4970 * initiate an long-lived DNS query
4972 if (enqueueLongLivedQuery(target
)) {
4973 /* request initiated */
4978 SCLog(_sc_debug
, LOG_INFO
,
4979 CFSTR("%sstart DNS query for %s%s%s%s%s"),
4980 targetPrivate
->log_prefix
,
4981 targetPrivate
->name
!= NULL
? "name = " : "",
4982 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
4983 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
4984 targetPrivate
->serv
!= NULL
? "serv = " : "",
4985 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
4988 * initiate an async DNS query
4990 if (startAsyncDNSQuery(target
)) {
4991 /* request initiated */
4995 /* if we could not initiate the request, process error */
4996 goto checkResolvedAddress
;
4999 SCLog(_sc_debug
, LOG_INFO
,
5000 CFSTR("%scheck DNS for %s%s%s%s%s"),
5001 targetPrivate
->log_prefix
,
5002 targetPrivate
->name
!= NULL
? "name = " : "",
5003 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
5004 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
5005 targetPrivate
->serv
!= NULL
? "serv = " : "",
5006 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
5009 * OK, all of the DNS name servers are available. Let's
5010 * resolve the nodename into an address.
5012 __dns_query_start(&dnsQueryStart
, &dnsQueryEnd
);
5014 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5015 if (targetPrivate
->if_index
== 0) {
5016 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5017 error
= getaddrinfo(targetPrivate
->name
,
5018 targetPrivate
->serv
,
5019 &targetPrivate
->hints
,
5021 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5023 error
= getaddrinfo_interface_sync(targetPrivate
->name
,
5024 targetPrivate
->serv
,
5025 &targetPrivate
->hints
,
5026 targetPrivate
->if_name
,
5029 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5031 __dns_query_end(target
,
5032 ((error
== 0) && (res
!= NULL
)), // if successful query
5033 dns_query_sync
, // sync
5034 &dnsQueryStart
, // start time
5035 &dnsQueryEnd
); // end time
5037 __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
);
5039 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
5041 checkResolvedAddress
:
5044 * We first assume that the requested host is NOT available.
5045 * Then, check each address for accessibility and return the
5046 * best status available.
5048 my_info
= NOT_REACHABLE
;
5050 if (isA_CFArray(addresses
)) {
5052 CFIndex n
= CFArrayGetCount(addresses
);
5054 for (i
= 0; i
< n
; i
++) {
5055 ReachabilityInfo ns_info
= NOT_REACHABLE
;
5056 struct sockaddr
*sa
;
5058 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
5060 ok
= checkAddress(store_info
,
5062 targetPrivate
->if_index
,
5064 targetPrivate
->log_prefix
);
5066 goto error
; /* not today */
5069 if (rankReachability(ns_info
.flags
) > rankReachability(my_info
.flags
)) {
5070 /* return the best case result */
5072 if (rankReachability(my_info
.flags
) == 2) {
5079 if ((error
== EAI_NONAME
)
5080 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
5081 || (error
== EAI_NODATA
)
5085 * the target host name could not be resolved
5087 if (!targetPrivate
->onDemandBypass
) {
5091 * our initial DNS query failed, check again to see if there
5092 * there is an OnDemand configuration that we should be using.
5094 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, TRUE
, &my_info
.flags
);
5096 /* if OnDemand connection is needed */
5101 if (!targetPrivate
->haveDNS
) {
5103 * No DNS servers are defined. Set flags based on
5104 * the availability of configured (but not active)
5107 ok
= checkAddress(store_info
,
5109 targetPrivate
->if_index
,
5111 targetPrivate
->log_prefix
);
5113 goto error
; /* not today */
5116 if ((my_info
.flags
& kSCNetworkReachabilityFlagsReachable
) &&
5117 (my_info
.flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
5119 * Since we might pick up a set of DNS servers when this connection
5120 * is established, don't reply with a "HOST NOT FOUND" error just yet.
5125 /* Host not found, not reachable! */
5126 my_info
= NOT_REACHABLE
;
5137 _reach_set(reach_info
, &my_info
, targetPrivate
->cycle
);
5141 if (addresses
!= NULL
) CFRelease(addresses
);
5146 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target
)
5148 SCNetworkReachabilityFlags flags
;
5151 ReachabilityStoreInfo store_info
;
5152 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5154 if (!isA_SCNetworkReachability(target
)) {
5155 _SCErrorSet(kSCStatusInvalidArgument
);
5159 ReachabilityStoreInfo_init(&store_info
);
5161 MUTEX_LOCK(&targetPrivate
->lock
);
5163 if (targetPrivate
->scheduled
) {
5164 // if being watched, return the last known (and what should be current) status
5165 flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
5170 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
);
5171 flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
5175 /* Only return the if_index if the connection is reachable not for reachable connection
5176 * required etc ... */
5177 if (ok
&& rankReachability(flags
) == 2) {
5178 if_index
= targetPrivate
->info
.if_index
;
5181 MUTEX_UNLOCK(&targetPrivate
->lock
);
5182 ReachabilityStoreInfo_free(&store_info
);
5188 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
5189 SCNetworkReachabilityFlags
*flags
)
5192 ReachabilityStoreInfo store_info
;
5193 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5195 if (!isA_SCNetworkReachability(target
)) {
5196 _SCErrorSet(kSCStatusInvalidArgument
);
5200 ReachabilityStoreInfo_init(&store_info
);
5202 MUTEX_LOCK(&targetPrivate
->lock
);
5204 if (targetPrivate
->scheduled
) {
5205 // if being watched, return the last known (and what should be current) status
5206 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
5211 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
);
5212 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
5216 MUTEX_UNLOCK(&targetPrivate
->lock
);
5217 ReachabilityStoreInfo_free(&store_info
);
5223 #pragma mark Notifications
5227 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
5230 CFMutableArrayRef keys
;
5231 CFStringRef pattern
;
5232 CFMutableArrayRef patterns
;
5234 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5235 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5237 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
5238 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5239 kSCDynamicStoreDomainSetup
,
5241 CFArrayAppendValue(keys
, key
);
5244 // State:/Network/Global/DNS
5245 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5246 kSCDynamicStoreDomainState
,
5248 CFArrayAppendValue(keys
, key
);
5251 // State:/Network/Global/IPv4 (default route)
5252 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5253 kSCDynamicStoreDomainState
,
5255 CFArrayAppendValue(keys
, key
);
5258 // State:/Network/Global/OnDemand
5259 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5260 kSCDynamicStoreDomainState
,
5262 CFArrayAppendValue(keys
, key
);
5265 // Setup: per-service Interface info
5266 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5267 kSCDynamicStoreDomainSetup
,
5269 kSCEntNetInterface
);
5270 CFArrayAppendValue(patterns
, pattern
);
5273 // per-service IPv4 info
5274 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5275 kSCDynamicStoreDomainSetup
,
5278 CFArrayAppendValue(patterns
, pattern
);
5280 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5281 kSCDynamicStoreDomainState
,
5284 CFArrayAppendValue(patterns
, pattern
);
5287 // per-service IPv6 info
5288 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5289 kSCDynamicStoreDomainSetup
,
5292 CFArrayAppendValue(patterns
, pattern
);
5294 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5295 kSCDynamicStoreDomainState
,
5298 CFArrayAppendValue(patterns
, pattern
);
5301 // per-service PPP info (for existence, kSCPropNetPPPDialOnDemand, kSCPropNetPPPStatus)
5302 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5303 kSCDynamicStoreDomainSetup
,
5306 CFArrayAppendValue(patterns
, pattern
);
5308 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5309 kSCDynamicStoreDomainState
,
5312 CFArrayAppendValue(patterns
, pattern
);
5315 #if !TARGET_IPHONE_SIMULATOR
5316 // per-service VPN info (for existence, kSCPropNetVPNStatus)
5317 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5318 kSCDynamicStoreDomainSetup
,
5321 CFArrayAppendValue(patterns
, pattern
);
5323 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5324 kSCDynamicStoreDomainState
,
5327 CFArrayAppendValue(patterns
, pattern
);
5329 #endif // !TARGET_IPHONE_SIMULATOR
5331 // per-service IPSec info (for existence, kSCPropNetIPSecStatus)
5332 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5333 kSCDynamicStoreDomainSetup
,
5336 CFArrayAppendValue(patterns
, pattern
);
5338 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5339 kSCDynamicStoreDomainState
,
5342 CFArrayAppendValue(patterns
, pattern
);
5345 #if !TARGET_OS_IPHONE
5346 // State: Power Management Capabilities
5347 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
5348 kSCDynamicStoreDomainState
,
5349 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
5350 CFArrayAppendValue(keys
, key
);
5352 #endif // TARGET_OS_IPHONE
5355 // SCDynamicStore key to force posting a reachability change
5356 CFArrayAppendValue(keys
, SCNETWORKREACHABILITY_TRIGGER_KEY
);
5358 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
5360 CFRelease(patterns
);
5366 static dispatch_queue_t
5369 static dispatch_once_t once
;
5370 static dispatch_queue_t q
;
5372 dispatch_once(&once
, ^{
5373 q
= dispatch_queue_create("SCNetworkReachabilty.changes", NULL
);
5381 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store
,
5382 CFArrayRef changedKeys
,
5385 #if !TARGET_OS_IPHONE
5386 Boolean cpuStatusChanged
= FALSE
;
5387 #endif // !TARGET_OS_IPHONE
5388 Boolean dnsConfigChanged
= FALSE
;
5390 Boolean forcedChange
= FALSE
;
5393 CFIndex nGlobals
= 0;
5395 Boolean networkConfigChanged
= FALSE
;
5397 #if !TARGET_OS_IPHONE
5398 Boolean powerStatusChanged
= FALSE
;
5399 #endif // !TARGET_OS_IPHONE
5400 ReachabilityStoreInfo store_info
;
5401 const void * targets_q
[N_QUICK
];
5402 const void ** targets
= targets_q
;
5403 __block CFSetRef watchers
= NULL
;
5405 nChanges
= CFArrayGetCount(changedKeys
);
5406 if (nChanges
== 0) {
5411 /* "something" changed, start fresh */
5412 ReachabilityStoreInfo_save(NULL
);
5414 dispatch_sync(_hn_queue(), ^{
5415 /* grab the currently watched targets */
5416 if (hn_targets
!= NULL
) {
5417 watchers
= CFSetCreateCopy(NULL
, hn_targets
);
5421 nTargets
= (watchers
!= NULL
) ? CFSetGetCount(watchers
) : 0;
5422 if (nTargets
== 0) {
5423 /* if no addresses being monitored */
5427 /* grab the current time */
5428 (void)gettimeofday(&now
, NULL
);
5430 #if !TARGET_OS_IPHONE
5431 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
5432 kSCDynamicStoreDomainState
,
5433 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
5434 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
5439 num
= SCDynamicStoreCopyValue(store
, key
);
5441 if (isA_CFNumber(num
) &&
5442 CFNumberGetValue(num
, kCFNumberSInt32Type
, &power_capabilities
)) {
5443 static Boolean haveCPU_old
= TRUE
;
5444 Boolean haveCPU_new
;
5446 haveCPU_new
= (power_capabilities
& kIOPMSystemPowerStateCapabilityCPU
) != 0;
5447 if ((haveCPU_old
!= haveCPU_new
) && haveCPU_new
) {
5449 * if the power state now shows CPU availability
5450 * then we will assume that the DNS configuration
5451 * has changed. This will force us to re-issue
5452 * our DNS queries since mDNSResponder does not
5453 * attempt to resolve names when "sleeping".
5455 cpuStatusChanged
= TRUE
;
5456 dnsConfigChanged
= TRUE
;
5458 haveCPU_old
= haveCPU_new
;
5460 power_capabilities
= kIOPMSytemPowerStateCapabilitiesMask
;
5465 power_capabilities
= kIOPMSytemPowerStateCapabilitiesMask
;
5468 powerStatusChanged
= TRUE
;
5471 #endif // !TARGET_OS_IPHONE
5473 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5474 kSCDynamicStoreDomainState
,
5476 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
5478 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
5482 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), SCNETWORKREACHABILITY_TRIGGER_KEY
)) {
5484 forcedChange
= TRUE
; /* an SCDynamicStore driven "network" change */
5487 if (nChanges
> nGlobals
) {
5488 networkConfigChanged
= TRUE
;
5492 unsigned int changes
= 0;
5493 static const char *change_strings
[] = {
5494 // with no "power" status change
5499 #if !TARGET_OS_IPHONE
5500 // with "power" status change
5502 "network and power ",
5504 "network, DNS, and power ",
5506 // with "power" status change (including CPU "on")
5508 "network and power* ",
5510 "network, DNS, and power* ",
5511 #endif // !TARGET_OS_IPHONE
5514 #if !TARGET_OS_IPHONE
5516 if (powerStatusChanged
) {
5518 if (cpuStatusChanged
) {
5522 #endif // !TARGET_OS_IPHONE
5525 if (dnsConfigChanged
) {
5530 if (networkConfigChanged
) {
5534 SCLog(TRUE
, LOG_INFO
,
5535 CFSTR("process %s%sconfiguration change"),
5536 forcedChange
? "[forced] " : "",
5537 change_strings
[changes
]);
5540 ReachabilityStoreInfo_init(&store_info
);
5542 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
5543 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
5544 CFSetGetValues(watchers
, targets
);
5545 for (i
= 0; i
< nTargets
; i
++) {
5546 SCNetworkReachabilityRef target
= targets
[i
];
5547 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5549 MUTEX_LOCK(&targetPrivate
->lock
);
5551 if (dnsConfigChanged
) {
5552 targetPrivate
->last_dns
= now
;
5553 targetPrivate
->dnsRetryCount
= 0;
5556 if (networkConfigChanged
) {
5557 targetPrivate
->last_network
= now
;
5560 #if !TARGET_OS_IPHONE
5561 if (powerStatusChanged
) {
5562 targetPrivate
->last_power
= now
;
5564 #endif // !TARGET_OS_IPHONE
5566 if (targetPrivate
->type
== reachabilityTypeName
) {
5567 Boolean dnsChanged
= dnsConfigChanged
;
5571 * if the DNS configuration didn't change we still need to
5572 * check that the DNS servers are accessible.
5574 SCNetworkReachabilityFlags ns_flags
;
5577 /* check the reachability of the DNS servers */
5578 ok
= ReachabilityStoreInfo_update(&store_info
, &store
, AF_UNSPEC
);
5580 ok
= _SC_R_checkResolverReachability(&store_info
,
5582 &targetPrivate
->haveDNS
,
5583 targetPrivate
->name
,
5584 targetPrivate
->serv
,
5585 targetPrivate
->if_index
,
5588 targetPrivate
->log_prefix
);
5592 /* if we could not get DNS server info */
5593 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
5594 targetPrivate
->log_prefix
);
5596 } else if (rankReachability(ns_flags
) < 2) {
5598 * if DNS servers are not (or are no longer) reachable, set
5599 * flags based on the availability of configured (but not
5602 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
5603 targetPrivate
->log_prefix
);
5609 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
5610 /* cancel the outstanding DNS query */
5611 SCLog(_sc_debug
, LOG_INFO
,
5612 CFSTR("%scancel DNS query for %s%s%s%s%s"),
5613 targetPrivate
->log_prefix
,
5614 targetPrivate
->name
!= NULL
? "name = " : "",
5615 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
5616 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
5617 targetPrivate
->serv
!= NULL
? "serv = " : "",
5618 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
5619 dequeueAsyncDNSQuery(target
, TRUE
);
5622 if (targetPrivate
->dnsRetry
!= NULL
) {
5623 /* cancel the outstanding DNS retry */
5624 dequeueAsyncDNSRetry(target
);
5627 /* schedule request to resolve the name again */
5628 targetPrivate
->needResolve
= TRUE
;
5633 targetPrivate
->cycle
++;
5636 if (targetPrivate
->scheduled
) {
5637 __SCNetworkReachabilityPerform(target
);
5640 MUTEX_UNLOCK(&targetPrivate
->lock
);
5642 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
5644 ReachabilityStoreInfo_free(&store_info
);
5648 if (watchers
!= NULL
) CFRelease(watchers
);
5653 #if !TARGET_OS_IPHONE
5656 darkWakeNotify(SCNetworkReachabilityRef target
)
5663 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities
)
5666 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU \
5667 | kIOPMSystemPowerStateCapabilityNetwork \
5668 | kIOPMSystemPowerStateCapabilityDisk)
5670 if ((power_capabilities
& POWER_CAPABILITIES_NEED
) != POWER_CAPABILITIES_NEED
) {
5672 * we're not awake (from a networking point of view) unless we
5673 * have the CPU, disk, *and* network.
5678 if ((power_capabilities
& kIOPMSytemPowerStateCapabilitiesMask
) == POWER_CAPABILITIES_NEED
) {
5680 * if all we have is the CPU, disk, and network than this must
5681 * be a "maintenance" wake.
5689 #endif // !TARGET_OS_IPHONE
5693 reachPerform(void *info
)
5696 void (*context_release
)(const void *);
5698 Boolean defer
= FALSE
;
5701 ReachabilityInfo reach_info
= NOT_REACHABLE
;
5702 SCNetworkReachabilityCallBack rlsFunction
;
5703 ReachabilityStoreInfo store_info
;
5704 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
5705 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5707 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%schecking target reachability"),
5708 targetPrivate
->log_prefix
);
5711 MUTEX_LOCK(&targetPrivate
->lock
);
5713 if (targetPrivate
->dnsRetry
!= NULL
) {
5715 dequeueAsyncDNSRetry(target
);
5718 if (!targetPrivate
->scheduled
) {
5719 // if not currently scheduled
5720 MUTEX_UNLOCK(&targetPrivate
->lock
);
5724 /* update reachability, notify if status changed */
5725 ReachabilityStoreInfo_init(&store_info
);
5726 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
);
5727 ReachabilityStoreInfo_free(&store_info
);
5729 /* if reachability status not available */
5730 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%flags not available"),
5731 targetPrivate
->log_prefix
);
5732 reach_info
= NOT_REACHABLE
;
5735 #if !TARGET_OS_IPHONE
5737 * We want to defer the notification if this is a maintenance wake *and*
5738 * the reachability flags that we would be reporting to the application
5739 * are better than those that we last reported.
5741 if (!systemIsAwake(power_capabilities
)) {
5742 /* if this is a maintenace wake */
5743 reach_info
.sleeping
= TRUE
;
5745 if (rankReachability(reach_info
.flags
) >= rankReachability(targetPrivate
->info
.flags
)) {
5747 * don't report the change if the new reachability flags are
5748 * the same or "better"
5750 defer
= !darkWakeNotify(target
);
5751 } else if (!__reach_changed(&targetPrivate
->last_notify
, &reach_info
)) {
5753 * if we have already posted this change
5755 defer
= !darkWakeNotify(target
);
5758 #endif // !TARGET_OS_IPHONE
5760 cycle
= targetPrivate
->cycle
;
5761 forced
= ((cycle
!= 0) && (targetPrivate
->info
.cycle
!= cycle
));
5763 if (!forced
&& !__reach_changed(&targetPrivate
->info
, &reach_info
)) {
5765 if (targetPrivate
->info
.sleeping
== reach_info
.sleeping
) {
5766 SCLog(TRUE
, LOG_INFO
,
5767 CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"),
5768 targetPrivate
->log_prefix
,
5770 reach_info
.if_index
,
5771 reach_info
.sleeping
? ", z" : "");
5773 SCLog(TRUE
, LOG_INFO
,
5774 CFSTR("%sflags/interface equiv (was 0x%08x/%hu%s, now 0x%08x/%hu%s)"),
5775 targetPrivate
->log_prefix
,
5776 targetPrivate
->info
.flags
,
5777 targetPrivate
->info
.if_index
,
5778 targetPrivate
->info
.sleeping
? ", z" : "",
5780 reach_info
.if_index
,
5781 reach_info
.sleeping
? ", z" : "");
5785 MUTEX_UNLOCK(&targetPrivate
->lock
);
5789 SCLog(_sc_debug
, LOG_INFO
,
5790 CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s%s"),
5791 targetPrivate
->log_prefix
,
5792 targetPrivate
->info
.flags
,
5793 targetPrivate
->info
.if_index
,
5794 targetPrivate
->info
.sleeping
? ", z" : "",
5796 reach_info
.if_index
,
5797 reach_info
.sleeping
? ", z" : "",
5798 defer
? ", deferred" : "",
5799 forced
? ", forced" : "");
5801 /* as needed, defer the notification */
5803 MUTEX_UNLOCK(&targetPrivate
->lock
);
5807 /* update flags / interface */
5808 _reach_set(&targetPrivate
->info
, &reach_info
, cycle
);
5810 /* save last notification info */
5811 _reach_set(&targetPrivate
->last_notify
, &reach_info
, cycle
);
5813 /* save last notification time */
5814 (void)gettimeofday(&targetPrivate
->last_push
, NULL
);
5817 rlsFunction
= targetPrivate
->rlsFunction
;
5818 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
5819 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
5820 context_release
= targetPrivate
->rlsContext
.release
;
5822 context_info
= targetPrivate
->rlsContext
.info
;
5823 context_release
= NULL
;
5826 MUTEX_UNLOCK(&targetPrivate
->lock
);
5828 if (rlsFunction
!= NULL
) {
5829 (*rlsFunction
)(target
,
5830 reach_info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
,
5834 if (context_release
!= NULL
) {
5835 (*context_release
)(context_info
);
5843 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
5844 SCNetworkReachabilityCallBack callout
,
5845 SCNetworkReachabilityContext
*context
)
5847 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5849 MUTEX_LOCK(&targetPrivate
->lock
);
5851 if (targetPrivate
->rlsContext
.release
!= NULL
) {
5852 /* let go of the current context */
5853 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
5856 targetPrivate
->rlsFunction
= callout
;
5857 targetPrivate
->rlsContext
.info
= NULL
;
5858 targetPrivate
->rlsContext
.retain
= NULL
;
5859 targetPrivate
->rlsContext
.release
= NULL
;
5860 targetPrivate
->rlsContext
.copyDescription
= NULL
;
5862 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
5863 if (context
->retain
!= NULL
) {
5864 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
5868 MUTEX_UNLOCK(&targetPrivate
->lock
);
5875 reachRLSCopyDescription(const void *info
)
5877 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
5879 return CFStringCreateWithFormat(NULL
,
5881 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
5887 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
5888 CFRunLoopRef runLoop
,
5889 CFStringRef runLoopMode
,
5890 dispatch_queue_t queue
,
5893 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5894 Boolean init
= FALSE
;
5895 __block Boolean ok
= FALSE
;
5897 MUTEX_LOCK(&targetPrivate
->lock
);
5899 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
5900 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
5901 _SCErrorSet(kSCStatusInvalidArgument
);
5905 #ifdef HAVE_REACHABILITY_SERVER
5906 if (!targetPrivate
->serverBypass
) {
5907 if (!targetPrivate
->serverActive
) {
5908 ok
= __SCNetworkReachabilityServer_targetAdd(target
);
5910 targetPrivate
->serverBypass
= TRUE
;
5914 if (targetPrivate
->serverActive
) {
5915 if (targetPrivate
->scheduled
) {
5916 // if already scheduled
5920 ok
= __SCNetworkReachabilityServer_targetSchedule(target
);
5922 SCLog(TRUE
, LOG_DEBUG
,
5923 CFSTR("__SCNetworkReachabilityScheduleWithRunLoop _targetMonitor() failed"));
5924 _SCErrorSet(kSCStatusFailed
);
5931 #endif // HAVE_REACHABILITY_SERVER
5933 /* schedule the SCNetworkReachability did-something-change handler */
5935 dispatch_sync(_hn_queue(), ^{
5938 if (!onDemand
&& (hn_store
== NULL
)) {
5940 * if we are not monitoring any hosts, start watching
5942 if (!dns_configuration_watch()) {
5944 _SCErrorSet(kSCStatusFailed
);
5948 hn_store
= SCDynamicStoreCreate(NULL
,
5949 CFSTR("SCNetworkReachability"),
5950 __SCNetworkReachabilityHandleChanges
,
5952 if (hn_store
== NULL
) {
5953 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed"));
5954 dns_configuration_unwatch();
5958 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
5960 hn_dispatchQueue
= dispatch_queue_create("SCNetworkReachabilty.changes", NULL
);
5961 if (hn_dispatchQueue
== NULL
) {
5962 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed"));
5963 CFRelease(hn_store
);
5965 dns_configuration_unwatch();
5966 _SCErrorSet(kSCStatusFailed
);
5970 ok
= SCDynamicStoreSetDispatchQueue(hn_store
, hn_dispatchQueue
);
5972 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
5973 dispatch_release(hn_dispatchQueue
);
5974 hn_dispatchQueue
= NULL
;
5975 CFRelease(hn_store
);
5977 dns_configuration_unwatch();
5980 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
5982 ReachabilityStoreInfo_enable(TRUE
);
5985 CFSetAddValue(hn_targets
, target
);
5994 #ifdef HAVE_REACHABILITY_SERVER
5996 #endif // HAVE_REACHABILITY_SERVER
5998 if (!targetPrivate
->scheduled
) {
5999 CFRunLoopSourceContext context
= { 0 // version
6000 , (void *)target
// info
6001 , CFRetain
// retain
6002 , CFRelease
// release
6003 , reachRLSCopyDescription
// copyDescription
6008 , reachPerform
// perform
6011 if (runLoop
!= NULL
) {
6012 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
6013 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
6016 if (targetPrivate
->type
== reachabilityTypeName
) {
6018 * we're now scheduled so let's ensure that we
6019 * are starting with a clean slate before we
6022 if (targetPrivate
->resolvedAddress
!= NULL
) {
6023 CFRelease(targetPrivate
->resolvedAddress
);
6024 targetPrivate
->resolvedAddress
= NULL
;
6026 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
6027 targetPrivate
->needResolve
= TRUE
;
6028 _reach_set(&targetPrivate
->info
, &NOT_REACHABLE
, targetPrivate
->info
.cycle
);
6029 targetPrivate
->info
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
6030 #ifdef HAVE_REACHABILITY_SERVER
6031 _reach_set(&targetPrivate
->serverInfo
, &NOT_REACHABLE
, targetPrivate
->serverInfo
.cycle
);
6032 targetPrivate
->serverInfo
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
6033 #endif // HAVE_REACHABILITY_SERVER
6036 targetPrivate
->scheduled
= TRUE
;
6041 if (queue
!= NULL
) {
6042 // retain dispatch queue
6043 dispatch_retain(queue
);
6044 targetPrivate
->dispatchQueue
= queue
;
6047 // We've taken a reference to the client's dispatch_queue and we
6048 // want to hold on to that reference until we've processed any/all
6049 // notifications. To facilitate this we create a group, dispatch
6050 // any notification blocks to via that group, and when the caller
6051 // has told us to stop the notifications (unschedule) we wait for
6052 // the group to empty and use the group's finalizer to release
6053 // our reference to the client's queue.
6056 // make sure that we have group to track any async requests
6057 targetPrivate
->dispatchGroup
= dispatch_group_create();
6059 // retain the target ... and release it when the group is released
6061 dispatch_set_context(targetPrivate
->dispatchGroup
, (void *)target
);
6062 dispatch_set_finalizer_f(targetPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
6064 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
6066 * if we do not already have host notifications scheduled with
6067 * this runLoop / runLoopMode
6069 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
6071 if (targetPrivate
->dnsRLS
!= NULL
) {
6072 // if we have an active async DNS query too
6073 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
6077 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
6081 ReachabilityInfo reach_info
= NOT_REACHABLE
;
6082 ReachabilityStoreInfo store_info
;
6085 * if we have yet to schedule SC notifications for this address
6086 * - initialize current reachability status
6088 ReachabilityStoreInfo_init(&store_info
);
6089 if (__SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
)) {
6091 * if reachability status available
6093 * - schedule notification to report status via callback
6095 #ifdef HAVE_REACHABILITY_SERVER
6096 reach_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
6097 #endif // HAVE_REACHABILITY_SERVER
6098 _reach_set(&targetPrivate
->info
, &reach_info
, targetPrivate
->cycle
);
6099 __SCNetworkReachabilityPerform(target
);
6101 /* if reachability status not available, async lookup started */
6102 _reach_set(&targetPrivate
->info
, &NOT_REACHABLE
, targetPrivate
->cycle
);
6103 #ifdef HAVE_REACHABILITY_SERVER
6104 _reach_set(&targetPrivate
->serverInfo
, &NOT_REACHABLE
, targetPrivate
->cycle
);
6105 #endif // HAVE_REACHABILITY_SERVER
6107 ReachabilityStoreInfo_free(&store_info
);
6110 if (targetPrivate
->onDemandServer
!= NULL
) {
6111 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, queue
, TRUE
);
6114 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%sscheduled"),
6115 targetPrivate
->log_prefix
);
6121 MUTEX_UNLOCK(&targetPrivate
->lock
);
6127 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
6128 CFRunLoopRef runLoop
,
6129 CFStringRef runLoopMode
,
6132 dispatch_group_t drainGroup
= NULL
;
6133 dispatch_queue_t drainQueue
= NULL
;
6136 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
6138 // hold a reference while we unschedule
6141 MUTEX_LOCK(&targetPrivate
->lock
);
6143 if (((runLoop
== NULL
) && (targetPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
6144 ((runLoop
!= NULL
) && (targetPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are not)
6145 _SCErrorSet(kSCStatusInvalidArgument
);
6149 if (!targetPrivate
->scheduled
) {
6150 // if not currently scheduled
6151 _SCErrorSet(kSCStatusInvalidArgument
);
6155 // unschedule the target specific sources
6156 if (targetPrivate
->dispatchQueue
!= NULL
) {
6157 if (targetPrivate
->onDemandServer
!= NULL
) {
6158 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
6161 // save dispatchQueue, release reference when we've queue'd blocks complete, allow re-scheduling
6162 drainGroup
= targetPrivate
->dispatchGroup
;
6163 targetPrivate
->dispatchGroup
= NULL
;
6164 drainQueue
= targetPrivate
->dispatchQueue
;
6165 targetPrivate
->dispatchQueue
= NULL
;
6167 if (!_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
6168 // if not currently scheduled
6169 _SCErrorSet(kSCStatusInvalidArgument
);
6173 if (targetPrivate
->onDemandServer
!= NULL
) {
6174 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, TRUE
);
6177 n
= CFArrayGetCount(targetPrivate
->rlList
);
6178 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
6179 // if target is no longer scheduled for this runLoop / runLoopMode
6180 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
6182 if (targetPrivate
->dnsRLS
!= NULL
) {
6183 // if we have an active async DNS query too
6184 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
6188 // if *all* notifications have been unscheduled
6189 CFRelease(targetPrivate
->rlList
);
6190 targetPrivate
->rlList
= NULL
;
6191 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
6192 CFRelease(targetPrivate
->rls
);
6193 targetPrivate
->rls
= NULL
;
6199 #ifdef HAVE_REACHABILITY_SERVER
6201 // Cancel our request for server monitoring
6203 if (targetPrivate
->serverActive
) {
6204 ok
= __SCNetworkReachabilityServer_targetUnschedule(target
);
6206 SCLog(TRUE
, LOG_DEBUG
,
6207 CFSTR("__SCNetworkReachabilityUnscheduleFromRunLoop _targetMonitor() failed"));
6208 _SCErrorSet(kSCStatusFailed
);
6211 #endif // HAVE_REACHABILITY_SERVER
6213 // if *all* notifications have been unscheduled
6214 targetPrivate
->scheduled
= FALSE
;
6217 #ifdef HAVE_REACHABILITY_SERVER
6218 if (targetPrivate
->serverActive
) {
6221 #endif // HAVE_REACHABILITY_SERVER
6224 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
6225 // if we have an active async DNS query
6226 dequeueAsyncDNSQuery(target
, TRUE
);
6229 if (targetPrivate
->dnsRetry
!= NULL
) {
6230 // if we have an outstanding DNS retry
6231 dequeueAsyncDNSRetry(target
);
6234 if (targetPrivate
->llqActive
) {
6235 // if we have a long-lived-query
6236 dequeueLongLivedQuery(target
);
6239 dispatch_sync(_hn_queue(), ^{
6240 CFSetRemoveValue(hn_targets
, target
);
6246 if (CFSetGetCount(hn_targets
) > 0) {
6250 // if we are no longer monitoring any targets
6251 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
6252 dispatch_release(hn_dispatchQueue
);
6253 hn_dispatchQueue
= NULL
;
6254 CFRelease(hn_store
);
6256 CFRelease(hn_targets
);
6259 ReachabilityStoreInfo_enable(FALSE
);
6260 ReachabilityStoreInfo_save(NULL
);
6263 * until we start monitoring again, ensure that
6264 * any resources associated with tracking the
6265 * DNS configuration have been released.
6267 dns_configuration_unwatch();
6271 #ifdef HAVE_REACHABILITY_SERVER
6273 #endif // HAVE_REACHABILITY_SERVER
6275 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%sunscheduled"),
6276 targetPrivate
->log_prefix
);
6282 MUTEX_UNLOCK(&targetPrivate
->lock
);
6284 if (drainGroup
!= NULL
) {
6285 dispatch_group_notify(drainGroup
, __SCNetworkReachability_concurrent_queue(), ^{
6286 // release group/queue references
6287 dispatch_release(drainQueue
);
6288 dispatch_release(drainGroup
); // releases our target reference
6292 // release our reference
6299 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
6300 CFRunLoopRef runLoop
,
6301 CFStringRef runLoopMode
)
6303 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
6304 _SCErrorSet(kSCStatusInvalidArgument
);
6308 return __SCNetworkReachabilityScheduleWithRunLoop(target
, runLoop
, runLoopMode
, NULL
, FALSE
);
6312 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
6313 CFRunLoopRef runLoop
,
6314 CFStringRef runLoopMode
)
6316 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
6317 _SCErrorSet(kSCStatusInvalidArgument
);
6321 return __SCNetworkReachabilityUnscheduleFromRunLoop(target
, runLoop
, runLoopMode
, FALSE
);
6325 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
6326 dispatch_queue_t queue
)
6330 if (!isA_SCNetworkReachability(target
)) {
6331 _SCErrorSet(kSCStatusInvalidArgument
);
6335 if (queue
!= NULL
) {
6336 ok
= __SCNetworkReachabilityScheduleWithRunLoop(target
, NULL
, NULL
, queue
, FALSE
);
6338 ok
= __SCNetworkReachabilityUnscheduleFromRunLoop(target
, NULL
, NULL
, FALSE
);