2 * Copyright (c) 2003-2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * April 12, 2011 Allan Nathanson <ajn@apple.com>
28 * - add SCNetworkReachability "server"
30 * March 31, 2004 Allan Nathanson <ajn@apple.com>
31 * - use [SC] DNS configuration information
33 * January 19, 2003 Allan Nathanson <ajn@apple.com>
34 * - add advanced reachability APIs
37 #include <Availability.h>
38 #include <TargetConditionals.h>
39 #include <sys/cdefs.h>
40 #include <dispatch/dispatch.h>
41 #include <dispatch/private.h>
42 #include <CoreFoundation/CoreFoundation.h>
43 #include <CoreFoundation/CFRuntime.h>
44 #include <SystemConfiguration/SystemConfiguration.h>
45 #include <SystemConfiguration/SCValidation.h>
46 #include <SystemConfiguration/SCPrivate.h>
47 #include <SystemConfiguration/VPNAppLayerPrivate.h>
49 #include <libkern/OSAtomic.h>
52 #include <IOKit/pwr_mgt/IOPMLibPrivate.h>
53 #endif // !TARGET_OS_IPHONE
57 #include <netinet/in.h>
58 #include <arpa/inet.h>
62 #include <sys/ioctl.h>
63 #include <sys/socket.h>
65 #include <net/if_dl.h>
66 #include <net/if_types.h>
67 #define KERNEL_PRIVATE
68 #include <net/route.h>
72 #define s6_addr16 __u6_addr.__u6_addr16
75 #include "SCNetworkConnectionInternal.h"
76 #include "SCNetworkReachabilityInternal.h"
78 #include <ppp/ppp_msg.h>
79 #include <ppp/PPPControllerPriv.h>
81 #include <network_information.h>
89 #define DEBUG_REACHABILITY_TYPE_NAME "create w/name"
90 #define DEBUG_REACHABILITY_TYPE_NAME_CLONE " > clone"
91 #define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
93 #define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"
94 #define DEBUG_REACHABILITY_TYPE_ADDRESS_CLONE " > clone"
95 #define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
97 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"
98 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_CLONE " > clone"
99 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
101 #define DEBUG_REACHABILITY_TYPE_PTR "create w/ptr"
102 #define DEBUG_REACHABILITY_TYPE_PTR_CLONE " > clone"
103 #define DEBUG_REACHABILITY_TYPE_PTR_OPTIONS " + options"
105 #define DNS_FLAGS_FORMAT "[%s%s%s%s%s]"
106 #define DNS_FLAGS_VALUES(t) t->dnsHaveV4 ? "4" : "", \
107 t->dnsHaveV6 ? "6" : "", \
108 t->dnsHavePTR ? "P" : "", \
109 t->dnsHaveTimeout ? "T" : "", \
110 t->dnsHaveError ? "E" : ""
113 static pthread_mutexattr_t lock_attr
;
115 #define MUTEX_INIT(m) { \
116 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \
120 #define MUTEX_LOCK(m) { \
121 int _lock_ = (pthread_mutex_lock(m) == 0); \
125 #define MUTEX_UNLOCK(m) { \
126 int _unlock_ = (pthread_mutex_unlock(m) == 0); \
130 #define MUTEX_ASSERT_HELD(m) { \
131 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \
136 #define SCNETWORKREACHABILITY_TRIGGER_KEY CFSTR("com.apple.SCNetworkReachability:FORCE-CHANGE")
142 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
143 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
144 static void reachPerform (void *info
);
145 static Boolean
reachUpdate (SCNetworkReachabilityRef target
);
149 __SCNetworkReachabilityHandleChanges (SCDynamicStoreRef store
,
150 CFArrayRef changedKeys
,
154 __SCNetworkReachabilityScheduleWithRunLoop (SCNetworkReachabilityRef target
,
155 CFRunLoopRef runLoop
,
156 CFStringRef runLoopMode
,
157 dispatch_queue_t queue
,
161 __SCNetworkReachabilityUnscheduleFromRunLoop (SCNetworkReachabilityRef target
,
162 CFRunLoopRef runLoop
,
163 CFStringRef runLoopMode
,
167 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
170 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
172 "SCNetworkReachability", // className
175 __SCNetworkReachabilityDeallocate
, // dealloc
178 NULL
, // copyFormattingDesc
179 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
183 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
184 static const ReachabilityInfo NOT_REACHABLE
= { 0, 0, 0, { 0 }, FALSE
};
185 static const ReachabilityInfo NOT_REPORTED
= { 0, 0xFFFFFFFF, 0, { 0 }, FALSE
};
186 static int rtm_seq
= 0;
189 static const struct timeval TIME_ZERO
= { 0, 0 };
192 static int dnsCount
= 0;
193 static int dnsGeneration
= 0;
194 static DNSServiceRef dnsMain
= NULL
;
195 static CFMutableSetRef dnsUpdated
= NULL
;
197 static Boolean D_serverBypass
= FALSE
;
201 #if !TARGET_OS_IPHONE
203 * Power capabilities (sleep/wake)
205 #define POWER_CAPABILITIES_NETWORK ( kIOPMCapabilityCPU \
206 | kIOPMCapabilityNetwork \
207 | kIOPMCapabilityVideo)
208 static IOPMSystemPowerStateCapabilities power_capabilities
= POWER_CAPABILITIES_NETWORK
;
209 #endif // !TARGET_OS_IPHONE
213 * host "something has changed" notifications
216 // Note: protected by _hn_target_queue()
217 static SCDynamicStoreRef hn_store
= NULL
;
218 static CFMutableSetRef hn_targets
= NULL
;
221 static dispatch_queue_t
224 static dispatch_once_t once
;
225 static dispatch_queue_t q
= NULL
;
227 dispatch_once(&once
, ^{
228 q
= dispatch_queue_create("SCNetworkReachability.handleChanges", NULL
);
235 static dispatch_queue_t
238 static dispatch_once_t once
;
239 static dispatch_queue_t q
;
241 dispatch_once(&once
, ^{
242 q
= dispatch_queue_create("SCNetworkReachability.targetManagement", NULL
);
254 dns_config_t
*config
;
256 } dns_configuration_t
;
259 // Note: protected by "dns_lock"
260 static pthread_mutex_t dns_lock
= PTHREAD_MUTEX_INITIALIZER
;
261 static dns_configuration_t
*dns_configuration
= NULL
;
262 static int dns_token
;
263 static Boolean dns_token_valid
= FALSE
;
271 dns_query_mdns_timeout
,
276 __mark_operation_start(struct timeval
*queryStart
,
277 struct timeval
*queryEnd
)
279 (void) gettimeofday(queryStart
, NULL
);
280 *queryEnd
= TIME_ZERO
;
287 __mark_operation_end(SCNetworkReachabilityRef target
,
289 query_type query_type
,
290 struct timeval
*queryStart
,
291 struct timeval
*queryEnd
)
293 struct timeval queryElapsed
;
294 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
296 (void) gettimeofday(queryEnd
, NULL
);
299 (query_type
!= dns_query_mdns_timeout
)) {
303 if (!timerisset(queryStart
)) {
307 timersub(queryEnd
, queryStart
, &queryElapsed
);
308 switch (query_type
) {
310 #define QUERY_TIME__FMT "%ld.%6.6d"
311 #define QUERY_TIME__DIV 1
313 case dns_query_async
:
314 SCLog(TRUE
, LOG_INFO
,
315 CFSTR("%sasync DNS complete%s (query time = " QUERY_TIME__FMT
")"),
316 targetPrivate
->log_prefix
,
317 found
? "" : ", host not found",
319 queryElapsed
.tv_usec
/ QUERY_TIME__DIV
);
321 case dns_query_mdns
:
322 SCLog(TRUE
, LOG_INFO
,
323 CFSTR("%s[m]DNS query complete%s (query time = " QUERY_TIME__FMT
"), " DNS_FLAGS_FORMAT
),
324 targetPrivate
->log_prefix
,
325 found
? "" : ", host not found",
327 queryElapsed
.tv_usec
/ QUERY_TIME__DIV
,
328 DNS_FLAGS_VALUES(targetPrivate
));
330 case dns_query_mdns_timeout
:
332 CFSTR("%s[m]DNS query timeout (query time = " QUERY_TIME__FMT
"), " DNS_FLAGS_FORMAT
),
333 targetPrivate
->log_prefix
,
335 queryElapsed
.tv_usec
/ QUERY_TIME__DIV
,
336 DNS_FLAGS_VALUES(targetPrivate
));
344 static __inline__ Boolean
345 __reach_changed(ReachabilityInfo
*r1
, ReachabilityInfo
*r2
)
347 if (r1
->flags
!= r2
->flags
) {
348 // if the reachability flags changed
352 if (r1
->if_index
!= r2
->if_index
) {
353 // if the target interface changed
357 if ((r1
->sleeping
!= r2
->sleeping
) && !r2
->sleeping
) {
358 // if our sleep/wake status changed and if we
359 // are no longer sleeping
367 static __inline__
void
368 _reach_set(ReachabilityInfo
*dst
,
369 const ReachabilityInfo
*src
,
371 unsigned int requested_if_index
,
372 const char *requested_if_name
)
374 memcpy(dst
, src
, sizeof(ReachabilityInfo
));
377 if (!(dst
->flags
& kSCNetworkReachabilityFlagsReachable
) ||
378 (dst
->flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
379 // if not reachable or connection required, return the
380 // requested if_index and if_name.
381 dst
->if_index
= requested_if_index
;
382 if (requested_if_name
!= NULL
) {
383 strlcpy(dst
->if_name
, requested_if_name
, sizeof(dst
->if_name
));
385 dst
->if_name
[0] = '\0';
394 #pragma mark SCDynamicStore info
398 SCDynamicStoreRef store
;
400 CFDictionaryRef dict
;
403 const void * keys_q
[N_QUICK
];
404 const void ** values
;
405 const void * values_q
[N_QUICK
];
406 } ReachabilityStoreInfo
, *ReachabilityStoreInfoRef
;
409 static ReachabilityStoreInfo S_storeInfo
= { 0 };
410 static Boolean S_storeInfoActive
= FALSE
;
413 static dispatch_queue_t
416 static dispatch_once_t once
;
417 static dispatch_queue_t q
;
419 dispatch_once(&once
, ^{
420 q
= dispatch_queue_create("SCNetworkReachability.storeInfo", NULL
);
428 ReachabilityStoreInfo_copy(ReachabilityStoreInfoRef src
,
429 ReachabilityStoreInfoRef dst
)
431 if (src
->dict
!= NULL
) {
432 dst
->store
= src
->store
;
433 CFRetain(dst
->store
);
435 dst
->dict
= src
->dict
;
440 if (dst
->n
<= (CFIndex
)(sizeof(dst
->keys_q
) / sizeof(CFTypeRef
))) {
441 dst
->keys
= dst
->keys_q
;
442 dst
->values
= dst
->values_q
;
444 dst
->keys
= CFAllocatorAllocate(NULL
, dst
->n
* sizeof(CFTypeRef
), 0);
445 dst
->values
= CFAllocatorAllocate(NULL
, dst
->n
* sizeof(CFTypeRef
), 0);
447 memcpy(dst
->keys
, src
->keys
, dst
->n
* sizeof(CFTypeRef
));
448 memcpy(dst
->values
, src
->values
, dst
->n
* sizeof(CFTypeRef
));
457 ReachabilityStoreInfo_enable(Boolean enable
)
459 dispatch_sync(_storeInfo_queue(), ^{
460 S_storeInfoActive
= enable
;
468 ReachabilityStoreInfo_free(ReachabilityStoreInfoRef store_info
)
470 if ((store_info
->n
> 0) && (store_info
->keys
!= store_info
->keys_q
)) {
471 CFAllocatorDeallocate(NULL
, store_info
->keys
);
472 store_info
->keys
= NULL
;
474 CFAllocatorDeallocate(NULL
, store_info
->values
);
475 store_info
->values
= NULL
;
479 if (store_info
->dict
!= NULL
) {
480 CFRelease(store_info
->dict
);
481 store_info
->dict
= NULL
;
484 if (store_info
->store
!= NULL
) {
485 CFRelease(store_info
->store
);
486 store_info
->store
= NULL
;
494 ReachabilityStoreInfo_init(ReachabilityStoreInfoRef store_info
)
496 dispatch_sync(_storeInfo_queue(), ^{
497 bzero(store_info
, sizeof(ReachabilityStoreInfo
));
499 if (S_storeInfoActive
&& (S_storeInfo
.dict
!= NULL
)) {
500 ReachabilityStoreInfo_copy(&S_storeInfo
, store_info
);
509 ReachabilityStoreInfo_save(ReachabilityStoreInfoRef store_info
)
511 dispatch_sync(_storeInfo_queue(), ^{
512 if ((store_info
== NULL
) ||
513 !_SC_CFEqual(store_info
->dict
, S_storeInfo
.dict
)) {
515 ReachabilityStoreInfo_free(&S_storeInfo
);
518 if (S_storeInfoActive
&&
519 (store_info
!= NULL
) &&
520 (store_info
->dict
!= NULL
)) {
521 ReachabilityStoreInfo_copy(store_info
, &S_storeInfo
);
531 ReachabilityStoreInfo_keys(CFMutableArrayRef
*fill_keys
, CFMutableArrayRef
*fill_patterns
)
534 CFMutableArrayRef keys
;
536 CFMutableArrayRef patterns
;
538 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
539 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
541 // get info for IPv4 services
542 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
543 kSCDynamicStoreDomainState
,
545 CFArrayAppendValue(keys
, key
);
547 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
548 kSCDynamicStoreDomainSetup
,
551 CFArrayAppendValue(patterns
, pattern
);
553 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
554 kSCDynamicStoreDomainState
,
557 CFArrayAppendValue(patterns
, pattern
);
560 // get info for IPv6 services
561 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
562 kSCDynamicStoreDomainState
,
564 CFArrayAppendValue(keys
, key
);
566 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
567 kSCDynamicStoreDomainSetup
,
570 CFArrayAppendValue(patterns
, pattern
);
572 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
573 kSCDynamicStoreDomainState
,
576 CFArrayAppendValue(patterns
, pattern
);
579 // get info for PPP services
580 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
581 kSCDynamicStoreDomainSetup
,
584 CFArrayAppendValue(patterns
, pattern
);
586 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
587 kSCDynamicStoreDomainState
,
590 CFArrayAppendValue(patterns
, pattern
);
593 // get info for VPN services
594 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
595 kSCDynamicStoreDomainSetup
,
598 CFArrayAppendValue(patterns
, pattern
);
600 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
601 kSCDynamicStoreDomainState
,
604 CFArrayAppendValue(patterns
, pattern
);
607 // get info for IPSec services
608 // pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
609 // kSCDynamicStoreDomainSetup,
612 // CFArrayAppendValue(patterns, pattern);
613 // CFRelease(pattern);
614 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
615 kSCDynamicStoreDomainState
,
618 CFArrayAppendValue(patterns
, pattern
);
621 // get info to identify "available" services
622 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
623 kSCDynamicStoreDomainSetup
,
626 CFArrayAppendValue(patterns
, pattern
);
631 *fill_patterns
= patterns
;
637 ReachabilityStoreInfo_fill(ReachabilityStoreInfoRef store_info
)
639 CFMutableArrayRef keys
;
640 CFMutableArrayRef patterns
;
642 // get the SCDynamicStore info
643 ReachabilityStoreInfo_keys(&keys
, &patterns
);
644 store_info
->dict
= SCDynamicStoreCopyMultiple(store_info
->store
, keys
, patterns
);
647 if (store_info
->dict
== NULL
) {
651 // and extract the keys/values for post-processing
652 store_info
->n
= CFDictionaryGetCount(store_info
->dict
);
653 if (store_info
->n
> 0) {
654 if (store_info
->n
<= (CFIndex
)(sizeof(store_info
->keys_q
) / sizeof(CFTypeRef
))) {
655 store_info
->keys
= store_info
->keys_q
;
656 store_info
->values
= store_info
->values_q
;
658 store_info
->keys
= CFAllocatorAllocate(NULL
, store_info
->n
* sizeof(CFTypeRef
), 0);
659 store_info
->values
= CFAllocatorAllocate(NULL
, store_info
->n
* sizeof(CFTypeRef
), 0);
661 CFDictionaryGetKeysAndValues(store_info
->dict
,
671 ReachabilityStoreInfo_update(ReachabilityStoreInfoRef store_info
,
672 SCDynamicStoreRef
*storeP
,
673 sa_family_t sa_family
)
675 __block Boolean ok
= TRUE
;
679 store_info
->entity
= NULL
;
682 store_info
->entity
= kSCEntNetIPv4
;
685 store_info
->entity
= kSCEntNetIPv6
;
691 if (store_info
->dict
!= NULL
) {
692 // if info already available
696 dispatch_sync(_storeInfo_queue(), ^{
697 if (S_storeInfoActive
&& (S_storeInfo
.dict
!= NULL
)) {
699 ReachabilityStoreInfo_free(store_info
);
701 // copy the shared/available info
702 ReachabilityStoreInfo_copy(&S_storeInfo
, store_info
);
705 if (store_info
->store
== NULL
) {
706 store_info
->store
= (storeP
!= NULL
) ? *storeP
: NULL
;
707 if (store_info
->store
!= NULL
) {
708 // keep a reference to the passed in SCDynamicStore
709 CFRetain(store_info
->store
);
711 store_info
->store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
712 if (store_info
->store
== NULL
) {
713 SCLog(TRUE
, LOG_ERR
, CFSTR("ReachabilityStoreInfo_update SCDynamicStoreCreate() failed"));
717 if (storeP
!= NULL
) {
718 // and pass back a reference
719 *storeP
= store_info
->store
;
725 if (sa_family
== AF_UNSPEC
) {
726 // if the address family was not specified than
727 // all we wanted, for now, was to establish the
728 // SCDynamicStore session
732 if (store_info
->dict
!= NULL
) {
733 // or we have picked up the shared info
737 ok
= ReachabilityStoreInfo_fill(store_info
);
742 if (!_SC_CFEqual(store_info
->dict
, S_storeInfo
.dict
)) {
744 ReachabilityStoreInfo_free(&S_storeInfo
);
747 if (S_storeInfoActive
&&
748 (store_info
->dict
!= NULL
)) {
749 ReachabilityStoreInfo_copy(store_info
, &S_storeInfo
);
759 #pragma mark Reachability engine
762 #define ROUNDUP(a, size) \
763 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
765 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
766 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
771 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
775 for (i
= 0; i
< RTAX_MAX
; i
++) {
776 if (addrs
& (1 << i
)) {
785 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
791 struct rt_msghdr rtm
;
794 struct sockaddr
*rti_info
[RTAX_MAX
];
795 struct rt_msghdr
*rtm
;
796 struct sockaddr_dl
*sdl
;
797 } route_info
, *route_info_p
;
802 * returns zero if route exists and data returned, EHOSTUNREACH
803 * if no route, or errno for any other error.
806 route_get(const struct sockaddr
*address
,
807 unsigned int if_index
,
812 pid_t pid
= getpid();
815 int32_t seq
= OSAtomicIncrement32Barrier(&rtm_seq
);
816 #ifndef RTM_GET_SILENT
817 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
818 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
819 int sosize
= 48 * 1024;
822 bzero(info
, sizeof(*info
));
824 info
->rtm
= &info
->buf
.rtm
;
825 info
->rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
826 info
->rtm
->rtm_version
= RTM_VERSION
;
827 #ifdef RTM_GET_SILENT
828 info
->rtm
->rtm_type
= RTM_GET_SILENT
;
830 info
->rtm
->rtm_type
= RTM_GET
;
832 info
->rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
833 info
->rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
834 info
->rtm
->rtm_pid
= pid
;
835 info
->rtm
->rtm_seq
= seq
;
838 info
->rtm
->rtm_flags
|= RTF_IFSCOPE
;
839 info
->rtm
->rtm_index
= if_index
;
842 switch (address
->sa_family
) {
844 struct sockaddr_in6
*sin6
;
846 /* ALIGN: caller ensures that the address is aligned */
847 sin6
= (struct sockaddr_in6
*)(void *)address
;
848 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
849 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
850 (sin6
->sin6_scope_id
!= 0)) {
851 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
852 sin6
->sin6_scope_id
= 0;
858 sa
= (struct sockaddr
*) (info
->rtm
+ 1);
859 bcopy(address
, sa
, address
->sa_len
);
860 n
= ROUNDUP(sa
->sa_len
, sizeof(uint32_t));
861 info
->rtm
->rtm_msglen
+= n
;
863 info
->sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
864 info
->sdl
->sdl_family
= AF_LINK
;
865 info
->sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
866 n
= ROUNDUP(info
->sdl
->sdl_len
, sizeof(uint32_t));
867 info
->rtm
->rtm_msglen
+= n
;
869 #ifndef RTM_GET_SILENT
870 pthread_mutex_lock(&lock
);
872 rsock
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
);
876 #ifndef RTM_GET_SILENT
877 pthread_mutex_unlock(&lock
);
879 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error
));
883 if (ioctl(rsock
, FIONBIO
, &opt
) < 0) {
887 #ifndef RTM_GET_SILENT
888 pthread_mutex_unlock(&lock
);
890 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl(FIONBIO) failed: %s"), strerror(error
));
894 #ifndef RTM_GET_SILENT
895 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
899 pthread_mutex_unlock(&lock
);
900 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error
));
905 if (write(rsock
, &info
->buf
, info
->rtm
->rtm_msglen
) == -1) {
909 #ifndef RTM_GET_SILENT
910 pthread_mutex_unlock(&lock
);
912 if (error
!= ESRCH
) {
913 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(error
));
920 * Type, seq, pid identify our response.
921 * Routing sockets are broadcasters on input.
926 n
= read(rsock
, &info
->buf
, sizeof(info
->buf
));
930 if (error
== EINTR
) {
934 #ifndef RTM_GET_SILENT
935 pthread_mutex_unlock(&lock
);
938 CFSTR("SCNetworkReachability: routing socket"
939 " read() failed: %s"), strerror(error
));
942 if ((info
->rtm
->rtm_type
== RTM_GET
) &&
943 (info
->rtm
->rtm_seq
== seq
) &&
944 (info
->rtm
->rtm_pid
== pid
)) {
950 #ifndef RTM_GET_SILENT
951 pthread_mutex_unlock(&lock
);
954 get_rtaddrs(info
->rtm
->rtm_addrs
, sa
, info
->rti_info
);
956 //#define LOG_RTADDRS
961 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), info
->rtm
->rtm_flags
);
963 if ((info
->rti_info
[RTAX_NETMASK
] != NULL
) && (info
->rti_info
[RTAX_DST
] != NULL
)) {
964 info
->rti_info
[RTAX_NETMASK
]->sa_family
= info
->rti_info
[RTAX_DST
]->sa_family
;
967 for (i
= 0; i
< RTAX_MAX
; i
++) {
968 if (info
->rti_info
[i
] != NULL
) {
971 _SC_sockaddr_to_string(info
->rti_info
[i
], addr
, sizeof(addr
));
972 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, addr
);
976 #endif // LOG_RTADDRS
978 if ((info
->rti_info
[RTAX_IFP
] == NULL
) ||
979 (info
->rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
980 /* no interface info */
981 SCLog(TRUE
, LOG_DEBUG
, CFSTR("route_get() no interface info"));
985 /* ALIGN: accessors are retrieving byte values, cast ok. */
986 info
->sdl
= (struct sockaddr_dl
*)(void *) info
->rti_info
[RTAX_IFP
];
987 if ((info
->sdl
->sdl_nlen
== 0) || (info
->sdl
->sdl_nlen
> IFNAMSIZ
)) {
988 /* no interface name */
997 log_address(const char *str
,
998 const struct sockaddr
*sa
,
999 unsigned int if_index
,
1000 const char *log_prefix
)
1003 char if_name
[IFNAMSIZ
+ 1];
1005 _SC_sockaddr_to_string(sa
, addr
, sizeof(addr
));
1007 if ((if_index
!= 0) &&
1008 (if_indextoname(if_index
, &if_name
[1]) != NULL
)) {
1014 SCLog(TRUE
, LOG_INFO
, CFSTR("%s%s(%s%s)"),
1025 checkAddress_route(const struct sockaddr
*address
,
1026 unsigned int if_index
,
1029 ReachabilityInfo
*reach_info
,
1032 const char *log_prefix
)
1036 char *statusMessage
= NULL
;
1037 struct sockaddr_in v4mapped
;
1039 switch (address
->sa_family
) {
1043 log_address("checkAddress", address
, if_index
, log_prefix
);
1048 * if no code for this address family (yet)
1050 SCLog(TRUE
, LOG_INFO
,
1051 CFSTR("checkAddress(): unexpected address family %d"),
1052 address
->sa_family
);
1053 *sc_status
= kSCStatusInvalidArgument
;
1058 if (address
->sa_family
== AF_INET6
) {
1059 /* ALIGN: sin6_addr accessed aligned, cast ok. */
1060 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)(void *)address
;
1062 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) {
1063 bzero(&v4mapped
, sizeof(v4mapped
));
1064 v4mapped
.sin_len
= sizeof(v4mapped
);
1065 v4mapped
.sin_family
= AF_INET
;
1066 v4mapped
.sin_port
= sin6
->sin6_port
;
1067 v4mapped
.sin_addr
.s_addr
= sin6
->sin6_addr
.__u6_addr
.__u6_addr32
[3];
1068 address
= (struct sockaddr
*)&v4mapped
;
1072 ret
= route_get(address
, if_index
, info
);
1085 /* get the interface flags */
1087 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
1090 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
));
1094 bzero(ifr
, sizeof(*ifr
));
1095 bcopy(info
->sdl
->sdl_data
, ifr
->ifr_name
, info
->sdl
->sdl_nlen
);
1097 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)ifr
) == -1) {
1099 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl(SIOCGIFFLAGS) failed: %s"), strerror(errno
));
1103 if (!(ifr
->ifr_flags
& IFF_UP
)) {
1108 statusMessage
= "isReachable";
1109 reach_info
->flags
|= kSCNetworkReachabilityFlagsReachable
;
1111 if (info
->rtm
->rtm_flags
& RTF_LOCAL
) {
1112 statusMessage
= "isReachable (is a local address)";
1113 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1114 } else if (ifr
->ifr_flags
& IFF_LOOPBACK
) {
1115 statusMessage
= "isReachable (is loopback network)";
1116 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1117 } else if ((info
->rti_info
[RTAX_IFA
] != NULL
) &&
1118 (info
->rti_info
[RTAX_IFA
]->sa_family
!= AF_LINK
)) {
1119 void *addr1
= (void *)address
;
1120 void *addr2
= (void *)info
->rti_info
[RTAX_IFA
];
1121 size_t len
= address
->sa_len
;
1123 if ((address
->sa_family
!= info
->rti_info
[RTAX_IFA
]->sa_family
) &&
1124 (address
->sa_len
!= info
->rti_info
[RTAX_IFA
]->sa_len
)) {
1125 SCLog(TRUE
, LOG_NOTICE
,
1126 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
1129 info
->rti_info
[RTAX_IFA
]->sa_family
,
1130 info
->rti_info
[RTAX_IFA
]->sa_len
);
1134 switch (address
->sa_family
) {
1136 /* ALIGN: cast ok, because only bcmp is used. */
1137 addr1
= &((struct sockaddr_in
*)(void *)address
)->sin_addr
;
1138 addr2
= &((struct sockaddr_in
*)(void *)info
->rti_info
[RTAX_IFA
])->sin_addr
;
1139 len
= sizeof(struct in_addr
);
1144 /* ALIGN: sin_addr should be aligned, cast ok. */
1145 if (((struct sockaddr_in
*)(void *)address
)->sin_addr
.s_addr
== 0) {
1146 statusMessage
= "isReachable (this host)";
1147 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1151 /* ALIGN: cast ok, because only bcmp is used. */
1152 addr1
= &((struct sockaddr_in6
*)(void *)address
)->sin6_addr
;
1153 addr2
= &((struct sockaddr_in6
*)(void *)info
->rti_info
[RTAX_IFA
])->sin6_addr
;
1154 len
= sizeof(struct in6_addr
);
1160 if (bcmp(addr1
, addr2
, len
) == 0) {
1161 statusMessage
= "isReachable (is interface address)";
1162 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsLocalAddress
;
1166 if (!(info
->rtm
->rtm_flags
& RTF_GATEWAY
) &&
1167 (info
->rti_info
[RTAX_GATEWAY
] != NULL
) &&
1168 (info
->rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
) &&
1169 !(ifr
->ifr_flags
& IFF_POINTOPOINT
)) {
1170 reach_info
->flags
|= kSCNetworkReachabilityFlagsIsDirect
;
1173 bzero(if_name
, IFNAMSIZ
);
1174 bcopy(info
->sdl
->sdl_data
,
1176 (info
->sdl
->sdl_nlen
<= IFNAMSIZ
) ? info
->sdl
->sdl_nlen
: IFNAMSIZ
);
1178 strlcpy(reach_info
->if_name
, if_name
, sizeof(reach_info
->if_name
));
1179 reach_info
->if_index
= info
->sdl
->sdl_index
;
1182 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = %s"), log_prefix
, statusMessage
);
1183 SCLog(TRUE
, LOG_INFO
, CFSTR("%s device = %s (%hu)"), log_prefix
, if_name
, info
->sdl
->sdl_index
);
1184 SCLog(TRUE
, LOG_INFO
, CFSTR("%s sdl_type = 0x%x"), log_prefix
, info
->sdl
->sdl_type
);
1185 SCLog(TRUE
, LOG_INFO
, CFSTR("%s ifr_flags = 0x%04hx"), log_prefix
, ifr
->ifr_flags
);
1186 SCLog(TRUE
, LOG_INFO
, CFSTR("%s rtm_flags = 0x%08x"), log_prefix
, info
->rtm
->rtm_flags
);
1190 if (isock
!= -1) (void)close(isock
);
1196 checkAddress(ReachabilityStoreInfoRef store_info
,
1197 const struct sockaddr
*address
,
1198 unsigned int if_index
,
1199 ReachabilityInfo
*reach_info
,
1200 const char *log_prefix
)
1204 char if_name
[IFNAMSIZ
];
1205 nwi_ifstate_t ifstate
;
1206 nwi_state_t nwi_state
;
1208 int sc_status
= kSCStatusReachabilityUnknown
;
1210 _reach_set(reach_info
, &NOT_REACHABLE
, reach_info
->cycle
, if_index
, NULL
);
1212 nwi_state
= nwi_state_copy();
1214 if (address
!= NULL
) {
1215 ret
= checkAddress_route(address
,
1224 /* special case: check only for available paths off the system */
1229 const struct sockaddr
*vpn_server_address
;
1231 sc_status
= kSCStatusOK
;
1233 ifstate
= nwi_state_get_ifstate(nwi_state
, if_name
);
1234 if (ifstate
== NULL
) {
1238 reach_info
->flags
|= nwi_ifstate_get_reachability_flags(ifstate
);
1241 vpn_server_address
= nwi_ifstate_get_vpn_server(ifstate
);
1242 if (vpn_server_address
!= NULL
) {
1243 char dst_if_name
[IFNAMSIZ
];
1244 route_info dst_info
;
1246 ret
= route_get(vpn_server_address
, 0, &dst_info
);
1251 bzero(&dst_if_name
, sizeof(dst_if_name
));
1252 bcopy(dst_info
.sdl
->sdl_data
,
1254 (dst_info
.sdl
->sdl_nlen
<= IFNAMSIZ
) ? dst_info
.sdl
->sdl_nlen
: IFNAMSIZ
);
1255 if (bcmp(if_name
, dst_if_name
, sizeof(if_name
)) != 0) {
1256 nwi_ifstate_t ifstate
;
1258 ifstate
= nwi_state_get_ifstate(nwi_state
, dst_if_name
);
1259 if (ifstate
!= NULL
) {
1260 reach_info
->flags
|= nwi_ifstate_get_reachability_flags(ifstate
);
1264 } else if (ret
== EHOSTUNREACH
) {
1265 if (if_index
== 0) {
1268 // if not "scoped" request
1269 af
= (address
!= NULL
) ? address
->sa_family
: AF_UNSPEC
;
1270 reach_info
->flags
|= nwi_state_get_reachability_flags(nwi_state
, af
);
1271 sc_status
= kSCStatusOK
;
1273 // if "scoped" request
1274 sc_status
= kSCStatusNoKey
;
1280 if (reach_info
->flags
== 0) {
1281 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s cannot be reached"), log_prefix
);
1284 if (nwi_state
!= NULL
) {
1285 nwi_state_release(nwi_state
);
1288 if ((sc_status
!= kSCStatusOK
) && (sc_status
!= kSCStatusNoKey
)) {
1289 _SCErrorSet(sc_status
);
1298 #pragma mark SCNetworkReachability APIs
1301 static __inline__ CFTypeRef
1302 isA_SCNetworkReachability(CFTypeRef obj
)
1304 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
1309 addr_to_PTR_name(const struct sockaddr
*sa
, char *name
, size_t name_len
)
1313 switch (sa
->sa_family
) {
1319 /* ALIGN: assuming sa is aligned, then cast ok. */
1320 struct sockaddr_in
*sin
= (struct sockaddr_in
*)(void *)sa
;
1323 * build "PTR" query name
1324 * NNN.NNN.NNN.NNN.in-addr.arpa.
1326 rev
.s_addr
= sin
->sin_addr
.s_addr
;
1327 n
= snprintf(name
, name_len
, "%u.%u.%u.%u.in-addr.arpa.",
1332 if ((n
== -1) || (n
>= name_len
)) {
1342 /* ALIGN: assume sa is aligned, cast ok. */
1343 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)(void *)sa
;
1344 size_t x
= name_len
;
1347 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
1348 * 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.
1350 for (i
= sizeof(sin6
->sin6_addr
) - 1; i
>= 0; i
--) {
1351 n
= snprintf(&name
[s
], x
, "%x.%x.",
1352 ( sin6
->sin6_addr
.s6_addr
[i
] & 0xf),
1353 ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf));
1354 if ((n
== -1) || (n
>= x
)) {
1362 n
= snprintf(&name
[s
], x
, "ip6.arpa.");
1363 if ((n
== -1) || (n
>= x
)) {
1379 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target
)
1381 CFAllocatorRef allocator
= CFGetAllocator(target
);
1382 CFMutableStringRef str
;
1383 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1385 str
= CFStringCreateMutable(allocator
, 0);
1386 switch (targetPrivate
->type
) {
1387 case reachabilityTypeAddress
:
1388 case reachabilityTypeAddressPair
: {
1391 if (targetPrivate
->localAddress
!= NULL
) {
1392 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
1393 CFStringAppendFormat(str
, NULL
, CFSTR("local address = %s"),
1397 if (targetPrivate
->remoteAddress
!= NULL
) {
1398 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
1399 CFStringAppendFormat(str
, NULL
, CFSTR("%s%saddress = %s"),
1400 targetPrivate
->localAddress
? ", " : "",
1401 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
1406 case reachabilityTypeName
: {
1407 CFStringAppendFormat(str
, NULL
, CFSTR("name = %s"), targetPrivate
->name
);
1410 case reachabilityTypePTR
: {
1411 CFStringAppendFormat(str
, NULL
, CFSTR("ptr = %s"), targetPrivate
->name
);
1421 _SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target
)
1423 CFAllocatorRef allocator
= CFGetAllocator(target
);
1425 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1427 str
= CFStringCreateWithFormat(allocator
,
1429 CFSTR("flags = 0x%08x, if_index = %u%s"),
1430 targetPrivate
->info
.flags
,
1431 targetPrivate
->info
.if_index
,
1432 targetPrivate
->info
.sleeping
? ", z" : "");
1438 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
1440 CFAllocatorRef allocator
= CFGetAllocator(cf
);
1441 CFMutableStringRef result
;
1443 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
1444 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1446 result
= CFStringCreateMutable(allocator
, 0);
1447 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
1449 // add target description
1450 str
= _SCNetworkReachabilityCopyTargetDescription(target
);
1451 CFStringAppend(result
, str
);
1454 // add additional "name" info
1455 if (isReachabilityTypeName(targetPrivate
->type
)) {
1456 if (targetPrivate
->dnsActive
) {
1457 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
1458 } else if (targetPrivate
->serverActive
&&
1459 (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
)) {
1460 CFStringAppendFormat(result
, NULL
, CFSTR(" (server query active)"));
1461 } else if ((targetPrivate
->resolvedAddresses
!= NULL
) || (targetPrivate
->resolvedError
!= NETDB_SUCCESS
)) {
1462 if (targetPrivate
->resolvedAddresses
!= NULL
) {
1463 if (isA_CFArray(targetPrivate
->resolvedAddresses
)) {
1465 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddresses
);
1467 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
1468 for (i
= 0; i
< n
; i
++) {
1471 CFStringAppendFormat(result
, NULL
, CFSTR("%s"),
1474 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddresses
, i
);
1475 if (isA_CFData(address
)) {
1477 struct sockaddr
*sa
;
1479 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
1480 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
1481 CFStringAppendFormat(result
, NULL
, CFSTR("%s"), buf
);
1483 CFStringAppendFormat(result
, NULL
, CFSTR("%@"), address
);
1486 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
1487 } else if (CFEqual(targetPrivate
->resolvedAddresses
, kCFNull
)) {
1488 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
1489 gai_strerror(targetPrivate
->resolvedError
));
1491 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)"));
1494 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
1495 gai_strerror(targetPrivate
->resolvedError
));
1498 if (targetPrivate
->dnsFlags
!= 0) {
1499 CFStringAppendFormat(result
, NULL
, CFSTR(", " DNS_FLAGS_FORMAT
),
1500 DNS_FLAGS_VALUES(targetPrivate
));
1504 if (targetPrivate
->onDemandBypass
) {
1505 CFStringAppendFormat(result
, NULL
, CFSTR(", !ondemand"));
1509 if (targetPrivate
->resolverBypass
) {
1510 CFStringAppendFormat(result
, NULL
, CFSTR(", !resolve"));
1515 if (targetPrivate
->scheduled
) {
1516 str
= _SCNetworkReachabilityCopyTargetFlags(target
);
1517 CFStringAppendFormat(result
, NULL
, CFSTR(", %@"), str
);
1521 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
1528 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1530 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)cf
;
1531 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1533 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%srelease"),
1534 targetPrivate
->log_prefix
);
1536 /* disconnect from the reachability server */
1538 if (targetPrivate
->serverActive
) {
1539 __SCNetworkReachabilityServer_targetRemove(target
);
1542 /* release resources */
1544 pthread_mutex_destroy(&targetPrivate
->lock
);
1546 if (targetPrivate
->name
!= NULL
)
1547 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
1549 if (targetPrivate
->resolvedAddresses
!= NULL
)
1550 CFRelease(targetPrivate
->resolvedAddresses
);
1552 if (targetPrivate
->localAddress
!= NULL
) {
1553 if (targetPrivate
->localAddress
== targetPrivate
->remoteAddress
) {
1554 targetPrivate
->remoteAddress
= NULL
;
1556 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
1559 if (targetPrivate
->remoteAddress
!= NULL
)
1560 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
1562 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1563 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1566 if (targetPrivate
->onDemandName
!= NULL
) {
1567 CFRelease(targetPrivate
->onDemandName
);
1570 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
1571 CFRelease(targetPrivate
->onDemandRemoteAddress
);
1574 if (targetPrivate
->onDemandServer
!= NULL
) {
1575 CFRelease(targetPrivate
->onDemandServer
);
1578 if (targetPrivate
->onDemandServiceID
!= NULL
) {
1579 CFRelease(targetPrivate
->onDemandServiceID
);
1582 if (targetPrivate
->serverDigest
!= NULL
) {
1583 CFRelease(targetPrivate
->serverDigest
);
1586 if (targetPrivate
->serverGroup
!= NULL
) {
1587 dispatch_release(targetPrivate
->serverGroup
);
1590 if (targetPrivate
->serverQueue
!= NULL
) {
1591 dispatch_release(targetPrivate
->serverQueue
);
1594 if (targetPrivate
->serverWatchers
!= NULL
) {
1595 CFRelease(targetPrivate
->serverWatchers
);
1598 if (targetPrivate
->nePolicyResult
) {
1599 free(targetPrivate
->nePolicyResult
);
1607 __SCNetworkReachabilityInitialize(void)
1609 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
1611 // provide a way to enable SCNetworkReachability logging without
1612 // having to set _sc_debug=1.
1613 if ((getenv("REACH_LOGGING") != NULL
) ||
1614 (CFPreferencesGetAppBooleanValue(CFSTR("com.apple.SCNetworkReachability.debug"),
1615 kCFPreferencesCurrentApplication
,
1620 // set per-process "bypass" of the SCNetworkReachability server
1621 if (getenv("REACH_SERVER_BYPASS") != NULL
) {
1622 D_serverBypass
= TRUE
;
1626 pthread_mutexattr_init(&lock_attr
);
1627 pthread_mutexattr_settype(&lock_attr
, PTHREAD_MUTEX_ERRORCHECK
);
1635 __SCNetworkReachability_concurrent_queue()
1637 static dispatch_once_t once
;
1638 static dispatch_queue_t q
;
1640 dispatch_once(&once
, ^{
1641 q
= dispatch_queue_create("SCNetworkReachability.concurrent",
1642 DISPATCH_QUEUE_CONCURRENT
);
1650 * __SCNetworkReachabilityUpdateConcurrent
1652 * Calls reachUpdate()
1653 * - caller must be holding a reference to the target
1654 * - caller must *not* be holding the target lock
1655 * - caller must be running on the __SCNetworkReachability_concurrent_queue()
1659 __SCNetworkReachabilityUpdateConcurrent(SCNetworkReachabilityRef target
)
1663 dispatch_queue_t queue
;
1664 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1666 changed
= reachUpdate((void *)target
);
1671 n
= _SC_ATOMIC_INC(&targetPrivate
->pending
);
1673 // if we already have a notification pending
1677 MUTEX_LOCK(&targetPrivate
->lock
);
1679 queue
= targetPrivate
->dispatchQueue
;
1680 if (queue
!= NULL
) {
1681 dispatch_group_t group
;
1683 dispatch_retain(queue
);
1685 group
= targetPrivate
->dispatchGroup
;
1686 dispatch_group_enter(group
);
1688 MUTEX_UNLOCK(&targetPrivate
->lock
);
1690 dispatch_sync(queue
, ^{
1691 reachPerform((void *)target
);
1692 dispatch_group_leave(group
);
1695 dispatch_release(queue
);
1697 if (targetPrivate
->rls
!= NULL
) {
1698 CFRunLoopSourceSignal(targetPrivate
->rls
);
1699 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1702 MUTEX_UNLOCK(&targetPrivate
->lock
);
1710 * __SCNetworkReachabilityUpdate
1712 * Calls reachUpdate() [indirectly]
1713 * - caller can be holding the target lock
1714 * - caller can be running on any dispatch queue
1718 __SCNetworkReachabilityUpdate(SCNetworkReachabilityRef target
)
1721 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{
1722 __SCNetworkReachabilityUpdateConcurrent(target
);
1730 static SCNetworkReachabilityPrivateRef
1731 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
1733 SCNetworkReachabilityPrivateRef targetPrivate
;
1736 /* initialize runtime */
1737 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
1739 /* allocate target */
1740 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
1741 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
1742 __kSCNetworkReachabilityTypeID
,
1745 if (targetPrivate
== NULL
) {
1749 bzero((void *)targetPrivate
+ sizeof(CFRuntimeBase
), size
);
1751 MUTEX_INIT(&targetPrivate
->lock
);
1753 targetPrivate
->cycle
= 1;
1754 targetPrivate
->last_notify
= NOT_REPORTED
;
1755 targetPrivate
->serverBypass
= D_serverBypass
;
1759 targetPrivate
->log_prefix
[0] = '\0';
1761 snprintf(targetPrivate
->log_prefix
,
1762 sizeof(targetPrivate
->log_prefix
),
1767 return targetPrivate
;
1773 static const struct sockaddr
*
1774 is_valid_address(const struct sockaddr
*address
)
1776 const struct sockaddr
*valid
= NULL
;
1777 static Boolean warned
= FALSE
;
1779 if ((address
!= NULL
) &&
1780 (address
->sa_len
<= sizeof(struct sockaddr_storage
))) {
1781 switch (address
->sa_family
) {
1783 if (address
->sa_len
>= sizeof(struct sockaddr_in
)) {
1787 SCLog(TRUE
, LOG_ERR
,
1788 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"),
1790 sizeof(struct sockaddr_in
));
1796 if (address
->sa_len
>= sizeof(struct sockaddr_in6
)) {
1798 } else if (!warned
) {
1799 SCLog(TRUE
, LOG_ERR
,
1800 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"),
1802 sizeof(struct sockaddr_in6
));
1808 SCLog(TRUE
, LOG_ERR
,
1809 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"),
1810 address
->sa_family
);
1824 SCNetworkReachabilityRef
1825 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
1826 const struct sockaddr
*address
)
1828 SCNetworkReachabilityPrivateRef targetPrivate
;
1830 address
= is_valid_address(address
);
1831 if (address
== NULL
) {
1832 _SCErrorSet(kSCStatusInvalidArgument
);
1836 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1837 if (targetPrivate
== NULL
) {
1841 targetPrivate
->type
= reachabilityTypeAddress
;
1842 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
1843 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
1847 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
1848 targetPrivate
->log_prefix
,
1849 DEBUG_REACHABILITY_TYPE_ADDRESS
,
1852 return (SCNetworkReachabilityRef
)targetPrivate
;
1857 is_same_address(const struct sockaddr
*a
, const struct sockaddr
*b
)
1865 (a
->sa_family
!= b
->sa_family
) ||
1866 (a
->sa_len
!= b
->sa_len
)) {
1870 switch (a
->sa_family
) {
1872 struct sockaddr_in
*a_sin
= (struct sockaddr_in
*)(void *)a
;
1873 struct sockaddr_in
*b_sin
= (struct sockaddr_in
*)(void *)b
;
1875 /* ALIGN: assuming a (and b) are aligned, then cast ok. */
1876 a_addr
= &a_sin
->sin_addr
;
1877 b_addr
= &b_sin
->sin_addr
;
1878 len
= sizeof(struct in_addr
);
1883 struct sockaddr_in6
*a_sin6
= (struct sockaddr_in6
*)(void *)a
;
1884 struct sockaddr_in6
*b_sin6
= (struct sockaddr_in6
*)(void *)b
;
1886 if (a_sin6
->sin6_scope_id
!= b_sin6
->sin6_scope_id
) {
1890 a_addr
= &a_sin6
->sin6_addr
;
1891 b_addr
= &b_sin6
->sin6_addr
;
1892 len
= sizeof(struct in6_addr
);
1903 return (bcmp(a_addr
, b_addr
, len
) == 0);
1907 SCNetworkReachabilityRef
1908 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
1909 const struct sockaddr
*localAddress
,
1910 const struct sockaddr
*remoteAddress
)
1912 SCNetworkReachabilityPrivateRef targetPrivate
;
1914 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
1915 _SCErrorSet(kSCStatusInvalidArgument
);
1919 if (localAddress
!= NULL
) {
1920 localAddress
= is_valid_address(localAddress
);
1921 if (localAddress
== NULL
) {
1922 _SCErrorSet(kSCStatusInvalidArgument
);
1927 if (remoteAddress
!= NULL
) {
1928 remoteAddress
= is_valid_address(remoteAddress
);
1929 if (remoteAddress
== NULL
) {
1930 _SCErrorSet(kSCStatusInvalidArgument
);
1935 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1936 if (targetPrivate
== NULL
) {
1940 targetPrivate
->type
= reachabilityTypeAddressPair
;
1942 if (localAddress
!= NULL
) {
1943 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
1944 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
1947 if (remoteAddress
!= NULL
) {
1948 if (is_same_address(localAddress
, remoteAddress
)) {
1949 targetPrivate
->remoteAddress
= targetPrivate
->localAddress
;
1951 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
1952 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
1958 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
1959 targetPrivate
->log_prefix
,
1960 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR
,
1963 return (SCNetworkReachabilityRef
)targetPrivate
;
1967 SCNetworkReachabilityRef
1968 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
1969 const char *nodename
)
1973 struct sockaddr_in sin
;
1974 struct sockaddr_in6 sin6
;
1977 SCNetworkReachabilityPrivateRef targetPrivate
;
1979 if (nodename
== NULL
) {
1980 _SCErrorSet(kSCStatusInvalidArgument
);
1984 nodenameLen
= strlen(nodename
);
1985 if (nodenameLen
== 0) {
1986 _SCErrorSet(kSCStatusInvalidArgument
);
1990 if (nodename
[nodenameLen
- 1] == '.') {
1994 // trim trailing "."s
1997 } while ((nodenameLen
> 0) && (nodename
[nodenameLen
- 1] == '.'));
1999 if (nodenameLen
== 0) {
2000 // if only trailing "."s
2001 _SCErrorSet(kSCStatusInvalidArgument
);
2005 // count the remaining "."s
2007 for (i
= 0; i
< nodenameLen
; i
++) {
2008 if (nodename
[i
] == '.') dots
++;
2012 // if only a single-label, add back the FQDN "."
2017 if (_SC_string_to_sockaddr(nodename
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) != NULL
) {
2018 /* if this "nodename" is really an IP[v6] address in disguise */
2019 return SCNetworkReachabilityCreateWithAddress(allocator
, &addr
.sa
);
2022 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
2023 if (targetPrivate
== NULL
) {
2027 targetPrivate
->type
= reachabilityTypeName
;
2029 targetPrivate
->name
= CFAllocatorAllocate(NULL
, nodenameLen
+ 1, 0);
2030 strlcpy((char *)targetPrivate
->name
, nodename
, nodenameLen
+ 1);
2032 targetPrivate
->needResolve
= TRUE
;
2033 targetPrivate
->info
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
2034 targetPrivate
->serverInfo
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
2037 /* make sure AppLayerVPN only is in client mode */
2038 CFDictionaryRef appLayerVPNProperties
;
2040 appLayerVPNProperties
= VPNAppLayerCopyCurrentAppProperties();
2041 if (appLayerVPNProperties
!= NULL
) {
2042 targetPrivate
->serverBypassForVPN
= TRUE
;
2043 targetPrivate
->serverBypass
= YES
;
2044 CFRelease(appLayerVPNProperties
);
2048 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%s%s %@"),
2049 targetPrivate
->log_prefix
,
2050 DEBUG_REACHABILITY_TYPE_NAME
,
2053 return (SCNetworkReachabilityRef
)targetPrivate
;
2057 static SCNetworkReachabilityRef
2058 __SCNetworkReachabilityCreateWithPtr(CFAllocatorRef allocator
,
2059 const char *ptrName
,
2060 const struct sockaddr
*ptrAddress
)
2062 SCNetworkReachabilityRef target
;
2063 SCNetworkReachabilityPrivateRef targetPrivate
;
2065 target
= SCNetworkReachabilityCreateWithName(NULL
, ptrName
);
2066 if (target
== NULL
) {
2070 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2073 targetPrivate
->type
= reachabilityTypePTR
;
2075 // and keep the address
2076 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, ptrAddress
->sa_len
, 0);
2077 bcopy(ptrAddress
, targetPrivate
->remoteAddress
, ptrAddress
->sa_len
);
2085 SCNetworkReachabilityRef
2086 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator
,
2087 CFDictionaryRef options
)
2089 const struct sockaddr
*addr_l
= NULL
;
2090 const struct sockaddr
*addr_p
= NULL
;
2091 const struct sockaddr
*addr_r
= NULL
;
2093 CFStringRef interface
= NULL
;
2094 CFStringRef nodename
;
2095 CFBooleanRef onDemandBypass
;
2096 CFBooleanRef resolverBypass
;
2097 CFBooleanRef serverBypass
;
2098 SCNetworkReachabilityRef target
;
2099 SCNetworkReachabilityPrivateRef targetPrivate
;
2101 if (!isA_CFDictionary(options
)) {
2102 _SCErrorSet(kSCStatusInvalidArgument
);
2106 nodename
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
);
2107 if ((nodename
!= NULL
) &&
2108 (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) {
2109 _SCErrorSet(kSCStatusInvalidArgument
);
2112 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
);
2114 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2115 _SCErrorSet(kSCStatusInvalidArgument
);
2118 addr_l
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2120 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionPTRAddress
);
2122 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2123 _SCErrorSet(kSCStatusInvalidArgument
);
2126 addr_p
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2128 data
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
);
2130 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) {
2131 _SCErrorSet(kSCStatusInvalidArgument
);
2134 addr_r
= (const struct sockaddr
*)CFDataGetBytePtr(data
);
2136 interface
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
);
2137 if ((interface
!= NULL
) &&
2138 (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) {
2139 _SCErrorSet(kSCStatusInvalidArgument
);
2142 onDemandBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandBypass
);
2143 if ((onDemandBypass
!= NULL
) && !isA_CFBoolean(onDemandBypass
)) {
2144 _SCErrorSet(kSCStatusInvalidArgument
);
2147 resolverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionResolverBypass
);
2148 if ((resolverBypass
!= NULL
) && !isA_CFBoolean(resolverBypass
)) {
2149 _SCErrorSet(kSCStatusInvalidArgument
);
2154 serverBypass
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServerBypass
);
2155 if ((serverBypass
!= NULL
) && !isA_CFBoolean(serverBypass
)) {
2156 _SCErrorSet(kSCStatusInvalidArgument
);
2161 if (nodename
!= NULL
) {
2164 if ((addr_l
!= NULL
) || (addr_r
!= NULL
) || (addr_p
!= NULL
)) {
2165 // can't have both a nodename and an address
2166 _SCErrorSet(kSCStatusInvalidArgument
);
2170 name
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
);
2171 target
= SCNetworkReachabilityCreateWithName(allocator
, name
);
2172 CFAllocatorDeallocate(NULL
, (void *)name
);
2173 } else if (addr_p
!= NULL
) {
2174 char name
[MAXHOSTNAMELEN
];
2176 if ((addr_l
!= NULL
) || // can't have PTR and target address
2177 (addr_r
!= NULL
) || // can't have PTR and target address
2178 !addr_to_PTR_name(addr_p
, name
, sizeof(name
))) { // can't convert PTR
2179 _SCErrorSet(kSCStatusInvalidArgument
);
2183 target
= __SCNetworkReachabilityCreateWithPtr(NULL
, name
, addr_p
);
2185 if ((addr_l
!= NULL
) && (addr_r
!= NULL
)) {
2186 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
);
2187 } else if (addr_r
!= NULL
) {
2188 target
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
);
2189 } else if (addr_l
!= NULL
) {
2190 target
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, NULL
);
2192 _SCErrorSet(kSCStatusInvalidArgument
);
2196 if (target
== NULL
) {
2200 targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2202 if (interface
!= NULL
) {
2203 if ((_SC_cfstring_to_cstring(interface
,
2204 targetPrivate
->if_name
,
2205 sizeof(targetPrivate
->if_name
),
2206 kCFStringEncodingASCII
) == NULL
) ||
2207 ((targetPrivate
->if_index
= if_nametoindex(targetPrivate
->if_name
)) == 0)) {
2208 CFRelease(targetPrivate
);
2209 _SCErrorSet(kSCStatusInvalidArgument
);
2215 if (onDemandBypass
!= NULL
) {
2216 targetPrivate
->onDemandBypass
= CFBooleanGetValue(onDemandBypass
);
2219 if (resolverBypass
!= NULL
) {
2220 targetPrivate
->resolverBypass
= CFBooleanGetValue(resolverBypass
);
2223 /* if by name, make sure client-only VPN types stay in client mode */
2224 if (serverBypass
!= NULL
&& targetPrivate
->serverBypassForVPN
== FALSE
) {
2225 targetPrivate
->serverBypass
= CFBooleanGetValue(serverBypass
);
2229 if (_sc_debug
&& (_sc_log
> 0)) {
2230 const char *opt
= "???";
2232 switch (targetPrivate
->type
) {
2233 case reachabilityTypeAddress
:
2234 opt
= DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS
;
2236 case reachabilityTypeAddressPair
:
2237 opt
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS
;
2239 case reachabilityTypeName
:
2240 opt
= DEBUG_REACHABILITY_TYPE_NAME_OPTIONS
;
2242 case reachabilityTypePTR
:
2243 opt
= DEBUG_REACHABILITY_TYPE_PTR_OPTIONS
;
2247 SCLog(TRUE
, LOG_INFO
, CFSTR("%s%s %@"),
2248 targetPrivate
->log_prefix
,
2253 return (SCNetworkReachabilityRef
)targetPrivate
;
2257 static SCNetworkReachabilityRef
2258 __SCNetworkReachabilityCreateCopy(SCNetworkReachabilityRef target
)
2260 SCNetworkReachabilityRef clone
= NULL
;
2261 SCNetworkReachabilityPrivateRef clonePrivate
;
2262 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2264 switch (targetPrivate
->type
) {
2265 case reachabilityTypeAddress
:
2266 clone
= SCNetworkReachabilityCreateWithAddress(NULL
,
2267 targetPrivate
->remoteAddress
);
2269 case reachabilityTypeAddressPair
:
2270 clone
= SCNetworkReachabilityCreateWithAddressPair(NULL
,
2271 targetPrivate
->localAddress
,
2272 targetPrivate
->remoteAddress
);
2274 case reachabilityTypeName
:
2275 clone
= SCNetworkReachabilityCreateWithName(NULL
,
2276 targetPrivate
->name
);
2278 case reachabilityTypePTR
:
2279 clone
= __SCNetworkReachabilityCreateWithPtr(NULL
,
2280 targetPrivate
->name
,
2281 targetPrivate
->remoteAddress
);
2284 if (clone
== NULL
) {
2288 clonePrivate
= (SCNetworkReachabilityPrivateRef
)clone
;
2290 clonePrivate
->quiet
= TRUE
;
2292 clonePrivate
->if_index
= targetPrivate
->if_index
;
2293 bcopy(targetPrivate
->if_name
, clonePrivate
->if_name
, sizeof(clonePrivate
->if_name
));
2295 clonePrivate
->onDemandBypass
= targetPrivate
->onDemandBypass
;
2298 clonePrivate
->serverBypass
= targetPrivate
->serverBypass
;
2300 clonePrivate
->resolverBypass
= targetPrivate
->resolverBypass
;
2303 if (_sc_debug
&& (_sc_log
> 0)) {
2304 const char *opt
= "???";
2306 switch (clonePrivate
->type
) {
2307 case reachabilityTypeAddress
:
2308 opt
= DEBUG_REACHABILITY_TYPE_ADDRESS_CLONE
;
2310 case reachabilityTypeAddressPair
:
2311 opt
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_CLONE
;
2313 case reachabilityTypeName
:
2314 opt
= DEBUG_REACHABILITY_TYPE_NAME_CLONE
;
2316 case reachabilityTypePTR
:
2317 opt
= DEBUG_REACHABILITY_TYPE_PTR_CLONE
;
2321 SCLog(TRUE
, LOG_INFO
, CFSTR("%s%s %p %@"),
2322 clonePrivate
->log_prefix
,
2333 SCNetworkReachabilityGetTypeID(void)
2335 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
2336 return __kSCNetworkReachabilityTypeID
;
2340 CFArrayRef
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */
2341 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
2344 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2346 if (!isA_SCNetworkReachability(target
)) {
2347 _SCErrorSet(kSCStatusInvalidArgument
);
2351 if (!isReachabilityTypeName(targetPrivate
->type
)) {
2352 _SCErrorSet(kSCStatusInvalidArgument
);
2357 *error_num
= targetPrivate
->resolvedError
;
2360 if (targetPrivate
->resolvedAddresses
!= NULL
) {
2361 if (isA_CFArray(targetPrivate
->resolvedAddresses
)) {
2362 return CFRetain(targetPrivate
->resolvedAddresses
);
2364 /* if status is known but no resolved addresses to return */
2365 _SCErrorSet(kSCStatusOK
);
2370 _SCErrorSet(kSCStatusReachabilityUnknown
);
2376 __SCNetworkReachabilitySetResolvedError(SCNetworkReachabilityRef target
,
2379 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2381 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
2383 __mark_operation_end(target
,
2384 FALSE
, // if successful query
2385 dns_query_async
, // async
2386 &targetPrivate
->dnsQueryStart
, // start time
2387 &targetPrivate
->dnsQueryEnd
); // end time
2389 if (targetPrivate
->resolvedAddresses
!= NULL
) {
2390 CFRelease(targetPrivate
->resolvedAddresses
);
2391 targetPrivate
->resolvedAddresses
= NULL
;
2394 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%scould not be resolved: %s"),
2395 targetPrivate
->log_prefix
,
2396 gai_strerror(status
));
2398 /* save the error associated with the attempt to resolve the name */
2399 targetPrivate
->resolvedAddresses
= CFRetain(kCFNull
);
2400 targetPrivate
->resolvedError
= status
;
2401 targetPrivate
->needResolve
= FALSE
;
2408 * rankReachability()
2409 * Not reachable == 0
2410 * Connection Required == 1
2414 rankReachability(SCNetworkReachabilityFlags flags
)
2418 if (flags
& kSCNetworkReachabilityFlagsReachable
) rank
= 2;
2419 if (flags
& kSCNetworkReachabilityFlagsConnectionRequired
) rank
= 1;
2425 #pragma mark DNS name resolution
2429 update_resolver_reachability(ReachabilityStoreInfoRef store_info
,
2430 dns_resolver_t
*resolver
,
2431 SCNetworkReachabilityFlags
*flags
,
2433 uint32_t *resolver_if_index
,
2434 const char *log_prefix
)
2436 if (resolver_if_index
) *resolver_if_index
= 0;
2438 if (resolver
->n_nameserver
> 0) {
2439 *flags
= (SCNetworkReachabilityFlags
)resolver
->reach_flags
;
2440 if (resolver_if_index
!= NULL
) {
2441 *resolver_if_index
= resolver
->if_index
;
2445 *flags
= kSCNetworkReachabilityFlagsReachable
;
2454 check_matching_resolvers(ReachabilityStoreInfoRef store_info
,
2455 dns_config_t
*dns_config
,
2457 unsigned int if_index
,
2458 SCNetworkReachabilityFlags
*flags
,
2460 uint32_t *resolver_if_index
,
2461 int *dns_config_index
,
2462 const char *log_prefix
)
2465 Boolean matched
= FALSE
;
2466 const char *name
= fqdn
;
2467 int32_t n_resolvers
;
2468 dns_resolver_t
**resolvers
;
2470 if (if_index
== 0) {
2471 n_resolvers
= dns_config
->n_resolver
;
2472 resolvers
= dns_config
->resolver
;
2474 n_resolvers
= dns_config
->n_scoped_resolver
;
2475 resolvers
= dns_config
->scoped_resolver
;
2478 /* In case we couldn't find a match, setting an index of -1
2479 and resolver_if_index 0 */
2480 if (dns_config_index
!= NULL
) *dns_config_index
= -1;
2481 if (resolver_if_index
!= NULL
) *resolver_if_index
= 0;
2483 while (!matched
&& (name
!= NULL
)) {
2487 * check if the provided name (or sub-component)
2488 * matches one of our resolver configurations.
2491 for (i
= 0; i
< n_resolvers
; i
++) {
2493 dns_resolver_t
*resolver
;
2495 resolver
= resolvers
[i
];
2496 if ((if_index
!= 0) && (if_index
!= resolver
->if_index
)) {
2500 domain
= resolver
->domain
;
2501 if (domain
!= NULL
&& (len
== strlen(domain
))) {
2502 if (strcasecmp(name
, domain
) == 0) {
2504 * if name matches domain
2507 update_resolver_reachability(store_info
,
2513 if (dns_config_index
!= NULL
) *dns_config_index
= i
;
2521 * we have not found a matching resolver, try
2522 * a less qualified domain
2524 name
= strchr(name
, '.');
2525 if ((name
!= NULL
) && (*name
!= '\0')) {
2537 static dns_resolver_t
*
2538 get_default_resolver(dns_config_t
*dns_config
, unsigned int if_index
)
2541 int32_t n_resolvers
;
2542 dns_resolver_t
*resolver
= NULL
;
2543 dns_resolver_t
**resolvers
;
2545 if (if_index
== 0) {
2546 n_resolvers
= dns_config
->n_resolver
;
2547 resolvers
= dns_config
->resolver
;
2549 n_resolvers
= dns_config
->n_scoped_resolver
;
2550 resolvers
= dns_config
->scoped_resolver
;
2553 for (i
= 0; i
< n_resolvers
; i
++) {
2554 if ((if_index
!= 0) && (if_index
!= resolvers
[i
]->if_index
)) {
2558 if (((if_index
== 0) && (i
== 0)) ||
2559 ((if_index
!= 0) && (resolver
== NULL
))) {
2560 // if this is the first (aka default) resolver
2561 resolver
= resolvers
[i
];
2562 } else if ((resolvers
[i
]->domain
== NULL
) &&
2563 (resolvers
[i
]->search_order
< resolver
->search_order
)) {
2564 // if this is a default resolver with a lower search order
2565 resolver
= resolvers
[i
];
2573 static dns_configuration_t
*
2574 dns_configuration_retain()
2576 dns_configuration_t
*config
;
2578 pthread_mutex_lock(&dns_lock
);
2580 if (dns_configuration
!= NULL
) {
2581 Boolean refresh
= TRUE
;
2583 if (dns_token_valid
) {
2588 * check if the global [DNS] configuration snapshot needs
2591 status
= notify_check(dns_token
, &check
);
2592 if (status
!= NOTIFY_STATUS_OK
) {
2593 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%u"), status
);
2594 } else if (check
== 0) {
2595 // if the snapshot does not need to be refreshed
2601 if (dns_configuration
->refs
== 0) {
2602 dns_configuration_free(dns_configuration
->config
);
2603 CFAllocatorDeallocate(NULL
, dns_configuration
);
2605 dns_configuration
= NULL
;
2609 if (dns_configuration
== NULL
) {
2610 dns_config_t
*new_config
;
2612 new_config
= dns_configuration_copy();
2613 if (new_config
!= NULL
) {
2614 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
2615 dns_configuration
->config
= new_config
;
2616 dns_configuration
->refs
= 0;
2620 if (dns_configuration
!= NULL
) {
2621 dns_configuration
->refs
++;
2624 config
= dns_configuration
;
2625 pthread_mutex_unlock(&dns_lock
);
2631 dns_configuration_release(dns_configuration_t
*config
)
2633 pthread_mutex_lock(&dns_lock
);
2636 if (config
->refs
== 0) {
2637 if (!dns_token_valid
&& (config
== dns_configuration
)) {
2638 dns_configuration
= NULL
;
2641 if (config
!= dns_configuration
) {
2642 dns_configuration_free(config
->config
);
2643 CFAllocatorDeallocate(NULL
, config
);
2647 pthread_mutex_unlock(&dns_lock
);
2653 dns_configuration_watch()
2656 const char *dns_key
;
2660 pthread_mutex_lock(&dns_lock
);
2662 dns_key
= dns_configuration_notify_key();
2663 if (dns_key
== NULL
) {
2664 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
2668 status
= notify_register_check(dns_key
, &dns_token
);
2669 if (status
== NOTIFY_STATUS_OK
) {
2670 dns_token_valid
= TRUE
;
2672 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%u"), status
);
2676 status
= notify_check(dns_token
, &dns_check
);
2677 if (status
!= NOTIFY_STATUS_OK
) {
2678 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%u"), status
);
2679 (void)notify_cancel(dns_token
);
2680 dns_token_valid
= FALSE
;
2688 pthread_mutex_unlock(&dns_lock
);
2694 dns_configuration_unwatch()
2696 pthread_mutex_lock(&dns_lock
);
2698 (void)notify_cancel(dns_token
);
2699 dns_token_valid
= FALSE
;
2701 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
2702 dns_configuration_free(dns_configuration
->config
);
2703 CFAllocatorDeallocate(NULL
, dns_configuration
);
2704 dns_configuration
= NULL
;
2707 pthread_mutex_unlock(&dns_lock
);
2713 _SC_R_updateResolverReachability(ReachabilityStoreInfoRef store_info
,
2714 SCNetworkReachabilityFlags
*flags
,
2716 const char *nodename
,
2717 unsigned int if_index
,
2718 uint32_t *resolver_if_index
,
2719 int *dns_config_index
,
2720 const char *log_prefix
2723 dns_resolver_t
*default_resolver
;
2724 dns_configuration_t
*dns
;
2725 Boolean found
= FALSE
;
2726 char *fqdn
= (char *)nodename
;
2728 Boolean isFQDN
= FALSE
;
2730 const int ndots
= 1;
2731 Boolean useDefault
= FALSE
;
2733 if (resolver_if_index
) *resolver_if_index
= 0;
2734 if (dns_config_index
) *dns_config_index
= -1;
2737 * We first assume that all of the configured DNS servers
2738 * are available. Since we don't know which name server will
2739 * be consulted to resolve the specified nodename we need to
2740 * check the availability of ALL name servers. We can only
2741 * proceed if we know that our query can be answered.
2744 *flags
= kSCNetworkReachabilityFlagsReachable
;
2747 len
= (nodename
!= NULL
) ? strlen(nodename
) : 0;
2749 // if no nodename, return not reachable
2754 dns
= dns_configuration_retain();
2757 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no configuration"), log_prefix
);
2761 default_resolver
= get_default_resolver(dns
->config
, if_index
);
2762 if (default_resolver
== NULL
) {
2763 // if no resolver configuration
2764 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no resolvers"), log_prefix
);
2768 if (fqdn
[len
- 1] == '.') {
2771 // trim trailing '.''s
2772 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
2773 if (fqdn
== nodename
) {
2774 fqdn
= strdup(nodename
);
2775 assert(fqdn
!= nodename
);
2782 * check if the provided name matches a supplemental domain
2784 found
= check_matching_resolvers(store_info
, dns
->config
, fqdn
, if_index
,
2785 flags
, haveDNS
, resolver_if_index
,
2786 dns_config_index
, log_prefix
);
2788 if (!found
&& !isFQDN
) {
2790 * if we did not match a supplemental domain name and if the
2791 * provided name has enough "."s then the first query will be
2792 * directed to the default resolver.
2798 for (cp
= fqdn
; *cp
!= '\0'; cp
++) {
2799 if (*cp
== '.') dots
++;
2802 /* Per KB: HT4845 */
2803 if (dots
>= ndots
) {
2808 if (!found
&& !isFQDN
&& !useDefault
&& (dns
->config
->n_resolver
> 1)) {
2810 * FQDN not specified, try matching w/search domains
2812 if (default_resolver
->n_search
> 0) {
2813 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
2815 char *search_fqdn
= NULL
;
2817 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
2822 // try the provided name with the search domain appended
2823 found
= check_matching_resolvers(store_info
,
2834 } else if (default_resolver
->domain
!= NULL
) {
2836 int domain_parts
= 0;
2838 // count domain parts
2839 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
2845 // remove trailing dots
2846 for (dp
--; (dp
>= default_resolver
->domain
) && (*dp
== '.'); dp
--) {
2851 if (dp
>= default_resolver
->domain
) {
2852 // dots are separators, bump # of components
2856 dp
= default_resolver
->domain
;
2857 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= (domain_parts
- ndots
)); i
++) {
2859 char *search_fqdn
= NULL
;
2861 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
2866 // try the provided name with the [default] domain appended
2867 found
= check_matching_resolvers(store_info
,
2878 // move to the next component of the [default] domain
2879 dp
= strchr(dp
, '.') + 1;
2885 // update the reachability of the default resolver
2886 update_resolver_reachability(store_info
,
2892 if (dns_config_index
!= NULL
) *dns_config_index
= 0;
2897 if (fqdn
!= nodename
) free(fqdn
);
2900 dns_configuration_release(dns
);
2908 __SC_checkResolverReachabilityInternal(SCDynamicStoreRef
*storeP
,
2909 SCNetworkReachabilityFlags
*flags
,
2911 const char *nodename
,
2912 uint32_t *resolver_if_index
,
2913 int *dns_config_index
)
2916 ReachabilityStoreInfo store_info
;
2918 ReachabilityStoreInfo_init(&store_info
);
2919 ok
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
);
2924 _SC_R_updateResolverReachability(&store_info
,
2935 ReachabilityStoreInfo_free(&store_info
);
2941 * _SC_checkResolverReachabilityByAddress()
2943 * Given an IP address, determine whether a reverse DNS query can be issued
2944 * using the current network configuration.
2947 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
2948 SCNetworkReachabilityFlags
*flags
,
2950 struct sockaddr
*sa
)
2954 ReachabilityStoreInfo store_info
;
2956 ReachabilityStoreInfo_init(&store_info
);
2957 ok
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
);
2963 * Ideally, we would have an API that given a local IP
2964 * address would return the DNS server(s) that would field
2965 * a given PTR query. Fortunately, we do have an SPI which
2966 * which will provide this information given a "name" so we
2967 * take the address, convert it into the inverse query name,
2968 * and find out which servers should be consulted.
2970 ok
= addr_to_PTR_name(sa
, ptr_name
, sizeof(ptr_name
));
2975 _SC_R_updateResolverReachability(&store_info
, flags
, haveDNS
, ptr_name
, 0, NULL
, NULL
, "");
2979 ReachabilityStoreInfo_free(&store_info
);
2985 #pragma mark DNSServiceGetAddrInfo support
2989 * DNS query handling
2993 * 1. We have a "contract" with mDNSResponder that for EVERY network
2994 * or DNS configuration change that should warrant our [re-]starting
2995 * a query, mDNSResponder will acknowledge the latest DNS configuration.
2997 * 2. IPMonitor also posts a notification AFTER every network or DNS
2998 * configuration change.
3000 * 3. We use IPMonitor's "trailing edge" as a signal to restart any
3005 // Note: protected by _hn_target_queue()
3006 static int dns_refresh_token
;
3007 static Boolean dns_refresh_token_valid
= FALSE
;
3011 * dns_refresh_handler
3013 * Called to notify/update all SCNetworkReachability by-name targets of
3014 * a network/DNS change. The change should [re-]start a DNS query to
3016 * - should be exec'd on the _hn_target_queue()
3019 dns_refresh_handler()
3023 __block SCDynamicStoreRef store
= NULL
;
3025 dispatch_sync(_hn_target_queue(), ^{
3026 if (dns_refresh_token_valid
&& (hn_store
!= NULL
)) {
3027 store
= CFRetain(hn_store
);
3031 if (store
== NULL
) {
3035 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3036 kSCDynamicStoreDomainState
,
3038 changes
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
3039 __SCNetworkReachabilityHandleChanges(store
, changes
, NULL
);
3049 * dns_refresh_enable
3051 * Called to monitor for network/DNS changes that should restart a DNS query.
3052 * - caller must be running on the _hn_target_queue()
3055 dns_refresh_enable(dispatch_queue_t q
)
3059 status
= notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE
,
3063 dns_refresh_handler();
3065 if (status
!= NOTIFY_STATUS_OK
) {
3066 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_dispatch() failed, status=%u"), status
);
3070 dns_refresh_token_valid
= TRUE
;
3077 * dns_refresh_disable
3079 * Called to stop monitoring for network/DNS changes
3080 * - caller must be running on the _hn_target_queue()
3083 dns_refresh_disable()
3085 (void)notify_cancel(dns_refresh_token
);
3086 dns_refresh_token_valid
= FALSE
;
3092 #pragma mark [m]DNS Queries
3096 dequeueDNSQuery(SCNetworkReachabilityRef target
);
3099 static dispatch_queue_t
3102 static dispatch_once_t once
;
3103 static dispatch_queue_t q
;
3105 dispatch_once(&once
, ^{
3106 q
= dispatch_queue_create("SCNetworkReachability.DNSService", NULL
);
3116 static __inline__ Boolean
3117 _dns_complete(SCNetworkReachabilityRef target
)
3119 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3121 if ((targetPrivate
->dnsHaveV4
&& targetPrivate
->dnsHaveV6
) ||
3122 targetPrivate
->dnsHavePTR
||
3123 targetPrivate
->dnsHaveError
||
3124 targetPrivate
->dnsHaveTimeout
) {
3135 * Called to push out a target's DNS changes
3136 * - caller must be running on the _dns_queue()
3139 _dns_notify(const void *value
, void *context
)
3141 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)value
;
3142 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3144 MUTEX_LOCK(&targetPrivate
->lock
);
3146 if (_dns_complete(target
)) {
3147 __mark_operation_end(target
,
3148 (targetPrivate
->resolvedError
== NETDB_SUCCESS
), // if successful query
3149 dns_query_mdns
, // [m]DNS query
3150 &targetPrivate
->dnsQueryStart
, // start time
3151 &targetPrivate
->dnsQueryEnd
); // end time
3153 // update target info
3154 if (targetPrivate
->resolvedAddresses
!= NULL
) {
3155 CFRelease(targetPrivate
->resolvedAddresses
);
3157 targetPrivate
->resolvedAddresses
= targetPrivate
->dnsAddresses
;
3158 targetPrivate
->dnsAddresses
= NULL
;
3160 targetPrivate
->resolvedError
= targetPrivate
->dnsError
;
3161 targetPrivate
->dnsError
= NETDB_SUCCESS
;
3163 dequeueDNSQuery(target
);
3165 targetPrivate
->needResolve
= FALSE
;
3167 if (targetPrivate
->scheduled
) {
3168 __SCNetworkReachabilityUpdate(target
);
3172 MUTEX_UNLOCK(&targetPrivate
->lock
);
3190 static __inline__
void
3191 _dns_mark(SCNetworkReachabilityRef target
, _dns_mark_t mark
)
3193 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3199 targetPrivate
->dnsHaveError
= TRUE
;
3202 targetPrivate
->dnsHaveTimeout
= TRUE
;
3205 targetPrivate
->dnsHaveV4
= TRUE
;
3208 targetPrivate
->dnsHaveV6
= TRUE
;
3210 case MARK_HAVE_PTR
:
3211 targetPrivate
->dnsHavePTR
= TRUE
;
3222 * Called to process [m]DNS query updates
3223 * - caller must be running on the _dns_queue()
3226 _dns_callback(DNSServiceRef sdRef
,
3227 DNSServiceFlags flags
,
3228 DNSServiceErrorType errorCode
,
3229 _dns_mark_t dnsMark
,
3230 CFTypeRef dnsAddress
, // CFData(struct sockaddr) or CFString(ptr_name)
3234 Boolean restart
= FALSE
;
3235 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
3236 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3238 MUTEX_LOCK(&targetPrivate
->lock
);
3240 if (sdRef
!= targetPrivate
->dnsTarget
) {
3241 // if this DNSServiceRef is no longer associated with the target
3242 MUTEX_UNLOCK(&targetPrivate
->lock
);
3246 switch (errorCode
) {
3247 case kDNSServiceErr_NoError
:
3248 if (dnsAddress
!= NULL
) {
3249 CFMutableArrayRef addresses
;
3252 _dns_mark(target
, dnsMark
);
3254 if (targetPrivate
->dnsAddresses
!= NULL
) {
3255 if (isA_CFArray(targetPrivate
->dnsAddresses
)) {
3256 addresses
= CFArrayCreateMutableCopy(NULL
, 0, targetPrivate
->dnsAddresses
);
3258 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3261 CFRelease(targetPrivate
->dnsAddresses
);
3262 targetPrivate
->dnsAddresses
= NULL
;
3264 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3267 i
= CFArrayGetFirstIndexOfValue(addresses
,
3268 CFRangeMake(0, CFArrayGetCount(addresses
)),
3270 if (flags
& kDNSServiceFlagsAdd
) {
3272 if (i
== kCFNotFound
) {
3273 CFArrayAppendValue(addresses
, dnsAddress
);
3275 #ifdef HANDLE_RMV_REQUESTS
3278 if (i
!= kCFNotFound
) {
3279 CFArrayRemoveValueAtIndex(addresses
, i
);
3281 #endif // HANDLE_RMV_REQUESTS
3284 if (CFArrayGetCount(addresses
) > 0) {
3285 targetPrivate
->dnsAddresses
= addresses
;
3286 targetPrivate
->dnsError
= NETDB_SUCCESS
;
3288 // if host not found
3289 targetPrivate
->dnsAddresses
= CFRetain(kCFNull
);
3290 targetPrivate
->dnsError
= EAI_NONAME
;
3291 CFRelease(addresses
);
3296 case kDNSServiceErr_BadParam
:
3297 _dns_mark(target
, MARK_ERROR
);
3299 if (targetPrivate
->dnsAddresses
!= NULL
) {
3300 CFRelease(targetPrivate
->dnsAddresses
);
3302 targetPrivate
->dnsAddresses
= CFRetain(kCFNull
);
3303 targetPrivate
->dnsError
= EAI_NONAME
;
3305 case kDNSServiceErr_NoSuchRecord
:
3306 _dns_mark(target
, dnsMark
);
3308 if (targetPrivate
->dnsAddresses
== NULL
) {
3309 targetPrivate
->dnsAddresses
= CFRetain(kCFNull
);
3310 targetPrivate
->dnsError
= EAI_NONAME
;
3313 case kDNSServiceErr_Timeout
:
3314 _dns_mark(target
, MARK_TIMEOUT
);
3316 if (targetPrivate
->dnsAddresses
== NULL
) {
3317 targetPrivate
->dnsAddresses
= CFRetain(kCFNull
);
3318 targetPrivate
->dnsError
= EAI_NONAME
;
3322 SCLog(TRUE
, LOG_ERR
,
3323 CFSTR("%sSCNetworkReachability _dns_callback w/error=%d (n=%d)"),
3324 targetPrivate
->log_prefix
,
3326 targetPrivate
->dnsFailures
+ 1);
3328 case kDNSServiceErr_ServiceNotRunning
:
3329 _dns_mark(target
, MARK_ERROR
);
3331 // bump per-target failure count
3332 failures
= ++targetPrivate
->dnsFailures
;
3334 // Check to see if we've seen too many failures for this target
3336 // if so, there's little point in retrying over
3337 // and over again so let's just return an error
3339 if (targetPrivate
->dnsAddresses
!= NULL
) {
3340 CFRelease(targetPrivate
->dnsAddresses
);
3342 targetPrivate
->dnsAddresses
= CFRetain(kCFNull
);
3343 targetPrivate
->dnsError
= EAI_NONAME
;
3344 } else if (targetPrivate
->dnsGeneration
== dnsGeneration
) {
3345 // if not, then "mDNSResponder" crashed or some
3346 // other/unexpected error occurred. In this
3347 // case, we'll try again with a clean slate and
3348 // restart all requests.
3349 if (dnsMain
!= NULL
) {
3350 DNSServiceRefDeallocate(dnsMain
);
3360 // update DNS failure count (and [re-]set to zero if we're OK)
3361 targetPrivate
->dnsFailures
= failures
;
3363 MUTEX_UNLOCK(&targetPrivate
->lock
);
3366 SCLog(TRUE
, LOG_DEBUG
,
3367 CFSTR("%sreconnecting SCNetworkReachability w/\"mDNSResponder\" (%d)"),
3368 targetPrivate
->log_prefix
,
3371 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 500 * NSEC_PER_MSEC
),
3372 _hn_changes_queue(),
3374 dns_refresh_handler();
3377 // and flush the dnsUpdated queue as any DNS results we may have
3378 // accumulated are no longer valid.
3379 if (dnsUpdated
!= NULL
) {
3380 CFRelease(dnsUpdated
);
3386 if (targetPrivate
->dnsHaveTimeout
) {
3387 targetPrivate
->dnsNoAddressesSinceLastTimeout
= TRUE
;
3388 } else if (targetPrivate
->dnsNoAddressesSinceLastTimeout
&&
3389 isA_CFArray(targetPrivate
->dnsAddresses
) &&
3390 CFArrayGetCount(targetPrivate
->dnsAddresses
) > 0)
3392 targetPrivate
->dnsNoAddressesSinceLastTimeout
= FALSE
;
3395 // the "more coming" flag applies to DNSService callouts for any/all
3396 // hosts that are being watched so we need to keep track of the targets
3397 // we have updated. When we [finally] have the last callout then we
3398 // push our notifications for all of the updated targets.
3400 if (dnsUpdated
== NULL
) {
3401 dnsUpdated
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
3403 CFSetAddValue(dnsUpdated
, target
);
3405 if (!(flags
& kDNSServiceFlagsMoreComing
)) {
3406 CFSetApplyFunction(dnsUpdated
, _dns_notify
, NULL
);
3407 CFRelease(dnsUpdated
);
3416 * _dns_getaddrinfo_callback
3418 * Called to process [m]DNS query updates
3419 * - caller must be running on the _dns_queue()
3422 _dns_getaddrinfo_callback(DNSServiceRef sdRef
,
3423 DNSServiceFlags flags
,
3424 uint32_t interfaceIndex
,
3425 DNSServiceErrorType errorCode
,
3426 const char *hostname
,
3427 const struct sockaddr
*address
,
3431 CFDataRef dnsAddress
= NULL
;
3432 _dns_mark_t dnsMark
= MARK_NONE
;
3434 if (address
!= NULL
) {
3435 switch (errorCode
) {
3436 case kDNSServiceErr_NoError
:
3437 dnsAddress
= CFDataCreate(NULL
, (void *)address
, address
->sa_len
);
3438 // ... and fall through
3439 case kDNSServiceErr_NoSuchRecord
:
3440 switch (address
->sa_family
) {
3442 dnsMark
= MARK_HAVE_V4
;
3445 dnsMark
= MARK_HAVE_V6
;
3454 _dns_callback(sdRef
, flags
, errorCode
, dnsMark
, dnsAddress
, context
);
3456 if (dnsAddress
!= NULL
) {
3457 CFRelease(dnsAddress
);
3465 _dns_copy_domain_name(const uint8_t *rdata
, uint16_t rdlen
)
3467 CFMutableStringRef domain
;
3468 const uint8_t *label
;
3471 domain
= CFStringCreateMutable(NULL
, 0);
3474 label_len
= *(label
++);
3475 while (label_len
!= 0) {
3476 while (label_len
-- > 0) {
3477 uint8_t byte
= *label
++;
3479 if ((byte
== '.') || (byte
== '\\')) {
3481 CFStringAppendFormat(domain
, NULL
, CFSTR("\\%c"), byte
);
3482 } else if (byte
<= ' ') {
3483 CFStringAppendFormat(domain
, NULL
, CFSTR("\\%c%c%c"),
3485 '0' + ((byte
/ 10) % 10),
3488 CFStringAppendFormat(domain
, NULL
, CFSTR("%c"), byte
);
3492 label_len
= *(label
++);
3493 if (label_len
!= 0) {
3494 CFStringAppendFormat(domain
, NULL
, CFSTR("."));
3503 _dns_queryrecord_callback(DNSServiceRef sdRef
,
3504 DNSServiceFlags flags
,
3505 uint32_t interfaceIndex
,
3506 DNSServiceErrorType errorCode
,
3507 const char *fullname
,
3515 _dns_mark_t dnsMark
= MARK_NONE
;
3516 CFStringRef dnsPTRName
= NULL
;
3517 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
3518 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3520 // for now, we only support using DNSServiceQueryRecord for PTR queries
3521 assert(targetPrivate
->type
== reachabilityTypePTR
);
3523 if (rdata
!= NULL
) {
3524 switch (errorCode
) {
3525 case kDNSServiceErr_NoError
:
3526 if (rrtype
== kDNSServiceType_PTR
) {
3527 dnsPTRName
= _dns_copy_domain_name(rdata
, rdlen
);
3529 // ... and fall through
3530 case kDNSServiceErr_NoSuchRecord
:
3531 dnsMark
= MARK_HAVE_PTR
;
3538 _dns_callback(sdRef
, flags
, errorCode
, dnsMark
, dnsPTRName
, context
);
3540 if (dnsPTRName
!= NULL
) {
3541 CFRelease(dnsPTRName
);
3549 enqueueDNSQuery(SCNetworkReachabilityRef target
)
3551 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3553 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3555 // clear DNS flags, mark the query active
3556 targetPrivate
->dnsFlags
= 0;
3557 targetPrivate
->dnsActive
= TRUE
;
3559 // track the DNS resolution time
3560 __mark_operation_start(&targetPrivate
->dnsQueryStart
, &targetPrivate
->dnsQueryEnd
);
3563 dispatch_async(_dns_queue(), ^{
3564 DNSServiceErrorType err
;
3565 const char *fcn
= "???";
3566 DNSServiceRef sdRef
= NULL
;
3568 if (targetPrivate
->dnsTarget
!= NULL
) {
3569 // if already running
3574 // if needed, start interacting with "mDNSResponder"
3575 if (dnsMain
== NULL
) {
3576 err
= DNSServiceCreateConnection(&dnsMain
);
3577 if (err
!= kDNSServiceErr_NoError
) {
3578 SCLog(TRUE
, LOG_ERR
,
3579 CFSTR("%sDNSServiceCreateConnection(&dnsMain) failed, error = %d"),
3580 targetPrivate
->log_prefix
,
3585 err
= DNSServiceSetDispatchQueue(dnsMain
, _dns_queue());
3586 if (err
!= kDNSServiceErr_NoError
) {
3587 SCLog(TRUE
, LOG_ERR
,
3588 CFSTR("%sDNSServiceSetDispatchQueue() failed, error = %d"),
3589 targetPrivate
->log_prefix
,
3591 DNSServiceRefDeallocate(dnsMain
);
3598 // start a query for this target
3601 switch (targetPrivate
->type
) {
3602 case reachabilityTypeName
:
3603 fcn
= "DNSServiceGetAddrInfo";
3604 err
= DNSServiceGetAddrInfo(&sdRef
, // sdRef
3605 kDNSServiceFlagsReturnIntermediates
// flags
3606 | kDNSServiceFlagsShareConnection
3607 | kDNSServiceFlagsSuppressUnusable
3608 | kDNSServiceFlagsTimeout
,
3609 targetPrivate
->if_index
, // interfaceIndex
3611 targetPrivate
->name
, // hostname
3612 _dns_getaddrinfo_callback
, // callback
3613 (void *)target
); // context
3615 case reachabilityTypePTR
:
3616 fcn
= "DNSServiceQueryRecord";
3617 err
= DNSServiceQueryRecord(&sdRef
, // sdRef
3618 kDNSServiceFlagsReturnIntermediates
// flags
3619 | kDNSServiceFlagsShareConnection
3620 | kDNSServiceFlagsSuppressUnusable
3621 | kDNSServiceFlagsTimeout
,
3622 targetPrivate
->if_index
, // interfaceIndex
3623 targetPrivate
->name
, // fullname
3624 kDNSServiceType_PTR
, // rrtype
3625 kDNSServiceClass_IN
, // rrclass
3626 _dns_queryrecord_callback
, // callback
3627 (void *)target
); // context
3630 err
= kDNSServiceErr_Unknown
;
3635 case kDNSServiceErr_NoError
:
3640 SCLog(TRUE
, LOG_ERR
,
3641 CFSTR("%s%s() failed, error = %d (%d)"),
3642 targetPrivate
->log_prefix
,
3648 case kDNSServiceErr_BadParam
:
3649 if (dnsCount
== 0) {
3650 // if this was the first request
3651 DNSServiceRefDeallocate(dnsMain
);
3661 MUTEX_LOCK(&targetPrivate
->lock
);
3663 if (err
== kDNSServiceErr_NoError
) {
3664 targetPrivate
->dnsGeneration
= dnsGeneration
;
3665 targetPrivate
->dnsTarget
= sdRef
;
3667 targetPrivate
->dnsActive
= FALSE
;
3669 // queue up the returned error
3670 dispatch_async(_dns_queue(), ^{
3671 _dns_callback(NULL
, // sdRef
3674 MARK_ERROR
, // dnsMark
3676 (void *)target
); // context
3681 MUTEX_UNLOCK(&targetPrivate
->lock
);
3691 dequeueDNSQuery(SCNetworkReachabilityRef target
)
3693 DNSServiceRef sdRef
;
3694 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3696 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
3698 // terminate the [target] [m]DNS query
3699 sdRef
= targetPrivate
->dnsTarget
;
3700 targetPrivate
->dnsTarget
= NULL
;
3702 // mark the query NOT active
3703 targetPrivate
->dnsActive
= FALSE
;
3705 // don't do anything if the sdRef is not valid
3706 if (sdRef
!= NULL
) {
3709 generation
= targetPrivate
->dnsGeneration
;
3710 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 3LL * NSEC_PER_SEC
),
3713 if (generation
== dnsGeneration
) {
3714 // if we're pointing to the same DNSService
3715 // generation as the main/active session
3716 // deallocate per-target query
3717 DNSServiceRefDeallocate(sdRef
);
3719 if (dnsCount
== 0) {
3720 // if no more queries active
3721 DNSServiceRefDeallocate(dnsMain
);
3731 if (targetPrivate
->dnsAddresses
!= NULL
) {
3732 CFRelease(targetPrivate
->dnsAddresses
);
3733 targetPrivate
->dnsAddresses
= NULL
;
3735 targetPrivate
->dnsError
= NETDB_SUCCESS
;
3742 #pragma mark Synchronous DNS query support
3745 #define SYNC_DNS_QUERY_TIMEOUT_NSEC 35 * NSEC_PER_SEC // 35s
3749 sync_DNS_query_callback(SCNetworkReachabilityRef clone
,
3750 SCNetworkReachabilityFlags cloneFlags
,
3753 dispatch_semaphore_t s
= (dispatch_semaphore_t
)info
;
3755 dispatch_semaphore_signal(s
);
3761 sync_DNS_query(SCNetworkReachabilityRef target
)
3763 SCNetworkReachabilityRef clone
;
3764 SCNetworkReachabilityPrivateRef clonePrivate
;
3765 SCNetworkReachabilityContext context
= { 0, NULL
, NULL
, NULL
, NULL
};
3768 dispatch_semaphore_t s
;
3769 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
3771 clone
= __SCNetworkReachabilityCreateCopy(target
);
3772 if (clone
== NULL
) {
3775 clonePrivate
= (SCNetworkReachabilityPrivateRef
)clone
;
3777 q
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
3778 s
= dispatch_semaphore_create(0);
3780 // start async query
3781 context
.info
= (void *)s
;
3782 SCNetworkReachabilitySetCallback(clone
, sync_DNS_query_callback
, &context
);
3783 SCNetworkReachabilitySetDispatchQueue(clone
, q
);
3785 // wait for reply (or timeout)
3786 ret
= dispatch_semaphore_wait(s
, dispatch_time(DISPATCH_TIME_NOW
,
3787 SYNC_DNS_QUERY_TIMEOUT_NSEC
));
3789 dispatch_sync(_dns_queue(), ^{
3790 // mark as both a timeout *and* an error
3791 _dns_mark(clone
, MARK_TIMEOUT
);
3792 _dns_mark(clone
, MARK_ERROR
);
3794 __mark_operation_end(clone
,
3795 FALSE
, // if successful query
3796 dns_query_mdns_timeout
, // [m]DNS query
3797 &clonePrivate
->dnsQueryStart
, // start time
3798 &clonePrivate
->dnsQueryEnd
); // end time
3800 MUTEX_LOCK(&clonePrivate
->lock
);
3802 // update target info with what's available
3803 if (clonePrivate
->resolvedAddresses
!= NULL
) {
3804 CFRelease(clonePrivate
->resolvedAddresses
);
3805 clonePrivate
->resolvedAddresses
= NULL
;
3807 if ((clonePrivate
->dnsAddresses
!= NULL
) &&
3808 isA_CFArray(clonePrivate
->dnsAddresses
) &&
3809 (CFArrayGetCount(clonePrivate
->dnsAddresses
) > 0)) {
3810 clonePrivate
->resolvedAddresses
= CFArrayCreateMutableCopy(NULL
,
3812 clonePrivate
->dnsAddresses
);
3814 if (clonePrivate
->resolvedAddresses
!= NULL
) {
3815 // if timeout w/partial results
3816 clonePrivate
->resolvedError
= NETDB_SUCCESS
;
3818 // if timeout w/no results
3819 clonePrivate
->resolvedAddresses
= CFRetain(kCFNull
);
3820 clonePrivate
->resolvedError
= EAI_NONAME
;
3823 MUTEX_UNLOCK(&clonePrivate
->lock
);
3828 SCNetworkReachabilitySetDispatchQueue(clone
, NULL
);
3829 SCNetworkReachabilitySetCallback(clone
, NULL
, NULL
);
3832 if (clonePrivate
->resolvedAddresses
!= NULL
) CFRetain(clonePrivate
->resolvedAddresses
);
3833 if (targetPrivate
->resolvedAddresses
!= NULL
) CFRelease(targetPrivate
->resolvedAddresses
);
3834 targetPrivate
->resolvedAddresses
= clonePrivate
->resolvedAddresses
;
3835 targetPrivate
->resolvedError
= clonePrivate
->resolvedError
;
3836 targetPrivate
->resolverFlags
= clonePrivate
->resolverFlags
;
3837 targetPrivate
->cycle
= clonePrivate
->cycle
;
3838 targetPrivate
->dnsFlags
= clonePrivate
->dnsFlags
;
3839 memcpy(&targetPrivate
->info
, &clonePrivate
->info
, sizeof(ReachabilityInfo
));
3840 memcpy(&targetPrivate
->last_notify
, &clonePrivate
->last_notify
, sizeof(ReachabilityInfo
));
3843 dispatch_release(s
);
3850 #pragma mark Network Information support
3853 // Note: protected by _hn_target_queue()
3854 static int network_changed_token
;
3855 static Boolean network_changed_token_valid
= FALSE
;
3859 * nwi_refresh_handler
3861 * Called to notify/update network changed events
3862 * - should be exec'd on the _hn_changes_queue()
3865 nwi_refresh_handler()
3869 __block SCDynamicStoreRef store
= NULL
;
3871 dispatch_sync(_hn_target_queue(), ^{
3872 if (network_changed_token_valid
&& (hn_store
!= NULL
)) {
3873 store
= CFRetain(hn_store
);
3877 if (store
== NULL
) {
3881 // Fake a network change.
3882 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
3883 kSCDynamicStoreDomainState
,
3885 changes
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
3886 __SCNetworkReachabilityHandleChanges(store
, changes
, NULL
);
3896 * nwi_refresh_enable
3898 * Called to monitor for network changes.
3899 * - caller must be running on the _hn_target_queue()
3900 * - passed in queue should be _hn_changes_queue()
3903 nwi_refresh_enable(dispatch_queue_t q
)
3907 status
= notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE_NWI
, // trailing nwi_state_get_notify_key()
3908 &network_changed_token
,
3911 nwi_refresh_handler();
3913 if (status
!= NOTIFY_STATUS_OK
) {
3914 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_dispatch() failed for network changes, status=%u"), status
);
3918 network_changed_token_valid
= TRUE
;
3925 * nwi_refresh_disable
3927 * Called to stop monitoring for network changes
3928 * - caller must be running on the _hn_target_queue()
3931 nwi_refresh_disable()
3933 if (network_changed_token_valid
) {
3934 (void)notify_cancel(network_changed_token
);
3935 network_changed_token_valid
= FALSE
;
3943 #pragma mark Sleep/wake support
3946 #if !TARGET_OS_IPHONE
3948 // Note: protected by _hn_target_queue()
3949 static IOPMConnection power_changed_connection
= NULL
;
3950 static const CFStringRef power_changed_key
= CFSTR("*** EARLY WAKE ***");
3954 * power_refresh_handler
3956 * Called to notify/update power capability changed events
3957 * - should be exec'd on the _hn_changes_queue()
3960 power_refresh_handler(void *param
,
3961 IOPMConnection connection
,
3962 IOPMConnectionMessageToken token
,
3963 IOPMSystemPowerStateCapabilities capabilities
)
3967 __block SCDynamicStoreRef store
= NULL
;
3969 dispatch_sync(_hn_target_queue(), ^{
3970 if ((power_changed_connection
!= NULL
) && (hn_store
!= NULL
)) {
3971 store
= CFRetain(hn_store
);
3975 if (store
== NULL
) {
3979 // check for [relevant] changes
3980 change
= ((power_capabilities
^ capabilities
) & POWER_CAPABILITIES_NETWORK
) != 0;
3982 // update capabilities
3983 power_capabilities
= capabilities
;
3988 // fake a network change.
3989 changes
= CFArrayCreate(NULL
, (const void **)&power_changed_key
, 1, &kCFTypeArrayCallBacks
);
3990 __SCNetworkReachabilityHandleChanges(store
, changes
, NULL
);
3994 ret
= IOPMConnectionAcknowledgeEvent(connection
, token
);
3995 if (ret
!= kIOReturnSuccess
) {
3996 SCLog(TRUE
, LOG_ERR
, CFSTR("IOPMConnectionAcknowledgeEvent failed, 0x%08x"), ret
);
4005 * power_refresh_enable
4007 * Called to monitor power changes.
4008 * - caller must be running on the _hn_target_queue()
4009 * - passed in queue should be _hn_changes_queue()
4012 power_refresh_enable(dispatch_queue_t q
)
4014 IOPMConnection connection
= NULL
;
4017 ret
= IOPMConnectionCreate(CFSTR("com.apple.SCNetworkReachability"),
4018 kIOPMEarlyWakeNotification
| kIOPMSleepWakeInterest
,
4020 if (ret
!= kIOReturnSuccess
) {
4021 SCLog(TRUE
, LOG_ERR
, CFSTR("IOPMConnectionCreate failed, 0x%08x"), ret
);
4025 ret
= IOPMConnectionSetNotification(connection
, NULL
, power_refresh_handler
);
4026 if (ret
!= kIOReturnSuccess
) {
4027 SCLog(TRUE
, LOG_ERR
, CFSTR("IOPMConnectionSetNotification failed, 0x%08x"), ret
);
4031 power_changed_connection
= connection
;
4032 IOPMConnectionSetDispatchQueue(connection
, q
);
4033 power_capabilities
= IOPMConnectionGetSystemCapabilities();
4039 if (connection
!= NULL
) {
4040 IOPMConnectionRelease(connection
);
4048 power_refresh_disable()
4050 if (power_changed_connection
!= NULL
) {
4051 IOPMConnectionSetDispatchQueue(power_changed_connection
, NULL
);
4052 IOPMConnectionRelease(power_changed_connection
);
4053 power_changed_connection
= NULL
;
4059 #endif // !TARGET_OS_IPHONE
4067 #pragma mark OnDemand
4071 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target
,
4072 CFDictionaryRef
*userOptions
)
4074 SCNetworkServiceRef service
= NULL
;
4075 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4077 if (!isA_SCNetworkReachability(target
)) {
4078 _SCErrorSet(kSCStatusInvalidArgument
);
4082 if (targetPrivate
->onDemandServiceID
!= NULL
) {
4083 service
= _SCNetworkServiceCopyActive(NULL
, targetPrivate
->onDemandServiceID
);
4086 if (userOptions
!= NULL
) {
4087 if (targetPrivate
->onDemandName
!= NULL
) {
4088 *userOptions
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4089 CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
);
4091 *userOptions
= NULL
;
4102 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer
,
4103 SCNetworkReachabilityFlags onDemandFlags
,
4106 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
4107 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4109 MUTEX_LOCK(&targetPrivate
->lock
);
4111 if (!targetPrivate
->scheduled
) {
4112 // if not currently scheduled
4113 MUTEX_UNLOCK(&targetPrivate
->lock
);
4117 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sOnDemand \"server\" status changed (now 0x%08x)"),
4118 targetPrivate
->log_prefix
,
4121 if (targetPrivate
->type
== reachabilityTypeName
) {
4122 // make sure that we resolve the name again
4123 targetPrivate
->needResolve
= TRUE
;
4126 __SCNetworkReachabilityUpdate(target
);
4128 MUTEX_UNLOCK(&targetPrivate
->lock
);
4135 __SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef store_info
,
4136 SCNetworkReachabilityRef target
,
4137 Boolean onDemandRetry
,
4138 SCNetworkReachabilityFlags
*flags
)
4140 SCNetworkConnectionRef connection
= NULL
;
4141 SCNetworkConnectionType connectionType
= kSCNetworkConnectionTypeUnknown
;
4142 Boolean isAppLayerVPN
= FALSE
;
4143 Boolean isOnDemandService
= FALSE
;
4145 CFStringRef onDemandRemoteAddress
= NULL
;
4146 CFStringRef onDemandServiceID
= NULL
;
4147 SCNetworkConnectionStatus onDemandStatus
= kSCNetworkConnectionInvalid
;
4148 CFMutableDictionaryRef selectOptions
= NULL
;
4149 Boolean success
= FALSE
;
4150 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4152 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4154 if (targetPrivate
->onDemandName
== NULL
) {
4155 targetPrivate
->onDemandName
= CFStringCreateWithCString(NULL
, targetPrivate
->name
, kCFStringEncodingUTF8
);
4159 * check if an OnDemand VPN configuration matches the name.
4162 connection
= SCNetworkConnectionCreate(kCFAllocatorDefault
, NULL
, NULL
);
4163 if (connection
== NULL
) {
4167 /* set select options */
4168 selectOptions
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4169 if (selectOptions
== NULL
) {
4173 CFDictionaryAddValue(selectOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
);
4174 CFDictionaryAddValue(selectOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
, onDemandRetry
? kCFBooleanTrue
: kCFBooleanFalse
);
4175 CFDictionaryAddValue(selectOptions
, kSCNetworkConnectionSelectionOptionNoUserPrefs
, kCFBooleanTrue
);
4177 /* select service. May be On Demand or App Layer VPN */
4178 if (!SCNetworkConnectionSelectServiceWithOptions(connection
, selectOptions
)) {
4182 /* get reachability flags (of VPN server) */
4183 (void) SCNetworkConnectionGetReachabilityInfo(connection
, flags
, NULL
);
4185 connectionType
= SCNetworkConnectionGetType(connection
);
4186 if (connectionType
== kSCNetworkConnectionTypeAppLayerVPN
) {
4187 isAppLayerVPN
= TRUE
;
4190 /* get on-demand info */
4191 onDemandServiceID
= SCNetworkConnectionCopyServiceID(connection
);
4192 if (SCNetworkConnectionCopyOnDemandInfo(connection
, &onDemandRemoteAddress
, &onDemandStatus
)) {
4193 if (onDemandRemoteAddress
!= NULL
) {
4194 isOnDemandService
= TRUE
;
4199 /* handle non-OnDemand App Layer VPN */
4200 if (isAppLayerVPN
&& !isOnDemandService
) {
4201 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status * = 0x%08x (App Layer VPN)"),
4202 targetPrivate
->log_prefix
,
4204 if (*flags
& kSCNetworkReachabilityFlagsReachable
) {
4205 // if VPN "server" is reachable
4207 if (!(*flags
& kSCNetworkReachabilityFlagsTransientConnection
)) {
4208 // start w/clean flags if not already layered on a transient network
4209 *flags
= kSCNetworkReachabilityFlagsReachable
;
4212 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
4213 if (onDemandStatus
!= kSCNetworkConnectionConnected
) {
4214 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4217 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status = isReachable%s"),
4218 (onDemandStatus
!= kSCNetworkConnectionConnected
)
4219 ? " (after App Layer connect)" : "",
4220 targetPrivate
->log_prefix
);
4227 if (!_SC_CFEqual(targetPrivate
->onDemandRemoteAddress
, onDemandRemoteAddress
) ||
4228 !_SC_CFEqual(targetPrivate
->onDemandServiceID
, onDemandServiceID
)) {
4229 if (targetPrivate
->onDemandRemoteAddress
!= NULL
) {
4230 CFRelease(targetPrivate
->onDemandRemoteAddress
);
4231 targetPrivate
->onDemandRemoteAddress
= NULL
;
4234 if (targetPrivate
->onDemandServer
!= NULL
) {
4235 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, NULL
, NULL
);
4236 if (targetPrivate
->dispatchQueue
!= NULL
) {
4238 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
4239 } else if (targetPrivate
->rls
!= NULL
) {
4244 n
= CFArrayGetCount(targetPrivate
->rlList
);
4245 for (i
= 0; i
< n
; i
+= 3) {
4246 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
4247 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
4249 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, TRUE
);
4253 CFRelease(targetPrivate
->onDemandServer
);
4254 targetPrivate
->onDemandServer
= NULL
;
4257 if (targetPrivate
->onDemandServiceID
!= NULL
) {
4258 CFRelease(targetPrivate
->onDemandServiceID
);
4259 targetPrivate
->onDemandServiceID
= NULL
;
4264 if (onDemandStatus
!= kSCNetworkConnectionConnected
) {
4266 * if we have a VPN configuration matching the name *and* we need to
4267 * bring the VPN up. Combine our flags with those of the VPN server.
4269 if (targetPrivate
->onDemandServer
== NULL
) {
4270 SCNetworkReachabilityPrivateRef demandPrivate
;
4271 CFMutableDictionaryRef options
;
4273 options
= CFDictionaryCreateMutable(NULL
,
4275 &kCFTypeDictionaryKeyCallBacks
,
4276 &kCFTypeDictionaryValueCallBacks
);
4277 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, onDemandRemoteAddress
);
4278 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandBypass
, kCFBooleanTrue
);
4279 if (targetPrivate
->serverBypass
) {
4280 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionServerBypass
, kCFBooleanTrue
);
4282 targetPrivate
->onDemandServer
= SCNetworkReachabilityCreateWithOptions(NULL
, options
);
4285 // indent OnDemand target
4286 demandPrivate
= (SCNetworkReachabilityPrivateRef
)targetPrivate
->onDemandServer
;
4287 strlcat(demandPrivate
->log_prefix
, ".... ", sizeof(demandPrivate
->log_prefix
));
4289 if (targetPrivate
->scheduled
) {
4290 SCNetworkReachabilityContext context
= { 0, NULL
, CFRetain
, CFRelease
, CFCopyDescription
};
4292 context
.info
= (void *)target
;
4293 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
,
4294 __SCNetworkReachabilityOnDemandCheckCallback
,
4297 // schedule server reachability to match that of the target
4298 if (targetPrivate
->dispatchQueue
!= NULL
) {
4299 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, targetPrivate
->dispatchQueue
, TRUE
);
4304 n
= CFArrayGetCount(targetPrivate
->rlList
);
4305 for (i
= 0; i
< n
; i
+= 3) {
4306 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
4307 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
4309 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, NULL
, TRUE
);
4315 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s status * = 0x%08x"),
4316 targetPrivate
->log_prefix
,
4320 if ((*flags
& kSCNetworkReachabilityFlagsReachable
) && !(*flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
4321 // if VPN "server" is [still] reachable
4323 if (!(*flags
& kSCNetworkReachabilityFlagsTransientConnection
)) {
4324 // start w/clean flags if not already layered on a transient network
4325 *flags
= kSCNetworkReachabilityFlagsReachable
;
4328 *flags
|= kSCNetworkReachabilityFlagsTransientConnection
;
4329 *flags
|= kSCNetworkReachabilityFlagsConnectionRequired
;
4330 *flags
|= kSCNetworkReachabilityFlagsConnectionOnDemand
;
4332 // set 'InterventionRequired' if the OnDemand connection is paused
4333 if (SCNetworkConnectionIsOnDemandSuspended(connection
)) {
4334 *flags
|= kSCNetworkReachabilityFlagsInterventionRequired
;
4338 SCLog(TRUE
, LOG_INFO
, CFSTR("%s service * = %@"),
4339 targetPrivate
->log_prefix
,
4341 SCLog(TRUE
, LOG_INFO
, CFSTR("%s status = isReachable (after OnDemand connect)"),
4342 targetPrivate
->log_prefix
);
4349 if (onDemandRemoteAddress
!= NULL
) {
4350 if (targetPrivate
->onDemandRemoteAddress
== NULL
) {
4351 targetPrivate
->onDemandRemoteAddress
= CFRetain(onDemandRemoteAddress
);
4355 if (onDemandServiceID
!= NULL
) {
4356 if (targetPrivate
->onDemandServiceID
== NULL
) {
4357 targetPrivate
->onDemandServiceID
= CFRetain(onDemandServiceID
);
4364 if (onDemandServiceID
!= NULL
) {
4365 CFRelease(onDemandServiceID
);
4367 if (onDemandRemoteAddress
!= NULL
) {
4368 CFRelease(onDemandRemoteAddress
);
4370 if (connection
!= NULL
) {
4371 CFRelease(connection
);
4373 if (selectOptions
!= NULL
) {
4374 CFRelease(selectOptions
);
4381 * OnDemand configuration handling
4385 * 1. We have a "contract" with mDNSResponder that for EVERY network
4386 * or DNS configuration change that should warrant our [re-]starting
4387 * a query, mDNSResponder will acknowledge the latest DNS configuration.
4389 * 2. IPMonitor also posts a notification AFTER every network or DNS
4390 * configuration change.
4392 * 3. We use IPMonitor's "trailing edge" as a signal to restart any
4397 // Note: protected by _hn_target_queue()
4398 static int onDemand_refresh_token
;
4399 static Boolean onDemand_refresh_token_valid
= FALSE
;
4403 * onDemand_refresh_handler
4405 * Called to notify/update all SCNetworkReachability targets of
4407 * - should be exec'd on the _hn_changes_queue()
4410 onDemand_refresh_handler()
4414 __block SCDynamicStoreRef store
= NULL
;
4416 dispatch_sync(_hn_target_queue(), ^{
4417 if (onDemand_refresh_token_valid
&& (hn_store
!= NULL
)) {
4418 store
= CFRetain(hn_store
);
4422 if (store
== NULL
) {
4426 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
4427 kSCDynamicStoreDomainState
,
4429 changes
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
);
4430 __SCNetworkReachabilityHandleChanges(store
, changes
, NULL
);
4440 * onDemand_refresh_enable
4442 * Called to monitor for OnDemand changes.
4443 * - caller must be running on the _hn_target_queue()
4446 onDemand_refresh_enable(dispatch_queue_t q
)
4450 status
= notify_register_dispatch(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY
,
4451 &onDemand_refresh_token
,
4454 onDemand_refresh_handler();
4456 if (status
!= NOTIFY_STATUS_OK
) {
4457 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_dispatch() failed, status=%u"), status
);
4461 onDemand_refresh_token_valid
= TRUE
;
4468 * onDemand_refresh_disable
4470 * Called to stop monitoring for OnDemand changes
4471 * - caller must be running on the _hn_target_queue()
4474 onDemand_refresh_disable()
4476 (void)notify_cancel(onDemand_refresh_token
);
4477 onDemand_refresh_token_valid
= FALSE
;
4485 #pragma mark Reachability Flags
4489 __SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef store_info
,
4490 SCNetworkReachabilityRef target
,
4491 ReachabilityInfo
*reach_info
,
4494 CFMutableArrayRef addresses
= NULL
;
4495 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4496 ReachabilityInfo my_info
= NOT_REACHABLE
;
4499 MUTEX_ASSERT_HELD(&targetPrivate
->lock
);
4501 _reach_set(reach_info
, &NOT_REACHABLE
, reach_info
->cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
);
4503 if (!isA_SCNetworkReachability(target
)) {
4504 _SCErrorSet(kSCStatusInvalidArgument
);
4508 #if TARGET_OS_IPHONE
4509 if (isReachabilityTypeName(targetPrivate
->type
) &&
4511 pthread_is_threaded_np() &&
4512 pthread_main_np()) {
4513 SCLog(TRUE
, LOG_WARNING
, CFSTR("Warning: sync SCNetworkReachability (by-name) query on main thread"));
4515 #endif // TARGET_OS_IPHONE
4517 if (!targetPrivate
->serverBypass
) {
4518 if (!targetPrivate
->serverActive
) {
4520 ok
= __SCNetworkReachabilityServer_targetAdd(target
);
4522 targetPrivate
->serverBypass
= TRUE
;
4526 if (targetPrivate
->serverActive
) {
4527 ok
= __SCNetworkReachabilityServer_targetStatus(target
);
4529 SCLog(TRUE
, LOG_DEBUG
,
4530 CFSTR("__SCNetworkReachabilityGetFlags _targetStatus() failed"));
4531 _SCErrorSet(kSCStatusFailed
);
4535 targetPrivate
->cycle
= targetPrivate
->serverInfo
.cycle
;
4536 _reach_set(&my_info
,
4537 &targetPrivate
->serverInfo
,
4538 targetPrivate
->serverInfo
.cycle
,
4539 targetPrivate
->if_index
,
4540 targetPrivate
->if_name
);
4546 switch (targetPrivate
->type
) {
4547 case reachabilityTypeAddress
:
4548 case reachabilityTypeAddressPair
: {
4550 * Check "local" address
4552 if (targetPrivate
->localAddress
!= NULL
) {
4554 * Check "local" address
4556 ok
= checkAddress(store_info
,
4557 targetPrivate
->localAddress
,
4558 targetPrivate
->if_index
,
4560 targetPrivate
->log_prefix
);
4562 goto done2
; /* not today */
4565 if (!(my_info
.flags
& kSCNetworkReachabilityFlagsIsLocalAddress
)) {
4566 goto done2
; /* not reachable, non-"local" address */
4571 * Check "remote" address
4573 if ((targetPrivate
->remoteAddress
!= NULL
) &&
4574 (targetPrivate
->localAddress
!= targetPrivate
->remoteAddress
)) {
4576 * in cases where we have different "local" and "remote" addresses
4577 * we need to re-initialize the to-be-returned flags.
4579 my_info
= NOT_REACHABLE
;
4582 * Check "remote" address
4584 ok
= checkAddress(store_info
,
4585 targetPrivate
->remoteAddress
,
4586 targetPrivate
->if_index
,
4588 targetPrivate
->log_prefix
);
4590 goto done2
; /* not today */
4598 case reachabilityTypeName
:
4599 case reachabilityTypePTR
: {
4601 int ns_dns_config
= -1;
4602 SCNetworkReachabilityFlags ns_flags
= 0;
4603 uint32_t ns_if_index
= 0;
4605 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
4606 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
4607 /* if resolved or an error had been detected */
4609 /* if not an async request */
4610 goto checkResolvedAddresses
;
4611 } else if (targetPrivate
->dnsActive
) {
4612 /* if [m]DNS query active */
4613 goto checkResolvedAddresses
;
4614 } else if (!targetPrivate
->needResolve
) {
4616 * if this is an async request (i.e. someone is watching the reachability
4617 * of this target), if no query active, and if no query is needed
4619 goto checkResolvedAddresses
;
4623 if (!targetPrivate
->onDemandBypass
) {
4625 SCNetworkReachabilityFlags onDemandFlags
= 0;
4628 * before we attempt our initial DNS query, check if there is
4629 * an OnDemand configuration that we should be using.
4631 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, FALSE
, &onDemandFlags
);
4633 /* if OnDemand connection is needed */
4634 my_info
.flags
= onDemandFlags
;
4639 targetPrivate
->dnsBlocked
= FALSE
;
4641 /* update the reachability of the DNS servers */
4642 _SC_R_updateResolverReachability(store_info
,
4644 &targetPrivate
->haveDNS
,
4645 targetPrivate
->name
,
4646 targetPrivate
->if_index
,
4649 targetPrivate
->log_prefix
);
4652 // save resolver reachability flags
4653 targetPrivate
->resolverFlags
= ns_flags
;
4655 if (rankReachability(ns_flags
) < 2) {
4657 * if DNS servers are not (or are no longer) reachable, set
4658 * flags based on the availability of configured (but not
4662 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
4663 targetPrivate
->log_prefix
);
4665 if (!targetPrivate
->dnsBlocked
) {
4666 ok
= checkAddress(store_info
,
4668 targetPrivate
->if_index
,
4670 targetPrivate
->log_prefix
);
4672 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sNo available networks"),
4673 targetPrivate
->log_prefix
);
4677 // if not checking "available" networks
4678 my_info
.flags
= ns_flags
;
4679 my_info
.if_index
= ns_if_index
;
4682 if (async
&& targetPrivate
->scheduled
) {
4684 * return "host not found", set flags appropriately,
4685 * and schedule notification.
4687 __SCNetworkReachabilitySetResolvedError(target
, EAI_NONAME
);
4688 my_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
4690 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sno DNS servers are reachable"),
4691 targetPrivate
->log_prefix
);
4692 __SCNetworkReachabilityUpdate(target
);
4698 if (targetPrivate
->resolverBypass
) {
4699 if (targetPrivate
->haveDNS
) {
4701 * if we are not resolving the name, and if we have
4702 * one or more DNS resolvers, then return flags that
4703 * reflect the reachability of the resolvers (and
4704 * not the actual name).
4706 my_info
.flags
= ns_flags
;
4707 my_info
.if_index
= ns_if_index
;
4713 /* for async requests we return the last known status */
4714 my_info
= targetPrivate
->info
;
4716 if (targetPrivate
->dnsActive
) {
4717 /* if [m]DNS query active */
4718 if (_sc_debug
&& !targetPrivate
->quiet
) {
4719 SCLog(TRUE
, LOG_INFO
,
4720 CFSTR("%swaiting for DNS reply"),
4721 targetPrivate
->log_prefix
);
4723 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
4724 /* updated reachability based on the previous reply */
4725 goto checkResolvedAddresses
;
4730 SCLog(_sc_debug
, LOG_INFO
,
4731 CFSTR("%sstart DNS query for name = %s"),
4732 targetPrivate
->log_prefix
,
4733 targetPrivate
->name
);
4736 * initiate an DNS query w/DNSServiceGetAddrInfo
4738 enqueueDNSQuery(target
);
4742 SCLog(_sc_debug
, LOG_INFO
,
4743 CFSTR("%scheckName(%s)"),
4744 targetPrivate
->log_prefix
,
4745 targetPrivate
->name
);
4748 * OK, all of the DNS name servers are available. Let's
4749 * resolve the nodename into an address.
4751 sync_DNS_query(target
);
4753 if (!(targetPrivate
->dnsHaveTimeout
&& targetPrivate
->dnsHaveError
)) {
4754 // if target reach info is valid
4755 memcpy(reach_info
, &targetPrivate
->info
, sizeof(ReachabilityInfo
));
4759 if (addresses
!= NULL
) CFRelease(addresses
);
4760 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
4762 checkResolvedAddresses
:
4765 * We first assume that the requested host is NOT available.
4766 * Then, check each address for accessibility and return the
4767 * best status available.
4769 my_info
= NOT_REACHABLE
;
4771 if ((targetPrivate
->type
== reachabilityTypeName
) && isA_CFArray(addresses
)) {
4773 CFIndex n
= CFArrayGetCount(addresses
);
4774 struct sockaddr
*sa
;
4776 for (i
= 0; i
< n
; i
++) {
4777 ReachabilityInfo ns_info
= NOT_REACHABLE
;
4779 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
4781 ok
= checkAddress(store_info
,
4783 targetPrivate
->if_index
,
4785 targetPrivate
->log_prefix
);
4787 goto done2
; /* not today */
4790 if (rankReachability(ns_info
.flags
) > rankReachability(my_info
.flags
)) {
4791 /* return the best case result */
4793 if (rankReachability(my_info
.flags
) == 2) {
4794 /* can't get any better than REACHABLE */
4801 for (i
++; i
< n
; i
++) {
4802 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
4803 log_address("skipAddress",
4805 targetPrivate
->if_index
,
4806 targetPrivate
->log_prefix
);
4809 } else if ((targetPrivate
->type
== reachabilityTypePTR
) && isA_CFArray(addresses
)) {
4811 CFIndex n
= CFArrayGetCount(addresses
);
4813 my_info
= NOT_REACHABLE
;
4815 for (i
= 0; i
< n
; i
++) {
4817 my_info
.flags
= kSCNetworkReachabilityFlagsReachable
;
4821 CFStringRef ptrName
;
4823 ptrName
= CFArrayGetValueAtIndex(addresses
, i
);
4824 SCLog(TRUE
, LOG_INFO
, CFSTR("%sPTR name(%@)"),
4825 targetPrivate
->log_prefix
,
4830 if ((error
== EAI_NONAME
)
4831 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
4832 || (error
== EAI_NODATA
)
4836 * the target host name could not be resolved
4838 if (!targetPrivate
->onDemandBypass
) {
4840 SCNetworkReachabilityFlags onDemandFlags
= 0;
4843 * our initial DNS query failed, check again to see if there
4844 * there is an OnDemand configuration that we should be using.
4846 onDemand
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, TRUE
, &onDemandFlags
);
4848 /* if OnDemand connection is needed */
4849 my_info
.flags
= onDemandFlags
;
4855 if (!targetPrivate
->haveDNS
) {
4857 * No DNS servers are defined. Set flags based on
4858 * the availability of configured (but not active)
4861 ok
= checkAddress(store_info
,
4863 targetPrivate
->if_index
,
4865 targetPrivate
->log_prefix
);
4867 goto done2
; /* not today */
4870 if ((my_info
.flags
& kSCNetworkReachabilityFlagsReachable
) &&
4871 (my_info
.flags
& kSCNetworkReachabilityFlagsConnectionRequired
)) {
4873 * Since we might pick up a set of DNS servers when this connection
4874 * is established, don't reply with a "HOST NOT FOUND" error just yet.
4879 /* Host not found, not reachable! */
4880 my_info
= NOT_REACHABLE
;
4892 _reach_set(reach_info
, &my_info
, targetPrivate
->cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
);
4896 if (addresses
!= NULL
) CFRelease(addresses
);
4901 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target
)
4905 ReachabilityStoreInfo store_info
;
4906 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4908 if (!isA_SCNetworkReachability(target
)) {
4909 _SCErrorSet(kSCStatusInvalidArgument
);
4913 ReachabilityStoreInfo_init(&store_info
);
4915 MUTEX_LOCK(&targetPrivate
->lock
);
4917 if (targetPrivate
->scheduled
) {
4918 // if being watched, return the last known (and what should be current) status
4923 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
);
4927 /* Only return the if_index if the connection is reachable not for reachable connection
4928 * required etc ... */
4929 if (ok
&& rankReachability(targetPrivate
->info
.flags
) == 2) {
4930 if_index
= targetPrivate
->info
.if_index
;
4933 MUTEX_UNLOCK(&targetPrivate
->lock
);
4934 ReachabilityStoreInfo_free(&store_info
);
4940 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
4941 SCNetworkReachabilityFlags
*flags
)
4944 ReachabilityStoreInfo store_info
;
4945 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
4947 if (!isA_SCNetworkReachability(target
)) {
4948 _SCErrorSet(kSCStatusInvalidArgument
);
4952 ReachabilityStoreInfo_init(&store_info
);
4954 MUTEX_LOCK(&targetPrivate
->lock
);
4956 if (targetPrivate
->scheduled
) {
4957 // if being watched, return the last known (and what should be current) status
4958 *flags
= targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsMask
;
4960 if (isReachabilityTypeName(targetPrivate
->type
) && targetPrivate
->dnsNoAddressesSinceLastTimeout
) {
4961 targetPrivate
->needResolve
= TRUE
;
4962 ReachabilityInfo tmp_reach_info
= NOT_REACHABLE
;
4963 __SCNetworkReachabilityGetFlags(&store_info
, target
, &tmp_reach_info
, TRUE
);
4970 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
);
4972 SCLog(TRUE
, LOG_INFO
, CFSTR("%s flags = 0x%08x"), targetPrivate
->log_prefix
, targetPrivate
->info
.flags
);
4975 *flags
= targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsMask
;
4979 MUTEX_UNLOCK(&targetPrivate
->lock
);
4980 ReachabilityStoreInfo_free(&store_info
);
4986 #pragma mark Notifications
4990 * __SCNetworkReachabilityHandleChanges
4992 * Called to process network configuration changes and determine
4993 * if a reachability notification is warranted.
4994 * - should be exec'd on the _hn_changes_queue()
4997 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store
,
4998 CFArrayRef changedKeys
,
5001 Boolean dnsConfigChanged
= FALSE
;
5003 Boolean forcedChange
= FALSE
;
5007 CFIndex nGlobals
= 0;
5009 Boolean neChanged
= FALSE
;
5010 Boolean networkConfigChanged
= FALSE
;
5012 Boolean onDemandConfigChanged
= FALSE
;
5013 #if !TARGET_OS_IPHONE
5014 Boolean powerStatusChanged
= FALSE
;
5015 #endif // !TARGET_OS_IPHONE
5016 ReachabilityStoreInfo store_info
;
5017 const void * targets_q
[N_QUICK
];
5018 const void ** targets
= targets_q
;
5019 __block CFSetRef watchers
= NULL
;
5021 nChanges
= CFArrayGetCount(changedKeys
);
5022 if (nChanges
== 0) {
5028 dispatch_sync(_hn_target_queue(), ^{
5029 /* grab the currently watched targets */
5030 if (hn_targets
!= NULL
) {
5031 watchers
= CFSetCreateCopy(NULL
, hn_targets
);
5035 nTargets
= (watchers
!= NULL
) ? CFSetGetCount(watchers
) : 0;
5036 if (nTargets
== 0) {
5037 /* if no addresses being monitored */
5041 /* grab the current time */
5042 (void)gettimeofday(&now
, NULL
);
5044 #if !TARGET_OS_IPHONE
5045 match
= CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), power_changed_key
);
5047 /* handle "early" wake notification */
5049 powerStatusChanged
= TRUE
;
5051 #endif // !TARGET_OS_IPHONE
5053 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5054 kSCDynamicStoreDomainState
,
5056 match
= CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
);
5060 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
5063 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
5064 kSCDynamicStoreDomainState
,
5066 match
= CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
);
5070 onDemandConfigChanged
= TRUE
; /* the OnDemand configuration has changed */
5072 // force OnDemand configuration refresh (if SC notification arrives before BSD notify)
5073 __SCNetworkConnectionForceOnDemandConfigurationRefresh();
5078 match
= CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), SCNETWORKREACHABILITY_TRIGGER_KEY
);
5081 forcedChange
= TRUE
; /* an SCDynamicStore driven "network" change */
5084 if (nChanges
> nGlobals
) {
5085 networkConfigChanged
= TRUE
;
5089 unsigned int changes
= 0;
5090 static const char *change_strings
[] = {
5091 // with no "power" status change
5093 "network ", // 00001
5095 "network and DNS ", // 00011
5096 "OnDemand ", // 00100
5097 "network and OnDemand ", // 00101
5098 "DNS and OnDemand ", // 00110
5099 "network, DNS, and OnDemand ", // 00111
5101 "network and NE ", // 01001
5102 "DNS and NE ", // 01010
5103 "network, DNS, and NE ", // 01011
5104 "OnDemand and NE ", // 01100
5105 "network, OnDemand, and NE ", // 01101
5106 "DNS, OnDemand, and NE ", // 01110
5107 "network, DNS, OnDemand, and NE ", // 01111
5108 #if !TARGET_OS_IPHONE
5109 // with "power" status change
5111 "network and power ", // 10001
5112 "DNS and power ", // 10010
5113 "network, DNS, and power ", // 10011
5114 "OnDemand and power ", // 10100
5115 "network, OnDemand, and power ", // 10101
5116 "DNS, OnDemand, and power ", // 10110
5117 "network, DNS, OnDemand, and power ", // 10111
5118 "NE and power ", // 11000
5119 "network, NE, and power ", // 11001
5120 "DNS, NE, and power ", // 11010
5121 "network, DNS, NE, and power ", // 11011
5122 "OnDemand, NE, and power ", // 11100
5123 "network, OnDemand, NE, and power ", // 11101
5124 "DNS, OnDemand, NE, and power ", // 11110
5125 "network, DNS, OnDemand, NE, and power ", // 11111
5126 #endif // !TARGET_OS_IPHONE
5129 #if !TARGET_OS_IPHONE
5131 if (powerStatusChanged
) {
5134 #endif // !TARGET_OS_IPHONE
5142 if (onDemandConfigChanged
) {
5147 if (dnsConfigChanged
) {
5152 if (networkConfigChanged
) {
5156 SCLog(TRUE
, LOG_INFO
,
5157 CFSTR("process %s%s%sconfiguration change"),
5158 forcedChange
? "[forced] " : "",
5159 change_strings
[changes
]);
5162 ReachabilityStoreInfo_init(&store_info
);
5164 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
5165 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
5166 CFSetGetValues(watchers
, targets
);
5167 for (i
= 0; i
< nTargets
; i
++) {
5168 Boolean dnsNeedsUpdate
= FALSE
;
5169 SCNetworkReachabilityRef target
= targets
[i
];
5170 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5172 MUTEX_LOCK(&targetPrivate
->lock
);
5175 if (dnsConfigChanged
) {
5176 targetPrivate
->last_dns
= now
;
5179 if (networkConfigChanged
) {
5180 targetPrivate
->last_network
= now
;
5183 #if !TARGET_OS_IPHONE
5184 if (powerStatusChanged
) {
5185 targetPrivate
->last_power
= now
;
5187 #endif // !TARGET_OS_IPHONE
5189 if (isReachabilityTypeName(targetPrivate
->type
)) {
5190 Boolean dnsChanged
= (dnsConfigChanged
|
5192 onDemandConfigChanged
|
5197 * if the DNS configuration didn't change we still need to
5198 * check that the DNS servers are accessible.
5200 Boolean ns_blocked
= FALSE
;
5201 int ns_dns_config
= -1;
5202 SCNetworkReachabilityFlags ns_flags
= 0;
5203 uint32_t ns_if_index
= 0;
5206 /* update the reachability of the DNS servers */
5207 ok
= ReachabilityStoreInfo_update(&store_info
, &store
, AF_UNSPEC
);
5209 _SC_R_updateResolverReachability(&store_info
,
5211 &targetPrivate
->haveDNS
,
5212 targetPrivate
->name
,
5213 targetPrivate
->if_index
,
5216 targetPrivate
->log_prefix
);
5218 ns_flags
= kSCNetworkReachabilityFlagsReachable
;
5223 if (rankReachability(ns_flags
) < 2) {
5225 * if DNS servers are not (or are no longer) reachable, set
5226 * flags based on the availability of configured (but not
5229 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"),
5230 targetPrivate
->log_prefix
);
5234 if ((targetPrivate
->dnsBlocked
!= ns_blocked
) ||
5235 (targetPrivate
->resolverFlags
!= ns_flags
)) {
5236 // if the DNS blocked or resolver reachability changed
5237 targetPrivate
->dnsBlocked
= ns_blocked
;
5238 targetPrivate
->resolverFlags
= ns_flags
;
5244 if (targetPrivate
->dnsActive
) {
5245 // if we have an outstanding [m]DNS query
5246 SCLog(_sc_debug
, LOG_INFO
,
5247 CFSTR("%scancel [m]DNS query for name = %s"),
5248 targetPrivate
->log_prefix
,
5249 targetPrivate
->name
);
5250 dequeueDNSQuery(target
);
5253 /* schedule request to resolve the name again */
5254 targetPrivate
->needResolve
= TRUE
;
5259 targetPrivate
->cycle
++;
5262 if (targetPrivate
->scheduled
) {
5263 __SCNetworkReachabilityUpdate(target
);
5266 MUTEX_UNLOCK(&targetPrivate
->lock
);
5268 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
5270 ReachabilityStoreInfo_free(&store_info
);
5274 if (watchers
!= NULL
) CFRelease(watchers
);
5280 * __SCNetworkReachabilityHandleStoreChanges
5282 * Called to process SCDynamicStore network configuration changes.
5283 * - should be exec'd on the _hn_changes_queue()
5286 __SCNetworkReachabilityHandleStoreChanges(SCDynamicStoreRef store
,
5287 CFArrayRef changedKeys
,
5290 nwi_state_t nwi_state
;
5292 if ((CFArrayGetCount(changedKeys
) == 1) &&
5293 CFArrayContainsValue(changedKeys
, CFRangeMake(0, 1), SCNETWORKREACHABILITY_TRIGGER_KEY
)) {
5297 /* "something" [else] changed, start fresh */
5298 ReachabilityStoreInfo_save(NULL
);
5300 nwi_state
= nwi_state_copy();
5301 if (nwi_state
!= NULL
) {
5302 // if we have some networking
5303 nwi_state_release(nwi_state
);
5307 // if no networking, use the [SC] changes to add/update
5308 // the kSCNetworkReachabilityFlagsConnectionRequired flag
5312 __SCNetworkReachabilityHandleChanges(store
, changedKeys
, info
);
5317 #if !TARGET_OS_IPHONE
5320 darkWakeNotify(SCNetworkReachabilityRef target
)
5327 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities
)
5329 if ((power_capabilities
& POWER_CAPABILITIES_NETWORK
) != POWER_CAPABILITIES_NETWORK
) {
5330 // if we're not fully awake (from a networking point of view).
5337 #endif // !TARGET_OS_IPHONE
5341 reachPerform(void *info
)
5344 void (*context_release
)(const void *);
5346 ReachabilityInfo reach_info
;
5347 SCNetworkReachabilityCallBack rlsFunction
;
5348 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
5349 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5351 n
= _SC_ATOMIC_ZERO(&targetPrivate
->pending
);
5352 if (_sc_debug
&& (n
> 1)) {
5353 SCLog(TRUE
, LOG_DEBUG
,
5354 CFSTR("%sdelivering SCNetworkReachability notifications (%u)"),
5355 targetPrivate
->log_prefix
,
5359 MUTEX_LOCK(&targetPrivate
->lock
);
5361 if (!targetPrivate
->scheduled
) {
5362 // if no longer scheduled
5363 SCLog(_sc_debug
, LOG_DEBUG
,
5364 CFSTR("%sskipping SCNetworkReachability callback, no longer scheduled"),
5365 targetPrivate
->log_prefix
);
5366 MUTEX_UNLOCK(&targetPrivate
->lock
);
5370 // capture current state
5371 memcpy(&reach_info
, &targetPrivate
->info
, sizeof(ReachabilityInfo
));
5374 rlsFunction
= targetPrivate
->rlsFunction
;
5375 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
5376 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
5377 context_release
= targetPrivate
->rlsContext
.release
;
5379 context_info
= targetPrivate
->rlsContext
.info
;
5380 context_release
= NULL
;
5383 // update last notification info
5384 _reach_set(&targetPrivate
->last_notify
, &reach_info
, targetPrivate
->cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
);
5385 (void)gettimeofday(&targetPrivate
->last_push
, NULL
);
5387 MUTEX_UNLOCK(&targetPrivate
->lock
);
5389 if (rlsFunction
!= NULL
) {
5390 (*rlsFunction
)(target
,
5391 reach_info
.flags
& kSCNetworkReachabilityFlagsMask
,
5395 if (context_release
!= NULL
) {
5396 (*context_release
)(context_info
);
5406 * - caller must *not* be holding the target lock
5407 * - caller must be running on the __SCNetworkReachability_concurrent_queue()
5410 reachUpdate(SCNetworkReachabilityRef target
)
5413 Boolean defer
= FALSE
;
5416 ReachabilityInfo reach_info
= NOT_REACHABLE
;
5417 ReachabilityStoreInfo store_info
;
5418 Boolean target_debug
;
5419 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5421 target_debug
= (_sc_debug
&& !targetPrivate
->quiet
);
5424 SCLog(TRUE
, LOG_INFO
, CFSTR("%schecking target reachability"),
5425 targetPrivate
->log_prefix
);
5429 MUTEX_LOCK(&targetPrivate
->lock
);
5431 if (!targetPrivate
->scheduled
) {
5432 // if not currently scheduled
5433 MUTEX_UNLOCK(&targetPrivate
->lock
);
5437 /* update reachability, notify if status changed */
5438 ReachabilityStoreInfo_init(&store_info
);
5439 ok
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
);
5440 ReachabilityStoreInfo_free(&store_info
);
5442 /* if reachability status not available */
5444 SCLog(TRUE
, LOG_INFO
, CFSTR("%sflags not available"),
5445 targetPrivate
->log_prefix
);
5447 reach_info
= NOT_REACHABLE
;
5450 #if !TARGET_OS_IPHONE
5452 * We want to defer the notification if this is a maintenance wake *and*
5453 * the reachability flags that we would be reporting to the application
5454 * are better than those that we last reported.
5456 if (!systemIsAwake(power_capabilities
)) {
5457 /* if this is a maintenace wake */
5458 reach_info
.sleeping
= TRUE
;
5460 if (rankReachability(reach_info
.flags
) >= rankReachability(targetPrivate
->info
.flags
)) {
5462 * don't report the change if the new reachability flags are
5463 * the same or "better"
5465 defer
= !darkWakeNotify(target
);
5466 } else if (!__reach_changed(&targetPrivate
->last_notify
, &reach_info
)) {
5468 * if we have already posted this change
5470 defer
= !darkWakeNotify(target
);
5473 #endif // !TARGET_OS_IPHONE
5475 cycle
= targetPrivate
->cycle
;
5476 forced
= ((cycle
!= 0) && (targetPrivate
->info
.cycle
!= cycle
));
5481 * changed forced pending
5482 * ======= ======= =======
5485 * N Y N Change (forced && !resolve pending)
5486 * N Y Y No change (suppress forced w/resolve pending)
5492 * Change == A || (B && !C)
5493 * No Change == !(A || (B && !C))
5494 * No Change == !A && !(B && !C)
5495 * No Change == !A && (!B || C)
5496 * No Change == (!B || C) && !A
5498 if ((!forced
|| (reach_info
.flags
== kSCNetworkReachabilityFlagsFirstResolvePending
))
5499 && !__reach_changed(&targetPrivate
->info
, &reach_info
)) {
5501 if (targetPrivate
->info
.sleeping
== reach_info
.sleeping
) {
5502 SCLog(TRUE
, LOG_INFO
,
5503 CFSTR("%sflags/interface match (now 0x%08x/%u%s)%s%s"),
5504 targetPrivate
->log_prefix
,
5506 reach_info
.if_index
,
5507 reach_info
.sleeping
? ", z" : "",
5508 defer
? ", deferred" : "",
5509 forced
? ", forced" : "");
5511 SCLog(TRUE
, LOG_INFO
,
5512 CFSTR("%sflags/interface equiv (was 0x%08x/%u%s, now 0x%08x/%u%s)%s%s"),
5513 targetPrivate
->log_prefix
,
5514 targetPrivate
->info
.flags
,
5515 targetPrivate
->info
.if_index
,
5516 targetPrivate
->info
.sleeping
? ", z" : "",
5518 reach_info
.if_index
,
5519 reach_info
.sleeping
? ", z" : "",
5520 defer
? ", deferred" : "",
5521 forced
? ", forced" : "");
5524 MUTEX_UNLOCK(&targetPrivate
->lock
);
5529 SCLog(TRUE
, LOG_INFO
,
5530 CFSTR("%sflags/interface have changed (was 0x%08x/%u%s, now 0x%08x/%u%s)%s%s"),
5531 targetPrivate
->log_prefix
,
5532 targetPrivate
->info
.flags
,
5533 targetPrivate
->info
.if_index
,
5534 targetPrivate
->info
.sleeping
? ", z" : "",
5536 reach_info
.if_index
,
5537 reach_info
.sleeping
? ", z" : "",
5538 defer
? ", deferred" : "",
5539 forced
? ", forced" : "");
5542 /* update flags / interface */
5543 _reach_set(&targetPrivate
->info
, &reach_info
, cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
);
5545 /* as needed, defer the notification */
5547 MUTEX_UNLOCK(&targetPrivate
->lock
);
5551 MUTEX_UNLOCK(&targetPrivate
->lock
);
5558 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
5559 SCNetworkReachabilityCallBack callout
,
5560 SCNetworkReachabilityContext
*context
)
5562 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5564 MUTEX_LOCK(&targetPrivate
->lock
);
5566 if (targetPrivate
->rlsContext
.release
!= NULL
) {
5567 /* let go of the current context */
5568 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
5571 targetPrivate
->rlsFunction
= callout
;
5572 targetPrivate
->rlsContext
.info
= NULL
;
5573 targetPrivate
->rlsContext
.retain
= NULL
;
5574 targetPrivate
->rlsContext
.release
= NULL
;
5575 targetPrivate
->rlsContext
.copyDescription
= NULL
;
5577 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
5578 if (context
->retain
!= NULL
) {
5579 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
5583 MUTEX_UNLOCK(&targetPrivate
->lock
);
5590 reachRLSCopyDescription(const void *info
)
5592 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
5594 return CFStringCreateWithFormat(NULL
,
5596 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
5602 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
5603 CFRunLoopRef runLoop
,
5604 CFStringRef runLoopMode
,
5605 dispatch_queue_t queue
,
5608 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5609 Boolean init
= FALSE
;
5610 __block Boolean ok
= FALSE
;
5612 MUTEX_LOCK(&targetPrivate
->lock
);
5614 if ((targetPrivate
->dispatchQueue
!= NULL
) || // if we are already scheduled with a dispatch queue
5615 ((queue
!= NULL
) && targetPrivate
->scheduled
)) { // if we are already scheduled on a CFRunLoop
5616 _SCErrorSet(kSCStatusInvalidArgument
);
5620 if (!targetPrivate
->serverBypass
) {
5621 if (!targetPrivate
->serverActive
) {
5623 ok
= __SCNetworkReachabilityServer_targetAdd(target
);
5625 targetPrivate
->serverBypass
= TRUE
;
5629 if (targetPrivate
->serverActive
) {
5630 if (targetPrivate
->scheduled
) {
5631 // if already scheduled
5635 ok
= __SCNetworkReachabilityServer_targetSchedule(target
);
5637 SCLog(TRUE
, LOG_DEBUG
,
5638 CFSTR("__SCNetworkReachabilityScheduleWithRunLoop _targetMonitor() failed"));
5639 _SCErrorSet(kSCStatusFailed
);
5647 /* schedule the SCNetworkReachability did-something-change handler */
5649 dispatch_sync(_hn_target_queue(), ^{
5652 if (!onDemand
&& (hn_store
== NULL
)) {
5653 CFMutableArrayRef keys
;
5654 CFMutableArrayRef patterns
;
5655 Boolean watch_dns_configuration
= FALSE
;
5656 Boolean watch_dns_changes
= FALSE
;
5657 Boolean watch_nwi
= FALSE
;
5658 Boolean watch_onDemand_networking
= FALSE
;
5659 #if !TARGET_OS_IPHONE
5660 Boolean watch_power
= FALSE
;
5661 #endif // !TARGET_OS_IPHONE
5663 hn_store
= SCDynamicStoreCreate(NULL
,
5664 CFSTR("SCNetworkReachability"),
5665 __SCNetworkReachabilityHandleStoreChanges
,
5667 if (hn_store
== NULL
) {
5668 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed"));
5672 ReachabilityStoreInfo_keys(&keys
, &patterns
);
5673 CFArrayAppendValue(keys
, SCNETWORKREACHABILITY_TRIGGER_KEY
); // force posting reach change
5674 ok
= SCDynamicStoreSetNotificationKeys(hn_store
, keys
, patterns
);
5676 CFRelease(patterns
);
5678 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetNotificationKeys() failed"));
5679 CFRelease(hn_store
);
5684 ok
= SCDynamicStoreSetDispatchQueue(hn_store
, _hn_changes_queue());
5686 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetDispatchQueue() failed"));
5687 CFRelease(hn_store
);
5692 // watch for network information changes
5693 watch_nwi
= nwi_refresh_enable(_hn_changes_queue());
5698 // watch for DNS configuration (resolver reachability) changes
5699 watch_dns_configuration
= dns_configuration_watch();
5700 if (!watch_dns_configuration
) {
5704 // watch for changes affecting DNS queries
5705 watch_dns_changes
= dns_refresh_enable(_hn_changes_queue());
5706 if (!watch_dns_changes
) {
5710 #if !TARGET_OS_IPHONE
5711 // watch for power capabilities (sleep/wake) changes
5712 watch_power
= power_refresh_enable(_hn_changes_queue());
5716 #endif // !TARGET_OS_IPHONE
5718 // watch for OnDemand network changes
5719 watch_onDemand_networking
= onDemand_refresh_enable(_hn_changes_queue());
5720 if (!watch_onDemand_networking
) {
5726 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
5727 ReachabilityStoreInfo_enable(TRUE
);
5737 if (watch_onDemand_networking
) {
5738 onDemand_refresh_disable();
5741 #if !TARGET_OS_IPHONE
5743 power_refresh_disable();
5745 #endif // !TARGET_OS_IPHONE
5747 if (watch_dns_changes
) {
5748 dns_refresh_disable();
5751 if (watch_dns_configuration
) {
5752 dns_configuration_unwatch();
5756 nwi_refresh_disable();
5759 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
5760 CFRelease(hn_store
);
5763 _SCErrorSet(kSCStatusFailed
);
5770 CFSetAddValue(hn_targets
, target
);
5781 if (!targetPrivate
->scheduled
) {
5782 CFRunLoopSourceContext context
= { 0 // version
5783 , (void *)target
// info
5784 , CFRetain
// retain
5785 , CFRelease
// release
5786 , reachRLSCopyDescription
// copyDescription
5791 , reachPerform
// perform
5794 if (runLoop
!= NULL
) {
5795 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
5796 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
5799 if (isReachabilityTypeName(targetPrivate
->type
)) {
5801 * we're now scheduled so let's ensure that we
5802 * are starting with a clean slate before we
5805 if (targetPrivate
->resolvedAddresses
!= NULL
) {
5806 CFRelease(targetPrivate
->resolvedAddresses
);
5807 targetPrivate
->resolvedAddresses
= NULL
;
5809 targetPrivate
->resolvedError
= NETDB_SUCCESS
;
5810 targetPrivate
->needResolve
= TRUE
;
5811 _reach_set(&targetPrivate
->info
,
5813 targetPrivate
->info
.cycle
,
5814 targetPrivate
->if_index
,
5815 targetPrivate
->if_name
);
5816 targetPrivate
->info
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
5817 _reach_set(&targetPrivate
->serverInfo
,
5819 targetPrivate
->serverInfo
.cycle
,
5820 targetPrivate
->if_index
,
5821 targetPrivate
->if_name
);
5822 targetPrivate
->serverInfo
.flags
|= kSCNetworkReachabilityFlagsFirstResolvePending
;
5826 targetPrivate
->scheduled
= TRUE
;
5831 if (queue
!= NULL
) {
5832 // retain dispatch queue
5833 dispatch_retain(queue
);
5834 targetPrivate
->dispatchQueue
= queue
;
5837 // We've taken a reference to the client's dispatch_queue and we
5838 // want to hold on to that reference until we've processed any/all
5839 // notifications. To facilitate this we create a group, dispatch
5840 // any notification blocks to via that group, and when the caller
5841 // has told us to stop the notifications (unschedule) we wait for
5842 // the group to empty and use the group's finalizer to release
5843 // our reference to the client's queue.
5846 // make sure that we have group to track any async requests
5847 targetPrivate
->dispatchGroup
= dispatch_group_create();
5849 // retain the target ... and release it when the group is released
5851 dispatch_set_context(targetPrivate
->dispatchGroup
, (void *)target
);
5852 dispatch_set_finalizer_f(targetPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
5854 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
5856 * if we do not already have host notifications scheduled with
5857 * this runLoop / runLoopMode
5859 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
5862 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
5866 ReachabilityInfo reach_info
= NOT_REACHABLE
;
5867 ReachabilityStoreInfo store_info
;
5870 * if we have yet to schedule SC notifications for this address
5871 * - initialize current reachability status
5873 ReachabilityStoreInfo_init(&store_info
);
5874 if (__SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
)) {
5876 * if reachability status available
5878 * - schedule notification to report status via callback
5880 reach_info
.flags
|= (targetPrivate
->info
.flags
& kSCNetworkReachabilityFlagsFirstResolvePending
);
5881 _reach_set(&targetPrivate
->info
,
5883 targetPrivate
->cycle
,
5884 targetPrivate
->if_index
,
5885 targetPrivate
->if_name
);
5886 __SCNetworkReachabilityUpdate(target
);
5888 /* if reachability status not available, async lookup started */
5889 _reach_set(&targetPrivate
->info
,
5891 targetPrivate
->cycle
,
5892 targetPrivate
->if_index
,
5893 targetPrivate
->if_name
);
5894 _reach_set(&targetPrivate
->serverInfo
,
5896 targetPrivate
->cycle
,
5897 targetPrivate
->if_index
,
5898 targetPrivate
->if_name
);
5900 ReachabilityStoreInfo_free(&store_info
);
5903 if (targetPrivate
->onDemandServer
!= NULL
) {
5904 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, queue
, TRUE
);
5907 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%sscheduled"),
5908 targetPrivate
->log_prefix
);
5914 MUTEX_UNLOCK(&targetPrivate
->lock
);
5920 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
5921 CFRunLoopRef runLoop
,
5922 CFStringRef runLoopMode
,
5925 dispatch_group_t drainGroup
= NULL
;
5926 dispatch_queue_t drainQueue
= NULL
;
5929 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
5931 // hold a reference while we unschedule
5934 MUTEX_LOCK(&targetPrivate
->lock
);
5936 if (((runLoop
== NULL
) && (targetPrivate
->dispatchQueue
== NULL
)) || // if we should be scheduled on a dispatch queue (but are not)
5937 ((runLoop
!= NULL
) && (targetPrivate
->dispatchQueue
!= NULL
))) { // if we should be scheduled on a CFRunLoop (but are not)
5938 _SCErrorSet(kSCStatusInvalidArgument
);
5942 if (!targetPrivate
->scheduled
) {
5943 // if not currently scheduled
5944 _SCErrorSet(kSCStatusInvalidArgument
);
5948 // unschedule the target specific sources
5949 if (targetPrivate
->dispatchQueue
!= NULL
) {
5950 if (targetPrivate
->onDemandServer
!= NULL
) {
5951 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, NULL
, NULL
);
5952 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
);
5955 // save dispatchQueue, release reference when we've queue'd blocks complete, allow re-scheduling
5956 drainGroup
= targetPrivate
->dispatchGroup
;
5957 targetPrivate
->dispatchGroup
= NULL
;
5958 drainQueue
= targetPrivate
->dispatchQueue
;
5959 targetPrivate
->dispatchQueue
= NULL
;
5961 if (!_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
5962 // if not currently scheduled
5963 _SCErrorSet(kSCStatusInvalidArgument
);
5967 if (targetPrivate
->onDemandServer
!= NULL
) {
5968 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, TRUE
);
5971 n
= CFArrayGetCount(targetPrivate
->rlList
);
5972 if ((n
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
5973 // if target is no longer scheduled for this runLoop / runLoopMode
5974 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
5977 // if *all* notifications have been unscheduled
5978 if (targetPrivate
->onDemandServer
!= NULL
) {
5979 SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, NULL
, NULL
);
5981 CFRelease(targetPrivate
->rlList
);
5982 targetPrivate
->rlList
= NULL
;
5983 CFRunLoopSourceInvalidate(targetPrivate
->rls
);
5984 CFRelease(targetPrivate
->rls
);
5985 targetPrivate
->rls
= NULL
;
5992 // Cancel our request for server monitoring
5994 if (targetPrivate
->serverActive
) {
5995 ok
= __SCNetworkReachabilityServer_targetUnschedule(target
);
5997 SCLog(TRUE
, LOG_DEBUG
,
5998 CFSTR("__SCNetworkReachabilityUnscheduleFromRunLoop _targetMonitor() failed"));
5999 _SCErrorSet(kSCStatusFailed
);
6003 // if *all* notifications have been unscheduled
6004 targetPrivate
->scheduled
= FALSE
;
6007 if (targetPrivate
->serverActive
) {
6012 if (targetPrivate
->dnsActive
) {
6013 // if we have an active [m]DNS query
6014 dequeueDNSQuery(target
);
6017 dispatch_sync(_hn_target_queue(), ^{
6018 CFSetRemoveValue(hn_targets
, target
);
6024 if (CFSetGetCount(hn_targets
) > 0) {
6028 // if we are no longer monitoring any targets
6029 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
);
6030 CFRelease(hn_store
);
6032 CFRelease(hn_targets
);
6035 ReachabilityStoreInfo_enable(FALSE
);
6036 ReachabilityStoreInfo_save(NULL
);
6039 * until we start monitoring again, ensure that
6040 * any resources associated with watching network
6041 * and configuration changes have been released.
6045 // OnDemand configuration
6046 onDemand_refresh_disable();
6048 #if !TARGET_OS_IPHONE
6049 // sleep/wake & power capabilities
6050 power_refresh_disable();
6051 #endif // !TARGET_OS_IPHONE
6053 // outstanding DNS queries
6054 dns_refresh_disable();
6056 // DNS configuration
6057 dns_configuration_unwatch();
6060 nwi_refresh_disable();
6066 SCLog((_sc_debug
&& (_sc_log
> 0)), LOG_INFO
, CFSTR("%sunscheduled"),
6067 targetPrivate
->log_prefix
);
6073 MUTEX_UNLOCK(&targetPrivate
->lock
);
6075 if (drainGroup
!= NULL
) {
6076 dispatch_group_notify(drainGroup
, __SCNetworkReachability_concurrent_queue(), ^{
6077 // release group/queue references
6078 dispatch_release(drainQueue
);
6079 dispatch_release(drainGroup
); // releases our target reference
6083 // release our reference
6090 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
6091 CFRunLoopRef runLoop
,
6092 CFStringRef runLoopMode
)
6094 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
6095 _SCErrorSet(kSCStatusInvalidArgument
);
6099 return __SCNetworkReachabilityScheduleWithRunLoop(target
, runLoop
, runLoopMode
, NULL
, FALSE
);
6103 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
6104 CFRunLoopRef runLoop
,
6105 CFStringRef runLoopMode
)
6107 if (!isA_SCNetworkReachability(target
) || (runLoop
== NULL
) || (runLoopMode
== NULL
)) {
6108 _SCErrorSet(kSCStatusInvalidArgument
);
6112 return __SCNetworkReachabilityUnscheduleFromRunLoop(target
, runLoop
, runLoopMode
, FALSE
);
6116 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target
,
6117 dispatch_queue_t queue
)
6121 if (!isA_SCNetworkReachability(target
)) {
6122 _SCErrorSet(kSCStatusInvalidArgument
);
6126 if (queue
!= NULL
) {
6127 ok
= __SCNetworkReachabilityScheduleWithRunLoop(target
, NULL
, NULL
, queue
, FALSE
);
6129 ok
= __SCNetworkReachabilityUnscheduleFromRunLoop(target
, NULL
, NULL
, FALSE
);