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)"));
1982 if (targetPrivate
->scheduled
) {
1983 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1984 CFStringAppendFormat(result
, NULL
, CFSTR(", %@"), str
);
1988 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
1995 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1997 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
1998 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2000 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%srelease"),
2001 targetPrivate
->log_prefix
);
2003 #ifdef HAVE_REACHABILITY_SERVER
2004 /* disconnect from the reachability server */
2006 if (targetPrivate
->serverActive
) {
2007 __SCNetworkReachabilityServer_targetRemove(target
);
2009 #endif // HAVE_REACHABILITY_SERVER
2011 /* release resources */
2013 pthread_mutex_destroy(&targetPrivate
->lock
);
2015 if (targetPrivate
->name
!= NULL
)
2016 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
2018 if (targetPrivate
->serv
!= NULL
)
2019 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->serv
);
2021 if (targetPrivate
->resolvedAddress
!= NULL
)
2022 CFRelease(targetPrivate
->resolvedAddress
);
2024 if (targetPrivate
->localAddress
!= NULL
)
2025 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
2027 if (targetPrivate
->remoteAddress
!= NULL
)
2028 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
2030 if (targetPrivate
->rlsContext
.release
!= NULL
) {
2031 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
2034 if (targetPrivate
->onDemandName
!= NULL
) {
2035 CFRelease(targetPrivate
->onDemandName
);
2038 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
2039 CFRelease(targetPrivate
->onDemandRemoteAddress
);
2042 if (targetPrivate
->onDemandServer
!= NULL
) {
2043 CFRelease(targetPrivate
->onDemandServer
);
2046 if (targetPrivate
->onDemandServiceID
!= NULL
) {
2047 CFRelease(targetPrivate
->onDemandServiceID
);
2050 #ifdef HAVE_REACHABILITY_SERVER
2051 if (targetPrivate
->serverDigest
!= NULL
) {
2052 CFRelease(targetPrivate
->serverDigest
);
2055 if (targetPrivate
->serverGroup
!= NULL
) {
2056 dispatch_release(targetPrivate
->serverGroup
);
2059 if (targetPrivate
->serverQueue
!= NULL
) {
2060 dispatch_release(targetPrivate
->serverQueue
);
2063 if (targetPrivate
->serverWatchers
!= NULL
) {
2064 CFRelease(targetPrivate
->serverWatchers
);
2066 #endif // HAVE_REACHABILITY_SERVER
2073 __SCNetworkReachabilityInitialize(void)
2075 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
2077 // provide a way to enable SCNetworkReachability logging without
2078 // having to set _sc_debug=1.
2079 if (getenv("REACH_LOGGING") != NULL
) {
2083 // set per-process "bypass" of the SCNetworkReachability server
2084 if (getenv("LONG_LIVED_QUERY_BYPASS") != NULL
) {
2088 #ifdef HAVE_REACHABILITY_SERVER
2089 // set per-process "bypass" of the SCNetworkReachability server
2090 if (getenv("REACH_SERVER_BYPASS") != NULL
) {
2091 D_serverBypass
= TRUE
;
2093 #endif // HAVE_REACHABILITY_SERVER
2095 pthread_mutexattr_init(&lock_attr
);
2096 pthread_mutexattr_settype(&lock_attr
, PTHREAD_MUTEX_ERRORCHECK
);
2104 __SCNetworkReachability_concurrent_queue()
2106 static dispatch_once_t once
;
2107 static dispatch_queue_t q
;
2109 dispatch_once(&once
, ^{
2110 q
= dispatch_queue_create("SCNetworkReachabilty.concurrent",
2111 DISPATCH_QUEUE_CONCURRENT
);
2112 dispatch_queue_set_width(q
, 32);
2120 * __SCNetworkReachabilityPerformInlineNoLock
2122 * Calls reachPerform()
2123 * - caller must be holding a reference to the target
2124 * - caller must *not* be holding the target lock
2125 * - caller must be running on the __SCNetworkReachability_concurrent_queue()
2127 static __inline__
void
2128 __SCNetworkReachabilityPerformInlineNoLock(SCNetworkReachabilityRef target
, Boolean needResolve
)
2130 dispatch_queue_t queue
;
2131 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2133 MUTEX_LOCK(&targetPrivate
->lock
);
2136 // allow the DNS query to be [re-]started
2137 targetPrivate
->needResolve
= TRUE
;
2140 queue
= targetPrivate
->dispatchQueue
;
2141 if (queue
!= NULL
) {
2142 dispatch_group_t group
;
2144 dispatch_retain(queue
);
2146 group
= targetPrivate
->dispatchGroup
;
2147 dispatch_group_enter(group
);
2149 MUTEX_UNLOCK(&targetPrivate
->lock
);
2151 dispatch_sync(queue
, ^{
2152 reachPerform((void *)target
);
2153 dispatch_group_leave(group
);
2154 dispatch_release(queue
);
2157 if (targetPrivate
->rls
!= NULL
) {
2158 CFRunLoopSourceSignal(targetPrivate
->rls
);
2159 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2162 MUTEX_UNLOCK(&targetPrivate
->lock
);
2169 #ifdef HAVE_REACHABILITY_SERVER
2171 * __SCNetworkReachabilityPerformNoLock
2173 * Calls reachPerform()
2174 * - caller must *not* be holding the target lock
2175 * - caller must *not* running on the __SCNetworkReachability_concurrent_queue()
2179 __SCNetworkReachabilityPerformNoLock(SCNetworkReachabilityRef target
)
2182 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
2183 __SCNetworkReachabilityPerformInlineNoLock(target
, FALSE
);
2189 #endif // HAVE_REACHABILITY_SERVER
2193 * __SCNetworkReachabilityPerformConcurrent
2195 * Calls reachPerform()
2196 * - caller must be holding the target lock
2197 * - caller running on the __SCNetworkReachability_concurrent_queue()
2199 static __inline__
void
2200 __SCNetworkReachabilityPerformConcurrent(SCNetworkReachabilityRef target
)
2202 dispatch_queue_t queue
;
2203 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2205 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2207 queue
= targetPrivate
->dispatchQueue
;
2208 if (queue
!= NULL
) {
2209 dispatch_retain(queue
);
2211 dispatch_group_async(targetPrivate
->dispatchGroup
, queue
, ^{
2212 reachPerform((void *)target
);
2214 dispatch_release(queue
);
2217 if (targetPrivate
->rls
!= NULL
) {
2218 CFRunLoopSourceSignal(targetPrivate
->rls
);
2219 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2228 * __SCNetworkReachabilityPerform
2230 * Calls reachPerform()
2231 * - caller must be holding the target lock
2232 * - caller not running on the __SCNetworkReachability_concurrent_queue()
2235 __SCNetworkReachabilityPerform(SCNetworkReachabilityRef target
)
2237 dispatch_queue_t queue
;
2238 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2240 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2242 queue
= targetPrivate
->dispatchQueue
;
2243 if (queue
!= NULL
) {
2244 dispatch_retain(queue
);
2246 dispatch_group_async(targetPrivate
->dispatchGroup
, __SCNetworkReachability_concurrent_queue(), ^{
2247 dispatch_sync(queue
, ^{
2248 reachPerform((void *)target
);
2250 dispatch_release(queue
);
2253 } else if (targetPrivate
->rls
!= NULL
) {
2254 CFRunLoopSourceSignal(targetPrivate
->rls
);
2255 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2262 static SCNetworkReachabilityPrivateRef
2263 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
2265 SCNetworkReachabilityPrivateRef targetPrivate
;
2268 /* initialize runtime */
2269 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
2271 /* allocate target */
2272 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
2273 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
2274 __kSCNetworkReachabilityTypeID
,
2277 if (targetPrivate
== NULL
) {
2281 MUTEX_INIT(&targetPrivate
->lock
);
2283 targetPrivate
->name
= NULL
;
2284 targetPrivate
->serv
= NULL
;
2285 targetPrivate
->hints
= HINTS_DEFAULT
;
2286 targetPrivate
->needResolve
= FALSE
;
2287 targetPrivate
->resolvedAddress
= NULL
;
2288 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
2290 targetPrivate
->if_index
= 0;
2292 targetPrivate
->localAddress
= NULL
;
2293 targetPrivate
->remoteAddress
= NULL
;
2295 targetPrivate
->cycle
= 1;
2296 targetPrivate
->info
= NOT_REACHABLE
;
2297 targetPrivate
->last_notify
= NOT_REPORTED
;
2299 targetPrivate
->scheduled
= FALSE
;
2300 targetPrivate
->rls
= NULL
;
2301 targetPrivate
->rlsFunction
= NULL
;
2302 targetPrivate
->rlsContext
.info
= NULL
;
2303 targetPrivate
->rlsContext
.retain
= NULL
;
2304 targetPrivate
->rlsContext
.release
= NULL
;
2305 targetPrivate
->rlsContext
.copyDescription
= NULL
;
2306 targetPrivate
->rlList
= NULL
;
2308 targetPrivate
->haveDNS
= FALSE
;
2309 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
2310 targetPrivate
->dnsPort
= NULL
;
2311 targetPrivate
->dnsRLS
= NULL
;
2312 targetPrivate
->dnsSource
= NULL
;
2313 targetPrivate
->dnsQueryStart
= TIME_ZERO
;
2314 targetPrivate
->dnsQueryEnd
= TIME_ZERO
;
2315 targetPrivate
->dnsRetry
= NULL
;
2316 targetPrivate
->dnsRetryCount
= 0;
2318 targetPrivate
->last_dns
= TIME_ZERO
;
2319 targetPrivate
->last_network
= TIME_ZERO
;
2320 #if !TARGET_OS_IPHONE
2321 targetPrivate
->last_power
= TIME_ZERO
;
2322 #endif // !TARGET_OS_IPHONE
2323 targetPrivate
->last_push
= TIME_ZERO
;
2325 targetPrivate
->onDemandBypass
= FALSE
;
2326 targetPrivate
->onDemandName
= NULL
;
2327 targetPrivate
->onDemandRemoteAddress
= NULL
;
2328 targetPrivate
->onDemandServer
= NULL
;
2329 targetPrivate
->onDemandServiceID
= NULL
;
2332 targetPrivate
->llqActive
= FALSE
;
2333 targetPrivate
->llqBypass
= D_llqBypass
;
2334 targetPrivate
->llqTarget
= NULL
;
2335 targetPrivate
->llqTimer
= NULL
;
2337 #ifdef HAVE_REACHABILITY_SERVER
2338 targetPrivate
->serverActive
= FALSE
;
2339 targetPrivate
->serverBypass
= D_serverBypass
;
2340 targetPrivate
->serverScheduled
= FALSE
;
2341 targetPrivate
->serverInfo
= NOT_REACHABLE
;
2343 targetPrivate
->serverDigest
= NULL
;
2344 targetPrivate
->serverGroup
= NULL
;
2345 targetPrivate
->serverInfoValid
= FALSE
;
2346 targetPrivate
->serverQueryActive
= 0;
2347 targetPrivate
->serverQueue
= NULL
;
2348 targetPrivate
->serverReferences
= 0;
2349 targetPrivate
->serverWatchers
= NULL
;
2350 #endif // HAVE_REACHABILITY_SERVER
2352 targetPrivate
->log_prefix
[0] = '\0';
2354 snprintf(targetPrivate
->log_prefix
,
2355 sizeof(targetPrivate
->log_prefix
),
2360 return targetPrivate
;
2366 static const struct sockaddr
*
2367 is_valid_address(const struct sockaddr
*address
)
2369 const struct sockaddr
*valid
= NULL
;
2370 static Boolean warned
= FALSE
;
2372 if ((address
!= NULL
) &&
2373 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
2374 switch (address
->sa_family
) {
2376 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
2380 SCLog(TRUE
, LOG_ERR
,
2381 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
2383 sizeof(struct sockaddr_in
));
2389 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
2391 } else if (!warned
) {
2392 SCLog(TRUE
, LOG_ERR
,
2393 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
2395 sizeof(struct sockaddr_in6
));
2401 SCLog(TRUE
, LOG_ERR
,
2402 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"),
2403 address
->sa_family
);
2413 SCNetworkReachabilityRef
2414 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
2415 const struct sockaddr
*address
)
2417 SCNetworkReachabilityPrivateRef targetPrivate
;
2419 address
= is_valid_address(address
);
2420 if (address
== NULL
) {
2421 _SCErrorSet(kSCStatusInvalidArgument
);
2425 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2426 if (targetPrivate
== NULL
) {
2430 targetPrivate
->type
= reachabilityTypeAddress
;
2431 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
2432 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
2434 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
2435 targetPrivate
->log_prefix
,
2436 DEBUG_REACHABILITY_TYPE_ADDRESS
,
2439 return (SCNetworkReachabilityRef
)targetPrivate
;
2443 SCNetworkReachabilityRef
2444 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
2445 const struct sockaddr
*localAddress
,
2446 const struct sockaddr
*remoteAddress
)
2448 SCNetworkReachabilityPrivateRef targetPrivate
;
2450 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
2451 _SCErrorSet(kSCStatusInvalidArgument
);
2455 if (localAddress
!= NULL
) {
2456 localAddress
= is_valid_address(localAddress
);
2457 if (localAddress
== NULL
) {
2458 _SCErrorSet(kSCStatusInvalidArgument
);
2463 if (remoteAddress
!= NULL
) {
2464 remoteAddress
= is_valid_address(remoteAddress
);
2465 if (remoteAddress
== NULL
) {
2466 _SCErrorSet(kSCStatusInvalidArgument
);
2471 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2472 if (targetPrivate
== NULL
) {
2476 targetPrivate
->type
= reachabilityTypeAddressPair
;
2478 if (localAddress
!= NULL
) {
2479 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
2480 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
2483 if (remoteAddress
!= NULL
) {
2484 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
2485 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
2488 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
2489 targetPrivate
->log_prefix
,
2490 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR
,
2493 return (SCNetworkReachabilityRef
)targetPrivate
;
2497 SCNetworkReachabilityRef
2498 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
2499 const char *nodename
)
2503 struct sockaddr_in sin
;
2504 struct sockaddr_in6 sin6
;
2507 SCNetworkReachabilityPrivateRef targetPrivate
;
2509 if (nodename
== NULL
) {
2510 _SCErrorSet(kSCStatusInvalidArgument
);
2514 nodenameLen
= strlen(nodename
);
2515 if (nodenameLen
== 0) {
2516 _SCErrorSet(kSCStatusInvalidArgument
);
2520 if (_SC_string_to_sockaddr(nodename
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) != NULL
) {
2521 /* if this "nodename" is really an IP[v6] address in disguise */
2522 return SCNetworkReachabilityCreateWithAddress(allocator
, &addr
.sa
);
2525 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2526 if (targetPrivate
== NULL
) {
2530 targetPrivate
->type
= reachabilityTypeName
;
2532 targetPrivate
->name
= CFAllocatorAllocate(NULL
, nodenameLen
+ 1, 0);
2533 strlcpy((char *)targetPrivate
->name
, nodename
, nodenameLen
+ 1);
2535 targetPrivate
->needResolve
= TRUE
;
2536 targetPrivate
->info
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
2537 #ifdef HAVE_REACHABILITY_SERVER
2538 targetPrivate
->serverInfo
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
2539 #endif // HAVE_REACHABILITY_SERVER
2541 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
2542 targetPrivate
->log_prefix
,
2543 DEBUG_REACHABILITY_TYPE_NAME
,
2546 return (SCNetworkReachabilityRef
)targetPrivate
;
2552 SCNetworkReachabilityRef
2553 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
2554 CFDictionaryRef options
)
2556 const struct sockaddr
*addr_l
= NULL
;
2557 const struct sockaddr
*addr_r
= NULL
;
2559 struct addrinfo
*hints
= NULL
;
2560 CFStringRef interface
= NULL
;
2561 CFBooleanRef llqBypass
;
2562 CFStringRef nodename
;
2563 CFBooleanRef onDemandBypass
;
2564 CFBooleanRef resolverBypass
;
2565 #ifdef HAVE_REACHABILITY_SERVER
2566 CFBooleanRef serverBypass
;
2567 #endif // HAVE_REACHABILITY_SERVER
2568 CFStringRef servname
;
2569 SCNetworkReachabilityRef target
;
2570 SCNetworkReachabilityPrivateRef targetPrivate
;
2572 if (!isA_CFDictionary(options
)) {
2573 _SCErrorSet(kSCStatusInvalidArgument
);
2577 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
2578 if ((nodename
!= NULL
) &&
2579 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
2580 _SCErrorSet(kSCStatusInvalidArgument
);
2583 servname
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServName
);
2584 if ((servname
!= NULL
) &&
2585 (!isA_CFString(servname
) || (CFStringGetLength(servname
) == 0))) {
2586 _SCErrorSet(kSCStatusInvalidArgument
);
2589 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
2591 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2592 _SCErrorSet(kSCStatusInvalidArgument
);
2595 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2597 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
2599 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2600 _SCErrorSet(kSCStatusInvalidArgument
);
2603 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2605 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionHints
);
2607 if (!isA_CFData(data
) || (CFDataGetLength(data
) != sizeof(targetPrivate
->hints
))) {
2608 _SCErrorSet(kSCStatusInvalidArgument
);
2612 /* ALIGN: CF aligns to >8 byte boundries */
2613 hints
= (struct addrinfo
*)(void *)CFDataGetBytePtr(data
);
2614 if ((hints
->ai_addrlen
!= 0) ||
2615 (hints
->ai_addr
!= NULL
) ||
2616 (hints
->ai_canonname
!= NULL
) ||
2617 (hints
->ai_next
!= NULL
)) {
2618 _SCErrorSet(kSCStatusInvalidArgument
);
2622 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
2623 if ((interface
!= NULL
) &&
2624 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
2625 _SCErrorSet(kSCStatusInvalidArgument
);
2628 onDemandBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandBypass
);
2629 if ((onDemandBypass
!= NULL
) && !isA_CFBoolean(onDemandBypass
)) {
2630 _SCErrorSet(kSCStatusInvalidArgument
);
2633 resolverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionResolverBypass
);
2634 if ((resolverBypass
!= NULL
) && !isA_CFBoolean(resolverBypass
)) {
2635 _SCErrorSet(kSCStatusInvalidArgument
);
2640 llqBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLongLivedQueryBypass
);
2641 if ((llqBypass
!= NULL
) && !isA_CFBoolean(llqBypass
)) {
2642 _SCErrorSet(kSCStatusInvalidArgument
);
2646 #ifdef HAVE_REACHABILITY_SERVER
2647 serverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServerBypass
);
2648 if ((serverBypass
!= NULL
) && !isA_CFBoolean(serverBypass
)) {
2649 _SCErrorSet(kSCStatusInvalidArgument
);
2652 #endif // HAVE_REACHABILITY_SERVER
2654 if ((nodename
!= NULL
) || (servname
!= NULL
)) {
2657 if ((addr_l
!= NULL
) || (addr_r
!= NULL
)) {
2658 // can't have both a name/serv and an address
2659 _SCErrorSet(kSCStatusInvalidArgument
);
2663 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
2664 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
2665 CFAllocatorDeallocate(NULL
, (void *)name
);
2667 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
2668 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
2669 } else if (addr_r
!= NULL
) {
2670 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
2671 } else if (addr_l
!= NULL
) {
2672 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_l
);
2674 _SCErrorSet(kSCStatusInvalidArgument
);
2678 if (target
== NULL
) {
2682 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2683 if (targetPrivate
->type
== reachabilityTypeName
) {
2684 if (servname
!= NULL
) {
2685 targetPrivate
->serv
= _SC_cfstring_to_cstring(servname
, NULL
, 0, kCFStringEncodingUTF8
);
2687 if (hints
!= NULL
) {
2688 bcopy(hints
, &targetPrivate
->hints
, sizeof(targetPrivate
->hints
));
2692 if (interface
!= NULL
) {
2693 if ((_SC_cfstring_to_cstring(interface
,
2694 targetPrivate
->if_name
,
2695 sizeof(targetPrivate
->if_name
),
2696 kCFStringEncodingASCII
) == NULL
) ||
2697 ((targetPrivate
->if_index
= if_nametoindex(targetPrivate
->if_name
)) == 0)) {
2698 CFRelease(targetPrivate
);
2699 _SCErrorSet(kSCStatusInvalidArgument
);
2705 if (llqBypass
!= NULL
) {
2706 targetPrivate
->llqBypass
= CFBooleanGetValue(llqBypass
);
2709 if (onDemandBypass
!= NULL
) {
2710 targetPrivate
->onDemandBypass
= CFBooleanGetValue(onDemandBypass
);
2713 if (resolverBypass
!= NULL
) {
2714 targetPrivate
->resolverBypass
= CFBooleanGetValue(resolverBypass
);
2717 #ifdef HAVE_REACHABILITY_SERVER
2718 if (serverBypass
!= NULL
) {
2719 targetPrivate
->serverBypass
= CFBooleanGetValue(serverBypass
);
2721 #endif // HAVE_REACHABILITY_SERVER
2723 if (_sc_debug
&& (_sc_log
> 0)) {
2726 switch (targetPrivate
->type
) {
2727 case reachabilityTypeName
:
2728 opt
= DEBUG_REACHABILITY_TYPE_NAME_OPTIONS
;
2730 case reachabilityTypeAddress
:
2731 opt
= DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS
;
2733 case reachabilityTypeAddressPair
:
2734 opt
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS
;
2741 SCLog(TRUE
, LOG_INFO
, CFSTR("%s%s %@"),
2742 targetPrivate
->log_prefix
,
2747 return (SCNetworkReachabilityRef
)targetPrivate
;
2752 SCNetworkReachabilityGetTypeID(void)
2754 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
2755 return __kSCNetworkReachabilityTypeID
;
2759 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
2760 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
2763 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2765 if (!isA_SCNetworkReachability(target
)) {
2766 _SCErrorSet(kSCStatusInvalidArgument
);
2770 if (targetPrivate
->type
!= reachabilityTypeName
) {
2771 _SCErrorSet(kSCStatusInvalidArgument
);
2776 *error_num
= targetPrivate
->resolvedAddressError
;
2779 if (targetPrivate
->resolvedAddress
!= NULL
) {
2780 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
2781 return CFRetain(targetPrivate
->resolvedAddress
);
2783 /* if status is known but no resolved addresses to return */
2784 _SCErrorSet(kSCStatusOK
);
2789 _SCErrorSet(kSCStatusReachabilityUnknown
);
2795 __SCNetworkReachabilitySetResolvedAddress(int32_t status
,
2796 struct addrinfo
*res
,
2797 SCNetworkReachabilityRef target
)
2799 struct addrinfo
*resP
;
2800 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2802 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2804 if (targetPrivate
->resolvedAddress
!= NULL
) {
2805 CFRelease(targetPrivate
->resolvedAddress
);
2806 targetPrivate
->resolvedAddress
= NULL
;
2809 if ((status
== 0) && (res
!= NULL
)) {
2810 CFMutableArrayRef addresses
;
2812 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2814 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
2816 CFDataRef newAddress
;
2818 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
2819 n
= CFArrayGetCount(addresses
);
2821 !CFArrayContainsValue(addresses
, CFRangeMake(0, n
), newAddress
)) {
2822 CFArrayAppendValue(addresses
, newAddress
);
2824 CFRelease(newAddress
);
2827 /* save the resolved address[es] */
2828 targetPrivate
->resolvedAddress
= addresses
;
2829 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
2831 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sgetaddrinfo() failed: %s"),
2832 targetPrivate
->log_prefix
,
2833 gai_strerror(status
));
2835 /* save the error associated with the attempt to resolve the name */
2836 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
2837 targetPrivate
->resolvedAddressError
= status
;
2839 targetPrivate
->needResolve
= FALSE
;
2841 if (res
!= NULL
) freeaddrinfo(res
);
2843 if (targetPrivate
->scheduled
) {
2844 __SCNetworkReachabilityPerform(target
);
2852 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo
*res
, void *context
)
2854 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
2855 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2857 __dns_query_end(target
,
2858 ((status
== 0) && (res
!= NULL
)), // if successful query
2859 dns_query_async
, // async
2860 &targetPrivate
->dnsQueryStart
, // start time
2861 &targetPrivate
->dnsQueryEnd
); // end time
2863 __SCNetworkReachabilitySetResolvedAddress(status
, res
, target
);
2869 * rankReachability()
2870 * Not reachable == 0
2871 * Connection Required == 1
2875 rankReachability(SCNetworkReachabilityFlags flags
)
2879 if (flags
& kSCNetworkReachabilityFlagsReachable
) rank
= 2;
2880 if (flags
& kSCNetworkReachabilityFlagsConnectionRequired
) rank
= 1;
2886 #pragma mark DNS name resolution
2890 replyMPCopyDescription(const void *info
)
2892 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2893 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2895 return CFStringCreateWithFormat(NULL
,
2897 CFSTR("<getaddrinfo_async_start reply MP> {%s%s%s%s%s, target = %p}"),
2898 targetPrivate
->name
!= NULL
? "name = " : "",
2899 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
2900 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
2901 targetPrivate
->serv
!= NULL
? "serv = " : "",
2902 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "",
2908 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
);
2912 enqueueAsyncDNSQuery_dispatch(SCNetworkReachabilityRef target
)
2915 dispatch_source_t source
;
2916 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2918 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2920 mp
= targetPrivate
->dnsMP
;
2922 // mach_port context <-- NULL (no msg received)
2923 mach_port_set_context(mach_task_self(), mp
, (mach_vm_address_t
)(uintptr_t)NULL
);
2925 // create dispatch source to handle DNS reply
2926 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
,
2929 __SCNetworkReachability_concurrent_queue());
2930 if (source
== NULL
) {
2931 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_source_create() failed"));
2936 // We created the dispatch_source to listen for (and process) the mach IPC
2937 // reply to our async DNS query. Because the source handler runs asychronously
2938 // we need to ensure that we're holding a reference to the target. Here, we take
2939 // a reference and setup the dispatch_source finalizer to drop it.
2942 dispatch_set_context(source
, (void *)target
);
2943 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
2945 dispatch_source_set_event_handler(source
, ^{
2946 mach_msg_size_t msg_size
= 8192;
2947 const mach_msg_options_t options
= MACH_RCV_MSG
2949 | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX
)
2950 | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
);
2954 mach_msg_header_t
*msg
= (mach_msg_header_t
*)malloc(msg_size
);
2956 kr
= mach_msg(msg
, /* msg */
2957 options
, /* options */
2959 msg_size
, /* rcv_size */
2961 MACH_MSG_TIMEOUT_NONE
, /* timeout */
2962 MACH_PORT_NULL
); /* notify */
2963 if (kr
== KERN_SUCCESS
) {
2964 // mach_port context <-- msg
2965 mach_port_set_context(mach_task_self(),
2967 (mach_vm_address_t
)(uintptr_t)msg
);
2968 } else if (kr
== MACH_RCV_TOO_LARGE
) {
2973 SCLog(TRUE
, LOG_ERR
,
2974 CFSTR("SCNetworkReachability async DNS handler, kr=0x%x"),
2981 dispatch_source_cancel(source
);
2984 dispatch_source_set_cancel_handler(source
, ^{
2985 #if !TARGET_OS_EMBEDDED
2986 mach_vm_address_t context
;
2987 #else // !TARGET_OS_EMBEDDED
2988 mach_port_context_t context
;
2989 #endif // !TARGET_OS_EMBEDDED
2993 // get the [async DNS query] mach port
2994 mp
= (mach_port_t
)dispatch_source_get_handle(source
);
2996 // check if we have a received message
2997 kr
= mach_port_get_context(mach_task_self(), mp
, &context
);
2998 if (kr
== KERN_SUCCESS
) {
3001 msg
= (void *)(uintptr_t)context
;
3003 MUTEX_LOCK(&targetPrivate
->lock
);
3004 getaddrinfo_async_handle_reply(msg
);
3005 targetPrivate
->dnsSource
= NULL
;
3006 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
3007 MUTEX_UNLOCK(&targetPrivate
->lock
);
3010 getaddrinfo_async_cancel(mp
);
3014 dispatch_release(source
);
3017 targetPrivate
->dnsSource
= source
;
3018 dispatch_resume(source
);
3025 enqueueAsyncDNSQuery_CF(SCNetworkReachabilityRef target
)
3027 CFMachPortContext context
= { 0
3031 , replyMPCopyDescription
3036 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3038 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3040 mp
= targetPrivate
->dnsMP
;
3042 targetPrivate
->dnsPort
= _SC_CFMachPortCreateWithPort("SCNetworkReachability",
3044 getaddrinfo_async_handleCFReply
,
3046 if (targetPrivate
->dnsPort
== NULL
) {
3047 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability CFMachPortCreateWithPort() failed"));
3051 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
3052 if (targetPrivate
->dnsRLS
== NULL
) {
3053 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability CFMachPortCreateRunLoopSource() failed"));
3057 n
= CFArrayGetCount(targetPrivate
->rlList
);
3058 for (i
= 0; i
< n
; i
+= 3) {
3059 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
3060 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
3062 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
3069 if (targetPrivate
->dnsRLS
!= NULL
) {
3070 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
3071 CFRelease(targetPrivate
->dnsRLS
);
3072 targetPrivate
->dnsRLS
= NULL
;
3074 if (targetPrivate
->dnsPort
!= NULL
) {
3075 CFMachPortInvalidate(targetPrivate
->dnsPort
);
3076 CFRelease(targetPrivate
->dnsPort
);
3077 targetPrivate
->dnsPort
= NULL
;
3085 enqueueAsyncDNSQuery(SCNetworkReachabilityRef target
, mach_port_t mp
)
3088 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3090 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3092 targetPrivate
->dnsMP
= mp
;
3094 if (targetPrivate
->dispatchQueue
!= NULL
) {
3095 ok
= enqueueAsyncDNSQuery_dispatch(target
);
3096 } else if (targetPrivate
->rls
!= NULL
) {
3097 ok
= enqueueAsyncDNSQuery_CF(target
);
3101 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
3102 _SCErrorSet(kSCStatusFailed
);
3111 dequeueAsyncDNSQuery(SCNetworkReachabilityRef target
, Boolean cancel
)
3113 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3115 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3117 if (targetPrivate
->dnsPort
!= NULL
) {
3118 CFMachPortInvalidate(targetPrivate
->dnsPort
);
3119 CFRelease(targetPrivate
->dnsPort
);
3120 targetPrivate
->dnsPort
= NULL
;
3123 if (targetPrivate
->dnsRLS
!= NULL
) {
3124 CFRelease(targetPrivate
->dnsRLS
);
3125 targetPrivate
->dnsRLS
= NULL
;
3128 if (targetPrivate
->dnsSource
!= NULL
) {
3129 dispatch_source_cancel(targetPrivate
->dnsSource
);
3130 targetPrivate
->dnsSource
= NULL
;
3131 cancel
= FALSE
; // the cancellation handler does the work
3134 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
3136 getaddrinfo_async_cancel(targetPrivate
->dnsMP
);
3138 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
3146 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
3148 mach_port_t mp
= CFMachPortGetPort(port
);
3150 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
3151 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3153 MUTEX_LOCK(&targetPrivate
->lock
);
3155 if (mp
!= targetPrivate
->dnsMP
) {
3156 // we've received a callback on the async DNS port but since the
3157 // associated CFMachPort doesn't match than the request must have
3158 // already been cancelled.
3159 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP"));
3160 MUTEX_UNLOCK(&targetPrivate
->lock
);
3164 dequeueAsyncDNSQuery(target
, FALSE
);
3165 status
= getaddrinfo_async_handle_reply(msg
);
3166 if ((status
== 0) &&
3167 (targetPrivate
->resolvedAddress
== NULL
) && (targetPrivate
->resolvedAddressError
== NETDB_SUCCESS
)) {
3170 // if the request is not complete and needs to be re-queued
3171 ok
= enqueueAsyncDNSQuery(target
, mp
);
3173 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply enqueueAsyncDNSQuery() failed"));
3177 MUTEX_UNLOCK(&targetPrivate
->lock
);
3184 check_resolver_reachability(ReachabilityStoreInfoRef store_info
,
3185 dns_resolver_t
*resolver
,
3186 SCNetworkReachabilityFlags
*flags
,
3188 uint32_t *resolver_if_index
,
3189 const char *log_prefix
)
3193 if (resolver_if_index
) *resolver_if_index
= 0;
3195 if (resolver
->n_nameserver
> 0) {
3196 #if !TARGET_IPHONE_SIMULATOR
3197 *flags
= (SCNetworkReachabilityFlags
)resolver
->reach_flags
;
3198 if (resolver_if_index
!= NULL
) {
3199 *resolver_if_index
= resolver
->if_index
;
3201 #else // !TARGET_IPHONE_SIMULATOR
3204 *flags
= kSCNetworkReachabilityFlagsReachable
;
3206 for (i
= 0; i
< resolver
->n_nameserver
; i
++) {
3207 struct sockaddr
*address
= resolver
->nameserver
[i
];
3208 ReachabilityInfo ns_info
;
3210 ok
= checkAddress(store_info
, address
, resolver
->if_index
, &ns_info
, log_prefix
);
3217 (rankReachability(ns_info
.flags
) < rankReachability(*flags
))) {
3218 /* return the worst case result */
3219 *flags
= ns_info
.flags
;
3220 if (resolver_if_index
!= NULL
) {
3221 *resolver_if_index
= ns_info
.if_index
;
3225 #endif // !TARGET_IPHONE_SIMULATOR
3228 *flags
= kSCNetworkReachabilityFlagsReachable
;
3237 check_matching_resolvers(ReachabilityStoreInfoRef store_info
,
3238 dns_config_t
*dns_config
,
3240 unsigned int if_index
,
3241 SCNetworkReachabilityFlags
*flags
,
3243 uint32_t *resolver_if_index
,
3244 int *dns_config_index
,
3245 const char *log_prefix
)
3248 Boolean matched
= FALSE
;
3249 const char *name
= fqdn
;
3250 int32_t n_resolvers
;
3251 dns_resolver_t
**resolvers
;
3253 if (if_index
== 0) {
3254 n_resolvers
= dns_config
->n_resolver
;
3255 resolvers
= dns_config
->resolver
;
3257 n_resolvers
= dns_config
->n_scoped_resolver
;
3258 resolvers
= dns_config
->scoped_resolver
;
3261 /* In case we couldn't find a match, setting an index of -1
3262 and resolver_if_index 0 */
3263 if (dns_config_index
!= NULL
) *dns_config_index
= -1;
3264 if (resolver_if_index
!= NULL
) *resolver_if_index
= 0;
3266 while (!matched
&& (name
!= NULL
)) {
3270 * check if the provided name (or sub-component)
3271 * matches one of our resolver configurations.
3274 for (i
= 0; i
< n_resolvers
; i
++) {
3276 dns_resolver_t
*resolver
;
3278 resolver
= resolvers
[i
];
3279 if ((if_index
!= 0) && (if_index
!= resolver
->if_index
)) {
3283 domain
= resolver
->domain
;
3284 if (domain
!= NULL
&& (len
== strlen(domain
))) {
3285 if (strcasecmp(name
, domain
) == 0) {
3289 * if name matches domain
3292 ok
= check_resolver_reachability(store_info
, resolver
, flags
, haveDNS
,
3293 resolver_if_index
, log_prefix
);
3298 if (dns_config_index
!= NULL
) *dns_config_index
= i
;
3305 * we have not found a matching resolver, try
3306 * a less qualified domain
3308 name
= strchr(name
, '.');
3309 if ((name
!= NULL
) && (*name
!= '\0')) {
3321 static dns_resolver_t
*
3322 get_default_resolver(dns_config_t
*dns_config
, unsigned int if_index
)
3325 int32_t n_resolvers
;
3326 dns_resolver_t
*resolver
= NULL
;
3327 dns_resolver_t
**resolvers
;
3329 if (if_index
== 0) {
3330 n_resolvers
= dns_config
->n_resolver
;
3331 resolvers
= dns_config
->resolver
;
3333 n_resolvers
= dns_config
->n_scoped_resolver
;
3334 resolvers
= dns_config
->scoped_resolver
;
3337 for (i
= 0; i
< n_resolvers
; i
++) {
3338 if ((if_index
!= 0) && (if_index
!= resolvers
[i
]->if_index
)) {
3342 if (((if_index
== 0) && (i
== 0)) ||
3343 ((if_index
!= 0) && (resolver
== NULL
))) {
3344 // if this is the first (aka default) resolver
3345 resolver
= resolvers
[i
];
3346 } else if ((resolvers
[i
]->domain
== NULL
) &&
3347 (resolvers
[i
]->search_order
< resolver
->search_order
)) {
3348 // if this is a default resolver with a lower search order
3349 resolver
= resolvers
[i
];
3357 static dns_configuration_t
*
3358 dns_configuration_retain()
3360 pthread_mutex_lock(&dns_lock
);
3362 if ((dns_configuration
!= NULL
) && dns_token_valid
) {
3367 * check if the global [DNS] configuration snapshot needs
3370 status
= notify_check(dns_token
, &check
);
3371 if (status
!= NOTIFY_STATUS_OK
) {
3372 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
3375 if ((status
!= NOTIFY_STATUS_OK
) || (check
!= 0)) {
3377 * if the snapshot needs to be refreshed
3379 if (dns_configuration
->refs
== 0) {
3380 dns_configuration_free(dns_configuration
->config
);
3381 CFAllocatorDeallocate(NULL
, dns_configuration
);
3383 dns_configuration
= NULL
;
3387 if (dns_configuration
== NULL
) {
3388 dns_config_t
*new_config
;
3390 new_config
= dns_configuration_copy();
3391 if (new_config
!= NULL
) {
3392 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
3393 dns_configuration
->config
= new_config
;
3394 dns_configuration
->refs
= 0;
3398 if (dns_configuration
!= NULL
) {
3399 dns_configuration
->refs
++;
3402 pthread_mutex_unlock(&dns_lock
);
3403 return dns_configuration
;
3408 dns_configuration_release(dns_configuration_t
*config
)
3410 pthread_mutex_lock(&dns_lock
);
3413 if (config
->refs
== 0) {
3414 if ((dns_configuration
!= config
)) {
3415 dns_configuration_free(config
->config
);
3416 CFAllocatorDeallocate(NULL
, config
);
3420 pthread_mutex_unlock(&dns_lock
);
3426 dns_configuration_watch()
3429 const char *dns_key
;
3433 pthread_mutex_lock(&dns_lock
);
3435 dns_key
= dns_configuration_notify_key();
3436 if (dns_key
== NULL
) {
3437 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
3441 status
= notify_register_check(dns_key
, &dns_token
);
3442 if (status
== NOTIFY_STATUS_OK
) {
3443 dns_token_valid
= TRUE
;
3445 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
);
3449 status
= notify_check(dns_token
, &dns_check
);
3450 if (status
!= NOTIFY_STATUS_OK
) {
3451 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
3452 (void)notify_cancel(dns_token
);
3453 dns_token_valid
= FALSE
;
3461 pthread_mutex_unlock(&dns_lock
);
3467 dns_configuration_unwatch()
3469 pthread_mutex_lock(&dns_lock
);
3471 (void)notify_cancel(dns_token
);
3472 dns_token_valid
= FALSE
;
3474 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
3475 dns_configuration_free(dns_configuration
->config
);
3476 CFAllocatorDeallocate(NULL
, dns_configuration
);
3477 dns_configuration
= NULL
;
3480 pthread_mutex_unlock(&dns_lock
);
3486 _SC_R_checkResolverReachability(ReachabilityStoreInfoRef store_info
,
3487 SCNetworkReachabilityFlags
*flags
,
3489 const char *nodename
,
3490 const char *servname
,
3491 unsigned int if_index
,
3492 uint32_t *resolver_if_index
,
3493 int *dns_config_index
,
3494 const char *log_prefix
3497 dns_resolver_t
*default_resolver
;
3498 dns_configuration_t
*dns
;
3499 Boolean found
= FALSE
;
3500 char *fqdn
= (char *)nodename
;
3502 Boolean isFQDN
= FALSE
;
3506 Boolean useDefault
= FALSE
;
3508 if (resolver_if_index
) *resolver_if_index
= 0;
3509 if (dns_config_index
) *dns_config_index
= -1;
3512 * We first assume that all of the configured DNS servers
3513 * are available. Since we don't know which name server will
3514 * be consulted to resolve the specified nodename we need to
3515 * check the availability of ALL name servers. We can only
3516 * proceed if we know that our query can be answered.
3519 *flags
= kSCNetworkReachabilityFlagsReachable
;
3522 len
= (nodename
!= NULL
) ? strlen(nodename
) : 0;
3524 if ((servname
== NULL
) || (strlen(servname
) == 0)) {
3525 // if no nodename or servname, return not reachable
3531 dns
= dns_configuration_retain();
3534 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no configuration"), log_prefix
);
3538 if (dns
->config
->n_resolver
== 0) {
3539 // if no resolver configuration
3540 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no resolvers"), log_prefix
);
3544 if (fqdn
[len
- 1] == '.') {
3547 // trim trailing '.''s
3548 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
3549 if (fqdn
== nodename
) {
3550 fqdn
= strdup(nodename
);
3556 default_resolver
= get_default_resolver(dns
->config
, if_index
);
3559 * check if the provided name matches a supplemental domain
3561 found
= check_matching_resolvers(store_info
, dns
->config
, fqdn
, if_index
,
3562 flags
, haveDNS
, resolver_if_index
,
3563 dns_config_index
, log_prefix
);
3565 if (!found
&& !isFQDN
) {
3567 * if we did not match a supplemental domain name and if the
3568 * provided name has enough "."s then the first query will be
3569 * directed to the default resolver.
3574 #define NDOTS_OPT "ndots="
3575 #define NDOTS_OPT_LEN (sizeof("ndots=") - 1)
3577 if (default_resolver
->options
!= NULL
) {
3578 cp
= strstr(default_resolver
->options
, NDOTS_OPT
);
3580 ((cp
== default_resolver
->options
) || isspace(cp
[-1])) &&
3581 ((cp
[NDOTS_OPT_LEN
] != '\0') && isdigit(cp
[NDOTS_OPT_LEN
]))) {
3585 cp
+= NDOTS_OPT_LEN
;
3587 val
= strtol(cp
, &end
, 10);
3588 if ((*cp
!= '\0') && (cp
!= end
) && (errno
== 0) &&
3589 ((*end
== '\0') || isspace(*end
))) {
3596 for (cp
= fqdn
; *cp
!= '\0'; cp
++) {
3597 if (*cp
== '.') dots
++;
3605 if (!found
&& !isFQDN
&& !useDefault
&& (dns
->config
->n_resolver
> 1)) {
3607 * FQDN not specified, try matching w/search domains
3609 if (default_resolver
->n_search
> 0) {
3610 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
3612 char *search_fqdn
= NULL
;
3614 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
3619 // try the provided name with the search domain appended
3620 found
= check_matching_resolvers(store_info
,
3631 } else if (default_resolver
->domain
!= NULL
) {
3633 int domain_parts
= 0;
3635 // count domain parts
3636 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
3642 // remove trailing dots
3643 for (dp
--; (dp
>= default_resolver
->domain
) && (*dp
== '.'); dp
--) {
3648 if (dp
>= default_resolver
->domain
) {
3649 // dots are separators, bump # of components
3653 dp
= default_resolver
->domain
;
3654 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= (domain_parts
- ndots
)); i
++) {
3656 char *search_fqdn
= NULL
;
3658 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
3663 // try the provided name with the [default] domain appended
3664 found
= check_matching_resolvers(store_info
,
3675 // move to the next component of the [default] domain
3676 dp
= strchr(dp
, '.') + 1;
3683 * check the reachability of the default resolver
3685 ok
= check_resolver_reachability(store_info
, default_resolver
, flags
, haveDNS
,
3686 resolver_if_index
, log_prefix
);
3687 if (ok
&& dns_config_index
!= NULL
) *dns_config_index
= 0;
3690 if (fqdn
!= nodename
) free(fqdn
);
3695 dns_configuration_release(dns
);
3703 _SC_checkResolverReachability(SCDynamicStoreRef
*storeP
,
3704 SCNetworkReachabilityFlags
*flags
,
3706 const char *nodename
,
3707 const char *servname
)
3710 ReachabilityStoreInfo store_info
;
3712 ReachabilityStoreInfo_init(&store_info
);
3713 ok
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
);
3718 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, nodename
,
3719 servname
, 0, NULL
, NULL
, "");
3723 ReachabilityStoreInfo_free(&store_info
);
3728 __SC_checkResolverReachabilityInternal(SCDynamicStoreRef
*storeP
,
3729 SCNetworkReachabilityFlags
*flags
,
3731 const char *nodename
,
3732 const char *servname
,
3733 uint32_t *resolver_if_index
,
3734 int *dns_config_index
)
3737 ReachabilityStoreInfo store_info
;
3739 ReachabilityStoreInfo_init(&store_info
);
3740 ok
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
);
3745 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, nodename
,
3746 servname
, 0, resolver_if_index
, dns_config_index
, "");
3750 ReachabilityStoreInfo_free(&store_info
);
3755 * _SC_checkResolverReachabilityByAddress()
3757 * Given an IP address, determine whether a reverse DNS query can be issued
3758 * using the current network configuration.
3761 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
3762 SCNetworkReachabilityFlags
*flags
,
3764 struct sockaddr
*sa
)
3769 ReachabilityStoreInfo store_info
;
3771 ReachabilityStoreInfo_init(&store_info
);
3772 ok
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
);
3778 * Ideally, we would have an API that given a local IP
3779 * address would return the DNS server(s) that would field
3780 * a given PTR query. Fortunately, we do have an SPI which
3781 * which will provide this information given a "name" so we
3782 * take the address, convert it into the inverse query name,
3783 * and find out which servers should be consulted.
3786 switch (sa
->sa_family
) {
3792 /* ALIGN: assuming sa is aligned, then cast ok. */
3793 struct sockaddr_in
*sin
= (struct sockaddr_in
*)(void *)sa
;
3796 * build "PTR" query name
3797 * NNN.NNN.NNN.NNN.in-addr.arpa.
3799 rev
.s_addr
= sin
->sin_addr
.s_addr
;
3800 (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.",
3811 /* ALIGN: assume sa is aligned, cast ok. */
3812 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)(void *)sa
;
3813 int x
= sizeof(ptr_name
);
3817 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
3818 * 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.
3820 for (i
= sizeof(sin6
->sin6_addr
) - 1; i
>= 0; i
--) {
3821 n
= snprintf(&ptr_name
[s
], x
, "%x.%x.",
3822 ( sin6
->sin6_addr
.s6_addr
[i
] & 0xf),
3823 ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf));
3824 if ((n
== -1) || (n
>= x
)) {
3832 n
= snprintf(&ptr_name
[s
], x
, "ip6.arpa.");
3833 if ((n
== -1) || (n
>= x
)) {
3844 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, ptr_name
, NULL
, 0, NULL
, NULL
, "");
3848 ReachabilityStoreInfo_free(&store_info
);
3854 startAsyncDNSQuery(SCNetworkReachabilityRef target
)
3857 mach_port_t mp
= MACH_PORT_NULL
;
3859 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3861 __dns_query_start(&targetPrivate
->dnsQueryStart
, &targetPrivate
->dnsQueryEnd
);
3863 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3864 if (targetPrivate
->if_index
== 0) {
3865 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3866 error
= getaddrinfo_async_start(&mp
,
3867 targetPrivate
->name
,
3868 targetPrivate
->serv
,
3869 &targetPrivate
->hints
,
3870 __SCNetworkReachabilityCallbackSetResolvedAddress
,
3872 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3874 mp
= _getaddrinfo_interface_async_call(targetPrivate
->name
,
3875 targetPrivate
->serv
,
3876 &targetPrivate
->hints
,
3877 targetPrivate
->if_name
,
3878 __SCNetworkReachabilityCallbackSetResolvedAddress
,
3880 if (mp
== MACH_PORT_NULL
) {
3884 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3886 /* save the error associated with the attempt to resolve the name */
3887 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
);
3891 ok
= enqueueAsyncDNSQuery(target
, mp
);
3900 enqueueAsyncDNSRetry(SCNetworkReachabilityRef target
)
3903 dispatch_source_t source
;
3904 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3906 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3908 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
3911 __SCNetworkReachability_concurrent_queue());
3912 if (source
== NULL
) {
3913 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability retry dispatch_source_create() failed"));
3917 // retain the target ... and release it when the [timer] source is released
3919 dispatch_set_context(source
, (void *)target
);
3920 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
3922 dispatch_source_set_event_handler(source
, ^(void) {
3923 __SCNetworkReachabilityPerformInlineNoLock(target
, TRUE
);
3926 // start a one-shot timer
3927 delay
= targetPrivate
->dnsRetryCount
* EAI_NONAME_RETRY_DELAY_USEC
* NSEC_PER_USEC
;
3928 dispatch_source_set_timer(source
,
3929 dispatch_time(DISPATCH_TIME_NOW
, delay
), // start
3931 10 * NSEC_PER_MSEC
); // leeway
3933 targetPrivate
->dnsRetry
= source
;
3934 dispatch_resume(source
);
3941 dequeueAsyncDNSRetry(SCNetworkReachabilityRef target
)
3943 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3945 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3947 if (targetPrivate
->dnsRetry
!= NULL
) {
3948 dispatch_source_cancel(targetPrivate
->dnsRetry
);
3949 dispatch_release(targetPrivate
->dnsRetry
);
3950 targetPrivate
->dnsRetry
= NULL
;
3960 static dispatch_queue_t
3963 static dispatch_once_t once
;
3964 static dispatch_queue_t q
;
3966 dispatch_once(&once
, ^{
3967 q
= dispatch_queue_create("SCNetworkReachabilty.longLivedQueries", NULL
);
3977 * Called to push out a target's DNS changes
3978 * - caller must be running on the _llq_queue()
3981 _llq_notify(const void *value
, void *context
)
3983 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
3984 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3986 MUTEX_LOCK(&targetPrivate
->lock
);
3988 __dns_query_end(target
,
3989 (targetPrivate
->resolvedAddressError
== NETDB_SUCCESS
), // if successful query
3990 dns_query_llq
, // long-lived-query
3991 &targetPrivate
->dnsQueryStart
, // start time
3992 &targetPrivate
->dnsQueryEnd
); // end time
3994 if (targetPrivate
->scheduled
) {
3995 __SCNetworkReachabilityPerform(target
);
3998 // last long-lived-query end time is new start time
3999 targetPrivate
->dnsQueryStart
= targetPrivate
->dnsQueryEnd
;
4001 MUTEX_UNLOCK(&targetPrivate
->lock
);
4009 * Called to process mDNSResponder long-lived-query updates
4010 * - caller must be running on the _llq_queue()
4013 _llq_callback(DNSServiceRef sdRef
,
4014 DNSServiceFlags flags
,
4015 uint32_t interfaceIndex
,
4016 DNSServiceErrorType errorCode
,
4017 const char *hostname
,
4018 const struct sockaddr
*address
,
4022 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
4023 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4025 MUTEX_LOCK(&targetPrivate
->lock
);
4027 if (targetPrivate
->llqTimer
!= NULL
) {
4028 dispatch_source_cancel(targetPrivate
->llqTimer
);
4029 dispatch_release(targetPrivate
->llqTimer
);
4030 targetPrivate
->llqTimer
= NULL
;
4033 switch (errorCode
) {
4034 case kDNSServiceErr_NoError
:
4035 if (address
!= NULL
) {
4036 CFMutableArrayRef addresses
;
4037 CFDataRef llqAddress
;
4039 if (targetPrivate
->resolvedAddress
!= NULL
) {
4040 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
4041 addresses
= CFArrayCreateMutableCopy(NULL
, 0, targetPrivate
->resolvedAddress
);
4043 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4046 CFRelease(targetPrivate
->resolvedAddress
);
4047 targetPrivate
->resolvedAddress
= NULL
;
4049 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4052 llqAddress
= CFDataCreate(NULL
, (void *)address
, address
->sa_len
);
4053 if (flags
& kDNSServiceFlagsAdd
) {
4055 CFArrayAppendValue(addresses
, llqAddress
);
4060 i
= CFArrayGetFirstIndexOfValue(addresses
,
4061 CFRangeMake(0, CFArrayGetCount(addresses
)),
4063 if (i
!= kCFNotFound
) {
4064 CFArrayRemoveValueAtIndex(addresses
, i
);
4067 CFRelease(llqAddress
);
4069 if (CFArrayGetCount(addresses
) > 0) {
4070 targetPrivate
->resolvedAddress
= addresses
;
4071 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
4073 // if host not found
4074 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
4075 targetPrivate
->resolvedAddressError
= EAI_NONAME
;
4076 CFRelease(addresses
);
4079 targetPrivate
->needResolve
= FALSE
;
4082 case kDNSServiceErr_NoSuchRecord
:
4083 if (address
!= NULL
) {
4084 // no IPv4/IPv6 address for name (NXDOMAIN)
4085 if (targetPrivate
->resolvedAddress
== NULL
) {
4086 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
4087 targetPrivate
->resolvedAddressError
= EAI_NONAME
;
4089 targetPrivate
->needResolve
= FALSE
;
4092 case kDNSServiceErr_Timeout
:
4093 if (targetPrivate
->resolvedAddress
== NULL
) {
4094 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
4095 targetPrivate
->resolvedAddressError
= EAI_NONAME
;
4097 targetPrivate
->needResolve
= FALSE
;
4100 SCLog(TRUE
, LOG_ERR
,
4101 CFSTR("%sSCNetworkReachability _llq_callback w/error=%d"),
4102 targetPrivate
->log_prefix
,
4107 MUTEX_UNLOCK(&targetPrivate
->lock
);
4109 // the "more coming" flag applies to DNSService callouts for any/all
4110 // hosts that are being watched so we need to keep track of the targets
4111 // we have updated. When we [finally] have the last callout then we
4112 // push our notifications for all of the updated targets.
4114 if (llqUpdated
== NULL
) {
4115 llqUpdated
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
4117 CFSetAddValue(llqUpdated
, target
);
4119 if (!(flags
& kDNSServiceFlagsMoreComing
)) {
4120 CFSetApplyFunction(llqUpdated
, _llq_notify
, NULL
);
4121 CFRelease(llqUpdated
);
4130 enqueueLongLivedQuery(SCNetworkReachabilityRef target
)
4132 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4134 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4136 if (targetPrivate
->serv
!= NULL
) {
4137 // if "serv" provided, can't use DNSServiceGetAddrInfo
4141 if (memcmp(&targetPrivate
->hints
, &HINTS_DEFAULT
, sizeof(targetPrivate
->hints
)) != 0) {
4142 // non-default "hints" provided, can't use DNSServiceGetAddrInfo
4146 // mark the long lived query active
4147 targetPrivate
->llqActive
= TRUE
;
4149 // track the DNS resolution time
4150 __dns_query_start(&targetPrivate
->dnsQueryStart
, &targetPrivate
->dnsQueryEnd
);
4153 dispatch_async(_llq_queue(), ^{
4154 DNSServiceErrorType err
;
4155 dispatch_source_t source
;
4157 MUTEX_LOCK(&targetPrivate
->lock
);
4159 if (targetPrivate
->llqTarget
!= NULL
) {
4160 // if already running
4161 MUTEX_UNLOCK(&targetPrivate
->lock
);
4166 // if needed, start interacting with mDNSResponder
4168 if (llqMain
== NULL
) {
4169 err
= DNSServiceCreateConnection(&llqMain
);
4170 if (err
!= kDNSServiceErr_NoError
) {
4171 SCLog(TRUE
, LOG_ERR
,
4172 CFSTR("DNSServiceCreateConnection(&llqMain) failed, error = %d"),
4175 targetPrivate
->llqActive
= FALSE
;
4177 MUTEX_UNLOCK(&targetPrivate
->lock
);
4182 err
= DNSServiceSetDispatchQueue(llqMain
, _llq_queue());
4183 if (err
!= kDNSServiceErr_NoError
) {
4184 SCLog(TRUE
, LOG_ERR
,
4185 CFSTR("DNSServiceSetDispatchQueue() failed, error = %d"),
4187 DNSServiceRefDeallocate(llqMain
);
4190 targetPrivate
->llqActive
= FALSE
;
4192 MUTEX_UNLOCK(&targetPrivate
->lock
);
4198 // start a long-lived-query for this target
4200 targetPrivate
->llqTarget
= llqMain
;
4201 err
= DNSServiceGetAddrInfo(&targetPrivate
->llqTarget
, // sdRef
4202 kDNSServiceFlagsReturnIntermediates
// flags
4203 | kDNSServiceFlagsShareConnection
,
4204 targetPrivate
->if_index
, // interfaceIndex
4206 targetPrivate
->name
, // hostname
4207 _llq_callback
, // callback
4208 (void *)target
); // context
4209 if (err
!= kDNSServiceErr_NoError
) {
4210 SCLog(TRUE
, LOG_ERR
,
4211 CFSTR("DNSServiceGetAddrInfo() failed, error = %d"),
4213 targetPrivate
->llqTarget
= NULL
;
4214 if (llqCount
== 0) {
4215 // if this was the first request
4216 DNSServiceRefDeallocate(llqMain
);
4220 targetPrivate
->llqActive
= FALSE
;
4222 MUTEX_UNLOCK(&targetPrivate
->lock
);
4229 // if case we don't get any callbacks from our long-lived-query (this
4230 // could happen if the DNS servers do not respond), we start a timer
4231 // to ensure that we fire off at least one reachability callback.
4233 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
4237 if (source
!= NULL
) {
4238 // retain the target ... and release it when the [timer] source is released
4240 dispatch_set_context(source
, (void *)target
);
4241 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
4243 dispatch_source_set_event_handler(source
, ^(void) {
4244 _llq_callback(NULL
, // sdRef
4246 0, // interfaceIndex
4247 kDNSServiceErr_Timeout
, // errorCode
4251 (void *)target
); // context
4254 dispatch_source_set_timer(source
,
4255 dispatch_time(DISPATCH_TIME_NOW
,
4256 LLQ_TIMEOUT_NSEC
), // start
4258 10 * NSEC_PER_MSEC
); // leeway
4260 targetPrivate
->llqTimer
= source
;
4261 dispatch_resume(source
);
4263 SCLog(TRUE
, LOG_ERR
,
4264 CFSTR("SCNetworkReachability llq dispatch_source_create(no-reply) failed"));
4267 MUTEX_UNLOCK(&targetPrivate
->lock
);
4276 dequeueLongLivedQuery(SCNetworkReachabilityRef target
)
4278 DNSServiceRef sdRef
;
4279 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4281 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4283 // terminate the [target] llq timer
4284 if (targetPrivate
->llqTimer
!= NULL
) {
4285 dispatch_source_cancel(targetPrivate
->llqTimer
);
4286 dispatch_release(targetPrivate
->llqTimer
);
4287 targetPrivate
->llqTimer
= NULL
;
4290 // terminate the [target] long lived query
4291 sdRef
= targetPrivate
->llqTarget
;
4292 targetPrivate
->llqTarget
= NULL
;
4294 // mark the long lived query NOT active
4295 targetPrivate
->llqActive
= FALSE
;
4297 if (sdRef
!= NULL
) {
4298 dispatch_async(_llq_queue(), ^{
4299 DNSServiceRefDeallocate(sdRef
);
4303 if (llqCount
== 0) {
4304 // if no more queries active
4305 DNSServiceRefDeallocate(llqMain
);
4316 #pragma mark OnDemand
4320 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target
,
4321 CFDictionaryRef
*userOptions
)
4323 SCNetworkServiceRef service
= NULL
;
4324 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4326 if (!isA_SCNetworkReachability(target
)) {
4327 _SCErrorSet(kSCStatusInvalidArgument
);
4331 if (targetPrivate
->onDemandServiceID
!= NULL
) {
4332 service
= _SCNetworkServiceCopyActive(NULL
, targetPrivate
->onDemandServiceID
);
4335 if (userOptions
!= NULL
) {
4336 if (targetPrivate
->onDemandName
!= NULL
) {
4337 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4338 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
);
4340 *userOptions
= NULL
;
4349 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer
,
4350 SCNetworkReachabilityFlags onDemandFlags
,
4353 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
4354 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4356 MUTEX_LOCK(&targetPrivate
->lock
);
4358 if (!targetPrivate
->scheduled
) {
4359 // if not currently scheduled
4360 MUTEX_UNLOCK(&targetPrivate
->lock
);
4364 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sOnDemand \"server\" status changed"),
4365 targetPrivate
->log_prefix
);
4366 __SCNetworkReachabilityPerform(target
);
4368 MUTEX_UNLOCK(&targetPrivate
->lock
);
4375 __SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef store_info
,
4376 SCNetworkReachabilityRef target
,
4377 Boolean onDemandRetry
,
4378 SCNetworkReachabilityFlags
*flags
)
4381 Boolean onDemand
= FALSE
;
4382 CFStringRef onDemandRemoteAddress
= NULL
;
4383 CFStringRef onDemandServiceID
= NULL
;
4384 SCNetworkConnectionStatus onDemandStatus
;
4385 SCDynamicStoreRef store
;
4386 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4388 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4390 // SCLog(_sc_debug, LOG_INFO,
4391 // CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"),
4392 // targetPrivate->log_prefix,
4393 // onDemandRetry ? "after" : "before");
4395 if (targetPrivate
->onDemandName
== NULL
) {
4396 targetPrivate
->onDemandName
= CFStringCreateWithCString(NULL
, targetPrivate
->name
, kCFStringEncodingUTF8
);
4400 * check if an OnDemand VPN configuration matches the name.
4402 store
= store_info
->store
;
4403 ok
= __SCNetworkConnectionCopyOnDemandInfoWithName(&store
,
4404 targetPrivate
->onDemandName
,
4408 &onDemandRemoteAddress
);
4409 if ((store_info
->store
== NULL
) && (store
!= NULL
)) {
4410 // if an SCDynamicStore session was added, keep it
4411 store_info
->store
= store
;
4413 if (!_SC_CFEqual(targetPrivate
->onDemandRemoteAddress
, onDemandRemoteAddress
) ||
4414 !_SC_CFEqual(targetPrivate
->onDemandServiceID
, onDemandServiceID
)) {
4415 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
4416 CFRelease(targetPrivate
->onDemandRemoteAddress
);
4417 targetPrivate
->onDemandRemoteAddress
= NULL
;
4420 if (targetPrivate
->onDemandServer
!= NULL
) {
4421 if (targetPrivate
->dispatchQueue
!= NULL
) {
4423 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
4424 } else if (targetPrivate
->rls
!= NULL
) {
4429 n
= CFArrayGetCount(targetPrivate
->rlList
);
4430 for (i
= 0; i
< n
; i
+= 3) {
4431 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
4432 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
4434 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, TRUE
);
4438 CFRelease(targetPrivate
->onDemandServer
);
4439 targetPrivate
->onDemandServer
= NULL
;
4442 if (targetPrivate
->onDemandServiceID
!= NULL
) {
4443 CFRelease(targetPrivate
->onDemandServiceID
);
4444 targetPrivate
->onDemandServiceID
= NULL
;
4448 if (onDemandStatus
!= kSCNetworkConnectionConnected
) {
4450 * if we have a VPN configuration matching the name *and* we need to
4451 * bring the VPN up. Combine our flags with those of the VPN server.
4453 if (targetPrivate
->onDemandServer
== NULL
) {
4454 CFMutableDictionaryRef options
;
4456 options
= CFDictionaryCreateMutable(NULL
,
4458 &kCFTypeDictionaryKeyCallBacks
,
4459 &kCFTypeDictionaryValueCallBacks
);
4460 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, onDemandRemoteAddress
);
4461 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandBypass
, kCFBooleanTrue
);
4462 #ifdef HAVE_REACHABILITY_SERVER
4463 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionServerBypass
, kCFBooleanTrue
);
4464 #endif // HAVE_REACHABILITY_SERVER
4465 targetPrivate
->onDemandServer
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
4468 if (targetPrivate
->scheduled
) {
4469 SCNetworkReachabilityContext context
= { 0, NULL
, CFRetain
, CFRelease
, CFCopyDescription
};
4471 context
.info
= (void *)target
;
4472 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
,
4473 __SCNetworkReachabilityOnDemandCheckCallback
,
4476 // schedule server reachability to match that of the target
4477 if (targetPrivate
->dispatchQueue
!= NULL
) {
4478 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, targetPrivate
->dispatchQueue
, TRUE
);
4483 n
= CFArrayGetCount(targetPrivate
->rlList
);
4484 for (i
= 0; i
< n
; i
+= 3) {
4485 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
4486 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
4488 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, NULL
, TRUE
);
4494 ok
= SCNetworkReachabilityGetFlags(targetPrivate
->onDemandServer
, flags
);
4495 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status * = 0x%08x"),
4496 targetPrivate
->log_prefix
,
4498 if (ok
&& (*flags
& kSCNetworkReachabilityFlagsReachable
)) {
4499 if (!(*flags
& kSCNetworkReachabilityFlagsTransientConnection
)) {
4500 // start clean if not already layered on a transient network
4503 *flags
|= kSCNetworkReachabilityFlagsReachable
;
4504 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
4505 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4506 *flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
4509 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service * = %@"),
4510 targetPrivate
->log_prefix
,
4512 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after OnDemand connect)"),
4513 targetPrivate
->log_prefix
);
4520 if (onDemandRemoteAddress
!= NULL
) {
4521 if (targetPrivate
->onDemandRemoteAddress
== NULL
) {
4522 targetPrivate
->onDemandRemoteAddress
= onDemandRemoteAddress
;
4524 CFRelease(onDemandRemoteAddress
);
4528 if (onDemandServiceID
!= NULL
) {
4529 if (targetPrivate
->onDemandServiceID
== NULL
) {
4530 targetPrivate
->onDemandServiceID
= onDemandServiceID
;
4532 CFRelease(onDemandServiceID
);
4542 #pragma mark Reachability Flags
4545 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
4548 struct addrinfo
*res
;
4553 reply_callback(int32_t status
, struct addrinfo
*res
, void *context
)
4555 reply_info
*reply
= (reply_info
*)context
;
4557 reply
->status
= status
;
4564 getaddrinfo_interface_sync(const char *nodename
,
4565 const char *servname
,
4566 const struct addrinfo
*hints
,
4567 const char *interface
,
4568 struct addrinfo
**res
)
4571 reply_info reply
= { NETDB_SUCCESS
, NULL
};
4573 mp
= _getaddrinfo_interface_async_call(nodename
,
4579 if (mp
== MACH_PORT_NULL
) {
4587 mach_msg_empty_rcv_t msg
;
4589 kern_return_t m_status
;
4591 m_status
= mach_msg(&m_reply
.msg
.header
, /* msg */
4592 MACH_RCV_MSG
, /* options */
4594 sizeof(m_reply
), /* rcv_size */
4596 MACH_MSG_TIMEOUT_NONE
, /* timeout */
4597 MACH_PORT_NULL
); /* notify */
4598 if (m_status
!= KERN_SUCCESS
) {
4602 g_status
= getaddrinfo_async_handle_reply((void *)m_reply
.buf
);
4603 if (g_status
!= 0) {
4604 if (reply
.res
!= NULL
) {
4605 freeaddrinfo(reply
.res
);
4611 if ((reply
.res
!= NULL
) || (reply
.status
!= NETDB_SUCCESS
)) {
4612 // if we have a reply or an error
4616 // if the request is not complete and needs to be re-queued
4620 return reply
.status
;
4622 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
4626 __SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef store_info
,
4627 SCNetworkReachabilityRef target
,
4628 ReachabilityInfo
*reach_info
,
4631 CFMutableArrayRef addresses
= NULL
;
4632 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4633 ReachabilityInfo my_info
= NOT_REACHABLE
;
4636 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4638 _reach_set(reach_info
, &NOT_REACHABLE
, reach_info
->cycle
);
4640 if (!isA_SCNetworkReachability(target
)) {
4641 _SCErrorSet(kSCStatusInvalidArgument
);
4645 #ifdef HAVE_REACHABILITY_SERVER
4646 if (!targetPrivate
->serverBypass
) {
4647 if (!targetPrivate
->serverActive
) {
4648 ok
= __SCNetworkReachabilityServer_targetAdd(target
);
4650 targetPrivate
->serverBypass
= TRUE
;
4654 if (targetPrivate
->serverActive
) {
4655 ok
= __SCNetworkReachabilityServer_targetStatus(target
);
4657 SCLog(TRUE
, LOG_DEBUG
,
4658 CFSTR("__SCNetworkReachabilityGetFlags _targetStatus() failed"));
4659 _SCErrorSet(kSCStatusFailed
);
4663 targetPrivate
->cycle
= targetPrivate
->serverInfo
.cycle
;
4664 _reach_set(&my_info
, &targetPrivate
->serverInfo
, targetPrivate
->cycle
);
4668 #endif // HAVE_REACHABILITY_SERVER
4670 switch (targetPrivate
->type
) {
4671 case reachabilityTypeAddress
:
4672 case reachabilityTypeAddressPair
: {
4674 * Check "local" address
4676 if (targetPrivate
->localAddress
!= NULL
) {
4678 * Check "local" address
4680 ok
= checkAddress(store_info
,
4681 targetPrivate
->localAddress
,
4682 targetPrivate
->if_index
,
4684 targetPrivate
->log_prefix
);
4686 goto error
; /* not today */
4689 if (!(my_info
.flags
& kSCNetworkReachabilityFlagsIsLocalAddress
)) {
4690 goto error
; /* not reachable, non-"local" address */
4695 * Check "remote" address
4697 if (targetPrivate
->remoteAddress
!= NULL
) {
4699 * in cases where we have "local" and "remote" addresses
4700 * we need to re-initialize the to-be-returned flags.
4702 my_info
= NOT_REACHABLE
;
4705 * Check "remote" address
4707 ok
= checkAddress(store_info
,
4708 targetPrivate
->remoteAddress
,
4709 targetPrivate
->if_index
,
4711 targetPrivate
->log_prefix
);
4713 goto error
; /* not today */
4721 case reachabilityTypeName
: {
4722 struct timeval dnsQueryStart
;
4723 struct timeval dnsQueryEnd
;
4725 SCNetworkReachabilityFlags ns_flags
;
4726 uint32_t ns_if_index
;
4727 struct addrinfo
*res
;
4729 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
4730 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
4731 /* if resolved or an error had been detected */
4733 /* if not an async request */
4734 goto checkResolvedAddress
;
4735 } else if (targetPrivate
->llqActive
) {
4736 /* if long-lived-query active */
4737 goto checkResolvedAddress
;
4738 } else if ((targetPrivate
->dnsMP
== MACH_PORT_NULL
) && !targetPrivate
->needResolve
) {
4739 struct timeval elapsed
;
4740 const struct timeval retry_limit
= { EAI_NONAME_RETRY_LIMIT_USEC
/ USEC_PER_SEC
,
4741 EAI_NONAME_RETRY_LIMIT_USEC
% USEC_PER_SEC
};
4744 * if this is an async request (i.e. someone is watching the reachability
4745 * of this target), if no query active, and if no query is needed
4748 if ((error
!= EAI_NONAME
)
4749 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
4750 && (error
!= EAI_NODATA
)
4753 /* if not "host not found" */
4754 goto checkResolvedAddress
;
4758 * if our last DNS query returned EAI_NONAME then we
4759 * "may" want to retry.
4761 * Specifically, if the [DNS] configuration was updated a while
4762 * back then we'll trust the EAI_NONAME reply. Otherwise, we
4763 * want to try again to ensure that we didn't get caught in a
4764 * race between the time when the configuration was changed and
4765 * when mDNSResponder is really ready to handle the query.
4767 * Retry handling details :
4769 * Compare the time when the DNS configuration was last changed and
4770 * when our DNS reply was started (->last_dns vs ->dnsQueryStart).
4772 * Expected: 0 < last_dns (t1) < dnsQueryStart (t2)
4774 * last start end description action
4775 * ==== ===== ==== ================================= ========
4776 * 0 N/A N/A no change, query error no retry
4777 * 0 N/A N/A no change, query complete no retry
4778 * N/A N/A 0 changed, query in-flight or error no retry
4779 * t1 > t2 query started, then [DNS] changed no retry
4780 * t1 == t2 changed & query started together no retry
4781 * t1 < t2 changed, then query started retry
4784 if (!timerisset(&targetPrivate
->last_dns
)) {
4786 * if we have not yet seen a DNS configuration
4789 goto checkResolvedAddress
;
4792 if (!timerisset(&targetPrivate
->dnsQueryEnd
)) {
4794 * if no query end time (new request in flight)
4796 goto checkResolvedAddress
;
4799 if (timercmp(&targetPrivate
->last_dns
,
4800 &targetPrivate
->dnsQueryStart
,
4803 * if our DNS query started and then, a
4804 * short time later, the DNS configuration
4805 * was changed we don't need to retry
4806 * because we will be re-issuing (and not
4807 * retrying) the query.
4809 goto checkResolvedAddress
;
4812 timersub(&targetPrivate
->dnsQueryStart
,
4813 &targetPrivate
->last_dns
,
4815 if (timercmp(&elapsed
, &retry_limit
, >)) {
4817 * if the DNS query started after mDNSResponder
4818 * had a chance to apply the last configuration
4819 * then we should trust the EAI_NONAME reply.
4821 goto checkResolvedAddress
;
4824 /* retry the DNS query */
4826 if (targetPrivate
->dnsRetry
!= NULL
) {
4827 // no need to schedule if we already have a
4828 // retry query in flight
4832 targetPrivate
->dnsRetryCount
++;
4834 SCLog(_sc_debug
, LOG_INFO
,
4835 CFSTR("%sretry [%d] DNS query for %s%s%s%s%s"),
4836 targetPrivate
->log_prefix
,
4837 targetPrivate
->dnsRetryCount
,
4838 targetPrivate
->name
!= NULL
? "name = " : "",
4839 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
4840 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
4841 targetPrivate
->serv
!= NULL
? "serv = " : "",
4842 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
4844 enqueueAsyncDNSRetry(target
);
4849 if (!targetPrivate
->onDemandBypass
) {
4853 * before we attempt our initial DNS query, check if there is
4854 * an OnDemand configuration that we should be using.
4856 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, FALSE
, &my_info
.flags
);
4858 /* if OnDemand connection is needed */
4863 /* check the reachability of the DNS servers */
4864 ok
= _SC_R_checkResolverReachability(store_info
,
4866 &targetPrivate
->haveDNS
,
4867 targetPrivate
->name
,
4868 targetPrivate
->serv
,
4869 targetPrivate
->if_index
,
4872 targetPrivate
->log_prefix
);
4874 /* if we could not get DNS server info */
4875 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
4876 targetPrivate
->log_prefix
);
4878 } else if (rankReachability(ns_flags
) < 2) {
4880 * if DNS servers are not (or are no longer) reachable, set
4881 * flags based on the availability of configured (but not
4885 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
4886 targetPrivate
->log_prefix
);
4888 ok
= checkAddress(store_info
,
4890 targetPrivate
->if_index
,
4892 targetPrivate
->log_prefix
);
4894 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sNo available networks"),
4895 targetPrivate
->log_prefix
);
4899 if (async
&& targetPrivate
->scheduled
) {
4901 * return "host not found", set flags appropriately,
4902 * and schedule notification.
4904 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME
,
4907 my_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
4909 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sno DNS servers are reachable"),
4910 targetPrivate
->log_prefix
);
4911 __SCNetworkReachabilityPerform(target
);
4916 if (targetPrivate
->resolverBypass
) {
4917 /* if we are not resolving the name,
4918 * set the flags of the resolvers */
4919 my_info
.flags
= ns_flags
;
4920 my_info
.if_index
= ns_if_index
;
4925 /* for async requests we return the last known status */
4926 my_info
= targetPrivate
->info
;
4928 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
4929 /* if request already in progress */
4930 SCLog(_sc_debug
, LOG_INFO
,
4931 CFSTR("%swaiting for DNS reply"),
4932 targetPrivate
->log_prefix
);
4933 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
4934 /* updated reachability based on the previous reply */
4935 goto checkResolvedAddress
;
4940 if (targetPrivate
->dnsRetry
!= NULL
) {
4941 /* if we already have a "retry" queued */
4945 if (targetPrivate
->llqActive
) {
4946 /* if long-lived-query active */
4947 SCLog(_sc_debug
, LOG_INFO
,
4948 CFSTR("%swaiting for DNS updates"),
4949 targetPrivate
->log_prefix
);
4950 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
4951 /* updated reachability based on the previous reply */
4952 goto checkResolvedAddress
;
4957 if (!targetPrivate
->llqBypass
) {
4958 SCLog(_sc_debug
, LOG_INFO
,
4959 CFSTR("%sstart long-lived DNS query for %s%s%s%s%s"),
4960 targetPrivate
->log_prefix
,
4961 targetPrivate
->name
!= NULL
? "name = " : "",
4962 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
4963 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
4964 targetPrivate
->serv
!= NULL
? "serv = " : "",
4965 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
4968 * initiate an long-lived DNS query
4970 if (enqueueLongLivedQuery(target
)) {
4971 /* request initiated */
4976 SCLog(_sc_debug
, LOG_INFO
,
4977 CFSTR("%sstart DNS query for %s%s%s%s%s"),
4978 targetPrivate
->log_prefix
,
4979 targetPrivate
->name
!= NULL
? "name = " : "",
4980 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
4981 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
4982 targetPrivate
->serv
!= NULL
? "serv = " : "",
4983 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
4986 * initiate an async DNS query
4988 if (startAsyncDNSQuery(target
)) {
4989 /* request initiated */
4993 /* if we could not initiate the request, process error */
4994 goto checkResolvedAddress
;
4997 SCLog(_sc_debug
, LOG_INFO
,
4998 CFSTR("%scheck DNS for %s%s%s%s%s"),
4999 targetPrivate
->log_prefix
,
5000 targetPrivate
->name
!= NULL
? "name = " : "",
5001 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
5002 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
5003 targetPrivate
->serv
!= NULL
? "serv = " : "",
5004 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
5007 * OK, all of the DNS name servers are available. Let's
5008 * resolve the nodename into an address.
5010 __dns_query_start(&dnsQueryStart
, &dnsQueryEnd
);
5012 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5013 if (targetPrivate
->if_index
== 0) {
5014 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5015 error
= getaddrinfo(targetPrivate
->name
,
5016 targetPrivate
->serv
,
5017 &targetPrivate
->hints
,
5019 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5021 error
= getaddrinfo_interface_sync(targetPrivate
->name
,
5022 targetPrivate
->serv
,
5023 &targetPrivate
->hints
,
5024 targetPrivate
->if_name
,
5027 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5029 __dns_query_end(target
,
5030 ((error
== 0) && (res
!= NULL
)), // if successful query
5031 dns_query_sync
, // sync
5032 &dnsQueryStart
, // start time
5033 &dnsQueryEnd
); // end time
5035 __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
);
5037 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
5039 checkResolvedAddress
:
5042 * We first assume that the requested host is NOT available.
5043 * Then, check each address for accessibility and return the
5044 * best status available.
5046 my_info
= NOT_REACHABLE
;
5048 if (isA_CFArray(addresses
)) {
5050 CFIndex n
= CFArrayGetCount(addresses
);
5052 for (i
= 0; i
< n
; i
++) {
5053 ReachabilityInfo ns_info
= NOT_REACHABLE
;
5054 struct sockaddr
*sa
;
5056 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
5058 ok
= checkAddress(store_info
,
5060 targetPrivate
->if_index
,
5062 targetPrivate
->log_prefix
);
5064 goto error
; /* not today */
5067 if (rankReachability(ns_info
.flags
) > rankReachability(my_info
.flags
)) {
5068 /* return the best case result */
5070 if (rankReachability(my_info
.flags
) == 2) {
5077 if ((error
== EAI_NONAME
)
5078 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
5079 || (error
== EAI_NODATA
)
5083 * the target host name could not be resolved
5085 if (!targetPrivate
->onDemandBypass
) {
5089 * our initial DNS query failed, check again to see if there
5090 * there is an OnDemand configuration that we should be using.
5092 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, TRUE
, &my_info
.flags
);
5094 /* if OnDemand connection is needed */
5099 if (!targetPrivate
->haveDNS
) {
5101 * No DNS servers are defined. Set flags based on
5102 * the availability of configured (but not active)
5105 ok
= checkAddress(store_info
,
5107 targetPrivate
->if_index
,
5109 targetPrivate
->log_prefix
);
5111 goto error
; /* not today */
5114 if ((my_info
.flags
& kSCNetworkReachabilityFlagsReachable
) &&
5115 (my_info
.flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
5117 * Since we might pick up a set of DNS servers when this connection
5118 * is established, don't reply with a "HOST NOT FOUND" error just yet.
5123 /* Host not found, not reachable! */
5124 my_info
= NOT_REACHABLE
;
5135 _reach_set(reach_info
, &my_info
, targetPrivate
->cycle
);
5139 if (addresses
!= NULL
) CFRelease(addresses
);
5144 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target
)
5146 SCNetworkReachabilityFlags flags
;
5149 ReachabilityStoreInfo store_info
;
5150 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5152 if (!isA_SCNetworkReachability(target
)) {
5153 _SCErrorSet(kSCStatusInvalidArgument
);
5157 ReachabilityStoreInfo_init(&store_info
);
5159 MUTEX_LOCK(&targetPrivate
->lock
);
5161 if (targetPrivate
->scheduled
) {
5162 // if being watched, return the last known (and what should be current) status
5163 flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
5168 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
);
5169 flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
5173 /* Only return the if_index if the connection is reachable not for reachable connection
5174 * required etc ... */
5175 if (ok
&& rankReachability(flags
) == 2) {
5176 if_index
= targetPrivate
->info
.if_index
;
5179 MUTEX_UNLOCK(&targetPrivate
->lock
);
5180 ReachabilityStoreInfo_free(&store_info
);
5186 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
5187 SCNetworkReachabilityFlags
*flags
)
5190 ReachabilityStoreInfo store_info
;
5191 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5193 if (!isA_SCNetworkReachability(target
)) {
5194 _SCErrorSet(kSCStatusInvalidArgument
);
5198 ReachabilityStoreInfo_init(&store_info
);
5200 MUTEX_LOCK(&targetPrivate
->lock
);
5202 if (targetPrivate
->scheduled
) {
5203 // if being watched, return the last known (and what should be current) status
5204 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
5209 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
);
5210 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
5214 MUTEX_UNLOCK(&targetPrivate
->lock
);
5215 ReachabilityStoreInfo_free(&store_info
);
5221 #pragma mark Notifications
5225 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
5228 CFMutableArrayRef keys
;
5229 CFStringRef pattern
;
5230 CFMutableArrayRef patterns
;
5232 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5233 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5235 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
5236 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5237 kSCDynamicStoreDomainSetup
,
5239 CFArrayAppendValue(keys
, key
);
5242 // State:/Network/Global/DNS
5243 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5244 kSCDynamicStoreDomainState
,
5246 CFArrayAppendValue(keys
, key
);
5249 // State:/Network/Global/IPv4 (default route)
5250 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5251 kSCDynamicStoreDomainState
,
5253 CFArrayAppendValue(keys
, key
);
5256 // State:/Network/Global/OnDemand
5257 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5258 kSCDynamicStoreDomainState
,
5260 CFArrayAppendValue(keys
, key
);
5263 // Setup: per-service Interface info
5264 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5265 kSCDynamicStoreDomainSetup
,
5267 kSCEntNetInterface
);
5268 CFArrayAppendValue(patterns
, pattern
);
5271 // per-service IPv4 info
5272 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5273 kSCDynamicStoreDomainSetup
,
5276 CFArrayAppendValue(patterns
, pattern
);
5278 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5279 kSCDynamicStoreDomainState
,
5282 CFArrayAppendValue(patterns
, pattern
);
5285 // per-service IPv6 info
5286 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5287 kSCDynamicStoreDomainSetup
,
5290 CFArrayAppendValue(patterns
, pattern
);
5292 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5293 kSCDynamicStoreDomainState
,
5296 CFArrayAppendValue(patterns
, pattern
);
5299 // per-service PPP info (for existence, kSCPropNetPPPDialOnDemand, kSCPropNetPPPStatus)
5300 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5301 kSCDynamicStoreDomainSetup
,
5304 CFArrayAppendValue(patterns
, pattern
);
5306 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5307 kSCDynamicStoreDomainState
,
5310 CFArrayAppendValue(patterns
, pattern
);
5313 #if !TARGET_IPHONE_SIMULATOR
5314 // per-service VPN info (for existence, kSCPropNetVPNStatus)
5315 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5316 kSCDynamicStoreDomainSetup
,
5319 CFArrayAppendValue(patterns
, pattern
);
5321 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5322 kSCDynamicStoreDomainState
,
5325 CFArrayAppendValue(patterns
, pattern
);
5327 #endif // !TARGET_IPHONE_SIMULATOR
5329 // per-service IPSec info (for existence, kSCPropNetIPSecStatus)
5330 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5331 kSCDynamicStoreDomainSetup
,
5334 CFArrayAppendValue(patterns
, pattern
);
5336 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5337 kSCDynamicStoreDomainState
,
5340 CFArrayAppendValue(patterns
, pattern
);
5343 #if !TARGET_OS_IPHONE
5344 // State: Power Management Capabilities
5345 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
5346 kSCDynamicStoreDomainState
,
5347 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
5348 CFArrayAppendValue(keys
, key
);
5350 #endif // TARGET_OS_IPHONE
5353 // SCDynamicStore key to force posting a reachability change
5354 CFArrayAppendValue(keys
, SCNETWORKREACHABILITY_TRIGGER_KEY
);
5356 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
5358 CFRelease(patterns
);
5364 static dispatch_queue_t
5367 static dispatch_once_t once
;
5368 static dispatch_queue_t q
;
5370 dispatch_once(&once
, ^{
5371 q
= dispatch_queue_create("SCNetworkReachabilty.changes", NULL
);
5379 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store
,
5380 CFArrayRef changedKeys
,
5383 #if !TARGET_OS_IPHONE
5384 Boolean cpuStatusChanged
= FALSE
;
5385 #endif // !TARGET_OS_IPHONE
5386 Boolean dnsConfigChanged
= FALSE
;
5388 Boolean forcedChange
= FALSE
;
5391 CFIndex nGlobals
= 0;
5393 Boolean networkConfigChanged
= FALSE
;
5395 #if !TARGET_OS_IPHONE
5396 Boolean powerStatusChanged
= FALSE
;
5397 #endif // !TARGET_OS_IPHONE
5398 ReachabilityStoreInfo store_info
;
5399 const void * targets_q
[N_QUICK
];
5400 const void ** targets
= targets_q
;
5401 __block CFSetRef watchers
= NULL
;
5403 nChanges
= CFArrayGetCount(changedKeys
);
5404 if (nChanges
== 0) {
5409 /* "something" changed, start fresh */
5410 ReachabilityStoreInfo_save(NULL
);
5412 dispatch_sync(_hn_queue(), ^{
5413 /* grab the currently watched targets */
5414 if (hn_targets
!= NULL
) {
5415 watchers
= CFSetCreateCopy(NULL
, hn_targets
);
5419 nTargets
= (watchers
!= NULL
) ? CFSetGetCount(watchers
) : 0;
5420 if (nTargets
== 0) {
5421 /* if no addresses being monitored */
5425 /* grab the current time */
5426 (void)gettimeofday(&now
, NULL
);
5428 #if !TARGET_OS_IPHONE
5429 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
5430 kSCDynamicStoreDomainState
,
5431 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
5432 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
5437 num
= SCDynamicStoreCopyValue(store
, key
);
5439 if (isA_CFNumber(num
) &&
5440 CFNumberGetValue(num
, kCFNumberSInt32Type
, &power_capabilities
)) {
5441 static Boolean haveCPU_old
= TRUE
;
5442 Boolean haveCPU_new
;
5444 powerStatusChanged
= TRUE
;
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
;
5465 #endif // !TARGET_OS_IPHONE
5467 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5468 kSCDynamicStoreDomainState
,
5470 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
5472 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
5476 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), SCNETWORKREACHABILITY_TRIGGER_KEY
)) {
5478 forcedChange
= TRUE
; /* an SCDynamicStore driven "network" change */
5481 if (nChanges
> nGlobals
) {
5482 networkConfigChanged
= TRUE
;
5486 unsigned int changes
= 0;
5487 static const char *change_strings
[] = {
5488 // with no "power" status change
5493 #if !TARGET_OS_IPHONE
5494 // with "power" status change
5496 "network and power ",
5498 "network, DNS, and power ",
5500 // with "power" status change (including CPU "on")
5502 "network and power* ",
5504 "network, DNS, and power* ",
5505 #endif // !TARGET_OS_IPHONE
5508 #if !TARGET_OS_IPHONE
5510 if (powerStatusChanged
) {
5512 if (cpuStatusChanged
) {
5516 #endif // !TARGET_OS_IPHONE
5519 if (dnsConfigChanged
) {
5524 if (networkConfigChanged
) {
5528 SCLog(TRUE
, LOG_INFO
,
5529 CFSTR("process %s%sconfiguration change"),
5530 forcedChange
? "[forced] " : "",
5531 change_strings
[changes
]);
5534 ReachabilityStoreInfo_init(&store_info
);
5536 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
5537 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
5538 CFSetGetValues(watchers
, targets
);
5539 for (i
= 0; i
< nTargets
; i
++) {
5540 SCNetworkReachabilityRef target
= targets
[i
];
5541 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5543 MUTEX_LOCK(&targetPrivate
->lock
);
5545 if (dnsConfigChanged
) {
5546 targetPrivate
->last_dns
= now
;
5547 targetPrivate
->dnsRetryCount
= 0;
5550 if (networkConfigChanged
) {
5551 targetPrivate
->last_network
= now
;
5554 #if !TARGET_OS_IPHONE
5555 if (powerStatusChanged
) {
5556 targetPrivate
->last_power
= now
;
5558 #endif // !TARGET_OS_IPHONE
5560 if (targetPrivate
->type
== reachabilityTypeName
) {
5561 Boolean dnsChanged
= dnsConfigChanged
;
5565 * if the DNS configuration didn't change we still need to
5566 * check that the DNS servers are accessible.
5568 SCNetworkReachabilityFlags ns_flags
;
5571 /* check the reachability of the DNS servers */
5572 ok
= ReachabilityStoreInfo_update(&store_info
, &store
, AF_UNSPEC
);
5574 ok
= _SC_R_checkResolverReachability(&store_info
,
5576 &targetPrivate
->haveDNS
,
5577 targetPrivate
->name
,
5578 targetPrivate
->serv
,
5579 targetPrivate
->if_index
,
5582 targetPrivate
->log_prefix
);
5586 /* if we could not get DNS server info */
5587 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
5588 targetPrivate
->log_prefix
);
5590 } else if (rankReachability(ns_flags
) < 2) {
5592 * if DNS servers are not (or are no longer) reachable, set
5593 * flags based on the availability of configured (but not
5596 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
5597 targetPrivate
->log_prefix
);
5603 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
5604 /* cancel the outstanding DNS query */
5605 SCLog(_sc_debug
, LOG_INFO
,
5606 CFSTR("%scancel DNS query for %s%s%s%s%s"),
5607 targetPrivate
->log_prefix
,
5608 targetPrivate
->name
!= NULL
? "name = " : "",
5609 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
5610 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
5611 targetPrivate
->serv
!= NULL
? "serv = " : "",
5612 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
5613 dequeueAsyncDNSQuery(target
, TRUE
);
5616 if (targetPrivate
->dnsRetry
!= NULL
) {
5617 /* cancel the outstanding DNS retry */
5618 dequeueAsyncDNSRetry(target
);
5621 /* schedule request to resolve the name again */
5622 targetPrivate
->needResolve
= TRUE
;
5627 targetPrivate
->cycle
++;
5630 if (targetPrivate
->scheduled
) {
5631 __SCNetworkReachabilityPerform(target
);
5634 MUTEX_UNLOCK(&targetPrivate
->lock
);
5636 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
5638 ReachabilityStoreInfo_free(&store_info
);
5642 if (watchers
!= NULL
) CFRelease(watchers
);
5647 #if !TARGET_OS_IPHONE
5650 darkWakeNotify(SCNetworkReachabilityRef target
)
5657 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities
)
5660 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU \
5661 | kIOPMSystemPowerStateCapabilityNetwork \
5662 | kIOPMSystemPowerStateCapabilityDisk)
5664 if ((power_capabilities
& POWER_CAPABILITIES_NEED
) != POWER_CAPABILITIES_NEED
) {
5666 * we're not awake (from a networking point of view) unless we
5667 * have the CPU, disk, *and* network.
5672 if ((power_capabilities
& kIOPMSytemPowerStateCapabilitiesMask
) == POWER_CAPABILITIES_NEED
) {
5674 * if all we have is the CPU, disk, and network than this must
5675 * be a "maintenance" wake.
5683 #endif // !TARGET_OS_IPHONE
5687 reachPerform(void *info
)
5690 void (*context_release
)(const void *);
5692 Boolean defer
= FALSE
;
5695 ReachabilityInfo reach_info
= NOT_REACHABLE
;
5696 SCNetworkReachabilityCallBack rlsFunction
;
5697 ReachabilityStoreInfo store_info
;
5698 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
5699 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5701 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%schecking target reachability"),
5702 targetPrivate
->log_prefix
);
5705 MUTEX_LOCK(&targetPrivate
->lock
);
5707 if (targetPrivate
->dnsRetry
!= NULL
) {
5709 dequeueAsyncDNSRetry(target
);
5712 if (!targetPrivate
->scheduled
) {
5713 // if not currently scheduled
5714 MUTEX_UNLOCK(&targetPrivate
->lock
);
5718 /* update reachability, notify if status changed */
5719 ReachabilityStoreInfo_init(&store_info
);
5720 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
);
5721 ReachabilityStoreInfo_free(&store_info
);
5723 /* if reachability status not available */
5724 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%flags not available"),
5725 targetPrivate
->log_prefix
);
5726 reach_info
= NOT_REACHABLE
;
5729 #if !TARGET_OS_IPHONE
5731 * We want to defer the notification if this is a maintenance wake *and*
5732 * the reachability flags that we would be reporting to the application
5733 * are better than those that we last reported.
5735 if (!systemIsAwake(power_capabilities
)) {
5736 /* if this is a maintenace wake */
5737 reach_info
.sleeping
= TRUE
;
5739 if (rankReachability(reach_info
.flags
) >= rankReachability(targetPrivate
->info
.flags
)) {
5741 * don't report the change if the new reachability flags are
5742 * the same or "better"
5744 defer
= !darkWakeNotify(target
);
5745 } else if (!__reach_changed(&targetPrivate
->last_notify
, &reach_info
)) {
5747 * if we have already posted this change
5749 defer
= !darkWakeNotify(target
);
5752 #endif // !TARGET_OS_IPHONE
5754 cycle
= targetPrivate
->cycle
;
5755 forced
= ((cycle
!= 0) && (targetPrivate
->info
.cycle
!= cycle
));
5757 if (!forced
&& !__reach_changed(&targetPrivate
->info
, &reach_info
)) {
5759 if (targetPrivate
->info
.sleeping
== reach_info
.sleeping
) {
5760 SCLog(TRUE
, LOG_INFO
,
5761 CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"),
5762 targetPrivate
->log_prefix
,
5764 reach_info
.if_index
,
5765 reach_info
.sleeping
? ", z" : "");
5767 SCLog(TRUE
, LOG_INFO
,
5768 CFSTR("%sflags/interface equiv (was 0x%08x/%hu%s, now 0x%08x/%hu%s)"),
5769 targetPrivate
->log_prefix
,
5770 targetPrivate
->info
.flags
,
5771 targetPrivate
->info
.if_index
,
5772 targetPrivate
->info
.sleeping
? ", z" : "",
5774 reach_info
.if_index
,
5775 reach_info
.sleeping
? ", z" : "");
5779 MUTEX_UNLOCK(&targetPrivate
->lock
);
5783 SCLog(_sc_debug
, LOG_INFO
,
5784 CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s%s"),
5785 targetPrivate
->log_prefix
,
5786 targetPrivate
->info
.flags
,
5787 targetPrivate
->info
.if_index
,
5788 targetPrivate
->info
.sleeping
? ", z" : "",
5790 reach_info
.if_index
,
5791 reach_info
.sleeping
? ", z" : "",
5792 defer
? ", deferred" : "",
5793 forced
? ", forced" : "");
5795 /* as needed, defer the notification */
5797 MUTEX_UNLOCK(&targetPrivate
->lock
);
5801 /* update flags / interface */
5802 _reach_set(&targetPrivate
->info
, &reach_info
, cycle
);
5804 /* save last notification info */
5805 _reach_set(&targetPrivate
->last_notify
, &reach_info
, cycle
);
5807 /* save last notification time */
5808 (void)gettimeofday(&targetPrivate
->last_push
, NULL
);
5811 rlsFunction
= targetPrivate
->rlsFunction
;
5812 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
5813 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
5814 context_release
= targetPrivate
->rlsContext
.release
;
5816 context_info
= targetPrivate
->rlsContext
.info
;
5817 context_release
= NULL
;
5820 MUTEX_UNLOCK(&targetPrivate
->lock
);
5822 if (rlsFunction
!= NULL
) {
5823 (*rlsFunction
)(target
,
5824 reach_info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
,
5828 if (context_release
!= NULL
) {
5829 (*context_release
)(context_info
);
5837 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
5838 SCNetworkReachabilityCallBack callout
,
5839 SCNetworkReachabilityContext
*context
)
5841 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5843 MUTEX_LOCK(&targetPrivate
->lock
);
5845 if (targetPrivate
->rlsContext
.release
!= NULL
) {
5846 /* let go of the current context */
5847 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
5850 targetPrivate
->rlsFunction
= callout
;
5851 targetPrivate
->rlsContext
.info
= NULL
;
5852 targetPrivate
->rlsContext
.retain
= NULL
;
5853 targetPrivate
->rlsContext
.release
= NULL
;
5854 targetPrivate
->rlsContext
.copyDescription
= NULL
;
5856 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
5857 if (context
->retain
!= NULL
) {
5858 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
5862 MUTEX_UNLOCK(&targetPrivate
->lock
);
5869 reachRLSCopyDescription(const void *info
)
5871 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
5873 return CFStringCreateWithFormat(NULL
,
5875 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
5881 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
5882 CFRunLoopRef runLoop
,
5883 CFStringRef runLoopMode
,
5884 dispatch_queue_t queue
,
5887 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5888 Boolean init
= FALSE
;
5889 __block Boolean ok
= FALSE
;
5891 MUTEX_LOCK(&targetPrivate
->lock
);
5893 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
5894 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
5895 _SCErrorSet(kSCStatusInvalidArgument
);
5899 #ifdef HAVE_REACHABILITY_SERVER
5900 if (!targetPrivate
->serverBypass
) {
5901 if (!targetPrivate
->serverActive
) {
5902 ok
= __SCNetworkReachabilityServer_targetAdd(target
);
5904 targetPrivate
->serverBypass
= TRUE
;
5908 if (targetPrivate
->serverActive
) {
5909 if (targetPrivate
->scheduled
) {
5910 // if already scheduled
5914 ok
= __SCNetworkReachabilityServer_targetSchedule(target
);
5916 SCLog(TRUE
, LOG_DEBUG
,
5917 CFSTR("__SCNetworkReachabilityScheduleWithRunLoop _targetMonitor() failed"));
5918 _SCErrorSet(kSCStatusFailed
);
5925 #endif // HAVE_REACHABILITY_SERVER
5927 /* schedule the SCNetworkReachability did-something-change handler */
5929 dispatch_sync(_hn_queue(), ^{
5932 if (!onDemand
&& (hn_store
== NULL
)) {
5934 * if we are not monitoring any hosts, start watching
5936 if (!dns_configuration_watch()) {
5938 _SCErrorSet(kSCStatusFailed
);
5942 hn_store
= SCDynamicStoreCreate(NULL
,
5943 CFSTR("SCNetworkReachability"),
5944 __SCNetworkReachabilityHandleChanges
,
5946 if (hn_store
== NULL
) {
5947 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed"));
5948 dns_configuration_unwatch();
5952 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
5954 hn_dispatchQueue
= dispatch_queue_create("SCNetworkReachabilty.changes", NULL
);
5955 if (hn_dispatchQueue
== NULL
) {
5956 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed"));
5957 CFRelease(hn_store
);
5959 dns_configuration_unwatch();
5960 _SCErrorSet(kSCStatusFailed
);
5964 ok
= SCDynamicStoreSetDispatchQueue(hn_store
, hn_dispatchQueue
);
5966 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
5967 dispatch_release(hn_dispatchQueue
);
5968 hn_dispatchQueue
= NULL
;
5969 CFRelease(hn_store
);
5971 dns_configuration_unwatch();
5974 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
5976 ReachabilityStoreInfo_enable(TRUE
);
5979 CFSetAddValue(hn_targets
, target
);
5988 #ifdef HAVE_REACHABILITY_SERVER
5990 #endif // HAVE_REACHABILITY_SERVER
5992 if (!targetPrivate
->scheduled
) {
5993 CFRunLoopSourceContext context
= { 0 // version
5994 , (void *)target
// info
5995 , CFRetain
// retain
5996 , CFRelease
// release
5997 , reachRLSCopyDescription
// copyDescription
6002 , reachPerform
// perform
6005 if (runLoop
!= NULL
) {
6006 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
6007 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
6010 if (targetPrivate
->type
== reachabilityTypeName
) {
6012 * we're now scheduled so let's ensure that we
6013 * are starting with a clean slate before we
6016 if (targetPrivate
->resolvedAddress
!= NULL
) {
6017 CFRelease(targetPrivate
->resolvedAddress
);
6018 targetPrivate
->resolvedAddress
= NULL
;
6020 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
6021 targetPrivate
->needResolve
= TRUE
;
6022 _reach_set(&targetPrivate
->info
, &NOT_REACHABLE
, targetPrivate
->info
.cycle
);
6023 targetPrivate
->info
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
6024 #ifdef HAVE_REACHABILITY_SERVER
6025 _reach_set(&targetPrivate
->serverInfo
, &NOT_REACHABLE
, targetPrivate
->serverInfo
.cycle
);
6026 targetPrivate
->serverInfo
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
6027 #endif // HAVE_REACHABILITY_SERVER
6030 targetPrivate
->scheduled
= TRUE
;
6035 if (queue
!= NULL
) {
6036 // retain dispatch queue
6037 dispatch_retain(queue
);
6038 targetPrivate
->dispatchQueue
= queue
;
6041 // We've taken a reference to the client's dispatch_queue and we
6042 // want to hold on to that reference until we've processed any/all
6043 // notifications. To facilitate this we create a group, dispatch
6044 // any notification blocks to via that group, and when the caller
6045 // has told us to stop the notifications (unschedule) we wait for
6046 // the group to empty and use the group's finalizer to release
6047 // our reference to the client's queue.
6050 // make sure that we have group to track any async requests
6051 targetPrivate
->dispatchGroup
= dispatch_group_create();
6053 // retain the target ... and release it when the group is released
6055 dispatch_set_context(targetPrivate
->dispatchGroup
, (void *)target
);
6056 dispatch_set_finalizer_f(targetPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
6058 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
6060 * if we do not already have host notifications scheduled with
6061 * this runLoop / runLoopMode
6063 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
6065 if (targetPrivate
->dnsRLS
!= NULL
) {
6066 // if we have an active async DNS query too
6067 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
6071 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
6075 ReachabilityInfo reach_info
= NOT_REACHABLE
;
6076 ReachabilityStoreInfo store_info
;
6079 * if we have yet to schedule SC notifications for this address
6080 * - initialize current reachability status
6082 ReachabilityStoreInfo_init(&store_info
);
6083 if (__SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
)) {
6085 * if reachability status available
6087 * - schedule notification to report status via callback
6089 #ifdef HAVE_REACHABILITY_SERVER
6090 reach_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
6091 #endif // HAVE_REACHABILITY_SERVER
6092 _reach_set(&targetPrivate
->info
, &reach_info
, targetPrivate
->cycle
);
6093 __SCNetworkReachabilityPerform(target
);
6095 /* if reachability status not available, async lookup started */
6096 _reach_set(&targetPrivate
->info
, &NOT_REACHABLE
, targetPrivate
->cycle
);
6097 #ifdef HAVE_REACHABILITY_SERVER
6098 _reach_set(&targetPrivate
->serverInfo
, &NOT_REACHABLE
, targetPrivate
->cycle
);
6099 #endif // HAVE_REACHABILITY_SERVER
6101 ReachabilityStoreInfo_free(&store_info
);
6104 if (targetPrivate
->onDemandServer
!= NULL
) {
6105 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, queue
, TRUE
);
6108 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%sscheduled"),
6109 targetPrivate
->log_prefix
);
6115 MUTEX_UNLOCK(&targetPrivate
->lock
);
6121 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
6122 CFRunLoopRef runLoop
,
6123 CFStringRef runLoopMode
,
6126 dispatch_group_t drainGroup
= NULL
;
6127 dispatch_queue_t drainQueue
= NULL
;
6130 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
6132 // hold a reference while we unschedule
6135 MUTEX_LOCK(&targetPrivate
->lock
);
6137 if (((runLoop
== NULL
) && (targetPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
6138 ((runLoop
!= NULL
) && (targetPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are not)
6139 _SCErrorSet(kSCStatusInvalidArgument
);
6143 if (!targetPrivate
->scheduled
) {
6144 // if not currently scheduled
6145 _SCErrorSet(kSCStatusInvalidArgument
);
6149 // unschedule the target specific sources
6150 if (targetPrivate
->dispatchQueue
!= NULL
) {
6151 if (targetPrivate
->onDemandServer
!= NULL
) {
6152 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
6155 // save dispatchQueue, release reference when we've queue'd blocks complete, allow re-scheduling
6156 drainGroup
= targetPrivate
->dispatchGroup
;
6157 targetPrivate
->dispatchGroup
= NULL
;
6158 drainQueue
= targetPrivate
->dispatchQueue
;
6159 targetPrivate
->dispatchQueue
= NULL
;
6161 if (!_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
6162 // if not currently scheduled
6163 _SCErrorSet(kSCStatusInvalidArgument
);
6167 if (targetPrivate
->onDemandServer
!= NULL
) {
6168 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, TRUE
);
6171 n
= CFArrayGetCount(targetPrivate
->rlList
);
6172 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
6173 // if target is no longer scheduled for this runLoop / runLoopMode
6174 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
6176 if (targetPrivate
->dnsRLS
!= NULL
) {
6177 // if we have an active async DNS query too
6178 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
6182 // if *all* notifications have been unscheduled
6183 CFRelease(targetPrivate
->rlList
);
6184 targetPrivate
->rlList
= NULL
;
6185 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
6186 CFRelease(targetPrivate
->rls
);
6187 targetPrivate
->rls
= NULL
;
6193 #ifdef HAVE_REACHABILITY_SERVER
6195 // Cancel our request for server monitoring
6197 if (targetPrivate
->serverActive
) {
6198 ok
= __SCNetworkReachabilityServer_targetUnschedule(target
);
6200 SCLog(TRUE
, LOG_DEBUG
,
6201 CFSTR("__SCNetworkReachabilityUnscheduleFromRunLoop _targetMonitor() failed"));
6202 _SCErrorSet(kSCStatusFailed
);
6205 #endif // HAVE_REACHABILITY_SERVER
6207 // if *all* notifications have been unscheduled
6208 targetPrivate
->scheduled
= FALSE
;
6211 #ifdef HAVE_REACHABILITY_SERVER
6212 if (targetPrivate
->serverActive
) {
6215 #endif // HAVE_REACHABILITY_SERVER
6218 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
6219 // if we have an active async DNS query
6220 dequeueAsyncDNSQuery(target
, TRUE
);
6223 if (targetPrivate
->dnsRetry
!= NULL
) {
6224 // if we have an outstanding DNS retry
6225 dequeueAsyncDNSRetry(target
);
6228 if (targetPrivate
->llqActive
) {
6229 // if we have a long-lived-query
6230 dequeueLongLivedQuery(target
);
6233 dispatch_sync(_hn_queue(), ^{
6234 CFSetRemoveValue(hn_targets
, target
);
6240 if (CFSetGetCount(hn_targets
) > 0) {
6244 // if we are no longer monitoring any targets
6245 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
6246 dispatch_release(hn_dispatchQueue
);
6247 hn_dispatchQueue
= NULL
;
6248 CFRelease(hn_store
);
6250 CFRelease(hn_targets
);
6253 ReachabilityStoreInfo_enable(FALSE
);
6254 ReachabilityStoreInfo_save(NULL
);
6257 * until we start monitoring again, ensure that
6258 * any resources associated with tracking the
6259 * DNS configuration have been released.
6261 dns_configuration_unwatch();
6265 #ifdef HAVE_REACHABILITY_SERVER
6267 #endif // HAVE_REACHABILITY_SERVER
6269 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%sunscheduled"),
6270 targetPrivate
->log_prefix
);
6276 MUTEX_UNLOCK(&targetPrivate
->lock
);
6278 if (drainGroup
!= NULL
) {
6279 dispatch_group_notify(drainGroup
, __SCNetworkReachability_concurrent_queue(), ^{
6280 // release group/queue references
6281 dispatch_release(drainQueue
);
6282 dispatch_release(drainGroup
); // releases our target reference
6286 // release our reference
6293 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
6294 CFRunLoopRef runLoop
,
6295 CFStringRef runLoopMode
)
6297 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
6298 _SCErrorSet(kSCStatusInvalidArgument
);
6302 return __SCNetworkReachabilityScheduleWithRunLoop(target
, runLoop
, runLoopMode
, NULL
, FALSE
);
6306 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
6307 CFRunLoopRef runLoop
,
6308 CFStringRef runLoopMode
)
6310 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
6311 _SCErrorSet(kSCStatusInvalidArgument
);
6315 return __SCNetworkReachabilityUnscheduleFromRunLoop(target
, runLoop
, runLoopMode
, FALSE
);
6319 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
6320 dispatch_queue_t queue
)
6324 if (!isA_SCNetworkReachability(target
)) {
6325 _SCErrorSet(kSCStatusInvalidArgument
);
6329 if (queue
!= NULL
) {
6330 ok
= __SCNetworkReachabilityScheduleWithRunLoop(target
, NULL
, NULL
, queue
, FALSE
);
6332 ok
= __SCNetworkReachabilityUnscheduleFromRunLoop(target
, NULL
, NULL
, FALSE
);