2 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * March 31, 2004 Allan Nathanson <ajn@apple.com>
28 * - use [SC] DNS configuration information
30 * January 19, 2003 Allan Nathanson <ajn@apple.com>
31 * - add advanced reachability APIs
34 #include <SystemConfiguration/SystemConfiguration.h>
35 #include <SystemConfiguration/SCValidation.h>
36 #include <SystemConfiguration/SCPrivate.h>
38 #include <CoreFoundation/CFRuntime.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
46 #include <netdb_async.h>
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
52 #include <net/if_dl.h>
53 #define KERNEL_PRIVATE
54 #include <net/route.h>
58 #define s6_addr16 __u6_addr.__u6_addr16
61 #include <ppp/ppp_msg.h>
64 #define kSCNetworkFlagsFirstResolvePending (1<<31)
71 reachabilityTypeAddress
,
72 reachabilityTypeAddressPair
,
77 static CFStringRef
__SCNetworkReachabilityCopyDescription (CFTypeRef cf
);
78 static void __SCNetworkReachabilityDeallocate (CFTypeRef cf
);
83 /* base CFType information */
92 /* target host name */
94 CFArrayRef resolvedAddress
; /* CFArray[CFData] */
95 int resolvedAddressError
;
97 /* local & remote addresses */
98 struct sockaddr
*localAddress
;
99 struct sockaddr
*remoteAddress
;
101 /* current reachability flags */
102 SCNetworkConnectionFlags flags
;
105 /* run loop source, callout, context, rl scheduling info */
106 CFRunLoopSourceRef rls
;
107 SCNetworkReachabilityCallBack rlsFunction
;
108 SCNetworkReachabilityContext rlsContext
;
109 CFMutableArrayRef rlList
;
111 /* [async] DNS query info */
113 CFMachPortRef dnsPort
;
114 CFRunLoopSourceRef dnsRLS
;
115 struct timeval dnsQueryStart
;
117 } SCNetworkReachabilityPrivate
, *SCNetworkReachabilityPrivateRef
;
120 static CFTypeID __kSCNetworkReachabilityTypeID
= _kCFRuntimeNotATypeID
;
123 static const CFRuntimeClass __SCNetworkReachabilityClass
= {
125 "SCNetworkReachability", // className
128 __SCNetworkReachabilityDeallocate
, // dealloc
131 NULL
, // copyFormattingDesc
132 __SCNetworkReachabilityCopyDescription
// copyDebugDesc
136 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
140 * host "something has changed" notifications
143 static pthread_mutex_t hn_lock
= PTHREAD_MUTEX_INITIALIZER
;
144 static SCDynamicStoreRef hn_store
= NULL
;
145 static CFRunLoopSourceRef hn_storeRLS
= NULL
;
146 static CFMutableArrayRef hn_rlList
= NULL
;
147 static CFMutableSetRef hn_targets
= NULL
;
155 dns_config_t
*config
;
157 } dns_configuration_t
;
160 static pthread_mutex_t dns_lock
= PTHREAD_MUTEX_INITIALIZER
;
161 static dns_configuration_t
*dns_configuration
= NULL
;
162 static int dns_token
;
163 static Boolean dns_token_valid
= FALSE
;
166 static __inline__ CFTypeRef
167 isA_SCNetworkReachability(CFTypeRef obj
)
169 return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID()));
174 __log_query_time(Boolean found
, Boolean async
, struct timeval
*start
)
176 struct timeval dnsQueryComplete
;
177 struct timeval dnsQueryElapsed
;
183 if (start
->tv_sec
== 0) {
187 (void) gettimeofday(&dnsQueryComplete
, NULL
);
188 timersub(&dnsQueryComplete
, start
, &dnsQueryElapsed
);
189 SCLog(TRUE
, LOG_DEBUG
,
190 CFSTR("%ssync DNS complete%s (query time = %d.%3.3d)"),
192 found
? "" : ", host not found",
193 dnsQueryElapsed
.tv_sec
,
194 dnsQueryElapsed
.tv_usec
/ 1000);
201 updatePPPStatus(SCDynamicStoreRef
*storeP
,
202 const struct sockaddr
*sa
,
204 SCNetworkConnectionFlags
*flags
)
206 CFDictionaryRef dict
= NULL
;
209 const void * keys_q
[N_QUICK
];
210 const void ** keys
= keys_q
;
213 int sc_status
= kSCStatusReachabilityUnknown
;
214 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
215 const void * values_q
[N_QUICK
];
216 const void ** values
= values_q
;
218 switch (sa
->sa_family
) {
220 entity
= kSCEntNetIPv4
;
223 entity
= kSCEntNetIPv6
;
230 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
232 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("updatePPPStatus SCDynamicStoreCreate() failed"));
238 * grab a snapshot of the PPP configuration from the dynamic store
242 CFMutableArrayRef patterns
;
244 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
245 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
246 kSCDynamicStoreDomainState
,
249 CFArrayAppendValue(patterns
, pattern
);
251 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
252 kSCDynamicStoreDomainSetup
,
255 CFArrayAppendValue(patterns
, pattern
);
257 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
258 kSCDynamicStoreDomainState
,
261 CFArrayAppendValue(patterns
, pattern
);
263 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
267 /* if we could not access the dynamic store */
271 sc_status
= kSCStatusOK
;
274 * look for the service which matches the provided interface
276 n
= CFDictionaryGetCount(dict
);
281 ppp_if
= CFStringCreateWithCStringNoCopy(NULL
,
283 kCFStringEncodingASCII
,
286 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
287 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
288 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
290 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
292 for (i
=0; i
< n
; i
++) {
293 CFArrayRef components
;
296 CFDictionaryRef p_setup
;
297 CFDictionaryRef p_state
;
299 CFStringRef service
= NULL
;
300 CFStringRef s_key
= (CFStringRef
) keys
[i
];
301 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
304 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
308 if (!CFStringHasSuffix(s_key
, entity
)) {
309 continue; // if not an IPv4 or IPv6 entity
312 s_if
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
);
313 if (!isA_CFString(s_if
)) {
314 continue; // if no interface
317 if (!CFEqual(ppp_if
, s_if
)) {
318 continue; // if not this interface
322 * extract service ID, get PPP "state" entity (for status), and get
323 * the "setup" entity (for dial-on-traffic flag)
325 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
326 if (CFArrayGetCount(components
) != 5) {
329 service
= CFArrayGetValueAtIndex(components
, 3);
330 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
331 kSCDynamicStoreDomainState
,
334 p_state
= CFDictionaryGetValue(dict
, key
);
336 key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
337 kSCDynamicStoreDomainSetup
,
340 p_setup
= CFDictionaryGetValue(dict
, key
);
342 CFRelease(components
);
345 if (!isA_CFDictionary(p_state
)) {
348 num
= CFDictionaryGetValue(p_state
, kSCPropNetPPPStatus
);
349 if (!isA_CFNumber(num
)) {
353 if (!CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) {
356 switch (ppp_status
) {
358 /* if we're really UP and RUNNING */
361 /* if we're effectively UP and RUNNING */
364 case PPP_STATERESERVED
:
365 /* if we're not connected at all */
366 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link idle, dial-on-traffic to connect"));
367 *flags
|= kSCNetworkFlagsConnectionRequired
;
370 /* if we're in the process of [dis]connecting */
371 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" PPP link, connection in progress"));
372 *flags
|= kSCNetworkFlagsConnectionRequired
;
376 // check PPP dial-on-traffic status
377 if (isA_CFDictionary(p_setup
)) {
378 num
= CFDictionaryGetValue(p_setup
, kSCPropNetPPPDialOnDemand
);
379 if (isA_CFNumber(num
)) {
382 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
384 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
385 if (ppp_status
== PPP_IDLE
) {
386 *flags
|= kSCNetworkFlagsInterventionRequired
;
397 if (keys
!= keys_q
) {
398 CFAllocatorDeallocate(NULL
, keys
);
399 CFAllocatorDeallocate(NULL
, values
);
404 if (dict
!= NULL
) CFRelease(dict
);
405 if (storeP
!= NULL
) *storeP
= store
;
411 updatePPPAvailable(SCDynamicStoreRef
*storeP
,
412 const struct sockaddr
*sa
,
413 SCNetworkConnectionFlags
*flags
)
415 CFDictionaryRef dict
= NULL
;
418 const void * keys_q
[N_QUICK
];
419 const void ** keys
= keys_q
;
421 int sc_status
= kSCStatusReachabilityUnknown
;
422 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
423 const void * values_q
[N_QUICK
];
424 const void ** values
= values_q
;
427 entity
= kSCEntNetIPv4
;
429 switch (sa
->sa_family
) {
431 entity
= kSCEntNetIPv4
;
434 entity
= kSCEntNetIPv6
;
442 store
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
);
444 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" status = unknown (could not access SCDynamicStore"));
450 * grab a snapshot of the PPP configuration from the dynamic store
454 CFMutableArrayRef patterns
;
456 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
457 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
458 kSCDynamicStoreDomainSetup
,
461 CFArrayAppendValue(patterns
, pattern
);
463 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
464 kSCDynamicStoreDomainSetup
,
467 CFArrayAppendValue(patterns
, pattern
);
469 dict
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
);
473 /* if we could not access the dynamic store */
477 sc_status
= kSCStatusOK
;
480 * look for an available service which will provide connectivity
481 * for the requested address family.
483 n
= CFDictionaryGetCount(dict
);
488 if (n
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
489 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
490 values
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
492 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
494 for (i
= 0; i
< n
; i
++) {
495 CFArrayRef components
;
496 Boolean found
= FALSE
;
498 CFDictionaryRef p_dict
;
500 CFStringRef s_key
= (CFStringRef
) keys
[i
];
501 CFDictionaryRef s_dict
= (CFDictionaryRef
)values
[i
];
503 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) {
507 if (!CFStringHasSuffix(s_key
, entity
)) {
508 continue; // if not an IPv4 or IPv6 entity
511 // extract service ID
512 components
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/"));
513 if (CFArrayGetCount(components
) != 5) {
516 service
= CFArrayGetValueAtIndex(components
, 3);
518 // check for PPP entity
519 p_key
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
520 kSCDynamicStoreDomainSetup
,
523 p_dict
= CFDictionaryGetValue(dict
, p_key
);
526 if (isA_CFDictionary(p_dict
)) {
530 * we have a PPP service for this address family
534 *flags
|= kSCNetworkFlagsReachable
;
535 *flags
|= kSCNetworkFlagsTransientConnection
;
536 *flags
|= kSCNetworkFlagsConnectionRequired
;
539 * get PPP dial-on-traffic status
541 num
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
);
542 if (isA_CFNumber(num
)) {
545 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) {
547 *flags
|= kSCNetworkFlagsConnectionAutomatic
;
553 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = isReachable (after connect)"));
554 SCLog(TRUE
, LOG_INFO
, CFSTR(" service = %@"), service
);
559 CFRelease(components
);
566 if (keys
!= keys_q
) {
567 CFAllocatorDeallocate(NULL
, keys
);
568 CFAllocatorDeallocate(NULL
, values
);
573 if (dict
!= NULL
) CFRelease(dict
);
574 if (storeP
!= NULL
) *storeP
= store
;
579 #define ROUNDUP(a, size) \
580 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
582 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \
583 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
588 get_rtaddrs(int addrs
, struct sockaddr
*sa
, struct sockaddr
**rti_info
)
592 for (i
= 0; i
< RTAX_MAX
; i
++) {
593 if (addrs
& (1 << i
)) {
602 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */
605 checkAddress(SCDynamicStoreRef
*storeP
,
606 const struct sockaddr
*address
,
607 SCNetworkConnectionFlags
*flags
,
612 char if_name
[IFNAMSIZ
+1];
615 pid_t pid
= getpid();
617 struct sockaddr
*rti_info
[RTAX_MAX
];
618 struct rt_msghdr
*rtm
;
620 int sc_status
= kSCStatusReachabilityUnknown
;
621 struct sockaddr_dl
*sdl
;
622 int seq
= (int)pthread_self();
623 SCDynamicStoreRef store
= (storeP
!= NULL
) ? *storeP
: NULL
;
624 char *statusMessage
= NULL
;
625 #ifndef RTM_GET_SILENT
626 #warning Note: Using RTM_GET (and not RTM_GET_SILENT)
627 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
628 int sosize
= 48 * 1024;
632 if (if_index
!= NULL
) {
636 if (address
== NULL
) {
637 /* special case: check only for available paths off the system */
641 switch (address
->sa_family
) {
645 _SC_sockaddr_to_string(address
, buf
, sizeof(buf
));
646 SCLog(TRUE
, LOG_INFO
, CFSTR("checkAddress(%s)"), buf
);
651 * if no code for this address family (yet)
653 SCLog(_sc_verbose
, LOG_ERR
,
654 CFSTR("checkAddress(): unexpected address family %d"),
656 sc_status
= kSCStatusInvalidArgument
;
660 bzero(&buf
, sizeof(buf
));
662 rtm
= (struct rt_msghdr
*)&buf
;
663 rtm
->rtm_msglen
= sizeof(struct rt_msghdr
);
664 rtm
->rtm_version
= RTM_VERSION
;
665 #ifdef RTM_GET_SILENT
666 rtm
->rtm_type
= RTM_GET_SILENT
;
668 rtm
->rtm_type
= RTM_GET
;
670 rtm
->rtm_flags
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
;
671 rtm
->rtm_addrs
= RTA_DST
|RTA_IFP
; /* Both destination and device */
675 switch (address
->sa_family
) {
677 struct sockaddr_in6
*sin6
;
679 sin6
= (struct sockaddr_in6
*)address
;
680 if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) ||
681 IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) &&
682 (sin6
->sin6_scope_id
!= 0)) {
683 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
);
684 sin6
->sin6_scope_id
= 0;
690 sa
= (struct sockaddr
*) (rtm
+ 1);
691 bcopy(address
, sa
, address
->sa_len
);
692 n
= ROUNDUP(sa
->sa_len
, sizeof(u_long
));
693 rtm
->rtm_msglen
+= n
;
695 sdl
= (struct sockaddr_dl
*) ((void *)sa
+ n
);
696 sdl
->sdl_family
= AF_LINK
;
697 sdl
->sdl_len
= sizeof (struct sockaddr_dl
);
698 n
= ROUNDUP(sdl
->sdl_len
, sizeof(u_long
));
699 rtm
->rtm_msglen
+= n
;
701 #ifndef RTM_GET_SILENT
702 pthread_mutex_lock(&lock
);
704 rsock
= socket(PF_ROUTE
, SOCK_RAW
, 0);
706 #ifndef RTM_GET_SILENT
707 pthread_mutex_unlock(&lock
);
709 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(errno
));
710 sc_status
= kSCStatusFailed
;
714 #ifndef RTM_GET_SILENT
715 if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) {
717 pthread_mutex_unlock(&lock
);
718 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(errno
));
719 sc_status
= kSCStatusFailed
;
724 if (write(rsock
, &buf
, rtm
->rtm_msglen
) == -1) {
728 #ifndef RTM_GET_SILENT
729 pthread_mutex_unlock(&lock
);
732 SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(err
));
739 * Type, seq, pid identify our response.
740 * Routing sockets are broadcasters on input.
745 n
= read(rsock
, (void *)&buf
, sizeof(buf
));
751 SCLog(TRUE
, LOG_ERR
, CFSTR("read() failed: %s"), strerror(err
));
752 #ifndef RTM_GET_SILENT
753 pthread_mutex_unlock(&lock
);
758 } while (rtm
->rtm_type
!= RTM_GET
||
759 rtm
->rtm_seq
!= seq
||
760 rtm
->rtm_pid
!= pid
);
763 #ifndef RTM_GET_SILENT
764 pthread_mutex_unlock(&lock
);
767 get_rtaddrs(rtm
->rtm_addrs
, sa
, rti_info
);
774 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), rtm
->rtm_flags
);
776 for (i
= 0; i
< RTAX_MAX
; i
++) {
777 if (rti_info
[i
] != NULL
) {
778 _SC_sockaddr_to_string(rti_info
[i
], buf
, sizeof(buf
));
779 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, buf
);
785 if ((rti_info
[RTAX_IFP
] == NULL
) ||
786 (rti_info
[RTAX_IFP
]->sa_family
!= AF_LINK
)) {
787 /* no interface info */
791 sdl
= (struct sockaddr_dl
*) rti_info
[RTAX_IFP
];
792 if ((sdl
->sdl_nlen
== 0) || (sdl
->sdl_nlen
> IFNAMSIZ
)) {
793 /* no interface name */
797 /* get the interface flags */
799 bzero(&ifr
, sizeof(ifr
));
800 bcopy(sdl
->sdl_data
, ifr
.ifr_name
, sdl
->sdl_nlen
);
802 isock
= socket(AF_INET
, SOCK_DGRAM
, 0);
804 SCLog(TRUE
, LOG_NOTICE
, CFSTR("socket() failed: %s"), strerror(errno
));
808 if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) < 0) {
809 SCLog(TRUE
, LOG_NOTICE
, CFSTR("ioctl() failed: %s"), strerror(errno
));
815 if (!(ifr
.ifr_flags
& IFF_UP
)) {
819 statusMessage
= "isReachable";
820 *flags
|= kSCNetworkFlagsReachable
;
822 if (rtm
->rtm_flags
& RTF_LOCAL
) {
823 statusMessage
= "isReachable (is a local address)";
824 *flags
|= kSCNetworkFlagsIsLocalAddress
;
825 } else if (ifr
.ifr_flags
& IFF_LOOPBACK
) {
826 statusMessage
= "isReachable (is loopback network)";
827 *flags
|= kSCNetworkFlagsIsLocalAddress
;
828 } else if (rti_info
[RTAX_IFA
]) {
829 void *addr1
= (void *)address
;
830 void *addr2
= (void *)rti_info
[RTAX_IFA
];
831 size_t len
= address
->sa_len
;
833 if ((address
->sa_family
!= rti_info
[RTAX_IFA
]->sa_family
) &&
834 (address
->sa_len
!= rti_info
[RTAX_IFA
]->sa_len
)) {
835 SCLog(TRUE
, LOG_NOTICE
,
836 CFSTR("address family/length mismatch: %d/%d != %d/%d"),
839 rti_info
[RTAX_IFA
]->sa_family
,
840 rti_info
[RTAX_IFA
]->sa_len
);
844 switch (address
->sa_family
) {
846 addr1
= &((struct sockaddr_in
*)address
)->sin_addr
;
847 addr2
= &((struct sockaddr_in
*)rti_info
[RTAX_IFA
])->sin_addr
;
848 len
= sizeof(struct in_addr
);
853 if (((struct sockaddr_in
*)address
)->sin_addr
.s_addr
== 0) {
854 statusMessage
= "isReachable (this host)";
855 *flags
|= kSCNetworkFlagsIsLocalAddress
;
859 addr1
= &((struct sockaddr_in6
*)address
)->sin6_addr
;
860 addr2
= &((struct sockaddr_in6
*)rti_info
[RTAX_IFA
])->sin6_addr
;
861 len
= sizeof(struct in6_addr
);
867 if (memcmp(addr1
, addr2
, len
) == 0) {
868 statusMessage
= "isReachable (is interface address)";
869 *flags
|= kSCNetworkFlagsIsLocalAddress
;
873 if (!(rtm
->rtm_flags
& RTF_GATEWAY
) &&
874 (rti_info
[RTAX_GATEWAY
] != NULL
) &&
875 (rti_info
[RTAX_GATEWAY
]->sa_family
== AF_LINK
) &&
876 !(ifr
.ifr_flags
& IFF_POINTOPOINT
)) {
877 *flags
|= kSCNetworkFlagsIsDirect
;
880 bzero(&if_name
, sizeof(if_name
));
883 (sdl
->sdl_nlen
<= IFNAMSIZ
) ? sdl
->sdl_nlen
: IFNAMSIZ
);
885 if (if_index
!= NULL
) {
886 *if_index
= sdl
->sdl_index
;
890 SCLog(TRUE
, LOG_INFO
, CFSTR(" status = %s"), statusMessage
);
891 SCLog(TRUE
, LOG_INFO
, CFSTR(" device = %s (%hu)"), if_name
, sdl
->sdl_index
);
892 SCLog(TRUE
, LOG_INFO
, CFSTR(" ifr_flags = 0x%04hx"), ifr
.ifr_flags
);
893 SCLog(TRUE
, LOG_INFO
, CFSTR(" rtm_flags = 0x%08x"), rtm
->rtm_flags
);
896 sc_status
= kSCStatusOK
;
898 if (ifr
.ifr_flags
& IFF_POINTOPOINT
) {
900 * We have an interface which "claims" to be a valid path
903 *flags
|= kSCNetworkFlagsTransientConnection
;
906 * Check if this is a dial-on-demand PPP link that isn't
909 sc_status
= updatePPPStatus(&store
, address
, if_name
, flags
);
916 sc_status
= updatePPPAvailable(&store
, address
, flags
);
921 SCLog(_sc_debug
, LOG_INFO
, CFSTR(" cannot be reached"));
924 if (storeP
!= NULL
) *storeP
= store
;
925 if (sc_status
!= kSCStatusOK
) {
926 _SCErrorSet(sc_status
);
935 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
)
937 CFAllocatorRef allocator
= CFGetAllocator(cf
);
938 CFMutableStringRef result
;
939 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
941 result
= CFStringCreateMutable(allocator
, 0);
942 CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> { "), cf
, allocator
);
943 switch (targetPrivate
->type
) {
944 case reachabilityTypeAddress
:
945 case reachabilityTypeAddressPair
: {
948 if (targetPrivate
->localAddress
!= NULL
) {
949 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
));
950 CFStringAppendFormat(result
, NULL
, CFSTR("local address=%s"),
954 if (targetPrivate
->remoteAddress
!= NULL
) {
955 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
));
956 CFStringAppendFormat(result
, NULL
, CFSTR("%s%saddress=%s"),
957 targetPrivate
->localAddress
? ", " : "",
958 (targetPrivate
->type
== reachabilityTypeAddressPair
) ? "remote " : "",
963 case reachabilityTypeName
: {
964 CFStringAppendFormat(result
, NULL
, CFSTR("name=%s"), targetPrivate
->name
);
965 if (targetPrivate
->resolvedAddress
|| (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
966 if (targetPrivate
->resolvedAddress
!= NULL
) {
967 if (isA_CFArray(targetPrivate
->resolvedAddress
)) {
969 CFIndex n
= CFArrayGetCount(targetPrivate
->resolvedAddress
);
971 CFStringAppendFormat(result
, NULL
, CFSTR(" ("));
972 for (i
= 0; i
< n
; i
++) {
977 address
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
);
978 sa
= (struct sockaddr
*)CFDataGetBytePtr(address
);
979 _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
));
980 CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"),
984 CFStringAppendFormat(result
, NULL
, CFSTR(")"));
986 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)"));
989 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"),
990 gai_strerror(targetPrivate
->resolvedAddressError
));
992 } else if (targetPrivate
->dnsPort
) {
993 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)"));
998 if (targetPrivate
->rls
!= NULL
) {
999 CFStringAppendFormat(result
,
1001 CFSTR(", flags=%8.8x, if_index=%hu"),
1002 targetPrivate
->flags
,
1003 targetPrivate
->if_index
);
1005 CFStringAppendFormat(result
, NULL
, CFSTR(" }"));
1012 __SCNetworkReachabilityDeallocate(CFTypeRef cf
)
1014 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)cf
;
1016 /* release resources */
1018 pthread_mutex_destroy(&targetPrivate
->lock
);
1020 if (targetPrivate
->name
!= NULL
)
1021 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
);
1023 if (targetPrivate
->resolvedAddress
!= NULL
)
1024 CFRelease(targetPrivate
->resolvedAddress
);
1026 if (targetPrivate
->localAddress
!= NULL
)
1027 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
);
1029 if (targetPrivate
->remoteAddress
!= NULL
)
1030 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
);
1032 if (targetPrivate
->rlsContext
.release
!= NULL
) {
1033 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
1041 __SCNetworkReachabilityInitialize(void)
1043 __kSCNetworkReachabilityTypeID
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
);
1048 static SCNetworkReachabilityPrivateRef
1049 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator
)
1051 SCNetworkReachabilityPrivateRef targetPrivate
;
1054 /* initialize runtime */
1055 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);
1057 /* allocate target */
1058 size
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
);
1059 targetPrivate
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
,
1060 __kSCNetworkReachabilityTypeID
,
1063 if (targetPrivate
== NULL
) {
1067 pthread_mutex_init(&targetPrivate
->lock
, NULL
);
1069 targetPrivate
->name
= NULL
;
1071 targetPrivate
->resolvedAddress
= NULL
;
1072 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1074 targetPrivate
->localAddress
= NULL
;
1075 targetPrivate
->remoteAddress
= NULL
;
1077 targetPrivate
->flags
= 0;
1078 targetPrivate
->if_index
= 0;
1080 targetPrivate
->rls
= NULL
;
1081 targetPrivate
->rlsFunction
= NULL
;
1082 targetPrivate
->rlsContext
.info
= NULL
;
1083 targetPrivate
->rlsContext
.retain
= NULL
;
1084 targetPrivate
->rlsContext
.release
= NULL
;
1085 targetPrivate
->rlsContext
.copyDescription
= NULL
;
1086 targetPrivate
->rlList
= NULL
;
1088 targetPrivate
->haveDNS
= FALSE
;
1089 targetPrivate
->dnsPort
= NULL
;
1090 targetPrivate
->dnsRLS
= NULL
;
1092 return targetPrivate
;
1096 SCNetworkReachabilityRef
1097 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator
,
1098 const struct sockaddr
*address
)
1100 SCNetworkReachabilityPrivateRef targetPrivate
;
1102 if ((address
== NULL
) ||
1103 (address
->sa_len
== 0) ||
1104 (address
->sa_len
> sizeof(struct sockaddr_storage
))) {
1105 _SCErrorSet(kSCStatusInvalidArgument
);
1109 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1110 if (targetPrivate
== NULL
) {
1114 targetPrivate
->type
= reachabilityTypeAddress
;
1115 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0);
1116 bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
);
1118 return (SCNetworkReachabilityRef
)targetPrivate
;
1122 SCNetworkReachabilityRef
1123 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator
,
1124 const struct sockaddr
*localAddress
,
1125 const struct sockaddr
*remoteAddress
)
1127 SCNetworkReachabilityPrivateRef targetPrivate
;
1129 if ((localAddress
== NULL
) && (remoteAddress
== NULL
)) {
1130 _SCErrorSet(kSCStatusInvalidArgument
);
1134 if (localAddress
!= NULL
) {
1135 if ((localAddress
->sa_len
== 0) ||
1136 (localAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1137 _SCErrorSet(kSCStatusInvalidArgument
);
1142 if (remoteAddress
!= NULL
) {
1143 if ((remoteAddress
->sa_len
== 0) ||
1144 (remoteAddress
->sa_len
> sizeof(struct sockaddr_storage
))) {
1145 _SCErrorSet(kSCStatusInvalidArgument
);
1150 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1151 if (targetPrivate
== NULL
) {
1155 targetPrivate
->type
= reachabilityTypeAddressPair
;
1157 if (localAddress
!= NULL
) {
1158 targetPrivate
->localAddress
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0);
1159 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
);
1162 if (remoteAddress
!= NULL
) {
1163 targetPrivate
->remoteAddress
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0);
1164 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
);
1167 return (SCNetworkReachabilityRef
)targetPrivate
;
1171 SCNetworkReachabilityRef
1172 SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator
,
1173 const char *nodename
)
1175 struct sockaddr_in sin
;
1176 struct sockaddr_in6 sin6
;
1177 SCNetworkReachabilityPrivateRef targetPrivate
;
1179 if (nodename
== NULL
) {
1180 _SCErrorSet(kSCStatusInvalidArgument
);
1184 /* check if this "nodename" is really an IP[v6] address in disguise */
1186 bzero(&sin
, sizeof(sin
));
1187 sin
.sin_len
= sizeof(sin
);
1188 sin
.sin_family
= AF_INET
;
1189 if (inet_aton(nodename
, &sin
.sin_addr
) == 1) {
1190 /* if IPv4 address */
1191 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin
);
1194 bzero(&sin6
, sizeof(sin6
));
1195 sin6
.sin6_len
= sizeof(sin6
);
1196 sin6
.sin6_family
= AF_INET6
;
1197 if (inet_pton(AF_INET6
, nodename
, &sin6
.sin6_addr
) == 1) {
1198 /* if IPv6 address */
1201 p
= strchr(nodename
, '%');
1203 sin6
.sin6_scope_id
= if_nametoindex(p
+1);
1206 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr
*)&sin6
);
1209 targetPrivate
= __SCNetworkReachabilityCreatePrivate(allocator
);
1210 if (targetPrivate
== NULL
) {
1214 targetPrivate
->type
= reachabilityTypeName
;
1216 targetPrivate
->flags
|= kSCNetworkFlagsFirstResolvePending
;
1218 targetPrivate
->name
= CFAllocatorAllocate(NULL
, strlen(nodename
) + 1, 0);
1219 strcpy((char *)targetPrivate
->name
, nodename
);
1221 return (SCNetworkReachabilityRef
)targetPrivate
;
1226 SCNetworkReachabilityGetTypeID(void)
1228 pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); /* initialize runtime */
1229 return __kSCNetworkReachabilityTypeID
;
1234 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target
,
1237 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1239 if (!isA_SCNetworkReachability(target
)) {
1240 _SCErrorSet(kSCStatusInvalidArgument
);
1244 if (targetPrivate
->type
!= reachabilityTypeName
) {
1245 _SCErrorSet(kSCStatusInvalidArgument
);
1250 *error_num
= targetPrivate
->resolvedAddressError
;
1253 if (targetPrivate
->resolvedAddress
|| (targetPrivate
->resolvedAddressError
!= NETDB_SUCCESS
)) {
1254 if (targetPrivate
->resolvedAddress
!= NULL
) {
1255 return CFRetain(targetPrivate
->resolvedAddress
);
1257 /* if status is known but no resolved addresses to return */
1258 _SCErrorSet(kSCStatusOK
);
1263 _SCErrorSet(kSCStatusReachabilityUnknown
);
1269 __SCNetworkReachabilitySetResolvedAddress(int32_t status
,
1270 struct addrinfo
*res
,
1271 SCNetworkReachabilityRef target
)
1273 struct addrinfo
*resP
;
1274 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1276 if (targetPrivate
->resolvedAddress
!= NULL
) {
1277 CFRelease(targetPrivate
->resolvedAddress
);
1278 targetPrivate
->resolvedAddress
= NULL
;
1281 if ((status
== 0) && (res
!= NULL
)) {
1283 CFMutableArrayRef addresses
;
1285 addresses
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1287 for (resP
= res
; resP
; resP
= resP
->ai_next
) {
1288 CFDataRef newAddress
;
1290 newAddress
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
);
1291 CFArrayAppendValue(addresses
, newAddress
);
1292 CFRelease(newAddress
);
1295 /* save the resolved address[es] */
1296 targetPrivate
->resolvedAddress
= addresses
;
1297 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
1299 SCLog(_sc_debug
, LOG_INFO
, CFSTR("getaddrinfo() failed: %s"), gai_strerror(status
));
1301 /* save the error associated with the attempt to resolve the name */
1302 targetPrivate
->resolvedAddress
= CFRetain(kCFNull
);
1303 targetPrivate
->resolvedAddressError
= status
;
1306 if (res
) freeaddrinfo(res
);
1308 if (targetPrivate
->rls
!= NULL
) {
1309 SCLog(_sc_debug
, LOG_INFO
, CFSTR("DNS request completed"));
1310 CFRunLoopSourceSignal(targetPrivate
->rls
);
1311 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1319 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo
*res
, void *context
)
1321 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)context
;
1322 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1324 __log_query_time(((status
== 0) && (res
!= NULL
)), // if successful query
1326 &targetPrivate
->dnsQueryStart
); // start time
1328 __SCNetworkReachabilitySetResolvedAddress(status
, res
, target
);
1334 * rankReachability()
1335 * Not reachable == 0
1336 * Connection Required == 1
1340 rankReachability(SCNetworkConnectionFlags flags
)
1344 if (flags
& kSCNetworkFlagsReachable
) rank
= 2;
1345 if (flags
& kSCNetworkFlagsConnectionRequired
) rank
= 1;
1351 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
1353 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
1354 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1356 pthread_mutex_lock(&targetPrivate
->lock
);
1358 getaddrinfo_async_handle_reply(msg
);
1360 if (port
== targetPrivate
->dnsPort
) {
1361 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
1362 CFRelease(targetPrivate
->dnsRLS
);
1363 targetPrivate
->dnsRLS
= NULL
;
1364 CFRelease(targetPrivate
->dnsPort
);
1365 targetPrivate
->dnsPort
= NULL
;
1368 pthread_mutex_unlock(&targetPrivate
->lock
);
1375 check_resolver_reachability(SCDynamicStoreRef
*storeP
,
1376 dns_resolver_t
*resolver
,
1377 SCNetworkConnectionFlags
*flags
,
1383 *flags
= kSCNetworkFlagsReachable
;
1386 for (i
= 0; i
< resolver
->n_nameserver
; i
++) {
1387 struct sockaddr
*address
= resolver
->nameserver
[i
];
1388 SCNetworkConnectionFlags ns_flags
= 0;
1392 if (address
->sa_family
!= AF_INET
) {
1394 * we need to skip non-IPv4 DNS server
1395 * addresses (at least until [3510431] has
1401 ok
= checkAddress(storeP
, address
, &ns_flags
, NULL
);
1407 if (rankReachability(ns_flags
) < rankReachability(*flags
)) {
1408 /* return the worst case result */
1420 check_matching_resolvers(SCDynamicStoreRef
*storeP
,
1421 dns_config_t
*dns_config
,
1423 SCNetworkConnectionFlags
*flags
,
1427 Boolean matched
= FALSE
;
1428 const char *name
= fqdn
;
1430 while (!matched
&& (name
!= NULL
)) {
1434 * check if the provided name (or sub-component)
1435 * matches one of our resolver configurations.
1438 for (i
= 0; i
< dns_config
->n_resolver
; i
++) {
1440 dns_resolver_t
*resolver
;
1442 resolver
= dns_config
->resolver
[i
];
1443 domain
= resolver
->domain
;
1444 if (domain
!= NULL
&& (len
== strlen(domain
))) {
1445 if (strcasecmp(name
, domain
) == 0) {
1449 * if name matches domain
1452 ok
= check_resolver_reachability(storeP
, resolver
, flags
, haveDNS
);
1463 * we have not found a matching resolver, try
1464 * a less qualified domain
1466 name
= strchr(name
, '.');
1467 if ((name
!= NULL
) && (*name
!= '\0')) {
1479 static dns_configuration_t
*
1480 dns_configuration_retain()
1482 pthread_mutex_lock(&dns_lock
);
1484 if ((dns_configuration
!= NULL
) && dns_token_valid
) {
1489 * check if the global [DNS] configuration snapshot needs
1492 status
= notify_check(dns_token
, &check
);
1493 if (status
!= NOTIFY_STATUS_OK
) {
1494 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
1497 if ((status
!= NOTIFY_STATUS_OK
) || (check
!= 0)) {
1499 * if the snapshot needs to be refreshed
1501 if (dns_configuration
->refs
== 0) {
1502 dns_configuration_free(dns_configuration
->config
);
1503 CFAllocatorDeallocate(NULL
, dns_configuration
);
1505 dns_configuration
= NULL
;
1509 if (dns_configuration
== NULL
) {
1510 dns_config_t
*new_config
;
1512 new_config
= dns_configuration_copy();
1513 if (new_config
!= NULL
) {
1514 dns_configuration
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0);
1515 dns_configuration
->config
= new_config
;
1516 dns_configuration
->refs
= 0;
1520 if (dns_configuration
!= NULL
) {
1521 dns_configuration
->refs
++;
1524 pthread_mutex_unlock(&dns_lock
);
1525 return dns_configuration
;
1530 dns_configuration_release(dns_configuration_t
*config
)
1532 pthread_mutex_lock(&dns_lock
);
1535 if (config
->refs
== 0) {
1536 if ((dns_configuration
!= config
)) {
1537 dns_configuration_free(config
->config
);
1538 CFAllocatorDeallocate(NULL
, config
);
1542 pthread_mutex_unlock(&dns_lock
);
1548 dns_configuration_watch()
1551 const char *dns_key
;
1555 pthread_mutex_lock(&dns_lock
);
1557 dns_key
= dns_configuration_notify_key();
1558 if (dns_key
== NULL
) {
1559 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed"));
1563 status
= notify_register_check(dns_key
, &dns_token
);
1564 if (status
== NOTIFY_STATUS_OK
) {
1565 dns_token_valid
= TRUE
;
1567 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
);
1571 status
= notify_check(dns_token
, &dns_check
);
1572 if (status
!= NOTIFY_STATUS_OK
) {
1573 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
);
1574 (void)notify_cancel(dns_token
);
1575 dns_token_valid
= FALSE
;
1583 pthread_mutex_unlock(&dns_lock
);
1589 dns_configuration_unwatch()
1591 pthread_mutex_lock(&dns_lock
);
1593 (void)notify_cancel(dns_token
);
1594 dns_token_valid
= FALSE
;
1596 if ((dns_configuration
!= NULL
) && (dns_configuration
->refs
== 0)) {
1597 dns_configuration_free(dns_configuration
->config
);
1598 CFAllocatorDeallocate(NULL
, dns_configuration
);
1599 dns_configuration
= NULL
;
1602 pthread_mutex_unlock(&dns_lock
);
1608 _SC_checkResolverReachability(SCDynamicStoreRef
*storeP
,
1609 SCNetworkConnectionFlags
*flags
,
1611 const char * nodename
)
1613 dns_resolver_t
*default_resolver
;
1614 dns_configuration_t
*dns
;
1615 Boolean found
= FALSE
;
1616 char *fqdn
= (char *)nodename
;
1618 Boolean isFQDN
= FALSE
;
1623 * We first assume that all of the configured DNS servers
1624 * are available. Since we don't know which name server will
1625 * be consulted to resolve the specified nodename we need to
1626 * check the availability of ALL name servers. We can only
1627 * proceed if we know that our query can be answered.
1630 *flags
= kSCNetworkFlagsReachable
;
1635 // if no nodename, return not reachable
1640 dns
= dns_configuration_retain();
1646 if (dns
->config
->n_resolver
== 0) {
1647 // if no resolver configuration
1651 *flags
= kSCNetworkFlagsReachable
;
1653 if (fqdn
[len
- 1] == '.') {
1656 // trim trailing '.''s
1657 while ((len
> 0) && (fqdn
[len
-1] == '.')) {
1658 if (fqdn
== nodename
) {
1659 fqdn
= strdup(nodename
);
1665 default_resolver
= dns
->config
->resolver
[0];
1668 * try the provided name
1670 found
= check_matching_resolvers(storeP
, dns
->config
, fqdn
, flags
, haveDNS
);
1671 if (!found
&& !isFQDN
&& (dns
->config
->n_resolver
> 1)) {
1673 * FQDN not specified, try w/search or default domain(s) too
1675 if (default_resolver
->n_search
> 0) {
1676 for (i
= 0; !found
&& (i
< default_resolver
->n_search
); i
++) {
1678 char *search_fqdn
= NULL
;
1680 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]);
1685 // try the provided name with the search domain appended
1686 found
= check_matching_resolvers(storeP
, dns
->config
, search_fqdn
, flags
, haveDNS
);
1689 } else if (default_resolver
->domain
!= NULL
) {
1691 int domain_parts
= 1;
1693 for (dp
= default_resolver
->domain
; *dp
!= '\0'; dp
++) {
1699 dp
= default_resolver
->domain
;
1700 for (i
= LOCALDOMAINPARTS
; !found
&& (i
<= domain_parts
); i
++) {
1702 char *search_fqdn
= NULL
;
1704 ret
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
);
1709 // try the provided name with the [default] domain appended
1710 found
= check_matching_resolvers(storeP
, dns
->config
, search_fqdn
, flags
, haveDNS
);
1713 // move to the next component of the [default] domain
1714 dp
= strchr(dp
, '.') + 1;
1721 * check the reachability of the default resolver
1723 ok
= check_resolver_reachability(storeP
, default_resolver
, flags
, haveDNS
);
1726 if (fqdn
!= nodename
) free(fqdn
);
1731 dns_configuration_release(dns
);
1739 startAsyncDNSQuery(SCNetworkReachabilityRef target
) {
1740 CFMachPortContext context
= { 0, (void *)target
, CFRetain
, CFRelease
, CFCopyDescription
};
1742 struct addrinfo hints
;
1746 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1748 (void) gettimeofday(&targetPrivate
->dnsQueryStart
, NULL
);
1750 bzero(&hints
, sizeof(hints
));
1751 hints
.ai_flags
= AI_ADDRCONFIG
;
1753 hints
.ai_flags
|= AI_PARALLEL
;
1754 #endif /* AI_PARALLEL */
1756 error
= getaddrinfo_async_start(&port
,
1757 targetPrivate
->name
,
1760 __SCNetworkReachabilityCallbackSetResolvedAddress
,
1763 /* save the error associated with the attempt to resolve the name */
1764 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
);
1768 targetPrivate
->dnsPort
= CFMachPortCreateWithPort(NULL
,
1770 getaddrinfo_async_handleCFReply
,
1773 targetPrivate
->dnsRLS
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0);
1775 n
= CFArrayGetCount(targetPrivate
->rlList
);
1776 for (i
= 0; i
< n
; i
+= 3) {
1777 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1);
1778 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2);
1780 CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
);
1788 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef
*storeP
,
1789 SCNetworkReachabilityRef target
,
1790 SCNetworkConnectionFlags
*flags
,
1794 CFMutableArrayRef addresses
= NULL
;
1795 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
1796 SCNetworkConnectionFlags my_flags
= 0;
1797 uint16_t my_index
= 0;
1801 if (if_index
!= NULL
) {
1805 if (!isA_SCNetworkReachability(target
)) {
1806 _SCErrorSet(kSCStatusInvalidArgument
);
1810 switch (targetPrivate
->type
) {
1811 case reachabilityTypeAddress
:
1812 case reachabilityTypeAddressPair
: {
1814 * Check "local" address
1816 if (targetPrivate
->localAddress
!= NULL
) {
1818 * Check "local" address
1820 ok
= checkAddress(storeP
, targetPrivate
->localAddress
, &my_flags
, &my_index
);
1822 goto error
; /* not today */
1825 if (!(my_flags
& kSCNetworkFlagsIsLocalAddress
)) {
1826 goto error
; /* not reachable, non-"local" address */
1831 * Check "remote" address
1833 if (targetPrivate
->remoteAddress
!= NULL
) {
1835 * in cases where we have "local" and "remote" addresses
1836 * we need to re-initialize the to-be-returned flags.
1842 * Check "remote" address
1844 ok
= checkAddress(storeP
, targetPrivate
->remoteAddress
, &my_flags
, &my_index
);
1846 goto error
; /* not today */
1854 case reachabilityTypeName
: {
1855 struct timeval dnsQueryStart
;
1857 struct addrinfo hints
;
1858 SCNetworkConnectionFlags ns_flags
;
1859 struct addrinfo
*res
;
1861 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
1862 if ((addresses
!= NULL
) || (error
!= NETDB_SUCCESS
)) {
1863 /* if resolved or an error had been detected */
1864 goto checkResolvedAddress
;
1867 /* check the reachability of the DNS servers */
1868 ok
= _SC_checkResolverReachability(storeP
,
1870 &targetPrivate
->haveDNS
,
1871 targetPrivate
->name
);
1873 /* if we could not get DNS server info */
1877 if (rankReachability(ns_flags
) < 2) {
1879 * if DNS servers are not (or are no longer) reachable, set
1880 * flags based on the availability of configured (but not
1883 if (!checkAddress(storeP
, NULL
, &my_flags
, &my_index
)) {
1887 if (async
&& (targetPrivate
->rls
!= NULL
)) {
1889 * return "host not found", set flags appropriately,
1890 * and schedule notification.
1892 __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NODATA
,
1895 my_flags
|= (targetPrivate
->flags
& kSCNetworkFlagsFirstResolvePending
);
1897 SCLog(_sc_debug
, LOG_INFO
, CFSTR("no DNS servers are reachable"));
1898 CFRunLoopSourceSignal(targetPrivate
->rls
);
1899 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
1905 /* for async requests we return the last known status */
1906 my_flags
= targetPrivate
->flags
;
1907 my_index
= targetPrivate
->if_index
;
1909 if (targetPrivate
->dnsPort
) {
1910 /* if request already in progress */
1914 SCLog(_sc_debug
, LOG_INFO
, CFSTR("start DNS query for \"%s\""), targetPrivate
->name
);
1917 * initiate an async DNS query
1919 if (!startAsyncDNSQuery(target
)) {
1920 /* if we could not initiate the request, process error */
1921 goto checkResolvedAddress
;
1924 /* if request initiated */
1928 SCLog(_sc_debug
, LOG_INFO
, CFSTR("check DNS for \"%s\""), targetPrivate
->name
);
1931 * OK, all of the DNS name servers are available. Let's
1932 * resolve the nodename into an address.
1935 (void) gettimeofday(&dnsQueryStart
, NULL
);
1938 bzero(&hints
, sizeof(hints
));
1939 hints
.ai_flags
= AI_ADDRCONFIG
;
1941 hints
.ai_flags
|= AI_PARALLEL
;
1942 #endif /* AI_PARALLEL */
1944 error
= getaddrinfo(targetPrivate
->name
, NULL
, &hints
, &res
);
1946 __log_query_time(((error
== 0) && (res
!= NULL
)),// if successful query
1948 &dnsQueryStart
); // start time
1950 __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
);
1952 addresses
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
);
1954 checkResolvedAddress
:
1957 * We first assume that the requested host is NOT available.
1958 * Then, check each address for accessibility and return the
1959 * best status available.
1964 if (isA_CFArray(addresses
)) {
1966 CFIndex n
= CFArrayGetCount(addresses
);
1968 for (i
= 0; i
< n
; i
++) {
1969 SCNetworkConnectionFlags ns_flags
= 0;
1970 uint16_t ns_if_index
= 0;
1971 struct sockaddr
*sa
;
1973 sa
= (struct sockaddr
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
));
1975 ok
= checkAddress(storeP
, sa
, &ns_flags
, &ns_if_index
);
1977 goto error
; /* not today */
1980 if (rankReachability(ns_flags
) > rankReachability(my_flags
)) {
1981 /* return the best case result */
1982 my_flags
= ns_flags
;
1983 my_index
= ns_if_index
;
1984 if (rankReachability(my_flags
) == 2) {
1991 if ((error
== EAI_NODATA
) && !targetPrivate
->haveDNS
) {
1993 * No DNS servers are defined. Set flags based on
1994 * the availability of configured (but not active)
1997 ok
= checkAddress(storeP
, NULL
, &my_flags
, &my_index
);
1999 goto error
; /* not today */
2002 if ((my_flags
& kSCNetworkFlagsReachable
) &&
2003 (my_flags
& kSCNetworkFlagsConnectionRequired
)) {
2005 * Since we might pick up a set of DNS servers when this connection
2006 * is established, don't reply with a "HOST NOT FOUND" error just yet.
2011 /* Host not found, not reachable! */
2022 if (if_index
!= NULL
) {
2023 *if_index
= my_index
;
2028 if (addresses
!= NULL
) CFRelease(addresses
);
2034 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target
,
2035 SCNetworkConnectionFlags
*flags
)
2038 SCDynamicStoreRef store
= NULL
;
2039 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2041 if (!isA_SCNetworkReachability(target
)) {
2042 _SCErrorSet(kSCStatusInvalidArgument
);
2046 if (targetPrivate
->rlList
!= NULL
) {
2047 // if being watched, return the last known (and what should be current) status
2048 *flags
= targetPrivate
->flags
& ~kSCNetworkFlagsFirstResolvePending
;
2052 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, flags
, NULL
, FALSE
);
2053 *flags
&= ~kSCNetworkFlagsFirstResolvePending
;
2054 if (store
!= NULL
) CFRelease(store
);
2060 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef store
)
2062 CFStringRef dns_key
;
2064 CFMutableArrayRef keys
;
2065 CFStringRef pattern
;
2066 CFMutableArrayRef patterns
;
2068 keys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2069 patterns
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2071 // Setup:/Network/Global/IPv4 (for the ServiceOrder)
2072 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2073 kSCDynamicStoreDomainSetup
,
2075 CFArrayAppendValue(keys
, key
);
2078 // Notify:com.apple.SystemConfiguration.dns_configuration
2079 dns_key
= CFStringCreateWithCString(NULL
,
2080 dns_configuration_notify_key(),
2081 kCFStringEncodingASCII
);
2082 key
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Notify:%@"), dns_key
);
2084 CFArrayAppendValue(keys
, key
);
2087 // State:/Network/Global/IPv4 (default route)
2088 key
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
,
2089 kSCDynamicStoreDomainState
,
2091 CFArrayAppendValue(keys
, key
);
2094 // Setup: per-service IPv4 info
2095 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2096 kSCDynamicStoreDomainSetup
,
2099 CFArrayAppendValue(patterns
, pattern
);
2102 // Setup: per-service Interface info
2103 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2104 kSCDynamicStoreDomainSetup
,
2106 kSCEntNetInterface
);
2107 CFArrayAppendValue(patterns
, pattern
);
2110 // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand)
2111 pattern
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
,
2112 kSCDynamicStoreDomainSetup
,
2115 CFArrayAppendValue(patterns
, pattern
);
2118 // State: per-interface IPv4 info
2119 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2120 kSCDynamicStoreDomainState
,
2123 CFArrayAppendValue(patterns
, pattern
);
2126 // State: per-interface IPv6 info
2127 pattern
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
,
2128 kSCDynamicStoreDomainState
,
2131 CFArrayAppendValue(patterns
, pattern
);
2134 (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
);
2136 CFRelease(patterns
);
2143 __SCNetworkReachabilityReachabilityHandleChanges(SCDynamicStoreRef store
,
2144 CFArrayRef changedKeys
,
2147 Boolean dnsConfigChanged
= FALSE
;
2152 const void * targets_q
[N_QUICK
];
2153 const void ** targets
= targets_q
;
2155 pthread_mutex_lock(&hn_lock
);
2157 nTargets
= CFSetGetCount(hn_targets
);
2158 if (nTargets
== 0) {
2159 /* if no addresses being monitored */
2163 if (CFArrayGetCount(changedKeys
) == 0) {
2168 SCLog(_sc_debug
, LOG_INFO
, CFSTR("process configuration change"));
2170 dnsKey
= CFStringCreateWithCString(NULL
,
2171 dns_configuration_notify_key(),
2172 kCFStringEncodingASCII
);
2173 key
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Notify:%@"), dnsKey
);
2175 if (CFArrayContainsValue(changedKeys
,
2176 CFRangeMake(0, CFArrayGetCount(changedKeys
)),
2178 dnsConfigChanged
= TRUE
; /* the DNS server(s) have changed */
2183 SCLog(_sc_debug
&& dnsConfigChanged
, LOG_INFO
, CFSTR(" DNS configuration changed"));
2185 if (nTargets
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
)))
2186 targets
= CFAllocatorAllocate(NULL
, nTargets
* sizeof(CFTypeRef
), 0);
2187 CFSetGetValues(hn_targets
, targets
);
2188 for (i
= 0; i
< nTargets
; i
++) {
2189 SCNetworkReachabilityRef target
= targets
[i
];
2190 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2192 pthread_mutex_lock(&targetPrivate
->lock
);
2194 if (targetPrivate
->type
== reachabilityTypeName
) {
2195 Boolean dnsChanged
= dnsConfigChanged
;
2199 * if the DNS configuration didn't change we still need to
2200 * check that the DNS servers are accessible.
2202 SCNetworkConnectionFlags ns_flags
;
2205 /* check the reachability of the DNS servers */
2206 ok
= _SC_checkResolverReachability(&store
,
2208 &targetPrivate
->haveDNS
,
2209 targetPrivate
->name
);
2210 if (!ok
|| (rankReachability(ns_flags
) < 2)) {
2211 /* if DNS servers are not reachable */
2217 if (targetPrivate
->dnsPort
) {
2218 /* cancel the outstanding DNS query */
2219 lu_async_call_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2220 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
2221 CFRelease(targetPrivate
->dnsRLS
);
2222 targetPrivate
->dnsRLS
= NULL
;
2223 CFRelease(targetPrivate
->dnsPort
);
2224 targetPrivate
->dnsPort
= NULL
;
2227 /* schedule request to resolve the name again */
2228 if (targetPrivate
->resolvedAddress
!= NULL
) {
2229 CFRelease(targetPrivate
->resolvedAddress
);
2230 targetPrivate
->resolvedAddress
= NULL
;
2232 targetPrivate
->resolvedAddress
= NULL
;
2233 targetPrivate
->resolvedAddressError
= NETDB_SUCCESS
;
2237 CFRunLoopSourceSignal(targetPrivate
->rls
);
2238 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2240 pthread_mutex_unlock(&targetPrivate
->lock
);
2242 if (targets
!= targets_q
) CFAllocatorDeallocate(NULL
, targets
);
2246 pthread_mutex_unlock(&hn_lock
);
2252 rlsPerform(void *info
)
2255 void (*context_release
)(const void *);
2256 SCNetworkConnectionFlags flags
;
2259 SCNetworkReachabilityCallBack rlsFunction
;
2260 SCDynamicStoreRef store
= NULL
;
2261 SCNetworkReachabilityRef target
= (SCNetworkReachabilityRef
)info
;
2262 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2264 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("process reachability change"));
2266 pthread_mutex_lock(&targetPrivate
->lock
);
2268 /* update reachability, notify if status changed */
2269 ok
= __SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
);
2270 if (store
!= NULL
) CFRelease(store
);
2272 /* if reachability status not available */
2277 if ((targetPrivate
->flags
== flags
) && (targetPrivate
->if_index
== if_index
)) {
2278 /* if reachability flags and interface have not changed */
2279 pthread_mutex_unlock(&targetPrivate
->lock
);
2280 SCLog(_sc_debug
, LOG_DEBUG
,
2281 CFSTR("flags/interface match (now %8.8x/%hu)"),
2285 SCLog(_sc_debug
, LOG_DEBUG
,
2286 CFSTR("flags/interface have changed (was %8.8x/%hu, now %8.8x/%hu)"),
2287 targetPrivate
->flags
, targetPrivate
->if_index
,
2291 /* update flags / interface */
2292 targetPrivate
->flags
= flags
;
2293 targetPrivate
->if_index
= if_index
;
2296 rlsFunction
= targetPrivate
->rlsFunction
;
2297 if (targetPrivate
->rlsContext
.retain
!= NULL
) {
2298 context_info
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
);
2299 context_release
= targetPrivate
->rlsContext
.release
;
2301 context_info
= targetPrivate
->rlsContext
.info
;
2302 context_release
= NULL
;
2305 pthread_mutex_unlock(&targetPrivate
->lock
);
2307 if (rlsFunction
!= NULL
) {
2308 (*rlsFunction
)(target
, flags
, context_info
);
2311 if (context_release
!= NULL
) {
2312 (*context_release
)(context_info
);
2320 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target
,
2321 SCNetworkReachabilityCallBack callout
,
2322 SCNetworkReachabilityContext
*context
)
2324 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2326 pthread_mutex_lock(&targetPrivate
->lock
);
2328 if (targetPrivate
->rlsContext
.release
!= NULL
) {
2329 /* let go of the current context */
2330 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
);
2333 targetPrivate
->rlsFunction
= callout
;
2334 targetPrivate
->rlsContext
.info
= NULL
;
2335 targetPrivate
->rlsContext
.retain
= NULL
;
2336 targetPrivate
->rlsContext
.release
= NULL
;
2337 targetPrivate
->rlsContext
.copyDescription
= NULL
;
2339 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
));
2340 if (context
->retain
!= NULL
) {
2341 targetPrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
2345 pthread_mutex_unlock(&targetPrivate
->lock
);
2352 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target
,
2353 CFRunLoopRef runLoop
,
2354 CFStringRef runLoopMode
)
2356 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2357 Boolean init
= FALSE
;
2360 if (!isA_SCNetworkReachability(target
) || runLoop
== NULL
|| runLoopMode
== NULL
) {
2361 _SCErrorSet(kSCStatusInvalidArgument
);
2365 /* schedule the SCNetworkReachability run loop source */
2367 pthread_mutex_lock(&hn_lock
);
2368 pthread_mutex_lock(&targetPrivate
->lock
);
2370 if (hn_store
== NULL
) {
2372 * if we are not monitoring any hosts, start watching
2374 if (!dns_configuration_watch()) {
2376 _SCErrorSet(kSCStatusFailed
);
2380 hn_store
= SCDynamicStoreCreate(NULL
,
2381 CFSTR("SCNetworkReachability"),
2382 __SCNetworkReachabilityReachabilityHandleChanges
,
2384 if (hn_store
== NULL
) {
2385 SCLog(_sc_verbose
, LOG_INFO
, CFSTR("SCDynamicStoreCreate() failed"));
2389 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
);
2391 hn_storeRLS
= SCDynamicStoreCreateRunLoopSource(NULL
, hn_store
, 0);
2392 hn_rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2393 hn_targets
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
2396 if (targetPrivate
->rls
== NULL
) {
2397 CFRunLoopSourceContext context
= { 0 // version
2398 , (void *)target
// info
2399 , CFRetain
// retain
2400 , CFRelease
// release
2401 , CFCopyDescription
// copyDescription
2406 , rlsPerform
// perform
2409 targetPrivate
->rls
= CFRunLoopSourceCreate(NULL
, 0, &context
);
2410 targetPrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2414 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2416 * if we do not already have host notifications scheduled with
2417 * this runLoop / runLoopMode
2419 CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2421 if (targetPrivate
->dnsRLS
!= NULL
) {
2422 /* if we have an active async DNS query too */
2423 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2427 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
);
2429 /* schedule the SCNetworkReachability run loop source */
2431 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2433 * if we do not already have SC notifications scheduled with
2434 * this runLoop / runLoopMode
2436 CFRunLoopAddSource(runLoop
, hn_storeRLS
, runLoopMode
);
2439 _SC_schedule(target
, runLoop
, runLoopMode
, hn_rlList
);
2440 CFSetAddValue(hn_targets
, target
);
2443 SCNetworkConnectionFlags flags
;
2445 SCDynamicStoreRef store
= NULL
;
2448 * if we have yet to schedule SC notifications for this address
2449 * - initialize current reachability status
2451 if (__SCNetworkReachabilityGetFlags(&store
, target
, &flags
, &if_index
, TRUE
)) {
2453 * if reachability status available
2455 * - schedule notification to report status via callback
2457 targetPrivate
->flags
= flags
;
2458 targetPrivate
->if_index
= if_index
;
2459 CFRunLoopSourceSignal(targetPrivate
->rls
);
2460 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
);
2462 /* if reachability status not available, async lookup started */
2463 targetPrivate
->flags
= 0;
2464 targetPrivate
->if_index
= 0;
2466 if (store
!= NULL
) CFRelease(store
);
2473 pthread_mutex_unlock(&targetPrivate
->lock
);
2474 pthread_mutex_unlock(&hn_lock
);
2480 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target
,
2481 CFRunLoopRef runLoop
,
2482 CFStringRef runLoopMode
)
2484 SCNetworkReachabilityPrivateRef targetPrivate
= (SCNetworkReachabilityPrivateRef
)target
;
2488 if (!isA_SCNetworkReachability(target
) || runLoop
== NULL
|| runLoopMode
== NULL
) {
2489 _SCErrorSet(kSCStatusInvalidArgument
);
2493 pthread_mutex_lock(&hn_lock
);
2494 pthread_mutex_lock(&targetPrivate
->lock
);
2496 if (targetPrivate
->rls
== NULL
) {
2497 /* if not currently scheduled */
2501 if (!_SC_unschedule(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) {
2502 /* if not currently scheduled */
2506 n
= CFArrayGetCount(targetPrivate
->rlList
);
2507 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) {
2509 * if this host is no longer scheduled for this runLoop / runLoopMode
2511 CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
);
2513 if (targetPrivate
->dnsRLS
!= NULL
) {
2514 /* if we have an active async DNS query too */
2515 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
);
2520 * if this host is no longer scheduled
2522 CFRunLoopSourceInvalidate(targetPrivate
->rls
); /* cleanup SCNetworkReachability resources */
2523 CFRelease(targetPrivate
->rls
);
2524 targetPrivate
->rls
= NULL
;
2525 CFRelease(targetPrivate
->rlList
);
2526 targetPrivate
->rlList
= NULL
;
2527 CFSetRemoveValue(hn_targets
, target
); /* cleanup notification resources */
2529 if (targetPrivate
->dnsPort
) {
2530 /* if we have an active async DNS query too */
2531 lu_async_call_cancel(CFMachPortGetPort(targetPrivate
->dnsPort
));
2532 CFRunLoopSourceInvalidate(targetPrivate
->dnsRLS
);
2533 CFRelease(targetPrivate
->dnsRLS
);
2534 targetPrivate
->dnsRLS
= NULL
;
2535 CFRelease(targetPrivate
->dnsPort
);
2536 targetPrivate
->dnsPort
= NULL
;
2541 (void)_SC_unschedule(target
, runLoop
, runLoopMode
, hn_rlList
, FALSE
);
2543 n
= CFArrayGetCount(hn_rlList
);
2544 if (n
== 0 || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) {
2546 * if we no longer have any addresses scheduled for
2547 * this runLoop / runLoopMode
2549 CFRunLoopRemoveSource(runLoop
, hn_storeRLS
, runLoopMode
);
2553 * if we are no longer monitoring any addresses
2555 CFRelease(hn_targets
);
2557 CFRelease(hn_rlList
);
2559 CFRunLoopSourceInvalidate(hn_storeRLS
);
2560 CFRelease(hn_storeRLS
);
2562 CFRelease(hn_store
);
2566 * until we start monitoring again, ensure that
2567 * any resources associated with tracking the
2568 * DNS configuration have been released.
2570 dns_configuration_unwatch();
2578 pthread_mutex_unlock(&targetPrivate
->lock
);
2579 pthread_mutex_unlock(&hn_lock
);