2 * Copyright (c) 2003 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 * January 19, 2003 Allan Nathanson <ajn@apple.com>
28 * - add advanced reachability APIs
31 #include <SystemConfiguration/SystemConfiguration.h>
32 #include <SystemConfiguration/SCValidation.h>
33 #include <SystemConfiguration/SCPrivate.h>
35 #include <CoreFoundation/CFRuntime.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <arpa/nameser.h>
42 #include <netdb_async.h>
45 #include <sys/ioctl.h>
46 #include <sys/socket.h>
48 #include <net/if_dl.h>
49 #define KERNEL_PRIVATE
50 #include <net/route.h>
54 #define s6_addr16 __u6_addr.__u6_addr16
60 #define kSCNetworkFlagsFirstResolvePending (1<<31)
67 reachabilityTypeAddress
,
68 reachabilityTypeAddressPair
,
73 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
74 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
79 /* base CFType information */
88 /* target host name */
90 CFArrayRef resolvedAddress
; /* CFArray[CFData] */
91 int resolvedAddressError
;
93 /* local & remote addresses */
94 struct sockaddr
*localAddress
;
95 struct sockaddr
*remoteAddress
;
97 /* current reachability flags */
98 SCNetworkConnectionFlags flags
;
101 /* run loop source, callout, context, rl scheduling info */
102 CFRunLoopSourceRef rls
;
103 SCNetworkReachabilityCallBack rlsFunction
;
104 SCNetworkReachabilityContext rlsContext
;
105 CFMutableArrayRef rlList
;
107 /* async DNS query info */
108 CFMachPortRef dnsPort
;
109 CFRunLoopSourceRef dnsRLS
;
111 } SCNetworkReachabilityPrivate
, *SCNetworkReachabilityPrivateRef
;
114 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
117 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
119 "SCNetworkReachability", // className
122 __SCNetworkReachabilityDeallocate
, // dealloc
125 NULL
, // copyFormattingDesc
126 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
130 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
131 static Boolean needDNS
= TRUE
;
134 /* host "something has changed" notifications */
135 static pthread_mutex_t hn_lock
= PTHREAD_MUTEX_INITIALIZER
;
136 static SCDynamicStoreRef hn_store
= NULL
;
137 static CFRunLoopSourceRef hn_storeRLS
= NULL
;
138 static CFMutableArrayRef hn_rlList
= NULL
;
139 static CFMutableSetRef hn_targets
= NULL
;
142 static __inline__ CFTypeRef
143 isA_SCNetworkReachability(CFTypeRef obj
)
145 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
150 sockaddr_to_string(const struct sockaddr
*address
, char *buf
, size_t bufLen
)
153 switch (address
->sa_family
) {
155 (void)inet_ntop(((struct sockaddr_in
*)address
)->sin_family
,
156 &((struct sockaddr_in
*)address
)->sin_addr
,
161 (void)inet_ntop(((struct sockaddr_in6
*)address
)->sin6_family
,
162 &((struct sockaddr_in6
*)address
)->sin6_addr
,
165 if (((struct sockaddr_in6
*)address
)->sin6_scope_id
!= 0) {
169 if ((n
+IF_NAMESIZE
+1) <= (int)bufLen
) {
171 if_indextoname(((struct sockaddr_in6
*)address
)->sin6_scope_id
, &buf
[n
]);
177 if (((struct sockaddr_dl
*)address
)->sdl_len
< bufLen
) {
178 bufLen
= ((struct sockaddr_dl
*)address
)->sdl_len
;
183 bcopy(((struct sockaddr_dl
*)address
)->sdl_data
, buf
, bufLen
);
186 snprintf(buf
, bufLen
, "unexpected address family %d", address
->sa_family
);
194 #ifndef CHECK_IPV6_REACHABILITY
196 __netdb_error(int error
)
201 case NETDB_INTERNAL
:
202 msg
= strerror(errno
);
204 case HOST_NOT_FOUND
:
205 msg
= "Host not found.";
211 msg
= "No recovery.";
214 msg
= "No data available.";
223 #endif /* CHECK_IPV6_REACHABILITY */
227 __signalRunLoop(CFTypeRef obj
, CFRunLoopSourceRef rls
, CFArrayRef rlList
)
229 CFRunLoopRef rl
= NULL
;
230 CFRunLoopRef rl1
= NULL
;
232 CFIndex n
= CFArrayGetCount(rlList
);
238 /* get first runLoop for this object */
239 for (i
= 0; i
< n
; i
+= 3) {
240 if (!CFEqual(obj
, CFArrayGetValueAtIndex(rlList
, i
))) {
244 rl1
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
249 /* if not scheduled */
253 /* check if we have another runLoop for this object */
255 for (i
= i
+3; i
< n
; i
+= 3) {
258 if (!CFEqual(obj
, CFArrayGetValueAtIndex(rlList
, i
))) {
262 rl2
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
263 if (!CFEqual(rl1
, rl2
)) {
264 /* we've got more than one runLoop */
271 /* if we only have one runLoop */
276 /* more than one different runLoop, so we must pick one */
277 for (i
= 0; i
< n
; i
+=3) {
280 if (!CFEqual(obj
, CFArrayGetValueAtIndex(rlList
, i
))) {
284 rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
285 rlMode
= CFRunLoopCopyCurrentMode(rl
);
286 if (rlMode
&& CFRunLoopIsWaiting(rl
) && CFRunLoopContainsSource(rl
, rls
, rlMode
)) {
287 /* we've found a runLoop that's "ready" */
292 if (rlMode
) CFRelease(rlMode
);
295 /* didn't choose one above, so choose first */
296 CFRunLoopWakeUp(rl1
);
302 updatePPPStatus(SCDynamicStoreRef
*storeP
,
303 const struct sockaddr
*sa
,
305 SCNetworkConnectionFlags
*flags
)
307 CFDictionaryRef dict
= NULL
;
310 const void * keys_q
[N_QUICK
];
311 const void ** keys
= keys_q
;
314 int sc_status
= kSCStatusReachabilityUnknown
;
315 SCDynamicStoreRef store
= (storeP
) ? *storeP
: NULL
;
316 const void * values_q
[N_QUICK
];
317 const void ** values
= values_q
;
319 switch (sa
->sa_family
) {
321 entity
= kSCEntNetIPv4
;
324 entity
= kSCEntNetIPv6
;
331 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
333 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
339 * grab a snapshot of the PPP configuration from the dynamic store
343 CFMutableArrayRef patterns
;
345 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
346 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
347 kSCDynamicStoreDomainState
,
350 CFArrayAppendValue(patterns
, pattern
);
352 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
353 kSCDynamicStoreDomainSetup
,
356 CFArrayAppendValue(patterns
, pattern
);
358 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
359 kSCDynamicStoreDomainState
,
362 CFArrayAppendValue(patterns
, pattern
);
364 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
368 /* if we could not access the dynamic store */
372 sc_status
= kSCStatusOK
;
375 * look for the service which matches the provided interface
377 n
= CFDictionaryGetCount(dict
);
382 ppp_if
= CFStringCreateWithCStringNoCopy(NULL
,
384 kCFStringEncodingASCII
,
387 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
388 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
389 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
391 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
393 for (i
=0; i
< n
; i
++) {
394 CFArrayRef components
;
397 CFDictionaryRef p_setup
;
398 CFDictionaryRef p_state
;
400 CFStringRef service
= NULL
;
401 CFStringRef s_key
= (CFStringRef
) keys
[i
];
402 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
405 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
409 if (!CFStringHasSuffix(s_key
, entity
)) {
410 continue; // if not an IPv4 or IPv6 entity
413 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
414 if (!isA_CFString(s_if
)) {
415 continue; // if no interface
418 if (!CFEqual(ppp_if
, s_if
)) {
419 continue; // if not this interface
423 * extract service ID, get PPP "state" entity (for status), and get
424 * the "setup" entity (for dial-on-traffic flag)
426 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
427 if (CFArrayGetCount(components
) != 5) {
430 service
= CFArrayGetValueAtIndex(components
, 3);
431 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
432 kSCDynamicStoreDomainState
,
435 p_state
= CFDictionaryGetValue(dict
, key
);
437 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
438 kSCDynamicStoreDomainSetup
,
441 p_setup
= CFDictionaryGetValue(dict
, key
);
443 CFRelease(components
);
446 if (!isA_CFDictionary(p_state
)) {
449 num
= CFDictionaryGetValue(p_state
, kSCPropNetPPPStatus
);
450 if (!isA_CFNumber(num
)) {
454 if (!CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) {
457 switch (ppp_status
) {
459 /* if we're really UP and RUNNING */
462 /* if we're effectively UP and RUNNING */
465 case PPP_STATERESERVED
:
466 /* if we're not connected at all */
467 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link idle, dial-on-traffic to connect"));
468 *flags
|= kSCNetworkFlagsConnectionRequired
;
471 /* if we're in the process of [dis]connecting */
472 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link, connection in progress"));
473 *flags
|= kSCNetworkFlagsConnectionRequired
;
477 // check PPP dial-on-traffic status
478 if (isA_CFDictionary(p_setup
)) {
479 num
= CFDictionaryGetValue(p_setup
, kSCPropNetPPPDialOnDemand
);
480 if (isA_CFNumber(num
)) {
483 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
485 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
486 if (ppp_status
== PPP_IDLE
) {
487 *flags
|= kSCNetworkFlagsInterventionRequired
;
498 if (keys
!= keys_q
) {
499 CFAllocatorDeallocate(NULL
, keys
);
500 CFAllocatorDeallocate(NULL
, values
);
505 if (dict
) CFRelease(dict
);
506 if (storeP
) *storeP
= store
;
512 updatePPPAvailable(SCDynamicStoreRef
*storeP
,
513 const struct sockaddr
*sa
,
514 SCNetworkConnectionFlags
*flags
)
516 CFDictionaryRef dict
= NULL
;
519 const void * keys_q
[N_QUICK
];
520 const void ** keys
= keys_q
;
522 int sc_status
= kSCStatusReachabilityUnknown
;
523 SCDynamicStoreRef store
= (storeP
) ? *storeP
: NULL
;
524 const void * values_q
[N_QUICK
];
525 const void ** values
= values_q
;
527 switch (sa
->sa_family
) {
529 entity
= kSCEntNetIPv4
;
532 entity
= kSCEntNetIPv6
;
539 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
541 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
547 * grab a snapshot of the PPP configuration from the dynamic store
551 CFMutableArrayRef patterns
;
553 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
554 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
555 kSCDynamicStoreDomainSetup
,
558 CFArrayAppendValue(patterns
, pattern
);
560 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
561 kSCDynamicStoreDomainSetup
,
564 CFArrayAppendValue(patterns
, pattern
);
566 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
570 /* if we could not access the dynamic store */
574 sc_status
= kSCStatusOK
;
577 * look for an available service which will provide connectivity
578 * for the requested address family.
580 n
= CFDictionaryGetCount(dict
);
585 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
586 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
587 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
589 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
591 for (i
= 0; i
< n
; i
++) {
592 CFArrayRef components
;
593 Boolean found
= FALSE
;
595 CFDictionaryRef p_dict
;
597 CFStringRef s_key
= (CFStringRef
) keys
[i
];
598 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
600 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
604 if (!CFStringHasSuffix(s_key
, entity
)) {
605 continue; // if not an IPv4 or IPv6 entity
608 // extract service ID
609 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
610 if (CFArrayGetCount(components
) != 5) {
613 service
= CFArrayGetValueAtIndex(components
, 3);
615 // check for PPP entity
616 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
617 kSCDynamicStoreDomainSetup
,
620 p_dict
= CFDictionaryGetValue(dict
, p_key
);
623 if (isA_CFDictionary(p_dict
)) {
627 * we have a PPP service for this address family
631 *flags
|= kSCNetworkFlagsReachable
;
632 *flags
|= kSCNetworkFlagsTransientConnection
;
633 *flags
|= kSCNetworkFlagsConnectionRequired
;
636 * get PPP dial-on-traffic status
638 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
639 if (isA_CFNumber(num
)) {
642 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
644 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
650 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = isReachable (after connect)"));
651 SCLog(TRUE
, LOG_INFO
, CFSTR(" service = %@"), service
);
656 CFRelease(components
);
663 if (keys
!= keys_q
) {
664 CFAllocatorDeallocate(NULL
, keys
);
665 CFAllocatorDeallocate(NULL
, values
);
670 if (dict
) CFRelease(dict
);
671 if (storeP
) *storeP
= store
;
676 #define ROUNDUP(a, size) \
677 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
679 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
680 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
685 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
689 for (i
= 0; i
< RTAX_MAX
; i
++) {
690 if (addrs
& (1 << i
)) {
699 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
702 checkAddress(SCDynamicStoreRef
*storeP
,
703 const struct sockaddr
*address
,
704 SCNetworkConnectionFlags
*flags
,
709 char if_name
[IFNAMSIZ
+1];
712 pid_t pid
= getpid();
714 struct sockaddr
*rti_info
[RTAX_MAX
];
715 struct rt_msghdr
*rtm
;
717 int sc_status
= kSCStatusReachabilityUnknown
;
718 struct sockaddr_dl
*sdl
;
719 int seq
= (int)pthread_self();
720 SCDynamicStoreRef store
= (storeP
) ? *storeP
: NULL
;
721 char *statusMessage
= NULL
;
722 #ifndef RTM_GET_SILENT
723 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
724 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
725 int sosize
= 48 * 1024;
728 if (!address
|| !flags
) {
729 sc_status
= kSCStatusInvalidArgument
;
733 switch (address
->sa_family
) {
737 sockaddr_to_string(address
, buf
, sizeof(buf
));
738 SCLog(TRUE
, LOG_INFO
, CFSTR("checkAddress(%s)"), buf
);
743 * if no code for this address family (yet)
745 SCLog(_sc_verbose
, LOG_ERR
,
746 CFSTR("checkAddress(): unexpected address family %d"),
748 sc_status
= kSCStatusInvalidArgument
;
757 if ((address
->sa_family
== AF_INET
) && (((struct sockaddr_in
*)address
)->sin_addr
.s_addr
== 0)) {
758 /* special case: check for available paths off the system */
762 bzero(&buf
, sizeof(buf
));
764 rtm
= (struct rt_msghdr
*)&buf
;
765 rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
766 rtm
->rtm_version
= RTM_VERSION
;
767 #ifdef RTM_GET_SILENT
768 rtm
->rtm_type
= RTM_GET_SILENT
;
770 rtm
->rtm_type
= RTM_GET
;
772 rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
773 rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
777 switch (address
->sa_family
) {
779 struct sockaddr_in6
*sin6
;
781 sin6
= (struct sockaddr_in6
*)address
;
782 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
783 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
784 (sin6
->sin6_scope_id
!= 0)) {
785 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
786 sin6
->sin6_scope_id
= 0;
792 sa
= (struct sockaddr
*) (rtm
+ 1);
793 bcopy(address
, sa
, address
->sa_len
);
794 n
= ROUNDUP(sa
->sa_len
, sizeof(u_long
));
795 rtm
->rtm_msglen
+= n
;
797 sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
798 sdl
->sdl_family
= AF_LINK
;
799 sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
800 n
= ROUNDUP(sdl
->sdl_len
, sizeof(u_long
));
801 rtm
->rtm_msglen
+= n
;
803 #ifndef RTM_GET_SILENT
804 pthread_mutex_lock(&lock
);
806 rsock
= socket(PF_ROUTE
, SOCK_RAW
, 0);
808 #ifndef RTM_GET_SILENT
809 pthread_mutex_unlock(&lock
);
811 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(errno
));
812 sc_status
= kSCStatusFailed
;
816 #ifndef RTM_GET_SILENT
817 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
819 pthread_mutex_unlock(&lock
);
820 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(errno
));
821 sc_status
= kSCStatusFailed
;
826 if (write(rsock
, &buf
, rtm
->rtm_msglen
) == -1) {
830 #ifndef RTM_GET_SILENT
831 pthread_mutex_unlock(&lock
);
834 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(err
));
841 * Type, seq, pid identify our response.
842 * Routing sockets are broadcasters on input.
847 n
= read(rsock
, (void *)&buf
, sizeof(buf
));
853 SCLog(TRUE
, LOG_ERR
, CFSTR("read() failed: %s"), strerror(err
));
854 #ifndef RTM_GET_SILENT
855 pthread_mutex_unlock(&lock
);
860 } while (rtm
->rtm_type
!= RTM_GET
||
861 rtm
->rtm_seq
!= seq
||
862 rtm
->rtm_pid
!= pid
);
865 #ifndef RTM_GET_SILENT
866 pthread_mutex_unlock(&lock
);
869 get_rtaddrs(rtm
->rtm_addrs
, sa
, rti_info
);
876 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), rtm
->rtm_flags
);
878 for (i
= 0; i
< RTAX_MAX
; i
++) {
879 if (rti_info
[i
] != NULL
) {
880 sockaddr_to_string(rti_info
[i
], buf
, sizeof(buf
));
881 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, buf
);
887 if ((rti_info
[RTAX_IFP
] == NULL
) ||
888 (rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
889 /* no interface info */
893 sdl
= (struct sockaddr_dl
*) rti_info
[RTAX_IFP
];
894 if ((sdl
->sdl_nlen
== 0) || (sdl
->sdl_nlen
> IFNAMSIZ
)) {
895 /* no interface name */
899 /* get the interface flags */
901 bzero(&ifr
, sizeof(ifr
));
902 bcopy(sdl
->sdl_data
, ifr
.ifr_name
, sdl
->sdl_nlen
);
904 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
906 SCLog(TRUE
, LOG_NOTICE
, CFSTR("socket() failed: %s"), strerror(errno
));
910 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) < 0) {
911 SCLog(TRUE
, LOG_NOTICE
, CFSTR("ioctl() failed: %s"), strerror(errno
));
917 if (!(ifr
.ifr_flags
& IFF_UP
)) {
921 statusMessage
= "isReachable";
922 *flags
|= kSCNetworkFlagsReachable
;
924 if (rtm
->rtm_flags
& RTF_LOCAL
) {
925 statusMessage
= "isReachable (is a local address)";
926 *flags
|= kSCNetworkFlagsIsLocalAddress
;
927 } else if (ifr
.ifr_flags
& IFF_LOOPBACK
) {
928 statusMessage
= "isReachable (is loopback network)";
929 *flags
|= kSCNetworkFlagsIsLocalAddress
;
930 } else if (rti_info
[RTAX_IFA
]) {
931 void *addr1
= (void *)address
;
932 void *addr2
= (void *)rti_info
[RTAX_IFA
];
933 size_t len
= address
->sa_len
;
935 if ((address
->sa_family
!= rti_info
[RTAX_IFA
]->sa_family
) &&
936 (address
->sa_len
!= rti_info
[RTAX_IFA
]->sa_len
)) {
937 SCLog(TRUE
, LOG_NOTICE
,
938 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
941 rti_info
[RTAX_IFA
]->sa_family
,
942 rti_info
[RTAX_IFA
]->sa_len
);
946 switch (address
->sa_family
) {
948 addr1
= &((struct sockaddr_in
*)address
)->sin_addr
;
949 addr2
= &((struct sockaddr_in
*)rti_info
[RTAX_IFA
])->sin_addr
;
950 len
= sizeof(struct in_addr
);
953 addr1
= &((struct sockaddr_in6
*)address
)->sin6_addr
;
954 addr2
= &((struct sockaddr_in6
*)rti_info
[RTAX_IFA
])->sin6_addr
;
955 len
= sizeof(struct in6_addr
);
961 if (memcmp(addr1
, addr2
, len
) == 0) {
962 statusMessage
= "isReachable (is interface address)";
963 *flags
|= kSCNetworkFlagsIsLocalAddress
;
967 if (rti_info
[RTAX_GATEWAY
] && (rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
)) {
968 *flags
|= kSCNetworkFlagsIsDirect
;
971 bzero(&if_name
, sizeof(if_name
));
974 (sdl
->sdl_nlen
<= IFNAMSIZ
) ? sdl
->sdl_nlen
: IFNAMSIZ
);
977 *if_index
= sdl
->sdl_index
;
981 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = %s"), statusMessage
);
982 SCLog(TRUE
, LOG_INFO
, CFSTR(" device = %s (%hu)"), if_name
, sdl
->sdl_index
);
983 SCLog(TRUE
, LOG_INFO
, CFSTR(" ifr_flags = 0x%04hx"), ifr
.ifr_flags
);
984 SCLog(TRUE
, LOG_INFO
, CFSTR(" rtm_flags = 0x%08x"), rtm
->rtm_flags
);
987 sc_status
= kSCStatusOK
;
989 if (ifr
.ifr_flags
& IFF_POINTOPOINT
) {
991 * We have an interface which "claims" to be a valid path
994 *flags
|= kSCNetworkFlagsTransientConnection
;
997 * Check if this is a dial-on-demand PPP link that isn't
1000 sc_status
= updatePPPStatus(&store
, address
, if_name
, flags
);
1007 sc_status
= updatePPPAvailable(&store
, address
, flags
);
1012 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" cannot be reached"));
1015 if (storeP
) *storeP
= store
;
1016 if (sc_status
!= kSCStatusOK
) {
1017 _SCErrorSet(sc_status
);
1026 checkAddressZero(SCDynamicStoreRef
*storeP
,
1027 SCNetworkConnectionFlags
*flags
,
1031 struct sockaddr_in sin
;
1033 bzero(&sin
, sizeof(sin
));
1034 sin
.sin_len
= sizeof(sin
);
1035 sin
.sin_family
= AF_INET
;
1036 sin
.sin_addr
.s_addr
= 0;
1038 ok
= checkAddress(storeP
, (struct sockaddr
*)&sin
, flags
, if_index
);
1045 isAddressZero(struct sockaddr
*sa
, SCNetworkConnectionFlags
*flags
)
1050 if (sa
->sa_family
== AF_INET
) {
1051 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
1053 if (sin
->sin_addr
.s_addr
== 0) {
1054 SCLog(_sc_debug
, LOG_INFO
, CFSTR("isAddressZero(0.0.0.0)"));
1055 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = isReachable (this host)"));
1056 *flags
|= kSCNetworkFlagsReachable
;
1057 *flags
|= kSCNetworkFlagsIsLocalAddress
;
1067 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
1069 CFAllocatorRef allocator
= CFGetAllocator(cf
);
1070 CFMutableStringRef result
;
1071 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1073 result
= CFStringCreateMutable(allocator
, 0);
1074 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> { "), cf
, allocator
);
1075 switch (targetPrivate
->type
) {
1076 case reachabilityTypeAddress
:
1077 case reachabilityTypeAddressPair
: {
1080 if (targetPrivate
->localAddress
) {
1081 sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
1082 CFStringAppendFormat(result
, NULL
, CFSTR("local address=%s"),
1086 if (targetPrivate
->remoteAddress
) {
1087 sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
1088 CFStringAppendFormat(result
, NULL
, CFSTR("%s%saddress=%s"),
1089 targetPrivate
->localAddress
? ", " : "",
1090 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
1095 case reachabilityTypeName
: {
1096 CFStringAppendFormat(result
, NULL
, CFSTR("name=%s"), targetPrivate
->name
);
1097 if (targetPrivate
->resolvedAddress
|| (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1098 if (targetPrivate
->resolvedAddress
) {
1099 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
1101 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddress
);
1103 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
1104 for (i
= 0; i
< n
; i
++) {
1107 struct sockaddr
*sa
;
1109 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
);
1110 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
1111 sockaddr_to_string(sa
, buf
, sizeof(buf
));
1112 CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"),
1116 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
1118 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)"));
1121 #ifdef CHECK_IPV6_REACHABILITY
1122 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
1123 gai_strerror(targetPrivate
->resolvedAddressError
));
1124 #else /* CHECK_IPV6_REACHABILITY */
1125 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
1126 __netdb_error(targetPrivate
->resolvedAddressError
));
1127 #endif /* CHECK_IPV6_REACHABILITY */
1129 } else if (targetPrivate
->dnsPort
) {
1130 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
1135 if (targetPrivate
->rls
) {
1136 CFStringAppendFormat(result
,
1138 CFSTR(", flags=%8.8x, if_index=%hu"),
1139 targetPrivate
->flags
,
1140 targetPrivate
->if_index
);
1142 CFStringAppendFormat(result
, NULL
, CFSTR(" }"));
1149 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1151 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1153 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCNetworkReachabilityDeallocate:"));
1155 /* release resources */
1157 pthread_mutex_destroy(&targetPrivate
->lock
);
1159 if (targetPrivate
->name
)
1160 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
1162 if (targetPrivate
->resolvedAddress
)
1163 CFRelease(targetPrivate
->resolvedAddress
);
1165 if (targetPrivate
->localAddress
)
1166 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
1168 if (targetPrivate
->remoteAddress
)
1169 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
1171 if (targetPrivate
->rlsContext
.release
) {
1172 targetPrivate
->rlsContext
.release(targetPrivate
->rlsContext
.info
);
1180 __SCNetworkReachabilityInitialize(void)
1182 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
1187 static SCNetworkReachabilityPrivateRef
1188 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
1190 SCNetworkReachabilityPrivateRef targetPrivate
;
1193 /* initialize runtime */
1194 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
1196 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCNetworkReachabilityCreatePrivate:"));
1198 /* allocate target */
1199 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
1200 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
1201 __kSCNetworkReachabilityTypeID
,
1204 if (!targetPrivate
) {
1208 pthread_mutex_init(&targetPrivate
->lock
, NULL
);
1210 targetPrivate
->name
= NULL
;
1212 targetPrivate
->resolvedAddress
= NULL
;
1213 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1215 targetPrivate
->localAddress
= NULL
;
1216 targetPrivate
->remoteAddress
= NULL
;
1218 targetPrivate
->flags
= 0;
1219 targetPrivate
->if_index
= 0;
1221 targetPrivate
->rls
= NULL
;
1222 targetPrivate
->rlsFunction
= NULL
;
1223 targetPrivate
->rlsContext
.info
= NULL
;
1224 targetPrivate
->rlsContext
.retain
= NULL
;
1225 targetPrivate
->rlsContext
.release
= NULL
;
1226 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1227 targetPrivate
->rlList
= NULL
;
1229 targetPrivate
->dnsPort
= NULL
;
1230 targetPrivate
->dnsRLS
= NULL
;
1232 return targetPrivate
;
1236 SCNetworkReachabilityRef
1237 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
1238 const struct sockaddr
*address
)
1240 SCNetworkReachabilityPrivateRef targetPrivate
;
1243 (address
->sa_len
== 0) ||
1244 (address
->sa_len
> sizeof(struct sockaddr_storage
))) {
1245 _SCErrorSet(kSCStatusInvalidArgument
);
1249 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1250 if (!targetPrivate
) {
1254 targetPrivate
->type
= reachabilityTypeAddress
;
1255 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
1256 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
1258 return (SCNetworkReachabilityRef
)targetPrivate
;
1262 SCNetworkReachabilityRef
1263 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
1264 const struct sockaddr
*localAddress
,
1265 const struct sockaddr
*remoteAddress
)
1267 SCNetworkReachabilityPrivateRef targetPrivate
;
1269 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
1270 _SCErrorSet(kSCStatusInvalidArgument
);
1275 if ((localAddress
->sa_len
== 0) ||
1276 (localAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1277 _SCErrorSet(kSCStatusInvalidArgument
);
1282 if (remoteAddress
) {
1283 if ((remoteAddress
->sa_len
== 0) ||
1284 (remoteAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1285 _SCErrorSet(kSCStatusInvalidArgument
);
1290 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1291 if (!targetPrivate
) {
1295 targetPrivate
->type
= reachabilityTypeAddressPair
;
1298 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
1299 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
1302 if (remoteAddress
) {
1303 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
1304 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
1307 return (SCNetworkReachabilityRef
)targetPrivate
;
1311 SCNetworkReachabilityRef
1312 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
1313 const char *nodename
)
1315 SCNetworkReachabilityPrivateRef targetPrivate
;
1318 _SCErrorSet(kSCStatusInvalidArgument
);
1322 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1323 if (!targetPrivate
) {
1327 targetPrivate
->type
= reachabilityTypeName
;
1329 targetPrivate
->flags
|= kSCNetworkFlagsFirstResolvePending
;
1331 targetPrivate
->name
= CFAllocatorAllocate(NULL
, strlen(nodename
) + 1, 0);
1332 strcpy((char *)targetPrivate
->name
, nodename
);
1334 return (SCNetworkReachabilityRef
)targetPrivate
;
1339 SCNetworkReachabilityGetTypeID(void)
1341 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
1342 return __kSCNetworkReachabilityTypeID
;
1347 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
1350 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1352 if (!isA_SCNetworkReachability(target
)) {
1353 _SCErrorSet(kSCStatusInvalidArgument
);
1357 if (targetPrivate
->type
!= reachabilityTypeName
) {
1358 _SCErrorSet(kSCStatusInvalidArgument
);
1363 *error_num
= targetPrivate
->resolvedAddressError
;
1366 if (targetPrivate
->resolvedAddress
|| (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1367 if (targetPrivate
->resolvedAddress
) {
1368 return CFRetain(targetPrivate
->resolvedAddress
);
1370 /* if status is known but no resolved addresses to return */
1371 _SCErrorSet(kSCStatusOK
);
1376 _SCErrorSet(kSCStatusReachabilityUnknown
);
1382 __SCNetworkReachabilitySetResolvedAddress(SCNetworkReachabilityRef target
,
1383 CFArrayRef addresses
,
1386 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1388 if (targetPrivate
->resolvedAddress
) {
1389 CFRelease(targetPrivate
->resolvedAddress
);
1391 targetPrivate
->resolvedAddress
= addresses
? CFRetain(addresses
) : NULL
;
1392 targetPrivate
->resolvedAddressError
= error_num
;
1397 #ifdef CHECK_IPV6_REACHABILITY
1399 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo
*res
, void *context
)
1402 struct addrinfo
*resP
;
1403 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
1404 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1406 ok
= (status
== 0) && (res
!= NULL
);
1408 SCLog(_sc_debug
, LOG_DEBUG
,
1409 CFSTR("process async DNS complete%s"),
1410 ok
? "" : ", host not found");
1413 CFMutableArrayRef addresses
;
1415 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1417 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
1418 CFDataRef newAddress
;
1420 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
1421 CFArrayAppendValue(addresses
, newAddress
);
1422 CFRelease(newAddress
);
1425 /* save the resolved address[es] */
1426 __SCNetworkReachabilitySetResolvedAddress(target
, addresses
, NETDB_SUCCESS
);
1427 CFRelease(addresses
);
1429 SCLog(_sc_debug
, LOG_INFO
, CFSTR("getaddrinfo() failed: %s"), gai_strerror(status
));
1431 /* save the error associated with the attempt to resolve the name */
1432 __SCNetworkReachabilitySetResolvedAddress(target
, (CFArrayRef
)kCFNull
, status
);
1435 if (res
) freeaddrinfo(res
);
1437 if (targetPrivate
->rls
) {
1438 SCLog(_sc_debug
, LOG_INFO
, CFSTR("DNS request completed"));
1439 CFRunLoopSourceSignal(targetPrivate
->rls
);
1440 __signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1445 #else /* CHECK_IPV6_REACHABILITY */
1447 __SCNetworkReachabilityCallbackSetResolvedAddress(struct hostent
*h
, int error
, void *context
)
1449 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
1450 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1452 SCLog(_sc_debug
, LOG_DEBUG
,
1453 CFSTR("process async DNS complete%s"),
1454 (h
== NULL
) ? ", host not found" : "");
1456 if (h
&& h
->h_length
) {
1457 CFMutableArrayRef addresses
;
1460 struct sockaddr_in sin
;
1461 struct sockaddr_in6 sin6
;
1462 struct sockaddr_storage ss
;
1464 char **ha
= h
->h_addr_list
;
1466 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1468 bzero(&addr
, sizeof(addr
));
1471 CFDataRef newAddress
;
1473 switch (h
->h_length
) {
1474 case sizeof(struct in_addr
) :
1475 addr
.sin
.sin_family
= AF_INET
;
1476 addr
.sin
.sin_len
= sizeof(struct sockaddr_in
);
1477 bcopy(*ha
, &addr
.sin
.sin_addr
, h
->h_length
);
1479 case sizeof(struct in6_addr
) :
1480 addr
.sin6
.sin6_family
= AF_INET6
;
1481 addr
.sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
1482 bcopy(*ha
, &addr
.sin6
.sin6_addr
, h
->h_length
);
1486 newAddress
= CFDataCreate(NULL
, (void *)&addr
, addr
.sa
.sa_len
);
1487 CFArrayAppendValue(addresses
, newAddress
);
1488 CFRelease(newAddress
);
1493 /* save the resolved address[es] */
1494 __SCNetworkReachabilitySetResolvedAddress(target
, addresses
, NETDB_SUCCESS
);
1495 CFRelease(addresses
);
1497 SCLog(_sc_debug
, LOG_INFO
, CFSTR("getipnodebyname() failed: %s"), __netdb_error(error
));
1499 /* save the error associated with the attempt to resolve the name */
1500 __SCNetworkReachabilitySetResolvedAddress(target
, (CFArrayRef
)kCFNull
, error
);
1503 if (h
) freehostent(h
);
1505 if (targetPrivate
->rls
) {
1506 SCLog(_sc_debug
, LOG_INFO
, CFSTR("DNS request completed"));
1507 CFRunLoopSourceSignal(targetPrivate
->rls
);
1508 __signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1513 #endif /* CHECK_IPV6_REACHABILITY */
1517 * rankReachability()
1518 * Not reachable == 0
1519 * Connection Required == 1
1523 rankReachability(SCNetworkConnectionFlags flags
)
1527 if (flags
& kSCNetworkFlagsReachable
) rank
= 2;
1528 if (flags
& kSCNetworkFlagsConnectionRequired
) rank
= 1;
1533 #ifdef CHECK_IPV6_REACHABILITY
1535 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1537 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1538 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1540 pthread_mutex_lock(&targetPrivate
->lock
);
1542 getaddrinfo_async_handle_reply(msg
);
1544 if (port
== targetPrivate
->dnsPort
) {
1545 CFRelease(targetPrivate
->dnsRLS
);
1546 targetPrivate
->dnsRLS
= NULL
;
1547 CFRelease(targetPrivate
->dnsPort
);
1548 targetPrivate
->dnsPort
= NULL
;
1551 pthread_mutex_unlock(&targetPrivate
->lock
);
1555 #else /* CHECK_IPV6_REACHABILITY */
1557 getipnodebyname_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1559 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1560 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1562 pthread_mutex_lock(&targetPrivate
->lock
);
1564 getipnodebyname_async_handleReply(msg
);
1566 if (port
== targetPrivate
->dnsPort
) {
1567 CFRelease(targetPrivate
->dnsRLS
);
1568 targetPrivate
->dnsRLS
= NULL
;
1569 CFRelease(targetPrivate
->dnsPort
);
1570 targetPrivate
->dnsPort
= NULL
;
1573 pthread_mutex_unlock(&targetPrivate
->lock
);
1577 #endif /* CHECK_IPV6_REACHABILITY */
1581 checkResolverReachability(SCDynamicStoreRef
*storeP
,
1582 SCNetworkConnectionFlags
*flags
,
1589 * We first assume that all of the configured DNS servers
1590 * are available. Since we don't know which name server will
1591 * be consulted to resolve the specified nodename we need to
1592 * check the availability of ALL name servers. We can only
1593 * proceed if we know that our query can be answered.
1596 *flags
= kSCNetworkFlagsReachable
;
1601 /* if we are actively watching at least one host */
1607 for (i
= 0; i
< _res
.nscount
; i
++) {
1608 SCNetworkConnectionFlags ns_flags
= 0;
1610 if (_res
.nsaddr_list
[i
].sin_addr
.s_addr
== 0) {
1616 if (_res
.nsaddr_list
[i
].sin_len
== 0) {
1617 _res
.nsaddr_list
[i
].sin_len
= sizeof(_res
.nsaddr_list
[i
]);
1620 ok
= checkAddress(storeP
, (struct sockaddr
*)&_res
.nsaddr_list
[i
], &ns_flags
, NULL
);
1625 if (rankReachability(ns_flags
) < rankReachability(*flags
)) {
1626 /* return the worst case result */
1632 /* if no DNS server addresses */
1641 startAsyncDNSQuery(SCNetworkReachabilityRef target
) {
1642 CFMachPortContext context
= { 0, (void *)target
, CFRetain
, CFRelease
, CFCopyDescription
};
1644 #ifdef CHECK_IPV6_REACHABILITY
1645 struct addrinfo hints
;
1646 #endif /* CHECK_IPV6_REACHABILITY */
1650 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1652 #ifdef CHECK_IPV6_REACHABILITY
1653 bzero(&hints
, sizeof(hints
));
1654 hints
.ai_flags
= AI_ADDRCONFIG
;
1656 error
= getaddrinfo_async_start(&port
,
1657 targetPrivate
->name
,
1660 __SCNetworkReachabilityCallbackSetResolvedAddress
,
1663 /* save the error associated with the attempt to resolve the name */
1664 __SCNetworkReachabilitySetResolvedAddress(target
, (CFArrayRef
)kCFNull
, error
);
1668 targetPrivate
->dnsPort
= CFMachPortCreateWithPort(NULL
,
1670 getaddrinfo_async_handleCFReply
,
1673 #else /* CHECK_IPV6_REACHABILITY */
1674 port
= getipnodebyname_async_start(targetPrivate
->name
,
1678 __SCNetworkReachabilityCallbackSetResolvedAddress
,
1680 if (port
== MACH_PORT_NULL
) {
1681 /* save the error associated with the attempt to resolve the name */
1682 __SCNetworkReachabilitySetResolvedAddress(target
, (CFArrayRef
)kCFNull
, error
);
1686 targetPrivate
->dnsPort
= CFMachPortCreateWithPort(NULL
,
1688 getipnodebyname_async_handleCFReply
,
1691 #endif /* CHECK_IPV6_REACHABILITY */
1693 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
1695 n
= CFArrayGetCount(targetPrivate
->rlList
);
1696 for (i
= 0; i
< n
; i
+= 3) {
1697 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
1698 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
1700 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
1708 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef
*storeP
,
1709 SCNetworkReachabilityRef target
,
1710 SCNetworkConnectionFlags
*flags
,
1714 CFMutableArrayRef addresses
= NULL
;
1715 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1716 SCNetworkConnectionFlags my_flags
= 0;
1717 uint16_t my_index
= 0;
1725 if (!isA_SCNetworkReachability(target
)) {
1726 _SCErrorSet(kSCStatusInvalidArgument
);
1730 switch (targetPrivate
->type
) {
1731 case reachabilityTypeAddress
:
1732 case reachabilityTypeAddressPair
: {
1734 * Check "local" address
1736 if (targetPrivate
->localAddress
) {
1740 if (isAddressZero(targetPrivate
->localAddress
, &my_flags
)) {
1745 * Check "local" address
1747 ok
= checkAddress(storeP
, targetPrivate
->localAddress
, &my_flags
, &my_index
);
1749 goto error
; /* not today */
1752 if (!(my_flags
& kSCNetworkFlagsIsLocalAddress
)) {
1753 goto error
; /* not reachable, non-"local" address */
1760 * Check "remote" address
1762 if (targetPrivate
->remoteAddress
) {
1764 * in cases where we have "local" and "remote" addresses
1765 * we need to re-initialize the to-be-returned flags.
1773 if (isAddressZero(targetPrivate
->remoteAddress
, &my_flags
)) {
1778 * Check "remote" address
1780 ok
= checkAddress(storeP
, targetPrivate
->remoteAddress
, &my_flags
, &my_index
);
1782 goto error
; /* not today */
1790 case reachabilityTypeName
: {
1792 #ifndef CHECK_IPV6_REACHABILITY
1794 #endif /* CHECK_IPV6_REACHABILITY */
1795 Boolean haveDNS
= FALSE
;
1796 #ifdef CHECK_IPV6_REACHABILITY
1797 struct addrinfo hints
;
1798 #endif /* CHECK_IPV6_REACHABILITY */
1799 SCNetworkConnectionFlags ns_flags
;
1800 #ifdef CHECK_IPV6_REACHABILITY
1801 struct addrinfo
*res
;
1802 #endif /* CHECK_IPV6_REACHABILITY */
1804 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
1805 if (addresses
|| (error
!= NETDB_SUCCESS
)) {
1806 /* if resolved or an error had been detected */
1807 goto checkResolvedAddress
;
1810 /* check the reachability of the DNS servers */
1811 ok
= checkResolverReachability(storeP
, &ns_flags
, &haveDNS
);\
1813 /* if we could not get DNS server info */
1817 if (rankReachability(ns_flags
) < 2) {
1819 * if DNS servers are not (or are no longer) reachable, set
1820 * flags based on the availability of configured (but not
1823 if (!checkAddressZero(storeP
, &my_flags
, &my_index
)) {
1827 if (async
&& targetPrivate
->rls
) {
1829 * return HOST_NOT_FOUND, set flags appropriately,
1830 * and schedule notification.
1832 #ifdef CHECK_IPV6_REACHABILITY
1833 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NODATA
,
1836 #else /* CHECK_IPV6_REACHABILITY */
1837 __SCNetworkReachabilityCallbackSetResolvedAddress(NULL
,
1840 #endif /* CHECK_IPV6_REACHABILITY */
1842 my_flags
|= (targetPrivate
->flags
& kSCNetworkFlagsFirstResolvePending
);
1844 SCLog(_sc_debug
, LOG_INFO
, CFSTR("no DNS servers are reachable"));
1845 CFRunLoopSourceSignal(targetPrivate
->rls
);
1846 __signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1852 /* for async requests we return the last known status */
1853 my_flags
= targetPrivate
->flags
;
1854 my_index
= targetPrivate
->if_index
;
1856 if (targetPrivate
->dnsPort
) {
1857 /* if request already in progress */
1861 SCLog(_sc_debug
, LOG_INFO
, CFSTR("start DNS query for \"%s\""), targetPrivate
->name
);
1864 * initiate an async DNS query
1866 if (!startAsyncDNSQuery(target
)) {
1867 /* if we could not initiate the request, process error */
1868 goto checkResolvedAddress
;
1871 /* if request initiated */
1875 SCLog(_sc_debug
, LOG_INFO
, CFSTR("check DNS for \"%s\""), targetPrivate
->name
);
1878 * OK, all of the DNS name servers are available. Let's
1879 * resolve the nodename into an address.
1881 #ifdef CHECK_IPV6_REACHABILITY
1882 bzero(&hints
, sizeof(hints
));
1883 hints
.ai_flags
= AI_ADDRCONFIG
;
1885 error
= getaddrinfo(targetPrivate
->name
, NULL
, &hints
, &res
);
1886 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, res
, (void *)target
);
1887 #else /* CHECK_IPV6_REACHABILITY */
1888 h
= getipnodebyname(targetPrivate
->name
, AF_INET
, 0, &error
);
1889 __SCNetworkReachabilityCallbackSetResolvedAddress(h
, error
, (void *)target
);
1890 #endif /* CHECK_IPV6_REACHABILITY */
1892 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
1894 checkResolvedAddress
:
1897 * We first assume that the requested host is NOT available.
1898 * Then, check each address for accessibility and return the
1899 * best status available.
1904 if (isA_CFArray(addresses
)) {
1906 CFIndex n
= CFArrayGetCount(addresses
);
1908 for (i
= 0; i
< n
; i
++) {
1909 SCNetworkConnectionFlags ns_flags
= 0;
1910 uint16_t ns_if_index
= 0;
1911 struct sockaddr
*sa
;
1913 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
1914 ok
= checkAddress(storeP
, sa
, &ns_flags
, &ns_if_index
);
1916 goto error
; /* not today */
1919 if (rankReachability(ns_flags
) > rankReachability(my_flags
)) {
1920 /* return the best case result */
1921 my_flags
= ns_flags
;
1922 my_index
= ns_if_index
;
1923 if (rankReachability(my_flags
) == 2) {
1930 if ((error
== HOST_NOT_FOUND
) && !haveDNS
) {
1932 * No DNS servers are defined. Set flags based on
1933 * the availability of configured (but not active)
1936 ok
= checkAddressZero(storeP
, &my_flags
, &my_index
);
1938 goto error
; /* not today */
1941 if ((my_flags
& kSCNetworkFlagsReachable
) &&
1942 (my_flags
& kSCNetworkFlagsConnectionRequired
)) {
1944 * Since we might pick up a set of DNS servers when this connection
1945 * is established, don't reply with a "HOST NOT FOUND" error just yet.
1950 /* Host not found, not reachable! */
1962 *if_index
= my_index
;
1967 if (addresses
) CFRelease(addresses
);
1973 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
1974 SCNetworkConnectionFlags
*flags
)
1977 SCDynamicStoreRef store
= NULL
;
1978 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1980 if (!isA_SCNetworkReachability(target
)) {
1981 _SCErrorSet(kSCStatusInvalidArgument
);
1985 if (targetPrivate
->rlList
) {
1986 /* if being watched, return current (OK, last known) status */
1987 *flags
= targetPrivate
->flags
& ~kSCNetworkFlagsFirstResolvePending
;
1991 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, flags
, NULL
, FALSE
);
1992 *flags
&= ~kSCNetworkFlagsFirstResolvePending
;
1993 if (store
) CFRelease(store
);
1999 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
2002 CFMutableArrayRef keys
;
2003 CFStringRef pattern
;
2004 CFMutableArrayRef patterns
;
2006 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2007 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2010 * Setup:/Network/Global/IPv4 (for the ServiceOrder)
2012 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2013 kSCDynamicStoreDomainSetup
,
2015 CFArrayAppendValue(keys
, key
);
2019 * State:/Network/Global/DNS
2021 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2022 kSCDynamicStoreDomainState
,
2024 CFArrayAppendValue(keys
, key
);
2028 * State:/Network/Global/IPv4
2030 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2031 kSCDynamicStoreDomainState
,
2033 CFArrayAppendValue(keys
, key
);
2036 /* Setup: per-service IPv4 info */
2037 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2038 kSCDynamicStoreDomainSetup
,
2041 CFArrayAppendValue(patterns
, pattern
);
2044 /* Setup: per-service Interface info */
2045 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2046 kSCDynamicStoreDomainSetup
,
2048 kSCEntNetInterface
);
2049 CFArrayAppendValue(patterns
, pattern
);
2052 /* Setup: per-service PPP info */
2053 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2054 kSCDynamicStoreDomainSetup
,
2057 CFArrayAppendValue(patterns
, pattern
);
2060 /* State: per-service IPv4 info */
2061 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2062 kSCDynamicStoreDomainState
,
2065 CFArrayAppendValue(patterns
, pattern
);
2068 /* State: per-interface IPv4 info */
2069 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2070 kSCDynamicStoreDomainState
,
2073 CFArrayAppendValue(patterns
, pattern
);
2076 /* State: per-interface IPv6 info */
2077 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2078 kSCDynamicStoreDomainState
,
2081 CFArrayAppendValue(patterns
, pattern
);
2084 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
2086 CFRelease(patterns
);
2093 __SCNetworkReachabilityReachabilityHandleChanges(SCDynamicStoreRef store
,
2094 CFArrayRef changedKeys
,
2097 Boolean dnsChanged
= FALSE
;
2101 const void * targets_q
[N_QUICK
];
2102 const void ** targets
= targets_q
;
2104 pthread_mutex_lock(&hn_lock
);
2106 nTargets
= CFSetGetCount(hn_targets
);
2107 if (nTargets
== 0) {
2108 /* if no addresses being monitored */
2112 if (CFArrayGetCount(changedKeys
) == 0) {
2117 SCLog(_sc_debug
, LOG_INFO
, CFSTR("process configuration change"));
2119 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2120 kSCDynamicStoreDomainState
,
2122 if (CFArrayContainsValue(changedKeys
,
2123 CFRangeMake(0, CFArrayGetCount(changedKeys
)),
2125 dnsChanged
= TRUE
; /* the DNS server(s) have changed */
2126 needDNS
= TRUE
; /* ... and we need to res_init() on the next query */
2132 * if the DNS configuration didn't change we still need to
2133 * check that the DNS servers are accessible.
2135 Boolean haveDNS
= FALSE
;
2136 SCNetworkConnectionFlags ns_flags
;
2139 /* check the reachability of the DNS servers */
2140 ok
= checkResolverReachability(&store
, &ns_flags
, &haveDNS
);\
2141 if (!ok
|| (rankReachability(ns_flags
) < 2)) {
2142 /* if DNS servers are not reachable */
2147 SCLog(_sc_debug
&& dnsChanged
, LOG_INFO
, CFSTR(" DNS changed"));
2149 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
2150 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
2151 CFSetGetValues(hn_targets
, targets
);
2152 for (i
= 0; i
< nTargets
; i
++) {
2153 SCNetworkReachabilityRef target
= targets
[i
];
2154 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2156 pthread_mutex_lock(&targetPrivate
->lock
);
2159 if (targetPrivate
->dnsPort
) {
2160 /* cancel the outstanding DNS query */
2161 #ifdef CHECK_IPV6_REACHABILITY
2162 lu_async_call_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2163 #else /* CHECK_IPV6_REACHABILITY */
2164 getipnodebyname_async_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2165 #endif /* CHECK_IPV6_REACHABILITY */
2166 CFRelease(targetPrivate
->dnsRLS
);
2167 targetPrivate
->dnsRLS
= NULL
;
2168 CFRelease(targetPrivate
->dnsPort
);
2169 targetPrivate
->dnsPort
= NULL
;
2172 /* schedule request to resolve the name again */
2173 __SCNetworkReachabilitySetResolvedAddress(target
, NULL
, NETDB_SUCCESS
);
2176 CFRunLoopSourceSignal(targetPrivate
->rls
);
2177 __signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2179 pthread_mutex_unlock(&targetPrivate
->lock
);
2181 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
2185 pthread_mutex_unlock(&hn_lock
);
2191 __isScheduled(CFTypeRef obj
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, CFMutableArrayRef rlList
)
2194 CFIndex n
= CFArrayGetCount(rlList
);
2196 for (i
= 0; i
< n
; i
+= 3) {
2197 if (obj
&& !CFEqual(obj
, CFArrayGetValueAtIndex(rlList
, i
))) {
2200 if (runLoop
&& !CFEqual(runLoop
, CFArrayGetValueAtIndex(rlList
, i
+1))) {
2203 if (runLoopMode
&& !CFEqual(runLoopMode
, CFArrayGetValueAtIndex(rlList
, i
+2))) {
2214 __schedule(CFTypeRef obj
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, CFMutableArrayRef rlList
)
2216 CFArrayAppendValue(rlList
, obj
);
2217 CFArrayAppendValue(rlList
, runLoop
);
2218 CFArrayAppendValue(rlList
, runLoopMode
);
2225 __unschedule(CFTypeRef obj
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, CFMutableArrayRef rlList
, Boolean all
)
2228 Boolean found
= FALSE
;
2229 CFIndex n
= CFArrayGetCount(rlList
);
2232 if (obj
&& !CFEqual(obj
, CFArrayGetValueAtIndex(rlList
, i
))) {
2236 if (runLoop
&& !CFEqual(runLoop
, CFArrayGetValueAtIndex(rlList
, i
+1))) {
2240 if (runLoopMode
&& !CFEqual(runLoopMode
, CFArrayGetValueAtIndex(rlList
, i
+2))) {
2247 CFArrayRemoveValueAtIndex(rlList
, i
+ 2);
2248 CFArrayRemoveValueAtIndex(rlList
, i
+ 1);
2249 CFArrayRemoveValueAtIndex(rlList
, i
);
2263 rlsPerform(void *info
)
2266 void (*context_release
)(const void *);
2267 SCNetworkConnectionFlags flags
;
2270 SCNetworkReachabilityCallBack rlsFunction
;
2271 SCDynamicStoreRef store
= NULL
;
2272 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2273 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2275 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("process reachability change"));
2277 pthread_mutex_lock(&targetPrivate
->lock
);
2279 /* update reachability, notify if status changed */
2280 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
);
2281 if (store
) CFRelease(store
);
2283 /* if reachability status not available */
2288 if ((targetPrivate
->flags
== flags
) && (targetPrivate
->if_index
== if_index
)) {
2289 /* if reachability flags and interface have not changed */
2290 pthread_mutex_unlock(&targetPrivate
->lock
);
2291 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("flags/interface match"));
2295 /* update flags / interface */
2296 targetPrivate
->flags
= flags
;
2297 targetPrivate
->if_index
= if_index
;
2300 rlsFunction
= targetPrivate
->rlsFunction
;
2301 if (NULL
!= targetPrivate
->rlsContext
.retain
) {
2302 context_info
= (void *)targetPrivate
->rlsContext
.retain(targetPrivate
->rlsContext
.info
);
2303 context_release
= targetPrivate
->rlsContext
.release
;
2305 context_info
= targetPrivate
->rlsContext
.info
;
2306 context_release
= NULL
;
2309 pthread_mutex_unlock(&targetPrivate
->lock
);
2312 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("flags/interface have changed"));
2313 (*rlsFunction
)(target
, flags
, context_info
);
2316 if (context_release
) {
2317 context_release(context_info
);
2325 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
2326 SCNetworkReachabilityCallBack callout
,
2327 SCNetworkReachabilityContext
*context
)
2329 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2331 pthread_mutex_lock(&targetPrivate
->lock
);
2333 if (targetPrivate
->rlsContext
.release
) {
2334 /* let go of the current context */
2335 targetPrivate
->rlsContext
.release(targetPrivate
->rlsContext
.info
);
2338 targetPrivate
->rlsFunction
= callout
;
2339 targetPrivate
->rlsContext
.info
= NULL
;
2340 targetPrivate
->rlsContext
.retain
= NULL
;
2341 targetPrivate
->rlsContext
.release
= NULL
;
2342 targetPrivate
->rlsContext
.copyDescription
= NULL
;
2344 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
2345 if (context
->retain
) {
2346 targetPrivate
->rlsContext
.info
= (void *)context
->retain(context
->info
);
2350 pthread_mutex_unlock(&targetPrivate
->lock
);
2357 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
2358 CFRunLoopRef runLoop
,
2359 CFStringRef runLoopMode
)
2361 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2362 Boolean init
= FALSE
;
2365 if (!isA_SCNetworkReachability(target
) || runLoop
== NULL
|| runLoopMode
== NULL
) {
2366 _SCErrorSet(kSCStatusInvalidArgument
);
2370 /* schedule the SCNetworkReachability run loop source */
2372 pthread_mutex_lock(&hn_lock
);
2373 pthread_mutex_lock(&targetPrivate
->lock
);
2377 * if we are not monitoring any hosts
2379 hn_store
= SCDynamicStoreCreate(NULL
,
2380 CFSTR("SCNetworkReachability"),
2381 __SCNetworkReachabilityReachabilityHandleChanges
,
2384 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
2388 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
2390 hn_storeRLS
= SCDynamicStoreCreateRunLoopSource(NULL
, hn_store
, 0);
2391 hn_rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2392 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
2395 if (!targetPrivate
->rls
) {
2396 CFRunLoopSourceContext context
= { 0 // version
2397 , (void *)target
// info
2398 , CFRetain
// retain
2399 , CFRelease
// release
2400 , CFCopyDescription
// copyDescription
2405 , rlsPerform
// perform
2408 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
2409 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2413 if (!__isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2415 * if we do not already have host notifications scheduled with
2416 * this runLoop / runLoopMode
2418 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2420 if (targetPrivate
->dnsRLS
) {
2421 /* if we have an active async DNS query too */
2422 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2426 __schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
2428 /* schedule the SCNetworkReachability run loop source */
2430 if (!__isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2432 * if we do not already have SC notifications scheduled with
2433 * this runLoop / runLoopMode
2435 CFRunLoopAddSource(runLoop
, hn_storeRLS
, runLoopMode
);
2438 __schedule(target
, runLoop
, runLoopMode
, hn_rlList
);
2439 CFSetAddValue(hn_targets
, target
);
2442 SCNetworkConnectionFlags flags
;
2444 SCDynamicStoreRef store
= NULL
;
2447 * if we have yet to schedule SC notifications for this address
2448 * - initialize current reachability status
2450 if (__SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
)) {
2452 * if reachability status available
2454 * - schedule notification to report status via callback
2456 targetPrivate
->flags
= flags
;
2457 targetPrivate
->if_index
= if_index
;
2458 CFRunLoopSourceSignal(targetPrivate
->rls
);
2459 __signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2461 /* if reachability status not available, async lookup started */
2462 targetPrivate
->flags
= 0;
2463 targetPrivate
->if_index
= 0;
2465 if (store
) CFRelease(store
);
2472 pthread_mutex_unlock(&targetPrivate
->lock
);
2473 pthread_mutex_unlock(&hn_lock
);
2479 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
2480 CFRunLoopRef runLoop
,
2481 CFStringRef runLoopMode
)
2483 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2487 if (!isA_SCNetworkReachability(target
) || runLoop
== NULL
|| runLoopMode
== NULL
) {
2488 _SCErrorSet(kSCStatusInvalidArgument
);
2492 pthread_mutex_lock(&hn_lock
);
2493 pthread_mutex_lock(&targetPrivate
->lock
);
2495 if (!targetPrivate
->rls
) {
2496 /* if not currently scheduled */
2500 if (!__unschedule(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
2501 /* if not currently scheduled */
2505 n
= CFArrayGetCount(targetPrivate
->rlList
);
2506 if (n
== 0 || !__isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2508 * if this host is no longer scheduled for this runLoop / runLoopMode
2510 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2512 if (targetPrivate
->dnsRLS
) {
2513 /* if we have an active async DNS query too */
2514 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2519 * if this host is no longer scheduled
2521 CFRelease(targetPrivate
->rls
); /* cleanup SCNetworkReachability resources */
2522 targetPrivate
->rls
= NULL
;
2523 CFRelease(targetPrivate
->rlList
);
2524 targetPrivate
->rlList
= NULL
;
2525 CFSetRemoveValue(hn_targets
, target
); /* cleanup notification resources */
2527 if (targetPrivate
->dnsPort
) {
2528 /* if we have an active async DNS query too */
2529 #ifdef CHECK_IPV6_REACHABILITY
2530 lu_async_call_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2531 #else /* CHECK_IPV6_REACHABILITY */
2532 getipnodebyname_async_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2533 #endif /* CHECK_IPV6_REACHABILITY */
2534 CFRelease(targetPrivate
->dnsRLS
);
2535 targetPrivate
->dnsRLS
= NULL
;
2536 CFRelease(targetPrivate
->dnsPort
);
2537 targetPrivate
->dnsPort
= NULL
;
2542 (void)__unschedule(target
, runLoop
, runLoopMode
, hn_rlList
, FALSE
);
2544 n
= CFArrayGetCount(hn_rlList
);
2545 if (n
== 0 || !__isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2547 * if we no longer have any addresses scheduled for
2548 * this runLoop / runLoopMode
2550 CFRunLoopRemoveSource(runLoop
, hn_storeRLS
, runLoopMode
);
2554 * if we are no longer monitoring any addresses
2556 CFRelease(hn_targets
);
2557 CFRelease(hn_rlList
);
2558 CFRelease(hn_storeRLS
);
2559 CFRelease(hn_store
);
2563 * until we start monitoring again, ensure that
2564 * all subsequent reachability-by-name checks
2575 pthread_mutex_unlock(&targetPrivate
->lock
);
2576 pthread_mutex_unlock(&hn_lock
);