2  * Copyright (c) 2003-2015 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  * April 12, 2011               Allan Nathanson <ajn@apple.com> 
  28  * - add SCNetworkReachability "server" 
  30  * March 31, 2004               Allan Nathanson <ajn@apple.com> 
  31  * - use [SC] DNS configuration information 
  33  * January 19, 2003             Allan Nathanson <ajn@apple.com> 
  34  * - add advanced reachability APIs 
  37 #include <Availability.h> 
  38 #include <TargetConditionals.h> 
  39 #include <sys/cdefs.h> 
  40 #include <dispatch/dispatch.h> 
  41 #include <dispatch/private.h> 
  42 #include <CoreFoundation/CoreFoundation.h> 
  43 #include <CoreFoundation/CFRuntime.h> 
  44 #include <SystemConfiguration/SystemConfiguration.h> 
  45 #include <SystemConfiguration/SCValidation.h> 
  46 #include <SystemConfiguration/SCPrivate.h> 
  47 #include <SystemConfiguration/VPNAppLayerPrivate.h> 
  49 #include <libkern/OSAtomic.h> 
  52 #include <IOKit/pwr_mgt/IOPMLibPrivate.h> 
  53 #endif  // !TARGET_OS_IPHONE 
  57 #include <netinet/in.h> 
  58 #include <arpa/inet.h> 
  62 #include <sys/ioctl.h> 
  63 #include <sys/socket.h> 
  65 #include <net/if_dl.h> 
  66 #include <net/if_types.h> 
  67 #define KERNEL_PRIVATE 
  68 #include <net/route.h> 
  72 #define s6_addr16 __u6_addr.__u6_addr16 
  75 #include "SCNetworkConnectionInternal.h" 
  76 #include "SCNetworkReachabilityInternal.h" 
  78 #include <ppp/ppp_msg.h> 
  79 #include <ppp/PPPControllerPriv.h> 
  81 #include <network_information.h> 
  89 #define DEBUG_REACHABILITY_TYPE_NAME                    "create w/name" 
  90 #define DEBUG_REACHABILITY_TYPE_NAME_CLONE              "      > clone" 
  91 #define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS            "    + options" 
  93 #define DEBUG_REACHABILITY_TYPE_ADDRESS                 "create w/address" 
  94 #define DEBUG_REACHABILITY_TYPE_ADDRESS_CLONE           "         > clone" 
  95 #define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS         "       + options" 
  97 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR             "create w/address pair" 
  98 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_CLONE       "              > clone" 
  99 #define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS     "            + options" 
 101 #define DEBUG_REACHABILITY_TYPE_PTR                     "create w/ptr" 
 102 #define DEBUG_REACHABILITY_TYPE_PTR_CLONE               "     > clone" 
 103 #define DEBUG_REACHABILITY_TYPE_PTR_OPTIONS             "   + options" 
 105 #define DNS_FLAGS_FORMAT        "[%s%s%s%s%s]" 
 106 #define DNS_FLAGS_VALUES(t)     t->dnsHaveV4      ? "4" : "",   \ 
 107                                 t->dnsHaveV6      ? "6" : "",   \ 
 108                                 t->dnsHavePTR     ? "P" : "",   \ 
 109                                 t->dnsHaveTimeout ? "T" : "",   \ 
 110                                 t->dnsHaveError   ? "E" : "" 
 113 static pthread_mutexattr_t      lock_attr
; 
 115 #define MUTEX_INIT(m) {                                                 \ 
 116         int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0);          \ 
 120 #define MUTEX_LOCK(m) {                                                 \ 
 121         int _lock_ = (pthread_mutex_lock(m) == 0);                      \ 
 125 #define MUTEX_UNLOCK(m) {                                               \ 
 126         int _unlock_ = (pthread_mutex_unlock(m) == 0);                  \ 
 130 #define MUTEX_ASSERT_HELD(m) {                                          \ 
 131         int _locked_ = (pthread_mutex_lock(m) == EDEADLK);              \ 
 136 #define SCNETWORKREACHABILITY_TRIGGER_KEY       CFSTR("com.apple.SCNetworkReachability:FORCE-CHANGE") 
 142 static CFStringRef      
__SCNetworkReachabilityCopyDescription  (CFTypeRef cf
); 
 143 static void             __SCNetworkReachabilityDeallocate       (CFTypeRef cf
); 
 144 static void             reachPerform                            (void *info
); 
 145 static Boolean          
