2 * Copyright (c) 2003-2009 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>
38 #include <dispatch/dispatch.h>
39 #endif // !TARGET_OS_IPHONE
40 #include <CoreFoundation/CoreFoundation.h>
41 #include <CoreFoundation/CFRuntime.h>
42 #include <SystemConfiguration/SystemConfiguration.h>
43 #include <SystemConfiguration/SCValidation.h>
44 #include <SystemConfiguration/SCPrivate.h>
46 #include <libkern/OSAtomic.h>
49 #include <IOKit/pwr_mgt/IOPMLibPrivate.h>
50 #endif // !TARGET_OS_IPHONE
54 #include <netinet/in.h>
55 #include <arpa/inet.h>
57 #include <netdb_async.h>
60 #include <sys/ioctl.h>
61 #include <sys/socket.h>
63 #include <net/if_dl.h>
64 #include <net/if_types.h>
65 #define KERNEL_PRIVATE
66 #include <net/route.h>
70 #define s6_addr16 __u6_addr.__u6_addr16
73 #include <ppp/ppp_msg.h>
77 #define kSCNetworkReachabilityFlagsFirstResolvePending (1<<31)
84 reachabilityTypeAddress
,
85 reachabilityTypeAddressPair
,
90 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
91 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
92 static void rlsPerform(void *info
);
96 __SCNetworkReachabilityScheduleWithRunLoop (SCNetworkReachabilityRef target
,
98 CFStringRef runLoopMode
,
100 dispatch_queue_t queue
,
101 #else // !TARGET_OS_IPHONE
103 #endif // !TARGET_OS_IPHONE
106 __SCNetworkReachabilityUnscheduleFromRunLoop (SCNetworkReachabilityRef target
,
107 CFRunLoopRef runLoop
,
108 CFStringRef runLoopMode
,
113 SCNetworkReachabilityFlags flags
;
121 /* base CFType information */
122 CFRuntimeBase cfBase
;
125 pthread_mutex_t lock
;
130 /* target host name */
133 struct addrinfo hints
;
135 CFArrayRef resolvedAddress
; /* CFArray[CFData] */
136 int resolvedAddressError
;
138 /* local & remote addresses */
139 struct sockaddr
*localAddress
;
140 struct sockaddr
*remoteAddress
;
142 /* current reachability flags */
143 ReachabilityInfo info
;
144 ReachabilityInfo last_notify
;
146 /* run loop source, callout, context, rl scheduling info */
148 CFRunLoopSourceRef rls
;
149 SCNetworkReachabilityCallBack rlsFunction
;
150 SCNetworkReachabilityContext rlsContext
;
151 CFMutableArrayRef rlList
;
153 #if !TARGET_OS_IPHONE
154 dispatch_queue_t dispatchQueue
; // SCNetworkReachabilitySetDispatchQueue
155 dispatch_queue_t asyncDNSQueue
;
156 dispatch_source_t asyncDNSSource
;
157 #endif // !TARGET_OS_IPHONE
159 /* [async] DNS query info */
162 CFMachPortRef dnsPort
;
163 CFRunLoopSourceRef dnsRLS
;
164 struct timeval dnsQueryStart
;
167 Boolean onDemandBypass
;
168 CFStringRef onDemandName
;
169 CFStringRef onDemandRemoteAddress
;
170 SCNetworkReachabilityRef onDemandServer
;
171 CFStringRef onDemandServiceID
;
176 } SCNetworkReachabilityPrivate
, *SCNetworkReachabilityPrivateRef
;
179 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
182 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
184 "SCNetworkReachability", // className
187 __SCNetworkReachabilityDeallocate
, // dealloc
190 NULL
, // copyFormattingDesc
191 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
195 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
196 static ReachabilityInfo NOT_REACHABLE
= { 0, 0, FALSE
};
197 static ReachabilityInfo NOT_REPORTED
= { 0xFFFFFFFF, 0, FALSE
};
198 static int rtm_seq
= 0;
201 #if !TARGET_OS_IPHONE
203 * Power capabilities (sleep/wake)
205 static IOPMSystemPowerStateCapabilities power_capabilities
= kIOPMSytemPowerStateCapabilitiesMask
;
206 #endif // !TARGET_OS_IPHONE
210 * host "something has changed" notifications
213 static pthread_mutex_t hn_lock
= PTHREAD_MUTEX_INITIALIZER
;
214 static SCDynamicStoreRef hn_store
= NULL
;
215 #if !TARGET_OS_IPHONE
216 static dispatch_queue_t hn_dispatchQueue
= NULL
;
217 #else // !TARGET_OS_IPHONE
218 static CFRunLoopSourceRef hn_storeRLS
= NULL
;
219 static CFMutableArrayRef hn_rlList
= NULL
;
220 #endif // !TARGET_OS_IPHONE
221 static CFMutableSetRef hn_targets
= NULL
;
229 dns_config_t
*config
;
231 } dns_configuration_t
;
234 static pthread_mutex_t dns_lock
= PTHREAD_MUTEX_INITIALIZER
;
235 static dns_configuration_t
*dns_configuration
= NULL
;
236 static int dns_token
;
237 static Boolean dns_token_valid
= FALSE
;
240 static __inline__ CFTypeRef
241 isA_SCNetworkReachability(CFTypeRef obj
)
243 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
248 __log_query_time(SCNetworkReachabilityRef target
, Boolean found
, Boolean async
, struct timeval
*start
)
250 struct timeval dnsQueryComplete
;
251 struct timeval dnsQueryElapsed
;
252 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
258 if (start
->tv_sec
== 0) {
262 (void) gettimeofday(&dnsQueryComplete
, NULL
);
263 timersub(&dnsQueryComplete
, start
, &dnsQueryElapsed
);
264 SCLog(TRUE
, LOG_INFO
,
265 CFSTR("%s%ssync DNS complete%s (query time = %d.%3.3d)"),
266 targetPrivate
->log_prefix
,
268 found
? "" : ", host not found",
269 dnsQueryElapsed
.tv_sec
,
270 dnsQueryElapsed
.tv_usec
/ 1000);
277 updatePPPStatus(SCDynamicStoreRef
*storeP
,
278 const struct sockaddr
*sa
,
280 SCNetworkReachabilityFlags
*flags
,
281 CFStringRef
*ppp_server
,
282 const char *log_prefix
)
284 CFDictionaryRef dict
= NULL
;
287 const void * keys_q
[N_QUICK
];
288 const void ** keys
= keys_q
;
291 int sc_status
= kSCStatusReachabilityUnknown
;
292 SCDynamicStoreRef store
= *storeP
;
293 const void * values_q
[N_QUICK
];
294 const void ** values
= values_q
;
296 switch (sa
->sa_family
) {
298 entity
= kSCEntNetIPv4
;
301 entity
= kSCEntNetIPv6
;
308 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
310 SCLog(TRUE
, LOG_ERR
, CFSTR("updatePPPStatus SCDynamicStoreCreate() failed"));
316 // grab a snapshot of the PPP configuration from the dynamic store
319 CFMutableArrayRef patterns
;
321 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
322 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
323 kSCDynamicStoreDomainState
,
326 CFArrayAppendValue(patterns
, pattern
);
328 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
329 kSCDynamicStoreDomainSetup
,
332 CFArrayAppendValue(patterns
, pattern
);
334 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
335 kSCDynamicStoreDomainState
,
338 CFArrayAppendValue(patterns
, pattern
);
340 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
344 // if we could not access the dynamic store
348 sc_status
= kSCStatusOK
;
350 // look for the service which matches the provided interface
351 n
= CFDictionaryGetCount(dict
);
356 ppp_if
= CFStringCreateWithCStringNoCopy(NULL
,
358 kCFStringEncodingASCII
,
361 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
362 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
363 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
365 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
367 for (i
=0; i
< n
; i
++) {
368 CFArrayRef components
;
371 CFDictionaryRef p_setup
;
372 CFDictionaryRef p_state
;
375 CFStringRef service
= NULL
;
376 CFStringRef s_key
= (CFStringRef
) keys
[i
];
377 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
380 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
384 if (!CFStringHasSuffix(s_key
, entity
)) {
385 continue; // if not an IPv4 or IPv6 entity
388 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
389 if (!isA_CFString(s_if
)) {
390 continue; // if no interface
393 if (!CFEqual(ppp_if
, s_if
)) {
394 continue; // if not this interface
397 // extract the service ID, get the PPP "state" entity for
398 // the "Status", and get the PPP "setup" entity for the
399 // the "DialOnDemand" flag
400 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
401 if (CFArrayGetCount(components
) != 5) {
402 CFRelease(components
);
405 service
= CFArrayGetValueAtIndex(components
, 3);
406 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
407 kSCDynamicStoreDomainState
,
410 p_state
= CFDictionaryGetValue(dict
, key
);
412 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
413 kSCDynamicStoreDomainSetup
,
416 p_setup
= CFDictionaryGetValue(dict
, key
);
418 CFRelease(components
);
420 // ensure that this is a PPP service
421 if (!isA_CFDictionary(p_state
)) {
425 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
428 if (ppp_server
!= NULL
) {
429 *ppp_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
430 *ppp_server
= isA_CFString(*ppp_server
);
431 if (*ppp_server
!= NULL
) {
432 CFRetain(*ppp_server
);
437 if (!CFDictionaryGetValueIfPresent(p_state
,
439 (const void **)&num
) ||
440 !isA_CFNumber(num
) ||
441 !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) {
444 switch (ppp_status
) {
446 // if we're really UP and RUNNING
449 // if we're effectively UP and RUNNING
452 case PPP_STATERESERVED
:
453 // if we're not connected at all
454 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link idle, dial-on-traffic to connect"),
456 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
459 // if we're in the process of [dis]connecting
460 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s PPP link, connection in progress"),
462 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
466 // get PPP dial-on-traffic status
467 if (isA_CFDictionary(p_setup
) &&
468 CFDictionaryGetValueIfPresent(p_setup
,
469 kSCPropNetPPPDialOnDemand
,
470 (const void **)&num
) &&
472 CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
) &&
474 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
475 if (ppp_status
== PPP_IDLE
) {
476 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
484 if (keys
!= keys_q
) {
485 CFAllocatorDeallocate(NULL
, keys
);
486 CFAllocatorDeallocate(NULL
, values
);
491 if (dict
!= NULL
) CFRelease(dict
);
497 updatePPPAvailable(SCDynamicStoreRef
*storeP
,
498 const struct sockaddr
*sa
,
499 SCNetworkReachabilityFlags
*flags
,
500 const char *log_prefix
)
502 CFDictionaryRef dict
= NULL
;
505 const void * keys_q
[N_QUICK
];
506 const void ** keys
= keys_q
;
508 int sc_status
= kSCStatusReachabilityUnknown
;
509 SCDynamicStoreRef store
= *storeP
;
510 const void * values_q
[N_QUICK
];
511 const void ** values
= values_q
;
514 entity
= kSCEntNetIPv4
;
516 switch (sa
->sa_family
) {
518 entity
= kSCEntNetIPv4
;
521 entity
= kSCEntNetIPv6
;
529 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
531 SCLog(TRUE
, LOG_ERR
, CFSTR("updatePPPAvailable SCDynamicStoreCreate() failed"));
537 // grab a snapshot of the PPP configuration from the dynamic store
540 CFMutableArrayRef patterns
;
542 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
543 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
544 kSCDynamicStoreDomainSetup
,
547 CFArrayAppendValue(patterns
, pattern
);
549 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
550 kSCDynamicStoreDomainSetup
,
553 CFArrayAppendValue(patterns
, pattern
);
555 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
556 kSCDynamicStoreDomainSetup
,
559 CFArrayAppendValue(patterns
, pattern
);
561 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
565 // if we could not access the dynamic store
569 sc_status
= kSCStatusOK
;
571 // look for an available service which will provide connectivity
572 // for the requested address family.
573 n
= CFDictionaryGetCount(dict
);
578 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
579 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
580 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
582 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
584 for (i
= 0; i
< n
; i
++) {
585 CFArrayRef components
;
586 Boolean found
= FALSE
;
588 CFDictionaryRef i_dict
;
590 CFDictionaryRef p_dict
;
592 CFStringRef s_key
= (CFStringRef
) keys
[i
];
593 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
595 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
599 if (!CFStringHasSuffix(s_key
, entity
)) {
600 continue; // if not an IPv4 or IPv6 entity
603 // extract service ID
604 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
605 if (CFArrayGetCount(components
) != 5) {
606 CFRelease(components
);
609 service
= CFArrayGetValueAtIndex(components
, 3);
611 // check for [non-VPN] PPP entity
612 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
613 kSCDynamicStoreDomainSetup
,
616 p_dict
= CFDictionaryGetValue(dict
, p_key
);
619 i_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
620 kSCDynamicStoreDomainSetup
,
623 i_dict
= CFDictionaryGetValue(dict
, i_key
);
626 if (isA_CFDictionary(p_dict
) &&
627 isA_CFDictionary(i_dict
) &&
628 CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) {
631 // we have a PPP service for this address family
634 *flags
|= kSCNetworkReachabilityFlagsReachable
;
635 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
636 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
638 // get PPP dial-on-traffic status
639 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
640 if (isA_CFNumber(num
)) {
643 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
645 *flags
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
;
651 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after connect)"),
653 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service = %@"),
660 CFRelease(components
);
667 if (keys
!= keys_q
) {
668 CFAllocatorDeallocate(NULL
, keys
);
669 CFAllocatorDeallocate(NULL
, values
);
674 if (dict
!= NULL
) CFRelease(dict
);
680 updateIPSecStatus(SCDynamicStoreRef
*storeP
,
681 const struct sockaddr
*sa
,
683 SCNetworkReachabilityFlags
*flags
,
684 CFStringRef
*ipsec_server
)
686 CFDictionaryRef dict
= NULL
;
689 CFStringRef ipsec_if
;
690 const void * keys_q
[N_QUICK
];
691 const void ** keys
= keys_q
;
693 int sc_status
= kSCStatusReachabilityUnknown
;
694 SCDynamicStoreRef store
= *storeP
;
695 const void * values_q
[N_QUICK
];
696 const void ** values
= values_q
;
698 switch (sa
->sa_family
) {
700 entity
= kSCEntNetIPv4
;
703 entity
= kSCEntNetIPv6
;
710 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
712 SCLog(TRUE
, LOG_ERR
, CFSTR("updateIPSecStatus SCDynamicStoreCreate() failed"));
718 // grab a snapshot of the IPSec configuration from the dynamic store
721 CFMutableArrayRef patterns
;
723 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
724 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
725 kSCDynamicStoreDomainState
,
728 CFArrayAppendValue(patterns
, pattern
);
730 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
731 kSCDynamicStoreDomainSetup
,
734 CFArrayAppendValue(patterns
, pattern
);
736 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
740 // if we could not access the dynamic store
744 sc_status
= kSCStatusOK
;
746 // look for the service which matches the provided interface
747 n
= CFDictionaryGetCount(dict
);
752 ipsec_if
= CFStringCreateWithCStringNoCopy(NULL
,
754 kCFStringEncodingASCII
,
757 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
758 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
759 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
761 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
763 for (i
=0; i
< n
; i
++) {
764 CFArrayRef components
;
766 CFDictionaryRef i_setup
;
767 CFStringRef service
= NULL
;
768 CFStringRef s_key
= (CFStringRef
) keys
[i
];
769 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
772 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
776 if (!CFStringHasSuffix(s_key
, entity
)) {
777 continue; // if not an IPv4 or IPv6 entity
780 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
781 if (!isA_CFString(s_if
)) {
782 continue; // if no interface
785 if (!CFEqual(ipsec_if
, s_if
)) {
786 continue; // if not this interface
789 // extract the service ID and get the IPSec "setup" entity
790 // to confirm that we're looking at what we're expecting
791 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
792 if (CFArrayGetCount(components
) != 5) {
793 CFRelease(components
);
796 service
= CFArrayGetValueAtIndex(components
, 3);
797 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
798 kSCDynamicStoreDomainSetup
,
801 i_setup
= CFDictionaryGetValue(dict
, key
);
803 CFRelease(components
);
805 // ensure that this is an IPSec service
806 if (!isA_CFDictionary(i_setup
)) {
810 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
813 if (ipsec_server
!= NULL
) {
814 *ipsec_server
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress"));
815 *ipsec_server
= isA_CFString(*ipsec_server
);
816 if (*ipsec_server
!= NULL
) {
817 CFRetain(*ipsec_server
);
825 if (keys
!= keys_q
) {
826 CFAllocatorDeallocate(NULL
, keys
);
827 CFAllocatorDeallocate(NULL
, values
);
832 if (dict
!= NULL
) CFRelease(dict
);
839 #define ROUNDUP(a, size) \
840 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
842 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
843 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
848 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
852 for (i
= 0; i
< RTAX_MAX
; i
++) {
853 if (addrs
& (1 << i
)) {
862 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
868 struct sockaddr
*rti_info
[RTAX_MAX
];
869 struct rt_msghdr
*rtm
;
870 struct sockaddr_dl
*sdl
;
871 } route_info
, *route_info_p
;
876 * returns zero if route exists an data returned, EHOSTUNREACH
877 * if no route, or errno for any other error.
880 route_get(const struct sockaddr
*address
,
884 pid_t pid
= getpid();
887 int32_t seq
= OSAtomicIncrement32Barrier(&rtm_seq
);
888 #ifndef RTM_GET_SILENT
889 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
890 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
891 int sosize
= 48 * 1024;
894 bzero(info
, sizeof(*info
));
896 info
->rtm
= (struct rt_msghdr
*)&info
->buf
;
897 info
->rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
898 info
->rtm
->rtm_version
= RTM_VERSION
;
899 #ifdef RTM_GET_SILENT
900 info
->rtm
->rtm_type
= RTM_GET_SILENT
;
902 info
->rtm
->rtm_type
= RTM_GET
;
904 info
->rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
905 info
->rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
906 info
->rtm
->rtm_pid
= pid
;
907 info
->rtm
->rtm_seq
= seq
;
909 switch (address
->sa_family
) {
911 struct sockaddr_in6
*sin6
;
913 sin6
= (struct sockaddr_in6
*)address
;
914 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
915 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
916 (sin6
->sin6_scope_id
!= 0)) {
917 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
918 sin6
->sin6_scope_id
= 0;
924 sa
= (struct sockaddr
*) (info
->rtm
+ 1);
925 bcopy(address
, sa
, address
->sa_len
);
926 n
= ROUNDUP(sa
->sa_len
, sizeof(uint32_t));
927 info
->rtm
->rtm_msglen
+= n
;
929 info
->sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
930 info
->sdl
->sdl_family
= AF_LINK
;
931 info
->sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
932 n
= ROUNDUP(info
->sdl
->sdl_len
, sizeof(uint32_t));
933 info
->rtm
->rtm_msglen
+= n
;
935 #ifndef RTM_GET_SILENT
936 pthread_mutex_lock(&lock
);
938 rsock
= socket(PF_ROUTE
, SOCK_RAW
, 0);
942 #ifndef RTM_GET_SILENT
943 pthread_mutex_unlock(&lock
);
945 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error
));
949 #ifndef RTM_GET_SILENT
950 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
954 pthread_mutex_unlock(&lock
);
955 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error
));
960 if (write(rsock
, &info
->buf
, info
->rtm
->rtm_msglen
) == -1) {
964 #ifndef RTM_GET_SILENT
965 pthread_mutex_unlock(&lock
);
967 if (error
!= ESRCH
) {
968 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(error
));
975 * Type, seq, pid identify our response.
976 * Routing sockets are broadcasters on input.
981 n
= read(rsock
, (void *)&info
->buf
, sizeof(info
->buf
));
983 if (errno
!= EINTR
) {
987 #ifndef RTM_GET_SILENT
988 pthread_mutex_unlock(&lock
);
990 SCLog(TRUE
, LOG_ERR
, CFSTR("read() failed: %s"), strerror(error
));
994 } while ((info
->rtm
->rtm_type
!= RTM_GET
) ||
995 (info
->rtm
->rtm_seq
!= seq
) ||
996 (info
->rtm
->rtm_pid
!= pid
));
999 #ifndef RTM_GET_SILENT
1000 pthread_mutex_unlock(&lock
);
1003 get_rtaddrs(info
->rtm
->rtm_addrs
, sa
, info
->rti_info
);
1009 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), info
->rtm
->rtm_flags
);
1011 if ((info
->rti_info
[RTAX_NETMASK
] != NULL
) && (info
->rti_info
[RTAX_DST
] != NULL
)) {
1012 info
->rti_info
[RTAX_NETMASK
]->sa_family
= info
->rti_info
[RTAX_DST
]->sa_family
;
1015 for (i
= 0; i
< RTAX_MAX
; i
++) {
1016 if (info
->rti_info
[i
] != NULL
) {
1019 _SC_sockaddr_to_string(info
->rti_info
[i
], addr
, sizeof(addr
));
1020 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, addr
);
1024 #endif /* LOG_RTADDRS */
1026 if ((info
->rti_info
[RTAX_IFP
] == NULL
) ||
1027 (info
->rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
1028 /* no interface info */
1029 SCLog(TRUE
, LOG_DEBUG
, CFSTR("route_get() no interface info"));
1033 info
->sdl
= (struct sockaddr_dl
*) info
->rti_info
[RTAX_IFP
];
1034 if ((info
->sdl
->sdl_nlen
== 0) || (info
->sdl
->sdl_nlen
> IFNAMSIZ
)) {
1035 /* no interface name */
1036 return EHOSTUNREACH
;
1044 checkAddress(SCDynamicStoreRef
*storeP
,
1045 const struct sockaddr
*address
,
1046 ReachabilityInfo
*reach_info
,
1047 const char *log_prefix
)
1051 char if_name
[IFNAMSIZ
+ 1];
1054 int sc_status
= kSCStatusReachabilityUnknown
;
1055 CFStringRef server
= NULL
;
1056 char *statusMessage
= NULL
;
1057 struct sockaddr_in v4mapped
;
1059 *reach_info
= NOT_REACHABLE
;
1061 if (address
== NULL
) {
1062 /* special case: check only for available paths off the system */
1063 goto checkAvailable
;
1066 switch (address
->sa_family
) {
1072 _SC_sockaddr_to_string(address
, addr
, sizeof(addr
));
1073 SCLog(TRUE
, LOG_INFO
, CFSTR("%scheckAddress(%s)"),
1080 * if no code for this address family (yet)
1082 SCLog(TRUE
, LOG_INFO
,
1083 CFSTR("checkAddress(): unexpected address family %d"),
1084 address
->sa_family
);
1085 sc_status
= kSCStatusInvalidArgument
;
1089 if (address
->sa_family
== AF_INET6
) {
1090 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)address
;
1092 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) {
1093 bzero(&v4mapped
, sizeof(v4mapped
));
1094 v4mapped
.sin_len
= sizeof(v4mapped
);
1095 v4mapped
.sin_family
= AF_INET
;
1096 v4mapped
.sin_port
= sin6
->sin6_port
;
1097 v4mapped
.sin_addr
.s_addr
= sin6
->sin6_addr
.__u6_addr
.__u6_addr32
[3];
1098 address
= (struct sockaddr
*)&v4mapped
;
1102 ret
= route_get(address
, &info
);
1108 goto checkAvailable
;
1115 /* get the interface flags */
1117 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
1119 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
1123 bzero(&ifr
, sizeof(ifr
));
1124 bcopy(info
.sdl
->sdl_data
, ifr
.ifr_name
, info
.sdl
->sdl_nlen
);
1126 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) == -1) {
1127 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl() failed: %s"), strerror(errno
));
1131 if (!(ifr
.ifr_flags
& IFF_UP
)) {
1132 goto checkAvailable
;
1135 statusMessage
= "isReachable";
1136 reach_info
->flags
|= kSCNetworkReachabilityFlagsReachable
;
1138 if (info
.rtm
->rtm_flags
& RTF_LOCAL
) {
1139 statusMessage
= "isReachable (is a local address)";
1140 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1141 } else if (ifr
.ifr_flags
& IFF_LOOPBACK
) {
1142 statusMessage
= "isReachable (is loopback network)";
1143 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1144 } else if ((info
.rti_info
[RTAX_IFA
] != NULL
) &&
1145 (info
.rti_info
[RTAX_IFA
]->sa_family
!= AF_LINK
)) {
1146 void *addr1
= (void *)address
;
1147 void *addr2
= (void *)info
.rti_info
[RTAX_IFA
];
1148 size_t len
= address
->sa_len
;
1150 if ((address
->sa_family
!= info
.rti_info
[RTAX_IFA
]->sa_family
) &&
1151 (address
->sa_len
!= info
.rti_info
[RTAX_IFA
]->sa_len
)) {
1152 SCLog(TRUE
, LOG_NOTICE
,
1153 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
1156 info
.rti_info
[RTAX_IFA
]->sa_family
,
1157 info
.rti_info
[RTAX_IFA
]->sa_len
);
1161 switch (address
->sa_family
) {
1163 addr1
= &((struct sockaddr_in
*)address
)->sin_addr
;
1164 addr2
= &((struct sockaddr_in
*)info
.rti_info
[RTAX_IFA
])->sin_addr
;
1165 len
= sizeof(struct in_addr
);
1170 if (((struct sockaddr_in
*)address
)->sin_addr
.s_addr
== 0) {
1171 statusMessage
= "isReachable (this host)";
1172 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1176 addr1
= &((struct sockaddr_in6
*)address
)->sin6_addr
;
1177 addr2
= &((struct sockaddr_in6
*)info
.rti_info
[RTAX_IFA
])->sin6_addr
;
1178 len
= sizeof(struct in6_addr
);
1184 if (bcmp(addr1
, addr2
, len
) == 0) {
1185 statusMessage
= "isReachable (is interface address)";
1186 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1190 if (!(info
.rtm
->rtm_flags
& RTF_GATEWAY
) &&
1191 (info
.rti_info
[RTAX_GATEWAY
] != NULL
) &&
1192 (info
.rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
) &&
1193 !(ifr
.ifr_flags
& IFF_POINTOPOINT
)) {
1194 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1197 bzero(&if_name
, sizeof(if_name
));
1198 bcopy(info
.sdl
->sdl_data
,
1200 (info
.sdl
->sdl_nlen
<= IFNAMSIZ
) ? info
.sdl
->sdl_nlen
: IFNAMSIZ
);
1202 reach_info
->if_index
= info
.sdl
->sdl_index
;
1205 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = %s"), log_prefix
, statusMessage
);
1206 SCLog(TRUE
, LOG_INFO
, CFSTR("%s device = %s (%hu)"), log_prefix
, if_name
, info
.sdl
->sdl_index
);
1207 SCLog(TRUE
, LOG_INFO
, CFSTR("%s sdl_type = 0x%x"), log_prefix
, info
.sdl
->sdl_type
);
1208 SCLog(TRUE
, LOG_INFO
, CFSTR("%s ifr_flags = 0x%04hx"), log_prefix
, ifr
.ifr_flags
);
1209 SCLog(TRUE
, LOG_INFO
, CFSTR("%s rtm_flags = 0x%08x"), log_prefix
, info
.rtm
->rtm_flags
);
1212 sc_status
= kSCStatusOK
;
1214 if (ifr
.ifr_flags
& IFF_POINTOPOINT
) {
1215 reach_info
->flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
1218 if (info
.sdl
->sdl_type
== IFT_PPP
) {
1220 * 1. check if PPP service
1221 * 2. check for dial-on-demand PPP link that is not yet connected
1222 * 3. get PPP server address
1224 sc_status
= updatePPPStatus(storeP
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
);
1225 } else if (info
.sdl
->sdl_type
== IFT_OTHER
) {
1227 * 1. check if IPSec service
1228 * 2. get IPSec server address
1230 sc_status
= updateIPSecStatus(storeP
, address
, if_name
, &reach_info
->flags
, &server
);
1239 sc_status
= updatePPPAvailable(storeP
, address
, &reach_info
->flags
, log_prefix
);
1243 if (reach_info
->flags
== 0) {
1244 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s cannot be reached"), log_prefix
);
1247 if (isock
!= -1) (void)close(isock
);
1248 if (server
!= NULL
) CFRelease(server
);
1249 if (sc_status
!= kSCStatusOK
) {
1250 _SCErrorSet(sc_status
);
1259 #pragma mark SCNetworkReachability APIs
1263 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
1265 CFAllocatorRef allocator
= CFGetAllocator(cf
);
1266 CFMutableStringRef result
;
1267 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1269 result
= CFStringCreateMutable(allocator
, 0);
1270 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
1271 switch (targetPrivate
->type
) {
1272 case reachabilityTypeAddress
:
1273 case reachabilityTypeAddressPair
: {
1276 if (targetPrivate
->localAddress
!= NULL
) {
1277 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
1278 CFStringAppendFormat(result
, NULL
, CFSTR("local address = %s"),
1282 if (targetPrivate
->remoteAddress
!= NULL
) {
1283 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
1284 CFStringAppendFormat(result
, NULL
, CFSTR("%s%saddress = %s"),
1285 targetPrivate
->localAddress
? ", " : "",
1286 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
1291 case reachabilityTypeName
: {
1292 if ((targetPrivate
->name
!= NULL
)) {
1293 CFStringAppendFormat(result
, NULL
, CFSTR("name = %s"), targetPrivate
->name
);
1295 if ((targetPrivate
->serv
!= NULL
)) {
1296 CFStringAppendFormat(result
, NULL
, CFSTR("%sserv = %s"),
1297 targetPrivate
->name
!= NULL
? ", " : "",
1298 targetPrivate
->serv
);
1300 if ((targetPrivate
->resolvedAddress
!= NULL
) || (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1301 if (targetPrivate
->resolvedAddress
!= NULL
) {
1302 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
1304 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddress
);
1306 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
1307 for (i
= 0; i
< n
; i
++) {
1310 struct sockaddr
*sa
;
1312 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
);
1313 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
1314 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
1315 CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"),
1319 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
1321 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)"));
1324 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
1325 gai_strerror(targetPrivate
->resolvedAddressError
));
1327 } else if (targetPrivate
->dnsPort
!= NULL
) {
1328 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
1333 if (targetPrivate
->scheduled
) {
1334 CFStringAppendFormat(result
,
1336 CFSTR(", flags = 0x%08x, if_index = %hu"),
1337 targetPrivate
->info
.flags
,
1338 targetPrivate
->info
.if_index
);
1340 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
1347 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1349 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1351 /* release resources */
1353 pthread_mutex_destroy(&targetPrivate
->lock
);
1355 if (targetPrivate
->name
!= NULL
)
1356 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
1358 if (targetPrivate
->serv
!= NULL
)
1359 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->serv
);
1361 if (targetPrivate
->resolvedAddress
!= NULL
)
1362 CFRelease(targetPrivate
->resolvedAddress
);
1364 if (targetPrivate
->localAddress
!= NULL
)
1365 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
1367 if (targetPrivate
->remoteAddress
!= NULL
)
1368 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
1370 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1371 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1374 if (targetPrivate
->onDemandName
!= NULL
) {
1375 CFRelease(targetPrivate
->onDemandName
);
1378 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
1379 CFRelease(targetPrivate
->onDemandRemoteAddress
);
1382 if (targetPrivate
->onDemandServer
!= NULL
) {
1383 CFRelease(targetPrivate
->onDemandServer
);
1386 if (targetPrivate
->onDemandServiceID
!= NULL
) {
1387 CFRelease(targetPrivate
->onDemandServiceID
);
1395 __SCNetworkReachabilityInitialize(void)
1397 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
1403 __SCNetworkReachabilityPerform(SCNetworkReachabilityRef target
)
1405 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1407 #if !TARGET_OS_IPHONE
1408 if (targetPrivate
->dispatchQueue
!= NULL
) {
1410 dispatch_async(targetPrivate
->dispatchQueue
,
1412 rlsPerform((void *)target
);
1416 #endif // !TARGET_OS_IPHONE
1417 if (targetPrivate
->rls
!= NULL
) {
1418 CFRunLoopSourceSignal(targetPrivate
->rls
);
1419 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1425 static SCNetworkReachabilityPrivateRef
1426 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
1428 SCNetworkReachabilityPrivateRef targetPrivate
;
1431 /* initialize runtime */
1432 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
1434 /* allocate target */
1435 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
1436 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
1437 __kSCNetworkReachabilityTypeID
,
1440 if (targetPrivate
== NULL
) {
1444 pthread_mutex_init(&targetPrivate
->lock
, NULL
);
1446 targetPrivate
->name
= NULL
;
1447 targetPrivate
->serv
= NULL
;
1448 bzero(&targetPrivate
->hints
, sizeof(targetPrivate
->hints
));
1449 targetPrivate
->hints
.ai_flags
= AI_ADDRCONFIG
;
1451 targetPrivate
->hints
.ai_flags
|= AI_PARALLEL
;
1452 #endif /* AI_PARALLEL */
1454 targetPrivate
->needResolve
= FALSE
;
1455 targetPrivate
->resolvedAddress
= NULL
;
1456 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1458 targetPrivate
->localAddress
= NULL
;
1459 targetPrivate
->remoteAddress
= NULL
;
1461 targetPrivate
->info
= NOT_REACHABLE
;
1462 targetPrivate
->last_notify
= NOT_REPORTED
;
1464 targetPrivate
->scheduled
= FALSE
;
1465 targetPrivate
->rls
= NULL
;
1466 targetPrivate
->rlsFunction
= NULL
;
1467 targetPrivate
->rlsContext
.info
= NULL
;
1468 targetPrivate
->rlsContext
.retain
= NULL
;
1469 targetPrivate
->rlsContext
.release
= NULL
;
1470 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1471 targetPrivate
->rlList
= NULL
;
1473 targetPrivate
->haveDNS
= FALSE
;
1474 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
1475 targetPrivate
->dnsPort
= NULL
;
1476 targetPrivate
->dnsRLS
= NULL
;
1478 targetPrivate
->onDemandBypass
= FALSE
;
1479 targetPrivate
->onDemandName
= NULL
;
1480 targetPrivate
->onDemandRemoteAddress
= NULL
;
1481 targetPrivate
->onDemandServer
= NULL
;
1482 targetPrivate
->onDemandServiceID
= NULL
;
1484 targetPrivate
->log_prefix
[0] = '\0';
1486 snprintf(targetPrivate
->log_prefix
,
1487 sizeof(targetPrivate
->log_prefix
),
1492 return targetPrivate
;
1496 SCNetworkReachabilityRef
1497 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
1498 const struct sockaddr
*address
)
1500 SCNetworkReachabilityPrivateRef targetPrivate
;
1502 if ((address
== NULL
) ||
1503 (address
->sa_len
== 0) ||
1504 (address
->sa_len
> sizeof(struct sockaddr_storage
))) {
1505 _SCErrorSet(kSCStatusInvalidArgument
);
1509 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1510 if (targetPrivate
== NULL
) {
1514 targetPrivate
->type
= reachabilityTypeAddress
;
1515 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
1516 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
1518 return (SCNetworkReachabilityRef
)targetPrivate
;
1522 SCNetworkReachabilityRef
1523 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
1524 const struct sockaddr
*localAddress
,
1525 const struct sockaddr
*remoteAddress
)
1527 SCNetworkReachabilityPrivateRef targetPrivate
;
1529 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
1530 _SCErrorSet(kSCStatusInvalidArgument
);
1534 if (localAddress
!= NULL
) {
1535 if ((localAddress
->sa_len
== 0) ||
1536 (localAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1537 _SCErrorSet(kSCStatusInvalidArgument
);
1542 if (remoteAddress
!= NULL
) {
1543 if ((remoteAddress
->sa_len
== 0) ||
1544 (remoteAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1545 _SCErrorSet(kSCStatusInvalidArgument
);
1550 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1551 if (targetPrivate
== NULL
) {
1555 targetPrivate
->type
= reachabilityTypeAddressPair
;
1557 if (localAddress
!= NULL
) {
1558 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
1559 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
1562 if (remoteAddress
!= NULL
) {
1563 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
1564 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
1567 return (SCNetworkReachabilityRef
)targetPrivate
;
1571 SCNetworkReachabilityRef
1572 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
1573 const char *nodename
)
1576 struct sockaddr_in sin
;
1577 struct sockaddr_in6 sin6
;
1578 SCNetworkReachabilityPrivateRef targetPrivate
;
1580 if (nodename
== NULL
) {
1581 _SCErrorSet(kSCStatusInvalidArgument
);
1585 nodenameLen
= strlen(nodename
);
1586 if (nodenameLen
== 0) {
1587 _SCErrorSet(kSCStatusInvalidArgument
);
1591 /* check if this "nodename" is really an IP[v6] address in disguise */
1593 bzero(&sin
, sizeof(sin
));
1594 sin
.sin_len
= sizeof(sin
);
1595 sin
.sin_family
= AF_INET
;
1596 if (inet_aton(nodename
, &sin
.sin_addr
) == 1) {
1597 /* if IPv4 address */
1598 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin
);
1601 bzero(&sin6
, sizeof(sin6
));
1602 sin6
.sin6_len
= sizeof(sin6
);
1603 sin6
.sin6_family
= AF_INET6
;
1604 if (inet_pton(AF_INET6
, nodename
, &sin6
.sin6_addr
) == 1) {
1605 /* if IPv6 address */
1608 p
= strchr(nodename
, '%');
1610 sin6
.sin6_scope_id
= if_nametoindex(p
+ 1);
1613 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin6
);
1616 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1617 if (targetPrivate
== NULL
) {
1621 targetPrivate
->type
= reachabilityTypeName
;
1623 targetPrivate
->name
= CFAllocatorAllocate(NULL
, nodenameLen
+ 1, 0);
1624 strlcpy((char *)targetPrivate
->name
, nodename
, nodenameLen
+ 1);
1626 targetPrivate
->needResolve
= TRUE
;
1627 targetPrivate
->info
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
1629 return (SCNetworkReachabilityRef
)targetPrivate
;
1633 SCNetworkReachabilityRef
1634 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
1635 CFDictionaryRef options
)
1637 CFBooleanRef bypass
;
1639 struct addrinfo
*hints
= NULL
;
1641 CFStringRef nodename
;
1642 CFStringRef servname
;
1643 SCNetworkReachabilityPrivateRef targetPrivate
;
1645 if (!isA_CFDictionary(options
)) {
1646 _SCErrorSet(kSCStatusInvalidArgument
);
1650 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
1651 if ((nodename
!= NULL
) &&
1652 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
1653 _SCErrorSet(kSCStatusInvalidArgument
);
1656 servname
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServName
);
1657 if ((servname
!= NULL
) &&
1658 (!isA_CFString(servname
) || (CFStringGetLength(servname
) == 0))) {
1659 _SCErrorSet(kSCStatusInvalidArgument
);
1662 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionHints
);
1664 if (!isA_CFData(data
) || (CFDataGetLength(data
) != sizeof(targetPrivate
->hints
))) {
1665 _SCErrorSet(kSCStatusInvalidArgument
);
1669 hints
= (struct addrinfo
*)CFDataGetBytePtr(data
);
1670 if ((hints
->ai_addrlen
!= 0) ||
1671 (hints
->ai_addr
!= NULL
) ||
1672 (hints
->ai_canonname
!= NULL
) ||
1673 (hints
->ai_next
!= NULL
)) {
1674 _SCErrorSet(kSCStatusInvalidArgument
);
1678 bypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandByPass
);
1679 if ((bypass
!= NULL
) && !isA_CFBoolean(bypass
)) {
1680 _SCErrorSet(kSCStatusInvalidArgument
);
1683 if ((nodename
== NULL
) && (servname
== NULL
)) {
1684 _SCErrorSet(kSCStatusInvalidArgument
);
1688 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
1689 targetPrivate
= (SCNetworkReachabilityPrivateRef
)SCNetworkReachabilityCreateWithName(allocator
, name
);
1690 CFAllocatorDeallocate(NULL
, (void *)name
);
1691 if (targetPrivate
== NULL
) {
1695 if (targetPrivate
->type
== reachabilityTypeName
) {
1696 if (servname
!= NULL
) {
1697 targetPrivate
->serv
= _SC_cfstring_to_cstring(servname
, NULL
, 0, kCFStringEncodingUTF8
);
1699 if (hints
!= NULL
) {
1700 bcopy(hints
, &targetPrivate
->hints
, sizeof(targetPrivate
->hints
));
1704 if (bypass
!= NULL
) {
1705 targetPrivate
->onDemandBypass
= CFBooleanGetValue(bypass
);
1708 return (SCNetworkReachabilityRef
)targetPrivate
;
1713 SCNetworkReachabilityGetTypeID(void)
1715 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
1716 return __kSCNetworkReachabilityTypeID
;
1720 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
1721 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
1724 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1726 if (!isA_SCNetworkReachability(target
)) {
1727 _SCErrorSet(kSCStatusInvalidArgument
);
1731 if (targetPrivate
->type
!= reachabilityTypeName
) {
1732 _SCErrorSet(kSCStatusInvalidArgument
);
1737 *error_num
= targetPrivate
->resolvedAddressError
;
1740 if ((targetPrivate
->resolvedAddress
!= NULL
) || (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1741 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
1742 return CFRetain(targetPrivate
->resolvedAddress
);
1744 /* if status is known but no resolved addresses to return */
1745 _SCErrorSet(kSCStatusOK
);
1750 _SCErrorSet(kSCStatusReachabilityUnknown
);
1756 __SCNetworkReachabilitySetResolvedAddress(int32_t status
,
1757 struct addrinfo
*res
,
1758 SCNetworkReachabilityRef target
)
1760 struct addrinfo
*resP
;
1761 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1763 if (targetPrivate
->resolvedAddress
!= NULL
) {
1764 CFRelease(targetPrivate
->resolvedAddress
);
1765 targetPrivate
->resolvedAddress
= NULL
;
1768 if ((status
== 0) && (res
!= NULL
)) {
1769 CFMutableArrayRef addresses
;
1771 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1773 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
1775 CFDataRef newAddress
;
1777 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
1778 n
= CFArrayGetCount(addresses
);
1780 !CFArrayContainsValue(addresses
, CFRangeMake(0, n
), newAddress
)) {
1781 CFArrayAppendValue(addresses
, newAddress
);
1783 CFRelease(newAddress
);
1786 /* save the resolved address[es] */
1787 targetPrivate
->resolvedAddress
= addresses
;
1788 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1790 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sgetaddrinfo() failed: %s"),
1791 targetPrivate
->log_prefix
,
1792 gai_strerror(status
));
1794 /* save the error associated with the attempt to resolve the name */
1795 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
1796 targetPrivate
->resolvedAddressError
= status
;
1798 targetPrivate
->needResolve
= FALSE
;
1800 if (res
!= NULL
) freeaddrinfo(res
);
1802 if (targetPrivate
->scheduled
) {
1803 __SCNetworkReachabilityPerform(target
);
1811 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo
*res
, void *context
)
1813 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
1814 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1816 __log_query_time(target
,
1817 ((status
== 0) && (res
!= NULL
)), // if successful query
1819 &targetPrivate
->dnsQueryStart
); // start time
1821 __SCNetworkReachabilitySetResolvedAddress(status
, res
, target
);
1827 * rankReachability()
1828 * Not reachable == 0
1829 * Connection Required == 1
1833 rankReachability(SCNetworkReachabilityFlags flags
)
1837 if (flags
& kSCNetworkReachabilityFlagsReachable
) rank
= 2;
1838 if (flags
& kSCNetworkReachabilityFlagsConnectionRequired
) rank
= 1;
1844 #pragma mark DNS name resolution
1848 replyMPCopyDescription(const void *info
)
1850 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1851 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1853 return CFStringCreateWithFormat(NULL
,
1855 CFSTR("<getaddrinfo_async_start reply MP> {%s%s%s%s%s, target = %p}"),
1856 targetPrivate
->name
!= NULL
? "name = " : "",
1857 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
1858 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
1859 targetPrivate
->serv
!= NULL
? "serv = " : "",
1860 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "",
1866 processAsyncDNSReply(mach_port_t mp
, void *msg
, SCNetworkReachabilityRef target
);
1870 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1872 mach_port_t mp
= CFMachPortGetPort(port
);
1873 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1875 processAsyncDNSReply(mp
, msg
, target
);
1880 #if !TARGET_OS_IPHONE
1882 SCNetworkReachabilityNotifyMIGCallback(mach_msg_header_t
*message
, mach_msg_header_t
*reply
)
1884 mach_port_t mp
= message
->msgh_local_port
;
1885 SCNetworkReachabilityRef target
= dispatch_get_context(dispatch_get_current_queue());
1887 processAsyncDNSReply(mp
, message
, target
);
1888 reply
->msgh_remote_port
= MACH_PORT_NULL
;
1891 #endif // !TARGET_OS_IPHONE
1895 enqueueAsyncDNSQuery(SCNetworkReachabilityRef target
, mach_port_t mp
)
1897 CFMachPortContext context
= { 0
1901 , replyMPCopyDescription
1903 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1905 targetPrivate
->dnsMP
= mp
;
1906 targetPrivate
->dnsPort
= CFMachPortCreateWithPort(NULL
,
1908 getaddrinfo_async_handleCFReply
,
1911 #if !TARGET_OS_IPHONE
1912 if (targetPrivate
->dispatchQueue
!= NULL
) {
1913 targetPrivate
->asyncDNSQueue
= dispatch_queue_create("com.apple.SCNetworkReachabilty.async_DNS_query", NULL
);
1914 if (targetPrivate
->asyncDNSQueue
== NULL
) {
1915 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_queue_create() failed"));
1918 CFRetain(target
); // Note: will be released when the dispatch queue is released
1919 dispatch_set_context(targetPrivate
->asyncDNSQueue
, (void *)target
);
1920 dispatch_set_finalizer_f(targetPrivate
->asyncDNSQueue
, (dispatch_function_t
)CFRelease
);
1922 targetPrivate
->asyncDNSSource
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
,
1925 targetPrivate
->asyncDNSQueue
);
1926 if (targetPrivate
->asyncDNSSource
== NULL
) {
1927 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_source_create() failed"));
1930 dispatch_source_set_event_handler(targetPrivate
->asyncDNSSource
, ^{
1931 dispatch_mig_server(targetPrivate
->asyncDNSSource
,
1932 sizeof(mach_msg_header_t
),
1933 SCNetworkReachabilityNotifyMIGCallback
);
1935 dispatch_resume(targetPrivate
->asyncDNSSource
);
1937 #endif // !TARGET_OS_IPHONE
1938 if (targetPrivate
->rls
!= NULL
) {
1942 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
1944 n
= CFArrayGetCount(targetPrivate
->rlList
);
1945 for (i
= 0; i
< n
; i
+= 3) {
1946 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
1947 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
1949 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
1955 #if !TARGET_OS_IPHONE
1958 if (targetPrivate
->asyncDNSSource
!= NULL
) {
1959 dispatch_source_cancel(targetPrivate
->asyncDNSSource
);
1960 dispatch_release(targetPrivate
->asyncDNSSource
);
1961 targetPrivate
->asyncDNSSource
= NULL
;
1963 if (targetPrivate
->asyncDNSQueue
!= NULL
) {
1964 dispatch_release(targetPrivate
->asyncDNSQueue
);
1965 targetPrivate
->asyncDNSQueue
= NULL
;
1968 CFMachPortInvalidate(targetPrivate
->dnsPort
);
1969 CFRelease(targetPrivate
->dnsPort
);
1970 targetPrivate
->dnsPort
= NULL
;
1971 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
1973 _SCErrorSet(kSCStatusFailed
);
1975 #endif // !TARGET_OS_IPHONE
1980 dequeueAsyncDNSQuery(SCNetworkReachabilityRef target
)
1982 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1984 #if !TARGET_OS_IPHONE
1985 if (targetPrivate
->asyncDNSSource
!= NULL
) {
1986 dispatch_source_cancel(targetPrivate
->asyncDNSSource
);
1987 if (targetPrivate
->asyncDNSQueue
!= dispatch_get_current_queue()) {
1988 // ensure the cancellation has completed
1989 pthread_mutex_unlock(&targetPrivate
->lock
);
1990 dispatch_sync(targetPrivate
->asyncDNSQueue
, ^{});
1991 pthread_mutex_lock(&targetPrivate
->lock
);
1994 if (targetPrivate
->asyncDNSSource
!= NULL
) {
1995 dispatch_release(targetPrivate
->asyncDNSSource
);
1996 targetPrivate
->asyncDNSSource
= NULL
;
1998 if (targetPrivate
->asyncDNSQueue
!= NULL
) {
1999 dispatch_release(targetPrivate
->asyncDNSQueue
);
2000 targetPrivate
->asyncDNSQueue
= NULL
;
2002 #endif // !TARGET_OS_IPHONE
2004 if (targetPrivate
->dnsRLS
!= NULL
) {
2005 CFRelease(targetPrivate
->dnsRLS
);
2006 targetPrivate
->dnsRLS
= NULL
;
2009 if (targetPrivate
->dnsPort
!= NULL
) {
2010 CFMachPortInvalidate(targetPrivate
->dnsPort
);
2011 CFRelease(targetPrivate
->dnsPort
);
2012 targetPrivate
->dnsPort
= NULL
;
2013 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
2021 processAsyncDNSReply(mach_port_t mp
, void *msg
, SCNetworkReachabilityRef target
)
2024 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2026 pthread_mutex_lock(&targetPrivate
->lock
);
2028 if (mp
!= targetPrivate
->dnsMP
) {
2029 // we've received a callback on the async DNS port but since the
2030 // associated CFMachPort doesn't match than the request must have
2031 // already been cancelled.
2032 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP"));
2033 pthread_mutex_unlock(&targetPrivate
->lock
);
2037 dequeueAsyncDNSQuery(target
);
2038 status
= getaddrinfo_async_handle_reply(msg
);
2039 if ((status
== 0) &&
2040 (targetPrivate
->resolvedAddress
== NULL
) && (targetPrivate
->resolvedAddressError
== NETDB_SUCCESS
)) {
2043 // if the request is not complete and needs to be re-queued
2044 ok
= enqueueAsyncDNSQuery(target
, mp
);
2046 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply enqueueAsyncDNSQuery() failed"));
2050 pthread_mutex_unlock(&targetPrivate
->lock
);
2057 check_resolver_reachability(SCDynamicStoreRef
*storeP
,
2058 dns_resolver_t
*resolver
,
2059 SCNetworkReachabilityFlags
*flags
,
2061 const char *log_prefix
)
2066 *flags
= kSCNetworkReachabilityFlagsReachable
;
2069 for (i
= 0; i
< resolver
->n_nameserver
; i
++) {
2070 struct sockaddr
*address
= resolver
->nameserver
[i
];
2071 ReachabilityInfo ns_info
;
2075 if (address
->sa_family
!= AF_INET
) {
2077 * we need to skip non-IPv4 DNS server
2078 * addresses (at least until [3510431] has
2084 ok
= checkAddress(storeP
, address
, &ns_info
, log_prefix
);
2090 if (rankReachability(ns_info
.flags
) < rankReachability(*flags
)) {
2091 /* return the worst case result */
2092 *flags
= ns_info
.flags
;
2103 check_matching_resolvers(SCDynamicStoreRef
*storeP
,
2104 dns_config_t
*dns_config
,
2106 SCNetworkReachabilityFlags
*flags
,
2108 const char *log_prefix
)
2111 Boolean matched
= FALSE
;
2112 const char *name
= fqdn
;
2114 while (!matched
&& (name
!= NULL
)) {
2118 * check if the provided name (or sub-component)
2119 * matches one of our resolver configurations.
2122 for (i
= 0; i
< dns_config
->n_resolver
; i
++) {
2124 dns_resolver_t
*resolver
;
2126 resolver
= dns_config
->resolver
[i
];
2127 domain
= resolver
->domain
;
2128 if (domain
!= NULL
&& (len
== strlen(domain
))) {
2129 if (strcasecmp(name
, domain
) == 0) {
2133 * if name matches domain
2136 ok
= check_resolver_reachability(storeP
, resolver
, flags
, haveDNS
, log_prefix
);
2147 * we have not found a matching resolver, try
2148 * a less qualified domain
2150 name
= strchr(name
, '.');
2151 if ((name
!= NULL
) && (*name
!= '\0')) {
2163 static dns_configuration_t
*
2164 dns_configuration_retain()
2166 pthread_mutex_lock(&dns_lock
);
2168 if ((dns_configuration
!= NULL
) && dns_token_valid
) {
2173 * check if the global [DNS] configuration snapshot needs
2176 status
= notify_check(dns_token
, &check
);
2177 if (status
!= NOTIFY_STATUS_OK
) {
2178 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
2181 if ((status
!= NOTIFY_STATUS_OK
) || (check
!= 0)) {
2183 * if the snapshot needs to be refreshed
2185 if (dns_configuration
->refs
== 0) {
2186 dns_configuration_free(dns_configuration
->config
);
2187 CFAllocatorDeallocate(NULL
, dns_configuration
);
2189 dns_configuration
= NULL
;
2193 if (dns_configuration
== NULL
) {
2194 dns_config_t
*new_config
;
2196 new_config
= dns_configuration_copy();
2197 if (new_config
!= NULL
) {
2198 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
2199 dns_configuration
->config
= new_config
;
2200 dns_configuration
->refs
= 0;
2204 if (dns_configuration
!= NULL
) {
2205 dns_configuration
->refs
++;
2208 pthread_mutex_unlock(&dns_lock
);
2209 return dns_configuration
;
2214 dns_configuration_release(dns_configuration_t
*config
)
2216 pthread_mutex_lock(&dns_lock
);
2219 if (config
->refs
== 0) {
2220 if ((dns_configuration
!= config
)) {
2221 dns_configuration_free(config
->config
);
2222 CFAllocatorDeallocate(NULL
, config
);
2226 pthread_mutex_unlock(&dns_lock
);
2232 dns_configuration_watch()
2235 const char *dns_key
;
2239 pthread_mutex_lock(&dns_lock
);
2241 dns_key
= dns_configuration_notify_key();
2242 if (dns_key
== NULL
) {
2243 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
2247 status
= notify_register_check(dns_key
, &dns_token
);
2248 if (status
== NOTIFY_STATUS_OK
) {
2249 dns_token_valid
= TRUE
;
2251 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
);
2255 status
= notify_check(dns_token
, &dns_check
);
2256 if (status
!= NOTIFY_STATUS_OK
) {
2257 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
2258 (void)notify_cancel(dns_token
);
2259 dns_token_valid
= FALSE
;
2267 pthread_mutex_unlock(&dns_lock
);
2273 dns_configuration_unwatch()
2275 pthread_mutex_lock(&dns_lock
);
2277 (void)notify_cancel(dns_token
);
2278 dns_token_valid
= FALSE
;
2280 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
2281 dns_configuration_free(dns_configuration
->config
);
2282 CFAllocatorDeallocate(NULL
, dns_configuration
);
2283 dns_configuration
= NULL
;
2286 pthread_mutex_unlock(&dns_lock
);
2292 _SC_R_checkResolverReachability(SCDynamicStoreRef
*storeP
,
2293 SCNetworkReachabilityFlags
*flags
,
2295 const char *nodename
,
2296 const char *servname
,
2297 const char *log_prefix
)
2299 dns_resolver_t
*default_resolver
;
2300 dns_configuration_t
*dns
;
2301 Boolean found
= FALSE
;
2302 char *fqdn
= (char *)nodename
;
2304 Boolean isFQDN
= FALSE
;
2307 Boolean useDefault
= FALSE
;
2310 * We first assume that all of the configured DNS servers
2311 * are available. Since we don't know which name server will
2312 * be consulted to resolve the specified nodename we need to
2313 * check the availability of ALL name servers. We can only
2314 * proceed if we know that our query can be answered.
2317 *flags
= kSCNetworkReachabilityFlagsReachable
;
2320 len
= (nodename
!= NULL
) ? strlen(nodename
) : 0;
2322 if ((servname
== NULL
) || (strlen(servname
) == 0)) {
2323 // if no nodename or servname, return not reachable
2329 dns
= dns_configuration_retain();
2332 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no configuration"), log_prefix
);
2336 if (dns
->config
->n_resolver
== 0) {
2337 // if no resolver configuration
2338 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no resolvers"), log_prefix
);
2342 *flags
= kSCNetworkReachabilityFlagsReachable
;
2344 if (fqdn
[len
- 1] == '.') {
2347 // trim trailing '.''s
2348 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
2349 if (fqdn
== nodename
) {
2350 fqdn
= strdup(nodename
);
2356 default_resolver
= dns
->config
->resolver
[0];
2359 * check if the provided name matches a supplemental domain
2361 found
= check_matching_resolvers(storeP
, dns
->config
, fqdn
, flags
, haveDNS
, log_prefix
);
2363 if (!found
&& !isFQDN
) {
2365 * if we did not match a supplemental domain name and if the
2366 * provided name has enough "."s then the first query will be
2367 * directed to the default resolver.
2373 #define NDOTS_OPT "ndots="
2374 #define NDOTS_OPT_LEN (sizeof("ndots=") - 1)
2376 if (default_resolver
->options
!= NULL
) {
2377 cp
= strstr(default_resolver
->options
, NDOTS_OPT
);
2379 ((cp
== default_resolver
->options
) || isspace(cp
[-1])) &&
2380 ((cp
[NDOTS_OPT_LEN
] != '\0') && isdigit(cp
[NDOTS_OPT_LEN
]))) {
2384 cp
+= NDOTS_OPT_LEN
;
2386 val
= strtol(cp
, &end
, 10);
2387 if ((*cp
!= '\0') && (cp
!= end
) && (errno
== 0) &&
2388 ((*end
== '\0') || isspace(*end
))) {
2395 for (cp
= fqdn
; *cp
!= '\0'; cp
++) {
2396 if (*cp
== '.') dots
++;
2404 if (!found
&& !isFQDN
&& !useDefault
&& (dns
->config
->n_resolver
> 1)) {
2406 * FQDN not specified, try matching w/search domains
2408 if (default_resolver
->n_search
> 0) {
2409 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
2411 char *search_fqdn
= NULL
;
2413 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
2418 // try the provided name with the search domain appended
2419 found
= check_matching_resolvers(storeP
,
2427 } else if (default_resolver
->domain
!= NULL
) {
2429 int domain_parts
= 0;
2431 // count domain parts
2432 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
2438 // remove trailing dots
2439 for (dp
--; (dp
>= default_resolver
->domain
) && (*dp
== '.'); dp
--) {
2444 if (dp
>= default_resolver
->domain
) {
2445 // dots are separators, bump # of components
2449 dp
= default_resolver
->domain
;
2450 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= domain_parts
); i
++) {
2452 char *search_fqdn
= NULL
;
2454 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
2459 // try the provided name with the [default] domain appended
2460 found
= check_matching_resolvers(storeP
,
2468 // move to the next component of the [default] domain
2469 dp
= strchr(dp
, '.') + 1;
2476 * check the reachability of the default resolver
2478 ok
= check_resolver_reachability(storeP
, default_resolver
, flags
, haveDNS
, log_prefix
);
2481 if (fqdn
!= nodename
) free(fqdn
);
2486 dns_configuration_release(dns
);
2494 _SC_checkResolverReachability(SCDynamicStoreRef
*storeP
,
2495 SCNetworkReachabilityFlags
*flags
,
2497 const char *nodename
,
2498 const char *servname
)
2500 return _SC_R_checkResolverReachability(storeP
, flags
, haveDNS
, nodename
, servname
, "");
2505 * _SC_checkResolverReachabilityByAddress()
2507 * Given an IP address, determine whether a reverse DNS query can be issued
2508 * using the current network configuration.
2511 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
2512 SCNetworkReachabilityFlags
*flags
,
2514 struct sockaddr
*sa
)
2521 * Ideally, we would have an API that given a local IP
2522 * address would return the DNS server(s) that would field
2523 * a given PTR query. Fortunately, we do have an SPI which
2524 * which will provide this information given a "name" so we
2525 * take the address, convert it into the inverse query name,
2526 * and find out which servers should be consulted.
2529 switch (sa
->sa_family
) {
2535 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
2538 * build "PTR" query name
2539 * NNN.NNN.NNN.NNN.in-addr.arpa.
2541 rev
.s_addr
= sin
->sin_addr
.s_addr
;
2542 (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.",
2553 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
2554 int x
= sizeof(ptr_name
);
2558 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
2559 * 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.
2561 for (i
= sizeof(sin6
->sin6_addr
) - 1; i
>= 0; i
--) {
2562 n
= snprintf(&ptr_name
[s
], x
, "%x.%x.",
2563 ( sin6
->sin6_addr
.s6_addr
[i
] & 0xf),
2564 ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf));
2565 if ((n
== -1) || (n
>= x
)) {
2573 n
= snprintf(&ptr_name
[s
], x
, "ip6.arpa.");
2574 if ((n
== -1) || (n
>= x
)) {
2585 ok
= _SC_R_checkResolverReachability(storeP
, flags
, haveDNS
, ptr_name
, NULL
, "");
2594 startAsyncDNSQuery(SCNetworkReachabilityRef target
) {
2598 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2600 (void) gettimeofday(&targetPrivate
->dnsQueryStart
, NULL
);
2602 error
= getaddrinfo_async_start(&mp
,
2603 targetPrivate
->name
,
2604 targetPrivate
->serv
,
2605 &targetPrivate
->hints
,
2606 __SCNetworkReachabilityCallbackSetResolvedAddress
,
2609 /* save the error associated with the attempt to resolve the name */
2610 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
);
2614 ok
= enqueueAsyncDNSQuery(target
, mp
);
2620 #pragma mark OnDemand
2624 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target
,
2625 CFDictionaryRef
*userOptions
)
2627 SCNetworkServiceRef service
= NULL
;
2628 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2630 if (!isA_SCNetworkReachability(target
)) {
2631 _SCErrorSet(kSCStatusInvalidArgument
);
2635 if (targetPrivate
->onDemandServiceID
!= NULL
) {
2636 service
= _SCNetworkServiceCopyActive(NULL
, targetPrivate
->onDemandServiceID
);
2639 if (userOptions
!= NULL
) {
2640 if (targetPrivate
->onDemandName
!= NULL
) {
2641 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2642 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
);
2644 *userOptions
= NULL
;
2653 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer
,
2654 SCNetworkReachabilityFlags onDemandFlags
,
2657 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2658 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2660 pthread_mutex_lock(&targetPrivate
->lock
);
2662 if (!targetPrivate
->scheduled
) {
2663 // if not currently scheduled
2664 pthread_mutex_unlock(&targetPrivate
->lock
);
2668 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sOnDemand \"server\" status changed"),
2669 targetPrivate
->log_prefix
);
2670 __SCNetworkReachabilityPerform(target
);
2672 pthread_mutex_unlock(&targetPrivate
->lock
);
2679 __SCNetworkReachabilityOnDemandCheck(SCDynamicStoreRef
*storeP
,
2680 SCNetworkReachabilityRef target
,
2681 Boolean onDemandRetry
,
2682 SCNetworkReachabilityFlags
*flags
)
2685 Boolean onDemand
= FALSE
;
2686 CFStringRef onDemandRemoteAddress
= NULL
;
2687 CFStringRef onDemandServiceID
= NULL
;
2688 SCNetworkConnectionStatus onDemandStatus
;
2689 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2691 // SCLog(_sc_debug, LOG_INFO,
2692 // CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"),
2693 // targetPrivate->log_prefix,
2694 // onDemandRetry ? "after" : "before");
2696 if (targetPrivate
->onDemandName
== NULL
) {
2697 targetPrivate
->onDemandName
= CFStringCreateWithCString(NULL
, targetPrivate
->name
, kCFStringEncodingUTF8
);
2701 * check if an OnDemand VPN configuration matches the name.
2703 ok
= __SCNetworkConnectionCopyOnDemandInfoWithName(storeP
,
2704 targetPrivate
->onDemandName
,
2708 &onDemandRemoteAddress
);
2709 if (!_SC_CFEqual(targetPrivate
->onDemandRemoteAddress
, onDemandRemoteAddress
) ||
2710 !_SC_CFEqual(targetPrivate
->onDemandServiceID
, onDemandServiceID
)) {
2711 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
2712 CFRelease(targetPrivate
->onDemandRemoteAddress
);
2713 targetPrivate
->onDemandRemoteAddress
= NULL
;
2716 if (targetPrivate
->onDemandServer
!= NULL
) {
2717 #if !TARGET_OS_IPHONE
2718 if (targetPrivate
->dispatchQueue
!= NULL
) {
2720 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
2722 #endif // !TARGET_OS_IPHONE
2723 if (targetPrivate
->rls
!= NULL
) {
2728 n
= CFArrayGetCount(targetPrivate
->rlList
);
2729 for (i
= 0; i
< n
; i
+= 3) {
2730 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
2731 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
2733 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, TRUE
);
2737 CFRelease(targetPrivate
->onDemandServer
);
2738 targetPrivate
->onDemandServer
= NULL
;
2741 if (targetPrivate
->onDemandServiceID
!= NULL
) {
2742 CFRelease(targetPrivate
->onDemandServiceID
);
2743 targetPrivate
->onDemandServiceID
= NULL
;
2747 if (onDemandStatus
!= kSCNetworkConnectionConnected
) {
2749 * if we have a VPN configuration matching the name *and* we need to
2750 * bring the VPN up. Combine our flags with those of the VPN server.
2752 if (targetPrivate
->onDemandServer
== NULL
) {
2753 CFMutableDictionaryRef options
;
2755 options
= CFDictionaryCreateMutable(NULL
,
2757 &kCFTypeDictionaryKeyCallBacks
,
2758 &kCFTypeDictionaryValueCallBacks
);
2759 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, onDemandRemoteAddress
);
2760 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandByPass
, kCFBooleanTrue
);
2761 targetPrivate
->onDemandServer
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
2764 if (targetPrivate
->scheduled
) {
2765 SCNetworkReachabilityContext context
= { 0, NULL
, CFRetain
, CFRelease
, CFCopyDescription
};
2767 context
.info
= (void *)target
;
2768 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
,
2769 __SCNetworkReachabilityOnDemandCheckCallback
,
2772 // schedule server reachability to match that of the target
2773 #if !TARGET_OS_IPHONE
2774 if (targetPrivate
->dispatchQueue
!= NULL
) {
2775 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, targetPrivate
->dispatchQueue
, TRUE
);
2777 #endif // !TARGET_OS_IPHONE
2782 n
= CFArrayGetCount(targetPrivate
->rlList
);
2783 for (i
= 0; i
< n
; i
+= 3) {
2784 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
2785 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
2787 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, NULL
, TRUE
);
2793 ok
= SCNetworkReachabilityGetFlags(targetPrivate
->onDemandServer
, flags
);
2794 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status * = 0x%08x"),
2795 targetPrivate
->log_prefix
,
2797 if (ok
&& (*flags
& kSCNetworkReachabilityFlagsReachable
)) {
2798 if (!(*flags
& kSCNetworkReachabilityFlagsTransientConnection
)) {
2799 // start clean if not already layered on a transient network
2802 *flags
|= kSCNetworkReachabilityFlagsReachable
;
2803 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
2804 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
2805 *flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
2808 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service * = %@"),
2809 targetPrivate
->log_prefix
,
2811 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after OnDemand connect)"),
2812 targetPrivate
->log_prefix
);
2819 if (onDemandRemoteAddress
!= NULL
) {
2820 if (targetPrivate
->onDemandRemoteAddress
== NULL
) {
2821 targetPrivate
->onDemandRemoteAddress
= onDemandRemoteAddress
;
2823 CFRelease(onDemandRemoteAddress
);
2827 if (onDemandServiceID
!= NULL
) {
2828 if (targetPrivate
->onDemandServiceID
== NULL
) {
2829 targetPrivate
->onDemandServiceID
= onDemandServiceID
;
2831 CFRelease(onDemandServiceID
);
2841 #pragma mark Reachability Flags
2845 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef
*storeP
,
2846 SCNetworkReachabilityRef target
,
2847 ReachabilityInfo
*reach_info
,
2850 CFMutableArrayRef addresses
= NULL
;
2851 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2852 ReachabilityInfo my_info
= NOT_REACHABLE
;
2855 *reach_info
= NOT_REACHABLE
;
2857 if (!isA_SCNetworkReachability(target
)) {
2858 _SCErrorSet(kSCStatusInvalidArgument
);
2862 switch (targetPrivate
->type
) {
2863 case reachabilityTypeAddress
:
2864 case reachabilityTypeAddressPair
: {
2866 * Check "local" address
2868 if (targetPrivate
->localAddress
!= NULL
) {
2870 * Check "local" address
2872 ok
= checkAddress(storeP
,
2873 targetPrivate
->localAddress
,
2875 targetPrivate
->log_prefix
);
2877 goto error
; /* not today */
2880 if (!(my_info
.flags
& kSCNetworkReachabilityFlagsIsLocalAddress
)) {
2881 goto error
; /* not reachable, non-"local" address */
2886 * Check "remote" address
2888 if (targetPrivate
->remoteAddress
!= NULL
) {
2890 * in cases where we have "local" and "remote" addresses
2891 * we need to re-initialize the to-be-returned flags.
2893 my_info
= NOT_REACHABLE
;
2896 * Check "remote" address
2898 ok
= checkAddress(storeP
,
2899 targetPrivate
->remoteAddress
,
2901 targetPrivate
->log_prefix
);
2903 goto error
; /* not today */
2911 case reachabilityTypeName
: {
2912 struct timeval dnsQueryStart
;
2914 SCNetworkReachabilityFlags ns_flags
;
2915 struct addrinfo
*res
;
2917 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
2918 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
2919 /* if resolved or an error had been detected */
2921 /* if not an async request */
2922 goto checkResolvedAddress
;
2923 } else if ((targetPrivate
->dnsPort
== NULL
) && !targetPrivate
->needResolve
) {
2924 /* if async request, no query active, and no query needed */
2925 goto checkResolvedAddress
;
2929 if (!targetPrivate
->onDemandBypass
) {
2933 * before we attempt our initial DNS query, check if there is
2934 * an OnDemand configuration that we should be using.
2936 onDemand
= __SCNetworkReachabilityOnDemandCheck(storeP
, target
, FALSE
, &my_info
.flags
);
2938 /* if OnDemand connection is needed */
2943 /* check the reachability of the DNS servers */
2944 ok
= _SC_R_checkResolverReachability(storeP
,
2946 &targetPrivate
->haveDNS
,
2947 targetPrivate
->name
,
2948 targetPrivate
->serv
,
2949 targetPrivate
->log_prefix
);
2951 /* if we could not get DNS server info */
2952 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
2953 targetPrivate
->log_prefix
);
2955 } else if (rankReachability(ns_flags
) < 2) {
2957 * if DNS servers are not (or are no longer) reachable, set
2958 * flags based on the availability of configured (but not
2962 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
2963 targetPrivate
->log_prefix
);
2965 ok
= checkAddress(storeP
,
2968 targetPrivate
->log_prefix
);
2970 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sNo available networks"),
2971 targetPrivate
->log_prefix
);
2975 if (async
&& targetPrivate
->scheduled
) {
2977 * return "host not found", set flags appropriately,
2978 * and schedule notification.
2980 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME
,
2983 my_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
2985 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sno DNS servers are reachable"),
2986 targetPrivate
->log_prefix
);
2987 __SCNetworkReachabilityPerform(target
);
2993 /* for async requests we return the last known status */
2994 my_info
= targetPrivate
->info
;
2996 if (targetPrivate
->dnsPort
!= NULL
) {
2997 /* if request already in progress */
2998 SCLog(_sc_debug
, LOG_INFO
,
2999 CFSTR("%swaiting for DNS reply"),
3000 targetPrivate
->log_prefix
);
3001 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
3002 /* updated reachability based on the previous reply */
3003 goto checkResolvedAddress
;
3008 SCLog(_sc_debug
, LOG_INFO
,
3009 CFSTR("%sstart DNS query for %s%s%s%s%s"),
3010 targetPrivate
->log_prefix
,
3011 targetPrivate
->name
!= NULL
? "name = " : "",
3012 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
3013 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
3014 targetPrivate
->serv
!= NULL
? "serv = " : "",
3015 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
3018 * initiate an async DNS query
3020 if (!startAsyncDNSQuery(target
)) {
3021 /* if we could not initiate the request, process error */
3022 goto checkResolvedAddress
;
3025 /* request initiated */
3029 SCLog(_sc_debug
, LOG_INFO
,
3030 CFSTR("%scheck DNS for %s%s%s%s%s"),
3031 targetPrivate
->log_prefix
,
3032 targetPrivate
->name
!= NULL
? "name = " : "",
3033 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
3034 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
3035 targetPrivate
->serv
!= NULL
? "serv = " : "",
3036 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
3039 * OK, all of the DNS name servers are available. Let's
3040 * resolve the nodename into an address.
3043 (void) gettimeofday(&dnsQueryStart
, NULL
);
3046 error
= getaddrinfo(targetPrivate
->name
,
3047 targetPrivate
->serv
,
3048 &targetPrivate
->hints
,
3051 __log_query_time(target
,
3052 ((error
== 0) && (res
!= NULL
)), // if successful query
3054 &dnsQueryStart
); // start time
3056 __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
);
3058 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
3060 checkResolvedAddress
:
3063 * We first assume that the requested host is NOT available.
3064 * Then, check each address for accessibility and return the
3065 * best status available.
3067 my_info
= NOT_REACHABLE
;
3069 if (isA_CFArray(addresses
)) {
3071 CFIndex n
= CFArrayGetCount(addresses
);
3073 for (i
= 0; i
< n
; i
++) {
3074 ReachabilityInfo ns_info
= NOT_REACHABLE
;
3075 struct sockaddr
*sa
;
3077 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
3079 ok
= checkAddress(storeP
,
3082 targetPrivate
->log_prefix
);
3084 goto error
; /* not today */
3087 if (rankReachability(ns_info
.flags
) > rankReachability(my_info
.flags
)) {
3088 /* return the best case result */
3090 if (rankReachability(my_info
.flags
) == 2) {
3097 if ((error
== EAI_NONAME
)
3098 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
3099 || (error
== EAI_NODATA
)
3103 * the target host name could not be resolved
3105 if (!targetPrivate
->onDemandBypass
) {
3109 * our initial DNS query failed, check again to see if there
3110 * there is an OnDemand configuration that we should be using.
3112 onDemand
= __SCNetworkReachabilityOnDemandCheck(storeP
, target
, TRUE
, &my_info
.flags
);
3114 /* if OnDemand connection is needed */
3119 if (!targetPrivate
->haveDNS
) {
3121 * No DNS servers are defined. Set flags based on
3122 * the availability of configured (but not active)
3125 ok
= checkAddress(storeP
,
3128 targetPrivate
->log_prefix
);
3130 goto error
; /* not today */
3133 if ((my_info
.flags
& kSCNetworkReachabilityFlagsReachable
) &&
3134 (my_info
.flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
3136 * Since we might pick up a set of DNS servers when this connection
3137 * is established, don't reply with a "HOST NOT FOUND" error just yet.
3142 /* Host not found, not reachable! */
3143 my_info
= NOT_REACHABLE
;
3154 *reach_info
= my_info
;
3158 if (addresses
!= NULL
) CFRelease(addresses
);
3164 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
3165 SCNetworkReachabilityFlags
*flags
)
3168 SCDynamicStoreRef store
= NULL
;
3169 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3171 if (!isA_SCNetworkReachability(target
)) {
3172 _SCErrorSet(kSCStatusInvalidArgument
);
3176 pthread_mutex_lock(&targetPrivate
->lock
);
3178 if (targetPrivate
->scheduled
) {
3179 // if being watched, return the last known (and what should be current) status
3180 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
3185 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, &targetPrivate
->info
, FALSE
);
3186 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
3187 if (store
!= NULL
) CFRelease(store
);
3191 pthread_mutex_unlock(&targetPrivate
->lock
);
3197 #pragma mark Notifications
3201 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
3204 CFMutableArrayRef keys
;
3205 CFStringRef pattern
;
3206 CFMutableArrayRef patterns
;
3208 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3209 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3211 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
3212 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3213 kSCDynamicStoreDomainSetup
,
3215 CFArrayAppendValue(keys
, key
);
3218 // State:/Network/Global/DNS
3219 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3220 kSCDynamicStoreDomainState
,
3222 CFArrayAppendValue(keys
, key
);
3225 // State:/Network/Global/IPv4 (default route)
3226 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3227 kSCDynamicStoreDomainState
,
3229 CFArrayAppendValue(keys
, key
);
3232 // State:/Network/Global/OnDemand
3233 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3234 kSCDynamicStoreDomainState
,
3236 CFArrayAppendValue(keys
, key
);
3239 // Setup: per-service IPv4 info
3240 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3241 kSCDynamicStoreDomainSetup
,
3244 CFArrayAppendValue(patterns
, pattern
);
3247 // Setup: per-service Interface info
3248 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3249 kSCDynamicStoreDomainSetup
,
3251 kSCEntNetInterface
);
3252 CFArrayAppendValue(patterns
, pattern
);
3255 // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand)
3256 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3257 kSCDynamicStoreDomainSetup
,
3260 CFArrayAppendValue(patterns
, pattern
);
3263 // State: per-service PPP info (for kSCPropNetPPPStatus)
3264 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3265 kSCDynamicStoreDomainState
,
3268 CFArrayAppendValue(patterns
, pattern
);
3271 // Setup: per-service IPSec info
3272 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3273 kSCDynamicStoreDomainSetup
,
3276 CFArrayAppendValue(patterns
, pattern
);
3279 // State: per-interface IPv4 info
3280 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
3281 kSCDynamicStoreDomainState
,
3284 CFArrayAppendValue(patterns
, pattern
);
3287 // State: per-interface IPv6 info
3288 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
3289 kSCDynamicStoreDomainState
,
3292 CFArrayAppendValue(patterns
, pattern
);
3295 #if !TARGET_OS_IPHONE
3296 // State: Power Management Capabilities
3297 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
3298 kSCDynamicStoreDomainState
,
3299 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
3300 CFArrayAppendValue(keys
, key
);
3302 #endif // TARGET_OS_IPHONE
3305 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
3307 CFRelease(patterns
);
3314 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store
,
3315 CFArrayRef changedKeys
,
3318 Boolean dnsConfigChanged
= FALSE
;
3321 CFIndex nChanges
= CFArrayGetCount(changedKeys
);
3323 #if !TARGET_OS_IPHONE
3324 Boolean powerStatusChanged
= FALSE
;
3325 #endif // !TARGET_OS_IPHONE
3326 const void * targets_q
[N_QUICK
];
3327 const void ** targets
= targets_q
;
3329 if (nChanges
== 0) {
3334 pthread_mutex_lock(&hn_lock
);
3336 nTargets
= (hn_targets
!= NULL
) ? CFSetGetCount(hn_targets
) : 0;
3337 if (nTargets
== 0) {
3338 /* if no addresses being monitored */
3342 #if !TARGET_OS_IPHONE
3343 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
3344 kSCDynamicStoreDomainState
,
3345 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
3346 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
3349 num
= SCDynamicStoreCopyValue(store
, key
);
3351 if (isA_CFNumber(num
) &&
3352 CFNumberGetValue(num
, kCFNumberSInt32Type
, &power_capabilities
)) {
3353 powerStatusChanged
= TRUE
;
3360 #endif // !TARGET_OS_IPHONE
3362 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3363 kSCDynamicStoreDomainState
,
3365 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
3366 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
3374 #if !TARGET_OS_IPHONE
3375 if (powerStatusChanged
) {
3379 #endif // !TARGET_OS_IPHONE
3381 if (dnsConfigChanged
) {
3391 case 0 : str
= ""; break;
3392 case 1 : str
= "network "; break;
3393 case 2 : str
= "DNS "; break;
3394 case 3 : str
= "network and DNS "; break;
3395 #if !TARGET_OS_IPHONE
3396 case 4 : str
= "power "; break;
3397 case 5 : str
= "network and power "; break;
3398 case 6 : str
= "DNS and power "; break;
3399 case 7 : str
= "network, DNS, and power "; break;
3400 #endif // !TARGET_OS_IPHONE
3401 default : str
= "??? ";
3404 SCLog(TRUE
, LOG_INFO
, CFSTR("process %sconfiguration change"), str
);
3407 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
3408 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
3409 CFSetGetValues(hn_targets
, targets
);
3410 for (i
= 0; i
< nTargets
; i
++) {
3411 SCNetworkReachabilityRef target
= targets
[i
];
3412 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3414 pthread_mutex_lock(&targetPrivate
->lock
);
3416 if (targetPrivate
->type
== reachabilityTypeName
) {
3417 Boolean dnsChanged
= dnsConfigChanged
;
3421 * if the DNS configuration didn't change we still need to
3422 * check that the DNS servers are accessible.
3424 SCNetworkReachabilityFlags ns_flags
;
3427 /* check the reachability of the DNS servers */
3428 ok
= _SC_R_checkResolverReachability(&store
,
3430 &targetPrivate
->haveDNS
,
3431 targetPrivate
->name
,
3432 targetPrivate
->serv
,
3433 targetPrivate
->log_prefix
);
3435 /* if we could not get DNS server info */
3436 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
3437 targetPrivate
->log_prefix
);
3439 } else if (rankReachability(ns_flags
) < 2) {
3441 * if DNS servers are not (or are no longer) reachable, set
3442 * flags based on the availability of configured (but not
3445 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
3446 targetPrivate
->log_prefix
);
3452 if (targetPrivate
->dnsPort
!= NULL
) {
3453 mach_port_t mp
= CFMachPortGetPort(targetPrivate
->dnsPort
);
3455 /* cancel the outstanding DNS query */
3456 SCLog(_sc_debug
, LOG_INFO
,
3457 CFSTR("%scancel DNS query for %s%s%s%s%s"),
3458 targetPrivate
->log_prefix
,
3459 targetPrivate
->name
!= NULL
? "name = " : "",
3460 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
3461 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
3462 targetPrivate
->serv
!= NULL
? "serv = " : "",
3463 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
3464 dequeueAsyncDNSQuery(target
);
3465 getaddrinfo_async_cancel(mp
);
3468 /* schedule request to resolve the name again */
3469 targetPrivate
->needResolve
= TRUE
;
3473 __SCNetworkReachabilityPerform(target
);
3475 pthread_mutex_unlock(&targetPrivate
->lock
);
3477 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
3481 pthread_mutex_unlock(&hn_lock
);
3486 #if !TARGET_OS_IPHONE
3487 static __inline__ Boolean
3488 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities
)
3491 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU \
3492 | kIOPMSystemPowerStateCapabilityNetwork \
3493 | kIOPMSystemPowerStateCapabilityDisk)
3495 if ((power_capabilities
& POWER_CAPABILITIES_NEED
) != POWER_CAPABILITIES_NEED
) {
3497 * we're not awake (from a networking point of view) unless we
3498 * have the CPU, disk, *and* network.
3503 if ((power_capabilities
& kIOPMSytemPowerStateCapabilitiesMask
) == POWER_CAPABILITIES_NEED
) {
3505 * if all we have is the CPU, disk, and network than this must
3506 * be a "maintenance" wake.
3513 #endif // !TARGET_OS_IPHONE
3517 rlsPerform(void *info
)
3520 void (*context_release
)(const void *);
3521 Boolean defer
= FALSE
;
3523 ReachabilityInfo reach_info
= NOT_REACHABLE
;
3524 SCNetworkReachabilityCallBack rlsFunction
;
3525 SCDynamicStoreRef store
= NULL
;
3526 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
3527 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3529 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%schecking target reachability"),
3530 targetPrivate
->log_prefix
);
3533 pthread_mutex_lock(&targetPrivate
->lock
);
3535 if (!targetPrivate
->scheduled
) {
3536 // if not currently scheduled
3537 pthread_mutex_unlock(&targetPrivate
->lock
);
3541 /* update reachability, notify if status changed */
3542 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, &reach_info
, TRUE
);
3543 if (store
!= NULL
) CFRelease(store
);
3545 /* if reachability status not available */
3546 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%flags not available"),
3547 targetPrivate
->log_prefix
);
3548 reach_info
= NOT_REACHABLE
;
3551 #if !TARGET_OS_IPHONE
3553 * We want to defer the notification if this is a maintenance wake *and*
3554 * the reachability flags that we would be reporting to the application
3555 * are better than those that we last reported.
3557 if (!systemIsAwake(power_capabilities
)) {
3558 /* if this is a maintenace wake */
3559 reach_info
.sleeping
= TRUE
;
3561 if (rankReachability(reach_info
.flags
) >= rankReachability(targetPrivate
->info
.flags
)) {
3563 * don't report the change if the new reachability flags are
3564 * the same or "better"
3567 } else if (bcmp(&targetPrivate
->last_notify
, &reach_info
, sizeof(reach_info
)) == 0) {
3568 /* if we have already posted this change */
3572 #endif // !TARGET_OS_IPHONE
3574 if (bcmp(&targetPrivate
->info
, &reach_info
, sizeof(reach_info
)) == 0) {
3575 SCLog(_sc_debug
, LOG_INFO
,
3576 CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"),
3577 targetPrivate
->log_prefix
,
3579 reach_info
.if_index
,
3580 reach_info
.sleeping
? "*" : "");
3581 pthread_mutex_unlock(&targetPrivate
->lock
);
3585 SCLog(_sc_debug
, LOG_INFO
,
3586 CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s"),
3587 targetPrivate
->log_prefix
,
3588 targetPrivate
->info
.flags
,
3589 targetPrivate
->info
.if_index
,
3590 targetPrivate
->info
.sleeping
? "*" : "",
3592 reach_info
.if_index
,
3593 reach_info
.sleeping
? "*" : "",
3594 defer
? ", deferred" : "");
3596 /* update flags / interface */
3597 targetPrivate
->info
= reach_info
;
3599 /* as needed, defer the notification */
3601 pthread_mutex_unlock(&targetPrivate
->lock
);
3605 /* save last notification info */
3606 targetPrivate
->last_notify
= reach_info
;
3609 rlsFunction
= targetPrivate
->rlsFunction
;
3610 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
3611 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
3612 context_release
= targetPrivate
->rlsContext
.release
;
3614 context_info
= targetPrivate
->rlsContext
.info
;
3615 context_release
= NULL
;
3618 pthread_mutex_unlock(&targetPrivate
->lock
);
3620 if (rlsFunction
!= NULL
) {
3621 (*rlsFunction
)(target
, reach_info
.flags
, context_info
);
3624 if (context_release
!= NULL
) {
3625 (*context_release
)(context_info
);
3633 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
3634 SCNetworkReachabilityCallBack callout
,
3635 SCNetworkReachabilityContext
*context
)
3637 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3639 pthread_mutex_lock(&targetPrivate
->lock
);
3641 if (targetPrivate
->rlsContext
.release
!= NULL
) {
3642 /* let go of the current context */
3643 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
3646 targetPrivate
->rlsFunction
= callout
;
3647 targetPrivate
->rlsContext
.info
= NULL
;
3648 targetPrivate
->rlsContext
.retain
= NULL
;
3649 targetPrivate
->rlsContext
.release
= NULL
;
3650 targetPrivate
->rlsContext
.copyDescription
= NULL
;
3652 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
3653 if (context
->retain
!= NULL
) {
3654 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
3658 pthread_mutex_unlock(&targetPrivate
->lock
);
3665 reachRLSCopyDescription(const void *info
)
3667 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
3669 return CFStringCreateWithFormat(NULL
,
3671 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
3677 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
3678 CFRunLoopRef runLoop
,
3679 CFStringRef runLoopMode
,
3680 #if !TARGET_OS_IPHONE
3681 dispatch_queue_t queue
,
3682 #else // !TARGET_OS_IPHONE
3684 #endif // !TARGET_OS_IPHONE
3687 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3688 Boolean init
= FALSE
;
3692 pthread_mutex_lock(&hn_lock
);
3694 pthread_mutex_lock(&targetPrivate
->lock
);
3696 #if !TARGET_OS_IPHONE
3697 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
3698 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
3699 _SCErrorSet(kSCStatusInvalidArgument
);
3702 #endif // !TARGET_OS_IPHONE
3704 /* schedule the SCNetworkReachability run loop source */
3706 if (!onDemand
&& (hn_store
== NULL
)) {
3708 * if we are not monitoring any hosts, start watching
3710 if (!dns_configuration_watch()) {
3712 _SCErrorSet(kSCStatusFailed
);
3716 hn_store
= SCDynamicStoreCreate(NULL
,
3717 CFSTR("SCNetworkReachability"),
3718 __SCNetworkReachabilityHandleChanges
,
3720 if (hn_store
== NULL
) {
3721 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed"));
3725 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
3727 #if !TARGET_OS_IPHONE
3728 hn_dispatchQueue
= dispatch_queue_create("com.apple.SCNetworkReachabilty.network_changes", NULL
);
3729 if (hn_dispatchQueue
== NULL
) {
3730 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed"));
3731 _SCErrorSet(kSCStatusFailed
);
3732 CFRelease(hn_store
);
3736 CFRetain(hn_store
); // Note: will be released when the dispatch queue is released
3737 dispatch_set_context(hn_dispatchQueue
, (void *)hn_store
);
3738 dispatch_set_finalizer_f(hn_dispatchQueue
, (dispatch_function_t
)CFRelease
);
3740 ok
= SCDynamicStoreSetDispatchQueue(hn_store
, hn_dispatchQueue
);
3742 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
3743 dispatch_release(hn_dispatchQueue
);
3744 hn_dispatchQueue
= NULL
;
3745 CFRelease(hn_store
);
3749 #else // !TARGET_OS_IPHONE
3750 hn_storeRLS
= SCDynamicStoreCreateRunLoopSource(NULL
, hn_store
, 0);
3751 hn_rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3752 #endif // !TARGET_OS_IPHONE
3753 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
3756 if (!targetPrivate
->scheduled
) {
3757 CFRunLoopSourceContext context
= { 0 // version
3758 , (void *)target
// info
3759 , CFRetain
// retain
3760 , CFRelease
// release
3761 , reachRLSCopyDescription
// copyDescription
3766 , rlsPerform
// perform
3769 if (runLoop
!= NULL
) {
3770 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
3771 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3774 targetPrivate
->scheduled
= TRUE
;
3775 if (targetPrivate
->type
== reachabilityTypeName
) {
3776 targetPrivate
->needResolve
= TRUE
;
3781 #if !TARGET_OS_IPHONE
3782 if (queue
!= NULL
) {
3783 targetPrivate
->dispatchQueue
= queue
;
3784 dispatch_retain(targetPrivate
->dispatchQueue
);
3786 #endif // !TARGET_OS_IPHONE
3788 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
3790 * if we do not already have host notifications scheduled with
3791 * this runLoop / runLoopMode
3793 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
3795 if (targetPrivate
->dnsRLS
!= NULL
) {
3796 /* if we have an active async DNS query too */
3797 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
3801 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
3803 #if TARGET_OS_IPHONE
3805 /* schedule the global SCDynamicStore run loop source */
3807 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
3809 * if we do not already have SC notifications scheduled with
3810 * this runLoop / runLoopMode
3812 CFRunLoopAddSource(runLoop
, hn_storeRLS
, runLoopMode
);
3815 _SC_schedule(target
, runLoop
, runLoopMode
, hn_rlList
);
3817 #endif // TARGET_OS_IPHONE
3820 CFSetAddValue(hn_targets
, target
);
3823 ReachabilityInfo reach_info
= NOT_REACHABLE
;
3824 SCDynamicStoreRef store
= NULL
;
3827 * if we have yet to schedule SC notifications for this address
3828 * - initialize current reachability status
3830 if (__SCNetworkReachabilityGetFlags(&store
, target
, &reach_info
, TRUE
)) {
3832 * if reachability status available
3834 * - schedule notification to report status via callback
3836 targetPrivate
->info
= reach_info
;
3837 __SCNetworkReachabilityPerform(target
);
3839 /* if reachability status not available, async lookup started */
3840 targetPrivate
->info
= NOT_REACHABLE
;
3842 if (store
!= NULL
) CFRelease(store
);
3845 if (targetPrivate
->onDemandServer
!= NULL
) {
3846 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, queue
, TRUE
);
3853 pthread_mutex_unlock(&targetPrivate
->lock
);
3855 pthread_mutex_unlock(&hn_lock
);
3862 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
3863 CFRunLoopRef runLoop
,
3864 CFStringRef runLoopMode
,
3867 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3872 pthread_mutex_lock(&hn_lock
);
3874 pthread_mutex_lock(&targetPrivate
->lock
);
3876 #if !TARGET_OS_IPHONE
3877 if (((runLoop
== NULL
) && (targetPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
3878 ((runLoop
!= NULL
) && (targetPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are not)
3879 _SCErrorSet(kSCStatusInvalidArgument
);
3882 #endif // !TARGET_OS_IPHONE
3884 if (!targetPrivate
->scheduled
) {
3885 // if not currently scheduled
3886 _SCErrorSet(kSCStatusInvalidArgument
);
3890 // first, unschedule the target specific sources
3891 #if !TARGET_OS_IPHONE
3892 if (targetPrivate
->dispatchQueue
!= NULL
) {
3893 if (targetPrivate
->onDemandServer
!= NULL
) {
3894 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
3897 #endif // !TARGET_OS_IPHONE
3899 if (!_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
3900 // if not currently scheduled
3901 _SCErrorSet(kSCStatusInvalidArgument
);
3905 if (targetPrivate
->onDemandServer
!= NULL
) {
3906 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, TRUE
);
3909 n
= CFArrayGetCount(targetPrivate
->rlList
);
3910 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
3911 // if target is no longer scheduled for this runLoop / runLoopMode
3912 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
3914 if (targetPrivate
->dnsRLS
!= NULL
) {
3915 // if we have an active async DNS query too
3916 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
3920 // if *all* notifications have been unscheduled
3921 CFRelease(targetPrivate
->rlList
);
3922 targetPrivate
->rlList
= NULL
;
3923 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
3924 CFRelease(targetPrivate
->rls
);
3925 targetPrivate
->rls
= NULL
;
3931 // if *all* notifications have been unscheduled
3932 targetPrivate
->scheduled
= FALSE
;
3935 CFSetRemoveValue(hn_targets
, target
); // cleanup notification resources
3938 if (targetPrivate
->dnsPort
!= NULL
) {
3939 mach_port_t mp
= CFMachPortGetPort(targetPrivate
->dnsPort
);
3941 // if we have an active async DNS query
3942 dequeueAsyncDNSQuery(target
);
3943 getaddrinfo_async_cancel(mp
);
3947 #if !TARGET_OS_IPHONE
3948 if (runLoop
== NULL
) {
3949 dispatch_release(targetPrivate
->dispatchQueue
);
3950 targetPrivate
->dispatchQueue
= NULL
;
3952 #endif // !TARGET_OS_IPHONE
3954 // now, unschedule the global dynamic store source
3955 #if TARGET_OS_IPHONE
3957 (void)_SC_unschedule(target
, runLoop
, runLoopMode
, hn_rlList
, FALSE
);
3959 n
= CFArrayGetCount(hn_rlList
);
3960 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
3962 * if we no longer have any addresses scheduled for
3963 * this runLoop / runLoopMode
3965 CFRunLoopRemoveSource(runLoop
, hn_storeRLS
, runLoopMode
);
3968 if (CFEqual(runLoopMode
, kCFRunLoopCommonModes
)) {
3971 modes
= CFRunLoopCopyAllModes(runLoop
);
3972 if (modes
!= NULL
) {
3974 CFIndex n
= CFArrayGetCount(modes
);
3976 for (i
= 0; i
< n
; i
++) {
3979 mode
= CFArrayGetValueAtIndex(modes
, i
);
3980 if (_SC_isScheduled(NULL
, runLoop
, mode
, hn_rlList
)) {
3982 * removing kCFRunLoopCommonModes cleaned up more
3983 * than we wanted. Add back the modes that were
3984 * expect to be present.
3986 CFRunLoopAddSource(runLoop
, hn_storeRLS
, mode
);
3992 } else if (_SC_isScheduled(NULL
, runLoop
, kCFRunLoopCommonModes
, hn_rlList
)) {
3994 * if we are still scheduling kCFRunLoopCommonModes, make sure that
3995 * none of the common modes were inadvertently removed.
3997 CFRunLoopAddSource(runLoop
, hn_storeRLS
, kCFRunLoopCommonModes
);
4002 #endif // TARGET_OS_IPHONE
4004 n
= CFSetGetCount(hn_targets
);
4006 // if we are no longer monitoring any targets
4007 #if !TARGET_OS_IPHONE
4008 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
4009 dispatch_release(hn_dispatchQueue
);
4010 hn_dispatchQueue
= NULL
;
4011 #else // !TARGET_OS_IPHONE
4012 CFRunLoopSourceInvalidate(hn_storeRLS
);
4013 CFRelease(hn_storeRLS
);
4015 CFRelease(hn_rlList
);
4017 #endif // !TARGET_OS_IPHONE
4018 CFRelease(hn_store
);
4020 CFRelease(hn_targets
);
4024 * until we start monitoring again, ensure that
4025 * any resources associated with tracking the
4026 * DNS configuration have been released.
4028 dns_configuration_unwatch();
4035 pthread_mutex_unlock(&targetPrivate
->lock
);
4037 pthread_mutex_unlock(&hn_lock
);
4043 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
4044 CFRunLoopRef runLoop
,
4045 CFStringRef runLoopMode
)
4047 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
4048 _SCErrorSet(kSCStatusInvalidArgument
);
4052 return __SCNetworkReachabilityScheduleWithRunLoop(target
, runLoop
, runLoopMode
, NULL
, FALSE
);
4056 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
4057 CFRunLoopRef runLoop
,
4058 CFStringRef runLoopMode
)
4060 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
4061 _SCErrorSet(kSCStatusInvalidArgument
);
4065 return __SCNetworkReachabilityUnscheduleFromRunLoop(target
, runLoop
, runLoopMode
, FALSE
);
4068 #if !TARGET_OS_IPHONE
4070 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
4071 dispatch_queue_t queue
)
4075 if (!isA_SCNetworkReachability(target
)) {
4076 _SCErrorSet(kSCStatusInvalidArgument
);
4080 if (queue
!= NULL
) {
4081 ok
= __SCNetworkReachabilityScheduleWithRunLoop(target
, NULL
, NULL
, queue
, FALSE
);
4083 ok
= __SCNetworkReachabilityUnscheduleFromRunLoop(target
, NULL
, NULL
, FALSE
);
4088 #endif // !TARGET_OS_IPHONE