2  * Copyright (c) 2003-2009 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 <Availability.h> 
  35 #include <TargetConditionals.h> 
  36 #include <sys/cdefs.h> 
  38 #include <dispatch/dispatch.h> 
  39 #endif  // !TARGET_OS_IPHONE 
  40 #include <CoreFoundation/CoreFoundation.h> 
  41 #include <CoreFoundation/CFRuntime.h> 
  42 #include <SystemConfiguration/SystemConfiguration.h> 
  43 #include <SystemConfiguration/SCValidation.h> 
  44 #include <SystemConfiguration/SCPrivate.h> 
  46 #include <libkern/OSAtomic.h> 
  49 #include <IOKit/pwr_mgt/IOPMLibPrivate.h> 
  50 #endif  // !TARGET_OS_IPHONE 
  54 #include <netinet/in.h> 
  55 #include <arpa/inet.h> 
  57 #include <netdb_async.h> 
  60 #include <sys/ioctl.h> 
  61 #include <sys/socket.h> 
  63 #include <net/if_dl.h> 
  64 #include <net/if_types.h> 
  65 #define KERNEL_PRIVATE 
  66 #include <net/route.h> 
  70 #define s6_addr16 __u6_addr.__u6_addr16 
  73 #include <ppp/ppp_msg.h> 
  77 #define kSCNetworkReachabilityFlagsFirstResolvePending  (1<<31) 
  84         reachabilityTypeAddress
, 
  85         reachabilityTypeAddressPair
, 
  90 static CFStringRef      
__SCNetworkReachabilityCopyDescription  (CFTypeRef cf
); 
  91 static void             __SCNetworkReachabilityDeallocate       (CFTypeRef cf
); 
  92 static void             rlsPerform(void *info
); 
  96 __SCNetworkReachabilityScheduleWithRunLoop      (SCNetworkReachabilityRef       target
, 
  98                                                  CFStringRef                    runLoopMode
, 
 100                                                  dispatch_queue_t               queue
, 
 101 #else   // !TARGET_OS_IPHONE 
 103 #endif  // !TARGET_OS_IPHONE 
 106 __SCNetworkReachabilityUnscheduleFromRunLoop    (SCNetworkReachabilityRef       target
, 
 107                                                  CFRunLoopRef                   runLoop
, 
 108                                                  CFStringRef                    runLoopMode
, 
 113         SCNetworkReachabilityFlags      flags
; 
 121         /* base CFType information */ 
 122         CFRuntimeBase                   cfBase
; 
 125         pthread_mutex_t                 lock
; 
 130         /* target host name */ 
 133         struct addrinfo                 hints
; 
 135         CFArrayRef                      resolvedAddress
;        /* CFArray[CFData] */ 
 136         int                             resolvedAddressError
; 
 138         /* local & remote addresses */ 
 139         struct sockaddr                 
*localAddress
; 
 140         struct sockaddr                 
*remoteAddress
; 
 142         /* current reachability flags */ 
 143         ReachabilityInfo                info
; 
 144         ReachabilityInfo                last_notify
; 
 146         /* run loop source, callout, context, rl scheduling info */ 
 148         CFRunLoopSourceRef              rls
; 
 149         SCNetworkReachabilityCallBack   rlsFunction
; 
 150         SCNetworkReachabilityContext    rlsContext
; 
 151         CFMutableArrayRef               rlList
; 
 153 #if     !TARGET_OS_IPHONE 
 154         dispatch_queue_t                dispatchQueue
;          // SCNetworkReachabilitySetDispatchQueue 
 155         dispatch_queue_t                asyncDNSQueue
; 
 156         dispatch_source_t               asyncDNSSource
; 
 157 #endif  // !TARGET_OS_IPHONE 
 159         /* [async] DNS query info */ 
 162         CFMachPortRef                   dnsPort
; 
 163         CFRunLoopSourceRef              dnsRLS
; 
 164         struct timeval                  dnsQueryStart
; 
 167         Boolean                         onDemandBypass
; 
 168         CFStringRef                     onDemandName
; 
 169         CFStringRef                     onDemandRemoteAddress
; 
 170         SCNetworkReachabilityRef        onDemandServer
; 
 171         CFStringRef                     onDemandServiceID
; 
 176 } SCNetworkReachabilityPrivate
, *SCNetworkReachabilityPrivateRef
; 
 179 static CFTypeID __kSCNetworkReachabilityTypeID  
= _kCFRuntimeNotATypeID
; 
 182 static const CFRuntimeClass __SCNetworkReachabilityClass 
= { 
 184         "SCNetworkReachability",                // className 
 187         __SCNetworkReachabilityDeallocate
,      // dealloc 
 190         NULL
,                                   // copyFormattingDesc 
 191         __SCNetworkReachabilityCopyDescription  
// copyDebugDesc 
 195 static pthread_once_t           initialized     
= PTHREAD_ONCE_INIT
; 
 196 static ReachabilityInfo         NOT_REACHABLE   
= { 0,          0,      FALSE 
}; 
 197 static ReachabilityInfo         NOT_REPORTED    
= { 0xFFFFFFFF, 0,      FALSE 
}; 
 198 static int                      rtm_seq         
= 0; 
 201 #if     !TARGET_OS_IPHONE 
 203  * Power capabilities (sleep/wake) 
 205 static IOPMSystemPowerStateCapabilities power_capabilities      
= kIOPMSytemPowerStateCapabilitiesMask
; 
 206 #endif  // !TARGET_OS_IPHONE 
 210  * host "something has changed" notifications 
 213 static pthread_mutex_t          hn_lock         
= PTHREAD_MUTEX_INITIALIZER
; 
 214 static SCDynamicStoreRef        hn_store        
= NULL
; 
 215 #if     !TARGET_OS_IPHONE 
 216 static dispatch_queue_t         hn_dispatchQueue 
= NULL
; 
 217 #else   // !TARGET_OS_IPHONE 
 218 static CFRunLoopSourceRef       hn_storeRLS     
= NULL
; 
 219 static CFMutableArrayRef        hn_rlList       
= NULL
; 
 220 #endif  // !TARGET_OS_IPHONE 
 221 static CFMutableSetRef          hn_targets      
= NULL
; 
 229         dns_config_t    
*config
; 
 231 } dns_configuration_t
; 
 234 static pthread_mutex_t          dns_lock                
= PTHREAD_MUTEX_INITIALIZER
; 
 235 static dns_configuration_t      
*dns_configuration      
= NULL
; 
 236 static int                      dns_token
; 
 237 static Boolean                  dns_token_valid         
= FALSE
; 
 240 static __inline__ CFTypeRef
 
 241 isA_SCNetworkReachability(CFTypeRef obj
) 
 243         return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID())); 
 248 __log_query_time(SCNetworkReachabilityRef target
, Boolean found
, Boolean async
, struct timeval 
*start
) 
 250         struct timeval                  dnsQueryComplete
; 
 251         struct timeval                  dnsQueryElapsed
; 
 252         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
 258         if (start
->tv_sec 
== 0) { 
 262         (void) gettimeofday(&dnsQueryComplete
, NULL
); 
 263         timersub(&dnsQueryComplete
, start
, &dnsQueryElapsed
); 
 264         SCLog(TRUE
, LOG_INFO
, 
 265               CFSTR("%s%ssync DNS complete%s (query time = %d.%3.3d)"), 
 266               targetPrivate
->log_prefix
, 
 268               found 
? "" : ", host not found", 
 269               dnsQueryElapsed
.tv_sec
, 
 270               dnsQueryElapsed
.tv_usec 
/ 1000); 
 277 updatePPPStatus(SCDynamicStoreRef               
*storeP
, 
 278                 const struct sockaddr           
*sa
, 
 280                 SCNetworkReachabilityFlags      
*flags
, 
 281                 CFStringRef                     
*ppp_server
, 
 282                 const char                      *log_prefix
) 
 284         CFDictionaryRef         dict            
= NULL
; 
 287         const void *            keys_q
[N_QUICK
]; 
 288         const void **           keys            
= keys_q
; 
 291         int                     sc_status       
= kSCStatusReachabilityUnknown
; 
 292         SCDynamicStoreRef       store           
= *storeP
; 
 293         const void *            values_q
[N_QUICK
]; 
 294         const void **           values  
= values_q
; 
 296         switch (sa
->sa_family
) { 
 298                         entity 
= kSCEntNetIPv4
; 
 301                         entity 
= kSCEntNetIPv6
; 
 308                 store 
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
); 
 310                         SCLog(TRUE
, LOG_ERR
, CFSTR("updatePPPStatus SCDynamicStoreCreate() failed")); 
 316         // grab a snapshot of the PPP configuration from the dynamic store 
 319                 CFMutableArrayRef       patterns
; 
 321                 patterns 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 322                 pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 323                                                                       kSCDynamicStoreDomainState
, 
 326                 CFArrayAppendValue(patterns
, pattern
); 
 328                 pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 329                                                                       kSCDynamicStoreDomainSetup
, 
 332                 CFArrayAppendValue(patterns
, pattern
); 
 334                 pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 335                                                                       kSCDynamicStoreDomainState
, 
 338                 CFArrayAppendValue(patterns
, pattern
); 
 340                 dict 
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
); 
 344                 // if we could not access the dynamic store 
 348         sc_status 
= kSCStatusOK
; 
 350         // look for the service which matches the provided interface 
 351         n 
= CFDictionaryGetCount(dict
); 
 356         ppp_if 
