2 * Copyright (c) 2003-2007 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * 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>
40 #include <libkern/OSAtomic.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
47 #include <netdb_async.h>
50 #include <sys/ioctl.h>
51 #include <sys/socket.h>
53 #include <net/if_dl.h>
54 #define KERNEL_PRIVATE
55 #include <net/route.h>
59 #define s6_addr16 __u6_addr.__u6_addr16
62 #include <ppp/ppp_msg.h>
67 #define kSCNetworkFlagsFirstResolvePending (1<<31)
74 reachabilityTypeAddress
,
75 reachabilityTypeAddressPair
,
80 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
81 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
86 /* base CFType information */
95 /* target host name */
97 CFArrayRef resolvedAddress
; /* CFArray[CFData] */
98 int resolvedAddressError
;
100 /* local & remote addresses */
101 struct sockaddr
*localAddress
;
102 struct sockaddr
*remoteAddress
;
104 /* current reachability flags */
105 SCNetworkConnectionFlags flags
;
108 /* run loop source, callout, context, rl scheduling info */
109 CFRunLoopSourceRef rls
;
110 SCNetworkReachabilityCallBack rlsFunction
;
111 SCNetworkReachabilityContext rlsContext
;
112 CFMutableArrayRef rlList
;
114 /* [async] DNS query info */
116 CFMachPortRef dnsPort
;
117 CFRunLoopSourceRef dnsRLS
;
118 struct timeval dnsQueryStart
;
120 } SCNetworkReachabilityPrivate
, *SCNetworkReachabilityPrivateRef
;
123 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
126 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
128 "SCNetworkReachability", // className
131 __SCNetworkReachabilityDeallocate
, // dealloc
134 NULL
, // copyFormattingDesc
135 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
139 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
140 static int rtm_seq
= 0;
144 * host "something has changed" notifications
147 static pthread_mutex_t hn_lock
= PTHREAD_MUTEX_INITIALIZER
;
148 static SCDynamicStoreRef hn_store
= NULL
;
149 static CFRunLoopSourceRef hn_storeRLS
= NULL
;
150 static CFMutableArrayRef hn_rlList
= NULL
;
151 static CFMutableSetRef hn_targets
= NULL
;
159 dns_config_t
*config
;
161 } dns_configuration_t
;
164 static pthread_mutex_t dns_lock
= PTHREAD_MUTEX_INITIALIZER
;
165 static dns_configuration_t
*dns_configuration
= NULL
;
166 static int dns_token
;
167 static Boolean dns_token_valid
= FALSE
;
170 static __inline__ CFTypeRef
171 isA_SCNetworkReachability(CFTypeRef obj
)
173 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
178 __log_query_time(Boolean found
, Boolean async
, struct timeval
*start
)
180 struct timeval dnsQueryComplete
;
181 struct timeval dnsQueryElapsed
;
187 if (start
->tv_sec
== 0) {
191 (void) gettimeofday(&dnsQueryComplete
, NULL
);
192 timersub(&dnsQueryComplete
, start
, &dnsQueryElapsed
);
193 SCLog(TRUE
, LOG_DEBUG
,
194 CFSTR("%ssync DNS complete%s (query time = %d.%3.3d)"),
196 found
? "" : ", host not found",
197 dnsQueryElapsed
.tv_sec
,
198 dnsQueryElapsed
.tv_usec
/ 1000);
205 updatePPPStatus(SCDynamicStoreRef
*storeP
,
206 const struct sockaddr
*sa
,
208 SCNetworkConnectionFlags
*flags
)
210 CFDictionaryRef dict
= NULL
;
213 const void * keys_q
[N_QUICK
];
214 const void ** keys
= keys_q
;
217 int sc_status
= kSCStatusReachabilityUnknown
;
218 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
219 const void * values_q
[N_QUICK
];
220 const void ** values
= values_q
;
222 switch (sa
->sa_family
) {
224 entity
= kSCEntNetIPv4
;
227 entity
= kSCEntNetIPv6
;
234 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
236 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("updatePPPStatus SCDynamicStoreCreate() failed"));
242 * grab a snapshot of the PPP configuration from the dynamic store
246 CFMutableArrayRef patterns
;
248 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
249 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
250 kSCDynamicStoreDomainState
,
253 CFArrayAppendValue(patterns
, pattern
);
255 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
256 kSCDynamicStoreDomainSetup
,
259 CFArrayAppendValue(patterns
, pattern
);
261 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
262 kSCDynamicStoreDomainState
,
265 CFArrayAppendValue(patterns
, pattern
);
267 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
271 /* if we could not access the dynamic store */
275 sc_status
= kSCStatusOK
;
278 * look for the service which matches the provided interface
280 n
= CFDictionaryGetCount(dict
);
285 ppp_if
= CFStringCreateWithCStringNoCopy(NULL
,
287 kCFStringEncodingASCII
,
290 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
291 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
292 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
294 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
296 for (i
=0; i
< n
; i
++) {
297 CFArrayRef components
;
300 CFDictionaryRef p_setup
;
301 CFDictionaryRef p_state
;
303 CFStringRef service
= NULL
;
304 CFStringRef s_key
= (CFStringRef
) keys
[i
];
305 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
308 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
312 if (!CFStringHasSuffix(s_key
, entity
)) {
313 continue; // if not an IPv4 or IPv6 entity
316 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
317 if (!isA_CFString(s_if
)) {
318 continue; // if no interface
321 if (!CFEqual(ppp_if
, s_if
)) {
322 continue; // if not this interface
326 * extract service ID, get PPP "state" entity (for status), and get
327 * the "setup" entity (for dial-on-traffic flag)
329 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
330 if (CFArrayGetCount(components
) != 5) {
331 CFRelease(components
);
334 service
= CFArrayGetValueAtIndex(components
, 3);
335 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
336 kSCDynamicStoreDomainState
,
339 p_state
= CFDictionaryGetValue(dict
, key
);
341 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
342 kSCDynamicStoreDomainSetup
,
345 p_setup
= CFDictionaryGetValue(dict
, key
);
347 CFRelease(components
);
350 if (!isA_CFDictionary(p_state
)) {
353 num
= CFDictionaryGetValue(p_state
, kSCPropNetPPPStatus
);
354 if (!isA_CFNumber(num
)) {
358 if (!CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) {
361 switch (ppp_status
) {
363 /* if we're really UP and RUNNING */
366 /* if we're effectively UP and RUNNING */
369 case PPP_STATERESERVED
:
370 /* if we're not connected at all */
371 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link idle, dial-on-traffic to connect"));
372 *flags
|= kSCNetworkFlagsConnectionRequired
;
375 /* if we're in the process of [dis]connecting */
376 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link, connection in progress"));
377 *flags
|= kSCNetworkFlagsConnectionRequired
;
381 // check PPP dial-on-traffic status
382 if (isA_CFDictionary(p_setup
)) {
383 num
= CFDictionaryGetValue(p_setup
, kSCPropNetPPPDialOnDemand
);
384 if (isA_CFNumber(num
)) {
387 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
389 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
390 if (ppp_status
== PPP_IDLE
) {
391 *flags
|= kSCNetworkFlagsInterventionRequired
;
402 if (keys
!= keys_q
) {
403 CFAllocatorDeallocate(NULL
, keys
);
404 CFAllocatorDeallocate(NULL
, values
);
409 if (dict
!= NULL
) CFRelease(dict
);
410 if (storeP
!= NULL
) *storeP
= store
;
416 updatePPPAvailable(SCDynamicStoreRef
*storeP
,
417 const struct sockaddr
*sa
,
418 SCNetworkConnectionFlags
*flags
)
420 CFDictionaryRef dict
= NULL
;
423 const void * keys_q
[N_QUICK
];
424 const void ** keys
= keys_q
;
426 int sc_status
= kSCStatusReachabilityUnknown
;
427 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
428 const void * values_q
[N_QUICK
];
429 const void ** values
= values_q
;
432 entity
= kSCEntNetIPv4
;
434 switch (sa
->sa_family
) {
436 entity
= kSCEntNetIPv4
;
439 entity
= kSCEntNetIPv6
;
447 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
449 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = unknown (could not access SCDynamicStore"));
455 * grab a snapshot of the PPP configuration from the dynamic store
459 CFMutableArrayRef patterns
;
461 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
462 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
463 kSCDynamicStoreDomainSetup
,
466 CFArrayAppendValue(patterns
, pattern
);
468 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
469 kSCDynamicStoreDomainSetup
,
472 CFArrayAppendValue(patterns
, pattern
);
474 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
478 /* if we could not access the dynamic store */
482 sc_status
= kSCStatusOK
;
485 * look for an available service which will provide connectivity
486 * for the requested address family.
488 n
= CFDictionaryGetCount(dict
);
493 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
494 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
495 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
497 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
499 for (i
= 0; i
< n
; i
++) {
500 CFArrayRef components
;
501 Boolean found
= FALSE
;
503 CFDictionaryRef p_dict
;
505 CFStringRef s_key
= (CFStringRef
) keys
[i
];
506 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
508 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
512 if (!CFStringHasSuffix(s_key
, entity
)) {
513 continue; // if not an IPv4 or IPv6 entity
516 // extract service ID
517 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
518 if (CFArrayGetCount(components
) != 5) {
519 CFRelease(components
);
522 service
= CFArrayGetValueAtIndex(components
, 3);
524 // check for PPP entity
525 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
526 kSCDynamicStoreDomainSetup
,
529 p_dict
= CFDictionaryGetValue(dict
, p_key
);
532 if (isA_CFDictionary(p_dict
)) {
536 * we have a PPP service for this address family
540 *flags
|= kSCNetworkFlagsReachable
;
541 *flags
|= kSCNetworkFlagsTransientConnection
;
542 *flags
|= kSCNetworkFlagsConnectionRequired
;
545 * get PPP dial-on-traffic status
547 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
548 if (isA_CFNumber(num
)) {
551 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
553 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
559 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = isReachable (after connect)"));
560 SCLog(TRUE
, LOG_INFO
, CFSTR(" service = %@"), service
);
565 CFRelease(components
);
572 if (keys
!= keys_q
) {
573 CFAllocatorDeallocate(NULL
, keys
);
574 CFAllocatorDeallocate(NULL
, values
);
579 if (dict
!= NULL
) CFRelease(dict
);
580 if (storeP
!= NULL
) *storeP
= store
;
585 #define ROUNDUP(a, size) \
586 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
588 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
589 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
594 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
598 for (i
= 0; i
< RTAX_MAX
; i
++) {
599 if (addrs
& (1 << i
)) {
608 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
611 checkAddress(SCDynamicStoreRef
*storeP
,
612 const struct sockaddr
*address
,
613 SCNetworkConnectionFlags
*flags
,
618 char if_name
[IFNAMSIZ
+ 1];
621 pid_t pid
= getpid();
623 struct sockaddr
*rti_info
[RTAX_MAX
];
624 struct rt_msghdr
*rtm
;
626 int sc_status
= kSCStatusReachabilityUnknown
;
627 struct sockaddr_dl
*sdl
;
628 int32_t seq
= OSAtomicIncrement32Barrier(&rtm_seq
);
629 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
630 char *statusMessage
= NULL
;
631 #ifndef RTM_GET_SILENT
632 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
633 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
634 int sosize
= 48 * 1024;
638 if (if_index
!= NULL
) {
642 if (address
== NULL
) {
643 /* special case: check only for available paths off the system */
647 switch (address
->sa_family
) {
651 _SC_sockaddr_to_string(address
, buf
, sizeof(buf
));
652 SCLog(TRUE
, LOG_INFO
, CFSTR("checkAddress(%s)"), buf
);
657 * if no code for this address family (yet)
659 SCLog(_sc_verbose
, LOG_ERR
,
660 CFSTR("checkAddress(): unexpected address family %d"),
662 sc_status
= kSCStatusInvalidArgument
;
666 bzero(&buf
, sizeof(buf
));
668 rtm
= (struct rt_msghdr
*)&buf
;
669 rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
670 rtm
->rtm_version
= RTM_VERSION
;
671 #ifdef RTM_GET_SILENT
672 rtm
->rtm_type
= RTM_GET_SILENT
;
674 rtm
->rtm_type
= RTM_GET
;
676 rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
677 rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
681 switch (address
->sa_family
) {
683 struct sockaddr_in6
*sin6
;
685 sin6
= (struct sockaddr_in6
*)address
;
686 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
687 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
688 (sin6
->sin6_scope_id
!= 0)) {
689 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
690 sin6
->sin6_scope_id
= 0;
696 sa
= (struct sockaddr
*) (rtm
+ 1);
697 bcopy(address
, sa
, address
->sa_len
);
698 n
= ROUNDUP(sa
->sa_len
, sizeof(uint32_t));
699 rtm
->rtm_msglen
+= n
;
701 sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
702 sdl
->sdl_family
= AF_LINK
;
703 sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
704 n
= ROUNDUP(sdl
->sdl_len
, sizeof(uint32_t));
705 rtm
->rtm_msglen
+= n
;
707 #ifndef RTM_GET_SILENT
708 pthread_mutex_lock(&lock
);
710 rsock
= socket(PF_ROUTE
, SOCK_RAW
, 0);
712 #ifndef RTM_GET_SILENT
713 pthread_mutex_unlock(&lock
);
715 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(errno
));
716 sc_status
= kSCStatusFailed
;
720 #ifndef RTM_GET_SILENT
721 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
723 pthread_mutex_unlock(&lock
);
724 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(errno
));
725 sc_status
= kSCStatusFailed
;
730 if (write(rsock
, &buf
, rtm
->rtm_msglen
) == -1) {
734 #ifndef RTM_GET_SILENT
735 pthread_mutex_unlock(&lock
);
738 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(err
));
745 * Type, seq, pid identify our response.
746 * Routing sockets are broadcasters on input.
751 n
= read(rsock
, (void *)&buf
, sizeof(buf
));
757 SCLog(TRUE
, LOG_ERR
, CFSTR("read() failed: %s"), strerror(err
));
758 #ifndef RTM_GET_SILENT
759 pthread_mutex_unlock(&lock
);
764 } while (rtm
->rtm_type
!= RTM_GET
||
765 rtm
->rtm_seq
!= seq
||
766 rtm
->rtm_pid
!= pid
);
769 #ifndef RTM_GET_SILENT
770 pthread_mutex_unlock(&lock
);
773 get_rtaddrs(rtm
->rtm_addrs
, sa
, rti_info
);
780 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), rtm
->rtm_flags
);
782 if ((rti_info
[RTAX_NETMASK
] != NULL
) && (rti_info
[RTAX_DST
] != NULL
)) {
783 rti_info
[RTAX_NETMASK
]->sa_family
= rti_info
[RTAX_DST
]->sa_family
;
786 for (i
= 0; i
< RTAX_MAX
; i
++) {
787 if (rti_info
[i
] != NULL
) {
788 _SC_sockaddr_to_string(rti_info
[i
], buf
, sizeof(buf
));
789 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, buf
);
795 if ((rti_info
[RTAX_IFP
] == NULL
) ||
796 (rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
797 /* no interface info */
801 sdl
= (struct sockaddr_dl
*) rti_info
[RTAX_IFP
];
802 if ((sdl
->sdl_nlen
== 0) || (sdl
->sdl_nlen
> IFNAMSIZ
)) {
803 /* no interface name */
807 /* get the interface flags */
809 bzero(&ifr
, sizeof(ifr
));
810 bcopy(sdl
->sdl_data
, ifr
.ifr_name
, sdl
->sdl_nlen
);
812 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
814 SCLog(TRUE
, LOG_NOTICE
, CFSTR("socket() failed: %s"), strerror(errno
));
818 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) == -1) {
819 SCLog(TRUE
, LOG_NOTICE
, CFSTR("ioctl() failed: %s"), strerror(errno
));
825 if (!(ifr
.ifr_flags
& IFF_UP
)) {
829 statusMessage
= "isReachable";
830 *flags
|= kSCNetworkFlagsReachable
;
832 if (rtm
->rtm_flags
& RTF_LOCAL
) {
833 statusMessage
= "isReachable (is a local address)";
834 *flags
|= kSCNetworkFlagsIsLocalAddress
;
835 } else if (ifr
.ifr_flags
& IFF_LOOPBACK
) {
836 statusMessage
= "isReachable (is loopback network)";
837 *flags
|= kSCNetworkFlagsIsLocalAddress
;
838 } else if (rti_info
[RTAX_IFA
]) {
839 void *addr1
= (void *)address
;
840 void *addr2
= (void *)rti_info
[RTAX_IFA
];
841 size_t len
= address
->sa_len
;
843 if ((address
->sa_family
!= rti_info
[RTAX_IFA
]->sa_family
) &&
844 (address
->sa_len
!= rti_info
[RTAX_IFA
]->sa_len
)) {
845 SCLog(TRUE
, LOG_NOTICE
,
846 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
849 rti_info
[RTAX_IFA
]->sa_family
,
850 rti_info
[RTAX_IFA
]->sa_len
);
854 switch (address
->sa_family
) {
856 addr1
= &((struct sockaddr_in
*)address
)->sin_addr
;
857 addr2
= &((struct sockaddr_in
*)rti_info
[RTAX_IFA
])->sin_addr
;
858 len
= sizeof(struct in_addr
);
863 if (((struct sockaddr_in
*)address
)->sin_addr
.s_addr
== 0) {
864 statusMessage
= "isReachable (this host)";
865 *flags
|= kSCNetworkFlagsIsLocalAddress
;
869 addr1
= &((struct sockaddr_in6
*)address
)->sin6_addr
;
870 addr2
= &((struct sockaddr_in6
*)rti_info
[RTAX_IFA
])->sin6_addr
;
871 len
= sizeof(struct in6_addr
);
877 if (memcmp(addr1
, addr2
, len
) == 0) {
878 statusMessage
= "isReachable (is interface address)";
879 *flags
|= kSCNetworkFlagsIsLocalAddress
;
883 if (!(rtm
->rtm_flags
& RTF_GATEWAY
) &&
884 (rti_info
[RTAX_GATEWAY
] != NULL
) &&
885 (rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
) &&
886 !(ifr
.ifr_flags
& IFF_POINTOPOINT
)) {
887 *flags
|= kSCNetworkFlagsIsDirect
;
890 bzero(&if_name
, sizeof(if_name
));
893 (sdl
->sdl_nlen
<= IFNAMSIZ
) ? sdl
->sdl_nlen
: IFNAMSIZ
);
895 if (if_index
!= NULL
) {
896 *if_index
= sdl
->sdl_index
;
900 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = %s"), statusMessage
);
901 SCLog(TRUE
, LOG_INFO
, CFSTR(" device = %s (%hu)"), if_name
, sdl
->sdl_index
);
902 SCLog(TRUE
, LOG_INFO
, CFSTR(" ifr_flags = 0x%04hx"), ifr
.ifr_flags
);
903 SCLog(TRUE
, LOG_INFO
, CFSTR(" rtm_flags = 0x%08x"), rtm
->rtm_flags
);
906 sc_status
= kSCStatusOK
;
908 if (ifr
.ifr_flags
& IFF_POINTOPOINT
) {
910 * We have an interface which "claims" to be a valid path
913 *flags
|= kSCNetworkFlagsTransientConnection
;
916 * Check if this is a dial-on-demand PPP link that isn't
919 sc_status
= updatePPPStatus(&store
, address
, if_name
, flags
);
926 sc_status
= updatePPPAvailable(&store
, address
, flags
);
931 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" cannot be reached"));
934 if (storeP
!= NULL
) *storeP
= store
;
935 if (sc_status
!= kSCStatusOK
) {
936 _SCErrorSet(sc_status
);
945 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
947 CFAllocatorRef allocator
= CFGetAllocator(cf
);
948 CFMutableStringRef result
;
949 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
951 result
= CFStringCreateMutable(allocator
, 0);
952 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
);
953 switch (targetPrivate
->type
) {
954 case reachabilityTypeAddress
:
955 case reachabilityTypeAddressPair
: {
958 if (targetPrivate
->localAddress
!= NULL
) {
959 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
960 CFStringAppendFormat(result
, NULL
, CFSTR("local address = %s"),
964 if (targetPrivate
->remoteAddress
!= NULL
) {
965 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
966 CFStringAppendFormat(result
, NULL
, CFSTR("%s%saddress = %s"),
967 targetPrivate
->localAddress
? ", " : "",
968 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
973 case reachabilityTypeName
: {
974 CFStringAppendFormat(result
, NULL
, CFSTR("name = %s"), targetPrivate
->name
);
975 if ((targetPrivate
->resolvedAddress
!= NULL
) || (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
976 if (targetPrivate
->resolvedAddress
!= NULL
) {
977 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
979 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddress
);
981 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
982 for (i
= 0; i
< n
; i
++) {
987 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
);
988 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
989 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
990 CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"),
994 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
996 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)"));
999 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
1000 gai_strerror(targetPrivate
->resolvedAddressError
));
1002 } else if (targetPrivate
->dnsPort
!= NULL
) {
1003 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
1008 if (targetPrivate
->rls
!= NULL
) {
1009 CFStringAppendFormat(result
,
1011 CFSTR(", flags = %8.8x, if_index = %hu"),
1012 targetPrivate
->flags
,
1013 targetPrivate
->if_index
);
1015 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
1022 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1024 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1026 /* release resources */
1028 pthread_mutex_destroy(&targetPrivate
->lock
);
1030 if (targetPrivate
->name
!= NULL
)
1031 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
1033 if (targetPrivate
->resolvedAddress
!= NULL
)
1034 CFRelease(targetPrivate
->resolvedAddress
);
1036 if (targetPrivate
->localAddress
!= NULL
)
1037 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
1039 if (targetPrivate
->remoteAddress
!= NULL
)
1040 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
1042 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1043 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1051 __SCNetworkReachabilityInitialize(void)
1053 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
1058 static SCNetworkReachabilityPrivateRef
1059 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
1061 SCNetworkReachabilityPrivateRef targetPrivate
;
1064 /* initialize runtime */
1065 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
1067 /* allocate target */
1068 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
1069 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
1070 __kSCNetworkReachabilityTypeID
,
1073 if (targetPrivate
== NULL
) {
1077 pthread_mutex_init(&targetPrivate
->lock
, NULL
);
1079 targetPrivate
->name
= NULL
;
1081 targetPrivate
->resolvedAddress
= NULL
;
1082 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1084 targetPrivate
->localAddress
= NULL
;
1085 targetPrivate
->remoteAddress
= NULL
;
1087 targetPrivate
->flags
= 0;
1088 targetPrivate
->if_index
= 0;
1090 targetPrivate
->rls
= NULL
;
1091 targetPrivate
->rlsFunction
= NULL
;
1092 targetPrivate
->rlsContext
.info
= NULL
;
1093 targetPrivate
->rlsContext
.retain
= NULL
;
1094 targetPrivate
->rlsContext
.release
= NULL
;
1095 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1096 targetPrivate
->rlList
= NULL
;
1098 targetPrivate
->haveDNS
= FALSE
;
1099 targetPrivate
->dnsPort
= NULL
;
1100 targetPrivate
->dnsRLS
= NULL
;
1102 return targetPrivate
;
1106 SCNetworkReachabilityRef
1107 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
1108 const struct sockaddr
*address
)
1110 SCNetworkReachabilityPrivateRef targetPrivate
;
1112 if ((address
== NULL
) ||
1113 (address
->sa_len
== 0) ||
1114 (address
->sa_len
> sizeof(struct sockaddr_storage
))) {
1115 _SCErrorSet(kSCStatusInvalidArgument
);
1119 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1120 if (targetPrivate
== NULL
) {
1124 targetPrivate
->type
= reachabilityTypeAddress
;
1125 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
1126 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
1128 return (SCNetworkReachabilityRef
)targetPrivate
;
1132 SCNetworkReachabilityRef
1133 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
1134 const struct sockaddr
*localAddress
,
1135 const struct sockaddr
*remoteAddress
)
1137 SCNetworkReachabilityPrivateRef targetPrivate
;
1139 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
1140 _SCErrorSet(kSCStatusInvalidArgument
);
1144 if (localAddress
!= NULL
) {
1145 if ((localAddress
->sa_len
== 0) ||
1146 (localAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1147 _SCErrorSet(kSCStatusInvalidArgument
);
1152 if (remoteAddress
!= NULL
) {
1153 if ((remoteAddress
->sa_len
== 0) ||
1154 (remoteAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1155 _SCErrorSet(kSCStatusInvalidArgument
);
1160 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1161 if (targetPrivate
== NULL
) {
1165 targetPrivate
->type
= reachabilityTypeAddressPair
;
1167 if (localAddress
!= NULL
) {
1168 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
1169 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
1172 if (remoteAddress
!= NULL
) {
1173 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
1174 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
1177 return (SCNetworkReachabilityRef
)targetPrivate
;
1181 SCNetworkReachabilityRef
1182 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
1183 const char *nodename
)
1186 struct sockaddr_in sin
;
1187 struct sockaddr_in6 sin6
;
1188 SCNetworkReachabilityPrivateRef targetPrivate
;
1190 if (nodename
== NULL
) {
1191 _SCErrorSet(kSCStatusInvalidArgument
);
1195 nodenameLen
= strlen(nodename
);
1196 if (nodenameLen
== 0) {
1197 _SCErrorSet(kSCStatusInvalidArgument
);
1201 /* check if this "nodename" is really an IP[v6] address in disguise */
1203 bzero(&sin
, sizeof(sin
));
1204 sin
.sin_len
= sizeof(sin
);
1205 sin
.sin_family
= AF_INET
;
1206 if (inet_aton(nodename
, &sin
.sin_addr
) == 1) {
1207 /* if IPv4 address */
1208 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin
);
1211 bzero(&sin6
, sizeof(sin6
));
1212 sin6
.sin6_len
= sizeof(sin6
);
1213 sin6
.sin6_family
= AF_INET6
;
1214 if (inet_pton(AF_INET6
, nodename
, &sin6
.sin6_addr
) == 1) {
1215 /* if IPv6 address */
1218 p
= strchr(nodename
, '%');
1220 sin6
.sin6_scope_id
= if_nametoindex(p
+ 1);
1223 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin6
);
1226 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1227 if (targetPrivate
== NULL
) {
1231 targetPrivate
->type
= reachabilityTypeName
;
1233 targetPrivate
->flags
|= kSCNetworkFlagsFirstResolvePending
;
1235 targetPrivate
->name
= CFAllocatorAllocate(NULL
, nodenameLen
+ 1, 0);
1236 strlcpy((char *)targetPrivate
->name
, nodename
, nodenameLen
+ 1);
1238 return (SCNetworkReachabilityRef
)targetPrivate
;
1243 SCNetworkReachabilityGetTypeID(void)
1245 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
1246 return __kSCNetworkReachabilityTypeID
;
1251 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
1254 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1256 if (!isA_SCNetworkReachability(target
)) {
1257 _SCErrorSet(kSCStatusInvalidArgument
);
1261 if (targetPrivate
->type
!= reachabilityTypeName
) {
1262 _SCErrorSet(kSCStatusInvalidArgument
);
1267 *error_num
= targetPrivate
->resolvedAddressError
;
1270 if ((targetPrivate
->resolvedAddress
!= NULL
) || (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1271 if (targetPrivate
->resolvedAddress
!= NULL
) {
1272 return CFRetain(targetPrivate
->resolvedAddress
);
1274 /* if status is known but no resolved addresses to return */
1275 _SCErrorSet(kSCStatusOK
);
1280 _SCErrorSet(kSCStatusReachabilityUnknown
);
1286 __SCNetworkReachabilitySetResolvedAddress(int32_t status
,
1287 struct addrinfo
*res
,
1288 SCNetworkReachabilityRef target
)
1290 struct addrinfo
*resP
;
1291 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1293 if (targetPrivate
->resolvedAddress
!= NULL
) {
1294 CFRelease(targetPrivate
->resolvedAddress
);
1295 targetPrivate
->resolvedAddress
= NULL
;
1298 if ((status
== 0) && (res
!= NULL
)) {
1300 CFMutableArrayRef addresses
;
1302 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1304 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
1305 CFDataRef newAddress
;
1307 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
1308 CFArrayAppendValue(addresses
, newAddress
);
1309 CFRelease(newAddress
);
1312 /* save the resolved address[es] */
1313 targetPrivate
->resolvedAddress
= addresses
;
1314 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1316 SCLog(_sc_debug
, LOG_INFO
, CFSTR("getaddrinfo() failed: %s"), gai_strerror(status
));
1318 /* save the error associated with the attempt to resolve the name */
1319 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
1320 targetPrivate
->resolvedAddressError
= status
;
1323 if (res
!= NULL
) freeaddrinfo(res
);
1325 if (targetPrivate
->rls
!= NULL
) {
1326 SCLog(_sc_debug
, LOG_INFO
, CFSTR("DNS request completed"));
1327 CFRunLoopSourceSignal(targetPrivate
->rls
);
1328 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1336 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo
*res
, void *context
)
1338 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
1339 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1341 __log_query_time(((status
== 0) && (res
!= NULL
)), // if successful query
1343 &targetPrivate
->dnsQueryStart
); // start time
1345 __SCNetworkReachabilitySetResolvedAddress(status
, res
, target
);
1351 * rankReachability()
1352 * Not reachable == 0
1353 * Connection Required == 1
1357 rankReachability(SCNetworkConnectionFlags flags
)
1361 if (flags
& kSCNetworkFlagsReachable
) rank
= 2;
1362 if (flags
& kSCNetworkFlagsConnectionRequired
) rank
= 1;
1368 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1371 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1372 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1374 pthread_mutex_lock(&targetPrivate
->lock
);
1376 status
= getaddrinfo_async_handle_reply(msg
);
1377 if ((status
== 0) &&
1378 (targetPrivate
->resolvedAddress
== NULL
) && (targetPrivate
->resolvedAddressError
== NETDB_SUCCESS
)) {
1379 // if request has been re-queued
1383 if (port
== targetPrivate
->dnsPort
) {
1384 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
1385 CFRelease(targetPrivate
->dnsRLS
);
1386 targetPrivate
->dnsRLS
= NULL
;
1387 CFRelease(targetPrivate
->dnsPort
);
1388 targetPrivate
->dnsPort
= NULL
;
1393 pthread_mutex_unlock(&targetPrivate
->lock
);
1400 check_resolver_reachability(SCDynamicStoreRef
*storeP
,
1401 dns_resolver_t
*resolver
,
1402 SCNetworkConnectionFlags
*flags
,
1408 *flags
= kSCNetworkFlagsReachable
;
1411 for (i
= 0; i
< resolver
->n_nameserver
; i
++) {
1412 struct sockaddr
*address
= resolver
->nameserver
[i
];
1413 SCNetworkConnectionFlags ns_flags
= 0;
1417 if (address
->sa_family
!= AF_INET
) {
1419 * we need to skip non-IPv4 DNS server
1420 * addresses (at least until [3510431] has
1426 ok
= checkAddress(storeP
, address
, &ns_flags
, NULL
);
1432 if (rankReachability(ns_flags
) < rankReachability(*flags
)) {
1433 /* return the worst case result */
1445 check_matching_resolvers(SCDynamicStoreRef
*storeP
,
1446 dns_config_t
*dns_config
,
1448 SCNetworkConnectionFlags
*flags
,
1452 Boolean matched
= FALSE
;
1453 const char *name
= fqdn
;
1455 while (!matched
&& (name
!= NULL
)) {
1459 * check if the provided name (or sub-component)
1460 * matches one of our resolver configurations.
1463 for (i
= 0; i
< dns_config
->n_resolver
; i
++) {
1465 dns_resolver_t
*resolver
;
1467 resolver
= dns_config
->resolver
[i
];
1468 domain
= resolver
->domain
;
1469 if (domain
!= NULL
&& (len
== strlen(domain
))) {
1470 if (strcasecmp(name
, domain
) == 0) {
1474 * if name matches domain
1477 ok
= check_resolver_reachability(storeP
, resolver
, flags
, haveDNS
);
1488 * we have not found a matching resolver, try
1489 * a less qualified domain
1491 name
= strchr(name
, '.');
1492 if ((name
!= NULL
) && (*name
!= '\0')) {
1504 static dns_configuration_t
*
1505 dns_configuration_retain()
1507 pthread_mutex_lock(&dns_lock
);
1509 if ((dns_configuration
!= NULL
) && dns_token_valid
) {
1514 * check if the global [DNS] configuration snapshot needs
1517 status
= notify_check(dns_token
, &check
);
1518 if (status
!= NOTIFY_STATUS_OK
) {
1519 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
1522 if ((status
!= NOTIFY_STATUS_OK
) || (check
!= 0)) {
1524 * if the snapshot needs to be refreshed
1526 if (dns_configuration
->refs
== 0) {
1527 dns_configuration_free(dns_configuration
->config
);
1528 CFAllocatorDeallocate(NULL
, dns_configuration
);
1530 dns_configuration
= NULL
;
1534 if (dns_configuration
== NULL
) {
1535 dns_config_t
*new_config
;
1537 new_config
= dns_configuration_copy();
1538 if (new_config
!= NULL
) {
1539 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
1540 dns_configuration
->config
= new_config
;
1541 dns_configuration
->refs
= 0;
1545 if (dns_configuration
!= NULL
) {
1546 dns_configuration
->refs
++;
1549 pthread_mutex_unlock(&dns_lock
);
1550 return dns_configuration
;
1555 dns_configuration_release(dns_configuration_t
*config
)
1557 pthread_mutex_lock(&dns_lock
);
1560 if (config
->refs
== 0) {
1561 if ((dns_configuration
!= config
)) {
1562 dns_configuration_free(config
->config
);
1563 CFAllocatorDeallocate(NULL
, config
);
1567 pthread_mutex_unlock(&dns_lock
);
1573 dns_configuration_watch()
1576 const char *dns_key
;
1580 pthread_mutex_lock(&dns_lock
);
1582 dns_key
= dns_configuration_notify_key();
1583 if (dns_key
== NULL
) {
1584 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
1588 status
= notify_register_check(dns_key
, &dns_token
);
1589 if (status
== NOTIFY_STATUS_OK
) {
1590 dns_token_valid
= TRUE
;
1592 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
);
1596 status
= notify_check(dns_token
, &dns_check
);
1597 if (status
!= NOTIFY_STATUS_OK
) {
1598 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
1599 (void)notify_cancel(dns_token
);
1600 dns_token_valid
= FALSE
;
1608 pthread_mutex_unlock(&dns_lock
);
1614 dns_configuration_unwatch()
1616 pthread_mutex_lock(&dns_lock
);
1618 (void)notify_cancel(dns_token
);
1619 dns_token_valid
= FALSE
;
1621 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
1622 dns_configuration_free(dns_configuration
->config
);
1623 CFAllocatorDeallocate(NULL
, dns_configuration
);
1624 dns_configuration
= NULL
;
1627 pthread_mutex_unlock(&dns_lock
);
1633 _SC_checkResolverReachability(SCDynamicStoreRef
*storeP
,
1634 SCNetworkConnectionFlags
*flags
,
1636 const char * nodename
)
1638 dns_resolver_t
*default_resolver
;
1639 dns_configuration_t
*dns
;
1640 Boolean found
= FALSE
;
1641 char *fqdn
= (char *)nodename
;
1643 Boolean isFQDN
= FALSE
;
1648 * We first assume that all of the configured DNS servers
1649 * are available. Since we don't know which name server will
1650 * be consulted to resolve the specified nodename we need to
1651 * check the availability of ALL name servers. We can only
1652 * proceed if we know that our query can be answered.
1655 *flags
= kSCNetworkFlagsReachable
;
1660 // if no nodename, return not reachable
1665 dns
= dns_configuration_retain();
1671 if (dns
->config
->n_resolver
== 0) {
1672 // if no resolver configuration
1676 *flags
= kSCNetworkFlagsReachable
;
1678 if (fqdn
[len
- 1] == '.') {
1681 // trim trailing '.''s
1682 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
1683 if (fqdn
== nodename
) {
1684 fqdn
= strdup(nodename
);
1690 default_resolver
= dns
->config
->resolver
[0];
1693 * try the provided name
1695 found
= check_matching_resolvers(storeP
, dns
->config
, fqdn
, flags
, haveDNS
);
1696 if (!found
&& !isFQDN
&& (dns
->config
->n_resolver
> 1)) {
1698 * FQDN not specified, try w/search or default domain(s) too
1700 if (default_resolver
->n_search
> 0) {
1701 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
1703 char *search_fqdn
= NULL
;
1705 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
1710 // try the provided name with the search domain appended
1711 found
= check_matching_resolvers(storeP
, dns
->config
, search_fqdn
, flags
, haveDNS
);
1714 } else if (default_resolver
->domain
!= NULL
) {
1716 int domain_parts
= 0;
1718 // count domain parts
1719 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
1725 // remove trailing dots
1726 for (dp
--; (dp
>= default_resolver
->domain
) && (*dp
== '.'); dp
--) {
1731 if (dp
>= default_resolver
->domain
) {
1732 // dots are separators, bump # of components
1736 dp
= default_resolver
->domain
;
1737 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= domain_parts
); i
++) {
1739 char *search_fqdn
= NULL
;
1741 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
1746 // try the provided name with the [default] domain appended
1747 found
= check_matching_resolvers(storeP
, dns
->config
, search_fqdn
, flags
, haveDNS
);
1750 // move to the next component of the [default] domain
1751 dp
= strchr(dp
, '.') + 1;
1758 * check the reachability of the default resolver
1760 ok
= check_resolver_reachability(storeP
, default_resolver
, flags
, haveDNS
);
1763 if (fqdn
!= nodename
) free(fqdn
);
1768 dns_configuration_release(dns
);
1776 * _SC_checkResolverReachabilityByAddress()
1778 * Given an IP address, determine whether a reverse DNS query can be issued
1779 * using the current network configuration.
1782 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef
*storeP
,
1783 SCNetworkConnectionFlags
*flags
,
1785 struct sockaddr
*sa
)
1792 * Ideally, we would have an API that given a local IP
1793 * address would return the DNS server(s) that would field
1794 * a given PTR query. Fortunately, we do have an SPI which
1795 * which will provide this information given a "name" so we
1796 * take the address, convert it into the inverse query name,
1797 * and find out which servers should be consulted.
1800 switch (sa
->sa_family
) {
1806 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
1809 * build "PTR" query name
1810 * NNN.NNN.NNN.NNN.in-addr.arpa.
1812 rev
.s_addr
= sin
->sin_addr
.s_addr
;
1813 (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.",
1824 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
1825 int x
= sizeof(ptr_name
);
1829 * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152)
1830 * N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.N.ip6.arpa.
1832 for (i
= sizeof(sin6
->sin6_addr
) - 1; i
>= 0; i
--) {
1833 n
= snprintf(&ptr_name
[s
], x
, "%x.%x.",
1834 ( sin6
->sin6_addr
.s6_addr
[i
] & 0xf),
1835 ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf));
1836 if ((n
== -1) || (n
>= x
)) {
1844 n
= snprintf(&ptr_name
[s
], x
, "ip6.arpa.");
1845 if ((n
== -1) || (n
>= x
)) {
1856 ok
= _SC_checkResolverReachability(storeP
, flags
, haveDNS
, ptr_name
);
1865 replyMPCopyDescription(const void *info
)
1867 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1868 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1870 return CFStringCreateWithFormat(NULL
,
1872 CFSTR("<getaddrinfo_async_start reply MP> {name = %s, target = %p}"),
1873 targetPrivate
->name
? targetPrivate
->name
: "?",
1879 startAsyncDNSQuery(SCNetworkReachabilityRef target
) {
1880 CFMachPortContext context
= { 0
1884 , replyMPCopyDescription
1887 struct addrinfo hints
;
1891 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1893 (void) gettimeofday(&targetPrivate
->dnsQueryStart
, NULL
);
1895 bzero(&hints
, sizeof(hints
));
1896 hints
.ai_flags
= AI_ADDRCONFIG
;
1898 hints
.ai_flags
|= AI_PARALLEL
;
1899 #endif /* AI_PARALLEL */
1901 error
= getaddrinfo_async_start(&port
,
1902 targetPrivate
->name
,
1905 __SCNetworkReachabilityCallbackSetResolvedAddress
,
1908 /* save the error associated with the attempt to resolve the name */
1909 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
);
1913 targetPrivate
->dnsPort
= CFMachPortCreateWithPort(NULL
,
1915 getaddrinfo_async_handleCFReply
,
1918 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
1920 n
= CFArrayGetCount(targetPrivate
->rlList
);
1921 for (i
= 0; i
< n
; i
+= 3) {
1922 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
1923 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
1925 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
1933 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef
*storeP
,
1934 SCNetworkReachabilityRef target
,
1935 SCNetworkConnectionFlags
*flags
,
1939 CFMutableArrayRef addresses
= NULL
;
1940 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1941 SCNetworkConnectionFlags my_flags
= 0;
1942 uint16_t my_index
= 0;
1946 if (if_index
!= NULL
) {
1950 if (!isA_SCNetworkReachability(target
)) {
1951 _SCErrorSet(kSCStatusInvalidArgument
);
1955 switch (targetPrivate
->type
) {
1956 case reachabilityTypeAddress
:
1957 case reachabilityTypeAddressPair
: {
1959 * Check "local" address
1961 if (targetPrivate
->localAddress
!= NULL
) {
1963 * Check "local" address
1965 ok
= checkAddress(storeP
, targetPrivate
->localAddress
, &my_flags
, &my_index
);
1967 goto error
; /* not today */
1970 if (!(my_flags
& kSCNetworkFlagsIsLocalAddress
)) {
1971 goto error
; /* not reachable, non-"local" address */
1976 * Check "remote" address
1978 if (targetPrivate
->remoteAddress
!= NULL
) {
1980 * in cases where we have "local" and "remote" addresses
1981 * we need to re-initialize the to-be-returned flags.
1987 * Check "remote" address
1989 ok
= checkAddress(storeP
, targetPrivate
->remoteAddress
, &my_flags
, &my_index
);
1991 goto error
; /* not today */
1999 case reachabilityTypeName
: {
2000 struct timeval dnsQueryStart
;
2002 struct addrinfo hints
;
2003 SCNetworkConnectionFlags ns_flags
;
2004 struct addrinfo
*res
;
2006 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
2007 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
2008 /* if resolved or an error had been detected */
2009 goto checkResolvedAddress
;
2012 /* check the reachability of the DNS servers */
2013 ok
= _SC_checkResolverReachability(storeP
,
2015 &targetPrivate
->haveDNS
,
2016 targetPrivate
->name
);
2018 /* if we could not get DNS server info */
2022 if (rankReachability(ns_flags
) < 2) {
2024 * if DNS servers are not (or are no longer) reachable, set
2025 * flags based on the availability of configured (but not
2029 SCLog(_sc_debug
, LOG_INFO
, CFSTR("DNS server(s) not available"));
2031 if (!checkAddress(storeP
, NULL
, &my_flags
, &my_index
)) {
2035 if (async
&& (targetPrivate
->rls
!= NULL
)) {
2037 * return "host not found", set flags appropriately,
2038 * and schedule notification.
2040 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME
,
2043 my_flags
|= (targetPrivate
->flags
& kSCNetworkFlagsFirstResolvePending
);
2045 SCLog(_sc_debug
, LOG_INFO
, CFSTR("no DNS servers are reachable"));
2046 CFRunLoopSourceSignal(targetPrivate
->rls
);
2047 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2053 /* for async requests we return the last known status */
2054 my_flags
= targetPrivate
->flags
;
2055 my_index
= targetPrivate
->if_index
;
2057 if (targetPrivate
->dnsPort
!= NULL
) {
2058 /* if request already in progress */
2062 SCLog(_sc_debug
, LOG_INFO
, CFSTR("start DNS query for \"%s\""), targetPrivate
->name
);
2065 * initiate an async DNS query
2067 if (!startAsyncDNSQuery(target
)) {
2068 /* if we could not initiate the request, process error */
2069 goto checkResolvedAddress
;
2072 /* request initiated */
2076 SCLog(_sc_debug
, LOG_INFO
, CFSTR("check DNS for \"%s\""), targetPrivate
->name
);
2079 * OK, all of the DNS name servers are available. Let's
2080 * resolve the nodename into an address.
2083 (void) gettimeofday(&dnsQueryStart
, NULL
);
2086 bzero(&hints
, sizeof(hints
));
2087 hints
.ai_flags
= AI_ADDRCONFIG
;
2089 hints
.ai_flags
|= AI_PARALLEL
;
2090 #endif /* AI_PARALLEL */
2092 error
= getaddrinfo(targetPrivate
->name
, NULL
, &hints
, &res
);
2094 __log_query_time(((error
== 0) && (res
!= NULL
)),// if successful query
2096 &dnsQueryStart
); // start time
2098 __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
);
2100 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
2102 checkResolvedAddress
:
2105 * We first assume that the requested host is NOT available.
2106 * Then, check each address for accessibility and return the
2107 * best status available.
2112 if (isA_CFArray(addresses
)) {
2114 CFIndex n
= CFArrayGetCount(addresses
);
2116 for (i
= 0; i
< n
; i
++) {
2117 SCNetworkConnectionFlags ns_flags
= 0;
2118 uint16_t ns_if_index
= 0;
2119 struct sockaddr
*sa
;
2121 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
2123 ok
= checkAddress(storeP
, sa
, &ns_flags
, &ns_if_index
);
2125 goto error
; /* not today */
2128 if (rankReachability(ns_flags
) > rankReachability(my_flags
)) {
2129 /* return the best case result */
2130 my_flags
= ns_flags
;
2131 my_index
= ns_if_index
;
2132 if (rankReachability(my_flags
) == 2) {
2139 if (((error
== EAI_NONAME
)
2140 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
2141 || (error
== EAI_NODATA
)
2143 ) && !targetPrivate
->haveDNS
) {
2145 * No DNS servers are defined. Set flags based on
2146 * the availability of configured (but not active)
2149 ok
= checkAddress(storeP
, NULL
, &my_flags
, &my_index
);
2151 goto error
; /* not today */
2154 if ((my_flags
& kSCNetworkFlagsReachable
) &&
2155 (my_flags
& kSCNetworkFlagsConnectionRequired
)) {
2157 * Since we might pick up a set of DNS servers when this connection
2158 * is established, don't reply with a "HOST NOT FOUND" error just yet.
2163 /* Host not found, not reachable! */
2174 if (if_index
!= NULL
) {
2175 *if_index
= my_index
;
2180 if (addresses
!= NULL
) CFRelease(addresses
);
2186 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
2187 SCNetworkConnectionFlags
*flags
)
2190 SCDynamicStoreRef store
= NULL
;
2191 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2193 if (!isA_SCNetworkReachability(target
)) {
2194 _SCErrorSet(kSCStatusInvalidArgument
);
2198 pthread_mutex_lock(&targetPrivate
->lock
);
2200 if (targetPrivate
->rlList
!= NULL
) {
2201 // if being watched, return the last known (and what should be current) status
2202 *flags
= targetPrivate
->flags
& ~kSCNetworkFlagsFirstResolvePending
;
2207 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, flags
, NULL
, FALSE
);
2208 *flags
&= ~kSCNetworkFlagsFirstResolvePending
;
2209 if (store
!= NULL
) CFRelease(store
);
2213 pthread_mutex_unlock(&targetPrivate
->lock
);
2219 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
2222 CFMutableArrayRef keys
;
2223 CFStringRef pattern
;
2224 CFMutableArrayRef patterns
;
2226 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2227 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2229 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
2230 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2231 kSCDynamicStoreDomainSetup
,
2233 CFArrayAppendValue(keys
, key
);
2236 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2237 kSCDynamicStoreDomainState
,
2239 CFArrayAppendValue(keys
, key
);
2242 // State:/Network/Global/IPv4 (default route)
2243 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2244 kSCDynamicStoreDomainState
,
2246 CFArrayAppendValue(keys
, key
);
2249 // Setup: per-service IPv4 info
2250 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2251 kSCDynamicStoreDomainSetup
,
2254 CFArrayAppendValue(patterns
, pattern
);
2257 // Setup: per-service Interface info
2258 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2259 kSCDynamicStoreDomainSetup
,
2261 kSCEntNetInterface
);
2262 CFArrayAppendValue(patterns
, pattern
);
2265 // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand)
2266 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2267 kSCDynamicStoreDomainSetup
,
2270 CFArrayAppendValue(patterns
, pattern
);
2273 // State: per-interface IPv4 info
2274 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2275 kSCDynamicStoreDomainState
,
2278 CFArrayAppendValue(patterns
, pattern
);
2281 // State: per-interface IPv6 info
2282 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2283 kSCDynamicStoreDomainState
,
2286 CFArrayAppendValue(patterns
, pattern
);
2289 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
2291 CFRelease(patterns
);
2298 __SCNetworkReachabilityReachabilityHandleChanges(SCDynamicStoreRef store
,
2299 CFArrayRef changedKeys
,
2302 Boolean dnsConfigChanged
= FALSE
;
2306 const void * targets_q
[N_QUICK
];
2307 const void ** targets
= targets_q
;
2309 pthread_mutex_lock(&hn_lock
);
2311 nTargets
= CFSetGetCount(hn_targets
);
2312 if (nTargets
== 0) {
2313 /* if no addresses being monitored */
2317 if (CFArrayGetCount(changedKeys
) == 0) {
2322 SCLog(_sc_debug
, LOG_INFO
, CFSTR("process configuration change"));
2324 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2325 kSCDynamicStoreDomainState
,
2327 if (CFArrayContainsValue(changedKeys
,
2328 CFRangeMake(0, CFArrayGetCount(changedKeys
)),
2330 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
2334 SCLog(_sc_debug
&& dnsConfigChanged
, LOG_INFO
, CFSTR(" DNS configuration changed"));
2336 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
2337 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
2338 CFSetGetValues(hn_targets
, targets
);
2339 for (i
= 0; i
< nTargets
; i
++) {
2340 SCNetworkReachabilityRef target
= targets
[i
];
2341 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2343 pthread_mutex_lock(&targetPrivate
->lock
);
2345 if (targetPrivate
->type
== reachabilityTypeName
) {
2346 Boolean dnsChanged
= dnsConfigChanged
;
2350 * if the DNS configuration didn't change we still need to
2351 * check that the DNS servers are accessible.
2353 SCNetworkConnectionFlags ns_flags
;
2356 /* check the reachability of the DNS servers */
2357 ok
= _SC_checkResolverReachability(&store
,
2359 &targetPrivate
->haveDNS
,
2360 targetPrivate
->name
);
2361 if (!ok
|| (rankReachability(ns_flags
) < 2)) {
2362 /* if DNS servers are not reachable */
2368 if (targetPrivate
->dnsPort
!= NULL
) {
2369 /* cancel the outstanding DNS query */
2370 getaddrinfo_async_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2371 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
2372 CFRelease(targetPrivate
->dnsRLS
);
2373 targetPrivate
->dnsRLS
= NULL
;
2374 CFRelease(targetPrivate
->dnsPort
);
2375 targetPrivate
->dnsPort
= NULL
;
2378 /* schedule request to resolve the name again */
2379 if (targetPrivate
->resolvedAddress
!= NULL
) {
2380 CFRelease(targetPrivate
->resolvedAddress
);
2381 targetPrivate
->resolvedAddress
= NULL
;
2383 targetPrivate
->resolvedAddress
= NULL
;
2384 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
2388 CFRunLoopSourceSignal(targetPrivate
->rls
);
2389 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2391 pthread_mutex_unlock(&targetPrivate
->lock
);
2393 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
2397 pthread_mutex_unlock(&hn_lock
);
2403 rlsPerform(void *info
)
2406 void (*context_release
)(const void *);
2407 SCNetworkConnectionFlags flags
;
2410 SCNetworkReachabilityCallBack rlsFunction
;
2411 SCDynamicStoreRef store
= NULL
;
2412 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2413 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2415 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("process reachability change"));
2418 pthread_mutex_lock(&targetPrivate
->lock
);
2420 /* update reachability, notify if status changed */
2421 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
);
2422 if (store
!= NULL
) CFRelease(store
);
2424 /* if reachability status not available */
2429 if ((targetPrivate
->flags
== flags
) && (targetPrivate
->if_index
== if_index
)) {
2430 /* if reachability flags and interface have not changed */
2431 pthread_mutex_unlock(&targetPrivate
->lock
);
2432 SCLog(_sc_debug
, LOG_DEBUG
,
2433 CFSTR("flags/interface match (now %8.8x/%hu)"),
2437 SCLog(_sc_debug
, LOG_DEBUG
,
2438 CFSTR("flags/interface have changed (was %8.8x/%hu, now %8.8x/%hu)"),
2439 targetPrivate
->flags
, targetPrivate
->if_index
,
2443 /* update flags / interface */
2444 targetPrivate
->flags
= flags
;
2445 targetPrivate
->if_index
= if_index
;
2448 rlsFunction
= targetPrivate
->rlsFunction
;
2449 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
2450 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
2451 context_release
= targetPrivate
->rlsContext
.release
;
2453 context_info
= targetPrivate
->rlsContext
.info
;
2454 context_release
= NULL
;
2457 pthread_mutex_unlock(&targetPrivate
->lock
);
2459 if (rlsFunction
!= NULL
) {
2460 (*rlsFunction
)(target
, flags
, context_info
);
2463 if (context_release
!= NULL
) {
2464 (*context_release
)(context_info
);
2472 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
2473 SCNetworkReachabilityCallBack callout
,
2474 SCNetworkReachabilityContext
*context
)
2476 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2478 pthread_mutex_lock(&targetPrivate
->lock
);
2480 if (targetPrivate
->rlsContext
.release
!= NULL
) {
2481 /* let go of the current context */
2482 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
2485 targetPrivate
->rlsFunction
= callout
;
2486 targetPrivate
->rlsContext
.info
= NULL
;
2487 targetPrivate
->rlsContext
.retain
= NULL
;
2488 targetPrivate
->rlsContext
.release
= NULL
;
2489 targetPrivate
->rlsContext
.copyDescription
= NULL
;
2491 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
2492 if (context
->retain
!= NULL
) {
2493 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
2497 pthread_mutex_unlock(&targetPrivate
->lock
);
2504 reachRLSCopyDescription(const void *info
)
2506 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2508 return CFStringCreateWithFormat(NULL
,
2510 CFSTR("<SCNetworkReachability RLS> {target = %p}"),
2516 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
2517 CFRunLoopRef runLoop
,
2518 CFStringRef runLoopMode
)
2520 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2521 Boolean init
= FALSE
;
2524 if (!isA_SCNetworkReachability(target
) || runLoop
== NULL
|| runLoopMode
== NULL
) {
2525 _SCErrorSet(kSCStatusInvalidArgument
);
2529 /* schedule the SCNetworkReachability run loop source */
2531 pthread_mutex_lock(&hn_lock
);
2532 pthread_mutex_lock(&targetPrivate
->lock
);
2534 if (hn_store
== NULL
) {
2536 * if we are not monitoring any hosts, start watching
2538 if (!dns_configuration_watch()) {
2540 _SCErrorSet(kSCStatusFailed
);
2544 hn_store
= SCDynamicStoreCreate(NULL
,
2545 CFSTR("SCNetworkReachability"),
2546 __SCNetworkReachabilityReachabilityHandleChanges
,
2548 if (hn_store
== NULL
) {
2549 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
2553 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
2555 hn_storeRLS
= SCDynamicStoreCreateRunLoopSource(NULL
, hn_store
, 0);
2556 hn_rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2557 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
2560 if (targetPrivate
->rls
== NULL
) {
2561 CFRunLoopSourceContext context
= { 0 // version
2562 , (void *)target
// info
2563 , CFRetain
// retain
2564 , CFRelease
// release
2565 , reachRLSCopyDescription
// copyDescription
2570 , rlsPerform
// perform
2573 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
2574 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2578 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2580 * if we do not already have host notifications scheduled with
2581 * this runLoop / runLoopMode
2583 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2585 if (targetPrivate
->dnsRLS
!= NULL
) {
2586 /* if we have an active async DNS query too */
2587 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2591 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
2593 /* schedule the SCNetworkReachability run loop source */
2595 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2597 * if we do not already have SC notifications scheduled with
2598 * this runLoop / runLoopMode
2600 CFRunLoopAddSource(runLoop
, hn_storeRLS
, runLoopMode
);
2603 _SC_schedule(target
, runLoop
, runLoopMode
, hn_rlList
);
2604 CFSetAddValue(hn_targets
, target
);
2607 SCNetworkConnectionFlags flags
;
2609 SCDynamicStoreRef store
= NULL
;
2612 * if we have yet to schedule SC notifications for this address
2613 * - initialize current reachability status
2615 if (__SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
)) {
2617 * if reachability status available
2619 * - schedule notification to report status via callback
2621 targetPrivate
->flags
= flags
;
2622 targetPrivate
->if_index
= if_index
;
2623 CFRunLoopSourceSignal(targetPrivate
->rls
);
2624 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2626 /* if reachability status not available, async lookup started */
2627 targetPrivate
->flags
= 0;
2628 targetPrivate
->if_index
= 0;
2630 if (store
!= NULL
) CFRelease(store
);
2637 pthread_mutex_unlock(&targetPrivate
->lock
);
2638 pthread_mutex_unlock(&hn_lock
);
2644 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
2645 CFRunLoopRef runLoop
,
2646 CFStringRef runLoopMode
)
2648 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2652 if (!isA_SCNetworkReachability(target
) || runLoop
== NULL
|| runLoopMode
== NULL
) {
2653 _SCErrorSet(kSCStatusInvalidArgument
);
2657 pthread_mutex_lock(&hn_lock
);
2658 pthread_mutex_lock(&targetPrivate
->lock
);
2660 if (targetPrivate
->rls
== NULL
) {
2661 /* if not currently scheduled */
2662 _SCErrorSet(kSCStatusInvalidArgument
);
2666 if (!_SC_unschedule(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
2667 /* if not currently scheduled */
2668 _SCErrorSet(kSCStatusInvalidArgument
);
2672 n
= CFArrayGetCount(targetPrivate
->rlList
);
2673 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2675 * if this host is no longer scheduled for this runLoop / runLoopMode
2677 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2679 if (targetPrivate
->dnsRLS
!= NULL
) {
2680 /* if we have an active async DNS query too */
2681 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2686 * if this host is no longer scheduled
2688 CFRunLoopSourceInvalidate(targetPrivate
->rls
); /* cleanup SCNetworkReachability resources */
2689 CFRelease(targetPrivate
->rls
);
2690 targetPrivate
->rls
= NULL
;
2691 CFRelease(targetPrivate
->rlList
);
2692 targetPrivate
->rlList
= NULL
;
2693 CFSetRemoveValue(hn_targets
, target
); /* cleanup notification resources */
2695 if (targetPrivate
->dnsPort
!= NULL
) {
2696 /* if we have an active async DNS query too */
2697 getaddrinfo_async_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2698 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
2699 CFRelease(targetPrivate
->dnsRLS
);
2700 targetPrivate
->dnsRLS
= NULL
;
2701 CFRelease(targetPrivate
->dnsPort
);
2702 targetPrivate
->dnsPort
= NULL
;
2707 (void)_SC_unschedule(target
, runLoop
, runLoopMode
, hn_rlList
, FALSE
);
2709 n
= CFArrayGetCount(hn_rlList
);
2710 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2712 * if we no longer have any addresses scheduled for
2713 * this runLoop / runLoopMode
2715 CFRunLoopRemoveSource(runLoop
, hn_storeRLS
, runLoopMode
);
2719 * if we are no longer monitoring any addresses
2721 CFRelease(hn_targets
);
2723 CFRelease(hn_rlList
);
2725 CFRunLoopSourceInvalidate(hn_storeRLS
);
2726 CFRelease(hn_storeRLS
);
2728 CFRelease(hn_store
);
2732 * until we start monitoring again, ensure that
2733 * any resources associated with tracking the
2734 * DNS configuration have been released.
2736 dns_configuration_unwatch();
2744 pthread_mutex_unlock(&targetPrivate
->lock
);
2745 pthread_mutex_unlock(&hn_lock
);