2  * Copyright (c) 2003-2011 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> 
  37 #include <dispatch/dispatch.h> 
  38 #include <CoreFoundation/CoreFoundation.h> 
  39 #include <CoreFoundation/CFRuntime.h> 
  40 #include <SystemConfiguration/SystemConfiguration.h> 
  41 #include <SystemConfiguration/SCValidation.h> 
  42 #include <SystemConfiguration/SCPrivate.h> 
  44 #include <libkern/OSAtomic.h> 
  47 #include <IOKit/pwr_mgt/IOPMLibPrivate.h> 
  48 #endif  // !TARGET_OS_IPHONE 
  52 #include <netinet/in.h> 
  53 #include <arpa/inet.h> 
  55 #include <netdb_async.h> 
  58 #include <sys/ioctl.h> 
  59 #include <sys/socket.h> 
  61 #include <net/if_dl.h> 
  62 #include <net/if_types.h> 
  63 #define KERNEL_PRIVATE 
  64 #include <net/route.h> 
  68 #define s6_addr16 __u6_addr.__u6_addr16 
  71 #include <ppp/ppp_msg.h> 
  73 #if     !TARGET_IPHONE_SIMULATOR 
  74 #include <ppp/PPPControllerPriv.h> 
  75 #endif  // !TARGET_IPHONE_SIMULATOR 
  79 #if     ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR 
  80 #define HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL 
  81 #endif  // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR 
  83 #if     ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_EMBEDDED_OTHER 
  84 #define HAVE_IPSEC_STATUS 
  85 #define HAVE_VPN_STATUS 
  86 #endif  // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_EMBEDDED_OTHER 
  91 #ifdef  HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL 
  94 _getaddrinfo_interface_async_call(const char                    *nodename
, 
  96                                   const struct addrinfo         
*hints
, 
  97                                   const char                    *interface
, 
  98                                   getaddrinfo_async_callback    callback
, 
 100 #endif  /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */ 
 103 #define kSCNetworkReachabilityFlagsFirstResolvePending  (1<<31) 
 109 typedef enum { NO 
= 0, YES
, UNKNOWN 
}   lazyBoolean
; 
 113         reachabilityTypeAddress
, 
 114         reachabilityTypeAddressPair
, 
 119 // how long (minimum time, us) to wait before retrying DNS query after EAI_NONAME 
 120 #define EAI_NONAME_RETRY_DELAY_USEC     250000 
 122 // how long (maximum time, us) after DNS configuration change we accept EAI_NONAME 
 124 #define EAI_NONAME_RETRY_LIMIT_USEC     2500000 
 127 static CFStringRef      
__SCNetworkReachabilityCopyDescription  (CFTypeRef cf
); 
 128 static void             __SCNetworkReachabilityDeallocate       (CFTypeRef cf
); 
 129 static void             rlsPerform(void *info
); 
 133 __SCNetworkReachabilityScheduleWithRunLoop      (SCNetworkReachabilityRef       target
, 
 134                                                  CFRunLoopRef                   runLoop
, 
 135                                                  CFStringRef                    runLoopMode
, 
 136                                                  dispatch_queue_t               queue
, 
 140 __SCNetworkReachabilityUnscheduleFromRunLoop    (SCNetworkReachabilityRef       target
, 
 141                                                  CFRunLoopRef                   runLoop
, 
 142                                                  CFStringRef                    runLoopMode
, 
 147         SCNetworkReachabilityFlags      flags
; 
 148         unsigned int                    if_index
; 
 155         /* base CFType information */ 
 156         CFRuntimeBase                   cfBase
; 
 159         pthread_mutex_t                 lock
; 
 164         /* target host name */ 
 167         struct addrinfo                 hints
; 
 169         CFArrayRef                      resolvedAddress
;        /* CFArray[CFData] */ 
 170         int                             resolvedAddressError
; 
 172         /* [scoped routing] interface constraints */ 
 173         unsigned int                    if_index
; 
 174         char                            if_name
[IFNAMSIZ
]; 
 176         /* local & remote addresses */ 
 177         struct sockaddr                 
*localAddress
; 
 178         struct sockaddr                 
*remoteAddress
; 
 180         /* current reachability flags */ 
 181         ReachabilityInfo                info
; 
 182         ReachabilityInfo                last_notify
; 
 184         /* run loop source, callout, context, rl scheduling info */ 
 186         CFRunLoopSourceRef              rls
; 
 187         SCNetworkReachabilityCallBack   rlsFunction
; 
 188         SCNetworkReachabilityContext    rlsContext
; 
 189         CFMutableArrayRef               rlList
; 
 191         dispatch_queue_t                dispatchQueue
;          // SCNetworkReachabilitySetDispatchQueue 
 192         dispatch_queue_t                asyncDNSQueue
; 
 193         dispatch_source_t               asyncDNSSource
; 
 195         /* [async] DNS query info */ 
 198         CFMachPortRef                   dnsPort
; 
 199         CFRunLoopSourceRef              dnsRLS
; 
 200         struct timeval                  dnsQueryStart
; 
 201         struct timeval                  dnsQueryEnd
; 
 202         dispatch_source_t               dnsRetry
;               // != NULL if DNS retry request queued 
 203         int                             dnsRetryCount
;          // number of retry attempts 
 205         /* [async] processing info */ 
 206         struct timeval                  last_dns
; 
 209         Boolean                         onDemandBypass
; 
 210         CFStringRef                     onDemandName
; 
 211         CFStringRef                     onDemandRemoteAddress
; 
 212         SCNetworkReachabilityRef        onDemandServer
; 
 213         CFStringRef                     onDemandServiceID
; 
 219 } SCNetworkReachabilityPrivate
, *SCNetworkReachabilityPrivateRef
; 
 222 static CFTypeID __kSCNetworkReachabilityTypeID  
= _kCFRuntimeNotATypeID
; 
 225 static const CFRuntimeClass __SCNetworkReachabilityClass 
= { 
 227         "SCNetworkReachability",                // className 
 230         __SCNetworkReachabilityDeallocate
,      // dealloc 
 233         NULL
,                                   // copyFormattingDesc 
 234         __SCNetworkReachabilityCopyDescription  
// copyDebugDesc 
 238 static pthread_once_t           initialized     
= PTHREAD_ONCE_INIT
; 
 239 static const ReachabilityInfo   NOT_REACHABLE   
= { 0,          0,      FALSE 
}; 
 240 static const ReachabilityInfo   NOT_REPORTED    
= { 0xFFFFFFFF, 0,      FALSE 
}; 
 241 static int                      rtm_seq         
= 0; 
 244 static const struct timeval     TIME_ZERO       
= { 0, 0 }; 
 247 #if     !TARGET_OS_IPHONE 
 249  * Power capabilities (sleep/wake) 
 251 static IOPMSystemPowerStateCapabilities power_capabilities      
= kIOPMSytemPowerStateCapabilitiesMask
; 
 252 #endif  // !TARGET_OS_IPHONE 
 256  * host "something has changed" notifications 
 259 static pthread_mutex_t          hn_lock         
= PTHREAD_MUTEX_INITIALIZER
; 
 260 static SCDynamicStoreRef        hn_store        
= NULL
; 
 261 static dispatch_queue_t         hn_dispatchQueue 
= NULL
; 
 262 static CFMutableSetRef          hn_targets      
= NULL
; 
 270         dns_config_t    
*config
; 
 272 } dns_configuration_t
; 
 275 static pthread_mutex_t          dns_lock                
= PTHREAD_MUTEX_INITIALIZER
; 
 276 static dns_configuration_t      
*dns_configuration      
= NULL
; 
 277 static int                      dns_token
; 
 278 static Boolean                  dns_token_valid         
= FALSE
; 
 281 static __inline__ CFTypeRef
 
 282 isA_SCNetworkReachability(CFTypeRef obj
) 
 284         return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID())); 
 289 __dns_query_start(struct timeval        
*dnsQueryStart
, 
 290                   struct timeval        
*dnsQueryEnd
) 
 292         (void) gettimeofday(dnsQueryStart
, NULL
); 
 293         *dnsQueryEnd 
= TIME_ZERO
; 
 300 __dns_query_end(SCNetworkReachabilityRef        target
, 
 303                 struct timeval                  
*dnsQueryStart
, 
 304                 struct timeval                  
*dnsQueryEnd
) 
 306         struct timeval                  dnsQueryElapsed
