2 * Copyright (c) 2003-2011 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 * March 31, 2004 Allan Nathanson <ajn@apple.com>
28 * - use [SC] DNS configuration information
30 * January 19, 2003 Allan Nathanson <ajn@apple.com>
31 * - add advanced reachability APIs
34 #include <Availability.h>
35 #include <TargetConditionals.h>
36 #include <sys/cdefs.h>
37 #include <dispatch/dispatch.h>
38 #include <CoreFoundation/CoreFoundation.h>
39 #include <CoreFoundation/CFRuntime.h>
40 #include <SystemConfiguration/SystemConfiguration.h>
41 #include <SystemConfiguration/SCValidation.h>
42 #include <SystemConfiguration/SCPrivate.h>
44 #include <libkern/OSAtomic.h>
47 #include <IOKit/pwr_mgt/IOPMLibPrivate.h>
48 #endif // !TARGET_OS_IPHONE
52 #include <netinet/in.h>
53 #include <arpa/inet.h>
55 #include <netdb_async.h>
58 #include <sys/ioctl.h>
59 #include <sys/socket.h>
61 #include <net/if_dl.h>
62 #include <net/if_types.h>
63 #define KERNEL_PRIVATE
64 #include <net/route.h>
68 #define s6_addr16 __u6_addr.__u6_addr16
71 #include <ppp/ppp_msg.h>
73 #if !TARGET_IPHONE_SIMULATOR
74 #include <ppp/PPPControllerPriv.h>
75 #endif // !TARGET_IPHONE_SIMULATOR
79 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR
80 #define HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
81 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR
83 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_EMBEDDED_OTHER
84 #define HAVE_IPSEC_STATUS
85 #define HAVE_VPN_STATUS
86 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_EMBEDDED_OTHER
89 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
92 _getaddrinfo_interface_async_call(const char *nodename
,
94 const struct addrinfo
*hints
,
95 const char *interface
,
96 getaddrinfo_async_callback callback
,
98 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
101 #define kSCNetworkReachabilityFlagsFirstResolvePending (1<<31)
108 reachabilityTypeAddress
,
109 reachabilityTypeAddressPair
,
114 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
115 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
116 static void rlsPerform(void *info
);
120 __SCNetworkReachabilityScheduleWithRunLoop (SCNetworkReachabilityRef target
,
121 CFRunLoopRef runLoop
,
122 CFStringRef runLoopMode
,
123 dispatch_queue_t queue
,
127 __SCNetworkReachabilityUnscheduleFromRunLoop (SCNetworkReachabilityRef target
,
128 CFRunLoopRef runLoop
,
129 CFStringRef runLoopMode
,
134 SCNetworkReachabilityFlags flags
;
135 unsigned int if_index
;
142 /* base CFType information */
143 CFRuntimeBase cfBase
;
146 pthread_mutex_t lock
;
151 /* target host name */
154 struct addrinfo hints
;
156 CFArrayRef resolvedAddress
; /* CFArray[CFData] */
157 int resolvedAddressError
;
159 /* [scoped routing] interface constraints */
160 unsigned int if_index
;
161 char if_name
[IFNAMSIZ
];
163 /* local & remote addresses */
164 struct sockaddr
*localAddress
;
165 struct sockaddr
*remoteAddress
;
167 /* current reachability flags */
168 ReachabilityInfo info
;
169 ReachabilityInfo last_notify
;
171 /* run loop source, callout, context, rl scheduling info */
173 CFRunLoopSourceRef rls
;
174 SCNetworkReachabilityCallBack rlsFunction
;
175 SCNetworkReachabilityContext rlsContext
;
176 CFMutableArrayRef rlList
;
178 dispatch_queue_t dispatchQueue
; // SCNetworkReachabilitySetDispatchQueue
179 dispatch_queue_t asyncDNSQueue
;
180 dispatch_source_t asyncDNSSource
;
182 /* [async] DNS query info */
185 CFMachPortRef dnsPort
;
186 CFRunLoopSourceRef dnsRLS
;
187 struct timeval dnsQueryStart
;
190 Boolean onDemandBypass
;
191 CFStringRef onDemandName
;
192 CFStringRef onDemandRemoteAddress
;
193 SCNetworkReachabilityRef onDemandServer
;
194 CFStringRef onDemandServiceID
;
199 } SCNetworkReachabilityPrivate
, *SCNetworkReachabilityPrivateRef
;
202 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
205 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
207 "SCNetworkReachability", // className
210 __SCNetworkReachabilityDeallocate
, // dealloc
213 NULL
, // copyFormattingDesc
214 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
218 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
219 static const ReachabilityInfo NOT_REACHABLE
= { 0, 0, FALSE
};
220 static const ReachabilityInfo NOT_REPORTED
= { 0xFFFFFFFF, 0, FALSE
};
221 static int rtm_seq
= 0;
224 #if !TARGET_OS_IPHONE
226 * Power capabilities (sleep/wake)
228 static IOPMSystemPowerStateCapabilities power_capabilities
= kIOPMSytemPowerStateCapabilitiesMask
;
229 #endif // !TARGET_OS_IPHONE
233 * host "something has changed" notifications
236 static pthread_mutex_t hn_lock
= PTHREAD_MUTEX_INITIALIZER
;
237 static SCDynamicStoreRef hn_store
= NULL
;
238 static dispatch_queue_t hn_dispatchQueue
= NULL
;
239 static CFMutableSetRef hn_targets
= NULL
;
247 dns_config_t
*config
;
249 } dns_configuration_t
;
252 static pthread_mutex_t dns_lock
= PTHREAD_MUTEX_INITIALIZER
;
253 static dns_configuration_t
*dns_configuration
= NULL
;
254 static int dns_token
;
255 static Boolean dns_token_valid
= FALSE
;
258 static __inline__ CFTypeRef
259 isA_SCNetworkReachability(CFTypeRef obj
)
261 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
266 __log_query_time(SCNetworkReachabilityRef target
, Boolean found
, Boolean async
, struct timeval
*start
)
268 struct timeval dnsQueryComplete
;
269 struct timeval dnsQueryElapsed
;
270 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
276 if (start
->tv_sec
== 0) {
280 (void) gettimeofday(&dnsQueryComplete
, NULL
);
281 timersub(&dnsQueryComplete
, start
, &dnsQueryElapsed
);
282 SCLog(TRUE
, LOG_INFO
,
283 CFSTR("%s%ssync DNS complete%s (query time = %d.%3.3d)"),
284 targetPrivate
->log_prefix
,
286 found
? "" : ", host not found",
287 dnsQueryElapsed
.tv_sec
,
288 dnsQueryElapsed
.tv_usec
/ 1000);
294 static __inline__ Boolean
295 __reach_equal(ReachabilityInfo
*r1
, ReachabilityInfo
*r2
)
297 if ((r1
->flags
== r2
->flags
) &&
298 (r1
->if_index
== r2
->if_index
) &&
299 (r1
->sleeping
== r2
->sleeping
)) {
308 SCDynamicStoreRef store
;
311 CFDictionaryRef dict
;
314 const void * keys_q
[N_QUICK
];
315 const void ** values
;
316 const void * values_q
[N_QUICK
];
317 } ReachabilityStoreInfo
, *ReachabilityStoreInfoRef
;
321 initReachabilityStoreInfo(ReachabilityStoreInfoRef store_info
)
323 bzero(store_info
, sizeof(ReachabilityStoreInfo
));
329 updateReachabilityStoreInfo(ReachabilityStoreInfoRef store_info
,
330 SCDynamicStoreRef
*storeP
,
331 sa_family_t sa_family
)
334 CFMutableArrayRef patterns
;
338 store_info
->entity
= NULL
;
341 store_info
->entity
= kSCEntNetIPv4
;
344 store_info
->entity
= kSCEntNetIPv6
;
350 if (store_info
->dict
!= NULL
) {
351 // if info already available
355 if (store_info
->store
== NULL
) {
356 store_info
->store
= (storeP
!= NULL
) ? *storeP
: NULL
;
357 if (store_info
->store
== NULL
) {
358 store_info
->store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
359 if (store_info
->store
== NULL
) {
360 SCLog(TRUE
, LOG_ERR
, CFSTR("updateReachabilityStoreInfo SCDynamicStoreCreate() failed"));
364 if (storeP
!= NULL
) {
365 /// pass back the allocated SCDynamicStoreRef
366 *storeP
= store_info
->store
;
369 store_info
->storeAdded
= TRUE
;
374 if (sa_family
== AF_UNSPEC
) {
375 // if the address family was not specified than
376 // all we wanted, for now, was to establish the
377 // SCDynamicStore session
381 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
383 // get info for IPv4 services
384 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
385 kSCDynamicStoreDomainSetup
,
388 CFArrayAppendValue(patterns
, pattern
);
390 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
391 kSCDynamicStoreDomainState
,
394 CFArrayAppendValue(patterns
, pattern
);
397 // get info for IPv6 services
398 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
399 kSCDynamicStoreDomainSetup
,
402 CFArrayAppendValue(patterns
, pattern
);
404 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
405 kSCDynamicStoreDomainState
,
408 CFArrayAppendValue(patterns
, pattern
);
411 // get info for PPP services
412 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
413 kSCDynamicStoreDomainSetup
,
416 CFArrayAppendValue(patterns
, pattern
);
418 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
419 kSCDynamicStoreDomainState
,
422 CFArrayAppendValue(patterns
, pattern
);
425 #if !TARGET_IPHONE_SIMULATOR
426 // get info for VPN services
427 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
428 kSCDynamicStoreDomainSetup
,
431 CFArrayAppendValue(patterns
, pattern
);
433 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
434 kSCDynamicStoreDomainState
,
437 CFArrayAppendValue(patterns
, pattern
);
439 #endif // !TARGET_IPHONE_SIMULATOR
441 // get info for IPSec services
442 // pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
443 // kSCDynamicStoreDomainSetup,
446 // CFArrayAppendValue(patterns, pattern);
447 // CFRelease(pattern);
448 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
449 kSCDynamicStoreDomainState
,
452 CFArrayAppendValue(patterns
, pattern
);
455 // get info to identify "available" services
456 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
457 kSCDynamicStoreDomainSetup
,
460 CFArrayAppendValue(patterns
, pattern
);
464 // get the SCDynamicStore info
465 store_info
->dict
= SCDynamicStoreCopyMultiple(store_info
->store
, NULL
, patterns
);
467 if (store_info
->dict
== NULL
) {
471 // and extract the keys/values for post-processing
472 store_info
->n
= CFDictionaryGetCount(store_info
->dict
);
473 if (store_info
->n
> 0) {
474 if (store_info
->n
<= (CFIndex
)(sizeof(store_info
->keys_q
) / sizeof(CFTypeRef
))) {
475 store_info
->keys
= store_info
->keys_q
;
476 store_info
->values
= store_info
->values_q
;
478 store_info
->keys
= CFAllocatorAllocate(NULL
, store_info
->n
* sizeof(CFTypeRef
), 0);
479 store_info
->values
= CFAllocatorAllocate(NULL
, store_info
->n
* sizeof(CFTypeRef
), 0);
481 CFDictionaryGetKeysAndValues(store_info
->dict
,
491 freeReachabilityStoreInfo(ReachabilityStoreInfoRef store_info
)
493 if ((store_info
->n
> 0) && (store_info
->keys
!= store_info
->keys_q
)) {
494 CFAllocatorDeallocate(NULL
, store_info
->keys
);
495 store_info
->keys
= NULL
;
497 CFAllocatorDeallocate(NULL
, store_info
->values
);
498 store_info
->values
= NULL
;
501 if (store_info
->dict
!= NULL
) {
502 CFRelease(store_info
->dict
);
503 store_info
->dict
= NULL
;
506 if (store_info
->storeAdded
&& (store_info
->store
!= NULL
)) {
507 CFRelease(store_info
->store
);
508 store_info
->store
= NULL
;
516 updatePPPStatus(ReachabilityStoreInfoRef store_info
,
517 const struct sockaddr
*sa
,
519 SCNetworkReachabilityFlags
*flags
,
520 CFStringRef
*ppp_server
,
521 const char *log_prefix
)
525 int sc_status
= kSCStatusNoKey
;
527 if (!updateReachabilityStoreInfo(store_info
, NULL
, sa
->sa_family
)) {
528 return kSCStatusReachabilityUnknown
;
531 if (store_info
->n
<= 0) {
533 return kSCStatusNoKey
;
536 // look for the [PPP] service which matches the provided interface
538 ppp_if
= CFStringCreateWithCStringNoCopy(NULL
,
540 kCFStringEncodingASCII
,
543 for (i
=0; i
< store_info
->n
; i
++) {
544 CFArrayRef components
;
547 CFDictionaryRef p_setup
;
548 CFDictionaryRef p_state
;
551 CFStringRef service
= NULL
;
552 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
553 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
556 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
560 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
561 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
562 continue; // if not an active IPv4 or IPv6 entity
565 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
566 if (!isA_CFString(s_if
)) {
567 continue; // if no interface
570 if (!CFEqual(ppp_if
, s_if
)) {
571 continue; // if not this interface
574 // extract the service ID, get the PPP "state" entity for
575 // the "Status", and get the PPP "setup" entity for the
576 // the "DialOnDemand" flag
577 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
578 if (CFArrayGetCount(components
) != 5) {
579 CFRelease(components
);
582 service
= CFArrayGetValueAtIndex(components
, 3);
583 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
584 kSCDynamicStoreDomainState
,
587 p_state
= CFDictionaryGetValue(store_info
->dict
, key
);
589 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
590 kSCDynamicStoreDomainSetup
,
593 p_setup
= CFDictionaryGetValue(store_info
->dict
, key
);
595 CFRelease(components
);
597 // ensure that this is a PPP service
598 if (!isA_CFDictionary(p_state
)) {
602 sc_status
= kSCStatusOK
;
604 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
607 if (ppp_server
!= NULL
) {
608 *ppp_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
609 *ppp_server
= isA_CFString(*ppp_server
);
610 if (*ppp_server
!= NULL
) {
611 CFRetain(*ppp_server
);
616 if (!CFDictionaryGetValueIfPresent(p_state
,
618 (const void **)&num
) ||
619 !isA_CFNumber(num
) ||
620 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) {
623 switch (ppp_status
) {
625 // if we're really UP and RUNNING
628 // if we're effectively UP and RUNNING
631 // if we're not connected at all
632 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link idle"),
634 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
636 case PPP_STATERESERVED
:
637 // if we're not connected at all
638 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link idle, dial-on-traffic to connect"),
640 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
643 // if we're in the process of [dis]connecting
644 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link, connection in progress"),
646 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
650 // get PPP dial-on-traffic status
651 if (isA_CFDictionary(p_setup
) &&
652 CFDictionaryGetValueIfPresent(p_setup
,
653 kSCPropNetPPPDialOnDemand
,
654 (const void **)&num
) &&
656 CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
) &&
658 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
659 if (ppp_status
== PPP_IDLE
) {
660 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
674 updatePPPAvailable(ReachabilityStoreInfoRef store_info
,
675 const struct sockaddr
*sa
,
676 SCNetworkReachabilityFlags
*flags
,
677 const char *log_prefix
)
680 int sc_status
= kSCStatusNoKey
;
682 if (!updateReachabilityStoreInfo(store_info
,
684 (sa
!= NULL
) ? sa
->sa_family
: AF_INET
)) {
685 return kSCStatusReachabilityUnknown
;
688 if (store_info
->n
<= 0) {
690 return kSCStatusNoKey
;
693 // look for an available service which will provide connectivity
694 // for the requested address family.
696 for (i
= 0; i
< store_info
->n
; i
++) {
697 CFArrayRef components
;
698 Boolean found
= FALSE
;
700 CFDictionaryRef i_dict
;
702 CFDictionaryRef p_dict
;
704 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
705 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
707 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
711 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
712 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainSetup
)) {
713 continue; // if not an IPv4 or IPv6 entity
716 // extract service ID
717 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
718 if (CFArrayGetCount(components
) != 5) {
719 CFRelease(components
);
722 service
= CFArrayGetValueAtIndex(components
, 3);
724 // check for [non-VPN] PPP entity
725 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
726 kSCDynamicStoreDomainSetup
,
729 p_dict
= CFDictionaryGetValue(store_info
->dict
, p_key
);
732 i_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
733 kSCDynamicStoreDomainSetup
,
736 i_dict
= CFDictionaryGetValue(store_info
->dict
, i_key
);
739 if (isA_CFDictionary(p_dict
) &&
740 isA_CFDictionary(i_dict
) &&
741 CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) {
744 // we have a PPP service for this address family
747 *flags
|= kSCNetworkReachabilityFlagsReachable
;
748 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
749 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
751 // get PPP dial-on-traffic status
752 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
753 if (isA_CFNumber(num
)) {
756 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
758 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
764 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after connect)"),
766 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service = %@"),
773 CFRelease(components
);
776 sc_status
= kSCStatusOK
;
785 #if !TARGET_IPHONE_SIMULATOR
787 updateVPNStatus(ReachabilityStoreInfoRef store_info
,
788 const struct sockaddr
*sa
,
790 SCNetworkReachabilityFlags
*flags
,
791 CFStringRef
*vpn_server
,
792 const char *log_prefix
)
796 int sc_status
= kSCStatusNoKey
;
798 if (!updateReachabilityStoreInfo(store_info
, NULL
, sa
->sa_family
)) {
799 return kSCStatusReachabilityUnknown
;
802 if (store_info
->n
<= 0) {
804 return kSCStatusNoKey
;
807 // look for the [VPN] service which matches the provided interface
809 vpn_if
= CFStringCreateWithCStringNoCopy(NULL
,
811 kCFStringEncodingASCII
,
814 for (i
=0; i
< store_info
->n
; i
++) {
815 CFArrayRef components
;
818 CFDictionaryRef p_state
;
820 CFStringRef service
= NULL
;
821 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
822 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
825 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
829 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
830 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
831 continue; // if not an active IPv4 or IPv6 entity
834 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
835 if (!isA_CFString(s_if
)) {
836 continue; // if no interface
839 if (!CFEqual(vpn_if
, s_if
)) {
840 continue; // if not this interface
843 // extract the service ID and get the VPN "state" entity for
845 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
846 if (CFArrayGetCount(components
) != 5) {
847 CFRelease(components
);
850 service
= CFArrayGetValueAtIndex(components
, 3);
851 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
852 kSCDynamicStoreDomainState
,
855 p_state
= CFDictionaryGetValue(store_info
->dict
, key
);
857 CFRelease(components
);
859 // ensure that this is a VPN service
860 if (!isA_CFDictionary(p_state
)) {
864 sc_status
= kSCStatusOK
;
866 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
869 if (vpn_server
!= NULL
) {
870 *vpn_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
871 *vpn_server
= isA_CFString(*vpn_server
);
872 if (*vpn_server
!= NULL
) {
873 CFRetain(*vpn_server
);
878 if (!CFDictionaryGetValueIfPresent(p_state
,
880 (const void **)&num
) ||
881 !isA_CFNumber(num
) ||
882 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &vpn_status
)) {
885 #ifdef HAVE_VPN_STATUS
886 switch (vpn_status
) {
888 // if we're really UP and RUNNING
894 // if we're not connected at all
895 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s VPN link idle"),
897 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
900 // if we're in the process of [dis]connecting
901 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s VPN link, connection in progress"),
903 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
906 #endif // HAVE_VPN_STATUS
918 updateVPNAvailable(ReachabilityStoreInfoRef store_info
,
919 const struct sockaddr
*sa
,
920 SCNetworkReachabilityFlags
*flags
,
921 const char *log_prefix
)
924 int sc_status
= kSCStatusNoKey
;
926 if (!updateReachabilityStoreInfo(store_info
,
928 (sa
!= NULL
) ? sa
->sa_family
: AF_INET
)) {
929 return kSCStatusReachabilityUnknown
;
932 if (store_info
->n
<= 0) {
934 return kSCStatusNoKey
;
937 // look for an available service which will provide connectivity
938 // for the requested address family.
940 for (i
= 0; i
< store_info
->n
; i
++) {
941 CFArrayRef components
;
942 Boolean found
= FALSE
;
944 CFDictionaryRef i_dict
;
946 CFDictionaryRef p_dict
;
948 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
949 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
951 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
955 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
956 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainSetup
)) {
957 continue; // if not an IPv4 or IPv6 entity
960 // extract service ID
961 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
962 if (CFArrayGetCount(components
) != 5) {
963 CFRelease(components
);
966 service
= CFArrayGetValueAtIndex(components
, 3);
968 // check for VPN entity
969 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
970 kSCDynamicStoreDomainSetup
,
973 p_dict
= CFDictionaryGetValue(store_info
->dict
, p_key
);
976 i_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
977 kSCDynamicStoreDomainSetup
,
980 i_dict
= CFDictionaryGetValue(store_info
->dict
, i_key
);
983 if (isA_CFDictionary(p_dict
) &&
984 isA_CFDictionary(i_dict
) &&
985 CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) {
986 // we have a VPN service for this address family
989 *flags
|= kSCNetworkReachabilityFlagsReachable
;
990 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
991 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
994 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after connect)"),
996 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service = %@"),
1003 CFRelease(components
);
1006 sc_status
= kSCStatusOK
;
1013 #endif // !TARGET_IPHONE_SIMULATOR
1017 updateIPSecStatus(ReachabilityStoreInfoRef store_info
,
1018 const struct sockaddr
*sa
,
1019 const char *if_name
,
1020 SCNetworkReachabilityFlags
*flags
,
1021 CFStringRef
*ipsec_server
,
1022 const char *log_prefix
)
1025 CFStringRef ipsec_if
;
1026 int sc_status
= kSCStatusNoKey
;
1028 if (!updateReachabilityStoreInfo(store_info
, NULL
, sa
->sa_family
)) {
1029 return kSCStatusReachabilityUnknown
;
1032 if (store_info
->n
<= 0) {
1034 return kSCStatusNoKey
;
1037 // look for the [IPSec] service that matches the provided interface
1039 ipsec_if
= CFStringCreateWithCStringNoCopy(NULL
,
1041 kCFStringEncodingASCII
,
1044 for (i
=0; i
< store_info
->n
; i
++) {
1045 CFArrayRef components
;
1047 CFDictionaryRef i_state
;
1048 int32_t ipsec_status
;
1050 CFStringRef service
= NULL
;
1051 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
1052 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
1055 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
1059 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
1060 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
1061 continue; // if not an IPv4 or IPv6 entity
1064 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
1065 if (!isA_CFString(s_if
)) {
1066 continue; // if no interface
1069 if (!CFEqual(ipsec_if
, s_if
)) {
1070 continue; // if not this interface
1073 // extract the service ID, get the IPSec "state" entity for
1074 // the "Status", and get the IPSec "setup" entity to confirm
1075 // that we're looking at what we're expecting
1076 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
1077 if (CFArrayGetCount(components
) != 5) {
1078 CFRelease(components
);
1081 service
= CFArrayGetValueAtIndex(components
, 3);
1082 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1083 kSCDynamicStoreDomainState
,
1086 i_state
= CFDictionaryGetValue(store_info
->dict
, key
);
1088 CFRelease(components
);
1090 // ensure that this is an IPSec service
1091 if (!isA_CFDictionary(i_state
)) {
1095 sc_status
= kSCStatusOK
;
1097 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1100 if (ipsec_server
!= NULL
) {
1101 *ipsec_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
1102 *ipsec_server
= isA_CFString(*ipsec_server
);
1103 if (*ipsec_server
!= NULL
) {
1104 CFRetain(*ipsec_server
);
1109 if (!CFDictionaryGetValueIfPresent(i_state
,
1110 kSCPropNetIPSecStatus
,
1111 (const void **)&num
) ||
1112 !isA_CFNumber(num
) ||
1113 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ipsec_status
)) {
1116 #ifdef HAVE_IPSEC_STATUS
1117 switch (ipsec_status
) {
1118 case IPSEC_RUNNING
:
1119 // if we're really UP and RUNNING
1122 // if we're not connected at all
1123 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s IPSec link idle"),
1125 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1128 // if we're in the process of [dis]connecting
1129 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s IPSec link, connection in progress"),
1131 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1134 #endif // HAVE_IPSEC_STATUS
1139 CFRelease(ipsec_if
);
1147 #define ROUNDUP(a, size) \
1148 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
1150 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
1151 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
1152 sizeof(uint32_t)) :\
1156 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
1160 for (i
= 0; i
< RTAX_MAX
; i
++) {
1161 if (addrs
& (1 << i
)) {
1170 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
1176 struct sockaddr
*rti_info
[RTAX_MAX
];
1177 struct rt_msghdr
*rtm
;
1178 struct sockaddr_dl
*sdl
;
1179 } route_info
, *route_info_p
;
1184 * returns zero if route exists an data returned, EHOSTUNREACH
1185 * if no route, or errno for any other error.
1188 route_get(const struct sockaddr
*address
,
1189 unsigned int if_index
,
1194 pid_t pid
= getpid();
1196 struct sockaddr
*sa
;
1197 int32_t seq
= OSAtomicIncrement32Barrier(&rtm_seq
);
1198 #ifndef RTM_GET_SILENT
1199 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
1200 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
1201 int sosize
= 48 * 1024;
1204 bzero(info
, sizeof(*info
));
1206 info
->rtm
= (struct rt_msghdr
*)&info
->buf
;
1207 info
->rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
1208 info
->rtm
->rtm_version
= RTM_VERSION
;
1209 #ifdef RTM_GET_SILENT
1210 info
->rtm
->rtm_type
= RTM_GET_SILENT
;
1212 info
->rtm
->rtm_type
= RTM_GET
;
1214 info
->rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
1215 info
->rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
1216 info
->rtm
->rtm_pid
= pid
;
1217 info
->rtm
->rtm_seq
= seq
;
1219 if (if_index
!= 0) {
1220 info
->rtm
->rtm_flags
|= RTF_IFSCOPE
;
1221 info
->rtm
->rtm_index
= if_index
;
1224 switch (address
->sa_family
) {
1226 struct sockaddr_in6
*sin6
;
1228 sin6
= (struct sockaddr_in6
*)address
;
1229 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
1230 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
1231 (sin6
->sin6_scope_id
!= 0)) {
1232 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
1233 sin6
->sin6_scope_id
= 0;
1239 sa
= (struct sockaddr
*) (info
->rtm
+ 1);
1240 bcopy(address
, sa
, address
->sa_len
);
1241 n
= ROUNDUP(sa
->sa_len
, sizeof(uint32_t));
1242 info
->rtm
->rtm_msglen
+= n
;
1244 info
->sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
1245 info
->sdl
->sdl_family
= AF_LINK
;
1246 info
->sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
1247 n
= ROUNDUP(info
->sdl
->sdl_len
, sizeof(uint32_t));
1248 info
->rtm
->rtm_msglen
+= n
;
1250 #ifndef RTM_GET_SILENT
1251 pthread_mutex_lock(&lock
);
1253 rsock
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
);
1257 #ifndef RTM_GET_SILENT
1258 pthread_mutex_unlock(&lock
);
1260 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error
));
1264 if (ioctl(rsock
, FIONBIO
, &opt
) < 0) {
1268 #ifndef RTM_GET_SILENT
1269 pthread_mutex_unlock(&lock
);
1271 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl(FIONBIO) failed: %s"), strerror(error
));
1275 #ifndef RTM_GET_SILENT
1276 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
1280 pthread_mutex_unlock(&lock
);
1281 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error
));
1286 if (write(rsock
, &info
->buf
, info
->rtm
->rtm_msglen
) == -1) {
1290 #ifndef RTM_GET_SILENT
1291 pthread_mutex_unlock(&lock
);
1293 if (error
!= ESRCH
) {
1294 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(error
));
1297 return EHOSTUNREACH
;
1301 * Type, seq, pid identify our response.
1302 * Routing sockets are broadcasters on input.
1307 n
= read(rsock
, (void *)&info
->buf
, sizeof(info
->buf
));
1311 if (error
== EINTR
) {
1315 #ifndef RTM_GET_SILENT
1316 pthread_mutex_unlock(&lock
);
1318 SCLog(TRUE
, LOG_ERR
,
1319 CFSTR("SCNetworkReachability: routing socket"
1320 " read() failed: %s"), strerror(error
));
1323 if ((info
->rtm
->rtm_type
== RTM_GET
) &&
1324 (info
->rtm
->rtm_seq
== seq
) &&
1325 (info
->rtm
->rtm_pid
== pid
)) {
1331 #ifndef RTM_GET_SILENT
1332 pthread_mutex_unlock(&lock
);
1335 get_rtaddrs(info
->rtm
->rtm_addrs
, sa
, info
->rti_info
);
1337 //#define LOG_RTADDRS
1342 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), info
->rtm
->rtm_flags
);
1344 if ((info
->rti_info
[RTAX_NETMASK
] != NULL
) && (info
->rti_info
[RTAX_DST
] != NULL
)) {
1345 info
->rti_info
[RTAX_NETMASK
]->sa_family
= info
->rti_info
[RTAX_DST
]->sa_family
;
1348 for (i
= 0; i
< RTAX_MAX
; i
++) {
1349 if (info
->rti_info
[i
] != NULL
) {
1352 _SC_sockaddr_to_string(info
->rti_info
[i
], addr
, sizeof(addr
));
1353 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, addr
);
1357 #endif /* LOG_RTADDRS */
1359 if ((info
->rti_info
[RTAX_IFP
] == NULL
) ||
1360 (info
->rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
1361 /* no interface info */
1362 SCLog(TRUE
, LOG_DEBUG
, CFSTR("route_get() no interface info"));
1366 info
->sdl
= (struct sockaddr_dl
*) info
->rti_info
[RTAX_IFP
];
1367 if ((info
->sdl
->sdl_nlen
== 0) || (info
->sdl
->sdl_nlen
> IFNAMSIZ
)) {
1368 /* no interface name */
1369 return EHOSTUNREACH
;
1377 checkAddress(ReachabilityStoreInfoRef store_info
,
1378 const struct sockaddr
*address
,
1379 unsigned int if_index
,
1380 ReachabilityInfo
*reach_info
,
1381 const char *log_prefix
)
1385 char if_name
[IFNAMSIZ
];
1388 int sc_status
= kSCStatusReachabilityUnknown
;
1389 CFStringRef server
= NULL
;
1390 char *statusMessage
= NULL
;
1391 struct sockaddr_in v4mapped
;
1393 *reach_info
= NOT_REACHABLE
;
1395 if (address
== NULL
) {
1396 /* special case: check only for available paths off the system */
1397 goto checkAvailable
;
1400 switch (address
->sa_family
) {
1405 char if_name
[IFNAMSIZ
+ 1];
1407 _SC_sockaddr_to_string(address
, addr
, sizeof(addr
));
1409 if ((if_index
!= 0) &&
1410 (if_indextoname(if_index
, &if_name
[1]) != NULL
)) {
1416 SCLog(TRUE
, LOG_INFO
, CFSTR("%scheckAddress(%s%s)"),
1424 * if no code for this address family (yet)
1426 SCLog(TRUE
, LOG_INFO
,
1427 CFSTR("checkAddress(): unexpected address family %d"),
1428 address
->sa_family
);
1429 sc_status
= kSCStatusInvalidArgument
;
1433 if (address
->sa_family
== AF_INET6
) {
1434 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)address
;
1436 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) {
1437 bzero(&v4mapped
, sizeof(v4mapped
));
1438 v4mapped
.sin_len
= sizeof(v4mapped
);
1439 v4mapped
.sin_family
= AF_INET
;
1440 v4mapped
.sin_port
= sin6
->sin6_port
;
1441 v4mapped
.sin_addr
.s_addr
= sin6
->sin6_addr
.__u6_addr
.__u6_addr32
[3];
1442 address
= (struct sockaddr
*)&v4mapped
;
1446 ret
= route_get(address
, if_index
, &info
);
1452 goto checkAvailable
;
1459 /* get the interface flags */
1461 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
1463 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
1467 bzero(&ifr
, sizeof(ifr
));
1468 bcopy(info
.sdl
->sdl_data
, ifr
.ifr_name
, info
.sdl
->sdl_nlen
);
1470 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) == -1) {
1471 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl() failed: %s"), strerror(errno
));
1475 if (!(ifr
.ifr_flags
& IFF_UP
)) {
1476 goto checkAvailable
;
1479 statusMessage
= "isReachable";
1480 reach_info
->flags
|= kSCNetworkReachabilityFlagsReachable
;
1482 if (info
.rtm
->rtm_flags
& RTF_LOCAL
) {
1483 statusMessage
= "isReachable (is a local address)";
1484 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1485 } else if (ifr
.ifr_flags
& IFF_LOOPBACK
) {
1486 statusMessage
= "isReachable (is loopback network)";
1487 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1488 } else if ((info
.rti_info
[RTAX_IFA
] != NULL
) &&
1489 (info
.rti_info
[RTAX_IFA
]->sa_family
!= AF_LINK
)) {
1490 void *addr1
= (void *)address
;
1491 void *addr2
= (void *)info
.rti_info
[RTAX_IFA
];
1492 size_t len
= address
->sa_len
;
1494 if ((address
->sa_family
!= info
.rti_info
[RTAX_IFA
]->sa_family
) &&
1495 (address
->sa_len
!= info
.rti_info
[RTAX_IFA
]->sa_len
)) {
1496 SCLog(TRUE
, LOG_NOTICE
,
1497 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
1500 info
.rti_info
[RTAX_IFA
]->sa_family
,
1501 info
.rti_info
[RTAX_IFA
]->sa_len
);
1505 switch (address
->sa_family
) {
1507 addr1
= &((struct sockaddr_in
*)address
)->sin_addr
;
1508 addr2
= &((struct sockaddr_in
*)info
.rti_info
[RTAX_IFA
])->sin_addr
;
1509 len
= sizeof(struct in_addr
);
1514 if (((struct sockaddr_in
*)address
)->sin_addr
.s_addr
== 0) {
1515 statusMessage
= "isReachable (this host)";
1516 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1520 addr1
= &((struct sockaddr_in6
*)address
)->sin6_addr
;
1521 addr2
= &((struct sockaddr_in6
*)info
.rti_info
[RTAX_IFA
])->sin6_addr
;
1522 len
= sizeof(struct in6_addr
);
1528 if (bcmp(addr1
, addr2
, len
) == 0) {
1529 statusMessage
= "isReachable (is interface address)";
1530 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1534 if (!(info
.rtm
->rtm_flags
& RTF_GATEWAY
) &&
1535 (info
.rti_info
[RTAX_GATEWAY
] != NULL
) &&
1536 (info
.rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
) &&
1537 !(ifr
.ifr_flags
& IFF_POINTOPOINT
)) {
1538 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1541 bzero(&if_name
, sizeof(if_name
));
1542 bcopy(info
.sdl
->sdl_data
,
1544 (info
.sdl
->sdl_nlen
<= IFNAMSIZ
) ? info
.sdl
->sdl_nlen
: IFNAMSIZ
);
1546 reach_info
->if_index
= info
.sdl
->sdl_index
;
1549 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = %s"), log_prefix
, statusMessage
);
1550 SCLog(TRUE
, LOG_INFO
, CFSTR("%s device = %s (%hu)"), log_prefix
, if_name
, info
.sdl
->sdl_index
);
1551 SCLog(TRUE
, LOG_INFO
, CFSTR("%s sdl_type = 0x%x"), log_prefix
, info
.sdl
->sdl_type
);
1552 SCLog(TRUE
, LOG_INFO
, CFSTR("%s ifr_flags = 0x%04hx"), log_prefix
, ifr
.ifr_flags
);
1553 SCLog(TRUE
, LOG_INFO
, CFSTR("%s rtm_flags = 0x%08x"), log_prefix
, info
.rtm
->rtm_flags
);
1556 sc_status
= kSCStatusOK
;
1558 if (ifr
.ifr_flags
& IFF_POINTOPOINT
) {
1559 reach_info
->flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1562 if (info
.sdl
->sdl_type
== IFT_PPP
) {
1564 * 1. check if PPP service
1565 * 2. check for dial-on-demand PPP link that is not yet connected
1566 * 3. get PPP server address
1568 sc_status
= updatePPPStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1569 } else if (info
.sdl
->sdl_type
== IFT_OTHER
) {
1571 * 1. check if IPSec service
1572 * 2. get IPSec server address
1574 sc_status
= updateIPSecStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1576 #if !TARGET_IPHONE_SIMULATOR
1577 if (sc_status
== kSCStatusNoKey
) {
1579 * 1. check if VPN service
1580 * 2. get VPN server address
1582 sc_status
= updateVPNStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1584 #endif // !TARGET_IPHONE_SIMULATOR
1593 sc_status
= updatePPPAvailable(store_info
, address
, &reach_info
->flags
, log_prefix
);
1594 if ((sc_status
== kSCStatusOK
) && (reach_info
->flags
!= 0)) {
1598 #if !TARGET_IPHONE_SIMULATOR
1599 sc_status
= updateVPNAvailable(store_info
, address
, &reach_info
->flags
, log_prefix
);
1600 if ((sc_status
== kSCStatusOK
) && (reach_info
->flags
!= 0)) {
1603 #endif // !TARGET_IPHONE_SIMULATOR
1607 if (reach_info
->flags
== 0) {
1608 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s cannot be reached"), log_prefix
);
1611 if (isock
!= -1) (void)close(isock
);
1612 if (server
!= NULL
) CFRelease(server
);
1613 if ((sc_status
!= kSCStatusOK
) && (sc_status
!= kSCStatusNoKey
)) {
1614 _SCErrorSet(sc_status
);
1623 #pragma mark SCNetworkReachability APIs
1627 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
1629 CFAllocatorRef allocator
= CFGetAllocator(cf
);
1630 CFMutableStringRef result
;
1631 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1633 result
= CFStringCreateMutable(allocator
, 0);
1634 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
1635 switch (targetPrivate
->type
) {
1636 case reachabilityTypeAddress
:
1637 case reachabilityTypeAddressPair
: {
1640 if (targetPrivate
->localAddress
!= NULL
) {
1641 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
1642 CFStringAppendFormat(result
, NULL
, CFSTR("local address = %s"),
1646 if (targetPrivate
->remoteAddress
!= NULL
) {
1647 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
1648 CFStringAppendFormat(result
, NULL
, CFSTR("%s%saddress = %s"),
1649 targetPrivate
->localAddress
? ", " : "",
1650 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
1655 case reachabilityTypeName
: {
1656 if ((targetPrivate
->name
!= NULL
)) {
1657 CFStringAppendFormat(result
, NULL
, CFSTR("name = %s"), targetPrivate
->name
);
1659 if ((targetPrivate
->serv
!= NULL
)) {
1660 CFStringAppendFormat(result
, NULL
, CFSTR("%sserv = %s"),
1661 targetPrivate
->name
!= NULL
? ", " : "",
1662 targetPrivate
->serv
);
1664 if ((targetPrivate
->resolvedAddress
!= NULL
) || (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1665 if (targetPrivate
->resolvedAddress
!= NULL
) {
1666 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
1668 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddress
);
1670 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
1671 for (i
= 0; i
< n
; i
++) {
1674 struct sockaddr
*sa
;
1676 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
);
1677 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
1678 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
1679 CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"),
1683 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
1685 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)"));
1688 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
1689 gai_strerror(targetPrivate
->resolvedAddressError
));
1691 } else if (targetPrivate
->dnsPort
!= NULL
) {
1692 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
1697 if (targetPrivate
->scheduled
) {
1698 CFStringAppendFormat(result
,
1700 CFSTR(", flags = 0x%08x, if_index = %hu"),
1701 targetPrivate
->info
.flags
,
1702 targetPrivate
->info
.if_index
);
1704 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
1711 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1713 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1715 /* release resources */
1717 pthread_mutex_destroy(&targetPrivate
->lock
);
1719 if (targetPrivate
->name
!= NULL
)
1720 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
1722 if (targetPrivate
->serv
!= NULL
)
1723 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->serv
);
1725 if (targetPrivate
->resolvedAddress
!= NULL
)
1726 CFRelease(targetPrivate
->resolvedAddress
);
1728 if (targetPrivate
->localAddress
!= NULL
)
1729 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
1731 if (targetPrivate
->remoteAddress
!= NULL
)
1732 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
1734 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1735 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1738 if (targetPrivate
->onDemandName
!= NULL
) {
1739 CFRelease(targetPrivate
->onDemandName
);
1742 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
1743 CFRelease(targetPrivate
->onDemandRemoteAddress
);
1746 if (targetPrivate
->onDemandServer
!= NULL
) {
1747 CFRelease(targetPrivate
->onDemandServer
);
1750 if (targetPrivate
->onDemandServiceID
!= NULL
) {
1751 CFRelease(targetPrivate
->onDemandServiceID
);
1759 __SCNetworkReachabilityInitialize(void)
1761 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
1767 __SCNetworkReachabilityPerform(SCNetworkReachabilityRef target
)
1769 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1771 if (targetPrivate
->dispatchQueue
!= NULL
) {
1773 dispatch_async(targetPrivate
->dispatchQueue
,
1775 rlsPerform((void *)target
);
1778 } else if (targetPrivate
->rls
!= NULL
) {
1779 CFRunLoopSourceSignal(targetPrivate
->rls
);
1780 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1786 static SCNetworkReachabilityPrivateRef
1787 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
1789 SCNetworkReachabilityPrivateRef targetPrivate
;
1792 /* initialize runtime */
1793 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
1795 /* allocate target */
1796 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
1797 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
1798 __kSCNetworkReachabilityTypeID
,
1801 if (targetPrivate
== NULL
) {
1805 pthread_mutex_init(&targetPrivate
->lock
, NULL
);
1807 targetPrivate
->name
= NULL
;
1808 targetPrivate
->serv
= NULL
;
1809 bzero(&targetPrivate
->hints
, sizeof(targetPrivate
->hints
));
1810 targetPrivate
->hints
.ai_flags
= AI_ADDRCONFIG
;
1812 targetPrivate
->hints
.ai_flags
|= AI_PARALLEL
;
1813 #endif /* AI_PARALLEL */
1815 targetPrivate
->needResolve
= FALSE
;
1816 targetPrivate
->resolvedAddress
= NULL
;
1817 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1819 targetPrivate
->if_index
= 0;
1821 targetPrivate
->localAddress
= NULL
;
1822 targetPrivate
->remoteAddress
= NULL
;
1824 targetPrivate
->info
= NOT_REACHABLE
;
1825 targetPrivate
->last_notify
= NOT_REPORTED
;
1827 targetPrivate
->scheduled
= FALSE
;
1828 targetPrivate
->rls
= NULL
;
1829 targetPrivate
->rlsFunction
= NULL
;
1830 targetPrivate
->rlsContext
.info
= NULL
;
1831 targetPrivate
->rlsContext
.retain
= NULL
;
1832 targetPrivate
->rlsContext
.release
= NULL
;
1833 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1834 targetPrivate
->rlList
= NULL
;
1836 targetPrivate
->haveDNS
= FALSE
;
1837 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
1838 targetPrivate
->dnsPort
= NULL
;
1839 targetPrivate
->dnsRLS
= NULL
;
1841 targetPrivate
->onDemandBypass
= FALSE
;
1842 targetPrivate
->onDemandName
= NULL
;
1843 targetPrivate
->onDemandRemoteAddress
= NULL
;
1844 targetPrivate
->onDemandServer
= NULL
;
1845 targetPrivate
->onDemandServiceID
= NULL
;
1847 targetPrivate
->log_prefix
[0] = '\0';
1849 snprintf(targetPrivate
->log_prefix
,
1850 sizeof(targetPrivate
->log_prefix
),
1855 return targetPrivate
;
1861 static const struct sockaddr
*
1862 is_valid_address(const struct sockaddr
*address
)
1864 const struct sockaddr
*valid
= NULL
;
1865 static Boolean warned
= FALSE
;
1867 if ((address
!= NULL
) &&
1868 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
1869 switch (address
->sa_family
) {
1871 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
1875 SCLog(TRUE
, LOG_ERR
,
1876 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
1878 sizeof(struct sockaddr_in
));
1884 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
1886 } else if (!warned
) {
1887 SCLog(TRUE
, LOG_ERR
,
1888 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
1890 sizeof(struct sockaddr_in6
));
1896 SCLog(TRUE
, LOG_ERR
,
1897 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"),
1898 address
->sa_family
);
1908 SCNetworkReachabilityRef
1909 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
1910 const struct sockaddr
*address
)
1912 SCNetworkReachabilityPrivateRef targetPrivate
;
1914 address
= is_valid_address(address
);
1915 if (address
== NULL
) {
1916 _SCErrorSet(kSCStatusInvalidArgument
);
1920 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1921 if (targetPrivate
== NULL
) {
1925 targetPrivate
->type
= reachabilityTypeAddress
;
1926 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
1927 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
1929 return (SCNetworkReachabilityRef
)targetPrivate
;
1933 SCNetworkReachabilityRef
1934 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
1935 const struct sockaddr
*localAddress
,
1936 const struct sockaddr
*remoteAddress
)
1938 SCNetworkReachabilityPrivateRef targetPrivate
;
1940 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
1941 _SCErrorSet(kSCStatusInvalidArgument
);
1945 if (localAddress
!= NULL
) {
1946 localAddress
= is_valid_address(localAddress
);
1947 if (localAddress
== NULL
) {
1948 _SCErrorSet(kSCStatusInvalidArgument
);
1953 if (remoteAddress
!= NULL
) {
1954 remoteAddress
= is_valid_address(remoteAddress
);
1955 if (remoteAddress
== NULL
) {
1956 _SCErrorSet(kSCStatusInvalidArgument
);
1961 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1962 if (targetPrivate
== NULL
) {
1966 targetPrivate
->type
= reachabilityTypeAddressPair
;
1968 if (localAddress
!= NULL
) {
1969 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
1970 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
1973 if (remoteAddress
!= NULL
) {
1974 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
1975 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
1978 return (SCNetworkReachabilityRef
)targetPrivate
;
1982 SCNetworkReachabilityRef
1983 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
1984 const char *nodename
)
1987 struct sockaddr_in sin
;
1988 struct sockaddr_in6 sin6
;
1989 SCNetworkReachabilityPrivateRef targetPrivate
;
1991 if (nodename
== NULL
) {
1992 _SCErrorSet(kSCStatusInvalidArgument
);
1996 nodenameLen
= strlen(nodename
);
1997 if (nodenameLen
== 0) {
1998 _SCErrorSet(kSCStatusInvalidArgument
);
2002 /* check if this "nodename" is really an IP[v6] address in disguise */
2004 bzero(&sin
, sizeof(sin
));
2005 sin
.sin_len
= sizeof(sin
);
2006 sin
.sin_family
= AF_INET
;
2007 if (inet_aton(nodename
, &sin
.sin_addr
) == 1) {
2008 /* if IPv4 address */
2009 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin
);
2012 bzero(&sin6
, sizeof(sin6
));
2013 sin6
.sin6_len
= sizeof(sin6
);
2014 sin6
.sin6_family
= AF_INET6
;
2015 if (inet_pton(AF_INET6
, nodename
, &sin6
.sin6_addr
) == 1) {
2016 /* if IPv6 address */
2019 p
= strchr(nodename
, '%');
2021 sin6
.sin6_scope_id
= if_nametoindex(p
+ 1);
2024 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin6
);
2027 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2028 if (targetPrivate
== NULL
) {
2032 targetPrivate
->type
= reachabilityTypeName
;
2034 targetPrivate
->name
= CFAllocatorAllocate(NULL
, nodenameLen
+ 1, 0);
2035 strlcpy((char *)targetPrivate
->name
, nodename
, nodenameLen
+ 1);
2037 targetPrivate
->needResolve
= TRUE
;
2038 targetPrivate
->info
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
2040 return (SCNetworkReachabilityRef
)targetPrivate
;
2044 SCNetworkReachabilityRef
2045 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
2046 CFDictionaryRef options
)
2048 const struct sockaddr
*addr_l
= NULL
;
2049 const struct sockaddr
*addr_r
= NULL
;
2050 CFBooleanRef bypass
;
2052 struct addrinfo
*hints
= NULL
;
2053 CFStringRef interface
= NULL
;
2054 CFStringRef nodename
;
2055 CFStringRef servname
;
2056 SCNetworkReachabilityRef target
;
2057 SCNetworkReachabilityPrivateRef targetPrivate
;
2059 if (!isA_CFDictionary(options
)) {
2060 _SCErrorSet(kSCStatusInvalidArgument
);
2064 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
2065 if ((nodename
!= NULL
) &&
2066 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
2067 _SCErrorSet(kSCStatusInvalidArgument
);
2070 servname
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServName
);
2071 if ((servname
!= NULL
) &&
2072 (!isA_CFString(servname
) || (CFStringGetLength(servname
) == 0))) {
2073 _SCErrorSet(kSCStatusInvalidArgument
);
2076 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
2078 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2079 _SCErrorSet(kSCStatusInvalidArgument
);
2082 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2084 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
2086 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2087 _SCErrorSet(kSCStatusInvalidArgument
);
2090 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2092 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionHints
);
2094 if (!isA_CFData(data
) || (CFDataGetLength(data
) != sizeof(targetPrivate
->hints
))) {
2095 _SCErrorSet(kSCStatusInvalidArgument
);
2099 hints
= (struct addrinfo
*)CFDataGetBytePtr(data
);
2100 if ((hints
->ai_addrlen
!= 0) ||
2101 (hints
->ai_addr
!= NULL
) ||
2102 (hints
->ai_canonname
!= NULL
) ||
2103 (hints
->ai_next
!= NULL
)) {
2104 _SCErrorSet(kSCStatusInvalidArgument
);
2108 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
2109 if ((interface
!= NULL
) &&
2110 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
2111 _SCErrorSet(kSCStatusInvalidArgument
);
2114 bypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandByPass
);
2115 if ((bypass
!= NULL
) && !isA_CFBoolean(bypass
)) {
2116 _SCErrorSet(kSCStatusInvalidArgument
);
2120 if ((nodename
!= NULL
) || (servname
!= NULL
)) {
2123 if ((addr_l
!= NULL
) || (addr_r
!= NULL
)) {
2124 // can't have both a name/serv and an address
2125 _SCErrorSet(kSCStatusInvalidArgument
);
2129 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
2130 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
2131 CFAllocatorDeallocate(NULL
, (void *)name
);
2133 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
2134 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
2135 } else if (addr_r
!= NULL
) {
2136 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
2137 } else if (addr_l
!= NULL
) {
2138 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_l
);
2140 _SCErrorSet(kSCStatusInvalidArgument
);
2144 if (target
== NULL
) {
2148 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2149 if (targetPrivate
->type
== reachabilityTypeName
) {
2150 if (servname
!= NULL
) {
2151 targetPrivate
->serv
= _SC_cfstring_to_cstring(servname
, NULL
, 0, kCFStringEncodingUTF8
);
2153 if (hints
!= NULL
) {
2154 bcopy(hints
, &targetPrivate
->hints
, sizeof(targetPrivate
->hints
));
2158 if (interface
!= NULL
) {
2159 if ((_SC_cfstring_to_cstring(interface
,
2160 targetPrivate
->if_name
,
2161 sizeof(targetPrivate
->if_name
),
2162 kCFStringEncodingASCII
) == NULL
) ||
2163 ((targetPrivate
->if_index
= if_nametoindex(targetPrivate
->if_name
)) == 0)) {
2164 CFRelease(targetPrivate
);
2165 _SCErrorSet(kSCStatusInvalidArgument
);
2170 if (bypass
!= NULL
) {
2171 targetPrivate
->onDemandBypass
= CFBooleanGetValue(bypass
);
2174 return (SCNetworkReachabilityRef
)targetPrivate
;
2179 SCNetworkReachabilityGetTypeID(void)
2181 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
2182 return __kSCNetworkReachabilityTypeID
;
2186 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
2187 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
2190 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2192 if (!isA_SCNetworkReachability(target
)) {
2193 _SCErrorSet(kSCStatusInvalidArgument
);
2197 if (targetPrivate
->type
!= reachabilityTypeName
) {
2198 _SCErrorSet(kSCStatusInvalidArgument
);
2203 *error_num
= targetPrivate
->resolvedAddressError
;
2206 if (targetPrivate
->resolvedAddress
!= NULL
) {
2207 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
2208 return CFRetain(targetPrivate
->resolvedAddress
);
2210 /* if status is known but no resolved addresses to return */
2211 _SCErrorSet(kSCStatusOK
);
2216 _SCErrorSet(kSCStatusReachabilityUnknown
);
2222 __SCNetworkReachabilitySetResolvedAddress(int32_t status
,
2223 struct addrinfo
*res
,
2224 SCNetworkReachabilityRef target
)
2226 struct addrinfo
*resP
;
2227 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2229 if (targetPrivate
->resolvedAddress
!= NULL
) {
2230 CFRelease(targetPrivate
->resolvedAddress
);
2231 targetPrivate
->resolvedAddress
= NULL
;
2234 if ((status
== 0) && (res
!= NULL
)) {
2235 CFMutableArrayRef addresses
;
2237 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2239 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
2241 CFDataRef newAddress
;
2243 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
2244 n
= CFArrayGetCount(addresses
);
2246 !CFArrayContainsValue(addresses
, CFRangeMake(0, n
), newAddress
)) {
2247 CFArrayAppendValue(addresses
, newAddress
);
2249 CFRelease(newAddress
);
2252 /* save the resolved address[es] */
2253 targetPrivate
->resolvedAddress
= addresses
;
2254 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
2256 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sgetaddrinfo() failed: %s"),
2257 targetPrivate
->log_prefix
,
2258 gai_strerror(status
));
2260 /* save the error associated with the attempt to resolve the name */
2261 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
2262 targetPrivate
->resolvedAddressError
= status
;
2264 targetPrivate
->needResolve
= FALSE
;
2266 if (res
!= NULL
) freeaddrinfo(res
);
2268 if (targetPrivate
->scheduled
) {
2269 __SCNetworkReachabilityPerform(target
);
2277 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo
*res
, void *context
)
2279 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
2280 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2282 __log_query_time(target
,
2283 ((status
== 0) && (res
!= NULL
)), // if successful query
2285 &targetPrivate
->dnsQueryStart
); // start time
2287 __SCNetworkReachabilitySetResolvedAddress(status
, res
, target
);
2293 * rankReachability()
2294 * Not reachable == 0
2295 * Connection Required == 1
2299 rankReachability(SCNetworkReachabilityFlags flags
)
2303 if (flags
& kSCNetworkReachabilityFlagsReachable
) rank
= 2;
2304 if (flags
& kSCNetworkReachabilityFlagsConnectionRequired
) rank
= 1;
2310 #pragma mark DNS name resolution
2314 replyMPCopyDescription(const void *info
)
2316 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2317 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2319 return CFStringCreateWithFormat(NULL
,
2321 CFSTR("<getaddrinfo_async_start reply MP> {%s%s%s%s%s, target = %p}"),
2322 targetPrivate
->name
!= NULL
? "name = " : "",
2323 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
2324 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
2325 targetPrivate
->serv
!= NULL
? "serv = " : "",
2326 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "",
2332 processAsyncDNSReply(mach_port_t mp
, void *msg
, SCNetworkReachabilityRef target
);
2336 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
2338 mach_port_t mp
= CFMachPortGetPort(port
);
2339 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2341 processAsyncDNSReply(mp
, msg
, target
);
2347 SCNetworkReachabilityNotifyMIGCallback(mach_msg_header_t
*message
, mach_msg_header_t
*reply
)
2349 mach_port_t mp
= message
->msgh_local_port
;
2350 SCNetworkReachabilityRef target
= dispatch_get_context(dispatch_get_current_queue());
2352 processAsyncDNSReply(mp
, message
, target
);
2353 reply
->msgh_remote_port
= MACH_PORT_NULL
;
2359 enqueueAsyncDNSQuery(SCNetworkReachabilityRef target
, mach_port_t mp
)
2361 CFMachPortContext context
= { 0
2365 , replyMPCopyDescription
2367 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2369 targetPrivate
->dnsMP
= mp
;
2370 targetPrivate
->dnsPort
= _SC_CFMachPortCreateWithPort("SCNetworkReachability",
2372 getaddrinfo_async_handleCFReply
,
2374 if (targetPrivate
->dispatchQueue
!= NULL
) {
2375 targetPrivate
->asyncDNSQueue
= dispatch_queue_create("com.apple.SCNetworkReachabilty.async_DNS_query", NULL
);
2376 if (targetPrivate
->asyncDNSQueue
== NULL
) {
2377 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_queue_create() failed"));
2380 CFRetain(target
); // Note: will be released when the dispatch queue is released
2381 dispatch_set_context(targetPrivate
->asyncDNSQueue
, (void *)target
);
2382 dispatch_set_finalizer_f(targetPrivate
->asyncDNSQueue
, (dispatch_function_t
)CFRelease
);
2384 targetPrivate
->asyncDNSSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
,
2387 targetPrivate
->asyncDNSQueue
);
2388 if (targetPrivate
->asyncDNSSource
== NULL
) {
2389 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_source_create() failed"));
2392 dispatch_source_set_event_handler(targetPrivate
->asyncDNSSource
, ^{
2393 dispatch_mig_server(targetPrivate
->asyncDNSSource
,
2394 sizeof(mach_msg_header_t
),
2395 SCNetworkReachabilityNotifyMIGCallback
);
2397 dispatch_resume(targetPrivate
->asyncDNSSource
);
2398 } else if (targetPrivate
->rls
!= NULL
) {
2402 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
2404 n
= CFArrayGetCount(targetPrivate
->rlList
);
2405 for (i
= 0; i
< n
; i
+= 3) {
2406 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
2407 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
2409 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
2417 if (targetPrivate
->asyncDNSSource
!= NULL
) {
2418 dispatch_source_cancel(targetPrivate
->asyncDNSSource
);
2419 dispatch_release(targetPrivate
->asyncDNSSource
);
2420 targetPrivate
->asyncDNSSource
= NULL
;
2422 if (targetPrivate
->asyncDNSQueue
!= NULL
) {
2423 dispatch_release(targetPrivate
->asyncDNSQueue
);
2424 targetPrivate
->asyncDNSQueue
= NULL
;
2427 CFMachPortInvalidate(targetPrivate
->dnsPort
);
2428 CFRelease(targetPrivate
->dnsPort
);
2429 targetPrivate
->dnsPort
= NULL
;
2430 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
2432 _SCErrorSet(kSCStatusFailed
);
2438 dequeueAsyncDNSQuery(SCNetworkReachabilityRef target
)
2440 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2442 if (targetPrivate
->asyncDNSSource
!= NULL
) {
2443 dispatch_source_cancel(targetPrivate
->asyncDNSSource
);
2444 if (targetPrivate
->asyncDNSQueue
!= dispatch_get_current_queue()) {
2445 // ensure the cancellation has completed
2446 pthread_mutex_unlock(&targetPrivate
->lock
);
2447 dispatch_sync(targetPrivate
->asyncDNSQueue
, ^{});
2448 pthread_mutex_lock(&targetPrivate
->lock
);
2451 if (targetPrivate
->asyncDNSSource
!= NULL
) {
2452 dispatch_release(targetPrivate
->asyncDNSSource
);
2453 targetPrivate
->asyncDNSSource
= NULL
;
2455 if (targetPrivate
->asyncDNSQueue
!= NULL
) {
2456 dispatch_release(targetPrivate
->asyncDNSQueue
);
2457 targetPrivate
->asyncDNSQueue
= NULL
;
2460 if (targetPrivate
->dnsRLS
!= NULL
) {
2461 CFRelease(targetPrivate
->dnsRLS
);
2462 targetPrivate
->dnsRLS
= NULL
;
2465 if (targetPrivate
->dnsPort
!= NULL
) {
2466 CFMachPortInvalidate(targetPrivate
->dnsPort
);
2467 CFRelease(targetPrivate
->dnsPort
);
2468 targetPrivate
->dnsPort
= NULL
;
2469 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
2477 processAsyncDNSReply(mach_port_t mp
, void *msg
, SCNetworkReachabilityRef target
)
2480 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2482 pthread_mutex_lock(&targetPrivate
->lock
);
2484 if (mp
!= targetPrivate
->dnsMP
) {
2485 // we've received a callback on the async DNS port but since the
2486 // associated CFMachPort doesn't match than the request must have
2487 // already been cancelled.
2488 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP"));
2489 pthread_mutex_unlock(&targetPrivate
->lock
);
2493 dequeueAsyncDNSQuery(target
);
2494 status
= getaddrinfo_async_handle_reply(msg
);
2495 if ((status
== 0) &&
2496 (targetPrivate
->resolvedAddress
== NULL
) && (targetPrivate
->resolvedAddressError
== NETDB_SUCCESS
)) {
2499 // if the request is not complete and needs to be re-queued
2500 ok
= enqueueAsyncDNSQuery(target
, mp
);
2502 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply enqueueAsyncDNSQuery() failed"));
2506 pthread_mutex_unlock(&targetPrivate
->lock
);
2513 check_resolver_reachability(ReachabilityStoreInfoRef store_info
,
2514 dns_resolver_t
*resolver
,
2515 SCNetworkReachabilityFlags
*flags
,
2517 const char *log_prefix
)
2522 *flags
= kSCNetworkReachabilityFlagsReachable
;
2525 for (i
= 0; i
< resolver
->n_nameserver
; i
++) {
2526 struct sockaddr
*address
= resolver
->nameserver
[i
];
2527 ReachabilityInfo ns_info
;
2531 if (address
->sa_family
!= AF_INET
) {
2533 * we need to skip non-IPv4 DNS server
2534 * addresses (at least until [3510431] has
2540 ok
= checkAddress(store_info
, address
, resolver
->if_index
, &ns_info
, log_prefix
);
2546 if (rankReachability(ns_info
.flags
) < rankReachability(*flags
)) {
2547 /* return the worst case result */
2548 *flags
= ns_info
.flags
;
2559 check_matching_resolvers(ReachabilityStoreInfoRef store_info
,
2560 dns_config_t
*dns_config
,
2562 unsigned int if_index
,
2563 SCNetworkReachabilityFlags
*flags
,
2565 const char *log_prefix
)
2568 Boolean matched
= FALSE
;
2569 const char *name
= fqdn
;
2570 int32_t n_resolvers
;
2571 dns_resolver_t
**resolvers
;
2573 if (if_index
== 0) {
2574 n_resolvers
= dns_config
->n_resolver
;
2575 resolvers
= dns_config
->resolver
;
2577 n_resolvers
= dns_config
->n_scoped_resolver
;
2578 resolvers
= dns_config
->scoped_resolver
;
2581 while (!matched
&& (name
!= NULL
)) {
2585 * check if the provided name (or sub-component)
2586 * matches one of our resolver configurations.
2589 for (i
= 0; i
< n_resolvers
; i
++) {
2591 dns_resolver_t
*resolver
;
2593 resolver
= resolvers
[i
];
2594 if ((if_index
!= 0) && (if_index
!= resolver
->if_index
)) {
2598 domain
= resolver
->domain
;
2599 if (domain
!= NULL
&& (len
== strlen(domain
))) {
2600 if (strcasecmp(name
, domain
) == 0) {
2604 * if name matches domain
2607 ok
= check_resolver_reachability(store_info
, resolver
, flags
, haveDNS
, log_prefix
);
2618 * we have not found a matching resolver, try
2619 * a less qualified domain
2621 name
= strchr(name
, '.');
2622 if ((name
!= NULL
) && (*name
!= '\0')) {
2634 static dns_resolver_t
*
2635 get_default_resolver(dns_config_t
*dns_config
, unsigned int if_index
)
2638 int32_t n_resolvers
;
2639 dns_resolver_t
*resolver
= NULL
;
2640 dns_resolver_t
**resolvers
;
2642 if (if_index
== 0) {
2643 n_resolvers
= dns_config
->n_resolver
;
2644 resolvers
= dns_config
->resolver
;
2646 n_resolvers
= dns_config
->n_scoped_resolver
;
2647 resolvers
= dns_config
->scoped_resolver
;
2650 for (i
= 0; i
< n_resolvers
; i
++) {
2651 if ((if_index
!= 0) && (if_index
!= resolvers
[i
]->if_index
)) {
2655 if (((if_index
== 0) && (i
== 0)) ||
2656 ((if_index
!= 0) && (resolver
== NULL
))) {
2657 // if this is the first (aka default) resolver
2658 resolver
= resolvers
[i
];
2659 } else if ((resolvers
[i
]->domain
== NULL
) &&
2660 (resolvers
[i
]->search_order
< resolver
->search_order
)) {
2661 // if this is a default resolver with a lower search order
2662 resolver
= resolvers
[i
];
2670 static dns_configuration_t
*
2671 dns_configuration_retain()
2673 pthread_mutex_lock(&dns_lock
);
2675 if ((dns_configuration
!= NULL
) && dns_token_valid
) {
2680 * check if the global [DNS] configuration snapshot needs
2683 status
= notify_check(dns_token
, &check
);
2684 if (status
!= NOTIFY_STATUS_OK
) {
2685 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
2688 if ((status
!= NOTIFY_STATUS_OK
) || (check
!= 0)) {
2690 * if the snapshot needs to be refreshed
2692 if (dns_configuration
->refs
== 0) {
2693 dns_configuration_free(dns_configuration
->config
);
2694 CFAllocatorDeallocate(NULL
, dns_configuration
);
2696 dns_configuration
= NULL
;
2700 if (dns_configuration
== NULL
) {
2701 dns_config_t
*new_config
;
2703 new_config
= dns_configuration_copy();
2704 if (new_config
!= NULL
) {
2705 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
2706 dns_configuration
->config
= new_config
;
2707 dns_configuration
->refs
= 0;
2711 if (dns_configuration
!= NULL
) {
2712 dns_configuration
->refs
++;
2715 pthread_mutex_unlock(&dns_lock
);
2716 return dns_configuration
;
2721 dns_configuration_release(dns_configuration_t
*config
)
2723 pthread_mutex_lock(&dns_lock
);
2726 if (config
->refs
== 0) {
2727 if ((dns_configuration
!= config
)) {
2728 dns_configuration_free(config
->config
);
2729 CFAllocatorDeallocate(NULL
, config
);
2733 pthread_mutex_unlock(&dns_lock
);
2739 dns_configuration_watch()
2742 const char *dns_key
;
2746 pthread_mutex_lock(&dns_lock
);
2748 dns_key
= dns_configuration_notify_key();
2749 if (dns_key
== NULL
) {
2750 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
2754 status
= notify_register_check(dns_key
, &dns_token
);
2755 if (status
== NOTIFY_STATUS_OK
) {
2756 dns_token_valid
= TRUE
;
2758 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
);
2762 status
= notify_check(dns_token
, &dns_check
);
2763 if (status
!= NOTIFY_STATUS_OK
) {
2764 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
2765 (void)notify_cancel(dns_token
);
2766 dns_token_valid
= FALSE
;
2774 pthread_mutex_unlock(&dns_lock
);
2780 dns_configuration_unwatch()
2782 pthread_mutex_lock(&dns_lock
);
2784 (void)notify_cancel(dns_token
);
2785 dns_token_valid
= FALSE
;
2787 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
2788 dns_configuration_free(dns_configuration
->config
);
2789 CFAllocatorDeallocate(NULL
, dns_configuration
);
2790 dns_configuration
= NULL
;
2793 pthread_mutex_unlock(&dns_lock
);
2799 _SC_R_checkResolverReachability(ReachabilityStoreInfoRef store_info
,
2800 SCNetworkReachabilityFlags
*flags
,
2802 const char *nodename
,
2803 const char *servname
,
2804 unsigned int if_index
,
2805 const char *log_prefix
)
2807 dns_resolver_t
*default_resolver
;
2808 dns_configuration_t
*dns
;
2809 Boolean found
= FALSE
;
2810 char *fqdn
= (char *)nodename
;
2812 Boolean isFQDN
= FALSE
;
2816 Boolean useDefault
= FALSE
;
2819 * We first assume that all of the configured DNS servers
2820 * are available. Since we don't know which name server will
2821 * be consulted to resolve the specified nodename we need to
2822 * check the availability of ALL name servers. We can only
2823 * proceed if we know that our query can be answered.
2826 *flags
= kSCNetworkReachabilityFlagsReachable
;
2829 len
= (nodename
!= NULL
) ? strlen(nodename
) : 0;
2831 if ((servname
== NULL
) || (strlen(servname
) == 0)) {
2832 // if no nodename or servname, return not reachable
2838 dns
= dns_configuration_retain();
2841 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no configuration"), log_prefix
);
2845 if (dns
->config
->n_resolver
== 0) {
2846 // if no resolver configuration
2847 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no resolvers"), log_prefix
);
2851 *flags
= kSCNetworkReachabilityFlagsReachable
;
2853 if (fqdn
[len
- 1] == '.') {
2856 // trim trailing '.''s
2857 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
2858 if (fqdn
== nodename
) {
2859 fqdn
= strdup(nodename
);
2865 default_resolver
= get_default_resolver(dns
->config
, if_index
);
2868 * check if the provided name matches a supplemental domain
2870 found
= check_matching_resolvers(store_info
, dns
->config
, fqdn
, if_index
, flags
, haveDNS
, log_prefix
);
2872 if (!found
&& !isFQDN
) {
2874 * if we did not match a supplemental domain name and if the
2875 * provided name has enough "."s then the first query will be
2876 * directed to the default resolver.
2881 #define NDOTS_OPT "ndots="
2882 #define NDOTS_OPT_LEN (sizeof("ndots=") - 1)
2884 if (default_resolver
->options
!= NULL
) {
2885 cp
= strstr(default_resolver
->options
, NDOTS_OPT
);
2887 ((cp
== default_resolver
->options
) || isspace(cp
[-1])) &&
2888 ((cp
[NDOTS_OPT_LEN
] != '\0') && isdigit(cp
[NDOTS_OPT_LEN
]))) {
2892 cp
+= NDOTS_OPT_LEN
;
2894 val
= strtol(cp
, &end
, 10);
2895 if ((*cp
!= '\0') && (cp
!= end
) && (errno
== 0) &&
2896 ((*end
== '\0') || isspace(*end
))) {
2903 for (cp
= fqdn
; *cp
!= '\0'; cp
++) {
2904 if (*cp
== '.') dots
++;
2912 if (!found
&& !isFQDN
&& !useDefault
&& (dns
->config
->n_resolver
> 1)) {
2914 * FQDN not specified, try matching w/search domains
2916 if (default_resolver
->n_search
> 0) {
2917 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
2919 char *search_fqdn
= NULL
;
2921 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
2926 // try the provided name with the search domain appended
2927 found
= check_matching_resolvers(store_info
,
2936 } else if (default_resolver
->domain
!= NULL
) {
2938 int domain_parts
= 0;
2940 // count domain parts
2941 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
2947 // remove trailing dots
2948 for (dp
--; (dp
>= default_resolver
->domain
) && (*dp
== '.'); dp
--) {
2953 if (dp
>= default_resolver
->domain
) {
2954 // dots are separators, bump # of components
2958 dp
= default_resolver
->domain
;
2959 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= (domain_parts
- ndots
)); i
++) {
2961 char *search_fqdn
= NULL
;
2963 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
2968 // try the provided name with the [default] domain appended
2969 found
= check_matching_resolvers(store_info
,
2978 // move to the next component of the [default] domain
2979 dp
= strchr(dp
, '.') + 1;
2986 * check the reachability of the default resolver
2988 ok
= check_resolver_reachability(store_info
, default_resolver
, flags
, haveDNS
, log_prefix
);
2991 if (fqdn
!= nodename
) free(fqdn
);
2996 dns_configuration_release(dns
);
3004 _SC_checkResolverReachability(SCDynamicStoreRef
*storeP
,
3005 SCNetworkReachabilityFlags
*flags
,
3007 const char *nodename
,
3008 const char *servname
)
3011 ReachabilityStoreInfo store_info
;
3013 initReachabilityStoreInfo(&store_info
);
3014 ok
= updateReachabilityStoreInfo(&store_info
, storeP
, AF_UNSPEC
);
3019 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, nodename
, servname
, 0, "");
3023 freeReachabilityStoreInfo(&store_info
);
3029 * _SC_checkResolverReachabilityByAddress()
3031 * Given an IP address, determine whether a reverse DNS query can be issued
3032 * using the current network configuration.
3035 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
3036 SCNetworkReachabilityFlags
*flags
,
3038 struct sockaddr
*sa
)
3043 ReachabilityStoreInfo store_info
;
3045 initReachabilityStoreInfo(&store_info
);
3046 ok
= updateReachabilityStoreInfo(&store_info
, storeP
, AF_UNSPEC
);
3052 * Ideally, we would have an API that given a local IP
3053 * address would return the DNS server(s) that would field
3054 * a given PTR query. Fortunately, we do have an SPI which
3055 * which will provide this information given a "name" so we
3056 * take the address, convert it into the inverse query name,
3057 * and find out which servers should be consulted.
3060 switch (sa
->sa_family
) {
3066 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
3069 * build "PTR" query name
3070 * NNN.NNN.NNN.NNN.in-addr.arpa.
3072 rev
.s_addr
= sin
->sin_addr
.s_addr
;
3073 (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.",
3084 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
3085 int x
= sizeof(ptr_name
);
3089 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
3090 * 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.
3092 for (i
= sizeof(sin6
->sin6_addr
) - 1; i
>= 0; i
--) {
3093 n
= snprintf(&ptr_name
[s
], x
, "%x.%x.",
3094 ( sin6
->sin6_addr
.s6_addr
[i
] & 0xf),
3095 ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf));
3096 if ((n
== -1) || (n
>= x
)) {
3104 n
= snprintf(&ptr_name
[s
], x
, "ip6.arpa.");
3105 if ((n
== -1) || (n
>= x
)) {
3116 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, ptr_name
, NULL
, 0, "");
3120 freeReachabilityStoreInfo(&store_info
);
3126 startAsyncDNSQuery(SCNetworkReachabilityRef target
) {
3128 mach_port_t mp
= MACH_PORT_NULL
;
3130 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3132 (void) gettimeofday(&targetPrivate
->dnsQueryStart
, NULL
);
3134 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3135 if (targetPrivate
->if_index
== 0) {
3136 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3137 error
= getaddrinfo_async_start(&mp
,
3138 targetPrivate
->name
,
3139 targetPrivate
->serv
,
3140 &targetPrivate
->hints
,
3141 __SCNetworkReachabilityCallbackSetResolvedAddress
,
3143 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3145 mp
= _getaddrinfo_interface_async_call(targetPrivate
->name
,
3146 targetPrivate
->serv
,
3147 &targetPrivate
->hints
,
3148 targetPrivate
->if_name
,
3149 __SCNetworkReachabilityCallbackSetResolvedAddress
,
3151 if (mp
== MACH_PORT_NULL
) {
3155 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3157 /* save the error associated with the attempt to resolve the name */
3158 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
);
3162 ok
= enqueueAsyncDNSQuery(target
, mp
);
3168 #pragma mark OnDemand
3172 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target
,
3173 CFDictionaryRef
*userOptions
)
3175 SCNetworkServiceRef service
= NULL
;
3176 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3178 if (!isA_SCNetworkReachability(target
)) {
3179 _SCErrorSet(kSCStatusInvalidArgument
);
3183 if (targetPrivate
->onDemandServiceID
!= NULL
) {
3184 service
= _SCNetworkServiceCopyActive(NULL
, targetPrivate
->onDemandServiceID
);
3187 if (userOptions
!= NULL
) {
3188 if (targetPrivate
->onDemandName
!= NULL
) {
3189 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3190 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
);
3192 *userOptions
= NULL
;
3201 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer
,
3202 SCNetworkReachabilityFlags onDemandFlags
,
3205 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
3206 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3208 pthread_mutex_lock(&targetPrivate
->lock
);
3210 if (!targetPrivate
->scheduled
) {
3211 // if not currently scheduled
3212 pthread_mutex_unlock(&targetPrivate
->lock
);
3216 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sOnDemand \"server\" status changed"),
3217 targetPrivate
->log_prefix
);
3218 __SCNetworkReachabilityPerform(target
);
3220 pthread_mutex_unlock(&targetPrivate
->lock
);
3227 __SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef store_info
,
3228 SCNetworkReachabilityRef target
,
3229 Boolean onDemandRetry
,
3230 SCNetworkReachabilityFlags
*flags
)
3233 Boolean onDemand
= FALSE
;
3234 CFStringRef onDemandRemoteAddress
= NULL
;
3235 CFStringRef onDemandServiceID
= NULL
;
3236 SCNetworkConnectionStatus onDemandStatus
;
3237 SCDynamicStoreRef store
;
3238 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3240 // SCLog(_sc_debug, LOG_INFO,
3241 // CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"),
3242 // targetPrivate->log_prefix,
3243 // onDemandRetry ? "after" : "before");
3245 if (targetPrivate
->onDemandName
== NULL
) {
3246 targetPrivate
->onDemandName
= CFStringCreateWithCString(NULL
, targetPrivate
->name
, kCFStringEncodingUTF8
);
3250 * check if an OnDemand VPN configuration matches the name.
3252 store
= store_info
->store
;
3253 ok
= __SCNetworkConnectionCopyOnDemandInfoWithName(&store
,
3254 targetPrivate
->onDemandName
,
3258 &onDemandRemoteAddress
);
3259 if (store_info
->store
!= store
) {
3260 store_info
->store
= store
;
3261 store_info
->storeAdded
= TRUE
;
3263 if (!_SC_CFEqual(targetPrivate
->onDemandRemoteAddress
, onDemandRemoteAddress
) ||
3264 !_SC_CFEqual(targetPrivate
->onDemandServiceID
, onDemandServiceID
)) {
3265 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
3266 CFRelease(targetPrivate
->onDemandRemoteAddress
);
3267 targetPrivate
->onDemandRemoteAddress
= NULL
;
3270 if (targetPrivate
->onDemandServer
!= NULL
) {
3271 if (targetPrivate
->dispatchQueue
!= NULL
) {
3273 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
3274 } else if (targetPrivate
->rls
!= NULL
) {
3279 n
= CFArrayGetCount(targetPrivate
->rlList
);
3280 for (i
= 0; i
< n
; i
+= 3) {
3281 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
3282 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
3284 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, TRUE
);
3288 CFRelease(targetPrivate
->onDemandServer
);
3289 targetPrivate
->onDemandServer
= NULL
;
3292 if (targetPrivate
->onDemandServiceID
!= NULL
) {
3293 CFRelease(targetPrivate
->onDemandServiceID
);
3294 targetPrivate
->onDemandServiceID
= NULL
;
3298 if (onDemandStatus
!= kSCNetworkConnectionConnected
) {
3300 * if we have a VPN configuration matching the name *and* we need to
3301 * bring the VPN up. Combine our flags with those of the VPN server.
3303 if (targetPrivate
->onDemandServer
== NULL
) {
3304 CFMutableDictionaryRef options
;
3306 options
= CFDictionaryCreateMutable(NULL
,
3308 &kCFTypeDictionaryKeyCallBacks
,
3309 &kCFTypeDictionaryValueCallBacks
);
3310 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, onDemandRemoteAddress
);
3311 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandByPass
, kCFBooleanTrue
);
3312 targetPrivate
->onDemandServer
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
3315 if (targetPrivate
->scheduled
) {
3316 SCNetworkReachabilityContext context
= { 0, NULL
, CFRetain
, CFRelease
, CFCopyDescription
};
3318 context
.info
= (void *)target
;
3319 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
,
3320 __SCNetworkReachabilityOnDemandCheckCallback
,
3323 // schedule server reachability to match that of the target
3324 if (targetPrivate
->dispatchQueue
!= NULL
) {
3325 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, targetPrivate
->dispatchQueue
, TRUE
);
3330 n
= CFArrayGetCount(targetPrivate
->rlList
);
3331 for (i
= 0; i
< n
; i
+= 3) {
3332 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
3333 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
3335 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, NULL
, TRUE
);
3341 ok
= SCNetworkReachabilityGetFlags(targetPrivate
->onDemandServer
, flags
);
3342 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status * = 0x%08x"),
3343 targetPrivate
->log_prefix
,
3345 if (ok
&& (*flags
& kSCNetworkReachabilityFlagsReachable
)) {
3346 if (!(*flags
& kSCNetworkReachabilityFlagsTransientConnection
)) {
3347 // start clean if not already layered on a transient network
3350 *flags
|= kSCNetworkReachabilityFlagsReachable
;
3351 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
3352 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
3353 *flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
3356 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service * = %@"),
3357 targetPrivate
->log_prefix
,
3359 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after OnDemand connect)"),
3360 targetPrivate
->log_prefix
);
3367 if (onDemandRemoteAddress
!= NULL
) {
3368 if (targetPrivate
->onDemandRemoteAddress
== NULL
) {
3369 targetPrivate
->onDemandRemoteAddress
= onDemandRemoteAddress
;
3371 CFRelease(onDemandRemoteAddress
);
3375 if (onDemandServiceID
!= NULL
) {
3376 if (targetPrivate
->onDemandServiceID
== NULL
) {
3377 targetPrivate
->onDemandServiceID
= onDemandServiceID
;
3379 CFRelease(onDemandServiceID
);
3389 #pragma mark Reachability Flags
3392 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3395 struct addrinfo
*res
;
3400 reply_callback(int32_t status
, struct addrinfo
*res
, void *context
)
3402 reply_info
*reply
= (reply_info
*)context
;
3404 reply
->status
= status
;
3411 getaddrinfo_interface_sync(const char *nodename
,
3412 const char *servname
,
3413 const struct addrinfo
*hints
,
3414 const char *interface
,
3415 struct addrinfo
**res
)
3418 reply_info reply
= { NETDB_SUCCESS
, NULL
};
3420 mp
= _getaddrinfo_interface_async_call(nodename
,
3426 if (mp
== MACH_PORT_NULL
) {
3434 mach_msg_empty_rcv_t msg
;
3436 kern_return_t m_status
;
3438 m_status
= mach_msg(&m_reply
.msg
.header
, /* msg */
3439 MACH_RCV_MSG
, /* options */
3441 sizeof(m_reply
), /* rcv_size */
3443 MACH_MSG_TIMEOUT_NONE
, /* timeout */
3444 MACH_PORT_NULL
); /* notify */
3445 if (m_status
!= KERN_SUCCESS
) {
3449 g_status
= getaddrinfo_async_handle_reply((void *)m_reply
.buf
);
3450 if (g_status
!= 0) {
3451 if (reply
.res
!= NULL
) {
3452 freeaddrinfo(reply
.res
);
3458 if ((reply
.res
!= NULL
) || (reply
.status
!= NETDB_SUCCESS
)) {
3459 // if we have a reply or an error
3463 // if the request is not complete and needs to be re-queued
3467 return reply
.status
;
3469 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3473 __SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef store_info
,
3474 SCNetworkReachabilityRef target
,
3475 ReachabilityInfo
*reach_info
,
3478 CFMutableArrayRef addresses
= NULL
;
3479 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3480 ReachabilityInfo my_info
= NOT_REACHABLE
;
3483 *reach_info
= NOT_REACHABLE
;
3485 if (!isA_SCNetworkReachability(target
)) {
3486 _SCErrorSet(kSCStatusInvalidArgument
);
3490 switch (targetPrivate
->type
) {
3491 case reachabilityTypeAddress
:
3492 case reachabilityTypeAddressPair
: {
3494 * Check "local" address
3496 if (targetPrivate
->localAddress
!= NULL
) {
3498 * Check "local" address
3500 ok
= checkAddress(store_info
,
3501 targetPrivate
->localAddress
,
3502 targetPrivate
->if_index
,
3504 targetPrivate
->log_prefix
);
3506 goto error
; /* not today */
3509 if (!(my_info
.flags
& kSCNetworkReachabilityFlagsIsLocalAddress
)) {
3510 goto error
; /* not reachable, non-"local" address */
3515 * Check "remote" address
3517 if (targetPrivate
->remoteAddress
!= NULL
) {
3519 * in cases where we have "local" and "remote" addresses
3520 * we need to re-initialize the to-be-returned flags.
3522 my_info
= NOT_REACHABLE
;
3525 * Check "remote" address
3527 ok
= checkAddress(store_info
,
3528 targetPrivate
->remoteAddress
,
3529 targetPrivate
->if_index
,
3531 targetPrivate
->log_prefix
);
3533 goto error
; /* not today */
3541 case reachabilityTypeName
: {
3542 struct timeval dnsQueryStart
;
3544 SCNetworkReachabilityFlags ns_flags
;
3545 struct addrinfo
*res
;
3547 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
3548 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
3549 /* if resolved or an error had been detected */
3551 /* if not an async request */
3552 goto checkResolvedAddress
;
3553 } else if ((targetPrivate
->dnsPort
== NULL
) && !targetPrivate
->needResolve
) {
3554 /* if async request, no query active, and no query needed */
3555 goto checkResolvedAddress
;
3559 if (!targetPrivate
->onDemandBypass
) {
3563 * before we attempt our initial DNS query, check if there is
3564 * an OnDemand configuration that we should be using.
3566 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, FALSE
, &my_info
.flags
);
3568 /* if OnDemand connection is needed */
3573 /* check the reachability of the DNS servers */
3574 ok
= _SC_R_checkResolverReachability(store_info
,
3576 &targetPrivate
->haveDNS
,
3577 targetPrivate
->name
,
3578 targetPrivate
->serv
,
3579 targetPrivate
->if_index
,
3580 targetPrivate
->log_prefix
);
3582 /* if we could not get DNS server info */
3583 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
3584 targetPrivate
->log_prefix
);
3586 } else if (rankReachability(ns_flags
) < 2) {
3588 * if DNS servers are not (or are no longer) reachable, set
3589 * flags based on the availability of configured (but not
3593 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
3594 targetPrivate
->log_prefix
);
3596 ok
= checkAddress(store_info
,
3598 targetPrivate
->if_index
,
3600 targetPrivate
->log_prefix
);
3602 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sNo available networks"),
3603 targetPrivate
->log_prefix
);
3607 if (async
&& targetPrivate
->scheduled
) {
3609 * return "host not found", set flags appropriately,
3610 * and schedule notification.
3612 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME
,
3615 my_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
3617 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sno DNS servers are reachable"),
3618 targetPrivate
->log_prefix
);
3619 __SCNetworkReachabilityPerform(target
);
3625 /* for async requests we return the last known status */
3626 my_info
= targetPrivate
->info
;
3628 if (targetPrivate
->dnsPort
!= NULL
) {
3629 /* if request already in progress */
3630 SCLog(_sc_debug
, LOG_INFO
,
3631 CFSTR("%swaiting for DNS reply"),
3632 targetPrivate
->log_prefix
);
3633 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
3634 /* updated reachability based on the previous reply */
3635 goto checkResolvedAddress
;
3640 SCLog(_sc_debug
, LOG_INFO
,
3641 CFSTR("%sstart DNS query for %s%s%s%s%s"),
3642 targetPrivate
->log_prefix
,
3643 targetPrivate
->name
!= NULL
? "name = " : "",
3644 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
3645 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
3646 targetPrivate
->serv
!= NULL
? "serv = " : "",
3647 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
3650 * initiate an async DNS query
3652 if (!startAsyncDNSQuery(target
)) {
3653 /* if we could not initiate the request, process error */
3654 goto checkResolvedAddress
;
3657 /* request initiated */
3661 SCLog(_sc_debug
, LOG_INFO
,
3662 CFSTR("%scheck DNS for %s%s%s%s%s"),
3663 targetPrivate
->log_prefix
,
3664 targetPrivate
->name
!= NULL
? "name = " : "",
3665 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
3666 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
3667 targetPrivate
->serv
!= NULL
? "serv = " : "",
3668 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
3671 * OK, all of the DNS name servers are available. Let's
3672 * resolve the nodename into an address.
3675 (void) gettimeofday(&dnsQueryStart
, NULL
);
3678 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3679 if (targetPrivate
->if_index
== 0) {
3680 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3681 error
= getaddrinfo(targetPrivate
->name
,
3682 targetPrivate
->serv
,
3683 &targetPrivate
->hints
,
3685 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3687 error
= getaddrinfo_interface_sync(targetPrivate
->name
,
3688 targetPrivate
->serv
,
3689 &targetPrivate
->hints
,
3690 targetPrivate
->if_name
,
3693 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3695 __log_query_time(target
,
3696 ((error
== 0) && (res
!= NULL
)), // if successful query
3698 &dnsQueryStart
); // start time
3700 __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
);
3702 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
3704 checkResolvedAddress
:
3707 * We first assume that the requested host is NOT available.
3708 * Then, check each address for accessibility and return the
3709 * best status available.
3711 my_info
= NOT_REACHABLE
;
3713 if (isA_CFArray(addresses
)) {
3715 CFIndex n
= CFArrayGetCount(addresses
);
3717 for (i
= 0; i
< n
; i
++) {
3718 ReachabilityInfo ns_info
= NOT_REACHABLE
;
3719 struct sockaddr
*sa
;
3721 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
3723 ok
= checkAddress(store_info
,
3725 targetPrivate
->if_index
,
3727 targetPrivate
->log_prefix
);
3729 goto error
; /* not today */
3732 if (rankReachability(ns_info
.flags
) > rankReachability(my_info
.flags
)) {
3733 /* return the best case result */
3735 if (rankReachability(my_info
.flags
) == 2) {
3742 if ((error
== EAI_NONAME
)
3743 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
3744 || (error
== EAI_NODATA
)
3748 * the target host name could not be resolved
3750 if (!targetPrivate
->onDemandBypass
) {
3754 * our initial DNS query failed, check again to see if there
3755 * there is an OnDemand configuration that we should be using.
3757 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, TRUE
, &my_info
.flags
);
3759 /* if OnDemand connection is needed */
3764 if (!targetPrivate
->haveDNS
) {
3766 * No DNS servers are defined. Set flags based on
3767 * the availability of configured (but not active)
3770 ok
= checkAddress(store_info
,
3772 targetPrivate
->if_index
,
3774 targetPrivate
->log_prefix
);
3776 goto error
; /* not today */
3779 if ((my_info
.flags
& kSCNetworkReachabilityFlagsReachable
) &&
3780 (my_info
.flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
3782 * Since we might pick up a set of DNS servers when this connection
3783 * is established, don't reply with a "HOST NOT FOUND" error just yet.
3788 /* Host not found, not reachable! */
3789 my_info
= NOT_REACHABLE
;
3800 *reach_info
= my_info
;
3804 if (addresses
!= NULL
) CFRelease(addresses
);
3810 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
3811 SCNetworkReachabilityFlags
*flags
)
3814 ReachabilityStoreInfo store_info
;
3815 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3817 if (!isA_SCNetworkReachability(target
)) {
3818 _SCErrorSet(kSCStatusInvalidArgument
);
3822 initReachabilityStoreInfo(&store_info
);
3823 pthread_mutex_lock(&targetPrivate
->lock
);
3825 if (targetPrivate
->scheduled
) {
3826 // if being watched, return the last known (and what should be current) status
3827 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
3832 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
);
3833 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
3837 pthread_mutex_unlock(&targetPrivate
->lock
);
3838 freeReachabilityStoreInfo(&store_info
);
3844 #pragma mark Notifications
3848 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
3851 CFMutableArrayRef keys
;
3852 CFStringRef pattern
;
3853 CFMutableArrayRef patterns
;
3855 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3856 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3858 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
3859 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3860 kSCDynamicStoreDomainSetup
,
3862 CFArrayAppendValue(keys
, key
);
3865 // State:/Network/Global/DNS
3866 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3867 kSCDynamicStoreDomainState
,
3869 CFArrayAppendValue(keys
, key
);
3872 // State:/Network/Global/IPv4 (default route)
3873 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3874 kSCDynamicStoreDomainState
,
3876 CFArrayAppendValue(keys
, key
);
3879 // State:/Network/Global/OnDemand
3880 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3881 kSCDynamicStoreDomainState
,
3883 CFArrayAppendValue(keys
, key
);
3886 // Setup: per-service Interface info
3887 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3888 kSCDynamicStoreDomainSetup
,
3890 kSCEntNetInterface
);
3891 CFArrayAppendValue(patterns
, pattern
);
3894 // per-service IPv4 info
3895 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3896 kSCDynamicStoreDomainSetup
,
3899 CFArrayAppendValue(patterns
, pattern
);
3901 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3902 kSCDynamicStoreDomainState
,
3905 CFArrayAppendValue(patterns
, pattern
);
3908 // per-service IPv6 info
3909 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3910 kSCDynamicStoreDomainSetup
,
3913 CFArrayAppendValue(patterns
, pattern
);
3915 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3916 kSCDynamicStoreDomainState
,
3919 CFArrayAppendValue(patterns
, pattern
);
3922 // per-service PPP info (for existence, kSCPropNetPPPDialOnDemand, kSCPropNetPPPStatus)
3923 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3924 kSCDynamicStoreDomainSetup
,
3927 CFArrayAppendValue(patterns
, pattern
);
3929 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3930 kSCDynamicStoreDomainState
,
3933 CFArrayAppendValue(patterns
, pattern
);
3936 #if !TARGET_IPHONE_SIMULATOR
3937 // per-service VPN info (for existence, kSCPropNetVPNStatus)
3938 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3939 kSCDynamicStoreDomainSetup
,
3942 CFArrayAppendValue(patterns
, pattern
);
3944 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3945 kSCDynamicStoreDomainState
,
3948 CFArrayAppendValue(patterns
, pattern
);
3950 #endif // !TARGET_IPHONE_SIMULATOR
3952 // per-service IPSec info (for existence, kSCPropNetIPSecStatus)
3953 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3954 kSCDynamicStoreDomainSetup
,
3957 CFArrayAppendValue(patterns
, pattern
);
3959 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3960 kSCDynamicStoreDomainState
,
3963 CFArrayAppendValue(patterns
, pattern
);
3966 #if !TARGET_OS_IPHONE
3967 // State: Power Management Capabilities
3968 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
3969 kSCDynamicStoreDomainState
,
3970 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
3971 CFArrayAppendValue(keys
, key
);
3973 #endif // TARGET_OS_IPHONE
3976 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
3978 CFRelease(patterns
);
3985 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store
,
3986 CFArrayRef changedKeys
,
3989 Boolean dnsConfigChanged
= FALSE
;
3992 CFIndex nChanges
= CFArrayGetCount(changedKeys
);
3994 #if !TARGET_OS_IPHONE
3995 Boolean powerStatusChanged
= FALSE
;
3996 #endif // !TARGET_OS_IPHONE
3997 ReachabilityStoreInfo store_info
;
3998 const void * targets_q
[N_QUICK
];
3999 const void ** targets
= targets_q
;
4001 if (nChanges
== 0) {
4006 pthread_mutex_lock(&hn_lock
);
4008 nTargets
= (hn_targets
!= NULL
) ? CFSetGetCount(hn_targets
) : 0;
4009 if (nTargets
== 0) {
4010 /* if no addresses being monitored */
4014 #if !TARGET_OS_IPHONE
4015 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
4016 kSCDynamicStoreDomainState
,
4017 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
4018 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
4021 num
= SCDynamicStoreCopyValue(store
, key
);
4023 if (isA_CFNumber(num
) &&
4024 CFNumberGetValue(num
, kCFNumberSInt32Type
, &power_capabilities
)) {
4025 powerStatusChanged
= TRUE
;
4032 #endif // !TARGET_OS_IPHONE
4034 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4035 kSCDynamicStoreDomainState
,
4037 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
4038 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
4046 #if !TARGET_OS_IPHONE
4048 if (powerStatusChanged
) {
4052 #endif // !TARGET_OS_IPHONE
4055 if (dnsConfigChanged
) {
4066 case 0 : str
= ""; break;
4067 case NET
: str
= "network "; break;
4068 case DNS
: str
= "DNS "; break;
4069 case DNS
|NET
: str
= "network and DNS "; break;
4070 #if !TARGET_OS_IPHONE
4071 case PWR
: str
= "power "; break;
4072 case PWR
|NET
: str
= "network and power "; break;
4073 case PWR
|DNS
: str
= "DNS and power "; break;
4074 case PWR
|DNS
|NET
: str
= "network, DNS, and power "; break;
4075 #endif // !TARGET_OS_IPHONE
4076 default : str
= "??? ";
4079 SCLog(TRUE
, LOG_INFO
, CFSTR("process %sconfiguration change"), str
);
4082 initReachabilityStoreInfo(&store_info
);
4084 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
4085 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
4086 CFSetGetValues(hn_targets
, targets
);
4087 for (i
= 0; i
< nTargets
; i
++) {
4088 SCNetworkReachabilityRef target
= targets
[i
];
4089 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4091 pthread_mutex_lock(&targetPrivate
->lock
);
4093 if (targetPrivate
->type
== reachabilityTypeName
) {
4094 Boolean dnsChanged
= dnsConfigChanged
;
4098 * if the DNS configuration didn't change we still need to
4099 * check that the DNS servers are accessible.
4101 SCNetworkReachabilityFlags ns_flags
;
4104 /* check the reachability of the DNS servers */
4105 ok
= updateReachabilityStoreInfo(&store_info
, &store
, AF_UNSPEC
);
4107 ok
= _SC_R_checkResolverReachability(&store_info
,
4109 &targetPrivate
->haveDNS
,
4110 targetPrivate
->name
,
4111 targetPrivate
->serv
,
4112 targetPrivate
->if_index
,
4113 targetPrivate
->log_prefix
);
4117 /* if we could not get DNS server info */
4118 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
4119 targetPrivate
->log_prefix
);
4121 } else if (rankReachability(ns_flags
) < 2) {
4123 * if DNS servers are not (or are no longer) reachable, set
4124 * flags based on the availability of configured (but not
4127 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
4128 targetPrivate
->log_prefix
);
4134 if (targetPrivate
->dnsPort
!= NULL
) {
4135 mach_port_t mp
= CFMachPortGetPort(targetPrivate
->dnsPort
);
4137 /* cancel the outstanding DNS query */
4138 SCLog(_sc_debug
, LOG_INFO
,
4139 CFSTR("%scancel DNS query for %s%s%s%s%s"),
4140 targetPrivate
->log_prefix
,
4141 targetPrivate
->name
!= NULL
? "name = " : "",
4142 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
4143 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
4144 targetPrivate
->serv
!= NULL
? "serv = " : "",
4145 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
4146 dequeueAsyncDNSQuery(target
);
4147 getaddrinfo_async_cancel(mp
);
4150 /* schedule request to resolve the name again */
4151 targetPrivate
->needResolve
= TRUE
;
4155 __SCNetworkReachabilityPerform(target
);
4157 pthread_mutex_unlock(&targetPrivate
->lock
);
4159 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
4161 freeReachabilityStoreInfo(&store_info
);
4165 pthread_mutex_unlock(&hn_lock
);
4170 #if !TARGET_OS_IPHONE
4171 static __inline__ Boolean
4172 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities
)
4175 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU \
4176 | kIOPMSystemPowerStateCapabilityNetwork \
4177 | kIOPMSystemPowerStateCapabilityDisk)
4179 if ((power_capabilities
& POWER_CAPABILITIES_NEED
) != POWER_CAPABILITIES_NEED
) {
4181 * we're not awake (from a networking point of view) unless we
4182 * have the CPU, disk, *and* network.
4187 if ((power_capabilities
& kIOPMSytemPowerStateCapabilitiesMask
) == POWER_CAPABILITIES_NEED
) {
4189 * if all we have is the CPU, disk, and network than this must
4190 * be a "maintenance" wake.
4197 #endif // !TARGET_OS_IPHONE
4201 rlsPerform(void *info
)
4204 void (*context_release
)(const void *);
4205 Boolean defer
= FALSE
;
4207 ReachabilityInfo reach_info
= NOT_REACHABLE
;
4208 SCNetworkReachabilityCallBack rlsFunction
;
4209 ReachabilityStoreInfo store_info
;
4210 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
4211 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4213 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%schecking target reachability"),
4214 targetPrivate
->log_prefix
);
4217 pthread_mutex_lock(&targetPrivate
->lock
);
4219 if (!targetPrivate
->scheduled
) {
4220 // if not currently scheduled
4221 pthread_mutex_unlock(&targetPrivate
->lock
);
4225 /* update reachability, notify if status changed */
4226 initReachabilityStoreInfo(&store_info
);
4227 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
);
4228 freeReachabilityStoreInfo(&store_info
);
4230 /* if reachability status not available */
4231 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%flags not available"),
4232 targetPrivate
->log_prefix
);
4233 reach_info
= NOT_REACHABLE
;
4236 #if !TARGET_OS_IPHONE
4238 * We want to defer the notification if this is a maintenance wake *and*
4239 * the reachability flags that we would be reporting to the application
4240 * are better than those that we last reported.
4242 if (!systemIsAwake(power_capabilities
)) {
4243 /* if this is a maintenace wake */
4244 reach_info
.sleeping
= TRUE
;
4246 if (rankReachability(reach_info
.flags
) >= rankReachability(targetPrivate
->info
.flags
)) {
4248 * don't report the change if the new reachability flags are
4249 * the same or "better"
4252 } else if (__reach_equal(&targetPrivate
->last_notify
, &reach_info
)) {
4253 /* if we have already posted this change */
4257 #endif // !TARGET_OS_IPHONE
4259 if (__reach_equal(&targetPrivate
->info
, &reach_info
)) {
4260 SCLog(_sc_debug
, LOG_INFO
,
4261 CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"),
4262 targetPrivate
->log_prefix
,
4264 reach_info
.if_index
,
4265 reach_info
.sleeping
? "*" : "");
4266 pthread_mutex_unlock(&targetPrivate
->lock
);
4270 SCLog(_sc_debug
, LOG_INFO
,
4271 CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s"),
4272 targetPrivate
->log_prefix
,
4273 targetPrivate
->info
.flags
,
4274 targetPrivate
->info
.if_index
,
4275 targetPrivate
->info
.sleeping
? "*" : "",
4277 reach_info
.if_index
,
4278 reach_info
.sleeping
? "*" : "",
4279 defer
? ", deferred" : "");
4281 /* update flags / interface */
4282 targetPrivate
->info
= reach_info
;
4284 /* as needed, defer the notification */
4286 pthread_mutex_unlock(&targetPrivate
->lock
);
4290 /* save last notification info */
4291 targetPrivate
->last_notify
= reach_info
;
4294 rlsFunction
= targetPrivate
->rlsFunction
;
4295 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
4296 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
4297 context_release
= targetPrivate
->rlsContext
.release
;
4299 context_info
= targetPrivate
->rlsContext
.info
;
4300 context_release
= NULL
;
4303 pthread_mutex_unlock(&targetPrivate
->lock
);
4305 if (rlsFunction
!= NULL
) {
4306 (*rlsFunction
)(target
,
4307 reach_info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
,
4311 if (context_release
!= NULL
) {
4312 (*context_release
)(context_info
);
4320 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
4321 SCNetworkReachabilityCallBack callout
,
4322 SCNetworkReachabilityContext
*context
)
4324 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4326 pthread_mutex_lock(&targetPrivate
->lock
);
4328 if (targetPrivate
->rlsContext
.release
!= NULL
) {
4329 /* let go of the current context */
4330 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
4333 targetPrivate
->rlsFunction
= callout
;
4334 targetPrivate
->rlsContext
.info
= NULL
;
4335 targetPrivate
->rlsContext
.retain
= NULL
;
4336 targetPrivate
->rlsContext
.release
= NULL
;
4337 targetPrivate
->rlsContext
.copyDescription
= NULL
;
4339 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
4340 if (context
->retain
!= NULL
) {
4341 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
4345 pthread_mutex_unlock(&targetPrivate
->lock
);
4352 reachRLSCopyDescription(const void *info
)
4354 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
4356 return CFStringCreateWithFormat(NULL
,
4358 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
4364 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
4365 CFRunLoopRef runLoop
,
4366 CFStringRef runLoopMode
,
4367 dispatch_queue_t queue
,
4370 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4371 Boolean init
= FALSE
;
4375 pthread_mutex_lock(&hn_lock
);
4377 pthread_mutex_lock(&targetPrivate
->lock
);
4379 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
4380 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
4381 _SCErrorSet(kSCStatusInvalidArgument
);
4385 /* schedule the SCNetworkReachability run loop source */
4387 if (!onDemand
&& (hn_store
== NULL
)) {
4389 * if we are not monitoring any hosts, start watching
4391 if (!dns_configuration_watch()) {
4393 _SCErrorSet(kSCStatusFailed
);
4397 hn_store
= SCDynamicStoreCreate(NULL
,
4398 CFSTR("SCNetworkReachability"),
4399 __SCNetworkReachabilityHandleChanges
,
4401 if (hn_store
== NULL
) {
4402 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed"));
4406 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
4408 hn_dispatchQueue
= dispatch_queue_create("com.apple.SCNetworkReachabilty.network_changes", NULL
);
4409 if (hn_dispatchQueue
== NULL
) {
4410 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed"));
4411 _SCErrorSet(kSCStatusFailed
);
4412 CFRelease(hn_store
);
4416 CFRetain(hn_store
); // Note: will be released when the dispatch queue is released
4417 dispatch_set_context(hn_dispatchQueue
, (void *)hn_store
);
4418 dispatch_set_finalizer_f(hn_dispatchQueue
, (dispatch_function_t
)CFRelease
);
4420 ok
= SCDynamicStoreSetDispatchQueue(hn_store
, hn_dispatchQueue
);
4422 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
4423 dispatch_release(hn_dispatchQueue
);
4424 hn_dispatchQueue
= NULL
;
4425 CFRelease(hn_store
);
4429 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
4432 if (!targetPrivate
->scheduled
) {
4433 CFRunLoopSourceContext context
= { 0 // version
4434 , (void *)target
// info
4435 , CFRetain
// retain
4436 , CFRelease
// release
4437 , reachRLSCopyDescription
// copyDescription
4442 , rlsPerform
// perform
4445 if (runLoop
!= NULL
) {
4446 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
4447 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4450 targetPrivate
->scheduled
= TRUE
;
4451 if (targetPrivate
->type
== reachabilityTypeName
) {
4452 targetPrivate
->needResolve
= TRUE
;
4457 if (queue
!= NULL
) {
4458 targetPrivate
->dispatchQueue
= queue
;
4459 dispatch_retain(targetPrivate
->dispatchQueue
);
4461 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
4463 * if we do not already have host notifications scheduled with
4464 * this runLoop / runLoopMode
4466 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
4468 if (targetPrivate
->dnsRLS
!= NULL
) {
4469 /* if we have an active async DNS query too */
4470 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
4474 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
4477 CFSetAddValue(hn_targets
, target
);
4480 ReachabilityInfo reach_info
= NOT_REACHABLE
;
4481 ReachabilityStoreInfo store_info
;
4484 * if we have yet to schedule SC notifications for this address
4485 * - initialize current reachability status
4487 initReachabilityStoreInfo(&store_info
);
4488 if (__SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
)) {
4490 * if reachability status available
4492 * - schedule notification to report status via callback
4494 targetPrivate
->info
= reach_info
;
4495 __SCNetworkReachabilityPerform(target
);
4497 /* if reachability status not available, async lookup started */
4498 targetPrivate
->info
= NOT_REACHABLE
;
4500 freeReachabilityStoreInfo(&store_info
);
4503 if (targetPrivate
->onDemandServer
!= NULL
) {
4504 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, queue
, TRUE
);
4511 pthread_mutex_unlock(&targetPrivate
->lock
);
4513 pthread_mutex_unlock(&hn_lock
);
4520 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
4521 CFRunLoopRef runLoop
,
4522 CFStringRef runLoopMode
,
4525 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4530 pthread_mutex_lock(&hn_lock
);
4532 pthread_mutex_lock(&targetPrivate
->lock
);
4534 if (((runLoop
== NULL
) && (targetPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
4535 ((runLoop
!= NULL
) && (targetPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are not)
4536 _SCErrorSet(kSCStatusInvalidArgument
);
4540 if (!targetPrivate
->scheduled
) {
4541 // if not currently scheduled
4542 _SCErrorSet(kSCStatusInvalidArgument
);
4546 // first, unschedule the target specific sources
4547 if (targetPrivate
->dispatchQueue
!= NULL
) {
4548 if (targetPrivate
->onDemandServer
!= NULL
) {
4549 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
4552 if (!_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
4553 // if not currently scheduled
4554 _SCErrorSet(kSCStatusInvalidArgument
);
4558 if (targetPrivate
->onDemandServer
!= NULL
) {
4559 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, TRUE
);
4562 n
= CFArrayGetCount(targetPrivate
->rlList
);
4563 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
4564 // if target is no longer scheduled for this runLoop / runLoopMode
4565 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
4567 if (targetPrivate
->dnsRLS
!= NULL
) {
4568 // if we have an active async DNS query too
4569 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
4573 // if *all* notifications have been unscheduled
4574 CFRelease(targetPrivate
->rlList
);
4575 targetPrivate
->rlList
= NULL
;
4576 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
4577 CFRelease(targetPrivate
->rls
);
4578 targetPrivate
->rls
= NULL
;
4584 // if *all* notifications have been unscheduled
4585 targetPrivate
->scheduled
= FALSE
;
4588 CFSetRemoveValue(hn_targets
, target
); // cleanup notification resources
4591 if (targetPrivate
->dnsPort
!= NULL
) {
4592 mach_port_t mp
= CFMachPortGetPort(targetPrivate
->dnsPort
);
4594 // if we have an active async DNS query
4595 dequeueAsyncDNSQuery(target
);
4596 getaddrinfo_async_cancel(mp
);
4600 if (runLoop
== NULL
) {
4601 dispatch_release(targetPrivate
->dispatchQueue
);
4602 targetPrivate
->dispatchQueue
= NULL
;
4605 n
= CFSetGetCount(hn_targets
);
4607 // if we are no longer monitoring any targets
4608 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
4609 dispatch_release(hn_dispatchQueue
);
4610 hn_dispatchQueue
= NULL
;
4611 CFRelease(hn_store
);
4613 CFRelease(hn_targets
);
4617 * until we start monitoring again, ensure that
4618 * any resources associated with tracking the
4619 * DNS configuration have been released.
4621 dns_configuration_unwatch();
4628 pthread_mutex_unlock(&targetPrivate
->lock
);
4630 pthread_mutex_unlock(&hn_lock
);
4636 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
4637 CFRunLoopRef runLoop
,
4638 CFStringRef runLoopMode
)
4640 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
4641 _SCErrorSet(kSCStatusInvalidArgument
);
4645 return __SCNetworkReachabilityScheduleWithRunLoop(target
, runLoop
, runLoopMode
, NULL
, FALSE
);
4649 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
4650 CFRunLoopRef runLoop
,
4651 CFStringRef runLoopMode
)
4653 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
4654 _SCErrorSet(kSCStatusInvalidArgument
);
4658 return __SCNetworkReachabilityUnscheduleFromRunLoop(target
, runLoop
, runLoopMode
, FALSE
);
4662 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
4663 dispatch_queue_t queue
)
4667 if (!isA_SCNetworkReachability(target
)) {
4668 _SCErrorSet(kSCStatusInvalidArgument
);
4672 if (queue
!= NULL
) {
4673 ok
= __SCNetworkReachabilityScheduleWithRunLoop(target
, NULL
, NULL
, queue
, FALSE
);
4675 ok
= __SCNetworkReachabilityUnscheduleFromRunLoop(target
, NULL
, NULL
, FALSE
);