2 * Copyright (c) 2003-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * March 31, 2004 Allan Nathanson <ajn@apple.com>
28 * - use [SC] DNS configuration information
30 * January 19, 2003 Allan Nathanson <ajn@apple.com>
31 * - add advanced reachability APIs
34 #include <SystemConfiguration/SystemConfiguration.h>
35 #include <SystemConfiguration/SCValidation.h>
36 #include <SystemConfiguration/SCPrivate.h>
38 #include <CoreFoundation/CFRuntime.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
46 #include <netdb_async.h>
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
52 #include <net/if_dl.h>
53 #define KERNEL_PRIVATE
54 #include <net/route.h>
58 #define s6_addr16 __u6_addr.__u6_addr16
61 #include <ppp/ppp_msg.h>
66 #define kSCNetworkFlagsFirstResolvePending (1<<31)
73 reachabilityTypeAddress
,
74 reachabilityTypeAddressPair
,
79 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
80 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
85 /* base CFType information */
94 /* target host name */
96 CFArrayRef resolvedAddress
; /* CFArray[CFData] */
97 int resolvedAddressError
;
99 /* local & remote addresses */
100 struct sockaddr
*localAddress
;
101 struct sockaddr
*remoteAddress
;
103 /* current reachability flags */
104 SCNetworkConnectionFlags flags
;
107 /* run loop source, callout, context, rl scheduling info */
108 CFRunLoopSourceRef rls
;
109 SCNetworkReachabilityCallBack rlsFunction
;
110 SCNetworkReachabilityContext rlsContext
;
111 CFMutableArrayRef rlList
;
113 /* [async] DNS query info */
115 CFMachPortRef dnsPort
;
116 CFRunLoopSourceRef dnsRLS
;
117 struct timeval dnsQueryStart
;
119 } SCNetworkReachabilityPrivate
, *SCNetworkReachabilityPrivateRef
;
122 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
125 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
127 "SCNetworkReachability", // className
130 __SCNetworkReachabilityDeallocate
, // dealloc
133 NULL
, // copyFormattingDesc
134 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
138 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
142 * host "something has changed" notifications
145 static pthread_mutex_t hn_lock
= PTHREAD_MUTEX_INITIALIZER
;
146 static SCDynamicStoreRef hn_store
= NULL
;
147 static CFRunLoopSourceRef hn_storeRLS
= NULL
;
148 static CFMutableArrayRef hn_rlList
= NULL
;
149 static CFMutableSetRef hn_targets
= NULL
;
157 dns_config_t
*config
;
159 } dns_configuration_t
;
162 static pthread_mutex_t dns_lock
= PTHREAD_MUTEX_INITIALIZER
;
163 static dns_configuration_t
*dns_configuration
= NULL
;
164 static int dns_token
;
165 static Boolean dns_token_valid
= FALSE
;
168 static __inline__ CFTypeRef
169 isA_SCNetworkReachability(CFTypeRef obj
)
171 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
176 __log_query_time(Boolean found
, Boolean async
, struct timeval
*start
)
178 struct timeval dnsQueryComplete
;
179 struct timeval dnsQueryElapsed
;
185 if (start
->tv_sec
== 0) {
189 (void) gettimeofday(&dnsQueryComplete
, NULL
);
190 timersub(&dnsQueryComplete
, start
, &dnsQueryElapsed
);
191 SCLog(TRUE
, LOG_DEBUG
,
192 CFSTR("%ssync DNS complete%s (query time = %d.%3.3d)"),
194 found
? "" : ", host not found",
195 dnsQueryElapsed
.tv_sec
,
196 dnsQueryElapsed
.tv_usec
/ 1000);
203 updatePPPStatus(SCDynamicStoreRef
*storeP
,
204 const struct sockaddr
*sa
,
206 SCNetworkConnectionFlags
*flags
)
208 CFDictionaryRef dict
= NULL
;
211 const void * keys_q
[N_QUICK
];
212 const void ** keys
= keys_q
;
215 int sc_status
= kSCStatusReachabilityUnknown
;
216 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
217 const void * values_q
[N_QUICK
];
218 const void ** values
= values_q
;
220 switch (sa
->sa_family
) {
222 entity
= kSCEntNetIPv4
;
225 entity
= kSCEntNetIPv6
;
232 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
234 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("updatePPPStatus SCDynamicStoreCreate() failed"));
240 * grab a snapshot of the PPP configuration from the dynamic store
244 CFMutableArrayRef patterns
;
246 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
247 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
248 kSCDynamicStoreDomainState
,
251 CFArrayAppendValue(patterns
, pattern
);
253 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
254 kSCDynamicStoreDomainSetup
,
257 CFArrayAppendValue(patterns
, pattern
);
259 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
260 kSCDynamicStoreDomainState
,
263 CFArrayAppendValue(patterns
, pattern
);
265 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
269 /* if we could not access the dynamic store */
273 sc_status
= kSCStatusOK
;
276 * look for the service which matches the provided interface
278 n
= CFDictionaryGetCount(dict
);
283 ppp_if
= CFStringCreateWithCStringNoCopy(NULL
,
285 kCFStringEncodingASCII
,
288 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
289 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
290 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
292 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
294 for (i
=0; i
< n
; i
++) {
295 CFArrayRef components
;
298 CFDictionaryRef p_setup
;
299 CFDictionaryRef p_state
;
301 CFStringRef service
= NULL
;
302 CFStringRef s_key
= (CFStringRef
) keys
[i
];
303 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
306 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
310 if (!CFStringHasSuffix(s_key
, entity
)) {
311 continue; // if not an IPv4 or IPv6 entity
314 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
315 if (!isA_CFString(s_if
)) {
316 continue; // if no interface
319 if (!CFEqual(ppp_if
, s_if
)) {
320 continue; // if not this interface
324 * extract service ID, get PPP "state" entity (for status), and get
325 * the "setup" entity (for dial-on-traffic flag)
327 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
328 if (CFArrayGetCount(components
) != 5) {
331 service
= CFArrayGetValueAtIndex(components
, 3);
332 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
333 kSCDynamicStoreDomainState
,
336 p_state
= CFDictionaryGetValue(dict
, key
);
338 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
339 kSCDynamicStoreDomainSetup
,
342 p_setup
= CFDictionaryGetValue(dict
, key
);
344 CFRelease(components
);
347 if (!isA_CFDictionary(p_state
)) {
350 num
= CFDictionaryGetValue(p_state
, kSCPropNetPPPStatus
);
351 if (!isA_CFNumber(num
)) {
355 if (!CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) {
358 switch (ppp_status
) {
360 /* if we're really UP and RUNNING */
363 /* if we're effectively UP and RUNNING */
366 case PPP_STATERESERVED
:
367 /* if we're not connected at all */
368 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link idle, dial-on-traffic to connect"));
369 *flags
|= kSCNetworkFlagsConnectionRequired
;
372 /* if we're in the process of [dis]connecting */
373 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link, connection in progress"));
374 *flags
|= kSCNetworkFlagsConnectionRequired
;
378 // check PPP dial-on-traffic status
379 if (isA_CFDictionary(p_setup
)) {
380 num
= CFDictionaryGetValue(p_setup
, kSCPropNetPPPDialOnDemand
);
381 if (isA_CFNumber(num
)) {
384 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
386 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
387 if (ppp_status
== PPP_IDLE
) {
388 *flags
|= kSCNetworkFlagsInterventionRequired
;
399 if (keys
!= keys_q
) {
400 CFAllocatorDeallocate(NULL
, keys
);
401 CFAllocatorDeallocate(NULL
, values
);
406 if (dict
!= NULL
) CFRelease(dict
);
407 if (storeP
!= NULL
) *storeP
= store
;
413 updatePPPAvailable(SCDynamicStoreRef
*storeP
,
414 const struct sockaddr
*sa
,
415 SCNetworkConnectionFlags
*flags
)
417 CFDictionaryRef dict
= NULL
;
420 const void * keys_q
[N_QUICK
];
421 const void ** keys
= keys_q
;
423 int sc_status
= kSCStatusReachabilityUnknown
;
424 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
425 const void * values_q
[N_QUICK
];
426 const void ** values
= values_q
;
429 entity
= kSCEntNetIPv4
;
431 switch (sa
->sa_family
) {
433 entity
= kSCEntNetIPv4
;
436 entity
= kSCEntNetIPv6
;
444 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
446 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = unknown (could not access SCDynamicStore"));
452 * grab a snapshot of the PPP configuration from the dynamic store
456 CFMutableArrayRef patterns
;
458 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
459 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
460 kSCDynamicStoreDomainSetup
,
463 CFArrayAppendValue(patterns
, pattern
);
465 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
466 kSCDynamicStoreDomainSetup
,
469 CFArrayAppendValue(patterns
, pattern
);
471 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
475 /* if we could not access the dynamic store */
479 sc_status
= kSCStatusOK
;
482 * look for an available service which will provide connectivity
483 * for the requested address family.
485 n
= CFDictionaryGetCount(dict
);
490 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
491 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
492 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
494 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
496 for (i
= 0; i
< n
; i
++) {
497 CFArrayRef components
;
498 Boolean found
= FALSE
;
500 CFDictionaryRef p_dict
;
502 CFStringRef s_key
= (CFStringRef
) keys
[i
];
503 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
505 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
509 if (!CFStringHasSuffix(s_key
, entity
)) {
510 continue; // if not an IPv4 or IPv6 entity
513 // extract service ID
514 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
515 if (CFArrayGetCount(components
) != 5) {
518 service
= CFArrayGetValueAtIndex(components
, 3);
520 // check for PPP entity
521 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
522 kSCDynamicStoreDomainSetup
,
525 p_dict
= CFDictionaryGetValue(dict
, p_key
);
528 if (isA_CFDictionary(p_dict
)) {
532 * we have a PPP service for this address family
536 *flags
|= kSCNetworkFlagsReachable
;
537 *flags
|= kSCNetworkFlagsTransientConnection
;
538 *flags
|= kSCNetworkFlagsConnectionRequired
;
541 * get PPP dial-on-traffic status
543 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
544 if (isA_CFNumber(num
)) {
547 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
549 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
555 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = isReachable (after connect)"));
556 SCLog(TRUE
, LOG_INFO
, CFSTR(" service = %@"), service
);
561 CFRelease(components
);
568 if (keys
!= keys_q
) {
569 CFAllocatorDeallocate(NULL
, keys
);
570 CFAllocatorDeallocate(NULL
, values
);
575 if (dict
!= NULL
) CFRelease(dict
);
576 if (storeP
!= NULL
) *storeP
= store
;
581 #define ROUNDUP(a, size) \
582 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
584 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
585 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
590 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
594 for (i
= 0; i
< RTAX_MAX
; i
++) {
595 if (addrs
& (1 << i
)) {
604 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
607 checkAddress(SCDynamicStoreRef
*storeP
,
608 const struct sockaddr
*address
,
609 SCNetworkConnectionFlags
*flags
,
614 char if_name
[IFNAMSIZ
+1];
617 pid_t pid
= getpid();
619 struct sockaddr
*rti_info
[RTAX_MAX
];
620 struct rt_msghdr
*rtm
;
622 int sc_status
= kSCStatusReachabilityUnknown
;
623 struct sockaddr_dl
*sdl
;
624 int seq
= (int)pthread_self();
625 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
626 char *statusMessage
= NULL
;
627 #ifndef RTM_GET_SILENT
628 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
629 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
630 int sosize
= 48 * 1024;
634 if (if_index
!= NULL
) {
638 if (address
== NULL
) {
639 /* special case: check only for available paths off the system */
643 switch (address
->sa_family
) {
647 _SC_sockaddr_to_string(address
, buf
, sizeof(buf
));
648 SCLog(TRUE
, LOG_INFO
, CFSTR("checkAddress(%s)"), buf
);
653 * if no code for this address family (yet)
655 SCLog(_sc_verbose
, LOG_ERR
,
656 CFSTR("checkAddress(): unexpected address family %d"),
658 sc_status
= kSCStatusInvalidArgument
;
662 bzero(&buf
, sizeof(buf
));
664 rtm
= (struct rt_msghdr
*)&buf
;
665 rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
666 rtm
->rtm_version
= RTM_VERSION
;
667 #ifdef RTM_GET_SILENT
668 rtm
->rtm_type
= RTM_GET_SILENT
;
670 rtm
->rtm_type
= RTM_GET
;
672 rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
673 rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
677 switch (address
->sa_family
) {
679 struct sockaddr_in6
*sin6
;
681 sin6
= (struct sockaddr_in6
*)address
;
682 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
683 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
684 (sin6
->sin6_scope_id
!= 0)) {
685 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
686 sin6
->sin6_scope_id
= 0;
692 sa
= (struct sockaddr
*) (rtm
+ 1);
693 bcopy(address
, sa
, address
->sa_len
);
694 n
= ROUNDUP(sa
->sa_len
, sizeof(u_long
));
695 rtm
->rtm_msglen
+= n
;
697 sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
698 sdl
->sdl_family
= AF_LINK
;
699 sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
700 n
= ROUNDUP(sdl
->sdl_len
, sizeof(u_long
));
701 rtm
->rtm_msglen
+= n
;
703 #ifndef RTM_GET_SILENT
704 pthread_mutex_lock(&lock
);
706 rsock
= socket(PF_ROUTE
, SOCK_RAW
, 0);
708 #ifndef RTM_GET_SILENT
709 pthread_mutex_unlock(&lock
);
711 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(errno
));
712 sc_status
= kSCStatusFailed
;
716 #ifndef RTM_GET_SILENT
717 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
719 pthread_mutex_unlock(&lock
);
720 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(errno
));
721 sc_status
= kSCStatusFailed
;
726 if (write(rsock
, &buf
, rtm
->rtm_msglen
) == -1) {
730 #ifndef RTM_GET_SILENT
731 pthread_mutex_unlock(&lock
);
734 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(err
));
741 * Type, seq, pid identify our response.
742 * Routing sockets are broadcasters on input.
747 n
= read(rsock
, (void *)&buf
, sizeof(buf
));
753 SCLog(TRUE
, LOG_ERR
, CFSTR("read() failed: %s"), strerror(err
));
754 #ifndef RTM_GET_SILENT
755 pthread_mutex_unlock(&lock
);
760 } while (rtm
->rtm_type
!= RTM_GET
||
761 rtm
->rtm_seq
!= seq
||
762 rtm
->rtm_pid
!= pid
);
765 #ifndef RTM_GET_SILENT
766 pthread_mutex_unlock(&lock
);
769 get_rtaddrs(rtm
->rtm_addrs
, sa
, rti_info
);
776 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), rtm
->rtm_flags
);
778 for (i
= 0; i
< RTAX_MAX
; i
++) {
779 if (rti_info
[i
] != NULL
) {
780 _SC_sockaddr_to_string(rti_info
[i
], buf
, sizeof(buf
));
781 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, buf
);
787 if ((rti_info
[RTAX_IFP
] == NULL
) ||
788 (rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
789 /* no interface info */
793 sdl
= (struct sockaddr_dl
*) rti_info
[RTAX_IFP
];
794 if ((sdl
->sdl_nlen
== 0) || (sdl
->sdl_nlen
> IFNAMSIZ
)) {
795 /* no interface name */
799 /* get the interface flags */
801 bzero(&ifr
, sizeof(ifr
));
802 bcopy(sdl
->sdl_data
, ifr
.ifr_name
, sdl
->sdl_nlen
);
804 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
806 SCLog(TRUE
, LOG_NOTICE
, CFSTR("socket() failed: %s"), strerror(errno
));
810 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) < 0) {
811 SCLog(TRUE
, LOG_NOTICE
, CFSTR("ioctl() failed: %s"), strerror(errno
));
817 if (!(ifr
.ifr_flags
& IFF_UP
)) {
821 statusMessage
= "isReachable";
822 *flags
|= kSCNetworkFlagsReachable
;
824 if (rtm
->rtm_flags
& RTF_LOCAL
) {
825 statusMessage
= "isReachable (is a local address)";
826 *flags
|= kSCNetworkFlagsIsLocalAddress
;
827 } else if (ifr
.ifr_flags
& IFF_LOOPBACK
) {
828 statusMessage
= "isReachable (is loopback network)";
829 *flags
|= kSCNetworkFlagsIsLocalAddress
;
830 } else if (rti_info
[RTAX_IFA
]) {
831 void *addr1
= (void *)address
;
832 void *addr2
= (void *)rti_info
[RTAX_IFA
];
833 size_t len
= address
->sa_len
;
835 if ((address
->sa_family
!= rti_info
[RTAX_IFA
]->sa_family
) &&
836 (address
->sa_len
!= rti_info
[RTAX_IFA
]->sa_len
)) {
837 SCLog(TRUE
, LOG_NOTICE
,
838 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
841 rti_info
[RTAX_IFA
]->sa_family
,
842 rti_info
[RTAX_IFA
]->sa_len
);
846 switch (address
->sa_family
) {
848 addr1
= &((struct sockaddr_in
*)address
)->sin_addr
;
849 addr2
= &((struct sockaddr_in
*)rti_info
[RTAX_IFA
])->sin_addr
;
850 len
= sizeof(struct in_addr
);
855 if (((struct sockaddr_in
*)address
)->sin_addr
.s_addr
== 0) {
856 statusMessage
= "isReachable (this host)";
857 *flags
|= kSCNetworkFlagsIsLocalAddress
;
861 addr1
= &((struct sockaddr_in6
*)address
)->sin6_addr
;
862 addr2
= &((struct sockaddr_in6
*)rti_info
[RTAX_IFA
])->sin6_addr
;
863 len
= sizeof(struct in6_addr
);
869 if (memcmp(addr1
, addr2
, len
) == 0) {
870 statusMessage
= "isReachable (is interface address)";
871 *flags
|= kSCNetworkFlagsIsLocalAddress
;
875 if (!(rtm
->rtm_flags
& RTF_GATEWAY
) &&
876 (rti_info
[RTAX_GATEWAY
] != NULL
) &&
877 (rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
) &&
878 !(ifr
.ifr_flags
& IFF_POINTOPOINT
)) {
879 *flags
|= kSCNetworkFlagsIsDirect
;
882 bzero(&if_name
, sizeof(if_name
));
885 (sdl
->sdl_nlen
<= IFNAMSIZ
) ? sdl
->sdl_nlen
: IFNAMSIZ
);
887 if (if_index
!= NULL
) {
888 *if_index
= sdl
->sdl_index
;
892 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = %s"), statusMessage
);
893 SCLog(TRUE
, LOG_INFO
, CFSTR(" device = %s (%hu)"), if_name
, sdl
->sdl_index
);
894 SCLog(TRUE
, LOG_INFO
, CFSTR(" ifr_flags = 0x%04hx"), ifr
.ifr_flags
);
895 SCLog(TRUE
, LOG_INFO
, CFSTR(" rtm_flags = 0x%08x"), rtm
->rtm_flags
);
898 sc_status
= kSCStatusOK
;
900 if (ifr
.ifr_flags
& IFF_POINTOPOINT
) {
902 * We have an interface which "claims" to be a valid path
905 *flags
|= kSCNetworkFlagsTransientConnection
;
908 * Check if this is a dial-on-demand PPP link that isn't
911 sc_status
= updatePPPStatus(&store
, address
, if_name
, flags
);
918 sc_status
= updatePPPAvailable(&store
, address
, flags
);
923 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" cannot be reached"));
926 if (storeP
!= NULL
) *storeP
= store
;
927 if (sc_status
!= kSCStatusOK
) {
928 _SCErrorSet(sc_status
);
937 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
939 CFAllocatorRef allocator
= CFGetAllocator(cf
);
940 CFMutableStringRef result
;
941 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
943 result
= CFStringCreateMutable(allocator
, 0);
944 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> { "), cf
, allocator
);
945 switch (targetPrivate
->type
) {
946 case reachabilityTypeAddress
:
947 case reachabilityTypeAddressPair
: {
950 if (targetPrivate
->localAddress
!= NULL
) {
951 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
952 CFStringAppendFormat(result
, NULL
, CFSTR("local address=%s"),
956 if (targetPrivate
->remoteAddress
!= NULL
) {
957 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
958 CFStringAppendFormat(result
, NULL
, CFSTR("%s%saddress=%s"),
959 targetPrivate
->localAddress
? ", " : "",
960 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
965 case reachabilityTypeName
: {
966 CFStringAppendFormat(result
, NULL
, CFSTR("name=%s"), targetPrivate
->name
);
967 if (targetPrivate
->resolvedAddress
|| (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
968 if (targetPrivate
->resolvedAddress
!= NULL
) {
969 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
971 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddress
);
973 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
974 for (i
= 0; i
< n
; i
++) {
979 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
);
980 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
981 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
982 CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"),
986 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
988 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)"));
991 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
992 gai_strerror(targetPrivate
->resolvedAddressError
));
994 } else if (targetPrivate
->dnsPort
) {
995 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
1000 if (targetPrivate
->rls
!= NULL
) {
1001 CFStringAppendFormat(result
,
1003 CFSTR(", flags=%8.8x, if_index=%hu"),
1004 targetPrivate
->flags
,
1005 targetPrivate
->if_index
);
1007 CFStringAppendFormat(result
, NULL
, CFSTR(" }"));
1014 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1016 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1018 /* release resources */
1020 pthread_mutex_destroy(&targetPrivate
->lock
);
1022 if (targetPrivate
->name
!= NULL
)
1023 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
1025 if (targetPrivate
->resolvedAddress
!= NULL
)
1026 CFRelease(targetPrivate
->resolvedAddress
);
1028 if (targetPrivate
->localAddress
!= NULL
)
1029 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
1031 if (targetPrivate
->remoteAddress
!= NULL
)
1032 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
1034 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1035 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1043 __SCNetworkReachabilityInitialize(void)
1045 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
1050 static SCNetworkReachabilityPrivateRef
1051 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
1053 SCNetworkReachabilityPrivateRef targetPrivate
;
1056 /* initialize runtime */
1057 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
1059 /* allocate target */
1060 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
1061 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
1062 __kSCNetworkReachabilityTypeID
,
1065 if (targetPrivate
== NULL
) {
1069 pthread_mutex_init(&targetPrivate
->lock
, NULL
);
1071 targetPrivate
->name
= NULL
;
1073 targetPrivate
->resolvedAddress
= NULL
;
1074 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1076 targetPrivate
->localAddress
= NULL
;
1077 targetPrivate
->remoteAddress
= NULL
;
1079 targetPrivate
->flags
= 0;
1080 targetPrivate
->if_index
= 0;
1082 targetPrivate
->rls
= NULL
;
1083 targetPrivate
->rlsFunction
= NULL
;
1084 targetPrivate
->rlsContext
.info
= NULL
;
1085 targetPrivate
->rlsContext
.retain
= NULL
;
1086 targetPrivate
->rlsContext
.release
= NULL
;
1087 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1088 targetPrivate
->rlList
= NULL
;
1090 targetPrivate
->haveDNS
= FALSE
;
1091 targetPrivate
->dnsPort
= NULL
;
1092 targetPrivate
->dnsRLS
= NULL
;
1094 return targetPrivate
;
1098 SCNetworkReachabilityRef
1099 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
1100 const struct sockaddr
*address
)
1102 SCNetworkReachabilityPrivateRef targetPrivate
;
1104 if ((address
== NULL
) ||
1105 (address
->sa_len
== 0) ||
1106 (address
->sa_len
> sizeof(struct sockaddr_storage
))) {
1107 _SCErrorSet(kSCStatusInvalidArgument
);
1111 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1112 if (targetPrivate
== NULL
) {
1116 targetPrivate
->type
= reachabilityTypeAddress
;
1117 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
1118 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
1120 return (SCNetworkReachabilityRef
)targetPrivate
;
1124 SCNetworkReachabilityRef
1125 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
1126 const struct sockaddr
*localAddress
,
1127 const struct sockaddr
*remoteAddress
)
1129 SCNetworkReachabilityPrivateRef targetPrivate
;
1131 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
1132 _SCErrorSet(kSCStatusInvalidArgument
);
1136 if (localAddress
!= NULL
) {
1137 if ((localAddress
->sa_len
== 0) ||
1138 (localAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1139 _SCErrorSet(kSCStatusInvalidArgument
);
1144 if (remoteAddress
!= NULL
) {
1145 if ((remoteAddress
->sa_len
== 0) ||
1146 (remoteAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1147 _SCErrorSet(kSCStatusInvalidArgument
);
1152 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1153 if (targetPrivate
== NULL
) {
1157 targetPrivate
->type
= reachabilityTypeAddressPair
;
1159 if (localAddress
!= NULL
) {
1160 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
1161 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
1164 if (remoteAddress
!= NULL
) {
1165 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
1166 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
1169 return (SCNetworkReachabilityRef
)targetPrivate
;
1173 SCNetworkReachabilityRef
1174 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
1175 const char *nodename
)
1177 struct sockaddr_in sin
;
1178 struct sockaddr_in6 sin6
;
1179 SCNetworkReachabilityPrivateRef targetPrivate
;
1181 if (nodename
== NULL
) {
1182 _SCErrorSet(kSCStatusInvalidArgument
);
1186 /* check if this "nodename" is really an IP[v6] address in disguise */
1188 bzero(&sin
, sizeof(sin
));
1189 sin
.sin_len
= sizeof(sin
);
1190 sin
.sin_family
= AF_INET
;
1191 if (inet_aton(nodename
, &sin
.sin_addr
) == 1) {
1192 /* if IPv4 address */
1193 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin
);
1196 bzero(&sin6
, sizeof(sin6
));
1197 sin6
.sin6_len
= sizeof(sin6
);
1198 sin6
.sin6_family
= AF_INET6
;
1199 if (inet_pton(AF_INET6
, nodename
, &sin6
.sin6_addr
) == 1) {
1200 /* if IPv6 address */
1203 p
= strchr(nodename
, '%');
1205 sin6
.sin6_scope_id
= if_nametoindex(p
+1);
1208 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin6
);
1211 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1212 if (targetPrivate
== NULL
) {
1216 targetPrivate
->type
= reachabilityTypeName
;
1218 targetPrivate
->flags
|= kSCNetworkFlagsFirstResolvePending
;
1220 targetPrivate
->name
= CFAllocatorAllocate(NULL
, strlen(nodename
) + 1, 0);
1221 strcpy((char *)targetPrivate
->name
, nodename
);
1223 return (SCNetworkReachabilityRef
)targetPrivate
;
1228 SCNetworkReachabilityGetTypeID(void)
1230 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
1231 return __kSCNetworkReachabilityTypeID
;
1236 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
1239 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1241 if (!isA_SCNetworkReachability(target
)) {
1242 _SCErrorSet(kSCStatusInvalidArgument
);
1246 if (targetPrivate
->type
!= reachabilityTypeName
) {
1247 _SCErrorSet(kSCStatusInvalidArgument
);
1252 *error_num
= targetPrivate
->resolvedAddressError
;
1255 if (targetPrivate
->resolvedAddress
|| (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1256 if (targetPrivate
->resolvedAddress
!= NULL
) {
1257 return CFRetain(targetPrivate
->resolvedAddress
);
1259 /* if status is known but no resolved addresses to return */
1260 _SCErrorSet(kSCStatusOK
);
1265 _SCErrorSet(kSCStatusReachabilityUnknown
);
1271 __SCNetworkReachabilitySetResolvedAddress(int32_t status
,
1272 struct addrinfo
*res
,
1273 SCNetworkReachabilityRef target
)
1275 struct addrinfo
*resP
;
1276 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1278 if (targetPrivate
->resolvedAddress
!= NULL
) {
1279 CFRelease(targetPrivate
->resolvedAddress
);
1280 targetPrivate
->resolvedAddress
= NULL
;
1283 if ((status
== 0) && (res
!= NULL
)) {
1285 CFMutableArrayRef addresses
;
1287 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1289 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
1290 CFDataRef newAddress
;
1292 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
1293 CFArrayAppendValue(addresses
, newAddress
);
1294 CFRelease(newAddress
);
1297 /* save the resolved address[es] */
1298 targetPrivate
->resolvedAddress
= addresses
;
1299 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1301 SCLog(_sc_debug
, LOG_INFO
, CFSTR("getaddrinfo() failed: %s"), gai_strerror(status
));
1303 /* save the error associated with the attempt to resolve the name */
1304 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
1305 targetPrivate
->resolvedAddressError
= status
;
1308 if (res
) freeaddrinfo(res
);
1310 if (targetPrivate
->rls
!= NULL
) {
1311 SCLog(_sc_debug
, LOG_INFO
, CFSTR("DNS request completed"));
1312 CFRunLoopSourceSignal(targetPrivate
->rls
);
1313 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1321 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo
*res
, void *context
)
1323 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
1324 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1326 __log_query_time(((status
== 0) && (res
!= NULL
)), // if successful query
1328 &targetPrivate
->dnsQueryStart
); // start time
1330 __SCNetworkReachabilitySetResolvedAddress(status
, res
, target
);
1336 * rankReachability()
1337 * Not reachable == 0
1338 * Connection Required == 1
1342 rankReachability(SCNetworkConnectionFlags flags
)
1346 if (flags
& kSCNetworkFlagsReachable
) rank
= 2;
1347 if (flags
& kSCNetworkFlagsConnectionRequired
) rank
= 1;
1353 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1356 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1357 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1359 pthread_mutex_lock(&targetPrivate
->lock
);
1361 status
= getaddrinfo_async_handle_reply(msg
);
1362 if ((status
== 0) &&
1363 (targetPrivate
->resolvedAddress
== NULL
) && (targetPrivate
->resolvedAddressError
== NETDB_SUCCESS
)) {
1364 // if request has been re-queued
1368 if (port
== targetPrivate
->dnsPort
) {
1369 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
1370 CFRelease(targetPrivate
->dnsRLS
);
1371 targetPrivate
->dnsRLS
= NULL
;
1372 CFRelease(targetPrivate
->dnsPort
);
1373 targetPrivate
->dnsPort
= NULL
;
1378 pthread_mutex_unlock(&targetPrivate
->lock
);
1385 check_resolver_reachability(SCDynamicStoreRef
*storeP
,
1386 dns_resolver_t
*resolver
,
1387 SCNetworkConnectionFlags
*flags
,
1393 *flags
= kSCNetworkFlagsReachable
;
1396 for (i
= 0; i
< resolver
->n_nameserver
; i
++) {
1397 struct sockaddr
*address
= resolver
->nameserver
[i
];
1398 SCNetworkConnectionFlags ns_flags
= 0;
1402 if (address
->sa_family
!= AF_INET
) {
1404 * we need to skip non-IPv4 DNS server
1405 * addresses (at least until [3510431] has
1411 ok
= checkAddress(storeP
, address
, &ns_flags
, NULL
);
1417 if (rankReachability(ns_flags
) < rankReachability(*flags
)) {
1418 /* return the worst case result */
1430 check_matching_resolvers(SCDynamicStoreRef
*storeP
,
1431 dns_config_t
*dns_config
,
1433 SCNetworkConnectionFlags
*flags
,
1437 Boolean matched
= FALSE
;
1438 const char *name
= fqdn
;
1440 while (!matched
&& (name
!= NULL
)) {
1444 * check if the provided name (or sub-component)
1445 * matches one of our resolver configurations.
1448 for (i
= 0; i
< dns_config
->n_resolver
; i
++) {
1450 dns_resolver_t
*resolver
;
1452 resolver
= dns_config
->resolver
[i
];
1453 domain
= resolver
->domain
;
1454 if (domain
!= NULL
&& (len
== strlen(domain
))) {
1455 if (strcasecmp(name
, domain
) == 0) {
1459 * if name matches domain
1462 ok
= check_resolver_reachability(storeP
, resolver
, flags
, haveDNS
);
1473 * we have not found a matching resolver, try
1474 * a less qualified domain
1476 name
= strchr(name
, '.');
1477 if ((name
!= NULL
) && (*name
!= '\0')) {
1489 static dns_configuration_t
*
1490 dns_configuration_retain()
1492 pthread_mutex_lock(&dns_lock
);
1494 if ((dns_configuration
!= NULL
) && dns_token_valid
) {
1499 * check if the global [DNS] configuration snapshot needs
1502 status
= notify_check(dns_token
, &check
);
1503 if (status
!= NOTIFY_STATUS_OK
) {
1504 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
1507 if ((status
!= NOTIFY_STATUS_OK
) || (check
!= 0)) {
1509 * if the snapshot needs to be refreshed
1511 if (dns_configuration
->refs
== 0) {
1512 dns_configuration_free(dns_configuration
->config
);
1513 CFAllocatorDeallocate(NULL
, dns_configuration
);
1515 dns_configuration
= NULL
;
1519 if (dns_configuration
== NULL
) {
1520 dns_config_t
*new_config
;
1522 new_config
= dns_configuration_copy();
1523 if (new_config
!= NULL
) {
1524 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
1525 dns_configuration
->config
= new_config
;
1526 dns_configuration
->refs
= 0;
1530 if (dns_configuration
!= NULL
) {
1531 dns_configuration
->refs
++;
1534 pthread_mutex_unlock(&dns_lock
);
1535 return dns_configuration
;
1540 dns_configuration_release(dns_configuration_t
*config
)
1542 pthread_mutex_lock(&dns_lock
);
1545 if (config
->refs
== 0) {
1546 if ((dns_configuration
!= config
)) {
1547 dns_configuration_free(config
->config
);
1548 CFAllocatorDeallocate(NULL
, config
);
1552 pthread_mutex_unlock(&dns_lock
);
1558 dns_configuration_watch()
1561 const char *dns_key
;
1565 pthread_mutex_lock(&dns_lock
);
1567 dns_key
= dns_configuration_notify_key();
1568 if (dns_key
== NULL
) {
1569 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
1573 status
= notify_register_check(dns_key
, &dns_token
);
1574 if (status
== NOTIFY_STATUS_OK
) {
1575 dns_token_valid
= TRUE
;
1577 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
);
1581 status
= notify_check(dns_token
, &dns_check
);
1582 if (status
!= NOTIFY_STATUS_OK
) {
1583 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
1584 (void)notify_cancel(dns_token
);
1585 dns_token_valid
= FALSE
;
1593 pthread_mutex_unlock(&dns_lock
);
1599 dns_configuration_unwatch()
1601 pthread_mutex_lock(&dns_lock
);
1603 (void)notify_cancel(dns_token
);
1604 dns_token_valid
= FALSE
;
1606 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
1607 dns_configuration_free(dns_configuration
->config
);
1608 CFAllocatorDeallocate(NULL
, dns_configuration
);
1609 dns_configuration
= NULL
;
1612 pthread_mutex_unlock(&dns_lock
);
1618 _SC_checkResolverReachability(SCDynamicStoreRef
*storeP
,
1619 SCNetworkConnectionFlags
*flags
,
1621 const char * nodename
)
1623 dns_resolver_t
*default_resolver
;
1624 dns_configuration_t
*dns
;
1625 Boolean found
= FALSE
;
1626 char *fqdn
= (char *)nodename
;
1628 Boolean isFQDN
= FALSE
;
1633 * We first assume that all of the configured DNS servers
1634 * are available. Since we don't know which name server will
1635 * be consulted to resolve the specified nodename we need to
1636 * check the availability of ALL name servers. We can only
1637 * proceed if we know that our query can be answered.
1640 *flags
= kSCNetworkFlagsReachable
;
1645 // if no nodename, return not reachable
1650 dns
= dns_configuration_retain();
1656 if (dns
->config
->n_resolver
== 0) {
1657 // if no resolver configuration
1661 *flags
= kSCNetworkFlagsReachable
;
1663 if (fqdn
[len
- 1] == '.') {
1666 // trim trailing '.''s
1667 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
1668 if (fqdn
== nodename
) {
1669 fqdn
= strdup(nodename
);
1675 default_resolver
= dns
->config
->resolver
[0];
1678 * try the provided name
1680 found
= check_matching_resolvers(storeP
, dns
->config
, fqdn
, flags
, haveDNS
);
1681 if (!found
&& !isFQDN
&& (dns
->config
->n_resolver
> 1)) {
1683 * FQDN not specified, try w/search or default domain(s) too
1685 if (default_resolver
->n_search
> 0) {
1686 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
1688 char *search_fqdn
= NULL
;
1690 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
1695 // try the provided name with the search domain appended
1696 found
= check_matching_resolvers(storeP
, dns
->config
, search_fqdn
, flags
, haveDNS
);
1699 } else if (default_resolver
->domain
!= NULL
) {
1701 int domain_parts
= 0;
1703 // count domain parts
1704 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
1710 // remove trailing dots
1711 for (dp
--; (dp
>= default_resolver
->domain
) && (*dp
== '.'); dp
--) {
1716 if (dp
>= default_resolver
->domain
) {
1717 // dots are separators, bump # of components
1721 dp
= default_resolver
->domain
;
1722 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= domain_parts
); i
++) {
1724 char *search_fqdn
= NULL
;
1726 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
1731 // try the provided name with the [default] domain appended
1732 found
= check_matching_resolvers(storeP
, dns
->config
, search_fqdn
, flags
, haveDNS
);
1735 // move to the next component of the [default] domain
1736 dp
= strchr(dp
, '.') + 1;
1743 * check the reachability of the default resolver
1745 ok
= check_resolver_reachability(storeP
, default_resolver
, flags
, haveDNS
);
1748 if (fqdn
!= nodename
) free(fqdn
);
1753 dns_configuration_release(dns
);
1761 startAsyncDNSQuery(SCNetworkReachabilityRef target
) {
1762 CFMachPortContext context
= { 0, (void *)target
, CFRetain
, CFRelease
, CFCopyDescription
};
1764 struct addrinfo hints
;
1768 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1770 (void) gettimeofday(&targetPrivate
->dnsQueryStart
, NULL
);
1772 bzero(&hints
, sizeof(hints
));
1773 hints
.ai_flags
= AI_ADDRCONFIG
;
1775 hints
.ai_flags
|= AI_PARALLEL
;
1776 #endif /* AI_PARALLEL */
1778 error
= getaddrinfo_async_start(&port
,
1779 targetPrivate
->name
,
1782 __SCNetworkReachabilityCallbackSetResolvedAddress
,
1785 /* save the error associated with the attempt to resolve the name */
1786 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
);
1790 targetPrivate
->dnsPort
= CFMachPortCreateWithPort(NULL
,
1792 getaddrinfo_async_handleCFReply
,
1795 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
1797 n
= CFArrayGetCount(targetPrivate
->rlList
);
1798 for (i
= 0; i
< n
; i
+= 3) {
1799 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
1800 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
1802 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
1810 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef
*storeP
,
1811 SCNetworkReachabilityRef target
,
1812 SCNetworkConnectionFlags
*flags
,
1816 CFMutableArrayRef addresses
= NULL
;
1817 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1818 SCNetworkConnectionFlags my_flags
= 0;
1819 uint16_t my_index
= 0;
1823 if (if_index
!= NULL
) {
1827 if (!isA_SCNetworkReachability(target
)) {
1828 _SCErrorSet(kSCStatusInvalidArgument
);
1832 switch (targetPrivate
->type
) {
1833 case reachabilityTypeAddress
:
1834 case reachabilityTypeAddressPair
: {
1836 * Check "local" address
1838 if (targetPrivate
->localAddress
!= NULL
) {
1840 * Check "local" address
1842 ok
= checkAddress(storeP
, targetPrivate
->localAddress
, &my_flags
, &my_index
);
1844 goto error
; /* not today */
1847 if (!(my_flags
& kSCNetworkFlagsIsLocalAddress
)) {
1848 goto error
; /* not reachable, non-"local" address */
1853 * Check "remote" address
1855 if (targetPrivate
->remoteAddress
!= NULL
) {
1857 * in cases where we have "local" and "remote" addresses
1858 * we need to re-initialize the to-be-returned flags.
1864 * Check "remote" address
1866 ok
= checkAddress(storeP
, targetPrivate
->remoteAddress
, &my_flags
, &my_index
);
1868 goto error
; /* not today */
1876 case reachabilityTypeName
: {
1877 struct timeval dnsQueryStart
;
1879 struct addrinfo hints
;
1880 SCNetworkConnectionFlags ns_flags
;
1881 struct addrinfo
*res
;
1883 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
1884 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
1885 /* if resolved or an error had been detected */
1886 goto checkResolvedAddress
;
1889 /* check the reachability of the DNS servers */
1890 ok
= _SC_checkResolverReachability(storeP
,
1892 &targetPrivate
->haveDNS
,
1893 targetPrivate
->name
);
1895 /* if we could not get DNS server info */
1899 if (rankReachability(ns_flags
) < 2) {
1901 * if DNS servers are not (or are no longer) reachable, set
1902 * flags based on the availability of configured (but not
1905 if (!checkAddress(storeP
, NULL
, &my_flags
, &my_index
)) {
1909 if (async
&& (targetPrivate
->rls
!= NULL
)) {
1911 * return "host not found", set flags appropriately,
1912 * and schedule notification.
1914 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NODATA
,
1917 my_flags
|= (targetPrivate
->flags
& kSCNetworkFlagsFirstResolvePending
);
1919 SCLog(_sc_debug
, LOG_INFO
, CFSTR("no DNS servers are reachable"));
1920 CFRunLoopSourceSignal(targetPrivate
->rls
);
1921 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1927 /* for async requests we return the last known status */
1928 my_flags
= targetPrivate
->flags
;
1929 my_index
= targetPrivate
->if_index
;
1931 if (targetPrivate
->dnsPort
) {
1932 /* if request already in progress */
1936 SCLog(_sc_debug
, LOG_INFO
, CFSTR("start DNS query for \"%s\""), targetPrivate
->name
);
1939 * initiate an async DNS query
1941 if (!startAsyncDNSQuery(target
)) {
1942 /* if we could not initiate the request, process error */
1943 goto checkResolvedAddress
;
1946 /* if request initiated */
1950 SCLog(_sc_debug
, LOG_INFO
, CFSTR("check DNS for \"%s\""), targetPrivate
->name
);
1953 * OK, all of the DNS name servers are available. Let's
1954 * resolve the nodename into an address.
1957 (void) gettimeofday(&dnsQueryStart
, NULL
);
1960 bzero(&hints
, sizeof(hints
));
1961 hints
.ai_flags
= AI_ADDRCONFIG
;
1963 hints
.ai_flags
|= AI_PARALLEL
;
1964 #endif /* AI_PARALLEL */
1966 error
= getaddrinfo(targetPrivate
->name
, NULL
, &hints
, &res
);
1968 __log_query_time(((error
== 0) && (res
!= NULL
)),// if successful query
1970 &dnsQueryStart
); // start time
1972 __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
);
1974 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
1976 checkResolvedAddress
:
1979 * We first assume that the requested host is NOT available.
1980 * Then, check each address for accessibility and return the
1981 * best status available.
1986 if (isA_CFArray(addresses
)) {
1988 CFIndex n
= CFArrayGetCount(addresses
);
1990 for (i
= 0; i
< n
; i
++) {
1991 SCNetworkConnectionFlags ns_flags
= 0;
1992 uint16_t ns_if_index
= 0;
1993 struct sockaddr
*sa
;
1995 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
1997 ok
= checkAddress(storeP
, sa
, &ns_flags
, &ns_if_index
);
1999 goto error
; /* not today */
2002 if (rankReachability(ns_flags
) > rankReachability(my_flags
)) {
2003 /* return the best case result */
2004 my_flags
= ns_flags
;
2005 my_index
= ns_if_index
;
2006 if (rankReachability(my_flags
) == 2) {
2013 if ((error
== EAI_NODATA
) && !targetPrivate
->haveDNS
) {
2015 * No DNS servers are defined. Set flags based on
2016 * the availability of configured (but not active)
2019 ok
= checkAddress(storeP
, NULL
, &my_flags
, &my_index
);
2021 goto error
; /* not today */
2024 if ((my_flags
& kSCNetworkFlagsReachable
) &&
2025 (my_flags
& kSCNetworkFlagsConnectionRequired
)) {
2027 * Since we might pick up a set of DNS servers when this connection
2028 * is established, don't reply with a "HOST NOT FOUND" error just yet.
2033 /* Host not found, not reachable! */
2044 if (if_index
!= NULL
) {
2045 *if_index
= my_index
;
2050 if (addresses
!= NULL
) CFRelease(addresses
);
2056 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
2057 SCNetworkConnectionFlags
*flags
)
2060 SCDynamicStoreRef store
= NULL
;
2061 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2063 if (!isA_SCNetworkReachability(target
)) {
2064 _SCErrorSet(kSCStatusInvalidArgument
);
2068 if (targetPrivate
->rlList
!= NULL
) {
2069 // if being watched, return the last known (and what should be current) status
2070 *flags
= targetPrivate
->flags
& ~kSCNetworkFlagsFirstResolvePending
;
2075 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, flags
, NULL
, FALSE
);
2076 *flags
&= ~kSCNetworkFlagsFirstResolvePending
;
2077 if (store
!= NULL
) CFRelease(store
);
2083 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
2085 CFStringRef dns_key
;
2087 CFMutableArrayRef keys
;
2088 CFStringRef pattern
;
2089 CFMutableArrayRef patterns
;
2091 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2092 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2094 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
2095 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2096 kSCDynamicStoreDomainSetup
,
2098 CFArrayAppendValue(keys
, key
);
2101 dns_key
= CFStringCreateWithCString(NULL
,
2102 dns_configuration_notify_key(),
2103 kCFStringEncodingASCII
);
2104 key
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Notify:%@"), dns_key
);
2106 CFArrayAppendValue(keys
, key
);
2109 // State:/Network/Global/IPv4 (default route)
2110 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2111 kSCDynamicStoreDomainState
,
2113 CFArrayAppendValue(keys
, key
);
2116 // Setup: per-service IPv4 info
2117 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2118 kSCDynamicStoreDomainSetup
,
2121 CFArrayAppendValue(patterns
, pattern
);
2124 // Setup: per-service Interface info
2125 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2126 kSCDynamicStoreDomainSetup
,
2128 kSCEntNetInterface
);
2129 CFArrayAppendValue(patterns
, pattern
);
2132 // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand)
2133 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2134 kSCDynamicStoreDomainSetup
,
2137 CFArrayAppendValue(patterns
, pattern
);
2140 // State: per-interface IPv4 info
2141 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2142 kSCDynamicStoreDomainState
,
2145 CFArrayAppendValue(patterns
, pattern
);
2148 // State: per-interface IPv6 info
2149 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2150 kSCDynamicStoreDomainState
,
2153 CFArrayAppendValue(patterns
, pattern
);
2156 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
2158 CFRelease(patterns
);
2165 __SCNetworkReachabilityReachabilityHandleChanges(SCDynamicStoreRef store
,
2166 CFArrayRef changedKeys
,
2169 Boolean dnsConfigChanged
= FALSE
;
2174 const void * targets_q
[N_QUICK
];
2175 const void ** targets
= targets_q
;
2177 pthread_mutex_lock(&hn_lock
);
2179 nTargets
= CFSetGetCount(hn_targets
);
2180 if (nTargets
== 0) {
2181 /* if no addresses being monitored */
2185 if (CFArrayGetCount(changedKeys
) == 0) {
2190 SCLog(_sc_debug
, LOG_INFO
, CFSTR("process configuration change"));
2192 dnsKey
= CFStringCreateWithCString(NULL
,
2193 dns_configuration_notify_key(),
2194 kCFStringEncodingASCII
);
2195 key
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Notify:%@"), dnsKey
);
2197 if (CFArrayContainsValue(changedKeys
,
2198 CFRangeMake(0, CFArrayGetCount(changedKeys
)),
2200 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
2205 SCLog(_sc_debug
&& dnsConfigChanged
, LOG_INFO
, CFSTR(" DNS configuration changed"));
2207 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
2208 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
2209 CFSetGetValues(hn_targets
, targets
);
2210 for (i
= 0; i
< nTargets
; i
++) {
2211 SCNetworkReachabilityRef target
= targets
[i
];
2212 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2214 pthread_mutex_lock(&targetPrivate
->lock
);
2216 if (targetPrivate
->type
== reachabilityTypeName
) {
2217 Boolean dnsChanged
= dnsConfigChanged
;
2221 * if the DNS configuration didn't change we still need to
2222 * check that the DNS servers are accessible.
2224 SCNetworkConnectionFlags ns_flags
;
2227 /* check the reachability of the DNS servers */
2228 ok
= _SC_checkResolverReachability(&store
,
2230 &targetPrivate
->haveDNS
,
2231 targetPrivate
->name
);
2232 if (!ok
|| (rankReachability(ns_flags
) < 2)) {
2233 /* if DNS servers are not reachable */
2239 if (targetPrivate
->dnsPort
) {
2240 /* cancel the outstanding DNS query */
2241 lu_async_call_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2242 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
2243 CFRelease(targetPrivate
->dnsRLS
);
2244 targetPrivate
->dnsRLS
= NULL
;
2245 CFRelease(targetPrivate
->dnsPort
);
2246 targetPrivate
->dnsPort
= NULL
;
2249 /* schedule request to resolve the name again */
2250 if (targetPrivate
->resolvedAddress
!= NULL
) {
2251 CFRelease(targetPrivate
->resolvedAddress
);
2252 targetPrivate
->resolvedAddress
= NULL
;
2254 targetPrivate
->resolvedAddress
= NULL
;
2255 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
2259 CFRunLoopSourceSignal(targetPrivate
->rls
);
2260 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2262 pthread_mutex_unlock(&targetPrivate
->lock
);
2264 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
2268 pthread_mutex_unlock(&hn_lock
);
2274 rlsPerform(void *info
)
2277 void (*context_release
)(const void *);
2278 SCNetworkConnectionFlags flags
;
2281 SCNetworkReachabilityCallBack rlsFunction
;
2282 SCDynamicStoreRef store
= NULL
;
2283 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2284 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2286 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("process reachability change"));
2289 pthread_mutex_lock(&targetPrivate
->lock
);
2291 /* update reachability, notify if status changed */
2292 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
);
2293 if (store
!= NULL
) CFRelease(store
);
2295 /* if reachability status not available */
2300 if ((targetPrivate
->flags
== flags
) && (targetPrivate
->if_index
== if_index
)) {
2301 /* if reachability flags and interface have not changed */
2302 pthread_mutex_unlock(&targetPrivate
->lock
);
2303 SCLog(_sc_debug
, LOG_DEBUG
,
2304 CFSTR("flags/interface match (now %8.8x/%hu)"),
2308 SCLog(_sc_debug
, LOG_DEBUG
,
2309 CFSTR("flags/interface have changed (was %8.8x/%hu, now %8.8x/%hu)"),
2310 targetPrivate
->flags
, targetPrivate
->if_index
,
2314 /* update flags / interface */
2315 targetPrivate
->flags
= flags
;
2316 targetPrivate
->if_index
= if_index
;
2319 rlsFunction
= targetPrivate
->rlsFunction
;
2320 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
2321 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
2322 context_release
= targetPrivate
->rlsContext
.release
;
2324 context_info
= targetPrivate
->rlsContext
.info
;
2325 context_release
= NULL
;
2328 pthread_mutex_unlock(&targetPrivate
->lock
);
2330 if (rlsFunction
!= NULL
) {
2331 (*rlsFunction
)(target
, flags
, context_info
);
2334 if (context_release
!= NULL
) {
2335 (*context_release
)(context_info
);
2343 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
2344 SCNetworkReachabilityCallBack callout
,
2345 SCNetworkReachabilityContext
*context
)
2347 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2349 pthread_mutex_lock(&targetPrivate
->lock
);
2351 if (targetPrivate
->rlsContext
.release
!= NULL
) {
2352 /* let go of the current context */
2353 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
2356 targetPrivate
->rlsFunction
= callout
;
2357 targetPrivate
->rlsContext
.info
= NULL
;
2358 targetPrivate
->rlsContext
.retain
= NULL
;
2359 targetPrivate
->rlsContext
.release
= NULL
;
2360 targetPrivate
->rlsContext
.copyDescription
= NULL
;
2362 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
2363 if (context
->retain
!= NULL
) {
2364 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
2368 pthread_mutex_unlock(&targetPrivate
->lock
);
2375 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
2376 CFRunLoopRef runLoop
,
2377 CFStringRef runLoopMode
)
2379 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2380 Boolean init
= FALSE
;
2383 if (!isA_SCNetworkReachability(target
) || runLoop
== NULL
|| runLoopMode
== NULL
) {
2384 _SCErrorSet(kSCStatusInvalidArgument
);
2388 /* schedule the SCNetworkReachability run loop source */
2390 pthread_mutex_lock(&hn_lock
);
2391 pthread_mutex_lock(&targetPrivate
->lock
);
2393 if (hn_store
== NULL
) {
2395 * if we are not monitoring any hosts, start watching
2397 if (!dns_configuration_watch()) {
2399 _SCErrorSet(kSCStatusFailed
);
2403 hn_store
= SCDynamicStoreCreate(NULL
,
2404 CFSTR("SCNetworkReachability"),
2405 __SCNetworkReachabilityReachabilityHandleChanges
,
2407 if (hn_store
== NULL
) {
2408 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
2412 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
2414 hn_storeRLS
= SCDynamicStoreCreateRunLoopSource(NULL
, hn_store
, 0);
2415 hn_rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2416 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
2419 if (targetPrivate
->rls
== NULL
) {
2420 CFRunLoopSourceContext context
= { 0 // version
2421 , (void *)target
// info
2422 , CFRetain
// retain
2423 , CFRelease
// release
2424 , CFCopyDescription
// copyDescription
2429 , rlsPerform
// perform
2432 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
2433 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2437 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2439 * if we do not already have host notifications scheduled with
2440 * this runLoop / runLoopMode
2442 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2444 if (targetPrivate
->dnsRLS
!= NULL
) {
2445 /* if we have an active async DNS query too */
2446 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2450 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
2452 /* schedule the SCNetworkReachability run loop source */
2454 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2456 * if we do not already have SC notifications scheduled with
2457 * this runLoop / runLoopMode
2459 CFRunLoopAddSource(runLoop
, hn_storeRLS
, runLoopMode
);
2462 _SC_schedule(target
, runLoop
, runLoopMode
, hn_rlList
);
2463 CFSetAddValue(hn_targets
, target
);
2466 SCNetworkConnectionFlags flags
;
2468 SCDynamicStoreRef store
= NULL
;
2471 * if we have yet to schedule SC notifications for this address
2472 * - initialize current reachability status
2474 if (__SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
)) {
2476 * if reachability status available
2478 * - schedule notification to report status via callback
2480 targetPrivate
->flags
= flags
;
2481 targetPrivate
->if_index
= if_index
;
2482 CFRunLoopSourceSignal(targetPrivate
->rls
);
2483 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2485 /* if reachability status not available, async lookup started */
2486 targetPrivate
->flags
= 0;
2487 targetPrivate
->if_index
= 0;
2489 if (store
!= NULL
) CFRelease(store
);
2496 pthread_mutex_unlock(&targetPrivate
->lock
);
2497 pthread_mutex_unlock(&hn_lock
);
2503 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
2504 CFRunLoopRef runLoop
,
2505 CFStringRef runLoopMode
)
2507 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2511 if (!isA_SCNetworkReachability(target
) || runLoop
== NULL
|| runLoopMode
== NULL
) {
2512 _SCErrorSet(kSCStatusInvalidArgument
);
2516 pthread_mutex_lock(&hn_lock
);
2517 pthread_mutex_lock(&targetPrivate
->lock
);
2519 if (targetPrivate
->rls
== NULL
) {
2520 /* if not currently scheduled */
2524 if (!_SC_unschedule(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
2525 /* if not currently scheduled */
2529 n
= CFArrayGetCount(targetPrivate
->rlList
);
2530 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2532 * if this host is no longer scheduled for this runLoop / runLoopMode
2534 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2536 if (targetPrivate
->dnsRLS
!= NULL
) {
2537 /* if we have an active async DNS query too */
2538 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2543 * if this host is no longer scheduled
2545 CFRunLoopSourceInvalidate(targetPrivate
->rls
); /* cleanup SCNetworkReachability resources */
2546 CFRelease(targetPrivate
->rls
);
2547 targetPrivate
->rls
= NULL
;
2548 CFRelease(targetPrivate
->rlList
);
2549 targetPrivate
->rlList
= NULL
;
2550 CFSetRemoveValue(hn_targets
, target
); /* cleanup notification resources */
2552 if (targetPrivate
->dnsPort
) {
2553 /* if we have an active async DNS query too */
2554 lu_async_call_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2555 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
2556 CFRelease(targetPrivate
->dnsRLS
);
2557 targetPrivate
->dnsRLS
= NULL
;
2558 CFRelease(targetPrivate
->dnsPort
);
2559 targetPrivate
->dnsPort
= NULL
;
2564 (void)_SC_unschedule(target
, runLoop
, runLoopMode
, hn_rlList
, FALSE
);
2566 n
= CFArrayGetCount(hn_rlList
);
2567 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2569 * if we no longer have any addresses scheduled for
2570 * this runLoop / runLoopMode
2572 CFRunLoopRemoveSource(runLoop
, hn_storeRLS
, runLoopMode
);
2576 * if we are no longer monitoring any addresses
2578 CFRelease(hn_targets
);
2580 CFRelease(hn_rlList
);
2582 CFRunLoopSourceInvalidate(hn_storeRLS
);
2583 CFRelease(hn_storeRLS
);
2585 CFRelease(hn_store
);
2589 * until we start monitoring again, ensure that
2590 * any resources associated with tracking the
2591 * DNS configuration have been released.
2593 dns_configuration_unwatch();
2601 pthread_mutex_unlock(&targetPrivate
->lock
);
2602 pthread_mutex_unlock(&hn_lock
);