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 CFMachPortInvalidate(targetPrivate
->dnsPort
);
2010 CFRelease(targetPrivate
->dnsPort
);
2011 targetPrivate
->dnsPort
= NULL
;
2012 targetPrivate
->dnsMP
= MACH_PORT_NULL
;
2019 processAsyncDNSReply(mach_port_t mp
, void *msg
, SCNetworkReachabilityRef target
)
2022 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2024 pthread_mutex_lock(&targetPrivate
->lock
);
2026 if (mp
!= targetPrivate
->dnsMP
) {
2027 // we've received a callback on the async DNS port but since the
2028 // associated CFMachPort doesn't match than the request must have
2029 // already been cancelled.
2030 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP"));
2031 pthread_mutex_unlock(&targetPrivate
->lock
);
2035 dequeueAsyncDNSQuery(target
);
2036 status
= getaddrinfo_async_handle_reply(msg
);
2037 if ((status
== 0) &&
2038 (targetPrivate
->resolvedAddress
== NULL
) && (targetPrivate
->resolvedAddressError
== NETDB_SUCCESS
)) {
2041 // if the request is not complete and needs to be re-queued
2042 ok
= enqueueAsyncDNSQuery(target
, mp
);
2044 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply enqueueAsyncDNSQuery() failed"));
2048 pthread_mutex_unlock(&targetPrivate
->lock
);
2055 check_resolver_reachability(SCDynamicStoreRef
*storeP
,
2056 dns_resolver_t
*resolver
,
2057 SCNetworkReachabilityFlags
*flags
,
2059 const char *log_prefix
)
2064 *flags
= kSCNetworkReachabilityFlagsReachable
;
2067 for (i
= 0; i
< resolver
->n_nameserver
; i
++) {
2068 struct sockaddr
*address
= resolver
->nameserver
[i
];
2069 ReachabilityInfo ns_info
;
2073 if (address
->sa_family
!= AF_INET
) {
2075 * we need to skip non-IPv4 DNS server
2076 * addresses (at least until [3510431] has
2082 ok
= checkAddress(storeP
, address
, &ns_info
, log_prefix
);
2088 if (rankReachability(ns_info
.flags
) < rankReachability(*flags
)) {
2089 /* return the worst case result */
2090 *flags
= ns_info
.flags
;
2101 check_matching_resolvers(SCDynamicStoreRef
*storeP
,
2102 dns_config_t
*dns_config
,
2104 SCNetworkReachabilityFlags
*flags
,
2106 const char *log_prefix
)
2109 Boolean matched
= FALSE
;
2110 const char *name
= fqdn
;
2112 while (!matched
&& (name
!= NULL
)) {
2116 * check if the provided name (or sub-component)
2117 * matches one of our resolver configurations.
2120 for (i
= 0; i
< dns_config
->n_resolver
; i
++) {
2122 dns_resolver_t
*resolver
;
2124 resolver
= dns_config
->resolver
[i
];
2125 domain
= resolver
->domain
;
2126 if (domain
!= NULL
&& (len
== strlen(domain
))) {
2127 if (strcasecmp(name
, domain
) == 0) {
2131 * if name matches domain
2134 ok
= check_resolver_reachability(storeP
, resolver
, flags
, haveDNS
, log_prefix
);
2145 * we have not found a matching resolver, try
2146 * a less qualified domain
2148 name
= strchr(name
, '.');
2149 if ((name
!= NULL
) && (*name
!= '\0')) {
2161 static dns_configuration_t
*
2162 dns_configuration_retain()
2164 pthread_mutex_lock(&dns_lock
);
2166 if ((dns_configuration
!= NULL
) && dns_token_valid
) {
2171 * check if the global [DNS] configuration snapshot needs
2174 status
= notify_check(dns_token
, &check
);
2175 if (status
!= NOTIFY_STATUS_OK
) {
2176 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
2179 if ((status
!= NOTIFY_STATUS_OK
) || (check
!= 0)) {
2181 * if the snapshot needs to be refreshed
2183 if (dns_configuration
->refs
== 0) {
2184 dns_configuration_free(dns_configuration
->config
);
2185 CFAllocatorDeallocate(NULL
, dns_configuration
);
2187 dns_configuration
= NULL
;
2191 if (dns_configuration
== NULL
) {
2192 dns_config_t
*new_config
;
2194 new_config
= dns_configuration_copy();
2195 if (new_config
!= NULL
) {
2196 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
2197 dns_configuration
->config
= new_config
;
2198 dns_configuration
->refs
= 0;
2202 if (dns_configuration
!= NULL
) {
2203 dns_configuration
->refs
++;
2206 pthread_mutex_unlock(&dns_lock
);
2207 return dns_configuration
;
2212 dns_configuration_release(dns_configuration_t
*config
)
2214 pthread_mutex_lock(&dns_lock
);
2217 if (config
->refs
== 0) {
2218 if ((dns_configuration
!= config
)) {
2219 dns_configuration_free(config
->config
);
2220 CFAllocatorDeallocate(NULL
, config
);
2224 pthread_mutex_unlock(&dns_lock
);
2230 dns_configuration_watch()
2233 const char *dns_key
;
2237 pthread_mutex_lock(&dns_lock
);
2239 dns_key
= dns_configuration_notify_key();
2240 if (dns_key
== NULL
) {
2241 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
2245 status
= notify_register_check(dns_key
, &dns_token
);
2246 if (status
== NOTIFY_STATUS_OK
) {
2247 dns_token_valid
= TRUE
;
2249 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
);
2253 status
= notify_check(dns_token
, &dns_check
);
2254 if (status
!= NOTIFY_STATUS_OK
) {
2255 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
2256 (void)notify_cancel(dns_token
);
2257 dns_token_valid
= FALSE
;
2265 pthread_mutex_unlock(&dns_lock
);
2271 dns_configuration_unwatch()
2273 pthread_mutex_lock(&dns_lock
);
2275 (void)notify_cancel(dns_token
);
2276 dns_token_valid
= FALSE
;
2278 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
2279 dns_configuration_free(dns_configuration
->config
);
2280 CFAllocatorDeallocate(NULL
, dns_configuration
);
2281 dns_configuration
= NULL
;
2284 pthread_mutex_unlock(&dns_lock
);
2290 _SC_R_checkResolverReachability(SCDynamicStoreRef
*storeP
,
2291 SCNetworkReachabilityFlags
*flags
,
2293 const char *nodename
,
2294 const char *servname
,
2295 const char *log_prefix
)
2297 dns_resolver_t
*default_resolver
;
2298 dns_configuration_t
*dns
;
2299 Boolean found
= FALSE
;
2300 char *fqdn
= (char *)nodename
;
2302 Boolean isFQDN
= FALSE
;
2305 Boolean useDefault
= FALSE
;
2308 * We first assume that all of the configured DNS servers
2309 * are available. Since we don't know which name server will
2310 * be consulted to resolve the specified nodename we need to
2311 * check the availability of ALL name servers. We can only
2312 * proceed if we know that our query can be answered.
2315 *flags
= kSCNetworkReachabilityFlagsReachable
;
2318 len
= (nodename
!= NULL
) ? strlen(nodename
) : 0;
2320 if ((servname
== NULL
) || (strlen(servname
) == 0)) {
2321 // if no nodename or servname, return not reachable
2327 dns
= dns_configuration_retain();
2330 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no configuration"), log_prefix
);
2334 if (dns
->config
->n_resolver
== 0) {
2335 // if no resolver configuration
2336 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no resolvers"), log_prefix
);
2340 *flags
= kSCNetworkReachabilityFlagsReachable
;
2342 if (fqdn
[len
- 1] == '.') {
2345 // trim trailing '.''s
2346 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
2347 if (fqdn
== nodename
) {
2348 fqdn
= strdup(nodename
);
2354 default_resolver
= dns
->config
->resolver
[0];
2357 * check if the provided name matches a supplemental domain
2359 found
= check_matching_resolvers(storeP
, dns
->config
, fqdn
, flags
, haveDNS
, log_prefix
);
2361 if (!found
&& !isFQDN
) {
2363 * if we did not match a supplemental domain name and if the
2364 * provided name has enough "."s then the first query will be
2365 * directed to the default resolver.
2371 #define NDOTS_OPT "ndots="
2372 #define NDOTS_OPT_LEN (sizeof("ndots=") - 1)
2374 if (default_resolver
->options
!= NULL
) {
2375 cp
= strstr(default_resolver
->options
, NDOTS_OPT
);
2377 ((cp
== default_resolver
->options
) || isspace(cp
[-1])) &&
2378 ((cp
[NDOTS_OPT_LEN
] != '\0') && isdigit(cp
[NDOTS_OPT_LEN
]))) {
2382 cp
+= NDOTS_OPT_LEN
;
2384 val
= strtol(cp
, &end
, 10);
2385 if ((*cp
!= '\0') && (cp
!= end
) && (errno
== 0) &&
2386 ((*end
== '\0') || isspace(*end
))) {
2393 for (cp
= fqdn
; *cp
!= '\0'; cp
++) {
2394 if (*cp
== '.') dots
++;
2402 if (!found
&& !isFQDN
&& !useDefault
&& (dns
->config
->n_resolver
> 1)) {
2404 * FQDN not specified, try matching w/search domains
2406 if (default_resolver
->n_search
> 0) {
2407 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
2409 char *search_fqdn
= NULL
;
2411 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
2416 // try the provided name with the search domain appended
2417 found
= check_matching_resolvers(storeP
,
2425 } else if (default_resolver
->domain
!= NULL
) {
2427 int domain_parts
= 0;
2429 // count domain parts
2430 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
2436 // remove trailing dots
2437 for (dp
--; (dp
>= default_resolver
->domain
) && (*dp
== '.'); dp
--) {
2442 if (dp
>= default_resolver
->domain
) {
2443 // dots are separators, bump # of components
2447 dp
= default_resolver
->domain
;
2448 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= domain_parts
); i
++) {
2450 char *search_fqdn
= NULL
;
2452 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
2457 // try the provided name with the [default] domain appended
2458 found
= check_matching_resolvers(storeP
,
2466 // move to the next component of the [default] domain
2467 dp
= strchr(dp
, '.') + 1;
2474 * check the reachability of the default resolver
2476 ok
= check_resolver_reachability(storeP
, default_resolver
, flags
, haveDNS
, log_prefix
);
2479 if (fqdn
!= nodename
) free(fqdn
);
2484 dns_configuration_release(dns
);
2492 _SC_checkResolverReachability(SCDynamicStoreRef
*storeP
,
2493 SCNetworkReachabilityFlags
*flags
,
2495 const char *nodename
,
2496 const char *servname
)
2498 return _SC_R_checkResolverReachability(storeP
, flags
, haveDNS
, nodename
, servname
, "");
2503 * _SC_checkResolverReachabilityByAddress()
2505 * Given an IP address, determine whether a reverse DNS query can be issued
2506 * using the current network configuration.
2509 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
2510 SCNetworkReachabilityFlags
*flags
,
2512 struct sockaddr
*sa
)
2519 * Ideally, we would have an API that given a local IP
2520 * address would return the DNS server(s) that would field
2521 * a given PTR query. Fortunately, we do have an SPI which
2522 * which will provide this information given a "name" so we
2523 * take the address, convert it into the inverse query name,
2524 * and find out which servers should be consulted.
2527 switch (sa
->sa_family
) {
2533 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
2536 * build "PTR" query name
2537 * NNN.NNN.NNN.NNN.in-addr.arpa.
2539 rev
.s_addr
= sin
->sin_addr
.s_addr
;
2540 (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.",
2551 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
2552 int x
= sizeof(ptr_name
);
2556 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
2557 * 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.
2559 for (i
= sizeof(sin6
->sin6_addr
) - 1; i
>= 0; i
--) {
2560 n
= snprintf(&ptr_name
[s
], x
, "%x.%x.",
2561 ( sin6
->sin6_addr
.s6_addr
[i
] & 0xf),
2562 ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf));
2563 if ((n
== -1) || (n
>= x
)) {
2571 n
= snprintf(&ptr_name
[s
], x
, "ip6.arpa.");
2572 if ((n
== -1) || (n
>= x
)) {
2583 ok
= _SC_R_checkResolverReachability(storeP
, flags
, haveDNS
, ptr_name
, NULL
, "");
2592 startAsyncDNSQuery(SCNetworkReachabilityRef target
) {
2596 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2598 (void) gettimeofday(&targetPrivate
->dnsQueryStart
, NULL
);
2600 error
= getaddrinfo_async_start(&mp
,
2601 targetPrivate
->name
,
2602 targetPrivate
->serv
,
2603 &targetPrivate
->hints
,
2604 __SCNetworkReachabilityCallbackSetResolvedAddress
,
2607 /* save the error associated with the attempt to resolve the name */
2608 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
);
2612 ok
= enqueueAsyncDNSQuery(target
, mp
);
2618 #pragma mark OnDemand
2622 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target
,
2623 CFDictionaryRef
*userOptions
)
2625 SCNetworkServiceRef service
= NULL
;
2626 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2628 if (!isA_SCNetworkReachability(target
)) {
2629 _SCErrorSet(kSCStatusInvalidArgument
);
2633 if (targetPrivate
->onDemandServiceID
!= NULL
) {
2634 service
= _SCNetworkServiceCopyActive(NULL
, targetPrivate
->onDemandServiceID
);
2637 if (userOptions
!= NULL
) {
2638 if (targetPrivate
->onDemandName
!= NULL
) {
2639 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2640 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
);
2642 *userOptions
= NULL
;
2651 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer
,
2652 SCNetworkReachabilityFlags onDemandFlags
,
2655 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2656 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2658 pthread_mutex_lock(&targetPrivate
->lock
);
2660 if (!targetPrivate
->scheduled
) {
2661 // if not currently scheduled
2662 pthread_mutex_unlock(&targetPrivate
->lock
);
2666 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sOnDemand \"server\" status changed"),
2667 targetPrivate
->log_prefix
);
2668 __SCNetworkReachabilityPerform(target
);
2670 pthread_mutex_unlock(&targetPrivate
->lock
);
2677 __SCNetworkReachabilityOnDemandCheck(SCDynamicStoreRef
*storeP
,
2678 SCNetworkReachabilityRef target
,
2679 Boolean onDemandRetry
,
2680 SCNetworkReachabilityFlags
*flags
)
2683 Boolean onDemand
= FALSE
;
2684 CFStringRef onDemandRemoteAddress
= NULL
;
2685 CFStringRef onDemandServiceID
= NULL
;
2686 SCNetworkConnectionStatus onDemandStatus
;
2687 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2689 // SCLog(_sc_debug, LOG_INFO,
2690 // CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"),
2691 // targetPrivate->log_prefix,
2692 // onDemandRetry ? "after" : "before");
2694 if (targetPrivate
->onDemandName
== NULL
) {
2695 targetPrivate
->onDemandName
= CFStringCreateWithCString(NULL
, targetPrivate
->name
, kCFStringEncodingUTF8
);
2699 * check if an OnDemand VPN configuration matches the name.
2701 ok
= __SCNetworkConnectionCopyOnDemandInfoWithName(storeP
,
2702 targetPrivate
->onDemandName
,
2706 &onDemandRemoteAddress
);
2707 if (!_SC_CFEqual(targetPrivate
->onDemandRemoteAddress
, onDemandRemoteAddress
) ||
2708 !_SC_CFEqual(targetPrivate
->onDemandServiceID
, onDemandServiceID
)) {
2709 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
2710 CFRelease(targetPrivate
->onDemandRemoteAddress
);
2711 targetPrivate
->onDemandRemoteAddress
= NULL
;
2714 if (targetPrivate
->onDemandServer
!= NULL
) {
2715 #if !TARGET_OS_IPHONE
2716 if (targetPrivate
->dispatchQueue
!= NULL
) {
2718 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
2720 #endif // !TARGET_OS_IPHONE
2721 if (targetPrivate
->rls
!= NULL
) {
2726 n
= CFArrayGetCount(targetPrivate
->rlList
);
2727 for (i
= 0; i
< n
; i
+= 3) {
2728 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
2729 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
2731 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, TRUE
);
2735 CFRelease(targetPrivate
->onDemandServer
);
2736 targetPrivate
->onDemandServer
= NULL
;
2739 if (targetPrivate
->onDemandServiceID
!= NULL
) {
2740 CFRelease(targetPrivate
->onDemandServiceID
);
2741 targetPrivate
->onDemandServiceID
= NULL
;
2745 if (onDemandStatus
!= kSCNetworkConnectionConnected
) {
2747 * if we have a VPN configuration matching the name *and* we need to
2748 * bring the VPN up. Combine our flags with those of the VPN server.
2750 if (targetPrivate
->onDemandServer
== NULL
) {
2751 CFMutableDictionaryRef options
;
2753 options
= CFDictionaryCreateMutable(NULL
,
2755 &kCFTypeDictionaryKeyCallBacks
,
2756 &kCFTypeDictionaryValueCallBacks
);
2757 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, onDemandRemoteAddress
);
2758 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandByPass
, kCFBooleanTrue
);
2759 targetPrivate
->onDemandServer
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
2762 if (targetPrivate
->scheduled
) {
2763 SCNetworkReachabilityContext context
= { 0, NULL
, CFRetain
, CFRelease
, CFCopyDescription
};
2765 context
.info
= (void *)target
;
2766 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
,
2767 __SCNetworkReachabilityOnDemandCheckCallback
,
2770 // schedule server reachability to match that of the target
2771 #if !TARGET_OS_IPHONE
2772 if (targetPrivate
->dispatchQueue
!= NULL
) {
2773 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, targetPrivate
->dispatchQueue
, TRUE
);
2775 #endif // !TARGET_OS_IPHONE
2780 n
= CFArrayGetCount(targetPrivate
->rlList
);
2781 for (i
= 0; i
< n
; i
+= 3) {
2782 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
2783 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
2785 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, NULL
, TRUE
);
2791 ok
= SCNetworkReachabilityGetFlags(targetPrivate
->onDemandServer
, flags
);
2792 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status * = 0x%08x"),
2793 targetPrivate
->log_prefix
,
2795 if (ok
&& (*flags
& kSCNetworkReachabilityFlagsReachable
)) {
2796 if (!(*flags
& kSCNetworkReachabilityFlagsTransientConnection
)) {
2797 // start clean if not already layered on a transient network
2800 *flags
|= kSCNetworkReachabilityFlagsReachable
;
2801 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
2802 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
2803 *flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
2806 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service * = %@"),
2807 targetPrivate
->log_prefix
,
2809 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after OnDemand connect)"),
2810 targetPrivate
->log_prefix
);
2817 if (onDemandRemoteAddress
!= NULL
) {
2818 if (targetPrivate
->onDemandRemoteAddress
== NULL
) {
2819 targetPrivate
->onDemandRemoteAddress
= onDemandRemoteAddress
;
2821 CFRelease(onDemandRemoteAddress
);
2825 if (onDemandServiceID
!= NULL
) {
2826 if (targetPrivate
->onDemandServiceID
== NULL
) {
2827 targetPrivate
->onDemandServiceID
= onDemandServiceID
;
2829 CFRelease(onDemandServiceID
);
2839 #pragma mark Reachability Flags
2843 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef
*storeP
,
2844 SCNetworkReachabilityRef target
,
2845 ReachabilityInfo
*reach_info
,
2848 CFMutableArrayRef addresses
= NULL
;
2849 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2850 ReachabilityInfo my_info
= NOT_REACHABLE
;
2853 *reach_info
= NOT_REACHABLE
;
2855 if (!isA_SCNetworkReachability(target
)) {
2856 _SCErrorSet(kSCStatusInvalidArgument
);
2860 switch (targetPrivate
->type
) {
2861 case reachabilityTypeAddress
:
2862 case reachabilityTypeAddressPair
: {
2864 * Check "local" address
2866 if (targetPrivate
->localAddress
!= NULL
) {
2868 * Check "local" address
2870 ok
= checkAddress(storeP
,
2871 targetPrivate
->localAddress
,
2873 targetPrivate
->log_prefix
);
2875 goto error
; /* not today */
2878 if (!(my_info
.flags
& kSCNetworkReachabilityFlagsIsLocalAddress
)) {
2879 goto error
; /* not reachable, non-"local" address */
2884 * Check "remote" address
2886 if (targetPrivate
->remoteAddress
!= NULL
) {
2888 * in cases where we have "local" and "remote" addresses
2889 * we need to re-initialize the to-be-returned flags.
2891 my_info
= NOT_REACHABLE
;
2894 * Check "remote" address
2896 ok
= checkAddress(storeP
,
2897 targetPrivate
->remoteAddress
,
2899 targetPrivate
->log_prefix
);
2901 goto error
; /* not today */
2909 case reachabilityTypeName
: {
2910 struct timeval dnsQueryStart
;
2912 SCNetworkReachabilityFlags ns_flags
;
2913 struct addrinfo
*res
;
2915 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
2916 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
2917 /* if resolved or an error had been detected */
2919 /* if not an async request */
2920 goto checkResolvedAddress
;
2921 } else if ((targetPrivate
->dnsPort
== NULL
) && !targetPrivate
->needResolve
) {
2922 /* if async request, no query active, and no query needed */
2923 goto checkResolvedAddress
;
2927 if (!targetPrivate
->onDemandBypass
) {
2931 * before we attempt our initial DNS query, check if there is
2932 * an OnDemand configuration that we should be using.
2934 onDemand
= __SCNetworkReachabilityOnDemandCheck(storeP
, target
, FALSE
, &my_info
.flags
);
2936 /* if OnDemand connection is needed */
2941 /* check the reachability of the DNS servers */
2942 ok
= _SC_R_checkResolverReachability(storeP
,
2944 &targetPrivate
->haveDNS
,
2945 targetPrivate
->name
,
2946 targetPrivate
->serv
,
2947 targetPrivate
->log_prefix
);
2949 /* if we could not get DNS server info */
2950 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
2951 targetPrivate
->log_prefix
);
2953 } else if (rankReachability(ns_flags
) < 2) {
2955 * if DNS servers are not (or are no longer) reachable, set
2956 * flags based on the availability of configured (but not
2960 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
2961 targetPrivate
->log_prefix
);
2963 ok
= checkAddress(storeP
,
2966 targetPrivate
->log_prefix
);
2968 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sNo available networks"),
2969 targetPrivate
->log_prefix
);
2973 if (async
&& targetPrivate
->scheduled
) {
2975 * return "host not found", set flags appropriately,
2976 * and schedule notification.
2978 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME
,
2981 my_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
2983 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sno DNS servers are reachable"),
2984 targetPrivate
->log_prefix
);
2985 __SCNetworkReachabilityPerform(target
);
2991 /* for async requests we return the last known status */
2992 my_info
= targetPrivate
->info
;
2994 if (targetPrivate
->dnsPort
!= NULL
) {
2995 /* if request already in progress */
2996 SCLog(_sc_debug
, LOG_INFO
,
2997 CFSTR("%swaiting for DNS reply"),
2998 targetPrivate
->log_prefix
);
2999 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
3000 /* updated reachability based on the previous reply */
3001 goto checkResolvedAddress
;
3006 SCLog(_sc_debug
, LOG_INFO
,
3007 CFSTR("%sstart DNS query for %s%s%s%s%s"),
3008 targetPrivate
->log_prefix
,
3009 targetPrivate
->name
!= NULL
? "name = " : "",
3010 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
3011 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
3012 targetPrivate
->serv
!= NULL
? "serv = " : "",
3013 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
3016 * initiate an async DNS query
3018 if (!startAsyncDNSQuery(target
)) {
3019 /* if we could not initiate the request, process error */
3020 goto checkResolvedAddress
;
3023 /* request initiated */
3027 SCLog(_sc_debug
, LOG_INFO
,
3028 CFSTR("%scheck DNS for %s%s%s%s%s"),
3029 targetPrivate
->log_prefix
,
3030 targetPrivate
->name
!= NULL
? "name = " : "",
3031 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
3032 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
3033 targetPrivate
->serv
!= NULL
? "serv = " : "",
3034 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
3037 * OK, all of the DNS name servers are available. Let's
3038 * resolve the nodename into an address.
3041 (void) gettimeofday(&dnsQueryStart
, NULL
);
3044 error
= getaddrinfo(targetPrivate
->name
,
3045 targetPrivate
->serv
,
3046 &targetPrivate
->hints
,
3049 __log_query_time(target
,
3050 ((error
== 0) && (res
!= NULL
)), // if successful query
3052 &dnsQueryStart
); // start time
3054 __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
);
3056 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
3058 checkResolvedAddress
:
3061 * We first assume that the requested host is NOT available.
3062 * Then, check each address for accessibility and return the
3063 * best status available.
3065 my_info
= NOT_REACHABLE
;
3067 if (isA_CFArray(addresses
)) {
3069 CFIndex n
= CFArrayGetCount(addresses
);
3071 for (i
= 0; i
< n
; i
++) {
3072 ReachabilityInfo ns_info
= NOT_REACHABLE
;
3073 struct sockaddr
*sa
;
3075 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
3077 ok
= checkAddress(storeP
,
3080 targetPrivate
->log_prefix
);
3082 goto error
; /* not today */
3085 if (rankReachability(ns_info
.flags
) > rankReachability(my_info
.flags
)) {
3086 /* return the best case result */
3088 if (rankReachability(my_info
.flags
) == 2) {
3095 if ((error
== EAI_NONAME
)
3096 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
3097 || (error
== EAI_NODATA
)
3101 * the target host name could not be resolved
3103 if (!targetPrivate
->onDemandBypass
) {
3107 * our initial DNS query failed, check again to see if there
3108 * there is an OnDemand configuration that we should be using.
3110 onDemand
= __SCNetworkReachabilityOnDemandCheck(storeP
, target
, TRUE
, &my_info
.flags
);
3112 /* if OnDemand connection is needed */
3117 if (!targetPrivate
->haveDNS
) {
3119 * No DNS servers are defined. Set flags based on
3120 * the availability of configured (but not active)
3123 ok
= checkAddress(storeP
,
3126 targetPrivate
->log_prefix
);
3128 goto error
; /* not today */
3131 if ((my_info
.flags
& kSCNetworkReachabilityFlagsReachable
) &&
3132 (my_info
.flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
3134 * Since we might pick up a set of DNS servers when this connection
3135 * is established, don't reply with a "HOST NOT FOUND" error just yet.
3140 /* Host not found, not reachable! */
3141 my_info
= NOT_REACHABLE
;
3152 *reach_info
= my_info
;
3156 if (addresses
!= NULL
) CFRelease(addresses
);
3162 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
3163 SCNetworkReachabilityFlags
*flags
)
3166 SCDynamicStoreRef store
= NULL
;
3167 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3169 if (!isA_SCNetworkReachability(target
)) {
3170 _SCErrorSet(kSCStatusInvalidArgument
);
3174 pthread_mutex_lock(&targetPrivate
->lock
);
3176 if (targetPrivate
->scheduled
) {
3177 // if being watched, return the last known (and what should be current) status
3178 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
3183 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, &targetPrivate
->info
, FALSE
);
3184 *flags
= targetPrivate
->info
.flags
& ~kSCNetworkReachabilityFlagsFirstResolvePending
;
3185 if (store
!= NULL
) CFRelease(store
);
3189 pthread_mutex_unlock(&targetPrivate
->lock
);
3195 #pragma mark Notifications
3199 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
3202 CFMutableArrayRef keys
;
3203 CFStringRef pattern
;
3204 CFMutableArrayRef patterns
;
3206 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3207 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3209 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
3210 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3211 kSCDynamicStoreDomainSetup
,
3213 CFArrayAppendValue(keys
, key
);
3216 // State:/Network/Global/DNS
3217 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3218 kSCDynamicStoreDomainState
,
3220 CFArrayAppendValue(keys
, key
);
3223 // State:/Network/Global/IPv4 (default route)
3224 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3225 kSCDynamicStoreDomainState
,
3227 CFArrayAppendValue(keys
, key
);
3230 // State:/Network/Global/OnDemand
3231 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3232 kSCDynamicStoreDomainState
,
3234 CFArrayAppendValue(keys
, key
);
3237 // Setup: per-service IPv4 info
3238 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3239 kSCDynamicStoreDomainSetup
,
3242 CFArrayAppendValue(patterns
, pattern
);
3245 // Setup: per-service Interface info
3246 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3247 kSCDynamicStoreDomainSetup
,
3249 kSCEntNetInterface
);
3250 CFArrayAppendValue(patterns
, pattern
);
3253 // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand)
3254 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3255 kSCDynamicStoreDomainSetup
,
3258 CFArrayAppendValue(patterns
, pattern
);
3261 // State: per-service PPP info (for kSCPropNetPPPStatus)
3262 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3263 kSCDynamicStoreDomainState
,
3266 CFArrayAppendValue(patterns
, pattern
);
3269 // Setup: per-service IPSec info
3270 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
3271 kSCDynamicStoreDomainSetup
,
3274 CFArrayAppendValue(patterns
, pattern
);
3277 // State: per-interface IPv4 info
3278 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
3279 kSCDynamicStoreDomainState
,
3282 CFArrayAppendValue(patterns
, pattern
);
3285 // State: per-interface IPv6 info
3286 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
3287 kSCDynamicStoreDomainState
,
3290 CFArrayAppendValue(patterns
, pattern
);
3293 #if !TARGET_OS_IPHONE
3294 // State: Power Management Capabilities
3295 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
3296 kSCDynamicStoreDomainState
,
3297 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
3298 CFArrayAppendValue(keys
, key
);
3300 #endif // TARGET_OS_IPHONE
3303 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
3305 CFRelease(patterns
);
3312 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store
,
3313 CFArrayRef changedKeys
,
3316 Boolean dnsConfigChanged
= FALSE
;
3319 CFIndex nChanges
= CFArrayGetCount(changedKeys
);
3321 #if !TARGET_OS_IPHONE
3322 Boolean powerStatusChanged
= FALSE
;
3323 #endif // !TARGET_OS_IPHONE
3324 const void * targets_q
[N_QUICK
];
3325 const void ** targets
= targets_q
;
3327 if (nChanges
== 0) {
3332 pthread_mutex_lock(&hn_lock
);
3334 nTargets
= (hn_targets
!= NULL
) ? CFSetGetCount(hn_targets
) : 0;
3335 if (nTargets
== 0) {
3336 /* if no addresses being monitored */
3340 #if !TARGET_OS_IPHONE
3341 key
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"),
3342 kSCDynamicStoreDomainState
,
3343 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
));
3344 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
3347 num
= SCDynamicStoreCopyValue(store
, key
);
3349 if (isA_CFNumber(num
) &&
3350 CFNumberGetValue(num
, kCFNumberSInt32Type
, &power_capabilities
)) {
3351 powerStatusChanged
= TRUE
;
3358 #endif // !TARGET_OS_IPHONE
3360 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3361 kSCDynamicStoreDomainState
,
3363 if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) {
3364 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
3372 #if !TARGET_OS_IPHONE
3373 if (powerStatusChanged
) {
3377 #endif // !TARGET_OS_IPHONE
3379 if (dnsConfigChanged
) {
3389 case 0 : str
= ""; break;
3390 case 1 : str
= "network "; break;
3391 case 2 : str
= "DNS "; break;
3392 case 3 : str
= "network and DNS "; break;
3393 #if !TARGET_OS_IPHONE
3394 case 4 : str
= "power "; break;
3395 case 5 : str
= "network and power "; break;
3396 case 6 : str
= "DNS and power "; break;
3397 case 7 : str
= "network, DNS, and power "; break;
3398 #endif // !TARGET_OS_IPHONE
3399 default : str
= "??? ";
3402 SCLog(TRUE
, LOG_INFO
, CFSTR("process %sconfiguration change"), str
);
3405 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
3406 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
3407 CFSetGetValues(hn_targets
, targets
);
3408 for (i
= 0; i
< nTargets
; i
++) {
3409 SCNetworkReachabilityRef target
= targets
[i
];
3410 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3412 pthread_mutex_lock(&targetPrivate
->lock
);
3414 if (targetPrivate
->type
== reachabilityTypeName
) {
3415 Boolean dnsChanged
= dnsConfigChanged
;
3419 * if the DNS configuration didn't change we still need to
3420 * check that the DNS servers are accessible.
3422 SCNetworkReachabilityFlags ns_flags
;
3425 /* check the reachability of the DNS servers */
3426 ok
= _SC_R_checkResolverReachability(&store
,
3428 &targetPrivate
->haveDNS
,
3429 targetPrivate
->name
,
3430 targetPrivate
->serv
,
3431 targetPrivate
->log_prefix
);
3433 /* if we could not get DNS server info */
3434 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"),
3435 targetPrivate
->log_prefix
);
3437 } else if (rankReachability(ns_flags
) < 2) {
3439 * if DNS servers are not (or are no longer) reachable, set
3440 * flags based on the availability of configured (but not
3443 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
3444 targetPrivate
->log_prefix
);
3450 if (targetPrivate
->dnsPort
!= NULL
) {
3451 mach_port_t mp
= CFMachPortGetPort(targetPrivate
->dnsPort
);
3453 /* cancel the outstanding DNS query */
3454 SCLog(_sc_debug
, LOG_INFO
,
3455 CFSTR("%scancel DNS query for %s%s%s%s%s"),
3456 targetPrivate
->log_prefix
,
3457 targetPrivate
->name
!= NULL
? "name = " : "",
3458 targetPrivate
->name
!= NULL
? targetPrivate
->name
: "",
3459 targetPrivate
->name
!= NULL
&& targetPrivate
->serv
!= NULL
? ", " : "",
3460 targetPrivate
->serv
!= NULL
? "serv = " : "",
3461 targetPrivate
->serv
!= NULL
? targetPrivate
->serv
: "");
3462 dequeueAsyncDNSQuery(target
);
3463 getaddrinfo_async_cancel(mp
);
3466 /* schedule request to resolve the name again */
3467 targetPrivate
->needResolve
= TRUE
;
3471 __SCNetworkReachabilityPerform(target
);
3473 pthread_mutex_unlock(&targetPrivate
->lock
);
3475 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
3479 pthread_mutex_unlock(&hn_lock
);
3484 #if !TARGET_OS_IPHONE
3485 static __inline__ Boolean
3486 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities
)
3489 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU \
3490 | kIOPMSystemPowerStateCapabilityNetwork \
3491 | kIOPMSystemPowerStateCapabilityDisk)
3493 if ((power_capabilities
& POWER_CAPABILITIES_NEED
) != POWER_CAPABILITIES_NEED
) {
3495 * we're not awake (from a networking point of view) unless we
3496 * have the CPU, disk, *and* network.
3501 if ((power_capabilities
& kIOPMSytemPowerStateCapabilitiesMask
) == POWER_CAPABILITIES_NEED
) {
3503 * if all we have is the CPU, disk, and network than this must
3504 * be a "maintenance" wake.
3511 #endif // !TARGET_OS_IPHONE
3515 rlsPerform(void *info
)
3518 void (*context_release
)(const void *);
3519 Boolean defer
= FALSE
;
3521 ReachabilityInfo reach_info
= NOT_REACHABLE
;
3522 SCNetworkReachabilityCallBack rlsFunction
;
3523 SCDynamicStoreRef store
= NULL
;
3524 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
3525 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3527 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%schecking target reachability"),
3528 targetPrivate
->log_prefix
);
3531 pthread_mutex_lock(&targetPrivate
->lock
);
3533 if (!targetPrivate
->scheduled
) {
3534 // if not currently scheduled
3535 pthread_mutex_unlock(&targetPrivate
->lock
);
3539 /* update reachability, notify if status changed */
3540 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, &reach_info
, TRUE
);
3541 if (store
!= NULL
) CFRelease(store
);
3543 /* if reachability status not available */
3544 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%flags not available"),
3545 targetPrivate
->log_prefix
);
3546 reach_info
= NOT_REACHABLE
;
3549 #if !TARGET_OS_IPHONE
3551 * We want to defer the notification if this is a maintenance wake *and*
3552 * the reachability flags that we would be reporting to the application
3553 * are better than those that we last reported.
3555 if (!systemIsAwake(power_capabilities
)) {
3556 /* if this is a maintenace wake */
3557 reach_info
.sleeping
= TRUE
;
3559 if (rankReachability(reach_info
.flags
) >= rankReachability(targetPrivate
->info
.flags
)) {
3561 * don't report the change if the new reachability flags are
3562 * the same or "better"
3565 } else if (bcmp(&targetPrivate
->last_notify
, &reach_info
, sizeof(reach_info
)) == 0) {
3566 /* if we have already posted this change */
3570 #endif // !TARGET_OS_IPHONE
3572 if (bcmp(&targetPrivate
->info
, &reach_info
, sizeof(reach_info
)) == 0) {
3573 SCLog(_sc_debug
, LOG_INFO
,
3574 CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"),
3575 targetPrivate
->log_prefix
,
3577 reach_info
.if_index
,
3578 reach_info
.sleeping
? "*" : "");
3579 pthread_mutex_unlock(&targetPrivate
->lock
);
3583 SCLog(_sc_debug
, LOG_INFO
,
3584 CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s"),
3585 targetPrivate
->log_prefix
,
3586 targetPrivate
->info
.flags
,
3587 targetPrivate
->info
.if_index
,
3588 targetPrivate
->info
.sleeping
? "*" : "",
3590 reach_info
.if_index
,
3591 reach_info
.sleeping
? "*" : "",
3592 defer
? ", deferred" : "");
3594 /* update flags / interface */
3595 targetPrivate
->info
= reach_info
;
3597 /* as needed, defer the notification */
3599 pthread_mutex_unlock(&targetPrivate
->lock
);
3603 /* save last notification info */
3604 targetPrivate
->last_notify
= reach_info
;
3607 rlsFunction
= targetPrivate
->rlsFunction
;
3608 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
3609 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
3610 context_release
= targetPrivate
->rlsContext
.release
;
3612 context_info
= targetPrivate
->rlsContext
.info
;
3613 context_release
= NULL
;
3616 pthread_mutex_unlock(&targetPrivate
->lock
);
3618 if (rlsFunction
!= NULL
) {
3619 (*rlsFunction
)(target
, reach_info
.flags
, context_info
);
3622 if (context_release
!= NULL
) {
3623 (*context_release
)(context_info
);
3631 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
3632 SCNetworkReachabilityCallBack callout
,
3633 SCNetworkReachabilityContext
*context
)
3635 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3637 pthread_mutex_lock(&targetPrivate
->lock
);
3639 if (targetPrivate
->rlsContext
.release
!= NULL
) {
3640 /* let go of the current context */
3641 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
3644 targetPrivate
->rlsFunction
= callout
;
3645 targetPrivate
->rlsContext
.info
= NULL
;
3646 targetPrivate
->rlsContext
.retain
= NULL
;
3647 targetPrivate
->rlsContext
.release
= NULL
;
3648 targetPrivate
->rlsContext
.copyDescription
= NULL
;
3650 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
3651 if (context
->retain
!= NULL
) {
3652 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
3656 pthread_mutex_unlock(&targetPrivate
->lock
);
3663 reachRLSCopyDescription(const void *info
)
3665 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
3667 return CFStringCreateWithFormat(NULL
,
3669 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
3675 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
3676 CFRunLoopRef runLoop
,
3677 CFStringRef runLoopMode
,
3678 #if !TARGET_OS_IPHONE
3679 dispatch_queue_t queue
,
3680 #else // !TARGET_OS_IPHONE
3682 #endif // !TARGET_OS_IPHONE
3685 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3686 Boolean init
= FALSE
;
3690 pthread_mutex_lock(&hn_lock
);
3692 pthread_mutex_lock(&targetPrivate
->lock
);
3694 #if !TARGET_OS_IPHONE
3695 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
3696 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
3697 _SCErrorSet(kSCStatusInvalidArgument
);
3700 #endif // !TARGET_OS_IPHONE
3702 /* schedule the SCNetworkReachability run loop source */
3704 if (!onDemand
&& (hn_store
== NULL
)) {
3706 * if we are not monitoring any hosts, start watching
3708 if (!dns_configuration_watch()) {
3710 _SCErrorSet(kSCStatusFailed
);
3714 hn_store
= SCDynamicStoreCreate(NULL
,
3715 CFSTR("SCNetworkReachability"),
3716 __SCNetworkReachabilityHandleChanges
,
3718 if (hn_store
== NULL
) {
3719 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed"));
3723 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
3725 #if !TARGET_OS_IPHONE
3726 hn_dispatchQueue
= dispatch_queue_create("com.apple.SCNetworkReachabilty.network_changes", NULL
);
3727 if (hn_dispatchQueue
== NULL
) {
3728 SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed"));
3729 _SCErrorSet(kSCStatusFailed
);
3730 CFRelease(hn_store
);
3734 CFRetain(hn_store
); // Note: will be released when the dispatch queue is released
3735 dispatch_set_context(hn_dispatchQueue
, (void *)hn_store
);
3736 dispatch_set_finalizer_f(hn_dispatchQueue
, (dispatch_function_t
)CFRelease
);
3738 ok
= SCDynamicStoreSetDispatchQueue(hn_store
, hn_dispatchQueue
);
3740 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
3741 dispatch_release(hn_dispatchQueue
);
3742 hn_dispatchQueue
= NULL
;
3743 CFRelease(hn_store
);
3747 #else // !TARGET_OS_IPHONE
3748 hn_storeRLS
= SCDynamicStoreCreateRunLoopSource(NULL
, hn_store
, 0);
3749 hn_rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3750 #endif // !TARGET_OS_IPHONE
3751 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
3754 if (!targetPrivate
->scheduled
) {
3755 CFRunLoopSourceContext context
= { 0 // version
3756 , (void *)target
// info
3757 , CFRetain
// retain
3758 , CFRelease
// release
3759 , reachRLSCopyDescription
// copyDescription
3764 , rlsPerform
// perform
3767 if (runLoop
!= NULL
) {
3768 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
3769 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3772 targetPrivate
->scheduled
= TRUE
;
3773 if (targetPrivate
->type
== reachabilityTypeName
) {
3774 targetPrivate
->needResolve
= TRUE
;
3779 #if !TARGET_OS_IPHONE
3780 if (queue
!= NULL
) {
3781 targetPrivate
->dispatchQueue
= queue
;
3782 dispatch_retain(targetPrivate
->dispatchQueue
);
3784 #endif // !TARGET_OS_IPHONE
3786 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
3788 * if we do not already have host notifications scheduled with
3789 * this runLoop / runLoopMode
3791 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
3793 if (targetPrivate
->dnsRLS
!= NULL
) {
3794 /* if we have an active async DNS query too */
3795 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
3799 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
3801 #if TARGET_OS_IPHONE
3803 /* schedule the global SCDynamicStore run loop source */
3805 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
3807 * if we do not already have SC notifications scheduled with
3808 * this runLoop / runLoopMode
3810 CFRunLoopAddSource(runLoop
, hn_storeRLS
, runLoopMode
);
3813 _SC_schedule(target
, runLoop
, runLoopMode
, hn_rlList
);
3815 #endif // TARGET_OS_IPHONE
3818 CFSetAddValue(hn_targets
, target
);
3821 ReachabilityInfo reach_info
= NOT_REACHABLE
;
3822 SCDynamicStoreRef store
= NULL
;
3825 * if we have yet to schedule SC notifications for this address
3826 * - initialize current reachability status
3828 if (__SCNetworkReachabilityGetFlags(&store
, target
, &reach_info
, TRUE
)) {
3830 * if reachability status available
3832 * - schedule notification to report status via callback
3834 targetPrivate
->info
= reach_info
;
3835 __SCNetworkReachabilityPerform(target
);
3837 /* if reachability status not available, async lookup started */
3838 targetPrivate
->info
= NOT_REACHABLE
;
3840 if (store
!= NULL
) CFRelease(store
);
3843 if (targetPrivate
->onDemandServer
!= NULL
) {
3844 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, queue
, TRUE
);
3851 pthread_mutex_unlock(&targetPrivate
->lock
);
3853 pthread_mutex_unlock(&hn_lock
);
3860 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
3861 CFRunLoopRef runLoop
,
3862 CFStringRef runLoopMode
,
3865 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3870 pthread_mutex_lock(&hn_lock
);
3872 pthread_mutex_lock(&targetPrivate
->lock
);
3874 #if !TARGET_OS_IPHONE
3875 if (((runLoop
== NULL
) && (targetPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
3876 ((runLoop
!= NULL
) && (targetPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are not)
3877 _SCErrorSet(kSCStatusInvalidArgument
);
3880 #endif // !TARGET_OS_IPHONE
3882 if (!targetPrivate
->scheduled
) {
3883 // if not currently scheduled
3884 _SCErrorSet(kSCStatusInvalidArgument
);
3888 // first, unschedule the target specific sources
3889 #if !TARGET_OS_IPHONE
3890 if (targetPrivate
->dispatchQueue
!= NULL
) {
3891 if (targetPrivate
->onDemandServer
!= NULL
) {
3892 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
3895 #endif // !TARGET_OS_IPHONE
3897 if (!_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
3898 // if not currently scheduled
3899 _SCErrorSet(kSCStatusInvalidArgument
);
3903 if (targetPrivate
->onDemandServer
!= NULL
) {
3904 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, TRUE
);
3907 n
= CFArrayGetCount(targetPrivate
->rlList
);
3908 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
3909 // if target is no longer scheduled for this runLoop / runLoopMode
3910 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
3912 if (targetPrivate
->dnsRLS
!= NULL
) {
3913 // if we have an active async DNS query too
3914 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
3918 // if *all* notifications have been unscheduled
3919 CFRelease(targetPrivate
->rlList
);
3920 targetPrivate
->rlList
= NULL
;
3921 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
3922 CFRelease(targetPrivate
->rls
);
3923 targetPrivate
->rls
= NULL
;
3929 // if *all* notifications have been unscheduled
3930 targetPrivate
->scheduled
= FALSE
;
3933 CFSetRemoveValue(hn_targets
, target
); // cleanup notification resources
3936 if (targetPrivate
->dnsPort
!= NULL
) {
3937 mach_port_t mp
= CFMachPortGetPort(targetPrivate
->dnsPort
);
3939 // if we have an active async DNS query
3940 dequeueAsyncDNSQuery(target
);
3941 getaddrinfo_async_cancel(mp
);
3945 #if !TARGET_OS_IPHONE
3946 if (runLoop
== NULL
) {
3947 dispatch_release(targetPrivate
->dispatchQueue
);
3948 targetPrivate
->dispatchQueue
= NULL
;
3950 #endif // !TARGET_OS_IPHONE
3952 // now, unschedule the global dynamic store source
3953 #if TARGET_OS_IPHONE
3955 (void)_SC_unschedule(target
, runLoop
, runLoopMode
, hn_rlList
, FALSE
);
3957 n
= CFArrayGetCount(hn_rlList
);
3958 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
3960 * if we no longer have any addresses scheduled for
3961 * this runLoop / runLoopMode
3963 CFRunLoopRemoveSource(runLoop
, hn_storeRLS
, runLoopMode
);
3966 if (CFEqual(runLoopMode
, kCFRunLoopCommonModes
)) {
3969 modes
= CFRunLoopCopyAllModes(runLoop
);
3970 if (modes
!= NULL
) {
3972 CFIndex n
= CFArrayGetCount(modes
);
3974 for (i
= 0; i
< n
; i
++) {
3977 mode
= CFArrayGetValueAtIndex(modes
, i
);
3978 if (_SC_isScheduled(NULL
, runLoop
, mode
, hn_rlList
)) {
3980 * removing kCFRunLoopCommonModes cleaned up more
3981 * than we wanted. Add back the modes that were
3982 * expect to be present.
3984 CFRunLoopAddSource(runLoop
, hn_storeRLS
, mode
);
3990 } else if (_SC_isScheduled(NULL
, runLoop
, kCFRunLoopCommonModes
, hn_rlList
)) {
3992 * if we are still scheduling kCFRunLoopCommonModes, make sure that
3993 * none of the common modes were inadvertently removed.
3995 CFRunLoopAddSource(runLoop
, hn_storeRLS
, kCFRunLoopCommonModes
);
4000 #endif // TARGET_OS_IPHONE
4002 n
= CFSetGetCount(hn_targets
);
4004 // if we are no longer monitoring any targets
4005 #if !TARGET_OS_IPHONE
4006 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
4007 dispatch_release(hn_dispatchQueue
);
4008 hn_dispatchQueue
= NULL
;
4009 #else // !TARGET_OS_IPHONE
4010 CFRunLoopSourceInvalidate(hn_storeRLS
);
4011 CFRelease(hn_storeRLS
);
4013 CFRelease(hn_rlList
);
4015 #endif // !TARGET_OS_IPHONE
4016 CFRelease(hn_store
);
4018 CFRelease(hn_targets
);
4022 * until we start monitoring again, ensure that
4023 * any resources associated with tracking the
4024 * DNS configuration have been released.
4026 dns_configuration_unwatch();
4033 pthread_mutex_unlock(&targetPrivate
->lock
);
4035 pthread_mutex_unlock(&hn_lock
);
4041 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
4042 CFRunLoopRef runLoop
,
4043 CFStringRef runLoopMode
)
4045 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
4046 _SCErrorSet(kSCStatusInvalidArgument
);
4050 return __SCNetworkReachabilityScheduleWithRunLoop(target
, runLoop
, runLoopMode
, NULL
, FALSE
);
4054 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
4055 CFRunLoopRef runLoop
,
4056 CFStringRef runLoopMode
)
4058 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
4059 _SCErrorSet(kSCStatusInvalidArgument
);
4063 return __SCNetworkReachabilityUnscheduleFromRunLoop(target
, runLoop
, runLoopMode
, FALSE
);
4066 #if !TARGET_OS_IPHONE
4068 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
4069 dispatch_queue_t queue
)
4073 if (!isA_SCNetworkReachability(target
)) {
4074 _SCErrorSet(kSCStatusInvalidArgument
);
4078 if (queue
!= NULL
) {
4079 ok
= __SCNetworkReachabilityScheduleWithRunLoop(target
, NULL
, NULL
, queue
, FALSE
);
4081 ok
= __SCNetworkReachabilityUnscheduleFromRunLoop(target
, NULL
, NULL
, FALSE
);
4086 #endif // !TARGET_OS_IPHONE