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
91 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
94 _getaddrinfo_interface_async_call(const char *nodename
,
96 const struct addrinfo
*hints
,
97 const char *interface
,
98 getaddrinfo_async_callback callback
,
100 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
103 #define kSCNetworkReachabilityFlagsFirstResolvePending (1<<31)
109 typedef enum { NO
= 0, YES
, UNKNOWN
} lazyBoolean
;
113 reachabilityTypeAddress
,
114 reachabilityTypeAddressPair
,
119 // how long (minimum time, us) to wait before retrying DNS query after EAI_NONAME
120 #define EAI_NONAME_RETRY_DELAY_USEC 250000
122 // how long (maximum time, us) after DNS configuration change we accept EAI_NONAME
124 #define EAI_NONAME_RETRY_LIMIT_USEC 2500000
127 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
128 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
129 static void rlsPerform(void *info
);
133 __SCNetworkReachabilityScheduleWithRunLoop (SCNetworkReachabilityRef target
,
134 CFRunLoopRef runLoop
,
135 CFStringRef runLoopMode
,
136 dispatch_queue_t queue
,
140 __SCNetworkReachabilityUnscheduleFromRunLoop (SCNetworkReachabilityRef target
,
141 CFRunLoopRef runLoop
,
142 CFStringRef runLoopMode
,
147 SCNetworkReachabilityFlags flags
;
148 unsigned int if_index
;
155 /* base CFType information */
156 CFRuntimeBase cfBase
;
159 pthread_mutex_t lock
;
164 /* target host name */
167 struct addrinfo hints
;
169 CFArrayRef resolvedAddress
; /* CFArray[CFData] */
170 int resolvedAddressError
;
172 /* [scoped routing] interface constraints */
173 unsigned int if_index
;
174 char if_name
[IFNAMSIZ
];
176 /* local & remote addresses */
177 struct sockaddr
*localAddress
;
178 struct sockaddr
*remoteAddress
;
180 /* current reachability flags */
181 ReachabilityInfo info
;
182 ReachabilityInfo last_notify
;
184 /* run loop source, callout, context, rl scheduling info */
186 CFRunLoopSourceRef rls
;
187 SCNetworkReachabilityCallBack rlsFunction
;
188 SCNetworkReachabilityContext rlsContext
;
189 CFMutableArrayRef rlList
;
191 dispatch_queue_t dispatchQueue
; // SCNetworkReachabilitySetDispatchQueue
192 dispatch_queue_t asyncDNSQueue
;
193 dispatch_source_t asyncDNSSource
;
195 /* [async] DNS query info */
198 CFMachPortRef dnsPort
;
199 CFRunLoopSourceRef dnsRLS
;
200 struct timeval dnsQueryStart
;
201 struct timeval dnsQueryEnd
;
202 dispatch_source_t dnsRetry
; // != NULL if DNS retry request queued
203 int dnsRetryCount
; // number of retry attempts
205 /* [async] processing info */
206 struct timeval last_dns
;
209 Boolean onDemandBypass
;
210 CFStringRef onDemandName
;
211 CFStringRef onDemandRemoteAddress
;
212 SCNetworkReachabilityRef onDemandServer
;
213 CFStringRef onDemandServiceID
;
219 } SCNetworkReachabilityPrivate
, *SCNetworkReachabilityPrivateRef
;
222 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
225 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
227 "SCNetworkReachability", // className
230 __SCNetworkReachabilityDeallocate
, // dealloc
233 NULL
, // copyFormattingDesc
234 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
238 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
239 static const ReachabilityInfo NOT_REACHABLE
= { 0, 0, FALSE
};
240 static const ReachabilityInfo NOT_REPORTED
= { 0xFFFFFFFF, 0, FALSE
};
241 static int rtm_seq
= 0;
244 static const struct timeval TIME_ZERO
= { 0, 0 };
247 #if !TARGET_OS_IPHONE
249 * Power capabilities (sleep/wake)
251 static IOPMSystemPowerStateCapabilities power_capabilities
= kIOPMSytemPowerStateCapabilitiesMask
;
252 #endif // !TARGET_OS_IPHONE
256 * host "something has changed" notifications
259 static pthread_mutex_t hn_lock
= PTHREAD_MUTEX_INITIALIZER
;
260 static SCDynamicStoreRef hn_store
= NULL
;
261 static dispatch_queue_t hn_dispatchQueue
= NULL
;
262 static CFMutableSetRef hn_targets
= NULL
;
270 dns_config_t
*config
;
272 } dns_configuration_t
;
275 static pthread_mutex_t dns_lock
= PTHREAD_MUTEX_INITIALIZER
;
276 static dns_configuration_t
*dns_configuration
= NULL
;
277 static int dns_token
;
278 static Boolean dns_token_valid
= FALSE
;
281 static __inline__ CFTypeRef
282 isA_SCNetworkReachability(CFTypeRef obj
)
284 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
289 __dns_query_start(struct timeval
*dnsQueryStart
,
290 struct timeval
*dnsQueryEnd
)
292 (void) gettimeofday(dnsQueryStart
, NULL
);
293 *dnsQueryEnd
= TIME_ZERO
;
300 __dns_query_end(SCNetworkReachabilityRef target
,
303 struct timeval
*dnsQueryStart
,
304 struct timeval
*dnsQueryEnd
)
306 struct timeval dnsQueryElapsed
;
307 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
309 (void) gettimeofday(dnsQueryEnd
, NULL
);
315 if (dnsQueryStart
->tv_sec
== 0) {
319 timersub(dnsQueryEnd
, dnsQueryStart
, &dnsQueryElapsed
);
320 SCLog(TRUE
, LOG_INFO
,
321 CFSTR("%s%ssync DNS complete%s (query time = %d.%3.3d)"),
322 targetPrivate
->log_prefix
,
324 found
? "" : ", host not found",
325 dnsQueryElapsed
.tv_sec
,
326 dnsQueryElapsed
.tv_usec
/ 1000);
332 static __inline__ Boolean
333 __reach_equal(ReachabilityInfo
*r1
, ReachabilityInfo
*r2
)
335 if (r1
->flags
!= r2
->flags
) {
336 // if the reachability flags changed
340 if (r1
->if_index
!= r2
->if_index
) {
341 // if the target interface changed
345 if ((r1
->sleeping
!= r2
->sleeping
) && !r2
->sleeping
) {
346 // if our sleep/wake status changed and if we
347 // are no longer sleeping
356 SCDynamicStoreRef store
;
359 CFDictionaryRef dict
;
362 const void * keys_q
[N_QUICK
];
363 const void ** values
;
364 const void * values_q
[N_QUICK
];
365 } ReachabilityStoreInfo
, *ReachabilityStoreInfoRef
;
369 initReachabilityStoreInfo(ReachabilityStoreInfoRef store_info
)
371 bzero(store_info
, sizeof(ReachabilityStoreInfo
));
377 updateReachabilityStoreInfo(ReachabilityStoreInfoRef store_info
,
378 SCDynamicStoreRef
*storeP
,
379 sa_family_t sa_family
)
382 CFMutableArrayRef patterns
;
386 store_info
->entity
= NULL
;
389 store_info
->entity
= kSCEntNetIPv4
;
392 store_info
->entity
= kSCEntNetIPv6
;
398 if (store_info
->dict
!= NULL
) {
399 // if info already available
403 if (store_info
->store
== NULL
) {
404 store_info
->store
= (storeP
!= NULL
) ? *storeP
: NULL
;
405 if (store_info
->store
== NULL
) {
406 store_info
->store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
407 if (store_info
->store
== NULL
) {
408 SCLog(TRUE
, LOG_ERR
, CFSTR("updateReachabilityStoreInfo SCDynamicStoreCreate() failed"));
412 if (storeP
!= NULL
) {
413 /// pass back the allocated SCDynamicStoreRef
414 *storeP
= store_info
->store
;
417 store_info
->storeAdded
= TRUE
;
422 if (sa_family
== AF_UNSPEC
) {
423 // if the address family was not specified than
424 // all we wanted, for now, was to establish the
425 // SCDynamicStore session
429 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
431 // get info for IPv4 services
432 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
433 kSCDynamicStoreDomainSetup
,
436 CFArrayAppendValue(patterns
, pattern
);
438 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
439 kSCDynamicStoreDomainState
,
442 CFArrayAppendValue(patterns
, pattern
);
445 // get info for IPv6 services
446 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
447 kSCDynamicStoreDomainSetup
,
450 CFArrayAppendValue(patterns
, pattern
);
452 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
453 kSCDynamicStoreDomainState
,
456 CFArrayAppendValue(patterns
, pattern
);
459 // get info for PPP services
460 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
461 kSCDynamicStoreDomainSetup
,
464 CFArrayAppendValue(patterns
, pattern
);
466 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
467 kSCDynamicStoreDomainState
,
470 CFArrayAppendValue(patterns
, pattern
);
473 #if !TARGET_IPHONE_SIMULATOR
474 // get info for VPN services
475 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
476 kSCDynamicStoreDomainSetup
,
479 CFArrayAppendValue(patterns
, pattern
);
481 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
482 kSCDynamicStoreDomainState
,
485 CFArrayAppendValue(patterns
, pattern
);
487 #endif // !TARGET_IPHONE_SIMULATOR
489 // get info for IPSec services
490 // pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
491 // kSCDynamicStoreDomainSetup,
494 // CFArrayAppendValue(patterns, pattern);
495 // CFRelease(pattern);
496 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
497 kSCDynamicStoreDomainState
,
500 CFArrayAppendValue(patterns
, pattern
);
503 // get info to identify "available" services
504 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
505 kSCDynamicStoreDomainSetup
,
508 CFArrayAppendValue(patterns
, pattern
);
512 // get the SCDynamicStore info
513 store_info
->dict
= SCDynamicStoreCopyMultiple(store_info
->store
, NULL
, patterns
);
515 if (store_info
->dict
== NULL
) {
519 // and extract the keys/values for post-processing
520 store_info
->n
= CFDictionaryGetCount(store_info
->dict
);
521 if (store_info
->n
> 0) {
522 if (store_info
->n
<= (CFIndex
)(sizeof(store_info
->keys_q
) / sizeof(CFTypeRef
))) {
523 store_info
->keys
= store_info
->keys_q
;
524 store_info
->values
= store_info
->values_q
;
526 store_info
->keys
= CFAllocatorAllocate(NULL
, store_info
->n
* sizeof(CFTypeRef
), 0);
527 store_info
->values
= CFAllocatorAllocate(NULL
, store_info
->n
* sizeof(CFTypeRef
), 0);
529 CFDictionaryGetKeysAndValues(store_info
->dict
,
539 freeReachabilityStoreInfo(ReachabilityStoreInfoRef store_info
)
541 if ((store_info
->n
> 0) && (store_info
->keys
!= store_info
->keys_q
)) {
542 CFAllocatorDeallocate(NULL
, store_info
->keys
);
543 store_info
->keys
= NULL
;
545 CFAllocatorDeallocate(NULL
, store_info
->values
);
546 store_info
->values
= NULL
;
549 if (store_info
->dict
!= NULL
) {
550 CFRelease(store_info
->dict
);
551 store_info
->dict
= NULL
;
554 if (store_info
->storeAdded
&& (store_info
->store
!= NULL
)) {
555 CFRelease(store_info
->store
);
556 store_info
->store
= NULL
;
564 updatePPPStatus(ReachabilityStoreInfoRef store_info
,
565 const struct sockaddr
*sa
,
567 SCNetworkReachabilityFlags
*flags
,
568 CFStringRef
*ppp_server
,
569 const char *log_prefix
)
573 int sc_status
= kSCStatusNoKey
;
575 if (!updateReachabilityStoreInfo(store_info
, NULL
, sa
->sa_family
)) {
576 return kSCStatusReachabilityUnknown
;
579 if (store_info
->n
<= 0) {
581 return kSCStatusNoKey
;
584 // look for the [PPP] service which matches the provided interface
586 ppp_if
= CFStringCreateWithCStringNoCopy(NULL
,
588 kCFStringEncodingASCII
,
591 for (i
=0; i
< store_info
->n
; i
++) {
592 CFArrayRef components
;
595 CFDictionaryRef p_setup
;
596 CFDictionaryRef p_state
;
599 CFStringRef service
= NULL
;
600 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
601 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
604 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
608 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
609 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
610 continue; // if not an active IPv4 or IPv6 entity
613 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
614 if (!isA_CFString(s_if
)) {
615 continue; // if no interface
618 if (!CFEqual(ppp_if
, s_if
)) {
619 continue; // if not this interface
622 // extract the service ID, get the PPP "state" entity for
623 // the "Status", and get the PPP "setup" entity for the
624 // the "DialOnDemand" flag
625 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
626 if (CFArrayGetCount(components
) != 5) {
627 CFRelease(components
);
630 service
= CFArrayGetValueAtIndex(components
, 3);
631 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
632 kSCDynamicStoreDomainState
,
635 p_state
= CFDictionaryGetValue(store_info
->dict
, key
);
637 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
638 kSCDynamicStoreDomainSetup
,
641 p_setup
= CFDictionaryGetValue(store_info
->dict
, key
);
643 CFRelease(components
);
645 // ensure that this is a PPP service
646 if (!isA_CFDictionary(p_state
)) {
650 sc_status
= kSCStatusOK
;
652 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
655 if (ppp_server
!= NULL
) {
656 *ppp_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
657 *ppp_server
= isA_CFString(*ppp_server
);
658 if (*ppp_server
!= NULL
) {
659 CFRetain(*ppp_server
);
664 if (!CFDictionaryGetValueIfPresent(p_state
,
666 (const void **)&num
) ||
667 !isA_CFNumber(num
) ||
668 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) {
671 switch (ppp_status
) {
673 // if we're really UP and RUNNING
676 // if we're effectively UP and RUNNING
679 // if we're not connected at all
680 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link idle"),
682 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
684 case PPP_STATERESERVED
:
685 // if we're not connected at all
686 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link idle, dial-on-traffic to connect"),
688 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
691 // if we're in the process of [dis]connecting
692 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link, connection in progress"),
694 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
698 // get PPP dial-on-traffic status
699 if (isA_CFDictionary(p_setup
) &&
700 CFDictionaryGetValueIfPresent(p_setup
,
701 kSCPropNetPPPDialOnDemand
,
702 (const void **)&num
) &&
704 CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
) &&
706 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
707 if (ppp_status
== PPP_IDLE
) {
708 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
722 updatePPPAvailable(ReachabilityStoreInfoRef store_info
,
723 const struct sockaddr
*sa
,
724 SCNetworkReachabilityFlags
*flags
,
725 const char *log_prefix
)
728 int sc_status
= kSCStatusNoKey
;
730 if (!updateReachabilityStoreInfo(store_info
,
732 (sa
!= NULL
) ? sa
->sa_family
: AF_INET
)) {
733 return kSCStatusReachabilityUnknown
;
736 if (store_info
->n
<= 0) {
738 return kSCStatusNoKey
;
741 // look for an available service which will provide connectivity
742 // for the requested address family.
744 for (i
= 0; i
< store_info
->n
; i
++) {
745 CFArrayRef components
;
746 Boolean found
= FALSE
;
748 CFDictionaryRef i_dict
;
750 CFDictionaryRef p_dict
;
752 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
753 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
755 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
759 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
760 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainSetup
)) {
761 continue; // if not an IPv4 or IPv6 entity
764 // extract service ID
765 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
766 if (CFArrayGetCount(components
) != 5) {
767 CFRelease(components
);
770 service
= CFArrayGetValueAtIndex(components
, 3);
772 // check for [non-VPN] PPP entity
773 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
774 kSCDynamicStoreDomainSetup
,
777 p_dict
= CFDictionaryGetValue(store_info
->dict
, p_key
);
780 i_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
781 kSCDynamicStoreDomainSetup
,
784 i_dict
= CFDictionaryGetValue(store_info
->dict
, i_key
);
787 if (isA_CFDictionary(p_dict
) &&
788 isA_CFDictionary(i_dict
) &&
789 CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) {
792 // we have a PPP service for this address family
795 *flags
|= kSCNetworkReachabilityFlagsReachable
;
796 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
797 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
799 // get PPP dial-on-traffic status
800 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
801 if (isA_CFNumber(num
)) {
804 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
806 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
812 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after connect)"),
814 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service = %@"),
821 CFRelease(components
);
824 sc_status
= kSCStatusOK
;
833 #if !TARGET_IPHONE_SIMULATOR
835 updateVPNStatus(ReachabilityStoreInfoRef store_info
,
836 const struct sockaddr
*sa
,
838 SCNetworkReachabilityFlags
*flags
,
839 CFStringRef
*vpn_server
,
840 const char *log_prefix
)
844 int sc_status
= kSCStatusNoKey
;
846 if (!updateReachabilityStoreInfo(store_info
, NULL
, sa
->sa_family
)) {
847 return kSCStatusReachabilityUnknown
;
850 if (store_info
->n
<= 0) {
852 return kSCStatusNoKey
;
855 // look for the [VPN] service which matches the provided interface
857 vpn_if
= CFStringCreateWithCStringNoCopy(NULL
,
859 kCFStringEncodingASCII
,
862 for (i
=0; i
< store_info
->n
; i
++) {
863 CFArrayRef components
;
866 CFDictionaryRef p_state
;
868 CFStringRef service
= NULL
;
869 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
870 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
873 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
877 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
878 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
879 continue; // if not an active IPv4 or IPv6 entity
882 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
883 if (!isA_CFString(s_if
)) {
884 continue; // if no interface
887 if (!CFEqual(vpn_if
, s_if
)) {
888 continue; // if not this interface
891 // extract the service ID and get the VPN "state" entity for
893 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
894 if (CFArrayGetCount(components
) != 5) {
895 CFRelease(components
);
898 service
= CFArrayGetValueAtIndex(components
, 3);
899 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
900 kSCDynamicStoreDomainState
,
903 p_state
= CFDictionaryGetValue(store_info
->dict
, key
);
905 CFRelease(components
);
907 // ensure that this is a VPN service
908 if (!isA_CFDictionary(p_state
)) {
912 sc_status
= kSCStatusOK
;
914 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
917 if (vpn_server
!= NULL
) {
918 *vpn_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
919 *vpn_server
= isA_CFString(*vpn_server
);
920 if (*vpn_server
!= NULL
) {
921 CFRetain(*vpn_server
);
926 if (!CFDictionaryGetValueIfPresent(p_state
,
928 (const void **)&num
) ||
929 !isA_CFNumber(num
) ||
930 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &vpn_status
)) {
933 #ifdef HAVE_VPN_STATUS
934 switch (vpn_status
) {
936 // if we're really UP and RUNNING
942 // if we're not connected at all
943 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s VPN link idle"),
945 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
948 // if we're in the process of [dis]connecting
949 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s VPN link, connection in progress"),
951 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
954 #endif // HAVE_VPN_STATUS
966 updateVPNAvailable(ReachabilityStoreInfoRef store_info
,
967 const struct sockaddr
*sa
,
968 SCNetworkReachabilityFlags
*flags
,
969 const char *log_prefix
)
972 int sc_status
= kSCStatusNoKey
;
974 if (!updateReachabilityStoreInfo(store_info
,
976 (sa
!= NULL
) ? sa
->sa_family
: AF_INET
)) {
977 return kSCStatusReachabilityUnknown
;
980 if (store_info
->n
<= 0) {
982 return kSCStatusNoKey
;
985 // look for an available service which will provide connectivity
986 // for the requested address family.
988 for (i
= 0; i
< store_info
->n
; i
++) {
989 CFArrayRef components
;
990 Boolean found
= FALSE
;
992 CFDictionaryRef i_dict
;
994 CFDictionaryRef p_dict
;
996 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
997 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
999 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
1003 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
1004 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainSetup
)) {
1005 continue; // if not an IPv4 or IPv6 entity
1008 // extract service ID
1009 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
1010 if (CFArrayGetCount(components
) != 5) {
1011 CFRelease(components
);
1014 service
= CFArrayGetValueAtIndex(components
, 3);
1016 // check for VPN entity
1017 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1018 kSCDynamicStoreDomainSetup
,
1021 p_dict
= CFDictionaryGetValue(store_info
->dict
, p_key
);
1024 i_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1025 kSCDynamicStoreDomainSetup
,
1027 kSCEntNetInterface
);
1028 i_dict
= CFDictionaryGetValue(store_info
->dict
, i_key
);
1031 if (isA_CFDictionary(p_dict
) &&
1032 isA_CFDictionary(i_dict
) &&
1033 CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) {
1034 // we have a VPN service for this address family
1037 *flags
|= kSCNetworkReachabilityFlagsReachable
;
1038 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1039 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1042 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after connect)"),
1044 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service = %@"),
1051 CFRelease(components
);
1054 sc_status
= kSCStatusOK
;
1061 #endif // !TARGET_IPHONE_SIMULATOR
1065 updateIPSecStatus(ReachabilityStoreInfoRef store_info
,
1066 const struct sockaddr
*sa
,
1067 const char *if_name
,
1068 SCNetworkReachabilityFlags
*flags
,
1069 CFStringRef
*ipsec_server
,
1070 const char *log_prefix
)
1073 CFStringRef ipsec_if
;
1074 int sc_status
= kSCStatusNoKey
;
1076 if (!updateReachabilityStoreInfo(store_info
, NULL
, sa
->sa_family
)) {
1077 return kSCStatusReachabilityUnknown
;
1080 if (store_info
->n
<= 0) {
1082 return kSCStatusNoKey
;
1085 // look for the [IPSec] service that matches the provided interface
1087 ipsec_if
= CFStringCreateWithCStringNoCopy(NULL
,
1089 kCFStringEncodingASCII
,
1092 for (i
=0; i
< store_info
->n
; i
++) {
1093 CFArrayRef components
;
1095 CFDictionaryRef i_state
;
1096 int32_t ipsec_status
;
1098 CFStringRef service
= NULL
;
1099 CFStringRef s_key
= (CFStringRef
) store_info
->keys
[i
];
1100 CFDictionaryRef s_dict
= (CFDictionaryRef
)store_info
->values
[i
];
1103 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
1107 if (!CFStringHasSuffix(s_key
, store_info
->entity
) ||
1108 !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) {
1109 continue; // if not an IPv4 or IPv6 entity
1112 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
1113 if (!isA_CFString(s_if
)) {
1114 continue; // if no interface
1117 if (!CFEqual(ipsec_if
, s_if
)) {
1118 continue; // if not this interface
1121 // extract the service ID, get the IPSec "state" entity for
1122 // the "Status", and get the IPSec "setup" entity to confirm
1123 // that we're looking at what we're expecting
1124 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
1125 if (CFArrayGetCount(components
) != 5) {
1126 CFRelease(components
);
1129 service
= CFArrayGetValueAtIndex(components
, 3);
1130 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
1131 kSCDynamicStoreDomainState
,
1134 i_state
= CFDictionaryGetValue(store_info
->dict
, key
);
1136 CFRelease(components
);
1138 // ensure that this is an IPSec service
1139 if (!isA_CFDictionary(i_state
)) {
1143 sc_status
= kSCStatusOK
;
1145 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1148 if (ipsec_server
!= NULL
) {
1149 *ipsec_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
1150 *ipsec_server
= isA_CFString(*ipsec_server
);
1151 if (*ipsec_server
!= NULL
) {
1152 CFRetain(*ipsec_server
);
1157 if (!CFDictionaryGetValueIfPresent(i_state
,
1158 kSCPropNetIPSecStatus
,
1159 (const void **)&num
) ||
1160 !isA_CFNumber(num
) ||
1161 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ipsec_status
)) {
1164 #ifdef HAVE_IPSEC_STATUS
1165 switch (ipsec_status
) {
1166 case IPSEC_RUNNING
:
1167 // if we're really UP and RUNNING
1170 // if we're not connected at all
1171 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s IPSec link idle"),
1173 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1176 // if we're in the process of [dis]connecting
1177 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s IPSec link, connection in progress"),
1179 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
1182 #endif // HAVE_IPSEC_STATUS
1187 CFRelease(ipsec_if
);
1195 #define ROUNDUP(a, size) \
1196 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
1198 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
1199 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
1200 sizeof(uint32_t)) :\
1204 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
1208 for (i
= 0; i
< RTAX_MAX
; i
++) {
1209 if (addrs
& (1 << i
)) {
1218 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
1224 struct sockaddr
*rti_info
[RTAX_MAX
];
1225 struct rt_msghdr
*rtm
;
1226 struct sockaddr_dl
*sdl
;
1227 } route_info
, *route_info_p
;
1232 * returns zero if route exists an data returned, EHOSTUNREACH
1233 * if no route, or errno for any other error.
1236 route_get(const struct sockaddr
*address
,
1237 unsigned int if_index
,
1242 pid_t pid
= getpid();
1244 struct sockaddr
*sa
;
1245 int32_t seq
= OSAtomicIncrement32Barrier(&rtm_seq
);
1246 #ifndef RTM_GET_SILENT
1247 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
1248 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
1249 int sosize
= 48 * 1024;
1252 bzero(info
, sizeof(*info
));
1254 info
->rtm
= (struct rt_msghdr
*)&info
->buf
;
1255 info
->rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
1256 info
->rtm
->rtm_version
= RTM_VERSION
;
1257 #ifdef RTM_GET_SILENT
1258 info
->rtm
->rtm_type
= RTM_GET_SILENT
;
1260 info
->rtm
->rtm_type
= RTM_GET
;
1262 info
->rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
1263 info
->rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
1264 info
->rtm
->rtm_pid
= pid
;
1265 info
->rtm
->rtm_seq
= seq
;
1267 if (if_index
!= 0) {
1268 info
->rtm
->rtm_flags
|= RTF_IFSCOPE
;
1269 info
->rtm
->rtm_index
= if_index
;
1272 switch (address
->sa_family
) {
1274 struct sockaddr_in6
*sin6
;
1276 sin6
= (struct sockaddr_in6
*)address
;
1277 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
1278 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
1279 (sin6
->sin6_scope_id
!= 0)) {
1280 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
1281 sin6
->sin6_scope_id
= 0;
1287 sa
= (struct sockaddr
*) (info
->rtm
+ 1);
1288 bcopy(address
, sa
, address
->sa_len
);
1289 n
= ROUNDUP(sa
->sa_len
, sizeof(uint32_t));
1290 info
->rtm
->rtm_msglen
+= n
;
1292 info
->sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
1293 info
->sdl
->sdl_family
= AF_LINK
;
1294 info
->sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
1295 n
= ROUNDUP(info
->sdl
->sdl_len
, sizeof(uint32_t));
1296 info
->rtm
->rtm_msglen
+= n
;
1298 #ifndef RTM_GET_SILENT
1299 pthread_mutex_lock(&lock
);
1301 rsock
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
);
1305 #ifndef RTM_GET_SILENT
1306 pthread_mutex_unlock(&lock
);
1308 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error
));
1312 if (ioctl(rsock
, FIONBIO
, &opt
) < 0) {
1316 #ifndef RTM_GET_SILENT
1317 pthread_mutex_unlock(&lock
);
1319 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl(FIONBIO) failed: %s"), strerror(error
));
1323 #ifndef RTM_GET_SILENT
1324 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
1328 pthread_mutex_unlock(&lock
);
1329 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error
));
1334 if (write(rsock
, &info
->buf
, info
->rtm
->rtm_msglen
) == -1) {
1338 #ifndef RTM_GET_SILENT
1339 pthread_mutex_unlock(&lock
);
1341 if (error
!= ESRCH
) {
1342 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(error
));
1345 return EHOSTUNREACH
;
1349 * Type, seq, pid identify our response.
1350 * Routing sockets are broadcasters on input.
1355 n
= read(rsock
, (void *)&info
->buf
, sizeof(info
->buf
));
1359 if (error
== EINTR
) {
1363 #ifndef RTM_GET_SILENT
1364 pthread_mutex_unlock(&lock
);
1366 SCLog(TRUE
, LOG_ERR
,
1367 CFSTR("SCNetworkReachability: routing socket"
1368 " read() failed: %s"), strerror(error
));
1371 if ((info
->rtm
->rtm_type
== RTM_GET
) &&
1372 (info
->rtm
->rtm_seq
== seq
) &&
1373 (info
->rtm
->rtm_pid
== pid
)) {
1379 #ifndef RTM_GET_SILENT
1380 pthread_mutex_unlock(&lock
);
1383 get_rtaddrs(info
->rtm
->rtm_addrs
, sa
, info
->rti_info
);
1385 //#define LOG_RTADDRS
1390 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), info
->rtm
->rtm_flags
);
1392 if ((info
->rti_info
[RTAX_NETMASK
] != NULL
) && (info
->rti_info
[RTAX_DST
] != NULL
)) {
1393 info
->rti_info
[RTAX_NETMASK
]->sa_family
= info
->rti_info
[RTAX_DST
]->sa_family
;
1396 for (i
= 0; i
< RTAX_MAX
; i
++) {
1397 if (info
->rti_info
[i
] != NULL
) {
1400 _SC_sockaddr_to_string(info
->rti_info
[i
], addr
, sizeof(addr
));
1401 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, addr
);
1405 #endif /* LOG_RTADDRS */
1407 if ((info
->rti_info
[RTAX_IFP
] == NULL
) ||
1408 (info
->rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
1409 /* no interface info */
1410 SCLog(TRUE
, LOG_DEBUG
, CFSTR("route_get() no interface info"));
1414 info
->sdl
= (struct sockaddr_dl
*) info
->rti_info
[RTAX_IFP
];
1415 if ((info
->sdl
->sdl_nlen
== 0) || (info
->sdl
->sdl_nlen
> IFNAMSIZ
)) {
1416 /* no interface name */
1417 return EHOSTUNREACH
;
1425 checkAddress(ReachabilityStoreInfoRef store_info
,
1426 const struct sockaddr
*address
,
1427 unsigned int if_index
,
1428 ReachabilityInfo
*reach_info
,
1429 const char *log_prefix
)
1433 char if_name
[IFNAMSIZ
];
1436 int sc_status
= kSCStatusReachabilityUnknown
;
1437 CFStringRef server
= NULL
;
1438 char *statusMessage
= NULL
;
1439 struct sockaddr_in v4mapped
;
1441 *reach_info
= NOT_REACHABLE
;
1443 if (address
== NULL
) {
1444 /* special case: check only for available paths off the system */
1445 goto checkAvailable
;
1448 switch (address
->sa_family
) {
1453 char if_name
[IFNAMSIZ
+ 1];
1455 _SC_sockaddr_to_string(address
, addr
, sizeof(addr
));
1457 if ((if_index
!= 0) &&
1458 (if_indextoname(if_index
, &if_name
[1]) != NULL
)) {
1464 SCLog(TRUE
, LOG_INFO
, CFSTR("%scheckAddress(%s%s)"),
1472 * if no code for this address family (yet)
1474 SCLog(TRUE
, LOG_INFO
,
1475 CFSTR("checkAddress(): unexpected address family %d"),
1476 address
->sa_family
);
1477 sc_status
= kSCStatusInvalidArgument
;
1481 if (address
->sa_family
== AF_INET6
) {
1482 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)address
;
1484 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) {
1485 bzero(&v4mapped
, sizeof(v4mapped
));
1486 v4mapped
.sin_len
= sizeof(v4mapped
);
1487 v4mapped
.sin_family
= AF_INET
;
1488 v4mapped
.sin_port
= sin6
->sin6_port
;
1489 v4mapped
.sin_addr
.s_addr
= sin6
->sin6_addr
.__u6_addr
.__u6_addr32
[3];
1490 address
= (struct sockaddr
*)&v4mapped
;
1494 ret
= route_get(address
, if_index
, &info
);
1500 goto checkAvailable
;
1507 /* get the interface flags */
1509 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
1511 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
1515 bzero(&ifr
, sizeof(ifr
));
1516 bcopy(info
.sdl
->sdl_data
, ifr
.ifr_name
, info
.sdl
->sdl_nlen
);
1518 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) == -1) {
1519 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl() failed: %s"), strerror(errno
));
1523 if (!(ifr
.ifr_flags
& IFF_UP
)) {
1524 goto checkAvailable
;
1527 statusMessage
= "isReachable";
1528 reach_info
->flags
|= kSCNetworkReachabilityFlagsReachable
;
1530 if (info
.rtm
->rtm_flags
& RTF_LOCAL
) {
1531 statusMessage
= "isReachable (is a local address)";
1532 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1533 } else if (ifr
.ifr_flags
& IFF_LOOPBACK
) {
1534 statusMessage
= "isReachable (is loopback network)";
1535 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1536 } else if ((info
.rti_info
[RTAX_IFA
] != NULL
) &&
1537 (info
.rti_info
[RTAX_IFA
]->sa_family
!= AF_LINK
)) {
1538 void *addr1
= (void *)address
;
1539 void *addr2
= (void *)info
.rti_info
[RTAX_IFA
];
1540 size_t len
= address
->sa_len
;
1542 if ((address
->sa_family
!= info
.rti_info
[RTAX_IFA
]->sa_family
) &&
1543 (address
->sa_len
!= info
.rti_info
[RTAX_IFA
]->sa_len
)) {
1544 SCLog(TRUE
, LOG_NOTICE
,
1545 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
1548 info
.rti_info
[RTAX_IFA
]->sa_family
,
1549 info
.rti_info
[RTAX_IFA
]->sa_len
);
1553 switch (address
->sa_family
) {
1555 addr1
= &((struct sockaddr_in
*)address
)->sin_addr
;
1556 addr2
= &((struct sockaddr_in
*)info
.rti_info
[RTAX_IFA
])->sin_addr
;
1557 len
= sizeof(struct in_addr
);
1562 if (((struct sockaddr_in
*)address
)->sin_addr
.s_addr
== 0) {
1563 statusMessage
= "isReachable (this host)";
1564 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1568 addr1
= &((struct sockaddr_in6
*)address
)->sin6_addr
;
1569 addr2
= &((struct sockaddr_in6
*)info
.rti_info
[RTAX_IFA
])->sin6_addr
;
1570 len
= sizeof(struct in6_addr
);
1576 if (bcmp(addr1
, addr2
, len
) == 0) {
1577 statusMessage
= "isReachable (is interface address)";
1578 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1582 if (!(info
.rtm
->rtm_flags
& RTF_GATEWAY
) &&
1583 (info
.rti_info
[RTAX_GATEWAY
] != NULL
) &&
1584 (info
.rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
) &&
1585 !(ifr
.ifr_flags
& IFF_POINTOPOINT
)) {
1586 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1589 bzero(&if_name
, sizeof(if_name
));
1590 bcopy(info
.sdl
->sdl_data
,
1592 (info
.sdl
->sdl_nlen
<= IFNAMSIZ
) ? info
.sdl
->sdl_nlen
: IFNAMSIZ
);
1594 reach_info
->if_index
= info
.sdl
->sdl_index
;
1597 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = %s"), log_prefix
, statusMessage
);
1598 SCLog(TRUE
, LOG_INFO
, CFSTR("%s device = %s (%hu)"), log_prefix
, if_name
, info
.sdl
->sdl_index
);
1599 SCLog(TRUE
, LOG_INFO
, CFSTR("%s sdl_type = 0x%x"), log_prefix
, info
.sdl
->sdl_type
);
1600 SCLog(TRUE
, LOG_INFO
, CFSTR("%s ifr_flags = 0x%04hx"), log_prefix
, ifr
.ifr_flags
);
1601 SCLog(TRUE
, LOG_INFO
, CFSTR("%s rtm_flags = 0x%08x"), log_prefix
, info
.rtm
->rtm_flags
);
1604 sc_status
= kSCStatusOK
;
1606 if (ifr
.ifr_flags
& IFF_POINTOPOINT
) {
1607 reach_info
->flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1610 if (info
.sdl
->sdl_type
== IFT_PPP
) {
1612 * 1. check if PPP service
1613 * 2. check for dial-on-demand PPP link that is not yet connected
1614 * 3. get PPP server address
1616 sc_status
= updatePPPStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1617 } else if (info
.sdl
->sdl_type
== IFT_OTHER
) {
1619 * 1. check if IPSec service
1620 * 2. get IPSec server address
1622 sc_status
= updateIPSecStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1624 #if !TARGET_IPHONE_SIMULATOR
1625 if (sc_status
== kSCStatusNoKey
) {
1627 * 1. check if VPN service
1628 * 2. get VPN server address
1630 sc_status
= updateVPNStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1632 #endif // !TARGET_IPHONE_SIMULATOR
1641 sc_status
= updatePPPAvailable(store_info
, address
, &reach_info
->flags
, log_prefix
);
1642 if ((sc_status
== kSCStatusOK
) && (reach_info
->flags
!= 0)) {
1646 #if !TARGET_IPHONE_SIMULATOR
1647 sc_status
= updateVPNAvailable(store_info
, address
, &reach_info
->flags
, log_prefix
);
1648 if ((sc_status
== kSCStatusOK
) && (reach_info
->flags
!= 0)) {
1651 #endif // !TARGET_IPHONE_SIMULATOR
1655 if (reach_info
->flags
== 0) {
1656 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s cannot be reached"), log_prefix
);
1659 if (isock
!= -1) (void)close(isock
);
1660 if (server
!= NULL
) CFRelease(server
);
1661 if ((sc_status
!= kSCStatusOK
) && (sc_status
!= kSCStatusNoKey
)) {
1662 _SCErrorSet(sc_status
);
1671 #pragma mark SCNetworkReachability APIs
1675 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
1677 CFAllocatorRef allocator
= CFGetAllocator(cf
);
1678 CFMutableStringRef result
;
1679 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1681 result
= CFStringCreateMutable(allocator
, 0);
1682 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
1683 switch (targetPrivate
->type
) {
1684 case reachabilityTypeAddress
:
1685 case reachabilityTypeAddressPair
: {
1688 if (targetPrivate
->localAddress
!= NULL
) {
1689 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
1690 CFStringAppendFormat(result
, NULL
, CFSTR("local address = %s"),
1694 if (targetPrivate
->remoteAddress
!= NULL
) {
1695 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
1696 CFStringAppendFormat(result
, NULL
, CFSTR("%s%saddress = %s"),
1697 targetPrivate
->localAddress
? ", " : "",
1698 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
1703 case reachabilityTypeName
: {
1704 if ((targetPrivate
->name
!= NULL
)) {
1705 CFStringAppendFormat(result
, NULL
, CFSTR("name = %s"), targetPrivate
->name
);
1707 if ((targetPrivate
->serv
!= NULL
)) {
1708 CFStringAppendFormat(result
, NULL
, CFSTR("%sserv = %s"),
1709 targetPrivate
->name
!= NULL
? ", " : "",
1710 targetPrivate
->serv
);
1712 if ((targetPrivate
->resolvedAddress
!= NULL
) || (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1713 if (targetPrivate
->resolvedAddress
!= NULL
) {
1714 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
1716 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddress
);
1718 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
1719 for (i
= 0; i
< n
; i
++) {
1722 struct sockaddr
*sa
;
1724 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
);
1725 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
1726 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
1727 CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"),
1731 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
1733 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)"));
1736 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
1737 gai_strerror(targetPrivate
->resolvedAddressError
));
1739 } else if (targetPrivate
->dnsPort
!= NULL
) {
1740 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
1745 if (targetPrivate
->scheduled
) {
1746 CFStringAppendFormat(result
,
1748 CFSTR(", flags = 0x%08x, if_index = %hu"),
1749 targetPrivate
->info
.flags
,
1750 targetPrivate
->info
.if_index
);
1752 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
1759 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1761 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1763 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%srelease"),
1764 targetPrivate
->log_prefix
);
1766 /* release resources */
1768 pthread_mutex_destroy(&targetPrivate
->lock
);
1770 if (targetPrivate
->name
!= NULL
)
1771 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
1773 if (targetPrivate
->serv
!= NULL
)
1774 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->serv
);
1776 if (targetPrivate
->resolvedAddress
!= NULL
)
1777 CFRelease(targetPrivate
->resolvedAddress
);
1779 if (targetPrivate
->localAddress
!= NULL
)
1780 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
1782 if (targetPrivate
->remoteAddress
!= NULL
)
1783 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
1785 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1786 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1789 if (targetPrivate
->onDemandName
!= NULL
) {
1790 CFRelease(targetPrivate
->onDemandName
);
1793 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
1794 CFRelease(targetPrivate
->onDemandRemoteAddress
);
1797 if (targetPrivate
->onDemandServer
!= NULL
) {
1798 CFRelease(targetPrivate
->onDemandServer
);
1801 if (targetPrivate
->onDemandServiceID
!= NULL
) {
1802 CFRelease(targetPrivate
->onDemandServiceID
);
1810 __SCNetworkReachabilityInitialize(void)
1812 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
1814 // provide a way to enable SCNetworkReachability logging without
1815 // having to set _sc_debug=1.
1816 if (getenv("REACH_LOGGING") != NULL
) {
1825 * __SCNetworkReachabilityPerformInline
1827 * Calls rlsPerform()
1828 * - caller must be holding a reference to the target
1829 * - caller must *not* be holding the target lock
1831 static __inline__
void
1832 __SCNetworkReachabilityPerformInline(SCNetworkReachabilityRef target
, Boolean needResolve
)
1834 dispatch_queue_t queue
;
1835 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1837 pthread_mutex_lock(&targetPrivate
->lock
);
1840 // allow the DNS query to be [re-]started
1841 targetPrivate
->needResolve
= TRUE
;
1844 queue
= targetPrivate
->dispatchQueue
;
1845 if (queue
!= NULL
) {
1846 dispatch_retain(queue
);
1848 pthread_mutex_unlock(&targetPrivate
->lock
);
1850 dispatch_sync(queue
, ^{
1851 rlsPerform((void *)target
);
1852 dispatch_release(queue
);
1855 if (targetPrivate
->rls
!= NULL
) {
1856 CFRunLoopSourceSignal(targetPrivate
->rls
);
1857 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1860 pthread_mutex_unlock(&targetPrivate
->lock
);
1868 __SCNetworkReachabilityPerform(SCNetworkReachabilityRef target
)
1870 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1872 if (targetPrivate
->dispatchQueue
!= NULL
) {
1874 dispatch_async(targetPrivate
->dispatchQueue
,
1876 rlsPerform((void *)target
);
1879 } else if (targetPrivate
->rls
!= NULL
) {
1880 CFRunLoopSourceSignal(targetPrivate
->rls
);
1881 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1887 static SCNetworkReachabilityPrivateRef
1888 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
1890 SCNetworkReachabilityPrivateRef targetPrivate
;
1893 /* initialize runtime */
1894 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
1896 /* allocate target */
1897 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
1898 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
1899 __kSCNetworkReachabilityTypeID
,
1902 if (targetPrivate
== NULL
) {
1906 pthread_mutex_init(&targetPrivate
->lock
, NULL
);
1908 targetPrivate
->name
= NULL
;
1909 targetPrivate
->serv
= NULL
;
1910 bzero(&targetPrivate
->hints
, sizeof(targetPrivate
->hints
));
1911 targetPrivate
->hints
.ai_flags
= AI_ADDRCONFIG
;
1913 targetPrivate
->hints
.ai_flags
|= AI_PARALLEL
;
1914 #endif /* AI_PARALLEL */
1916 targetPrivate
->needResolve
= FALSE
;
1917 targetPrivate
->resolvedAddress
= NULL
;
1918 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1920 targetPrivate
->if_index
= 0;
1922 targetPrivate
->localAddress
= NULL
;
1923 targetPrivate
->remoteAddress
= NULL
;
1925 targetPrivate
->info
= NOT_REACHABLE
;
1926 targetPrivate
->last_notify
= NOT_REPORTED
;
1928 targetPrivate
->scheduled
= FALSE
;
1929 targetPrivate
->rls
= NULL
;
1930 targetPrivate
->rlsFunction
= NULL
;
1931 targetPrivate
->rlsContext
.info
= NULL
;
1932 targetPrivate
->rlsContext
.retain
= NULL
;
1933 targetPrivate
->rlsContext
.release
= NULL
;
1934 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1935 targetPrivate
->rlList
= NULL
;
1937 targetPrivate
->haveDNS
= FALSE
;
1938 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
1939 targetPrivate
->dnsPort
= NULL
;
1940 targetPrivate
->dnsRLS
= NULL
;
1941 targetPrivate
->dnsQueryStart
= TIME_ZERO
;
1942 targetPrivate
->dnsQueryEnd
= TIME_ZERO
;
1943 targetPrivate
->dnsRetry
= NULL
;
1944 targetPrivate
->dnsRetryCount
= 0;
1946 targetPrivate
->last_dns
= TIME_ZERO
;
1948 targetPrivate
->onDemandBypass
= FALSE
;
1949 targetPrivate
->onDemandName
= NULL
;
1950 targetPrivate
->onDemandRemoteAddress
= NULL
;
1951 targetPrivate
->onDemandServer
= NULL
;
1952 targetPrivate
->onDemandServiceID
= NULL
;
1955 targetPrivate
->log_prefix
[0] = '\0';
1957 snprintf(targetPrivate
->log_prefix
,
1958 sizeof(targetPrivate
->log_prefix
),
1963 return targetPrivate
;
1969 static const struct sockaddr
*
1970 is_valid_address(const struct sockaddr
*address
)
1972 const struct sockaddr
*valid
= NULL
;
1973 static Boolean warned
= FALSE
;
1975 if ((address
!= NULL
) &&
1976 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
1977 switch (address
->sa_family
) {
1979 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
1983 SCLog(TRUE
, LOG_ERR
,
1984 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
1986 sizeof(struct sockaddr_in
));
1992 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
1994 } else if (!warned
) {
1995 SCLog(TRUE
, LOG_ERR
,
1996 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"),
1998 sizeof(struct sockaddr_in6
));
2004 SCLog(TRUE
, LOG_ERR
,
2005 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"),
2006 address
->sa_family
);
2016 SCNetworkReachabilityRef
2017 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
2018 const struct sockaddr
*address
)
2020 SCNetworkReachabilityPrivateRef targetPrivate
;
2022 address
= is_valid_address(address
);
2023 if (address
== NULL
) {
2024 _SCErrorSet(kSCStatusInvalidArgument
);
2028 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2029 if (targetPrivate
== NULL
) {
2033 targetPrivate
->type
= reachabilityTypeAddress
;
2034 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
2035 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
2037 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%screate w/address %@"),
2038 targetPrivate
->log_prefix
,
2041 return (SCNetworkReachabilityRef
)targetPrivate
;
2045 SCNetworkReachabilityRef
2046 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
2047 const struct sockaddr
*localAddress
,
2048 const struct sockaddr
*remoteAddress
)
2050 SCNetworkReachabilityPrivateRef targetPrivate
;
2052 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
2053 _SCErrorSet(kSCStatusInvalidArgument
);
2057 if (localAddress
!= NULL
) {
2058 localAddress
= is_valid_address(localAddress
);
2059 if (localAddress
== NULL
) {
2060 _SCErrorSet(kSCStatusInvalidArgument
);
2065 if (remoteAddress
!= NULL
) {
2066 remoteAddress
= is_valid_address(remoteAddress
);
2067 if (remoteAddress
== NULL
) {
2068 _SCErrorSet(kSCStatusInvalidArgument
);
2073 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2074 if (targetPrivate
== NULL
) {
2078 targetPrivate
->type
= reachabilityTypeAddressPair
;
2080 if (localAddress
!= NULL
) {
2081 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
2082 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
2085 if (remoteAddress
!= NULL
) {
2086 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
2087 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
2090 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%screate w/address pair %@"),
2091 targetPrivate
->log_prefix
,
2094 return (SCNetworkReachabilityRef
)targetPrivate
;
2098 SCNetworkReachabilityRef
2099 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
2100 const char *nodename
)
2103 struct sockaddr_in sin
;
2104 struct sockaddr_in6 sin6
;
2105 SCNetworkReachabilityPrivateRef targetPrivate
;
2107 if (nodename
== NULL
) {
2108 _SCErrorSet(kSCStatusInvalidArgument
);
2112 nodenameLen
= strlen(nodename
);
2113 if (nodenameLen
== 0) {
2114 _SCErrorSet(kSCStatusInvalidArgument
);
2118 /* check if this "nodename" is really an IP[v6] address in disguise */
2120 bzero(&sin
, sizeof(sin
));
2121 sin
.sin_len
= sizeof(sin
);
2122 sin
.sin_family
= AF_INET
;
2123 if (inet_aton(nodename
, &sin
.sin_addr
) == 1) {
2124 /* if IPv4 address */
2125 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin
);
2128 bzero(&sin6
, sizeof(sin6
));
2129 sin6
.sin6_len
= sizeof(sin6
);
2130 sin6
.sin6_family
= AF_INET6
;
2131 if (inet_pton(AF_INET6
, nodename
, &sin6
.sin6_addr
) == 1) {
2132 /* if IPv6 address */
2135 p
= strchr(nodename
, '%');
2137 sin6
.sin6_scope_id
= if_nametoindex(p
+ 1);
2140 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin6
);
2143 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2144 if (targetPrivate
== NULL
) {
2148 targetPrivate
->type
= reachabilityTypeName
;
2150 targetPrivate
->name
= CFAllocatorAllocate(NULL
, nodenameLen
+ 1, 0);
2151 strlcpy((char *)targetPrivate
->name
, nodename
, nodenameLen
+ 1);
2153 targetPrivate
->needResolve
= TRUE
;
2154 targetPrivate
->info
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
2156 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%screate w/name %@"),
2157 targetPrivate
->log_prefix
,
2160 return (SCNetworkReachabilityRef
)targetPrivate
;
2166 SCNetworkReachabilityRef
2167 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
2168 CFDictionaryRef options
)
2170 const struct sockaddr
*addr_l
= NULL
;
2171 const struct sockaddr
*addr_r
= NULL
;
2172 CFBooleanRef bypass
;
2174 struct addrinfo
*hints
= NULL
;
2175 CFStringRef interface
= NULL
;
2176 CFStringRef nodename
;
2177 CFStringRef servname
;
2178 SCNetworkReachabilityRef target
;
2179 SCNetworkReachabilityPrivateRef targetPrivate
;
2181 if (!isA_CFDictionary(options
)) {
2182 _SCErrorSet(kSCStatusInvalidArgument
);
2186 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
2187 if ((nodename
!= NULL
) &&
2188 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
2189 _SCErrorSet(kSCStatusInvalidArgument
);
2192 servname
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServName
);
2193 if ((servname
!= NULL
) &&
2194 (!isA_CFString(servname
) || (CFStringGetLength(servname
) == 0))) {
2195 _SCErrorSet(kSCStatusInvalidArgument
);
2198 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
2200 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2201 _SCErrorSet(kSCStatusInvalidArgument
);
2204 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2206 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
2208 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2209 _SCErrorSet(kSCStatusInvalidArgument
);
2212 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2214 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionHints
);
2216 if (!isA_CFData(data
) || (CFDataGetLength(data
) != sizeof(targetPrivate
->hints
))) {
2217 _SCErrorSet(kSCStatusInvalidArgument
);
2221 hints
= (struct addrinfo
*)CFDataGetBytePtr(data
);
2222 if ((hints
->ai_addrlen
!= 0) ||
2223 (hints
->ai_addr
!= NULL
) ||
2224 (hints
->ai_canonname
!= NULL
) ||
2225 (hints
->ai_next
!= NULL
)) {
2226 _SCErrorSet(kSCStatusInvalidArgument
);
2230 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
2231 if ((interface
!= NULL
) &&
2232 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
2233 _SCErrorSet(kSCStatusInvalidArgument
);
2236 bypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandByPass
);
2237 if ((bypass
!= NULL
) && !isA_CFBoolean(bypass
)) {
2238 _SCErrorSet(kSCStatusInvalidArgument
);
2243 if ((nodename
!= NULL
) || (servname
!= NULL
)) {
2246 if ((addr_l
!= NULL
) || (addr_r
!= NULL
)) {
2247 // can't have both a name/serv and an address
2248 _SCErrorSet(kSCStatusInvalidArgument
);
2252 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
2253 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
2254 CFAllocatorDeallocate(NULL
, (void *)name
);
2256 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
2257 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
2258 } else if (addr_r
!= NULL
) {
2259 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
2260 } else if (addr_l
!= NULL
) {
2261 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_l
);
2263 _SCErrorSet(kSCStatusInvalidArgument
);
2267 if (target
== NULL
) {
2271 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2272 if (targetPrivate
->type
== reachabilityTypeName
) {
2273 if (servname
!= NULL
) {
2274 targetPrivate
->serv
= _SC_cfstring_to_cstring(servname
, NULL
, 0, kCFStringEncodingUTF8
);
2276 if (hints
!= NULL
) {
2277 bcopy(hints
, &targetPrivate
->hints
, sizeof(targetPrivate
->hints
));
2281 if (interface
!= NULL
) {
2282 if ((_SC_cfstring_to_cstring(interface
,
2283 targetPrivate
->if_name
,
2284 sizeof(targetPrivate
->if_name
),
2285 kCFStringEncodingASCII
) == NULL
) ||
2286 ((targetPrivate
->if_index
= if_nametoindex(targetPrivate
->if_name
)) == 0)) {
2287 CFRelease(targetPrivate
);
2288 _SCErrorSet(kSCStatusInvalidArgument
);
2294 if (bypass
!= NULL
) {
2295 targetPrivate
->onDemandBypass
= CFBooleanGetValue(bypass
);
2298 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s + options %@"),
2299 targetPrivate
->log_prefix
,
2302 return (SCNetworkReachabilityRef
)targetPrivate
;
2307 SCNetworkReachabilityGetTypeID(void)
2309 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
2310 return __kSCNetworkReachabilityTypeID
;
2314 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
2315 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
2318 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2320 if (!isA_SCNetworkReachability(target
)) {
2321 _SCErrorSet(kSCStatusInvalidArgument
);
2325 if (targetPrivate
->type
!= reachabilityTypeName
) {
2326 _SCErrorSet(kSCStatusInvalidArgument
);
2331 *error_num
= targetPrivate
->resolvedAddressError
;
2334 if (targetPrivate
->resolvedAddress
!= NULL
) {
2335 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
2336 return CFRetain(targetPrivate
->resolvedAddress
);
2338 /* if status is known but no resolved addresses to return */
2339 _SCErrorSet(kSCStatusOK
);
2344 _SCErrorSet(kSCStatusReachabilityUnknown
);
2350 __SCNetworkReachabilitySetResolvedAddress(int32_t status
,
2351 struct addrinfo
*res
,
2352 SCNetworkReachabilityRef target
)
2354 struct addrinfo
*resP
;
2355 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2357 if (targetPrivate
->resolvedAddress
!= NULL
) {
2358 CFRelease(targetPrivate
->resolvedAddress
);
2359 targetPrivate
->resolvedAddress
= NULL
;
2362 if ((status
== 0) && (res
!= NULL
)) {
2363 CFMutableArrayRef addresses
;
2365 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2367 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
2369 CFDataRef newAddress
;
2371 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
2372 n
= CFArrayGetCount(addresses
);
2374 !CFArrayContainsValue(addresses
, CFRangeMake(0, n
), newAddress
)) {
2375 CFArrayAppendValue(addresses
, newAddress
);
2377 CFRelease(newAddress
);
2380 /* save the resolved address[es] */
2381 targetPrivate
->resolvedAddress
= addresses
;
2382 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
2384 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sgetaddrinfo() failed: %s"),
2385 targetPrivate
->log_prefix
,
2386 gai_strerror(status
));
2388 /* save the error associated with the attempt to resolve the name */
2389 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
2390 targetPrivate
->resolvedAddressError
= status
;
2392 targetPrivate
->needResolve
= FALSE
;
2394 if (res
!= NULL
) freeaddrinfo(res
);
2396 if (targetPrivate
->scheduled
) {
2397 __SCNetworkReachabilityPerform(target
);
2405 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo
*res
, void *context
)
2407 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
2408 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2410 __dns_query_end(target
,
2411 ((status
== 0) && (res
!= NULL
)), // if successful query
2413 &targetPrivate
->dnsQueryStart
, // start time
2414 &targetPrivate
->dnsQueryEnd
); // end time
2416 __SCNetworkReachabilitySetResolvedAddress(status
, res
, target
);
2422 * rankReachability()
2423 * Not reachable == 0
2424 * Connection Required == 1
2428 rankReachability(SCNetworkReachabilityFlags flags
)
2432 if (flags
& kSCNetworkReachabilityFlagsReachable
) rank
= 2;
2433 if (flags
& kSCNetworkReachabilityFlagsConnectionRequired
) rank
= 1;
2439 #pragma mark DNS name resolution
2443 replyMPCopyDescription(const void *info
)
2445 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2446 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2448 return CFStringCreateWithFormat(NULL
,
2450 CFSTR("<getaddrinfo_async_start reply MP> {%s%s%s%s%s, target = %p}"),
2451 targetPrivate
->name
!= NULL
? "name = " : "",
2452 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
2453 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
2454 targetPrivate
->serv
!= NULL
? "serv = " : "",
2455 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "",
2461 processAsyncDNSReply(mach_port_t mp
, void *msg
, SCNetworkReachabilityRef target
);
2465 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
2467 mach_port_t mp
= CFMachPortGetPort(port
);
2468 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2470 processAsyncDNSReply(mp
, msg
, target
);
2476 SCNetworkReachabilityNotifyMIGCallback(mach_msg_header_t
*message
, mach_msg_header_t
*reply
)
2478 mach_port_t mp
= message
->msgh_local_port
;
2479 SCNetworkReachabilityRef target
= dispatch_get_context(dispatch_get_current_queue());
2481 processAsyncDNSReply(mp
, message
, target
);
2482 reply
->msgh_remote_port
= MACH_PORT_NULL
;
2488 enqueueAsyncDNSQuery(SCNetworkReachabilityRef target
, mach_port_t mp
)
2490 CFMachPortContext context
= { 0
2494 , replyMPCopyDescription
2496 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2498 targetPrivate
->dnsMP
= mp
;
2499 targetPrivate
->dnsPort
= _SC_CFMachPortCreateWithPort("SCNetworkReachability",
2501 getaddrinfo_async_handleCFReply
,
2503 if (targetPrivate
->dispatchQueue
!= NULL
) {
2504 targetPrivate
->asyncDNSQueue
= dispatch_queue_create("com.apple.SCNetworkReachabilty.async_DNS_query", NULL
);
2505 if (targetPrivate
->asyncDNSQueue
== NULL
) {
2506 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_queue_create() failed"));
2509 CFRetain(target
); // Note: will be released when the dispatch queue is released
2510 dispatch_set_context(targetPrivate
->asyncDNSQueue
, (void *)target
);
2511 dispatch_set_finalizer_f(targetPrivate
->asyncDNSQueue
, (dispatch_function_t
)CFRelease
);
2513 targetPrivate
->asyncDNSSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
,
2516 targetPrivate
->asyncDNSQueue
);
2517 if (targetPrivate
->asyncDNSSource
== NULL
) {
2518 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_source_create() failed"));
2521 dispatch_source_set_event_handler(targetPrivate
->asyncDNSSource
, ^{
2522 dispatch_mig_server(targetPrivate
->asyncDNSSource
,
2523 sizeof(mach_msg_header_t
),
2524 SCNetworkReachabilityNotifyMIGCallback
);
2526 dispatch_resume(targetPrivate
->asyncDNSSource
);
2527 } else if (targetPrivate
->rls
!= NULL
) {
2531 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
2533 n
= CFArrayGetCount(targetPrivate
->rlList
);
2534 for (i
= 0; i
< n
; i
+= 3) {
2535 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
2536 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
2538 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
2546 if (targetPrivate
->asyncDNSSource
!= NULL
) {
2547 dispatch_source_cancel(targetPrivate
->asyncDNSSource
);
2548 dispatch_release(targetPrivate
->asyncDNSSource
);
2549 targetPrivate
->asyncDNSSource
= NULL
;
2551 if (targetPrivate
->asyncDNSQueue
!= NULL
) {
2552 dispatch_release(targetPrivate
->asyncDNSQueue
);
2553 targetPrivate
->asyncDNSQueue
= NULL
;
2556 CFMachPortInvalidate(targetPrivate
->dnsPort
);
2557 CFRelease(targetPrivate
->dnsPort
);
2558 targetPrivate
->dnsPort
= NULL
;
2559 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
2561 _SCErrorSet(kSCStatusFailed
);
2567 dequeueAsyncDNSQuery(SCNetworkReachabilityRef target
)
2569 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2571 if (targetPrivate
->asyncDNSSource
!= NULL
) {
2572 dispatch_source_cancel(targetPrivate
->asyncDNSSource
);
2573 if (targetPrivate
->asyncDNSQueue
!= dispatch_get_current_queue()) {
2574 // ensure the cancellation has completed
2575 pthread_mutex_unlock(&targetPrivate
->lock
);
2576 dispatch_sync(targetPrivate
->asyncDNSQueue
, ^{});
2577 pthread_mutex_lock(&targetPrivate
->lock
);
2580 if (targetPrivate
->asyncDNSSource
!= NULL
) {
2581 dispatch_release(targetPrivate
->asyncDNSSource
);
2582 targetPrivate
->asyncDNSSource
= NULL
;
2584 if (targetPrivate
->asyncDNSQueue
!= NULL
) {
2585 dispatch_release(targetPrivate
->asyncDNSQueue
);
2586 targetPrivate
->asyncDNSQueue
= NULL
;
2589 if (targetPrivate
->dnsRLS
!= NULL
) {
2590 CFRelease(targetPrivate
->dnsRLS
);
2591 targetPrivate
->dnsRLS
= NULL
;
2594 if (targetPrivate
->dnsPort
!= NULL
) {
2595 CFMachPortInvalidate(targetPrivate
->dnsPort
);
2596 CFRelease(targetPrivate
->dnsPort
);
2597 targetPrivate
->dnsPort
= NULL
;
2598 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
2606 processAsyncDNSReply(mach_port_t mp
, void *msg
, SCNetworkReachabilityRef target
)
2609 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2611 pthread_mutex_lock(&targetPrivate
->lock
);
2613 if (mp
!= targetPrivate
->dnsMP
) {
2614 // we've received a callback on the async DNS port but since the
2615 // associated CFMachPort doesn't match than the request must have
2616 // already been cancelled.
2617 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP"));
2618 pthread_mutex_unlock(&targetPrivate
->lock
);
2622 dequeueAsyncDNSQuery(target
);
2623 status
= getaddrinfo_async_handle_reply(msg
);
2624 if ((status
== 0) &&
2625 (targetPrivate
->resolvedAddress
== NULL
) && (targetPrivate
->resolvedAddressError
== NETDB_SUCCESS
)) {
2628 // if the request is not complete and needs to be re-queued
2629 ok
= enqueueAsyncDNSQuery(target
, mp
);
2631 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply enqueueAsyncDNSQuery() failed"));
2635 pthread_mutex_unlock(&targetPrivate
->lock
);
2642 check_resolver_reachability(ReachabilityStoreInfoRef store_info
,
2643 dns_resolver_t
*resolver
,
2644 SCNetworkReachabilityFlags
*flags
,
2646 const char *log_prefix
)
2651 *flags
= kSCNetworkReachabilityFlagsReachable
;
2654 for (i
= 0; i
< resolver
->n_nameserver
; i
++) {
2655 struct sockaddr
*address
= resolver
->nameserver
[i
];
2656 ReachabilityInfo ns_info
;
2660 if (address
->sa_family
!= AF_INET
) {
2662 * we need to skip non-IPv4 DNS server
2663 * addresses (at least until [3510431] has
2669 ok
= checkAddress(store_info
, address
, resolver
->if_index
, &ns_info
, log_prefix
);
2675 if (rankReachability(ns_info
.flags
) < rankReachability(*flags
)) {
2676 /* return the worst case result */
2677 *flags
= ns_info
.flags
;
2688 check_matching_resolvers(ReachabilityStoreInfoRef store_info
,
2689 dns_config_t
*dns_config
,
2691 unsigned int if_index
,
2692 SCNetworkReachabilityFlags
*flags
,
2694 const char *log_prefix
)
2697 Boolean matched
= FALSE
;
2698 const char *name
= fqdn
;
2699 int32_t n_resolvers
;
2700 dns_resolver_t
**resolvers
;
2702 if (if_index
== 0) {
2703 n_resolvers
= dns_config
->n_resolver
;
2704 resolvers
= dns_config
->resolver
;
2706 n_resolvers
= dns_config
->n_scoped_resolver
;
2707 resolvers
= dns_config
->scoped_resolver
;
2710 while (!matched
&& (name
!= NULL
)) {
2714 * check if the provided name (or sub-component)
2715 * matches one of our resolver configurations.
2718 for (i
= 0; i
< n_resolvers
; i
++) {
2720 dns_resolver_t
*resolver
;
2722 resolver
= resolvers
[i
];
2723 if ((if_index
!= 0) && (if_index
!= resolver
->if_index
)) {
2727 domain
= resolver
->domain
;
2728 if (domain
!= NULL
&& (len
== strlen(domain
))) {
2729 if (strcasecmp(name
, domain
) == 0) {
2733 * if name matches domain
2736 ok
= check_resolver_reachability(store_info
, resolver
, flags
, haveDNS
, log_prefix
);
2747 * we have not found a matching resolver, try
2748 * a less qualified domain
2750 name
= strchr(name
, '.');
2751 if ((name
!= NULL
) && (*name
!= '\0')) {
2763 static dns_resolver_t
*
2764 get_default_resolver(dns_config_t
*dns_config
, unsigned int if_index
)
2767 int32_t n_resolvers
;
2768 dns_resolver_t
*resolver
= NULL
;
2769 dns_resolver_t
**resolvers
;
2771 if (if_index
== 0) {
2772 n_resolvers
= dns_config
->n_resolver
;
2773 resolvers
= dns_config
->resolver
;
2775 n_resolvers
= dns_config
->n_scoped_resolver
;
2776 resolvers
= dns_config
->scoped_resolver
;
2779 for (i
= 0; i
< n_resolvers
; i
++) {
2780 if ((if_index
!= 0) && (if_index
!= resolvers
[i
]->if_index
)) {
2784 if (((if_index
== 0) && (i
== 0)) ||
2785 ((if_index
!= 0) && (resolver
== NULL
))) {
2786 // if this is the first (aka default) resolver
2787 resolver
= resolvers
[i
];
2788 } else if ((resolvers
[i
]->domain
== NULL
) &&
2789 (resolvers
[i
]->search_order
< resolver
->search_order
)) {
2790 // if this is a default resolver with a lower search order
2791 resolver
= resolvers
[i
];
2799 static dns_configuration_t
*
2800 dns_configuration_retain()
2802 pthread_mutex_lock(&dns_lock
);
2804 if ((dns_configuration
!= NULL
) && dns_token_valid
) {
2809 * check if the global [DNS] configuration snapshot needs
2812 status
= notify_check(dns_token
, &check
);
2813 if (status
!= NOTIFY_STATUS_OK
) {
2814 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
2817 if ((status
!= NOTIFY_STATUS_OK
) || (check
!= 0)) {
2819 * if the snapshot needs to be refreshed
2821 if (dns_configuration
->refs
== 0) {
2822 dns_configuration_free(dns_configuration
->config
);
2823 CFAllocatorDeallocate(NULL
, dns_configuration
);
2825 dns_configuration
= NULL
;
2829 if (dns_configuration
== NULL
) {
2830 dns_config_t
*new_config
;
2832 new_config
= dns_configuration_copy();
2833 if (new_config
!= NULL
) {
2834 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
2835 dns_configuration
->config
= new_config
;
2836 dns_configuration
->refs
= 0;
2840 if (dns_configuration
!= NULL
) {
2841 dns_configuration
->refs
++;
2844 pthread_mutex_unlock(&dns_lock
);
2845 return dns_configuration
;
2850 dns_configuration_release(dns_configuration_t
*config
)
2852 pthread_mutex_lock(&dns_lock
);
2855 if (config
->refs
== 0) {
2856 if ((dns_configuration
!= config
)) {
2857 dns_configuration_free(config
->config
);
2858 CFAllocatorDeallocate(NULL
, config
);
2862 pthread_mutex_unlock(&dns_lock
);
2868 dns_configuration_watch()
2871 const char *dns_key
;
2875 pthread_mutex_lock(&dns_lock
);
2877 dns_key
= dns_configuration_notify_key();
2878 if (dns_key
== NULL
) {
2879 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
2883 status
= notify_register_check(dns_key
, &dns_token
);
2884 if (status
== NOTIFY_STATUS_OK
) {
2885 dns_token_valid
= TRUE
;
2887 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
);
2891 status
= notify_check(dns_token
, &dns_check
);
2892 if (status
!= NOTIFY_STATUS_OK
) {
2893 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
2894 (void)notify_cancel(dns_token
);
2895 dns_token_valid
= FALSE
;
2903 pthread_mutex_unlock(&dns_lock
);
2909 dns_configuration_unwatch()
2911 pthread_mutex_lock(&dns_lock
);
2913 (void)notify_cancel(dns_token
);
2914 dns_token_valid
= FALSE
;
2916 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
2917 dns_configuration_free(dns_configuration
->config
);
2918 CFAllocatorDeallocate(NULL
, dns_configuration
);
2919 dns_configuration
= NULL
;
2922 pthread_mutex_unlock(&dns_lock
);
2928 _SC_R_checkResolverReachability(ReachabilityStoreInfoRef store_info
,
2929 SCNetworkReachabilityFlags
*flags
,
2931 const char *nodename
,
2932 const char *servname
,
2933 unsigned int if_index
,
2934 const char *log_prefix
)
2936 dns_resolver_t
*default_resolver
;
2937 dns_configuration_t
*dns
;
2938 Boolean found
= FALSE
;
2939 char *fqdn
= (char *)nodename
;
2941 Boolean isFQDN
= FALSE
;
2945 Boolean useDefault
= FALSE
;
2948 * We first assume that all of the configured DNS servers
2949 * are available. Since we don't know which name server will
2950 * be consulted to resolve the specified nodename we need to
2951 * check the availability of ALL name servers. We can only
2952 * proceed if we know that our query can be answered.
2955 *flags
= kSCNetworkReachabilityFlagsReachable
;
2958 len
= (nodename
!= NULL
) ? strlen(nodename
) : 0;
2960 if ((servname
== NULL
) || (strlen(servname
) == 0)) {
2961 // if no nodename or servname, return not reachable
2967 dns
= dns_configuration_retain();
2970 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no configuration"), log_prefix
);
2974 if (dns
->config
->n_resolver
== 0) {
2975 // if no resolver configuration
2976 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no resolvers"), log_prefix
);
2980 *flags
= kSCNetworkReachabilityFlagsReachable
;
2982 if (fqdn
[len
- 1] == '.') {
2985 // trim trailing '.''s
2986 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
2987 if (fqdn
== nodename
) {
2988 fqdn
= strdup(nodename
);
2994 default_resolver
= get_default_resolver(dns
->config
, if_index
);
2997 * check if the provided name matches a supplemental domain
2999 found
= check_matching_resolvers(store_info
, dns
->config
, fqdn
, if_index
, flags
, haveDNS
, log_prefix
);
3001 if (!found
&& !isFQDN
) {
3003 * if we did not match a supplemental domain name and if the
3004 * provided name has enough "."s then the first query will be
3005 * directed to the default resolver.
3010 #define NDOTS_OPT "ndots="
3011 #define NDOTS_OPT_LEN (sizeof("ndots=") - 1)
3013 if (default_resolver
->options
!= NULL
) {
3014 cp
= strstr(default_resolver
->options
, NDOTS_OPT
);
3016 ((cp
== default_resolver
->options
) || isspace(cp
[-1])) &&
3017 ((cp
[NDOTS_OPT_LEN
] != '\0') && isdigit(cp
[NDOTS_OPT_LEN
]))) {
3021 cp
+= NDOTS_OPT_LEN
;
3023 val
= strtol(cp
, &end
, 10);
3024 if ((*cp
!= '\0') && (cp
!= end
) && (errno
== 0) &&
3025 ((*end
== '\0') || isspace(*end
))) {
3032 for (cp
= fqdn
; *cp
!= '\0'; cp
++) {
3033 if (*cp
== '.') dots
++;
3041 if (!found
&& !isFQDN
&& !useDefault
&& (dns
->config
->n_resolver
> 1)) {
3043 * FQDN not specified, try matching w/search domains
3045 if (default_resolver
->n_search
> 0) {
3046 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
3048 char *search_fqdn
= NULL
;
3050 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
3055 // try the provided name with the search domain appended
3056 found
= check_matching_resolvers(store_info
,
3065 } else if (default_resolver
->domain
!= NULL
) {
3067 int domain_parts
= 0;
3069 // count domain parts
3070 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
3076 // remove trailing dots
3077 for (dp
--; (dp
>= default_resolver
->domain
) && (*dp
== '.'); dp
--) {
3082 if (dp
>= default_resolver
->domain
) {
3083 // dots are separators, bump # of components
3087 dp
= default_resolver
->domain
;
3088 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= (domain_parts
- ndots
)); i
++) {
3090 char *search_fqdn
= NULL
;
3092 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
3097 // try the provided name with the [default] domain appended
3098 found
= check_matching_resolvers(store_info
,
3107 // move to the next component of the [default] domain
3108 dp
= strchr(dp
, '.') + 1;
3115 * check the reachability of the default resolver
3117 ok
= check_resolver_reachability(store_info
, default_resolver
, flags
, haveDNS
, log_prefix
);
3120 if (fqdn
!= nodename
) free(fqdn
);
3125 dns_configuration_release(dns
);
3133 _SC_checkResolverReachability(SCDynamicStoreRef
*storeP
,
3134 SCNetworkReachabilityFlags
*flags
,
3136 const char *nodename
,
3137 const char *servname
)
3140 ReachabilityStoreInfo store_info
;
3142 initReachabilityStoreInfo(&store_info
);
3143 ok
= updateReachabilityStoreInfo(&store_info
, storeP
, AF_UNSPEC
);
3148 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, nodename
, servname
, 0, "");
3152 freeReachabilityStoreInfo(&store_info
);
3158 * _SC_checkResolverReachabilityByAddress()
3160 * Given an IP address, determine whether a reverse DNS query can be issued
3161 * using the current network configuration.
3164 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
3165 SCNetworkReachabilityFlags
*flags
,
3167 struct sockaddr
*sa
)
3172 ReachabilityStoreInfo store_info
;
3174 initReachabilityStoreInfo(&store_info
);
3175 ok
= updateReachabilityStoreInfo(&store_info
, storeP
, AF_UNSPEC
);
3181 * Ideally, we would have an API that given a local IP
3182 * address would return the DNS server(s) that would field
3183 * a given PTR query. Fortunately, we do have an SPI which
3184 * which will provide this information given a "name" so we
3185 * take the address, convert it into the inverse query name,
3186 * and find out which servers should be consulted.
3189 switch (sa
->sa_family
) {
3195 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
3198 * build "PTR" query name
3199 * NNN.NNN.NNN.NNN.in-addr.arpa.
3201 rev
.s_addr
= sin
->sin_addr
.s_addr
;
3202 (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.",
3213 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
3214 int x
= sizeof(ptr_name
);
3218 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
3219 * 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.
3221 for (i
= sizeof(sin6
->sin6_addr
) - 1; i
>= 0; i
--) {
3222 n
= snprintf(&ptr_name
[s
], x
, "%x.%x.",
3223 ( sin6
->sin6_addr
.s6_addr
[i
] & 0xf),
3224 ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf));
3225 if ((n
== -1) || (n
>= x
)) {
3233 n
= snprintf(&ptr_name
[s
], x
, "ip6.arpa.");
3234 if ((n
== -1) || (n
>= x
)) {
3245 ok
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, ptr_name
, NULL
, 0, "");
3249 freeReachabilityStoreInfo(&store_info
);
3255 startAsyncDNSQuery(SCNetworkReachabilityRef target
) {
3257 mach_port_t mp
= MACH_PORT_NULL
;
3259 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3261 __dns_query_start(&targetPrivate
->dnsQueryStart
, &targetPrivate
->dnsQueryEnd
);
3263 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3264 if (targetPrivate
->if_index
== 0) {
3265 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3266 error
= getaddrinfo_async_start(&mp
,
3267 targetPrivate
->name
,
3268 targetPrivate
->serv
,
3269 &targetPrivate
->hints
,
3270 __SCNetworkReachabilityCallbackSetResolvedAddress
,
3272 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3274 mp
= _getaddrinfo_interface_async_call(targetPrivate
->name
,
3275 targetPrivate
->serv
,
3276 &targetPrivate
->hints
,
3277 targetPrivate
->if_name
,
3278 __SCNetworkReachabilityCallbackSetResolvedAddress
,
3280 if (mp
== MACH_PORT_NULL
) {
3284 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3286 /* save the error associated with the attempt to resolve the name */
3287 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
);
3291 ok
= enqueueAsyncDNSQuery(target
, mp
);
3300 enqueueAsyncDNSRetry(SCNetworkReachabilityRef target
)
3303 dispatch_source_t source
;
3304 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3306 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
,
3309 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0));
3310 if (source
== NULL
) {
3311 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability retry dispatch_source_create() failed"));
3315 // retain the target ... and release it when the [timer] source is released
3317 dispatch_set_context(source
, (void *)target
);
3318 dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
);
3320 dispatch_source_set_event_handler(source
, ^(void) {
3321 __SCNetworkReachabilityPerformInline(target
, TRUE
);
3324 // start a one-shot timer
3325 delay
= targetPrivate
->dnsRetryCount
* EAI_NONAME_RETRY_DELAY_USEC
* NSEC_PER_USEC
;
3326 dispatch_source_set_timer(source
,
3327 dispatch_time(DISPATCH_TIME_NOW
, delay
), // start
3329 10 * NSEC_PER_MSEC
); // leeway
3331 targetPrivate
->dnsRetry
= source
;
3332 dispatch_resume(source
);
3339 dequeueAsyncDNSRetry(SCNetworkReachabilityRef target
)
3341 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3343 if (targetPrivate
->dnsRetry
!= NULL
) {
3344 dispatch_source_cancel(targetPrivate
->dnsRetry
);
3345 dispatch_release(targetPrivate
->dnsRetry
);
3346 targetPrivate
->dnsRetry
= NULL
;
3354 #pragma mark OnDemand
3358 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target
,
3359 CFDictionaryRef
*userOptions
)
3361 SCNetworkServiceRef service
= NULL
;
3362 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3364 if (!isA_SCNetworkReachability(target
)) {
3365 _SCErrorSet(kSCStatusInvalidArgument
);
3369 if (targetPrivate
->onDemandServiceID
!= NULL
) {
3370 service
= _SCNetworkServiceCopyActive(NULL
, targetPrivate
->onDemandServiceID
);
3373 if (userOptions
!= NULL
) {
3374 if (targetPrivate
->onDemandName
!= NULL
) {
3375 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3376 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
);
3378 *userOptions
= NULL
;
3387 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer
,
3388 SCNetworkReachabilityFlags onDemandFlags
,
3391 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
3392 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3394 pthread_mutex_lock(&targetPrivate
->lock
);
3396 if (!targetPrivate
->scheduled
) {
3397 // if not currently scheduled
3398 pthread_mutex_unlock(&targetPrivate
->lock
);
3402 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sOnDemand \"server\" status changed"),
3403 targetPrivate
->log_prefix
);
3404 __SCNetworkReachabilityPerform(target
);
3406 pthread_mutex_unlock(&targetPrivate
->lock
);
3413 __SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef store_info
,
3414 SCNetworkReachabilityRef target
,
3415 Boolean onDemandRetry
,
3416 SCNetworkReachabilityFlags
*flags
)
3419 Boolean onDemand
= FALSE
;
3420 CFStringRef onDemandRemoteAddress
= NULL
;
3421 CFStringRef onDemandServiceID
= NULL
;
3422 SCNetworkConnectionStatus onDemandStatus
;
3423 SCDynamicStoreRef store
;
3424 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3426 // SCLog(_sc_debug, LOG_INFO,
3427 // CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"),
3428 // targetPrivate->log_prefix,
3429 // onDemandRetry ? "after" : "before");
3431 if (targetPrivate
->onDemandName
== NULL
) {
3432 targetPrivate
->onDemandName
= CFStringCreateWithCString(NULL
, targetPrivate
->name
, kCFStringEncodingUTF8
);
3436 * check if an OnDemand VPN configuration matches the name.
3438 store
= store_info
->store
;
3439 ok
= __SCNetworkConnectionCopyOnDemandInfoWithName(&store
,
3440 targetPrivate
->onDemandName
,
3444 &onDemandRemoteAddress
);
3445 if (store_info
->store
!= store
) {
3446 store_info
->store
= store
;
3447 store_info
->storeAdded
= TRUE
;
3449 if (!_SC_CFEqual(targetPrivate
->onDemandRemoteAddress
, onDemandRemoteAddress
) ||
3450 !_SC_CFEqual(targetPrivate
->onDemandServiceID
, onDemandServiceID
)) {
3451 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
3452 CFRelease(targetPrivate
->onDemandRemoteAddress
);
3453 targetPrivate
->onDemandRemoteAddress
= NULL
;
3456 if (targetPrivate
->onDemandServer
!= NULL
) {
3457 if (targetPrivate
->dispatchQueue
!= NULL
) {
3459 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
3460 } else if (targetPrivate
->rls
!= NULL
) {
3465 n
= CFArrayGetCount(targetPrivate
->rlList
);
3466 for (i
= 0; i
< n
; i
+= 3) {
3467 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
3468 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
3470 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, TRUE
);
3474 CFRelease(targetPrivate
->onDemandServer
);
3475 targetPrivate
->onDemandServer
= NULL
;
3478 if (targetPrivate
->onDemandServiceID
!= NULL
) {
3479 CFRelease(targetPrivate
->onDemandServiceID
);
3480 targetPrivate
->onDemandServiceID
= NULL
;
3484 if (onDemandStatus
!= kSCNetworkConnectionConnected
) {
3486 * if we have a VPN configuration matching the name *and* we need to
3487 * bring the VPN up. Combine our flags with those of the VPN server.
3489 if (targetPrivate
->onDemandServer
== NULL
) {
3490 CFMutableDictionaryRef options
;
3492 options
= CFDictionaryCreateMutable(NULL
,
3494 &kCFTypeDictionaryKeyCallBacks
,
3495 &kCFTypeDictionaryValueCallBacks
);
3496 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, onDemandRemoteAddress
);
3497 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandByPass
, kCFBooleanTrue
);
3498 targetPrivate
->onDemandServer
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
3501 if (targetPrivate
->scheduled
) {
3502 SCNetworkReachabilityContext context
= { 0, NULL
, CFRetain
, CFRelease
, CFCopyDescription
};
3504 context
.info
= (void *)target
;
3505 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
,
3506 __SCNetworkReachabilityOnDemandCheckCallback
,
3509 // schedule server reachability to match that of the target
3510 if (targetPrivate
->dispatchQueue
!= NULL
) {
3511 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, targetPrivate
->dispatchQueue
, TRUE
);
3516 n
= CFArrayGetCount(targetPrivate
->rlList
);
3517 for (i
= 0; i
< n
; i
+= 3) {
3518 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
3519 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
3521 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, NULL
, TRUE
);
3527 ok
= SCNetworkReachabilityGetFlags(targetPrivate
->onDemandServer
, flags
);
3528 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status * = 0x%08x"),
3529 targetPrivate
->log_prefix
,
3531 if (ok
&& (*flags
& kSCNetworkReachabilityFlagsReachable
)) {
3532 if (!(*flags
& kSCNetworkReachabilityFlagsTransientConnection
)) {
3533 // start clean if not already layered on a transient network
3536 *flags
|= kSCNetworkReachabilityFlagsReachable
;
3537 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
3538 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
3539 *flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
3542 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service * = %@"),
3543 targetPrivate
->log_prefix
,
3545 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after OnDemand connect)"),
3546 targetPrivate
->log_prefix
);
3553 if (onDemandRemoteAddress
!= NULL
) {
3554 if (targetPrivate
->onDemandRemoteAddress
== NULL
) {
3555 targetPrivate
->onDemandRemoteAddress
= onDemandRemoteAddress
;
3557 CFRelease(onDemandRemoteAddress
);
3561 if (onDemandServiceID
!= NULL
) {
3562 if (targetPrivate
->onDemandServiceID
== NULL
) {
3563 targetPrivate
->onDemandServiceID
= onDemandServiceID
;
3565 CFRelease(onDemandServiceID
);
3575 #pragma mark Reachability Flags
3578 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3581 struct addrinfo
*res
;
3586 reply_callback(int32_t status
, struct addrinfo
*res
, void *context
)
3588 reply_info
*reply
= (reply_info
*)context
;
3590 reply
->status
= status
;
3597 getaddrinfo_interface_sync(const char *nodename
,
3598 const char *servname
,
3599 const struct addrinfo
*hints
,
3600 const char *interface
,
3601 struct addrinfo
**res
)
3604 reply_info reply
= { NETDB_SUCCESS
, NULL
};
3606 mp
= _getaddrinfo_interface_async_call(nodename
,
3612 if (mp
== MACH_PORT_NULL
) {
3620 mach_msg_empty_rcv_t msg
;
3622 kern_return_t m_status
;
3624 m_status
= mach_msg(&m_reply
.msg
.header
, /* msg */
3625 MACH_RCV_MSG
, /* options */
3627 sizeof(m_reply
), /* rcv_size */
3629 MACH_MSG_TIMEOUT_NONE
, /* timeout */
3630 MACH_PORT_NULL
); /* notify */
3631 if (m_status
!= KERN_SUCCESS
) {
3635 g_status
= getaddrinfo_async_handle_reply((void *)m_reply
.buf
);
3636 if (g_status
!= 0) {
3637 if (reply
.res
!= NULL
) {
3638 freeaddrinfo(reply
.res
);
3644 if ((reply
.res
!= NULL
) || (reply
.status
!= NETDB_SUCCESS
)) {
3645 // if we have a reply or an error
3649 // if the request is not complete and needs to be re-queued
3653 return reply
.status
;
3655 #endif /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */
3659 __SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef store_info
,
3660 SCNetworkReachabilityRef target
,
3661 ReachabilityInfo
*reach_info
,
3664 CFMutableArrayRef addresses
= NULL
;
3665 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3666 ReachabilityInfo my_info
= NOT_REACHABLE
;
3669 *reach_info
= NOT_REACHABLE
;
3671 if (!isA_SCNetworkReachability(target
)) {
3672 _SCErrorSet(kSCStatusInvalidArgument
);
3676 switch (targetPrivate
->type
) {
3677 case reachabilityTypeAddress
:
3678 case reachabilityTypeAddressPair
: {
3680 * Check "local" address
3682 if (targetPrivate
->localAddress
!= NULL
) {
3684 * Check "local" address
3686 ok
= checkAddress(store_info
,
3687 targetPrivate
->localAddress
,
3688 targetPrivate
->if_index
,
3690 targetPrivate
->log_prefix
);
3692 goto error
; /* not today */
3695 if (!(my_info
.flags
& kSCNetworkReachabilityFlagsIsLocalAddress
)) {
3696 goto error
; /* not reachable, non-"local" address */
3701 * Check "remote" address
3703 if (targetPrivate
->remoteAddress
!= NULL
) {
3705 * in cases where we have "local" and "remote" addresses
3706 * we need to re-initialize the to-be-returned flags.
3708 my_info
= NOT_REACHABLE
;
3711 * Check "remote" address
3713 ok
= checkAddress(store_info
,
3714 targetPrivate
->remoteAddress
,
3715 targetPrivate
->if_index
,
3717 targetPrivate
->log_prefix
);
3719 goto error
; /* not today */
3727 case reachabilityTypeName
: {
3728 struct timeval dnsQueryStart
;
3729 struct timeval dnsQueryEnd
;
3731 SCNetworkReachabilityFlags ns_flags
;
3732 struct addrinfo
*res
;
3734 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
3735 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
3736 /* if resolved or an error had been detected */
3738 /* if not an async request */
3739 goto checkResolvedAddress
;
3740 } else if ((targetPrivate
->dnsMP
== MACH_PORT_NULL
) && !targetPrivate
->needResolve
) {
3741 struct timeval elapsed
;
3742 const struct timeval retry_limit
= { EAI_NONAME_RETRY_LIMIT_USEC
/ USEC_PER_SEC
,
3743 EAI_NONAME_RETRY_LIMIT_USEC
% USEC_PER_SEC
};
3746 * if this is an async request (i.e. someone is watching the reachability
3747 * of this target), if no query active, and if no query is needed
3750 if ((error
!= EAI_NONAME
)
3751 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
3752 && (error
!= EAI_NODATA
)
3755 /* if not "host not found" */
3756 goto checkResolvedAddress
;
3760 * if our last DNS query returned EAI_NONAME then we
3761 * "may" want to retry.
3763 * Specifically, if the [DNS] configuration was updated a while
3764 * back then we'll trust the EAI_NONAME reply. Otherwise, we
3765 * want to try again to ensure that we didn't get caught in a
3766 * race between the time when the configuration was changed and
3767 * when mDNSResponder is really ready to handle the query.
3769 * Retry handling details :
3771 * Compare the time when the DNS configuration was last changed and
3772 * when our DNS reply was started (->last_dns vs ->dnsQueryStart).
3774 * Expected: 0 < last_dns (t1) < dnsQueryStart (t2)
3776 * last start end description action
3777 * ==== ===== ==== ================================= ========
3778 * 0 N/A N/A no change, query error no retry
3779 * 0 N/A N/A no change, query complete no retry
3780 * N/A N/A 0 changed, query in-flight or error no retry
3781 * t1 > t2 query started, then [DNS] changed no retry
3782 * t1 == t2 changed & query started together no retry
3783 * t1 < t2 changed, then query started retry
3786 if (!timerisset(&targetPrivate
->last_dns
)) {
3788 * if we have not yet seen a DNS configuration
3791 goto checkResolvedAddress
;
3794 if (!timerisset(&targetPrivate
->dnsQueryEnd
)) {
3796 * if no query end time (new request in flight)
3798 goto checkResolvedAddress
;
3801 if (timercmp(&targetPrivate
->last_dns
,
3802 &targetPrivate
->dnsQueryStart
,
3805 * if our DNS query started and then, a
3806 * short time later, the DNS configuration
3807 * was changed we don't need to retry
3808 * because we will be re-issuing (and not
3809 * retrying) the query.
3811 goto checkResolvedAddress
;
3814 timersub(&targetPrivate
->dnsQueryStart
,
3815 &targetPrivate
->last_dns
,
3817 if (timercmp(&elapsed
, &retry_limit
, >)) {
3819 * if the DNS query started after mDNSResponder
3820 * had a chance to apply the last configuration
3821 * then we should trust the EAI_NONAME reply.
3823 goto checkResolvedAddress
;
3826 /* retry the DNS query */
3828 if (targetPrivate
->dnsRetry
!= NULL
) {
3829 // no need to schedule if we already have a
3830 // retry query in flight
3834 targetPrivate
->dnsRetryCount
++;
3836 SCLog(_sc_debug
, LOG_INFO
,
3837 CFSTR("%sretry [%d] DNS query for %s%s%s%s%s"),
3838 targetPrivate
->log_prefix
,
3839 targetPrivate
->dnsRetryCount
,
3840 targetPrivate
->name
!= NULL
? "name = " : "",
3841 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
3842 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
3843 targetPrivate
->serv
!= NULL
? "serv = " : "",
3844 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
3846 enqueueAsyncDNSRetry(target
);
3852 if (!targetPrivate
->onDemandBypass
) {
3856 * before we attempt our initial DNS query, check if there is
3857 * an OnDemand configuration that we should be using.
3859 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, FALSE
, &my_info
.flags
);
3861 /* if OnDemand connection is needed */
3866 /* check the reachability of the DNS servers */
3867 ok
= _SC_R_checkResolverReachability(store_info
,
3869 &targetPrivate
->haveDNS
,
3870 targetPrivate
->name
,
3871 targetPrivate
->serv
,
3872 targetPrivate
->if_index
,
3873 targetPrivate
->log_prefix
);
3875 /* if we could not get DNS server info */
3876 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
3877 targetPrivate
->log_prefix
);
3879 } else if (rankReachability(ns_flags
) < 2) {
3881 * if DNS servers are not (or are no longer) reachable, set
3882 * flags based on the availability of configured (but not
3886 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
3887 targetPrivate
->log_prefix
);
3889 ok
= checkAddress(store_info
,
3891 targetPrivate
->if_index
,
3893 targetPrivate
->log_prefix
);
3895 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sNo available networks"),
3896 targetPrivate
->log_prefix
);
3900 if (async
&& targetPrivate
->scheduled
) {
3902 * return "host not found", set flags appropriately,
3903 * and schedule notification.
3905 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME
,
3908 my_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
3910 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sno DNS servers are reachable"),
3911 targetPrivate
->log_prefix
);
3912 __SCNetworkReachabilityPerform(target
);
3918 /* for async requests we return the last known status */
3919 my_info
= targetPrivate
->info
;
3921 if (targetPrivate
->dnsPort
!= NULL
) {
3922 /* if request already in progress */
3923 SCLog(_sc_debug
, LOG_INFO
,
3924 CFSTR("%swaiting for DNS reply"),
3925 targetPrivate
->log_prefix
);
3926 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
3927 /* updated reachability based on the previous reply */
3928 goto checkResolvedAddress
;
3933 if (targetPrivate
->dnsRetry
!= NULL
) {
3934 /* if we already have a "retry" queued */
3938 SCLog(_sc_debug
, LOG_INFO
,
3939 CFSTR("%sstart DNS query for %s%s%s%s%s"),
3940 targetPrivate
->log_prefix
,
3941 targetPrivate
->name
!= NULL
? "name = " : "",
3942 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
3943 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
3944 targetPrivate
->serv
!= NULL
? "serv = " : "",
3945 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
3948 * initiate an async DNS query
3950 if (!startAsyncDNSQuery(target
)) {
3951 /* if we could not initiate the request, process error */
3952 goto checkResolvedAddress
;
3955 /* request initiated */
3959 SCLog(_sc_debug
, LOG_INFO
,
3960 CFSTR("%scheck DNS for %s%s%s%s%s"),
3961 targetPrivate
->log_prefix
,
3962 targetPrivate
->name
!= NULL
? "name = " : "",
3963 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
3964 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
3965 targetPrivate
->serv
!= NULL
? "serv = " : "",
3966 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
3969 * OK, all of the DNS name servers are available. Let's
3970 * resolve the nodename into an address.
3972 __dns_query_start(&dnsQueryStart
, &dnsQueryEnd
);
3974 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3975 if (targetPrivate
->if_index
== 0) {
3976 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3977 error
= getaddrinfo(targetPrivate
->name
,
3978 targetPrivate
->serv
,
3979 &targetPrivate
->hints
,
3981 #ifdef HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3983 error
= getaddrinfo_interface_sync(targetPrivate
->name
,
3984 targetPrivate
->serv
,
3985 &targetPrivate
->hints
,
3986 targetPrivate
->if_name
,
3989 #endif // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL
3991 __dns_query_end(target
,
3992 ((error
== 0) && (res
!= NULL
)), // if successful query
3994 &dnsQueryStart
, // start time
3995 &dnsQueryEnd
); // end time
3997 __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
);
3999 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
4001 checkResolvedAddress
:
4004 * We first assume that the requested host is NOT available.
4005 * Then, check each address for accessibility and return the
4006 * best status available.
4008 my_info
= NOT_REACHABLE
;
4010 if (isA_CFArray(addresses
)) {
4012 CFIndex n
= CFArrayGetCount(addresses
);
4014 for (i
= 0; i
< n
; i
++) {
4015 ReachabilityInfo ns_info
= NOT_REACHABLE
;
4016 struct sockaddr
*sa
;
4018 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
4020 ok
= checkAddress(store_info
,
4022 targetPrivate
->if_index
,
4024 targetPrivate
->log_prefix
);
4026 goto error
; /* not today */
4029 if (rankReachability(ns_info
.flags
) > rankReachability(my_info
.flags
)) {
4030 /* return the best case result */
4032 if (rankReachability(my_info
.flags
) == 2) {
4039 if ((error
== EAI_NONAME
)
4040 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
4041 || (error
== EAI_NODATA
)
4045 * the target host name could not be resolved
4047 if (!targetPrivate
->onDemandBypass
) {
4051 * our initial DNS query failed, check again to see if there
4052 * there is an OnDemand configuration that we should be using.
4054 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, TRUE
, &my_info
.flags
);
4056 /* if OnDemand connection is needed */
4061 if (!targetPrivate
->haveDNS
) {
4063 * No DNS servers are defined. Set flags based on
4064 * the availability of configured (but not active)
4067 ok
= checkAddress(store_info
,
4069 targetPrivate
->if_index
,
4071 targetPrivate
->log_prefix
);
4073 goto error
; /* not today */
4076 if ((my_info
.flags
& kSCNetworkReachabilityFlagsReachable
) &&
4077 (my_info
.flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
4079 * Since we might pick up a set of DNS servers when this connection
4080 * is established, don't reply with a "HOST NOT FOUND" error just yet.
4085 /* Host not found, not reachable! */
4086 my_info
= NOT_REACHABLE
;
4097 *reach_info
= my_info
;
4101 if (addresses
!= NULL
) CFRelease(addresses
);
4107 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
4108 SCNetworkReachabilityFlags
*flags
)
4111 ReachabilityStoreInfo store_info
;
4112 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4114 if (!isA_SCNetworkReachability(target
)) {
4115 _SCErrorSet(kSCStatusInvalidArgument
);
4119 initReachabilityStoreInfo(&store_info
);
4120 pthread_mutex_lock(&targetPrivate
->lock
);
4122 if (targetPrivate
->scheduled
) {
4123 // if being watched, return the last known (and what should be current) status
4124 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
4129 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
);
4130 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
4134 pthread_mutex_unlock(&targetPrivate
->lock
);
4135 freeReachabilityStoreInfo(&store_info
);
4141 #pragma mark Notifications
4145 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
4148 CFMutableArrayRef keys
;
4149 CFStringRef pattern
;
4150 CFMutableArrayRef patterns
;
4152 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4153 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4155 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
4156 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4157 kSCDynamicStoreDomainSetup
,
4159 CFArrayAppendValue(keys
, key
);
4162 // State:/Network/Global/DNS
4163 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4164 kSCDynamicStoreDomainState
,
4166 CFArrayAppendValue(keys
, key
);
4169 // State:/Network/Global/IPv4 (default route)
4170 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4171 kSCDynamicStoreDomainState
,
4173 CFArrayAppendValue(keys
, key
);
4176 // State:/Network/Global/OnDemand
4177 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4178 kSCDynamicStoreDomainState
,
4180 CFArrayAppendValue(keys
, key
);
4183 // Setup: per-service Interface info
4184 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4185 kSCDynamicStoreDomainSetup
,
4187 kSCEntNetInterface
);
4188 CFArrayAppendValue(patterns
, pattern
);
4191 // per-service IPv4 info
4192 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4193 kSCDynamicStoreDomainSetup
,
4196 CFArrayAppendValue(patterns
, pattern
);
4198 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4199 kSCDynamicStoreDomainState
,
4202 CFArrayAppendValue(patterns
, pattern
);
4205 // per-service IPv6 info
4206 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4207 kSCDynamicStoreDomainSetup
,
4210 CFArrayAppendValue(patterns
, pattern
);
4212 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4213 kSCDynamicStoreDomainState
,
4216 CFArrayAppendValue(patterns
, pattern
);
4219 // per-service PPP info (for existence, kSCPropNetPPPDialOnDemand, kSCPropNetPPPStatus)
4220 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4221 kSCDynamicStoreDomainSetup
,
4224 CFArrayAppendValue(patterns
, pattern
);
4226 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4227 kSCDynamicStoreDomainState
,
4230 CFArrayAppendValue(patterns
, pattern
);
4233 #if !TARGET_IPHONE_SIMULATOR
4234 // per-service VPN info (for existence, kSCPropNetVPNStatus)
4235 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4236 kSCDynamicStoreDomainSetup
,
4239 CFArrayAppendValue(patterns
, pattern
);
4241 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4242 kSCDynamicStoreDomainState
,
4245 CFArrayAppendValue(patterns
, pattern
);
4247 #endif // !TARGET_IPHONE_SIMULATOR
4249 // per-service IPSec info (for existence, kSCPropNetIPSecStatus)
4250 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4251 kSCDynamicStoreDomainSetup
,
4254 CFArrayAppendValue(patterns
, pattern
);
4256 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
4257 kSCDynamicStoreDomainState
,
4260 CFArrayAppendValue(patterns
, pattern
);
4263 #if !TARGET_OS_IPHONE
4264 // State: Power Management Capabilities
4265 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
4266 kSCDynamicStoreDomainState
,
4267 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
4268 CFArrayAppendValue(keys
, key
);
4270 #endif // TARGET_OS_IPHONE
4273 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
4275 CFRelease(patterns
);
4282 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store
,
4283 CFArrayRef changedKeys
,
4286 #if !TARGET_OS_IPHONE
4287 Boolean cpuStatusChanged
= FALSE
;
4288 #endif // !TARGET_OS_IPHONE
4289 Boolean dnsConfigChanged
= FALSE
;
4292 CFIndex nChanges
= CFArrayGetCount(changedKeys
);
4295 #if !TARGET_OS_IPHONE
4296 Boolean powerStatusChanged
= FALSE
;
4297 #endif // !TARGET_OS_IPHONE
4298 ReachabilityStoreInfo store_info
;
4299 const void * targets_q
[N_QUICK
];
4300 const void ** targets
= targets_q
;
4302 if (nChanges
== 0) {
4307 pthread_mutex_lock(&hn_lock
);
4309 nTargets
= (hn_targets
!= NULL
) ? CFSetGetCount(hn_targets
) : 0;
4310 if (nTargets
== 0) {
4311 /* if no addresses being monitored */
4315 /* grab the current time */
4316 (void)gettimeofday(&now
, NULL
);
4318 #if !TARGET_OS_IPHONE
4319 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
4320 kSCDynamicStoreDomainState
,
4321 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
4322 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
4325 num
= SCDynamicStoreCopyValue(store
, key
);
4327 if (isA_CFNumber(num
) &&
4328 CFNumberGetValue(num
, kCFNumberSInt32Type
, &power_capabilities
)) {
4329 static Boolean haveCPU_old
= TRUE
;
4330 Boolean haveCPU_new
;
4332 powerStatusChanged
= TRUE
;
4334 haveCPU_new
= (power_capabilities
& kIOPMSystemPowerStateCapabilityCPU
) != 0;
4335 if ((haveCPU_old
!= haveCPU_new
) && haveCPU_new
) {
4337 * if the power state now shows CPU availability
4338 * then we will assume that the DNS configuration
4339 * has changed. This will force us to re-issue
4340 * our DNS queries since mDNSResponder does not
4341 * attempt to resolve names when "sleeping".
4343 cpuStatusChanged
= TRUE
;
4344 dnsConfigChanged
= TRUE
;
4346 haveCPU_old
= haveCPU_new
;
4353 #endif // !TARGET_OS_IPHONE
4355 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4356 kSCDynamicStoreDomainState
,
4358 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
4359 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
4364 unsigned int changes
= 0;
4365 static const char *change_strings
[] = {
4366 // with no "power" status change
4371 #if !TARGET_OS_IPHONE
4372 // with "power" status change
4374 "network and power ",
4376 "network, DNS, and power ",
4378 // with no "power" status change (including CPU "on")
4380 "network and power* ",
4382 "network, DNS, and power* ",
4383 #endif // !TARGET_OS_IPHONE
4386 #if !TARGET_OS_IPHONE
4388 if (powerStatusChanged
) {
4390 if (cpuStatusChanged
) {
4395 #endif // !TARGET_OS_IPHONE
4398 if (dnsConfigChanged
) {
4408 SCLog(TRUE
, LOG_INFO
,
4409 CFSTR("process %sconfiguration change"),
4410 change_strings
[changes
]);
4413 initReachabilityStoreInfo(&store_info
);
4415 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
4416 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
4417 CFSetGetValues(hn_targets
, targets
);
4418 for (i
= 0; i
< nTargets
; i
++) {
4419 SCNetworkReachabilityRef target
= targets
[i
];
4420 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4422 pthread_mutex_lock(&targetPrivate
->lock
);
4424 if (dnsConfigChanged
) {
4425 targetPrivate
->last_dns
= now
;
4426 targetPrivate
->dnsRetryCount
= 0;
4429 if (targetPrivate
->type
== reachabilityTypeName
) {
4430 Boolean dnsChanged
= dnsConfigChanged
;
4434 * if the DNS configuration didn't change we still need to
4435 * check that the DNS servers are accessible.
4437 SCNetworkReachabilityFlags ns_flags
;
4440 /* check the reachability of the DNS servers */
4441 ok
= updateReachabilityStoreInfo(&store_info
, &store
, AF_UNSPEC
);
4443 ok
= _SC_R_checkResolverReachability(&store_info
,
4445 &targetPrivate
->haveDNS
,
4446 targetPrivate
->name
,
4447 targetPrivate
->serv
,
4448 targetPrivate
->if_index
,
4449 targetPrivate
->log_prefix
);
4453 /* if we could not get DNS server info */
4454 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
4455 targetPrivate
->log_prefix
);
4457 } else if (rankReachability(ns_flags
) < 2) {
4459 * if DNS servers are not (or are no longer) reachable, set
4460 * flags based on the availability of configured (but not
4463 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
4464 targetPrivate
->log_prefix
);
4470 if (targetPrivate
->dnsPort
!= NULL
) {
4471 mach_port_t mp
= CFMachPortGetPort(targetPrivate
->dnsPort
);
4473 /* cancel the outstanding DNS query */
4474 SCLog(_sc_debug
, LOG_INFO
,
4475 CFSTR("%scancel DNS query for %s%s%s%s%s"),
4476 targetPrivate
->log_prefix
,
4477 targetPrivate
->name
!= NULL
? "name = " : "",
4478 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
4479 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
4480 targetPrivate
->serv
!= NULL
? "serv = " : "",
4481 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
4482 dequeueAsyncDNSQuery(target
);
4483 getaddrinfo_async_cancel(mp
);
4486 if (targetPrivate
->dnsRetry
!= NULL
) {
4487 /* cancel the outstanding DNS retry */
4488 dequeueAsyncDNSRetry(target
);
4491 /* schedule request to resolve the name again */
4492 targetPrivate
->needResolve
= TRUE
;
4496 __SCNetworkReachabilityPerform(target
);
4498 pthread_mutex_unlock(&targetPrivate
->lock
);
4500 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
4502 freeReachabilityStoreInfo(&store_info
);
4506 pthread_mutex_unlock(&hn_lock
);
4511 #if !TARGET_OS_IPHONE
4514 darkWakeNotify(SCNetworkReachabilityRef target
)
4521 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities
)
4524 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU \
4525 | kIOPMSystemPowerStateCapabilityNetwork \
4526 | kIOPMSystemPowerStateCapabilityDisk)
4528 if ((power_capabilities
& POWER_CAPABILITIES_NEED
) != POWER_CAPABILITIES_NEED
) {
4530 * we're not awake (from a networking point of view) unless we
4531 * have the CPU, disk, *and* network.
4536 if ((power_capabilities
& kIOPMSytemPowerStateCapabilitiesMask
) == POWER_CAPABILITIES_NEED
) {
4538 * if all we have is the CPU, disk, and network than this must
4539 * be a "maintenance" wake.
4547 #endif // !TARGET_OS_IPHONE
4551 rlsPerform(void *info
)
4554 void (*context_release
)(const void *);
4555 Boolean defer
= FALSE
;
4557 ReachabilityInfo reach_info
= NOT_REACHABLE
;
4558 SCNetworkReachabilityCallBack rlsFunction
;
4559 ReachabilityStoreInfo store_info
;
4560 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
4561 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4563 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%schecking target reachability"),
4564 targetPrivate
->log_prefix
);
4567 pthread_mutex_lock(&targetPrivate
->lock
);
4569 if (targetPrivate
->dnsRetry
!= NULL
) {
4571 dequeueAsyncDNSRetry(target
);
4574 if (!targetPrivate
->scheduled
) {
4575 // if not currently scheduled
4576 pthread_mutex_unlock(&targetPrivate
->lock
);
4580 /* update reachability, notify if status changed */
4581 initReachabilityStoreInfo(&store_info
);
4582 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
);
4583 freeReachabilityStoreInfo(&store_info
);
4585 /* if reachability status not available */
4586 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%flags not available"),
4587 targetPrivate
->log_prefix
);
4588 reach_info
= NOT_REACHABLE
;
4591 #if !TARGET_OS_IPHONE
4593 * We want to defer the notification if this is a maintenance wake *and*
4594 * the reachability flags that we would be reporting to the application
4595 * are better than those that we last reported.
4597 if (!systemIsAwake(power_capabilities
)) {
4598 /* if this is a maintenace wake */
4599 reach_info
.sleeping
= TRUE
;
4601 if (rankReachability(reach_info
.flags
) >= rankReachability(targetPrivate
->info
.flags
)) {
4603 * don't report the change if the new reachability flags are
4604 * the same or "better"
4606 defer
= !darkWakeNotify(target
);
4607 } else if (__reach_equal(&targetPrivate
->last_notify
, &reach_info
)) {
4609 * if we have already posted this change
4611 defer
= !darkWakeNotify(target
);
4614 #endif // !TARGET_OS_IPHONE
4616 if (__reach_equal(&targetPrivate
->info
, &reach_info
)) {
4618 if (targetPrivate
->info
.sleeping
== reach_info
.sleeping
) {
4619 SCLog(TRUE
, LOG_INFO
,
4620 CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"),
4621 targetPrivate
->log_prefix
,
4623 reach_info
.if_index
,
4624 reach_info
.sleeping
? ", z" : "");
4626 SCLog(TRUE
, LOG_INFO
,
4627 CFSTR("%sflags/interface equiv (was 0x%08x/%hu%s, now 0x%08x/%hu%s)"),
4628 targetPrivate
->log_prefix
,
4629 targetPrivate
->info
.flags
,
4630 targetPrivate
->info
.if_index
,
4631 targetPrivate
->info
.sleeping
? ", z" : "",
4633 reach_info
.if_index
,
4634 reach_info
.sleeping
? ", z" : "");
4638 pthread_mutex_unlock(&targetPrivate
->lock
);
4642 SCLog(_sc_debug
, LOG_INFO
,
4643 CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s"),
4644 targetPrivate
->log_prefix
,
4645 targetPrivate
->info
.flags
,
4646 targetPrivate
->info
.if_index
,
4647 targetPrivate
->info
.sleeping
? ", z" : "",
4649 reach_info
.if_index
,
4650 reach_info
.sleeping
? ", z" : "",
4651 defer
? ", deferred" : "");
4653 /* as needed, defer the notification */
4655 pthread_mutex_unlock(&targetPrivate
->lock
);
4659 /* update flags / interface */
4660 targetPrivate
->info
= reach_info
;
4662 /* save last notification info */
4663 targetPrivate
->last_notify
= reach_info
;
4666 rlsFunction
= targetPrivate
->rlsFunction
;
4667 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
4668 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
4669 context_release
= targetPrivate
->rlsContext
.release
;
4671 context_info
= targetPrivate
->rlsContext
.info
;
4672 context_release
= NULL
;
4675 pthread_mutex_unlock(&targetPrivate
->lock
);
4677 if (rlsFunction
!= NULL
) {
4678 (*rlsFunction
)(target
,
4679 reach_info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
,
4683 if (context_release
!= NULL
) {
4684 (*context_release
)(context_info
);
4692 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
4693 SCNetworkReachabilityCallBack callout
,
4694 SCNetworkReachabilityContext
*context
)
4696 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4698 pthread_mutex_lock(&targetPrivate
->lock
);
4700 if (targetPrivate
->rlsContext
.release
!= NULL
) {
4701 /* let go of the current context */
4702 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
4705 targetPrivate
->rlsFunction
= callout
;
4706 targetPrivate
->rlsContext
.info
= NULL
;
4707 targetPrivate
->rlsContext
.retain
= NULL
;
4708 targetPrivate
->rlsContext
.release
= NULL
;
4709 targetPrivate
->rlsContext
.copyDescription
= NULL
;
4711 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
4712 if (context
->retain
!= NULL
) {
4713 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
4717 pthread_mutex_unlock(&targetPrivate
->lock
);
4724 reachRLSCopyDescription(const void *info
)
4726 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
4728 return CFStringCreateWithFormat(NULL
,
4730 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
4736 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
4737 CFRunLoopRef runLoop
,
4738 CFStringRef runLoopMode
,
4739 dispatch_queue_t queue
,
4742 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4743 Boolean init
= FALSE
;
4747 pthread_mutex_lock(&hn_lock
);
4749 pthread_mutex_lock(&targetPrivate
->lock
);
4751 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
4752 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
4753 _SCErrorSet(kSCStatusInvalidArgument
);
4757 /* schedule the SCNetworkReachability run loop source */
4759 if (!onDemand
&& (hn_store
== NULL
)) {
4761 * if we are not monitoring any hosts, start watching
4763 if (!dns_configuration_watch()) {
4765 _SCErrorSet(kSCStatusFailed
);
4769 hn_store
= SCDynamicStoreCreate(NULL
,
4770 CFSTR("SCNetworkReachability"),
4771 __SCNetworkReachabilityHandleChanges
,
4773 if (hn_store
== NULL
) {
4774 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed"));
4778 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
4780 hn_dispatchQueue
= dispatch_queue_create("com.apple.SCNetworkReachabilty.network_changes", NULL
);
4781 if (hn_dispatchQueue
== NULL
) {
4782 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed"));
4783 _SCErrorSet(kSCStatusFailed
);
4784 CFRelease(hn_store
);
4788 CFRetain(hn_store
); // Note: will be released when the dispatch queue is released
4789 dispatch_set_context(hn_dispatchQueue
, (void *)hn_store
);
4790 dispatch_set_finalizer_f(hn_dispatchQueue
, (dispatch_function_t
)CFRelease
);
4792 ok
= SCDynamicStoreSetDispatchQueue(hn_store
, hn_dispatchQueue
);
4794 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
4795 dispatch_release(hn_dispatchQueue
);
4796 hn_dispatchQueue
= NULL
;
4797 CFRelease(hn_store
);
4801 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
4804 if (!targetPrivate
->scheduled
) {
4805 CFRunLoopSourceContext context
= { 0 // version
4806 , (void *)target
// info
4807 , CFRetain
// retain
4808 , CFRelease
// release
4809 , reachRLSCopyDescription
// copyDescription
4814 , rlsPerform
// perform
4817 if (runLoop
!= NULL
) {
4818 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
4819 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
4822 targetPrivate
->scheduled
= TRUE
;
4823 if (targetPrivate
->type
== reachabilityTypeName
) {
4824 targetPrivate
->needResolve
= TRUE
;
4829 if (queue
!= NULL
) {
4830 targetPrivate
->dispatchQueue
= queue
;
4831 dispatch_retain(targetPrivate
->dispatchQueue
);
4833 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
4835 * if we do not already have host notifications scheduled with
4836 * this runLoop / runLoopMode
4838 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
4840 if (targetPrivate
->dnsRLS
!= NULL
) {
4841 /* if we have an active async DNS query too */
4842 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
4846 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
4849 CFSetAddValue(hn_targets
, target
);
4852 ReachabilityInfo reach_info
= NOT_REACHABLE
;
4853 ReachabilityStoreInfo store_info
;
4856 * if we have yet to schedule SC notifications for this address
4857 * - initialize current reachability status
4859 initReachabilityStoreInfo(&store_info
);
4860 if (__SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
)) {
4862 * if reachability status available
4864 * - schedule notification to report status via callback
4866 targetPrivate
->info
= reach_info
;
4867 __SCNetworkReachabilityPerform(target
);
4869 /* if reachability status not available, async lookup started */
4870 targetPrivate
->info
= NOT_REACHABLE
;
4872 freeReachabilityStoreInfo(&store_info
);
4875 if (targetPrivate
->onDemandServer
!= NULL
) {
4876 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, queue
, TRUE
);
4879 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%sscheduled"),
4880 targetPrivate
->log_prefix
);
4886 pthread_mutex_unlock(&targetPrivate
->lock
);
4888 pthread_mutex_unlock(&hn_lock
);
4895 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
4896 CFRunLoopRef runLoop
,
4897 CFStringRef runLoopMode
,
4900 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4905 pthread_mutex_lock(&hn_lock
);
4907 pthread_mutex_lock(&targetPrivate
->lock
);
4909 if (((runLoop
== NULL
) && (targetPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
4910 ((runLoop
!= NULL
) && (targetPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are not)
4911 _SCErrorSet(kSCStatusInvalidArgument
);
4915 if (!targetPrivate
->scheduled
) {
4916 // if not currently scheduled
4917 _SCErrorSet(kSCStatusInvalidArgument
);
4921 // first, unschedule the target specific sources
4922 if (targetPrivate
->dispatchQueue
!= NULL
) {
4923 if (targetPrivate
->onDemandServer
!= NULL
) {
4924 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
4927 if (!_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
4928 // if not currently scheduled
4929 _SCErrorSet(kSCStatusInvalidArgument
);
4933 if (targetPrivate
->onDemandServer
!= NULL
) {
4934 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, TRUE
);
4937 n
= CFArrayGetCount(targetPrivate
->rlList
);
4938 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
4939 // if target is no longer scheduled for this runLoop / runLoopMode
4940 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
4942 if (targetPrivate
->dnsRLS
!= NULL
) {
4943 // if we have an active async DNS query too
4944 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
4948 // if *all* notifications have been unscheduled
4949 CFRelease(targetPrivate
->rlList
);
4950 targetPrivate
->rlList
= NULL
;
4951 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
4952 CFRelease(targetPrivate
->rls
);
4953 targetPrivate
->rls
= NULL
;
4959 // if *all* notifications have been unscheduled
4960 targetPrivate
->scheduled
= FALSE
;
4963 CFSetRemoveValue(hn_targets
, target
); // cleanup notification resources
4966 if (targetPrivate
->dnsPort
!= NULL
) {
4967 mach_port_t mp
= CFMachPortGetPort(targetPrivate
->dnsPort
);
4969 // if we have an active async DNS query
4970 dequeueAsyncDNSQuery(target
);
4971 getaddrinfo_async_cancel(mp
);
4974 if (targetPrivate
->dnsRetry
!= NULL
) {
4975 // if we have an outstanding DNS retry
4976 dequeueAsyncDNSRetry(target
);
4980 if (runLoop
== NULL
) {
4981 dispatch_release(targetPrivate
->dispatchQueue
);
4982 targetPrivate
->dispatchQueue
= NULL
;
4985 n
= CFSetGetCount(hn_targets
);
4987 // if we are no longer monitoring any targets
4988 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
4989 dispatch_release(hn_dispatchQueue
);
4990 hn_dispatchQueue
= NULL
;
4991 CFRelease(hn_store
);
4993 CFRelease(hn_targets
);
4997 * until we start monitoring again, ensure that
4998 * any resources associated with tracking the
4999 * DNS configuration have been released.
5001 dns_configuration_unwatch();
5004 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%sunscheduled"),
5005 targetPrivate
->log_prefix
);
5011 pthread_mutex_unlock(&targetPrivate
->lock
);
5013 pthread_mutex_unlock(&hn_lock
);
5019 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
5020 CFRunLoopRef runLoop
,
5021 CFStringRef runLoopMode
)
5023 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
5024 _SCErrorSet(kSCStatusInvalidArgument
);
5028 return __SCNetworkReachabilityScheduleWithRunLoop(target
, runLoop
, runLoopMode
, NULL
, FALSE
);
5032 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
5033 CFRunLoopRef runLoop
,
5034 CFStringRef runLoopMode
)
5036 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
5037 _SCErrorSet(kSCStatusInvalidArgument
);
5041 return __SCNetworkReachabilityUnscheduleFromRunLoop(target
, runLoop
, runLoopMode
, FALSE
);
5045 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
5046 dispatch_queue_t queue
)
5050 if (!isA_SCNetworkReachability(target
)) {
5051 _SCErrorSet(kSCStatusInvalidArgument
);
5055 if (queue
!= NULL
) {
5056 ok
= __SCNetworkReachabilityScheduleWithRunLoop(target
, NULL
, NULL
, queue
, FALSE
);
5058 ok
= __SCNetworkReachabilityUnscheduleFromRunLoop(target
, NULL
, NULL
, FALSE
);