; 
 307         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
 309         (void) gettimeofday(dnsQueryEnd
, NULL
); 
 315         if (dnsQueryStart
->tv_sec 
== 0) { 
 319         timersub(dnsQueryEnd
, dnsQueryStart
, &dnsQueryElapsed
); 
 320         SCLog(TRUE
, LOG_INFO
, 
 321               CFSTR("%s%ssync DNS complete%s (query time = %d.%3.3d)"), 
 322               targetPrivate
->log_prefix
, 
 324               found 
? "" : ", host not found", 
 325               dnsQueryElapsed
.tv_sec
, 
 326               dnsQueryElapsed
.tv_usec 
/ 1000); 
 332 static __inline__ Boolean
 
 333 __reach_equal(ReachabilityInfo 
*r1
, ReachabilityInfo 
*r2
) 
 335         if (r1
->flags 
!= r2
->flags
) { 
 336                 // if the reachability flags changed 
 340         if (r1
->if_index 
!= r2
->if_index
) { 
 341                 // if the target interface changed 
 345         if ((r1
->sleeping 
!= r2
->sleeping
) && !r2
->sleeping
) { 
 346                 // if our sleep/wake status changed and if we 
 347                 // are no longer sleeping 
 356         SCDynamicStoreRef       store
; 
 359         CFDictionaryRef         dict
; 
 362         const void *            keys_q
[N_QUICK
]; 
 363         const void **           values
; 
 364         const void *            values_q
[N_QUICK
]; 
 365 } ReachabilityStoreInfo
, *ReachabilityStoreInfoRef
; 
 369 initReachabilityStoreInfo(ReachabilityStoreInfoRef store_info
) 
 371         bzero(store_info
, sizeof(ReachabilityStoreInfo
)); 
 377 updateReachabilityStoreInfo(ReachabilityStoreInfoRef    store_info
, 
 378                             SCDynamicStoreRef           
*storeP
, 
 379                             sa_family_t                 sa_family
) 
 382         CFMutableArrayRef       patterns
; 
 386                         store_info
->entity 
= NULL
; 
 389                         store_info
->entity 
= kSCEntNetIPv4
; 
 392                         store_info
->entity 
= kSCEntNetIPv6
; 
 398         if (store_info
->dict 
!= NULL
) { 
 399                 // if info already available 
 403         if (store_info
->store 
== NULL
) { 
 404                 store_info
->store 
= (storeP 
!= NULL
) ? *storeP 
: NULL
; 
 405                 if (store_info
->store 
== NULL
) { 
 406                         store_info
->store 
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
); 
 407                         if (store_info
->store 
== NULL
) { 
 408                                 SCLog(TRUE
, LOG_ERR
, CFSTR("updateReachabilityStoreInfo SCDynamicStoreCreate() failed")); 
 412                         if (storeP 
!= NULL
) { 
 413                                 /// pass back the allocated SCDynamicStoreRef 
 414                                 *storeP 
= store_info
->store
; 
 417                                 store_info
->storeAdded 
= TRUE
; 
 422         if (sa_family 
== AF_UNSPEC
) { 
 423                 // if the address family was not specified than 
 424                 // all we wanted, for now, was to establish the 
 425                 // SCDynamicStore session 
 429         patterns 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 431         // get info for IPv4 services 
 432         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 433                                                               kSCDynamicStoreDomainSetup
, 
 436         CFArrayAppendValue(patterns
, pattern
); 
 438         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 439                                                               kSCDynamicStoreDomainState
, 
 442         CFArrayAppendValue(patterns
, pattern
); 
 445         // get info for IPv6 services 
 446         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 447                                                               kSCDynamicStoreDomainSetup
, 
 450         CFArrayAppendValue(patterns
, pattern
); 
 452         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 453                                                               kSCDynamicStoreDomainState
, 
 456         CFArrayAppendValue(patterns
, pattern
); 
 459         // get info for PPP services 
 460         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 461                                                               kSCDynamicStoreDomainSetup
, 
 464         CFArrayAppendValue(patterns
, pattern
); 
 466         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 467                                                               kSCDynamicStoreDomainState
, 
 470         CFArrayAppendValue(patterns
, pattern
); 
 473 #if     !TARGET_IPHONE_SIMULATOR 
 474         // get info for VPN services 
 475         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 476                                                               kSCDynamicStoreDomainSetup
, 
 479         CFArrayAppendValue(patterns
, pattern
); 
 481         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 482                                                               kSCDynamicStoreDomainState
, 
 485         CFArrayAppendValue(patterns
, pattern
); 
 487 #endif  // !TARGET_IPHONE_SIMULATOR 
 489         // get info for IPSec services 
 490 //      pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 
 491 //                                                            kSCDynamicStoreDomainSetup, 
 494 //      CFArrayAppendValue(patterns, pattern); 
 495 //      CFRelease(pattern); 
 496         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 497                                                               kSCDynamicStoreDomainState
, 
 500         CFArrayAppendValue(patterns
, pattern
); 
 503         // get info to identify "available" services 
 504         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 505                                                               kSCDynamicStoreDomainSetup
, 
 508         CFArrayAppendValue(patterns
, pattern
); 
 512         // get the SCDynamicStore info 
 513         store_info
->dict 
= SCDynamicStoreCopyMultiple(store_info
->store
, NULL
, patterns
); 
 515         if (store_info
->dict 
== NULL
) { 
 519         // and extract the keys/values for post-processing 
 520         store_info
->n 
= CFDictionaryGetCount(store_info
->dict
); 
 521         if (store_info
->n 
> 0) { 
 522                 if (store_info
->n 
<= (CFIndex
)(sizeof(store_info
->keys_q
) / sizeof(CFTypeRef
))) { 
 523                         store_info
->keys   
= store_info
->keys_q
; 
 524                         store_info
->values 
= store_info
->values_q
; 
 526                         store_info
->keys   
= CFAllocatorAllocate(NULL
, store_info
->n 
* sizeof(CFTypeRef
), 0); 
 527                         store_info
->values 
= CFAllocatorAllocate(NULL
, store_info
->n 
* sizeof(CFTypeRef
), 0); 
 529                 CFDictionaryGetKeysAndValues(store_info
->dict
, 
 539 freeReachabilityStoreInfo(ReachabilityStoreInfoRef store_info
) 
 541         if ((store_info
->n 
> 0) && (store_info
->keys 
!= store_info
->keys_q
)) { 
 542                 CFAllocatorDeallocate(NULL
, store_info
->keys
); 
 543                 store_info
->keys 
= NULL
; 
 545                 CFAllocatorDeallocate(NULL
, store_info
->values
); 
 546                 store_info
->values 
= NULL
; 
 549         if (store_info
->dict 
!= NULL
) { 
 550                 CFRelease(store_info
->dict
); 
 551                 store_info
->dict 
= NULL
; 
 554         if (store_info
->storeAdded 
&& (store_info
->store 
!= NULL
)) { 
 555                 CFRelease(store_info
->store
); 
 556                 store_info
->store 
= NULL
; 
 564 updatePPPStatus(ReachabilityStoreInfoRef        store_info
, 
 565                 const struct sockaddr           
*sa
, 
 567                 SCNetworkReachabilityFlags      
*flags
, 
 568                 CFStringRef                     
*ppp_server
, 
 569                 const char                      *log_prefix
) 
 573         int             sc_status       
= kSCStatusNoKey
; 
 575         if (!updateReachabilityStoreInfo(store_info
, NULL
, sa
->sa_family
)) { 
 576                 return kSCStatusReachabilityUnknown
; 
 579         if (store_info
->n 
<= 0) { 
 581                 return kSCStatusNoKey
; 
 584         // look for the [PPP] service which matches the provided interface 
 586         ppp_if 
= CFStringCreateWithCStringNoCopy(NULL
, 
 588                                                  kCFStringEncodingASCII
, 
 591         for (i
=0; i 
< store_info
->n
; i
++) { 
 592                 CFArrayRef      components
; 
 595                 CFDictionaryRef p_setup
; 
 596                 CFDictionaryRef p_state
; 
 599                 CFStringRef     service         
= NULL
; 
 600                 CFStringRef     s_key           
= (CFStringRef
)    store_info
->keys
[i
]; 
 601                 CFDictionaryRef s_dict          
= (CFDictionaryRef
)store_info
->values
[i
]; 
 604                 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) { 
 608                 if (!CFStringHasSuffix(s_key
, store_info
->entity
) || 
 609                     !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) { 
 610                         continue;       // if not an active IPv4 or IPv6 entity 
 613                 s_if 
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
); 
 614                 if (!isA_CFString(s_if
)) { 
 615                         continue;       // if no interface 
 618                 if (!CFEqual(ppp_if
, s_if
)) { 
 619                         continue;       // if not this interface 
 622                 // extract the service ID, get the PPP "state" entity for 
 623                 // the "Status", and get the PPP "setup" entity for the 
 624                 // the "DialOnDemand" flag 
 625                 components 
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/")); 
 626                 if (CFArrayGetCount(components
) != 5) { 
 627                         CFRelease(components
); 
 630                 service 
= CFArrayGetValueAtIndex(components
, 3); 
 631                 key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 632                                                                   kSCDynamicStoreDomainState
, 
 635                 p_state 
= CFDictionaryGetValue(store_info
->dict
, key
); 
 637                 key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 638                                                                   kSCDynamicStoreDomainSetup
, 
 641                 p_setup 
= CFDictionaryGetValue(store_info
->dict
, key
); 
 643                 CFRelease(components
); 
 645                 // ensure that this is a PPP service 
 646                 if (!isA_CFDictionary(p_state
)) { 
 650                 sc_status 
= kSCStatusOK
; 
 652                 *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
 655                 if (ppp_server 
!= NULL
) { 
 656                         *ppp_server 
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress")); 
 657                         *ppp_server 
= isA_CFString(*ppp_server
); 
 658                         if (*ppp_server 
!= NULL
) { 
 659                                 CFRetain(*ppp_server
); 
 664                 if (!CFDictionaryGetValueIfPresent(p_state
, 
 666                                                    (const void **)&num
) || 
 667                     !isA_CFNumber(num
) || 
 668                     !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_status
)) { 
 671                 switch (ppp_status
) { 
 673                                 // if we're really UP and RUNNING 
 676                                 // if we're effectively UP and RUNNING 
 679                                 // if we're not connected at all 
 680                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  PPP link idle"), 
 682                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
 684                         case PPP_STATERESERVED 
: 
 685                                 // if we're not connected at all 
 686                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  PPP link idle, dial-on-traffic to connect"), 
 688                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
 691                                 // if we're in the process of [dis]connecting 
 692                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  PPP link, connection in progress"), 
 694                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
 698                 // get PPP dial-on-traffic status 
 699                 if (isA_CFDictionary(p_setup
) && 
 700                     CFDictionaryGetValueIfPresent(p_setup
, 
 701                                                   kSCPropNetPPPDialOnDemand
, 
 702                                                   (const void **)&num
) && 
 704                     CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
) && 
 706                         *flags 
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
; 
 707                         if (ppp_status 
== PPP_IDLE
) { 
 708                                 *flags 
|= kSCNetworkReachabilityFlagsInterventionRequired
; 
 722 updatePPPAvailable(ReachabilityStoreInfoRef     store_info
, 
 723                    const struct sockaddr        
*sa
, 
 724                    SCNetworkReachabilityFlags   
*flags
, 
 725                    const char                   *log_prefix
) 
 728         int             sc_status       
= kSCStatusNoKey
; 
 730         if (!updateReachabilityStoreInfo(store_info
, 
 732                                          (sa 
!= NULL
) ? sa
->sa_family 
: AF_INET
)) { 
 733                 return kSCStatusReachabilityUnknown
; 
 736         if (store_info
->n 
<= 0) { 
 738                 return kSCStatusNoKey
; 
 741         // look for an available service which will provide connectivity 
 742         // for the requested address family. 
 744         for (i 
= 0; i 
< store_info
->n
; i
++) { 
 745                 CFArrayRef      components
; 
 746                 Boolean         found           
= FALSE
; 
 748                 CFDictionaryRef i_dict
; 
 750                 CFDictionaryRef p_dict
; 
 752                 CFStringRef     s_key           
= (CFStringRef
)    store_info
->keys
[i
]; 
 753                 CFDictionaryRef s_dict          
= (CFDictionaryRef
)store_info
->values
[i
]; 
 755                 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) { 
 759                 if (!CFStringHasSuffix(s_key
, store_info
->entity
) || 
 760                     !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainSetup
)) { 
 761                         continue;       // if not an IPv4 or IPv6 entity 
 764                 // extract service ID 
 765                 components 
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/")); 
 766                 if (CFArrayGetCount(components
) != 5) { 
 767                         CFRelease(components
); 
 770                 service 
= CFArrayGetValueAtIndex(components
, 3); 
 772                 // check for [non-VPN] PPP entity 
 773                 p_key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 774                                                                     kSCDynamicStoreDomainSetup
, 
 777                 p_dict 
= CFDictionaryGetValue(store_info
->dict
, p_key
); 
 780                 i_key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 781                                                                     kSCDynamicStoreDomainSetup
, 
 784                 i_dict 
= CFDictionaryGetValue(store_info
->dict
, i_key
); 
 787                 if (isA_CFDictionary(p_dict
) && 
 788                     isA_CFDictionary(i_dict
) && 
 789                     CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) { 
 792                         // we have a PPP service for this address family 
 795                         *flags 
|= kSCNetworkReachabilityFlagsReachable
; 
 796                         *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
 797                         *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
 799                         // get PPP dial-on-traffic status 
 800                         num 
= CFDictionaryGetValue(p_dict
, kSCPropNetPPPDialOnDemand
); 
 801                         if (isA_CFNumber(num
)) { 
 804                                 if (CFNumberGetValue(num
, kCFNumberSInt32Type
, &ppp_demand
)) { 
 806                                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionOnTraffic
; 
 812                                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  status    = isReachable (after connect)"), 
 814                                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  service   = %@"), 
 821                 CFRelease(components
); 
 824                         sc_status 
= kSCStatusOK
; 
 833 #if     !TARGET_IPHONE_SIMULATOR 
 835 updateVPNStatus(ReachabilityStoreInfoRef        store_info
, 
 836                 const struct sockaddr           
*sa
, 
 838                 SCNetworkReachabilityFlags      
*flags
, 
 839                 CFStringRef                     
*vpn_server
, 
 840                 const char                      *log_prefix
) 
 844         int             sc_status       
= kSCStatusNoKey
; 
 846         if (!updateReachabilityStoreInfo(store_info
, NULL
, sa
->sa_family
)) { 
 847                 return kSCStatusReachabilityUnknown
; 
 850         if (store_info
->n 
<= 0) { 
 852                 return kSCStatusNoKey
; 
 855         // look for the [VPN] service which matches the provided interface 
 857         vpn_if 
= CFStringCreateWithCStringNoCopy(NULL
, 
 859                                                  kCFStringEncodingASCII
, 
 862         for (i
=0; i 
< store_info
->n
; i
++) { 
 863                 CFArrayRef      components
; 
 866                 CFDictionaryRef p_state
; 
 868                 CFStringRef     service         
= NULL
; 
 869                 CFStringRef     s_key           
= (CFStringRef
)    store_info
->keys
[i
]; 
 870                 CFDictionaryRef s_dict          
= (CFDictionaryRef
)store_info
->values
[i
]; 
 873                 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) { 
 877                 if (!CFStringHasSuffix(s_key
, store_info
->entity
) || 
 878                     !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) { 
 879                         continue;       // if not an active IPv4 or IPv6 entity 
 882                 s_if 
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
); 
 883                 if (!isA_CFString(s_if
)) { 
 884                         continue;       // if no interface 
 887                 if (!CFEqual(vpn_if
, s_if
)) { 
 888                         continue;       // if not this interface 
 891                 // extract the service ID and get the VPN "state" entity for 
 893                 components 
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/")); 
 894                 if (CFArrayGetCount(components
) != 5) { 
 895                         CFRelease(components
); 
 898                 service 
= CFArrayGetValueAtIndex(components
, 3); 
 899                 key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 900                                                                   kSCDynamicStoreDomainState
, 
 903                 p_state 
= CFDictionaryGetValue(store_info
->dict
, key
); 
 905                 CFRelease(components
); 
 907                 // ensure that this is a VPN service 
 908                 if (!isA_CFDictionary(p_state
)) { 
 912                 sc_status 
= kSCStatusOK
; 
 914                 *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
 917                 if (vpn_server 
!= NULL
) { 
 918                         *vpn_server 
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress")); 
 919                         *vpn_server 
= isA_CFString(*vpn_server
); 
 920                         if (*vpn_server 
!= NULL
) { 
 921                                 CFRetain(*vpn_server
); 
 926                 if (!CFDictionaryGetValueIfPresent(p_state
, 
 928                                                    (const void **)&num
) || 
 929                     !isA_CFNumber(num
) || 
 930                     !CFNumberGetValue(num
, kCFNumberSInt32Type
, &vpn_status
)) { 
 933 #ifdef  HAVE_VPN_STATUS 
 934                 switch (vpn_status
) { 
 936                                 // if we're really UP and RUNNING 
 942                                 // if we're not connected at all 
 943                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  VPN link idle"), 
 945                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
 948                                 // if we're in the process of [dis]connecting 
 949                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  VPN link, connection in progress"), 
 951                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
 954 #endif  // HAVE_VPN_STATUS 
 966 updateVPNAvailable(ReachabilityStoreInfoRef     store_info
, 
 967                    const struct sockaddr        
*sa
, 
 968                    SCNetworkReachabilityFlags   
*flags
, 
 969                    const char                   *log_prefix
) 
 972         int             sc_status       
= kSCStatusNoKey
; 
 974         if (!updateReachabilityStoreInfo(store_info
, 
 976                                          (sa 
!= NULL
) ? sa
->sa_family 
: AF_INET
)) { 
 977                 return kSCStatusReachabilityUnknown
; 
 980         if (store_info
->n 
<= 0) { 
 982                 return kSCStatusNoKey
; 
 985         // look for an available service which will provide connectivity 
 986         // for the requested address family. 
 988         for (i 
= 0; i 
< store_info
->n
; i
++) { 
 989                 CFArrayRef      components
; 
 990                 Boolean         found           
= FALSE
; 
 992                 CFDictionaryRef i_dict
; 
 994                 CFDictionaryRef p_dict
; 
 996                 CFStringRef     s_key           
= (CFStringRef
)    store_info
->keys
[i
]; 
 997                 CFDictionaryRef s_dict          
= (CFDictionaryRef
)store_info
->values
[i
]; 
 999                 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) { 
1003                 if (!CFStringHasSuffix(s_key
, store_info
->entity
) || 
1004                     !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainSetup
)) { 
1005                         continue;       // if not an IPv4 or IPv6 entity 
1008                 // extract service ID 
1009                 components 
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/")); 
1010                 if (CFArrayGetCount(components
) != 5) { 
1011                         CFRelease(components
); 
1014                 service 
= CFArrayGetValueAtIndex(components
, 3); 
1016                 // check for VPN entity 
1017                 p_key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
1018                                                                     kSCDynamicStoreDomainSetup
, 
1021                 p_dict 
= CFDictionaryGetValue(store_info
->dict
, p_key
); 
1024                 i_key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
1025                                                                     kSCDynamicStoreDomainSetup
, 
1027                                                                     kSCEntNetInterface
); 
1028                 i_dict 
= CFDictionaryGetValue(store_info
->dict
, i_key
); 
1031                 if (isA_CFDictionary(p_dict
) && 
1032                     isA_CFDictionary(i_dict
) && 
1033                     CFDictionaryContainsKey(i_dict
, kSCPropNetInterfaceDeviceName
)) { 
1034                         // we have a VPN service for this address family 
1037                         *flags 
|= kSCNetworkReachabilityFlagsReachable
; 
1038                         *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
1039                         *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
1042                                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  status    = isReachable (after connect)"), 
1044                                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  service   = %@"), 
1051                 CFRelease(components
); 
1054                         sc_status 
= kSCStatusOK
; 
1061 #endif  // !TARGET_IPHONE_SIMULATOR 
1065 updateIPSecStatus(ReachabilityStoreInfoRef      store_info
, 
1066                   const struct sockaddr         
*sa
, 
1067                   const char                    *if_name
, 
1068                   SCNetworkReachabilityFlags    
*flags
, 
1069                   CFStringRef                   
*ipsec_server
, 
1070                   const char                    *log_prefix
) 
1073         CFStringRef     ipsec_if
; 
1074         int             sc_status       
= kSCStatusNoKey
; 
1076         if (!updateReachabilityStoreInfo(store_info
, NULL
, sa
->sa_family
)) { 
1077                 return kSCStatusReachabilityUnknown
; 
1080         if (store_info
->n 
<= 0) { 
1082                 return kSCStatusNoKey
; 
1085         // look for the [IPSec] service that matches the provided interface 
1087         ipsec_if 
= CFStringCreateWithCStringNoCopy(NULL
, 
1089                                                    kCFStringEncodingASCII
, 
1092         for (i
=0; i 
< store_info
->n
; i
++) { 
1093                 CFArrayRef      components
; 
1095                 CFDictionaryRef i_state
; 
1096                 int32_t         ipsec_status
; 
1098                 CFStringRef     service         
= NULL
; 
1099                 CFStringRef     s_key           
= (CFStringRef
)    store_info
->keys
[i
]; 
1100                 CFDictionaryRef s_dict          
= (CFDictionaryRef
)store_info
->values
[i
]; 
1103                 if (!isA_CFString(s_key
) || !isA_CFDictionary(s_dict
)) { 
1107                 if (!CFStringHasSuffix(s_key
, store_info
->entity
) || 
1108                     !CFStringHasPrefix(s_key
, kSCDynamicStoreDomainState
)) { 
1109                         continue;       // if not an IPv4 or IPv6 entity 
1112                 s_if 
= CFDictionaryGetValue(s_dict
, kSCPropInterfaceName
); 
1113                 if (!isA_CFString(s_if
)) { 
1114                         continue;       // if no interface 
1117                 if (!CFEqual(ipsec_if
, s_if
)) { 
1118                         continue;       // if not this interface 
1121                 // extract the service ID, get the IPSec "state" entity for 
1122                 // the "Status", and get the IPSec "setup" entity to confirm 
1123                 // that we're looking at what we're expecting 
1124                 components 
= CFStringCreateArrayBySeparatingStrings(NULL
, s_key
, CFSTR("/")); 
1125                 if (CFArrayGetCount(components
) != 5) { 
1126                         CFRelease(components
); 
1129                 service 
= CFArrayGetValueAtIndex(components
, 3); 
1130                 key 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
1131                                                                   kSCDynamicStoreDomainState
, 
1134                 i_state 
= CFDictionaryGetValue(store_info
->dict
, key
); 
1136                 CFRelease(components
); 
1138                 // ensure that this is an IPSec service 
1139                 if (!isA_CFDictionary(i_state
)) { 
1143                 sc_status 
= kSCStatusOK
; 
1145                 *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
1148                 if (ipsec_server 
!= NULL
) { 
1149                         *ipsec_server 
= CFDictionaryGetValue(s_dict
, CFSTR("ServerAddress")); 
1150                         *ipsec_server 
= isA_CFString(*ipsec_server
); 
1151                         if (*ipsec_server 
!= NULL
) { 
1152                                 CFRetain(*ipsec_server
); 
1157                 if (!CFDictionaryGetValueIfPresent(i_state
, 
1158                                                    kSCPropNetIPSecStatus
, 
1159                                                    (const void **)&num
) || 
1160                     !isA_CFNumber(num
) || 
1161                     !CFNumberGetValue(num
, kCFNumberSInt32Type
, &ipsec_status
)) { 
1164 #ifdef  HAVE_IPSEC_STATUS 
1165                 switch (ipsec_status
) { 
1166                         case IPSEC_RUNNING 
: 
1167                                 // if we're really UP and RUNNING 
1170                                 // if we're not connected at all 
1171                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  IPSec link idle"), 
1173                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
1176                                 // if we're in the process of [dis]connecting 
1177                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  IPSec link, connection in progress"), 
1179                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
1182 #endif  // HAVE_IPSEC_STATUS 
1187         CFRelease(ipsec_if
); 
1195 #define ROUNDUP(a, size) \ 
1196         (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) 
1198 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \ 
1199         ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ 
1200                                                  sizeof(uint32_t)) :\ 
1204 get_rtaddrs(int addrs
, struct sockaddr 
*sa
, struct sockaddr 
**rti_info
) 
1208         for (i 
= 0; i 
< RTAX_MAX
; i
++) { 
1209                 if (addrs 
& (1 << i
)) { 
1218 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */ 
1224         struct sockaddr         
*rti_info
[RTAX_MAX
]; 
1225         struct rt_msghdr        
*rtm
; 
1226         struct sockaddr_dl      
*sdl
; 
1227 } route_info
, *route_info_p
; 
1232  *      returns zero if route exists an data returned, EHOSTUNREACH 
1233  *      if no route, or errno for any other error. 
1236 route_get(const struct sockaddr 
*address
, 
1237           unsigned int          if_index
, 
1242         pid_t                   pid             
= getpid(); 
1244         struct sockaddr         
*sa
; 
1245         int32_t                 seq             
= OSAtomicIncrement32Barrier(&rtm_seq
); 
1246 #ifndef RTM_GET_SILENT 
1247 #warning Note: Using RTM_GET (and not RTM_GET_SILENT) 
1248         static pthread_mutex_t  lock            
= PTHREAD_MUTEX_INITIALIZER
; 
1249         int                     sosize          
= 48 * 1024; 
1252         bzero(info
, sizeof(*info
)); 
1254         info
->rtm 
= (struct rt_msghdr 
*)&info
->buf
; 
1255         info
->rtm
->rtm_msglen  
= sizeof(struct rt_msghdr
); 
1256         info
->rtm
->rtm_version 
= RTM_VERSION
; 
1257 #ifdef  RTM_GET_SILENT 
1258         info
->rtm
->rtm_type    
= RTM_GET_SILENT
; 
1260         info
->rtm
->rtm_type    
= RTM_GET
; 
1262         info
->rtm
->rtm_flags   
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
; 
1263         info
->rtm
->rtm_addrs   
= RTA_DST
|RTA_IFP
; /* Both destination and device */ 
1264         info
->rtm
->rtm_pid     
= pid
; 
1265         info
->rtm
->rtm_seq     
= seq
; 
1267         if (if_index 
!= 0) { 
1268                 info
->rtm
->rtm_flags 
|= RTF_IFSCOPE
; 
1269                 info
->rtm
->rtm_index 
= if_index
; 
1272         switch (address
->sa_family
) { 
1274                         struct sockaddr_in6     
*sin6
; 
1276                         sin6 
= (struct sockaddr_in6 
*)address
; 
1277                         if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) || 
1278                              IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) && 
1279                             (sin6
->sin6_scope_id 
!= 0)) { 
1280                                 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
); 
1281                                 sin6
->sin6_scope_id 
= 0; 
1287         sa  
= (struct sockaddr 
*) (info
->rtm 
+ 1); 
1288         bcopy(address
, sa
, address
->sa_len
); 
1289         n 
= ROUNDUP(sa
->sa_len
, sizeof(uint32_t)); 
1290         info
->rtm
->rtm_msglen 
+= n
; 
1292         info
->sdl 
= (struct sockaddr_dl 
*) ((void *)sa 
+ n
); 
1293         info
->sdl
->sdl_family 
= AF_LINK
; 
1294         info
->sdl
->sdl_len 
= sizeof (struct sockaddr_dl
); 
1295         n 
= ROUNDUP(info
->sdl
->sdl_len
, sizeof(uint32_t)); 
1296         info
->rtm
->rtm_msglen 
+= n
; 
1298 #ifndef RTM_GET_SILENT 
1299         pthread_mutex_lock(&lock
); 
1301         rsock 
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
); 
1305 #ifndef RTM_GET_SILENT 
1306                 pthread_mutex_unlock(&lock
); 
1308                 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error
)); 
1312         if (ioctl(rsock
, FIONBIO
, &opt
) < 0) { 
1316 #ifndef RTM_GET_SILENT 
1317                 pthread_mutex_unlock(&lock
); 
1319                 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl(FIONBIO) failed: %s"), strerror(error
)); 
1323 #ifndef RTM_GET_SILENT 
1324         if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) { 
1328                 pthread_mutex_unlock(&lock
); 
1329                 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error
)); 
1334         if (write(rsock
, &info
->buf
, info
->rtm
->rtm_msglen
) == -1) { 
1338 #ifndef RTM_GET_SILENT 
1339                 pthread_mutex_unlock(&lock
); 
1341                 if (error 
!= ESRCH
) { 
1342                         SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(error
)); 
1345                 return EHOSTUNREACH
; 
1349          * Type, seq, pid identify our response. 
1350          * Routing sockets are broadcasters on input. 
1355                 n 
= read(rsock
, (void *)&info
->buf
, sizeof(info
->buf
)); 
1359                         if (error 
== EINTR
) { 
1363 #ifndef RTM_GET_SILENT 
1364                         pthread_mutex_unlock(&lock
); 
1366                         SCLog(TRUE
, LOG_ERR
, 
1367                               CFSTR("SCNetworkReachability: routing socket" 
1368                                     " read() failed: %s"), strerror(error
)); 
1371                 if ((info
->rtm
->rtm_type 
== RTM_GET
)    && 
1372                     (info
->rtm
->rtm_seq 
== seq
)         && 
1373                     (info
->rtm
->rtm_pid 
== pid
)) { 
1379 #ifndef RTM_GET_SILENT 
1380         pthread_mutex_unlock(&lock
); 
1383         get_rtaddrs(info
->rtm
->rtm_addrs
, sa
, info
->rti_info
); 
1385 //#define LOG_RTADDRS 
1390                 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), info
->rtm
->rtm_flags
); 
1392                 if ((info
->rti_info
[RTAX_NETMASK
] != NULL
) && (info
->rti_info
[RTAX_DST
] != NULL
)) { 
1393                         info
->rti_info
[RTAX_NETMASK
]->sa_family 
= info
->rti_info
[RTAX_DST
]->sa_family
; 
1396                 for (i 
= 0; i 
< RTAX_MAX
; i
++) { 
1397                         if (info
->rti_info
[i
] != NULL
) { 
1400                                 _SC_sockaddr_to_string(info
->rti_info
[i
], addr
, sizeof(addr
)); 
1401                                 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, addr
); 
1405 #endif  /* LOG_RTADDRS */ 
1407         if ((info
->rti_info
[RTAX_IFP
] == NULL
) || 
1408             (info
->rti_info
[RTAX_IFP
]->sa_family 
!= AF_LINK
)) { 
1409                 /* no interface info */ 
1410                 SCLog(TRUE
, LOG_DEBUG
, CFSTR("route_get() no interface info")); 
1414         info
->sdl 
= (struct sockaddr_dl 
*) info
->rti_info
[RTAX_IFP
]; 
1415         if ((info
->sdl
->sdl_nlen 
== 0) || (info
->sdl
->sdl_nlen 
> IFNAMSIZ
)) { 
1416                 /* no interface name */ 
1417                 return EHOSTUNREACH
; 
1425 checkAddress(ReachabilityStoreInfoRef   store_info
, 
1426              const struct sockaddr      
*address
, 
1427              unsigned int               if_index
, 
1428              ReachabilityInfo           
*reach_info
, 
1429              const char                 *log_prefix
) 
1433         char                    if_name
[IFNAMSIZ
]; 
1436         int                     sc_status       
= kSCStatusReachabilityUnknown
; 
1437         CFStringRef             server          
= NULL
; 
1438         char                    *statusMessage  
= NULL
; 
1439         struct sockaddr_in      v4mapped
; 
1441         *reach_info 
= NOT_REACHABLE
; 
1443         if (address 
== NULL
) { 
1444                 /* special case: check only for available paths off the system */ 
1445                 goto checkAvailable
; 
1448         switch (address
->sa_family
) { 
1453                                 char    if_name
[IFNAMSIZ 
+ 1]; 
1455                                 _SC_sockaddr_to_string(address
, addr
, sizeof(addr
)); 
1457                                 if ((if_index 
!= 0) && 
1458                                     (if_indextoname(if_index
, &if_name
[1]) != NULL
)) { 
1464                                 SCLog(TRUE
, LOG_INFO
, CFSTR("%scheckAddress(%s%s)"), 
1472                          * if no code for this address family (yet) 
1474                         SCLog(TRUE
, LOG_INFO
, 
1475                               CFSTR("checkAddress(): unexpected address family %d"), 
1476                               address
->sa_family
); 
1477                         sc_status 
= kSCStatusInvalidArgument
; 
1481         if (address
->sa_family 
== AF_INET6
) { 
1482                 struct sockaddr_in6     
*sin6   
= (struct sockaddr_in6 
*)address
; 
1484                 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) { 
1485                         bzero(&v4mapped
, sizeof(v4mapped
)); 
1486                         v4mapped
.sin_len         
= sizeof(v4mapped
); 
1487                         v4mapped
.sin_family      
= AF_INET
; 
1488                         v4mapped
.sin_port        
= sin6
->sin6_port
; 
1489                         v4mapped
.sin_addr
.s_addr 
= sin6
->sin6_addr
.__u6_addr
.__u6_addr32
[3]; 
1490                         address 
= (struct sockaddr 
*)&v4mapped
; 
1494         ret 
= route_get(address
, if_index
, &info
); 
1500                         goto checkAvailable
; 
1507         /* get the interface flags */ 
1509         isock 
= socket(AF_INET
, SOCK_DGRAM
, 0); 
1511                 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
)); 
1515         bzero(&ifr
, sizeof(ifr
)); 
1516         bcopy(info
.sdl
->sdl_data
, ifr
.ifr_name
, info
.sdl
->sdl_nlen
); 
1518         if (ioctl(isock
, SIOCGIFFLAGS
, (char *)&ifr
) == -1) { 
1519                 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl() failed: %s"), strerror(errno
)); 
1523         if (!(ifr
.ifr_flags 
& IFF_UP
)) { 
1524                 goto checkAvailable
; 
1527         statusMessage 
= "isReachable"; 
1528         reach_info
->flags 
|= kSCNetworkReachabilityFlagsReachable
; 
1530         if (info
.rtm
->rtm_flags 
& RTF_LOCAL
) { 
1531                 statusMessage 
= "isReachable (is a local address)"; 
1532                 reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1533         } else if (ifr
.ifr_flags 
& IFF_LOOPBACK
) { 
1534                 statusMessage 
= "isReachable (is loopback network)"; 
1535                 reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1536         } else if ((info
.rti_info
[RTAX_IFA
] != NULL
) && 
1537                    (info
.rti_info
[RTAX_IFA
]->sa_family 
!= AF_LINK
)) { 
1538                 void    *addr1  
= (void *)address
; 
1539                 void    *addr2  
= (void *)info
.rti_info
[RTAX_IFA
]; 
1540                 size_t  len     
= address
->sa_len
; 
1542                 if ((address
->sa_family 
!= info
.rti_info
[RTAX_IFA
]->sa_family
) && 
1543                     (address
->sa_len    
!= info
.rti_info
[RTAX_IFA
]->sa_len
)) { 
1544                         SCLog(TRUE
, LOG_NOTICE
, 
1545                               CFSTR("address family/length mismatch: %d/%d != %d/%d"), 
1548                               info
.rti_info
[RTAX_IFA
]->sa_family
, 
1549                               info
.rti_info
[RTAX_IFA
]->sa_len
); 
1553                 switch (address
->sa_family
) { 
1555                                 addr1 
= &((struct sockaddr_in 
*)address
)->sin_addr
; 
1556                                 addr2 
= &((struct sockaddr_in 
*)info
.rti_info
[RTAX_IFA
])->sin_addr
; 
1557                                 len 
= sizeof(struct in_addr
); 
1562                                 if (((struct sockaddr_in 
*)address
)->sin_addr
.s_addr 
== 0) { 
1563                                         statusMessage 
= "isReachable (this host)"; 
1564                                         reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1568                                 addr1 
= &((struct sockaddr_in6 
*)address
)->sin6_addr
; 
1569                                 addr2 
= &((struct sockaddr_in6 
*)info
.rti_info
[RTAX_IFA
])->sin6_addr
; 
1570                                 len 
= sizeof(struct in6_addr
); 
1576                 if (bcmp(addr1
, addr2
, len
) == 0) { 
1577                         statusMessage 
= "isReachable (is interface address)"; 
1578                         reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1582         if (!(info
.rtm
->rtm_flags 
& RTF_GATEWAY
) && 
1583             (info
.rti_info
[RTAX_GATEWAY
] != NULL
) && 
1584             (info
.rti_info
[RTAX_GATEWAY
]->sa_family 
== AF_LINK
) && 
1585             !(ifr
.ifr_flags 
& IFF_POINTOPOINT
)) { 
1586                 reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsDirect
; 
1589         bzero(&if_name
, sizeof(if_name
)); 
1590         bcopy(info
.sdl
->sdl_data
, 
1592               (info
.sdl
->sdl_nlen 
<= IFNAMSIZ
) ? info
.sdl
->sdl_nlen 
: IFNAMSIZ
); 
1594         reach_info
->if_index 
= info
.sdl
->sdl_index
; 
1597                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  status    = %s"), log_prefix
, statusMessage
); 
1598                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  device    = %s (%hu)"), log_prefix
, if_name
, info
.sdl
->sdl_index
); 
1599                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  sdl_type  = 0x%x"), log_prefix
, info
.sdl
->sdl_type
); 
1600                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  ifr_flags = 0x%04hx"), log_prefix
, ifr
.ifr_flags
); 
1601                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  rtm_flags = 0x%08x"), log_prefix
, info
.rtm
->rtm_flags
); 
1604         sc_status 
= kSCStatusOK
; 
1606         if (ifr
.ifr_flags 
& IFF_POINTOPOINT
) { 
1607                 reach_info
->flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
1610         if (info
.sdl
->sdl_type 
== IFT_PPP
) { 
1612                  * 1. check if PPP service 
1613                  * 2. check for dial-on-demand PPP link that is not yet connected 
1614                  * 3. get PPP server address 
1616                 sc_status 
= updatePPPStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
); 
1617         } else if (info
.sdl
->sdl_type 
== IFT_OTHER
) { 
1619                  * 1. check if IPSec service 
1620                  * 2. get IPSec server address 
1622                 sc_status 
= updateIPSecStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
); 
1624 #if     !TARGET_IPHONE_SIMULATOR 
1625                 if (sc_status 
== kSCStatusNoKey
) { 
1627                          * 1. check if VPN service 
1628                          * 2. get VPN server address 
1630                         sc_status 
= updateVPNStatus(store_info
, address
, if_name
, &reach_info
->flags
, &server
, log_prefix
); 
1632 #endif  // !TARGET_IPHONE_SIMULATOR 
1641         sc_status 
= updatePPPAvailable(store_info
, address
, &reach_info
->flags
, log_prefix
); 
1642         if ((sc_status 
== kSCStatusOK
) && (reach_info
->flags 
!= 0)) { 
1646 #if     !TARGET_IPHONE_SIMULATOR 
1647         sc_status 
= updateVPNAvailable(store_info
, address
, &reach_info
->flags
, log_prefix
); 
1648         if ((sc_status 
== kSCStatusOK
) && (reach_info
->flags 
!= 0)) { 
1651 #endif  // !TARGET_IPHONE_SIMULATOR 
1655         if (reach_info
->flags 
== 0) { 
1656                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  cannot be reached"), log_prefix
); 
1659         if (isock 
!= -1)        (void)close(isock
); 
1660         if (server 
!= NULL
)     CFRelease(server
); 
1661         if ((sc_status 
!= kSCStatusOK
) && (sc_status 
!= kSCStatusNoKey
)) { 
1662                 _SCErrorSet(sc_status
); 
1671 #pragma mark SCNetworkReachability APIs 
1675 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
) 
1677         CFAllocatorRef                  allocator       
= CFGetAllocator(cf
); 
1678         CFMutableStringRef              result
; 
1679         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)cf
; 
1681         result 
= CFStringCreateMutable(allocator
, 0); 
1682         CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
); 
1683         switch (targetPrivate
->type
) { 
1684                 case reachabilityTypeAddress 
: 
1685                 case reachabilityTypeAddressPair 
: { 
1688                         if (targetPrivate
->localAddress 
!= NULL
) { 
1689                                 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
)); 
1690                                 CFStringAppendFormat(result
, NULL
, CFSTR("local address = %s"), 
1694                         if (targetPrivate
->remoteAddress 
!= NULL
) { 
1695                                 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
)); 
1696                                 CFStringAppendFormat(result
, NULL
, CFSTR("%s%saddress = %s"), 
1697                                                      targetPrivate
->localAddress 
? ", " : "", 
1698                                                      (targetPrivate
->type 
== reachabilityTypeAddressPair
) ? "remote " : "", 
1703                 case reachabilityTypeName 
: { 
1704                         if ((targetPrivate
->name 
!= NULL
)) { 
1705                                 CFStringAppendFormat(result
, NULL
, CFSTR("name = %s"), targetPrivate
->name
); 
1707                         if ((targetPrivate
->serv 
!= NULL
)) { 
1708                                 CFStringAppendFormat(result
, NULL
, CFSTR("%sserv = %s"), 
1709                                                      targetPrivate
->name 
!= NULL 
? ", " : "", 
1710                                                      targetPrivate
->serv
); 
1712                         if ((targetPrivate
->resolvedAddress 
!= NULL
) || (targetPrivate
->resolvedAddressError 
!= NETDB_SUCCESS
)) { 
1713                                 if (targetPrivate
->resolvedAddress 
!= NULL
) { 
1714                                         if (isA_CFArray(targetPrivate
->resolvedAddress
)) { 
1716                                                 CFIndex n       
= CFArrayGetCount(targetPrivate
->resolvedAddress
); 
1718                                                 CFStringAppendFormat(result
, NULL
, CFSTR(" (")); 
1719                                                 for (i 
= 0; i 
< n
; i
++) { 
1722                                                         struct sockaddr 
*sa
; 
1724                                                         address 
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddress
, i
); 
1725                                                         sa      
= (struct sockaddr 
*)CFDataGetBytePtr(address
); 
1726                                                         _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
)); 
1727                                                         CFStringAppendFormat(result
, NULL
, CFSTR("%s%s"), 
1731                                                 CFStringAppendFormat(result
, NULL
, CFSTR(")")); 
1733                                                 CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)")); 
1736                                         CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"), 
1737                                                              gai_strerror(targetPrivate
->resolvedAddressError
)); 
1739                         } else if (targetPrivate
->dnsPort 
!= NULL
) { 
1740                                 CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)")); 
1745         if (targetPrivate
->scheduled
) { 
1746                 CFStringAppendFormat(result
, 
1748                                      CFSTR(", flags = 0x%08x, if_index = %hu"), 
1749                                      targetPrivate
->info
.flags
, 
1750                                      targetPrivate
->info
.if_index
); 
1752         CFStringAppendFormat(result
, NULL
, CFSTR("}")); 
1759 __SCNetworkReachabilityDeallocate(CFTypeRef cf
) 
1761         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)cf
; 
1763         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%srelease"), 
1764               targetPrivate
->log_prefix
); 
1766         /* release resources */ 
1768         pthread_mutex_destroy(&targetPrivate
->lock
); 
1770         if (targetPrivate
->name 
!= NULL
) 
1771                 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
); 
1773         if (targetPrivate
->serv 
!= NULL
) 
1774                 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->serv
); 
1776         if (targetPrivate
->resolvedAddress 
!= NULL
) 
1777                 CFRelease(targetPrivate
->resolvedAddress
); 
1779         if (targetPrivate
->localAddress 
!= NULL
) 
1780                 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
); 
1782         if (targetPrivate
->remoteAddress 
!= NULL
) 
1783                 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
); 
1785         if (targetPrivate
->rlsContext
.release 
!= NULL
) { 
1786                 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
); 
1789         if (targetPrivate
->onDemandName 
!= NULL
) { 
1790                 CFRelease(targetPrivate
->onDemandName
); 
1793         if (targetPrivate
->onDemandRemoteAddress 
!= NULL
) { 
1794                 CFRelease(targetPrivate
->onDemandRemoteAddress
); 
1797         if (targetPrivate
->onDemandServer 
!= NULL
) { 
1798                 CFRelease(targetPrivate
->onDemandServer
); 
1801         if (targetPrivate
->onDemandServiceID 
!= NULL
) { 
1802                 CFRelease(targetPrivate
->onDemandServiceID
); 
1810 __SCNetworkReachabilityInitialize(void) 
1812         __kSCNetworkReachabilityTypeID 
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
); 
1814         // provide a way to enable SCNetworkReachability logging without 
1815         // having to set _sc_debug=1. 
1816         if (getenv("REACH_LOGGING") != NULL
) { 
1825  * __SCNetworkReachabilityPerformInline 
1827  * Calls rlsPerform() 
1828  * - caller must be holding a reference to the target 
1829  * - caller must *not* be holding the target lock 
1831 static __inline__ 
void 
1832 __SCNetworkReachabilityPerformInline(SCNetworkReachabilityRef target
, Boolean needResolve
) 
1834         dispatch_queue_t                queue
; 
1835         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1837         pthread_mutex_lock(&targetPrivate
->lock
); 
1840                 // allow the DNS query to be [re-]started 
1841                 targetPrivate
->needResolve 
= TRUE
; 
1844         queue 
= targetPrivate
->dispatchQueue
; 
1845         if (queue 
!= NULL
) { 
1846                 dispatch_retain(queue
); 
1848                 pthread_mutex_unlock(&targetPrivate
->lock
); 
1850                 dispatch_sync(queue
, ^{ 
1851                         rlsPerform((void *)target
); 
1852                         dispatch_release(queue
); 
1855                 if (targetPrivate
->rls 
!= NULL
) { 
1856                         CFRunLoopSourceSignal(targetPrivate
->rls
); 
1857                         _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
); 
1860                 pthread_mutex_unlock(&targetPrivate
->lock
); 
1868 __SCNetworkReachabilityPerform(SCNetworkReachabilityRef target
) 
1870         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1872         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
1874                 dispatch_async(targetPrivate
->dispatchQueue
, 
1876                                        rlsPerform((void *)target
); 
1879         } else if (targetPrivate
->rls 
!= NULL
) { 
1880                 CFRunLoopSourceSignal(targetPrivate
->rls
); 
1881                 _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
); 
1887 static SCNetworkReachabilityPrivateRef
 
1888 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef     allocator
) 
1890         SCNetworkReachabilityPrivateRef         targetPrivate
; 
1893         /* initialize runtime */ 
1894         pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); 
1896         /* allocate target */ 
1897         size          
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
); 
1898         targetPrivate 
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
, 
1899                                                                                   __kSCNetworkReachabilityTypeID
, 
1902         if (targetPrivate 
== NULL
) { 
1906         pthread_mutex_init(&targetPrivate
->lock
, NULL
); 
1908         targetPrivate
->name                             
= NULL
; 
1909         targetPrivate
->serv                             
= NULL
; 
1910         bzero(&targetPrivate
->hints
, sizeof(targetPrivate
->hints
)); 
1911         targetPrivate
->hints
.ai_flags 
= AI_ADDRCONFIG
; 
1913         targetPrivate
->hints
.ai_flags 
|= AI_PARALLEL
; 
1914 #endif  /* AI_PARALLEL */ 
1916         targetPrivate
->needResolve                      
= FALSE
; 
1917         targetPrivate
->resolvedAddress                  
= NULL
; 
1918         targetPrivate
->resolvedAddressError             
= NETDB_SUCCESS
; 
1920         targetPrivate
->if_index                         
= 0; 
1922         targetPrivate
->localAddress                     
= NULL
; 
1923         targetPrivate
->remoteAddress                    
= NULL
; 
1925         targetPrivate
->info                             
= NOT_REACHABLE
; 
1926         targetPrivate
->last_notify                      
= NOT_REPORTED
; 
1928         targetPrivate
->scheduled                        
= FALSE
; 
1929         targetPrivate
->rls                              
= NULL
; 
1930         targetPrivate
->rlsFunction                      
= NULL
; 
1931         targetPrivate
->rlsContext
.info                  
= NULL
; 
1932         targetPrivate
->rlsContext
.retain                
= NULL
; 
1933         targetPrivate
->rlsContext
.release               
= NULL
; 
1934         targetPrivate
->rlsContext
.copyDescription       
= NULL
; 
1935         targetPrivate
->rlList                           
= NULL
; 
1937         targetPrivate
->haveDNS                          
= FALSE
; 
1938         targetPrivate
->dnsMP                            
= MACH_PORT_NULL
; 
1939         targetPrivate
->dnsPort                          
= NULL
; 
1940         targetPrivate
->dnsRLS                           
= NULL
; 
1941         targetPrivate
->dnsQueryStart                    
= TIME_ZERO
; 
1942         targetPrivate
->dnsQueryEnd                      
= TIME_ZERO
; 
1943         targetPrivate
->dnsRetry                         
= NULL
; 
1944         targetPrivate
->dnsRetryCount                    
= 0; 
1946         targetPrivate
->last_dns                         
= TIME_ZERO
; 
1948         targetPrivate
->onDemandBypass                   
= FALSE
; 
1949         targetPrivate
->onDemandName                     
= NULL
; 
1950         targetPrivate
->onDemandRemoteAddress            
= NULL
; 
1951         targetPrivate
->onDemandServer                   
= NULL
; 
1952         targetPrivate
->onDemandServiceID                
= NULL
; 
1955         targetPrivate
->log_prefix
[0] = '\0'; 
1957                 snprintf(targetPrivate
->log_prefix
, 
1958                          sizeof(targetPrivate
->log_prefix
), 
1963         return targetPrivate
; 
1969 static const struct sockaddr 
* 
1970 is_valid_address(const struct sockaddr 
*address
) 
1972         const struct sockaddr   
*valid  
= NULL
; 
1973         static Boolean  warned  
= FALSE
; 
1975         if ((address 
!= NULL
) && 
1976             (address
->sa_len 
<= sizeof(struct sockaddr_storage
))) { 
1977                 switch (address
->sa_family
) { 
1979                                 if (address
->sa_len 
>= sizeof(struct sockaddr_in
)) { 
1983                                                 SCLog(TRUE
, LOG_ERR
, 
1984                                                       CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"), 
1986                                                       sizeof(struct sockaddr_in
)); 
1992                                 if (address
->sa_len 
>= sizeof(struct sockaddr_in6
)) { 
1994                                 } else if (!warned
) { 
1995                                         SCLog(TRUE
, LOG_ERR
, 
1996                                               CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %d"), 
1998                                               sizeof(struct sockaddr_in6
)); 
2004                                         SCLog(TRUE
, LOG_ERR
, 
2005                                               CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"), 
2006                                               address
->sa_family
); 
2016 SCNetworkReachabilityRef
 
2017 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef           allocator
, 
2018                                        const struct sockaddr    
*address
) 
2020         SCNetworkReachabilityPrivateRef targetPrivate
; 
2022         address 
= is_valid_address(address
); 
2023         if (address 
== NULL
) { 
2024                 _SCErrorSet(kSCStatusInvalidArgument
); 
2028         targetPrivate 
= __SCNetworkReachabilityCreatePrivate(allocator
); 
2029         if (targetPrivate 
== NULL
) { 
2033         targetPrivate
->type 
= reachabilityTypeAddress
; 
2034         targetPrivate
->remoteAddress 
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0); 
2035         bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
); 
2037         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%screate w/address %@"), 
2038               targetPrivate
->log_prefix
, 
2041         return (SCNetworkReachabilityRef
)targetPrivate
; 
2045 SCNetworkReachabilityRef
 
2046 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef               allocator
, 
2047                                            const struct sockaddr        
*localAddress
, 
2048                                            const struct sockaddr        
*remoteAddress
) 
2050         SCNetworkReachabilityPrivateRef targetPrivate
; 
2052         if ((localAddress 
== NULL
) && (remoteAddress 
== NULL
)) { 
2053                 _SCErrorSet(kSCStatusInvalidArgument
); 
2057         if (localAddress 
!= NULL
) { 
2058                 localAddress 
= is_valid_address(localAddress
); 
2059                 if (localAddress 
== NULL
) { 
2060                         _SCErrorSet(kSCStatusInvalidArgument
); 
2065         if (remoteAddress 
!= NULL
) { 
2066                 remoteAddress 
= is_valid_address(remoteAddress
); 
2067                 if (remoteAddress 
== NULL
) { 
2068                         _SCErrorSet(kSCStatusInvalidArgument
); 
2073         targetPrivate 
= __SCNetworkReachabilityCreatePrivate(allocator
); 
2074         if (targetPrivate 
== NULL
) { 
2078         targetPrivate
->type 
= reachabilityTypeAddressPair
; 
2080         if (localAddress 
!= NULL
) { 
2081                 targetPrivate
->localAddress 
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0); 
2082                 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
); 
2085         if (remoteAddress 
!= NULL
) { 
2086                 targetPrivate
->remoteAddress 
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0); 
2087                 bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
); 
2090         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%screate w/address pair %@"), 
2091               targetPrivate
->log_prefix
, 
2094         return (SCNetworkReachabilityRef
)targetPrivate
; 
2098 SCNetworkReachabilityRef
 
2099 SCNetworkReachabilityCreateWithName(CFAllocatorRef      allocator
, 
2100                                     const char          *nodename
) 
2103         struct sockaddr_in              sin
; 
2104         struct sockaddr_in6             sin6
; 
2105         SCNetworkReachabilityPrivateRef targetPrivate
; 
2107         if (nodename 
== NULL
) { 
2108                 _SCErrorSet(kSCStatusInvalidArgument
); 
2112         nodenameLen 
= strlen(nodename
); 
2113         if (nodenameLen 
== 0) { 
2114                 _SCErrorSet(kSCStatusInvalidArgument
); 
2118         /* check if this "nodename" is really an IP[v6] address in disguise */ 
2120         bzero(&sin
, sizeof(sin
)); 
2121         sin
.sin_len    
= sizeof(sin
); 
2122         sin
.sin_family 
= AF_INET
; 
2123         if (inet_aton(nodename
, &sin
.sin_addr
) == 1) { 
2124                 /* if IPv4 address */ 
2125                 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr 
*)&sin
); 
2128         bzero(&sin6
, sizeof(sin6
)); 
2129         sin6
.sin6_len    
= sizeof(sin6
); 
2130         sin6
.sin6_family 
= AF_INET6
; 
2131         if (inet_pton(AF_INET6
, nodename
, &sin6
.sin6_addr
) == 1) { 
2132                 /* if IPv6 address */ 
2135                 p 
= strchr(nodename
, '%'); 
2137                         sin6
.sin6_scope_id 
= if_nametoindex(p 
+ 1); 
2140                 return SCNetworkReachabilityCreateWithAddress(allocator
, (struct sockaddr 
*)&sin6
); 
2143         targetPrivate 
= __SCNetworkReachabilityCreatePrivate(allocator
); 
2144         if (targetPrivate 
== NULL
) { 
2148         targetPrivate
->type 
= reachabilityTypeName
; 
2150         targetPrivate
->name 
= CFAllocatorAllocate(NULL
, nodenameLen 
+ 1, 0); 
2151         strlcpy((char *)targetPrivate
->name
, nodename
, nodenameLen 
+ 1); 
2153         targetPrivate
->needResolve 
= TRUE
; 
2154         targetPrivate
->info
.flags 
|= kSCNetworkReachabilityFlagsFirstResolvePending
; 
2156         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%screate w/name %@"), 
2157               targetPrivate
->log_prefix
, 
2160         return (SCNetworkReachabilityRef
)targetPrivate
; 
2166 SCNetworkReachabilityRef
 
2167 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef   allocator
, 
2168                                        CFDictionaryRef  options
) 
2170         const struct sockaddr           
*addr_l         
= NULL
; 
2171         const struct sockaddr           
*addr_r         
= NULL
; 
2172         CFBooleanRef                    bypass
; 
2174         struct addrinfo                 
*hints          
= NULL
; 
2175         CFStringRef                     interface       
= NULL
; 
2176         CFStringRef                     nodename
; 
2177         CFStringRef                     servname
; 
2178         SCNetworkReachabilityRef        target
; 
2179         SCNetworkReachabilityPrivateRef targetPrivate
; 
2181         if (!isA_CFDictionary(options
)) { 
2182                 _SCErrorSet(kSCStatusInvalidArgument
); 
2186         nodename 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
); 
2187         if ((nodename 
!= NULL
) && 
2188             (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) { 
2189                 _SCErrorSet(kSCStatusInvalidArgument
); 
2192         servname 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServName
); 
2193         if ((servname 
!= NULL
) && 
2194             (!isA_CFString(servname
) || (CFStringGetLength(servname
) == 0))) { 
2195                 _SCErrorSet(kSCStatusInvalidArgument
); 
2198         data 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
); 
2200                 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) { 
2201                         _SCErrorSet(kSCStatusInvalidArgument
); 
2204                 addr_l 
= (const struct sockaddr 
*)CFDataGetBytePtr(data
); 
2206         data 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
); 
2208                 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) { 
2209                         _SCErrorSet(kSCStatusInvalidArgument
); 
2212                 addr_r 
= (const struct sockaddr 
*)CFDataGetBytePtr(data
); 
2214         data 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionHints
); 
2216                 if (!isA_CFData(data
) || (CFDataGetLength(data
) != sizeof(targetPrivate
->hints
))) { 
2217                         _SCErrorSet(kSCStatusInvalidArgument
); 
2221                 hints 
= (struct addrinfo 
*)CFDataGetBytePtr(data
); 
2222                 if ((hints
->ai_addrlen   
!= 0)    || 
2223                     (hints
->ai_addr      
!= NULL
) || 
2224                     (hints
->ai_canonname 
!= NULL
) || 
2225                     (hints
->ai_next      
!= NULL
)) { 
2226                         _SCErrorSet(kSCStatusInvalidArgument
); 
2230         interface 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
); 
2231         if ((interface 
!= NULL
) && 
2232             (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) { 
2233                 _SCErrorSet(kSCStatusInvalidArgument
); 
2236         bypass 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandByPass
); 
2237         if ((bypass 
!= NULL
) && !isA_CFBoolean(bypass
)) { 
2238                 _SCErrorSet(kSCStatusInvalidArgument
); 
2243         if ((nodename 
!= NULL
) || (servname 
!= NULL
)) { 
2246                 if ((addr_l 
!= NULL
) || (addr_r 
!= NULL
)) { 
2247                         // can't have both a name/serv and an address 
2248                         _SCErrorSet(kSCStatusInvalidArgument
); 
2252                 name 
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
); 
2253                 target 
= SCNetworkReachabilityCreateWithName(allocator
, name
); 
2254                 CFAllocatorDeallocate(NULL
, (void *)name
); 
2256                 if ((addr_l 
!= NULL
) && (addr_r 
!= NULL
)) { 
2257                         target 
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
); 
2258                 } else if (addr_r 
!= NULL
) { 
2259                         target 
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
); 
2260                 } else if (addr_l 
!= NULL
) { 
2261                         target 
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_l
); 
2263                         _SCErrorSet(kSCStatusInvalidArgument
); 
2267         if (target 
== NULL
) { 
2271         targetPrivate 
= (SCNetworkReachabilityPrivateRef
)target
; 
2272         if (targetPrivate
->type 
== reachabilityTypeName
) { 
2273                 if (servname 
!= NULL
) { 
2274                         targetPrivate
->serv 
= _SC_cfstring_to_cstring(servname
, NULL
, 0, kCFStringEncodingUTF8
); 
2276                 if (hints 
!= NULL
) { 
2277                         bcopy(hints
, &targetPrivate
->hints
, sizeof(targetPrivate
->hints
)); 
2281         if (interface 
!= NULL
) { 
2282                 if ((_SC_cfstring_to_cstring(interface
, 
2283                                              targetPrivate
->if_name
, 
2284                                              sizeof(targetPrivate
->if_name
), 
2285                                              kCFStringEncodingASCII
) == NULL
) || 
2286                     ((targetPrivate
->if_index 
= if_nametoindex(targetPrivate
->if_name
)) == 0)) { 
2287                         CFRelease(targetPrivate
); 
2288                         _SCErrorSet(kSCStatusInvalidArgument
); 
2294         if (bypass 
!= NULL
) { 
2295                 targetPrivate
->onDemandBypass 
= CFBooleanGetValue(bypass
); 
2298         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%s    + options %@"), 
2299               targetPrivate
->log_prefix
, 
2302         return (SCNetworkReachabilityRef
)targetPrivate
; 
2307 SCNetworkReachabilityGetTypeID(void) 
2309         pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);  /* initialize runtime */ 
2310         return __kSCNetworkReachabilityTypeID
; 
2314 CFArrayRef      
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */ 
2315 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef       target
, 
2318         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2320         if (!isA_SCNetworkReachability(target
)) { 
2321                 _SCErrorSet(kSCStatusInvalidArgument
); 
2325         if (targetPrivate
->type 
!= reachabilityTypeName
) { 
2326                 _SCErrorSet(kSCStatusInvalidArgument
); 
2331                 *error_num 
= targetPrivate
->resolvedAddressError
; 
2334         if (targetPrivate
->resolvedAddress 
!= NULL
) { 
2335                 if (isA_CFArray(targetPrivate
->resolvedAddress
)) { 
2336                         return CFRetain(targetPrivate
->resolvedAddress
); 
2338                         /* if status is known but no resolved addresses to return */ 
2339                         _SCErrorSet(kSCStatusOK
); 
2344         _SCErrorSet(kSCStatusReachabilityUnknown
); 
2350 __SCNetworkReachabilitySetResolvedAddress(int32_t                       status
, 
2351                                           struct addrinfo               
*res
, 
2352                                           SCNetworkReachabilityRef      target
) 
2354         struct addrinfo                         
*resP
; 
2355         SCNetworkReachabilityPrivateRef         targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2357         if (targetPrivate
->resolvedAddress 
!= NULL
) { 
2358                 CFRelease(targetPrivate
->resolvedAddress
); 
2359                 targetPrivate
->resolvedAddress 
= NULL
; 
2362         if ((status 
== 0) && (res 
!= NULL
)) { 
2363                 CFMutableArrayRef       addresses
; 
2365                 addresses 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
2367                 for (resP 
= res
; resP
; resP 
= resP
->ai_next
) { 
2369                         CFDataRef       newAddress
; 
2371                         newAddress 
= CFDataCreate(NULL
, (void *)resP
->ai_addr
, resP
->ai_addr
->sa_len
); 
2372                         n 
= CFArrayGetCount(addresses
); 
2374                             !CFArrayContainsValue(addresses
, CFRangeMake(0, n
), newAddress
)) { 
2375                                 CFArrayAppendValue(addresses
, newAddress
); 
2377                         CFRelease(newAddress
); 
2380                 /* save the resolved address[es] */ 
2381                 targetPrivate
->resolvedAddress      
= addresses
; 
2382                 targetPrivate
->resolvedAddressError 
= NETDB_SUCCESS
; 
2384                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sgetaddrinfo() failed: %s"), 
2385                       targetPrivate
->log_prefix
, 
2386                       gai_strerror(status
)); 
2388                 /* save the error associated with the attempt to resolve the name */ 
2389                 targetPrivate
->resolvedAddress      
= CFRetain(kCFNull
); 
2390                 targetPrivate
->resolvedAddressError 
= status
; 
2392         targetPrivate
->needResolve 
= FALSE
; 
2394         if (res 
!= NULL
)        freeaddrinfo(res
); 
2396         if (targetPrivate
->scheduled
) { 
2397                 __SCNetworkReachabilityPerform(target
); 
2405 __SCNetworkReachabilityCallbackSetResolvedAddress(int32_t status
, struct addrinfo 
*res
, void *context
) 
2407         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)context
; 
2408         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2410         __dns_query_end(target
, 
2411                         ((status 
== 0) && (res 
!= NULL
)),       // if successful query 
2413                         &targetPrivate
->dnsQueryStart
,          // start time 
2414                         &targetPrivate
->dnsQueryEnd
);           // end time 
2416         __SCNetworkReachabilitySetResolvedAddress(status
, res
, target
); 
2422  * rankReachability() 
2423  *   Not reachable       == 0 
2424  *   Connection Required == 1 
2428 rankReachability(SCNetworkReachabilityFlags flags
) 
2432         if (flags 
& kSCNetworkReachabilityFlagsReachable
)               rank 
= 2; 
2433         if (flags 
& kSCNetworkReachabilityFlagsConnectionRequired
)      rank 
= 1; 
2439 #pragma mark DNS name resolution 
2443 replyMPCopyDescription(const void *info
) 
2445         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)info
; 
2446         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2448         return CFStringCreateWithFormat(NULL
, 
2450                                         CFSTR("<getaddrinfo_async_start reply MP> {%s%s%s%s%s, target = %p}"), 
2451                                         targetPrivate
->name 
!= NULL 
? "name = " : "", 
2452                                         targetPrivate
->name 
!= NULL 
? targetPrivate
->name 
: "", 
2453                                         targetPrivate
->name 
!= NULL 
&& targetPrivate
->serv 
!= NULL 
? ", " : "", 
2454                                         targetPrivate
->serv 
!= NULL 
? "serv = " : "", 
2455                                         targetPrivate
->serv 
!= NULL 
? targetPrivate
->serv 
: "", 
2461 processAsyncDNSReply(mach_port_t mp
, void *msg
, SCNetworkReachabilityRef target
); 
2465 getaddrinfo_async_handleCFReply(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) 
2467         mach_port_t                     mp      
= CFMachPortGetPort(port
); 
2468         SCNetworkReachabilityRef        target  
= (SCNetworkReachabilityRef
)info
; 
2470         processAsyncDNSReply(mp
, msg
, target
); 
2476 SCNetworkReachabilityNotifyMIGCallback(mach_msg_header_t 
*message
, mach_msg_header_t 
*reply
) 
2478         mach_port_t                     mp      
= message
->msgh_local_port
; 
2479         SCNetworkReachabilityRef        target  
= dispatch_get_context(dispatch_get_current_queue()); 
2481         processAsyncDNSReply(mp
, message
, target
); 
2482         reply
->msgh_remote_port 
= MACH_PORT_NULL
; 
2488 enqueueAsyncDNSQuery(SCNetworkReachabilityRef target
, mach_port_t mp
) 
2490         CFMachPortContext               context         
= { 0 
2494                                                           , replyMPCopyDescription
 
2496         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2498         targetPrivate
->dnsMP   
= mp
; 
2499         targetPrivate
->dnsPort 
= _SC_CFMachPortCreateWithPort("SCNetworkReachability", 
2501                                                               getaddrinfo_async_handleCFReply
, 
2503         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
2504                 targetPrivate
->asyncDNSQueue 
= dispatch_queue_create("com.apple.SCNetworkReachabilty.async_DNS_query", NULL
); 
2505                 if (targetPrivate
->asyncDNSQueue 
== NULL
) { 
2506                         SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_queue_create() failed")); 
2509                 CFRetain(target
);       // Note: will be released when the dispatch queue is released 
2510                 dispatch_set_context(targetPrivate
->asyncDNSQueue
, (void *)target
); 
2511                 dispatch_set_finalizer_f(targetPrivate
->asyncDNSQueue
, (dispatch_function_t
)CFRelease
); 
2513                 targetPrivate
->asyncDNSSource 
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, 
2516                                                                        targetPrivate
->asyncDNSQueue
); 
2517                 if (targetPrivate
->asyncDNSSource 
== NULL
) { 
2518                         SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability dispatch_source_create() failed")); 
2521                 dispatch_source_set_event_handler(targetPrivate
->asyncDNSSource
, ^{ 
2522                         dispatch_mig_server(targetPrivate
->asyncDNSSource
, 
2523                                             sizeof(mach_msg_header_t
), 
2524                                             SCNetworkReachabilityNotifyMIGCallback
); 
2526                 dispatch_resume(targetPrivate
->asyncDNSSource
); 
2527         } else if (targetPrivate
->rls 
!= NULL
) { 
2531                 targetPrivate
->dnsRLS 
= CFMachPortCreateRunLoopSource(NULL
, targetPrivate
->dnsPort
, 0); 
2533                 n 
= CFArrayGetCount(targetPrivate
->rlList
); 
2534                 for (i 
= 0; i 
< n
; i 
+= 3) { 
2535                         CFRunLoopRef    rl      
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1); 
2536                         CFStringRef     rlMode  
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2); 
2538                         CFRunLoopAddSource(rl
, targetPrivate
->dnsRLS
, rlMode
); 
2546         if (targetPrivate
->asyncDNSSource 
!= NULL
) { 
2547                 dispatch_source_cancel(targetPrivate
->asyncDNSSource
); 
2548                 dispatch_release(targetPrivate
->asyncDNSSource
); 
2549                 targetPrivate
->asyncDNSSource 
= NULL
; 
2551         if (targetPrivate
->asyncDNSQueue 
!= NULL
) { 
2552                 dispatch_release(targetPrivate
->asyncDNSQueue
); 
2553                 targetPrivate
->asyncDNSQueue 
= NULL
; 
2556         CFMachPortInvalidate(targetPrivate
->dnsPort
); 
2557         CFRelease(targetPrivate
->dnsPort
); 
2558         targetPrivate
->dnsPort 
= NULL
; 
2559         targetPrivate
->dnsMP 
= MACH_PORT_NULL
; 
2561         _SCErrorSet(kSCStatusFailed
); 
2567 dequeueAsyncDNSQuery(SCNetworkReachabilityRef target
) 
2569         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2571         if (targetPrivate
->asyncDNSSource 
!= NULL
) { 
2572                 dispatch_source_cancel(targetPrivate
->asyncDNSSource
); 
2573                 if (targetPrivate
->asyncDNSQueue 
!= dispatch_get_current_queue()) { 
2574                         // ensure the cancellation has completed 
2575                         pthread_mutex_unlock(&targetPrivate
->lock
); 
2576                         dispatch_sync(targetPrivate
->asyncDNSQueue
, ^{}); 
2577                         pthread_mutex_lock(&targetPrivate
->lock
); 
2580         if (targetPrivate
->asyncDNSSource 
!= NULL
) { 
2581                 dispatch_release(targetPrivate
->asyncDNSSource
); 
2582                 targetPrivate
->asyncDNSSource 
= NULL
; 
2584         if (targetPrivate
->asyncDNSQueue 
!= NULL
) { 
2585                 dispatch_release(targetPrivate
->asyncDNSQueue
); 
2586                 targetPrivate
->asyncDNSQueue 
= NULL
; 
2589         if (targetPrivate
->dnsRLS 
!= NULL
) { 
2590                 CFRelease(targetPrivate
->dnsRLS
); 
2591                 targetPrivate
->dnsRLS 
= NULL
; 
2594         if (targetPrivate
->dnsPort 
!= NULL
) { 
2595                 CFMachPortInvalidate(targetPrivate
->dnsPort
); 
2596                 CFRelease(targetPrivate
->dnsPort
); 
2597                 targetPrivate
->dnsPort 
= NULL
; 
2598                 targetPrivate
->dnsMP 
= MACH_PORT_NULL
; 
2606 processAsyncDNSReply(mach_port_t mp
, void *msg
, SCNetworkReachabilityRef target
) 
2609         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2611         pthread_mutex_lock(&targetPrivate
->lock
); 
2613         if (mp 
!= targetPrivate
->dnsMP
) { 
2614                 // we've received a callback on the async DNS port but since the 
2615                 // associated CFMachPort doesn't match than the request must have 
2616                 // already been cancelled. 
2617                 SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply(): mp != targetPrivate->dnsMP")); 
2618                 pthread_mutex_unlock(&targetPrivate
->lock
); 
2622         dequeueAsyncDNSQuery(target
); 
2623         status 
= getaddrinfo_async_handle_reply(msg
); 
2624         if ((status 
== 0) && 
2625             (targetPrivate
->resolvedAddress 
== NULL
) && (targetPrivate
->resolvedAddressError 
== NETDB_SUCCESS
)) { 
2628                 // if the request is not complete and needs to be re-queued 
2629                 ok 
= enqueueAsyncDNSQuery(target
, mp
); 
2631                         SCLog(TRUE
, LOG_ERR
, CFSTR("processAsyncDNSReply enqueueAsyncDNSQuery() failed")); 
2635         pthread_mutex_unlock(&targetPrivate
->lock
); 
2642 check_resolver_reachability(ReachabilityStoreInfoRef    store_info
, 
2643                             dns_resolver_t              
*resolver
, 
2644                             SCNetworkReachabilityFlags  
*flags
, 
2646                             const char                  *log_prefix
) 
2651         *flags   
= kSCNetworkReachabilityFlagsReachable
; 
2654         for (i 
= 0; i 
< resolver
->n_nameserver
; i
++) { 
2655                 struct sockaddr         
*address        
= resolver
->nameserver
[i
]; 
2656                 ReachabilityInfo        ns_info
; 
2660                 if (address
->sa_family 
!= AF_INET
) { 
2662                          * we need to skip non-IPv4 DNS server 
2663                          * addresses (at least until [3510431] has 
2669                 ok 
= checkAddress(store_info
, address
, resolver
->if_index
, &ns_info
, log_prefix
); 
2675                 if (rankReachability(ns_info
.flags
) < rankReachability(*flags
)) { 
2676                         /* return the worst case result */ 
2677                         *flags 
= ns_info
.flags
; 
2688 check_matching_resolvers(ReachabilityStoreInfoRef       store_info
, 
2689                          dns_config_t                   
*dns_config
, 
2691                          unsigned int                   if_index
, 
2692                          SCNetworkReachabilityFlags     
*flags
, 
2694                          const char                     *log_prefix
) 
2697         Boolean         matched         
= FALSE
; 
2698         const char      *name           
= fqdn
; 
2699         int32_t         n_resolvers
; 
2700         dns_resolver_t  
**resolvers
; 
2702         if (if_index 
== 0) { 
2703                 n_resolvers 
= dns_config
->n_resolver
; 
2704                 resolvers   
= dns_config
->resolver
; 
2706                 n_resolvers 
= dns_config
->n_scoped_resolver
; 
2707                 resolvers   
= dns_config
->scoped_resolver
; 
2710         while (!matched 
&& (name 
!= NULL
)) { 
2714                  * check if the provided name (or sub-component) 
2715                  * matches one of our resolver configurations. 
2718                 for (i 
= 0; i 
< n_resolvers
; i
++) { 
2720                         dns_resolver_t  
*resolver
; 
2722                         resolver 
= resolvers
[i
]; 
2723                         if ((if_index 
!= 0) && (if_index 
!= resolver
->if_index
)) { 
2727                         domain   
= resolver
->domain
; 
2728                         if (domain 
!= NULL 
&& (len 
== strlen(domain
))) { 
2729                                 if (strcasecmp(name
, domain
) == 0) { 
2733                                          * if name matches domain 
2736                                         ok 
= check_resolver_reachability(store_info
, resolver
, flags
, haveDNS
, log_prefix
); 
2747                          * we have not found a matching resolver, try 
2748                          * a less qualified domain 
2750                         name 
= strchr(name
, '.'); 
2751                         if ((name 
!= NULL
) && (*name 
!= '\0')) { 
2763 static dns_resolver_t 
* 
2764 get_default_resolver(dns_config_t 
*dns_config
, unsigned int if_index
) 
2767         int32_t         n_resolvers
; 
2768         dns_resolver_t  
*resolver       
= NULL
; 
2769         dns_resolver_t  
**resolvers
; 
2771         if (if_index 
== 0) { 
2772                 n_resolvers 
= dns_config
->n_resolver
; 
2773                 resolvers   
= dns_config
->resolver
; 
2775                 n_resolvers 
= dns_config
->n_scoped_resolver
; 
2776                 resolvers   
= dns_config
->scoped_resolver
; 
2779         for (i 
= 0; i 
< n_resolvers
; i
++) { 
2780                 if ((if_index 
!= 0) && (if_index 
!= resolvers
[i
]->if_index
)) { 
2784                 if (((if_index 
== 0) && (i 
== 0)) || 
2785                     ((if_index 
!= 0) && (resolver 
== NULL
))) { 
2786                         // if this is the first (aka default) resolver 
2787                         resolver 
= resolvers
[i
]; 
2788                 } else if ((resolvers
[i
]->domain 
== NULL
) && 
2789                            (resolvers
[i
]->search_order 
< resolver
->search_order
)) { 
2790                         // if this is a default resolver with a lower search order 
2791                         resolver 
= resolvers
[i
]; 
2799 static dns_configuration_t 
* 
2800 dns_configuration_retain() 
2802         pthread_mutex_lock(&dns_lock
); 
2804         if ((dns_configuration 
!= NULL
) && dns_token_valid
) { 
2809                  * check if the global [DNS] configuration snapshot needs 
2812                 status 
= notify_check(dns_token
, &check
); 
2813                 if (status 
!= NOTIFY_STATUS_OK
) { 
2814                         SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
); 
2817                 if ((status 
!= NOTIFY_STATUS_OK
) || (check 
!= 0)) { 
2819                          * if the snapshot needs to be refreshed 
2821                         if (dns_configuration
->refs 
== 0) { 
2822                                 dns_configuration_free(dns_configuration
->config
); 
2823                                 CFAllocatorDeallocate(NULL
, dns_configuration
); 
2825                         dns_configuration 
= NULL
; 
2829         if (dns_configuration 
== NULL
) { 
2830                 dns_config_t    
*new_config
; 
2832                 new_config 
= dns_configuration_copy(); 
2833                 if (new_config 
!= NULL
) { 
2834                         dns_configuration 
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0); 
2835                         dns_configuration
->config 
= new_config
; 
2836                         dns_configuration
->refs   
= 0; 
2840         if (dns_configuration 
!= NULL
) { 
2841                 dns_configuration
->refs
++; 
2844         pthread_mutex_unlock(&dns_lock
); 
2845         return dns_configuration
; 
2850 dns_configuration_release(dns_configuration_t 
*config
) 
2852         pthread_mutex_lock(&dns_lock
); 
2855         if (config
->refs 
== 0) { 
2856                 if ((dns_configuration 
!= config
)) { 
2857                         dns_configuration_free(config
->config
); 
2858                         CFAllocatorDeallocate(NULL
, config
); 
2862         pthread_mutex_unlock(&dns_lock
); 
2868 dns_configuration_watch() 
2871         const char      *dns_key
; 
2875         pthread_mutex_lock(&dns_lock
); 
2877         dns_key 
= dns_configuration_notify_key(); 
2878         if (dns_key 
== NULL
) { 
2879                 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed")); 
2883         status 
= notify_register_check(dns_key
, &dns_token
); 
2884         if (status 
== NOTIFY_STATUS_OK
) { 
2885                 dns_token_valid 
= TRUE
; 
2887                 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%lu"), status
); 
2891         status 
= notify_check(dns_token
, &dns_check
); 
2892         if (status 
!= NOTIFY_STATUS_OK
) { 
2893                 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%lu"), status
); 
2894                 (void)notify_cancel(dns_token
); 
2895                 dns_token_valid 
= FALSE
; 
2903         pthread_mutex_unlock(&dns_lock
); 
2909 dns_configuration_unwatch() 
2911         pthread_mutex_lock(&dns_lock
); 
2913         (void)notify_cancel(dns_token
); 
2914         dns_token_valid 
= FALSE
; 
2916         if ((dns_configuration 
!= NULL
) && (dns_configuration
->refs 
== 0)) { 
2917                 dns_configuration_free(dns_configuration
->config
); 
2918                 CFAllocatorDeallocate(NULL
, dns_configuration
); 
2919                 dns_configuration 
= NULL
; 
2922         pthread_mutex_unlock(&dns_lock
); 
2928 _SC_R_checkResolverReachability(ReachabilityStoreInfoRef        store_info
, 
2929                                 SCNetworkReachabilityFlags      
*flags
, 
2931                                 const char                      *nodename
, 
2932                                 const char                      *servname
, 
2933                                 unsigned int                    if_index
, 
2934                                 const char                      *log_prefix
) 
2936         dns_resolver_t          
*default_resolver
; 
2937         dns_configuration_t     
*dns
; 
2938         Boolean                 found                   
= FALSE
; 
2939         char                    *fqdn                   
= (char *)nodename
; 
2941         Boolean                 isFQDN                  
= FALSE
; 
2945         Boolean                 useDefault              
= FALSE
; 
2948          * We first assume that all of the configured DNS servers 
2949          * are available.  Since we don't know which name server will 
2950          * be consulted to resolve the specified nodename we need to 
2951          * check the availability of ALL name servers.  We can only 
2952          * proceed if we know that our query can be answered. 
2955         *flags   
= kSCNetworkReachabilityFlagsReachable
; 
2958         len 
= (nodename 
!= NULL
) ? strlen(nodename
) : 0; 
2960                 if ((servname 
== NULL
) || (strlen(servname
) == 0)) { 
2961                         // if no nodename or servname, return not reachable 
2967         dns 
= dns_configuration_retain(); 
2970                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no configuration"), log_prefix
); 
2974         if (dns
->config
->n_resolver 
== 0) { 
2975                 // if no resolver configuration 
2976                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no resolvers"), log_prefix
); 
2980         *flags 
= kSCNetworkReachabilityFlagsReachable
; 
2982         if (fqdn
[len 
- 1] == '.') { 
2985                 // trim trailing '.''s 
2986                 while ((len 
> 0) && (fqdn
[len
-1] == '.')) { 
2987                         if (fqdn 
== nodename
) { 
2988                                 fqdn 
= strdup(nodename
); 
2994         default_resolver 
= get_default_resolver(dns
->config
, if_index
); 
2997          * check if the provided name matches a supplemental domain 
2999         found 
= check_matching_resolvers(store_info
, dns
->config
, fqdn
, if_index
, flags
, haveDNS
, log_prefix
); 
3001         if (!found 
&& !isFQDN
) { 
3003                  * if we did not match a supplemental domain name and if the 
3004                  * provided name has enough "."s then the first query will be 
3005                  * directed to the default resolver. 
3010 #define NDOTS_OPT       "ndots=" 
3011 #define NDOTS_OPT_LEN   (sizeof("ndots=") - 1) 
3013                 if (default_resolver
->options 
!= NULL
) { 
3014                         cp 
= strstr(default_resolver
->options
, NDOTS_OPT
); 
3016                             ((cp 
== default_resolver
->options
) || isspace(cp
[-1])) && 
3017                             ((cp
[NDOTS_OPT_LEN
] != '\0') && isdigit(cp
[NDOTS_OPT_LEN
]))) { 
3021                                 cp 
+=  NDOTS_OPT_LEN
; 
3023                                 val 
= strtol(cp
, &end
, 10); 
3024                                 if ((*cp 
!= '\0') && (cp 
!= end
) && (errno 
== 0) && 
3025                                 ((*end 
== '\0') || isspace(*end
))) { 
3032                 for (cp 
= fqdn
; *cp 
!= '\0'; cp
++) { 
3033                         if (*cp 
== '.') dots
++; 
3041         if (!found 
&& !isFQDN 
&& !useDefault 
&& (dns
->config
->n_resolver 
> 1)) { 
3043                  * FQDN not specified, try matching w/search domains 
3045                 if (default_resolver
->n_search 
> 0) { 
3046                         for (i 
= 0; !found 
&& (i 
< default_resolver
->n_search
); i
++) { 
3048                                 char    *search_fqdn    
= NULL
; 
3050                                 ret 
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]); 
3055                                 // try the provided name with the search domain appended 
3056                                 found 
= check_matching_resolvers(store_info
, 
3065                 } else if (default_resolver
->domain 
!= NULL
) { 
3067                         int     domain_parts    
= 0; 
3069                         // count domain parts 
3070                         for (dp 
= default_resolver
->domain
; *dp 
!= '\0'; dp
++) { 
3076                         // remove trailing dots 
3077                         for (dp
--; (dp 
>= default_resolver
->domain
) && (*dp 
== '.'); dp
--) { 
3082                         if (dp 
>= default_resolver
->domain
) { 
3083                                 // dots are separators, bump # of components 
3087                         dp 
= default_resolver
->domain
; 
3088                         for (i 
= LOCALDOMAINPARTS
; !found 
&& (i 
<= (domain_parts 
- ndots
)); i
++) { 
3090                                 char    *search_fqdn    
= NULL
; 
3092                                 ret 
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
); 
3097                                 // try the provided name with the [default] domain appended 
3098                                 found 
= check_matching_resolvers(store_info
, 
3107                                 // move to the next component of the [default] domain 
3108                                 dp 
= strchr(dp
, '.') + 1; 
3115                  * check the reachability of the default resolver 
3117                 ok 
= check_resolver_reachability(store_info
, default_resolver
, flags
, haveDNS
, log_prefix
); 
3120         if (fqdn 
!= nodename
)   free(fqdn
); 
3125                 dns_configuration_release(dns
); 
3133 _SC_checkResolverReachability(SCDynamicStoreRef                 
*storeP
, 
3134                               SCNetworkReachabilityFlags        
*flags
, 
3136                               const char                        *nodename
, 
3137                               const char                        *servname
) 
3140         ReachabilityStoreInfo   store_info
; 
3142         initReachabilityStoreInfo(&store_info
); 
3143         ok 
= updateReachabilityStoreInfo(&store_info
, storeP
, AF_UNSPEC
); 
3148         ok 
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, nodename
, servname
, 0, ""); 
3152         freeReachabilityStoreInfo(&store_info
); 
3158  * _SC_checkResolverReachabilityByAddress() 
3160  * Given an IP address, determine whether a reverse DNS query can be issued 
3161  * using the current network configuration. 
3164 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef                
*storeP
, 
3165                                        SCNetworkReachabilityFlags       
*flags
, 
3167                                        struct sockaddr                  
*sa
) 
3172         ReachabilityStoreInfo   store_info
; 
3174         initReachabilityStoreInfo(&store_info
); 
3175         ok 
= updateReachabilityStoreInfo(&store_info
, storeP
, AF_UNSPEC
); 
3181          * Ideally, we would have an API that given a local IP 
3182          * address would return the DNS server(s) that would field 
3183          * a given PTR query.  Fortunately, we do have an SPI which 
3184          * which will provide this information given a "name" so we 
3185          * take the address, convert it into the inverse query name, 
3186          * and find out which servers should be consulted. 
3189         switch (sa
->sa_family
) { 
3195                         struct sockaddr_in      
*sin    
= (struct sockaddr_in 
*)sa
; 
3198                          * build "PTR" query name 
3199                          *   NNN.NNN.NNN.NNN.in-addr.arpa. 
3201                         rev
.s_addr 
= sin
->sin_addr
.s_addr
; 
3202                         (void) snprintf(ptr_name
, sizeof(ptr_name
), "%u.%u.%u.%u.in-addr.arpa.", 
3213                         struct sockaddr_in6     
*sin6   
= (struct sockaddr_in6 
*)sa
; 
3214                         int                     x       
= sizeof(ptr_name
); 
3218                          * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152) 
3219                          *   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. 
3221                         for (i 
= sizeof(sin6
->sin6_addr
) - 1; i 
>= 0; i
--) { 
3222                                 n 
= snprintf(&ptr_name
[s
], x
, "%x.%x.", 
3223                                              ( sin6
->sin6_addr
.s6_addr
[i
]       & 0xf), 
3224                                              ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf)); 
3225                                 if ((n 
== -1) || (n 
>= x
)) { 
3233                         n 
= snprintf(&ptr_name
[s
], x
, "ip6.arpa."); 
3234                         if ((n 
== -1) || (n 
>= x
)) { 
3245         ok 
= _SC_R_checkResolverReachability(&store_info
, flags
, haveDNS
, ptr_name
, NULL
, 0, ""); 
3249         freeReachabilityStoreInfo(&store_info
); 
3255 startAsyncDNSQuery(SCNetworkReachabilityRef target
) { 
3257         mach_port_t                     mp      
= MACH_PORT_NULL
; 
3259         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3261         __dns_query_start(&targetPrivate
->dnsQueryStart
, &targetPrivate
->dnsQueryEnd
); 
3263 #ifdef  HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL 
3264         if (targetPrivate
->if_index 
== 0) { 
3265 #endif  /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */ 
3266                 error 
= getaddrinfo_async_start(&mp
, 
3267                                                 targetPrivate
->name
, 
3268                                                 targetPrivate
->serv
, 
3269                                                 &targetPrivate
->hints
, 
3270                                                 __SCNetworkReachabilityCallbackSetResolvedAddress
, 
3272 #ifdef  HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL 
3274                 mp 
= _getaddrinfo_interface_async_call(targetPrivate
->name
, 
3275                                                        targetPrivate
->serv
, 
3276                                                        &targetPrivate
->hints
, 
3277                                                        targetPrivate
->if_name
, 
3278                                                        __SCNetworkReachabilityCallbackSetResolvedAddress
, 
3280                 if (mp 
== MACH_PORT_NULL
) { 
3284 #endif  /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */ 
3286                 /* save the error associated with the attempt to resolve the name */ 
3287                 __SCNetworkReachabilityCallbackSetResolvedAddress(error
, NULL
, (void *)target
); 
3291         ok 
= enqueueAsyncDNSQuery(target
, mp
); 
3300 enqueueAsyncDNSRetry(SCNetworkReachabilityRef   target
) 
3303         dispatch_source_t               source
; 
3304         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3306         source 
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 
3309                                         dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0)); 
3310         if (source 
== NULL
) { 
3311                 SCLog(TRUE
, LOG_ERR
, CFSTR("SCNetworkReachability retry dispatch_source_create() failed")); 
3315         // retain the target ... and release it when the [timer] source is released 
3317         dispatch_set_context(source
, (void *)target
); 
3318         dispatch_set_finalizer_f(source
, (dispatch_function_t
)CFRelease
); 
3320         dispatch_source_set_event_handler(source
, ^(void) { 
3321                 __SCNetworkReachabilityPerformInline(target
, TRUE
); 
3324         // start a one-shot timer 
3325         delay 
= targetPrivate
->dnsRetryCount 
* EAI_NONAME_RETRY_DELAY_USEC 
* NSEC_PER_USEC
; 
3326         dispatch_source_set_timer(source
, 
3327                                   dispatch_time(DISPATCH_TIME_NOW
, delay
),      // start 
3329                                   10 * NSEC_PER_MSEC
);                          // leeway 
3331         targetPrivate
->dnsRetry 
= source
; 
3332         dispatch_resume(source
); 
3339 dequeueAsyncDNSRetry(SCNetworkReachabilityRef   target
) 
3341         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3343         if (targetPrivate
->dnsRetry 
!= NULL
) { 
3344                 dispatch_source_cancel(targetPrivate
->dnsRetry
); 
3345                 dispatch_release(targetPrivate
->dnsRetry
); 
3346                 targetPrivate
->dnsRetry 
= NULL
; 
3354 #pragma mark OnDemand 
3358 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef       target
, 
3359                                          CFDictionaryRef                
*userOptions
) 
3361         SCNetworkServiceRef             service         
= NULL
; 
3362         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3364         if (!isA_SCNetworkReachability(target
)) { 
3365                 _SCErrorSet(kSCStatusInvalidArgument
); 
3369         if (targetPrivate
->onDemandServiceID 
!= NULL
) { 
3370                 service 
= _SCNetworkServiceCopyActive(NULL
, targetPrivate
->onDemandServiceID
); 
3373         if (userOptions 
!= NULL
) { 
3374                 if (targetPrivate
->onDemandName 
!= NULL
) { 
3375                         *userOptions 
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
3376                         CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
); 
3378                         *userOptions 
= NULL
; 
3387 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef   onDemandServer
, 
3388                                              SCNetworkReachabilityFlags onDemandFlags
, 
3391         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)info
; 
3392         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3394         pthread_mutex_lock(&targetPrivate
->lock
); 
3396         if (!targetPrivate
->scheduled
) { 
3397                 // if not currently scheduled 
3398                 pthread_mutex_unlock(&targetPrivate
->lock
); 
3402         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sOnDemand \"server\" status changed"), 
3403               targetPrivate
->log_prefix
); 
3404         __SCNetworkReachabilityPerform(target
); 
3406         pthread_mutex_unlock(&targetPrivate
->lock
); 
3413 __SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef   store_info
, 
3414                                      SCNetworkReachabilityRef   target
, 
3415                                      Boolean                    onDemandRetry
, 
3416                                      SCNetworkReachabilityFlags 
*flags
) 
3419         Boolean                         onDemand                
= FALSE
; 
3420         CFStringRef                     onDemandRemoteAddress   
= NULL
; 
3421         CFStringRef                     onDemandServiceID       
= NULL
; 
3422         SCNetworkConnectionStatus       onDemandStatus
; 
3423         SCDynamicStoreRef               store
; 
3424         SCNetworkReachabilityPrivateRef targetPrivate           
= (SCNetworkReachabilityPrivateRef
)target
; 
3426 //      SCLog(_sc_debug, LOG_INFO, 
3427 //            CFSTR("%s__SCNetworkReachabilityOnDemandCheck %s"), 
3428 //            targetPrivate->log_prefix, 
3429 //            onDemandRetry ? "after" : "before"); 
3431         if (targetPrivate
->onDemandName 
== NULL
) { 
3432                 targetPrivate
->onDemandName 
= CFStringCreateWithCString(NULL
, targetPrivate
->name
, kCFStringEncodingUTF8
); 
3436          * check if an OnDemand VPN configuration matches the name. 
3438         store 
= store_info
->store
; 
3439         ok 
= __SCNetworkConnectionCopyOnDemandInfoWithName(&store
, 
3440                                                            targetPrivate
->onDemandName
, 
3444                                                            &onDemandRemoteAddress
); 
3445         if (store_info
->store 
!= store
) { 
3446                 store_info
->store 
= store
; 
3447                 store_info
->storeAdded 
= TRUE
; 
3449         if (!_SC_CFEqual(targetPrivate
->onDemandRemoteAddress
, onDemandRemoteAddress
) || 
3450             !_SC_CFEqual(targetPrivate
->onDemandServiceID
, onDemandServiceID
)) { 
3451                 if (targetPrivate
->onDemandRemoteAddress 
!= NULL
) { 
3452                         CFRelease(targetPrivate
->onDemandRemoteAddress
); 
3453                         targetPrivate
->onDemandRemoteAddress 
= NULL
; 
3456                 if (targetPrivate
->onDemandServer 
!= NULL
) { 
3457                         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
3459                                 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
); 
3460                         } else if (targetPrivate
->rls 
!= NULL
) { 
3465                                 n 
= CFArrayGetCount(targetPrivate
->rlList
); 
3466                                 for (i 
= 0; i 
< n
; i 
+= 3) { 
3467                                         CFRunLoopRef    rl      
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1); 
3468                                         CFStringRef     rlMode  
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2); 
3470                                         __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, TRUE
); 
3474                         CFRelease(targetPrivate
->onDemandServer
); 
3475                         targetPrivate
->onDemandServer 
= NULL
; 
3478                 if (targetPrivate
->onDemandServiceID 
!= NULL
) { 
3479                         CFRelease(targetPrivate
->onDemandServiceID
); 
3480                         targetPrivate
->onDemandServiceID 
= NULL
; 
3484                 if (onDemandStatus 
!= kSCNetworkConnectionConnected
) { 
3486                          * if we have a VPN configuration matching the name *and* we need to 
3487                          * bring the VPN up.  Combine our flags with those of the VPN server. 
3489                         if (targetPrivate
->onDemandServer 
== NULL
) { 
3490                                 CFMutableDictionaryRef          options
; 
3492                                 options 
= CFDictionaryCreateMutable(NULL
, 
3494                                                                     &kCFTypeDictionaryKeyCallBacks
, 
3495                                                                     &kCFTypeDictionaryValueCallBacks
); 
3496                                 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, onDemandRemoteAddress
); 
3497                                 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandByPass
, kCFBooleanTrue
); 
3498                                 targetPrivate
->onDemandServer 
= SCNetworkReachabilityCreateWithOptions(NULL
, options
); 
3501                                 if (targetPrivate
->scheduled
) { 
3502                                         SCNetworkReachabilityContext    context 
= { 0, NULL
, CFRetain
, CFRelease
, CFCopyDescription 
}; 
3504                                         context
.info 
= (void *)target
; 
3505                                         SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, 
3506                                                                          __SCNetworkReachabilityOnDemandCheckCallback
, 
3509                                         // schedule server reachability to match that of the target 
3510                                         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
3511                                                 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, targetPrivate
->dispatchQueue
, TRUE
); 
3516                                                 n 
= CFArrayGetCount(targetPrivate
->rlList
); 
3517                                                 for (i 
= 0; i 
< n
; i 
+= 3) { 
3518                                                         CFRunLoopRef    rl      
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1); 
3519                                                         CFStringRef     rlMode  
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2); 
3521                                                         __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, NULL
, TRUE
); 
3527                         ok 
= SCNetworkReachabilityGetFlags(targetPrivate
->onDemandServer
, flags
); 
3528                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  status  * = 0x%08x"), 
3529                               targetPrivate
->log_prefix
, 
3531                         if (ok 
&& (*flags 
& kSCNetworkReachabilityFlagsReachable
)) { 
3532                                 if (!(*flags 
& kSCNetworkReachabilityFlagsTransientConnection
)) { 
3533                                         // start clean if not already layered on a transient network 
3536                                 *flags 
|= kSCNetworkReachabilityFlagsReachable
; 
3537                                 *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
3538                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
3539                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionOnDemand
; 
3542                                         SCLog(TRUE
, LOG_INFO
, CFSTR("%s  service * = %@"), 
3543                                               targetPrivate
->log_prefix
, 
3545                                         SCLog(TRUE
, LOG_INFO
, CFSTR("%s  status    = isReachable (after OnDemand connect)"), 
3546                                               targetPrivate
->log_prefix
); 
3553                 if (onDemandRemoteAddress 
!= NULL
) { 
3554                         if (targetPrivate
->onDemandRemoteAddress 
== NULL
) { 
3555                                 targetPrivate
->onDemandRemoteAddress 
= onDemandRemoteAddress
; 
3557                                 CFRelease(onDemandRemoteAddress
); 
3561                 if (onDemandServiceID 
!= NULL
) { 
3562                         if (targetPrivate
->onDemandServiceID 
== NULL
) { 
3563                                 targetPrivate
->onDemandServiceID 
= onDemandServiceID
; 
3565                                 CFRelease(onDemandServiceID
); 
3575 #pragma mark Reachability Flags 
3578 #ifdef  HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL 
3581         struct addrinfo 
*res
; 
3586 reply_callback(int32_t status
, struct addrinfo 
*res
, void *context
) 
3588         reply_info      
*reply  
= (reply_info 
*)context
; 
3590         reply
->status 
= status
; 
3597 getaddrinfo_interface_sync(const char                   *nodename
, 
3598                            const char                   *servname
, 
3599                            const struct addrinfo        
*hints
, 
3600                            const char                   *interface
, 
3601                            struct addrinfo              
**res
) 
3604         reply_info      reply   
= { NETDB_SUCCESS
, NULL 
}; 
3606         mp 
= _getaddrinfo_interface_async_call(nodename
, 
3612         if (mp 
== MACH_PORT_NULL
) { 
3620                         mach_msg_empty_rcv_t    msg
; 
3622                 kern_return_t   m_status
; 
3624                 m_status 
= mach_msg(&m_reply
.msg
.header
,        /* msg */ 
3625                                     MACH_RCV_MSG
,               /* options */ 
3627                                     sizeof(m_reply
),            /* rcv_size */ 
3629                                     MACH_MSG_TIMEOUT_NONE
,      /* timeout */ 
3630                                     MACH_PORT_NULL
);            /* notify */ 
3631                 if (m_status 
!= KERN_SUCCESS
) { 
3635                 g_status 
= getaddrinfo_async_handle_reply((void *)m_reply
.buf
); 
3636                 if (g_status 
!= 0) { 
3637                         if (reply
.res 
!= NULL
) { 
3638                                 freeaddrinfo(reply
.res
); 
3644                 if ((reply
.res 
!= NULL
) || (reply
.status 
!= NETDB_SUCCESS
)) { 
3645                         // if we have a reply or an error 
3649                 // if the request is not complete and needs to be re-queued 
3653         return reply
.status
; 
3655 #endif  /* HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL */ 
3659 __SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef        store_info
, 
3660                                 SCNetworkReachabilityRef        target
, 
3661                                 ReachabilityInfo                
*reach_info
, 
3664         CFMutableArrayRef               addresses       
= NULL
; 
3665         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3666         ReachabilityInfo                my_info         
= NOT_REACHABLE
; 
3669         *reach_info 
= NOT_REACHABLE
; 
3671         if (!isA_SCNetworkReachability(target
)) { 
3672                 _SCErrorSet(kSCStatusInvalidArgument
); 
3676         switch (targetPrivate
->type
) { 
3677                 case reachabilityTypeAddress 
: 
3678                 case reachabilityTypeAddressPair 
: { 
3680                          * Check "local" address 
3682                         if (targetPrivate
->localAddress 
!= NULL
) { 
3684                                  * Check "local" address 
3686                                 ok 
= checkAddress(store_info
, 
3687                                                   targetPrivate
->localAddress
, 
3688                                                   targetPrivate
->if_index
, 
3690                                                   targetPrivate
->log_prefix
); 
3692                                         goto error
;     /* not today */ 
3695                                 if (!(my_info
.flags 
& kSCNetworkReachabilityFlagsIsLocalAddress
)) { 
3696                                         goto error
;     /* not reachable, non-"local" address */ 
3701                          * Check "remote" address 
3703                         if (targetPrivate
->remoteAddress 
!= NULL
) { 
3705                                  * in cases where we have "local" and "remote" addresses 
3706                                  * we need to re-initialize the to-be-returned flags. 
3708                                 my_info 
= NOT_REACHABLE
; 
3711                                  * Check "remote" address 
3713                                 ok 
= checkAddress(store_info
, 
3714                                                   targetPrivate
->remoteAddress
, 
3715                                                   targetPrivate
->if_index
, 
3717                                                   targetPrivate
->log_prefix
); 
3719                                         goto error
;     /* not today */ 
3727                 case reachabilityTypeName 
: { 
3728                         struct timeval                  dnsQueryStart
; 
3729                         struct timeval                  dnsQueryEnd
; 
3731                         SCNetworkReachabilityFlags      ns_flags
; 
3732                         struct addrinfo                 
*res
; 
3734                         addresses 
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
); 
3735                         if ((addresses 
!= NULL
) || (error 
!= NETDB_SUCCESS
)) { 
3736                                 /* if resolved or an error had been detected */ 
3738                                         /* if not an async request */ 
3739                                         goto checkResolvedAddress
; 
3740                                 } else if ((targetPrivate
->dnsMP 
== MACH_PORT_NULL
) && !targetPrivate
->needResolve
) { 
3741                                         struct timeval          elapsed
; 
3742                                         const struct timeval    retry_limit     
= { EAI_NONAME_RETRY_LIMIT_USEC 
/ USEC_PER_SEC
, 
3743                                                                                     EAI_NONAME_RETRY_LIMIT_USEC 
% USEC_PER_SEC 
}; 
3746                                          * if this is an async request (i.e. someone is watching the reachability 
3747                                          * of this target), if no query active, and if no query is needed 
3750                                         if ((error 
!= EAI_NONAME
) 
3751 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) 
3752                                             && (error 
!= EAI_NODATA
) 
3755                                                 /* if not "host not found" */ 
3756                                                 goto checkResolvedAddress
; 
3760                                          * if our last DNS query returned EAI_NONAME then we 
3761                                          * "may" want to retry. 
3763                                          * Specifically, if the [DNS] configuration was updated a while 
3764                                          * back then we'll trust the EAI_NONAME reply. Otherwise, we 
3765                                          * want to try again to ensure that we didn't get caught in a 
3766                                          * race between the time when the configuration was changed and 
3767                                          * when mDNSResponder is really ready to handle the query. 
3769                                          * Retry handling details : 
3771                                          * Compare the time when the DNS configuration was last changed and 
3772                                          * when our DNS reply was started (->last_dns vs ->dnsQueryStart). 
3774                                          * Expected: 0 < last_dns (t1) < dnsQueryStart (t2) 
3776                                          * last  start  end   description                        action 
3777                                          * ====  =====  ====  =================================  ======== 
3778                                          *  0     N/A    N/A  no change, query error             no retry 
3779                                          *  0     N/A    N/A  no change, query complete          no retry 
3780                                          *  N/A   N/A    0    changed, query in-flight or error  no retry 
3781                                          *  t1 >  t2          query started, then [DNS] changed  no retry 
3782                                          *  t1 == t2          changed & query started together   no retry 
3783                                          *  t1 <  t2          changed, then query started        retry 
3786                                         if (!timerisset(&targetPrivate
->last_dns
)) { 
3788                                                  * if we have not yet seen a DNS configuration 
3791                                                 goto checkResolvedAddress
; 
3794                                         if (!timerisset(&targetPrivate
->dnsQueryEnd
)) { 
3796                                                  * if no query end time (new request in flight) 
3798                                                 goto checkResolvedAddress
; 
3801                                         if (timercmp(&targetPrivate
->last_dns
, 
3802                                                      &targetPrivate
->dnsQueryStart
, 
3805                                                  * if our DNS query started and then, a 
3806                                                  * short time later, the DNS configuration 
3807                                                  * was changed we don't need to retry 
3808                                                  * because we will be re-issuing (and not 
3809                                                  * retrying) the query. 
3811                                                 goto checkResolvedAddress
; 
3814                                         timersub(&targetPrivate
->dnsQueryStart
, 
3815                                                  &targetPrivate
->last_dns
, 
3817                                         if (timercmp(&elapsed
, &retry_limit
, >)) { 
3819                                                  * if the DNS query started after mDNSResponder 
3820                                                  * had a chance to apply the last configuration 
3821                                                  * then we should trust the EAI_NONAME reply. 
3823                                                 goto checkResolvedAddress
; 
3826                                         /* retry the DNS query */ 
3828                                         if (targetPrivate
->dnsRetry 
!= NULL
) { 
3829                                                 // no need to schedule if we already have a 
3830                                                 // retry query in flight 
3834                                         targetPrivate
->dnsRetryCount
++; 
3836                                         SCLog(_sc_debug
, LOG_INFO
, 
3837                                               CFSTR("%sretry [%d] DNS query for %s%s%s%s%s"), 
3838                                               targetPrivate
->log_prefix
, 
3839                                               targetPrivate
->dnsRetryCount
, 
3840                                               targetPrivate
->name 
!= NULL 
? "name = " : "", 
3841                                               targetPrivate
->name 
!= NULL 
? targetPrivate
->name 
: "", 
3842                                               targetPrivate
->name 
!= NULL 
&& targetPrivate
->serv 
!= NULL 
? ", " : "", 
3843                                               targetPrivate
->serv 
!= NULL 
? "serv = " : "", 
3844                                               targetPrivate
->serv 
!= NULL 
? targetPrivate
->serv 
: ""); 
3846                                         enqueueAsyncDNSRetry(target
); 
3852                         if (!targetPrivate
->onDemandBypass
) { 
3856                                  * before we attempt our initial DNS query, check if there is 
3857                                  * an OnDemand configuration that we should be using. 
3859                                 onDemand 
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, FALSE
, &my_info
.flags
); 
3861                                         /* if OnDemand connection is needed */ 
3866                         /* check the reachability of the DNS servers */ 
3867                         ok 
= _SC_R_checkResolverReachability(store_info
, 
3869                                                              &targetPrivate
->haveDNS
, 
3870                                                              targetPrivate
->name
, 
3871                                                              targetPrivate
->serv
, 
3872                                                              targetPrivate
->if_index
, 
3873                                                              targetPrivate
->log_prefix
); 
3875                                 /* if we could not get DNS server info */ 
3876                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"), 
3877                                       targetPrivate
->log_prefix
); 
3879                         } else if (rankReachability(ns_flags
) < 2) { 
3881                                  * if DNS servers are not (or are no longer) reachable, set 
3882                                  * flags based on the availability of configured (but not 
3886                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"), 
3887                                       targetPrivate
->log_prefix
); 
3889                                 ok 
= checkAddress(store_info
, 
3891                                                   targetPrivate
->if_index
, 
3893                                                   targetPrivate
->log_prefix
); 
3895                                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sNo available networks"), 
3896                                               targetPrivate
->log_prefix
); 
3900                                 if (async 
&& targetPrivate
->scheduled
) { 
3902                                          * return "host not found", set flags appropriately, 
3903                                          * and schedule notification. 
3905                                         __SCNetworkReachabilityCallbackSetResolvedAddress(EAI_NONAME
, 
3908                                         my_info
.flags 
|= (targetPrivate
->info
.flags 
& kSCNetworkReachabilityFlagsFirstResolvePending
); 
3910                                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sno DNS servers are reachable"), 
3911                                               targetPrivate
->log_prefix
); 
3912                                         __SCNetworkReachabilityPerform(target
); 
3918                                 /* for async requests we return the last known status */ 
3919                                 my_info 
= targetPrivate
->info
; 
3921                                 if (targetPrivate
->dnsPort 
!= NULL
) { 
3922                                         /* if request already in progress */ 
3923                                         SCLog(_sc_debug
, LOG_INFO
, 
3924                                               CFSTR("%swaiting for DNS reply"), 
3925                                               targetPrivate
->log_prefix
); 
3926                                         if ((addresses 
!= NULL
) || (error 
!= NETDB_SUCCESS
)) { 
3927                                                 /* updated reachability based on the previous reply */ 
3928                                                 goto checkResolvedAddress
; 
3933                                 if (targetPrivate
->dnsRetry 
!= NULL
) { 
3934                                         /* if we already have a "retry" queued */ 
3938                                 SCLog(_sc_debug
, LOG_INFO
, 
3939                                       CFSTR("%sstart DNS query for %s%s%s%s%s"), 
3940                                       targetPrivate
->log_prefix
, 
3941                                       targetPrivate
->name 
!= NULL 
? "name = " : "", 
3942                                       targetPrivate
->name 
!= NULL 
? targetPrivate
->name 
: "", 
3943                                       targetPrivate
->name 
!= NULL 
&& targetPrivate
->serv 
!= NULL 
? ", " : "", 
3944                                       targetPrivate
->serv 
!= NULL 
? "serv = " : "", 
3945                                       targetPrivate
->serv 
!= NULL 
? targetPrivate
->serv 
: ""); 
3948                                  * initiate an async DNS query 
3950                                 if (!startAsyncDNSQuery(target
)) { 
3951                                         /* if we could not initiate the request, process error */ 
3952                                         goto checkResolvedAddress
; 
3955                                 /* request initiated */ 
3959                         SCLog(_sc_debug
, LOG_INFO
, 
3960                               CFSTR("%scheck DNS for %s%s%s%s%s"), 
3961                               targetPrivate
->log_prefix
, 
3962                               targetPrivate
->name 
!= NULL 
? "name = " : "", 
3963                               targetPrivate
->name 
!= NULL 
? targetPrivate
->name 
: "", 
3964                               targetPrivate
->name 
!= NULL 
&& targetPrivate
->serv 
!= NULL 
? ", " : "", 
3965                               targetPrivate
->serv 
!= NULL 
? "serv = " : "", 
3966                               targetPrivate
->serv 
!= NULL 
? targetPrivate
->serv 
: ""); 
3969                          * OK, all of the DNS name servers are available.  Let's 
3970                          * resolve the nodename into an address. 
3972                         __dns_query_start(&dnsQueryStart
, &dnsQueryEnd
); 
3974 #ifdef  HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL 
3975                         if (targetPrivate
->if_index 
== 0) { 
3976 #endif  // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL 
3977                                 error 
= getaddrinfo(targetPrivate
->name
, 
3978                                                     targetPrivate
->serv
, 
3979                                                     &targetPrivate
->hints
, 
3981 #ifdef  HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL 
3983                                 error 
= getaddrinfo_interface_sync(targetPrivate
->name
, 
3984                                                                    targetPrivate
->serv
, 
3985                                                                    &targetPrivate
->hints
, 
3986                                                                    targetPrivate
->if_name
, 
3989 #endif  // HAVE_GETADDRINFO_INTERFACE_ASYNC_CALL 
3991                         __dns_query_end(target
, 
3992                                          ((error 
== 0) && (res 
!= NULL
)),       // if successful query 
3994                                          &dnsQueryStart
,                        // start time 
3995                                          &dnsQueryEnd
);                         // end time 
3997                         __SCNetworkReachabilitySetResolvedAddress(error
, res
, target
); 
3999                         addresses 
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
); 
4001                     checkResolvedAddress 
: 
4004                          * We first assume that the requested host is NOT available. 
4005                          * Then, check each address for accessibility and return the 
4006                          * best status available. 
4008                         my_info 
= NOT_REACHABLE
; 
4010                         if (isA_CFArray(addresses
)) { 
4012                                 CFIndex         n       
= CFArrayGetCount(addresses
); 
4014                                 for (i 
= 0; i 
< n
; i
++) { 
4015                                         ReachabilityInfo        ns_info 
= NOT_REACHABLE
; 
4016                                         struct sockaddr         
*sa
; 
4018                                         sa 
= (struct sockaddr 
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
)); 
4020                                         ok 
= checkAddress(store_info
, 
4022                                                           targetPrivate
->if_index
, 
4024                                                           targetPrivate
->log_prefix
); 
4026                                                 goto error
;     /* not today */ 
4029                                         if (rankReachability(ns_info
.flags
) > rankReachability(my_info
.flags
)) { 
4030                                                 /* return the best case result */ 
4032                                                 if (rankReachability(my_info
.flags
) == 2) { 
4039                                 if ((error 
== EAI_NONAME
) 
4040 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) 
4041                                      || (error 
== EAI_NODATA
) 
4045                                          * the target host name could not be resolved 
4047                                         if (!targetPrivate
->onDemandBypass
) { 
4051                                                  * our initial DNS query failed, check again to see if there 
4052                                                  * there is an OnDemand configuration that we should be using. 
4054                                                 onDemand 
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, TRUE
, &my_info
.flags
); 
4056                                                         /* if OnDemand connection is needed */ 
4061                                         if (!targetPrivate
->haveDNS
) { 
4063                                                  * No DNS servers are defined. Set flags based on 
4064                                                  * the availability of configured (but not active) 
4067                                                 ok 
= checkAddress(store_info
, 
4069                                                                   targetPrivate
->if_index
, 
4071                                                                   targetPrivate
->log_prefix
); 
4073                                                         goto error
;     /* not today */ 
4076                                                 if ((my_info
.flags 
& kSCNetworkReachabilityFlagsReachable
) && 
4077                                                         (my_info
.flags 
& kSCNetworkReachabilityFlagsConnectionRequired
)) { 
4079                                                          * Since we might pick up a set of DNS servers when this connection 
4080                                                          * is established, don't reply with a "HOST NOT FOUND" error just yet. 
4085                                                 /* Host not found, not reachable! */ 
4086                                                 my_info 
= NOT_REACHABLE
; 
4097         *reach_info 
= my_info
; 
4101         if (addresses 
!= NULL
)  CFRelease(addresses
); 
4107 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef          target
, 
4108                               SCNetworkReachabilityFlags        
*flags
) 
4111         ReachabilityStoreInfo           store_info
; 
4112         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
4114         if (!isA_SCNetworkReachability(target
)) { 
4115                 _SCErrorSet(kSCStatusInvalidArgument
); 
4119         initReachabilityStoreInfo(&store_info
); 
4120         pthread_mutex_lock(&targetPrivate
->lock
); 
4122         if (targetPrivate
->scheduled
) { 
4123                 // if being watched, return the last known (and what should be current) status 
4124                 *flags 
= targetPrivate
->info
.flags 
& ~kSCNetworkReachabilityFlagsFirstResolvePending
; 
4129         ok 
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
); 
4130         *flags 
= targetPrivate
->info
.flags 
& ~kSCNetworkReachabilityFlagsFirstResolvePending
; 
4134         pthread_mutex_unlock(&targetPrivate
->lock
); 
4135         freeReachabilityStoreInfo(&store_info
); 
4141 #pragma mark Notifications 
4145 __SCNetworkReachabilityReachabilitySetNotifications(SCDynamicStoreRef   store
) 
4148         CFMutableArrayRef               keys
; 
4149         CFStringRef                     pattern
; 
4150         CFMutableArrayRef               patterns
; 
4152         keys     
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
4153         patterns 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
4155         // Setup:/Network/Global/IPv4 (for the ServiceOrder) 
4156         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
4157                                                          kSCDynamicStoreDomainSetup
, 
4159         CFArrayAppendValue(keys
, key
); 
4162         // State:/Network/Global/DNS 
4163         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
4164                                                          kSCDynamicStoreDomainState
, 
4166         CFArrayAppendValue(keys
, key
); 
4169         // State:/Network/Global/IPv4 (default route) 
4170         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
4171                                                          kSCDynamicStoreDomainState
, 
4173         CFArrayAppendValue(keys
, key
); 
4176         // State:/Network/Global/OnDemand 
4177         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
4178                                                          kSCDynamicStoreDomainState
, 
4180         CFArrayAppendValue(keys
, key
); 
4183         // Setup: per-service Interface info 
4184         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
4185                                                               kSCDynamicStoreDomainSetup
, 
4187                                                               kSCEntNetInterface
); 
4188         CFArrayAppendValue(patterns
, pattern
); 
4191         // per-service IPv4 info 
4192         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
4193                                                               kSCDynamicStoreDomainSetup
, 
4196         CFArrayAppendValue(patterns
, pattern
); 
4198         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
4199                                                               kSCDynamicStoreDomainState
, 
4202         CFArrayAppendValue(patterns
, pattern
); 
4205         // per-service IPv6 info 
4206         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
4207                                                               kSCDynamicStoreDomainSetup
, 
4210         CFArrayAppendValue(patterns
, pattern
); 
4212         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
4213                                                               kSCDynamicStoreDomainState
, 
4216         CFArrayAppendValue(patterns
, pattern
); 
4219         // per-service PPP info (for existence, kSCPropNetPPPDialOnDemand, kSCPropNetPPPStatus) 
4220         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
4221                                                               kSCDynamicStoreDomainSetup
, 
4224         CFArrayAppendValue(patterns
, pattern
); 
4226         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
4227                                                               kSCDynamicStoreDomainState
, 
4230         CFArrayAppendValue(patterns
, pattern
); 
4233 #if     !TARGET_IPHONE_SIMULATOR 
4234         // per-service VPN info (for existence, kSCPropNetVPNStatus) 
4235         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
4236                                                               kSCDynamicStoreDomainSetup
, 
4239         CFArrayAppendValue(patterns
, pattern
); 
4241         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
4242                                                               kSCDynamicStoreDomainState
, 
4245         CFArrayAppendValue(patterns
, pattern
); 
4247 #endif  // !TARGET_IPHONE_SIMULATOR 
4249         // per-service IPSec info (for existence, kSCPropNetIPSecStatus) 
4250         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
4251                                                               kSCDynamicStoreDomainSetup
, 
4254         CFArrayAppendValue(patterns
, pattern
); 
4256         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
4257                                                               kSCDynamicStoreDomainState
, 
4260         CFArrayAppendValue(patterns
, pattern
); 
4263 #if     !TARGET_OS_IPHONE 
4264         // State: Power Management Capabilities 
4265         key 
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"), 
4266                                       kSCDynamicStoreDomainState
, 
4267                                       CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
)); 
4268         CFArrayAppendValue(keys
, key
); 
4270 #endif  // TARGET_OS_IPHONE 
4273         (void)SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
); 
4275         CFRelease(patterns
); 
4282 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef  store
, 
4283                                      CFArrayRef         changedKeys
, 
4286 #if     !TARGET_OS_IPHONE 
4287         Boolean                 cpuStatusChanged        
= FALSE
; 
4288 #endif  // !TARGET_OS_IPHONE 
4289         Boolean                 dnsConfigChanged        
= FALSE
; 
4292         CFIndex                 nChanges                
= CFArrayGetCount(changedKeys
); 
4295 #if     !TARGET_OS_IPHONE 
4296         Boolean                 powerStatusChanged      
= FALSE
; 
4297 #endif  // !TARGET_OS_IPHONE 
4298         ReachabilityStoreInfo   store_info
; 
4299         const void *            targets_q
[N_QUICK
]; 
4300         const void **           targets                 
= targets_q
; 
4302         if (nChanges 
== 0) { 
4307         pthread_mutex_lock(&hn_lock
); 
4309         nTargets 
= (hn_targets 
!= NULL
) ? CFSetGetCount(hn_targets
) : 0; 
4310         if (nTargets 
== 0) { 
4311                 /* if no addresses being monitored */ 
4315         /* grab the current time */ 
4316         (void)gettimeofday(&now
, NULL
); 
4318 #if     !TARGET_OS_IPHONE 
4319         key 
= SCDynamicStoreKeyCreate(NULL
, CFSTR("%@%@"), 
4320                                       kSCDynamicStoreDomainState
, 
4321                                       CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix
)); 
4322         if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) { 
4325                 num 
= SCDynamicStoreCopyValue(store
, key
); 
4327                         if (isA_CFNumber(num
) && 
4328                             CFNumberGetValue(num
, kCFNumberSInt32Type
, &power_capabilities
)) { 
4329                                 static Boolean  haveCPU_old     
= TRUE
; 
4330                                 Boolean         haveCPU_new
; 
4332                                 powerStatusChanged 
= TRUE
; 
4334                                 haveCPU_new 
= (power_capabilities 
& kIOPMSystemPowerStateCapabilityCPU
) != 0; 
4335                                 if ((haveCPU_old 
!= haveCPU_new
) && haveCPU_new
) { 
4337                                          * if the power state now shows CPU availability 
4338                                          * then we will assume that the DNS configuration 
4339                                          * has changed.  This will force us to re-issue 
4340                                          * our DNS queries since mDNSResponder does not 
4341                                          * attempt to resolve names when "sleeping". 
4343                                         cpuStatusChanged 
= TRUE
; 
4344                                         dnsConfigChanged 
= TRUE
; 
4346                                 haveCPU_old 
= haveCPU_new
; 
4353 #endif  // !TARGET_OS_IPHONE 
4355         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
4356                                                          kSCDynamicStoreDomainState
, 
4358         if (CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
)) { 
4359                 dnsConfigChanged 
= TRUE
;        /* the DNS server(s) have changed */ 
4364                 unsigned int            changes                 
= 0; 
4365                 static const char       *change_strings
[]       = { 
4366                         // with no "power" status change 
4371 #if     !TARGET_OS_IPHONE 
4372                         // with "power" status change 
4374                         "network and power ", 
4376                         "network, DNS, and power ", 
4378                         // with no "power" status change (including CPU "on") 
4380                         "network and power* ", 
4382                         "network, DNS, and power* ", 
4383 #endif  // !TARGET_OS_IPHONE 
4386 #if     !TARGET_OS_IPHONE 
4388                 if (powerStatusChanged
) { 
4390                         if (cpuStatusChanged
) { 
4395 #endif  // !TARGET_OS_IPHONE 
4398                 if (dnsConfigChanged
) { 
4408                 SCLog(TRUE
, LOG_INFO
, 
4409                       CFSTR("process %sconfiguration change"), 
4410                       change_strings
[changes
]); 
4413         initReachabilityStoreInfo(&store_info
); 
4415         if (nTargets 
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
))) 
4416                 targets 
= CFAllocatorAllocate(NULL
, nTargets 
* sizeof(CFTypeRef
), 0); 
4417         CFSetGetValues(hn_targets
, targets
); 
4418         for (i 
= 0; i 
< nTargets
; i
++) { 
4419                 SCNetworkReachabilityRef        target          
= targets
[i
]; 
4420                 SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
4422                 pthread_mutex_lock(&targetPrivate
->lock
); 
4424                 if (dnsConfigChanged
) { 
4425                         targetPrivate
->last_dns 
= now
; 
4426                         targetPrivate
->dnsRetryCount 
= 0; 
4429                 if (targetPrivate
->type 
== reachabilityTypeName
) { 
4430                         Boolean         dnsChanged      
= dnsConfigChanged
; 
4434                                  * if the DNS configuration didn't change we still need to 
4435                                  * check that the DNS servers are accessible. 
4437                                 SCNetworkReachabilityFlags      ns_flags
; 
4440                                 /* check the reachability of the DNS servers */ 
4441                                 ok 
= updateReachabilityStoreInfo(&store_info
, &store
, AF_UNSPEC
); 
4443                                         ok 
= _SC_R_checkResolverReachability(&store_info
, 
4445                                                                              &targetPrivate
->haveDNS
, 
4446                                                                              targetPrivate
->name
, 
4447                                                                              targetPrivate
->serv
, 
4448                                                                              targetPrivate
->if_index
, 
4449                                                                              targetPrivate
->log_prefix
); 
4453                                         /* if we could not get DNS server info */ 
4454                                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server reachability unknown"), 
4455                                               targetPrivate
->log_prefix
); 
4457                                 } else if (rankReachability(ns_flags
) < 2) { 
4459                                          * if DNS servers are not (or are no longer) reachable, set 
4460                                          * flags based on the availability of configured (but not 
4463                                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"), 
4464                                               targetPrivate
->log_prefix
); 
4470                                 if (targetPrivate
->dnsPort 
!= NULL
) { 
4471                                         mach_port_t     mp      
= CFMachPortGetPort(targetPrivate
->dnsPort
); 
4473                                         /* cancel the outstanding DNS query */ 
4474                                         SCLog(_sc_debug
, LOG_INFO
, 
4475                                               CFSTR("%scancel DNS query for %s%s%s%s%s"), 
4476                                               targetPrivate
->log_prefix
, 
4477                                               targetPrivate
->name 
!= NULL 
? "name = " : "", 
4478                                               targetPrivate
->name 
!= NULL 
? targetPrivate
->name 
: "", 
4479                                               targetPrivate
->name 
!= NULL 
&& targetPrivate
->serv 
!= NULL 
? ", " : "", 
4480                                               targetPrivate
->serv 
!= NULL 
? "serv = " : "", 
4481                                               targetPrivate
->serv 
!= NULL 
? targetPrivate
->serv 
: ""); 
4482                                         dequeueAsyncDNSQuery(target
); 
4483                                         getaddrinfo_async_cancel(mp
); 
4486                                 if (targetPrivate
->dnsRetry 
!= NULL
) { 
4487                                         /* cancel the outstanding DNS retry */ 
4488                                         dequeueAsyncDNSRetry(target
); 
4491                                 /* schedule request to resolve the name again */ 
4492                                 targetPrivate
->needResolve 
= TRUE
; 
4496                 __SCNetworkReachabilityPerform(target
); 
4498                 pthread_mutex_unlock(&targetPrivate
->lock
); 
4500         if (targets 
!= targets_q
)       CFAllocatorDeallocate(NULL
, targets
); 
4502         freeReachabilityStoreInfo(&store_info
); 
4506         pthread_mutex_unlock(&hn_lock
); 
4511 #if     !TARGET_OS_IPHONE 
4514 darkWakeNotify(SCNetworkReachabilityRef target
) 
4521 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities
) 
4524 #define POWER_CAPABILITIES_NEED (kIOPMSystemPowerStateCapabilityCPU             \ 
4525                                  | kIOPMSystemPowerStateCapabilityNetwork       \ 
4526                                  | kIOPMSystemPowerStateCapabilityDisk) 
4528         if ((power_capabilities 
& POWER_CAPABILITIES_NEED
) != POWER_CAPABILITIES_NEED
) { 
4530                  * we're not awake (from a networking point of view) unless we 
4531                  * have the CPU, disk, *and* network. 
4536         if ((power_capabilities 
& kIOPMSytemPowerStateCapabilitiesMask
) == POWER_CAPABILITIES_NEED
) { 
4538                  * if all we have is the CPU, disk, and network than this must 
4539                  * be a "maintenance" wake. 
4547 #endif  // !TARGET_OS_IPHONE 
4551 rlsPerform(void *info
) 
4554         void                            (*context_release
)(const void *); 
4555         Boolean                         defer           
= FALSE
; 
4557         ReachabilityInfo                reach_info      
= NOT_REACHABLE
; 
4558         SCNetworkReachabilityCallBack   rlsFunction
; 
4559         ReachabilityStoreInfo           store_info
; 
4560         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)info
; 
4561         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
4563         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%schecking target reachability"), 
4564               targetPrivate
->log_prefix
); 
4567         pthread_mutex_lock(&targetPrivate
->lock
); 
4569         if (targetPrivate
->dnsRetry 
!= NULL
) { 
4571                 dequeueAsyncDNSRetry(target
); 
4574         if (!targetPrivate
->scheduled
) { 
4575                 // if not currently scheduled 
4576                 pthread_mutex_unlock(&targetPrivate
->lock
); 
4580         /* update reachability, notify if status changed */ 
4581         initReachabilityStoreInfo(&store_info
); 
4582         ok 
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
); 
4583         freeReachabilityStoreInfo(&store_info
); 
4585                 /* if reachability status not available */ 
4586                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%flags not available"), 
4587                       targetPrivate
->log_prefix
); 
4588                 reach_info 
= NOT_REACHABLE
; 
4591 #if     !TARGET_OS_IPHONE 
4593          * We want to defer the notification if this is a maintenance wake *and* 
4594          * the reachability flags that we would be reporting to the application 
4595          * are better than those that we last reported. 
4597         if (!systemIsAwake(power_capabilities
)) { 
4598                 /* if this is a maintenace wake */ 
4599                 reach_info
.sleeping 
= TRUE
; 
4601                 if (rankReachability(reach_info
.flags
) >= rankReachability(targetPrivate
->info
.flags
)) { 
4603                          * don't report the change if the new reachability flags are 
4604                          * the same or "better" 
4606                         defer 
= !darkWakeNotify(target
); 
4607                 } else if (__reach_equal(&targetPrivate
->last_notify
, &reach_info
)) { 
4609                          * if we have already posted this change 
4611                         defer 
= !darkWakeNotify(target
); 
4614 #endif  // !TARGET_OS_IPHONE 
4616         if (__reach_equal(&targetPrivate
->info
, &reach_info
)) { 
4618                         if (targetPrivate
->info
.sleeping 
== reach_info
.sleeping
) { 
4619                                 SCLog(TRUE
, LOG_INFO
, 
4620                                       CFSTR("%sflags/interface match (now 0x%08x/%hu%s)"), 
4621                                       targetPrivate
->log_prefix
, 
4623                                       reach_info
.if_index
, 
4624                                       reach_info
.sleeping 
? ", z" : ""); 
4626                                 SCLog(TRUE
, LOG_INFO
, 
4627                                       CFSTR("%sflags/interface equiv (was 0x%08x/%hu%s, now 0x%08x/%hu%s)"), 
4628                                       targetPrivate
->log_prefix
, 
4629                                       targetPrivate
->info
.flags
, 
4630                                       targetPrivate
->info
.if_index
, 
4631                                       targetPrivate
->info
.sleeping 
? ", z" : "", 
4633                                       reach_info
.if_index
, 
4634                                       reach_info
.sleeping 
? ", z" : ""); 
4638                 pthread_mutex_unlock(&targetPrivate
->lock
); 
4642         SCLog(_sc_debug
, LOG_INFO
, 
4643               CFSTR("%sflags/interface have changed (was 0x%08x/%hu%s, now 0x%08x/%hu%s)%s"), 
4644               targetPrivate
->log_prefix
, 
4645               targetPrivate
->info
.flags
, 
4646               targetPrivate
->info
.if_index
, 
4647               targetPrivate
->info
.sleeping 
? ", z" : "", 
4649               reach_info
.if_index
, 
4650               reach_info
.sleeping 
? ", z" : "", 
4651               defer 
? ", deferred" : ""); 
4653         /* as needed, defer the notification */ 
4655                 pthread_mutex_unlock(&targetPrivate
->lock
); 
4659         /* update flags / interface */ 
4660         targetPrivate
->info 
= reach_info
; 
4662         /* save last notification info */ 
4663         targetPrivate
->last_notify 
= reach_info
; 
4666         rlsFunction 
= targetPrivate
->rlsFunction
; 
4667         if (targetPrivate
->rlsContext
.retain 
!= NULL
) { 
4668                 context_info    
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
); 
4669                 context_release 
= targetPrivate
->rlsContext
.release
; 
4671                 context_info    
= targetPrivate
->rlsContext
.info
; 
4672                 context_release 
= NULL
; 
4675         pthread_mutex_unlock(&targetPrivate
->lock
); 
4677         if (rlsFunction 
!= NULL
) { 
4678                 (*rlsFunction
)(target
, 
4679                                reach_info
.flags 
& ~kSCNetworkReachabilityFlagsFirstResolvePending
, 
4683         if (context_release 
!= NULL
) { 
4684                 (*context_release
)(context_info
); 
4692 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef       target
, 
4693                                  SCNetworkReachabilityCallBack  callout
, 
4694                                  SCNetworkReachabilityContext   
*context
) 
4696         SCNetworkReachabilityPrivateRef targetPrivate 
= (SCNetworkReachabilityPrivateRef
)target
; 
4698         pthread_mutex_lock(&targetPrivate
->lock
); 
4700         if (targetPrivate
->rlsContext
.release 
!= NULL
) { 
4701                 /* let go of the current context */ 
4702                 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
); 
4705         targetPrivate
->rlsFunction                      
= callout
; 
4706         targetPrivate
->rlsContext
.info                  
= NULL
; 
4707         targetPrivate
->rlsContext
.retain                
= NULL
; 
4708         targetPrivate
->rlsContext
.release               
= NULL
; 
4709         targetPrivate
->rlsContext
.copyDescription       
= NULL
; 
4711                 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
)); 
4712                 if (context
->retain 
!= NULL
) { 
4713                         targetPrivate
->rlsContext
.info 
= (void *)(*context
->retain
)(context
->info
); 
4717         pthread_mutex_unlock(&targetPrivate
->lock
); 
4724 reachRLSCopyDescription(const void *info
) 
4726         SCNetworkReachabilityRef                target  
= (SCNetworkReachabilityRef
)info
; 
4728         return CFStringCreateWithFormat(NULL
, 
4730                                         CFSTR("<SCNetworkReachability RLS> {target = %p}"), 
4736 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef     target
, 
4737                                            CFRunLoopRef                 runLoop
, 
4738                                            CFStringRef                  runLoopMode
, 
4739                                            dispatch_queue_t             queue
, 
4742         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
4743         Boolean                         init            
= FALSE
; 
4747                 pthread_mutex_lock(&hn_lock
); 
4749         pthread_mutex_lock(&targetPrivate
->lock
); 
4751         if ((targetPrivate
->dispatchQueue 
!= NULL
) ||           // if we are already scheduled with a dispatch queue 
4752             ((queue 
!= NULL
) && targetPrivate
->scheduled
)) {    // if we are already scheduled on a CFRunLoop 
4753                 _SCErrorSet(kSCStatusInvalidArgument
); 
4757         /* schedule the SCNetworkReachability run loop source */ 
4759         if (!onDemand 
&& (hn_store 
== NULL
)) { 
4761                  * if we are not monitoring any hosts, start watching 
4763                 if (!dns_configuration_watch()) { 
4765                         _SCErrorSet(kSCStatusFailed
); 
4769                 hn_store 
= SCDynamicStoreCreate(NULL
, 
4770                                                 CFSTR("SCNetworkReachability"), 
4771                                                 __SCNetworkReachabilityHandleChanges
, 
4773                 if (hn_store 
== NULL
) { 
4774                         SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed")); 
4778                 __SCNetworkReachabilityReachabilitySetNotifications(hn_store
); 
4780                 hn_dispatchQueue 
= dispatch_queue_create("com.apple.SCNetworkReachabilty.network_changes", NULL
); 
4781                 if (hn_dispatchQueue 
== NULL
) { 
4782                         SCLog(TRUE
, LOG_ERR
, CFSTR("__SCNetworkReachabilityScheduleWithRunLoop dispatch_queue_create() failed")); 
4783                         _SCErrorSet(kSCStatusFailed
); 
4784                         CFRelease(hn_store
); 
4788                 CFRetain(hn_store
);     // Note: will be released when the dispatch queue is released 
4789                 dispatch_set_context(hn_dispatchQueue
, (void *)hn_store
); 
4790                 dispatch_set_finalizer_f(hn_dispatchQueue
, (dispatch_function_t
)CFRelease
); 
4792                 ok 
= SCDynamicStoreSetDispatchQueue(hn_store
, hn_dispatchQueue
); 
4794                         SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetDispatchQueue() failed")); 
4795                         dispatch_release(hn_dispatchQueue
); 
4796                         hn_dispatchQueue 
= NULL
; 
4797                         CFRelease(hn_store
); 
4801                 hn_targets  
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
); 
4804         if (!targetPrivate
->scheduled
) { 
4805                 CFRunLoopSourceContext  context 
= { 0                           // version 
4806                                                   , (void *)target              
// info 
4807                                                   , CFRetain                    
// retain 
4808                                                   , CFRelease                   
// release 
4809                                                   , reachRLSCopyDescription     
// copyDescription 
4814                                                   , rlsPerform                  
// perform 
4817                 if (runLoop 
!= NULL
) { 
4818                         targetPrivate
->rls    
= CFRunLoopSourceCreate(NULL
, 0, &context
); 
4819                         targetPrivate
->rlList 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
4822                 targetPrivate
->scheduled 
= TRUE
; 
4823                 if (targetPrivate
->type 
== reachabilityTypeName
) { 
4824                         targetPrivate
->needResolve 
= TRUE
; 
4829         if (queue 
!= NULL
) { 
4830                 targetPrivate
->dispatchQueue 
= queue
; 
4831                 dispatch_retain(targetPrivate
->dispatchQueue
); 
4833                 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) { 
4835                          * if we do not already have host notifications scheduled with 
4836                          * this runLoop / runLoopMode 
4838                         CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
); 
4840                         if (targetPrivate
->dnsRLS 
!= NULL
) { 
4841                                 /* if we have an active async DNS query too */ 
4842                                 CFRunLoopAddSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
); 
4846                 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
); 
4849         CFSetAddValue(hn_targets
, target
); 
4852                 ReachabilityInfo        reach_info      
= NOT_REACHABLE
; 
4853                 ReachabilityStoreInfo   store_info
; 
4856                  * if we have yet to schedule SC notifications for this address 
4857                  * - initialize current reachability status 
4859                 initReachabilityStoreInfo(&store_info
); 
4860                 if (__SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
)) { 
4862                          * if reachability status available 
4864                          * - schedule notification to report status via callback 
4866                         targetPrivate
->info 
= reach_info
; 
4867                         __SCNetworkReachabilityPerform(target
); 
4869                         /* if reachability status not available, async lookup started */ 
4870                         targetPrivate
->info 
= NOT_REACHABLE
; 
4872                 freeReachabilityStoreInfo(&store_info
); 
4875         if (targetPrivate
->onDemandServer 
!= NULL
) { 
4876                 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, queue
, TRUE
); 
4879         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%sscheduled"), 
4880               targetPrivate
->log_prefix
); 
4886         pthread_mutex_unlock(&targetPrivate
->lock
); 
4888                 pthread_mutex_unlock(&hn_lock
); 
4895 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef   target
, 
4896                                              CFRunLoopRef               runLoop
, 
4897                                              CFStringRef                runLoopMode
, 
4900         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
4905                 pthread_mutex_lock(&hn_lock
); 
4907         pthread_mutex_lock(&targetPrivate
->lock
); 
4909         if (((runLoop 
== NULL
) && (targetPrivate
->dispatchQueue 
== NULL
)) ||    // if we should be scheduled on a dispatch queue (but are not) 
4910             ((runLoop 
!= NULL
) && (targetPrivate
->dispatchQueue 
!= NULL
))) {    // if we should be scheduled on a CFRunLoop (but are not) 
4911                 _SCErrorSet(kSCStatusInvalidArgument
); 
4915         if (!targetPrivate
->scheduled
) { 
4916                 // if not currently scheduled 
4917                 _SCErrorSet(kSCStatusInvalidArgument
); 
4921         // first, unschedule the target specific sources 
4922         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
4923                 if (targetPrivate
->onDemandServer 
!= NULL
) { 
4924                         __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
); 
4927                 if (!_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) { 
4928                         // if not currently scheduled 
4929                         _SCErrorSet(kSCStatusInvalidArgument
); 
4933                 if (targetPrivate
->onDemandServer 
!= NULL
) { 
4934                         __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, TRUE
); 
4937                 n 
= CFArrayGetCount(targetPrivate
->rlList
); 
4938                 if ((n 
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) { 
4939                         // if target is no longer scheduled for this runLoop / runLoopMode 
4940                         CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
); 
4942                         if (targetPrivate
->dnsRLS 
!= NULL
) { 
4943                                 // if we have an active async DNS query too 
4944                                 CFRunLoopRemoveSource(runLoop
, targetPrivate
->dnsRLS
, runLoopMode
); 
4948                                 // if *all* notifications have been unscheduled 
4949                                 CFRelease(targetPrivate
->rlList
); 
4950                                 targetPrivate
->rlList 
= NULL
; 
4951                                 CFRunLoopSourceInvalidate(targetPrivate
->rls
); 
4952                                 CFRelease(targetPrivate
->rls
); 
4953                                 targetPrivate
->rls 
= NULL
; 
4959                 // if *all* notifications have been unscheduled 
4960                 targetPrivate
->scheduled 
= FALSE
; 
4963                         CFSetRemoveValue(hn_targets
, target
);   // cleanup notification resources 
4966                 if (targetPrivate
->dnsPort 
!= NULL
) { 
4967                         mach_port_t     mp      
= CFMachPortGetPort(targetPrivate
->dnsPort
); 
4969                         // if we have an active async DNS query 
4970                         dequeueAsyncDNSQuery(target
); 
4971                         getaddrinfo_async_cancel(mp
); 
4974                 if (targetPrivate
->dnsRetry 
!= NULL
) { 
4975                         // if we have an outstanding DNS retry 
4976                         dequeueAsyncDNSRetry(target
); 
4980         if (runLoop 
== NULL
) { 
4981                 dispatch_release(targetPrivate
->dispatchQueue
); 
4982                 targetPrivate
->dispatchQueue 
= NULL
; 
4985         n 
= CFSetGetCount(hn_targets
); 
4987                 // if we are no longer monitoring any targets 
4988                 SCDynamicStoreSetDispatchQueue(hn_store
, NULL
); 
4989                 dispatch_release(hn_dispatchQueue
); 
4990                 hn_dispatchQueue 
= NULL
; 
4991                 CFRelease(hn_store
); 
4993                 CFRelease(hn_targets
); 
4997                  * until we start monitoring again, ensure that 
4998                  * any resources associated with tracking the 
4999                  * DNS configuration have been released. 
5001                 dns_configuration_unwatch(); 
5004         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%sunscheduled"), 
5005               targetPrivate
->log_prefix
); 
5011         pthread_mutex_unlock(&targetPrivate
->lock
); 
5013                 pthread_mutex_unlock(&hn_lock
); 
5019 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef       target
, 
5020                                          CFRunLoopRef                   runLoop
, 
5021                                          CFStringRef                    runLoopMode
) 
5023         if (!isA_SCNetworkReachability(target
) || (runLoop 
== NULL
) || (runLoopMode 
== NULL
)) { 
5024                 _SCErrorSet(kSCStatusInvalidArgument
); 
5028         return __SCNetworkReachabilityScheduleWithRunLoop(target
, runLoop
, runLoopMode
, NULL
, FALSE
); 
5032 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef     target
, 
5033                                            CFRunLoopRef                 runLoop
, 
5034                                            CFStringRef                  runLoopMode
) 
5036         if (!isA_SCNetworkReachability(target
) || (runLoop 
== NULL
) || (runLoopMode 
== NULL
)) { 
5037                 _SCErrorSet(kSCStatusInvalidArgument
); 
5041         return __SCNetworkReachabilityUnscheduleFromRunLoop(target
, runLoop
, runLoopMode
, FALSE
); 
5045 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef  target
, 
5046                                       dispatch_queue_t          queue
) 
5050         if (!isA_SCNetworkReachability(target
)) { 
5051                 _SCErrorSet(kSCStatusInvalidArgument
); 
5055         if (queue 
!= NULL
) { 
5056                 ok 
= __SCNetworkReachabilityScheduleWithRunLoop(target
, NULL
, NULL
, queue
, FALSE
); 
5058                 ok 
= __SCNetworkReachabilityUnscheduleFromRunLoop(target
, NULL
, NULL
, FALSE
);