2 * Copyright (c) 2003-2013 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>
47 #include <SystemConfiguration/VPNAppLayerPrivate.h>
49 #include <libkern/OSAtomic.h>
52 #include <IOKit/pwr_mgt/IOPMLibPrivate.h>
53 #endif // !TARGET_OS_IPHONE
57 #include <netinet/in.h>
58 #include <arpa/inet.h>
60 #include <netdb_async.h>
63 #include <sys/ioctl.h>
64 #include <sys/socket.h>
66 #include <net/if_dl.h>
67 #include <net/if_types.h>
68 #define KERNEL_PRIVATE
69 #include <net/route.h>
73 #define s6_addr16 __u6_addr.__u6_addr16
76 #include "SCNetworkConnectionInternal.h"
78 #include "SCNetworkReachabilityInternal.h"
80 #include <ppp/ppp_msg.h>
81 #include <network_information.h>
83 #if defined(HAVE_IPSEC_STATUS) || defined(HAVE_VPN_STATUS)
84 #include <ppp/PPPControllerPriv.h>
85 #endif // !defined(HAVE_IPSEC_STATUS) || defined(HAVE_VPN_STATUS)
92 #define DEBUG_REACHABILITY_TYPE_NAME "create w/name"
93 #define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
95 #define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"
96 #define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
98 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"
99 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
101 #define DNS_FLAGS_FORMAT "[%s%s%s%s]"
102 #define DNS_FLAGS_VALUES(t) t->dnsHaveV4 ? "4" : "", \
103 t->dnsHaveV6 ? "6" : "", \
104 t->dnsHaveTimeout ? "T" : "", \
105 t->dnsHaveError ? "E" : ""
108 static pthread_mutexattr_t lock_attr
;
110 #define MUTEX_INIT(m) { \
111 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \
115 #define MUTEX_LOCK(m) { \
116 int _lock_ = (pthread_mutex_lock(m) == 0); \
120 #define MUTEX_UNLOCK(m) { \
121 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
125 #define MUTEX_ASSERT_HELD(m) { \
126 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \
131 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
134 _getaddrinfo_interface_async_call(const char *nodename
,
135 const char *servname
,
136 const struct addrinfo
*hints
,
137 const char *interface
,
138 getaddrinfo_async_callback callback
,
140 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
143 #define SCNETWORKREACHABILITY_TRIGGER_KEY CFSTR("com.apple.SCNetworkReachability:FORCE-CHANGE")
149 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
150 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
151 static void reachPerform (void *info
);
155 __SCNetworkReachabilityScheduleWithRunLoop (SCNetworkReachabilityRef target
,
156 CFRunLoopRef runLoop
,
157 CFStringRef runLoopMode
,
158 dispatch_queue_t queue
,
162 __SCNetworkReachabilityUnscheduleFromRunLoop (SCNetworkReachabilityRef target
,
163 CFRunLoopRef runLoop
,
164 CFStringRef runLoopMode
,
168 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
171 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
173 "SCNetworkReachability", // className
176 __SCNetworkReachabilityDeallocate
, // dealloc
179 NULL
, // copyFormattingDesc
180 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
184 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
185 static const ReachabilityInfo NOT_REACHABLE
= { 0, 0, 0, { 0 }, FALSE
};
186 static const ReachabilityInfo NOT_REPORTED
= { 0, 0xFFFFFFFF, 0, { 0 }, FALSE
};
187 static int rtm_seq
= 0;
190 static const struct addrinfo HINTS_DEFAULT
= {
192 .ai_flags
= AI_ADDRCONFIG
| AI_PARALLEL
,
194 .ai_flags
= AI_ADDRCONFIG
,
195 #endif // AI_PARALLEL
199 static const struct timeval TIME_ZERO
= { 0, 0 };
202 static int dnsCount
= 0;
203 static DNSServiceRef dnsMain
= NULL
;
204 #ifdef USE_DNSSERVICEGETADDRINFO
205 static CFMutableSetRef dnsUpdated
= NULL
;
206 #endif // USE_DNSSERVICEGETADDRINFO
209 #ifdef HAVE_REACHABILITY_SERVER
210 static Boolean D_serverBypass
= FALSE
;
211 #endif // HAVE_REACHABILITY_SERVER
213 static Boolean D_nwiBypass
= FALSE
;
217 #if !TARGET_OS_IPHONE
219 * Power capabilities (sleep/wake)
221 static IOPMSystemPowerStateCapabilities power_capabilities
= kIOPMSytemPowerStateCapabilitiesMask
;
222 #endif // !TARGET_OS_IPHONE
226 * host "something has changed" notifications
229 // Note: protected by _hn_target_queue()
230 static SCDynamicStoreRef hn_store
= NULL
;
231 static CFMutableSetRef hn_targets
= NULL
;
234 static dispatch_queue_t
237 static dispatch_once_t once
;
238 static dispatch_queue_t q
= NULL
;
240 dispatch_once(&once
, ^{
241 q
= dispatch_queue_create("SCNetworkReachabilty.handleChanges", NULL
);
248 static dispatch_queue_t
251 static dispatch_once_t once
;
252 static dispatch_queue_t q
;
254 dispatch_once(&once
, ^{
255 q
= dispatch_queue_create("SCNetworkReachabilty.targetManagement", NULL
);
267 dns_config_t
*config
;
269 } dns_configuration_t
;
272 // Note: protected by "dns_lock"
273 static pthread_mutex_t dns_lock
= PTHREAD_MUTEX_INITIALIZER
;
274 static dns_configuration_t
*dns_configuration
= NULL
;
275 static int dns_token
;
276 static Boolean dns_token_valid
= FALSE
;
289 __mark_operation_start(struct timeval
*queryStart
,
290 struct timeval
*queryEnd
)
292 (void) gettimeofday(queryStart
, NULL
);
293 *queryEnd
= TIME_ZERO
;
300 __mark_operation_end(SCNetworkReachabilityRef target
,
302 query_type query_type
,
303 struct timeval
*queryStart
,
304 struct timeval
*queryEnd
)
306 struct timeval queryElapsed
;
307 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
309 (void) gettimeofday(queryEnd
, NULL
);
315 if (!timerisset(queryStart
)) {
319 timersub(queryEnd
, queryStart
, &queryElapsed
);
320 switch (query_type
) {
322 #define QUERY_TIME__FMT "%d.%6.6d"
323 #define QUERY_TIME__DIV 1
325 case dns_query_sync
:
326 SCLog(TRUE
, LOG_INFO
,
327 CFSTR("%ssync DNS complete%s (query time = " QUERY_TIME__FMT
")"),
328 targetPrivate
->log_prefix
,
329 found
? "" : ", host not found",
331 queryElapsed
.tv_usec
/ QUERY_TIME__DIV
);
333 case dns_query_async
:
334 SCLog(TRUE
, LOG_INFO
,
335 CFSTR("%sasync DNS complete%s (query time = " QUERY_TIME__FMT
")"),
336 targetPrivate
->log_prefix
,
337 found
? "" : ", host not found",
339 queryElapsed
.tv_usec
/ QUERY_TIME__DIV
);
341 case dns_query_mdns
:
342 SCLog(TRUE
, LOG_INFO
,
343 CFSTR("%s[m]DNS query complete%s (query time = " QUERY_TIME__FMT
"), " DNS_FLAGS_FORMAT
),
344 targetPrivate
->log_prefix
,
345 found
? "" : ", host not found",
347 queryElapsed
.tv_usec
/ QUERY_TIME__DIV
,
348 DNS_FLAGS_VALUES(targetPrivate
));
356 static __inline__ Boolean
357 __reach_changed(ReachabilityInfo
*r1
, ReachabilityInfo
*r2
)
359 if (r1
->flags
!= r2
->flags
) {
360 // if the reachability flags changed
364 if (r1
->if_index
!= r2
->if_index
) {
365 // if the target interface changed
369 if ((r1
->sleeping
!= r2
->sleeping
) && !r2
->sleeping
) {
370 // if our sleep/wake status changed and if we
371 // are no longer sleeping
379 static __inline__
void
380 _reach_set(ReachabilityInfo
*dst
,
381 const ReachabilityInfo
*src
,
383 unsigned int requested_if_index
,
384 const char *requested_if_name
)
386 memcpy(dst
, src
, sizeof(ReachabilityInfo
));
389 if (!(dst
->flags
& kSCNetworkReachabilityFlagsReachable
) ||
390 (dst
->flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
391 // if not reachable or connection required, return the
392 // requested if_index and if_name.
393 dst
->if_index
= requested_if_index
;
394 if (requested_if_name
!= NULL
) {
395 strlcpy(dst
->if_name
, requested_if_name
, sizeof(dst
->if_name
));
397 dst
->if_name
[0] = '\0';
406 #pragma mark SCDynamicStore info
410 SCDynamicStoreRef store
;
412 CFDictionaryRef dict
;
415 const void * keys_q
[N_QUICK
];
416 const void ** values
;
417 const void * values_q
[N_QUICK
];
418 } ReachabilityStoreInfo
, *ReachabilityStoreInfoRef
;
421 static ReachabilityStoreInfo S_storeInfo
= { 0 };
422 static Boolean S_storeInfoActive
= FALSE
;
425 static dispatch_queue_t
428 static dispatch_once_t once
;
429 static dispatch_queue_t q
;
431 dispatch_once(&once
, ^{
432 q
= dispatch_queue_create("SCNetworkReachabilty.storeInfo", NULL
);
440 ReachabilityStoreInfo_copy(ReachabilityStoreInfoRef src
,
441 ReachabilityStoreInfoRef dst
)
443 if (src
->dict
!= NULL
) {
444 dst
->store
= src
->store
;
445 CFRetain(dst
->store
);
447 dst
->dict
= src
->dict
;
452 if (dst
->n
<= (CFIndex
)(sizeof(dst
->keys_q
) / sizeof(CFTypeRef
))) {
453 dst
->keys
= dst
->keys_q
;
454 dst
->values
= dst
->values_q
;
456 dst
->keys
= CFAllocatorAllocate(NULL
, dst
->n
* sizeof(CFTypeRef
), 0);
457 dst
->values
= CFAllocatorAllocate(NULL
, dst
->n
* sizeof(CFTypeRef
), 0);
459 memcpy(dst
->keys
, src
->keys
, dst
->n
* sizeof(CFTypeRef
));
460 memcpy(dst
->values
, src
->values
, dst
->n
* sizeof(CFTypeRef
));
469 ReachabilityStoreInfo_enable(Boolean enable
)
471 dispatch_sync(_storeInfo_queue(), ^{
472 S_storeInfoActive
= enable
;
480 ReachabilityStoreInfo_free(ReachabilityStoreInfoRef store_info
)
482 if ((store_info
->n
> 0) && (store_info
->keys
!= store_info
->keys_q
)) {
483 CFAllocatorDeallocate(NULL
, store_info
->keys
);
484 store_info
->keys
= NULL
;
486 CFAllocatorDeallocate(NULL
, store_info
->values
);
487 store_info
->values
= NULL
;
491 if (store_info
->dict
!= NULL
) {
492 CFRelease(store_info
->dict
);
493 store_info
->dict
= NULL
;
496 if (store_info
->store
!= NULL
) {
497 CFRelease(store_info
->store
);
498 store_info
->store
= NULL
;
506 ReachabilityStoreInfo_init(ReachabilityStoreInfoRef store_info
)
508 dispatch_sync(_storeInfo_queue(), ^{
509 bzero(store_info
, sizeof(ReachabilityStoreInfo
));
511 if (S_storeInfoActive
&& (S_storeInfo
.dict
!= NULL
)) {
512 ReachabilityStoreInfo_copy(&S_storeInfo
, store_info
);
521 ReachabilityStoreInfo_save(ReachabilityStoreInfoRef store_info
)
523 dispatch_sync(_storeInfo_queue(), ^{
524 if ((store_info
== NULL
) ||
525 !_SC_CFEqual(store_info
->dict
, S_storeInfo
.dict
)) {
527 ReachabilityStoreInfo_free(&S_storeInfo
);
530 if (S_storeInfoActive
&&
531 (store_info
!= NULL
) &&
532 (store_info
->dict
!= NULL
)) {
533 ReachabilityStoreInfo_copy(store_info
, &S_storeInfo
);
543 ReachabilityStoreInfo_fill(ReachabilityStoreInfoRef store_info
)
546 CFMutableArrayRef keys
;
548 CFMutableArrayRef patterns
;
550 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
551 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
553 // get info for IPv4 services
554 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
555 kSCDynamicStoreDomainState
,
557 CFArrayAppendValue(keys
, key
);
559 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
560 kSCDynamicStoreDomainSetup
,
563 CFArrayAppendValue(patterns
, pattern
);
565 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
566 kSCDynamicStoreDomainState
,
569 CFArrayAppendValue(patterns
, pattern
);
572 // get info for IPv6 services
573 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
574 kSCDynamicStoreDomainState
,
576 CFArrayAppendValue(keys
, key
);
578 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
579 kSCDynamicStoreDomainSetup
,
582 CFArrayAppendValue(patterns
, pattern
);
584 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
585 kSCDynamicStoreDomainState
,
588 CFArrayAppendValue(patterns
, pattern
);
591 // get info for PPP services
592 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
593 kSCDynamicStoreDomainSetup
,
596 CFArrayAppendValue(patterns
, pattern
);
598 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
599 kSCDynamicStoreDomainState
,
602 CFArrayAppendValue(patterns
, pattern
);
605 // get info for VPN services
606 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
607 kSCDynamicStoreDomainSetup
,
610 CFArrayAppendValue(patterns
, pattern
);
612 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
613 kSCDynamicStoreDomainState
,
616 CFArrayAppendValue(patterns
, pattern
);
619 // get info for IPSec services
620 // pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
621 // kSCDynamicStoreDomainSetup,
624 // CFArrayAppendValue(patterns, pattern);
625 // CFRelease(pattern);
626 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
627 kSCDynamicStoreDomainState
,
630 CFArrayAppendValue(patterns
, pattern
);
633 // get info to identify "available" services
634 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
635 kSCDynamicStoreDomainSetup
,
638 CFArrayAppendValue(patterns
, pattern
);
642 // get the SCDynamicStore info
643 store_info
->dict
= SCDynamicStoreCopyMultiple(store_info
->store
, keys
, patterns
);
646 if (store_info
->dict
== NULL
) {
650 // and extract the keys/values for post-processing
651 store_info
->n
= CFDictionaryGetCount(store_info
->dict
);
652 if (store_info
->n
> 0) {
653 if (store_info
->n
<= (CFIndex
)(sizeof(store_info
->keys_q
) / sizeof(CFTypeRef
))) {
654 store_info
->keys
= store_info
->keys_q
;
655 store_info
->values
= store_info
->values_q
;
657 store_info
->keys
= CFAllocatorAllocate(NULL
, store_info
->n
* sizeof(CFTypeRef
), 0);
658 store_info
->values
= CFAllocatorAllocate(NULL
, store_info
->n
* sizeof(CFTypeRef
), 0);
660 CFDictionaryGetKeysAndValues(store_info
->dict
,
670 ReachabilityStoreInfo_update(ReachabilityStoreInfoRef store_info
,
671 SCDynamicStoreRef
*storeP
,
672 sa_family_t sa_family
)
674 __block Boolean ok
= TRUE
;
678 store_info
->entity
= NULL
;
681 store_info
->entity
= kSCEntNetIPv4
;
684 store_info
->entity
= kSCEntNetIPv6
;
690 if (store_info
->dict
!= NULL
) {
691 // if info already available
695 dispatch_sync(_storeInfo_queue(), ^{
696 if (S_storeInfoActive
&& (S_storeInfo
.dict
!= NULL
)) {
698 ReachabilityStoreInfo_free(store_info
);
700 // copy the shared/available info
701 ReachabilityStoreInfo_copy(&S_storeInfo
, store_info
);
704 if (store_info
->store
== NULL
) {
705 store_info
->store
= (storeP
!= NULL
) ? *storeP
: NULL
;
706 if (store_info
->store
!= NULL
) {
707 // keep a reference to the passed in SCDynamicStore
708 CFRetain(store_info
->store
);
710 store_info
->store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
711 if (store_info
->store
== NULL
) {
712 SCLog(TRUE
, LOG_ERR
, CFSTR("ReachabilityStoreInfo_update SCDynamicStoreCreate() failed"));
716 if (storeP
!= NULL
) {
717 // and pass back a reference
718 *storeP
= store_info
->store
;
724 if (sa_family
== AF_UNSPEC
) {
725 // if the address family was not specified than
726 // all we wanted, for now, was to establish the
727 // SCDynamicStore session
731 if (store_info
->dict
!= NULL
) {
732 // or we have picked up the shared info
736 ok
= ReachabilityStoreInfo_fill(store_info
);
741 if (!_SC_CFEqual(store_info
->dict
, S_storeInfo
.dict
)) {
743 ReachabilityStoreInfo_free(&S_storeInfo
);
746 if (S_storeInfoActive
&&
747 (store_info
->dict
!= NULL
)) {
748 ReachabilityStoreInfo_copy(store_info
, &S_storeInfo
);
758 #pragma mark Legacy Reachability Functions - to be deprecated.
760 // Note: these can/should be removed when the iOS simulator will only
761 // be used on a system with the enhanced (with reachability
762 // flags) NWI content.
764 #pragma mark PPP info
768 updatePPPStatus(ReachabilityStoreInfoRef store_info
,
769 const struct sockaddr
*sa
,
771 SCNetworkReachabilityFlags
*flags
,
772 CFStringRef
*ppp_server
,
773 const char *log_prefix
)
777 int sc_status
= kSCStatusNoKey
;
779 if (!ReachabilityStoreInfo_update(store_info
, NULL
, sa
->sa_family
)) {
780 return kSCStatusReachabilityUnknown
;
783 if (store_info
->n
<= 0) {
785 return kSCStatusNoKey
;
788 // look for the [PPP] service which matches the provided interface
790 ppp_if
= CFStringCreateWithCStringNoCopy(NULL
,
792 kCFStringEncodingASCII
,
795 for (i
=0; i
< store_info
->n
; i
++) {
796 CFArrayRef components
;
799 CFDictionaryRef p_setup
;
800 CFDictionaryRef p_state
;
803 CFStringRef service
= NULL
;
804 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
805 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
808 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
812 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
813 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
814 continue; // if not an active IPv4 or IPv6 entity
817 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
818 if (!isA_CFString(s_if
)) {
819 continue; // if no interface
822 if (!CFEqual(ppp_if
, s_if
)) {
823 continue; // if not this interface
826 // extract the service ID, get the PPP "state" entity for
827 // the "Status", and get the PPP "setup" entity for the
828 // the "DialOnDemand" flag
829 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
830 if (CFArrayGetCount(components
) != 5) {
831 CFRelease(components
);
834 service
= CFArrayGetValueAtIndex(components
, 3);
835 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
836 kSCDynamicStoreDomainState
,
839 p_state
= CFDictionaryGetValue(store_info
->dict
, key
);
841 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
842 kSCDynamicStoreDomainSetup
,
845 p_setup
= CFDictionaryGetValue(store_info
->dict
, key
);
847 CFRelease(components
);
849 // ensure that this is a PPP service
850 if (!isA_CFDictionary(p_state
)) {
854 sc_status
= kSCStatusOK
;
856 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
859 if (ppp_server
!= NULL
) {
860 *ppp_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
861 *ppp_server
= isA_CFString(*ppp_server
);
862 if (*ppp_server
!= NULL
) {
863 CFRetain(*ppp_server
);
868 if (!CFDictionaryGetValueIfPresent(p_state
,
870 (const void **)&num
) ||
871 !isA_CFNumber(num
) ||
872 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) {
875 switch (ppp_status
) {
877 // if we're really UP and RUNNING
880 // if we're effectively UP and RUNNING
883 // if we're not connected at all
884 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link idle"),
886 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
888 case PPP_STATERESERVED
:
889 // if we're not connected at all
890 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link idle, dial-on-traffic to connect"),
892 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
895 // if we're in the process of [dis]connecting
896 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link, connection in progress"),
898 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
902 // get PPP dial-on-traffic status
903 if (isA_CFDictionary(p_setup
) &&
904 CFDictionaryGetValueIfPresent(p_setup
,
905 kSCPropNetPPPDialOnDemand
,
906 (const void **)&num
) &&
908 CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
) &&
910 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
911 if (ppp_status
== PPP_IDLE
) {
912 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
926 updatePPPAvailable(ReachabilityStoreInfoRef store_info
,
927 const struct sockaddr
*sa
,
928 SCNetworkReachabilityFlags
*flags
,
929 const char *log_prefix
)
932 int sc_status
= kSCStatusNoKey
;
934 if (!ReachabilityStoreInfo_update(store_info
,
936 (sa
!= NULL
) ? sa
->sa_family
: AF_INET
)) {
937 return kSCStatusReachabilityUnknown
;
940 if (store_info
->n
<= 0) {
942 return kSCStatusNoKey
;
945 // look for an available service which will provide connectivity
946 // for the requested address family.
948 for (i
= 0; i
< store_info
->n
; i
++) {
949 CFArrayRef components
;
950 Boolean found
= FALSE
;
952 CFDictionaryRef i_dict
;
954 CFDictionaryRef p_dict
;
956 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
957 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
959 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
963 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
964 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainSetup
)) {
965 continue; // if not an IPv4 or IPv6 entity
968 // extract service ID
969 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
970 if (CFArrayGetCount(components
) != 5) {
971 CFRelease(components
);
974 service
= CFArrayGetValueAtIndex(components
, 3);
976 // check for [non-VPN] PPP entity
977 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
978 kSCDynamicStoreDomainSetup
,
981 p_dict
= CFDictionaryGetValue(store_info
->dict
, p_key
);
984 i_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
985 kSCDynamicStoreDomainSetup
,
988 i_dict
= CFDictionaryGetValue(store_info
->dict
, i_key
);
991 if (isA_CFDictionary(p_dict
) &&
992 isA_CFDictionary(i_dict
) &&
993 CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) {
996 // we have a PPP service for this address family
999 *flags
|= kSCNetworkReachabilityFlagsReachable
;
1000 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1001 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1003 // get PPP dial-on-traffic status
1004 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
1005 if (isA_CFNumber(num
)) {
1008 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
1010 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
1016 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after connect)"),
1018 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service = %@"),
1025 CFRelease(components
);
1028 sc_status
= kSCStatusOK
;
1036 #pragma mark VPN info
1040 updateVPNStatus(ReachabilityStoreInfoRef store_info
,
1041 const struct sockaddr
*sa
,
1042 const char *if_name
,
1043 SCNetworkReachabilityFlags
*flags
,
1044 CFStringRef
*vpn_server
,
1045 const char *log_prefix
)
1049 int sc_status
= kSCStatusNoKey
;
1051 if (!ReachabilityStoreInfo_update(store_info
, NULL
, sa
->sa_family
)) {
1052 return kSCStatusReachabilityUnknown
;
1055 if (store_info
->n
<= 0) {
1057 return kSCStatusNoKey
;
1060 // look for the [VPN] service which matches the provided interface
1062 vpn_if
= CFStringCreateWithCStringNoCopy(NULL
,
1064 kCFStringEncodingASCII
,
1067 for (i
=0; i
< store_info
->n
; i
++) {
1068 CFArrayRef components
;
1071 CFDictionaryRef p_state
;
1073 CFStringRef service
= NULL
;
1074 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
1075 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
1078 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
1082 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
1083 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
1084 continue; // if not an active IPv4 or IPv6 entity
1087 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
1088 if (!isA_CFString(s_if
)) {
1089 continue; // if no interface
1092 if (!CFEqual(vpn_if
, s_if
)) {
1093 continue; // if not this interface
1096 // extract the service ID and get the VPN "state" entity for
1098 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
1099 if (CFArrayGetCount(components
) != 5) {
1100 CFRelease(components
);
1103 service
= CFArrayGetValueAtIndex(components
, 3);
1104 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1105 kSCDynamicStoreDomainState
,
1108 p_state
= CFDictionaryGetValue(store_info
->dict
, key
);
1110 CFRelease(components
);
1112 // ensure that this is a VPN service
1113 if (!isA_CFDictionary(p_state
)) {
1117 sc_status
= kSCStatusOK
;
1119 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1122 if (vpn_server
!= NULL
) {
1123 *vpn_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
1124 *vpn_server
= isA_CFString(*vpn_server
);
1125 if (*vpn_server
!= NULL
) {
1126 CFRetain(*vpn_server
);
1131 if (!CFDictionaryGetValueIfPresent(p_state
,
1132 kSCPropNetVPNStatus
,
1133 (const void **)&num
) ||
1134 !isA_CFNumber(num
) ||
1135 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &vpn_status
)) {
1138 #ifdef HAVE_VPN_STATUS
1139 switch (vpn_status
) {
1141 // if we're really UP and RUNNING
1146 case VPN_UNLOADING
:
1147 // if we're not connected at all
1148 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s VPN link idle"),
1150 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1153 // if we're in the process of [dis]connecting
1154 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s VPN link, connection in progress"),
1156 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1159 #endif // HAVE_VPN_STATUS
1171 updateVPNAvailable(ReachabilityStoreInfoRef store_info
,
1172 const struct sockaddr
*sa
,
1173 SCNetworkReachabilityFlags
*flags
,
1174 const char *log_prefix
)
1177 int sc_status
= kSCStatusNoKey
;
1179 if (!ReachabilityStoreInfo_update(store_info
,
1181 (sa
!= NULL
) ? sa
->sa_family
: AF_INET
)) {
1182 return kSCStatusReachabilityUnknown
;
1185 if (store_info
->n
<= 0) {
1187 return kSCStatusNoKey
;
1190 // look for an available service which will provide connectivity
1191 // for the requested address family.
1193 for (i
= 0; i
< store_info
->n
; i
++) {
1194 CFArrayRef components
;
1195 Boolean found
= FALSE
;
1197 CFDictionaryRef i_dict
;
1199 CFDictionaryRef p_dict
;
1200 CFStringRef service
;
1201 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
1202 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
1204 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
1208 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
1209 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainSetup
)) {
1210 continue; // if not an IPv4 or IPv6 entity
1213 // extract service ID
1214 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
1215 if (CFArrayGetCount(components
) != 5) {
1216 CFRelease(components
);
1219 service
= CFArrayGetValueAtIndex(components
, 3);
1221 // check for VPN entity
1222 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1223 kSCDynamicStoreDomainSetup
,
1226 p_dict
= CFDictionaryGetValue(store_info
->dict
, p_key
);
1229 i_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1230 kSCDynamicStoreDomainSetup
,
1232 kSCEntNetInterface
);
1233 i_dict
= CFDictionaryGetValue(store_info
->dict
, i_key
);
1236 if (isA_CFDictionary(p_dict
) &&
1237 isA_CFDictionary(i_dict
) &&
1238 CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) {
1239 // we have a VPN service for this address family
1242 *flags
|= kSCNetworkReachabilityFlagsReachable
;
1243 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1244 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1247 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after connect)"),
1249 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service = %@"),
1256 CFRelease(components
);
1259 sc_status
= kSCStatusOK
;
1268 #pragma mark IPSec info
1272 updateIPSecStatus(ReachabilityStoreInfoRef store_info
,
1273 const struct sockaddr
*sa
,
1274 const char *if_name
,
1275 SCNetworkReachabilityFlags
*flags
,
1276 CFStringRef
*ipsec_server
,
1277 const char *log_prefix
)
1280 CFStringRef ipsec_if
;
1281 int sc_status
= kSCStatusNoKey
;
1283 if (!ReachabilityStoreInfo_update(store_info
, NULL
, sa
->sa_family
)) {
1284 return kSCStatusReachabilityUnknown
;
1287 if (store_info
->n
<= 0) {
1289 return kSCStatusNoKey
;
1292 // look for the [IPSec] service that matches the provided interface
1294 ipsec_if
= CFStringCreateWithCStringNoCopy(NULL
,
1296 kCFStringEncodingASCII
,
1299 for (i
=0; i
< store_info
->n
; i
++) {
1300 CFArrayRef components
;
1302 CFDictionaryRef i_state
;
1303 int32_t ipsec_status
;
1305 CFStringRef service
= NULL
;
1306 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
1307 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
1310 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
1314 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
1315 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
1316 continue; // if not an IPv4 or IPv6 entity
1319 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
1320 if (!isA_CFString(s_if
)) {
1321 continue; // if no interface
1324 if (!CFEqual(ipsec_if
, s_if
)) {
1325 continue; // if not this interface
1328 // extract the service ID, get the IPSec "state" entity for
1329 // the "Status", and get the IPSec "setup" entity to confirm
1330 // that we're looking at what we're expecting
1331 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
1332 if (CFArrayGetCount(components
) != 5) {
1333 CFRelease(components
);
1336 service
= CFArrayGetValueAtIndex(components
, 3);
1337 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1338 kSCDynamicStoreDomainState
,
1341 i_state
= CFDictionaryGetValue(store_info
->dict
, key
);
1343 CFRelease(components
);
1345 // ensure that this is an IPSec service
1346 if (!isA_CFDictionary(i_state
)) {
1350 sc_status
= kSCStatusOK
;
1352 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1355 if (ipsec_server
!= NULL
) {
1356 *ipsec_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
1357 *ipsec_server
= isA_CFString(*ipsec_server
);
1358 if (*ipsec_server
!= NULL
) {
1359 CFRetain(*ipsec_server
);
1364 if (!CFDictionaryGetValueIfPresent(i_state
,
1365 kSCPropNetIPSecStatus
,
1366 (const void **)&num
) ||
1367 !isA_CFNumber(num
) ||
1368 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ipsec_status
)) {
1371 #ifdef HAVE_IPSEC_STATUS
1372 switch (ipsec_status
) {
1373 case IPSEC_RUNNING
:
1374 // if we're really UP and RUNNING
1377 // if we're not connected at all
1378 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s IPSec link idle"),
1380 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1383 // if we're in the process of [dis]connecting
1384 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s IPSec link, connection in progress"),
1386 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1389 #endif // HAVE_IPSEC_STATUS
1394 CFRelease(ipsec_if
);
1403 #pragma mark Reachability engine
1406 #define ROUNDUP(a, size) \
1407 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
1409 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
1410 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
1411 sizeof(uint32_t)) :\
1415 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
1419 for (i
= 0; i
< RTAX_MAX
; i
++) {
1420 if (addrs
& (1 << i
)) {
1429 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
1435 struct rt_msghdr rtm
;
1438 struct sockaddr
*rti_info
[RTAX_MAX
];
1439 struct rt_msghdr
*rtm
;
1440 struct sockaddr_dl
*sdl
;
1441 } route_info
, *route_info_p
;
1446 * returns zero if route exists and data returned, EHOSTUNREACH
1447 * if no route, or errno for any other error.
1450 route_get(const struct sockaddr
*address
,
1451 unsigned int if_index
,
1456 pid_t pid
= getpid();
1458 struct sockaddr
*sa
;
1459 int32_t seq
= OSAtomicIncrement32Barrier(&rtm_seq
);
1460 #ifndef RTM_GET_SILENT
1461 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
1462 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
1463 int sosize
= 48 * 1024;
1466 bzero(info
, sizeof(*info
));
1468 info
->rtm
= &info
->buf
.rtm
;
1469 info
->rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
1470 info
->rtm
->rtm_version
= RTM_VERSION
;
1471 #ifdef RTM_GET_SILENT
1472 info
->rtm
->rtm_type
= RTM_GET_SILENT
;
1474 info
->rtm
->rtm_type
= RTM_GET
;
1476 info
->rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
1477 info
->rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
1478 info
->rtm
->rtm_pid
= pid
;
1479 info
->rtm
->rtm_seq
= seq
;
1481 if (if_index
!= 0) {
1482 info
->rtm
->rtm_flags
|= RTF_IFSCOPE
;
1483 info
->rtm
->rtm_index
= if_index
;
1486 switch (address
->sa_family
) {
1488 struct sockaddr_in6
*sin6
;
1490 /* ALIGN: caller ensures that the address is aligned */
1491 sin6
= (struct sockaddr_in6
*)(void *)address
;
1492 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
1493 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
1494 (sin6
->sin6_scope_id
!= 0)) {
1495 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
1496 sin6
->sin6_scope_id
= 0;
1502 sa
= (struct sockaddr
*) (info
->rtm
+ 1);
1503 bcopy(address
, sa
, address
->sa_len
);
1504 n
= ROUNDUP(sa
->sa_len
, sizeof(uint32_t));
1505 info
->rtm
->rtm_msglen
+= n
;
1507 info
->sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
1508 info
->sdl
->sdl_family
= AF_LINK
;
1509 info
->sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
1510 n
= ROUNDUP(info
->sdl
->sdl_len
, sizeof(uint32_t));
1511 info
->rtm
->rtm_msglen
+= n
;
1513 #ifndef RTM_GET_SILENT
1514 pthread_mutex_lock(&lock
);
1516 rsock
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
);
1520 #ifndef RTM_GET_SILENT
1521 pthread_mutex_unlock(&lock
);
1523 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error
));
1527 if (ioctl(rsock
, FIONBIO
, &opt
) < 0) {
1531 #ifndef RTM_GET_SILENT
1532 pthread_mutex_unlock(&lock
);
1534 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl(FIONBIO) failed: %s"), strerror(error
));
1538 #ifndef RTM_GET_SILENT
1539 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
1543 pthread_mutex_unlock(&lock
);
1544 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error
));
1549 if (write(rsock
, &info
->buf
, info
->rtm
->rtm_msglen
) == -1) {
1553 #ifndef RTM_GET_SILENT
1554 pthread_mutex_unlock(&lock
);
1556 if (error
!= ESRCH
) {
1557 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(error
));
1560 return EHOSTUNREACH
;
1564 * Type, seq, pid identify our response.
1565 * Routing sockets are broadcasters on input.
1570 n
= read(rsock
, &info
->buf
, sizeof(info
->buf
));
1574 if (error
== EINTR
) {
1578 #ifndef RTM_GET_SILENT
1579 pthread_mutex_unlock(&lock
);
1581 SCLog(TRUE
, LOG_ERR
,
1582 CFSTR("SCNetworkReachability: routing socket"
1583 " read() failed: %s"), strerror(error
));
1586 if ((info
->rtm
->rtm_type
== RTM_GET
) &&
1587 (info
->rtm
->rtm_seq
== seq
) &&
1588 (info
->rtm
->rtm_pid
== pid
)) {
1594 #ifndef RTM_GET_SILENT
1595 pthread_mutex_unlock(&lock
);
1598 get_rtaddrs(info
->rtm
->rtm_addrs
, sa
, info
->rti_info
);
1600 //#define LOG_RTADDRS
1605 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), info
->rtm
->rtm_flags
);
1607 if ((info
->rti_info
[RTAX_NETMASK
] != NULL
) && (info
->rti_info
[RTAX_DST
] != NULL
)) {
1608 info
->rti_info
[RTAX_NETMASK
]->sa_family
= info
->rti_info
[RTAX_DST
]->sa_family
;
1611 for (i
= 0; i
< RTAX_MAX
; i
++) {
1612 if (info
->rti_info
[i
] != NULL
) {
1615 _SC_sockaddr_to_string(info
->rti_info
[i
], addr
, sizeof(addr
));
1616 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, addr
);
1620 #endif // LOG_RTADDRS
1622 if ((info
->rti_info
[RTAX_IFP
] == NULL
) ||
1623 (info
->rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
1624 /* no interface info */
1625 SCLog(TRUE
, LOG_DEBUG
, CFSTR("route_get() no interface info"));
1629 /* ALIGN: accessors are retrieving byte values, cast ok. */
1630 info
->sdl
= (struct sockaddr_dl
*)(void *) info
->rti_info
[RTAX_IFP
];
1631 if ((info
->sdl
->sdl_nlen
== 0) || (info
->sdl
->sdl_nlen
> IFNAMSIZ
)) {
1632 /* no interface name */
1633 return EHOSTUNREACH
;
1640 checkAddressRoute(const struct sockaddr
*address
,
1641 unsigned int if_index
,
1644 ReachabilityInfo
*reach_info
,
1647 const char *log_prefix
)
1651 char *statusMessage
= NULL
;
1652 struct sockaddr_in v4mapped
;
1654 switch (address
->sa_family
) {
1659 char if_name
[IFNAMSIZ
+ 1];
1661 _SC_sockaddr_to_string(address
, addr
, sizeof(addr
));
1663 if ((if_index
!= 0) &&
1664 (if_indextoname(if_index
, &if_name
[1]) != NULL
)) {
1670 SCLog(TRUE
, LOG_INFO
, CFSTR("%scheckAddress(%s%s)"),
1678 * if no code for this address family (yet)
1680 SCLog(TRUE
, LOG_INFO
,
1681 CFSTR("checkAddress(): unexpected address family %d"),
1682 address
->sa_family
);
1683 *sc_status
= kSCStatusInvalidArgument
;
1688 if (address
->sa_family
== AF_INET6
) {
1689 /* ALIGN: sin6_addr accessed aligned, cast ok. */
1690 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)(void *)address
;
1692 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) {
1693 bzero(&v4mapped
, sizeof(v4mapped
));
1694 v4mapped
.sin_len
= sizeof(v4mapped
);
1695 v4mapped
.sin_family
= AF_INET
;
1696 v4mapped
.sin_port
= sin6
->sin6_port
;
1697 v4mapped
.sin_addr
.s_addr
= sin6
->sin6_addr
.__u6_addr
.__u6_addr32
[3];
1698 address
= (struct sockaddr
*)&v4mapped
;
1702 ret
= route_get(address
, if_index
, info
);
1715 /* get the interface flags */
1717 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
1720 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
1724 bzero(ifr
, sizeof(*ifr
));
1725 bcopy(info
->sdl
->sdl_data
, ifr
->ifr_name
, info
->sdl
->sdl_nlen
);
1727 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)ifr
) == -1) {
1729 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl(SIOCGIFFLAGS) failed: %s"), strerror(errno
));
1733 if (!(ifr
->ifr_flags
& IFF_UP
)) {
1738 statusMessage
= "isReachable";
1739 reach_info
->flags
|= kSCNetworkReachabilityFlagsReachable
;
1741 if (info
->rtm
->rtm_flags
& RTF_LOCAL
) {
1742 statusMessage
= "isReachable (is a local address)";
1743 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1744 } else if (ifr
->ifr_flags
& IFF_LOOPBACK
) {
1745 statusMessage
= "isReachable (is loopback network)";
1746 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1747 } else if ((info
->rti_info
[RTAX_IFA
] != NULL
) &&
1748 (info
->rti_info
[RTAX_IFA
]->sa_family
!= AF_LINK
)) {
1749 void *addr1
= (void *)address
;
1750 void *addr2
= (void *)info
->rti_info
[RTAX_IFA
];
1751 size_t len
= address
->sa_len
;
1753 if ((address
->sa_family
!= info
->rti_info
[RTAX_IFA
]->sa_family
) &&
1754 (address
->sa_len
!= info
->rti_info
[RTAX_IFA
]->sa_len
)) {
1755 SCLog(TRUE
, LOG_NOTICE
,
1756 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
1759 info
->rti_info
[RTAX_IFA
]->sa_family
,
1760 info
->rti_info
[RTAX_IFA
]->sa_len
);
1764 switch (address
->sa_family
) {
1766 /* ALIGN: cast ok, because only bcmp is used. */
1767 addr1
= &((struct sockaddr_in
*)(void *)address
)->sin_addr
;
1768 addr2
= &((struct sockaddr_in
*)(void *)info
->rti_info
[RTAX_IFA
])->sin_addr
;
1769 len
= sizeof(struct in_addr
);
1774 /* ALIGN: sin_addr should be aligned, cast ok. */
1775 if (((struct sockaddr_in
*)(void *)address
)->sin_addr
.s_addr
== 0) {
1776 statusMessage
= "isReachable (this host)";
1777 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1781 /* ALIGN: cast ok, because only bcmp is used. */
1782 addr1
= &((struct sockaddr_in6
*)(void *)address
)->sin6_addr
;
1783 addr2
= &((struct sockaddr_in6
*)(void *)info
->rti_info
[RTAX_IFA
])->sin6_addr
;
1784 len
= sizeof(struct in6_addr
);
1790 if (bcmp(addr1
, addr2
, len
) == 0) {
1791 statusMessage
= "isReachable (is interface address)";
1792 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1796 if (!(info
->rtm
->rtm_flags
& RTF_GATEWAY
) &&
1797 (info
->rti_info
[RTAX_GATEWAY
] != NULL
) &&
1798 (info
->rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
) &&
1799 !(ifr
->ifr_flags
& IFF_POINTOPOINT
)) {
1800 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1803 bzero(if_name
, IFNAMSIZ
);
1804 bcopy(info
->sdl
->sdl_data
,
1806 (info
->sdl
->sdl_nlen
<= IFNAMSIZ
) ? info
->sdl
->sdl_nlen
: IFNAMSIZ
);
1808 strlcpy(reach_info
->if_name
, if_name
, sizeof(reach_info
->if_name
));
1809 reach_info
->if_index
= info
->sdl
->sdl_index
;
1812 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = %s"), log_prefix
, statusMessage
);
1813 SCLog(TRUE
, LOG_INFO
, CFSTR("%s device = %s (%hu)"), log_prefix
, if_name
, info
->sdl
->sdl_index
);
1814 SCLog(TRUE
, LOG_INFO
, CFSTR("%s sdl_type = 0x%x"), log_prefix
, info
->sdl
->sdl_type
);
1815 SCLog(TRUE
, LOG_INFO
, CFSTR("%s ifr_flags = 0x%04hx"), log_prefix
, ifr
->ifr_flags
);
1816 SCLog(TRUE
, LOG_INFO
, CFSTR("%s rtm_flags = 0x%08x"), log_prefix
, info
->rtm
->rtm_flags
);
1819 if (isock
!= -1) (void)close(isock
);
1824 checkAddress_with_nwi(const struct sockaddr
*address
,
1825 unsigned int if_index
,
1826 ReachabilityInfo
*reach_info
,
1827 const char *log_prefix
)
1831 char if_name
[IFNAMSIZ
];
1832 nwi_ifstate_t ifstate
;
1833 nwi_state_t nwi_state
;
1835 int sc_status
= kSCStatusReachabilityUnknown
;
1837 _reach_set(reach_info
, &NOT_REACHABLE
, reach_info
->cycle
, if_index
, NULL
);
1839 nwi_state
= nwi_state_copy();
1841 if (address
!= NULL
) {
1842 ret
= checkAddressRoute(address
,
1853 /* special case: check only for available paths off the system */
1858 const struct sockaddr
*vpn_server_address
;
1860 sc_status
= kSCStatusOK
;
1862 ifstate
= nwi_state_get_ifstate(nwi_state
, if_name
);
1864 if (ifstate
== NULL
) {
1868 reach_info
->flags
|= nwi_ifstate_get_reachability_flags(ifstate
);
1871 vpn_server_address
= nwi_ifstate_get_vpn_server(ifstate
);
1872 if (vpn_server_address
!= NULL
) {
1873 char dst_if_name
[IFNAMSIZ
];
1874 route_info dst_info
;
1876 ret
= route_get(vpn_server_address
, 0, &dst_info
);
1881 bzero(&dst_if_name
, sizeof(dst_if_name
));
1882 bcopy(dst_info
.sdl
->sdl_data
,
1884 (dst_info
.sdl
->sdl_nlen
<= IFNAMSIZ
) ? dst_info
.sdl
->sdl_nlen
: IFNAMSIZ
);
1885 if (bcmp(if_name
, dst_if_name
, sizeof(if_name
)) != 0) {
1886 nwi_ifstate_t ifstate
;
1888 ifstate
= nwi_state_get_ifstate(nwi_state
, dst_if_name
);
1889 if (ifstate
!= NULL
) {
1890 reach_info
->flags
|= nwi_ifstate_get_reachability_flags(ifstate
);
1894 } else if (ret
== EHOSTUNREACH
) {
1895 if (if_index
== 0) {
1896 sc_status
= kSCStatusOK
;
1897 reach_info
->flags
|= nwi_state_get_reachability_flags(nwi_state
,
1898 (address
!= NULL
) ? address
->sa_family
1901 // if scoped request
1902 sc_status
= kSCStatusNoKey
;
1908 if (reach_info
->flags
== 0) {
1909 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s cannot be reached"), log_prefix
);
1912 if (nwi_state
!= NULL
) {
1913 nwi_state_release(nwi_state
);
1916 if ((sc_status
!= kSCStatusOK
) && (sc_status
!= kSCStatusNoKey
)) {
1917 _SCErrorSet(sc_status
);
1926 checkAddress_bypass_nwi(ReachabilityStoreInfoRef store_info
,
1927 const struct sockaddr
*address
,
1928 unsigned int if_index
,
1929 ReachabilityInfo
*reach_info
,
1930 const char *log_prefix
)
1934 char if_name
[IFNAMSIZ
];
1936 CFStringRef server
= NULL
;
1937 int sc_status
= kSCStatusReachabilityUnknown
;
1939 _reach_set(reach_info
, &NOT_REACHABLE
, reach_info
->cycle
, if_index
, NULL
);
1941 if (address
!= NULL
) {
1942 ret
= checkAddressRoute(address
,
1953 /* special case: check only for available paths off the system */
1958 sc_status
= kSCStatusOK
;
1960 if (ifr
.ifr_flags
& IFF_POINTOPOINT
) {
1961 reach_info
->flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1964 if (info
.sdl
->sdl_type
== IFT_PPP
) {
1966 * 1. check if PPP service
1967 * 2. check for dial-on-demand PPP link that is not yet connected
1968 * 3. get PPP server address
1970 sc_status
= updatePPPStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1971 } else if (info
.sdl
->sdl_type
== IFT_OTHER
) {
1973 * 1. check if IPSec service
1974 * 2. get IPSec server address
1976 sc_status
= updateIPSecStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1977 if (sc_status
== kSCStatusNoKey
) {
1979 * 1. check if VPN service
1980 * 2. get VPN server address
1982 sc_status
= updateVPNStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1986 } else if (ret
== EHOSTUNREACH
) {
1987 if (if_index
!= 0) {
1988 // if scoped request
1989 sc_status
= kSCStatusNoKey
;
1994 sc_status
= updatePPPAvailable(store_info
, address
, &reach_info
->flags
, log_prefix
);
1995 if ((sc_status
== kSCStatusOK
) && (reach_info
->flags
!= 0)) {
1999 sc_status
= updateVPNAvailable(store_info
, address
, &reach_info
->flags
, log_prefix
);
2000 if ((sc_status
== kSCStatusOK
) && (reach_info
->flags
!= 0)) {
2008 if (reach_info
->flags
== 0) {
2009 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s cannot be reached"), log_prefix
);
2012 if (server
!= NULL
) CFRelease(server
);
2014 if ((sc_status
!= kSCStatusOK
) && (sc_status
!= kSCStatusNoKey
)) {
2015 _SCErrorSet(sc_status
);
2023 static inline Boolean
2024 checkAddress(ReachabilityStoreInfoRef store_info
,
2025 const struct sockaddr
*address
,
2026 unsigned int if_index
,
2027 ReachabilityInfo
*reach_info
,
2028 const char *log_prefix
)
2031 return (checkAddress_with_nwi(address
,
2038 return (checkAddress_bypass_nwi(store_info
,
2046 #pragma mark SCNetworkReachability APIs
2049 static __inline__ CFTypeRef
2050 isA_SCNetworkReachability(CFTypeRef obj
)
2052 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
2057 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target
)
2059 CFAllocatorRef allocator
= CFGetAllocator(target
);
2060 CFMutableStringRef str
;
2061 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2063 str
= CFStringCreateMutable(allocator
, 0);
2064 switch (targetPrivate
->type
) {
2065 case reachabilityTypeAddress
:
2066 case reachabilityTypeAddressPair
: {
2069 if (targetPrivate
->localAddress
!= NULL
) {
2070 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
2071 CFStringAppendFormat(str
, NULL
, CFSTR("local address = %s"),
2075 if (targetPrivate
->remoteAddress
!= NULL
) {
2076 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
2077 CFStringAppendFormat(str
, NULL
, CFSTR("%s%saddress = %s"),
2078 targetPrivate
->localAddress
? ", " : "",
2079 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
2084 case reachabilityTypeName
: {
2085 CFStringAppendFormat(str
, NULL
, CFSTR("name = %s"), targetPrivate
->name
);
2095 _SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target
)
2097 CFAllocatorRef allocator
= CFGetAllocator(target
);
2099 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2101 str
= CFStringCreateWithFormat(allocator
,
2103 CFSTR("flags = 0x%08x, if_index = %u%s"),
2104 targetPrivate
->info
.flags
,
2105 targetPrivate
->info
.if_index
,
2106 targetPrivate
->info
.sleeping
? ", z" : "");
2112 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
2114 CFAllocatorRef allocator
= CFGetAllocator(cf
);
2115 CFMutableStringRef result
;
2117 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
2118 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2120 result
= CFStringCreateMutable(allocator
, 0);
2121 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
2123 // add target description
2124 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
2125 CFStringAppend(result
, str
);
2128 // add additional "name" info
2129 if (targetPrivate
->type
== reachabilityTypeName
) {
2130 if (targetPrivate
->dnsActive
) {
2131 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
2132 #ifdef HAVE_REACHABILITY_SERVER
2133 } else if (targetPrivate
->serverActive
&&
2134 (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
)) {
2135 CFStringAppendFormat(result
, NULL
, CFSTR(" (server query active)"));
2136 #endif // HAVE_REACHABILITY_SERVER
2137 } else if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
2138 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS* query active)"));
2139 } else if ((targetPrivate
->resolvedAddresses
!= NULL
) || (targetPrivate
->resolvedError
!= NETDB_SUCCESS
)) {
2140 if (targetPrivate
->resolvedAddresses
!= NULL
) {
2141 if (isA_CFArray(targetPrivate
->resolvedAddresses
)) {
2143 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddresses
);
2145 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
2146 for (i
= 0; i
< n
; i
++) {
2149 struct sockaddr
*sa
;
2151 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddresses
, i
);
2152 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
2153 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
2154 CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"),
2158 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
2159 } else if (CFEqual(targetPrivate
->resolvedAddresses
, kCFNull
)) {
2160 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
2161 gai_strerror(targetPrivate
->resolvedError
));
2163 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)"));
2166 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
2167 gai_strerror(targetPrivate
->resolvedError
));
2170 if (targetPrivate
->dnsFlags
!= 0) {
2171 CFStringAppendFormat(result
, NULL
, CFSTR(", " DNS_FLAGS_FORMAT
),
2172 DNS_FLAGS_VALUES(targetPrivate
));
2176 if (targetPrivate
->onDemandBypass
) {
2177 CFStringAppendFormat(result
, NULL
, CFSTR(", !ondemand"));
2181 if (targetPrivate
->resolverBypass
) {
2182 CFStringAppendFormat(result
, NULL
, CFSTR(", !resolve"));
2187 if (targetPrivate
->scheduled
) {
2188 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
2189 CFStringAppendFormat(result
, NULL
, CFSTR(", %@"), str
);
2193 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
2200 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
2202 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
2203 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2205 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%srelease"),
2206 targetPrivate
->log_prefix
);
2208 #ifdef HAVE_REACHABILITY_SERVER
2209 /* disconnect from the reachability server */
2211 if (targetPrivate
->serverActive
) {
2212 __SCNetworkReachabilityServer_targetRemove(target
);
2214 #endif // HAVE_REACHABILITY_SERVER
2216 /* release resources */
2218 pthread_mutex_destroy(&targetPrivate
->lock
);
2220 if (targetPrivate
->name
!= NULL
)
2221 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
2223 if (targetPrivate
->resolvedAddresses
!= NULL
)
2224 CFRelease(targetPrivate
->resolvedAddresses
);
2226 if (targetPrivate
->localAddress
!= NULL
)
2227 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
2229 if (targetPrivate
->remoteAddress
!= NULL
)
2230 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
2232 if (targetPrivate
->rlsContext
.release
!= NULL
) {
2233 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
2236 if (targetPrivate
->onDemandName
!= NULL
) {
2237 CFRelease(targetPrivate
->onDemandName
);
2240 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
2241 CFRelease(targetPrivate
->onDemandRemoteAddress
);
2244 if (targetPrivate
->onDemandServer
!= NULL
) {
2245 CFRelease(targetPrivate
->onDemandServer
);
2248 if (targetPrivate
->onDemandServiceID
!= NULL
) {
2249 CFRelease(targetPrivate
->onDemandServiceID
);
2252 #ifdef HAVE_REACHABILITY_SERVER
2253 if (targetPrivate
->serverDigest
!= NULL
) {
2254 CFRelease(targetPrivate
->serverDigest
);
2257 if (targetPrivate
->serverGroup
!= NULL
) {
2258 dispatch_release(targetPrivate
->serverGroup
);
2261 if (targetPrivate
->serverQueue
!= NULL
) {
2262 dispatch_release(targetPrivate
->serverQueue
);
2265 if (targetPrivate
->serverWatchers
!= NULL
) {
2266 CFRelease(targetPrivate
->serverWatchers
);
2268 #endif // HAVE_REACHABILITY_SERVER
2275 __SCNetworkReachabilityInitialize(void)
2277 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
2279 // provide a way to enable SCNetworkReachability logging without
2280 // having to set _sc_debug=1.
2281 if (getenv("REACH_LOGGING") != NULL
) {
2285 #ifdef HAVE_REACHABILITY_SERVER
2286 // set per-process "bypass" of the SCNetworkReachability server
2287 if (getenv("REACH_SERVER_BYPASS") != NULL
) {
2288 D_serverBypass
= TRUE
;
2290 #endif // HAVE_REACHABILITY_SERVER
2293 if (getenv("NWI_BYPASS") != NULL
) {
2297 pthread_mutexattr_init(&lock_attr
);
2298 pthread_mutexattr_settype(&lock_attr
, PTHREAD_MUTEX_ERRORCHECK
);
2306 __SCNetworkReachability_concurrent_queue()
2308 static dispatch_once_t once
;
2309 static dispatch_queue_t q
;
2311 dispatch_once(&once
, ^{
2312 q
= dispatch_queue_create("SCNetworkReachabilty.concurrent",
2313 DISPATCH_QUEUE_CONCURRENT
);
2314 dispatch_queue_set_width(q
, 32);
2322 * __SCNetworkReachabilityPerformConcurrent
2324 * Calls reachPerform()
2325 * - caller must be holding a reference to the target
2326 * - caller must *not* be holding the target lock
2327 * - caller must be running on the __SCNetworkReachability_concurrent_queue()
2331 __SCNetworkReachabilityPerformConcurrent(SCNetworkReachabilityRef target
)
2333 dispatch_queue_t queue
;
2334 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2336 MUTEX_LOCK(&targetPrivate
->lock
);
2338 queue
= targetPrivate
->dispatchQueue
;
2339 if (queue
!= NULL
) {
2340 dispatch_group_t group
;
2342 dispatch_retain(queue
);
2344 group
= targetPrivate
->dispatchGroup
;
2345 dispatch_group_enter(group
);
2347 MUTEX_UNLOCK(&targetPrivate
->lock
);
2349 dispatch_sync(queue
, ^{
2350 reachPerform((void *)target
);
2351 dispatch_group_leave(group
);
2353 dispatch_release(queue
);
2355 if (targetPrivate
->rls
!= NULL
) {
2356 CFRunLoopSourceSignal(targetPrivate
->rls
);
2357 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2360 MUTEX_UNLOCK(&targetPrivate
->lock
);
2368 * __SCNetworkReachabilityPerform
2370 * Calls reachPerform()
2371 * - caller can be holding the target lock
2372 * - caller can be running on any dispatch queue
2376 __SCNetworkReachabilityPerform(SCNetworkReachabilityRef target
)
2379 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
2380 __SCNetworkReachabilityPerformConcurrent(target
);
2389 * __SCNetworkReachabilityPerformLocked
2391 * Calls reachPerform()
2392 * - caller must be holding the target lock
2393 * - caller not running on the __SCNetworkReachability_concurrent_queue()
2396 __SCNetworkReachabilityPerformLocked(SCNetworkReachabilityRef target
)
2398 dispatch_queue_t queue
;
2399 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2401 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2403 queue
= targetPrivate
->dispatchQueue
;
2404 if (queue
!= NULL
) {
2405 dispatch_retain(queue
);
2407 dispatch_group_async(targetPrivate
->dispatchGroup
, __SCNetworkReachability_concurrent_queue(), ^{
2408 dispatch_sync(queue
, ^{
2409 reachPerform((void *)target
);
2412 dispatch_release(queue
);
2414 } else if (targetPrivate
->rls
!= NULL
) {
2415 CFRunLoopSourceSignal(targetPrivate
->rls
);
2416 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2423 static SCNetworkReachabilityPrivateRef
2424 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
2426 SCNetworkReachabilityPrivateRef targetPrivate
;
2429 /* initialize runtime */
2430 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
2432 /* allocate target */
2433 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
2434 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
2435 __kSCNetworkReachabilityTypeID
,
2438 if (targetPrivate
== NULL
) {
2442 bzero((void *)targetPrivate
+ sizeof(CFRuntimeBase
), size
);
2444 MUTEX_INIT(&targetPrivate
->lock
);
2446 targetPrivate
->cycle
= 1;
2447 targetPrivate
->last_notify
= NOT_REPORTED
;
2449 #ifdef HAVE_REACHABILITY_SERVER
2450 targetPrivate
->serverBypass
= D_serverBypass
;
2451 #endif // HAVE_REACHABILITY_SERVER
2455 targetPrivate
->log_prefix
[0] = '\0';
2457 snprintf(targetPrivate
->log_prefix
,
2458 sizeof(targetPrivate
->log_prefix
),
2463 return targetPrivate
;
2469 static const struct sockaddr
*
2470 is_valid_address(const struct sockaddr
*address
)
2472 const struct sockaddr
*valid
= NULL
;
2473 static Boolean warned
= FALSE
;
2475 if ((address
!= NULL
) &&
2476 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
2477 switch (address
->sa_family
) {
2479 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
2483 SCLog(TRUE
, LOG_ERR
,
2484 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
2486 sizeof(struct sockaddr_in
));
2492 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
2494 } else if (!warned
) {
2495 SCLog(TRUE
, LOG_ERR
,
2496 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
2498 sizeof(struct sockaddr_in6
));
2504 SCLog(TRUE
, LOG_ERR
,
2505 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"),
2506 address
->sa_family
);
2518 SCNetworkReachabilityRef
2519 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
2520 const struct sockaddr
*address
)
2522 SCNetworkReachabilityPrivateRef targetPrivate
;
2524 address
= is_valid_address(address
);
2525 if (address
== NULL
) {
2526 _SCErrorSet(kSCStatusInvalidArgument
);
2530 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2531 if (targetPrivate
== NULL
) {
2535 targetPrivate
->type
= reachabilityTypeAddress
;
2536 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
2537 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
2540 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
2541 targetPrivate
->log_prefix
,
2542 DEBUG_REACHABILITY_TYPE_ADDRESS
,
2545 return (SCNetworkReachabilityRef
)targetPrivate
;
2549 SCNetworkReachabilityRef
2550 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
2551 const struct sockaddr
*localAddress
,
2552 const struct sockaddr
*remoteAddress
)
2554 SCNetworkReachabilityPrivateRef targetPrivate
;
2556 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
2557 _SCErrorSet(kSCStatusInvalidArgument
);
2561 if (localAddress
!= NULL
) {
2562 localAddress
= is_valid_address(localAddress
);
2563 if (localAddress
== NULL
) {
2564 _SCErrorSet(kSCStatusInvalidArgument
);
2569 if (remoteAddress
!= NULL
) {
2570 remoteAddress
= is_valid_address(remoteAddress
);
2571 if (remoteAddress
== NULL
) {
2572 _SCErrorSet(kSCStatusInvalidArgument
);
2577 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2578 if (targetPrivate
== NULL
) {
2582 targetPrivate
->type
= reachabilityTypeAddressPair
;
2584 if (localAddress
!= NULL
) {
2585 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
2586 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
2589 if (remoteAddress
!= NULL
) {
2590 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
2591 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
2595 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
2596 targetPrivate
->log_prefix
,
2597 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR
,
2600 return (SCNetworkReachabilityRef
)targetPrivate
;
2604 SCNetworkReachabilityRef
2605 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
2606 const char *nodename
)
2608 #ifdef HAVE_REACHABILITY_SERVER
2609 CFDictionaryRef appLayerVPNProperties
;
2610 #endif // HAVE_REACHABILITY_SERVER
2613 struct sockaddr_in sin
;
2614 struct sockaddr_in6 sin6
;
2617 SCNetworkReachabilityPrivateRef targetPrivate
;
2619 if (nodename
== NULL
) {
2620 _SCErrorSet(kSCStatusInvalidArgument
);
2624 nodenameLen
= strlen(nodename
);
2625 if (nodenameLen
== 0) {
2626 _SCErrorSet(kSCStatusInvalidArgument
);
2630 if (_SC_string_to_sockaddr(nodename
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) != NULL
) {
2631 /* if this "nodename" is really an IP[v6] address in disguise */
2632 return SCNetworkReachabilityCreateWithAddress(allocator
, &addr
.sa
);
2635 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2636 if (targetPrivate
== NULL
) {
2640 targetPrivate
->type
= reachabilityTypeName
;
2642 targetPrivate
->name
= CFAllocatorAllocate(NULL
, nodenameLen
+ 1, 0);
2643 strlcpy((char *)targetPrivate
->name
, nodename
, nodenameLen
+ 1);
2645 targetPrivate
->needResolve
= TRUE
;
2646 targetPrivate
->info
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
2647 #ifdef HAVE_REACHABILITY_SERVER
2648 targetPrivate
->serverInfo
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
2650 /* make sure AppLayerVPN only is in client mode */
2651 appLayerVPNProperties
= VPNAppLayerCopyCurrentAppProperties();
2652 if (appLayerVPNProperties
!= NULL
) {
2653 targetPrivate
->useVPNAppLayer
= TRUE
;
2654 targetPrivate
->serverBypass
= YES
;
2655 CFRelease(appLayerVPNProperties
);
2657 #endif // HAVE_REACHABILITY_SERVER
2659 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
2660 targetPrivate
->log_prefix
,
2661 DEBUG_REACHABILITY_TYPE_NAME
,
2664 return (SCNetworkReachabilityRef
)targetPrivate
;
2670 SCNetworkReachabilityRef
2671 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
2672 CFDictionaryRef options
)
2674 const struct sockaddr
*addr_l
= NULL
;
2675 const struct sockaddr
*addr_r
= NULL
;
2677 CFStringRef interface
= NULL
;
2678 CFStringRef nodename
;
2679 CFBooleanRef onDemandBypass
;
2680 CFBooleanRef resolverBypass
;
2681 #ifdef HAVE_REACHABILITY_SERVER
2682 CFBooleanRef serverBypass
;
2683 #endif // HAVE_REACHABILITY_SERVER
2684 SCNetworkReachabilityRef target
;
2685 SCNetworkReachabilityPrivateRef targetPrivate
;
2687 if (!isA_CFDictionary(options
)) {
2688 _SCErrorSet(kSCStatusInvalidArgument
);
2692 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
2693 if ((nodename
!= NULL
) &&
2694 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
2695 _SCErrorSet(kSCStatusInvalidArgument
);
2698 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
2700 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2701 _SCErrorSet(kSCStatusInvalidArgument
);
2704 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2706 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
2708 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2709 _SCErrorSet(kSCStatusInvalidArgument
);
2712 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2714 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
2715 if ((interface
!= NULL
) &&
2716 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
2717 _SCErrorSet(kSCStatusInvalidArgument
);
2720 onDemandBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandBypass
);
2721 if ((onDemandBypass
!= NULL
) && !isA_CFBoolean(onDemandBypass
)) {
2722 _SCErrorSet(kSCStatusInvalidArgument
);
2725 resolverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionResolverBypass
);
2726 if ((resolverBypass
!= NULL
) && !isA_CFBoolean(resolverBypass
)) {
2727 _SCErrorSet(kSCStatusInvalidArgument
);
2732 #ifdef HAVE_REACHABILITY_SERVER
2733 serverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServerBypass
);
2734 if ((serverBypass
!= NULL
) && !isA_CFBoolean(serverBypass
)) {
2735 _SCErrorSet(kSCStatusInvalidArgument
);
2738 #endif // HAVE_REACHABILITY_SERVER
2741 if (nodename
!= NULL
) {
2744 if ((addr_l
!= NULL
) || (addr_r
!= NULL
)) {
2745 // can't have both a nodename and an address
2746 _SCErrorSet(kSCStatusInvalidArgument
);
2750 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
2751 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
2752 CFAllocatorDeallocate(NULL
, (void *)name
);
2754 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
2755 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
2756 } else if (addr_r
!= NULL
) {
2757 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
2758 } else if (addr_l
!= NULL
) {
2759 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, NULL
);
2761 _SCErrorSet(kSCStatusInvalidArgument
);
2765 if (target
== NULL
) {
2769 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2771 if (interface
!= NULL
) {
2772 if ((_SC_cfstring_to_cstring(interface
,
2773 targetPrivate
->if_name
,
2774 sizeof(targetPrivate
->if_name
),
2775 kCFStringEncodingASCII
) == NULL
) ||
2776 ((targetPrivate
->if_index
= if_nametoindex(targetPrivate
->if_name
)) == 0)) {
2777 CFRelease(targetPrivate
);
2778 _SCErrorSet(kSCStatusInvalidArgument
);
2784 if (onDemandBypass
!= NULL
) {
2785 targetPrivate
->onDemandBypass
= CFBooleanGetValue(onDemandBypass
);
2788 if (resolverBypass
!= NULL
) {
2789 targetPrivate
->resolverBypass
= CFBooleanGetValue(resolverBypass
);
2792 #ifdef HAVE_REACHABILITY_SERVER
2793 /* if by name, make sure AppLayerVPN only is in client mode */
2794 if (serverBypass
!= NULL
&& targetPrivate
->useVPNAppLayer
== FALSE
) {
2795 targetPrivate
->serverBypass
= CFBooleanGetValue(serverBypass
);
2797 #endif // HAVE_REACHABILITY_SERVER
2800 if (_sc_debug
&& (_sc_log
> 0)) {
2803 switch (targetPrivate
->type
) {
2804 case reachabilityTypeName
:
2805 opt
= DEBUG_REACHABILITY_TYPE_NAME_OPTIONS
;
2807 case reachabilityTypeAddress
:
2808 opt
= DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS
;
2810 case reachabilityTypeAddressPair
:
2811 opt
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS
;
2818 SCLog(TRUE
, LOG_INFO
, CFSTR("%s%s %@"),
2819 targetPrivate
->log_prefix
,
2824 return (SCNetworkReachabilityRef
)targetPrivate
;
2829 SCNetworkReachabilityGetTypeID(void)
2831 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
2832 return __kSCNetworkReachabilityTypeID
;
2836 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
2837 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
2840 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2842 if (!isA_SCNetworkReachability(target
)) {
2843 _SCErrorSet(kSCStatusInvalidArgument
);
2847 if (targetPrivate
->type
!= reachabilityTypeName
) {
2848 _SCErrorSet(kSCStatusInvalidArgument
);
2853 *error_num
= targetPrivate
->resolvedError
;
2856 if (targetPrivate
->resolvedAddresses
!= NULL
) {
2857 if (isA_CFArray(targetPrivate
->resolvedAddresses
)) {
2858 return CFRetain(targetPrivate
->resolvedAddresses
);
2860 /* if status is known but no resolved addresses to return */
2861 _SCErrorSet(kSCStatusOK
);
2866 _SCErrorSet(kSCStatusReachabilityUnknown
);
2872 __SCNetworkReachabilitySetResolvedAddresses(int32_t status
,
2873 struct addrinfo
*res
,
2874 SCNetworkReachabilityRef target
)
2876 struct addrinfo
*resP
;
2877 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2879 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2881 if (targetPrivate
->resolvedAddresses
!= NULL
) {
2882 CFRelease(targetPrivate
->resolvedAddresses
);
2883 targetPrivate
->resolvedAddresses
= NULL
;
2886 if ((status
== 0) && (res
!= NULL
)) {
2887 CFMutableArrayRef addresses
;
2889 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2891 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
2893 CFDataRef newAddress
;
2895 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
2896 n
= CFArrayGetCount(addresses
);
2898 !CFArrayContainsValue(addresses
, CFRangeMake(0, n
), newAddress
)) {
2899 CFArrayAppendValue(addresses
, newAddress
);
2901 CFRelease(newAddress
);
2904 /* save the resolved address[es] */
2905 targetPrivate
->resolvedAddresses
= addresses
;
2906 targetPrivate
->resolvedError
= NETDB_SUCCESS
;
2908 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sgetaddrinfo() failed: %s"),
2909 targetPrivate
->log_prefix
,
2910 gai_strerror(status
));
2912 /* save the error associated with the attempt to resolve the name */
2913 targetPrivate
->resolvedAddresses
= CFRetain(kCFNull
);
2914 targetPrivate
->resolvedError
= status
;
2916 targetPrivate
->needResolve
= FALSE
;
2918 if (res
!= NULL
) freeaddrinfo(res
);
2920 if (targetPrivate
->scheduled
) {
2921 __SCNetworkReachabilityPerformLocked(target
);
2929 __SCNetworkReachabilityCallbackSetResolvedAddresses(int32_t status
, struct addrinfo
*res
, void *context
)
2931 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
2932 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2934 __mark_operation_end(target
,
2935 ((status
== 0) && (res
!= NULL
)), // if successful query
2936 dns_query_async
, // async
2937 &targetPrivate
->dnsQueryStart
, // start time
2938 &targetPrivate
->dnsQueryEnd
); // end time
2940 __SCNetworkReachabilitySetResolvedAddresses(status
, res
, target
);
2946 * rankReachability()
2947 * Not reachable == 0
2948 * Connection Required == 1
2952 rankReachability(SCNetworkReachabilityFlags flags
)
2956 if (flags
& kSCNetworkReachabilityFlagsReachable
) rank
= 2;
2957 if (flags
& kSCNetworkReachabilityFlagsConnectionRequired
) rank
= 1;
2963 #pragma mark DNS name resolution
2967 replyMPCopyDescription(const void *info
)
2969 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2970 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2972 return CFStringCreateWithFormat(NULL
,
2974 CFSTR("<getaddrinfo_async_start reply MP> {name = %s, target = %p}"),
2975 targetPrivate
->name
,
2981 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
);
2985 enqueueAsyncDNSQuery_dispatch(SCNetworkReachabilityRef target
)
2988 dispatch_source_t source
;
2989 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2991 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2993 mp
= targetPrivate
->dnsMP
;
2995 // mach_port context <-- NULL (no msg received)
2996 mach_port_set_context(mach_task_self(), mp
, (mach_vm_address_t
)(uintptr_t)NULL
);
2998 // create dispatch source to handle DNS reply
2999 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
,
3002 __SCNetworkReachability_concurrent_queue());
3003 if (source
== NULL
) {
3004 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_source_create() failed"));
3009 // We created the dispatch_source to listen for (and process) the mach IPC
3010 // reply to our async DNS query. Because the source handler runs asychronously
3011 // we need to ensure that we're holding a reference to the target. Here, we take
3012 // a reference and setup the dispatch_source finalizer to drop it.
3015 dispatch_set_context(source
, (void *)target
);
3016 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
3018 dispatch_source_set_event_handler(source
, ^{
3019 mach_msg_size_t msg_size
= 8192;
3020 const mach_msg_options_t options
= MACH_RCV_MSG
3022 | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX
)
3023 | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
);
3027 mach_msg_header_t
*msg
= (mach_msg_header_t
*)malloc(msg_size
);
3029 kr
= mach_msg(msg
, /* msg */
3030 options
, /* options */
3032 msg_size
, /* rcv_size */
3034 MACH_MSG_TIMEOUT_NONE
, /* timeout */
3035 MACH_PORT_NULL
); /* notify */
3036 if (kr
== KERN_SUCCESS
) {
3037 // mach_port context <-- msg
3038 mach_port_set_context(mach_task_self(),
3040 (mach_vm_address_t
)(uintptr_t)msg
);
3041 } else if (kr
== MACH_RCV_TOO_LARGE
) {
3046 SCLog(TRUE
, LOG_ERR
,
3047 CFSTR("SCNetworkReachability async DNS handler, kr=0x%x"),
3054 dispatch_source_cancel(source
);
3057 dispatch_source_set_cancel_handler(source
, ^{
3058 #if !TARGET_OS_IPHONE
3059 mach_vm_address_t context
;
3060 #else // !TARGET_OS_IPHONE
3061 mach_port_context_t context
;
3062 #endif // !TARGET_OS_IPHONE
3066 // get the [async DNS query] mach port
3067 mp
= (mach_port_t
)dispatch_source_get_handle(source
);
3069 // check if we have a received message
3070 kr
= mach_port_get_context(mach_task_self(), mp
, &context
);
3071 if (kr
== KERN_SUCCESS
) {
3074 msg
= (void *)(uintptr_t)context
;
3076 MUTEX_LOCK(&targetPrivate
->lock
);
3077 getaddrinfo_async_handle_reply(msg
);
3078 targetPrivate
->dnsSource
= NULL
;
3079 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
3080 MUTEX_UNLOCK(&targetPrivate
->lock
);
3083 getaddrinfo_async_cancel(mp
);
3087 dispatch_release(source
);
3090 targetPrivate
->dnsSource
= source
;
3091 dispatch_resume(source
);
3098 enqueueAsyncDNSQuery_CF(SCNetworkReachabilityRef target
)
3100 CFMachPortContext context
= { 0
3104 , replyMPCopyDescription
3109 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3111 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3113 mp
= targetPrivate
->dnsMP
;
3115 targetPrivate
->dnsPort
= _SC_CFMachPortCreateWithPort("SCNetworkReachability",
3117 getaddrinfo_async_handleCFReply
,
3119 if (targetPrivate
->dnsPort
== NULL
) {
3120 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability CFMachPortCreateWithPort() failed"));
3124 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
3125 if (targetPrivate
->dnsRLS
== NULL
) {
3126 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability CFMachPortCreateRunLoopSource() failed"));
3130 n
= CFArrayGetCount(targetPrivate
->rlList
);
3131 for (i
= 0; i
< n
; i
+= 3) {
3132 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
3133 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
3135 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
3142 if (targetPrivate
->dnsRLS
!= NULL
) {
3143 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
3144 CFRelease(targetPrivate
->dnsRLS
);
3145 targetPrivate
->dnsRLS
= NULL
;
3147 if (targetPrivate
->dnsPort
!= NULL
) {
3148 CFMachPortInvalidate(targetPrivate
->dnsPort
);
3149 CFRelease(targetPrivate
->dnsPort
);
3150 targetPrivate
->dnsPort
= NULL
;
3158 requeueAsyncDNSQuery(SCNetworkReachabilityRef target
, mach_port_t mp
)
3161 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3163 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3165 targetPrivate
->dnsMP
= mp
;
3167 if (targetPrivate
->dispatchQueue
!= NULL
) {
3168 ok
= enqueueAsyncDNSQuery_dispatch(target
);
3169 } else if (targetPrivate
->rls
!= NULL
) {
3170 ok
= enqueueAsyncDNSQuery_CF(target
);
3174 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
3175 _SCErrorSet(kSCStatusFailed
);
3184 enqueueAsyncDNSQuery(SCNetworkReachabilityRef target
)
3187 mach_port_t mp
= MACH_PORT_NULL
;
3189 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3191 // track the DNS resolution time
3192 __mark_operation_start(&targetPrivate
->dnsQueryStart
, &targetPrivate
->dnsQueryEnd
);
3194 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3195 if (targetPrivate
->if_index
== 0) {
3196 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3197 error
= getaddrinfo_async_start(&mp
,
3198 targetPrivate
->name
,
3201 __SCNetworkReachabilityCallbackSetResolvedAddresses
,
3203 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3205 mp
= _getaddrinfo_interface_async_call(targetPrivate
->name
,
3208 targetPrivate
->if_name
,
3209 __SCNetworkReachabilityCallbackSetResolvedAddresses
,
3211 if (mp
== MACH_PORT_NULL
) {
3215 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3217 /* save the error associated with the attempt to resolve the name */
3218 __SCNetworkReachabilityCallbackSetResolvedAddresses(error
, NULL
, (void *)target
);
3222 ok
= requeueAsyncDNSQuery(target
, mp
);
3228 dequeueAsyncDNSQuery(SCNetworkReachabilityRef target
, Boolean cancel
)
3230 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3232 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3234 if (targetPrivate
->dnsPort
!= NULL
) {
3235 CFMachPortInvalidate(targetPrivate
->dnsPort
);
3236 CFRelease(targetPrivate
->dnsPort
);
3237 targetPrivate
->dnsPort
= NULL
;
3240 if (targetPrivate
->dnsRLS
!= NULL
) {
3241 CFRelease(targetPrivate
->dnsRLS
);
3242 targetPrivate
->dnsRLS
= NULL
;
3245 if (targetPrivate
->dnsSource
!= NULL
) {
3246 dispatch_source_cancel(targetPrivate
->dnsSource
);
3247 targetPrivate
->dnsSource
= NULL
;
3248 cancel
= FALSE
; // the cancellation handler does the work
3251 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
3253 getaddrinfo_async_cancel(targetPrivate
->dnsMP
);
3255 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
3263 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
3265 mach_port_t mp
= CFMachPortGetPort(port
);
3267 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
3268 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3270 MUTEX_LOCK(&targetPrivate
->lock
);
3272 if (mp
!= targetPrivate
->dnsMP
) {
3273 // we've received a callback on the async DNS port but since the
3274 // associated CFMachPort doesn't match than the request must have
3275 // already been cancelled.
3276 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP"));
3277 MUTEX_UNLOCK(&targetPrivate
->lock
);
3281 dequeueAsyncDNSQuery(target
, FALSE
);
3282 status
= getaddrinfo_async_handle_reply(msg
);
3283 if ((status
== 0) &&
3284 (targetPrivate
->resolvedAddresses
== NULL
) && (targetPrivate
->resolvedError
== NETDB_SUCCESS
)) {
3287 // if the request is not complete and needs to be re-queued
3288 ok
= requeueAsyncDNSQuery(target
, mp
);
3290 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply requeueAsyncDNSQuery() failed"));
3294 MUTEX_UNLOCK(&targetPrivate
->lock
);
3301 check_resolver_reachability(ReachabilityStoreInfoRef store_info
,
3302 dns_resolver_t
*resolver
,
3303 SCNetworkReachabilityFlags
*flags
,
3305 uint32_t *resolver_if_index
,
3306 const char *log_prefix
)
3310 if (resolver_if_index
) *resolver_if_index
= 0;
3312 if (resolver
->n_nameserver
> 0) {
3313 *flags
= (SCNetworkReachabilityFlags
)resolver
->reach_flags
;
3314 if (resolver_if_index
!= NULL
) {
3315 *resolver_if_index
= resolver
->if_index
;
3319 *flags
= kSCNetworkReachabilityFlagsReachable
;
3328 check_matching_resolvers(ReachabilityStoreInfoRef store_info
,
3329 dns_config_t
*dns_config
,
3331 unsigned int if_index
,
3332 SCNetworkReachabilityFlags
*flags
,
3334 uint32_t *resolver_if_index
,
3335 int *dns_config_index
,
3336 const char *log_prefix
)
3339 Boolean matched
= FALSE
;
3340 const char *name
= fqdn
;
3341 int32_t n_resolvers
;
3342 dns_resolver_t
**resolvers
;
3344 if (if_index
== 0) {
3345 n_resolvers
= dns_config
->n_resolver
;
3346 resolvers
= dns_config
->resolver
;
3348 n_resolvers
= dns_config
->n_scoped_resolver
;
3349 resolvers
= dns_config
->scoped_resolver
;
3352 /* In case we couldn't find a match, setting an index of -1
3353 and resolver_if_index 0 */
3354 if (dns_config_index
!= NULL
) *dns_config_index
= -1;
3355 if (resolver_if_index
!= NULL
) *resolver_if_index
= 0;
3357 while (!matched
&& (name
!= NULL
)) {
3361 * check if the provided name (or sub-component)
3362 * matches one of our resolver configurations.
3365 for (i
= 0; i
< n_resolvers
; i
++) {
3367 dns_resolver_t
*resolver
;
3369 resolver
= resolvers
[i
];
3370 if ((if_index
!= 0) && (if_index
!= resolver
->if_index
)) {
3374 domain
= resolver
->domain
;
3375 if (domain
!= NULL
&& (len
== strlen(domain
))) {
3376 if (strcasecmp(name
, domain
) == 0) {
3380 * if name matches domain
3383 ok
= check_resolver_reachability(store_info
, resolver
, flags
, haveDNS
,
3384 resolver_if_index
, log_prefix
);
3389 if (dns_config_index
!= NULL
) *dns_config_index
= i
;
3396 * we have not found a matching resolver, try
3397 * a less qualified domain
3399 name
= strchr(name
, '.');
3400 if ((name
!= NULL
) && (*name
!= '\0')) {
3412 static dns_resolver_t
*
3413 get_default_resolver(dns_config_t
*dns_config
, unsigned int if_index
)
3416 int32_t n_resolvers
;
3417 dns_resolver_t
*resolver
= NULL
;
3418 dns_resolver_t
**resolvers
;
3420 if (if_index
== 0) {
3421 n_resolvers
= dns_config
->n_resolver
;
3422 resolvers
= dns_config
->resolver
;
3424 n_resolvers
= dns_config
->n_scoped_resolver
;
3425 resolvers
= dns_config
->scoped_resolver
;
3428 for (i
= 0; i
< n_resolvers
; i
++) {
3429 if ((if_index
!= 0) && (if_index
!= resolvers
[i
]->if_index
)) {
3433 if (((if_index
== 0) && (i
== 0)) ||
3434 ((if_index
!= 0) && (resolver
== NULL
))) {
3435 // if this is the first (aka default) resolver
3436 resolver
= resolvers
[i
];
3437 } else if ((resolvers
[i
]->domain
== NULL
) &&
3438 (resolvers
[i
]->search_order
< resolver
->search_order
)) {
3439 // if this is a default resolver with a lower search order
3440 resolver
= resolvers
[i
];
3448 static dns_configuration_t
*
3449 dns_configuration_retain()
3451 pthread_mutex_lock(&dns_lock
);
3453 if ((dns_configuration
!= NULL
) && dns_token_valid
) {
3458 * check if the global [DNS] configuration snapshot needs
3461 status
= notify_check(dns_token
, &check
);
3462 if (status
!= NOTIFY_STATUS_OK
) {
3463 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
3466 if ((status
!= NOTIFY_STATUS_OK
) || (check
!= 0)) {
3468 * if the snapshot needs to be refreshed
3470 if (dns_configuration
->refs
== 0) {
3471 dns_configuration_free(dns_configuration
->config
);
3472 CFAllocatorDeallocate(NULL
, dns_configuration
);
3474 dns_configuration
= NULL
;
3478 if (dns_configuration
== NULL
) {
3479 dns_config_t
*new_config
;
3481 new_config
= dns_configuration_copy();
3482 if (new_config
!= NULL
) {
3483 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
3484 dns_configuration
->config
= new_config
;
3485 dns_configuration
->refs
= 0;
3489 if (dns_configuration
!= NULL
) {
3490 dns_configuration
->refs
++;
3493 pthread_mutex_unlock(&dns_lock
);
3494 return dns_configuration
;
3499 dns_configuration_release(dns_configuration_t
*config
)
3501 pthread_mutex_lock(&dns_lock
);
3504 if (config
->refs
== 0) {
3505 if ((dns_configuration
!= config
)) {
3506 dns_configuration_free(config
->config
);
3507 CFAllocatorDeallocate(NULL
, config
);
3511 pthread_mutex_unlock(&dns_lock
);
3517 dns_configuration_watch()
3520 const char *dns_key
;
3524 pthread_mutex_lock(&dns_lock
);
3526 dns_key
= dns_configuration_notify_key();
3527 if (dns_key
== NULL
) {
3528 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
3532 status
= notify_register_check(dns_key
, &dns_token
);
3533 if (status
== NOTIFY_STATUS_OK
) {
3534 dns_token_valid
= TRUE
;
3536 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
);
3540 status
= notify_check(dns_token
, &dns_check
);
3541 if (status
!= NOTIFY_STATUS_OK
) {
3542 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
3543 (void)notify_cancel(dns_token
);
3544 dns_token_valid
= FALSE
;
3552 pthread_mutex_unlock(&dns_lock
);
3558 dns_configuration_unwatch()
3560 pthread_mutex_lock(&dns_lock
);
3562 (void)notify_cancel(dns_token
);
3563 dns_token_valid
= FALSE
;
3565 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
3566 dns_configuration_free(dns_configuration
->config
);
3567 CFAllocatorDeallocate(NULL
, dns_configuration
);
3568 dns_configuration
= NULL
;
3571 pthread_mutex_unlock(&dns_lock
);
3577 _SC_R_checkResolverReachability(ReachabilityStoreInfoRef store_info
,
3578 SCNetworkReachabilityFlags
*flags
,
3580 const char *nodename
,
3581 unsigned int if_index
,
3582 uint32_t *resolver_if_index
,
3583 int *dns_config_index
,
3584 const char *log_prefix
3587 dns_resolver_t
*default_resolver
;
3588 dns_configuration_t
*dns
;
3589 Boolean found
= FALSE
;
3590 char *fqdn
= (char *)nodename
;
3592 Boolean isFQDN
= FALSE
;
3596 Boolean useDefault
= FALSE
;
3598 if (resolver_if_index
) *resolver_if_index
= 0;
3599 if (dns_config_index
) *dns_config_index
= -1;
3602 * We first assume that all of the configured DNS servers
3603 * are available. Since we don't know which name server will
3604 * be consulted to resolve the specified nodename we need to
3605 * check the availability of ALL name servers. We can only
3606 * proceed if we know that our query can be answered.
3609 *flags
= kSCNetworkReachabilityFlagsReachable
;
3612 len
= (nodename
!= NULL
) ? strlen(nodename
) : 0;
3614 // if no nodename, return not reachable
3619 dns
= dns_configuration_retain();
3622 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no configuration"), log_prefix
);
3626 default_resolver
= get_default_resolver(dns
->config
, if_index
);
3627 if (default_resolver
== NULL
) {
3628 // if no resolver configuration
3629 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no resolvers"), log_prefix
);
3633 if (fqdn
[len
- 1] == '.') {
3636 // trim trailing '.''s
3637 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
3638 if (fqdn
== nodename
) {
3639 fqdn
= strdup(nodename
);
3646 * check if the provided name matches a supplemental domain
3648 found
= check_matching_resolvers(store_info
, dns
->config
, fqdn
, if_index
,
3649 flags
, haveDNS
, resolver_if_index
,
3650 dns_config_index
, log_prefix
);
3652 if (!found
&& !isFQDN
) {
3654 * if we did not match a supplemental domain name and if the
3655 * provided name has enough "."s then the first query will be
3656 * directed to the default resolver.
3661 #define NDOTS_OPT "ndots="
3662 #define NDOTS_OPT_LEN (sizeof("ndots=") - 1)
3664 if (default_resolver
->options
!= NULL
) {
3665 cp
= strstr(default_resolver
->options
, NDOTS_OPT
);
3667 ((cp
== default_resolver
->options
) || isspace(cp
[-1])) &&
3668 ((cp
[NDOTS_OPT_LEN
] != '\0') && isdigit(cp
[NDOTS_OPT_LEN
]))) {
3672 cp
+= NDOTS_OPT_LEN
;
3674 val
= strtol(cp
, &end
, 10);
3675 if ((*cp
!= '\0') && (cp
!= end
) && (errno
== 0) &&
3676 ((*end
== '\0') || isspace(*end
))) {
3683 for (cp
= fqdn
; *cp
!= '\0'; cp
++) {
3684 if (*cp
== '.') dots
++;
3687 /* Per KB: HT4845 */
3688 if (dots
>= ndots
) {
3693 if (!found
&& !isFQDN
&& !useDefault
&& (dns
->config
->n_resolver
> 1)) {
3695 * FQDN not specified, try matching w/search domains
3697 if (default_resolver
->n_search
> 0) {
3698 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
3700 char *search_fqdn
= NULL
;
3702 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
3707 // try the provided name with the search domain appended
3708 found
= check_matching_resolvers(store_info
,
3719 } else if (default_resolver
->domain
!= NULL
) {
3721 int domain_parts
= 0;
3723 // count domain parts
3724 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
3730 // remove trailing dots
3731 for (dp
--; (dp
>= default_resolver
->domain
) && (*dp
== '.'); dp
--) {
3736 if (dp
>= default_resolver
->domain
) {
3737 // dots are separators, bump # of components
3741 dp
= default_resolver
->domain
;
3742 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= (domain_parts
- ndots
)); i
++) {
3744 char *search_fqdn
= NULL
;
3746 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
3751 // try the provided name with the [default] domain appended
3752 found
= check_matching_resolvers(store_info
,
3763 // move to the next component of the [default] domain
3764 dp
= strchr(dp
, '.') + 1;
3771 * check the reachability of the default resolver
3773 ok
= check_resolver_reachability(store_info
, default_resolver
, flags
, haveDNS
,
3774 resolver_if_index
, log_prefix
);
3775 if (ok
&& dns_config_index
!= NULL
) *dns_config_index
= 0;
3778 if (fqdn
!= nodename
) free(fqdn
);
3783 dns_configuration_release(dns
);
3791 __SC_checkResolverReachabilityInternal(SCDynamicStoreRef
*storeP
,
3792 SCNetworkReachabilityFlags
*flags
,
3794 const char *nodename
,
3795 uint32_t *resolver_if_index
,
3796 int *dns_config_index
)
3799 ReachabilityStoreInfo store_info
;
3801 ReachabilityStoreInfo_init(&store_info
);
3802 ok
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
);
3807 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, nodename
,
3808 0, resolver_if_index
, dns_config_index
, "");
3812 ReachabilityStoreInfo_free(&store_info
);
3817 * _SC_checkResolverReachabilityByAddress()
3819 * Given an IP address, determine whether a reverse DNS query can be issued
3820 * using the current network configuration.
3823 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
3824 SCNetworkReachabilityFlags
*flags
,
3826 struct sockaddr
*sa
)
3831 ReachabilityStoreInfo store_info
;
3833 ReachabilityStoreInfo_init(&store_info
);
3834 ok
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
);
3840 * Ideally, we would have an API that given a local IP
3841 * address would return the DNS server(s) that would field
3842 * a given PTR query. Fortunately, we do have an SPI which
3843 * which will provide this information given a "name" so we
3844 * take the address, convert it into the inverse query name,
3845 * and find out which servers should be consulted.
3848 switch (sa
->sa_family
) {
3854 /* ALIGN: assuming sa is aligned, then cast ok. */
3855 struct sockaddr_in
*sin
= (struct sockaddr_in
*)(void *)sa
;
3858 * build "PTR" query name
3859 * NNN.NNN.NNN.NNN.in-addr.arpa.
3861 rev
.s_addr
= sin
->sin_addr
.s_addr
;
3862 (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.",
3873 /* ALIGN: assume sa is aligned, cast ok. */
3874 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)(void *)sa
;
3875 int x
= sizeof(ptr_name
);
3879 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
3880 * 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.
3882 for (i
= sizeof(sin6
->sin6_addr
) - 1; i
>= 0; i
--) {
3883 n
= snprintf(&ptr_name
[s
], x
, "%x.%x.",
3884 ( sin6
->sin6_addr
.s6_addr
[i
] & 0xf),
3885 ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf));
3886 if ((n
== -1) || (n
>= x
)) {
3894 n
= snprintf(&ptr_name
[s
], x
, "ip6.arpa.");
3895 if ((n
== -1) || (n
>= x
)) {
3906 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, ptr_name
, 0, NULL
, NULL
, "");
3910 ReachabilityStoreInfo_free(&store_info
);
3916 #pragma mark DNSServiceGetAddrInfo support
3919 #ifdef USE_DNSSERVICEGETADDRINFO
3922 * DNS query handling
3926 * 1. We have a "contract" with mDNSResponder that for EVERY network
3927 * or DNS configuration change that should warrant our [re-]starting
3928 * a query, mDNSResponder will acknowledge the latest DNS configuration.
3930 * 2. IPMonitor also posts a notification AFTER every network or DNS
3931 * configuration change.
3933 * 3. We use IPMonitor's "trailing edge" as a signal to restart any
3938 // Note: protected by _hn_changes_queue()
3939 static SCDynamicStoreCallBack dns_callback
= NULL
;
3940 static int dns_refresh_token
;
3941 static Boolean dns_refresh_token_valid
= FALSE
;
3942 static SCDynamicStoreRef dns_store
= NULL
;
3946 * dns_refresh_handler
3948 * Called to notify/update all SCNetworkReachability by-name targets of
3949 * a network/DNS change. The change should [re-]start a DNS query to
3951 * - caller must be running on the _hn_changes_queue()
3954 dns_refresh_handler()
3959 if (!dns_refresh_token_valid
|| (dns_callback
== NULL
)) {
3963 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3964 kSCDynamicStoreDomainState
,
3966 changes
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
3967 (*dns_callback
)(dns_store
, changes
, NULL
);
3976 * dns_refresh_enable
3978 * Called to monitor for network/DNS changes that should restart a DNS query.
3979 * - caller must be running on the _hn_changes_queue()
3982 dns_refresh_enable(dispatch_queue_t q
, SCDynamicStoreRef store
, SCDynamicStoreCallBack callback
)
3986 dns_callback
= callback
;
3987 dns_store
= CFRetain(store
);
3989 status
= notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE
,
3993 dns_refresh_handler();
3995 if (status
!= NOTIFY_STATUS_OK
) {
3996 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_dispatch() failed, status=%lu"), status
);
4000 dns_refresh_token_valid
= TRUE
;
4007 * dns_refresh_disable
4009 * Called to stop monitoring for network/DNS changes
4010 * - caller must be running on the _hn_changes_queue()
4013 dns_refresh_disable()
4015 (void)notify_cancel(dns_refresh_token
);
4016 dns_refresh_token_valid
= FALSE
;
4017 CFRelease(dns_store
);
4019 dns_callback
= NULL
;
4023 #endif // USE_DNSSERVICEGETADDRINFO
4027 #pragma mark [m]DNS Queries
4031 dequeueDNSQuery(SCNetworkReachabilityRef target
);
4034 static dispatch_queue_t
4037 static dispatch_once_t once
;
4038 static dispatch_queue_t q
;
4040 dispatch_once(&once
, ^{
4041 q
= dispatch_queue_create("SCNetworkReachabilty.DNSService", NULL
);
4048 #ifdef USE_DNSSERVICEGETADDRINFO
4053 static __inline__ Boolean
4054 _dns_complete(SCNetworkReachabilityRef target
)
4056 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4058 if ((targetPrivate
->dnsHaveV4
&& targetPrivate
->dnsHaveV6
) ||
4059 targetPrivate
->dnsHaveError
||
4060 targetPrivate
->dnsHaveTimeout
) {
4071 * Called to push out a target's DNS changes
4072 * - caller must be running on the _dns_queue()
4075 _dns_notify(const void *value
, void *context
)
4077 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
4078 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4080 MUTEX_LOCK(&targetPrivate
->lock
);
4082 if (_dns_complete(target
)) {
4083 __mark_operation_end(target
,
4084 (targetPrivate
->resolvedError
== NETDB_SUCCESS
), // if successful query
4085 dns_query_mdns
, // [m]DNS query
4086 &targetPrivate
->dnsQueryStart
, // start time
4087 &targetPrivate
->dnsQueryEnd
); // end time
4089 // update target info
4090 if (targetPrivate
->resolvedAddresses
!= NULL
) {
4091 CFRelease(targetPrivate
->resolvedAddresses
);
4093 targetPrivate
->resolvedAddresses
= targetPrivate
->dnsAddresses
;
4094 targetPrivate
->dnsAddresses
= NULL
;
4096 targetPrivate
->resolvedError
= targetPrivate
->dnsError
;
4097 targetPrivate
->dnsError
= NETDB_SUCCESS
;
4099 dequeueDNSQuery(target
);
4101 targetPrivate
->needResolve
= FALSE
;
4103 if (targetPrivate
->scheduled
) {
4104 __SCNetworkReachabilityPerformLocked(target
);
4108 MUTEX_UNLOCK(&targetPrivate
->lock
);
4116 static __inline__
void
4117 _dns_mark(SCNetworkReachabilityRef target
, Boolean valid
, const struct sockaddr
*sa
)
4119 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4122 targetPrivate
->dnsHaveError
= TRUE
;
4127 targetPrivate
->dnsHaveTimeout
= TRUE
;
4131 switch (sa
->sa_family
) {
4133 targetPrivate
->dnsHaveV4
= TRUE
;
4136 targetPrivate
->dnsHaveV6
= TRUE
;
4147 * Called to process [m]DNS query updates
4148 * - caller must be running on the _dns_queue()
4151 _dns_callback(DNSServiceRef sdRef
,
4152 DNSServiceFlags flags
,
4153 uint32_t interfaceIndex
,
4154 DNSServiceErrorType errorCode
,
4155 const char *hostname
,
4156 const struct sockaddr
*address
,
4160 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
4161 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4163 MUTEX_LOCK(&targetPrivate
->lock
);
4165 if (sdRef
!= targetPrivate
->dnsTarget
) {
4166 // if this DNSServiceRef is no longer associated with
4168 MUTEX_UNLOCK(&targetPrivate
->lock
);
4172 switch (errorCode
) {
4173 case kDNSServiceErr_NoError
:
4174 if (address
!= NULL
) {
4175 CFMutableArrayRef addresses
;
4176 CFDataRef dnsAddress
;
4179 _dns_mark(target
, TRUE
, address
);
4181 if (targetPrivate
->dnsAddresses
!= NULL
) {
4182 if (isA_CFArray(targetPrivate
->dnsAddresses
)) {
4183 addresses
= CFArrayCreateMutableCopy(NULL
, 0, targetPrivate
->dnsAddresses
);
4185 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4188 CFRelease(targetPrivate
->dnsAddresses
);
4189 targetPrivate
->dnsAddresses
= NULL
;
4191 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4194 dnsAddress
= CFDataCreate(NULL
, (void *)address
, address
->sa_len
);
4195 i
= CFArrayGetFirstIndexOfValue(addresses
,
4196 CFRangeMake(0, CFArrayGetCount(addresses
)),
4198 if (flags
& kDNSServiceFlagsAdd
) {
4200 if (i
== kCFNotFound
) {
4201 CFArrayAppendValue(addresses
, dnsAddress
);
4203 #ifdef HANDLE_RMV_REQUESTS
4206 if (i
!= kCFNotFound
) {
4207 CFArrayRemoveValueAtIndex(addresses
, i
);
4209 #endif // HANDLE_RMV_REQUESTS
4211 CFRelease(dnsAddress
);
4213 if (CFArrayGetCount(addresses
) > 0) {
4214 targetPrivate
->dnsAddresses
= addresses
;
4215 targetPrivate
->dnsError
= NETDB_SUCCESS
;
4217 // if host not found
4218 targetPrivate
->dnsAddresses
= CFRetain(kCFNull
);
4219 targetPrivate
->dnsError
= EAI_NONAME
;
4220 CFRelease(addresses
);
4225 case kDNSServiceErr_BadParam
:
4226 _dns_mark(target
, FALSE
, NULL
);
4228 if (targetPrivate
->dnsAddresses
!= NULL
) {
4229 CFRelease(targetPrivate
->dnsAddresses
);
4231 targetPrivate
->dnsAddresses
= CFRetain(kCFNull
);
4232 targetPrivate
->dnsError
= EAI_NONAME
;
4234 case kDNSServiceErr_NoSuchRecord
:
4235 if (address
!= NULL
) {
4236 // no IPv4/IPv6 address for name (NXDOMAIN)
4238 _dns_mark(target
, TRUE
, address
);
4240 if (targetPrivate
->dnsAddresses
== NULL
) {
4241 targetPrivate
->dnsAddresses
= CFRetain(kCFNull
);
4242 targetPrivate
->dnsError
= EAI_NONAME
;
4246 case kDNSServiceErr_Timeout
:
4247 _dns_mark(target
, TRUE
, NULL
);
4249 if (targetPrivate
->dnsAddresses
== NULL
) {
4250 targetPrivate
->dnsAddresses
= CFRetain(kCFNull
);
4251 targetPrivate
->dnsError
= EAI_NONAME
;
4254 case kDNSServiceErr_ServiceNotRunning
:
4255 // mDNSResponder crashed, restart query
4256 DNSServiceRefDeallocate(dnsMain
);
4260 _dns_mark(target
, FALSE
, NULL
);
4262 SCLog(TRUE
, LOG_ERR
,
4263 CFSTR("%sSCNetworkReachability _dns_callback w/error=%d"),
4264 targetPrivate
->log_prefix
,
4269 MUTEX_UNLOCK(&targetPrivate
->lock
);
4271 if (errorCode
== kDNSServiceErr_ServiceNotRunning
) {
4272 dispatch_async(_hn_changes_queue(), ^{
4273 dns_refresh_handler();
4276 // and flush the dnsUpdated queue as any DNS results we may have
4277 // accumulated are no longer valid.
4278 if (dnsUpdated
!= NULL
) {
4279 CFRelease(dnsUpdated
);
4285 // the "more coming" flag applies to DNSService callouts for any/all
4286 // hosts that are being watched so we need to keep track of the targets
4287 // we have updated. When we [finally] have the last callout then we
4288 // push our notifications for all of the updated targets.
4290 if (dnsUpdated
== NULL
) {
4291 dnsUpdated
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
4293 CFSetAddValue(dnsUpdated
, target
);
4295 if (!(flags
& kDNSServiceFlagsMoreComing
)) {
4296 CFSetApplyFunction(dnsUpdated
, _dns_notify
, NULL
);
4297 CFRelease(dnsUpdated
);
4306 enqueueDNSQuery(SCNetworkReachabilityRef target
)
4308 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4310 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4312 // clear DNS flags, mark the [m]DNS query active
4313 targetPrivate
->dnsFlags
= 0;
4314 targetPrivate
->dnsActive
= TRUE
;
4316 // track the DNS resolution time
4317 __mark_operation_start(&targetPrivate
->dnsQueryStart
, &targetPrivate
->dnsQueryEnd
);
4320 dispatch_async(_dns_queue(), ^{
4321 DNSServiceErrorType err
= kDNSServiceErr_NoError
;
4322 DNSServiceRef sdRef
= NULL
;
4324 if (targetPrivate
->dnsTarget
!= NULL
) {
4325 // if already running
4330 // if needed, start interacting with mDNSResponder
4331 // Note: we must not hold a [target] lock while interacting
4333 if (dnsMain
== NULL
) {
4334 err
= DNSServiceCreateConnection(&dnsMain
);
4335 if (err
!= kDNSServiceErr_NoError
) {
4336 SCLog(TRUE
, LOG_ERR
,
4337 CFSTR("DNSServiceCreateConnection(&dnsMain) failed, error = %d"),
4342 err
= DNSServiceSetDispatchQueue(dnsMain
, _dns_queue());
4343 if (err
!= kDNSServiceErr_NoError
) {
4344 SCLog(TRUE
, LOG_ERR
,
4345 CFSTR("DNSServiceSetDispatchQueue() failed, error = %d"),
4347 DNSServiceRefDeallocate(dnsMain
);
4353 // start an [m]DNS query for this target
4356 err
= DNSServiceGetAddrInfo(&sdRef
, // sdRef
4357 kDNSServiceFlagsReturnIntermediates
// flags
4358 | kDNSServiceFlagsShareConnection
4359 | kDNSServiceFlagsTimeout
,
4360 targetPrivate
->if_index
, // interfaceIndex
4362 targetPrivate
->name
, // hostname
4363 _dns_callback
, // callback
4364 (void *)target
); // context
4365 if (err
== kDNSServiceErr_NoError
) {
4369 if ((dnsCount
== 0) || (err
== kDNSServiceErr_ServiceNotRunning
)) {
4370 // if this was the first request or the service is dead
4371 DNSServiceRefDeallocate(dnsMain
);
4378 case kDNSServiceErr_NoError
:
4380 case kDNSServiceErr_BadParam
:
4382 dispatch_async(_dns_queue(), ^{
4383 _dns_callback(NULL
, // sdRef
4385 0, // interfaceIndex
4386 kDNSServiceErr_BadParam
, // errorCode
4390 (void *)target
); // context
4395 SCLog(TRUE
, LOG_ERR
,
4396 CFSTR("DNSServiceGetAddrInfo() failed, error = %d"),
4403 MUTEX_LOCK(&targetPrivate
->lock
);
4404 if (err
== kDNSServiceErr_NoError
) {
4405 // query active, save DNSServiceRef, retain target reference
4406 targetPrivate
->dnsMain
= dnsMain
;
4407 targetPrivate
->dnsTarget
= sdRef
;
4408 MUTEX_UNLOCK(&targetPrivate
->lock
);
4410 // query no longer active, release target reference
4411 targetPrivate
->dnsActive
= FALSE
;
4412 MUTEX_UNLOCK(&targetPrivate
->lock
);
4414 if (err
== kDNSServiceErr_ServiceNotRunning
) {
4415 dispatch_async(_hn_changes_queue(), ^{
4416 dns_refresh_handler();
4427 #endif // USE_DNSSERVICEGETADDRINFO
4431 dequeueDNSQuery(SCNetworkReachabilityRef target
)
4433 DNSServiceRef sdRef
;
4434 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4436 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4438 // terminate the [target] [m]DNS query
4439 sdRef
= targetPrivate
->dnsTarget
;
4440 targetPrivate
->dnsTarget
= NULL
;
4442 // mark the [m]DNS query NOT active
4443 targetPrivate
->dnsActive
= FALSE
;
4445 // don't do anything if the sdRef is not valid (e.g., "dnsMain" changed)
4447 && targetPrivate
->dnsMain
== dnsMain
) {
4448 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 3LL * NSEC_PER_SEC
),
4451 DNSServiceRefDeallocate(sdRef
);
4455 if (dnsCount
== 0) {
4456 // if no more queries active
4457 DNSServiceRefDeallocate(dnsMain
);
4463 if (targetPrivate
->dnsAddresses
!= NULL
) {
4464 CFRelease(targetPrivate
->dnsAddresses
);
4465 targetPrivate
->dnsAddresses
= NULL
;
4467 targetPrivate
->dnsError
= NETDB_SUCCESS
;
4474 #pragma mark Network Information support
4477 // Note: protected by _hn_changes_queue()
4478 static SCDynamicStoreCallBack network_changed_callback
= NULL
;
4479 static int network_changed_token
;
4480 static Boolean network_changed_token_valid
= FALSE
;
4481 static SCDynamicStoreRef network_change_store
= NULL
;
4485 * nwi_refresh_handler
4487 * Called to notify/update network changed events
4488 * - caller must be running on the _hn_changes_queue()
4491 nwi_refresh_handler()
4496 if (!network_changed_token_valid
|| (network_changed_callback
== NULL
)) {
4500 // Fake a network change.
4501 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4502 kSCDynamicStoreDomainState
,
4505 changes
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
4506 (*network_changed_callback
)(network_change_store
, changes
, NULL
);
4515 * nwi_refresh_enable
4517 * Called to monitor for network changes.
4518 * - caller must be running on the _hn_changes_queue()
4521 nwi_refresh_enable(dispatch_queue_t q
, SCDynamicStoreRef store
, SCDynamicStoreCallBack callback
)
4525 network_changed_callback
= callback
;
4526 network_change_store
= CFRetain(store
);
4528 status
= notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE_NWI
, // trailing nwi_state_get_notify_key()
4529 &network_changed_token
,
4532 nwi_refresh_handler();
4534 if (status
!= NOTIFY_STATUS_OK
) {
4535 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_dispatch() failed for network changes, status=%lu"), status
);
4539 network_changed_token_valid
= TRUE
;
4546 * nwi_refresh_disable
4548 * Called to stop monitoring for network changes
4549 * - caller must be running on the _hn_changes_queue()
4552 nwi_refresh_disable()
4554 if (network_changed_token_valid
) {
4555 (void)notify_cancel(network_changed_token
);
4556 network_changed_token_valid
= FALSE
;
4558 if (network_change_store
!= NULL
) {
4559 CFRelease(network_change_store
);
4560 network_change_store
= NULL
;
4561 network_changed_callback
= NULL
;
4572 #pragma mark OnDemand
4576 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target
,
4577 CFDictionaryRef
*userOptions
)
4579 SCNetworkServiceRef service
= NULL
;
4580 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4582 if (!isA_SCNetworkReachability(target
)) {
4583 _SCErrorSet(kSCStatusInvalidArgument
);
4587 if (targetPrivate
->onDemandServiceID
!= NULL
) {
4588 service
= _SCNetworkServiceCopyActive(NULL
, targetPrivate
->onDemandServiceID
);
4591 if (userOptions
!= NULL
) {
4592 if (targetPrivate
->onDemandName
!= NULL
) {
4593 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4594 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
);
4596 *userOptions
= NULL
;
4605 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer
,
4606 SCNetworkReachabilityFlags onDemandFlags
,
4609 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
4610 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4612 MUTEX_LOCK(&targetPrivate
->lock
);
4614 if (!targetPrivate
->scheduled
) {
4615 // if not currently scheduled
4616 MUTEX_UNLOCK(&targetPrivate
->lock
);
4620 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sOnDemand \"server\" status changed (now 0x%08x)"),
4621 targetPrivate
->log_prefix
,
4624 if (targetPrivate
->type
== reachabilityTypeName
) {
4625 // make sure that we resolve the name again
4626 targetPrivate
->needResolve
= TRUE
;
4629 __SCNetworkReachabilityPerformLocked(target
);
4631 MUTEX_UNLOCK(&targetPrivate
->lock
);
4638 __SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef store_info
,
4639 SCNetworkReachabilityRef target
,
4640 Boolean onDemandRetry
,
4641 SCNetworkReachabilityFlags
*flags
)
4643 SCNetworkConnectionRef connection
= NULL
;
4644 SCNetworkConnectionType connectionType
= kSCNetworkConnectionTypeUnknown
;
4645 Boolean isAppLayerVPN
= FALSE
;
4646 Boolean isOnDemandService
= FALSE
;
4648 CFStringRef onDemandRemoteAddress
= NULL
;
4649 CFStringRef onDemandServiceID
= NULL
;
4650 SCNetworkConnectionStatus onDemandStatus
= kSCNetworkConnectionInvalid
;
4651 CFMutableDictionaryRef selectOptions
= NULL
;
4652 Boolean success
= FALSE
;
4653 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4655 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4657 if (targetPrivate
->onDemandName
== NULL
) {
4658 targetPrivate
->onDemandName
= CFStringCreateWithCString(NULL
, targetPrivate
->name
, kCFStringEncodingUTF8
);
4662 * check if an OnDemand VPN configuration matches the name.
4665 connection
= SCNetworkConnectionCreate(kCFAllocatorDefault
, NULL
, NULL
);
4666 if (connection
== NULL
) {
4670 /* set select options */
4671 selectOptions
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4672 if (selectOptions
== NULL
) {
4676 CFDictionaryAddValue(selectOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
);
4677 CFDictionaryAddValue(selectOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
, onDemandRetry
? kCFBooleanTrue
: kCFBooleanFalse
);
4678 CFDictionaryAddValue(selectOptions
, kSCNetworkConnectionSelectionOptionNoUserPrefs
, kCFBooleanTrue
);
4680 /* select service. May be On Demand or App Layer VPN */
4681 if (!SCNetworkConnectionSelectServiceWithOptions(connection
, selectOptions
)) {
4685 /* get reachability flags (of VPN server) */
4686 (void) SCNetworkConnectionGetReachabilityInfo(connection
, flags
, NULL
);
4688 connectionType
= SCNetworkConnectionGetType(connection
);
4689 if (connectionType
== kSCNetworkConnectionTypeAppLayerVPN
) {
4690 isAppLayerVPN
= TRUE
;
4693 /* get on-demand info */
4694 onDemandServiceID
= SCNetworkConnectionCopyServiceID(connection
);
4695 if (SCNetworkConnectionCopyOnDemandInfo(connection
, &onDemandRemoteAddress
, &onDemandStatus
)) {
4696 isOnDemandService
= TRUE
;
4700 /* handle non-OnDemand App-Layer VPN */
4701 if (isAppLayerVPN
&& !isOnDemandService
) {
4702 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status * = 0x%08x (App-Layer VPN)"),
4703 targetPrivate
->log_prefix
,
4705 if (*flags
& kSCNetworkReachabilityFlagsReachable
) {
4706 // if VPN "server" is reachable
4708 if (!(*flags
& kSCNetworkReachabilityFlagsTransientConnection
)) {
4709 // start w/clean flags if not already layered on a transient network
4710 *flags
= kSCNetworkReachabilityFlagsReachable
;
4713 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
4714 if (onDemandStatus
!= kSCNetworkConnectionConnected
) {
4715 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4718 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status = isReachable%s"),
4719 (onDemandStatus
!= kSCNetworkConnectionConnected
)
4720 ? " (after App Layer connect)" : "",
4721 targetPrivate
->log_prefix
);
4728 if (!_SC_CFEqual(targetPrivate
->onDemandRemoteAddress
, onDemandRemoteAddress
) ||
4729 !_SC_CFEqual(targetPrivate
->onDemandServiceID
, onDemandServiceID
)) {
4730 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
4731 CFRelease(targetPrivate
->onDemandRemoteAddress
);
4732 targetPrivate
->onDemandRemoteAddress
= NULL
;
4735 if (targetPrivate
->onDemandServer
!= NULL
) {
4736 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, NULL
, NULL
);
4737 if (targetPrivate
->dispatchQueue
!= NULL
) {
4739 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
4740 } else if (targetPrivate
->rls
!= NULL
) {
4745 n
= CFArrayGetCount(targetPrivate
->rlList
);
4746 for (i
= 0; i
< n
; i
+= 3) {
4747 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
4748 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
4750 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, TRUE
);
4754 CFRelease(targetPrivate
->onDemandServer
);
4755 targetPrivate
->onDemandServer
= NULL
;
4758 if (targetPrivate
->onDemandServiceID
!= NULL
) {
4759 CFRelease(targetPrivate
->onDemandServiceID
);
4760 targetPrivate
->onDemandServiceID
= NULL
;
4765 if (onDemandStatus
!= kSCNetworkConnectionConnected
) {
4767 * if we have a VPN configuration matching the name *and* we need to
4768 * bring the VPN up. Combine our flags with those of the VPN server.
4770 if (targetPrivate
->onDemandServer
== NULL
) {
4771 SCNetworkReachabilityPrivateRef demandPrivate
;
4772 CFMutableDictionaryRef options
;
4774 options
= CFDictionaryCreateMutable(NULL
,
4776 &kCFTypeDictionaryKeyCallBacks
,
4777 &kCFTypeDictionaryValueCallBacks
);
4778 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, onDemandRemoteAddress
);
4779 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandBypass
, kCFBooleanTrue
);
4780 #ifdef HAVE_REACHABILITY_SERVER
4781 if (targetPrivate
->serverBypass
) {
4782 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionServerBypass
, kCFBooleanTrue
);
4784 #endif // HAVE_REACHABILITY_SERVER
4785 targetPrivate
->onDemandServer
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
4788 // indent OnDemand target
4789 demandPrivate
= (SCNetworkReachabilityPrivateRef
)targetPrivate
->onDemandServer
;
4790 strlcat(demandPrivate
->log_prefix
, ".... ", sizeof(demandPrivate
->log_prefix
));
4792 if (targetPrivate
->scheduled
) {
4793 SCNetworkReachabilityContext context
= { 0, NULL
, CFRetain
, CFRelease
, CFCopyDescription
};
4795 context
.info
= (void *)target
;
4796 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
,
4797 __SCNetworkReachabilityOnDemandCheckCallback
,
4800 // schedule server reachability to match that of the target
4801 if (targetPrivate
->dispatchQueue
!= NULL
) {
4802 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, targetPrivate
->dispatchQueue
, TRUE
);
4807 n
= CFArrayGetCount(targetPrivate
->rlList
);
4808 for (i
= 0; i
< n
; i
+= 3) {
4809 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
4810 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
4812 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, NULL
, TRUE
);
4818 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status * = 0x%08x"),
4819 targetPrivate
->log_prefix
,
4823 if ((*flags
& kSCNetworkReachabilityFlagsReachable
) && !(*flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
4824 // if VPN "server" is [still] reachable
4826 if (!(*flags
& kSCNetworkReachabilityFlagsTransientConnection
)) {
4827 // start w/clean flags if not already layered on a transient network
4828 *flags
= kSCNetworkReachabilityFlagsReachable
;
4831 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
4832 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4833 *flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
4835 // set 'InterventionRequired' if the OnDemand connection is paused
4836 if (SCNetworkConnectionIsOnDemandSuspended(connection
)) {
4837 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
4841 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service * = %@"),
4842 targetPrivate
->log_prefix
,
4844 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after OnDemand connect)"),
4845 targetPrivate
->log_prefix
);
4852 if (onDemandRemoteAddress
!= NULL
) {
4853 if (targetPrivate
->onDemandRemoteAddress
== NULL
) {
4854 targetPrivate
->onDemandRemoteAddress
= CFRetain(onDemandRemoteAddress
);
4858 if (onDemandServiceID
!= NULL
) {
4859 if (targetPrivate
->onDemandServiceID
== NULL
) {
4860 targetPrivate
->onDemandServiceID
= CFRetain(onDemandServiceID
);
4867 if (onDemandServiceID
!= NULL
) {
4868 CFRelease(onDemandServiceID
);
4870 if (onDemandRemoteAddress
!= NULL
) {
4871 CFRelease(onDemandRemoteAddress
);
4873 if (connection
!= NULL
) {
4874 CFRelease(connection
);
4876 if (selectOptions
!= NULL
) {
4877 CFRelease(selectOptions
);
4884 * OnDemand configuration handling
4888 * 1. We have a "contract" with mDNSResponder that for EVERY network
4889 * or DNS configuration change that should warrant our [re-]starting
4890 * a query, mDNSResponder will acknowledge the latest DNS configuration.
4892 * 2. IPMonitor also posts a notification AFTER every network or DNS
4893 * configuration change.
4895 * 3. We use IPMonitor's "trailing edge" as a signal to restart any
4900 // Note: protected by _hn_changes_queue()
4901 static SCDynamicStoreCallBack onDemand_callback
= NULL
;
4902 static int onDemand_refresh_token
;
4903 static Boolean onDemand_refresh_token_valid
= FALSE
;
4904 static SCDynamicStoreRef onDemand_store
= NULL
;
4908 * onDemand_refresh_handler
4910 * Called to notify/update all SCNetworkReachability targets of
4912 * - caller must be running on the _hn_changes_queue()
4915 onDemand_refresh_handler()
4920 if (!onDemand_refresh_token_valid
|| (onDemand_callback
== NULL
)) {
4924 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4925 kSCDynamicStoreDomainState
,
4927 changes
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
4928 (*onDemand_callback
)(onDemand_store
, changes
, NULL
);
4937 * onDemand_refresh_enable
4939 * Called to monitor for OnDemand changes.
4940 * - caller must be running on the _hn_changes_queue()
4943 onDemand_refresh_enable(dispatch_queue_t q
, SCDynamicStoreRef store
, SCDynamicStoreCallBack callback
)
4947 onDemand_callback
= callback
;
4948 onDemand_store
= CFRetain(store
);
4950 status
= notify_register_dispatch(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY
,
4951 &onDemand_refresh_token
,
4954 onDemand_refresh_handler();
4956 if (status
!= NOTIFY_STATUS_OK
) {
4957 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_dispatch() failed, status=%lu"), status
);
4961 onDemand_refresh_token_valid
= TRUE
;
4968 * onDemand_refresh_disable
4970 * Called to stop monitoring for OnDemand changes
4971 * - caller must be running on the _hn_changes_queue()
4974 onDemand_refresh_disable()
4976 (void)notify_cancel(onDemand_refresh_token
);
4977 onDemand_refresh_token_valid
= FALSE
;
4978 CFRelease(onDemand_store
);
4979 onDemand_store
= NULL
;
4980 onDemand_callback
= NULL
;
4986 #pragma mark Reachability Flags
4989 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
4992 struct addrinfo
*res
;
4997 reply_callback(int32_t status
, struct addrinfo
*res
, void *context
)
4999 reply_info
*reply
= (reply_info
*)context
;
5001 reply
->status
= status
;
5008 getaddrinfo_interface_sync(const char *nodename
,
5009 const char *interface
,
5010 struct addrinfo
**res
)
5013 reply_info reply
= { NETDB_SUCCESS
, NULL
};
5015 mp
= _getaddrinfo_interface_async_call(nodename
,
5021 if (mp
== MACH_PORT_NULL
) {
5029 mach_msg_empty_rcv_t msg
;
5031 kern_return_t m_status
;
5033 m_status
= mach_msg(&m_reply
.msg
.header
, /* msg */
5034 MACH_RCV_MSG
, /* options */
5036 sizeof(m_reply
), /* rcv_size */
5038 MACH_MSG_TIMEOUT_NONE
, /* timeout */
5039 MACH_PORT_NULL
); /* notify */
5040 if (m_status
!= KERN_SUCCESS
) {
5044 g_status
= getaddrinfo_async_handle_reply((void *)m_reply
.buf
);
5045 if (g_status
!= 0) {
5046 if (reply
.res
!= NULL
) {
5047 freeaddrinfo(reply
.res
);
5053 if ((reply
.res
!= NULL
) || (reply
.status
!= NETDB_SUCCESS
)) {
5054 // if we have a reply or an error
5058 // if the request is not complete and needs to be re-queued
5062 return reply
.status
;
5064 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5068 __SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef store_info
,
5069 SCNetworkReachabilityRef target
,
5070 ReachabilityInfo
*reach_info
,
5073 CFMutableArrayRef addresses
= NULL
;
5074 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5075 ReachabilityInfo my_info
= NOT_REACHABLE
;
5078 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
5080 _reach_set(reach_info
, &NOT_REACHABLE
, reach_info
->cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
);
5082 if (!isA_SCNetworkReachability(target
)) {
5083 _SCErrorSet(kSCStatusInvalidArgument
);
5087 #ifdef HAVE_REACHABILITY_SERVER
5088 if (!targetPrivate
->serverBypass
) {
5089 if (!targetPrivate
->serverActive
) {
5091 ok
= __SCNetworkReachabilityServer_targetAdd(target
);
5093 targetPrivate
->serverBypass
= TRUE
;
5097 if (targetPrivate
->serverActive
) {
5098 ok
= __SCNetworkReachabilityServer_targetStatus(target
);
5100 SCLog(TRUE
, LOG_DEBUG
,
5101 CFSTR("__SCNetworkReachabilityGetFlags _targetStatus() failed"));
5102 _SCErrorSet(kSCStatusFailed
);
5106 targetPrivate
->cycle
= targetPrivate
->serverInfo
.cycle
;
5107 _reach_set(&my_info
,
5108 &targetPrivate
->serverInfo
,
5109 targetPrivate
->serverInfo
.cycle
,
5110 targetPrivate
->if_index
,
5111 targetPrivate
->if_name
);
5115 #endif // HAVE_REACHABILITY_SERVER
5117 switch (targetPrivate
->type
) {
5118 case reachabilityTypeAddress
:
5119 case reachabilityTypeAddressPair
: {
5121 * Check "local" address
5123 if (targetPrivate
->localAddress
!= NULL
) {
5125 * Check "local" address
5127 ok
= checkAddress(store_info
,
5128 targetPrivate
->localAddress
,
5129 targetPrivate
->if_index
,
5131 targetPrivate
->log_prefix
);
5133 goto error
; /* not today */
5136 if (!(my_info
.flags
& kSCNetworkReachabilityFlagsIsLocalAddress
)) {
5137 goto error
; /* not reachable, non-"local" address */
5142 * Check "remote" address
5144 if (targetPrivate
->remoteAddress
!= NULL
) {
5146 * in cases where we have "local" and "remote" addresses
5147 * we need to re-initialize the to-be-returned flags.
5149 my_info
= NOT_REACHABLE
;
5152 * Check "remote" address
5154 ok
= checkAddress(store_info
,
5155 targetPrivate
->remoteAddress
,
5156 targetPrivate
->if_index
,
5158 targetPrivate
->log_prefix
);
5160 goto error
; /* not today */
5168 case reachabilityTypeName
: {
5169 struct timeval dnsQueryStart
;
5170 struct timeval dnsQueryEnd
;
5172 int ns_dns_config
= -1;
5173 SCNetworkReachabilityFlags ns_flags
= 0;
5174 uint32_t ns_if_index
= 0;
5175 struct addrinfo
*res
;
5177 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
5178 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
5179 /* if resolved or an error had been detected */
5181 /* if not an async request */
5182 goto checkResolvedAddresses
;
5183 } else if (targetPrivate
->dnsActive
) {
5184 /* if [m]DNS query active */
5185 goto checkResolvedAddresses
;
5186 } else if ((targetPrivate
->dnsMP
== MACH_PORT_NULL
) && !targetPrivate
->needResolve
) {
5188 * if this is an async request (i.e. someone is watching the reachability
5189 * of this target), if no query active, and if no query is needed
5191 goto checkResolvedAddresses
;
5195 if (!targetPrivate
->onDemandBypass
) {
5197 SCNetworkReachabilityFlags onDemandFlags
= 0;
5200 * before we attempt our initial DNS query, check if there is
5201 * an OnDemand configuration that we should be using.
5203 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, FALSE
, &onDemandFlags
);
5205 /* if OnDemand connection is needed */
5206 my_info
.flags
= onDemandFlags
;
5211 targetPrivate
->dnsBlocked
= FALSE
;
5213 /* check the reachability of the DNS servers */
5214 ok
= _SC_R_checkResolverReachability(store_info
,
5216 &targetPrivate
->haveDNS
,
5217 targetPrivate
->name
,
5218 targetPrivate
->if_index
,
5221 targetPrivate
->log_prefix
);
5223 /* if we could not get DNS server info */
5224 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
5225 targetPrivate
->log_prefix
);
5226 targetPrivate
->resolverFlags
= kSCNetworkReachabilityFlagsReachable
;
5230 // save resolver reachability flags
5231 targetPrivate
->resolverFlags
= ns_flags
;
5233 if (rankReachability(ns_flags
) < 2) {
5235 * if DNS servers are not (or are no longer) reachable, set
5236 * flags based on the availability of configured (but not
5240 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
5241 targetPrivate
->log_prefix
);
5243 if (!targetPrivate
->dnsBlocked
) {
5244 ok
= checkAddress(store_info
,
5246 targetPrivate
->if_index
,
5248 targetPrivate
->log_prefix
);
5250 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sNo available networks"),
5251 targetPrivate
->log_prefix
);
5255 // if not checking "available" networks
5256 my_info
.flags
= ns_flags
;
5257 my_info
.if_index
= ns_if_index
;
5260 if (async
&& targetPrivate
->scheduled
) {
5262 * return "host not found", set flags appropriately,
5263 * and schedule notification.
5265 __SCNetworkReachabilityCallbackSetResolvedAddresses(EAI_NONAME
,
5268 my_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
5270 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sno DNS servers are reachable"),
5271 targetPrivate
->log_prefix
);
5272 __SCNetworkReachabilityPerformLocked(target
);
5278 if (targetPrivate
->resolverBypass
) {
5279 /* if we are not resolving the name,
5280 * set the flags of the resolvers */
5281 my_info
.flags
= ns_flags
;
5282 my_info
.if_index
= ns_if_index
;
5287 /* for async requests we return the last known status */
5288 my_info
= targetPrivate
->info
;
5290 if (targetPrivate
->dnsActive
) {
5291 /* if [m]DNS query active */
5292 SCLog(_sc_debug
, LOG_INFO
,
5293 CFSTR("%swaiting for DNS reply"),
5294 targetPrivate
->log_prefix
);
5295 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
5296 /* updated reachability based on the previous reply */
5297 goto checkResolvedAddresses
;
5302 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
5303 /* if request already in progress */
5304 SCLog(_sc_debug
, LOG_INFO
,
5305 CFSTR("%swaiting for DNS* reply"),
5306 targetPrivate
->log_prefix
);
5307 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
5308 /* updated reachability based on the previous reply */
5309 goto checkResolvedAddresses
;
5314 SCLog(_sc_debug
, LOG_INFO
,
5315 CFSTR("%sstart DNS query for name = %s"),
5316 targetPrivate
->log_prefix
,
5317 targetPrivate
->name
);
5319 #ifdef USE_DNSSERVICEGETADDRINFO
5321 * initiate an DNS query w/DNSServiceGetAddrInfo
5323 if (enqueueDNSQuery(target
)) {
5324 /* request initiated */
5327 #endif // USE_DNSSERVICEGETADDRINFO
5330 * if we were unable to use DNSServiceGetAddrInfo
5331 * then try with getaddrinfo[_async_start]
5333 if (enqueueAsyncDNSQuery(target
)) {
5334 /* request initiated */
5338 /* if we could not initiate the request, process error */
5339 goto checkResolvedAddresses
;
5342 SCLog(_sc_debug
, LOG_INFO
,
5343 CFSTR("%scheck DNS for name = %s"),
5344 targetPrivate
->log_prefix
,
5345 targetPrivate
->name
);
5348 * OK, all of the DNS name servers are available. Let's
5349 * resolve the nodename into an address.
5351 __mark_operation_start(&dnsQueryStart
, &dnsQueryEnd
);
5353 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5354 if (targetPrivate
->if_index
== 0) {
5355 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5356 error
= getaddrinfo(targetPrivate
->name
,
5360 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5362 error
= getaddrinfo_interface_sync(targetPrivate
->name
,
5363 targetPrivate
->if_name
,
5366 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
5368 __mark_operation_end(target
,
5369 ((error
== 0) && (res
!= NULL
)), // if successful query
5370 dns_query_sync
, // sync
5371 &dnsQueryStart
, // start time
5372 &dnsQueryEnd
); // end time
5374 __SCNetworkReachabilitySetResolvedAddresses(error
, res
, target
);
5376 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
5378 checkResolvedAddresses
:
5381 * We first assume that the requested host is NOT available.
5382 * Then, check each address for accessibility and return the
5383 * best status available.
5385 my_info
= NOT_REACHABLE
;
5387 if (isA_CFArray(addresses
)) {
5389 CFIndex n
= CFArrayGetCount(addresses
);
5391 for (i
= 0; i
< n
; i
++) {
5392 ReachabilityInfo ns_info
= NOT_REACHABLE
;
5393 struct sockaddr
*sa
;
5395 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
5397 ok
= checkAddress(store_info
,
5399 targetPrivate
->if_index
,
5401 targetPrivate
->log_prefix
);
5403 goto error
; /* not today */
5406 if (rankReachability(ns_info
.flags
) > rankReachability(my_info
.flags
)) {
5407 /* return the best case result */
5409 if (rankReachability(my_info
.flags
) == 2) {
5416 if ((error
== EAI_NONAME
)
5417 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
5418 || (error
== EAI_NODATA
)
5422 * the target host name could not be resolved
5424 if (!targetPrivate
->onDemandBypass
) {
5426 SCNetworkReachabilityFlags onDemandFlags
= 0;
5429 * our initial DNS query failed, check again to see if there
5430 * there is an OnDemand configuration that we should be using.
5432 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, TRUE
, &onDemandFlags
);
5434 /* if OnDemand connection is needed */
5435 my_info
.flags
= onDemandFlags
;
5441 if (!targetPrivate
->haveDNS
) {
5443 * No DNS servers are defined. Set flags based on
5444 * the availability of configured (but not active)
5447 ok
= checkAddress(store_info
,
5449 targetPrivate
->if_index
,
5451 targetPrivate
->log_prefix
);
5453 goto error
; /* not today */
5456 if ((my_info
.flags
& kSCNetworkReachabilityFlagsReachable
) &&
5457 (my_info
.flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
5459 * Since we might pick up a set of DNS servers when this connection
5460 * is established, don't reply with a "HOST NOT FOUND" error just yet.
5465 /* Host not found, not reachable! */
5466 my_info
= NOT_REACHABLE
;
5478 _reach_set(reach_info
, &my_info
, targetPrivate
->cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
);
5482 if (addresses
!= NULL
) CFRelease(addresses
);
5487 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target
)
5491 ReachabilityStoreInfo store_info
;
5492 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5494 if (!isA_SCNetworkReachability(target
)) {
5495 _SCErrorSet(kSCStatusInvalidArgument
);
5499 ReachabilityStoreInfo_init(&store_info
);
5501 MUTEX_LOCK(&targetPrivate
->lock
);
5503 if (targetPrivate
->scheduled
) {
5504 // if being watched, return the last known (and what should be current) status
5509 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
);
5513 /* Only return the if_index if the connection is reachable not for reachable connection
5514 * required etc ... */
5515 if (ok
&& rankReachability(targetPrivate
->info
.flags
) == 2) {
5516 if_index
= targetPrivate
->info
.if_index
;
5519 MUTEX_UNLOCK(&targetPrivate
->lock
);
5520 ReachabilityStoreInfo_free(&store_info
);
5526 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
5527 SCNetworkReachabilityFlags
*flags
)
5530 ReachabilityStoreInfo store_info
;
5531 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5533 if (!isA_SCNetworkReachability(target
)) {
5534 _SCErrorSet(kSCStatusInvalidArgument
);
5538 ReachabilityStoreInfo_init(&store_info
);
5540 MUTEX_LOCK(&targetPrivate
->lock
);
5542 if (targetPrivate
->scheduled
) {
5543 // if being watched, return the last known (and what should be current) status
5544 *flags
= targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsMask
;
5549 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
);
5551 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags = 0x%08x"), targetPrivate
->log_prefix
, targetPrivate
->info
.flags
);
5554 *flags
= targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsMask
;
5558 MUTEX_UNLOCK(&targetPrivate
->lock
);
5559 ReachabilityStoreInfo_free(&store_info
);
5565 #pragma mark Notifications
5569 __SCNetworkReachabilitySetNotifications(SCDynamicStoreRef store
)
5572 CFMutableArrayRef keys
;
5573 CFStringRef pattern
;
5574 CFMutableArrayRef patterns
;
5576 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5577 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5579 // If we are bypassing nwi, then we need to get the info from the store.
5581 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
5582 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5583 kSCDynamicStoreDomainSetup
,
5585 CFArrayAppendValue(keys
, key
);
5588 #ifndef USE_DNSSERVICEGETADDRINFO
5589 // State:/Network/Global/DNS
5590 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5591 kSCDynamicStoreDomainState
,
5593 CFArrayAppendValue(keys
, key
);
5595 #endif // USE_DNSSERVICEGETADDRINFO
5597 // State:/Network/Global/IPv4 (default route)
5598 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5599 kSCDynamicStoreDomainState
,
5601 CFArrayAppendValue(keys
, key
);
5604 // State:/Network/Global/OnDemand
5605 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5606 kSCDynamicStoreDomainState
,
5608 CFArrayAppendValue(keys
, key
);
5611 // Setup: per-service Interface info
5612 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5613 kSCDynamicStoreDomainSetup
,
5615 kSCEntNetInterface
);
5616 CFArrayAppendValue(patterns
, pattern
);
5619 // per-service IPv4 info
5620 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5621 kSCDynamicStoreDomainSetup
,
5624 CFArrayAppendValue(patterns
, pattern
);
5626 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5627 kSCDynamicStoreDomainState
,
5630 CFArrayAppendValue(patterns
, pattern
);
5633 // per-service IPv6 info
5634 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5635 kSCDynamicStoreDomainSetup
,
5638 CFArrayAppendValue(patterns
, pattern
);
5640 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5641 kSCDynamicStoreDomainState
,
5644 CFArrayAppendValue(patterns
, pattern
);
5647 // per-service PPP info (for existence, kSCPropNetPPPDialOnDemand, kSCPropNetPPPStatus)
5648 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5649 kSCDynamicStoreDomainSetup
,
5652 CFArrayAppendValue(patterns
, pattern
);
5654 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5655 kSCDynamicStoreDomainState
,
5658 CFArrayAppendValue(patterns
, pattern
);
5661 // per-service VPN info (for existence, kSCPropNetVPNStatus)
5662 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5663 kSCDynamicStoreDomainSetup
,
5666 CFArrayAppendValue(patterns
, pattern
);
5668 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5669 kSCDynamicStoreDomainState
,
5672 CFArrayAppendValue(patterns
, pattern
);
5675 // per-service IPSec info (for existence, kSCPropNetIPSecStatus)
5676 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5677 kSCDynamicStoreDomainSetup
,
5680 CFArrayAppendValue(patterns
, pattern
);
5682 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
5683 kSCDynamicStoreDomainState
,
5686 CFArrayAppendValue(patterns
, pattern
);
5691 #if !TARGET_OS_IPHONE
5692 // State: Power Management Capabilities
5693 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
5694 kSCDynamicStoreDomainState
,
5695 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
5696 CFArrayAppendValue(keys
, key
);
5698 #endif // TARGET_OS_IPHONE
5700 // SCDynamicStore key to force posting a reachability change
5701 CFArrayAppendValue(keys
, SCNETWORKREACHABILITY_TRIGGER_KEY
);
5703 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
5705 CFRelease(patterns
);
5712 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store
,
5713 CFArrayRef changedKeys
,
5716 Boolean dnsConfigChanged
= FALSE
;
5718 Boolean forcedChange
= FALSE
;
5721 CFIndex nGlobals
= 0;
5723 Boolean networkConfigChanged
= FALSE
;
5725 Boolean onDemandConfigChanged
= FALSE
;
5726 #if !TARGET_OS_IPHONE
5727 Boolean powerStatusChanged
= FALSE
;
5728 #endif // !TARGET_OS_IPHONE
5729 ReachabilityStoreInfo store_info
;
5730 const void * targets_q
[N_QUICK
];
5731 const void ** targets
= targets_q
;
5732 __block CFSetRef watchers
= NULL
;
5734 nChanges
= CFArrayGetCount(changedKeys
);
5735 if (nChanges
== 0) {
5740 /* "something" changed, start fresh */
5741 ReachabilityStoreInfo_save(NULL
);
5743 dispatch_sync(_hn_target_queue(), ^{
5744 /* grab the currently watched targets */
5745 if (hn_targets
!= NULL
) {
5746 watchers
= CFSetCreateCopy(NULL
, hn_targets
);
5750 nTargets
= (watchers
!= NULL
) ? CFSetGetCount(watchers
) : 0;
5751 if (nTargets
== 0) {
5752 /* if no addresses being monitored */
5756 /* grab the current time */
5757 (void)gettimeofday(&now
, NULL
);
5759 #if !TARGET_OS_IPHONE
5760 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
5761 kSCDynamicStoreDomainState
,
5762 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
5763 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
5768 num
= SCDynamicStoreCopyValue(store
, key
);
5770 if (!isA_CFNumber(num
) ||
5771 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &power_capabilities
)) {
5772 // data not as expected, use default
5773 power_capabilities
= kIOPMSytemPowerStateCapabilitiesMask
;
5778 // data not available, use default
5779 power_capabilities
= kIOPMSytemPowerStateCapabilitiesMask
;
5782 powerStatusChanged
= TRUE
;
5785 #endif // !TARGET_OS_IPHONE
5787 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5788 kSCDynamicStoreDomainState
,
5790 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
5792 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
5796 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5797 kSCDynamicStoreDomainState
,
5799 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
5801 onDemandConfigChanged
= TRUE
; /* the OnDemand configuration has changed */
5803 // force OnDemand configuration refresh (if SC notification arrives before BSD notify)
5804 __SCNetworkConnectionForceOnDemandConfigurationRefresh();
5809 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), SCNETWORKREACHABILITY_TRIGGER_KEY
)) {
5811 forcedChange
= TRUE
; /* an SCDynamicStore driven "network" change */
5814 if (nChanges
> nGlobals
) {
5815 networkConfigChanged
= TRUE
;
5819 unsigned int changes
= 0;
5820 static const char *change_strings
[] = {
5821 // with no "power" status change
5827 "network and OnDemand ",
5828 "DNS and OnDemand ",
5829 "network, DNS, and OnDemand ",
5830 #if !TARGET_OS_IPHONE
5831 // with "power" status change
5833 "network and power ",
5835 "network, DNS, and power ",
5837 "network, OnDemand, and power ",
5838 "DNS, OnDemand, and power ",
5839 "network, DNS, OnDemand, and power ",
5840 "OnDemand and power ",
5841 "network, OnDemand, and power ",
5842 "DNS, OnDemand, and power ",
5843 "network, DNS, OnDemand, and power ",
5844 #endif // !TARGET_OS_IPHONE
5847 #if !TARGET_OS_IPHONE
5849 if (powerStatusChanged
) {
5852 #endif // !TARGET_OS_IPHONE
5855 if (onDemandConfigChanged
) {
5860 if (dnsConfigChanged
) {
5865 if (networkConfigChanged
) {
5869 SCLog(TRUE
, LOG_INFO
,
5870 CFSTR("process %s%s%sconfiguration change"),
5871 forcedChange
? "[forced] " : "",
5872 change_strings
[changes
]);
5875 ReachabilityStoreInfo_init(&store_info
);
5877 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
5878 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
5879 CFSetGetValues(watchers
, targets
);
5880 for (i
= 0; i
< nTargets
; i
++) {
5881 Boolean dnsNeedsUpdate
= FALSE
;
5882 SCNetworkReachabilityRef target
= targets
[i
];
5883 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5885 MUTEX_LOCK(&targetPrivate
->lock
);
5888 if (dnsConfigChanged
) {
5889 targetPrivate
->last_dns
= now
;
5892 if (networkConfigChanged
) {
5893 targetPrivate
->last_network
= now
;
5896 #if !TARGET_OS_IPHONE
5897 if (powerStatusChanged
) {
5898 targetPrivate
->last_power
= now
;
5900 #endif // !TARGET_OS_IPHONE
5902 if (targetPrivate
->type
== reachabilityTypeName
) {
5903 Boolean dnsChanged
= (dnsConfigChanged
|
5905 onDemandConfigChanged
);
5909 * if the DNS configuration didn't change we still need to
5910 * check that the DNS servers are accessible.
5912 Boolean ns_blocked
= FALSE
;
5913 int ns_dns_config
= -1;
5914 SCNetworkReachabilityFlags ns_flags
= 0;
5915 uint32_t ns_if_index
= 0;
5918 /* check the reachability of the DNS servers */
5919 ok
= ReachabilityStoreInfo_update(&store_info
, &store
, AF_UNSPEC
);
5921 ok
= _SC_R_checkResolverReachability(&store_info
,
5923 &targetPrivate
->haveDNS
,
5924 targetPrivate
->name
,
5925 targetPrivate
->if_index
,
5928 targetPrivate
->log_prefix
);
5932 /* if we could not get DNS server info */
5933 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
5934 targetPrivate
->log_prefix
);
5935 ns_flags
= kSCNetworkReachabilityFlagsReachable
;
5939 if (rankReachability(ns_flags
) < 2) {
5941 * if DNS servers are not (or are no longer) reachable, set
5942 * flags based on the availability of configured (but not
5945 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
5946 targetPrivate
->log_prefix
);
5951 if ((targetPrivate
->dnsBlocked
!= ns_blocked
) ||
5952 (targetPrivate
->resolverFlags
!= ns_flags
)) {
5953 // if the DNS blocked or resolver reachability changed
5954 targetPrivate
->dnsBlocked
= ns_blocked
;
5955 targetPrivate
->resolverFlags
= ns_flags
;
5961 if (targetPrivate
->dnsActive
) {
5962 // if we have an outstanding [m]DNS query
5963 SCLog(_sc_debug
, LOG_INFO
,
5964 CFSTR("%scancel [m]DNS query for name = %s"),
5965 targetPrivate
->log_prefix
,
5966 targetPrivate
->name
);
5967 dequeueDNSQuery(target
);
5970 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
5971 /* if we have an outstanding [async] DNS query */
5972 SCLog(_sc_debug
, LOG_INFO
,
5973 CFSTR("%scancel DNS query for name = %s"),
5974 targetPrivate
->log_prefix
,
5975 targetPrivate
->name
);
5976 dequeueAsyncDNSQuery(target
, TRUE
);
5979 /* schedule request to resolve the name again */
5980 targetPrivate
->needResolve
= TRUE
;
5985 targetPrivate
->cycle
++;
5988 if (targetPrivate
->scheduled
) {
5989 __SCNetworkReachabilityPerformLocked(target
);
5992 MUTEX_UNLOCK(&targetPrivate
->lock
);
5994 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
5996 ReachabilityStoreInfo_free(&store_info
);
6000 if (watchers
!= NULL
) CFRelease(watchers
);
6005 #if !TARGET_OS_IPHONE
6008 darkWakeNotify(SCNetworkReachabilityRef target
)
6015 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities
)
6018 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU \
6019 | kIOPMSystemPowerStateCapabilityNetwork \
6020 | kIOPMSystemPowerStateCapabilityDisk)
6022 if ((power_capabilities
& POWER_CAPABILITIES_NEED
) != POWER_CAPABILITIES_NEED
) {
6024 * we're not awake (from a networking point of view) unless we
6025 * have the CPU, disk, *and* network.
6030 if ((power_capabilities
& kIOPMSytemPowerStateCapabilitiesMask
) == POWER_CAPABILITIES_NEED
) {
6032 * if all we have is the CPU, disk, and network than this must
6033 * be a "maintenance" wake.
6041 #endif // !TARGET_OS_IPHONE
6045 reachPerform(void *info
)
6048 void (*context_release
)(const void *);
6050 Boolean defer
= FALSE
;
6053 ReachabilityInfo reach_info
= NOT_REACHABLE
;
6054 SCNetworkReachabilityCallBack rlsFunction
;
6055 ReachabilityStoreInfo store_info
;
6056 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
6057 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
6059 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%schecking target reachability"),
6060 targetPrivate
->log_prefix
);
6063 MUTEX_LOCK(&targetPrivate
->lock
);
6065 if (!targetPrivate
->scheduled
) {
6066 // if not currently scheduled
6067 MUTEX_UNLOCK(&targetPrivate
->lock
);
6071 /* update reachability, notify if status changed */
6072 ReachabilityStoreInfo_init(&store_info
);
6073 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
);
6074 ReachabilityStoreInfo_free(&store_info
);
6076 /* if reachability status not available */
6077 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%flags not available"),
6078 targetPrivate
->log_prefix
);
6079 reach_info
= NOT_REACHABLE
;
6082 #if !TARGET_OS_IPHONE
6084 * We want to defer the notification if this is a maintenance wake *and*
6085 * the reachability flags that we would be reporting to the application
6086 * are better than those that we last reported.
6088 if (!systemIsAwake(power_capabilities
)) {
6089 /* if this is a maintenace wake */
6090 reach_info
.sleeping
= TRUE
;
6092 if (rankReachability(reach_info
.flags
) >= rankReachability(targetPrivate
->info
.flags
)) {
6094 * don't report the change if the new reachability flags are
6095 * the same or "better"
6097 defer
= !darkWakeNotify(target
);
6098 } else if (!__reach_changed(&targetPrivate
->last_notify
, &reach_info
)) {
6100 * if we have already posted this change
6102 defer
= !darkWakeNotify(target
);
6105 #endif // !TARGET_OS_IPHONE
6107 cycle
= targetPrivate
->cycle
;
6108 forced
= ((cycle
!= 0) && (targetPrivate
->info
.cycle
!= cycle
));
6110 if (!forced
&& !__reach_changed(&targetPrivate
->info
, &reach_info
)) {
6112 if (targetPrivate
->info
.sleeping
== reach_info
.sleeping
) {
6113 SCLog(TRUE
, LOG_INFO
,
6114 CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"),
6115 targetPrivate
->log_prefix
,
6117 reach_info
.if_index
,
6118 reach_info
.sleeping
? ", z" : "");
6120 SCLog(TRUE
, LOG_INFO
,
6121 CFSTR("%sflags/interface equiv (was 0x%08x/%hu%s, now 0x%08x/%hu%s)"),
6122 targetPrivate
->log_prefix
,
6123 targetPrivate
->info
.flags
,
6124 targetPrivate
->info
.if_index
,
6125 targetPrivate
->info
.sleeping
? ", z" : "",
6127 reach_info
.if_index
,
6128 reach_info
.sleeping
? ", z" : "");
6132 MUTEX_UNLOCK(&targetPrivate
->lock
);
6136 SCLog(_sc_debug
, LOG_INFO
,
6137 CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s%s"),
6138 targetPrivate
->log_prefix
,
6139 targetPrivate
->info
.flags
,
6140 targetPrivate
->info
.if_index
,
6141 targetPrivate
->info
.sleeping
? ", z" : "",
6143 reach_info
.if_index
,
6144 reach_info
.sleeping
? ", z" : "",
6145 defer
? ", deferred" : "",
6146 forced
? ", forced" : "");
6148 /* update flags / interface */
6149 _reach_set(&targetPrivate
->info
, &reach_info
, cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
);
6151 /* save last notification info */
6152 _reach_set(&targetPrivate
->last_notify
, &reach_info
, cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
);
6154 /* as needed, defer the notification */
6156 MUTEX_UNLOCK(&targetPrivate
->lock
);
6160 /* save last notification time */
6161 (void)gettimeofday(&targetPrivate
->last_push
, NULL
);
6164 rlsFunction
= targetPrivate
->rlsFunction
;
6165 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
6166 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
6167 context_release
= targetPrivate
->rlsContext
.release
;
6169 context_info
= targetPrivate
->rlsContext
.info
;
6170 context_release
= NULL
;
6173 MUTEX_UNLOCK(&targetPrivate
->lock
);
6175 if (rlsFunction
!= NULL
) {
6176 (*rlsFunction
)(target
,
6177 reach_info
.flags
& kSCNetworkReachabilityFlagsMask
,
6181 if (context_release
!= NULL
) {
6182 (*context_release
)(context_info
);
6190 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
6191 SCNetworkReachabilityCallBack callout
,
6192 SCNetworkReachabilityContext
*context
)
6194 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
6196 MUTEX_LOCK(&targetPrivate
->lock
);
6198 if (targetPrivate
->rlsContext
.release
!= NULL
) {
6199 /* let go of the current context */
6200 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
6203 targetPrivate
->rlsFunction
= callout
;
6204 targetPrivate
->rlsContext
.info
= NULL
;
6205 targetPrivate
->rlsContext
.retain
= NULL
;
6206 targetPrivate
->rlsContext
.release
= NULL
;
6207 targetPrivate
->rlsContext
.copyDescription
= NULL
;
6209 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
6210 if (context
->retain
!= NULL
) {
6211 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
6215 MUTEX_UNLOCK(&targetPrivate
->lock
);
6222 reachRLSCopyDescription(const void *info
)
6224 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
6226 return CFStringCreateWithFormat(NULL
,
6228 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
6234 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
6235 CFRunLoopRef runLoop
,
6236 CFStringRef runLoopMode
,
6237 dispatch_queue_t queue
,
6240 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
6241 Boolean init
= FALSE
;
6242 __block Boolean ok
= FALSE
;
6244 MUTEX_LOCK(&targetPrivate
->lock
);
6246 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
6247 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
6248 _SCErrorSet(kSCStatusInvalidArgument
);
6252 #ifdef HAVE_REACHABILITY_SERVER
6253 if (!targetPrivate
->serverBypass
) {
6254 if (!targetPrivate
->serverActive
) {
6256 ok
= __SCNetworkReachabilityServer_targetAdd(target
);
6258 targetPrivate
->serverBypass
= TRUE
;
6262 if (targetPrivate
->serverActive
) {
6263 if (targetPrivate
->scheduled
) {
6264 // if already scheduled
6268 ok
= __SCNetworkReachabilityServer_targetSchedule(target
);
6270 SCLog(TRUE
, LOG_DEBUG
,
6271 CFSTR("__SCNetworkReachabilityScheduleWithRunLoop _targetMonitor() failed"));
6272 _SCErrorSet(kSCStatusFailed
);
6280 #endif // HAVE_REACHABILITY_SERVER
6282 /* schedule the SCNetworkReachability did-something-change handler */
6284 dispatch_sync(_hn_target_queue(), ^{
6287 if (!onDemand
&& (hn_store
== NULL
)) {
6288 hn_store
= SCDynamicStoreCreate(NULL
,
6289 CFSTR("SCNetworkReachability"),
6290 __SCNetworkReachabilityHandleChanges
,
6292 if (hn_store
== NULL
) {
6293 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed"));
6297 __SCNetworkReachabilitySetNotifications(hn_store
);
6299 ok
= SCDynamicStoreSetDispatchQueue(hn_store
, _hn_changes_queue());
6301 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
6302 CFRelease(hn_store
);
6307 if (!dns_configuration_watch()) {
6309 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
6310 CFRelease(hn_store
);
6312 _SCErrorSet(kSCStatusFailed
);
6316 #ifdef USE_DNSSERVICEGETADDRINFO
6317 if (!dns_refresh_enable(_hn_changes_queue(),
6319 __SCNetworkReachabilityHandleChanges
)) {
6321 dns_configuration_unwatch();
6322 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
6323 CFRelease(hn_store
);
6325 _SCErrorSet(kSCStatusFailed
);
6328 #endif // USE_DNSSERVICEGETADDRINFO
6331 if (!onDemand_refresh_enable(_hn_changes_queue(),
6333 __SCNetworkReachabilityHandleChanges
)) {
6335 dns_configuration_unwatch();
6336 #ifdef USE_DNSSERVICEGETADDRINFO
6337 dns_refresh_disable();
6338 #endif // USE_DNSSERVICEGETADDRINFO
6339 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
6340 CFRelease(hn_store
);
6342 _SCErrorSet(kSCStatusFailed
);
6346 if (!nwi_refresh_enable(_hn_changes_queue(),
6348 __SCNetworkReachabilityHandleChanges
)) {
6350 dns_configuration_unwatch();
6351 #ifdef USE_DNSSERVICEGETADDRINFO
6352 dns_refresh_disable();
6353 #endif // USE_DNSSERVICEGETADDRINFO
6354 onDemand_refresh_disable();
6355 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
6356 CFRelease(hn_store
);
6358 _SCErrorSet(kSCStatusFailed
);
6364 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
6366 ReachabilityStoreInfo_enable(TRUE
);
6369 CFSetAddValue(hn_targets
, target
);
6378 #ifdef HAVE_REACHABILITY_SERVER
6380 #endif // HAVE_REACHABILITY_SERVER
6382 if (!targetPrivate
->scheduled
) {
6383 CFRunLoopSourceContext context
= { 0 // version
6384 , (void *)target
// info
6385 , CFRetain
// retain
6386 , CFRelease
// release
6387 , reachRLSCopyDescription
// copyDescription
6392 , reachPerform
// perform
6395 if (runLoop
!= NULL
) {
6396 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
6397 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
6400 if (targetPrivate
->type
== reachabilityTypeName
) {
6402 * we're now scheduled so let's ensure that we
6403 * are starting with a clean slate before we
6406 if (targetPrivate
->resolvedAddresses
!= NULL
) {
6407 CFRelease(targetPrivate
->resolvedAddresses
);
6408 targetPrivate
->resolvedAddresses
= NULL
;
6410 targetPrivate
->resolvedError
= NETDB_SUCCESS
;
6411 targetPrivate
->needResolve
= TRUE
;
6412 _reach_set(&targetPrivate
->info
,
6414 targetPrivate
->info
.cycle
,
6415 targetPrivate
->if_index
,
6416 targetPrivate
->if_name
);
6417 targetPrivate
->info
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
6418 #ifdef HAVE_REACHABILITY_SERVER
6419 _reach_set(&targetPrivate
->serverInfo
,
6421 targetPrivate
->serverInfo
.cycle
,
6422 targetPrivate
->if_index
,
6423 targetPrivate
->if_name
);
6424 targetPrivate
->serverInfo
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
6425 #endif // HAVE_REACHABILITY_SERVER
6429 targetPrivate
->scheduled
= TRUE
;
6434 if (queue
!= NULL
) {
6435 // retain dispatch queue
6436 dispatch_retain(queue
);
6437 targetPrivate
->dispatchQueue
= queue
;
6440 // We've taken a reference to the client's dispatch_queue and we
6441 // want to hold on to that reference until we've processed any/all
6442 // notifications. To facilitate this we create a group, dispatch
6443 // any notification blocks to via that group, and when the caller
6444 // has told us to stop the notifications (unschedule) we wait for
6445 // the group to empty and use the group's finalizer to release
6446 // our reference to the client's queue.
6449 // make sure that we have group to track any async requests
6450 targetPrivate
->dispatchGroup
= dispatch_group_create();
6452 // retain the target ... and release it when the group is released
6454 dispatch_set_context(targetPrivate
->dispatchGroup
, (void *)target
);
6455 dispatch_set_finalizer_f(targetPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
6457 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
6459 * if we do not already have host notifications scheduled with
6460 * this runLoop / runLoopMode
6462 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
6464 if (targetPrivate
->dnsRLS
!= NULL
) {
6465 // if we have an active async DNS query too
6466 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
6470 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
6474 ReachabilityInfo reach_info
= NOT_REACHABLE
;
6475 ReachabilityStoreInfo store_info
;
6478 * if we have yet to schedule SC notifications for this address
6479 * - initialize current reachability status
6481 ReachabilityStoreInfo_init(&store_info
);
6482 if (__SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
)) {
6484 * if reachability status available
6486 * - schedule notification to report status via callback
6488 #ifdef HAVE_REACHABILITY_SERVER
6489 reach_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
6490 #endif // HAVE_REACHABILITY_SERVER
6491 _reach_set(&targetPrivate
->info
,
6493 targetPrivate
->cycle
,
6494 targetPrivate
->if_index
,
6495 targetPrivate
->if_name
);
6496 __SCNetworkReachabilityPerformLocked(target
);
6498 /* if reachability status not available, async lookup started */
6499 _reach_set(&targetPrivate
->info
,
6501 targetPrivate
->cycle
,
6502 targetPrivate
->if_index
,
6503 targetPrivate
->if_name
);
6504 #ifdef HAVE_REACHABILITY_SERVER
6505 _reach_set(&targetPrivate
->serverInfo
,
6507 targetPrivate
->cycle
,
6508 targetPrivate
->if_index
,
6509 targetPrivate
->if_name
);
6510 #endif // HAVE_REACHABILITY_SERVER
6512 ReachabilityStoreInfo_free(&store_info
);
6515 if (targetPrivate
->onDemandServer
!= NULL
) {
6516 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, queue
, TRUE
);
6519 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%sscheduled"),
6520 targetPrivate
->log_prefix
);
6526 MUTEX_UNLOCK(&targetPrivate
->lock
);
6532 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
6533 CFRunLoopRef runLoop
,
6534 CFStringRef runLoopMode
,
6537 dispatch_group_t drainGroup
= NULL
;
6538 dispatch_queue_t drainQueue
= NULL
;
6541 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
6543 // hold a reference while we unschedule
6546 MUTEX_LOCK(&targetPrivate
->lock
);
6548 if (((runLoop
== NULL
) && (targetPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
6549 ((runLoop
!= NULL
) && (targetPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are not)
6550 _SCErrorSet(kSCStatusInvalidArgument
);
6554 if (!targetPrivate
->scheduled
) {
6555 // if not currently scheduled
6556 _SCErrorSet(kSCStatusInvalidArgument
);
6560 // unschedule the target specific sources
6561 if (targetPrivate
->dispatchQueue
!= NULL
) {
6562 if (targetPrivate
->onDemandServer
!= NULL
) {
6563 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, NULL
, NULL
);
6564 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
6567 // save dispatchQueue, release reference when we've queue'd blocks complete, allow re-scheduling
6568 drainGroup
= targetPrivate
->dispatchGroup
;
6569 targetPrivate
->dispatchGroup
= NULL
;
6570 drainQueue
= targetPrivate
->dispatchQueue
;
6571 targetPrivate
->dispatchQueue
= NULL
;
6573 if (!_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
6574 // if not currently scheduled
6575 _SCErrorSet(kSCStatusInvalidArgument
);
6579 if (targetPrivate
->onDemandServer
!= NULL
) {
6580 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, TRUE
);
6583 n
= CFArrayGetCount(targetPrivate
->rlList
);
6584 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
6585 // if target is no longer scheduled for this runLoop / runLoopMode
6586 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
6588 if (targetPrivate
->dnsRLS
!= NULL
) {
6589 // if we have an active async DNS query too
6590 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
6594 // if *all* notifications have been unscheduled
6595 if (targetPrivate
->onDemandServer
!= NULL
) {
6596 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, NULL
, NULL
);
6598 CFRelease(targetPrivate
->rlList
);
6599 targetPrivate
->rlList
= NULL
;
6600 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
6601 CFRelease(targetPrivate
->rls
);
6602 targetPrivate
->rls
= NULL
;
6608 #ifdef HAVE_REACHABILITY_SERVER
6610 // Cancel our request for server monitoring
6612 if (targetPrivate
->serverActive
) {
6614 ok
= __SCNetworkReachabilityServer_targetUnschedule(target
);
6616 SCLog(TRUE
, LOG_DEBUG
,
6617 CFSTR("__SCNetworkReachabilityUnscheduleFromRunLoop _targetMonitor() failed"));
6618 _SCErrorSet(kSCStatusFailed
);
6621 #endif // HAVE_REACHABILITY_SERVER
6623 // if *all* notifications have been unscheduled
6624 targetPrivate
->scheduled
= FALSE
;
6627 #ifdef HAVE_REACHABILITY_SERVER
6628 if (targetPrivate
->serverActive
) {
6631 #endif // HAVE_REACHABILITY_SERVER
6634 if (targetPrivate
->dnsActive
) {
6635 // if we have an active [m]DNS query
6636 dequeueDNSQuery(target
);
6639 if (targetPrivate
->dnsMP
!= MACH_PORT_NULL
) {
6640 // if we have an active [async] DNS query
6641 dequeueAsyncDNSQuery(target
, TRUE
);
6644 dispatch_sync(_hn_target_queue(), ^{
6645 CFSetRemoveValue(hn_targets
, target
);
6651 if (CFSetGetCount(hn_targets
) > 0) {
6655 // if we are no longer monitoring any targets
6656 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
6657 CFRelease(hn_store
);
6659 CFRelease(hn_targets
);
6662 ReachabilityStoreInfo_enable(FALSE
);
6663 ReachabilityStoreInfo_save(NULL
);
6668 * until we start monitoring again, ensure that
6669 * any resources associated with tracking the
6670 * network changes (nwi) have been released.
6672 nwi_refresh_disable();
6675 * until we start monitoring again, ensure that
6676 * any resources associated with tracking the
6677 * OnDemand configuration have been released.
6679 onDemand_refresh_disable();
6682 #ifdef USE_DNSSERVICEGETADDRINFO
6684 * until we start monitoring again, ensure that
6685 * any resources associated with restarting
6686 * [m]DNS queries have been released.
6688 dns_refresh_disable();
6689 #endif // USE_DNSSERVICEGETADDRINFO
6692 * until we start monitoring again, ensure that
6693 * any resources associated with tracking the
6694 * DNS configuration have been released.
6696 dns_configuration_unwatch();
6700 #ifdef HAVE_REACHABILITY_SERVER
6702 #endif // HAVE_REACHABILITY_SERVER
6704 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%sunscheduled"),
6705 targetPrivate
->log_prefix
);
6711 MUTEX_UNLOCK(&targetPrivate
->lock
);
6713 if (drainGroup
!= NULL
) {
6714 dispatch_group_notify(drainGroup
, __SCNetworkReachability_concurrent_queue(), ^{
6715 // release group/queue references
6716 dispatch_release(drainQueue
);
6717 dispatch_release(drainGroup
); // releases our target reference
6721 // release our reference
6728 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
6729 CFRunLoopRef runLoop
,
6730 CFStringRef runLoopMode
)
6732 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
6733 _SCErrorSet(kSCStatusInvalidArgument
);
6737 return __SCNetworkReachabilityScheduleWithRunLoop(target
, runLoop
, runLoopMode
, NULL
, FALSE
);
6741 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
6742 CFRunLoopRef runLoop
,
6743 CFStringRef runLoopMode
)
6745 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
6746 _SCErrorSet(kSCStatusInvalidArgument
);
6750 return __SCNetworkReachabilityUnscheduleFromRunLoop(target
, runLoop
, runLoopMode
, FALSE
);
6754 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
6755 dispatch_queue_t queue
)
6759 if (!isA_SCNetworkReachability(target
)) {
6760 _SCErrorSet(kSCStatusInvalidArgument
);
6764 if (queue
!= NULL
) {
6765 ok
= __SCNetworkReachabilityScheduleWithRunLoop(target
, NULL
, NULL
, queue
, FALSE
);
6767 ok
= __SCNetworkReachabilityUnscheduleFromRunLoop(target
, NULL
, NULL
, FALSE
);