2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
27 * Modification History
29 * January 19, 2003 Allan Nathanson <ajn@apple.com>
30 * - add advanced reachability APIs
33 #include <SystemConfiguration/SystemConfiguration.h>
34 #include <SystemConfiguration/SCValidation.h>
35 #include <SystemConfiguration/SCPrivate.h>
37 #include <CoreFoundation/CFRuntime.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <arpa/nameser.h>
44 #include <netdb_async.h>
47 #include <sys/ioctl.h>
48 #include <sys/socket.h>
50 #include <net/if_dl.h>
51 #define KERNEL_PRIVATE
52 #include <net/route.h>
56 #define s6_addr16 __u6_addr.__u6_addr16
62 #define kSCNetworkFlagsFirstResolvePending (1<<31)
69 reachabilityTypeAddress
,
70 reachabilityTypeAddressPair
,
75 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
76 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
81 /* base CFType information */
90 /* target host name */
92 CFArrayRef resolvedAddress
; /* CFArray[CFData] */
93 int resolvedAddressError
;
95 /* local & remote addresses */
96 struct sockaddr
*localAddress
;
97 struct sockaddr
*remoteAddress
;
99 /* current reachability flags */
100 SCNetworkConnectionFlags flags
;
103 /* run loop source, callout, context, rl scheduling info */
104 CFRunLoopSourceRef rls
;
105 SCNetworkReachabilityCallBack rlsFunction
;
106 SCNetworkReachabilityContext rlsContext
;
107 CFMutableArrayRef rlList
;
109 /* async DNS query info */
110 CFMachPortRef dnsPort
;
111 CFRunLoopSourceRef dnsRLS
;
113 } SCNetworkReachabilityPrivate
, *SCNetworkReachabilityPrivateRef
;
116 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
119 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
121 "SCNetworkReachability", // className
124 __SCNetworkReachabilityDeallocate
, // dealloc
127 NULL
, // copyFormattingDesc
128 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
132 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
133 static Boolean needDNS
= TRUE
;
136 /* host "something has changed" notifications */
137 static pthread_mutex_t hn_lock
= PTHREAD_MUTEX_INITIALIZER
;
138 static SCDynamicStoreRef hn_store
= NULL
;
139 static CFRunLoopSourceRef hn_storeRLS
= NULL
;
140 static CFMutableArrayRef hn_rlList
= NULL
;
141 static CFMutableSetRef hn_targets
= NULL
;
144 static __inline__ CFTypeRef
145 isA_SCNetworkReachability(CFTypeRef obj
)
147 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
152 sockaddr_to_string(const struct sockaddr
*address
, char *buf
, size_t bufLen
)
155 switch (address
->sa_family
) {
157 (void)inet_ntop(((struct sockaddr_in
*)address
)->sin_family
,
158 &((struct sockaddr_in
*)address
)->sin_addr
,
163 (void)inet_ntop(((struct sockaddr_in6
*)address
)->sin6_family
,
164 &((struct sockaddr_in6
*)address
)->sin6_addr
,
167 if (((struct sockaddr_in6
*)address
)->sin6_scope_id
!= 0) {
171 if ((n
+IF_NAMESIZE
+1) <= (int)bufLen
) {
173 if_indextoname(((struct sockaddr_in6
*)address
)->sin6_scope_id
, &buf
[n
]);
179 if (((struct sockaddr_dl
*)address
)->sdl_len
< bufLen
) {
180 bufLen
= ((struct sockaddr_dl
*)address
)->sdl_len
;
185 bcopy(((struct sockaddr_dl
*)address
)->sdl_data
, buf
, bufLen
);
188 snprintf(buf
, bufLen
, "unexpected address family %d", address
->sa_family
);
196 #ifndef CHECK_IPV6_REACHABILITY
198 __netdb_error(int error
)
203 case NETDB_INTERNAL
:
204 msg
= strerror(errno
);
206 case HOST_NOT_FOUND
:
207 msg
= "Host not found.";
213 msg
= "No recovery.";
216 msg
= "No data available.";
225 #endif /* CHECK_IPV6_REACHABILITY */
229 __signalRunLoop(CFTypeRef obj
, CFRunLoopSourceRef rls
, CFArrayRef rlList
)
231 CFRunLoopRef rl
= NULL
;
232 CFRunLoopRef rl1
= NULL
;
234 CFIndex n
= CFArrayGetCount(rlList
);
240 /* get first runLoop for this object */
241 for (i
= 0; i
< n
; i
+= 3) {
242 if (!CFEqual(obj
, CFArrayGetValueAtIndex(rlList
, i
))) {
246 rl1
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
251 /* if not scheduled */
255 /* check if we have another runLoop for this object */
257 for (i
= i
+3; i
< n
; i
+= 3) {
260 if (!CFEqual(obj
, CFArrayGetValueAtIndex(rlList
, i
))) {
264 rl2
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
265 if (!CFEqual(rl1
, rl2
)) {
266 /* we've got more than one runLoop */
273 /* if we only have one runLoop */
278 /* more than one different runLoop, so we must pick one */
279 for (i
= 0; i
< n
; i
+=3) {
282 if (!CFEqual(obj
, CFArrayGetValueAtIndex(rlList
, i
))) {
286 rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
287 rlMode
= CFRunLoopCopyCurrentMode(rl
);
288 if (rlMode
&& CFRunLoopIsWaiting(rl
) && CFRunLoopContainsSource(rl
, rls
, rlMode
)) {
289 /* we've found a runLoop that's "ready" */
294 if (rlMode
) CFRelease(rlMode
);
297 /* didn't choose one above, so choose first */
298 CFRunLoopWakeUp(rl1
);
304 updatePPPStatus(SCDynamicStoreRef
*storeP
,
305 const struct sockaddr
*sa
,
307 SCNetworkConnectionFlags
*flags
)
309 CFDictionaryRef dict
= NULL
;
312 const void * keys_q
[N_QUICK
];
313 const void ** keys
= keys_q
;
316 int sc_status
= kSCStatusReachabilityUnknown
;
317 SCDynamicStoreRef store
= (storeP
) ? *storeP
: NULL
;
318 const void * values_q
[N_QUICK
];
319 const void ** values
= values_q
;
321 switch (sa
->sa_family
) {
323 entity
= kSCEntNetIPv4
;
326 entity
= kSCEntNetIPv6
;
333 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
335 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
341 * grab a snapshot of the PPP configuration from the dynamic store
345 CFMutableArrayRef patterns
;
347 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
348 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
349 kSCDynamicStoreDomainState
,
352 CFArrayAppendValue(patterns
, pattern
);
354 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
355 kSCDynamicStoreDomainSetup
,
358 CFArrayAppendValue(patterns
, pattern
);
360 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
361 kSCDynamicStoreDomainState
,
364 CFArrayAppendValue(patterns
, pattern
);
366 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
370 /* if we could not access the dynamic store */
374 sc_status
= kSCStatusOK
;
377 * look for the service which matches the provided interface
379 n
= CFDictionaryGetCount(dict
);
384 ppp_if
= CFStringCreateWithCStringNoCopy(NULL
,
386 kCFStringEncodingASCII
,
389 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
390 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
391 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
393 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
395 for (i
=0; i
< n
; i
++) {
396 CFArrayRef components
;
399 CFDictionaryRef p_setup
;
400 CFDictionaryRef p_state
;
402 CFStringRef service
= NULL
;
403 CFStringRef s_key
= (CFStringRef
) keys
[i
];
404 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
407 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
411 if (!CFStringHasSuffix(s_key
, entity
)) {
412 continue; // if not an IPv4 or IPv6 entity
415 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
416 if (!isA_CFString(s_if
)) {
417 continue; // if no interface
420 if (!CFEqual(ppp_if
, s_if
)) {
421 continue; // if not this interface
425 * extract service ID, get PPP "state" entity (for status), and get
426 * the "setup" entity (for dial-on-traffic flag)
428 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
429 if (CFArrayGetCount(components
) != 5) {
432 service
= CFArrayGetValueAtIndex(components
, 3);
433 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
434 kSCDynamicStoreDomainState
,
437 p_state
= CFDictionaryGetValue(dict
, key
);
439 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
440 kSCDynamicStoreDomainSetup
,
443 p_setup
= CFDictionaryGetValue(dict
, key
);
445 CFRelease(components
);
448 if (!isA_CFDictionary(p_state
)) {
451 num
= CFDictionaryGetValue(p_state
, kSCPropNetPPPStatus
);
452 if (!isA_CFNumber(num
)) {
456 if (!CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) {
459 switch (ppp_status
) {
461 /* if we're really UP and RUNNING */
464 /* if we're effectively UP and RUNNING */
467 case PPP_STATERESERVED
:
468 /* if we're not connected at all */
469 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link idle, dial-on-traffic to connect"));
470 *flags
|= kSCNetworkFlagsConnectionRequired
;
473 /* if we're in the process of [dis]connecting */
474 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link, connection in progress"));
475 *flags
|= kSCNetworkFlagsConnectionRequired
;
479 // check PPP dial-on-traffic status
480 if (isA_CFDictionary(p_setup
)) {
481 num
= CFDictionaryGetValue(p_setup
, kSCPropNetPPPDialOnDemand
);
482 if (isA_CFNumber(num
)) {
485 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
487 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
488 if (ppp_status
== PPP_IDLE
) {
489 *flags
|= kSCNetworkFlagsInterventionRequired
;
500 if (keys
!= keys_q
) {
501 CFAllocatorDeallocate(NULL
, keys
);
502 CFAllocatorDeallocate(NULL
, values
);
507 if (dict
) CFRelease(dict
);
508 if (storeP
) *storeP
= store
;
514 updatePPPAvailable(SCDynamicStoreRef
*storeP
,
515 const struct sockaddr
*sa
,
516 SCNetworkConnectionFlags
*flags
)
518 CFDictionaryRef dict
= NULL
;
521 const void * keys_q
[N_QUICK
];
522 const void ** keys
= keys_q
;
524 int sc_status
= kSCStatusReachabilityUnknown
;
525 SCDynamicStoreRef store
= (storeP
) ? *storeP
: NULL
;
526 const void * values_q
[N_QUICK
];
527 const void ** values
= values_q
;
529 switch (sa
->sa_family
) {
531 entity
= kSCEntNetIPv4
;
534 entity
= kSCEntNetIPv6
;
541 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
543 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
549 * grab a snapshot of the PPP configuration from the dynamic store
553 CFMutableArrayRef patterns
;
555 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
556 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
557 kSCDynamicStoreDomainSetup
,
560 CFArrayAppendValue(patterns
, pattern
);
562 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
563 kSCDynamicStoreDomainSetup
,
566 CFArrayAppendValue(patterns
, pattern
);
568 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
572 /* if we could not access the dynamic store */
576 sc_status
= kSCStatusOK
;
579 * look for an available service which will provide connectivity
580 * for the requested address family.
582 n
= CFDictionaryGetCount(dict
);
587 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
588 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
589 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
591 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
593 for (i
= 0; i
< n
; i
++) {
594 CFArrayRef components
;
595 Boolean found
= FALSE
;
597 CFDictionaryRef p_dict
;
599 CFStringRef s_key
= (CFStringRef
) keys
[i
];
600 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
602 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
606 if (!CFStringHasSuffix(s_key
, entity
)) {
607 continue; // if not an IPv4 or IPv6 entity
610 // extract service ID
611 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
612 if (CFArrayGetCount(components
) != 5) {
615 service
= CFArrayGetValueAtIndex(components
, 3);
617 // check for PPP entity
618 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
619 kSCDynamicStoreDomainSetup
,
622 p_dict
= CFDictionaryGetValue(dict
, p_key
);
625 if (isA_CFDictionary(p_dict
)) {
629 * we have a PPP service for this address family
633 *flags
|= kSCNetworkFlagsReachable
;
634 *flags
|= kSCNetworkFlagsTransientConnection
;
635 *flags
|= kSCNetworkFlagsConnectionRequired
;
638 * get PPP dial-on-traffic status
640 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
641 if (isA_CFNumber(num
)) {
644 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
646 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
652 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = isReachable (after connect)"));
653 SCLog(TRUE
, LOG_INFO
, CFSTR(" service = %@"), service
);
658 CFRelease(components
);
665 if (keys
!= keys_q
) {
666 CFAllocatorDeallocate(NULL
, keys
);
667 CFAllocatorDeallocate(NULL
, values
);
672 if (dict
) CFRelease(dict
);
673 if (storeP
) *storeP
= store
;
678 #define ROUNDUP(a, size) \
679 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
681 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
682 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
687 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
691 for (i
= 0; i
< RTAX_MAX
; i
++) {
692 if (addrs
& (1 << i
)) {
701 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
704 checkAddress(SCDynamicStoreRef
*storeP
,
705 const struct sockaddr
*address
,
706 SCNetworkConnectionFlags
*flags
,
711 char if_name
[IFNAMSIZ
+1];
714 pid_t pid
= getpid();
716 struct sockaddr
*rti_info
[RTAX_MAX
];
717 struct rt_msghdr
*rtm
;
719 int sc_status
= kSCStatusReachabilityUnknown
;
720 struct sockaddr_dl
*sdl
;
721 int seq
= (int)pthread_self();
722 SCDynamicStoreRef store
= (storeP
) ? *storeP
: NULL
;
723 char *statusMessage
= NULL
;
724 #ifndef RTM_GET_SILENT
725 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
726 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
727 int sosize
= 48 * 1024;
730 if (!address
|| !flags
) {
731 sc_status
= kSCStatusInvalidArgument
;
735 switch (address
->sa_family
) {
739 sockaddr_to_string(address
, buf
, sizeof(buf
));
740 SCLog(TRUE
, LOG_INFO
, CFSTR("checkAddress(%s)"), buf
);
745 * if no code for this address family (yet)
747 SCLog(_sc_verbose
, LOG_ERR
,
748 CFSTR("checkAddress(): unexpected address family %d"),
750 sc_status
= kSCStatusInvalidArgument
;
759 if ((address
->sa_family
== AF_INET
) && (((struct sockaddr_in
*)address
)->sin_addr
.s_addr
== 0)) {
760 /* special case: check for available paths off the system */
764 bzero(&buf
, sizeof(buf
));
766 rtm
= (struct rt_msghdr
*)&buf
;
767 rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
768 rtm
->rtm_version
= RTM_VERSION
;
769 #ifdef RTM_GET_SILENT
770 rtm
->rtm_type
= RTM_GET_SILENT
;
772 rtm
->rtm_type
= RTM_GET
;
774 rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
775 rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
779 switch (address
->sa_family
) {
781 struct sockaddr_in6
*sin6
;
783 sin6
= (struct sockaddr_in6
*)address
;
784 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
785 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
786 (sin6
->sin6_scope_id
!= 0)) {
787 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
788 sin6
->sin6_scope_id
= 0;
794 sa
= (struct sockaddr
*) (rtm
+ 1);
795 bcopy(address
, sa
, address
->sa_len
);
796 n
= ROUNDUP(sa
->sa_len
, sizeof(u_long
));
797 rtm
->rtm_msglen
+= n
;
799 sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
800 sdl
->sdl_family
= AF_LINK
;
801 sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
802 n
= ROUNDUP(sdl
->sdl_len
, sizeof(u_long
));
803 rtm
->rtm_msglen
+= n
;
805 #ifndef RTM_GET_SILENT
806 pthread_mutex_lock(&lock
);
808 rsock
= socket(PF_ROUTE
, SOCK_RAW
, 0);
810 #ifndef RTM_GET_SILENT
811 pthread_mutex_unlock(&lock
);
813 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(errno
));
814 sc_status
= kSCStatusFailed
;
818 #ifndef RTM_GET_SILENT
819 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
821 pthread_mutex_unlock(&lock
);
822 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(errno
));
823 sc_status
= kSCStatusFailed
;
828 if (write(rsock
, &buf
, rtm
->rtm_msglen
) == -1) {
832 #ifndef RTM_GET_SILENT
833 pthread_mutex_unlock(&lock
);
836 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(err
));
843 * Type, seq, pid identify our response.
844 * Routing sockets are broadcasters on input.
849 n
= read(rsock
, (void *)&buf
, sizeof(buf
));
855 SCLog(TRUE
, LOG_ERR
, CFSTR("read() failed: %s"), strerror(err
));
856 #ifndef RTM_GET_SILENT
857 pthread_mutex_unlock(&lock
);
862 } while (rtm
->rtm_type
!= RTM_GET
||
863 rtm
->rtm_seq
!= seq
||
864 rtm
->rtm_pid
!= pid
);
867 #ifndef RTM_GET_SILENT
868 pthread_mutex_unlock(&lock
);
871 get_rtaddrs(rtm
->rtm_addrs
, sa
, rti_info
);
878 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), rtm
->rtm_flags
);
880 for (i
= 0; i
< RTAX_MAX
; i
++) {
881 if (rti_info
[i
] != NULL
) {
882 sockaddr_to_string(rti_info
[i
], buf
, sizeof(buf
));
883 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, buf
);
889 if ((rti_info
[RTAX_IFP
] == NULL
) ||
890 (rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
891 /* no interface info */
895 sdl
= (struct sockaddr_dl
*) rti_info
[RTAX_IFP
];
896 if ((sdl
->sdl_nlen
== 0) || (sdl
->sdl_nlen
> IFNAMSIZ
)) {
897 /* no interface name */
901 /* get the interface flags */
903 bzero(&ifr
, sizeof(ifr
));
904 bcopy(sdl
->sdl_data
, ifr
.ifr_name
, sdl
->sdl_nlen
);
906 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
908 SCLog(TRUE
, LOG_NOTICE
, CFSTR("socket() failed: %s"), strerror(errno
));
912 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) < 0) {
913 SCLog(TRUE
, LOG_NOTICE
, CFSTR("ioctl() failed: %s"), strerror(errno
));
919 if (!(ifr
.ifr_flags
& IFF_UP
)) {
923 statusMessage
= "isReachable";
924 *flags
|= kSCNetworkFlagsReachable
;
926 if (rtm
->rtm_flags
& RTF_LOCAL
) {
927 statusMessage
= "isReachable (is a local address)";
928 *flags
|= kSCNetworkFlagsIsLocalAddress
;
929 } else if (ifr
.ifr_flags
& IFF_LOOPBACK
) {
930 statusMessage
= "isReachable (is loopback network)";
931 *flags
|= kSCNetworkFlagsIsLocalAddress
;
932 } else if (rti_info
[RTAX_IFA
]) {
933 void *addr1
= (void *)address
;
934 void *addr2
= (void *)rti_info
[RTAX_IFA
];
935 size_t len
= address
->sa_len
;
937 if ((address
->sa_family
!= rti_info
[RTAX_IFA
]->sa_family
) &&
938 (address
->sa_len
!= rti_info
[RTAX_IFA
]->sa_len
)) {
939 SCLog(TRUE
, LOG_NOTICE
,
940 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
943 rti_info
[RTAX_IFA
]->sa_family
,
944 rti_info
[RTAX_IFA
]->sa_len
);
948 switch (address
->sa_family
) {
950 addr1
= &((struct sockaddr_in
*)address
)->sin_addr
;
951 addr2
= &((struct sockaddr_in
*)rti_info
[RTAX_IFA
])->sin_addr
;
952 len
= sizeof(struct in_addr
);
955 addr1
= &((struct sockaddr_in6
*)address
)->sin6_addr
;
956 addr2
= &((struct sockaddr_in6
*)rti_info
[RTAX_IFA
])->sin6_addr
;
957 len
= sizeof(struct in6_addr
);
963 if (memcmp(addr1
, addr2
, len
) == 0) {
964 statusMessage
= "isReachable (is interface address)";
965 *flags
|= kSCNetworkFlagsIsLocalAddress
;
969 if (rti_info
[RTAX_GATEWAY
] && (rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
)) {
970 *flags
|= kSCNetworkFlagsIsDirect
;
973 bzero(&if_name
, sizeof(if_name
));
976 (sdl
->sdl_nlen
<= IFNAMSIZ
) ? sdl
->sdl_nlen
: IFNAMSIZ
);
979 *if_index
= sdl
->sdl_index
;
983 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = %s"), statusMessage
);
984 SCLog(TRUE
, LOG_INFO
, CFSTR(" device = %s (%hu)"), if_name
, sdl
->sdl_index
);
985 SCLog(TRUE
, LOG_INFO
, CFSTR(" ifr_flags = 0x%04hx"), ifr
.ifr_flags
);
986 SCLog(TRUE
, LOG_INFO
, CFSTR(" rtm_flags = 0x%08x"), rtm
->rtm_flags
);
989 sc_status
= kSCStatusOK
;
991 if (ifr
.ifr_flags
& IFF_POINTOPOINT
) {
993 * We have an interface which "claims" to be a valid path
996 *flags
|= kSCNetworkFlagsTransientConnection
;
999 * Check if this is a dial-on-demand PPP link that isn't
1002 sc_status
= updatePPPStatus(&store
, address
, if_name
, flags
);
1009 sc_status
= updatePPPAvailable(&store
, address
, flags
);
1014 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" cannot be reached"));
1017 if (storeP
) *storeP
= store
;
1018 if (sc_status
!= kSCStatusOK
) {
1019 _SCErrorSet(sc_status
);
1028 checkAddressZero(SCDynamicStoreRef
*storeP
,
1029 SCNetworkConnectionFlags
*flags
,
1033 struct sockaddr_in sin
;
1035 bzero(&sin
, sizeof(sin
));
1036 sin
.sin_len
= sizeof(sin
);
1037 sin
.sin_family
= AF_INET
;
1038 sin
.sin_addr
.s_addr
= 0;
1040 ok
= checkAddress(storeP
, (struct sockaddr
*)&sin
, flags
, if_index
);
1047 isAddressZero(struct sockaddr
*sa
, SCNetworkConnectionFlags
*flags
)
1052 if (sa
->sa_family
== AF_INET
) {
1053 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
1055 if (sin
->sin_addr
.s_addr
== 0) {
1056 SCLog(_sc_debug
, LOG_INFO
, CFSTR("isAddressZero(0.0.0.0)"));
1057 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = isReachable (this host)"));
1058 *flags
|= kSCNetworkFlagsReachable
;
1059 *flags
|= kSCNetworkFlagsIsLocalAddress
;
1069 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
1071 CFAllocatorRef allocator
= CFGetAllocator(cf
);
1072 CFMutableStringRef result
;
1073 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1075 result
= CFStringCreateMutable(allocator
, 0);
1076 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> { "), cf
, allocator
);
1077 switch (targetPrivate
->type
) {
1078 case reachabilityTypeAddress
:
1079 case reachabilityTypeAddressPair
: {
1082 if (targetPrivate
->localAddress
) {
1083 sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
1084 CFStringAppendFormat(result
, NULL
, CFSTR("local address=%s"),
1088 if (targetPrivate
->remoteAddress
) {
1089 sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
1090 CFStringAppendFormat(result
, NULL
, CFSTR("%s%saddress=%s"),
1091 targetPrivate
->localAddress
? ", " : "",
1092 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
1097 case reachabilityTypeName
: {
1098 CFStringAppendFormat(result
, NULL
, CFSTR("name=%s"), targetPrivate
->name
);
1099 if (targetPrivate
->resolvedAddress
|| (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1100 if (targetPrivate
->resolvedAddress
) {
1101 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
1103 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddress
);
1105 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
1106 for (i
= 0; i
< n
; i
++) {
1109 struct sockaddr
*sa
;
1111 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
);
1112 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
1113 sockaddr_to_string(sa
, buf
, sizeof(buf
));
1114 CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"),
1118 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
1120 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)"));
1123 #ifdef CHECK_IPV6_REACHABILITY
1124 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
1125 gai_strerror(targetPrivate
->resolvedAddressError
));
1126 #else /* CHECK_IPV6_REACHABILITY */
1127 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
1128 __netdb_error(targetPrivate
->resolvedAddressError
));
1129 #endif /* CHECK_IPV6_REACHABILITY */
1131 } else if (targetPrivate
->dnsPort
) {
1132 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
1137 if (targetPrivate
->rls
) {
1138 CFStringAppendFormat(result
,
1140 CFSTR(", flags=%8.8x, if_index=%hu"),
1141 targetPrivate
->flags
,
1142 targetPrivate
->if_index
);
1144 CFStringAppendFormat(result
, NULL
, CFSTR(" }"));
1151 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1153 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1155 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCNetworkReachabilityDeallocate:"));
1157 /* release resources */
1159 pthread_mutex_destroy(&targetPrivate
->lock
);
1161 if (targetPrivate
->name
)
1162 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
1164 if (targetPrivate
->resolvedAddress
)
1165 CFRelease(targetPrivate
->resolvedAddress
);
1167 if (targetPrivate
->localAddress
)
1168 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
1170 if (targetPrivate
->remoteAddress
)
1171 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
1173 if (targetPrivate
->rlsContext
.release
) {
1174 targetPrivate
->rlsContext
.release(targetPrivate
->rlsContext
.info
);
1182 __SCNetworkReachabilityInitialize(void)
1184 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
1189 static SCNetworkReachabilityPrivateRef
1190 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
1192 SCNetworkReachabilityPrivateRef targetPrivate
;
1195 /* initialize runtime */
1196 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
1198 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("__SCNetworkReachabilityCreatePrivate:"));
1200 /* allocate target */
1201 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
1202 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
1203 __kSCNetworkReachabilityTypeID
,
1206 if (!targetPrivate
) {
1210 pthread_mutex_init(&targetPrivate
->lock
, NULL
);
1212 targetPrivate
->name
= NULL
;
1214 targetPrivate
->resolvedAddress
= NULL
;
1215 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1217 targetPrivate
->localAddress
= NULL
;
1218 targetPrivate
->remoteAddress
= NULL
;
1220 targetPrivate
->flags
= 0;
1221 targetPrivate
->if_index
= 0;
1223 targetPrivate
->rls
= NULL
;
1224 targetPrivate
->rlsFunction
= NULL
;
1225 targetPrivate
->rlsContext
.info
= NULL
;
1226 targetPrivate
->rlsContext
.retain
= NULL
;
1227 targetPrivate
->rlsContext
.release
= NULL
;
1228 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1229 targetPrivate
->rlList
= NULL
;
1231 targetPrivate
->dnsPort
= NULL
;
1232 targetPrivate
->dnsRLS
= NULL
;
1234 return targetPrivate
;
1238 SCNetworkReachabilityRef
1239 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
1240 const struct sockaddr
*address
)
1242 SCNetworkReachabilityPrivateRef targetPrivate
;
1245 (address
->sa_len
== 0) ||
1246 (address
->sa_len
> sizeof(struct sockaddr_storage
))) {
1247 _SCErrorSet(kSCStatusInvalidArgument
);
1251 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1252 if (!targetPrivate
) {
1256 targetPrivate
->type
= reachabilityTypeAddress
;
1257 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
1258 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
1260 return (SCNetworkReachabilityRef
)targetPrivate
;
1264 SCNetworkReachabilityRef
1265 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
1266 const struct sockaddr
*localAddress
,
1267 const struct sockaddr
*remoteAddress
)
1269 SCNetworkReachabilityPrivateRef targetPrivate
;
1271 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
1272 _SCErrorSet(kSCStatusInvalidArgument
);
1277 if ((localAddress
->sa_len
== 0) ||
1278 (localAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1279 _SCErrorSet(kSCStatusInvalidArgument
);
1284 if (remoteAddress
) {
1285 if ((remoteAddress
->sa_len
== 0) ||
1286 (remoteAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1287 _SCErrorSet(kSCStatusInvalidArgument
);
1292 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1293 if (!targetPrivate
) {
1297 targetPrivate
->type
= reachabilityTypeAddressPair
;
1300 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
1301 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
1304 if (remoteAddress
) {
1305 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
1306 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
1309 return (SCNetworkReachabilityRef
)targetPrivate
;
1313 SCNetworkReachabilityRef
1314 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
1315 const char *nodename
)
1317 SCNetworkReachabilityPrivateRef targetPrivate
;
1320 _SCErrorSet(kSCStatusInvalidArgument
);
1324 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1325 if (!targetPrivate
) {
1329 targetPrivate
->type
= reachabilityTypeName
;
1331 targetPrivate
->flags
|= kSCNetworkFlagsFirstResolvePending
;
1333 targetPrivate
->name
= CFAllocatorAllocate(NULL
, strlen(nodename
) + 1, 0);
1334 strcpy((char *)targetPrivate
->name
, nodename
);
1336 return (SCNetworkReachabilityRef
)targetPrivate
;
1341 SCNetworkReachabilityGetTypeID(void)
1343 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
1344 return __kSCNetworkReachabilityTypeID
;
1349 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
1352 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1354 if (!isA_SCNetworkReachability(target
)) {
1355 _SCErrorSet(kSCStatusInvalidArgument
);
1359 if (targetPrivate
->type
!= reachabilityTypeName
) {
1360 _SCErrorSet(kSCStatusInvalidArgument
);
1365 *error_num
= targetPrivate
->resolvedAddressError
;
1368 if (targetPrivate
->resolvedAddress
|| (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1369 if (targetPrivate
->resolvedAddress
) {
1370 return CFRetain(targetPrivate
->resolvedAddress
);
1372 /* if status is known but no resolved addresses to return */
1373 _SCErrorSet(kSCStatusOK
);
1378 _SCErrorSet(kSCStatusReachabilityUnknown
);
1384 __SCNetworkReachabilitySetResolvedAddress(SCNetworkReachabilityRef target
,
1385 CFArrayRef addresses
,
1388 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1390 if (targetPrivate
->resolvedAddress
) {
1391 CFRelease(targetPrivate
->resolvedAddress
);
1393 targetPrivate
->resolvedAddress
= addresses
? CFRetain(addresses
) : NULL
;
1394 targetPrivate
->resolvedAddressError
= error_num
;
1399 #ifdef CHECK_IPV6_REACHABILITY
1401 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo
*res
, void *context
)
1404 struct addrinfo
*resP
;
1405 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
1406 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1408 ok
= (status
== 0) && (res
!= NULL
);
1410 SCLog(_sc_debug
, LOG_DEBUG
,
1411 CFSTR("process async DNS complete%s"),
1412 ok
? "" : ", host not found");
1415 CFMutableArrayRef addresses
;
1417 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1419 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
1420 CFDataRef newAddress
;
1422 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
1423 CFArrayAppendValue(addresses
, newAddress
);
1424 CFRelease(newAddress
);
1427 /* save the resolved address[es] */
1428 __SCNetworkReachabilitySetResolvedAddress(target
, addresses
, NETDB_SUCCESS
);
1429 CFRelease(addresses
);
1431 SCLog(_sc_debug
, LOG_INFO
, CFSTR("getaddrinfo() failed: %s"), gai_strerror(status
));
1433 /* save the error associated with the attempt to resolve the name */
1434 __SCNetworkReachabilitySetResolvedAddress(target
, (CFArrayRef
)kCFNull
, status
);
1437 if (res
) freeaddrinfo(res
);
1439 if (targetPrivate
->rls
) {
1440 SCLog(_sc_debug
, LOG_INFO
, CFSTR("DNS request completed"));
1441 CFRunLoopSourceSignal(targetPrivate
->rls
);
1442 __signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1447 #else /* CHECK_IPV6_REACHABILITY */
1449 __SCNetworkReachabilityCallbackSetResolvedAddress(struct hostent
*h
, int error
, void *context
)
1451 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
1452 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1454 SCLog(_sc_debug
, LOG_DEBUG
,
1455 CFSTR("process async DNS complete%s"),
1456 (h
== NULL
) ? ", host not found" : "");
1458 if (h
&& h
->h_length
) {
1459 CFMutableArrayRef addresses
;
1462 struct sockaddr_in sin
;
1463 struct sockaddr_in6 sin6
;
1464 struct sockaddr_storage ss
;
1466 char **ha
= h
->h_addr_list
;
1468 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1470 bzero(&addr
, sizeof(addr
));
1473 CFDataRef newAddress
;
1475 switch (h
->h_length
) {
1476 case sizeof(struct in_addr
) :
1477 addr
.sin
.sin_family
= AF_INET
;
1478 addr
.sin
.sin_len
= sizeof(struct sockaddr_in
);
1479 bcopy(*ha
, &addr
.sin
.sin_addr
, h
->h_length
);
1481 case sizeof(struct in6_addr
) :
1482 addr
.sin6
.sin6_family
= AF_INET6
;
1483 addr
.sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
1484 bcopy(*ha
, &addr
.sin6
.sin6_addr
, h
->h_length
);
1488 newAddress
= CFDataCreate(NULL
, (void *)&addr
, addr
.sa
.sa_len
);
1489 CFArrayAppendValue(addresses
, newAddress
);
1490 CFRelease(newAddress
);
1495 /* save the resolved address[es] */
1496 __SCNetworkReachabilitySetResolvedAddress(target
, addresses
, NETDB_SUCCESS
);
1497 CFRelease(addresses
);
1499 SCLog(_sc_debug
, LOG_INFO
, CFSTR("getipnodebyname() failed: %s"), __netdb_error(error
));
1501 /* save the error associated with the attempt to resolve the name */
1502 __SCNetworkReachabilitySetResolvedAddress(target
, (CFArrayRef
)kCFNull
, error
);
1505 if (h
) freehostent(h
);
1507 if (targetPrivate
->rls
) {
1508 SCLog(_sc_debug
, LOG_INFO
, CFSTR("DNS request completed"));
1509 CFRunLoopSourceSignal(targetPrivate
->rls
);
1510 __signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1515 #endif /* CHECK_IPV6_REACHABILITY */
1519 * rankReachability()
1520 * Not reachable == 0
1521 * Connection Required == 1
1525 rankReachability(SCNetworkConnectionFlags flags
)
1529 if (flags
& kSCNetworkFlagsReachable
) rank
= 2;
1530 if (flags
& kSCNetworkFlagsConnectionRequired
) rank
= 1;
1535 #ifdef CHECK_IPV6_REACHABILITY
1537 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1539 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1540 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1542 pthread_mutex_lock(&targetPrivate
->lock
);
1544 getaddrinfo_async_handle_reply(msg
);
1546 if (port
== targetPrivate
->dnsPort
) {
1547 CFRelease(targetPrivate
->dnsRLS
);
1548 targetPrivate
->dnsRLS
= NULL
;
1549 CFRelease(targetPrivate
->dnsPort
);
1550 targetPrivate
->dnsPort
= NULL
;
1553 pthread_mutex_unlock(&targetPrivate
->lock
);
1557 #else /* CHECK_IPV6_REACHABILITY */
1559 getipnodebyname_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1561 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1562 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1564 pthread_mutex_lock(&targetPrivate
->lock
);
1566 getipnodebyname_async_handleReply(msg
);
1568 if (port
== targetPrivate
->dnsPort
) {
1569 CFRelease(targetPrivate
->dnsRLS
);
1570 targetPrivate
->dnsRLS
= NULL
;
1571 CFRelease(targetPrivate
->dnsPort
);
1572 targetPrivate
->dnsPort
= NULL
;
1575 pthread_mutex_unlock(&targetPrivate
->lock
);
1579 #endif /* CHECK_IPV6_REACHABILITY */
1583 checkResolverReachability(SCDynamicStoreRef
*storeP
,
1584 SCNetworkConnectionFlags
*flags
,
1591 * We first assume that all of the configured DNS servers
1592 * are available. Since we don't know which name server will
1593 * be consulted to resolve the specified nodename we need to
1594 * check the availability of ALL name servers. We can only
1595 * proceed if we know that our query can be answered.
1598 *flags
= kSCNetworkFlagsReachable
;
1603 /* if we are actively watching at least one host */
1609 for (i
= 0; i
< _res
.nscount
; i
++) {
1610 SCNetworkConnectionFlags ns_flags
= 0;
1612 if (_res
.nsaddr_list
[i
].sin_addr
.s_addr
== 0) {
1618 if (_res
.nsaddr_list
[i
].sin_len
== 0) {
1619 _res
.nsaddr_list
[i
].sin_len
= sizeof(_res
.nsaddr_list
[i
]);
1622 ok
= checkAddress(storeP
, (struct sockaddr
*)&_res
.nsaddr_list
[i
], &ns_flags
, NULL
);
1627 if (rankReachability(ns_flags
) < rankReachability(*flags
)) {
1628 /* return the worst case result */
1634 /* if no DNS server addresses */
1643 startAsyncDNSQuery(SCNetworkReachabilityRef target
) {
1644 CFMachPortContext context
= { 0, (void *)target
, CFRetain
, CFRelease
, CFCopyDescription
};
1646 #ifdef CHECK_IPV6_REACHABILITY
1647 struct addrinfo hints
;
1648 #endif /* CHECK_IPV6_REACHABILITY */
1652 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1654 #ifdef CHECK_IPV6_REACHABILITY
1655 bzero(&hints
, sizeof(hints
));
1656 hints
.ai_flags
= AI_ADDRCONFIG
;
1658 error
= getaddrinfo_async_start(&port
,
1659 targetPrivate
->name
,
1662 __SCNetworkReachabilityCallbackSetResolvedAddress
,
1665 /* save the error associated with the attempt to resolve the name */
1666 __SCNetworkReachabilitySetResolvedAddress(target
, (CFArrayRef
)kCFNull
, error
);
1670 targetPrivate
->dnsPort
= CFMachPortCreateWithPort(NULL
,
1672 getaddrinfo_async_handleCFReply
,
1675 #else /* CHECK_IPV6_REACHABILITY */
1676 port
= getipnodebyname_async_start(targetPrivate
->name
,
1680 __SCNetworkReachabilityCallbackSetResolvedAddress
,
1682 if (port
== MACH_PORT_NULL
) {
1683 /* save the error associated with the attempt to resolve the name */
1684 __SCNetworkReachabilitySetResolvedAddress(target
, (CFArrayRef
)kCFNull
, error
);
1688 targetPrivate
->dnsPort
= CFMachPortCreateWithPort(NULL
,
1690 getipnodebyname_async_handleCFReply
,
1693 #endif /* CHECK_IPV6_REACHABILITY */
1695 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
1697 n
= CFArrayGetCount(targetPrivate
->rlList
);
1698 for (i
= 0; i
< n
; i
+= 3) {
1699 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
1700 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
1702 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
1710 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef
*storeP
,
1711 SCNetworkReachabilityRef target
,
1712 SCNetworkConnectionFlags
*flags
,
1716 CFMutableArrayRef addresses
= NULL
;
1717 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1718 SCNetworkConnectionFlags my_flags
= 0;
1719 uint16_t my_index
= 0;
1727 if (!isA_SCNetworkReachability(target
)) {
1728 _SCErrorSet(kSCStatusInvalidArgument
);
1732 switch (targetPrivate
->type
) {
1733 case reachabilityTypeAddress
:
1734 case reachabilityTypeAddressPair
: {
1736 * Check "local" address
1738 if (targetPrivate
->localAddress
) {
1742 if (isAddressZero(targetPrivate
->localAddress
, &my_flags
)) {
1747 * Check "local" address
1749 ok
= checkAddress(storeP
, targetPrivate
->localAddress
, &my_flags
, &my_index
);
1751 goto error
; /* not today */
1754 if (!(my_flags
& kSCNetworkFlagsIsLocalAddress
)) {
1755 goto error
; /* not reachable, non-"local" address */
1762 * Check "remote" address
1764 if (targetPrivate
->remoteAddress
) {
1766 * in cases where we have "local" and "remote" addresses
1767 * we need to re-initialize the to-be-returned flags.
1775 if (isAddressZero(targetPrivate
->remoteAddress
, &my_flags
)) {
1780 * Check "remote" address
1782 ok
= checkAddress(storeP
, targetPrivate
->remoteAddress
, &my_flags
, &my_index
);
1784 goto error
; /* not today */
1792 case reachabilityTypeName
: {
1794 #ifndef CHECK_IPV6_REACHABILITY
1796 #endif /* CHECK_IPV6_REACHABILITY */
1797 Boolean haveDNS
= FALSE
;
1798 #ifdef CHECK_IPV6_REACHABILITY
1799 struct addrinfo hints
;
1800 #endif /* CHECK_IPV6_REACHABILITY */
1801 SCNetworkConnectionFlags ns_flags
;
1802 #ifdef CHECK_IPV6_REACHABILITY
1803 struct addrinfo
*res
;
1804 #endif /* CHECK_IPV6_REACHABILITY */
1806 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
1807 if (addresses
|| (error
!= NETDB_SUCCESS
)) {
1808 /* if resolved or an error had been detected */
1809 goto checkResolvedAddress
;
1812 /* check the reachability of the DNS servers */
1813 ok
= checkResolverReachability(storeP
, &ns_flags
, &haveDNS
);\
1815 /* if we could not get DNS server info */
1819 if (rankReachability(ns_flags
) < 2) {
1821 * if DNS servers are not (or are no longer) reachable, set
1822 * flags based on the availability of configured (but not
1825 if (!checkAddressZero(storeP
, &my_flags
, &my_index
)) {
1829 if (async
&& targetPrivate
->rls
) {
1831 * return HOST_NOT_FOUND, set flags appropriately,
1832 * and schedule notification.
1834 #ifdef CHECK_IPV6_REACHABILITY
1835 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NODATA
,
1838 #else /* CHECK_IPV6_REACHABILITY */
1839 __SCNetworkReachabilityCallbackSetResolvedAddress(NULL
,
1842 #endif /* CHECK_IPV6_REACHABILITY */
1844 my_flags
|= (targetPrivate
->flags
& kSCNetworkFlagsFirstResolvePending
);
1846 SCLog(_sc_debug
, LOG_INFO
, CFSTR("no DNS servers are reachable"));
1847 CFRunLoopSourceSignal(targetPrivate
->rls
);
1848 __signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1854 /* for async requests we return the last known status */
1855 my_flags
= targetPrivate
->flags
;
1856 my_index
= targetPrivate
->if_index
;
1858 if (targetPrivate
->dnsPort
) {
1859 /* if request already in progress */
1863 SCLog(_sc_debug
, LOG_INFO
, CFSTR("start DNS query for \"%s\""), targetPrivate
->name
);
1866 * initiate an async DNS query
1868 if (!startAsyncDNSQuery(target
)) {
1869 /* if we could not initiate the request, process error */
1870 goto checkResolvedAddress
;
1873 /* if request initiated */
1877 SCLog(_sc_debug
, LOG_INFO
, CFSTR("check DNS for \"%s\""), targetPrivate
->name
);
1880 * OK, all of the DNS name servers are available. Let's
1881 * resolve the nodename into an address.
1883 #ifdef CHECK_IPV6_REACHABILITY
1884 bzero(&hints
, sizeof(hints
));
1885 hints
.ai_flags
= AI_ADDRCONFIG
;
1887 error
= getaddrinfo(targetPrivate
->name
, NULL
, &hints
, &res
);
1888 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, res
, (void *)target
);
1889 #else /* CHECK_IPV6_REACHABILITY */
1890 h
= getipnodebyname(targetPrivate
->name
, AF_INET
, 0, &error
);
1891 __SCNetworkReachabilityCallbackSetResolvedAddress(h
, error
, (void *)target
);
1892 #endif /* CHECK_IPV6_REACHABILITY */
1894 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
1896 checkResolvedAddress
:
1899 * We first assume that the requested host is NOT available.
1900 * Then, check each address for accessibility and return the
1901 * best status available.
1906 if (isA_CFArray(addresses
)) {
1908 CFIndex n
= CFArrayGetCount(addresses
);
1910 for (i
= 0; i
< n
; i
++) {
1911 SCNetworkConnectionFlags ns_flags
= 0;
1912 uint16_t ns_if_index
= 0;
1913 struct sockaddr
*sa
;
1915 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
1916 ok
= checkAddress(storeP
, sa
, &ns_flags
, &ns_if_index
);
1918 goto error
; /* not today */
1921 if (rankReachability(ns_flags
) > rankReachability(my_flags
)) {
1922 /* return the best case result */
1923 my_flags
= ns_flags
;
1924 my_index
= ns_if_index
;
1925 if (rankReachability(my_flags
) == 2) {
1932 if ((error
== HOST_NOT_FOUND
) && !haveDNS
) {
1934 * No DNS servers are defined. Set flags based on
1935 * the availability of configured (but not active)
1938 ok
= checkAddressZero(storeP
, &my_flags
, &my_index
);
1940 goto error
; /* not today */
1943 if ((my_flags
& kSCNetworkFlagsReachable
) &&
1944 (my_flags
& kSCNetworkFlagsConnectionRequired
)) {
1946 * Since we might pick up a set of DNS servers when this connection
1947 * is established, don't reply with a "HOST NOT FOUND" error just yet.
1952 /* Host not found, not reachable! */
1964 *if_index
= my_index
;
1969 if (addresses
) CFRelease(addresses
);
1975 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
1976 SCNetworkConnectionFlags
*flags
)
1979 SCDynamicStoreRef store
= NULL
;
1980 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1982 if (!isA_SCNetworkReachability(target
)) {
1983 _SCErrorSet(kSCStatusInvalidArgument
);
1987 if (targetPrivate
->rlList
) {
1988 /* if being watched, return current (OK, last known) status */
1989 *flags
= targetPrivate
->flags
& ~kSCNetworkFlagsFirstResolvePending
;
1993 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, flags
, NULL
, FALSE
);
1994 *flags
&= ~kSCNetworkFlagsFirstResolvePending
;
1995 if (store
) CFRelease(store
);
2001 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
2004 CFMutableArrayRef keys
;
2005 CFStringRef pattern
;
2006 CFMutableArrayRef patterns
;
2008 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2009 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2012 * Setup:/Network/Global/IPv4 (for the ServiceOrder)
2014 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2015 kSCDynamicStoreDomainSetup
,
2017 CFArrayAppendValue(keys
, key
);
2021 * State:/Network/Global/DNS
2023 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2024 kSCDynamicStoreDomainState
,
2026 CFArrayAppendValue(keys
, key
);
2030 * State:/Network/Global/IPv4
2032 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2033 kSCDynamicStoreDomainState
,
2035 CFArrayAppendValue(keys
, key
);
2038 /* Setup: per-service IPv4 info */
2039 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2040 kSCDynamicStoreDomainSetup
,
2043 CFArrayAppendValue(patterns
, pattern
);
2046 /* Setup: per-service Interface info */
2047 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2048 kSCDynamicStoreDomainSetup
,
2050 kSCEntNetInterface
);
2051 CFArrayAppendValue(patterns
, pattern
);
2054 /* Setup: per-service PPP info */
2055 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2056 kSCDynamicStoreDomainSetup
,
2059 CFArrayAppendValue(patterns
, pattern
);
2062 /* State: per-service IPv4 info */
2063 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2064 kSCDynamicStoreDomainState
,
2067 CFArrayAppendValue(patterns
, pattern
);
2070 /* State: per-interface IPv4 info */
2071 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2072 kSCDynamicStoreDomainState
,
2075 CFArrayAppendValue(patterns
, pattern
);
2078 /* State: per-interface IPv6 info */
2079 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2080 kSCDynamicStoreDomainState
,
2083 CFArrayAppendValue(patterns
, pattern
);
2086 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
2088 CFRelease(patterns
);
2095 __SCNetworkReachabilityReachabilityHandleChanges(SCDynamicStoreRef store
,
2096 CFArrayRef changedKeys
,
2099 Boolean dnsChanged
= FALSE
;
2103 const void * targets_q
[N_QUICK
];
2104 const void ** targets
= targets_q
;
2106 pthread_mutex_lock(&hn_lock
);
2108 nTargets
= CFSetGetCount(hn_targets
);
2109 if (nTargets
== 0) {
2110 /* if no addresses being monitored */
2114 if (CFArrayGetCount(changedKeys
) == 0) {
2119 SCLog(_sc_debug
, LOG_INFO
, CFSTR("process configuration change"));
2121 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2122 kSCDynamicStoreDomainState
,
2124 if (CFArrayContainsValue(changedKeys
,
2125 CFRangeMake(0, CFArrayGetCount(changedKeys
)),
2127 dnsChanged
= TRUE
; /* the DNS server(s) have changed */
2128 needDNS
= TRUE
; /* ... and we need to res_init() on the next query */
2134 * if the DNS configuration didn't change we still need to
2135 * check that the DNS servers are accessible.
2137 Boolean haveDNS
= FALSE
;
2138 SCNetworkConnectionFlags ns_flags
;
2141 /* check the reachability of the DNS servers */
2142 ok
= checkResolverReachability(&store
, &ns_flags
, &haveDNS
);\
2143 if (!ok
|| (rankReachability(ns_flags
) < 2)) {
2144 /* if DNS servers are not reachable */
2149 SCLog(_sc_debug
&& dnsChanged
, LOG_INFO
, CFSTR(" DNS changed"));
2151 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
2152 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
2153 CFSetGetValues(hn_targets
, targets
);
2154 for (i
= 0; i
< nTargets
; i
++) {
2155 SCNetworkReachabilityRef target
= targets
[i
];
2156 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2158 pthread_mutex_lock(&targetPrivate
->lock
);
2161 if (targetPrivate
->dnsPort
) {
2162 /* cancel the outstanding DNS query */
2163 #ifdef CHECK_IPV6_REACHABILITY
2164 lu_async_call_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2165 #else /* CHECK_IPV6_REACHABILITY */
2166 getipnodebyname_async_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2167 #endif /* CHECK_IPV6_REACHABILITY */
2168 CFRelease(targetPrivate
->dnsRLS
);
2169 targetPrivate
->dnsRLS
= NULL
;
2170 CFRelease(targetPrivate
->dnsPort
);
2171 targetPrivate
->dnsPort
= NULL
;
2174 /* schedule request to resolve the name again */
2175 __SCNetworkReachabilitySetResolvedAddress(target
, NULL
, NETDB_SUCCESS
);
2178 CFRunLoopSourceSignal(targetPrivate
->rls
);
2179 __signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2181 pthread_mutex_unlock(&targetPrivate
->lock
);
2183 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
2187 pthread_mutex_unlock(&hn_lock
);
2193 __isScheduled(CFTypeRef obj
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, CFMutableArrayRef rlList
)
2196 CFIndex n
= CFArrayGetCount(rlList
);
2198 for (i
= 0; i
< n
; i
+= 3) {
2199 if (obj
&& !CFEqual(obj
, CFArrayGetValueAtIndex(rlList
, i
))) {
2202 if (runLoop
&& !CFEqual(runLoop
, CFArrayGetValueAtIndex(rlList
, i
+1))) {
2205 if (runLoopMode
&& !CFEqual(runLoopMode
, CFArrayGetValueAtIndex(rlList
, i
+2))) {
2216 __schedule(CFTypeRef obj
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, CFMutableArrayRef rlList
)
2218 CFArrayAppendValue(rlList
, obj
);
2219 CFArrayAppendValue(rlList
, runLoop
);
2220 CFArrayAppendValue(rlList
, runLoopMode
);
2227 __unschedule(CFTypeRef obj
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, CFMutableArrayRef rlList
, Boolean all
)
2230 Boolean found
= FALSE
;
2231 CFIndex n
= CFArrayGetCount(rlList
);
2234 if (obj
&& !CFEqual(obj
, CFArrayGetValueAtIndex(rlList
, i
))) {
2238 if (runLoop
&& !CFEqual(runLoop
, CFArrayGetValueAtIndex(rlList
, i
+1))) {
2242 if (runLoopMode
&& !CFEqual(runLoopMode
, CFArrayGetValueAtIndex(rlList
, i
+2))) {
2249 CFArrayRemoveValueAtIndex(rlList
, i
+ 2);
2250 CFArrayRemoveValueAtIndex(rlList
, i
+ 1);
2251 CFArrayRemoveValueAtIndex(rlList
, i
);
2265 rlsPerform(void *info
)
2268 void (*context_release
)(const void *);
2269 SCNetworkConnectionFlags flags
;
2272 SCNetworkReachabilityCallBack rlsFunction
;
2273 SCDynamicStoreRef store
= NULL
;
2274 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2275 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2277 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("process reachability change"));
2279 pthread_mutex_lock(&targetPrivate
->lock
);
2281 /* update reachability, notify if status changed */
2282 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
);
2283 if (store
) CFRelease(store
);
2285 /* if reachability status not available */
2290 if ((targetPrivate
->flags
== flags
) && (targetPrivate
->if_index
== if_index
)) {
2291 /* if reachability flags and interface have not changed */
2292 pthread_mutex_unlock(&targetPrivate
->lock
);
2293 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("flags/interface match"));
2297 /* update flags / interface */
2298 targetPrivate
->flags
= flags
;
2299 targetPrivate
->if_index
= if_index
;
2302 rlsFunction
= targetPrivate
->rlsFunction
;
2303 if (NULL
!= targetPrivate
->rlsContext
.retain
) {
2304 context_info
= (void *)targetPrivate
->rlsContext
.retain(targetPrivate
->rlsContext
.info
);
2305 context_release
= targetPrivate
->rlsContext
.release
;
2307 context_info
= targetPrivate
->rlsContext
.info
;
2308 context_release
= NULL
;
2311 pthread_mutex_unlock(&targetPrivate
->lock
);
2314 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("flags/interface have changed"));
2315 (*rlsFunction
)(target
, flags
, context_info
);
2318 if (context_release
) {
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
) {
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
) {
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
);
2379 * if we are not monitoring any hosts
2381 hn_store
= SCDynamicStoreCreate(NULL
,
2382 CFSTR("SCNetworkReachability"),
2383 __SCNetworkReachabilityReachabilityHandleChanges
,
2386 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
2390 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
2392 hn_storeRLS
= SCDynamicStoreCreateRunLoopSource(NULL
, hn_store
, 0);
2393 hn_rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2394 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
2397 if (!targetPrivate
->rls
) {
2398 CFRunLoopSourceContext context
= { 0 // version
2399 , (void *)target
// info
2400 , CFRetain
// retain
2401 , CFRelease
// release
2402 , CFCopyDescription
// copyDescription
2407 , rlsPerform
// perform
2410 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
2411 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2415 if (!__isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2417 * if we do not already have host notifications scheduled with
2418 * this runLoop / runLoopMode
2420 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2422 if (targetPrivate
->dnsRLS
) {
2423 /* if we have an active async DNS query too */
2424 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2428 __schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
2430 /* schedule the SCNetworkReachability run loop source */
2432 if (!__isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2434 * if we do not already have SC notifications scheduled with
2435 * this runLoop / runLoopMode
2437 CFRunLoopAddSource(runLoop
, hn_storeRLS
, runLoopMode
);
2440 __schedule(target
, runLoop
, runLoopMode
, hn_rlList
);
2441 CFSetAddValue(hn_targets
, target
);
2444 SCNetworkConnectionFlags flags
;
2446 SCDynamicStoreRef store
= NULL
;
2449 * if we have yet to schedule SC notifications for this address
2450 * - initialize current reachability status
2452 if (__SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
)) {
2454 * if reachability status available
2456 * - schedule notification to report status via callback
2458 targetPrivate
->flags
= flags
;
2459 targetPrivate
->if_index
= if_index
;
2460 CFRunLoopSourceSignal(targetPrivate
->rls
);
2461 __signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2463 /* if reachability status not available, async lookup started */
2464 targetPrivate
->flags
= 0;
2465 targetPrivate
->if_index
= 0;
2467 if (store
) CFRelease(store
);
2474 pthread_mutex_unlock(&targetPrivate
->lock
);
2475 pthread_mutex_unlock(&hn_lock
);
2481 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
2482 CFRunLoopRef runLoop
,
2483 CFStringRef runLoopMode
)
2485 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2489 if (!isA_SCNetworkReachability(target
) || runLoop
== NULL
|| runLoopMode
== NULL
) {
2490 _SCErrorSet(kSCStatusInvalidArgument
);
2494 pthread_mutex_lock(&hn_lock
);
2495 pthread_mutex_lock(&targetPrivate
->lock
);
2497 if (!targetPrivate
->rls
) {
2498 /* if not currently scheduled */
2502 if (!__unschedule(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
2503 /* if not currently scheduled */
2507 n
= CFArrayGetCount(targetPrivate
->rlList
);
2508 if (n
== 0 || !__isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2510 * if this host is no longer scheduled for this runLoop / runLoopMode
2512 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2514 if (targetPrivate
->dnsRLS
) {
2515 /* if we have an active async DNS query too */
2516 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2521 * if this host is no longer scheduled
2523 CFRelease(targetPrivate
->rls
); /* cleanup SCNetworkReachability resources */
2524 targetPrivate
->rls
= NULL
;
2525 CFRelease(targetPrivate
->rlList
);
2526 targetPrivate
->rlList
= NULL
;
2527 CFSetRemoveValue(hn_targets
, target
); /* cleanup notification resources */
2529 if (targetPrivate
->dnsPort
) {
2530 /* if we have an active async DNS query too */
2531 #ifdef CHECK_IPV6_REACHABILITY
2532 lu_async_call_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2533 #else /* CHECK_IPV6_REACHABILITY */
2534 getipnodebyname_async_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2535 #endif /* CHECK_IPV6_REACHABILITY */
2536 CFRelease(targetPrivate
->dnsRLS
);
2537 targetPrivate
->dnsRLS
= NULL
;
2538 CFRelease(targetPrivate
->dnsPort
);
2539 targetPrivate
->dnsPort
= NULL
;
2544 (void)__unschedule(target
, runLoop
, runLoopMode
, hn_rlList
, FALSE
);
2546 n
= CFArrayGetCount(hn_rlList
);
2547 if (n
== 0 || !__isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2549 * if we no longer have any addresses scheduled for
2550 * this runLoop / runLoopMode
2552 CFRunLoopRemoveSource(runLoop
, hn_storeRLS
, runLoopMode
);
2556 * if we are no longer monitoring any addresses
2558 CFRelease(hn_targets
);
2559 CFRelease(hn_rlList
);
2560 CFRelease(hn_storeRLS
);
2561 CFRelease(hn_store
);
2565 * until we start monitoring again, ensure that
2566 * all subsequent reachability-by-name checks
2577 pthread_mutex_unlock(&targetPrivate
->lock
);
2578 pthread_mutex_unlock(&hn_lock
);