reachUpdate                             (SCNetworkReachabilityRef target
); 
 149 __SCNetworkReachabilityHandleChanges            (SCDynamicStoreRef              store
, 
 150                                                  CFArrayRef                     changedKeys
, 
 154 __SCNetworkReachabilityScheduleWithRunLoop      (SCNetworkReachabilityRef       target
, 
 155                                                  CFRunLoopRef                   runLoop
, 
 156                                                  CFStringRef                    runLoopMode
, 
 157                                                  dispatch_queue_t               queue
, 
 161 __SCNetworkReachabilityUnscheduleFromRunLoop    (SCNetworkReachabilityRef       target
, 
 162                                                  CFRunLoopRef                   runLoop
, 
 163                                                  CFStringRef                    runLoopMode
, 
 167 static CFTypeID __kSCNetworkReachabilityTypeID  
= _kCFRuntimeNotATypeID
; 
 170 static const CFRuntimeClass __SCNetworkReachabilityClass 
= { 
 172         "SCNetworkReachability",                // className 
 175         __SCNetworkReachabilityDeallocate
,      // dealloc 
 178         NULL
,                                   // copyFormattingDesc 
 179         __SCNetworkReachabilityCopyDescription  
// copyDebugDesc 
 183 static pthread_once_t           initialized     
= PTHREAD_ONCE_INIT
; 
 184 static const ReachabilityInfo   NOT_REACHABLE   
= { 0, 0,               0, { 0 }, FALSE 
}; 
 185 static const ReachabilityInfo   NOT_REPORTED    
= { 0, 0xFFFFFFFF,      0, { 0 }, FALSE 
}; 
 186 static int                      rtm_seq         
= 0; 
 189 static const struct timeval     TIME_ZERO       
= { 0, 0 }; 
 192 static int                      dnsCount        
= 0; 
 193 static int                      dnsGeneration   
= 0; 
 194 static DNSServiceRef            dnsMain         
= NULL
; 
 195 static CFMutableSetRef          dnsUpdated      
= NULL
; 
 197 static Boolean                  D_serverBypass  
= FALSE
; 
 201 #if     !TARGET_OS_IPHONE 
 203  * Power capabilities (sleep/wake) 
 205 #define POWER_CAPABILITIES_NETWORK      ( kIOPMCapabilityCPU            \ 
 206                                         | kIOPMCapabilityNetwork        \ 
 207                                         | kIOPMCapabilityVideo) 
 208 static IOPMSystemPowerStateCapabilities power_capabilities      
= POWER_CAPABILITIES_NETWORK
; 
 209 #endif  // !TARGET_OS_IPHONE 
 213  * host "something has changed" notifications 
 216 // Note: protected by _hn_target_queue() 
 217 static SCDynamicStoreRef        hn_store        
= NULL
; 
 218 static CFMutableSetRef          hn_targets      
= NULL
; 
 221 static dispatch_queue_t
 
 224         static dispatch_once_t  once
; 
 225         static dispatch_queue_t q 
= NULL
; 
 227         dispatch_once(&once
, ^{ 
 228                 q 
= dispatch_queue_create("SCNetworkReachability.handleChanges", NULL
); 
 235 static dispatch_queue_t
 
 238         static dispatch_once_t  once
; 
 239         static dispatch_queue_t q
; 
 241         dispatch_once(&once
, ^{ 
 242                 q 
= dispatch_queue_create("SCNetworkReachability.targetManagement", NULL
); 
 254         dns_config_t    
*config
; 
 256 } dns_configuration_t
; 
 259 // Note: protected by "dns_lock" 
 260 static pthread_mutex_t          dns_lock                
= PTHREAD_MUTEX_INITIALIZER
; 
 261 static dns_configuration_t      
*dns_configuration      
= NULL
; 
 262 static int                      dns_token
; 
 263 static Boolean                  dns_token_valid         
= FALSE
; 
 271         dns_query_mdns_timeout
, 
 276 __mark_operation_start(struct timeval   
*queryStart
, 
 277                        struct timeval   
*queryEnd
) 
 279         (void) gettimeofday(queryStart
, NULL
); 
 280         *queryEnd 
= TIME_ZERO
; 
 287 __mark_operation_end(SCNetworkReachabilityRef   target
, 
 289                      query_type                 query_type
, 
 290                      struct timeval             
*queryStart
, 
 291                      struct timeval             
*queryEnd
) 
 293         struct timeval                  queryElapsed
; 
 294         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
 296         (void) gettimeofday(queryEnd
, NULL
); 
 299             (query_type 
!= dns_query_mdns_timeout
)) { 
 303         if (!timerisset(queryStart
)) { 
 307         timersub(queryEnd
, queryStart
, &queryElapsed
); 
 308         switch (query_type
) { 
 310                 #define QUERY_TIME__FMT "%ld.%6.6d" 
 311                 #define QUERY_TIME__DIV 1 
 313                 case dns_query_async 
: 
 314                         SCLog(TRUE
, LOG_INFO
, 
 315                               CFSTR("%sasync DNS complete%s (query time = " QUERY_TIME__FMT 
")"), 
 316                               targetPrivate
->log_prefix
, 
 317                               found 
? "" : ", host not found", 
 319                               queryElapsed
.tv_usec 
/ QUERY_TIME__DIV
); 
 321                 case dns_query_mdns 
: 
 322                         SCLog(TRUE
, LOG_INFO
, 
 323                               CFSTR("%s[m]DNS query complete%s (query time = " QUERY_TIME__FMT 
"), " DNS_FLAGS_FORMAT
), 
 324                               targetPrivate
->log_prefix
, 
 325                               found 
? "" : ", host not found", 
 327                               queryElapsed
.tv_usec 
/ QUERY_TIME__DIV
, 
 328                               DNS_FLAGS_VALUES(targetPrivate
)); 
 330                 case dns_query_mdns_timeout 
: 
 332                               CFSTR("%s[m]DNS query timeout (query time = " QUERY_TIME__FMT 
"), " DNS_FLAGS_FORMAT
), 
 333                               targetPrivate
->log_prefix
, 
 335                               queryElapsed
.tv_usec 
/ QUERY_TIME__DIV
, 
 336                               DNS_FLAGS_VALUES(targetPrivate
)); 
 344 static __inline__ Boolean
 
 345 __reach_changed(ReachabilityInfo 
*r1
, ReachabilityInfo 
*r2
) 
 347         if (r1
->flags 
!= r2
->flags
) { 
 348                 // if the reachability flags changed 
 352         if (r1
->if_index 
!= r2
->if_index
) { 
 353                 // if the target interface changed 
 357         if ((r1
->sleeping 
!= r2
->sleeping
) && !r2
->sleeping
) { 
 358                 // if our sleep/wake status changed and if we 
 359                 // are no longer sleeping 
 367 static __inline__ 
void 
 368 _reach_set(ReachabilityInfo             
*dst
, 
 369            const ReachabilityInfo       
*src
, 
 371            unsigned int                 requested_if_index
, 
 372            const char                   *requested_if_name
) 
 374         memcpy(dst
, src
, sizeof(ReachabilityInfo
)); 
 377         if (!(dst
->flags 
& kSCNetworkReachabilityFlagsReachable
) || 
 378                 (dst
->flags 
& kSCNetworkReachabilityFlagsConnectionRequired
)) { 
 379                 // if not reachable or connection required, return the 
 380                 // requested if_index and if_name. 
 381                 dst
->if_index 
= requested_if_index
; 
 382                 if (requested_if_name 
!= NULL
) { 
 383                         strlcpy(dst
->if_name
, requested_if_name
, sizeof(dst
->if_name
)); 
 385                         dst
->if_name
[0] = '\0'; 
 394 #pragma mark SCDynamicStore info 
 398         SCDynamicStoreRef       store
; 
 400         CFDictionaryRef         dict
; 
 403         const void *            keys_q
[N_QUICK
]; 
 404         const void **           values
; 
 405         const void *            values_q
[N_QUICK
]; 
 406 } ReachabilityStoreInfo
, *ReachabilityStoreInfoRef
; 
 409 static ReachabilityStoreInfo    S_storeInfo             
= { 0 }; 
 410 static Boolean                  S_storeInfoActive       
= FALSE
; 
 413 static dispatch_queue_t
 
 416         static dispatch_once_t  once
; 
 417         static dispatch_queue_t q
; 
 419         dispatch_once(&once
, ^{ 
 420                 q 
= dispatch_queue_create("SCNetworkReachability.storeInfo", NULL
); 
 428 ReachabilityStoreInfo_copy(ReachabilityStoreInfoRef     src
, 
 429                            ReachabilityStoreInfoRef     dst
) 
 431         if (src
->dict 
!= NULL
) { 
 432                 dst
->store 
= src
->store
; 
 433                 CFRetain(dst
->store
); 
 435                 dst
->dict 
= src
->dict
; 
 440                         if (dst
->n 
<= (CFIndex
)(sizeof(dst
->keys_q
) / sizeof(CFTypeRef
))) { 
 441                                 dst
->keys   
= dst
->keys_q
; 
 442                                 dst
->values 
= dst
->values_q
; 
 444                                 dst
->keys   
= CFAllocatorAllocate(NULL
, dst
->n 
* sizeof(CFTypeRef
), 0); 
 445                                 dst
->values 
= CFAllocatorAllocate(NULL
, dst
->n 
* sizeof(CFTypeRef
), 0); 
 447                         memcpy(dst
->keys
,   src
->keys
,   dst
->n 
* sizeof(CFTypeRef
)); 
 448                         memcpy(dst
->values
, src
->values
, dst
->n 
* sizeof(CFTypeRef
)); 
 457 ReachabilityStoreInfo_enable(Boolean enable
) 
 459         dispatch_sync(_storeInfo_queue(), ^{ 
 460                 S_storeInfoActive 
= enable
; 
 468 ReachabilityStoreInfo_free(ReachabilityStoreInfoRef store_info
) 
 470         if ((store_info
->n 
> 0) && (store_info
->keys 
!= store_info
->keys_q
)) { 
 471                 CFAllocatorDeallocate(NULL
, store_info
->keys
); 
 472                 store_info
->keys 
= NULL
; 
 474                 CFAllocatorDeallocate(NULL
, store_info
->values
); 
 475                 store_info
->values 
= NULL
; 
 479         if (store_info
->dict 
!= NULL
) { 
 480                 CFRelease(store_info
->dict
); 
 481                 store_info
->dict 
= NULL
; 
 484         if (store_info
->store 
!= NULL
) { 
 485                 CFRelease(store_info
->store
); 
 486                 store_info
->store 
= NULL
; 
 494 ReachabilityStoreInfo_init(ReachabilityStoreInfoRef store_info
) 
 496         dispatch_sync(_storeInfo_queue(), ^{ 
 497                 bzero(store_info
, sizeof(ReachabilityStoreInfo
)); 
 499                 if (S_storeInfoActive 
&& (S_storeInfo
.dict 
!= NULL
)) { 
 500                         ReachabilityStoreInfo_copy(&S_storeInfo
, store_info
); 
 509 ReachabilityStoreInfo_save(ReachabilityStoreInfoRef store_info
) 
 511         dispatch_sync(_storeInfo_queue(), ^{ 
 512                 if ((store_info 
== NULL
) || 
 513                     !_SC_CFEqual(store_info
->dict
, S_storeInfo
.dict
)) { 
 515                         ReachabilityStoreInfo_free(&S_storeInfo
); 
 518                         if (S_storeInfoActive 
&& 
 519                             (store_info 
!= NULL
) && 
 520                             (store_info
->dict 
!= NULL
)) { 
 521                                 ReachabilityStoreInfo_copy(store_info
, &S_storeInfo
); 
 531 ReachabilityStoreInfo_keys(CFMutableArrayRef 
*fill_keys
, CFMutableArrayRef 
*fill_patterns
) 
 534         CFMutableArrayRef       keys
; 
 536         CFMutableArrayRef       patterns
; 
 538         keys 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 539         patterns 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
 541         // get info for IPv4 services 
 542         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
 543                                                          kSCDynamicStoreDomainState
, 
 545         CFArrayAppendValue(keys
, key
); 
 547         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 548                                                               kSCDynamicStoreDomainSetup
, 
 551         CFArrayAppendValue(patterns
, pattern
); 
 553         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 554                                                               kSCDynamicStoreDomainState
, 
 557         CFArrayAppendValue(patterns
, pattern
); 
 560         // get info for IPv6 services 
 561         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
 562                                                          kSCDynamicStoreDomainState
, 
 564         CFArrayAppendValue(keys
, key
); 
 566         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 567                                                               kSCDynamicStoreDomainSetup
, 
 570         CFArrayAppendValue(patterns
, pattern
); 
 572         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 573                                                               kSCDynamicStoreDomainState
, 
 576         CFArrayAppendValue(patterns
, pattern
); 
 579         // get info for PPP services 
 580         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 581                                                               kSCDynamicStoreDomainSetup
, 
 584         CFArrayAppendValue(patterns
, pattern
); 
 586         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 587                                                               kSCDynamicStoreDomainState
, 
 590         CFArrayAppendValue(patterns
, pattern
); 
 593         // get info for VPN services 
 594         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 595                                                               kSCDynamicStoreDomainSetup
, 
 598         CFArrayAppendValue(patterns
, pattern
); 
 600         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 601                                                               kSCDynamicStoreDomainState
, 
 604         CFArrayAppendValue(patterns
, pattern
); 
 607         // get info for IPSec services 
 608 //      pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 
 609 //                                                            kSCDynamicStoreDomainSetup, 
 612 //      CFArrayAppendValue(patterns, pattern); 
 613 //      CFRelease(pattern); 
 614         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 615                                                               kSCDynamicStoreDomainState
, 
 618         CFArrayAppendValue(patterns
, pattern
); 
 621         // get info to identify "available" services 
 622         pattern 
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL
, 
 623                                                               kSCDynamicStoreDomainSetup
, 
 626         CFArrayAppendValue(patterns
, pattern
); 
 631         *fill_patterns 
= patterns
; 
 637 ReachabilityStoreInfo_fill(ReachabilityStoreInfoRef store_info
) 
 639         CFMutableArrayRef       keys
; 
 640         CFMutableArrayRef       patterns
; 
 642         // get the SCDynamicStore info 
 643         ReachabilityStoreInfo_keys(&keys
, &patterns
); 
 644         store_info
->dict 
= SCDynamicStoreCopyMultiple(store_info
->store
, keys
, patterns
); 
 647         if (store_info
->dict 
== NULL
) { 
 651         // and extract the keys/values for post-processing 
 652         store_info
->n 
= CFDictionaryGetCount(store_info
->dict
); 
 653         if (store_info
->n 
> 0) { 
 654                 if (store_info
->n 
<= (CFIndex
)(sizeof(store_info
->keys_q
) / sizeof(CFTypeRef
))) { 
 655                         store_info
->keys   
= store_info
->keys_q
; 
 656                         store_info
->values 
= store_info
->values_q
; 
 658                         store_info
->keys   
= CFAllocatorAllocate(NULL
, store_info
->n 
* sizeof(CFTypeRef
), 0); 
 659                         store_info
->values 
= CFAllocatorAllocate(NULL
, store_info
->n 
* sizeof(CFTypeRef
), 0); 
 661                 CFDictionaryGetKeysAndValues(store_info
->dict
, 
 671 ReachabilityStoreInfo_update(ReachabilityStoreInfoRef   store_info
, 
 672                              SCDynamicStoreRef          
*storeP
, 
 673                              sa_family_t                sa_family
) 
 675         __block Boolean         ok      
= TRUE
; 
 679                         store_info
->entity 
= NULL
; 
 682                         store_info
->entity 
= kSCEntNetIPv4
; 
 685                         store_info
->entity 
= kSCEntNetIPv6
; 
 691         if (store_info
->dict 
!= NULL
) { 
 692                 // if info already available 
 696         dispatch_sync(_storeInfo_queue(), ^{ 
 697                 if (S_storeInfoActive 
&& (S_storeInfo
.dict 
!= NULL
)) { 
 699                         ReachabilityStoreInfo_free(store_info
); 
 701                         // copy the shared/available info 
 702                         ReachabilityStoreInfo_copy(&S_storeInfo
, store_info
); 
 705                 if (store_info
->store 
== NULL
) { 
 706                         store_info
->store 
= (storeP 
!= NULL
) ? *storeP 
: NULL
; 
 707                         if (store_info
->store 
!= NULL
) { 
 708                                 // keep a reference to the passed in SCDynamicStore 
 709                                 CFRetain(store_info
->store
); 
 711                                 store_info
->store 
= SCDynamicStoreCreate(NULL
, CFSTR("SCNetworkReachability"), NULL
, NULL
); 
 712                                 if (store_info
->store 
== NULL
) { 
 713                                         SCLog(TRUE
, LOG_ERR
, CFSTR("ReachabilityStoreInfo_update SCDynamicStoreCreate() failed")); 
 717                                 if (storeP 
!= NULL
) { 
 718                                         // and pass back a reference 
 719                                         *storeP 
= store_info
->store
; 
 725                 if (sa_family 
== AF_UNSPEC
) { 
 726                         // if the address family was not specified than 
 727                         // all we wanted, for now, was to establish the 
 728                         // SCDynamicStore session 
 732                 if (store_info
->dict 
!= NULL
) { 
 733                         // or we have picked up the shared info 
 737                 ok 
= ReachabilityStoreInfo_fill(store_info
); 
 742                 if (!_SC_CFEqual(store_info
->dict
, S_storeInfo
.dict
)) { 
 744                         ReachabilityStoreInfo_free(&S_storeInfo
); 
 747                         if (S_storeInfoActive 
&& 
 748                             (store_info
->dict 
!= NULL
)) { 
 749                                 ReachabilityStoreInfo_copy(store_info
, &S_storeInfo
); 
 759 #pragma mark Reachability engine 
 762 #define ROUNDUP(a, size) \ 
 763         (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) 
 765 #define NEXT_SA(ap) (ap) = (struct sockaddr *) \ 
 766         ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ 
 771 get_rtaddrs(int addrs
, struct sockaddr 
*sa
, struct sockaddr 
**rti_info
) 
 775         for (i 
= 0; i 
< RTAX_MAX
; i
++) { 
 776                 if (addrs 
& (1 << i
)) { 
 785 #define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */ 
 791                 struct rt_msghdr        rtm
; 
 794         struct sockaddr         
*rti_info
[RTAX_MAX
]; 
 795         struct rt_msghdr        
*rtm
; 
 796         struct sockaddr_dl      
*sdl
; 
 797 } route_info
, *route_info_p
; 
 802  *      returns zero if route exists and data returned, EHOSTUNREACH 
 803  *      if no route, or errno for any other error. 
 806 route_get(const struct sockaddr 
*address
, 
 807           unsigned int          if_index
, 
 812         pid_t                   pid             
= getpid(); 
 815         int32_t                 seq             
= OSAtomicIncrement32Barrier(&rtm_seq
); 
 816 #ifndef RTM_GET_SILENT 
 817 #warning Note: Using RTM_GET (and not RTM_GET_SILENT) 
 818         static pthread_mutex_t  lock            
= PTHREAD_MUTEX_INITIALIZER
; 
 819         int                     sosize          
= 48 * 1024; 
 822         bzero(info
, sizeof(*info
)); 
 824         info
->rtm 
= &info
->buf
.rtm
; 
 825         info
->rtm
->rtm_msglen  
= sizeof(struct rt_msghdr
); 
 826         info
->rtm
->rtm_version 
= RTM_VERSION
; 
 827 #ifdef  RTM_GET_SILENT 
 828         info
->rtm
->rtm_type    
= RTM_GET_SILENT
; 
 830         info
->rtm
->rtm_type    
= RTM_GET
; 
 832         info
->rtm
->rtm_flags   
= RTF_STATIC
|RTF_UP
|RTF_HOST
|RTF_GATEWAY
; 
 833         info
->rtm
->rtm_addrs   
= RTA_DST
|RTA_IFP
; /* Both destination and device */ 
 834         info
->rtm
->rtm_pid     
= pid
; 
 835         info
->rtm
->rtm_seq     
= seq
; 
 838                 info
->rtm
->rtm_flags 
|= RTF_IFSCOPE
; 
 839                 info
->rtm
->rtm_index 
= if_index
; 
 842         switch (address
->sa_family
) { 
 844                         struct sockaddr_in6     
*sin6
; 
 846                         /* ALIGN: caller ensures that the address is aligned */ 
 847                         sin6 
= (struct sockaddr_in6 
*)(void *)address
; 
 848                         if ((IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
) || 
 849                              IN6_IS_ADDR_MC_LINKLOCAL(&sin6
->sin6_addr
)) && 
 850                             (sin6
->sin6_scope_id 
!= 0)) { 
 851                                 sin6
->sin6_addr
.s6_addr16
[1] = htons(sin6
->sin6_scope_id
); 
 852                                 sin6
->sin6_scope_id 
= 0; 
 858         sa  
= (struct sockaddr 
*) (info
->rtm 
+ 1); 
 859         bcopy(address
, sa
, address
->sa_len
); 
 860         n 
= ROUNDUP(sa
->sa_len
, sizeof(uint32_t)); 
 861         info
->rtm
->rtm_msglen 
+= n
; 
 863         info
->sdl 
= (struct sockaddr_dl 
*) ((void *)sa 
+ n
); 
 864         info
->sdl
->sdl_family 
= AF_LINK
; 
 865         info
->sdl
->sdl_len 
= sizeof (struct sockaddr_dl
); 
 866         n 
= ROUNDUP(info
->sdl
->sdl_len
, sizeof(uint32_t)); 
 867         info
->rtm
->rtm_msglen 
+= n
; 
 869 #ifndef RTM_GET_SILENT 
 870         pthread_mutex_lock(&lock
); 
 872         rsock 
= socket(PF_ROUTE
, SOCK_RAW
, PF_ROUTE
); 
 876 #ifndef RTM_GET_SILENT 
 877                 pthread_mutex_unlock(&lock
); 
 879                 SCLog(TRUE
, LOG_ERR
, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error
)); 
 883         if (ioctl(rsock
, FIONBIO
, &opt
) < 0) { 
 887 #ifndef RTM_GET_SILENT 
 888                 pthread_mutex_unlock(&lock
); 
 890                 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl(FIONBIO) failed: %s"), strerror(error
)); 
 894 #ifndef RTM_GET_SILENT 
 895         if (setsockopt(rsock
, SOL_SOCKET
, SO_RCVBUF
, &sosize
, sizeof(sosize
)) == -1) { 
 899                 pthread_mutex_unlock(&lock
); 
 900                 SCLog(TRUE
, LOG_ERR
, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error
)); 
 905         if (write(rsock
, &info
->buf
, info
->rtm
->rtm_msglen
) == -1) { 
 909 #ifndef RTM_GET_SILENT 
 910                 pthread_mutex_unlock(&lock
); 
 912                 if (error 
!= ESRCH
) { 
 913                         SCLog(TRUE
, LOG_ERR
, CFSTR("write() failed: %s"), strerror(error
)); 
 920          * Type, seq, pid identify our response. 
 921          * Routing sockets are broadcasters on input. 
 926                 n 
= read(rsock
, &info
->buf
, sizeof(info
->buf
)); 
 930                         if (error 
== EINTR
) { 
 934 #ifndef RTM_GET_SILENT 
 935                         pthread_mutex_unlock(&lock
); 
 938                               CFSTR("SCNetworkReachability: routing socket" 
 939                                     " read() failed: %s"), strerror(error
)); 
 942                 if ((info
->rtm
->rtm_type 
== RTM_GET
)    && 
 943                     (info
->rtm
->rtm_seq 
== seq
)         && 
 944                     (info
->rtm
->rtm_pid 
== pid
)) { 
 950 #ifndef RTM_GET_SILENT 
 951         pthread_mutex_unlock(&lock
); 
 954         get_rtaddrs(info
->rtm
->rtm_addrs
, sa
, info
->rti_info
); 
 956 //#define LOG_RTADDRS 
 961                 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("rtm_flags = 0x%8.8x"), info
->rtm
->rtm_flags
); 
 963                 if ((info
->rti_info
[RTAX_NETMASK
] != NULL
) && (info
->rti_info
[RTAX_DST
] != NULL
)) { 
 964                         info
->rti_info
[RTAX_NETMASK
]->sa_family 
= info
->rti_info
[RTAX_DST
]->sa_family
; 
 967                 for (i 
= 0; i 
< RTAX_MAX
; i
++) { 
 968                         if (info
->rti_info
[i
] != NULL
) { 
 971                                 _SC_sockaddr_to_string(info
->rti_info
[i
], addr
, sizeof(addr
)); 
 972                                 SCLog(_sc_debug
, LOG_DEBUG
, CFSTR("%d: %s"), i
, addr
); 
 976 #endif  // LOG_RTADDRS 
 978         if ((info
->rti_info
[RTAX_IFP
] == NULL
) || 
 979             (info
->rti_info
[RTAX_IFP
]->sa_family 
!= AF_LINK
)) { 
 980                 /* no interface info */ 
 981                 SCLog(TRUE
, LOG_DEBUG
, CFSTR("route_get() no interface info")); 
 985         /* ALIGN: accessors are retrieving byte values, cast ok. */ 
 986         info
->sdl 
= (struct sockaddr_dl 
*)(void *) info
->rti_info
[RTAX_IFP
]; 
 987         if ((info
->sdl
->sdl_nlen 
== 0) || (info
->sdl
->sdl_nlen 
> IFNAMSIZ
)) { 
 988                 /* no interface name */ 
 997 log_address(const char                  *str
, 
 998             const struct sockaddr       
*sa
, 
 999             unsigned int                if_index
, 
1000             const char                  *log_prefix
) 
1003         char    if_name
[IFNAMSIZ 
+ 1]; 
1005         _SC_sockaddr_to_string(sa
, addr
, sizeof(addr
)); 
1007         if ((if_index 
!= 0) && 
1008             (if_indextoname(if_index
, &if_name
[1]) != NULL
)) { 
1014         SCLog(TRUE
, LOG_INFO
, CFSTR("%s%s(%s%s)"), 
1025 checkAddress_route(const struct sockaddr        
*address
, 
1026                    unsigned int                 if_index
, 
1029                    ReachabilityInfo             
*reach_info
, 
1032                    const char                   *log_prefix
) 
1036         char                    *statusMessage  
= NULL
; 
1037         struct sockaddr_in      v4mapped
; 
1039         switch (address
->sa_family
) { 
1043                                 log_address("checkAddress", address
, if_index
, log_prefix
); 
1048                          * if no code for this address family (yet) 
1050                         SCLog(TRUE
, LOG_INFO
, 
1051                               CFSTR("checkAddress(): unexpected address family %d"), 
1052                               address
->sa_family
); 
1053                         *sc_status 
= kSCStatusInvalidArgument
; 
1058         if (address
->sa_family 
== AF_INET6
) { 
1059                 /* ALIGN: sin6_addr accessed aligned, cast ok. */ 
1060                 struct sockaddr_in6     
*sin6   
= (struct sockaddr_in6 
*)(void *)address
; 
1062                 if (IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
)) { 
1063                         bzero(&v4mapped
, sizeof(v4mapped
)); 
1064                         v4mapped
.sin_len         
= sizeof(v4mapped
); 
1065                         v4mapped
.sin_family      
= AF_INET
; 
1066                         v4mapped
.sin_port        
= sin6
->sin6_port
; 
1067                         v4mapped
.sin_addr
.s_addr 
= sin6
->sin6_addr
.__u6_addr
.__u6_addr32
[3]; 
1068                         address 
= (struct sockaddr 
*)&v4mapped
; 
1072         ret 
= route_get(address
, if_index
, info
); 
1085         /* get the interface flags */ 
1087         isock 
= socket(AF_INET
, SOCK_DGRAM
, 0); 
1090                 SCLog(TRUE
, LOG_ERR
, CFSTR("socket() failed: %s"), strerror(errno
)); 
1094         bzero(ifr
, sizeof(*ifr
)); 
1095         bcopy(info
->sdl
->sdl_data
, ifr
->ifr_name
, info
->sdl
->sdl_nlen
); 
1097         if (ioctl(isock
, SIOCGIFFLAGS
, (char *)ifr
) == -1) { 
1099                 SCLog(TRUE
, LOG_ERR
, CFSTR("ioctl(SIOCGIFFLAGS) failed: %s"), strerror(errno
)); 
1103         if (!(ifr
->ifr_flags 
& IFF_UP
)) { 
1108         statusMessage 
= "isReachable"; 
1109         reach_info
->flags 
|= kSCNetworkReachabilityFlagsReachable
; 
1111         if (info
->rtm
->rtm_flags 
& RTF_LOCAL
) { 
1112                 statusMessage 
= "isReachable (is a local address)"; 
1113                 reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1114         } else if (ifr
->ifr_flags 
& IFF_LOOPBACK
) { 
1115                 statusMessage 
= "isReachable (is loopback network)"; 
1116                 reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1117         } else if ((info
->rti_info
[RTAX_IFA
] != NULL
) && 
1118                    (info
->rti_info
[RTAX_IFA
]->sa_family 
!= AF_LINK
)) { 
1119                 void    *addr1  
= (void *)address
; 
1120                 void    *addr2  
= (void *)info
->rti_info
[RTAX_IFA
]; 
1121                 size_t  len     
= address
->sa_len
; 
1123                 if ((address
->sa_family 
!= info
->rti_info
[RTAX_IFA
]->sa_family
) && 
1124                     (address
->sa_len    
!= info
->rti_info
[RTAX_IFA
]->sa_len
)) { 
1125                         SCLog(TRUE
, LOG_NOTICE
, 
1126                               CFSTR("address family/length mismatch: %d/%d != %d/%d"), 
1129                               info
->rti_info
[RTAX_IFA
]->sa_family
, 
1130                               info
->rti_info
[RTAX_IFA
]->sa_len
); 
1134                 switch (address
->sa_family
) { 
1136                                 /* ALIGN: cast ok, because only bcmp is used. */ 
1137                                 addr1 
= &((struct sockaddr_in 
*)(void *)address
)->sin_addr
; 
1138                                 addr2 
= &((struct sockaddr_in 
*)(void *)info
->rti_info
[RTAX_IFA
])->sin_addr
; 
1139                                 len 
= sizeof(struct in_addr
); 
1144                                 /* ALIGN: sin_addr should be aligned, cast ok. */ 
1145                                 if (((struct sockaddr_in 
*)(void *)address
)->sin_addr
.s_addr 
== 0) { 
1146                                         statusMessage 
= "isReachable (this host)"; 
1147                                         reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1151                                 /* ALIGN: cast ok, because only bcmp is used. */ 
1152                                 addr1 
= &((struct sockaddr_in6 
*)(void *)address
)->sin6_addr
; 
1153                                 addr2 
= &((struct sockaddr_in6 
*)(void *)info
->rti_info
[RTAX_IFA
])->sin6_addr
; 
1154                                 len 
= sizeof(struct in6_addr
); 
1160                 if (bcmp(addr1
, addr2
, len
) == 0) { 
1161                         statusMessage 
= "isReachable (is interface address)"; 
1162                         reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsLocalAddress
; 
1166         if (!(info
->rtm
->rtm_flags 
& RTF_GATEWAY
) && 
1167             (info
->rti_info
[RTAX_GATEWAY
] != NULL
) && 
1168             (info
->rti_info
[RTAX_GATEWAY
]->sa_family 
== AF_LINK
) && 
1169             !(ifr
->ifr_flags 
& IFF_POINTOPOINT
)) { 
1170                 reach_info
->flags 
|= kSCNetworkReachabilityFlagsIsDirect
; 
1173         bzero(if_name
, IFNAMSIZ
); 
1174         bcopy(info
->sdl
->sdl_data
, 
1176               (info
->sdl
->sdl_nlen 
<= IFNAMSIZ
) ? info
->sdl
->sdl_nlen 
: IFNAMSIZ
); 
1178         strlcpy(reach_info
->if_name
, if_name
, sizeof(reach_info
->if_name
)); 
1179         reach_info
->if_index 
= info
->sdl
->sdl_index
; 
1182                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  status    = %s"), log_prefix
, statusMessage
); 
1183                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  device    = %s (%hu)"), log_prefix
, if_name
, info
->sdl
->sdl_index
); 
1184                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  sdl_type  = 0x%x"), log_prefix
, info
->sdl
->sdl_type
); 
1185                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  ifr_flags = 0x%04hx"), log_prefix
, ifr
->ifr_flags
); 
1186                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  rtm_flags = 0x%08x"), log_prefix
, info
->rtm
->rtm_flags
); 
1190         if (isock 
!= -1) (void)close(isock
); 
1196 checkAddress(ReachabilityStoreInfoRef   store_info
, 
1197              const struct sockaddr      
*address
, 
1198              unsigned int               if_index
, 
1199              ReachabilityInfo           
*reach_info
, 
1200              const char                 *log_prefix
) 
1204         char                    if_name
[IFNAMSIZ
]; 
1205         nwi_ifstate_t           ifstate
; 
1206         nwi_state_t             nwi_state
; 
1208         int                     sc_status       
= kSCStatusReachabilityUnknown
; 
1210         _reach_set(reach_info
, &NOT_REACHABLE
, reach_info
->cycle
, if_index
, NULL
); 
1212         nwi_state 
= nwi_state_copy(); 
1214         if (address 
!= NULL
) { 
1215                 ret 
= checkAddress_route(address
, 
1224                 /* special case: check only for available paths off the system */ 
1229                 const struct sockaddr   
*vpn_server_address
; 
1231                 sc_status 
= kSCStatusOK
; 
1233                 ifstate 
= nwi_state_get_ifstate(nwi_state
, if_name
); 
1234                 if (ifstate 
== NULL
) { 
1238                 reach_info
->flags 
|= nwi_ifstate_get_reachability_flags(ifstate
); 
1241                 vpn_server_address 
= nwi_ifstate_get_vpn_server(ifstate
); 
1242                 if (vpn_server_address 
!= NULL
) { 
1243                         char            dst_if_name
[IFNAMSIZ
]; 
1244                         route_info      dst_info
; 
1246                         ret 
= route_get(vpn_server_address
, 0, &dst_info
); 
1251                         bzero(&dst_if_name
, sizeof(dst_if_name
)); 
1252                         bcopy(dst_info
.sdl
->sdl_data
, 
1254                               (dst_info
.sdl
->sdl_nlen 
<= IFNAMSIZ
) ? dst_info
.sdl
->sdl_nlen 
: IFNAMSIZ
); 
1255                         if (bcmp(if_name
, dst_if_name
, sizeof(if_name
)) != 0) { 
1256                                 nwi_ifstate_t ifstate
; 
1258                                 ifstate 
= nwi_state_get_ifstate(nwi_state
, dst_if_name
); 
1259                                 if (ifstate 
!= NULL
) { 
1260                                         reach_info
->flags 
|= nwi_ifstate_get_reachability_flags(ifstate
); 
1264         } else if (ret 
== EHOSTUNREACH
) { 
1265                 if (if_index 
== 0) { 
1268                         // if not "scoped" request 
1269                         af 
= (address 
!= NULL
) ? address
->sa_family 
: AF_UNSPEC
; 
1270                         reach_info
->flags 
|= nwi_state_get_reachability_flags(nwi_state
, af
); 
1271                         sc_status 
= kSCStatusOK
; 
1273                         // if "scoped" request 
1274                         sc_status 
= kSCStatusNoKey
; 
1280         if (reach_info
->flags 
== 0) { 
1281                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  cannot be reached"), log_prefix
); 
1284         if (nwi_state 
!= NULL
) { 
1285                 nwi_state_release(nwi_state
); 
1288         if ((sc_status 
!= kSCStatusOK
) && (sc_status 
!= kSCStatusNoKey
)) { 
1289                 _SCErrorSet(sc_status
); 
1298 #pragma mark SCNetworkReachability APIs 
1301 static __inline__ CFTypeRef
 
1302 isA_SCNetworkReachability(CFTypeRef obj
) 
1304         return (isA_CFType(obj
, SCNetworkReachabilityGetTypeID())); 
1309 addr_to_PTR_name(const struct sockaddr 
*sa
, char *name
, size_t name_len
) 
1313         switch (sa
->sa_family
) { 
1319                         /* ALIGN: assuming sa is aligned, then cast ok. */ 
1320                         struct sockaddr_in      
*sin    
= (struct sockaddr_in 
*)(void *)sa
; 
1323                          * build "PTR" query name 
1324                          *   NNN.NNN.NNN.NNN.in-addr.arpa. 
1326                         rev
.s_addr 
= sin
->sin_addr
.s_addr
; 
1327                         n 
= snprintf(name
, name_len
, "%u.%u.%u.%u.in-addr.arpa.", 
1332                         if ((n 
== -1) || (n 
>= name_len
)) { 
1342                         /* ALIGN: assume sa is aligned, cast ok. */ 
1343                         struct sockaddr_in6     
*sin6   
= (struct sockaddr_in6 
*)(void *)sa
; 
1344                         size_t                  x       
= name_len
; 
1347                          * build IPv6 "nibble" PTR query name (RFC 1886, RFC 3152) 
1348                          *   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. 
1350                         for (i 
= sizeof(sin6
->sin6_addr
) - 1; i 
>= 0; i
--) { 
1351                                 n 
= snprintf(&name
[s
], x
, "%x.%x.", 
1352                                              ( sin6
->sin6_addr
.s6_addr
[i
]       & 0xf), 
1353                                              ((sin6
->sin6_addr
.s6_addr
[i
] >> 4) & 0xf)); 
1354                                 if ((n 
== -1) || (n 
>= x
)) { 
1362                         n 
= snprintf(&name
[s
], x
, "ip6.arpa."); 
1363                         if ((n 
== -1) || (n 
>= x
)) { 
1379 _SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target
) 
1381         CFAllocatorRef                  allocator       
= CFGetAllocator(target
); 
1382         CFMutableStringRef              str
; 
1383         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1385         str 
= CFStringCreateMutable(allocator
, 0); 
1386         switch (targetPrivate
->type
) { 
1387                 case reachabilityTypeAddress 
: 
1388                 case reachabilityTypeAddressPair 
: { 
1391                         if (targetPrivate
->localAddress 
!= NULL
) { 
1392                                 _SC_sockaddr_to_string(targetPrivate
->localAddress
, buf
, sizeof(buf
)); 
1393                                 CFStringAppendFormat(str
, NULL
, CFSTR("local address = %s"), 
1397                         if (targetPrivate
->remoteAddress 
!= NULL
) { 
1398                                 _SC_sockaddr_to_string(targetPrivate
->remoteAddress
, buf
, sizeof(buf
)); 
1399                                 CFStringAppendFormat(str
, NULL
, CFSTR("%s%saddress = %s"), 
1400                                                      targetPrivate
->localAddress 
? ", " : "", 
1401                                                      (targetPrivate
->type 
== reachabilityTypeAddressPair
) ? "remote " : "", 
1406                 case reachabilityTypeName 
: { 
1407                         CFStringAppendFormat(str
, NULL
, CFSTR("name = %s"), targetPrivate
->name
); 
1410                 case reachabilityTypePTR 
: { 
1411                         CFStringAppendFormat(str
, NULL
, CFSTR("ptr = %s"), targetPrivate
->name
); 
1421 _SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target
) 
1423         CFAllocatorRef                  allocator       
= CFGetAllocator(target
); 
1425         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1427         str 
= CFStringCreateWithFormat(allocator
, 
1429                                        CFSTR("flags = 0x%08x, if_index = %u%s"), 
1430                                        targetPrivate
->info
.flags
, 
1431                                        targetPrivate
->info
.if_index
, 
1432                                        targetPrivate
->info
.sleeping 
? ", z" : ""); 
1438 __SCNetworkReachabilityCopyDescription(CFTypeRef cf
) 
1440         CFAllocatorRef                  allocator       
= CFGetAllocator(cf
); 
1441         CFMutableStringRef              result
; 
1443         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)cf
; 
1444         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1446         result 
= CFStringCreateMutable(allocator
, 0); 
1447         CFStringAppendFormat(result
, NULL
, CFSTR("<SCNetworkReachability %p [%p]> {"), cf
, allocator
); 
1449         // add target description 
1450         str 
= _SCNetworkReachabilityCopyTargetDescription(target
); 
1451         CFStringAppend(result
, str
); 
1454         // add additional "name" info 
1455         if (isReachabilityTypeName(targetPrivate
->type
)) { 
1456                 if (targetPrivate
->dnsActive
) { 
1457                         CFStringAppendFormat(result
, NULL
, CFSTR(" (DNS query active)")); 
1458                 } else if (targetPrivate
->serverActive 
&& 
1459                            (targetPrivate
->info
.flags 
& kSCNetworkReachabilityFlagsFirstResolvePending
)) { 
1460                         CFStringAppendFormat(result
, NULL
, CFSTR(" (server query active)")); 
1461                 } else if ((targetPrivate
->resolvedAddresses 
!= NULL
) || (targetPrivate
->resolvedError 
!= NETDB_SUCCESS
)) { 
1462                         if (targetPrivate
->resolvedAddresses 
!= NULL
) { 
1463                                 if (isA_CFArray(targetPrivate
->resolvedAddresses
)) { 
1465                                         CFIndex n       
= CFArrayGetCount(targetPrivate
->resolvedAddresses
); 
1467                                         CFStringAppendFormat(result
, NULL
, CFSTR(" (")); 
1468                                         for (i 
= 0; i 
< n
; i
++) { 
1471                                                 CFStringAppendFormat(result
, NULL
, CFSTR("%s"), 
1474                                                 address 
= CFArrayGetValueAtIndex(targetPrivate
->resolvedAddresses
, i
); 
1475                                                 if (isA_CFData(address
)) { 
1477                                                         struct sockaddr 
*sa
; 
1479                                                         sa      
= (struct sockaddr 
*)CFDataGetBytePtr(address
); 
1480                                                         _SC_sockaddr_to_string(sa
, buf
, sizeof(buf
)); 
1481                                                         CFStringAppendFormat(result
, NULL
, CFSTR("%s"), buf
); 
1483                                                         CFStringAppendFormat(result
, NULL
, CFSTR("%@"), address
); 
1486                                         CFStringAppendFormat(result
, NULL
, CFSTR(")")); 
1487                                 } else if (CFEqual(targetPrivate
->resolvedAddresses
, kCFNull
)) { 
1488                                         CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"), 
1489                                                              gai_strerror(targetPrivate
->resolvedError
)); 
1491                                         CFStringAppendFormat(result
, NULL
, CFSTR(" (no addresses)")); 
1494                                 CFStringAppendFormat(result
, NULL
, CFSTR(" (%s)"), 
1495                                                      gai_strerror(targetPrivate
->resolvedError
)); 
1498                 if (targetPrivate
->dnsFlags 
!= 0) { 
1499                         CFStringAppendFormat(result
, NULL
, CFSTR(", " DNS_FLAGS_FORMAT
), 
1500                                              DNS_FLAGS_VALUES(targetPrivate
)); 
1504         if (targetPrivate
->onDemandBypass
) { 
1505                 CFStringAppendFormat(result
, NULL
, CFSTR(", !ondemand")); 
1509         if (targetPrivate
->resolverBypass
) { 
1510                 CFStringAppendFormat(result
, NULL
, CFSTR(", !resolve")); 
1515         if (targetPrivate
->scheduled
) { 
1516                 str 
= _SCNetworkReachabilityCopyTargetFlags(target
); 
1517                 CFStringAppendFormat(result
, NULL
, CFSTR(", %@"), str
); 
1521         CFStringAppendFormat(result
, NULL
, CFSTR("}")); 
1528 __SCNetworkReachabilityDeallocate(CFTypeRef cf
) 
1530         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)cf
; 
1531         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1533         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%srelease"), 
1534               targetPrivate
->log_prefix
); 
1536         /* disconnect from the reachability server */ 
1538         if (targetPrivate
->serverActive
) { 
1539                 __SCNetworkReachabilityServer_targetRemove(target
); 
1542         /* release resources */ 
1544         pthread_mutex_destroy(&targetPrivate
->lock
); 
1546         if (targetPrivate
->name 
!= NULL
) 
1547                 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->name
); 
1549         if (targetPrivate
->resolvedAddresses 
!= NULL
) 
1550                 CFRelease(targetPrivate
->resolvedAddresses
); 
1552         if (targetPrivate
->localAddress 
!= NULL
) { 
1553                 if (targetPrivate
->localAddress 
== targetPrivate
->remoteAddress
) { 
1554                         targetPrivate
->remoteAddress 
= NULL
; 
1556                 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->localAddress
); 
1559         if (targetPrivate
->remoteAddress 
!= NULL
) 
1560                 CFAllocatorDeallocate(NULL
, (void *)targetPrivate
->remoteAddress
); 
1562         if (targetPrivate
->rlsContext
.release 
!= NULL
) { 
1563                 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
); 
1566         if (targetPrivate
->onDemandName 
!= NULL
) { 
1567                 CFRelease(targetPrivate
->onDemandName
); 
1570         if (targetPrivate
->onDemandRemoteAddress 
!= NULL
) { 
1571                 CFRelease(targetPrivate
->onDemandRemoteAddress
); 
1574         if (targetPrivate
->onDemandServer 
!= NULL
) { 
1575                 CFRelease(targetPrivate
->onDemandServer
); 
1578         if (targetPrivate
->onDemandServiceID 
!= NULL
) { 
1579                 CFRelease(targetPrivate
->onDemandServiceID
); 
1582         if (targetPrivate
->serverDigest 
!= NULL
) { 
1583                 CFRelease(targetPrivate
->serverDigest
); 
1586         if (targetPrivate
->serverGroup 
!= NULL
) { 
1587                 dispatch_release(targetPrivate
->serverGroup
); 
1590         if (targetPrivate
->serverQueue 
!= NULL
) { 
1591                 dispatch_release(targetPrivate
->serverQueue
); 
1594         if (targetPrivate
->serverWatchers 
!= NULL
) { 
1595                 CFRelease(targetPrivate
->serverWatchers
); 
1598         if (targetPrivate
->nePolicyResult
) { 
1599                 free(targetPrivate
->nePolicyResult
); 
1607 __SCNetworkReachabilityInitialize(void) 
1609         __kSCNetworkReachabilityTypeID 
= _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass
); 
1611         // provide a way to enable SCNetworkReachability logging without 
1612         // having to set _sc_debug=1. 
1613         if ((getenv("REACH_LOGGING") != NULL
) || 
1614             (CFPreferencesGetAppBooleanValue(CFSTR("com.apple.SCNetworkReachability.debug"), 
1615                                              kCFPreferencesCurrentApplication
, 
1620         // set per-process "bypass" of the SCNetworkReachability server 
1621         if (getenv("REACH_SERVER_BYPASS") != NULL
) { 
1622                 D_serverBypass 
= TRUE
; 
1626         pthread_mutexattr_init(&lock_attr
); 
1627         pthread_mutexattr_settype(&lock_attr
, PTHREAD_MUTEX_ERRORCHECK
); 
1635 __SCNetworkReachability_concurrent_queue() 
1637         static dispatch_once_t  once
; 
1638         static dispatch_queue_t q
; 
1640         dispatch_once(&once
, ^{ 
1641                 q 
= dispatch_queue_create("SCNetworkReachability.concurrent", 
1642                                           DISPATCH_QUEUE_CONCURRENT
); 
1650  * __SCNetworkReachabilityUpdateConcurrent 
1652  * Calls reachUpdate() 
1653  * - caller must be holding a reference to the target 
1654  * - caller must *not* be holding the target lock 
1655  * - caller must be running on the __SCNetworkReachability_concurrent_queue() 
1659 __SCNetworkReachabilityUpdateConcurrent(SCNetworkReachabilityRef target
) 
1663         dispatch_queue_t                queue
; 
1664         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
1666         changed 
= reachUpdate((void *)target
); 
1671         n 
= _SC_ATOMIC_INC(&targetPrivate
->pending
); 
1673                 // if we already have a notification pending 
1677         MUTEX_LOCK(&targetPrivate
->lock
); 
1679         queue 
= targetPrivate
->dispatchQueue
; 
1680         if (queue 
!= NULL
) { 
1681                 dispatch_group_t        group
; 
1683                 dispatch_retain(queue
); 
1685                 group 
= targetPrivate
->dispatchGroup
; 
1686                 dispatch_group_enter(group
); 
1688                 MUTEX_UNLOCK(&targetPrivate
->lock
); 
1690                 dispatch_sync(queue
, ^{ 
1691                         reachPerform((void *)target
); 
1692                         dispatch_group_leave(group
); 
1695                 dispatch_release(queue
); 
1697                 if (targetPrivate
->rls 
!= NULL
) { 
1698                         CFRunLoopSourceSignal(targetPrivate
->rls
); 
1699                         _SC_signalRunLoop(target
, targetPrivate
->rls
, targetPrivate
->rlList
); 
1702                 MUTEX_UNLOCK(&targetPrivate
->lock
); 
1710  * __SCNetworkReachabilityUpdate 
1712  * Calls reachUpdate() [indirectly] 
1713  * - caller can be holding the target lock 
1714  * - caller can be running on any dispatch queue 
1718 __SCNetworkReachabilityUpdate(SCNetworkReachabilityRef target
) 
1721         dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{ 
1722                 __SCNetworkReachabilityUpdateConcurrent(target
); 
1730 static SCNetworkReachabilityPrivateRef
 
1731 __SCNetworkReachabilityCreatePrivate(CFAllocatorRef     allocator
) 
1733         SCNetworkReachabilityPrivateRef         targetPrivate
; 
1736         /* initialize runtime */ 
1737         pthread_once(&initialized
, __SCNetworkReachabilityInitialize
); 
1739         /* allocate target */ 
1740         size          
= sizeof(SCNetworkReachabilityPrivate
) - sizeof(CFRuntimeBase
); 
1741         targetPrivate 
= (SCNetworkReachabilityPrivateRef
)_CFRuntimeCreateInstance(allocator
, 
1742                                                                                   __kSCNetworkReachabilityTypeID
, 
1745         if (targetPrivate 
== NULL
) { 
1749         bzero((void *)targetPrivate 
+ sizeof(CFRuntimeBase
), size
); 
1751         MUTEX_INIT(&targetPrivate
->lock
); 
1753         targetPrivate
->cycle                            
= 1; 
1754         targetPrivate
->last_notify                      
= NOT_REPORTED
; 
1755         targetPrivate
->serverBypass                     
= D_serverBypass
; 
1759         targetPrivate
->log_prefix
[0] = '\0'; 
1761                 snprintf(targetPrivate
->log_prefix
, 
1762                          sizeof(targetPrivate
->log_prefix
), 
1767         return targetPrivate
; 
1773 static const struct sockaddr 
* 
1774 is_valid_address(const struct sockaddr 
*address
) 
1776         const struct sockaddr   
*valid  
= NULL
; 
1777         static Boolean  warned  
= FALSE
; 
1779         if ((address 
!= NULL
) && 
1780             (address
->sa_len 
<= sizeof(struct sockaddr_storage
))) { 
1781                 switch (address
->sa_family
) { 
1783                                 if (address
->sa_len 
>= sizeof(struct sockaddr_in
)) { 
1787                                                 SCLog(TRUE
, LOG_ERR
, 
1788                                                       CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"), 
1790                                                       sizeof(struct sockaddr_in
)); 
1796                                 if (address
->sa_len 
>= sizeof(struct sockaddr_in6
)) { 
1798                                 } else if (!warned
) { 
1799                                         SCLog(TRUE
, LOG_ERR
, 
1800                                               CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"), 
1802                                               sizeof(struct sockaddr_in6
)); 
1808                                         SCLog(TRUE
, LOG_ERR
, 
1809                                               CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"), 
1810                                               address
->sa_family
); 
1824 SCNetworkReachabilityRef
 
1825 SCNetworkReachabilityCreateWithAddress(CFAllocatorRef           allocator
, 
1826                                        const struct sockaddr    
*address
) 
1828         SCNetworkReachabilityPrivateRef targetPrivate
; 
1830         address 
= is_valid_address(address
); 
1831         if (address 
== NULL
) { 
1832                 _SCErrorSet(kSCStatusInvalidArgument
); 
1836         targetPrivate 
= __SCNetworkReachabilityCreatePrivate(allocator
); 
1837         if (targetPrivate 
== NULL
) { 
1841         targetPrivate
->type 
= reachabilityTypeAddress
; 
1842         targetPrivate
->remoteAddress 
= CFAllocatorAllocate(NULL
, address
->sa_len
, 0); 
1843         bcopy(address
, targetPrivate
->remoteAddress
, address
->sa_len
); 
1847         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%s%s %@"), 
1848               targetPrivate
->log_prefix
, 
1849               DEBUG_REACHABILITY_TYPE_ADDRESS
, 
1852         return (SCNetworkReachabilityRef
)targetPrivate
; 
1857 is_same_address(const struct sockaddr 
*a
, const struct sockaddr 
*b
) 
1865             (a
->sa_family 
!= b
->sa_family
) || 
1866             (a
->sa_len    
!= b
->sa_len   
)) { 
1870         switch (a
->sa_family
) { 
1872                         struct sockaddr_in      
*a_sin  
= (struct sockaddr_in 
*)(void *)a
; 
1873                         struct sockaddr_in      
*b_sin  
= (struct sockaddr_in 
*)(void *)b
; 
1875                         /* ALIGN: assuming a (and b) are aligned, then cast ok. */ 
1876                         a_addr 
= &a_sin
->sin_addr
; 
1877                         b_addr 
= &b_sin
->sin_addr
; 
1878                         len 
= sizeof(struct in_addr
); 
1883                         struct sockaddr_in6     
*a_sin6 
= (struct sockaddr_in6 
*)(void *)a
; 
1884                         struct sockaddr_in6     
*b_sin6 
= (struct sockaddr_in6 
*)(void *)b
; 
1886                         if (a_sin6
->sin6_scope_id 
!= b_sin6
->sin6_scope_id
) { 
1890                         a_addr 
= &a_sin6
->sin6_addr
; 
1891                         b_addr 
= &b_sin6
->sin6_addr
; 
1892                         len 
= sizeof(struct in6_addr
); 
1903         return (bcmp(a_addr
, b_addr
, len
) == 0); 
1907 SCNetworkReachabilityRef
 
1908 SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef               allocator
, 
1909                                            const struct sockaddr        
*localAddress
, 
1910                                            const struct sockaddr        
*remoteAddress
) 
1912         SCNetworkReachabilityPrivateRef targetPrivate
; 
1914         if ((localAddress 
== NULL
) && (remoteAddress 
== NULL
)) { 
1915                 _SCErrorSet(kSCStatusInvalidArgument
); 
1919         if (localAddress 
!= NULL
) { 
1920                 localAddress 
= is_valid_address(localAddress
); 
1921                 if (localAddress 
== NULL
) { 
1922                         _SCErrorSet(kSCStatusInvalidArgument
); 
1927         if (remoteAddress 
!= NULL
) { 
1928                 remoteAddress 
= is_valid_address(remoteAddress
); 
1929                 if (remoteAddress 
== NULL
) { 
1930                         _SCErrorSet(kSCStatusInvalidArgument
); 
1935         targetPrivate 
= __SCNetworkReachabilityCreatePrivate(allocator
); 
1936         if (targetPrivate 
== NULL
) { 
1940         targetPrivate
->type 
= reachabilityTypeAddressPair
; 
1942         if (localAddress 
!= NULL
) { 
1943                 targetPrivate
->localAddress 
= CFAllocatorAllocate(NULL
, localAddress
->sa_len
, 0); 
1944                 bcopy(localAddress
, targetPrivate
->localAddress
, localAddress
->sa_len
); 
1947         if (remoteAddress 
!= NULL
) { 
1948                 if (is_same_address(localAddress
, remoteAddress
)) { 
1949                         targetPrivate
->remoteAddress 
= targetPrivate
->localAddress
; 
1951                         targetPrivate
->remoteAddress 
= CFAllocatorAllocate(NULL
, remoteAddress
->sa_len
, 0); 
1952                         bcopy(remoteAddress
, targetPrivate
->remoteAddress
, remoteAddress
->sa_len
); 
1958         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%s%s %@"), 
1959               targetPrivate
->log_prefix
, 
1960               DEBUG_REACHABILITY_TYPE_ADDRESSPAIR
, 
1963         return (SCNetworkReachabilityRef
)targetPrivate
; 
1967 SCNetworkReachabilityRef
 
1968 SCNetworkReachabilityCreateWithName(CFAllocatorRef      allocator
, 
1969                                     const char          *nodename
) 
1973                 struct sockaddr_in      sin
; 
1974                 struct sockaddr_in6     sin6
; 
1977         SCNetworkReachabilityPrivateRef targetPrivate
; 
1979         if (nodename 
== NULL
) { 
1980                 _SCErrorSet(kSCStatusInvalidArgument
); 
1984         nodenameLen 
= strlen(nodename
); 
1985         if (nodenameLen 
== 0) { 
1986                 _SCErrorSet(kSCStatusInvalidArgument
); 
1990         if (nodename
[nodenameLen 
- 1] == '.') { 
1994                 // trim trailing "."s 
1997                 } while ((nodenameLen 
> 0) && (nodename
[nodenameLen 
- 1] == '.')); 
1999                 if (nodenameLen 
== 0) { 
2000                         // if only trailing "."s 
2001                         _SCErrorSet(kSCStatusInvalidArgument
); 
2005                 // count the remaining "."s 
2007                 for (i 
= 0; i 
< nodenameLen
; i
++) { 
2008                         if (nodename
[i
] == '.') dots
++; 
2012                         // if only a single-label, add back the FQDN "." 
2017         if (_SC_string_to_sockaddr(nodename
, AF_UNSPEC
, (void *)&addr
, sizeof(addr
)) != NULL
) { 
2018                 /* if this "nodename" is really an IP[v6] address in disguise */ 
2019                 return SCNetworkReachabilityCreateWithAddress(allocator
, &addr
.sa
); 
2022         targetPrivate 
= __SCNetworkReachabilityCreatePrivate(allocator
); 
2023         if (targetPrivate 
== NULL
) { 
2027         targetPrivate
->type 
= reachabilityTypeName
; 
2029         targetPrivate
->name 
= CFAllocatorAllocate(NULL
, nodenameLen 
+ 1, 0); 
2030         strlcpy((char *)targetPrivate
->name
, nodename
, nodenameLen 
+ 1); 
2032         targetPrivate
->needResolve 
= TRUE
; 
2033         targetPrivate
->info
.flags 
|= kSCNetworkReachabilityFlagsFirstResolvePending
; 
2034         targetPrivate
->serverInfo
.flags 
|= kSCNetworkReachabilityFlagsFirstResolvePending
; 
2037                 /* make sure AppLayerVPN only is in client mode */ 
2038                 CFDictionaryRef appLayerVPNProperties
; 
2040                 appLayerVPNProperties 
= VPNAppLayerCopyCurrentAppProperties(); 
2041                 if (appLayerVPNProperties 
!= NULL
) { 
2042                         targetPrivate
->serverBypassForVPN 
= TRUE
; 
2043                         targetPrivate
->serverBypass 
= YES
; 
2044                         CFRelease(appLayerVPNProperties
); 
2048         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%s%s %@"), 
2049               targetPrivate
->log_prefix
, 
2050               DEBUG_REACHABILITY_TYPE_NAME
, 
2053         return (SCNetworkReachabilityRef
)targetPrivate
; 
2057 static SCNetworkReachabilityRef
 
2058 __SCNetworkReachabilityCreateWithPtr(CFAllocatorRef             allocator
, 
2059                                      const char                 *ptrName
, 
2060                                      const struct sockaddr      
*ptrAddress
) 
2062         SCNetworkReachabilityRef        target
; 
2063         SCNetworkReachabilityPrivateRef targetPrivate
; 
2065         target 
= SCNetworkReachabilityCreateWithName(NULL
, ptrName
); 
2066         if (target 
== NULL
) { 
2070         targetPrivate 
= (SCNetworkReachabilityPrivateRef
)target
; 
2073         targetPrivate
->type 
= reachabilityTypePTR
; 
2075         // and keep the address 
2076         targetPrivate
->remoteAddress 
= CFAllocatorAllocate(NULL
, ptrAddress
->sa_len
, 0); 
2077         bcopy(ptrAddress
, targetPrivate
->remoteAddress
, ptrAddress
->sa_len
); 
2085 SCNetworkReachabilityRef
 
2086 SCNetworkReachabilityCreateWithOptions(CFAllocatorRef   allocator
, 
2087                                        CFDictionaryRef  options
) 
2089         const struct sockaddr           
*addr_l         
= NULL
; 
2090         const struct sockaddr           
*addr_p         
= NULL
; 
2091         const struct sockaddr           
*addr_r         
= NULL
; 
2093         CFStringRef                     interface       
= NULL
; 
2094         CFStringRef                     nodename
; 
2095         CFBooleanRef                    onDemandBypass
; 
2096         CFBooleanRef                    resolverBypass
; 
2097         CFBooleanRef                    serverBypass
; 
2098         SCNetworkReachabilityRef        target
; 
2099         SCNetworkReachabilityPrivateRef targetPrivate
; 
2101         if (!isA_CFDictionary(options
)) { 
2102                 _SCErrorSet(kSCStatusInvalidArgument
); 
2106         nodename 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionNodeName
); 
2107         if ((nodename 
!= NULL
) && 
2108             (!isA_CFString(nodename
) || (CFStringGetLength(nodename
) == 0))) { 
2109                 _SCErrorSet(kSCStatusInvalidArgument
); 
2112         data 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionLocalAddress
); 
2114                 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) { 
2115                         _SCErrorSet(kSCStatusInvalidArgument
); 
2118                 addr_l 
= (const struct sockaddr 
*)CFDataGetBytePtr(data
); 
2120         data 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionPTRAddress
); 
2122                 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) { 
2123                         _SCErrorSet(kSCStatusInvalidArgument
); 
2126                 addr_p 
= (const struct sockaddr 
*)CFDataGetBytePtr(data
); 
2128         data 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionRemoteAddress
); 
2130                 if (!isA_CFData(data
) || (CFDataGetLength(data
) < sizeof(struct sockaddr_in
))) { 
2131                         _SCErrorSet(kSCStatusInvalidArgument
); 
2134                 addr_r 
= (const struct sockaddr 
*)CFDataGetBytePtr(data
); 
2136         interface 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionInterface
); 
2137         if ((interface 
!= NULL
) && 
2138             (!isA_CFString(interface
) || (CFStringGetLength(interface
) == 0))) { 
2139                 _SCErrorSet(kSCStatusInvalidArgument
); 
2142         onDemandBypass 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandBypass
); 
2143         if ((onDemandBypass 
!= NULL
) && !isA_CFBoolean(onDemandBypass
)) { 
2144                 _SCErrorSet(kSCStatusInvalidArgument
); 
2147         resolverBypass 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionResolverBypass
); 
2148         if ((resolverBypass 
!= NULL
) && !isA_CFBoolean(resolverBypass
)) { 
2149                 _SCErrorSet(kSCStatusInvalidArgument
); 
2154         serverBypass 
= CFDictionaryGetValue(options
, kSCNetworkReachabilityOptionServerBypass
); 
2155         if ((serverBypass 
!= NULL
) && !isA_CFBoolean(serverBypass
)) { 
2156                 _SCErrorSet(kSCStatusInvalidArgument
); 
2161         if (nodename 
!= NULL
) { 
2164                 if ((addr_l 
!= NULL
) || (addr_r 
!= NULL
) || (addr_p 
!= NULL
)) { 
2165                         // can't have both a nodename and an address 
2166                         _SCErrorSet(kSCStatusInvalidArgument
); 
2170                 name 
= _SC_cfstring_to_cstring(nodename
, NULL
, 0, kCFStringEncodingUTF8
); 
2171                 target 
= SCNetworkReachabilityCreateWithName(allocator
, name
); 
2172                 CFAllocatorDeallocate(NULL
, (void *)name
); 
2173         } else if (addr_p 
!= NULL
) { 
2174                 char    name
[MAXHOSTNAMELEN
]; 
2176                 if ((addr_l 
!= NULL
) ||                                 // can't have PTR and target address 
2177                     (addr_r 
!= NULL
) ||                                 // can't have PTR and target address 
2178                     !addr_to_PTR_name(addr_p
, name
, sizeof(name
))) {    // can't convert PTR 
2179                         _SCErrorSet(kSCStatusInvalidArgument
); 
2183                 target 
= __SCNetworkReachabilityCreateWithPtr(NULL
, name
, addr_p
); 
2185                 if ((addr_l 
!= NULL
) && (addr_r 
!= NULL
)) { 
2186                         target 
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, addr_r
); 
2187                 } else if (addr_r 
!= NULL
) { 
2188                         target 
= SCNetworkReachabilityCreateWithAddress(NULL
, addr_r
); 
2189                 } else if (addr_l 
!= NULL
) { 
2190                         target 
= SCNetworkReachabilityCreateWithAddressPair(NULL
, addr_l
, NULL
); 
2192                         _SCErrorSet(kSCStatusInvalidArgument
); 
2196         if (target 
== NULL
) { 
2200         targetPrivate 
= (SCNetworkReachabilityPrivateRef
)target
; 
2202         if (interface 
!= NULL
) { 
2203                 if ((_SC_cfstring_to_cstring(interface
, 
2204                                              targetPrivate
->if_name
, 
2205                                              sizeof(targetPrivate
->if_name
), 
2206                                              kCFStringEncodingASCII
) == NULL
) || 
2207                     ((targetPrivate
->if_index 
= if_nametoindex(targetPrivate
->if_name
)) == 0)) { 
2208                         CFRelease(targetPrivate
); 
2209                         _SCErrorSet(kSCStatusInvalidArgument
); 
2215         if (onDemandBypass 
!= NULL
) { 
2216                 targetPrivate
->onDemandBypass 
= CFBooleanGetValue(onDemandBypass
); 
2219         if (resolverBypass 
!= NULL
) { 
2220                 targetPrivate
->resolverBypass 
= CFBooleanGetValue(resolverBypass
); 
2223         /* if by name, make sure client-only VPN types stay in client mode */ 
2224         if (serverBypass 
!= NULL 
&& targetPrivate
->serverBypassForVPN 
== FALSE
) { 
2225                 targetPrivate
->serverBypass 
= CFBooleanGetValue(serverBypass
); 
2229         if (_sc_debug 
&& (_sc_log 
> 0)) { 
2230                 const char      *opt    
= "???"; 
2232                 switch (targetPrivate
->type
) { 
2233                         case reachabilityTypeAddress 
: 
2234                                 opt 
= DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS
; 
2236                         case reachabilityTypeAddressPair 
: 
2237                                 opt 
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS
; 
2239                         case reachabilityTypeName 
: 
2240                                 opt 
= DEBUG_REACHABILITY_TYPE_NAME_OPTIONS
; 
2242                         case reachabilityTypePTR 
: 
2243                                 opt 
= DEBUG_REACHABILITY_TYPE_PTR_OPTIONS
; 
2247                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s%s %@"), 
2248                       targetPrivate
->log_prefix
, 
2253         return (SCNetworkReachabilityRef
)targetPrivate
; 
2257 static SCNetworkReachabilityRef
 
2258 __SCNetworkReachabilityCreateCopy(SCNetworkReachabilityRef target
) 
2260         SCNetworkReachabilityRef        clone           
= NULL
; 
2261         SCNetworkReachabilityPrivateRef clonePrivate
; 
2262         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2264         switch (targetPrivate
->type
) { 
2265                 case reachabilityTypeAddress 
: 
2266                         clone 
= SCNetworkReachabilityCreateWithAddress(NULL
, 
2267                                                                        targetPrivate
->remoteAddress
); 
2269                 case reachabilityTypeAddressPair 
: 
2270                         clone 
= SCNetworkReachabilityCreateWithAddressPair(NULL
, 
2271                                                                            targetPrivate
->localAddress
, 
2272                                                                            targetPrivate
->remoteAddress
); 
2274                 case reachabilityTypeName 
: 
2275                         clone 
= SCNetworkReachabilityCreateWithName(NULL
, 
2276                                                                     targetPrivate
->name
); 
2278                 case reachabilityTypePTR 
: 
2279                         clone 
= __SCNetworkReachabilityCreateWithPtr(NULL
, 
2280                                                                      targetPrivate
->name
, 
2281                                                                      targetPrivate
->remoteAddress
); 
2284         if (clone 
== NULL
) { 
2288         clonePrivate 
= (SCNetworkReachabilityPrivateRef
)clone
; 
2290         clonePrivate
->quiet 
= TRUE
; 
2292         clonePrivate
->if_index 
= targetPrivate
->if_index
; 
2293         bcopy(targetPrivate
->if_name
, clonePrivate
->if_name
, sizeof(clonePrivate
->if_name
)); 
2295         clonePrivate
->onDemandBypass 
= targetPrivate
->onDemandBypass
; 
2298         clonePrivate
->serverBypass 
= targetPrivate
->serverBypass
; 
2300         clonePrivate
->resolverBypass 
= targetPrivate
->resolverBypass
; 
2303         if (_sc_debug 
&& (_sc_log 
> 0)) { 
2304                 const char      *opt    
= "???"; 
2306                 switch (clonePrivate
->type
) { 
2307                         case reachabilityTypeAddress 
: 
2308                                 opt 
= DEBUG_REACHABILITY_TYPE_ADDRESS_CLONE
; 
2310                         case reachabilityTypeAddressPair 
: 
2311                                 opt 
= DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_CLONE
; 
2313                         case reachabilityTypeName 
: 
2314                                 opt 
= DEBUG_REACHABILITY_TYPE_NAME_CLONE
; 
2316                         case reachabilityTypePTR 
: 
2317                                 opt 
= DEBUG_REACHABILITY_TYPE_PTR_CLONE
; 
2321                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s%s %p %@"), 
2322                       clonePrivate
->log_prefix
, 
2333 SCNetworkReachabilityGetTypeID(void) 
2335         pthread_once(&initialized
, __SCNetworkReachabilityInitialize
);  /* initialize runtime */ 
2336         return __kSCNetworkReachabilityTypeID
; 
2340 CFArrayRef      
/* CFArray[CFData], where each CFData is a (struct sockaddr *) */ 
2341 SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef       target
, 
2344         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2346         if (!isA_SCNetworkReachability(target
)) { 
2347                 _SCErrorSet(kSCStatusInvalidArgument
); 
2351         if (!isReachabilityTypeName(targetPrivate
->type
)) { 
2352                 _SCErrorSet(kSCStatusInvalidArgument
); 
2357                 *error_num 
= targetPrivate
->resolvedError
; 
2360         if (targetPrivate
->resolvedAddresses 
!= NULL
) { 
2361                 if (isA_CFArray(targetPrivate
->resolvedAddresses
)) { 
2362                         return CFRetain(targetPrivate
->resolvedAddresses
); 
2364                         /* if status is known but no resolved addresses to return */ 
2365                         _SCErrorSet(kSCStatusOK
); 
2370         _SCErrorSet(kSCStatusReachabilityUnknown
); 
2376 __SCNetworkReachabilitySetResolvedError(SCNetworkReachabilityRef        target
, 
2379         SCNetworkReachabilityPrivateRef         targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
2381         MUTEX_ASSERT_HELD(&targetPrivate
->lock
); 
2383         __mark_operation_end(target
, 
2384                              FALSE
,                             // if successful query 
2385                              dns_query_async
,                   // async 
2386                              &targetPrivate
->dnsQueryStart
,     // start time 
2387                              &targetPrivate
->dnsQueryEnd
);      // end time 
2389         if (targetPrivate
->resolvedAddresses 
!= NULL
) { 
2390                 CFRelease(targetPrivate
->resolvedAddresses
); 
2391                 targetPrivate
->resolvedAddresses 
= NULL
; 
2394         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%scould not be resolved: %s"), 
2395               targetPrivate
->log_prefix
, 
2396               gai_strerror(status
)); 
2398         /* save the error associated with the attempt to resolve the name */ 
2399         targetPrivate
->resolvedAddresses 
= CFRetain(kCFNull
); 
2400         targetPrivate
->resolvedError     
= status
; 
2401         targetPrivate
->needResolve       
= FALSE
; 
2408  * rankReachability() 
2409  *   Not reachable       == 0 
2410  *   Connection Required == 1 
2414 rankReachability(SCNetworkReachabilityFlags flags
) 
2418         if (flags 
& kSCNetworkReachabilityFlagsReachable
)               rank 
= 2; 
2419         if (flags 
& kSCNetworkReachabilityFlagsConnectionRequired
)      rank 
= 1; 
2425 #pragma mark DNS name resolution 
2429 update_resolver_reachability(ReachabilityStoreInfoRef   store_info
, 
2430                              dns_resolver_t             
*resolver
, 
2431                              SCNetworkReachabilityFlags 
*flags
, 
2433                              uint32_t                   *resolver_if_index
, 
2434                              const char                 *log_prefix
) 
2436         if (resolver_if_index
) *resolver_if_index 
= 0; 
2438         if (resolver
->n_nameserver 
> 0) { 
2439                 *flags   
= (SCNetworkReachabilityFlags
)resolver
->reach_flags
; 
2440                 if (resolver_if_index 
!= NULL
) { 
2441                         *resolver_if_index 
= resolver
->if_index
; 
2445                 *flags   
= kSCNetworkReachabilityFlagsReachable
; 
2454 check_matching_resolvers(ReachabilityStoreInfoRef       store_info
, 
2455                          dns_config_t                   
*dns_config
, 
2457                          unsigned int                   if_index
, 
2458                          SCNetworkReachabilityFlags     
*flags
, 
2460                          uint32_t                       *resolver_if_index
, 
2461                          int                            *dns_config_index
, 
2462                          const char                     *log_prefix
) 
2465         Boolean         matched         
= FALSE
; 
2466         const char      *name           
= fqdn
; 
2467         int32_t         n_resolvers
; 
2468         dns_resolver_t  
**resolvers
; 
2470         if (if_index 
== 0) { 
2471                 n_resolvers 
= dns_config
->n_resolver
; 
2472                 resolvers   
= dns_config
->resolver
; 
2474                 n_resolvers 
= dns_config
->n_scoped_resolver
; 
2475                 resolvers   
= dns_config
->scoped_resolver
; 
2478         /* In case we couldn't find a match, setting an index of -1 
2479            and resolver_if_index 0 */ 
2480         if (dns_config_index 
!= NULL
) *dns_config_index 
= -1; 
2481         if (resolver_if_index 
!= NULL
) *resolver_if_index 
= 0; 
2483         while (!matched 
&& (name 
!= NULL
)) { 
2487                  * check if the provided name (or sub-component) 
2488                  * matches one of our resolver configurations. 
2491                 for (i 
= 0; i 
< n_resolvers
; i
++) { 
2493                         dns_resolver_t  
*resolver
; 
2495                         resolver 
= resolvers
[i
]; 
2496                         if ((if_index 
!= 0) && (if_index 
!= resolver
->if_index
)) { 
2500                         domain   
= resolver
->domain
; 
2501                         if (domain 
!= NULL 
&& (len 
== strlen(domain
))) { 
2502                                 if (strcasecmp(name
, domain
) == 0) { 
2504                                          * if name matches domain 
2507                                         update_resolver_reachability(store_info
, 
2513                                         if (dns_config_index 
!= NULL
) *dns_config_index 
= i
; 
2521                          * we have not found a matching resolver, try 
2522                          * a less qualified domain 
2524                         name 
= strchr(name
, '.'); 
2525                         if ((name 
!= NULL
) && (*name 
!= '\0')) { 
2537 static dns_resolver_t 
* 
2538 get_default_resolver(dns_config_t 
*dns_config
, unsigned int if_index
) 
2541         int32_t         n_resolvers
; 
2542         dns_resolver_t  
*resolver       
= NULL
; 
2543         dns_resolver_t  
**resolvers
; 
2545         if (if_index 
== 0) { 
2546                 n_resolvers 
= dns_config
->n_resolver
; 
2547                 resolvers   
= dns_config
->resolver
; 
2549                 n_resolvers 
= dns_config
->n_scoped_resolver
; 
2550                 resolvers   
= dns_config
->scoped_resolver
; 
2553         for (i 
= 0; i 
< n_resolvers
; i
++) { 
2554                 if ((if_index 
!= 0) && (if_index 
!= resolvers
[i
]->if_index
)) { 
2558                 if (((if_index 
== 0) && (i 
== 0)) || 
2559                     ((if_index 
!= 0) && (resolver 
== NULL
))) { 
2560                         // if this is the first (aka default) resolver 
2561                         resolver 
= resolvers
[i
]; 
2562                 } else if ((resolvers
[i
]->domain 
== NULL
) && 
2563                            (resolvers
[i
]->search_order 
< resolver
->search_order
)) { 
2564                         // if this is a default resolver with a lower search order 
2565                         resolver 
= resolvers
[i
]; 
2573 static dns_configuration_t 
* 
2574 dns_configuration_retain() 
2576         dns_configuration_t     
*config
; 
2578         pthread_mutex_lock(&dns_lock
); 
2580         if (dns_configuration 
!= NULL
) { 
2581                 Boolean         refresh 
= TRUE
; 
2583                 if (dns_token_valid
) { 
2588                          * check if the global [DNS] configuration snapshot needs 
2591                         status 
= notify_check(dns_token
, &check
); 
2592                         if (status 
!= NOTIFY_STATUS_OK
) { 
2593                                 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%u"), status
); 
2594                         } else if (check 
== 0) { 
2595                                 // if the snapshot does not need to be refreshed 
2601                         if (dns_configuration
->refs 
== 0) { 
2602                                 dns_configuration_free(dns_configuration
->config
); 
2603                                 CFAllocatorDeallocate(NULL
, dns_configuration
); 
2605                         dns_configuration 
= NULL
; 
2609         if (dns_configuration 
== NULL
) { 
2610                 dns_config_t    
*new_config
; 
2612                 new_config 
= dns_configuration_copy(); 
2613                 if (new_config 
!= NULL
) { 
2614                         dns_configuration 
= CFAllocatorAllocate(NULL
, sizeof(dns_configuration_t
), 0); 
2615                         dns_configuration
->config 
= new_config
; 
2616                         dns_configuration
->refs   
= 0; 
2620         if (dns_configuration 
!= NULL
) { 
2621                 dns_configuration
->refs
++; 
2624         config 
= dns_configuration
; 
2625         pthread_mutex_unlock(&dns_lock
); 
2631 dns_configuration_release(dns_configuration_t 
*config
) 
2633         pthread_mutex_lock(&dns_lock
); 
2636         if (config
->refs 
== 0) { 
2637                 if (!dns_token_valid 
&& (config 
== dns_configuration
)) { 
2638                         dns_configuration 
= NULL
; 
2641                 if (config 
!= dns_configuration
) { 
2642                         dns_configuration_free(config
->config
); 
2643                         CFAllocatorDeallocate(NULL
, config
); 
2647         pthread_mutex_unlock(&dns_lock
); 
2653 dns_configuration_watch() 
2656         const char      *dns_key
; 
2660         pthread_mutex_lock(&dns_lock
); 
2662         dns_key 
= dns_configuration_notify_key(); 
2663         if (dns_key 
== NULL
) { 
2664                 SCLog(TRUE
, LOG_INFO
, CFSTR("dns_configuration_notify_key() failed")); 
2668         status 
= notify_register_check(dns_key
, &dns_token
); 
2669         if (status 
== NOTIFY_STATUS_OK
) { 
2670                 dns_token_valid 
= TRUE
; 
2672                 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_check() failed, status=%u"), status
); 
2676         status 
= notify_check(dns_token
, &dns_check
); 
2677         if (status 
!= NOTIFY_STATUS_OK
) { 
2678                 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_check() failed, status=%u"), status
); 
2679                 (void)notify_cancel(dns_token
); 
2680                 dns_token_valid 
= FALSE
; 
2688         pthread_mutex_unlock(&dns_lock
); 
2694 dns_configuration_unwatch() 
2696         pthread_mutex_lock(&dns_lock
); 
2698         (void)notify_cancel(dns_token
); 
2699         dns_token_valid 
= FALSE
; 
2701         if ((dns_configuration 
!= NULL
) && (dns_configuration
->refs 
== 0)) { 
2702                 dns_configuration_free(dns_configuration
->config
); 
2703                 CFAllocatorDeallocate(NULL
, dns_configuration
); 
2704                 dns_configuration 
= NULL
; 
2707         pthread_mutex_unlock(&dns_lock
); 
2713 _SC_R_updateResolverReachability(ReachabilityStoreInfoRef       store_info
, 
2714                                  SCNetworkReachabilityFlags     
*flags
, 
2716                                  const char                     *nodename
, 
2717                                  unsigned int                   if_index
, 
2718                                  uint32_t                       *resolver_if_index
, 
2719                                  int                            *dns_config_index
, 
2720                                  const char                     *log_prefix
 
2723         dns_resolver_t          
*default_resolver
; 
2724         dns_configuration_t     
*dns
; 
2725         Boolean                 found                   
= FALSE
; 
2726         char                    *fqdn                   
= (char *)nodename
; 
2728         Boolean                 isFQDN                  
= FALSE
; 
2730         const int               ndots                   
= 1; 
2731         Boolean                 useDefault              
= FALSE
; 
2733         if (resolver_if_index
) *resolver_if_index 
= 0; 
2734         if (dns_config_index
) *dns_config_index 
= -1; 
2737          * We first assume that all of the configured DNS servers 
2738          * are available.  Since we don't know which name server will 
2739          * be consulted to resolve the specified nodename we need to 
2740          * check the availability of ALL name servers.  We can only 
2741          * proceed if we know that our query can be answered. 
2744         *flags   
= kSCNetworkReachabilityFlagsReachable
; 
2747         len 
= (nodename 
!= NULL
) ? strlen(nodename
) : 0; 
2749                 // if no nodename, return not reachable 
2754         dns 
= dns_configuration_retain(); 
2757                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no configuration"), log_prefix
); 
2761         default_resolver 
= get_default_resolver(dns
->config
, if_index
); 
2762         if (default_resolver 
== NULL
) { 
2763                 // if no resolver configuration 
2764                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS: no resolvers"), log_prefix
); 
2768         if (fqdn
[len 
- 1] == '.') { 
2771                 // trim trailing '.''s 
2772                 while ((len 
> 0) && (fqdn
[len
-1] == '.')) { 
2773                         if (fqdn 
== nodename
) { 
2774                                 fqdn 
= strdup(nodename
); 
2775                                 assert(fqdn 
!= nodename
); 
2782          * check if the provided name matches a supplemental domain 
2784         found 
= check_matching_resolvers(store_info
, dns
->config
, fqdn
, if_index
, 
2785                                          flags
, haveDNS
, resolver_if_index
, 
2786                                          dns_config_index
, log_prefix
); 
2788         if (!found 
&& !isFQDN
) { 
2790                  * if we did not match a supplemental domain name and if the 
2791                  * provided name has enough "."s then the first query will be 
2792                  * directed to the default resolver. 
2798                 for (cp 
= fqdn
; *cp 
!= '\0'; cp
++) { 
2799                         if (*cp 
== '.') dots
++; 
2802                 /* Per KB: HT4845 */ 
2803                 if (dots 
>= ndots
) { 
2808         if (!found 
&& !isFQDN 
&& !useDefault 
&& (dns
->config
->n_resolver 
> 1)) { 
2810                  * FQDN not specified, try matching w/search domains 
2812                 if (default_resolver
->n_search 
> 0) { 
2813                         for (i 
= 0; !found 
&& (i 
< default_resolver
->n_search
); i
++) { 
2815                                 char    *search_fqdn    
= NULL
; 
2817                                 ret 
= asprintf(&search_fqdn
, "%s.%s", fqdn
, default_resolver
->search
[i
]); 
2822                                 // try the provided name with the search domain appended 
2823                                 found 
= check_matching_resolvers(store_info
, 
2834                 } else if (default_resolver
->domain 
!= NULL
) { 
2836                         int     domain_parts    
= 0; 
2838                         // count domain parts 
2839                         for (dp 
= default_resolver
->domain
; *dp 
!= '\0'; dp
++) { 
2845                         // remove trailing dots 
2846                         for (dp
--; (dp 
>= default_resolver
->domain
) && (*dp 
== '.'); dp
--) { 
2851                         if (dp 
>= default_resolver
->domain
) { 
2852                                 // dots are separators, bump # of components 
2856                         dp 
= default_resolver
->domain
; 
2857                         for (i 
= LOCALDOMAINPARTS
; !found 
&& (i 
<= (domain_parts 
- ndots
)); i
++) { 
2859                                 char    *search_fqdn    
= NULL
; 
2861                                 ret 
= asprintf(&search_fqdn
, "%s.%s", fqdn
, dp
); 
2866                                 // try the provided name with the [default] domain appended 
2867                                 found 
= check_matching_resolvers(store_info
, 
2878                                 // move to the next component of the [default] domain 
2879                                 dp 
= strchr(dp
, '.') + 1; 
2885                 // update the reachability of the default resolver 
2886                 update_resolver_reachability(store_info
, 
2892                 if (dns_config_index 
!= NULL
) *dns_config_index 
= 0; 
2897         if (fqdn 
!= nodename
)   free(fqdn
); 
2900                 dns_configuration_release(dns
); 
2908 __SC_checkResolverReachabilityInternal(SCDynamicStoreRef                
*storeP
, 
2909                                        SCNetworkReachabilityFlags       
*flags
, 
2911                                        const char                       *nodename
, 
2912                                        uint32_t                         *resolver_if_index
, 
2913                                        int                              *dns_config_index
) 
2916         ReachabilityStoreInfo   store_info
; 
2918         ReachabilityStoreInfo_init(&store_info
); 
2919         ok 
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
); 
2924         _SC_R_updateResolverReachability(&store_info
, 
2935         ReachabilityStoreInfo_free(&store_info
); 
2941  * _SC_checkResolverReachabilityByAddress() 
2943  * Given an IP address, determine whether a reverse DNS query can be issued 
2944  * using the current network configuration. 
2947 _SC_checkResolverReachabilityByAddress(SCDynamicStoreRef                
*storeP
, 
2948                                        SCNetworkReachabilityFlags       
*flags
, 
2950                                        struct sockaddr                  
*sa
) 
2954         ReachabilityStoreInfo   store_info
; 
2956         ReachabilityStoreInfo_init(&store_info
); 
2957         ok 
= ReachabilityStoreInfo_update(&store_info
, storeP
, AF_UNSPEC
); 
2963          * Ideally, we would have an API that given a local IP 
2964          * address would return the DNS server(s) that would field 
2965          * a given PTR query.  Fortunately, we do have an SPI which 
2966          * which will provide this information given a "name" so we 
2967          * take the address, convert it into the inverse query name, 
2968          * and find out which servers should be consulted. 
2970         ok 
= addr_to_PTR_name(sa
, ptr_name
, sizeof(ptr_name
)); 
2975         _SC_R_updateResolverReachability(&store_info
, flags
, haveDNS
, ptr_name
, 0, NULL
, NULL
, ""); 
2979         ReachabilityStoreInfo_free(&store_info
); 
2985 #pragma mark DNSServiceGetAddrInfo support 
2989  * DNS query handling 
2993  * 1. We have a "contract" with mDNSResponder that for EVERY network 
2994  *    or DNS configuration change that should warrant our [re-]starting 
2995  *    a query, mDNSResponder will acknowledge the latest DNS configuration. 
2997  * 2. IPMonitor also posts a notification AFTER every network or DNS 
2998  *    configuration change. 
3000  * 3. We use IPMonitor's "trailing edge" as a signal to restart any 
3005 // Note: protected by _hn_target_queue() 
3006 static int                      dns_refresh_token
; 
3007 static Boolean                  dns_refresh_token_valid 
= FALSE
; 
3011  * dns_refresh_handler 
3013  * Called to notify/update all SCNetworkReachability by-name targets of 
3014  * a network/DNS change.  The change should [re-]start a DNS query to 
3016  * - should be exec'd on the _hn_target_queue() 
3019 dns_refresh_handler() 
3023         __block SCDynamicStoreRef       store   
= NULL
; 
3025         dispatch_sync(_hn_target_queue(), ^{ 
3026                 if (dns_refresh_token_valid 
&& (hn_store 
!= NULL
)) { 
3027                         store 
= CFRetain(hn_store
); 
3031         if (store 
== NULL
) { 
3035         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
3036                                                          kSCDynamicStoreDomainState
, 
3038         changes 
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
); 
3039         __SCNetworkReachabilityHandleChanges(store
, changes
, NULL
); 
3049  * dns_refresh_enable 
3051  * Called to monitor for network/DNS changes that should restart a DNS query. 
3052  * - caller must be running on the _hn_target_queue() 
3055 dns_refresh_enable(dispatch_queue_t q
) 
3059         status 
= notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE
, 
3063                                                   dns_refresh_handler(); 
3065         if (status 
!= NOTIFY_STATUS_OK
) { 
3066                 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_dispatch() failed, status=%u"), status
); 
3070         dns_refresh_token_valid 
= TRUE
; 
3077  * dns_refresh_disable 
3079  * Called to stop monitoring for network/DNS changes 
3080  * - caller must be running on the _hn_target_queue() 
3083 dns_refresh_disable() 
3085         (void)notify_cancel(dns_refresh_token
); 
3086         dns_refresh_token_valid 
= FALSE
; 
3092 #pragma mark [m]DNS Queries 
3096 dequeueDNSQuery(SCNetworkReachabilityRef target
); 
3099 static dispatch_queue_t
 
3102         static dispatch_once_t  once
; 
3103         static dispatch_queue_t q
; 
3105         dispatch_once(&once
, ^{ 
3106                 q 
= dispatch_queue_create("SCNetworkReachability.DNSService", NULL
); 
3116 static __inline__ Boolean
 
3117 _dns_complete(SCNetworkReachabilityRef target
) 
3119         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3121         if ((targetPrivate
->dnsHaveV4 
&& targetPrivate
->dnsHaveV6
) || 
3122             targetPrivate
->dnsHavePTR 
|| 
3123             targetPrivate
->dnsHaveError 
|| 
3124             targetPrivate
->dnsHaveTimeout
) { 
3135  * Called to push out a target's DNS changes 
3136  * - caller must be running on the _dns_queue() 
3139 _dns_notify(const void *value
, void *context
) 
3141         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)value
; 
3142         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3144         MUTEX_LOCK(&targetPrivate
->lock
); 
3146         if (_dns_complete(target
)) { 
3147                 __mark_operation_end(target
, 
3148                                      (targetPrivate
->resolvedError 
== NETDB_SUCCESS
),   // if successful query 
3149                                      dns_query_mdns
,                                    // [m]DNS query 
3150                                      &targetPrivate
->dnsQueryStart
,                     // start time 
3151                                      &targetPrivate
->dnsQueryEnd
);                      // end time 
3153                 // update target info 
3154                 if (targetPrivate
->resolvedAddresses 
!= NULL
) { 
3155                         CFRelease(targetPrivate
->resolvedAddresses
); 
3157                 targetPrivate
->resolvedAddresses 
= targetPrivate
->dnsAddresses
; 
3158                 targetPrivate
->dnsAddresses      
= NULL
; 
3160                 targetPrivate
->resolvedError     
= targetPrivate
->dnsError
; 
3161                 targetPrivate
->dnsError          
= NETDB_SUCCESS
; 
3163                 dequeueDNSQuery(target
); 
3165                 targetPrivate
->needResolve 
= FALSE
; 
3167                 if (targetPrivate
->scheduled
) { 
3168                         __SCNetworkReachabilityUpdate(target
); 
3172         MUTEX_UNLOCK(&targetPrivate
->lock
); 
3190 static __inline__ 
void 
3191 _dns_mark(SCNetworkReachabilityRef target
, _dns_mark_t mark
) 
3193         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3199                         targetPrivate
->dnsHaveError 
= TRUE
; 
3202                         targetPrivate
->dnsHaveTimeout 
= TRUE
; 
3205                         targetPrivate
->dnsHaveV4 
= TRUE
; 
3208                         targetPrivate
->dnsHaveV6 
= TRUE
; 
3210                 case MARK_HAVE_PTR 
: 
3211                         targetPrivate
->dnsHavePTR 
= TRUE
; 
3222  * Called to process [m]DNS query updates 
3223  * - caller must be running on the _dns_queue() 
3226 _dns_callback(DNSServiceRef             sdRef
, 
3227               DNSServiceFlags           flags
, 
3228               DNSServiceErrorType       errorCode
, 
3229               _dns_mark_t               dnsMark
, 
3230               CFTypeRef                 dnsAddress
,     // CFData(struct sockaddr) or CFString(ptr_name) 
3234         Boolean                         restart         
= FALSE
; 
3235         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)context
; 
3236         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3238         MUTEX_LOCK(&targetPrivate
->lock
); 
3240         if (sdRef 
!= targetPrivate
->dnsTarget
) { 
3241                 // if this DNSServiceRef is no longer associated with the target 
3242                 MUTEX_UNLOCK(&targetPrivate
->lock
); 
3246         switch (errorCode
) { 
3247                 case kDNSServiceErr_NoError 
: 
3248                         if (dnsAddress 
!= NULL
) { 
3249                                 CFMutableArrayRef       addresses
; 
3252                                 _dns_mark(target
, dnsMark
); 
3254                                 if (targetPrivate
->dnsAddresses 
!= NULL
) { 
3255                                         if (isA_CFArray(targetPrivate
->dnsAddresses
)) { 
3256                                                 addresses 
= CFArrayCreateMutableCopy(NULL
, 0, targetPrivate
->dnsAddresses
); 
3258                                                 addresses 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
3261                                         CFRelease(targetPrivate
->dnsAddresses
); 
3262                                         targetPrivate
->dnsAddresses 
= NULL
; 
3264                                         addresses 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
3267                                 i 
= CFArrayGetFirstIndexOfValue(addresses
, 
3268                                                                 CFRangeMake(0, CFArrayGetCount(addresses
)), 
3270                                 if (flags 
& kDNSServiceFlagsAdd
) { 
3272                                         if (i 
== kCFNotFound
) { 
3273                                                 CFArrayAppendValue(addresses
, dnsAddress
); 
3275 #ifdef  HANDLE_RMV_REQUESTS 
3278                                         if (i 
!= kCFNotFound
) { 
3279                                                 CFArrayRemoveValueAtIndex(addresses
, i
); 
3281 #endif  // HANDLE_RMV_REQUESTS 
3284                                 if (CFArrayGetCount(addresses
) > 0) { 
3285                                         targetPrivate
->dnsAddresses 
= addresses
; 
3286                                         targetPrivate
->dnsError     
= NETDB_SUCCESS
; 
3288                                         // if host not found 
3289                                         targetPrivate
->dnsAddresses 
= CFRetain(kCFNull
); 
3290                                         targetPrivate
->dnsError     
= EAI_NONAME
; 
3291                                         CFRelease(addresses
); 
3296                 case kDNSServiceErr_BadParam 
: 
3297                         _dns_mark(target
, MARK_ERROR
); 
3299                         if (targetPrivate
->dnsAddresses 
!= NULL
) { 
3300                                 CFRelease(targetPrivate
->dnsAddresses
); 
3302                         targetPrivate
->dnsAddresses 
= CFRetain(kCFNull
); 
3303                         targetPrivate
->dnsError     
= EAI_NONAME
; 
3305                 case kDNSServiceErr_NoSuchRecord 
: 
3306                         _dns_mark(target
, dnsMark
); 
3308                         if (targetPrivate
->dnsAddresses 
== NULL
) { 
3309                                 targetPrivate
->dnsAddresses 
= CFRetain(kCFNull
); 
3310                                 targetPrivate
->dnsError     
= EAI_NONAME
; 
3313                 case kDNSServiceErr_Timeout 
: 
3314                         _dns_mark(target
, MARK_TIMEOUT
); 
3316                         if (targetPrivate
->dnsAddresses 
== NULL
) { 
3317                                 targetPrivate
->dnsAddresses 
= CFRetain(kCFNull
); 
3318                                 targetPrivate
->dnsError     
= EAI_NONAME
; 
3322                         SCLog(TRUE
, LOG_ERR
, 
3323                               CFSTR("%sSCNetworkReachability _dns_callback w/error=%d (n=%d)"), 
3324                               targetPrivate
->log_prefix
, 
3326                               targetPrivate
->dnsFailures 
+ 1); 
3328                 case kDNSServiceErr_ServiceNotRunning 
: 
3329                         _dns_mark(target
, MARK_ERROR
); 
3331                         // bump per-target failure count 
3332                         failures 
= ++targetPrivate
->dnsFailures
; 
3334                         // Check to see if we've seen too many failures for this target 
3336                                 // if so, there's little point in retrying over 
3337                                 // and over again so let's just return an error 
3339                                 if (targetPrivate
->dnsAddresses 
!= NULL
) { 
3340                                         CFRelease(targetPrivate
->dnsAddresses
); 
3342                                 targetPrivate
->dnsAddresses 
= CFRetain(kCFNull
); 
3343                                 targetPrivate
->dnsError     
= EAI_NONAME
; 
3344                         } else if (targetPrivate
->dnsGeneration 
== dnsGeneration
) { 
3345                                 // if not, then "mDNSResponder" crashed or some 
3346                                 // other/unexpected error occurred.  In this 
3347                                 // case, we'll try again with a clean slate and 
3348                                 // restart all requests. 
3349                                 if (dnsMain 
!= NULL
) { 
3350                                         DNSServiceRefDeallocate(dnsMain
); 
3360         // update DNS failure count (and [re-]set to zero if we're OK) 
3361         targetPrivate
->dnsFailures 
= failures
; 
3363         MUTEX_UNLOCK(&targetPrivate
->lock
); 
3366                 SCLog(TRUE
, LOG_DEBUG
, 
3367                       CFSTR("%sreconnecting SCNetworkReachability w/\"mDNSResponder\" (%d)"), 
3368                       targetPrivate
->log_prefix
, 
3371                 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 500 * NSEC_PER_MSEC
), 
3372                                _hn_changes_queue(), 
3374                         dns_refresh_handler(); 
3377                 // and flush the dnsUpdated queue as any DNS results we may have 
3378                 // accumulated are no longer valid. 
3379                 if (dnsUpdated 
!= NULL
) { 
3380                         CFRelease(dnsUpdated
); 
3386         if (targetPrivate
->dnsHaveTimeout
) { 
3387             targetPrivate
->dnsNoAddressesSinceLastTimeout 
= TRUE
; 
3388         } else if (targetPrivate
->dnsNoAddressesSinceLastTimeout 
&& 
3389                    isA_CFArray(targetPrivate
->dnsAddresses
) && 
3390                    CFArrayGetCount(targetPrivate
->dnsAddresses
) > 0) 
3392             targetPrivate
->dnsNoAddressesSinceLastTimeout 
= FALSE
; 
3395         // the "more coming" flag applies to DNSService callouts for any/all 
3396         // hosts that are being watched so we need to keep track of the targets 
3397         // we have updated.  When we [finally] have the last callout then we 
3398         // push our notifications for all of the updated targets. 
3400         if (dnsUpdated 
== NULL
) { 
3401                 dnsUpdated 
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
); 
3403         CFSetAddValue(dnsUpdated
, target
); 
3405         if (!(flags 
& kDNSServiceFlagsMoreComing
)) { 
3406                 CFSetApplyFunction(dnsUpdated
, _dns_notify
, NULL
); 
3407                 CFRelease(dnsUpdated
); 
3416  * _dns_getaddrinfo_callback 
3418  * Called to process [m]DNS query updates 
3419  * - caller must be running on the _dns_queue() 
3422 _dns_getaddrinfo_callback(DNSServiceRef                 sdRef
, 
3423                           DNSServiceFlags               flags
, 
3424                           uint32_t                      interfaceIndex
, 
3425                           DNSServiceErrorType           errorCode
, 
3426                           const char                    *hostname
, 
3427                           const struct sockaddr         
*address
, 
3431         CFDataRef       dnsAddress      
= NULL
; 
3432         _dns_mark_t     dnsMark         
= MARK_NONE
; 
3434         if (address 
!= NULL
) { 
3435                 switch (errorCode
) { 
3436                         case kDNSServiceErr_NoError 
: 
3437                                 dnsAddress 
= CFDataCreate(NULL
, (void *)address
, address
->sa_len
); 
3438                                 // ... and fall through 
3439                         case kDNSServiceErr_NoSuchRecord 
: 
3440                                 switch (address
->sa_family
) { 
3442                                                 dnsMark 
= MARK_HAVE_V4
; 
3445                                                 dnsMark 
= MARK_HAVE_V6
; 
3454         _dns_callback(sdRef
, flags
, errorCode
, dnsMark
, dnsAddress
, context
); 
3456         if (dnsAddress 
!= NULL
) { 
3457                 CFRelease(dnsAddress
); 
3465 _dns_copy_domain_name(const uint8_t *rdata
, uint16_t rdlen
) 
3467         CFMutableStringRef      domain
; 
3468         const uint8_t           *label
; 
3471         domain 
= CFStringCreateMutable(NULL
, 0); 
3474         label_len 
= *(label
++); 
3475         while (label_len 
!= 0) { 
3476                 while (label_len
-- > 0) { 
3477                         uint8_t         byte    
= *label
++; 
3479                         if ((byte 
== '.') || (byte 
== '\\')) { 
3481                                 CFStringAppendFormat(domain
, NULL
, CFSTR("\\%c"), byte
); 
3482                         } else if (byte 
<= ' ') { 
3483                                 CFStringAppendFormat(domain
, NULL
, CFSTR("\\%c%c%c"), 
3485                                                      '0' + ((byte 
/ 10) % 10), 
3488                                 CFStringAppendFormat(domain
, NULL
, CFSTR("%c"), byte
); 
3492                 label_len 
= *(label
++); 
3493                 if (label_len 
!= 0) { 
3494                         CFStringAppendFormat(domain
, NULL
, CFSTR(".")); 
3503 _dns_queryrecord_callback(DNSServiceRef                 sdRef
, 
3504                           DNSServiceFlags               flags
, 
3505                           uint32_t                      interfaceIndex
, 
3506                           DNSServiceErrorType           errorCode
, 
3507                           const char                    *fullname
, 
3515         _dns_mark_t                     dnsMark         
= MARK_NONE
; 
3516         CFStringRef                     dnsPTRName      
= NULL
; 
3517         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)context
; 
3518         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3520         // for now, we only support using DNSServiceQueryRecord for PTR queries 
3521         assert(targetPrivate
->type 
== reachabilityTypePTR
); 
3523         if (rdata 
!= NULL
) { 
3524                 switch (errorCode
) { 
3525                         case kDNSServiceErr_NoError 
: 
3526                                 if (rrtype 
== kDNSServiceType_PTR
) { 
3527                                         dnsPTRName 
= _dns_copy_domain_name(rdata
, rdlen
); 
3529                                 // ... and fall through 
3530                         case kDNSServiceErr_NoSuchRecord 
: 
3531                                 dnsMark 
= MARK_HAVE_PTR
; 
3538         _dns_callback(sdRef
, flags
, errorCode
, dnsMark
, dnsPTRName
, context
); 
3540         if (dnsPTRName 
!= NULL
) { 
3541                 CFRelease(dnsPTRName
); 
3549 enqueueDNSQuery(SCNetworkReachabilityRef target
) 
3551         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3553         MUTEX_ASSERT_HELD(&targetPrivate
->lock
); 
3555         // clear DNS flags, mark the query active 
3556         targetPrivate
->dnsFlags 
= 0; 
3557         targetPrivate
->dnsActive 
= TRUE
; 
3559         // track the DNS resolution time 
3560         __mark_operation_start(&targetPrivate
->dnsQueryStart
, &targetPrivate
->dnsQueryEnd
); 
3563         dispatch_async(_dns_queue(), ^{ 
3564                 DNSServiceErrorType     err
; 
3565                 const char              *fcn    
= "???"; 
3566                 DNSServiceRef           sdRef   
= NULL
; 
3568                 if (targetPrivate
->dnsTarget 
!= NULL
) { 
3569                         // if already running 
3574                 // if needed, start interacting with "mDNSResponder" 
3575                 if (dnsMain 
== NULL
) { 
3576                         err 
= DNSServiceCreateConnection(&dnsMain
); 
3577                         if (err 
!= kDNSServiceErr_NoError
) { 
3578                                 SCLog(TRUE
, LOG_ERR
, 
3579                                       CFSTR("%sDNSServiceCreateConnection(&dnsMain) failed, error = %d"), 
3580                                       targetPrivate
->log_prefix
, 
3585                         err 
= DNSServiceSetDispatchQueue(dnsMain
, _dns_queue()); 
3586                         if (err 
!= kDNSServiceErr_NoError
) { 
3587                                 SCLog(TRUE
, LOG_ERR
, 
3588                                       CFSTR("%sDNSServiceSetDispatchQueue() failed, error = %d"), 
3589                                       targetPrivate
->log_prefix
, 
3591                                 DNSServiceRefDeallocate(dnsMain
); 
3598                 // start a query for this target 
3601                 switch (targetPrivate
->type
) { 
3602                         case reachabilityTypeName 
: 
3603                                 fcn 
= "DNSServiceGetAddrInfo"; 
3604                                 err 
= DNSServiceGetAddrInfo(&sdRef
,                             // sdRef 
3605                                                             kDNSServiceFlagsReturnIntermediates 
// flags 
3606                                                             | kDNSServiceFlagsShareConnection
 
3607                                                             | kDNSServiceFlagsSuppressUnusable
 
3608                                                             | kDNSServiceFlagsTimeout
, 
3609                                                             targetPrivate
->if_index
,            // interfaceIndex 
3611                                                             targetPrivate
->name
,                // hostname 
3612                                                             _dns_getaddrinfo_callback
,          // callback 
3613                                                             (void *)target
);                    // context 
3615                         case reachabilityTypePTR 
: 
3616                                 fcn 
= "DNSServiceQueryRecord"; 
3617                                 err 
= DNSServiceQueryRecord(&sdRef
,                             // sdRef 
3618                                                             kDNSServiceFlagsReturnIntermediates 
// flags 
3619                                                             | kDNSServiceFlagsShareConnection
 
3620                                                             | kDNSServiceFlagsSuppressUnusable
 
3621                                                             | kDNSServiceFlagsTimeout
, 
3622                                                             targetPrivate
->if_index
,            // interfaceIndex 
3623                                                             targetPrivate
->name
,                // fullname 
3624                                                             kDNSServiceType_PTR
,                // rrtype 
3625                                                             kDNSServiceClass_IN
,                // rrclass 
3626                                                             _dns_queryrecord_callback
,          // callback 
3627                                                             (void *)target
);                    // context 
3630                                 err 
= kDNSServiceErr_Unknown
; 
3635                         case kDNSServiceErr_NoError 
: 
3640                                 SCLog(TRUE
, LOG_ERR
, 
3641                                       CFSTR("%s%s() failed, error = %d (%d)"), 
3642                                       targetPrivate
->log_prefix
, 
3648                         case kDNSServiceErr_BadParam 
: 
3649                                 if (dnsCount 
== 0) { 
3650                                         // if this was the first request 
3651                                         DNSServiceRefDeallocate(dnsMain
); 
3661                 MUTEX_LOCK(&targetPrivate
->lock
); 
3663                 if (err 
== kDNSServiceErr_NoError
) { 
3664                         targetPrivate
->dnsGeneration 
= dnsGeneration
; 
3665                         targetPrivate
->dnsTarget 
= sdRef
; 
3667                         targetPrivate
->dnsActive 
= FALSE
; 
3669                         // queue up the returned error 
3670                         dispatch_async(_dns_queue(), ^{ 
3671                                 _dns_callback(NULL
,                     // sdRef 
3674                                               MARK_ERROR
,               // dnsMark 
3676                                               (void *)target
);          // context 
3681                 MUTEX_UNLOCK(&targetPrivate
->lock
); 
3691 dequeueDNSQuery(SCNetworkReachabilityRef target
) 
3693         DNSServiceRef                   sdRef
; 
3694         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3696         MUTEX_ASSERT_HELD(&targetPrivate
->lock
); 
3698         // terminate the [target] [m]DNS query 
3699         sdRef 
= targetPrivate
->dnsTarget
; 
3700         targetPrivate
->dnsTarget 
= NULL
; 
3702         // mark the query NOT active 
3703         targetPrivate
->dnsActive 
= FALSE
; 
3705         // don't do anything if the sdRef is not valid 
3706         if (sdRef 
!= NULL
) { 
3709                 generation 
= targetPrivate
->dnsGeneration
; 
3710                 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 3LL * NSEC_PER_SEC
), 
3713                         if (generation 
== dnsGeneration
) { 
3714                                 // if we're pointing to the same DNSService 
3715                                 // generation as the main/active session 
3716                                 // deallocate per-target query 
3717                                 DNSServiceRefDeallocate(sdRef
); 
3719                                 if (dnsCount 
== 0) { 
3720                                         // if no more queries active 
3721                                         DNSServiceRefDeallocate(dnsMain
); 
3731         if (targetPrivate
->dnsAddresses 
!= NULL
) { 
3732                 CFRelease(targetPrivate
->dnsAddresses
); 
3733                 targetPrivate
->dnsAddresses 
= NULL
; 
3735         targetPrivate
->dnsError 
= NETDB_SUCCESS
; 
3742 #pragma mark Synchronous DNS query support 
3745 #define SYNC_DNS_QUERY_TIMEOUT_NSEC     35 * NSEC_PER_SEC       // 35s 
3749 sync_DNS_query_callback(SCNetworkReachabilityRef        clone
, 
3750                         SCNetworkReachabilityFlags      cloneFlags
, 
3753         dispatch_semaphore_t            s       
= (dispatch_semaphore_t
)info
; 
3755         dispatch_semaphore_signal(s
); 
3761 sync_DNS_query(SCNetworkReachabilityRef target
) 
3763         SCNetworkReachabilityRef        clone
; 
3764         SCNetworkReachabilityPrivateRef clonePrivate
; 
3765         SCNetworkReachabilityContext    context 
= { 0, NULL
, NULL
, NULL
, NULL 
}; 
3768         dispatch_semaphore_t            s
; 
3769         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
3771         clone 
= __SCNetworkReachabilityCreateCopy(target
); 
3772         if (clone 
== NULL
) { 
3775         clonePrivate 
= (SCNetworkReachabilityPrivateRef
)clone
; 
3777         q 
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0); 
3778         s 
= dispatch_semaphore_create(0); 
3780         // start async query 
3781         context
.info 
= (void *)s
; 
3782         SCNetworkReachabilitySetCallback(clone
, sync_DNS_query_callback
, &context
); 
3783         SCNetworkReachabilitySetDispatchQueue(clone
, q
); 
3785         // wait for reply (or timeout) 
3786         ret 
= dispatch_semaphore_wait(s
, dispatch_time(DISPATCH_TIME_NOW
, 
3787                                                        SYNC_DNS_QUERY_TIMEOUT_NSEC
)); 
3789                 dispatch_sync(_dns_queue(), ^{ 
3790                         // mark as both a timeout *and* an error 
3791                         _dns_mark(clone
, MARK_TIMEOUT
); 
3792                         _dns_mark(clone
, MARK_ERROR
); 
3794                         __mark_operation_end(clone
, 
3795                                              FALSE
,                             // if successful query 
3796                                              dns_query_mdns_timeout
,            // [m]DNS query 
3797                                              &clonePrivate
->dnsQueryStart
,      // start time 
3798                                              &clonePrivate
->dnsQueryEnd
);       // end time 
3800                         MUTEX_LOCK(&clonePrivate
->lock
); 
3802                         // update target info with what's available 
3803                         if (clonePrivate
->resolvedAddresses 
!= NULL
) { 
3804                                 CFRelease(clonePrivate
->resolvedAddresses
); 
3805                                 clonePrivate
->resolvedAddresses 
= NULL
; 
3807                         if ((clonePrivate
->dnsAddresses 
!= NULL
) && 
3808                             isA_CFArray(clonePrivate
->dnsAddresses
) && 
3809                             (CFArrayGetCount(clonePrivate
->dnsAddresses
) > 0)) { 
3810                                 clonePrivate
->resolvedAddresses 
= CFArrayCreateMutableCopy(NULL
, 
3812                                                                                            clonePrivate
->dnsAddresses
); 
3814                         if (clonePrivate
->resolvedAddresses 
!= NULL
) { 
3815                                 // if timeout w/partial results 
3816                                 clonePrivate
->resolvedError     
= NETDB_SUCCESS
; 
3818                                 // if timeout w/no results 
3819                                 clonePrivate
->resolvedAddresses 
= CFRetain(kCFNull
); 
3820                                 clonePrivate
->resolvedError     
= EAI_NONAME
; 
3823                         MUTEX_UNLOCK(&clonePrivate
->lock
); 
3828         SCNetworkReachabilitySetDispatchQueue(clone
, NULL
); 
3829         SCNetworkReachabilitySetCallback(clone
, NULL
, NULL
); 
3832         if (clonePrivate
->resolvedAddresses  
!= NULL
) CFRetain(clonePrivate
->resolvedAddresses
); 
3833         if (targetPrivate
->resolvedAddresses 
!= NULL
) CFRelease(targetPrivate
->resolvedAddresses
); 
3834         targetPrivate
->resolvedAddresses 
= clonePrivate
->resolvedAddresses
; 
3835         targetPrivate
->resolvedError     
= clonePrivate
->resolvedError
; 
3836         targetPrivate
->resolverFlags     
= clonePrivate
->resolverFlags
; 
3837         targetPrivate
->cycle             
= clonePrivate
->cycle
; 
3838         targetPrivate
->dnsFlags          
= clonePrivate
->dnsFlags
; 
3839         memcpy(&targetPrivate
->info
, &clonePrivate
->info
, sizeof(ReachabilityInfo
)); 
3840         memcpy(&targetPrivate
->last_notify
, &clonePrivate
->last_notify
, sizeof(ReachabilityInfo
)); 
3843         dispatch_release(s
); 
3850 #pragma mark Network Information support 
3853 // Note: protected by _hn_target_queue() 
3854 static int                      network_changed_token
; 
3855 static Boolean                  network_changed_token_valid     
= FALSE
; 
3859  * nwi_refresh_handler 
3861  * Called to notify/update network changed events 
3862  * - should be exec'd on the _hn_changes_queue() 
3865 nwi_refresh_handler() 
3869         __block SCDynamicStoreRef       store   
= NULL
; 
3871         dispatch_sync(_hn_target_queue(), ^{ 
3872                 if (network_changed_token_valid 
&& (hn_store 
!= NULL
)) { 
3873                         store 
= CFRetain(hn_store
); 
3877         if (store 
== NULL
) { 
3881         // Fake a network change. 
3882         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
3883                                                          kSCDynamicStoreDomainState
, 
3885         changes 
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
); 
3886         __SCNetworkReachabilityHandleChanges(store
, changes
, NULL
); 
3896  * nwi_refresh_enable 
3898  * Called to monitor for network changes. 
3899  * - caller must be running on the _hn_target_queue() 
3900  * - passed in queue should be _hn_changes_queue() 
3903 nwi_refresh_enable(dispatch_queue_t q
) 
3907         status 
= notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE_NWI
,        // trailing nwi_state_get_notify_key() 
3908                                           &network_changed_token
, 
3911                                                   nwi_refresh_handler(); 
3913         if (status 
!= NOTIFY_STATUS_OK
) { 
3914                 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_dispatch() failed for network changes, status=%u"), status
); 
3918         network_changed_token_valid 
= TRUE
; 
3925  * nwi_refresh_disable 
3927  * Called to stop monitoring for network changes 
3928  * - caller must be running on the _hn_target_queue() 
3931 nwi_refresh_disable() 
3933         if (network_changed_token_valid
) { 
3934                 (void)notify_cancel(network_changed_token
); 
3935                 network_changed_token_valid 
= FALSE
; 
3943 #pragma mark Sleep/wake support 
3946 #if     !TARGET_OS_IPHONE 
3948 // Note: protected by _hn_target_queue() 
3949 static IOPMConnection           power_changed_connection        
= NULL
; 
3950 static const CFStringRef        power_changed_key               
= CFSTR("*** EARLY WAKE ***"); 
3954  * power_refresh_handler 
3956  * Called to notify/update power capability changed events 
3957  * - should be exec'd on the _hn_changes_queue() 
3960 power_refresh_handler(void                              *param
, 
3961                       IOPMConnection                    connection
, 
3962                       IOPMConnectionMessageToken        token
, 
3963                       IOPMSystemPowerStateCapabilities  capabilities
) 
3967         __block SCDynamicStoreRef       store   
= NULL
; 
3969         dispatch_sync(_hn_target_queue(), ^{ 
3970                 if ((power_changed_connection 
!= NULL
) && (hn_store 
!= NULL
)) { 
3971                         store 
= CFRetain(hn_store
); 
3975         if (store 
== NULL
) { 
3979         // check for [relevant] changes 
3980         change 
= ((power_capabilities 
^ capabilities
) & POWER_CAPABILITIES_NETWORK
) != 0; 
3982         // update capabilities 
3983         power_capabilities 
= capabilities
; 
3988                 // fake a network change. 
3989                 changes 
= CFArrayCreate(NULL
, (const void **)&power_changed_key
, 1, &kCFTypeArrayCallBacks
); 
3990                 __SCNetworkReachabilityHandleChanges(store
, changes
, NULL
); 
3994         ret 
= IOPMConnectionAcknowledgeEvent(connection
, token
); 
3995         if (ret 
!= kIOReturnSuccess
) { 
3996                 SCLog(TRUE
, LOG_ERR
, CFSTR("IOPMConnectionAcknowledgeEvent failed, 0x%08x"), ret
); 
4005  * power_refresh_enable 
4007  * Called to monitor power changes. 
4008  * - caller must be running on the _hn_target_queue() 
4009  * - passed in queue should be _hn_changes_queue() 
4012 power_refresh_enable(dispatch_queue_t q
) 
4014         IOPMConnection  connection      
= NULL
; 
4017         ret 
= IOPMConnectionCreate(CFSTR("com.apple.SCNetworkReachability"), 
4018                                    kIOPMEarlyWakeNotification 
| kIOPMSleepWakeInterest
, 
4020         if (ret 
!= kIOReturnSuccess
) { 
4021                 SCLog(TRUE
, LOG_ERR
, CFSTR("IOPMConnectionCreate failed, 0x%08x"), ret
); 
4025         ret 
= IOPMConnectionSetNotification(connection
, NULL
, power_refresh_handler
); 
4026         if (ret 
!= kIOReturnSuccess
) { 
4027                 SCLog(TRUE
, LOG_ERR
, CFSTR("IOPMConnectionSetNotification failed, 0x%08x"), ret
); 
4031         power_changed_connection 
= connection
; 
4032         IOPMConnectionSetDispatchQueue(connection
, q
); 
4033         power_capabilities 
= IOPMConnectionGetSystemCapabilities(); 
4039         if (connection 
!= NULL
) { 
4040                 IOPMConnectionRelease(connection
); 
4048 power_refresh_disable() 
4050         if (power_changed_connection 
!= NULL
) { 
4051                 IOPMConnectionSetDispatchQueue(power_changed_connection
, NULL
); 
4052                 IOPMConnectionRelease(power_changed_connection
); 
4053                 power_changed_connection 
= NULL
; 
4059 #endif  // !TARGET_OS_IPHONE 
4067 #pragma mark OnDemand 
4071 SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef       target
, 
4072                                          CFDictionaryRef                
*userOptions
) 
4074         SCNetworkServiceRef             service         
= NULL
; 
4075         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
4077         if (!isA_SCNetworkReachability(target
)) { 
4078                 _SCErrorSet(kSCStatusInvalidArgument
); 
4082         if (targetPrivate
->onDemandServiceID 
!= NULL
) { 
4083                 service 
= _SCNetworkServiceCopyActive(NULL
, targetPrivate
->onDemandServiceID
); 
4086         if (userOptions 
!= NULL
) { 
4087                 if (targetPrivate
->onDemandName 
!= NULL
) { 
4088                         *userOptions 
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
4089                         CFDictionarySetValue((CFMutableDictionaryRef
)*userOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
); 
4091                         *userOptions 
= NULL
; 
4102 __SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef   onDemandServer
, 
4103                                              SCNetworkReachabilityFlags onDemandFlags
, 
4106         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)info
; 
4107         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
4109         MUTEX_LOCK(&targetPrivate
->lock
); 
4111         if (!targetPrivate
->scheduled
) { 
4112                 // if not currently scheduled 
4113                 MUTEX_UNLOCK(&targetPrivate
->lock
); 
4117         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sOnDemand \"server\" status changed (now 0x%08x)"), 
4118               targetPrivate
->log_prefix
, 
4121         if (targetPrivate
->type 
== reachabilityTypeName
) { 
4122                 // make sure that we resolve the name again 
4123                 targetPrivate
->needResolve 
= TRUE
; 
4126         __SCNetworkReachabilityUpdate(target
); 
4128         MUTEX_UNLOCK(&targetPrivate
->lock
); 
4135 __SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef   store_info
, 
4136                                      SCNetworkReachabilityRef   target
, 
4137                                      Boolean                    onDemandRetry
, 
4138                                      SCNetworkReachabilityFlags 
*flags
) 
4140         SCNetworkConnectionRef          connection              
= NULL
; 
4141         SCNetworkConnectionType         connectionType          
= kSCNetworkConnectionTypeUnknown
; 
4142         Boolean                         isAppLayerVPN           
= FALSE
; 
4143         Boolean                         isOnDemandService       
= FALSE
; 
4145         CFStringRef                     onDemandRemoteAddress   
= NULL
; 
4146         CFStringRef                     onDemandServiceID       
= NULL
; 
4147         SCNetworkConnectionStatus       onDemandStatus          
= kSCNetworkConnectionInvalid
; 
4148         CFMutableDictionaryRef          selectOptions           
= NULL
; 
4149         Boolean                         success                 
= FALSE
; 
4150         SCNetworkReachabilityPrivateRef targetPrivate           
= (SCNetworkReachabilityPrivateRef
)target
; 
4152         MUTEX_ASSERT_HELD(&targetPrivate
->lock
); 
4154         if (targetPrivate
->onDemandName 
== NULL
) { 
4155                 targetPrivate
->onDemandName 
= CFStringCreateWithCString(NULL
, targetPrivate
->name
, kCFStringEncodingUTF8
); 
4159          * check if an OnDemand VPN configuration matches the name. 
4162         connection 
= SCNetworkConnectionCreate(kCFAllocatorDefault
, NULL
, NULL
); 
4163         if (connection 
== NULL
) { 
4167         /* set select options */ 
4168         selectOptions 
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
4169         if (selectOptions 
== NULL
) { 
4173         CFDictionaryAddValue(selectOptions
, kSCNetworkConnectionSelectionOptionOnDemandHostName
, targetPrivate
->onDemandName
); 
4174         CFDictionaryAddValue(selectOptions
, kSCNetworkConnectionSelectionOptionOnDemandRetry
, onDemandRetry 
? kCFBooleanTrue 
: kCFBooleanFalse
); 
4175         CFDictionaryAddValue(selectOptions
, kSCNetworkConnectionSelectionOptionNoUserPrefs
, kCFBooleanTrue
); 
4177         /* select service. May be On Demand or App Layer VPN */ 
4178         if (!SCNetworkConnectionSelectServiceWithOptions(connection
, selectOptions
)) { 
4182         /* get reachability flags (of VPN server) */ 
4183         (void) SCNetworkConnectionGetReachabilityInfo(connection
, flags
, NULL
); 
4185         connectionType 
= SCNetworkConnectionGetType(connection
); 
4186         if (connectionType 
== kSCNetworkConnectionTypeAppLayerVPN
) { 
4187                 isAppLayerVPN 
= TRUE
; 
4190         /* get on-demand info */ 
4191         onDemandServiceID 
= SCNetworkConnectionCopyServiceID(connection
); 
4192         if (SCNetworkConnectionCopyOnDemandInfo(connection
, &onDemandRemoteAddress
, &onDemandStatus
)) { 
4193                 if (onDemandRemoteAddress 
!= NULL
) { 
4194                         isOnDemandService 
= TRUE
; 
4199         /* handle non-OnDemand App Layer VPN */ 
4200         if (isAppLayerVPN 
&& !isOnDemandService
) { 
4201                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  status  * = 0x%08x (App Layer VPN)"), 
4202                       targetPrivate
->log_prefix
, 
4204                 if (*flags 
& kSCNetworkReachabilityFlagsReachable
) { 
4205                         // if VPN "server" is reachable 
4207                         if (!(*flags 
& kSCNetworkReachabilityFlagsTransientConnection
)) { 
4208                                 // start w/clean flags if not already layered on a transient network 
4209                                 *flags 
= kSCNetworkReachabilityFlagsReachable
; 
4212                         *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
4213                         if (onDemandStatus 
!= kSCNetworkConnectionConnected
) { 
4214                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
4217                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  status    = isReachable%s"), 
4218                               (onDemandStatus 
!= kSCNetworkConnectionConnected
) 
4219                                         ? " (after App Layer connect)" : "", 
4220                               targetPrivate
->log_prefix
); 
4227         if (!_SC_CFEqual(targetPrivate
->onDemandRemoteAddress
, onDemandRemoteAddress
) || 
4228             !_SC_CFEqual(targetPrivate
->onDemandServiceID
, onDemandServiceID
)) { 
4229                 if (targetPrivate
->onDemandRemoteAddress 
!= NULL
) { 
4230                         CFRelease(targetPrivate
->onDemandRemoteAddress
); 
4231                         targetPrivate
->onDemandRemoteAddress 
= NULL
; 
4234                 if (targetPrivate
->onDemandServer 
!= NULL
) { 
4235                         SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, NULL
, NULL
); 
4236                         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
4238                                 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
); 
4239                         } else if (targetPrivate
->rls 
!= NULL
) { 
4244                                 n 
= CFArrayGetCount(targetPrivate
->rlList
); 
4245                                 for (i 
= 0; i 
< n
; i 
+= 3) { 
4246                                         CFRunLoopRef    rl      
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1); 
4247                                         CFStringRef     rlMode  
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2); 
4249                                         __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, TRUE
); 
4253                         CFRelease(targetPrivate
->onDemandServer
); 
4254                         targetPrivate
->onDemandServer 
= NULL
; 
4257                 if (targetPrivate
->onDemandServiceID 
!= NULL
) { 
4258                         CFRelease(targetPrivate
->onDemandServiceID
); 
4259                         targetPrivate
->onDemandServiceID 
= NULL
; 
4264                 if (onDemandStatus 
!= kSCNetworkConnectionConnected
) { 
4266                          * if we have a VPN configuration matching the name *and* we need to 
4267                          * bring the VPN up.  Combine our flags with those of the VPN server. 
4269                         if (targetPrivate
->onDemandServer 
== NULL
) { 
4270                                 SCNetworkReachabilityPrivateRef demandPrivate
; 
4271                                 CFMutableDictionaryRef          options
; 
4273                                 options 
= CFDictionaryCreateMutable(NULL
, 
4275                                                                     &kCFTypeDictionaryKeyCallBacks
, 
4276                                                                     &kCFTypeDictionaryValueCallBacks
); 
4277                                 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionNodeName
, onDemandRemoteAddress
); 
4278                                 CFDictionarySetValue(options
, kSCNetworkReachabilityOptionConnectionOnDemandBypass
, kCFBooleanTrue
); 
4279                                 if (targetPrivate
->serverBypass
) { 
4280                                         CFDictionarySetValue(options
, kSCNetworkReachabilityOptionServerBypass
, kCFBooleanTrue
); 
4282                                 targetPrivate
->onDemandServer 
= SCNetworkReachabilityCreateWithOptions(NULL
, options
); 
4285                                 // indent OnDemand target 
4286                                 demandPrivate 
= (SCNetworkReachabilityPrivateRef
)targetPrivate
->onDemandServer
; 
4287                                 strlcat(demandPrivate
->log_prefix
, ".... ", sizeof(demandPrivate
->log_prefix
)); 
4289                                 if (targetPrivate
->scheduled
) { 
4290                                         SCNetworkReachabilityContext    context 
= { 0, NULL
, CFRetain
, CFRelease
, CFCopyDescription 
}; 
4292                                         context
.info 
= (void *)target
; 
4293                                         SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, 
4294                                                                          __SCNetworkReachabilityOnDemandCheckCallback
, 
4297                                         // schedule server reachability to match that of the target 
4298                                         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
4299                                                 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, targetPrivate
->dispatchQueue
, TRUE
); 
4304                                                 n 
= CFArrayGetCount(targetPrivate
->rlList
); 
4305                                                 for (i 
= 0; i 
< n
; i 
+= 3) { 
4306                                                         CFRunLoopRef    rl      
= (CFRunLoopRef
)CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+1); 
4307                                                         CFStringRef     rlMode  
= (CFStringRef
) CFArrayGetValueAtIndex(targetPrivate
->rlList
, i
+2); 
4309                                                         __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, rl
, rlMode
, NULL
, TRUE
); 
4315                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%s  status  * = 0x%08x"), 
4316                               targetPrivate
->log_prefix
, 
4320                         if ((*flags 
& kSCNetworkReachabilityFlagsReachable
) && !(*flags 
& kSCNetworkReachabilityFlagsConnectionRequired
)) { 
4321                                 // if VPN "server" is [still] reachable 
4323                                 if (!(*flags 
& kSCNetworkReachabilityFlagsTransientConnection
)) { 
4324                                         // start w/clean flags if not already layered on a transient network 
4325                                         *flags 
= kSCNetworkReachabilityFlagsReachable
; 
4328                                 *flags 
|= kSCNetworkReachabilityFlagsTransientConnection
; 
4329                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionRequired
; 
4330                                 *flags 
|= kSCNetworkReachabilityFlagsConnectionOnDemand
; 
4332                                 // set 'InterventionRequired' if the OnDemand connection is paused 
4333                                 if (SCNetworkConnectionIsOnDemandSuspended(connection
)) { 
4334                                         *flags 
|= kSCNetworkReachabilityFlagsInterventionRequired
; 
4338                                         SCLog(TRUE
, LOG_INFO
, CFSTR("%s  service * = %@"), 
4339                                               targetPrivate
->log_prefix
, 
4341                                         SCLog(TRUE
, LOG_INFO
, CFSTR("%s  status    = isReachable (after OnDemand connect)"), 
4342                                               targetPrivate
->log_prefix
); 
4349                 if (onDemandRemoteAddress 
!= NULL
) { 
4350                         if (targetPrivate
->onDemandRemoteAddress 
== NULL
) { 
4351                                 targetPrivate
->onDemandRemoteAddress 
= CFRetain(onDemandRemoteAddress
); 
4355                 if (onDemandServiceID 
!= NULL
) { 
4356                         if (targetPrivate
->onDemandServiceID 
== NULL
) { 
4357                                 targetPrivate
->onDemandServiceID 
= CFRetain(onDemandServiceID
); 
4364         if (onDemandServiceID 
!= NULL
) { 
4365                 CFRelease(onDemandServiceID
); 
4367         if (onDemandRemoteAddress 
!= NULL
) { 
4368                 CFRelease(onDemandRemoteAddress
); 
4370         if (connection 
!= NULL
) { 
4371                 CFRelease(connection
); 
4373         if (selectOptions 
!= NULL
) { 
4374                 CFRelease(selectOptions
); 
4381  * OnDemand configuration handling 
4385  * 1. We have a "contract" with mDNSResponder that for EVERY network 
4386  *    or DNS configuration change that should warrant our [re-]starting 
4387  *    a query, mDNSResponder will acknowledge the latest DNS configuration. 
4389  * 2. IPMonitor also posts a notification AFTER every network or DNS 
4390  *    configuration change. 
4392  * 3. We use IPMonitor's "trailing edge" as a signal to restart any 
4397 // Note: protected by _hn_target_queue() 
4398 static int                      onDemand_refresh_token
; 
4399 static Boolean                  onDemand_refresh_token_valid    
= FALSE
; 
4403  * onDemand_refresh_handler 
4405  * Called to notify/update all SCNetworkReachability targets of 
4407  * - should be exec'd on the _hn_changes_queue() 
4410 onDemand_refresh_handler() 
4414         __block SCDynamicStoreRef       store   
= NULL
; 
4416         dispatch_sync(_hn_target_queue(), ^{ 
4417                 if (onDemand_refresh_token_valid 
&& (hn_store 
!= NULL
)) { 
4418                         store 
= CFRetain(hn_store
); 
4422         if (store 
== NULL
) { 
4426         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
4427                                                          kSCDynamicStoreDomainState
, 
4429         changes 
= CFArrayCreate(NULL
, (const void **)&key
, 1, &kCFTypeArrayCallBacks
); 
4430         __SCNetworkReachabilityHandleChanges(store
, changes
, NULL
); 
4440  * onDemand_refresh_enable 
4442  * Called to monitor for OnDemand changes. 
4443  * - caller must be running on the _hn_target_queue() 
4446 onDemand_refresh_enable(dispatch_queue_t q
) 
4450         status 
= notify_register_dispatch(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY
, 
4451                                           &onDemand_refresh_token
, 
4454                                                   onDemand_refresh_handler(); 
4456         if (status 
!= NOTIFY_STATUS_OK
) { 
4457                 SCLog(TRUE
, LOG_INFO
, CFSTR("notify_register_dispatch() failed, status=%u"), status
); 
4461         onDemand_refresh_token_valid 
= TRUE
; 
4468  * onDemand_refresh_disable 
4470  * Called to stop monitoring for OnDemand changes 
4471  * - caller must be running on the _hn_target_queue() 
4474 onDemand_refresh_disable() 
4476         (void)notify_cancel(onDemand_refresh_token
); 
4477         onDemand_refresh_token_valid 
= FALSE
; 
4485 #pragma mark Reachability Flags 
4489 __SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef        store_info
, 
4490                                 SCNetworkReachabilityRef        target
, 
4491                                 ReachabilityInfo                
*reach_info
, 
4494         CFMutableArrayRef               addresses       
= NULL
; 
4495         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
4496         ReachabilityInfo                my_info         
= NOT_REACHABLE
; 
4499         MUTEX_ASSERT_HELD(&targetPrivate
->lock
); 
4501         _reach_set(reach_info
, &NOT_REACHABLE
, reach_info
->cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
); 
4503         if (!isA_SCNetworkReachability(target
)) { 
4504                 _SCErrorSet(kSCStatusInvalidArgument
); 
4508 #if     TARGET_OS_IPHONE 
4509         if (isReachabilityTypeName(targetPrivate
->type
) && 
4511             pthread_is_threaded_np() && 
4512             pthread_main_np()) { 
4513                 SCLog(TRUE
, LOG_WARNING
, CFSTR("Warning: sync SCNetworkReachability (by-name) query on main thread")); 
4515 #endif  // TARGET_OS_IPHONE 
4517         if (!targetPrivate
->serverBypass
) { 
4518                 if (!targetPrivate
->serverActive
) { 
4520                         ok 
= __SCNetworkReachabilityServer_targetAdd(target
); 
4522                                 targetPrivate
->serverBypass 
= TRUE
; 
4526                 if (targetPrivate
->serverActive
) { 
4527                         ok 
= __SCNetworkReachabilityServer_targetStatus(target
); 
4529                                 SCLog(TRUE
, LOG_DEBUG
, 
4530                                       CFSTR("__SCNetworkReachabilityGetFlags _targetStatus() failed")); 
4531                                 _SCErrorSet(kSCStatusFailed
); 
4535                         targetPrivate
->cycle 
= targetPrivate
->serverInfo
.cycle
; 
4536                         _reach_set(&my_info
, 
4537                                    &targetPrivate
->serverInfo
, 
4538                                    targetPrivate
->serverInfo
.cycle
, 
4539                                    targetPrivate
->if_index
, 
4540                                    targetPrivate
->if_name
); 
4546         switch (targetPrivate
->type
) { 
4547                 case reachabilityTypeAddress 
: 
4548                 case reachabilityTypeAddressPair 
: { 
4550                          * Check "local" address 
4552                         if (targetPrivate
->localAddress 
!= NULL
) { 
4554                                  * Check "local" address 
4556                                 ok 
= checkAddress(store_info
, 
4557                                                   targetPrivate
->localAddress
, 
4558                                                   targetPrivate
->if_index
, 
4560                                                   targetPrivate
->log_prefix
); 
4562                                         goto done2
;     /* not today */ 
4565                                 if (!(my_info
.flags 
& kSCNetworkReachabilityFlagsIsLocalAddress
)) { 
4566                                         goto done2
;     /* not reachable, non-"local" address */ 
4571                          * Check "remote" address 
4573                         if ((targetPrivate
->remoteAddress 
!= NULL
) && 
4574                             (targetPrivate
->localAddress 
!= targetPrivate
->remoteAddress
)) { 
4576                                  * in cases where we have different "local" and "remote" addresses 
4577                                  * we need to re-initialize the to-be-returned flags. 
4579                                 my_info 
= NOT_REACHABLE
; 
4582                                  * Check "remote" address 
4584                                 ok 
= checkAddress(store_info
, 
4585                                                   targetPrivate
->remoteAddress
, 
4586                                                   targetPrivate
->if_index
, 
4588                                                   targetPrivate
->log_prefix
); 
4590                                         goto done2
;     /* not today */ 
4598                 case reachabilityTypeName 
: 
4599                 case reachabilityTypePTR  
: { 
4601                         int                             ns_dns_config   
= -1; 
4602                         SCNetworkReachabilityFlags      ns_flags        
= 0; 
4603                         uint32_t                        ns_if_index     
= 0; 
4605                         addresses 
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
); 
4606                         if ((addresses 
!= NULL
) || (error 
!= NETDB_SUCCESS
)) { 
4607                                 /* if resolved or an error had been detected */ 
4609                                         /* if not an async request */ 
4610                                         goto checkResolvedAddresses
; 
4611                                 } else if (targetPrivate
->dnsActive
) { 
4612                                         /* if [m]DNS query active */ 
4613                                         goto checkResolvedAddresses
; 
4614                                 } else if (!targetPrivate
->needResolve
) { 
4616                                          * if this is an async request (i.e. someone is watching the reachability 
4617                                          * of this target), if no query active, and if no query is needed 
4619                                         goto checkResolvedAddresses
; 
4623                         if (!targetPrivate
->onDemandBypass
) { 
4625                                 SCNetworkReachabilityFlags      onDemandFlags   
= 0; 
4628                                  * before we attempt our initial DNS query, check if there is 
4629                                  * an OnDemand configuration that we should be using. 
4631                                 onDemand 
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, FALSE
, &onDemandFlags
); 
4633                                         /* if OnDemand connection is needed */ 
4634                                         my_info
.flags 
= onDemandFlags
; 
4639                         targetPrivate
->dnsBlocked 
= FALSE
; 
4641                         /* update the reachability of the DNS servers */ 
4642                         _SC_R_updateResolverReachability(store_info
, 
4644                                                          &targetPrivate
->haveDNS
, 
4645                                                          targetPrivate
->name
, 
4646                                                          targetPrivate
->if_index
, 
4649                                                          targetPrivate
->log_prefix
); 
4652                         // save resolver reachability flags 
4653                         targetPrivate
->resolverFlags 
= ns_flags
; 
4655                         if (rankReachability(ns_flags
) < 2) { 
4657                                  * if DNS servers are not (or are no longer) reachable, set 
4658                                  * flags based on the availability of configured (but not 
4662                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"), 
4663                                       targetPrivate
->log_prefix
); 
4665                                 if (!targetPrivate
->dnsBlocked
) { 
4666                                         ok 
= checkAddress(store_info
, 
4668                                                           targetPrivate
->if_index
, 
4670                                                           targetPrivate
->log_prefix
); 
4672                                                 SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sNo available networks"), 
4673                                                       targetPrivate
->log_prefix
); 
4677                                         // if not checking "available" networks 
4678                                         my_info
.flags 
= ns_flags
; 
4679                                         my_info
.if_index 
= ns_if_index
; 
4682                                 if (async 
&& targetPrivate
->scheduled
) { 
4684                                          * return "host not found", set flags appropriately, 
4685                                          * and schedule notification. 
4687                                         __SCNetworkReachabilitySetResolvedError(target
, EAI_NONAME
); 
4688                                         my_info
.flags 
|= (targetPrivate
->info
.flags 
& kSCNetworkReachabilityFlagsFirstResolvePending
); 
4690                                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sno DNS servers are reachable"), 
4691                                               targetPrivate
->log_prefix
); 
4692                                         __SCNetworkReachabilityUpdate(target
); 
4698                         if (targetPrivate
->resolverBypass
) { 
4699                                 if (targetPrivate
->haveDNS
) { 
4701                                          * if we are not resolving the name, and if we have 
4702                                          * one or more DNS resolvers, then return flags that 
4703                                          * reflect the reachability of the resolvers (and 
4704                                          * not the actual name). 
4706                                         my_info
.flags 
= ns_flags
; 
4707                                         my_info
.if_index 
= ns_if_index
; 
4713                                 /* for async requests we return the last known status */ 
4714                                 my_info 
= targetPrivate
->info
; 
4716                                 if (targetPrivate
->dnsActive
) { 
4717                                         /* if [m]DNS query active */ 
4718                                         if (_sc_debug 
&& !targetPrivate
->quiet
) { 
4719                                                 SCLog(TRUE
, LOG_INFO
, 
4720                                                       CFSTR("%swaiting for DNS reply"), 
4721                                                       targetPrivate
->log_prefix
); 
4723                                         if ((addresses 
!= NULL
) || (error 
!= NETDB_SUCCESS
)) { 
4724                                                 /* updated reachability based on the previous reply */ 
4725                                                 goto checkResolvedAddresses
; 
4730                                 SCLog(_sc_debug
, LOG_INFO
, 
4731                                       CFSTR("%sstart DNS query for name = %s"), 
4732                                       targetPrivate
->log_prefix
, 
4733                                       targetPrivate
->name
); 
4736                                  * initiate an DNS query w/DNSServiceGetAddrInfo 
4738                                 enqueueDNSQuery(target
); 
4742                         SCLog(_sc_debug
, LOG_INFO
, 
4743                               CFSTR("%scheckName(%s)"), 
4744                               targetPrivate
->log_prefix
, 
4745                               targetPrivate
->name
); 
4748                          * OK, all of the DNS name servers are available.  Let's 
4749                          * resolve the nodename into an address. 
4751                         sync_DNS_query(target
); 
4753                         if (!(targetPrivate
->dnsHaveTimeout 
&& targetPrivate
->dnsHaveError
)) { 
4754                                 // if target reach info is valid 
4755                                 memcpy(reach_info
, &targetPrivate
->info
, sizeof(ReachabilityInfo
)); 
4759                         if (addresses 
!= NULL
)  CFRelease(addresses
); 
4760                         addresses 
= (CFMutableArrayRef
)SCNetworkReachabilityCopyResolvedAddress(target
, &error
); 
4762                     checkResolvedAddresses 
: 
4765                          * We first assume that the requested host is NOT available. 
4766                          * Then, check each address for accessibility and return the 
4767                          * best status available. 
4769                         my_info 
= NOT_REACHABLE
; 
4771                         if ((targetPrivate
->type 
== reachabilityTypeName
) && isA_CFArray(addresses
)) { 
4773                                 CFIndex         n       
= CFArrayGetCount(addresses
); 
4774                                 struct sockaddr 
*sa
; 
4776                                 for (i 
= 0; i 
< n
; i
++) { 
4777                                         ReachabilityInfo        ns_info 
= NOT_REACHABLE
; 
4779                                         sa 
= (struct sockaddr 
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
)); 
4781                                         ok 
= checkAddress(store_info
, 
4783                                                           targetPrivate
->if_index
, 
4785                                                           targetPrivate
->log_prefix
); 
4787                                                 goto done2
;     /* not today */ 
4790                                         if (rankReachability(ns_info
.flags
) > rankReachability(my_info
.flags
)) { 
4791                                                 /* return the best case result */ 
4793                                                 if (rankReachability(my_info
.flags
) == 2) { 
4794                                                         /* can't get any better than REACHABLE */ 
4801                                         for (i
++; i 
< n
; i
++) { 
4802                                                 sa 
= (struct sockaddr 
*)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses
, i
)); 
4803                                                 log_address("skipAddress", 
4805                                                             targetPrivate
->if_index
, 
4806                                                             targetPrivate
->log_prefix
); 
4809                         } else if ((targetPrivate
->type 
== reachabilityTypePTR
) && isA_CFArray(addresses
)) { 
4811                                 CFIndex         n       
= CFArrayGetCount(addresses
); 
4813                                 my_info 
= NOT_REACHABLE
; 
4815                                 for (i 
= 0; i 
< n
; i
++) { 
4817                                                 my_info
.flags 
= kSCNetworkReachabilityFlagsReachable
; 
4821                                                 CFStringRef     ptrName
; 
4823                                                 ptrName 
= CFArrayGetValueAtIndex(addresses
, i
); 
4824                                                 SCLog(TRUE
, LOG_INFO
, CFSTR("%sPTR name(%@)"), 
4825                                                       targetPrivate
->log_prefix
, 
4830                                 if ((error 
== EAI_NONAME
) 
4831 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) 
4832                                      || (error 
== EAI_NODATA
) 
4836                                          * the target host name could not be resolved 
4838                                         if (!targetPrivate
->onDemandBypass
) { 
4840                                                 SCNetworkReachabilityFlags      onDemandFlags   
= 0; 
4843                                                  * our initial DNS query failed, check again to see if there 
4844                                                  * there is an OnDemand configuration that we should be using. 
4846                                                 onDemand 
= __SCNetworkReachabilityOnDemandCheck(store_info
, target
, TRUE
, &onDemandFlags
); 
4848                                                         /* if OnDemand connection is needed */ 
4849                                                         my_info
.flags 
= onDemandFlags
; 
4855                                         if (!targetPrivate
->haveDNS
) { 
4857                                                  * No DNS servers are defined. Set flags based on 
4858                                                  * the availability of configured (but not active) 
4861                                                 ok 
= checkAddress(store_info
, 
4863                                                                   targetPrivate
->if_index
, 
4865                                                                   targetPrivate
->log_prefix
); 
4867                                                         goto done2
;     /* not today */ 
4870                                                 if ((my_info
.flags 
& kSCNetworkReachabilityFlagsReachable
) && 
4871                                                         (my_info
.flags 
& kSCNetworkReachabilityFlagsConnectionRequired
)) { 
4873                                                          * Since we might pick up a set of DNS servers when this connection 
4874                                                          * is established, don't reply with a "HOST NOT FOUND" error just yet. 
4879                                                 /* Host not found, not reachable! */ 
4880                                                 my_info 
= NOT_REACHABLE
; 
4892         _reach_set(reach_info
, &my_info
, targetPrivate
->cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
); 
4896         if (addresses 
!= NULL
)  CFRelease(addresses
); 
4901 SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target
) 
4905         ReachabilityStoreInfo           store_info
; 
4906         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
4908         if (!isA_SCNetworkReachability(target
)) { 
4909                 _SCErrorSet(kSCStatusInvalidArgument
); 
4913         ReachabilityStoreInfo_init(&store_info
); 
4915         MUTEX_LOCK(&targetPrivate
->lock
); 
4917         if (targetPrivate
->scheduled
) { 
4918                 // if being watched, return the last known (and what should be current) status 
4923         ok 
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
); 
4927         /* Only return the if_index if the connection is reachable not for reachable connection 
4928          * required etc ... */ 
4929         if (ok 
&& rankReachability(targetPrivate
->info
.flags
) == 2) { 
4930                 if_index 
= targetPrivate
->info
.if_index
; 
4933         MUTEX_UNLOCK(&targetPrivate
->lock
); 
4934         ReachabilityStoreInfo_free(&store_info
); 
4940 SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef          target
, 
4941                               SCNetworkReachabilityFlags        
*flags
) 
4944         ReachabilityStoreInfo           store_info
; 
4945         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
4947         if (!isA_SCNetworkReachability(target
)) { 
4948                 _SCErrorSet(kSCStatusInvalidArgument
); 
4952         ReachabilityStoreInfo_init(&store_info
); 
4954         MUTEX_LOCK(&targetPrivate
->lock
); 
4956         if (targetPrivate
->scheduled
) { 
4957                 // if being watched, return the last known (and what should be current) status 
4958                 *flags 
= targetPrivate
->info
.flags 
& kSCNetworkReachabilityFlagsMask
; 
4960                 if (isReachabilityTypeName(targetPrivate
->type
) && targetPrivate
->dnsNoAddressesSinceLastTimeout
) { 
4961                         targetPrivate
->needResolve 
= TRUE
; 
4962                         ReachabilityInfo tmp_reach_info 
= NOT_REACHABLE
; 
4963                         __SCNetworkReachabilityGetFlags(&store_info
, target
, &tmp_reach_info
, TRUE
); 
4970         ok 
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &targetPrivate
->info
, FALSE
); 
4972                 SCLog(TRUE
, LOG_INFO
, CFSTR("%s  flags     = 0x%08x"), targetPrivate
->log_prefix
, targetPrivate
->info
.flags
); 
4975         *flags 
= targetPrivate
->info
.flags 
& kSCNetworkReachabilityFlagsMask
; 
4979         MUTEX_UNLOCK(&targetPrivate
->lock
); 
4980         ReachabilityStoreInfo_free(&store_info
); 
4986 #pragma mark Notifications 
4990  * __SCNetworkReachabilityHandleChanges 
4992  * Called to process network configuration changes and determine 
4993  * if a reachability notification is warranted. 
4994  * - should be exec'd on the _hn_changes_queue() 
4997 __SCNetworkReachabilityHandleChanges(SCDynamicStoreRef  store
, 
4998                                      CFArrayRef         changedKeys
, 
5001         Boolean                 dnsConfigChanged        
= FALSE
; 
5003         Boolean                 forcedChange            
= FALSE
; 
5007         CFIndex                 nGlobals                
= 0; 
5009         Boolean                 neChanged               
= FALSE
; 
5010         Boolean                 networkConfigChanged    
= FALSE
; 
5012         Boolean                 onDemandConfigChanged   
= FALSE
; 
5013 #if     !TARGET_OS_IPHONE 
5014         Boolean                 powerStatusChanged      
= FALSE
; 
5015 #endif  // !TARGET_OS_IPHONE 
5016         ReachabilityStoreInfo   store_info
; 
5017         const void *            targets_q
[N_QUICK
]; 
5018         const void **           targets                 
= targets_q
; 
5019         __block CFSetRef        watchers                
= NULL
; 
5021         nChanges 
= CFArrayGetCount(changedKeys
); 
5022         if (nChanges 
== 0) { 
5028         dispatch_sync(_hn_target_queue(), ^{ 
5029                 /* grab the currently watched targets */ 
5030                 if (hn_targets 
!= NULL
) { 
5031                         watchers 
= CFSetCreateCopy(NULL
, hn_targets
); 
5035         nTargets 
= (watchers 
!= NULL
) ? CFSetGetCount(watchers
) : 0; 
5036         if (nTargets 
== 0) { 
5037                 /* if no addresses being monitored */ 
5041         /* grab the current time */ 
5042         (void)gettimeofday(&now
, NULL
); 
5044 #if     !TARGET_OS_IPHONE 
5045         match 
= CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), power_changed_key
); 
5047                 /* handle "early" wake notification */ 
5049                 powerStatusChanged 
= TRUE
; 
5051 #endif  // !TARGET_OS_IPHONE 
5053         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
5054                                                          kSCDynamicStoreDomainState
, 
5056         match 
= CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
); 
5060                 dnsConfigChanged 
= TRUE
;        /* the DNS server(s) have changed */ 
5063         key 
= SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL
, 
5064                                                          kSCDynamicStoreDomainState
, 
5066         match 
= CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), key
); 
5070                 onDemandConfigChanged 
= TRUE
;   /* the OnDemand configuration has changed */ 
5072                 // force OnDemand configuration refresh (if SC notification arrives before BSD notify) 
5073                 __SCNetworkConnectionForceOnDemandConfigurationRefresh(); 
5078         match 
= CFArrayContainsValue(changedKeys
, CFRangeMake(0, nChanges
), SCNETWORKREACHABILITY_TRIGGER_KEY
); 
5081                 forcedChange 
= TRUE
;            /* an SCDynamicStore driven "network" change */ 
5084         if (nChanges 
> nGlobals
) { 
5085                 networkConfigChanged 
= TRUE
; 
5089                 unsigned int            changes                 
= 0; 
5090                 static const char       *change_strings
[]       = { 
5091                         // with no "power" status change 
5093                         "network ",                                     // 00001 
5095                         "network and DNS ",                             // 00011 
5096                         "OnDemand ",                                    // 00100 
5097                         "network and OnDemand ",                        // 00101 
5098                         "DNS and OnDemand ",                            // 00110 
5099                         "network, DNS, and OnDemand ",                  // 00111 
5101                         "network and NE ",                              // 01001 
5102                         "DNS and NE ",                                  // 01010 
5103                         "network, DNS, and NE ",                        // 01011 
5104                         "OnDemand and NE ",                             // 01100 
5105                         "network, OnDemand, and NE ",                   // 01101 
5106                         "DNS, OnDemand, and NE ",                       // 01110 
5107                         "network, DNS, OnDemand, and NE ",              // 01111 
5108 #if     !TARGET_OS_IPHONE 
5109                         // with "power" status change 
5111                         "network and power ",                           // 10001 
5112                         "DNS and power ",                               // 10010 
5113                         "network, DNS, and power ",                     // 10011 
5114                         "OnDemand and power ",                          // 10100 
5115                         "network, OnDemand, and power ",                // 10101 
5116                         "DNS, OnDemand, and power ",                    // 10110 
5117                         "network, DNS, OnDemand, and power ",           // 10111 
5118                         "NE and power ",                                // 11000 
5119                         "network, NE, and power ",                      // 11001 
5120                         "DNS, NE, and power ",                          // 11010 
5121                         "network, DNS, NE, and power ",                 // 11011 
5122                         "OnDemand, NE, and power ",                     // 11100 
5123                         "network, OnDemand, NE, and power ",            // 11101 
5124                         "DNS, OnDemand, NE, and power ",                // 11110 
5125                         "network, DNS, OnDemand, NE, and power ",       // 11111 
5126 #endif  // !TARGET_OS_IPHONE 
5129 #if     !TARGET_OS_IPHONE 
5131                 if (powerStatusChanged
) { 
5134 #endif  // !TARGET_OS_IPHONE 
5142                 if (onDemandConfigChanged
) { 
5147                 if (dnsConfigChanged
) { 
5152                 if (networkConfigChanged
) { 
5156                 SCLog(TRUE
, LOG_INFO
, 
5157                       CFSTR("process %s%s%sconfiguration change"), 
5158                       forcedChange 
? "[forced] " : "", 
5159                       change_strings
[changes
]); 
5162         ReachabilityStoreInfo_init(&store_info
); 
5164         if (nTargets 
> (CFIndex
)(sizeof(targets_q
) / sizeof(CFTypeRef
))) 
5165                 targets 
= CFAllocatorAllocate(NULL
, nTargets 
* sizeof(CFTypeRef
), 0); 
5166         CFSetGetValues(watchers
, targets
); 
5167         for (i 
= 0; i 
< nTargets
; i
++) { 
5168                 Boolean                         dnsNeedsUpdate  
= FALSE
; 
5169                 SCNetworkReachabilityRef        target          
= targets
[i
]; 
5170                 SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
5172                 MUTEX_LOCK(&targetPrivate
->lock
); 
5175                 if (dnsConfigChanged
) { 
5176                         targetPrivate
->last_dns 
= now
; 
5179                 if (networkConfigChanged
) { 
5180                         targetPrivate
->last_network 
= now
; 
5183 #if     !TARGET_OS_IPHONE 
5184                 if (powerStatusChanged
) { 
5185                         targetPrivate
->last_power 
= now
; 
5187 #endif  // !TARGET_OS_IPHONE 
5189                 if (isReachabilityTypeName(targetPrivate
->type
)) { 
5190                         Boolean         dnsChanged      
= (dnsConfigChanged      
| 
5192                                                            onDemandConfigChanged 
| 
5197                                  * if the DNS configuration didn't change we still need to 
5198                                  * check that the DNS servers are accessible. 
5200                                 Boolean                         ns_blocked      
= FALSE
; 
5201                                 int                             ns_dns_config   
= -1; 
5202                                 SCNetworkReachabilityFlags      ns_flags        
= 0; 
5203                                 uint32_t                        ns_if_index     
= 0; 
5206                                 /* update the reachability of the DNS servers */ 
5207                                 ok 
= ReachabilityStoreInfo_update(&store_info
, &store
, AF_UNSPEC
); 
5209                                         _SC_R_updateResolverReachability(&store_info
, 
5211                                                                          &targetPrivate
->haveDNS
, 
5212                                                                          targetPrivate
->name
, 
5213                                                                          targetPrivate
->if_index
, 
5216                                                                          targetPrivate
->log_prefix
); 
5218                                         ns_flags 
= kSCNetworkReachabilityFlagsReachable
; 
5223                                 if (rankReachability(ns_flags
) < 2) { 
5225                                          * if DNS servers are not (or are no longer) reachable, set 
5226                                          * flags based on the availability of configured (but not 
5229                                         SCLog(_sc_debug
, LOG_INFO
, CFSTR("%sDNS server(s) not available"), 
5230                                               targetPrivate
->log_prefix
); 
5234                                 if ((targetPrivate
->dnsBlocked 
!= ns_blocked
) || 
5235                                     (targetPrivate
->resolverFlags 
!= ns_flags
)) { 
5236                                         // if the DNS blocked or resolver reachability changed 
5237                                         targetPrivate
->dnsBlocked 
= ns_blocked
; 
5238                                         targetPrivate
->resolverFlags 
= ns_flags
; 
5244                                 if (targetPrivate
->dnsActive
) { 
5245                                         // if we have an outstanding [m]DNS query 
5246                                         SCLog(_sc_debug
, LOG_INFO
, 
5247                                               CFSTR("%scancel [m]DNS query for name = %s"), 
5248                                               targetPrivate
->log_prefix
, 
5249                                               targetPrivate
->name
); 
5250                                         dequeueDNSQuery(target
); 
5253                                 /* schedule request to resolve the name again */ 
5254                                 targetPrivate
->needResolve 
= TRUE
; 
5259                         targetPrivate
->cycle
++; 
5262                 if (targetPrivate
->scheduled
) { 
5263                         __SCNetworkReachabilityUpdate(target
); 
5266                 MUTEX_UNLOCK(&targetPrivate
->lock
); 
5268         if (targets 
!= targets_q
)       CFAllocatorDeallocate(NULL
, targets
); 
5270         ReachabilityStoreInfo_free(&store_info
); 
5274         if (watchers 
!= NULL
) CFRelease(watchers
); 
5280  * __SCNetworkReachabilityHandleStoreChanges 
5282  * Called to process SCDynamicStore network configuration changes. 
5283  * - should be exec'd on the _hn_changes_queue() 
5286 __SCNetworkReachabilityHandleStoreChanges(SCDynamicStoreRef     store
, 
5287                                           CFArrayRef            changedKeys
, 
5290         nwi_state_t     nwi_state
; 
5292         if ((CFArrayGetCount(changedKeys
) == 1) && 
5293             CFArrayContainsValue(changedKeys
, CFRangeMake(0, 1), SCNETWORKREACHABILITY_TRIGGER_KEY
)) { 
5297         /* "something" [else] changed, start fresh */ 
5298         ReachabilityStoreInfo_save(NULL
); 
5300         nwi_state 
= nwi_state_copy(); 
5301         if (nwi_state 
!= NULL
) { 
5302                 // if we have some networking 
5303                 nwi_state_release(nwi_state
); 
5307         // if no networking, use the [SC] changes to add/update 
5308         // the kSCNetworkReachabilityFlagsConnectionRequired flag 
5312         __SCNetworkReachabilityHandleChanges(store
, changedKeys
, info
); 
5317 #if     !TARGET_OS_IPHONE 
5320 darkWakeNotify(SCNetworkReachabilityRef target
) 
5327 systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities
) 
5329         if ((power_capabilities 
& POWER_CAPABILITIES_NETWORK
) != POWER_CAPABILITIES_NETWORK
) { 
5330                 // if we're not fully awake (from a networking point of view). 
5337 #endif  // !TARGET_OS_IPHONE 
5341 reachPerform(void *info
) 
5344         void                            (*context_release
)(const void *); 
5346         ReachabilityInfo                reach_info
; 
5347         SCNetworkReachabilityCallBack   rlsFunction
; 
5348         SCNetworkReachabilityRef        target          
= (SCNetworkReachabilityRef
)info
; 
5349         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
5351         n 
= _SC_ATOMIC_ZERO(&targetPrivate
->pending
); 
5352         if (_sc_debug 
&& (n 
> 1)) { 
5353                 SCLog(TRUE
, LOG_DEBUG
, 
5354                       CFSTR("%sdelivering SCNetworkReachability notifications (%u)"), 
5355                       targetPrivate
->log_prefix
, 
5359         MUTEX_LOCK(&targetPrivate
->lock
); 
5361         if (!targetPrivate
->scheduled
) { 
5362                 // if no longer scheduled 
5363                 SCLog(_sc_debug
, LOG_DEBUG
, 
5364                       CFSTR("%sskipping SCNetworkReachability callback, no longer scheduled"), 
5365                       targetPrivate
->log_prefix
); 
5366                 MUTEX_UNLOCK(&targetPrivate
->lock
); 
5370         // capture current state 
5371         memcpy(&reach_info
, &targetPrivate
->info
, sizeof(ReachabilityInfo
)); 
5374         rlsFunction 
= targetPrivate
->rlsFunction
; 
5375         if (targetPrivate
->rlsContext
.retain 
!= NULL
) { 
5376                 context_info    
= (void *)(*targetPrivate
->rlsContext
.retain
)(targetPrivate
->rlsContext
.info
); 
5377                 context_release 
= targetPrivate
->rlsContext
.release
; 
5379                 context_info    
= targetPrivate
->rlsContext
.info
; 
5380                 context_release 
= NULL
; 
5383         // update last notification info 
5384         _reach_set(&targetPrivate
->last_notify
, &reach_info
, targetPrivate
->cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
); 
5385         (void)gettimeofday(&targetPrivate
->last_push
, NULL
); 
5387         MUTEX_UNLOCK(&targetPrivate
->lock
); 
5389         if (rlsFunction 
!= NULL
) { 
5390                 (*rlsFunction
)(target
, 
5391                                reach_info
.flags 
& kSCNetworkReachabilityFlagsMask
, 
5395         if (context_release 
!= NULL
) { 
5396                 (*context_release
)(context_info
); 
5406  * - caller must *not* be holding the target lock 
5407  * - caller must be running on the __SCNetworkReachability_concurrent_queue() 
5410 reachUpdate(SCNetworkReachabilityRef target
) 
5413         Boolean                         defer           
= FALSE
; 
5416         ReachabilityInfo                reach_info      
= NOT_REACHABLE
; 
5417         ReachabilityStoreInfo           store_info
; 
5418         Boolean                         target_debug
; 
5419         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
5421         target_debug 
= (_sc_debug 
&& !targetPrivate
->quiet
); 
5424                 SCLog(TRUE
, LOG_INFO
, CFSTR("%schecking target reachability"), 
5425                       targetPrivate
->log_prefix
); 
5429         MUTEX_LOCK(&targetPrivate
->lock
); 
5431         if (!targetPrivate
->scheduled
) { 
5432                 // if not currently scheduled 
5433                 MUTEX_UNLOCK(&targetPrivate
->lock
); 
5437         /* update reachability, notify if status changed */ 
5438         ReachabilityStoreInfo_init(&store_info
); 
5439         ok 
= __SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
); 
5440         ReachabilityStoreInfo_free(&store_info
); 
5442                 /* if reachability status not available */ 
5444                         SCLog(TRUE
, LOG_INFO
, CFSTR("%sflags not available"), 
5445                               targetPrivate
->log_prefix
); 
5447                 reach_info 
= NOT_REACHABLE
; 
5450 #if     !TARGET_OS_IPHONE 
5452          * We want to defer the notification if this is a maintenance wake *and* 
5453          * the reachability flags that we would be reporting to the application 
5454          * are better than those that we last reported. 
5456         if (!systemIsAwake(power_capabilities
)) { 
5457                 /* if this is a maintenace wake */ 
5458                 reach_info
.sleeping 
= TRUE
; 
5460                 if (rankReachability(reach_info
.flags
) >= rankReachability(targetPrivate
->info
.flags
)) { 
5462                          * don't report the change if the new reachability flags are 
5463                          * the same or "better" 
5465                         defer 
= !darkWakeNotify(target
); 
5466                 } else if (!__reach_changed(&targetPrivate
->last_notify
, &reach_info
)) { 
5468                          * if we have already posted this change 
5470                         defer 
= !darkWakeNotify(target
); 
5473 #endif  // !TARGET_OS_IPHONE 
5475         cycle 
= targetPrivate
->cycle
; 
5476         forced 
= ((cycle 
!= 0) && (targetPrivate
->info
.cycle 
!= cycle
)); 
5481          *      changed forced  pending 
5482          *      ======= ======= ======= 
5485          *         N       Y       N    Change          (forced && !resolve pending) 
5486          *         N       Y       Y    No change       (suppress forced w/resolve pending) 
5492          *      Change    == A || (B && !C) 
5493          *      No Change == !(A || (B && !C)) 
5494          *      No Change == !A && !(B && !C) 
5495          *      No Change == !A && (!B || C) 
5496          *      No Change == (!B || C) && !A 
5498         if ((!forced 
|| (reach_info
.flags 
== kSCNetworkReachabilityFlagsFirstResolvePending
)) 
5499             && !__reach_changed(&targetPrivate
->info
, &reach_info
)) { 
5501                         if (targetPrivate
->info
.sleeping 
== reach_info
.sleeping
) { 
5502                                 SCLog(TRUE
, LOG_INFO
, 
5503                                       CFSTR("%sflags/interface match (now 0x%08x/%u%s)%s%s"), 
5504                                       targetPrivate
->log_prefix
, 
5506                                       reach_info
.if_index
, 
5507                                       reach_info
.sleeping 
? ", z" : "", 
5508                                       defer 
? ", deferred" : "", 
5509                                       forced 
? ", forced" : ""); 
5511                                 SCLog(TRUE
, LOG_INFO
, 
5512                                       CFSTR("%sflags/interface equiv (was 0x%08x/%u%s, now 0x%08x/%u%s)%s%s"), 
5513                                       targetPrivate
->log_prefix
, 
5514                                       targetPrivate
->info
.flags
, 
5515                                       targetPrivate
->info
.if_index
, 
5516                                       targetPrivate
->info
.sleeping 
? ", z" : "", 
5518                                       reach_info
.if_index
, 
5519                                       reach_info
.sleeping 
? ", z" : "", 
5520                                       defer 
? ", deferred" : "", 
5521                                       forced 
? ", forced" : ""); 
5524                 MUTEX_UNLOCK(&targetPrivate
->lock
); 
5529                 SCLog(TRUE
, LOG_INFO
, 
5530                       CFSTR("%sflags/interface have changed (was 0x%08x/%u%s, now 0x%08x/%u%s)%s%s"), 
5531                       targetPrivate
->log_prefix
, 
5532                       targetPrivate
->info
.flags
, 
5533                       targetPrivate
->info
.if_index
, 
5534                       targetPrivate
->info
.sleeping 
? ", z" : "", 
5536                       reach_info
.if_index
, 
5537                       reach_info
.sleeping 
? ", z" : "", 
5538                       defer 
? ", deferred" : "", 
5539                       forced 
? ", forced" : ""); 
5542         /* update flags / interface */ 
5543         _reach_set(&targetPrivate
->info
, &reach_info
, cycle
, targetPrivate
->if_index
, targetPrivate
->if_name
); 
5545         /* as needed, defer the notification */ 
5547                 MUTEX_UNLOCK(&targetPrivate
->lock
); 
5551         MUTEX_UNLOCK(&targetPrivate
->lock
); 
5558 SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef       target
, 
5559                                  SCNetworkReachabilityCallBack  callout
, 
5560                                  SCNetworkReachabilityContext   
*context
) 
5562         SCNetworkReachabilityPrivateRef targetPrivate 
= (SCNetworkReachabilityPrivateRef
)target
; 
5564         MUTEX_LOCK(&targetPrivate
->lock
); 
5566         if (targetPrivate
->rlsContext
.release 
!= NULL
) { 
5567                 /* let go of the current context */ 
5568                 (*targetPrivate
->rlsContext
.release
)(targetPrivate
->rlsContext
.info
); 
5571         targetPrivate
->rlsFunction                      
= callout
; 
5572         targetPrivate
->rlsContext
.info                  
= NULL
; 
5573         targetPrivate
->rlsContext
.retain                
= NULL
; 
5574         targetPrivate
->rlsContext
.release               
= NULL
; 
5575         targetPrivate
->rlsContext
.copyDescription       
= NULL
; 
5577                 bcopy(context
, &targetPrivate
->rlsContext
, sizeof(SCNetworkReachabilityContext
)); 
5578                 if (context
->retain 
!= NULL
) { 
5579                         targetPrivate
->rlsContext
.info 
= (void *)(*context
->retain
)(context
->info
); 
5583         MUTEX_UNLOCK(&targetPrivate
->lock
); 
5590 reachRLSCopyDescription(const void *info
) 
5592         SCNetworkReachabilityRef                target  
= (SCNetworkReachabilityRef
)info
; 
5594         return CFStringCreateWithFormat(NULL
, 
5596                                         CFSTR("<SCNetworkReachability RLS> {target = %p}"), 
5602 __SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef     target
, 
5603                                            CFRunLoopRef                 runLoop
, 
5604                                            CFStringRef                  runLoopMode
, 
5605                                            dispatch_queue_t             queue
, 
5608         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
5609         Boolean                         init            
= FALSE
; 
5610         __block Boolean                 ok              
= FALSE
; 
5612         MUTEX_LOCK(&targetPrivate
->lock
); 
5614         if ((targetPrivate
->dispatchQueue 
!= NULL
) ||           // if we are already scheduled with a dispatch queue 
5615             ((queue 
!= NULL
) && targetPrivate
->scheduled
)) {    // if we are already scheduled on a CFRunLoop 
5616                 _SCErrorSet(kSCStatusInvalidArgument
); 
5620         if (!targetPrivate
->serverBypass
) { 
5621                 if (!targetPrivate
->serverActive
) { 
5623                         ok 
= __SCNetworkReachabilityServer_targetAdd(target
); 
5625                                 targetPrivate
->serverBypass 
= TRUE
; 
5629                 if (targetPrivate
->serverActive
) { 
5630                         if (targetPrivate
->scheduled
) { 
5631                                 // if already scheduled 
5635                         ok 
= __SCNetworkReachabilityServer_targetSchedule(target
); 
5637                                 SCLog(TRUE
, LOG_DEBUG
, 
5638                                       CFSTR("__SCNetworkReachabilityScheduleWithRunLoop _targetMonitor() failed")); 
5639                                 _SCErrorSet(kSCStatusFailed
); 
5647         /* schedule the SCNetworkReachability did-something-change handler */ 
5649         dispatch_sync(_hn_target_queue(), ^{ 
5652                 if (!onDemand 
&& (hn_store 
== NULL
)) { 
5653                         CFMutableArrayRef       keys
; 
5654                         CFMutableArrayRef       patterns
; 
5655                         Boolean                 watch_dns_configuration         
= FALSE
; 
5656                         Boolean                 watch_dns_changes               
= FALSE
; 
5657                         Boolean                 watch_nwi                       
= FALSE
; 
5658                         Boolean                 watch_onDemand_networking       
= FALSE
; 
5659 #if     !TARGET_OS_IPHONE 
5660                         Boolean                 watch_power                     
= FALSE
; 
5661 #endif  // !TARGET_OS_IPHONE 
5663                         hn_store 
= SCDynamicStoreCreate(NULL
, 
5664                                                         CFSTR("SCNetworkReachability"), 
5665                                                         __SCNetworkReachabilityHandleStoreChanges
, 
5667                         if (hn_store 
== NULL
) { 
5668                                 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreCreate() failed")); 
5672                         ReachabilityStoreInfo_keys(&keys
, &patterns
); 
5673                         CFArrayAppendValue(keys
, SCNETWORKREACHABILITY_TRIGGER_KEY
);    // force posting reach change 
5674                         ok 
= SCDynamicStoreSetNotificationKeys(hn_store
, keys
, patterns
); 
5676                         CFRelease(patterns
); 
5678                                 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetNotificationKeys() failed")); 
5679                                 CFRelease(hn_store
); 
5684                         ok 
= SCDynamicStoreSetDispatchQueue(hn_store
, _hn_changes_queue()); 
5686                                 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStoreSetDispatchQueue() failed")); 
5687                                 CFRelease(hn_store
); 
5692                         // watch for network information changes 
5693                         watch_nwi 
= nwi_refresh_enable(_hn_changes_queue()); 
5698                         // watch for DNS configuration (resolver reachability) changes 
5699                         watch_dns_configuration 
= dns_configuration_watch(); 
5700                         if (!watch_dns_configuration
) { 
5704                         // watch for changes affecting DNS queries 
5705                         watch_dns_changes 
= dns_refresh_enable(_hn_changes_queue()); 
5706                         if (!watch_dns_changes
) { 
5710 #if     !TARGET_OS_IPHONE 
5711                         // watch for power capabilities (sleep/wake) changes 
5712                         watch_power 
= power_refresh_enable(_hn_changes_queue()); 
5716 #endif  // !TARGET_OS_IPHONE 
5718                         // watch for OnDemand network changes 
5719                         watch_onDemand_networking 
= onDemand_refresh_enable(_hn_changes_queue()); 
5720                         if (!watch_onDemand_networking
) { 
5726                         hn_targets 
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
); 
5727                         ReachabilityStoreInfo_enable(TRUE
); 
5737                         if (watch_onDemand_networking
) { 
5738                                 onDemand_refresh_disable(); 
5741 #if     !TARGET_OS_IPHONE 
5743                                 power_refresh_disable(); 
5745 #endif  // !TARGET_OS_IPHONE 
5747                         if (watch_dns_changes
) { 
5748                                 dns_refresh_disable(); 
5751                         if (watch_dns_configuration
) { 
5752                                 dns_configuration_unwatch(); 
5756                                 nwi_refresh_disable(); 
5759                         SCDynamicStoreSetDispatchQueue(hn_store
, NULL
); 
5760                         CFRelease(hn_store
); 
5763                         _SCErrorSet(kSCStatusFailed
); 
5770                 CFSetAddValue(hn_targets
, target
); 
5781         if (!targetPrivate
->scheduled
) { 
5782                 CFRunLoopSourceContext  context 
= { 0                           // version 
5783                                                   , (void *)target              
// info 
5784                                                   , CFRetain                    
// retain 
5785                                                   , CFRelease                   
// release 
5786                                                   , reachRLSCopyDescription     
// copyDescription 
5791                                                   , reachPerform                
// perform 
5794                 if (runLoop 
!= NULL
) { 
5795                         targetPrivate
->rls    
= CFRunLoopSourceCreate(NULL
, 0, &context
); 
5796                         targetPrivate
->rlList 
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
); 
5799                 if (isReachabilityTypeName(targetPrivate
->type
)) { 
5801                          * we're now scheduled so let's ensure that we 
5802                          * are starting with a clean slate before we 
5805                         if (targetPrivate
->resolvedAddresses 
!= NULL
) { 
5806                                 CFRelease(targetPrivate
->resolvedAddresses
); 
5807                                 targetPrivate
->resolvedAddresses 
= NULL
; 
5809                         targetPrivate
->resolvedError 
= NETDB_SUCCESS
; 
5810                         targetPrivate
->needResolve 
= TRUE
; 
5811                         _reach_set(&targetPrivate
->info
, 
5813                                    targetPrivate
->info
.cycle
, 
5814                                    targetPrivate
->if_index
, 
5815                                    targetPrivate
->if_name
); 
5816                         targetPrivate
->info
.flags 
|= kSCNetworkReachabilityFlagsFirstResolvePending
; 
5817                         _reach_set(&targetPrivate
->serverInfo
, 
5819                                    targetPrivate
->serverInfo
.cycle
, 
5820                                    targetPrivate
->if_index
, 
5821                                    targetPrivate
->if_name
); 
5822                         targetPrivate
->serverInfo
.flags 
|= kSCNetworkReachabilityFlagsFirstResolvePending
; 
5826                 targetPrivate
->scheduled 
= TRUE
; 
5831         if (queue 
!= NULL
) { 
5832                 // retain dispatch queue 
5833                 dispatch_retain(queue
); 
5834                 targetPrivate
->dispatchQueue 
= queue
; 
5837                 // We've taken a reference to the client's dispatch_queue and we 
5838                 // want to hold on to that reference until we've processed any/all 
5839                 // notifications.  To facilitate this we create a group, dispatch 
5840                 // any notification blocks to via that group, and when the caller 
5841                 // has told us to stop the notifications (unschedule) we wait for 
5842                 // the group to empty and use the group's finalizer to release 
5843                 // our reference to the client's queue. 
5846                 // make sure that we have group to track any async requests 
5847                 targetPrivate
->dispatchGroup 
= dispatch_group_create(); 
5849                 // retain the target ... and release it when the group is released 
5851                 dispatch_set_context(targetPrivate
->dispatchGroup
, (void *)target
); 
5852                 dispatch_set_finalizer_f(targetPrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
); 
5854                 if (!_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) { 
5856                          * if we do not already have host notifications scheduled with 
5857                          * this runLoop / runLoopMode 
5859                         CFRunLoopAddSource(runLoop
, targetPrivate
->rls
, runLoopMode
); 
5862                 _SC_schedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
); 
5866                 ReachabilityInfo        reach_info      
= NOT_REACHABLE
; 
5867                 ReachabilityStoreInfo   store_info
; 
5870                  * if we have yet to schedule SC notifications for this address 
5871                  * - initialize current reachability status 
5873                 ReachabilityStoreInfo_init(&store_info
); 
5874                 if (__SCNetworkReachabilityGetFlags(&store_info
, target
, &reach_info
, TRUE
)) { 
5876                          * if reachability status available 
5878                          * - schedule notification to report status via callback 
5880                         reach_info
.flags 
|= (targetPrivate
->info
.flags 
& kSCNetworkReachabilityFlagsFirstResolvePending
); 
5881                         _reach_set(&targetPrivate
->info
, 
5883                                    targetPrivate
->cycle
, 
5884                                    targetPrivate
->if_index
, 
5885                                    targetPrivate
->if_name
); 
5886                         __SCNetworkReachabilityUpdate(target
); 
5888                         /* if reachability status not available, async lookup started */ 
5889                         _reach_set(&targetPrivate
->info
, 
5891                                    targetPrivate
->cycle
, 
5892                                    targetPrivate
->if_index
, 
5893                                    targetPrivate
->if_name
); 
5894                         _reach_set(&targetPrivate
->serverInfo
, 
5896                                    targetPrivate
->cycle
, 
5897                                    targetPrivate
->if_index
, 
5898                                    targetPrivate
->if_name
); 
5900                 ReachabilityStoreInfo_free(&store_info
); 
5903         if (targetPrivate
->onDemandServer 
!= NULL
) { 
5904                 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, queue
, TRUE
); 
5907         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%sscheduled"), 
5908               targetPrivate
->log_prefix
); 
5914         MUTEX_UNLOCK(&targetPrivate
->lock
); 
5920 __SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef   target
, 
5921                                              CFRunLoopRef               runLoop
, 
5922                                              CFStringRef                runLoopMode
, 
5925         dispatch_group_t                drainGroup      
= NULL
; 
5926         dispatch_queue_t                drainQueue      
= NULL
; 
5929         SCNetworkReachabilityPrivateRef targetPrivate   
= (SCNetworkReachabilityPrivateRef
)target
; 
5931         // hold a reference while we unschedule 
5934         MUTEX_LOCK(&targetPrivate
->lock
); 
5936         if (((runLoop 
== NULL
) && (targetPrivate
->dispatchQueue 
== NULL
)) ||    // if we should be scheduled on a dispatch queue (but are not) 
5937             ((runLoop 
!= NULL
) && (targetPrivate
->dispatchQueue 
!= NULL
))) {    // if we should be scheduled on a CFRunLoop (but are not) 
5938                 _SCErrorSet(kSCStatusInvalidArgument
); 
5942         if (!targetPrivate
->scheduled
) { 
5943                 // if not currently scheduled 
5944                 _SCErrorSet(kSCStatusInvalidArgument
); 
5948         // unschedule the target specific sources 
5949         if (targetPrivate
->dispatchQueue 
!= NULL
) { 
5950                 if (targetPrivate
->onDemandServer 
!= NULL
) { 
5951                         SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, NULL
, NULL
); 
5952                         __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, NULL
, NULL
, TRUE
); 
5955                 // save dispatchQueue, release reference when we've queue'd blocks complete, allow re-scheduling 
5956                 drainGroup 
= targetPrivate
->dispatchGroup
; 
5957                 targetPrivate
->dispatchGroup 
= NULL
; 
5958                 drainQueue 
= targetPrivate
->dispatchQueue
; 
5959                 targetPrivate
->dispatchQueue 
= NULL
; 
5961                 if (!_SC_unschedule(target
, runLoop
, runLoopMode
, targetPrivate
->rlList
, FALSE
)) { 
5962                         // if not currently scheduled 
5963                         _SCErrorSet(kSCStatusInvalidArgument
); 
5967                 if (targetPrivate
->onDemandServer 
!= NULL
) { 
5968                         __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate
->onDemandServer
, runLoop
, runLoopMode
, TRUE
); 
5971                 n 
= CFArrayGetCount(targetPrivate
->rlList
); 
5972                 if ((n 
== 0) || !_SC_isScheduled(NULL
, runLoop
, runLoopMode
, targetPrivate
->rlList
)) { 
5973                         // if target is no longer scheduled for this runLoop / runLoopMode 
5974                         CFRunLoopRemoveSource(runLoop
, targetPrivate
->rls
, runLoopMode
); 
5977                                 // if *all* notifications have been unscheduled 
5978                                 if (targetPrivate
->onDemandServer 
!= NULL
) { 
5979                                         SCNetworkReachabilitySetCallback(targetPrivate
->onDemandServer
, NULL
, NULL
); 
5981                                 CFRelease(targetPrivate
->rlList
); 
5982                                 targetPrivate
->rlList 
= NULL
; 
5983                                 CFRunLoopSourceInvalidate(targetPrivate
->rls
); 
5984                                 CFRelease(targetPrivate
->rls
); 
5985                                 targetPrivate
->rls 
= NULL
; 
5992                 // Cancel our request for server monitoring 
5994                 if (targetPrivate
->serverActive
) { 
5995                         ok 
= __SCNetworkReachabilityServer_targetUnschedule(target
); 
5997                                 SCLog(TRUE
, LOG_DEBUG
, 
5998                                       CFSTR("__SCNetworkReachabilityUnscheduleFromRunLoop _targetMonitor() failed")); 
5999                                 _SCErrorSet(kSCStatusFailed
); 
6003                 // if *all* notifications have been unscheduled 
6004                 targetPrivate
->scheduled 
= FALSE
; 
6007         if (targetPrivate
->serverActive
) { 
6012                 if (targetPrivate
->dnsActive
) { 
6013                         // if we have an active [m]DNS query 
6014                         dequeueDNSQuery(target
); 
6017                 dispatch_sync(_hn_target_queue(), ^{ 
6018                         CFSetRemoveValue(hn_targets
, target
); 
6024                         if (CFSetGetCount(hn_targets
) > 0) { 
6028                         // if we are no longer monitoring any targets 
6029                         SCDynamicStoreSetDispatchQueue(hn_store
, NULL
); 
6030                         CFRelease(hn_store
); 
6032                         CFRelease(hn_targets
); 
6035                         ReachabilityStoreInfo_enable(FALSE
); 
6036                         ReachabilityStoreInfo_save(NULL
); 
6039                          * until we start monitoring again, ensure that 
6040                          * any resources associated with watching network 
6041                          * and configuration changes have been released. 
6045                         // OnDemand configuration 
6046                         onDemand_refresh_disable(); 
6048 #if     !TARGET_OS_IPHONE 
6049                         // sleep/wake & power capabilities 
6050                         power_refresh_disable(); 
6051 #endif  // !TARGET_OS_IPHONE 
6053                         // outstanding DNS queries 
6054                         dns_refresh_disable(); 
6056                         // DNS configuration 
6057                         dns_configuration_unwatch(); 
6060                         nwi_refresh_disable(); 
6066         SCLog((_sc_debug 
&& (_sc_log 
> 0)), LOG_INFO
, CFSTR("%sunscheduled"), 
6067               targetPrivate
->log_prefix
); 
6073         MUTEX_UNLOCK(&targetPrivate
->lock
); 
6075         if (drainGroup 
!= NULL
) { 
6076                 dispatch_group_notify(drainGroup
, __SCNetworkReachability_concurrent_queue(), ^{ 
6077                         // release group/queue references 
6078                         dispatch_release(drainQueue
); 
6079                         dispatch_release(drainGroup
);   // releases our target reference 
6083         // release our reference 
6090 SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef       target
, 
6091                                          CFRunLoopRef                   runLoop
, 
6092                                          CFStringRef                    runLoopMode
) 
6094         if (!isA_SCNetworkReachability(target
) || (runLoop 
== NULL
) || (runLoopMode 
== NULL
)) { 
6095                 _SCErrorSet(kSCStatusInvalidArgument
); 
6099         return __SCNetworkReachabilityScheduleWithRunLoop(target
, runLoop
, runLoopMode
, NULL
, FALSE
); 
6103 SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef     target
, 
6104                                            CFRunLoopRef                 runLoop
, 
6105                                            CFStringRef                  runLoopMode
) 
6107         if (!isA_SCNetworkReachability(target
) || (runLoop 
== NULL
) || (runLoopMode 
== NULL
)) { 
6108                 _SCErrorSet(kSCStatusInvalidArgument
); 
6112         return __SCNetworkReachabilityUnscheduleFromRunLoop(target
, runLoop
, runLoopMode
, FALSE
); 
6116 SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef  target
, 
6117                                       dispatch_queue_t          queue
) 
6121         if (!isA_SCNetworkReachability(target
)) { 
6122                 _SCErrorSet(kSCStatusInvalidArgument
); 
6126         if (queue 
!= NULL
) { 
6127                 ok 
= __SCNetworkReachabilityScheduleWithRunLoop(target
, NULL
, NULL
, queue
, FALSE
); 
6129                 ok 
= __SCNetworkReachabilityUnscheduleFromRunLoop(target
, NULL
, NULL
, FALSE
);