2 * Copyright (c) 2003-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * March 31, 2004 Allan Nathanson <ajn@apple.com>
28 * - use [SC] DNS configuration information
30 * January 19, 2003 Allan Nathanson <ajn@apple.com>
31 * - add advanced reachability APIs
34 #include <SystemConfiguration/SystemConfiguration.h>
35 #include <SystemConfiguration/SCValidation.h>
36 #include <SystemConfiguration/SCPrivate.h>
38 #include <CoreFoundation/CFRuntime.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
46 #include <netdb_async.h>
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
52 #include <net/if_dl.h>
53 #define KERNEL_PRIVATE
54 #include <net/route.h>
58 #define s6_addr16 __u6_addr.__u6_addr16
61 #include <ppp/ppp_msg.h>
64 #define kSCNetworkFlagsFirstResolvePending (1<<31)
71 reachabilityTypeAddress
,
72 reachabilityTypeAddressPair
,
77 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
78 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
83 /* base CFType information */
92 /* target host name */
94 CFArrayRef resolvedAddress
; /* CFArray[CFData] */
95 int resolvedAddressError
;
97 /* local & remote addresses */
98 struct sockaddr
*localAddress
;
99 struct sockaddr
*remoteAddress
;
101 /* current reachability flags */
102 SCNetworkConnectionFlags flags
;
105 /* run loop source, callout, context, rl scheduling info */
106 CFRunLoopSourceRef rls
;
107 SCNetworkReachabilityCallBack rlsFunction
;
108 SCNetworkReachabilityContext rlsContext
;
109 CFMutableArrayRef rlList
;
111 /* [async] DNS query info */
113 CFMachPortRef dnsPort
;
114 CFRunLoopSourceRef dnsRLS
;
115 struct timeval dnsQueryStart
;
117 } SCNetworkReachabilityPrivate
, *SCNetworkReachabilityPrivateRef
;
120 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
123 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
125 "SCNetworkReachability", // className
128 __SCNetworkReachabilityDeallocate
, // dealloc
131 NULL
, // copyFormattingDesc
132 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
136 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
140 * host "something has changed" notifications
143 static pthread_mutex_t hn_lock
= PTHREAD_MUTEX_INITIALIZER
;
144 static SCDynamicStoreRef hn_store
= NULL
;
145 static CFRunLoopSourceRef hn_storeRLS
= NULL
;
146 static CFMutableArrayRef hn_rlList
= NULL
;
147 static CFMutableSetRef hn_targets
= NULL
;
155 dns_config_t
*config
;
157 } dns_configuration_t
;
160 static pthread_mutex_t dns_lock
= PTHREAD_MUTEX_INITIALIZER
;
161 static dns_configuration_t
*dns_configuration
= NULL
;
162 static int dns_token
;
163 static Boolean dns_token_valid
= FALSE
;
166 static __inline__ CFTypeRef
167 isA_SCNetworkReachability(CFTypeRef obj
)
169 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
174 __log_query_time(Boolean found
, Boolean async
, struct timeval
*start
)
176 struct timeval dnsQueryComplete
;
177 struct timeval dnsQueryElapsed
;
183 if (start
->tv_sec
== 0) {
187 (void) gettimeofday(&dnsQueryComplete
, NULL
);
188 timersub(&dnsQueryComplete
, start
, &dnsQueryElapsed
);
189 SCLog(TRUE
, LOG_DEBUG
,
190 CFSTR("%ssync DNS complete%s (query time = %d.%3.3d)"),
192 found
? "" : ", host not found",
193 dnsQueryElapsed
.tv_sec
,
194 dnsQueryElapsed
.tv_usec
/ 1000);
201 updatePPPStatus(SCDynamicStoreRef
*storeP
,
202 const struct sockaddr
*sa
,
204 SCNetworkConnectionFlags
*flags
)
206 CFDictionaryRef dict
= NULL
;
209 const void * keys_q
[N_QUICK
];
210 const void ** keys
= keys_q
;
213 int sc_status
= kSCStatusReachabilityUnknown
;
214 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
215 const void * values_q
[N_QUICK
];
216 const void ** values
= values_q
;
218 switch (sa
->sa_family
) {
220 entity
= kSCEntNetIPv4
;
223 entity
= kSCEntNetIPv6
;
230 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
232 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("updatePPPStatus SCDynamicStoreCreate() failed"));
238 * grab a snapshot of the PPP configuration from the dynamic store
242 CFMutableArrayRef patterns
;
244 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
245 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
246 kSCDynamicStoreDomainState
,
249 CFArrayAppendValue(patterns
, pattern
);
251 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
252 kSCDynamicStoreDomainSetup
,
255 CFArrayAppendValue(patterns
, pattern
);
257 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
258 kSCDynamicStoreDomainState
,
261 CFArrayAppendValue(patterns
, pattern
);
263 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
267 /* if we could not access the dynamic store */
271 sc_status
= kSCStatusOK
;
274 * look for the service which matches the provided interface
276 n
= CFDictionaryGetCount(dict
);
281 ppp_if
= CFStringCreateWithCStringNoCopy(NULL
,
283 kCFStringEncodingASCII
,
286 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
287 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
288 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
290 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
292 for (i
=0; i
< n
; i
++) {
293 CFArrayRef components
;
296 CFDictionaryRef p_setup
;
297 CFDictionaryRef p_state
;
299 CFStringRef service
= NULL
;
300 CFStringRef s_key
= (CFStringRef
) keys
[i
];
301 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
304 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
308 if (!CFStringHasSuffix(s_key
, entity
)) {
309 continue; // if not an IPv4 or IPv6 entity
312 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
313 if (!isA_CFString(s_if
)) {
314 continue; // if no interface
317 if (!CFEqual(ppp_if
, s_if
)) {
318 continue; // if not this interface
322 * extract service ID, get PPP "state" entity (for status), and get
323 * the "setup" entity (for dial-on-traffic flag)
325 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
326 if (CFArrayGetCount(components
) != 5) {
329 service
= CFArrayGetValueAtIndex(components
, 3);
330 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
331 kSCDynamicStoreDomainState
,
334 p_state
= CFDictionaryGetValue(dict
, key
);
336 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
337 kSCDynamicStoreDomainSetup
,
340 p_setup
= CFDictionaryGetValue(dict
, key
);
342 CFRelease(components
);
345 if (!isA_CFDictionary(p_state
)) {
348 num
= CFDictionaryGetValue(p_state
, kSCPropNetPPPStatus
);
349 if (!isA_CFNumber(num
)) {
353 if (!CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) {
356 switch (ppp_status
) {
358 /* if we're really UP and RUNNING */
361 /* if we're effectively UP and RUNNING */
364 case PPP_STATERESERVED
:
365 /* if we're not connected at all */
366 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link idle, dial-on-traffic to connect"));
367 *flags
|= kSCNetworkFlagsConnectionRequired
;
370 /* if we're in the process of [dis]connecting */
371 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link, connection in progress"));
372 *flags
|= kSCNetworkFlagsConnectionRequired
;
376 // check PPP dial-on-traffic status
377 if (isA_CFDictionary(p_setup
)) {
378 num
= CFDictionaryGetValue(p_setup
, kSCPropNetPPPDialOnDemand
);
379 if (isA_CFNumber(num
)) {
382 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
384 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
385 if (ppp_status
== PPP_IDLE
) {
386 *flags
|= kSCNetworkFlagsInterventionRequired
;
397 if (keys
!= keys_q
) {
398 CFAllocatorDeallocate(NULL
, keys
);
399 CFAllocatorDeallocate(NULL
, values
);
404 if (dict
!= NULL
) CFRelease(dict
);
405 if (storeP
!= NULL
) *storeP
= store
;
411 updatePPPAvailable(SCDynamicStoreRef
*storeP
,
412 const struct sockaddr
*sa
,
413 SCNetworkConnectionFlags
*flags
)
415 CFDictionaryRef dict
= NULL
;
418 const void * keys_q
[N_QUICK
];
419 const void ** keys
= keys_q
;
421 int sc_status
= kSCStatusReachabilityUnknown
;
422 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
423 const void * values_q
[N_QUICK
];
424 const void ** values
= values_q
;
427 entity
= kSCEntNetIPv4
;
429 switch (sa
->sa_family
) {
431 entity
= kSCEntNetIPv4
;
434 entity
= kSCEntNetIPv6
;
442 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
444 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = unknown (could not access SCDynamicStore"));
450 * grab a snapshot of the PPP configuration from the dynamic store
454 CFMutableArrayRef patterns
;
456 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
457 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
458 kSCDynamicStoreDomainSetup
,
461 CFArrayAppendValue(patterns
, pattern
);
463 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
464 kSCDynamicStoreDomainSetup
,
467 CFArrayAppendValue(patterns
, pattern
);
469 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
473 /* if we could not access the dynamic store */
477 sc_status
= kSCStatusOK
;
480 * look for an available service which will provide connectivity
481 * for the requested address family.
483 n
= CFDictionaryGetCount(dict
);
488 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
489 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
490 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
492 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
494 for (i
= 0; i
< n
; i
++) {
495 CFArrayRef components
;
496 Boolean found
= FALSE
;
498 CFDictionaryRef p_dict
;
500 CFStringRef s_key
= (CFStringRef
) keys
[i
];
501 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
503 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
507 if (!CFStringHasSuffix(s_key
, entity
)) {
508 continue; // if not an IPv4 or IPv6 entity
511 // extract service ID
512 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
513 if (CFArrayGetCount(components
) != 5) {
516 service
= CFArrayGetValueAtIndex(components
, 3);
518 // check for PPP entity
519 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
520 kSCDynamicStoreDomainSetup
,
523 p_dict
= CFDictionaryGetValue(dict
, p_key
);
526 if (isA_CFDictionary(p_dict
)) {
530 * we have a PPP service for this address family
534 *flags
|= kSCNetworkFlagsReachable
;
535 *flags
|= kSCNetworkFlagsTransientConnection
;
536 *flags
|= kSCNetworkFlagsConnectionRequired
;
539 * get PPP dial-on-traffic status
541 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
542 if (isA_CFNumber(num
)) {
545 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
547 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
553 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = isReachable (after connect)"));
554 SCLog(TRUE
, LOG_INFO
, CFSTR(" service = %@"), service
);
559 CFRelease(components
);
566 if (keys
!= keys_q
) {
567 CFAllocatorDeallocate(NULL
, keys
);
568 CFAllocatorDeallocate(NULL
, values
);
573 if (dict
!= NULL
) CFRelease(dict
);
574 if (storeP
!= NULL
) *storeP
= store
;
579 #define ROUNDUP(a, size) \
580 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
582 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
583 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
588 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
592 for (i
= 0; i
< RTAX_MAX
; i
++) {
593 if (addrs
& (1 << i
)) {
602 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
605 checkAddress(SCDynamicStoreRef
*storeP
,
606 const struct sockaddr
*address
,
607 SCNetworkConnectionFlags
*flags
,
612 char if_name
[IFNAMSIZ
+1];
615 pid_t pid
= getpid();
617 struct sockaddr
*rti_info
[RTAX_MAX
];
618 struct rt_msghdr
*rtm
;
620 int sc_status
= kSCStatusReachabilityUnknown
;
621 struct sockaddr_dl
*sdl
;
622 int seq
= (int)pthread_self();
623 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
624 char *statusMessage
= NULL
;
625 #ifndef RTM_GET_SILENT
626 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
627 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
628 int sosize
= 48 * 1024;
632 if (if_index
!= NULL
) {
636 if (address
== NULL
) {
637 /* special case: check only for available paths off the system */
641 switch (address
->sa_family
) {
645 _SC_sockaddr_to_string(address
, buf
, sizeof(buf
));
646 SCLog(TRUE
, LOG_INFO
, CFSTR("checkAddress(%s)"), buf
);
651 * if no code for this address family (yet)
653 SCLog(_sc_verbose
, LOG_ERR
,
654 CFSTR("checkAddress(): unexpected address family %d"),
656 sc_status
= kSCStatusInvalidArgument
;
660 bzero(&buf
, sizeof(buf
));
662 rtm
= (struct rt_msghdr
*)&buf
;
663 rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
664 rtm
->rtm_version
= RTM_VERSION
;
665 #ifdef RTM_GET_SILENT
666 rtm
->rtm_type
= RTM_GET_SILENT
;
668 rtm
->rtm_type
= RTM_GET
;
670 rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
671 rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
675 switch (address
->sa_family
) {
677 struct sockaddr_in6
*sin6
;
679 sin6
= (struct sockaddr_in6
*)address
;
680 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
681 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
682 (sin6
->sin6_scope_id
!= 0)) {
683 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
684 sin6
->sin6_scope_id
= 0;
690 sa
= (struct sockaddr
*) (rtm
+ 1);
691 bcopy(address
, sa
, address
->sa_len
);
692 n
= ROUNDUP(sa
->sa_len
, sizeof(u_long
));
693 rtm
->rtm_msglen
+= n
;
695 sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
696 sdl
->sdl_family
= AF_LINK
;
697 sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
698 n
= ROUNDUP(sdl
->sdl_len
, sizeof(u_long
));
699 rtm
->rtm_msglen
+= n
;
701 #ifndef RTM_GET_SILENT
702 pthread_mutex_lock(&lock
);
704 rsock
= socket(PF_ROUTE
, SOCK_RAW
, 0);
706 #ifndef RTM_GET_SILENT
707 pthread_mutex_unlock(&lock
);
709 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(errno
));
710 sc_status
= kSCStatusFailed
;
714 #ifndef RTM_GET_SILENT
715 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
717 pthread_mutex_unlock(&lock
);
718 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(errno
));
719 sc_status
= kSCStatusFailed
;
724 if (write(rsock
, &buf
, rtm
->rtm_msglen
) == -1) {
728 #ifndef RTM_GET_SILENT
729 pthread_mutex_unlock(&lock
);
732 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(err
));
739 * Type, seq, pid identify our response.
740 * Routing sockets are broadcasters on input.
745 n
= read(rsock
, (void *)&buf
, sizeof(buf
));
751 SCLog(TRUE
, LOG_ERR
, CFSTR("read() failed: %s"), strerror(err
));
752 #ifndef RTM_GET_SILENT
753 pthread_mutex_unlock(&lock
);
758 } while (rtm
->rtm_type
!= RTM_GET
||
759 rtm
->rtm_seq
!= seq
||
760 rtm
->rtm_pid
!= pid
);
763 #ifndef RTM_GET_SILENT
764 pthread_mutex_unlock(&lock
);
767 get_rtaddrs(rtm
->rtm_addrs
, sa
, rti_info
);
774 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), rtm
->rtm_flags
);
776 for (i
= 0; i
< RTAX_MAX
; i
++) {
777 if (rti_info
[i
] != NULL
) {
778 _SC_sockaddr_to_string(rti_info
[i
], buf
, sizeof(buf
));
779 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, buf
);
785 if ((rti_info
[RTAX_IFP
] == NULL
) ||
786 (rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
787 /* no interface info */
791 sdl
= (struct sockaddr_dl
*) rti_info
[RTAX_IFP
];
792 if ((sdl
->sdl_nlen
== 0) || (sdl
->sdl_nlen
> IFNAMSIZ
)) {
793 /* no interface name */
797 /* get the interface flags */
799 bzero(&ifr
, sizeof(ifr
));
800 bcopy(sdl
->sdl_data
, ifr
.ifr_name
, sdl
->sdl_nlen
);
802 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
804 SCLog(TRUE
, LOG_NOTICE
, CFSTR("socket() failed: %s"), strerror(errno
));
808 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) < 0) {
809 SCLog(TRUE
, LOG_NOTICE
, CFSTR("ioctl() failed: %s"), strerror(errno
));
815 if (!(ifr
.ifr_flags
& IFF_UP
)) {
819 statusMessage
= "isReachable";
820 *flags
|= kSCNetworkFlagsReachable
;
822 if (rtm
->rtm_flags
& RTF_LOCAL
) {
823 statusMessage
= "isReachable (is a local address)";
824 *flags
|= kSCNetworkFlagsIsLocalAddress
;
825 } else if (ifr
.ifr_flags
& IFF_LOOPBACK
) {
826 statusMessage
= "isReachable (is loopback network)";
827 *flags
|= kSCNetworkFlagsIsLocalAddress
;
828 } else if (rti_info
[RTAX_IFA
]) {
829 void *addr1
= (void *)address
;
830 void *addr2
= (void *)rti_info
[RTAX_IFA
];
831 size_t len
= address
->sa_len
;
833 if ((address
->sa_family
!= rti_info
[RTAX_IFA
]->sa_family
) &&
834 (address
->sa_len
!= rti_info
[RTAX_IFA
]->sa_len
)) {
835 SCLog(TRUE
, LOG_NOTICE
,
836 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
839 rti_info
[RTAX_IFA
]->sa_family
,
840 rti_info
[RTAX_IFA
]->sa_len
);
844 switch (address
->sa_family
) {
846 addr1
= &((struct sockaddr_in
*)address
)->sin_addr
;
847 addr2
= &((struct sockaddr_in
*)rti_info
[RTAX_IFA
])->sin_addr
;
848 len
= sizeof(struct in_addr
);
853 if (((struct sockaddr_in
*)address
)->sin_addr
.s_addr
== 0) {
854 statusMessage
= "isReachable (this host)";
855 *flags
|= kSCNetworkFlagsIsLocalAddress
;
859 addr1
= &((struct sockaddr_in6
*)address
)->sin6_addr
;
860 addr2
= &((struct sockaddr_in6
*)rti_info
[RTAX_IFA
])->sin6_addr
;
861 len
= sizeof(struct in6_addr
);
867 if (memcmp(addr1
, addr2
, len
) == 0) {
868 statusMessage
= "isReachable (is interface address)";
869 *flags
|= kSCNetworkFlagsIsLocalAddress
;
873 if (!(rtm
->rtm_flags
& RTF_GATEWAY
) &&
874 (rti_info
[RTAX_GATEWAY
] != NULL
) &&
875 (rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
) &&
876 !(ifr
.ifr_flags
& IFF_POINTOPOINT
)) {
877 *flags
|= kSCNetworkFlagsIsDirect
;
880 bzero(&if_name
, sizeof(if_name
));
883 (sdl
->sdl_nlen
<= IFNAMSIZ
) ? sdl
->sdl_nlen
: IFNAMSIZ
);
885 if (if_index
!= NULL
) {
886 *if_index
= sdl
->sdl_index
;
890 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = %s"), statusMessage
);
891 SCLog(TRUE
, LOG_INFO
, CFSTR(" device = %s (%hu)"), if_name
, sdl
->sdl_index
);
892 SCLog(TRUE
, LOG_INFO
, CFSTR(" ifr_flags = 0x%04hx"), ifr
.ifr_flags
);
893 SCLog(TRUE
, LOG_INFO
, CFSTR(" rtm_flags = 0x%08x"), rtm
->rtm_flags
);
896 sc_status
= kSCStatusOK
;
898 if (ifr
.ifr_flags
& IFF_POINTOPOINT
) {
900 * We have an interface which "claims" to be a valid path
903 *flags
|= kSCNetworkFlagsTransientConnection
;
906 * Check if this is a dial-on-demand PPP link that isn't
909 sc_status
= updatePPPStatus(&store
, address
, if_name
, flags
);
916 sc_status
= updatePPPAvailable(&store
, address
, flags
);
921 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" cannot be reached"));
924 if (storeP
!= NULL
) *storeP
= store
;
925 if (sc_status
!= kSCStatusOK
) {
926 _SCErrorSet(sc_status
);
935 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
937 CFAllocatorRef allocator
= CFGetAllocator(cf
);
938 CFMutableStringRef result
;
939 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
941 result
= CFStringCreateMutable(allocator
, 0);
942 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> { "), cf
, allocator
);
943 switch (targetPrivate
->type
) {
944 case reachabilityTypeAddress
:
945 case reachabilityTypeAddressPair
: {
948 if (targetPrivate
->localAddress
!= NULL
) {
949 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
950 CFStringAppendFormat(result
, NULL
, CFSTR("local address=%s"),
954 if (targetPrivate
->remoteAddress
!= NULL
) {
955 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
956 CFStringAppendFormat(result
, NULL
, CFSTR("%s%saddress=%s"),
957 targetPrivate
->localAddress
? ", " : "",
958 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
963 case reachabilityTypeName
: {
964 CFStringAppendFormat(result
, NULL
, CFSTR("name=%s"), targetPrivate
->name
);
965 if (targetPrivate
->resolvedAddress
|| (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
966 if (targetPrivate
->resolvedAddress
!= NULL
) {
967 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
969 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddress
);
971 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
972 for (i
= 0; i
< n
; i
++) {
977 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
);
978 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
979 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
980 CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"),
984 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
986 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)"));
989 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
990 gai_strerror(targetPrivate
->resolvedAddressError
));
992 } else if (targetPrivate
->dnsPort
) {
993 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
998 if (targetPrivate
->rls
!= NULL
) {
999 CFStringAppendFormat(result
,
1001 CFSTR(", flags=%8.8x, if_index=%hu"),
1002 targetPrivate
->flags
,
1003 targetPrivate
->if_index
);
1005 CFStringAppendFormat(result
, NULL
, CFSTR(" }"));
1012 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1014 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1016 /* release resources */
1018 pthread_mutex_destroy(&targetPrivate
->lock
);
1020 if (targetPrivate
->name
!= NULL
)
1021 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
1023 if (targetPrivate
->resolvedAddress
!= NULL
)
1024 CFRelease(targetPrivate
->resolvedAddress
);
1026 if (targetPrivate
->localAddress
!= NULL
)
1027 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
1029 if (targetPrivate
->remoteAddress
!= NULL
)
1030 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
1032 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1033 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1041 __SCNetworkReachabilityInitialize(void)
1043 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
1048 static SCNetworkReachabilityPrivateRef
1049 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
1051 SCNetworkReachabilityPrivateRef targetPrivate
;
1054 /* initialize runtime */
1055 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
1057 /* allocate target */
1058 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
1059 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
1060 __kSCNetworkReachabilityTypeID
,
1063 if (targetPrivate
== NULL
) {
1067 pthread_mutex_init(&targetPrivate
->lock
, NULL
);
1069 targetPrivate
->name
= NULL
;
1071 targetPrivate
->resolvedAddress
= NULL
;
1072 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1074 targetPrivate
->localAddress
= NULL
;
1075 targetPrivate
->remoteAddress
= NULL
;
1077 targetPrivate
->flags
= 0;
1078 targetPrivate
->if_index
= 0;
1080 targetPrivate
->rls
= NULL
;
1081 targetPrivate
->rlsFunction
= NULL
;
1082 targetPrivate
->rlsContext
.info
= NULL
;
1083 targetPrivate
->rlsContext
.retain
= NULL
;
1084 targetPrivate
->rlsContext
.release
= NULL
;
1085 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1086 targetPrivate
->rlList
= NULL
;
1088 targetPrivate
->haveDNS
= FALSE
;
1089 targetPrivate
->dnsPort
= NULL
;
1090 targetPrivate
->dnsRLS
= NULL
;
1092 return targetPrivate
;
1096 SCNetworkReachabilityRef
1097 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
1098 const struct sockaddr
*address
)
1100 SCNetworkReachabilityPrivateRef targetPrivate
;
1102 if ((address
== NULL
) ||
1103 (address
->sa_len
== 0) ||
1104 (address
->sa_len
> sizeof(struct sockaddr_storage
))) {
1105 _SCErrorSet(kSCStatusInvalidArgument
);
1109 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1110 if (targetPrivate
== NULL
) {
1114 targetPrivate
->type
= reachabilityTypeAddress
;
1115 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
1116 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
1118 return (SCNetworkReachabilityRef
)targetPrivate
;
1122 SCNetworkReachabilityRef
1123 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
1124 const struct sockaddr
*localAddress
,
1125 const struct sockaddr
*remoteAddress
)
1127 SCNetworkReachabilityPrivateRef targetPrivate
;
1129 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
1130 _SCErrorSet(kSCStatusInvalidArgument
);
1134 if (localAddress
!= NULL
) {
1135 if ((localAddress
->sa_len
== 0) ||
1136 (localAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1137 _SCErrorSet(kSCStatusInvalidArgument
);
1142 if (remoteAddress
!= NULL
) {
1143 if ((remoteAddress
->sa_len
== 0) ||
1144 (remoteAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1145 _SCErrorSet(kSCStatusInvalidArgument
);
1150 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1151 if (targetPrivate
== NULL
) {
1155 targetPrivate
->type
= reachabilityTypeAddressPair
;
1157 if (localAddress
!= NULL
) {
1158 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
1159 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
1162 if (remoteAddress
!= NULL
) {
1163 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
1164 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
1167 return (SCNetworkReachabilityRef
)targetPrivate
;
1171 SCNetworkReachabilityRef
1172 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
1173 const char *nodename
)
1175 struct sockaddr_in sin
;
1176 struct sockaddr_in6 sin6
;
1177 SCNetworkReachabilityPrivateRef targetPrivate
;
1179 if (nodename
== NULL
) {
1180 _SCErrorSet(kSCStatusInvalidArgument
);
1184 /* check if this "nodename" is really an IP[v6] address in disguise */
1186 bzero(&sin
, sizeof(sin
));
1187 sin
.sin_len
= sizeof(sin
);
1188 sin
.sin_family
= AF_INET
;
1189 if (inet_aton(nodename
, &sin
.sin_addr
) == 1) {
1190 /* if IPv4 address */
1191 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin
);
1194 bzero(&sin6
, sizeof(sin6
));
1195 sin6
.sin6_len
= sizeof(sin6
);
1196 sin6
.sin6_family
= AF_INET6
;
1197 if (inet_pton(AF_INET6
, nodename
, &sin6
.sin6_addr
) == 1) {
1198 /* if IPv6 address */
1201 p
= strchr(nodename
, '%');
1203 sin6
.sin6_scope_id
= if_nametoindex(p
+1);
1206 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin6
);
1209 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1210 if (targetPrivate
== NULL
) {
1214 targetPrivate
->type
= reachabilityTypeName
;
1216 targetPrivate
->flags
|= kSCNetworkFlagsFirstResolvePending
;
1218 targetPrivate
->name
= CFAllocatorAllocate(NULL
, strlen(nodename
) + 1, 0);
1219 strcpy((char *)targetPrivate
->name
, nodename
);
1221 return (SCNetworkReachabilityRef
)targetPrivate
;
1226 SCNetworkReachabilityGetTypeID(void)
1228 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
1229 return __kSCNetworkReachabilityTypeID
;
1234 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
1237 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1239 if (!isA_SCNetworkReachability(target
)) {
1240 _SCErrorSet(kSCStatusInvalidArgument
);
1244 if (targetPrivate
->type
!= reachabilityTypeName
) {
1245 _SCErrorSet(kSCStatusInvalidArgument
);
1250 *error_num
= targetPrivate
->resolvedAddressError
;
1253 if (targetPrivate
->resolvedAddress
|| (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1254 if (targetPrivate
->resolvedAddress
!= NULL
) {
1255 return CFRetain(targetPrivate
->resolvedAddress
);
1257 /* if status is known but no resolved addresses to return */
1258 _SCErrorSet(kSCStatusOK
);
1263 _SCErrorSet(kSCStatusReachabilityUnknown
);
1269 __SCNetworkReachabilitySetResolvedAddress(int32_t status
,
1270 struct addrinfo
*res
,
1271 SCNetworkReachabilityRef target
)
1273 struct addrinfo
*resP
;
1274 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1276 if (targetPrivate
->resolvedAddress
!= NULL
) {
1277 CFRelease(targetPrivate
->resolvedAddress
);
1278 targetPrivate
->resolvedAddress
= NULL
;
1281 if ((status
== 0) && (res
!= NULL
)) {
1283 CFMutableArrayRef addresses
;
1285 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1287 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
1288 CFDataRef newAddress
;
1290 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
1291 CFArrayAppendValue(addresses
, newAddress
);
1292 CFRelease(newAddress
);
1295 /* save the resolved address[es] */
1296 targetPrivate
->resolvedAddress
= addresses
;
1297 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1299 SCLog(_sc_debug
, LOG_INFO
, CFSTR("getaddrinfo() failed: %s"), gai_strerror(status
));
1301 /* save the error associated with the attempt to resolve the name */
1302 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
1303 targetPrivate
->resolvedAddressError
= status
;
1306 if (res
) freeaddrinfo(res
);
1308 if (targetPrivate
->rls
!= NULL
) {
1309 SCLog(_sc_debug
, LOG_INFO
, CFSTR("DNS request completed"));
1310 CFRunLoopSourceSignal(targetPrivate
->rls
);
1311 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1319 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo
*res
, void *context
)
1321 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
1322 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1324 __log_query_time(((status
== 0) && (res
!= NULL
)), // if successful query
1326 &targetPrivate
->dnsQueryStart
); // start time
1328 __SCNetworkReachabilitySetResolvedAddress(status
, res
, target
);
1334 * rankReachability()
1335 * Not reachable == 0
1336 * Connection Required == 1
1340 rankReachability(SCNetworkConnectionFlags flags
)
1344 if (flags
& kSCNetworkFlagsReachable
) rank
= 2;
1345 if (flags
& kSCNetworkFlagsConnectionRequired
) rank
= 1;
1351 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1354 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1355 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1357 pthread_mutex_lock(&targetPrivate
->lock
);
1359 status
= getaddrinfo_async_handle_reply(msg
);
1360 if ((status
== 0) &&
1361 (targetPrivate
->resolvedAddress
== NULL
) && (targetPrivate
->resolvedAddressError
== NETDB_SUCCESS
)) {
1362 // if request has been re-queued
1366 if (port
== targetPrivate
->dnsPort
) {
1367 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
1368 CFRelease(targetPrivate
->dnsRLS
);
1369 targetPrivate
->dnsRLS
= NULL
;
1370 CFRelease(targetPrivate
->dnsPort
);
1371 targetPrivate
->dnsPort
= NULL
;
1376 pthread_mutex_unlock(&targetPrivate
->lock
);
1383 check_resolver_reachability(SCDynamicStoreRef
*storeP
,
1384 dns_resolver_t
*resolver
,
1385 SCNetworkConnectionFlags
*flags
,
1391 *flags
= kSCNetworkFlagsReachable
;
1394 for (i
= 0; i
< resolver
->n_nameserver
; i
++) {
1395 struct sockaddr
*address
= resolver
->nameserver
[i
];
1396 SCNetworkConnectionFlags ns_flags
= 0;
1400 if (address
->sa_family
!= AF_INET
) {
1402 * we need to skip non-IPv4 DNS server
1403 * addresses (at least until [3510431] has
1409 ok
= checkAddress(storeP
, address
, &ns_flags
, NULL
);
1415 if (rankReachability(ns_flags
) < rankReachability(*flags
)) {
1416 /* return the worst case result */
1428 check_matching_resolvers(SCDynamicStoreRef
*storeP
,
1429 dns_config_t
*dns_config
,
1431 SCNetworkConnectionFlags
*flags
,
1435 Boolean matched
= FALSE
;
1436 const char *name
= fqdn
;
1438 while (!matched
&& (name
!= NULL
)) {
1442 * check if the provided name (or sub-component)
1443 * matches one of our resolver configurations.
1446 for (i
= 0; i
< dns_config
->n_resolver
; i
++) {
1448 dns_resolver_t
*resolver
;
1450 resolver
= dns_config
->resolver
[i
];
1451 domain
= resolver
->domain
;
1452 if (domain
!= NULL
&& (len
== strlen(domain
))) {
1453 if (strcasecmp(name
, domain
) == 0) {
1457 * if name matches domain
1460 ok
= check_resolver_reachability(storeP
, resolver
, flags
, haveDNS
);
1471 * we have not found a matching resolver, try
1472 * a less qualified domain
1474 name
= strchr(name
, '.');
1475 if ((name
!= NULL
) && (*name
!= '\0')) {
1487 static dns_configuration_t
*
1488 dns_configuration_retain()
1490 pthread_mutex_lock(&dns_lock
);
1492 if ((dns_configuration
!= NULL
) && dns_token_valid
) {
1497 * check if the global [DNS] configuration snapshot needs
1500 status
= notify_check(dns_token
, &check
);
1501 if (status
!= NOTIFY_STATUS_OK
) {
1502 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
1505 if ((status
!= NOTIFY_STATUS_OK
) || (check
!= 0)) {
1507 * if the snapshot needs to be refreshed
1509 if (dns_configuration
->refs
== 0) {
1510 dns_configuration_free(dns_configuration
->config
);
1511 CFAllocatorDeallocate(NULL
, dns_configuration
);
1513 dns_configuration
= NULL
;
1517 if (dns_configuration
== NULL
) {
1518 dns_config_t
*new_config
;
1520 new_config
= dns_configuration_copy();
1521 if (new_config
!= NULL
) {
1522 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
1523 dns_configuration
->config
= new_config
;
1524 dns_configuration
->refs
= 0;
1528 if (dns_configuration
!= NULL
) {
1529 dns_configuration
->refs
++;
1532 pthread_mutex_unlock(&dns_lock
);
1533 return dns_configuration
;
1538 dns_configuration_release(dns_configuration_t
*config
)
1540 pthread_mutex_lock(&dns_lock
);
1543 if (config
->refs
== 0) {
1544 if ((dns_configuration
!= config
)) {
1545 dns_configuration_free(config
->config
);
1546 CFAllocatorDeallocate(NULL
, config
);
1550 pthread_mutex_unlock(&dns_lock
);
1556 dns_configuration_watch()
1559 const char *dns_key
;
1563 pthread_mutex_lock(&dns_lock
);
1565 dns_key
= dns_configuration_notify_key();
1566 if (dns_key
== NULL
) {
1567 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
1571 status
= notify_register_check(dns_key
, &dns_token
);
1572 if (status
== NOTIFY_STATUS_OK
) {
1573 dns_token_valid
= TRUE
;
1575 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
);
1579 status
= notify_check(dns_token
, &dns_check
);
1580 if (status
!= NOTIFY_STATUS_OK
) {
1581 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
1582 (void)notify_cancel(dns_token
);
1583 dns_token_valid
= FALSE
;
1591 pthread_mutex_unlock(&dns_lock
);
1597 dns_configuration_unwatch()
1599 pthread_mutex_lock(&dns_lock
);
1601 (void)notify_cancel(dns_token
);
1602 dns_token_valid
= FALSE
;
1604 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
1605 dns_configuration_free(dns_configuration
->config
);
1606 CFAllocatorDeallocate(NULL
, dns_configuration
);
1607 dns_configuration
= NULL
;
1610 pthread_mutex_unlock(&dns_lock
);
1616 _SC_checkResolverReachability(SCDynamicStoreRef
*storeP
,
1617 SCNetworkConnectionFlags
*flags
,
1619 const char * nodename
)
1621 dns_resolver_t
*default_resolver
;
1622 dns_configuration_t
*dns
;
1623 Boolean found
= FALSE
;
1624 char *fqdn
= (char *)nodename
;
1626 Boolean isFQDN
= FALSE
;
1631 * We first assume that all of the configured DNS servers
1632 * are available. Since we don't know which name server will
1633 * be consulted to resolve the specified nodename we need to
1634 * check the availability of ALL name servers. We can only
1635 * proceed if we know that our query can be answered.
1638 *flags
= kSCNetworkFlagsReachable
;
1643 // if no nodename, return not reachable
1648 dns
= dns_configuration_retain();
1654 if (dns
->config
->n_resolver
== 0) {
1655 // if no resolver configuration
1659 *flags
= kSCNetworkFlagsReachable
;
1661 if (fqdn
[len
- 1] == '.') {
1664 // trim trailing '.''s
1665 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
1666 if (fqdn
== nodename
) {
1667 fqdn
= strdup(nodename
);
1673 default_resolver
= dns
->config
->resolver
[0];
1676 * try the provided name
1678 found
= check_matching_resolvers(storeP
, dns
->config
, fqdn
, flags
, haveDNS
);
1679 if (!found
&& !isFQDN
&& (dns
->config
->n_resolver
> 1)) {
1681 * FQDN not specified, try w/search or default domain(s) too
1683 if (default_resolver
->n_search
> 0) {
1684 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
1686 char *search_fqdn
= NULL
;
1688 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
1693 // try the provided name with the search domain appended
1694 found
= check_matching_resolvers(storeP
, dns
->config
, search_fqdn
, flags
, haveDNS
);
1697 } else if (default_resolver
->domain
!= NULL
) {
1699 int domain_parts
= 1;
1701 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
1707 dp
= default_resolver
->domain
;
1708 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= domain_parts
); i
++) {
1710 char *search_fqdn
= NULL
;
1712 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
1717 // try the provided name with the [default] domain appended
1718 found
= check_matching_resolvers(storeP
, dns
->config
, search_fqdn
, flags
, haveDNS
);
1721 // move to the next component of the [default] domain
1722 dp
= strchr(dp
, '.') + 1;
1729 * check the reachability of the default resolver
1731 ok
= check_resolver_reachability(storeP
, default_resolver
, flags
, haveDNS
);
1734 if (fqdn
!= nodename
) free(fqdn
);
1739 dns_configuration_release(dns
);
1747 startAsyncDNSQuery(SCNetworkReachabilityRef target
) {
1748 CFMachPortContext context
= { 0, (void *)target
, CFRetain
, CFRelease
, CFCopyDescription
};
1750 struct addrinfo hints
;
1754 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1756 (void) gettimeofday(&targetPrivate
->dnsQueryStart
, NULL
);
1758 bzero(&hints
, sizeof(hints
));
1759 hints
.ai_flags
= AI_ADDRCONFIG
;
1761 hints
.ai_flags
|= AI_PARALLEL
;
1762 #endif /* AI_PARALLEL */
1764 error
= getaddrinfo_async_start(&port
,
1765 targetPrivate
->name
,
1768 __SCNetworkReachabilityCallbackSetResolvedAddress
,
1771 /* save the error associated with the attempt to resolve the name */
1772 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
);
1776 targetPrivate
->dnsPort
= CFMachPortCreateWithPort(NULL
,
1778 getaddrinfo_async_handleCFReply
,
1781 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
1783 n
= CFArrayGetCount(targetPrivate
->rlList
);
1784 for (i
= 0; i
< n
; i
+= 3) {
1785 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
1786 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
1788 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
1796 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef
*storeP
,
1797 SCNetworkReachabilityRef target
,
1798 SCNetworkConnectionFlags
*flags
,
1802 CFMutableArrayRef addresses
= NULL
;
1803 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1804 SCNetworkConnectionFlags my_flags
= 0;
1805 uint16_t my_index
= 0;
1809 if (if_index
!= NULL
) {
1813 if (!isA_SCNetworkReachability(target
)) {
1814 _SCErrorSet(kSCStatusInvalidArgument
);
1818 switch (targetPrivate
->type
) {
1819 case reachabilityTypeAddress
:
1820 case reachabilityTypeAddressPair
: {
1822 * Check "local" address
1824 if (targetPrivate
->localAddress
!= NULL
) {
1826 * Check "local" address
1828 ok
= checkAddress(storeP
, targetPrivate
->localAddress
, &my_flags
, &my_index
);
1830 goto error
; /* not today */
1833 if (!(my_flags
& kSCNetworkFlagsIsLocalAddress
)) {
1834 goto error
; /* not reachable, non-"local" address */
1839 * Check "remote" address
1841 if (targetPrivate
->remoteAddress
!= NULL
) {
1843 * in cases where we have "local" and "remote" addresses
1844 * we need to re-initialize the to-be-returned flags.
1850 * Check "remote" address
1852 ok
= checkAddress(storeP
, targetPrivate
->remoteAddress
, &my_flags
, &my_index
);
1854 goto error
; /* not today */
1862 case reachabilityTypeName
: {
1863 struct timeval dnsQueryStart
;
1865 struct addrinfo hints
;
1866 SCNetworkConnectionFlags ns_flags
;
1867 struct addrinfo
*res
;
1869 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
1870 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
1871 /* if resolved or an error had been detected */
1872 goto checkResolvedAddress
;
1875 /* check the reachability of the DNS servers */
1876 ok
= _SC_checkResolverReachability(storeP
,
1878 &targetPrivate
->haveDNS
,
1879 targetPrivate
->name
);
1881 /* if we could not get DNS server info */
1885 if (rankReachability(ns_flags
) < 2) {
1887 * if DNS servers are not (or are no longer) reachable, set
1888 * flags based on the availability of configured (but not
1891 if (!checkAddress(storeP
, NULL
, &my_flags
, &my_index
)) {
1895 if (async
&& (targetPrivate
->rls
!= NULL
)) {
1897 * return "host not found", set flags appropriately,
1898 * and schedule notification.
1900 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NODATA
,
1903 my_flags
|= (targetPrivate
->flags
& kSCNetworkFlagsFirstResolvePending
);
1905 SCLog(_sc_debug
, LOG_INFO
, CFSTR("no DNS servers are reachable"));
1906 CFRunLoopSourceSignal(targetPrivate
->rls
);
1907 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1913 /* for async requests we return the last known status */
1914 my_flags
= targetPrivate
->flags
;
1915 my_index
= targetPrivate
->if_index
;
1917 if (targetPrivate
->dnsPort
) {
1918 /* if request already in progress */
1922 SCLog(_sc_debug
, LOG_INFO
, CFSTR("start DNS query for \"%s\""), targetPrivate
->name
);
1925 * initiate an async DNS query
1927 if (!startAsyncDNSQuery(target
)) {
1928 /* if we could not initiate the request, process error */
1929 goto checkResolvedAddress
;
1932 /* if request initiated */
1936 SCLog(_sc_debug
, LOG_INFO
, CFSTR("check DNS for \"%s\""), targetPrivate
->name
);
1939 * OK, all of the DNS name servers are available. Let's
1940 * resolve the nodename into an address.
1943 (void) gettimeofday(&dnsQueryStart
, NULL
);
1946 bzero(&hints
, sizeof(hints
));
1947 hints
.ai_flags
= AI_ADDRCONFIG
;
1949 hints
.ai_flags
|= AI_PARALLEL
;
1950 #endif /* AI_PARALLEL */
1952 error
= getaddrinfo(targetPrivate
->name
, NULL
, &hints
, &res
);
1954 __log_query_time(((error
== 0) && (res
!= NULL
)),// if successful query
1956 &dnsQueryStart
); // start time
1958 __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
);
1960 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
1962 checkResolvedAddress
:
1965 * We first assume that the requested host is NOT available.
1966 * Then, check each address for accessibility and return the
1967 * best status available.
1972 if (isA_CFArray(addresses
)) {
1974 CFIndex n
= CFArrayGetCount(addresses
);
1976 for (i
= 0; i
< n
; i
++) {
1977 SCNetworkConnectionFlags ns_flags
= 0;
1978 uint16_t ns_if_index
= 0;
1979 struct sockaddr
*sa
;
1981 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
1983 ok
= checkAddress(storeP
, sa
, &ns_flags
, &ns_if_index
);
1985 goto error
; /* not today */
1988 if (rankReachability(ns_flags
) > rankReachability(my_flags
)) {
1989 /* return the best case result */
1990 my_flags
= ns_flags
;
1991 my_index
= ns_if_index
;
1992 if (rankReachability(my_flags
) == 2) {
1999 if ((error
== EAI_NODATA
) && !targetPrivate
->haveDNS
) {
2001 * No DNS servers are defined. Set flags based on
2002 * the availability of configured (but not active)
2005 ok
= checkAddress(storeP
, NULL
, &my_flags
, &my_index
);
2007 goto error
; /* not today */
2010 if ((my_flags
& kSCNetworkFlagsReachable
) &&
2011 (my_flags
& kSCNetworkFlagsConnectionRequired
)) {
2013 * Since we might pick up a set of DNS servers when this connection
2014 * is established, don't reply with a "HOST NOT FOUND" error just yet.
2019 /* Host not found, not reachable! */
2030 if (if_index
!= NULL
) {
2031 *if_index
= my_index
;
2036 if (addresses
!= NULL
) CFRelease(addresses
);
2042 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
2043 SCNetworkConnectionFlags
*flags
)
2046 SCDynamicStoreRef store
= NULL
;
2047 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2049 if (!isA_SCNetworkReachability(target
)) {
2050 _SCErrorSet(kSCStatusInvalidArgument
);
2054 if (targetPrivate
->rlList
!= NULL
) {
2055 // if being watched, return the last known (and what should be current) status
2056 *flags
= targetPrivate
->flags
& ~kSCNetworkFlagsFirstResolvePending
;
2060 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, flags
, NULL
, FALSE
);
2061 *flags
&= ~kSCNetworkFlagsFirstResolvePending
;
2062 if (store
!= NULL
) CFRelease(store
);
2068 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
2070 CFStringRef dns_key
;
2072 CFMutableArrayRef keys
;
2073 CFStringRef pattern
;
2074 CFMutableArrayRef patterns
;
2076 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2077 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2079 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
2080 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2081 kSCDynamicStoreDomainSetup
,
2083 CFArrayAppendValue(keys
, key
);
2086 dns_key
= CFStringCreateWithCString(NULL
,
2087 dns_configuration_notify_key(),
2088 kCFStringEncodingASCII
);
2089 key
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Notify:%@"), dns_key
);
2091 CFArrayAppendValue(keys
, key
);
2094 // State:/Network/Global/IPv4 (default route)
2095 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2096 kSCDynamicStoreDomainState
,
2098 CFArrayAppendValue(keys
, key
);
2101 // Setup: per-service IPv4 info
2102 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2103 kSCDynamicStoreDomainSetup
,
2106 CFArrayAppendValue(patterns
, pattern
);
2109 // Setup: per-service Interface info
2110 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2111 kSCDynamicStoreDomainSetup
,
2113 kSCEntNetInterface
);
2114 CFArrayAppendValue(patterns
, pattern
);
2117 // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand)
2118 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2119 kSCDynamicStoreDomainSetup
,
2122 CFArrayAppendValue(patterns
, pattern
);
2125 // State: per-interface IPv4 info
2126 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2127 kSCDynamicStoreDomainState
,
2130 CFArrayAppendValue(patterns
, pattern
);
2133 // State: per-interface IPv6 info
2134 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2135 kSCDynamicStoreDomainState
,
2138 CFArrayAppendValue(patterns
, pattern
);
2141 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
2143 CFRelease(patterns
);
2150 __SCNetworkReachabilityReachabilityHandleChanges(SCDynamicStoreRef store
,
2151 CFArrayRef changedKeys
,
2154 Boolean dnsConfigChanged
= FALSE
;
2159 const void * targets_q
[N_QUICK
];
2160 const void ** targets
= targets_q
;
2162 pthread_mutex_lock(&hn_lock
);
2164 nTargets
= CFSetGetCount(hn_targets
);
2165 if (nTargets
== 0) {
2166 /* if no addresses being monitored */
2170 if (CFArrayGetCount(changedKeys
) == 0) {
2175 SCLog(_sc_debug
, LOG_INFO
, CFSTR("process configuration change"));
2177 dnsKey
= CFStringCreateWithCString(NULL
,
2178 dns_configuration_notify_key(),
2179 kCFStringEncodingASCII
);
2180 key
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Notify:%@"), dnsKey
);
2182 if (CFArrayContainsValue(changedKeys
,
2183 CFRangeMake(0, CFArrayGetCount(changedKeys
)),
2185 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
2190 SCLog(_sc_debug
&& dnsConfigChanged
, LOG_INFO
, CFSTR(" DNS configuration changed"));
2192 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
2193 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
2194 CFSetGetValues(hn_targets
, targets
);
2195 for (i
= 0; i
< nTargets
; i
++) {
2196 SCNetworkReachabilityRef target
= targets
[i
];
2197 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2199 pthread_mutex_lock(&targetPrivate
->lock
);
2201 if (targetPrivate
->type
== reachabilityTypeName
) {
2202 Boolean dnsChanged
= dnsConfigChanged
;
2206 * if the DNS configuration didn't change we still need to
2207 * check that the DNS servers are accessible.
2209 SCNetworkConnectionFlags ns_flags
;
2212 /* check the reachability of the DNS servers */
2213 ok
= _SC_checkResolverReachability(&store
,
2215 &targetPrivate
->haveDNS
,
2216 targetPrivate
->name
);
2217 if (!ok
|| (rankReachability(ns_flags
) < 2)) {
2218 /* if DNS servers are not reachable */
2224 if (targetPrivate
->dnsPort
) {
2225 /* cancel the outstanding DNS query */
2226 lu_async_call_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2227 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
2228 CFRelease(targetPrivate
->dnsRLS
);
2229 targetPrivate
->dnsRLS
= NULL
;
2230 CFRelease(targetPrivate
->dnsPort
);
2231 targetPrivate
->dnsPort
= NULL
;
2234 /* schedule request to resolve the name again */
2235 if (targetPrivate
->resolvedAddress
!= NULL
) {
2236 CFRelease(targetPrivate
->resolvedAddress
);
2237 targetPrivate
->resolvedAddress
= NULL
;
2239 targetPrivate
->resolvedAddress
= NULL
;
2240 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
2244 CFRunLoopSourceSignal(targetPrivate
->rls
);
2245 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2247 pthread_mutex_unlock(&targetPrivate
->lock
);
2249 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
2253 pthread_mutex_unlock(&hn_lock
);
2259 rlsPerform(void *info
)
2262 void (*context_release
)(const void *);
2263 SCNetworkConnectionFlags flags
;
2266 SCNetworkReachabilityCallBack rlsFunction
;
2267 SCDynamicStoreRef store
= NULL
;
2268 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2269 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2271 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("process reachability change"));
2273 pthread_mutex_lock(&targetPrivate
->lock
);
2275 /* update reachability, notify if status changed */
2276 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
);
2277 if (store
!= NULL
) CFRelease(store
);
2279 /* if reachability status not available */
2284 if ((targetPrivate
->flags
== flags
) && (targetPrivate
->if_index
== if_index
)) {
2285 /* if reachability flags and interface have not changed */
2286 pthread_mutex_unlock(&targetPrivate
->lock
);
2287 SCLog(_sc_debug
, LOG_DEBUG
,
2288 CFSTR("flags/interface match (now %8.8x/%hu)"),
2292 SCLog(_sc_debug
, LOG_DEBUG
,
2293 CFSTR("flags/interface have changed (was %8.8x/%hu, now %8.8x/%hu)"),
2294 targetPrivate
->flags
, targetPrivate
->if_index
,
2298 /* update flags / interface */
2299 targetPrivate
->flags
= flags
;
2300 targetPrivate
->if_index
= if_index
;
2303 rlsFunction
= targetPrivate
->rlsFunction
;
2304 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
2305 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
2306 context_release
= targetPrivate
->rlsContext
.release
;
2308 context_info
= targetPrivate
->rlsContext
.info
;
2309 context_release
= NULL
;
2312 pthread_mutex_unlock(&targetPrivate
->lock
);
2314 if (rlsFunction
!= NULL
) {
2315 (*rlsFunction
)(target
, flags
, context_info
);
2318 if (context_release
!= NULL
) {
2319 (*context_release
)(context_info
);
2327 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
2328 SCNetworkReachabilityCallBack callout
,
2329 SCNetworkReachabilityContext
*context
)
2331 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2333 pthread_mutex_lock(&targetPrivate
->lock
);
2335 if (targetPrivate
->rlsContext
.release
!= NULL
) {
2336 /* let go of the current context */
2337 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
2340 targetPrivate
->rlsFunction
= callout
;
2341 targetPrivate
->rlsContext
.info
= NULL
;
2342 targetPrivate
->rlsContext
.retain
= NULL
;
2343 targetPrivate
->rlsContext
.release
= NULL
;
2344 targetPrivate
->rlsContext
.copyDescription
= NULL
;
2346 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
2347 if (context
->retain
!= NULL
) {
2348 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
2352 pthread_mutex_unlock(&targetPrivate
->lock
);
2359 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
2360 CFRunLoopRef runLoop
,
2361 CFStringRef runLoopMode
)
2363 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2364 Boolean init
= FALSE
;
2367 if (!isA_SCNetworkReachability(target
) || runLoop
== NULL
|| runLoopMode
== NULL
) {
2368 _SCErrorSet(kSCStatusInvalidArgument
);
2372 /* schedule the SCNetworkReachability run loop source */
2374 pthread_mutex_lock(&hn_lock
);
2375 pthread_mutex_lock(&targetPrivate
->lock
);
2377 if (hn_store
== NULL
) {
2379 * if we are not monitoring any hosts, start watching
2381 if (!dns_configuration_watch()) {
2383 _SCErrorSet(kSCStatusFailed
);
2387 hn_store
= SCDynamicStoreCreate(NULL
,
2388 CFSTR("SCNetworkReachability"),
2389 __SCNetworkReachabilityReachabilityHandleChanges
,
2391 if (hn_store
== NULL
) {
2392 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
2396 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
2398 hn_storeRLS
= SCDynamicStoreCreateRunLoopSource(NULL
, hn_store
, 0);
2399 hn_rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2400 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
2403 if (targetPrivate
->rls
== NULL
) {
2404 CFRunLoopSourceContext context
= { 0 // version
2405 , (void *)target
// info
2406 , CFRetain
// retain
2407 , CFRelease
// release
2408 , CFCopyDescription
// copyDescription
2413 , rlsPerform
// perform
2416 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
2417 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2421 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2423 * if we do not already have host notifications scheduled with
2424 * this runLoop / runLoopMode
2426 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2428 if (targetPrivate
->dnsRLS
!= NULL
) {
2429 /* if we have an active async DNS query too */
2430 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2434 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
2436 /* schedule the SCNetworkReachability run loop source */
2438 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2440 * if we do not already have SC notifications scheduled with
2441 * this runLoop / runLoopMode
2443 CFRunLoopAddSource(runLoop
, hn_storeRLS
, runLoopMode
);
2446 _SC_schedule(target
, runLoop
, runLoopMode
, hn_rlList
);
2447 CFSetAddValue(hn_targets
, target
);
2450 SCNetworkConnectionFlags flags
;
2452 SCDynamicStoreRef store
= NULL
;
2455 * if we have yet to schedule SC notifications for this address
2456 * - initialize current reachability status
2458 if (__SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
)) {
2460 * if reachability status available
2462 * - schedule notification to report status via callback
2464 targetPrivate
->flags
= flags
;
2465 targetPrivate
->if_index
= if_index
;
2466 CFRunLoopSourceSignal(targetPrivate
->rls
);
2467 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2469 /* if reachability status not available, async lookup started */
2470 targetPrivate
->flags
= 0;
2471 targetPrivate
->if_index
= 0;
2473 if (store
!= NULL
) CFRelease(store
);
2480 pthread_mutex_unlock(&targetPrivate
->lock
);
2481 pthread_mutex_unlock(&hn_lock
);
2487 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
2488 CFRunLoopRef runLoop
,
2489 CFStringRef runLoopMode
)
2491 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2495 if (!isA_SCNetworkReachability(target
) || runLoop
== NULL
|| runLoopMode
== NULL
) {
2496 _SCErrorSet(kSCStatusInvalidArgument
);
2500 pthread_mutex_lock(&hn_lock
);
2501 pthread_mutex_lock(&targetPrivate
->lock
);
2503 if (targetPrivate
->rls
== NULL
) {
2504 /* if not currently scheduled */
2508 if (!_SC_unschedule(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
2509 /* if not currently scheduled */
2513 n
= CFArrayGetCount(targetPrivate
->rlList
);
2514 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2516 * if this host is no longer scheduled for this runLoop / runLoopMode
2518 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2520 if (targetPrivate
->dnsRLS
!= NULL
) {
2521 /* if we have an active async DNS query too */
2522 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2527 * if this host is no longer scheduled
2529 CFRunLoopSourceInvalidate(targetPrivate
->rls
); /* cleanup SCNetworkReachability resources */
2530 CFRelease(targetPrivate
->rls
);
2531 targetPrivate
->rls
= NULL
;
2532 CFRelease(targetPrivate
->rlList
);
2533 targetPrivate
->rlList
= NULL
;
2534 CFSetRemoveValue(hn_targets
, target
); /* cleanup notification resources */
2536 if (targetPrivate
->dnsPort
) {
2537 /* if we have an active async DNS query too */
2538 lu_async_call_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2539 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
2540 CFRelease(targetPrivate
->dnsRLS
);
2541 targetPrivate
->dnsRLS
= NULL
;
2542 CFRelease(targetPrivate
->dnsPort
);
2543 targetPrivate
->dnsPort
= NULL
;
2548 (void)_SC_unschedule(target
, runLoop
, runLoopMode
, hn_rlList
, FALSE
);
2550 n
= CFArrayGetCount(hn_rlList
);
2551 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2553 * if we no longer have any addresses scheduled for
2554 * this runLoop / runLoopMode
2556 CFRunLoopRemoveSource(runLoop
, hn_storeRLS
, runLoopMode
);
2560 * if we are no longer monitoring any addresses
2562 CFRelease(hn_targets
);
2564 CFRelease(hn_rlList
);
2566 CFRunLoopSourceInvalidate(hn_storeRLS
);
2567 CFRelease(hn_storeRLS
);
2569 CFRelease(hn_store
);
2573 * until we start monitoring again, ensure that
2574 * any resources associated with tracking the
2575 * DNS configuration have been released.
2577 dns_configuration_unwatch();
2585 pthread_mutex_unlock(&targetPrivate
->lock
);
2586 pthread_mutex_unlock(&hn_lock
);