= CFStringCreateWithCStringNoCopy(NULL
, 
 358                                                  kCFStringEncodingASCII
, 
 361         if (n 
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) { 
 362                 keys   
= CFAllocatorAllocate(NULL
, n 
* sizeof(CFTypeRef
), 0); 
 363                 values 
= CFAllocatorAllocate(NULL
, n 
* sizeof(CFTypeRef
), 0); 
 365         CFDictionaryGetKeysAndValues(dict
, keys
, values
); 
 367         for (i
=0; i 
< n
; i
++) { 
 368                 CFArrayRef      components
; 
 371                 CFDictionaryRef p_setup
; 
 372                 CFDictionaryRef p_state
; 
 375                 CFStringRef     service         
= NULL
; 
 376                 CFStringRef     s_key           
= (CFStringRef
)    keys
[i
]; 
 377                 CFDictionaryRef s_dict          
= (CFDictionaryRef
)values
[i
]; 
 380                 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) { 
 384                 if (!CFStringHasSuffix(s_key
, entity
)) { 
 385                         continue;       // if not an IPv4 or IPv6 entity 
 388                 s_if 
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
); 
 389                 if (!isA_CFString(s_if
)) { 
 390                         continue;       // if no interface 
 393                 if (!CFEqual(ppp_if
, s_if
)) { 
 394                         continue;       // if not this interface 
 397                 // extract the service ID, get the PPP "state" entity for 
 398                 // the "Status", and get the PPP "setup" entity for the 
 399                 // the "DialOnDemand" flag 
 400                 components 
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/")); 
 401                 if (CFArrayGetCount(components
) != 5) { 
 402                         CFRelease(components
); 
 405                 service 
= CFArrayGetValueAtIndex(components
, 3); 
 406                 key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 407                                                                   kSCDynamicStoreDomainState
, 
 410                 p_state 
= CFDictionaryGetValue(dict
, key
); 
 412                 key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 413                                                                   kSCDynamicStoreDomainSetup
, 
 416                 p_setup 
= CFDictionaryGetValue(dict
, key
); 
 418                 CFRelease(components
); 
 420                 // ensure that this is a PPP service 
 421                 if (!isA_CFDictionary(p_state
)) { 
 425                 *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
 428                 if (ppp_server 
!= NULL
) { 
 429                         *ppp_server 
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress")); 
 430                         *ppp_server 
= isA_CFString(*ppp_server
); 
 431                         if (*ppp_server 
!= NULL
) { 
 432                                 CFRetain(*ppp_server
); 
 437                 if (!CFDictionaryGetValueIfPresent(p_state
, 
 439                                                    (const void **)&num
) || 
 440                     !isA_CFNumber(num
) || 
 441                     !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) { 
 444                 switch (ppp_status
) { 
 446                                 // if we're really UP and RUNNING 
 449                                 // if we're effectively UP and RUNNING 
 452                         case PPP_STATERESERVED 
: 
 453                                 // if we're not connected at all 
 454                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  PPP link idle, dial-on-traffic to connect"), 
 456                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
 459                                 // if we're in the process of [dis]connecting 
 460                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  PPP link, connection in progress"), 
 462                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
 466                 // get PPP dial-on-traffic status 
 467                 if (isA_CFDictionary(p_setup
) && 
 468                     CFDictionaryGetValueIfPresent(p_setup
, 
 469                                                   kSCPropNetPPPDialOnDemand
, 
 470                                                   (const void **)&num
) && 
 472                     CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
) && 
 474                         *flags 
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
; 
 475                         if (ppp_status 
== PPP_IDLE
) { 
 476                                 *flags 
|= kSCNetworkReachabilityFlagsInterventionRequired
; 
 484         if (keys 
!= keys_q
) { 
 485                 CFAllocatorDeallocate(NULL
, keys
); 
 486                 CFAllocatorDeallocate(NULL
, values
); 
 491         if (dict 
!= NULL
)       CFRelease(dict
); 
 497 updatePPPAvailable(SCDynamicStoreRef            
*storeP
, 
 498                    const struct sockaddr        
*sa
, 
 499                    SCNetworkReachabilityFlags   
*flags
, 
 500                    const char                   *log_prefix
) 
 502         CFDictionaryRef         dict            
= NULL
; 
 505         const void *            keys_q
[N_QUICK
]; 
 506         const void **           keys            
= keys_q
; 
 508         int                     sc_status       
= kSCStatusReachabilityUnknown
; 
 509         SCDynamicStoreRef       store           
= *storeP
; 
 510         const void *            values_q
[N_QUICK
]; 
 511         const void **           values  
= values_q
; 
 514                 entity 
= kSCEntNetIPv4
; 
 516                 switch (sa
->sa_family
) { 
 518                                 entity 
= kSCEntNetIPv4
; 
 521                                 entity 
= kSCEntNetIPv6
; 
 529                 store 
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
); 
 531                         SCLog(TRUE
, LOG_ERR
, CFSTR("updatePPPAvailable SCDynamicStoreCreate() failed")); 
 537         // grab a snapshot of the PPP configuration from the dynamic store 
 540                 CFMutableArrayRef       patterns
; 
 542                 patterns 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 543                 pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 544                                                                       kSCDynamicStoreDomainSetup
, 
 547                 CFArrayAppendValue(patterns
, pattern
); 
 549                 pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 550                                                                       kSCDynamicStoreDomainSetup
, 
 553                 CFArrayAppendValue(patterns
, pattern
); 
 555                 pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 556                                                                       kSCDynamicStoreDomainSetup
, 
 559                 CFArrayAppendValue(patterns
, pattern
); 
 561                 dict 
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
); 
 565                 // if we could not access the dynamic store 
 569         sc_status 
= kSCStatusOK
; 
 571         // look for an available service which will provide connectivity 
 572         // for the requested address family. 
 573         n 
= CFDictionaryGetCount(dict
); 
 578         if (n 
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) { 
 579                 keys   
= CFAllocatorAllocate(NULL
, n 
* sizeof(CFTypeRef
), 0); 
 580                 values 
= CFAllocatorAllocate(NULL
, n 
* sizeof(CFTypeRef
), 0); 
 582         CFDictionaryGetKeysAndValues(dict
, keys
, values
); 
 584         for (i 
= 0; i 
< n
; i
++) { 
 585                 CFArrayRef      components
; 
 586                 Boolean         found           
= FALSE
; 
 588                 CFDictionaryRef i_dict
; 
 590                 CFDictionaryRef p_dict
; 
 592                 CFStringRef     s_key           
= (CFStringRef
)    keys
[i
]; 
 593                 CFDictionaryRef s_dict          
= (CFDictionaryRef
)values
[i
]; 
 595                 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) { 
 599                 if (!CFStringHasSuffix(s_key
, entity
)) { 
 600                         continue;       // if not an IPv4 or IPv6 entity 
 603                 // extract service ID 
 604                 components 
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/")); 
 605                 if (CFArrayGetCount(components
) != 5) { 
 606                         CFRelease(components
); 
 609                 service 
= CFArrayGetValueAtIndex(components
, 3); 
 611                 // check for [non-VPN] PPP entity 
 612                 p_key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 613                                                                     kSCDynamicStoreDomainSetup
, 
 616                 p_dict 
= CFDictionaryGetValue(dict
, p_key
); 
 619                 i_key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 620                                                                     kSCDynamicStoreDomainSetup
, 
 623                 i_dict 
= CFDictionaryGetValue(dict
, i_key
); 
 626                 if (isA_CFDictionary(p_dict
) && 
 627                     isA_CFDictionary(i_dict
) && 
 628                     CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) { 
 631                         // we have a PPP service for this address family 
 634                         *flags 
|= kSCNetworkReachabilityFlagsReachable
; 
 635                         *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
 636                         *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
 638                         // get PPP dial-on-traffic status 
 639                         num 
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
); 
 640                         if (isA_CFNumber(num
)) { 
 643                                 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) { 
 645                                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
; 
 651                                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  status    = isReachable (after connect)"), 
 653                                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  service   = %@"), 
 660                 CFRelease(components
); 
 667         if (keys 
!= keys_q
) { 
 668                 CFAllocatorDeallocate(NULL
, keys
); 
 669                 CFAllocatorDeallocate(NULL
, values
); 
 674         if (dict 
!= NULL
)       CFRelease(dict
); 
 680 updateIPSecStatus(SCDynamicStoreRef             
*storeP
, 
 681                   const struct sockaddr         
*sa
, 
 683                   SCNetworkReachabilityFlags    
*flags
, 
 684                   CFStringRef                   
*ipsec_server
) 
 686         CFDictionaryRef         dict            
= NULL
; 
 689         CFStringRef             ipsec_if
; 
 690         const void *            keys_q
[N_QUICK
]; 
 691         const void **           keys            
= keys_q
; 
 693         int                     sc_status       
= kSCStatusReachabilityUnknown
; 
 694         SCDynamicStoreRef       store           
= *storeP
; 
 695         const void *            values_q
[N_QUICK
]; 
 696         const void **           values  
= values_q
; 
 698         switch (sa
->sa_family
) { 
 700                         entity 
= kSCEntNetIPv4
; 
 703                         entity 
= kSCEntNetIPv6
; 
 710                 store 
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
); 
 712                         SCLog(TRUE
, LOG_ERR
, CFSTR("updateIPSecStatus SCDynamicStoreCreate() failed")); 
 718         // grab a snapshot of the IPSec configuration from the dynamic store 
 721                 CFMutableArrayRef       patterns
; 
 723                 patterns 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 724                 pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 725                                                                       kSCDynamicStoreDomainState
, 
 728                 CFArrayAppendValue(patterns
, pattern
); 
 730                 pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 731                                                                       kSCDynamicStoreDomainSetup
, 
 734                 CFArrayAppendValue(patterns
, pattern
); 
 736                 dict 
= SCDynamicStoreCopyMultiple(store
, NULL
, patterns
); 
 740                 // if we could not access the dynamic store 
 744         sc_status 
= kSCStatusOK
; 
 746         // look for the service which matches the provided interface 
 747         n 
= CFDictionaryGetCount(dict
); 
 752         ipsec_if 
= CFStringCreateWithCStringNoCopy(NULL
, 
 754                                                    kCFStringEncodingASCII
, 
 757         if (n 
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) { 
 758                 keys   
= CFAllocatorAllocate(NULL
, n 
* sizeof(CFTypeRef
), 0); 
 759                 values 
= CFAllocatorAllocate(NULL
, n 
* sizeof(CFTypeRef
), 0); 
 761         CFDictionaryGetKeysAndValues(dict
, keys
, values
); 
 763         for (i
=0; i 
< n
; i
++) { 
 764                 CFArrayRef      components
; 
 766                 CFDictionaryRef i_setup
; 
 767                 CFStringRef     service         
= NULL
; 
 768                 CFStringRef     s_key           
= (CFStringRef
)    keys
[i
]; 
 769                 CFDictionaryRef s_dict          
= (CFDictionaryRef
)values
[i
]; 
 772                 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) { 
 776                 if (!CFStringHasSuffix(s_key
, entity
)) { 
 777                         continue;       // if not an IPv4 or IPv6 entity 
 780                 s_if 
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
); 
 781                 if (!isA_CFString(s_if
)) { 
 782                         continue;       // if no interface 
 785                 if (!CFEqual(ipsec_if
, s_if
)) { 
 786                         continue;       // if not this interface 
 789                 // extract the service ID and get the IPSec "setup" entity 
 790                 // to confirm that we're looking at what we're expecting 
 791                 components 
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/")); 
 792                 if (CFArrayGetCount(components
) != 5) { 
 793                         CFRelease(components
); 
 796                 service 
= CFArrayGetValueAtIndex(components
, 3); 
 797                 key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 798                                                                   kSCDynamicStoreDomainSetup
, 
 801                 i_setup 
= CFDictionaryGetValue(dict
, key
); 
 803                 CFRelease(components
); 
 805                 // ensure that this is an IPSec service 
 806                 if (!isA_CFDictionary(i_setup
)) { 
 810                 *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
 813                 if (ipsec_server 
!= NULL
) { 
 814                         *ipsec_server 
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress")); 
 815                         *ipsec_server 
= isA_CFString(*ipsec_server
); 
 816                         if (*ipsec_server 
!= NULL
) { 
 817                                 CFRetain(*ipsec_server
); 
 825         if (keys 
!= keys_q
) { 
 826                 CFAllocatorDeallocate(NULL
, keys
); 
 827                 CFAllocatorDeallocate(NULL
, values
); 
 832         if (dict 
!= NULL
)       CFRelease(dict
); 
 839 #define ROUNDUP(a, size) \ 
 840         (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) 
 842 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \ 
 843         ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ 
 848 get_rtaddrs(int addrs
, struct sockaddr 
*sa
, struct sockaddr 
**rti_info
) 
 852         for (i 
= 0; i 
< RTAX_MAX
; i
++) { 
 853                 if (addrs 
& (1 << i
)) { 
 862 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */ 
 868         struct sockaddr         
*rti_info
[RTAX_MAX
]; 
 869         struct rt_msghdr        
*rtm
; 
 870         struct sockaddr_dl      
*sdl
; 
 871 } route_info
, *route_info_p
; 
 876  *      returns zero if route exists an data returned, EHOSTUNREACH 
 877  *      if no route, or errno for any other error. 
 880 route_get(const struct sockaddr 
*address
, 
 884         pid_t                   pid             
= getpid(); 
 887         int32_t                 seq             
= OSAtomicIncrement32Barrier(&rtm_seq
); 
 888 #ifndef RTM_GET_SILENT 
 889 #warning Note: Using RTM_GET (and not RTM_GET_SILENT) 
 890         static pthread_mutex_t  lock            
= PTHREAD_MUTEX_INITIALIZER
; 
 891         int                     sosize          
= 48 * 1024; 
 894         bzero(info
, sizeof(*info
)); 
 896         info
->rtm 
= (struct rt_msghdr 
*)&info
->buf
; 
 897         info
->rtm
->rtm_msglen  
= sizeof(struct rt_msghdr
); 
 898         info
->rtm
->rtm_version 
= RTM_VERSION
; 
 899 #ifdef  RTM_GET_SILENT 
 900         info
->rtm
->rtm_type    
= RTM_GET_SILENT
; 
 902         info
->rtm
->rtm_type    
= RTM_GET
; 
 904         info
->rtm
->rtm_flags   
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
; 
 905         info
->rtm
->rtm_addrs   
= RTA_DST
|RTA_IFP
; /* Both destination and device */ 
 906         info
->rtm
->rtm_pid     
= pid
; 
 907         info
->rtm
->rtm_seq     
= seq
; 
 909         switch (address
->sa_family
) { 
 911                         struct sockaddr_in6     
*sin6
; 
 913                         sin6 
= (struct sockaddr_in6 
*)address
; 
 914                         if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) || 
 915                              IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) && 
 916                             (sin6
->sin6_scope_id 
!= 0)) { 
 917                                 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
); 
 918                                 sin6
->sin6_scope_id 
= 0; 
 924         sa  
= (struct sockaddr 
*) (info
->rtm 
+ 1); 
 925         bcopy(address
, sa
, address
->sa_len
); 
 926         n 
= ROUNDUP(sa
->sa_len
, sizeof(uint32_t)); 
 927         info
->rtm
->rtm_msglen 
+= n
; 
 929         info
->sdl 
= (struct sockaddr_dl 
*) ((void *)sa 
+ n
); 
 930         info
->sdl
->sdl_family 
= AF_LINK
; 
 931         info
->sdl
->sdl_len 
= sizeof (struct sockaddr_dl
); 
 932         n 
= ROUNDUP(info
->sdl
->sdl_len
, sizeof(uint32_t)); 
 933         info
->rtm
->rtm_msglen 
+= n
; 
 935 #ifndef RTM_GET_SILENT 
 936         pthread_mutex_lock(&lock
); 
 938         rsock 
= socket(PF_ROUTE
, SOCK_RAW
, 0); 
 942 #ifndef RTM_GET_SILENT 
 943                 pthread_mutex_unlock(&lock
); 
 945                 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error
)); 
 949 #ifndef RTM_GET_SILENT 
 950         if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) { 
 954                 pthread_mutex_unlock(&lock
); 
 955                 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error
)); 
 960         if (write(rsock
, &info
->buf
, info
->rtm
->rtm_msglen
) == -1) { 
 964 #ifndef RTM_GET_SILENT 
 965                 pthread_mutex_unlock(&lock
); 
 967                 if (error 
!= ESRCH
) { 
 968                         SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(error
)); 
 975          * Type, seq, pid identify our response. 
 976          * Routing sockets are broadcasters on input. 
 981                 n 
= read(rsock
, (void *)&info
->buf
, sizeof(info
->buf
)); 
 983                         if (errno 
!= EINTR
) { 
 987 #ifndef RTM_GET_SILENT 
 988                                 pthread_mutex_unlock(&lock
); 
 990                                 SCLog(TRUE
, LOG_ERR
, CFSTR("read() failed: %s"), strerror(error
)); 
 994         } while ((info
->rtm
->rtm_type 
!= RTM_GET
)       || 
 995                  (info
->rtm
->rtm_seq  
!= seq
)           || 
 996                  (info
->rtm
->rtm_pid  
!= pid
)); 
 999 #ifndef RTM_GET_SILENT 
1000         pthread_mutex_unlock(&lock
); 
1003         get_rtaddrs(info
->rtm
->rtm_addrs
, sa
, info
->rti_info
); 
1009                 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), info
->rtm
->rtm_flags
); 
1011                 if ((info
->rti_info
[RTAX_NETMASK
] != NULL
) && (info
->rti_info
[RTAX_DST
] != NULL
)) { 
1012                         info
->rti_info
[RTAX_NETMASK
]->sa_family 
= info
->rti_info
[RTAX_DST
]->sa_family
; 
1015                 for (i 
= 0; i 
< RTAX_MAX
; i
++) { 
1016                         if (info
->rti_info
[i
] != NULL
) { 
1019                                 _SC_sockaddr_to_string(info
->rti_info
[i
], addr
, sizeof(addr
)); 
1020                                 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, addr
); 
1024 #endif  /* LOG_RTADDRS */ 
1026         if ((info
->rti_info
[RTAX_IFP
] == NULL
) || 
1027             (info
->rti_info
[RTAX_IFP
]->sa_family 
!= AF_LINK
)) { 
1028                 /* no interface info */ 
1029                 SCLog(TRUE
, LOG_DEBUG
, CFSTR("route_get() no interface info")); 
1033         info
->sdl 
= (struct sockaddr_dl 
*) info
->rti_info
[RTAX_IFP
]; 
1034         if ((info
->sdl
->sdl_nlen 
== 0) || (info
->sdl
->sdl_nlen 
> IFNAMSIZ
)) { 
1035                 /* no interface name */ 
1036                 return EHOSTUNREACH
; 
1044 checkAddress(SCDynamicStoreRef          
*storeP
, 
1045              const struct sockaddr      
*address
, 
1046              ReachabilityInfo           
*reach_info
, 
1047              const char                 *log_prefix
) 
1051         char                    if_name
[IFNAMSIZ 
+ 1]; 
1054         int                     sc_status       
= kSCStatusReachabilityUnknown
; 
1055         CFStringRef             server          
= NULL
; 
1056         char                    *statusMessage  
= NULL
; 
1057         struct sockaddr_in      v4mapped
; 
1059         *reach_info 
= NOT_REACHABLE
; 
1061         if (address 
== NULL
) { 
1062                 /* special case: check only for available paths off the system */ 
1063                 goto checkAvailable
; 
1066         switch (address
->sa_family
) { 
1072                                 _SC_sockaddr_to_string(address
, addr
, sizeof(addr
)); 
1073                                 SCLog(TRUE
, LOG_INFO
, CFSTR("%scheckAddress(%s)"), 
1080                          * if no code for this address family (yet) 
1082                         SCLog(TRUE
, LOG_INFO
, 
1083                               CFSTR("checkAddress(): unexpected address family %d"), 
1084                               address
->sa_family
); 
1085                         sc_status 
= kSCStatusInvalidArgument
; 
1089         if (address
->sa_family 
== AF_INET6
) { 
1090                 struct sockaddr_in6     
*sin6   
= (struct sockaddr_in6 
*)address
; 
1092                 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) { 
1093                         bzero(&v4mapped
, sizeof(v4mapped
)); 
1094                         v4mapped
.sin_len         
= sizeof(v4mapped
); 
1095                         v4mapped
.sin_family      
= AF_INET
; 
1096                         v4mapped
.sin_port        
= sin6
->sin6_port
; 
1097                         v4mapped
.sin_addr
.s_addr 
= sin6
->sin6_addr
.__u6_addr
.__u6_addr32
[3]; 
1098                         address 
= (struct sockaddr 
*)&v4mapped
; 
1102         ret 
= route_get(address
, &info
); 
1108                         goto checkAvailable
; 
1115         /* get the interface flags */ 
1117         isock 
= socket(AF_INET
, SOCK_DGRAM
, 0); 
1119                 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
)); 
1123         bzero(&ifr
, sizeof(ifr
)); 
1124         bcopy(info
.sdl
->sdl_data
, ifr
.ifr_name
, info
.sdl
->sdl_nlen
); 
1126         if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) == -1) { 
1127                 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl() failed: %s"), strerror(errno
)); 
1131         if (!(ifr
.ifr_flags 
& IFF_UP
)) { 
1132                 goto checkAvailable
; 
1135         statusMessage 
= "isReachable"; 
1136         reach_info
->flags 
|= kSCNetworkReachabilityFlagsReachable
; 
1138         if (info
.rtm
->rtm_flags 
& RTF_LOCAL
) { 
1139                 statusMessage 
= "isReachable (is a local address)"; 
1140                 reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1141         } else if (ifr
.ifr_flags 
& IFF_LOOPBACK
) { 
1142                 statusMessage 
= "isReachable (is loopback network)"; 
1143                 reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1144         } else if ((info
.rti_info
[RTAX_IFA
] != NULL
) && 
1145                    (info
.rti_info
[RTAX_IFA
]->sa_family 
!= AF_LINK
)) { 
1146                 void    *addr1  
= (void *)address
; 
1147                 void    *addr2  
= (void *)info
.rti_info
[RTAX_IFA
]; 
1148                 size_t  len     
= address
->sa_len
; 
1150                 if ((address
->sa_family 
!= info
.rti_info
[RTAX_IFA
]->sa_family
) && 
1151                     (address
->sa_len    
!= info
.rti_info
[RTAX_IFA
]->sa_len
)) { 
1152                         SCLog(TRUE
, LOG_NOTICE
, 
1153                               CFSTR("address family/length mismatch: %d/%d != %d/%d"), 
1156                               info
.rti_info
[RTAX_IFA
]->sa_family
, 
1157                               info
.rti_info
[RTAX_IFA
]->sa_len
); 
1161                 switch (address
->sa_family
) { 
1163                                 addr1 
= &((struct sockaddr_in 
*)address
)->sin_addr
; 
1164                                 addr2 
= &((struct sockaddr_in 
*)info
.rti_info
[RTAX_IFA
])->sin_addr
; 
1165                                 len 
= sizeof(struct in_addr
); 
1170                                 if (((struct sockaddr_in 
*)address
)->sin_addr
.s_addr 
== 0) { 
1171                                         statusMessage 
= "isReachable (this host)"; 
1172                                         reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1176                                 addr1 
= &((struct sockaddr_in6 
*)address
)->sin6_addr
; 
1177                                 addr2 
= &((struct sockaddr_in6 
*)info
.rti_info
[RTAX_IFA
])->sin6_addr
; 
1178                                 len 
= sizeof(struct in6_addr
); 
1184                 if (bcmp(addr1
, addr2
, len
) == 0) { 
1185                         statusMessage 
= "isReachable (is interface address)"; 
1186                         reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1190         if (!(info
.rtm
->rtm_flags 
& RTF_GATEWAY
) && 
1191             (info
.rti_info
[RTAX_GATEWAY
] != NULL
) && 
1192             (info
.rti_info
[RTAX_GATEWAY
]->sa_family 
== AF_LINK
) && 
1193             !(ifr
.ifr_flags 
& IFF_POINTOPOINT
)) { 
1194                 reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsDirect
; 
1197         bzero(&if_name
, sizeof(if_name
)); 
1198         bcopy(info
.sdl
->sdl_data
, 
1200               (info
.sdl
->sdl_nlen 
<= IFNAMSIZ
) ? info
.sdl
->sdl_nlen 
: IFNAMSIZ
); 
1202         reach_info
->if_index 
= info
.sdl
->sdl_index
; 
1205                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  status    = %s"), log_prefix
, statusMessage
); 
1206                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  device    = %s (%hu)"), log_prefix
, if_name
, info
.sdl
->sdl_index
); 
1207                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  sdl_type  = 0x%x"), log_prefix
, info
.sdl
->sdl_type
); 
1208                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  ifr_flags = 0x%04hx"), log_prefix
, ifr
.ifr_flags
); 
1209                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  rtm_flags = 0x%08x"), log_prefix
, info
.rtm
->rtm_flags
); 
1212         sc_status 
= kSCStatusOK
; 
1214         if (ifr
.ifr_flags 
& IFF_POINTOPOINT
) { 
1215                 reach_info
->flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
1218         if (info
.sdl
->sdl_type 
== IFT_PPP
) { 
1220                  * 1. check if PPP service 
1221                  * 2. check for dial-on-demand PPP link that is not yet connected 
1222                  * 3. get PPP server address 
1224                 sc_status 
= updatePPPStatus(storeP
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
); 
1225         } else if (info
.sdl
->sdl_type 
== IFT_OTHER
) { 
1227                  * 1. check if IPSec service 
1228                  * 2. get IPSec server address 
1230                 sc_status 
= updateIPSecStatus(storeP
, address
, if_name
, &reach_info
->flags
, &server
); 
1239         sc_status 
= updatePPPAvailable(storeP
, address
, &reach_info
->flags
, log_prefix
); 
1243         if (reach_info
->flags 
== 0) { 
1244                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  cannot be reached"), log_prefix
); 
1247         if (isock 
!= -1)        (void)close(isock
); 
1248         if (server 
!= NULL
)     CFRelease(server
); 
1249         if (sc_status 
!= kSCStatusOK
) { 
1250                 _SCErrorSet(sc_status
); 
1259 #pragma mark SCNetworkReachability APIs 
1263 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
) 
1265         CFAllocatorRef                  allocator       
= CFGetAllocator(cf
); 
1266         CFMutableStringRef              result
; 
1267         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)cf
; 
1269         result 
= CFStringCreateMutable(allocator
, 0); 
1270         CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
); 
1271         switch (targetPrivate
->type
) { 
1272                 case reachabilityTypeAddress 
: 
1273                 case reachabilityTypeAddressPair 
: { 
1276                         if (targetPrivate
->localAddress 
!= NULL
) { 
1277                                 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
)); 
1278                                 CFStringAppendFormat(result
, NULL
, CFSTR("local address = %s"), 
1282                         if (targetPrivate
->remoteAddress 
!= NULL
) { 
1283                                 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
)); 
1284                                 CFStringAppendFormat(result
, NULL
, CFSTR("%s%saddress = %s"), 
1285                                                      targetPrivate
->localAddress 
? ", " : "", 
1286                                                      (targetPrivate
->type 
== reachabilityTypeAddressPair
) ? "remote " : "", 
1291                 case reachabilityTypeName 
: { 
1292                         if ((targetPrivate
->name 
!= NULL
)) { 
1293                                 CFStringAppendFormat(result
, NULL
, CFSTR("name = %s"), targetPrivate
->name
); 
1295                         if ((targetPrivate
->serv 
!= NULL
)) { 
1296                                 CFStringAppendFormat(result
, NULL
, CFSTR("%sserv = %s"), 
1297                                                      targetPrivate
->name 
!= NULL 
? ", " : "", 
1298                                                      targetPrivate
->serv
); 
1300                         if ((targetPrivate
->resolvedAddress 
!= NULL
) || (targetPrivate
->resolvedAddressError 
!= NETDB_SUCCESS
)) { 
1301                                 if (targetPrivate
->resolvedAddress 
!= NULL
) { 
1302                                         if (isA_CFArray(targetPrivate
->resolvedAddress
)) { 
1304                                                 CFIndex n       
= CFArrayGetCount(targetPrivate
->resolvedAddress
); 
1306                                                 CFStringAppendFormat(result
, NULL
, CFSTR(" (")); 
1307                                                 for (i 
= 0; i 
< n
; i
++) { 
1310                                                         struct sockaddr 
*sa
; 
1312                                                         address 
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
); 
1313                                                         sa      
= (struct sockaddr 
*)CFDataGetBytePtr(address
); 
1314                                                         _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
)); 
1315                                                         CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"), 
1319                                                 CFStringAppendFormat(result
, NULL
, CFSTR(")")); 
1321                                                 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)")); 
1324                                         CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"), 
1325                                                              gai_strerror(targetPrivate
->resolvedAddressError
)); 
1327                         } else if (targetPrivate
->dnsPort 
!= NULL
) { 
1328                                 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)")); 
1333         if (targetPrivate
->scheduled
) { 
1334                 CFStringAppendFormat(result
, 
1336                                      CFSTR(", flags = 0x%08x, if_index = %hu"), 
1337                                      targetPrivate
->info
.flags
, 
1338                                      targetPrivate
->info
.if_index
); 
1340         CFStringAppendFormat(result
, NULL
, CFSTR("}")); 
1347 __SCNetworkReachabilityDeallocate(CFTypeRef cf
) 
1349         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)cf
; 
1351         /* release resources */ 
1353         pthread_mutex_destroy(&targetPrivate
->lock
); 
1355         if (targetPrivate
->name 
!= NULL
) 
1356                 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
); 
1358         if (targetPrivate
->serv 
!= NULL
) 
1359                 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->serv
); 
1361         if (targetPrivate
->resolvedAddress 
!= NULL
) 
1362                 CFRelease(targetPrivate
->resolvedAddress
); 
1364         if (targetPrivate
->localAddress 
!= NULL
) 
1365                 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
); 
1367         if (targetPrivate
->remoteAddress 
!= NULL
) 
1368                 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
); 
1370         if (targetPrivate
->rlsContext
.release 
!= NULL
) { 
1371                 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
); 
1374         if (targetPrivate
->onDemandName 
!= NULL
) { 
1375                 CFRelease(targetPrivate
->onDemandName
); 
1378         if (targetPrivate
->onDemandRemoteAddress 
!= NULL
) { 
1379                 CFRelease(targetPrivate
->onDemandRemoteAddress
); 
1382         if (targetPrivate
->onDemandServer 
!= NULL
) { 
1383                 CFRelease(targetPrivate
->onDemandServer
); 
1386         if (targetPrivate
->onDemandServiceID 
!= NULL
) { 
1387                 CFRelease(targetPrivate
->onDemandServiceID
); 
1395 __SCNetworkReachabilityInitialize(void) 
1397         __kSCNetworkReachabilityTypeID 
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
); 
1403 __SCNetworkReachabilityPerform(SCNetworkReachabilityRef target
) 
1405         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1407 #if     !TARGET_OS_IPHONE 
1408         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
1410                 dispatch_async(targetPrivate
->dispatchQueue
, 
1412                                        rlsPerform((void *)target
); 
1416 #endif  // !TARGET_OS_IPHONE 
1417         if (targetPrivate
->rls 
!= NULL
) { 
1418                 CFRunLoopSourceSignal(targetPrivate
->rls
); 
1419                 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
); 
1425 static SCNetworkReachabilityPrivateRef
 
1426 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef     allocator
) 
1428         SCNetworkReachabilityPrivateRef         targetPrivate
; 
1431         /* initialize runtime */ 
1432         pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); 
1434         /* allocate target */ 
1435         size          
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
); 
1436         targetPrivate 
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
, 
1437                                                                                   __kSCNetworkReachabilityTypeID
, 
1440         if (targetPrivate 
== NULL
) { 
1444         pthread_mutex_init(&targetPrivate
->lock
, NULL
); 
1446         targetPrivate
->name                             
= NULL
; 
1447         targetPrivate
->serv                             
= NULL
; 
1448         bzero(&targetPrivate
->hints
, sizeof(targetPrivate
->hints
)); 
1449         targetPrivate
->hints
.ai_flags 
= AI_ADDRCONFIG
; 
1451         targetPrivate
->hints
.ai_flags 
|= AI_PARALLEL
; 
1452 #endif  /* AI_PARALLEL */ 
1454         targetPrivate
->needResolve                      
= FALSE
; 
1455         targetPrivate
->resolvedAddress                  
= NULL
; 
1456         targetPrivate
->resolvedAddressError             
= NETDB_SUCCESS
; 
1458         targetPrivate
->localAddress                     
= NULL
; 
1459         targetPrivate
->remoteAddress                    
= NULL
; 
1461         targetPrivate
->info                             
= NOT_REACHABLE
; 
1462         targetPrivate
->last_notify                      
= NOT_REPORTED
; 
1464         targetPrivate
->scheduled                        
= FALSE
; 
1465         targetPrivate
->rls                              
= NULL
; 
1466         targetPrivate
->rlsFunction                      
= NULL
; 
1467         targetPrivate
->rlsContext
.info                  
= NULL
; 
1468         targetPrivate
->rlsContext
.retain                
= NULL
; 
1469         targetPrivate
->rlsContext
.release               
= NULL
; 
1470         targetPrivate
->rlsContext
.copyDescription       
= NULL
; 
1471         targetPrivate
->rlList                           
= NULL
; 
1473         targetPrivate
->haveDNS                          
= FALSE
; 
1474         targetPrivate
->dnsMP                            
= MACH_PORT_NULL
; 
1475         targetPrivate
->dnsPort                          
= NULL
; 
1476         targetPrivate
->dnsRLS                           
= NULL
; 
1478         targetPrivate
->onDemandBypass                   
= FALSE
; 
1479         targetPrivate
->onDemandName                     
= NULL
; 
1480         targetPrivate
->onDemandRemoteAddress            
= NULL
; 
1481         targetPrivate
->onDemandServer                   
= NULL
; 
1482         targetPrivate
->onDemandServiceID                
= NULL
; 
1484         targetPrivate
->log_prefix
[0] = '\0'; 
1486                 snprintf(targetPrivate
->log_prefix
, 
1487                          sizeof(targetPrivate
->log_prefix
), 
1492         return targetPrivate
; 
1496 SCNetworkReachabilityRef
 
1497 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef           allocator
, 
1498                                        const struct sockaddr    
*address
) 
1500         SCNetworkReachabilityPrivateRef targetPrivate
; 
1502         if ((address 
== NULL
) || 
1503             (address
->sa_len 
== 0) || 
1504             (address
->sa_len 
> sizeof(struct sockaddr_storage
))) { 
1505                 _SCErrorSet(kSCStatusInvalidArgument
); 
1509         targetPrivate 
= __SCNetworkReachabilityCreatePrivate(allocator
); 
1510         if (targetPrivate 
== NULL
) { 
1514         targetPrivate
->type 
= reachabilityTypeAddress
; 
1515         targetPrivate
->remoteAddress 
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0); 
1516         bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
); 
1518         return (SCNetworkReachabilityRef
)targetPrivate
; 
1522 SCNetworkReachabilityRef
 
1523 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef               allocator
, 
1524                                            const struct sockaddr        
*localAddress
, 
1525                                            const struct sockaddr        
*remoteAddress
) 
1527         SCNetworkReachabilityPrivateRef targetPrivate
; 
1529         if ((localAddress 
== NULL
) && (remoteAddress 
== NULL
)) { 
1530                 _SCErrorSet(kSCStatusInvalidArgument
); 
1534         if (localAddress 
!= NULL
) { 
1535                 if ((localAddress
->sa_len 
== 0) || 
1536                     (localAddress
->sa_len 
> sizeof(struct sockaddr_storage
))) { 
1537                             _SCErrorSet(kSCStatusInvalidArgument
); 
1542         if (remoteAddress 
!= NULL
) { 
1543                 if ((remoteAddress
->sa_len 
== 0) || 
1544                     (remoteAddress
->sa_len 
> sizeof(struct sockaddr_storage
))) { 
1545                             _SCErrorSet(kSCStatusInvalidArgument
); 
1550         targetPrivate 
= __SCNetworkReachabilityCreatePrivate(allocator
); 
1551         if (targetPrivate 
== NULL
) { 
1555         targetPrivate
->type 
= reachabilityTypeAddressPair
; 
1557         if (localAddress 
!= NULL
) { 
1558                 targetPrivate
->localAddress 
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0); 
1559                 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
); 
1562         if (remoteAddress 
!= NULL
) { 
1563                 targetPrivate
->remoteAddress 
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0); 
1564                 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
); 
1567         return (SCNetworkReachabilityRef
)targetPrivate
; 
1571 SCNetworkReachabilityRef
 
1572 SCNetworkReachabilityCreateWithName(CFAllocatorRef      allocator
, 
1573                                     const char          *nodename
) 
1576         struct sockaddr_in              sin
; 
1577         struct sockaddr_in6             sin6
; 
1578         SCNetworkReachabilityPrivateRef targetPrivate
; 
1580         if (nodename 
== NULL
) { 
1581                 _SCErrorSet(kSCStatusInvalidArgument
); 
1585         nodenameLen 
= strlen(nodename
); 
1586         if (nodenameLen 
== 0) { 
1587                 _SCErrorSet(kSCStatusInvalidArgument
); 
1591         /* check if this "nodename" is really an IP[v6] address in disguise */ 
1593         bzero(&sin
, sizeof(sin
)); 
1594         sin
.sin_len    
= sizeof(sin
); 
1595         sin
.sin_family 
= AF_INET
; 
1596         if (inet_aton(nodename
, &sin
.sin_addr
) == 1) { 
1597                 /* if IPv4 address */ 
1598                 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr 
*)&sin
); 
1601         bzero(&sin6
, sizeof(sin6
)); 
1602         sin6
.sin6_len    
= sizeof(sin6
); 
1603         sin6
.sin6_family 
= AF_INET6
; 
1604         if (inet_pton(AF_INET6
, nodename
, &sin6
.sin6_addr
) == 1) { 
1605                 /* if IPv6 address */ 
1608                 p 
= strchr(nodename
, '%'); 
1610                         sin6
.sin6_scope_id 
= if_nametoindex(p 
+ 1); 
1613                 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr 
*)&sin6
); 
1616         targetPrivate 
= __SCNetworkReachabilityCreatePrivate(allocator
); 
1617         if (targetPrivate 
== NULL
) { 
1621         targetPrivate
->type 
= reachabilityTypeName
; 
1623         targetPrivate
->name 
= CFAllocatorAllocate(NULL
, nodenameLen 
+ 1, 0); 
1624         strlcpy((char *)targetPrivate
->name
, nodename
, nodenameLen 
+ 1); 
1626         targetPrivate
->needResolve 
= TRUE
; 
1627         targetPrivate
->info
.flags 
|= kSCNetworkReachabilityFlagsFirstResolvePending
; 
1629         return (SCNetworkReachabilityRef
)targetPrivate
; 
1633 SCNetworkReachabilityRef
 
1634 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef   allocator
, 
1635                                        CFDictionaryRef  options
) 
1637         CFBooleanRef                    bypass
; 
1639         struct addrinfo                 
*hints  
= NULL
; 
1641         CFStringRef                     nodename
; 
1642         CFStringRef                     servname
; 
1643         SCNetworkReachabilityPrivateRef targetPrivate
; 
1645         if (!isA_CFDictionary(options
)) { 
1646                 _SCErrorSet(kSCStatusInvalidArgument
); 
1650         nodename 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
); 
1651         if ((nodename 
!= NULL
) && 
1652             (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) { 
1653                 _SCErrorSet(kSCStatusInvalidArgument
); 
1656         servname 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServName
); 
1657         if ((servname 
!= NULL
) && 
1658             (!isA_CFString(servname
) || (CFStringGetLength(servname
) == 0))) { 
1659                 _SCErrorSet(kSCStatusInvalidArgument
); 
1662         data 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionHints
); 
1664                 if (!isA_CFData(data
) || (CFDataGetLength(data
) != sizeof(targetPrivate
->hints
))) { 
1665                         _SCErrorSet(kSCStatusInvalidArgument
); 
1669                 hints 
= (struct addrinfo 
*)CFDataGetBytePtr(data
); 
1670                 if ((hints
->ai_addrlen   
!= 0)    || 
1671                     (hints
->ai_addr      
!= NULL
) || 
1672                     (hints
->ai_canonname 
!= NULL
) || 
1673                     (hints
->ai_next      
!= NULL
)) { 
1674                         _SCErrorSet(kSCStatusInvalidArgument
); 
1678         bypass 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandByPass
); 
1679         if ((bypass 
!= NULL
) && !isA_CFBoolean(bypass
)) { 
1680                 _SCErrorSet(kSCStatusInvalidArgument
); 
1683         if ((nodename 
== NULL
) && (servname 
== NULL
)) { 
1684                 _SCErrorSet(kSCStatusInvalidArgument
); 
1688         name 
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
); 
1689         targetPrivate 
= (SCNetworkReachabilityPrivateRef
)SCNetworkReachabilityCreateWithName(allocator
, name
); 
1690         CFAllocatorDeallocate(NULL
, (void *)name
); 
1691         if (targetPrivate 
== NULL
) { 
1695         if (targetPrivate
->type 
== reachabilityTypeName
) { 
1696                 if (servname 
!= NULL
) { 
1697                         targetPrivate
->serv 
= _SC_cfstring_to_cstring(servname
, NULL
, 0, kCFStringEncodingUTF8
); 
1699                 if (hints 
!= NULL
) { 
1700                         bcopy(hints
, &targetPrivate
->hints
, sizeof(targetPrivate
->hints
)); 
1704         if (bypass 
!= NULL
) { 
1705                 targetPrivate
->onDemandBypass 
= CFBooleanGetValue(bypass
); 
1708         return (SCNetworkReachabilityRef
)targetPrivate
; 
1713 SCNetworkReachabilityGetTypeID(void) 
1715         pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);  /* initialize runtime */ 
1716         return __kSCNetworkReachabilityTypeID
; 
1720 CFArrayRef      
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */ 
1721 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef       target
, 
1724         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1726         if (!isA_SCNetworkReachability(target
)) { 
1727                 _SCErrorSet(kSCStatusInvalidArgument
); 
1731         if (targetPrivate
->type 
!= reachabilityTypeName
) { 
1732                 _SCErrorSet(kSCStatusInvalidArgument
); 
1737                 *error_num 
= targetPrivate
->resolvedAddressError
; 
1740         if ((targetPrivate
->resolvedAddress 
!= NULL
) || (targetPrivate
->resolvedAddressError 
!= NETDB_SUCCESS
)) { 
1741                 if (isA_CFArray(targetPrivate
->resolvedAddress
)) { 
1742                         return CFRetain(targetPrivate
->resolvedAddress
); 
1744                         /* if status is known but no resolved addresses to return */ 
1745                         _SCErrorSet(kSCStatusOK
); 
1750         _SCErrorSet(kSCStatusReachabilityUnknown
); 
1756 __SCNetworkReachabilitySetResolvedAddress(int32_t                       status
, 
1757                                           struct addrinfo               
*res
, 
1758                                           SCNetworkReachabilityRef      target
) 
1760         struct addrinfo                         
*resP
; 
1761         SCNetworkReachabilityPrivateRef         targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1763         if (targetPrivate
->resolvedAddress 
!= NULL
) { 
1764                 CFRelease(targetPrivate
->resolvedAddress
); 
1765                 targetPrivate
->resolvedAddress 
= NULL
; 
1768         if ((status 
== 0) && (res 
!= NULL
)) { 
1769                 CFMutableArrayRef       addresses
; 
1771                 addresses 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
1773                 for (resP 
= res
; resP
; resP 
= resP
->ai_next
) { 
1775                         CFDataRef       newAddress
; 
1777                         newAddress 
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
); 
1778                         n 
= CFArrayGetCount(addresses
); 
1780                             !CFArrayContainsValue(addresses
, CFRangeMake(0, n
), newAddress
)) { 
1781                                 CFArrayAppendValue(addresses
, newAddress
); 
1783                         CFRelease(newAddress
); 
1786                 /* save the resolved address[es] */ 
1787                 targetPrivate
->resolvedAddress      
= addresses
; 
1788                 targetPrivate
->resolvedAddressError 
= NETDB_SUCCESS
; 
1790                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sgetaddrinfo() failed: %s"), 
1791                       targetPrivate
->log_prefix
, 
1792                       gai_strerror(status
)); 
1794                 /* save the error associated with the attempt to resolve the name */ 
1795                 targetPrivate
->resolvedAddress      
= CFRetain(kCFNull
); 
1796                 targetPrivate
->resolvedAddressError 
= status
; 
1798         targetPrivate
->needResolve 
= FALSE
; 
1800         if (res 
!= NULL
)        freeaddrinfo(res
); 
1802         if (targetPrivate
->scheduled
) { 
1803                 __SCNetworkReachabilityPerform(target
); 
1811 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo 
*res
, void *context
) 
1813         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)context
; 
1814         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1816         __log_query_time(target
, 
1817                          ((status 
== 0) && (res 
!= NULL
)),      // if successful query 
1819                          &targetPrivate
->dnsQueryStart
);        // start time 
1821         __SCNetworkReachabilitySetResolvedAddress(status
, res
, target
); 
1827  * rankReachability() 
1828  *   Not reachable       == 0 
1829  *   Connection Required == 1 
1833 rankReachability(SCNetworkReachabilityFlags flags
) 
1837         if (flags 
& kSCNetworkReachabilityFlagsReachable
)               rank 
= 2; 
1838         if (flags 
& kSCNetworkReachabilityFlagsConnectionRequired
)      rank 
= 1; 
1844 #pragma mark DNS name resolution 
1848 replyMPCopyDescription(const void *info
) 
1850         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)info
; 
1851         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1853         return CFStringCreateWithFormat(NULL
, 
1855                                         CFSTR("<getaddrinfo_async_start reply MP> {%s%s%s%s%s, target = %p}"), 
1856                                         targetPrivate
->name 
!= NULL 
? "name = " : "", 
1857                                         targetPrivate
->name 
!= NULL 
? targetPrivate
->name 
: "", 
1858                                         targetPrivate
->name 
!= NULL 
&& targetPrivate
->serv 
!= NULL 
? ", " : "", 
1859                                         targetPrivate
->serv 
!= NULL 
? "serv = " : "", 
1860                                         targetPrivate
->serv 
!= NULL 
? targetPrivate
->serv 
: "", 
1866 processAsyncDNSReply(mach_port_t mp
, void *msg
, SCNetworkReachabilityRef target
); 
1870 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) 
1872         mach_port_t                     mp      
= CFMachPortGetPort(port
); 
1873         SCNetworkReachabilityRef        target  
= (SCNetworkReachabilityRef
)info
; 
1875         processAsyncDNSReply(mp
, msg
, target
); 
1880 #if     !TARGET_OS_IPHONE 
1882 SCNetworkReachabilityNotifyMIGCallback(mach_msg_header_t 
*message
, mach_msg_header_t 
*reply
) 
1884         mach_port_t                     mp      
= message
->msgh_local_port
; 
1885         SCNetworkReachabilityRef        target  
= dispatch_get_context(dispatch_get_current_queue()); 
1887         processAsyncDNSReply(mp
, message
, target
); 
1888         reply
->msgh_remote_port 
= MACH_PORT_NULL
; 
1891 #endif  // !TARGET_OS_IPHONE 
1895 enqueueAsyncDNSQuery(SCNetworkReachabilityRef target
, mach_port_t mp
) 
1897         CFMachPortContext               context 
= { 0 
1901                                                   , replyMPCopyDescription
 
1903         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1905         targetPrivate
->dnsMP 
= mp
; 
1906         targetPrivate
->dnsPort 
= CFMachPortCreateWithPort(NULL
, 
1908                                                           getaddrinfo_async_handleCFReply
, 
1911 #if     !TARGET_OS_IPHONE 
1912         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
1913                 targetPrivate
->asyncDNSQueue 
= dispatch_queue_create("com.apple.SCNetworkReachabilty.async_DNS_query", NULL
); 
1914                 if (targetPrivate
->asyncDNSQueue 
== NULL
) { 
1915                         SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_queue_create() failed")); 
1918                 CFRetain(target
);       // Note: will be released when the dispatch queue is released 
1919                 dispatch_set_context(targetPrivate
->asyncDNSQueue
, (void *)target
); 
1920                 dispatch_set_finalizer_f(targetPrivate
->asyncDNSQueue
, (dispatch_function_t
)CFRelease
); 
1922                 targetPrivate
->asyncDNSSource 
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, 
1925                                                                        targetPrivate
->asyncDNSQueue
); 
1926                 if (targetPrivate
->asyncDNSSource 
== NULL
) { 
1927                         SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_source_create() failed")); 
1930                 dispatch_source_set_event_handler(targetPrivate
->asyncDNSSource
, ^{ 
1931                         dispatch_mig_server(targetPrivate
->asyncDNSSource
, 
1932                                             sizeof(mach_msg_header_t
), 
1933                                             SCNetworkReachabilityNotifyMIGCallback
); 
1935                 dispatch_resume(targetPrivate
->asyncDNSSource
); 
1937 #endif  // !TARGET_OS_IPHONE 
1938         if (targetPrivate
->rls 
!= NULL
) { 
1942                 targetPrivate
->dnsRLS 
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0); 
1944                 n 
= CFArrayGetCount(targetPrivate
->rlList
); 
1945                 for (i 
= 0; i 
< n
; i 
+= 3) { 
1946                         CFRunLoopRef    rl      
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1); 
1947                         CFStringRef     rlMode  
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2); 
1949                         CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
); 
1955 #if     !TARGET_OS_IPHONE 
1958         if (targetPrivate
->asyncDNSSource 
!= NULL
) { 
1959                 dispatch_source_cancel(targetPrivate
->asyncDNSSource
); 
1960                 dispatch_release(targetPrivate
->asyncDNSSource
); 
1961                 targetPrivate
->asyncDNSSource 
= NULL
; 
1963         if (targetPrivate
->asyncDNSQueue 
!= NULL
) { 
1964                 dispatch_release(targetPrivate
->asyncDNSQueue
); 
1965                 targetPrivate
->asyncDNSQueue 
= NULL
; 
1968         CFMachPortInvalidate(targetPrivate
->dnsPort
); 
1969         CFRelease(targetPrivate
->dnsPort
); 
1970         targetPrivate
->dnsPort 
= NULL
; 
1971         targetPrivate
->dnsMP 
= MACH_PORT_NULL
; 
1973         _SCErrorSet(kSCStatusFailed
); 
1975 #endif  // !TARGET_OS_IPHONE 
1980 dequeueAsyncDNSQuery(SCNetworkReachabilityRef target
) 
1982         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1984 #if     !TARGET_OS_IPHONE 
1985         if (targetPrivate
->asyncDNSSource 
!= NULL
) { 
1986                 dispatch_source_cancel(targetPrivate
->asyncDNSSource
); 
1987                 if (targetPrivate
->asyncDNSQueue 
!= dispatch_get_current_queue()) { 
1988                         // ensure the cancellation has completed 
1989                         pthread_mutex_unlock(&targetPrivate
->lock
); 
1990                         dispatch_sync(targetPrivate
->asyncDNSQueue
, ^{}); 
1991                         pthread_mutex_lock(&targetPrivate
->lock
); 
1994         if (targetPrivate
->asyncDNSSource 
!= NULL
) { 
1995                 dispatch_release(targetPrivate
->asyncDNSSource
); 
1996                 targetPrivate
->asyncDNSSource 
= NULL
; 
1998         if (targetPrivate
->asyncDNSQueue 
!= NULL
) { 
1999                 dispatch_release(targetPrivate
->asyncDNSQueue
); 
2000                 targetPrivate
->asyncDNSQueue 
= NULL
; 
2002 #endif  // !TARGET_OS_IPHONE 
2004         if (targetPrivate
->dnsRLS 
!= NULL
) { 
2005                 CFRelease(targetPrivate
->dnsRLS
); 
2006                 targetPrivate
->dnsRLS 
= NULL
; 
2009         if (targetPrivate
->dnsPort 
!= NULL
) { 
2010                 CFMachPortInvalidate(targetPrivate
->dnsPort
); 
2011                 CFRelease(targetPrivate
->dnsPort
); 
2012                 targetPrivate
->dnsPort 
= NULL
; 
2013                 targetPrivate
->dnsMP 
= MACH_PORT_NULL
; 
2021 processAsyncDNSReply(mach_port_t mp
, void *msg
, SCNetworkReachabilityRef target
) 
2024         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2026         pthread_mutex_lock(&targetPrivate
->lock
); 
2028         if (mp 
!= targetPrivate
->dnsMP
) { 
2029                 // we've received a callback on the async DNS port but since the 
2030                 // associated CFMachPort doesn't match than the request must have 
2031                 // already been cancelled. 
2032                 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP")); 
2033                 pthread_mutex_unlock(&targetPrivate
->lock
); 
2037         dequeueAsyncDNSQuery(target
); 
2038         status 
= getaddrinfo_async_handle_reply(msg
); 
2039         if ((status 
== 0) && 
2040             (targetPrivate
->resolvedAddress 
== NULL
) && (targetPrivate
->resolvedAddressError 
== NETDB_SUCCESS
)) { 
2043                 // if the request is not complete and needs to be re-queued 
2044                 ok 
= enqueueAsyncDNSQuery(target
, mp
); 
2046                         SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply enqueueAsyncDNSQuery() failed")); 
2050         pthread_mutex_unlock(&targetPrivate
->lock
); 
2057 check_resolver_reachability(SCDynamicStoreRef           
*storeP
, 
2058                             dns_resolver_t              
*resolver
, 
2059                             SCNetworkReachabilityFlags  
*flags
, 
2061                             const char                  *log_prefix
) 
2066         *flags   
= kSCNetworkReachabilityFlagsReachable
; 
2069         for (i 
= 0; i 
< resolver
->n_nameserver
; i
++) { 
2070                 struct sockaddr         
*address        
= resolver
->nameserver
[i
]; 
2071                 ReachabilityInfo        ns_info
; 
2075                 if (address
->sa_family 
!= AF_INET
) { 
2077                          * we need to skip non-IPv4 DNS server 
2078                          * addresses (at least until [3510431] has 
2084                 ok 
= checkAddress(storeP
, address
, &ns_info
, log_prefix
); 
2090                 if (rankReachability(ns_info
.flags
) < rankReachability(*flags
)) { 
2091                         /* return the worst case result */ 
2092                         *flags 
= ns_info
.flags
; 
2103 check_matching_resolvers(SCDynamicStoreRef              
*storeP
, 
2104                          dns_config_t                   
*dns_config
, 
2106                          SCNetworkReachabilityFlags     
*flags
, 
2108                          const char                     *log_prefix
) 
2111         Boolean         matched 
= FALSE
; 
2112         const char      *name   
= fqdn
; 
2114         while (!matched 
&& (name 
!= NULL
)) { 
2118                  * check if the provided name (or sub-component) 
2119                  * matches one of our resolver configurations. 
2122                 for (i 
= 0; i 
< dns_config
->n_resolver
; i
++) { 
2124                         dns_resolver_t  
*resolver
; 
2126                         resolver 
= dns_config
->resolver
[i
]; 
2127                         domain   
= resolver
->domain
; 
2128                         if (domain 
!= NULL 
&& (len 
== strlen(domain
))) { 
2129                                 if (strcasecmp(name
, domain
) == 0) { 
2133                                          * if name matches domain 
2136                                         ok 
= check_resolver_reachability(storeP
, resolver
, flags
, haveDNS
, log_prefix
); 
2147                          * we have not found a matching resolver, try 
2148                          * a less qualified domain 
2150                         name 
= strchr(name
, '.'); 
2151                         if ((name 
!= NULL
) && (*name 
!= '\0')) { 
2163 static dns_configuration_t 
* 
2164 dns_configuration_retain() 
2166         pthread_mutex_lock(&dns_lock
); 
2168         if ((dns_configuration 
!= NULL
) && dns_token_valid
) { 
2173                  * check if the global [DNS] configuration snapshot needs 
2176                 status 
= notify_check(dns_token
, &check
); 
2177                 if (status 
!= NOTIFY_STATUS_OK
) { 
2178                         SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
); 
2181                 if ((status 
!= NOTIFY_STATUS_OK
) || (check 
!= 0)) { 
2183                          * if the snapshot needs to be refreshed 
2185                         if (dns_configuration
->refs 
== 0) { 
2186                                 dns_configuration_free(dns_configuration
->config
); 
2187                                 CFAllocatorDeallocate(NULL
, dns_configuration
); 
2189                         dns_configuration 
= NULL
; 
2193         if (dns_configuration 
== NULL
) { 
2194                 dns_config_t    
*new_config
; 
2196                 new_config 
= dns_configuration_copy(); 
2197                 if (new_config 
!= NULL
) { 
2198                         dns_configuration 
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0); 
2199                         dns_configuration
->config 
= new_config
; 
2200                         dns_configuration
->refs   
= 0; 
2204         if (dns_configuration 
!= NULL
) { 
2205                 dns_configuration
->refs
++; 
2208         pthread_mutex_unlock(&dns_lock
); 
2209         return dns_configuration
; 
2214 dns_configuration_release(dns_configuration_t 
*config
) 
2216         pthread_mutex_lock(&dns_lock
); 
2219         if (config
->refs 
== 0) { 
2220                 if ((dns_configuration 
!= config
)) { 
2221                         dns_configuration_free(config
->config
); 
2222                         CFAllocatorDeallocate(NULL
, config
); 
2226         pthread_mutex_unlock(&dns_lock
); 
2232 dns_configuration_watch() 
2235         const char      *dns_key
; 
2239         pthread_mutex_lock(&dns_lock
); 
2241         dns_key 
= dns_configuration_notify_key(); 
2242         if (dns_key 
== NULL
) { 
2243                 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed")); 
2247         status 
= notify_register_check(dns_key
, &dns_token
); 
2248         if (status 
== NOTIFY_STATUS_OK
) { 
2249                 dns_token_valid 
= TRUE
; 
2251                 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
); 
2255         status 
= notify_check(dns_token
, &dns_check
); 
2256         if (status 
!= NOTIFY_STATUS_OK
) { 
2257                 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
); 
2258                 (void)notify_cancel(dns_token
); 
2259                 dns_token_valid 
= FALSE
; 
2267         pthread_mutex_unlock(&dns_lock
); 
2273 dns_configuration_unwatch() 
2275         pthread_mutex_lock(&dns_lock
); 
2277         (void)notify_cancel(dns_token
); 
2278         dns_token_valid 
= FALSE
; 
2280         if ((dns_configuration 
!= NULL
) && (dns_configuration
->refs 
== 0)) { 
2281                 dns_configuration_free(dns_configuration
->config
); 
2282                 CFAllocatorDeallocate(NULL
, dns_configuration
); 
2283                 dns_configuration 
= NULL
; 
2286         pthread_mutex_unlock(&dns_lock
); 
2292 _SC_R_checkResolverReachability(SCDynamicStoreRef               
*storeP
, 
2293                                 SCNetworkReachabilityFlags      
*flags
, 
2295                                 const char                      *nodename
, 
2296                                 const char                      *servname
, 
2297                                 const char                      *log_prefix
) 
2299         dns_resolver_t          
*default_resolver
; 
2300         dns_configuration_t     
*dns
; 
2301         Boolean                 found                   
= FALSE
; 
2302         char                    *fqdn                   
= (char *)nodename
; 
2304         Boolean                 isFQDN                  
= FALSE
; 
2307         Boolean                 useDefault              
= FALSE
; 
2310          * We first assume that all of the configured DNS servers 
2311          * are available.  Since we don't know which name server will 
2312          * be consulted to resolve the specified nodename we need to 
2313          * check the availability of ALL name servers.  We can only 
2314          * proceed if we know that our query can be answered. 
2317         *flags   
= kSCNetworkReachabilityFlagsReachable
; 
2320         len 
= (nodename 
!= NULL
) ? strlen(nodename
) : 0; 
2322                 if ((servname 
== NULL
) || (strlen(servname
) == 0)) { 
2323                         // if no nodename or servname, return not reachable 
2329         dns 
= dns_configuration_retain(); 
2332                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no configuration"), log_prefix
); 
2336         if (dns
->config
->n_resolver 
== 0) { 
2337                 // if no resolver configuration 
2338                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no resolvers"), log_prefix
); 
2342         *flags 
= kSCNetworkReachabilityFlagsReachable
; 
2344         if (fqdn
[len 
- 1] == '.') { 
2347                 // trim trailing '.''s 
2348                 while ((len 
> 0) && (fqdn
[len
-1] == '.')) { 
2349                         if (fqdn 
== nodename
) { 
2350                                 fqdn 
= strdup(nodename
); 
2356         default_resolver 
= dns
->config
->resolver
[0]; 
2359          * check if the provided name matches a supplemental domain 
2361         found 
= check_matching_resolvers(storeP
, dns
->config
, fqdn
, flags
, haveDNS
, log_prefix
); 
2363         if (!found 
&& !isFQDN
) { 
2365                  * if we did not match a supplemental domain name and if the 
2366                  * provided name has enough "."s then the first query will be 
2367                  * directed to the default resolver. 
2373 #define NDOTS_OPT       "ndots=" 
2374 #define NDOTS_OPT_LEN   (sizeof("ndots=") - 1) 
2376                 if (default_resolver
->options 
!= NULL
) { 
2377                         cp 
= strstr(default_resolver
->options
, NDOTS_OPT
); 
2379                             ((cp 
== default_resolver
->options
) || isspace(cp
[-1])) && 
2380                             ((cp
[NDOTS_OPT_LEN
] != '\0') && isdigit(cp
[NDOTS_OPT_LEN
]))) { 
2384                                 cp 
+=  NDOTS_OPT_LEN
; 
2386                                 val 
= strtol(cp
, &end
, 10); 
2387                                 if ((*cp 
!= '\0') && (cp 
!= end
) && (errno 
== 0) && 
2388                                 ((*end 
== '\0') || isspace(*end
))) { 
2395                 for (cp 
= fqdn
; *cp 
!= '\0'; cp
++) { 
2396                         if (*cp 
== '.') dots
++; 
2404         if (!found 
&& !isFQDN 
&& !useDefault 
&& (dns
->config
->n_resolver 
> 1)) { 
2406                  * FQDN not specified, try matching w/search domains 
2408                 if (default_resolver
->n_search 
> 0) { 
2409                         for (i 
= 0; !found 
&& (i 
< default_resolver
->n_search
); i
++) { 
2411                                 char    *search_fqdn    
= NULL
; 
2413                                 ret 
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]); 
2418                                 // try the provided name with the search domain appended 
2419                                 found 
= check_matching_resolvers(storeP
, 
2427                 } else if (default_resolver
->domain 
!= NULL
) { 
2429                         int     domain_parts    
= 0; 
2431                         // count domain parts 
2432                         for (dp 
= default_resolver
->domain
; *dp 
!= '\0'; dp
++) { 
2438                         // remove trailing dots 
2439                         for (dp
--; (dp 
>= default_resolver
->domain
) && (*dp 
== '.'); dp
--) { 
2444                         if (dp 
>= default_resolver
->domain
) { 
2445                                 // dots are separators, bump # of components 
2449                         dp 
= default_resolver
->domain
; 
2450                         for (i 
= LOCALDOMAINPARTS
; !found 
&& (i 
<= domain_parts
); i
++) { 
2452                                 char    *search_fqdn    
= NULL
; 
2454                                 ret 
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
); 
2459                                 // try the provided name with the [default] domain appended 
2460                                 found 
= check_matching_resolvers(storeP
, 
2468                                 // move to the next component of the [default] domain 
2469                                 dp 
= strchr(dp
, '.') + 1; 
2476                  * check the reachability of the default resolver 
2478                 ok 
= check_resolver_reachability(storeP
, default_resolver
, flags
, haveDNS
, log_prefix
); 
2481         if (fqdn 
!= nodename
)   free(fqdn
); 
2486                 dns_configuration_release(dns
); 
2494 _SC_checkResolverReachability(SCDynamicStoreRef                 
*storeP
, 
2495                               SCNetworkReachabilityFlags        
*flags
, 
2497                               const char                        *nodename
, 
2498                               const char                        *servname
) 
2500         return _SC_R_checkResolverReachability(storeP
, flags
, haveDNS
, nodename
, servname
, ""); 
2505  * _SC_checkResolverReachabilityByAddress() 
2507  * Given an IP address, determine whether a reverse DNS query can be issued 
2508  * using the current network configuration. 
2511 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef                
*storeP
, 
2512                                        SCNetworkReachabilityFlags       
*flags
, 
2514                                        struct sockaddr                  
*sa
) 
2521          * Ideally, we would have an API that given a local IP 
2522          * address would return the DNS server(s) that would field 
2523          * a given PTR query.  Fortunately, we do have an SPI which 
2524          * which will provide this information given a "name" so we 
2525          * take the address, convert it into the inverse query name, 
2526          * and find out which servers should be consulted. 
2529         switch (sa
->sa_family
) { 
2535                         struct sockaddr_in      
*sin    
= (struct sockaddr_in 
*)sa
; 
2538                          * build "PTR" query name 
2539                          *   NNN.NNN.NNN.NNN.in-addr.arpa. 
2541                         rev
.s_addr 
= sin
->sin_addr
.s_addr
; 
2542                         (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.", 
2553                         struct sockaddr_in6     
*sin6   
= (struct sockaddr_in6 
*)sa
; 
2554                         int                     x       
= sizeof(ptr_name
); 
2558                          * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152) 
2559                          *   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. 
2561                         for (i 
= sizeof(sin6
->sin6_addr
) - 1; i 
>= 0; i
--) { 
2562                                 n 
= snprintf(&ptr_name
[s
], x
, "%x.%x.", 
2563                                              ( sin6
->sin6_addr
.s6_addr
[i
]       & 0xf), 
2564                                              ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf)); 
2565                                 if ((n 
== -1) || (n 
>= x
)) { 
2573                         n 
= snprintf(&ptr_name
[s
], x
, "ip6.arpa."); 
2574                         if ((n 
== -1) || (n 
>= x
)) { 
2585         ok 
= _SC_R_checkResolverReachability(storeP
, flags
, haveDNS
, ptr_name
, NULL
, ""); 
2594 startAsyncDNSQuery(SCNetworkReachabilityRef target
) { 
2598         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2600         (void) gettimeofday(&targetPrivate
->dnsQueryStart
, NULL
); 
2602         error 
= getaddrinfo_async_start(&mp
, 
2603                                         targetPrivate
->name
, 
2604                                         targetPrivate
->serv
, 
2605                                         &targetPrivate
->hints
, 
2606                                         __SCNetworkReachabilityCallbackSetResolvedAddress
, 
2609                 /* save the error associated with the attempt to resolve the name */ 
2610                 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
); 
2614         ok 
= enqueueAsyncDNSQuery(target
, mp
); 
2620 #pragma mark OnDemand 
2624 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef       target
, 
2625                                          CFDictionaryRef                
*userOptions
) 
2627         SCNetworkServiceRef             service         
= NULL
; 
2628         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2630         if (!isA_SCNetworkReachability(target
)) { 
2631                 _SCErrorSet(kSCStatusInvalidArgument
); 
2635         if (targetPrivate
->onDemandServiceID 
!= NULL
) { 
2636                 service 
= _SCNetworkServiceCopyActive(NULL
, targetPrivate
->onDemandServiceID
); 
2639         if (userOptions 
!= NULL
) { 
2640                 if (targetPrivate
->onDemandName 
!= NULL
) { 
2641                         *userOptions 
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
2642                         CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
); 
2644                         *userOptions 
= NULL
; 
2653 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef   onDemandServer
, 
2654                                              SCNetworkReachabilityFlags onDemandFlags
, 
2657         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)info
; 
2658         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2660         pthread_mutex_lock(&targetPrivate
->lock
); 
2662         if (!targetPrivate
->scheduled
) { 
2663                 // if not currently scheduled 
2664                 pthread_mutex_unlock(&targetPrivate
->lock
); 
2668         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sOnDemand \"server\" status changed"), 
2669               targetPrivate
->log_prefix
); 
2670         __SCNetworkReachabilityPerform(target
); 
2672         pthread_mutex_unlock(&targetPrivate
->lock
); 
2679 __SCNetworkReachabilityOnDemandCheck(SCDynamicStoreRef          
*storeP
, 
2680                                      SCNetworkReachabilityRef   target
, 
2681                                      Boolean                    onDemandRetry
, 
2682                                      SCNetworkReachabilityFlags 
*flags
) 
2685         Boolean                         onDemand                
= FALSE
; 
2686         CFStringRef                     onDemandRemoteAddress   
= NULL
; 
2687         CFStringRef                     onDemandServiceID       
= NULL
; 
2688         SCNetworkConnectionStatus       onDemandStatus
; 
2689         SCNetworkReachabilityPrivateRef targetPrivate           
= (SCNetworkReachabilityPrivateRef
)target
; 
2691 //      SCLog(_sc_debug, LOG_INFO, 
2692 //            CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"), 
2693 //            targetPrivate->log_prefix, 
2694 //            onDemandRetry ? "after" : "before"); 
2696         if (targetPrivate
->onDemandName 
== NULL
) { 
2697                 targetPrivate
->onDemandName 
= CFStringCreateWithCString(NULL
, targetPrivate
->name
, kCFStringEncodingUTF8
); 
2701          * check if an OnDemand VPN configuration matches the name. 
2703         ok 
= __SCNetworkConnectionCopyOnDemandInfoWithName(storeP
, 
2704                                                            targetPrivate
->onDemandName
, 
2708                                                            &onDemandRemoteAddress
); 
2709         if (!_SC_CFEqual(targetPrivate
->onDemandRemoteAddress
, onDemandRemoteAddress
) || 
2710             !_SC_CFEqual(targetPrivate
->onDemandServiceID
, onDemandServiceID
)) { 
2711                 if (targetPrivate
->onDemandRemoteAddress 
!= NULL
) { 
2712                         CFRelease(targetPrivate
->onDemandRemoteAddress
); 
2713                         targetPrivate
->onDemandRemoteAddress 
= NULL
; 
2716                 if (targetPrivate
->onDemandServer 
!= NULL
) { 
2717 #if     !TARGET_OS_IPHONE 
2718                         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
2720                                 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
); 
2722 #endif  // !TARGET_OS_IPHONE 
2723                         if (targetPrivate
->rls 
!= NULL
) { 
2728                                 n 
= CFArrayGetCount(targetPrivate
->rlList
); 
2729                                 for (i 
= 0; i 
< n
; i 
+= 3) { 
2730                                         CFRunLoopRef    rl      
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1); 
2731                                         CFStringRef     rlMode  
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2); 
2733                                         __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, TRUE
); 
2737                         CFRelease(targetPrivate
->onDemandServer
); 
2738                         targetPrivate
->onDemandServer 
= NULL
; 
2741                 if (targetPrivate
->onDemandServiceID 
!= NULL
) { 
2742                         CFRelease(targetPrivate
->onDemandServiceID
); 
2743                         targetPrivate
->onDemandServiceID 
= NULL
; 
2747                 if (onDemandStatus 
!= kSCNetworkConnectionConnected
) { 
2749                          * if we have a VPN configuration matching the name *and* we need to 
2750                          * bring the VPN up.  Combine our flags with those of the VPN server. 
2752                         if (targetPrivate
->onDemandServer 
== NULL
) { 
2753                                 CFMutableDictionaryRef          options
; 
2755                                 options 
= CFDictionaryCreateMutable(NULL
, 
2757                                                                     &kCFTypeDictionaryKeyCallBacks
, 
2758                                                                     &kCFTypeDictionaryValueCallBacks
); 
2759                                 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, onDemandRemoteAddress
); 
2760                                 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandByPass
, kCFBooleanTrue
); 
2761                                 targetPrivate
->onDemandServer 
= SCNetworkReachabilityCreateWithOptions(NULL
, options
); 
2764                                 if (targetPrivate
->scheduled
) { 
2765                                         SCNetworkReachabilityContext    context 
= { 0, NULL
, CFRetain
, CFRelease
, CFCopyDescription 
}; 
2767                                         context
.info 
= (void *)target
; 
2768                                         SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, 
2769                                                                          __SCNetworkReachabilityOnDemandCheckCallback
, 
2772                                         // schedule server reachability to match that of the target 
2773 #if     !TARGET_OS_IPHONE 
2774                                         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
2775                                                 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, targetPrivate
->dispatchQueue
, TRUE
); 
2777 #endif  // !TARGET_OS_IPHONE 
2782                                                 n 
= CFArrayGetCount(targetPrivate
->rlList
); 
2783                                                 for (i 
= 0; i 
< n
; i 
+= 3) { 
2784                                                         CFRunLoopRef    rl      
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1); 
2785                                                         CFStringRef     rlMode  
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2); 
2787                                                         __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, NULL
, TRUE
); 
2793                         ok 
= SCNetworkReachabilityGetFlags(targetPrivate
->onDemandServer
, flags
); 
2794                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  status  * = 0x%08x"), 
2795                               targetPrivate
->log_prefix
, 
2797                         if (ok 
&& (*flags 
& kSCNetworkReachabilityFlagsReachable
)) { 
2798                                 if (!(*flags 
& kSCNetworkReachabilityFlagsTransientConnection
)) { 
2799                                         // start clean if not already layered on a transient network 
2802                                 *flags 
|= kSCNetworkReachabilityFlagsReachable
; 
2803                                 *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
2804                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
2805                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionOnDemand
; 
2808                                         SCLog(TRUE
, LOG_INFO
, CFSTR("%s  service * = %@"), 
2809                                               targetPrivate
->log_prefix
, 
2811                                         SCLog(TRUE
, LOG_INFO
, CFSTR("%s  status    = isReachable (after OnDemand connect)"), 
2812                                               targetPrivate
->log_prefix
); 
2819                 if (onDemandRemoteAddress 
!= NULL
) { 
2820                         if (targetPrivate
->onDemandRemoteAddress 
== NULL
) { 
2821                                 targetPrivate
->onDemandRemoteAddress 
= onDemandRemoteAddress
; 
2823                                 CFRelease(onDemandRemoteAddress
); 
2827                 if (onDemandServiceID 
!= NULL
) { 
2828                         if (targetPrivate
->onDemandServiceID 
== NULL
) { 
2829                                 targetPrivate
->onDemandServiceID 
= onDemandServiceID
; 
2831                                 CFRelease(onDemandServiceID
); 
2841 #pragma mark Reachability Flags 
2845 __SCNetworkReachabilityGetFlags(SCDynamicStoreRef               
*storeP
, 
2846                                 SCNetworkReachabilityRef        target
, 
2847                                 ReachabilityInfo                
*reach_info
, 
2850         CFMutableArrayRef               addresses       
= NULL
; 
2851         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2852         ReachabilityInfo                my_info         
= NOT_REACHABLE
; 
2855         *reach_info 
= NOT_REACHABLE
; 
2857         if (!isA_SCNetworkReachability(target
)) { 
2858                 _SCErrorSet(kSCStatusInvalidArgument
); 
2862         switch (targetPrivate
->type
) { 
2863                 case reachabilityTypeAddress 
: 
2864                 case reachabilityTypeAddressPair 
: { 
2866                          * Check "local" address 
2868                         if (targetPrivate
->localAddress 
!= NULL
) { 
2870                                  * Check "local" address 
2872                                 ok 
= checkAddress(storeP
, 
2873                                                   targetPrivate
->localAddress
, 
2875                                                   targetPrivate
->log_prefix
); 
2877                                         goto error
;     /* not today */ 
2880                                 if (!(my_info
.flags 
& kSCNetworkReachabilityFlagsIsLocalAddress
)) { 
2881                                         goto error
;     /* not reachable, non-"local" address */ 
2886                          * Check "remote" address 
2888                         if (targetPrivate
->remoteAddress 
!= NULL
) { 
2890                                  * in cases where we have "local" and "remote" addresses 
2891                                  * we need to re-initialize the to-be-returned flags. 
2893                                 my_info 
= NOT_REACHABLE
; 
2896                                  * Check "remote" address 
2898                                 ok 
= checkAddress(storeP
, 
2899                                                   targetPrivate
->remoteAddress
, 
2901                                                   targetPrivate
->log_prefix
); 
2903                                         goto error
;     /* not today */ 
2911                 case reachabilityTypeName 
: { 
2912                         struct timeval                  dnsQueryStart
; 
2914                         SCNetworkReachabilityFlags      ns_flags
; 
2915                         struct addrinfo                 
*res
; 
2917                         addresses 
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
); 
2918                         if ((addresses 
!= NULL
) || (error 
!= NETDB_SUCCESS
)) { 
2919                                 /* if resolved or an error had been detected */ 
2921                                         /* if not an async request */ 
2922                                         goto checkResolvedAddress
; 
2923                                 } else if ((targetPrivate
->dnsPort 
== NULL
) && !targetPrivate
->needResolve
) { 
2924                                         /* if async request, no query active, and no query needed */ 
2925                                         goto checkResolvedAddress
; 
2929                         if (!targetPrivate
->onDemandBypass
) { 
2933                                  * before we attempt our initial DNS query, check if there is 
2934                                  * an OnDemand configuration that we should be using. 
2936                                 onDemand 
= __SCNetworkReachabilityOnDemandCheck(storeP
, target
, FALSE
, &my_info
.flags
); 
2938                                         /* if OnDemand connection is needed */ 
2943                         /* check the reachability of the DNS servers */ 
2944                         ok 
= _SC_R_checkResolverReachability(storeP
, 
2946                                                              &targetPrivate
->haveDNS
, 
2947                                                              targetPrivate
->name
, 
2948                                                              targetPrivate
->serv
, 
2949                                                              targetPrivate
->log_prefix
); 
2951                                 /* if we could not get DNS server info */ 
2952                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"), 
2953                                       targetPrivate
->log_prefix
); 
2955                         } else if (rankReachability(ns_flags
) < 2) { 
2957                                  * if DNS servers are not (or are no longer) reachable, set 
2958                                  * flags based on the availability of configured (but not 
2962                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"), 
2963                                       targetPrivate
->log_prefix
); 
2965                                 ok 
= checkAddress(storeP
, 
2968                                                   targetPrivate
->log_prefix
); 
2970                                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sNo available networks"), 
2971                                               targetPrivate
->log_prefix
); 
2975                                 if (async 
&& targetPrivate
->scheduled
) { 
2977                                          * return "host not found", set flags appropriately, 
2978                                          * and schedule notification. 
2980                                         __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME
, 
2983                                         my_info
.flags 
|= (targetPrivate
->info
.flags 
& kSCNetworkReachabilityFlagsFirstResolvePending
); 
2985                                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sno DNS servers are reachable"), 
2986                                               targetPrivate
->log_prefix
); 
2987                                         __SCNetworkReachabilityPerform(target
); 
2993                                 /* for async requests we return the last known status */ 
2994                                 my_info 
= targetPrivate
->info
; 
2996                                 if (targetPrivate
->dnsPort 
!= NULL
) { 
2997                                         /* if request already in progress */ 
2998                                         SCLog(_sc_debug
, LOG_INFO
, 
2999                                               CFSTR("%swaiting for DNS reply"), 
3000                                               targetPrivate
->log_prefix
); 
3001                                         if ((addresses 
!= NULL
) || (error 
!= NETDB_SUCCESS
)) { 
3002                                                 /* updated reachability based on the previous reply */ 
3003                                                 goto checkResolvedAddress
; 
3008                                 SCLog(_sc_debug
, LOG_INFO
, 
3009                                       CFSTR("%sstart DNS query for %s%s%s%s%s"), 
3010                                       targetPrivate
->log_prefix
, 
3011                                       targetPrivate
->name 
!= NULL 
? "name = " : "", 
3012                                       targetPrivate
->name 
!= NULL 
? targetPrivate
->name 
: "", 
3013                                       targetPrivate
->name 
!= NULL 
&& targetPrivate
->serv 
!= NULL 
? ", " : "", 
3014                                       targetPrivate
->serv 
!= NULL 
? "serv = " : "", 
3015                                       targetPrivate
->serv 
!= NULL 
? targetPrivate
->serv 
: ""); 
3018                                  * initiate an async DNS query 
3020                                 if (!startAsyncDNSQuery(target
)) { 
3021                                         /* if we could not initiate the request, process error */ 
3022                                         goto checkResolvedAddress
; 
3025                                 /* request initiated */ 
3029                         SCLog(_sc_debug
, LOG_INFO
, 
3030                               CFSTR("%scheck DNS for %s%s%s%s%s"), 
3031                               targetPrivate
->log_prefix
, 
3032                               targetPrivate
->name 
!= NULL 
? "name = " : "", 
3033                               targetPrivate
->name 
!= NULL 
? targetPrivate
->name 
: "", 
3034                               targetPrivate
->name 
!= NULL 
&& targetPrivate
->serv 
!= NULL 
? ", " : "", 
3035                               targetPrivate
->serv 
!= NULL 
? "serv = " : "", 
3036                               targetPrivate
->serv 
!= NULL 
? targetPrivate
->serv 
: ""); 
3039                          * OK, all of the DNS name servers are available.  Let's 
3040                          * resolve the nodename into an address. 
3043                                 (void) gettimeofday(&dnsQueryStart
, NULL
); 
3046                         error 
= getaddrinfo(targetPrivate
->name
, 
3047                                             targetPrivate
->serv
, 
3048                                             &targetPrivate
->hints
, 
3051                         __log_query_time(target
, 
3052                                          ((error 
== 0) && (res 
!= NULL
)),       // if successful query 
3054                                          &dnsQueryStart
);                       // start time 
3056                         __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
); 
3058                         addresses 
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
); 
3060                     checkResolvedAddress 
: 
3063                          * We first assume that the requested host is NOT available. 
3064                          * Then, check each address for accessibility and return the 
3065                          * best status available. 
3067                         my_info 
= NOT_REACHABLE
; 
3069                         if (isA_CFArray(addresses
)) { 
3071                                 CFIndex         n       
= CFArrayGetCount(addresses
); 
3073                                 for (i 
= 0; i 
< n
; i
++) { 
3074                                         ReachabilityInfo        ns_info 
= NOT_REACHABLE
; 
3075                                         struct sockaddr         
*sa
; 
3077                                         sa 
= (struct sockaddr 
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
)); 
3079                                         ok 
= checkAddress(storeP
, 
3082                                                           targetPrivate
->log_prefix
); 
3084                                                 goto error
;     /* not today */ 
3087                                         if (rankReachability(ns_info
.flags
) > rankReachability(my_info
.flags
)) { 
3088                                                 /* return the best case result */ 
3090                                                 if (rankReachability(my_info
.flags
) == 2) { 
3097                                 if ((error 
== EAI_NONAME
) 
3098 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) 
3099                                      || (error 
== EAI_NODATA
) 
3103                                          * the target host name could not be resolved 
3105                                         if (!targetPrivate
->onDemandBypass
) { 
3109                                                  * our initial DNS query failed, check again to see if there 
3110                                                  * there is an OnDemand configuration that we should be using. 
3112                                                 onDemand 
= __SCNetworkReachabilityOnDemandCheck(storeP
, target
, TRUE
, &my_info
.flags
); 
3114                                                         /* if OnDemand connection is needed */ 
3119                                         if (!targetPrivate
->haveDNS
) { 
3121                                                  * No DNS servers are defined. Set flags based on 
3122                                                  * the availability of configured (but not active) 
3125                                                 ok 
= checkAddress(storeP
, 
3128                                                                   targetPrivate
->log_prefix
); 
3130                                                         goto error
;     /* not today */ 
3133                                                 if ((my_info
.flags 
& kSCNetworkReachabilityFlagsReachable
) && 
3134                                                         (my_info
.flags 
& kSCNetworkReachabilityFlagsConnectionRequired
)) { 
3136                                                          * Since we might pick up a set of DNS servers when this connection 
3137                                                          * is established, don't reply with a "HOST NOT FOUND" error just yet. 
3142                                                 /* Host not found, not reachable! */ 
3143                                                 my_info 
= NOT_REACHABLE
; 
3154         *reach_info 
= my_info
; 
3158         if (addresses 
!= NULL
)  CFRelease(addresses
); 
3164 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef          target
, 
3165                               SCNetworkReachabilityFlags        
*flags
) 
3168         SCDynamicStoreRef               store           
= NULL
; 
3169         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3171         if (!isA_SCNetworkReachability(target
)) { 
3172                 _SCErrorSet(kSCStatusInvalidArgument
); 
3176         pthread_mutex_lock(&targetPrivate
->lock
); 
3178         if (targetPrivate
->scheduled
) { 
3179                 // if being watched, return the last known (and what should be current) status 
3180                 *flags 
= targetPrivate
->info
.flags 
& ~kSCNetworkReachabilityFlagsFirstResolvePending
; 
3185         ok 
= __SCNetworkReachabilityGetFlags(&store
, target
, &targetPrivate
->info
, FALSE
); 
3186         *flags 
= targetPrivate
->info
.flags 
& ~kSCNetworkReachabilityFlagsFirstResolvePending
; 
3187         if (store 
!= NULL
) CFRelease(store
); 
3191         pthread_mutex_unlock(&targetPrivate
->lock
); 
3197 #pragma mark Notifications 
3201 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef   store
) 
3204         CFMutableArrayRef               keys
; 
3205         CFStringRef                     pattern
; 
3206         CFMutableArrayRef               patterns
; 
3208         keys     
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
3209         patterns 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
3211         // Setup:/Network/Global/IPv4 (for the ServiceOrder) 
3212         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
3213                                                          kSCDynamicStoreDomainSetup
, 
3215         CFArrayAppendValue(keys
, key
); 
3218         // State:/Network/Global/DNS 
3219         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
3220                                                          kSCDynamicStoreDomainState
, 
3222         CFArrayAppendValue(keys
, key
); 
3225         // State:/Network/Global/IPv4 (default route) 
3226         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
3227                                                          kSCDynamicStoreDomainState
, 
3229         CFArrayAppendValue(keys
, key
); 
3232         // State:/Network/Global/OnDemand 
3233         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
3234                                                          kSCDynamicStoreDomainState
, 
3236         CFArrayAppendValue(keys
, key
); 
3239         // Setup: per-service IPv4 info 
3240         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
3241                                                               kSCDynamicStoreDomainSetup
, 
3244         CFArrayAppendValue(patterns
, pattern
); 
3247         // Setup: per-service Interface info 
3248         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
3249                                                               kSCDynamicStoreDomainSetup
, 
3251                                                               kSCEntNetInterface
); 
3252         CFArrayAppendValue(patterns
, pattern
); 
3255         // Setup: per-service PPP info (for kSCPropNetPPPDialOnDemand) 
3256         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
3257                                                               kSCDynamicStoreDomainSetup
, 
3260         CFArrayAppendValue(patterns
, pattern
); 
3263         // State: per-service PPP info (for kSCPropNetPPPStatus) 
3264         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
3265                                                               kSCDynamicStoreDomainState
, 
3268         CFArrayAppendValue(patterns
, pattern
); 
3271         // Setup: per-service IPSec info 
3272         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
3273                                                               kSCDynamicStoreDomainSetup
, 
3276         CFArrayAppendValue(patterns
, pattern
); 
3279         // State: per-interface IPv4 info 
3280         pattern 
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
, 
3281                                                                 kSCDynamicStoreDomainState
, 
3284         CFArrayAppendValue(patterns
, pattern
); 
3287         // State: per-interface IPv6 info 
3288         pattern 
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL
, 
3289                                                                 kSCDynamicStoreDomainState
, 
3292         CFArrayAppendValue(patterns
, pattern
); 
3295 #if     !TARGET_OS_IPHONE 
3296         // State: Power Management Capabilities 
3297         key 
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"), 
3298                                       kSCDynamicStoreDomainState
, 
3299                                       CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
)); 
3300         CFArrayAppendValue(keys
, key
); 
3302 #endif  // TARGET_OS_IPHONE 
3305         (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
); 
3307         CFRelease(patterns
); 
3314 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef  store
, 
3315                                      CFArrayRef         changedKeys
, 
3318         Boolean         dnsConfigChanged        
= FALSE
; 
3321         CFIndex         nChanges                
= CFArrayGetCount(changedKeys
); 
3323 #if     !TARGET_OS_IPHONE 
3324         Boolean         powerStatusChanged      
= FALSE
; 
3325 #endif  // !TARGET_OS_IPHONE 
3326         const void *    targets_q
[N_QUICK
]; 
3327         const void **   targets                 
= targets_q
; 
3329         if (nChanges 
== 0) { 
3334         pthread_mutex_lock(&hn_lock
); 
3336         nTargets 
= (hn_targets 
!= NULL
) ? CFSetGetCount(hn_targets
) : 0; 
3337         if (nTargets 
== 0) { 
3338                 /* if no addresses being monitored */ 
3342 #if     !TARGET_OS_IPHONE 
3343         key 
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"), 
3344                                       kSCDynamicStoreDomainState
, 
3345                                       CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
)); 
3346         if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) { 
3349                 num 
= SCDynamicStoreCopyValue(store
, key
); 
3351                         if (isA_CFNumber(num
) && 
3352                             CFNumberGetValue(num
, kCFNumberSInt32Type
, &power_capabilities
)) { 
3353                                 powerStatusChanged 
= TRUE
; 
3360 #endif  // !TARGET_OS_IPHONE 
3362         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
3363                                                          kSCDynamicStoreDomainState
, 
3365         if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) { 
3366                 dnsConfigChanged 
= TRUE
;        /* the DNS server(s) have changed */ 
3374 #if     !TARGET_OS_IPHONE 
3375                 if (powerStatusChanged
) { 
3379 #endif  // !TARGET_OS_IPHONE 
3381                 if (dnsConfigChanged
) { 
3391                         case 0  : str 
= "";                             break; 
3392                         case 1  : str 
= "network ";                     break; 
3393                         case 2  : str 
= "DNS ";                         break; 
3394                         case 3  : str 
= "network and DNS ";             break; 
3395 #if     !TARGET_OS_IPHONE 
3396                         case 4  : str 
= "power ";                       break; 
3397                         case 5  : str 
= "network and power ";           break; 
3398                         case 6  : str 
= "DNS and power ";               break; 
3399                         case 7  : str 
= "network, DNS, and power ";     break; 
3400 #endif  // !TARGET_OS_IPHONE 
3401                         default : str 
= "??? "; 
3404                 SCLog(TRUE
, LOG_INFO
, CFSTR("process %sconfiguration change"), str
); 
3407         if (nTargets 
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
))) 
3408                 targets 
= CFAllocatorAllocate(NULL
, nTargets 
* sizeof(CFTypeRef
), 0); 
3409         CFSetGetValues(hn_targets
, targets
); 
3410         for (i 
= 0; i 
< nTargets
; i
++) { 
3411                 SCNetworkReachabilityRef        target          
= targets
[i
]; 
3412                 SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3414                 pthread_mutex_lock(&targetPrivate
->lock
); 
3416                 if (targetPrivate
->type 
== reachabilityTypeName
) { 
3417                         Boolean         dnsChanged      
= dnsConfigChanged
; 
3421                                  * if the DNS configuration didn't change we still need to 
3422                                  * check that the DNS servers are accessible. 
3424                                 SCNetworkReachabilityFlags      ns_flags
; 
3427                                 /* check the reachability of the DNS servers */ 
3428                                 ok 
= _SC_R_checkResolverReachability(&store
, 
3430                                                                      &targetPrivate
->haveDNS
, 
3431                                                                      targetPrivate
->name
, 
3432                                                                      targetPrivate
->serv
, 
3433                                                                      targetPrivate
->log_prefix
); 
3435                                         /* if we could not get DNS server info */ 
3436                                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"), 
3437                                               targetPrivate
->log_prefix
); 
3439                                 } else if (rankReachability(ns_flags
) < 2) { 
3441                                          * if DNS servers are not (or are no longer) reachable, set 
3442                                          * flags based on the availability of configured (but not 
3445                                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"), 
3446                                               targetPrivate
->log_prefix
); 
3452                                 if (targetPrivate
->dnsPort 
!= NULL
) { 
3453                                         mach_port_t     mp      
= CFMachPortGetPort(targetPrivate
->dnsPort
); 
3455                                         /* cancel the outstanding DNS query */ 
3456                                         SCLog(_sc_debug
, LOG_INFO
, 
3457                                               CFSTR("%scancel DNS query for %s%s%s%s%s"), 
3458                                               targetPrivate
->log_prefix
, 
3459                                               targetPrivate
->name 
!= NULL 
? "name = " : "", 
3460                                               targetPrivate
->name 
!= NULL 
? targetPrivate
->name 
: "", 
3461                                               targetPrivate
->name 
!= NULL 
&& targetPrivate
->serv 
!= NULL 
? ", " : "", 
3462                                               targetPrivate
->serv 
!= NULL 
? "serv = " : "", 
3463                                               targetPrivate
->serv 
!= NULL 
? targetPrivate
->serv 
: ""); 
3464                                         dequeueAsyncDNSQuery(target
); 
3465                                         getaddrinfo_async_cancel(mp
); 
3468                                 /* schedule request to resolve the name again */ 
3469                                 targetPrivate
->needResolve 
= TRUE
; 
3473                 __SCNetworkReachabilityPerform(target
); 
3475                 pthread_mutex_unlock(&targetPrivate
->lock
); 
3477         if (targets 
!= targets_q
)       CFAllocatorDeallocate(NULL
, targets
); 
3481         pthread_mutex_unlock(&hn_lock
); 
3486 #if     !TARGET_OS_IPHONE 
3487 static __inline__ Boolean
 
3488 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities
) 
3491 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU             \ 
3492                                  | kIOPMSystemPowerStateCapabilityNetwork       \ 
3493                                  | kIOPMSystemPowerStateCapabilityDisk) 
3495         if ((power_capabilities 
& POWER_CAPABILITIES_NEED
) != POWER_CAPABILITIES_NEED
) { 
3497                  * we're not awake (from a networking point of view) unless we 
3498                  * have the CPU, disk, *and* network. 
3503         if ((power_capabilities 
& kIOPMSytemPowerStateCapabilitiesMask
) == POWER_CAPABILITIES_NEED
) { 
3505                  * if all we have is the CPU, disk, and network than this must 
3506                  * be a "maintenance" wake. 
3513 #endif  // !TARGET_OS_IPHONE 
3517 rlsPerform(void *info
) 
3520         void                            (*context_release
)(const void *); 
3521         Boolean                         defer           
= FALSE
; 
3523         ReachabilityInfo                reach_info      
= NOT_REACHABLE
; 
3524         SCNetworkReachabilityCallBack   rlsFunction
; 
3525         SCDynamicStoreRef               store           
= NULL
; 
3526         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)info
; 
3527         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3529         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%schecking target reachability"), 
3530               targetPrivate
->log_prefix
); 
3533         pthread_mutex_lock(&targetPrivate
->lock
); 
3535         if (!targetPrivate
->scheduled
) { 
3536                 // if not currently scheduled 
3537                 pthread_mutex_unlock(&targetPrivate
->lock
); 
3541         /* update reachability, notify if status changed */ 
3542         ok 
= __SCNetworkReachabilityGetFlags(&store
, target
, &reach_info
, TRUE
); 
3543         if (store 
!= NULL
) CFRelease(store
); 
3545                 /* if reachability status not available */ 
3546                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%flags not available"), 
3547                       targetPrivate
->log_prefix
); 
3548                 reach_info 
= NOT_REACHABLE
; 
3551 #if     !TARGET_OS_IPHONE 
3553          * We want to defer the notification if this is a maintenance wake *and* 
3554          * the reachability flags that we would be reporting to the application 
3555          * are better than those that we last reported. 
3557         if (!systemIsAwake(power_capabilities
)) { 
3558                 /* if this is a maintenace wake */ 
3559                 reach_info
.sleeping 
= TRUE
; 
3561                 if (rankReachability(reach_info
.flags
) >= rankReachability(targetPrivate
->info
.flags
)) { 
3563                          * don't report the change if the new reachability flags are 
3564                          * the same or "better" 
3567                 } else if (bcmp(&targetPrivate
->last_notify
, &reach_info
, sizeof(reach_info
)) == 0) { 
3568                         /* if we have already posted this change */ 
3572 #endif  // !TARGET_OS_IPHONE 
3574         if (bcmp(&targetPrivate
->info
, &reach_info
, sizeof(reach_info
)) == 0) { 
3575                 SCLog(_sc_debug
, LOG_INFO
, 
3576                       CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"), 
3577                       targetPrivate
->log_prefix
, 
3579                       reach_info
.if_index
, 
3580                       reach_info
.sleeping 
? "*" : ""); 
3581                 pthread_mutex_unlock(&targetPrivate
->lock
); 
3585         SCLog(_sc_debug
, LOG_INFO
, 
3586               CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s"), 
3587               targetPrivate
->log_prefix
, 
3588               targetPrivate
->info
.flags
, 
3589               targetPrivate
->info
.if_index
, 
3590               targetPrivate
->info
.sleeping 
? "*" : "", 
3592               reach_info
.if_index
, 
3593               reach_info
.sleeping 
? "*" : "", 
3594               defer 
? ", deferred" : ""); 
3596         /* update flags / interface */ 
3597         targetPrivate
->info 
= reach_info
; 
3599         /* as needed, defer the notification */ 
3601                 pthread_mutex_unlock(&targetPrivate
->lock
); 
3605         /* save last notification info */ 
3606         targetPrivate
->last_notify 
= reach_info
; 
3609         rlsFunction 
= targetPrivate
->rlsFunction
; 
3610         if (targetPrivate
->rlsContext
.retain 
!= NULL
) { 
3611                 context_info    
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
); 
3612                 context_release 
= targetPrivate
->rlsContext
.release
; 
3614                 context_info    
= targetPrivate
->rlsContext
.info
; 
3615                 context_release 
= NULL
; 
3618         pthread_mutex_unlock(&targetPrivate
->lock
); 
3620         if (rlsFunction 
!= NULL
) { 
3621                 (*rlsFunction
)(target
, reach_info
.flags
, context_info
); 
3624         if (context_release 
!= NULL
) { 
3625                 (*context_release
)(context_info
); 
3633 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef       target
, 
3634                                  SCNetworkReachabilityCallBack  callout
, 
3635                                  SCNetworkReachabilityContext   
*context
) 
3637         SCNetworkReachabilityPrivateRef targetPrivate 
= (SCNetworkReachabilityPrivateRef
)target
; 
3639         pthread_mutex_lock(&targetPrivate
->lock
); 
3641         if (targetPrivate
->rlsContext
.release 
!= NULL
) { 
3642                 /* let go of the current context */ 
3643                 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
); 
3646         targetPrivate
->rlsFunction                      
= callout
; 
3647         targetPrivate
->rlsContext
.info                  
= NULL
; 
3648         targetPrivate
->rlsContext
.retain                
= NULL
; 
3649         targetPrivate
->rlsContext
.release               
= NULL
; 
3650         targetPrivate
->rlsContext
.copyDescription       
= NULL
; 
3652                 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
)); 
3653                 if (context
->retain 
!= NULL
) { 
3654                         targetPrivate
->rlsContext
.info 
= (void *)(*context
->retain
)(context
->info
); 
3658         pthread_mutex_unlock(&targetPrivate
->lock
); 
3665 reachRLSCopyDescription(const void *info
) 
3667         SCNetworkReachabilityRef                target  
= (SCNetworkReachabilityRef
)info
; 
3669         return CFStringCreateWithFormat(NULL
, 
3671                                         CFSTR("<SCNetworkReachability RLS> {target = %p}"), 
3677 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef     target
, 
3678                                            CFRunLoopRef                 runLoop
, 
3679                                            CFStringRef                  runLoopMode
, 
3680 #if     !TARGET_OS_IPHONE 
3681                                            dispatch_queue_t             queue
, 
3682 #else   // !TARGET_OS_IPHONE 
3684 #endif  // !TARGET_OS_IPHONE 
3687         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3688         Boolean                         init            
= FALSE
; 
3692                 pthread_mutex_lock(&hn_lock
); 
3694         pthread_mutex_lock(&targetPrivate
->lock
); 
3696 #if     !TARGET_OS_IPHONE 
3697         if ((targetPrivate
->dispatchQueue 
!= NULL
) ||           // if we are already scheduled with a dispatch queue 
3698             ((queue 
!= NULL
) && targetPrivate
->scheduled
)) {    // if we are already scheduled on a CFRunLoop 
3699                 _SCErrorSet(kSCStatusInvalidArgument
); 
3702 #endif  // !TARGET_OS_IPHONE 
3704         /* schedule the SCNetworkReachability run loop source */ 
3706         if (!onDemand 
&& (hn_store 
== NULL
)) { 
3708                  * if we are not monitoring any hosts, start watching 
3710                 if (!dns_configuration_watch()) { 
3712                         _SCErrorSet(kSCStatusFailed
); 
3716                 hn_store 
= SCDynamicStoreCreate(NULL
, 
3717                                                 CFSTR("SCNetworkReachability"), 
3718                                                 __SCNetworkReachabilityHandleChanges
, 
3720                 if (hn_store 
== NULL
) { 
3721                         SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed")); 
3725                 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
); 
3727 #if     !TARGET_OS_IPHONE 
3728                 hn_dispatchQueue 
= dispatch_queue_create("com.apple.SCNetworkReachabilty.network_changes", NULL
); 
3729                 if (hn_dispatchQueue 
== NULL
) { 
3730                         SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed")); 
3731                         _SCErrorSet(kSCStatusFailed
); 
3732                         CFRelease(hn_store
); 
3736                 CFRetain(hn_store
);     // Note: will be released when the dispatch queue is released 
3737                 dispatch_set_context(hn_dispatchQueue
, (void *)hn_store
); 
3738                 dispatch_set_finalizer_f(hn_dispatchQueue
, (dispatch_function_t
)CFRelease
); 
3740                 ok 
= SCDynamicStoreSetDispatchQueue(hn_store
, hn_dispatchQueue
); 
3742                         SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetDispatchQueue() failed")); 
3743                         dispatch_release(hn_dispatchQueue
); 
3744                         hn_dispatchQueue 
= NULL
; 
3745                         CFRelease(hn_store
); 
3749 #else   // !TARGET_OS_IPHONE 
3750                 hn_storeRLS 
= SCDynamicStoreCreateRunLoopSource(NULL
, hn_store
, 0); 
3751                 hn_rlList   
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
3752 #endif  // !TARGET_OS_IPHONE 
3753                 hn_targets  
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
); 
3756         if (!targetPrivate
->scheduled
) { 
3757                 CFRunLoopSourceContext  context 
= { 0                           // version 
3758                                                   , (void *)target              
// info 
3759                                                   , CFRetain                    
// retain 
3760                                                   , CFRelease                   
// release 
3761                                                   , reachRLSCopyDescription     
// copyDescription 
3766                                                   , rlsPerform                  
// perform 
3769                 if (runLoop 
!= NULL
) { 
3770                         targetPrivate
->rls    
= CFRunLoopSourceCreate(NULL
, 0, &context
); 
3771                         targetPrivate
->rlList 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
3774                 targetPrivate
->scheduled 
= TRUE
; 
3775                 if (targetPrivate
->type 
== reachabilityTypeName
) { 
3776                         targetPrivate
->needResolve 
= TRUE
; 
3781 #if     !TARGET_OS_IPHONE 
3782         if (queue 
!= NULL
) { 
3783                 targetPrivate
->dispatchQueue 
= queue
; 
3784                 dispatch_retain(targetPrivate
->dispatchQueue
); 
3786 #endif  // !TARGET_OS_IPHONE 
3788                 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) { 
3790                          * if we do not already have host notifications scheduled with 
3791                          * this runLoop / runLoopMode 
3793                         CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
); 
3795                         if (targetPrivate
->dnsRLS 
!= NULL
) { 
3796                                 /* if we have an active async DNS query too */ 
3797                                 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
); 
3801                 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
); 
3803 #if     TARGET_OS_IPHONE 
3805                         /* schedule the global SCDynamicStore run loop source */ 
3807                         if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) { 
3809                                  * if we do not already have SC notifications scheduled with 
3810                                  * this runLoop / runLoopMode 
3812                                 CFRunLoopAddSource(runLoop
, hn_storeRLS
, runLoopMode
); 
3815                         _SC_schedule(target
, runLoop
, runLoopMode
, hn_rlList
); 
3817 #endif  // TARGET_OS_IPHONE 
3820         CFSetAddValue(hn_targets
, target
); 
3823                 ReachabilityInfo        reach_info      
= NOT_REACHABLE
; 
3824                 SCDynamicStoreRef       store           
= NULL
; 
3827                  * if we have yet to schedule SC notifications for this address 
3828                  * - initialize current reachability status 
3830                 if (__SCNetworkReachabilityGetFlags(&store
, target
, &reach_info
, TRUE
)) { 
3832                          * if reachability status available 
3834                          * - schedule notification to report status via callback 
3836                         targetPrivate
->info 
= reach_info
; 
3837                         __SCNetworkReachabilityPerform(target
); 
3839                         /* if reachability status not available, async lookup started */ 
3840                         targetPrivate
->info 
= NOT_REACHABLE
; 
3842                 if (store 
!= NULL
) CFRelease(store
); 
3845         if (targetPrivate
->onDemandServer 
!= NULL
) { 
3846                 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, queue
, TRUE
); 
3853         pthread_mutex_unlock(&targetPrivate
->lock
); 
3855                 pthread_mutex_unlock(&hn_lock
); 
3862 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef   target
, 
3863                                              CFRunLoopRef               runLoop
, 
3864                                              CFStringRef                runLoopMode
, 
3867         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3872                 pthread_mutex_lock(&hn_lock
); 
3874         pthread_mutex_lock(&targetPrivate
->lock
); 
3876 #if     !TARGET_OS_IPHONE 
3877         if (((runLoop 
== NULL
) && (targetPrivate
->dispatchQueue 
== NULL
)) ||    // if we should be scheduled on a dispatch queue (but are not) 
3878             ((runLoop 
!= NULL
) && (targetPrivate
->dispatchQueue 
!= NULL
))) {    // if we should be scheduled on a CFRunLoop (but are not) 
3879                 _SCErrorSet(kSCStatusInvalidArgument
); 
3882 #endif  // !TARGET_OS_IPHONE 
3884         if (!targetPrivate
->scheduled
) { 
3885                 // if not currently scheduled 
3886                 _SCErrorSet(kSCStatusInvalidArgument
); 
3890         // first, unschedule the target specific sources 
3891 #if     !TARGET_OS_IPHONE 
3892         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
3893                 if (targetPrivate
->onDemandServer 
!= NULL
) { 
3894                         __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
); 
3897 #endif  // !TARGET_OS_IPHONE 
3899                 if (!_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) { 
3900                         // if not currently scheduled 
3901                         _SCErrorSet(kSCStatusInvalidArgument
); 
3905                 if (targetPrivate
->onDemandServer 
!= NULL
) { 
3906                         __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, TRUE
); 
3909                 n 
= CFArrayGetCount(targetPrivate
->rlList
); 
3910                 if ((n 
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) { 
3911                         // if target is no longer scheduled for this runLoop / runLoopMode 
3912                         CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
); 
3914                         if (targetPrivate
->dnsRLS 
!= NULL
) { 
3915                                 // if we have an active async DNS query too 
3916                                 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
); 
3920                                 // if *all* notifications have been unscheduled 
3921                                 CFRelease(targetPrivate
->rlList
); 
3922                                 targetPrivate
->rlList 
= NULL
; 
3923                                 CFRunLoopSourceInvalidate(targetPrivate
->rls
); 
3924                                 CFRelease(targetPrivate
->rls
); 
3925                                 targetPrivate
->rls 
= NULL
; 
3931                 // if *all* notifications have been unscheduled 
3932                 targetPrivate
->scheduled 
= FALSE
; 
3935                         CFSetRemoveValue(hn_targets
, target
);   // cleanup notification resources 
3938                 if (targetPrivate
->dnsPort 
!= NULL
) { 
3939                         mach_port_t     mp      
= CFMachPortGetPort(targetPrivate
->dnsPort
); 
3941                         // if we have an active async DNS query 
3942                         dequeueAsyncDNSQuery(target
); 
3943                         getaddrinfo_async_cancel(mp
); 
3947 #if     !TARGET_OS_IPHONE 
3948         if (runLoop 
== NULL
) { 
3949                 dispatch_release(targetPrivate
->dispatchQueue
); 
3950                 targetPrivate
->dispatchQueue 
= NULL
; 
3952 #endif  // !TARGET_OS_IPHONE 
3954         // now, unschedule the global dynamic store source 
3955 #if     TARGET_OS_IPHONE 
3957                 (void)_SC_unschedule(target
, runLoop
, runLoopMode
, hn_rlList
, FALSE
); 
3959                 n 
= CFArrayGetCount(hn_rlList
); 
3960                 if ((n 
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, hn_rlList
)) { 
3962                          * if we no longer have any addresses scheduled for 
3963                          * this runLoop / runLoopMode 
3965                         CFRunLoopRemoveSource(runLoop
, hn_storeRLS
, runLoopMode
); 
3968                                 if (CFEqual(runLoopMode
, kCFRunLoopCommonModes
)) { 
3971                                         modes 
= CFRunLoopCopyAllModes(runLoop
); 
3972                                         if (modes 
!= NULL
) { 
3974                                                 CFIndex n       
= CFArrayGetCount(modes
); 
3976                                                 for (i 
= 0; i 
< n
; i
++) { 
3979                                                         mode 
= CFArrayGetValueAtIndex(modes
, i
); 
3980                                                         if (_SC_isScheduled(NULL
, runLoop
, mode
, hn_rlList
)) { 
3982                                                                  * removing kCFRunLoopCommonModes cleaned up more 
3983                                                                  * than we wanted.  Add back the modes that were 
3984                                                                  * expect to be present. 
3986                                                                 CFRunLoopAddSource(runLoop
, hn_storeRLS
, mode
); 
3992                                 } else if (_SC_isScheduled(NULL
, runLoop
, kCFRunLoopCommonModes
, hn_rlList
)) { 
3994                                          * if we are still scheduling kCFRunLoopCommonModes, make sure that 
3995                                          * none of the common modes were inadvertently removed. 
3997                                         CFRunLoopAddSource(runLoop
, hn_storeRLS
, kCFRunLoopCommonModes
); 
4002 #endif  // TARGET_OS_IPHONE 
4004         n 
= CFSetGetCount(hn_targets
); 
4006                 // if we are no longer monitoring any targets 
4007 #if     !TARGET_OS_IPHONE 
4008                 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
); 
4009                 dispatch_release(hn_dispatchQueue
); 
4010                 hn_dispatchQueue 
= NULL
; 
4011 #else   // !TARGET_OS_IPHONE 
4012                 CFRunLoopSourceInvalidate(hn_storeRLS
); 
4013                 CFRelease(hn_storeRLS
); 
4015                 CFRelease(hn_rlList
); 
4017 #endif  // !TARGET_OS_IPHONE 
4018                 CFRelease(hn_store
); 
4020                 CFRelease(hn_targets
); 
4024                  * until we start monitoring again, ensure that 
4025                  * any resources associated with tracking the 
4026                  * DNS configuration have been released. 
4028                 dns_configuration_unwatch(); 
4035         pthread_mutex_unlock(&targetPrivate
->lock
); 
4037                 pthread_mutex_unlock(&hn_lock
); 
4043 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef       target
, 
4044                                          CFRunLoopRef                   runLoop
, 
4045                                          CFStringRef                    runLoopMode
) 
4047         if (!isA_SCNetworkReachability(target
) || (runLoop 
== NULL
) || (runLoopMode 
== NULL
)) { 
4048                 _SCErrorSet(kSCStatusInvalidArgument
); 
4052         return __SCNetworkReachabilityScheduleWithRunLoop(target
, runLoop
, runLoopMode
, NULL
, FALSE
); 
4056 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef     target
, 
4057                                            CFRunLoopRef                 runLoop
, 
4058                                            CFStringRef                  runLoopMode
) 
4060         if (!isA_SCNetworkReachability(target
) || (runLoop 
== NULL
) || (runLoopMode 
== NULL
)) { 
4061                 _SCErrorSet(kSCStatusInvalidArgument
); 
4065         return __SCNetworkReachabilityUnscheduleFromRunLoop(target
, runLoop
, runLoopMode
, FALSE
); 
4068 #if     !TARGET_OS_IPHONE 
4070 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef  target
, 
4071                                       dispatch_queue_t          queue
) 
4075         if (!isA_SCNetworkReachability(target
)) { 
4076                 _SCErrorSet(kSCStatusInvalidArgument
); 
4080         if (queue 
!= NULL
) { 
4081                 ok 
= __SCNetworkReachabilityScheduleWithRunLoop(target
, NULL
, NULL
, queue
, FALSE
); 
4083                 ok 
= __SCNetworkReachabilityUnscheduleFromRunLoop(target
, NULL
, NULL
, FALSE
); 
4088 #endif  // !TARGET_OS_IPHONE