1/* 2 * Copyright (c) 2003-2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * Modification History 26 * 27 * April 12, 2011 Allan Nathanson <ajn@apple.com> 28 * - add SCNetworkReachability "server" 29 * 30 * March 31, 2004 Allan Nathanson <ajn@apple.com> 31 * - use [SC] DNS configuration information 32 * 33 * January 19, 2003 Allan Nathanson <ajn@apple.com> 34 * - add advanced reachability APIs 35 */ 36 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> 48#include <pthread.h> 49#include <libkern/OSAtomic.h> 50 51#if !TARGET_OS_IPHONE 52#include <IOKit/pwr_mgt/IOPMLibPrivate.h> 53#endif // !TARGET_OS_IPHONE 54 55#include <notify.h> 56#include <dnsinfo.h> 57#include <netinet/in.h> 58#include <arpa/inet.h> 59#include <netdb.h> 60#include <resolv.h> 61#include <unistd.h> 62#include <sys/ioctl.h> 63#include <sys/socket.h> 64#include <net/if.h> 65#include <net/if_dl.h> 66#include <net/if_types.h> 67#define KERNEL_PRIVATE 68#include <net/route.h> 69#undef KERNEL_PRIVATE 70 71#ifndef s6_addr16 72#define s6_addr16 __u6_addr.__u6_addr16 73#endif 74 75#include "SCNetworkConnectionInternal.h" 76#include "SCNetworkReachabilityInternal.h" 77 78#include <ppp/ppp_msg.h> 79#include <ppp/PPPControllerPriv.h> 80 81#include <network_information.h> 82 83 84 85 86 87 88 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" 92 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" 96 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" 100 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" 104 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" : "" 111 112 113static pthread_mutexattr_t lock_attr; 114 115#define MUTEX_INIT(m) { \ 116 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \ 117 assert(_lock_); \ 118} 119 120#define MUTEX_LOCK(m) { \ 121 int _lock_ = (pthread_mutex_lock(m) == 0); \ 122 assert(_lock_); \ 123} 124 125#define MUTEX_UNLOCK(m) { \ 126 int _unlock_ = (pthread_mutex_unlock(m) == 0); \ 127 assert(_unlock_); \ 128} 129 130#define MUTEX_ASSERT_HELD(m) { \ 131 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \ 132 assert(_locked_); \ 133} 134 135 136#define SCNETWORKREACHABILITY_TRIGGER_KEY CFSTR("com.apple.SCNetworkReachability:FORCE-CHANGE") 137 138 139#define N_QUICK 64 140 141 142static CFStringRef __SCNetworkReachabilityCopyDescription (CFTypeRef cf); 143static void __SCNetworkReachabilityDeallocate (CFTypeRef cf); 144static void reachPerform (void *info); 145static Boolean reachUpdate (SCNetworkReachabilityRef target); 146 147 148static void 149__SCNetworkReachabilityHandleChanges (SCDynamicStoreRef store, 150 CFArrayRef changedKeys, 151 void *info); 152 153static Boolean 154__SCNetworkReachabilityScheduleWithRunLoop (SCNetworkReachabilityRef target, 155 CFRunLoopRef runLoop, 156 CFStringRef runLoopMode, 157 dispatch_queue_t queue, 158 Boolean onDemand); 159 160static Boolean 161__SCNetworkReachabilityUnscheduleFromRunLoop (SCNetworkReachabilityRef target, 162 CFRunLoopRef runLoop, 163 CFStringRef runLoopMode, 164 Boolean onDemand); 165 166 167static CFTypeID __kSCNetworkReachabilityTypeID = _kCFRuntimeNotATypeID; 168 169 170static const CFRuntimeClass __SCNetworkReachabilityClass = { 171 0, // version 172 "SCNetworkReachability", // className 173 NULL, // init 174 NULL, // copy 175 __SCNetworkReachabilityDeallocate, // dealloc 176 NULL, // equal 177 NULL, // hash 178 NULL, // copyFormattingDesc 179 __SCNetworkReachabilityCopyDescription // copyDebugDesc 180}; 181 182 183static pthread_once_t initialized = PTHREAD_ONCE_INIT; 184static const ReachabilityInfo NOT_REACHABLE = { 0, 0, 0, { 0 }, FALSE }; 185static const ReachabilityInfo NOT_REPORTED = { 0, 0xFFFFFFFF, 0, { 0 }, FALSE }; 186static int rtm_seq = 0; 187 188 189static const struct timeval TIME_ZERO = { 0, 0 }; 190 191 192static int dnsCount = 0; 193static int dnsGeneration = 0; 194static DNSServiceRef dnsMain = NULL; 195static CFMutableSetRef dnsUpdated = NULL; 196 197static Boolean D_serverBypass = FALSE; 198 199 200 201#if !TARGET_OS_IPHONE 202/* 203 * Power capabilities (sleep/wake) 204 */ 205#define POWER_CAPABILITIES_NETWORK ( kIOPMCapabilityCPU \ 206 | kIOPMCapabilityNetwork \ 207 | kIOPMCapabilityVideo) 208static IOPMSystemPowerStateCapabilities power_capabilities = POWER_CAPABILITIES_NETWORK; 209#endif // !TARGET_OS_IPHONE 210 211 212/* 213 * host "something has changed" notifications 214 */ 215 216// Note: protected by _hn_target_queue() 217static SCDynamicStoreRef hn_store = NULL; 218static CFMutableSetRef hn_targets = NULL; 219 220 221static dispatch_queue_t 222_hn_changes_queue() 223{ 224 static dispatch_once_t once; 225 static dispatch_queue_t q = NULL; 226 227 dispatch_once(&once, ^{ 228 q = dispatch_queue_create("SCNetworkReachability.handleChanges", NULL); 229 }); 230 231 return q; 232} 233 234 235static dispatch_queue_t 236_hn_target_queue() 237{ 238 static dispatch_once_t once; 239 static dispatch_queue_t q; 240 241 dispatch_once(&once, ^{ 242 q = dispatch_queue_create("SCNetworkReachability.targetManagement", NULL); 243 }); 244 245 return q; 246} 247 248 249/* 250 * DNS configuration 251 */ 252 253typedef struct { 254 dns_config_t *config; 255 int refs; 256} dns_configuration_t; 257 258 259// Note: protected by "dns_lock" 260static pthread_mutex_t dns_lock = PTHREAD_MUTEX_INITIALIZER; 261static dns_configuration_t *dns_configuration = NULL; 262static int dns_token; 263static Boolean dns_token_valid = FALSE; 264 265 266 267 268typedef enum { 269 dns_query_async, 270 dns_query_mdns, 271 dns_query_mdns_timeout, 272} query_type; 273 274 275static void 276__mark_operation_start(struct timeval *queryStart, 277 struct timeval *queryEnd) 278{ 279 (void) gettimeofday(queryStart, NULL); 280 *queryEnd = TIME_ZERO; 281 282 return; 283} 284 285 286static void 287__mark_operation_end(SCNetworkReachabilityRef target, 288 Boolean found, 289 query_type query_type, 290 struct timeval *queryStart, 291 struct timeval *queryEnd) 292{ 293 struct timeval queryElapsed; 294 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 295 296 (void) gettimeofday(queryEnd, NULL); 297 298 if (!_sc_debug && 299 (query_type != dns_query_mdns_timeout)) { 300 return; 301 } 302 303 if (!timerisset(queryStart)) { 304 return; 305 } 306 307 timersub(queryEnd, queryStart, &queryElapsed); 308 switch (query_type) { 309 310 #define QUERY_TIME__FMT "%ld.%6.6d" 311 #define QUERY_TIME__DIV 1 312 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", 318 queryElapsed.tv_sec, 319 queryElapsed.tv_usec / QUERY_TIME__DIV); 320 break; 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", 326 queryElapsed.tv_sec, 327 queryElapsed.tv_usec / QUERY_TIME__DIV, 328 DNS_FLAGS_VALUES(targetPrivate)); 329 break; 330 case dns_query_mdns_timeout : 331 SCLog(TRUE, LOG_ERR, 332 CFSTR("%s[m]DNS query timeout (query time = " QUERY_TIME__FMT "), " DNS_FLAGS_FORMAT), 333 targetPrivate->log_prefix, 334 queryElapsed.tv_sec, 335 queryElapsed.tv_usec / QUERY_TIME__DIV, 336 DNS_FLAGS_VALUES(targetPrivate)); 337 break; 338 } 339 340 return; 341} 342 343 344static __inline__ Boolean 345__reach_changed(ReachabilityInfo *r1, ReachabilityInfo *r2) 346{ 347 if (r1->flags != r2->flags) { 348 // if the reachability flags changed 349 return TRUE; 350 } 351 352 if (r1->if_index != r2->if_index) { 353 // if the target interface changed 354 return TRUE; 355 } 356 357 if ((r1->sleeping != r2->sleeping) && !r2->sleeping) { 358 // if our sleep/wake status changed and if we 359 // are no longer sleeping 360 return TRUE; 361 } 362 363 return FALSE; 364} 365 366 367static __inline__ void 368_reach_set(ReachabilityInfo *dst, 369 const ReachabilityInfo *src, 370 uint64_t cycle, 371 unsigned int requested_if_index, 372 const char *requested_if_name) 373{ 374 memcpy(dst, src, sizeof(ReachabilityInfo)); 375 dst->cycle = cycle; 376 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)); 384 } else { 385 dst->if_name[0] = '\0'; 386 } 387 } 388 389 return; 390} 391 392 393#pragma mark - 394#pragma mark SCDynamicStore info 395 396 397typedef struct { 398 SCDynamicStoreRef store; 399 CFStringRef entity; 400 CFDictionaryRef dict; 401 CFIndex n; 402 const void ** keys; 403 const void * keys_q[N_QUICK]; 404 const void ** values; 405 const void * values_q[N_QUICK]; 406} ReachabilityStoreInfo, *ReachabilityStoreInfoRef; 407 408 409static ReachabilityStoreInfo S_storeInfo = { 0 }; 410static Boolean S_storeInfoActive = FALSE; 411 412 413static dispatch_queue_t 414_storeInfo_queue() 415{ 416 static dispatch_once_t once; 417 static dispatch_queue_t q; 418 419 dispatch_once(&once, ^{ 420 q = dispatch_queue_create("SCNetworkReachability.storeInfo", NULL); 421 }); 422 423 return q; 424} 425 426 427static void 428ReachabilityStoreInfo_copy(ReachabilityStoreInfoRef src, 429 ReachabilityStoreInfoRef dst) 430{ 431 if (src->dict != NULL) { 432 dst->store = src->store; 433 CFRetain(dst->store); 434 435 dst->dict = src->dict; 436 CFRetain(dst->dict); 437 438 dst->n = src->n; 439 if (dst->n > 0) { 440 if (dst->n <= (CFIndex)(sizeof(dst->keys_q) / sizeof(CFTypeRef))) { 441 dst->keys = dst->keys_q; 442 dst->values = dst->values_q; 443 } else { 444 dst->keys = CFAllocatorAllocate(NULL, dst->n * sizeof(CFTypeRef), 0); 445 dst->values = CFAllocatorAllocate(NULL, dst->n * sizeof(CFTypeRef), 0); 446 } 447 memcpy(dst->keys, src->keys, dst->n * sizeof(CFTypeRef)); 448 memcpy(dst->values, src->values, dst->n * sizeof(CFTypeRef)); 449 } 450 } 451 452 return; 453} 454 455 456static void 457ReachabilityStoreInfo_enable(Boolean enable) 458{ 459 dispatch_sync(_storeInfo_queue(), ^{ 460 S_storeInfoActive = enable; 461 }); 462 463 return; 464} 465 466 467static void 468ReachabilityStoreInfo_free(ReachabilityStoreInfoRef store_info) 469{ 470 if ((store_info->n > 0) && (store_info->keys != store_info->keys_q)) { 471 CFAllocatorDeallocate(NULL, store_info->keys); 472 store_info->keys = NULL; 473 474 CFAllocatorDeallocate(NULL, store_info->values); 475 store_info->values = NULL; 476 } 477 store_info->n = 0; 478 479 if (store_info->dict != NULL) { 480 CFRelease(store_info->dict); 481 store_info->dict = NULL; 482 } 483 484 if (store_info->store != NULL) { 485 CFRelease(store_info->store); 486 store_info->store = NULL; 487 } 488 489 return; 490} 491 492 493static void 494ReachabilityStoreInfo_init(ReachabilityStoreInfoRef store_info) 495{ 496 dispatch_sync(_storeInfo_queue(), ^{ 497 bzero(store_info, sizeof(ReachabilityStoreInfo)); 498 499 if (S_storeInfoActive && (S_storeInfo.dict != NULL)) { 500 ReachabilityStoreInfo_copy(&S_storeInfo, store_info); 501 } 502 }); 503 504 return; 505} 506 507 508static void 509ReachabilityStoreInfo_save(ReachabilityStoreInfoRef store_info) 510{ 511 dispatch_sync(_storeInfo_queue(), ^{ 512 if ((store_info == NULL) || 513 !_SC_CFEqual(store_info->dict, S_storeInfo.dict)) { 514 // free any old info 515 ReachabilityStoreInfo_free(&S_storeInfo); 516 517 // save new info 518 if (S_storeInfoActive && 519 (store_info != NULL) && 520 (store_info->dict != NULL)) { 521 ReachabilityStoreInfo_copy(store_info, &S_storeInfo); 522 } 523 } 524 }); 525 526 return; 527} 528 529 530static void 531ReachabilityStoreInfo_keys(CFMutableArrayRef *fill_keys, CFMutableArrayRef *fill_patterns) 532{ 533 CFStringRef key; 534 CFMutableArrayRef keys; 535 CFStringRef pattern; 536 CFMutableArrayRef patterns; 537 538 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 539 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 540 541 // get info for IPv4 services 542 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 543 kSCDynamicStoreDomainState, 544 kSCEntNetIPv4); 545 CFArrayAppendValue(keys, key); 546 CFRelease(key); 547 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 548 kSCDynamicStoreDomainSetup, 549 kSCCompAnyRegex, 550 kSCEntNetIPv4); 551 CFArrayAppendValue(patterns, pattern); 552 CFRelease(pattern); 553 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 554 kSCDynamicStoreDomainState, 555 kSCCompAnyRegex, 556 kSCEntNetIPv4); 557 CFArrayAppendValue(patterns, pattern); 558 CFRelease(pattern); 559 560 // get info for IPv6 services 561 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 562 kSCDynamicStoreDomainState, 563 kSCEntNetIPv6); 564 CFArrayAppendValue(keys, key); 565 CFRelease(key); 566 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 567 kSCDynamicStoreDomainSetup, 568 kSCCompAnyRegex, 569 kSCEntNetIPv6); 570 CFArrayAppendValue(patterns, pattern); 571 CFRelease(pattern); 572 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 573 kSCDynamicStoreDomainState, 574 kSCCompAnyRegex, 575 kSCEntNetIPv6); 576 CFArrayAppendValue(patterns, pattern); 577 CFRelease(pattern); 578 579 // get info for PPP services 580 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 581 kSCDynamicStoreDomainSetup, 582 kSCCompAnyRegex, 583 kSCEntNetPPP); 584 CFArrayAppendValue(patterns, pattern); 585 CFRelease(pattern); 586 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 587 kSCDynamicStoreDomainState, 588 kSCCompAnyRegex, 589 kSCEntNetPPP); 590 CFArrayAppendValue(patterns, pattern); 591 CFRelease(pattern); 592 593 // get info for VPN services 594 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 595 kSCDynamicStoreDomainSetup, 596 kSCCompAnyRegex, 597 kSCEntNetVPN); 598 CFArrayAppendValue(patterns, pattern); 599 CFRelease(pattern); 600 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 601 kSCDynamicStoreDomainState, 602 kSCCompAnyRegex, 603 kSCEntNetVPN); 604 CFArrayAppendValue(patterns, pattern); 605 CFRelease(pattern); 606 607 // get info for IPSec services 608// pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 609// kSCDynamicStoreDomainSetup, 610// kSCCompAnyRegex, 611// kSCEntNetIPSec); 612// CFArrayAppendValue(patterns, pattern); 613// CFRelease(pattern); 614 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 615 kSCDynamicStoreDomainState, 616 kSCCompAnyRegex, 617 kSCEntNetIPSec); 618 CFArrayAppendValue(patterns, pattern); 619 CFRelease(pattern); 620 621 // get info to identify "available" services 622 pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 623 kSCDynamicStoreDomainSetup, 624 kSCCompAnyRegex, 625 kSCEntNetInterface); 626 CFArrayAppendValue(patterns, pattern); 627 CFRelease(pattern); 628 629 630 *fill_keys = keys; 631 *fill_patterns = patterns; 632 return; 633} 634 635 636static Boolean 637ReachabilityStoreInfo_fill(ReachabilityStoreInfoRef store_info) 638{ 639 CFMutableArrayRef keys; 640 CFMutableArrayRef patterns; 641 642 // get the SCDynamicStore info 643 ReachabilityStoreInfo_keys(&keys, &patterns); 644 store_info->dict = SCDynamicStoreCopyMultiple(store_info->store, keys, patterns); 645 CFRelease(keys); 646 CFRelease(patterns); 647 if (store_info->dict == NULL) { 648 return FALSE; 649 } 650 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; 657 } else { 658 store_info->keys = CFAllocatorAllocate(NULL, store_info->n * sizeof(CFTypeRef), 0); 659 store_info->values = CFAllocatorAllocate(NULL, store_info->n * sizeof(CFTypeRef), 0); 660 } 661 CFDictionaryGetKeysAndValues(store_info->dict, 662 store_info->keys, 663 store_info->values); 664 } 665 666 return TRUE; 667} 668 669 670static Boolean 671ReachabilityStoreInfo_update(ReachabilityStoreInfoRef store_info, 672 SCDynamicStoreRef *storeP, 673 sa_family_t sa_family) 674{ 675 __block Boolean ok = TRUE; 676 677 switch (sa_family) { 678 case AF_UNSPEC : 679 store_info->entity = NULL; 680 break; 681 case AF_INET : 682 store_info->entity = kSCEntNetIPv4; 683 break; 684 case AF_INET6 : 685 store_info->entity = kSCEntNetIPv6; 686 break; 687 default : 688 return FALSE; 689 } 690 691 if (store_info->dict != NULL) { 692 // if info already available 693 return TRUE; 694 } 695 696 dispatch_sync(_storeInfo_queue(), ^{ 697 if (S_storeInfoActive && (S_storeInfo.dict != NULL)) { 698 // free any info 699 ReachabilityStoreInfo_free(store_info); 700 701 // copy the shared/available info 702 ReachabilityStoreInfo_copy(&S_storeInfo, store_info); 703 } 704 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); 710 } else { 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")); 714 return; 715 } 716 717 if (storeP != NULL) { 718 // and pass back a reference 719 *storeP = store_info->store; 720 CFRetain(*storeP); 721 } 722 } 723 } 724 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 729 return; 730 } 731 732 if (store_info->dict != NULL) { 733 // or we have picked up the shared info 734 return; 735 } 736 737 ok = ReachabilityStoreInfo_fill(store_info); 738 if (!ok) { 739 return; 740 } 741 742 if (!_SC_CFEqual(store_info->dict, S_storeInfo.dict)) { 743 // free any old info 744 ReachabilityStoreInfo_free(&S_storeInfo); 745 746 // save new info 747 if (S_storeInfoActive && 748 (store_info->dict != NULL)) { 749 ReachabilityStoreInfo_copy(store_info, &S_storeInfo); 750 } 751 } 752 }); 753 754 return ok; 755} 756 757 758#pragma mark - 759#pragma mark Reachability engine 760 761 762#define ROUNDUP(a, size) \ 763 (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a)) 764 765#define NEXT_SA(ap) (ap) = (struct sockaddr *) \ 766 ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\ 767 sizeof(uint32_t)) :\ 768 sizeof(uint32_t))) 769 770static void 771get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 772{ 773 int i; 774 775 for (i = 0; i < RTAX_MAX; i++) { 776 if (addrs & (1 << i)) { 777 rti_info[i] = sa; 778 NEXT_SA(sa); 779 } else 780 rti_info[i] = NULL; 781 } 782} 783 784 785#define BUFLEN (sizeof(struct rt_msghdr) + 512) /* 8 * sizeof(struct sockaddr_in6) = 192 */ 786 787 788typedef struct { 789 union { 790 char bytes[BUFLEN]; 791 struct rt_msghdr rtm; 792 } buf; 793 int error; 794 struct sockaddr *rti_info[RTAX_MAX]; 795 struct rt_msghdr *rtm; 796 struct sockaddr_dl *sdl; 797} route_info, *route_info_p; 798 799 800/* 801 * route_get() 802 * returns zero if route exists and data returned, EHOSTUNREACH 803 * if no route, or errno for any other error. 804 */ 805static int 806route_get(const struct sockaddr *address, 807 unsigned int if_index, 808 route_info *info) 809{ 810 int n; 811 int opt; 812 pid_t pid = getpid(); 813 int rsock; 814 struct sockaddr *sa; 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; 820#endif 821 822 bzero(info, sizeof(*info)); 823 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; 829#else 830 info->rtm->rtm_type = RTM_GET; 831#endif 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; 836 837 if (if_index != 0) { 838 info->rtm->rtm_flags |= RTF_IFSCOPE; 839 info->rtm->rtm_index = if_index; 840 } 841 842 switch (address->sa_family) { 843 case AF_INET6: { 844 struct sockaddr_in6 *sin6; 845 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; 853 } 854 break; 855 } 856 } 857 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; 862 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; 868 869#ifndef RTM_GET_SILENT 870 pthread_mutex_lock(&lock); 871#endif 872 rsock = socket(PF_ROUTE, SOCK_RAW, PF_ROUTE); 873 if (rsock == -1) { 874 int error = errno; 875 876#ifndef RTM_GET_SILENT 877 pthread_mutex_unlock(&lock); 878#endif 879 SCLog(TRUE, LOG_ERR, CFSTR("socket(PF_ROUTE) failed: %s"), strerror(error)); 880 return error; 881 } 882 opt = 1; 883 if (ioctl(rsock, FIONBIO, &opt) < 0) { 884 int error = errno; 885 886 (void)close(rsock); 887#ifndef RTM_GET_SILENT 888 pthread_mutex_unlock(&lock); 889#endif 890 SCLog(TRUE, LOG_ERR, CFSTR("ioctl(FIONBIO) failed: %s"), strerror(error)); 891 return error; 892 } 893 894#ifndef RTM_GET_SILENT 895 if (setsockopt(rsock, SOL_SOCKET, SO_RCVBUF, &sosize, sizeof(sosize)) == -1) { 896 int error = errno; 897 898 (void)close(rsock); 899 pthread_mutex_unlock(&lock); 900 SCLog(TRUE, LOG_ERR, CFSTR("setsockopt(SO_RCVBUF) failed: %s"), strerror(error)); 901 return error; 902 } 903#endif 904 905 if (write(rsock, &info->buf, info->rtm->rtm_msglen) == -1) { 906 int error = errno; 907 908 (void)close(rsock); 909#ifndef RTM_GET_SILENT 910 pthread_mutex_unlock(&lock); 911#endif 912 if (error != ESRCH) { 913 SCLog(TRUE, LOG_ERR, CFSTR("write() failed: %s"), strerror(error)); 914 return error; 915 } 916 return EHOSTUNREACH; 917 } 918 919 /* 920 * Type, seq, pid identify our response. 921 * Routing sockets are broadcasters on input. 922 */ 923 while (TRUE) { 924 ssize_t n; 925 926 n = read(rsock, &info->buf, sizeof(info->buf)); 927 if (n == -1) { 928 int error = errno; 929 930 if (error == EINTR) { 931 continue; 932 } 933 (void)close(rsock); 934#ifndef RTM_GET_SILENT 935 pthread_mutex_unlock(&lock); 936#endif 937 SCLog(TRUE, LOG_ERR, 938 CFSTR("SCNetworkReachability: routing socket" 939 " read() failed: %s"), strerror(error)); 940 return error; 941 } 942 if ((info->rtm->rtm_type == RTM_GET) && 943 (info->rtm->rtm_seq == seq) && 944 (info->rtm->rtm_pid == pid)) { 945 break; 946 } 947 } 948 949 (void)close(rsock); 950#ifndef RTM_GET_SILENT 951 pthread_mutex_unlock(&lock); 952#endif 953 954 get_rtaddrs(info->rtm->rtm_addrs, sa, info->rti_info); 955 956//#define LOG_RTADDRS 957#ifdef LOG_RTADDRS 958 { 959 int i; 960 961 SCLog(_sc_debug, LOG_DEBUG, CFSTR("rtm_flags = 0x%8.8x"), info->rtm->rtm_flags); 962 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; 965 } 966 967 for (i = 0; i < RTAX_MAX; i++) { 968 if (info->rti_info[i] != NULL) { 969 char addr[128]; 970 971 _SC_sockaddr_to_string(info->rti_info[i], addr, sizeof(addr)); 972 SCLog(_sc_debug, LOG_DEBUG, CFSTR("%d: %s"), i, addr); 973 } 974 } 975 } 976#endif // LOG_RTADDRS 977 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")); 982 return EINVAL; 983 } 984 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 */ 989 return EHOSTUNREACH; 990 } 991 992 return 0; 993} 994 995 996static void 997log_address(const char *str, 998 const struct sockaddr *sa, 999 unsigned int if_index, 1000 const char *log_prefix) 1001{ 1002 char addr[128]; 1003 char if_name[IFNAMSIZ + 1]; 1004 1005 _SC_sockaddr_to_string(sa, addr, sizeof(addr)); 1006 1007 if ((if_index != 0) && 1008 (if_indextoname(if_index, &if_name[1]) != NULL)) { 1009 if_name[0] = '%'; 1010 } else { 1011 if_name[0] = '\0'; 1012 } 1013 1014 SCLog(TRUE, LOG_INFO, CFSTR("%s%s(%s%s)"), 1015 log_prefix, 1016 str, 1017 addr, 1018 if_name); 1019 1020 return; 1021} 1022 1023 1024static int 1025checkAddress_route(const struct sockaddr *address, 1026 unsigned int if_index, 1027 char *if_name, 1028 struct ifreq *ifr, 1029 ReachabilityInfo *reach_info, 1030 route_info *info, 1031 int *sc_status, 1032 const char *log_prefix) 1033{ 1034 int isock = -1; 1035 int ret = 0; 1036 char *statusMessage = NULL; 1037 struct sockaddr_in v4mapped; 1038 1039 switch (address->sa_family) { 1040 case AF_INET : 1041 case AF_INET6 : 1042 if (_sc_debug) { 1043 log_address("checkAddress", address, if_index, log_prefix); 1044 } 1045 break; 1046 default : 1047 /* 1048 * if no code for this address family (yet) 1049 */ 1050 SCLog(TRUE, LOG_INFO, 1051 CFSTR("checkAddress(): unexpected address family %d"), 1052 address->sa_family); 1053 *sc_status = kSCStatusInvalidArgument; 1054 ret = EPERM; 1055 goto done; 1056 } 1057 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; 1061 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; 1069 } 1070 } 1071 1072 ret = route_get(address, if_index, info); 1073 switch (ret) { 1074 case 0 : 1075 break; 1076 case EHOSTUNREACH : 1077 // if no route 1078 goto done; 1079 default : 1080 // if error 1081 *sc_status = ret; 1082 goto done; 1083 } 1084 1085 /* get the interface flags */ 1086 1087 isock = socket(AF_INET, SOCK_DGRAM, 0); 1088 if (isock == -1) { 1089 ret = errno; 1090 SCLog(TRUE, LOG_ERR, CFSTR("socket() failed: %s"), strerror(errno)); 1091 goto done; 1092 } 1093 1094 bzero(ifr, sizeof(*ifr)); 1095 bcopy(info->sdl->sdl_data, ifr->ifr_name, info->sdl->sdl_nlen); 1096 1097 if (ioctl(isock, SIOCGIFFLAGS, (char *)ifr) == -1) { 1098 ret = errno; 1099 SCLog(TRUE, LOG_ERR, CFSTR("ioctl(SIOCGIFFLAGS) failed: %s"), strerror(errno)); 1100 goto done; 1101 } 1102 1103 if (!(ifr->ifr_flags & IFF_UP)) { 1104 ret = EHOSTUNREACH; 1105 goto done; 1106 } 1107 1108 statusMessage = "isReachable"; 1109 reach_info->flags |= kSCNetworkReachabilityFlagsReachable; 1110 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; 1122 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"), 1127 address->sa_family, 1128 address->sa_len, 1129 info->rti_info[RTAX_IFA]->sa_family, 1130 info->rti_info[RTAX_IFA]->sa_len); 1131 goto done; 1132 } 1133 1134 switch (address->sa_family) { 1135 case AF_INET : 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); 1140 1141 /* 1142 * check if 0.0.0.0 1143 */ 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; 1148 } 1149 break; 1150 case AF_INET6 : 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); 1155 break; 1156 default : 1157 break; 1158 } 1159 1160 if (bcmp(addr1, addr2, len) == 0) { 1161 statusMessage = "isReachable (is interface address)"; 1162 reach_info->flags |= kSCNetworkReachabilityFlagsIsLocalAddress; 1163 } 1164 } 1165 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; 1171 } 1172 1173 bzero(if_name, IFNAMSIZ); 1174 bcopy(info->sdl->sdl_data, 1175 if_name, 1176 (info->sdl->sdl_nlen <= IFNAMSIZ) ? info->sdl->sdl_nlen : IFNAMSIZ); 1177 1178 strlcpy(reach_info->if_name, if_name, sizeof(reach_info->if_name)); 1179 reach_info->if_index = info->sdl->sdl_index; 1180 1181 if (_sc_debug) { 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); 1187 } 1188 1189 done : 1190 if (isock != -1) (void)close(isock); 1191 return ret; 1192} 1193 1194 1195static Boolean 1196checkAddress(ReachabilityStoreInfoRef store_info, 1197 const struct sockaddr *address, 1198 unsigned int if_index, 1199 ReachabilityInfo *reach_info, 1200 const char *log_prefix) 1201{ 1202 route_info info; 1203 struct ifreq ifr; 1204 char if_name[IFNAMSIZ]; 1205 nwi_ifstate_t ifstate; 1206 nwi_state_t nwi_state; 1207 int ret; 1208 int sc_status = kSCStatusReachabilityUnknown; 1209 1210 _reach_set(reach_info, &NOT_REACHABLE, reach_info->cycle, if_index, NULL); 1211 1212 nwi_state = nwi_state_copy(); 1213 1214 if (address != NULL) { 1215 ret = checkAddress_route(address, 1216 if_index, 1217 if_name, 1218 &ifr, 1219 reach_info, 1220 &info, 1221 &sc_status, 1222 log_prefix); 1223 } else { 1224 /* special case: check only for available paths off the system */ 1225 ret = EHOSTUNREACH; 1226 } 1227 1228 if (ret == 0) { 1229 const struct sockaddr *vpn_server_address; 1230 1231 sc_status = kSCStatusOK; 1232 1233 ifstate = nwi_state_get_ifstate(nwi_state, if_name); 1234 if (ifstate == NULL) { 1235 goto done; 1236 } 1237 1238 reach_info->flags |= nwi_ifstate_get_reachability_flags(ifstate); 1239 1240 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; 1245 1246 ret = route_get(vpn_server_address, 0, &dst_info); 1247 if (ret != 0) { 1248 goto done; 1249 } 1250 1251 bzero(&dst_if_name, sizeof(dst_if_name)); 1252 bcopy(dst_info.sdl->sdl_data, 1253 dst_if_name, 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; 1257 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); 1261 } 1262 } 1263 } 1264 } else if (ret == EHOSTUNREACH) { 1265 if (if_index == 0) { 1266 int af; 1267 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; 1272 } else { 1273 // if "scoped" request 1274 sc_status = kSCStatusNoKey; 1275 } 1276 } 1277 1278 done: 1279 1280 if (reach_info->flags == 0) { 1281 SCLog(_sc_debug, LOG_INFO, CFSTR("%s cannot be reached"), log_prefix); 1282 } 1283 1284 if (nwi_state != NULL) { 1285 nwi_state_release(nwi_state); 1286 } 1287 1288 if ((sc_status != kSCStatusOK) && (sc_status != kSCStatusNoKey)) { 1289 _SCErrorSet(sc_status); 1290 return FALSE; 1291 } 1292 1293 return TRUE; 1294} 1295 1296 1297#pragma mark - 1298#pragma mark SCNetworkReachability APIs 1299 1300 1301static __inline__ CFTypeRef 1302isA_SCNetworkReachability(CFTypeRef obj) 1303{ 1304 return (isA_CFType(obj, SCNetworkReachabilityGetTypeID())); 1305} 1306 1307 1308static Boolean 1309addr_to_PTR_name(const struct sockaddr *sa, char *name, size_t name_len) 1310{ 1311 int n; 1312 1313 switch (sa->sa_family) { 1314 case AF_INET : { 1315 union { 1316 in_addr_t s_addr; 1317 unsigned char b[4]; 1318 } rev; 1319 /* ALIGN: assuming sa is aligned, then cast ok. */ 1320 struct sockaddr_in *sin = (struct sockaddr_in *)(void *)sa; 1321 1322 /* 1323 * build "PTR" query name 1324 * NNN.NNN.NNN.NNN.in-addr.arpa. 1325 */ 1326 rev.s_addr = sin->sin_addr.s_addr; 1327 n = snprintf(name, name_len, "%u.%u.%u.%u.in-addr.arpa.", 1328 rev.b[3], 1329 rev.b[2], 1330 rev.b[1], 1331 rev.b[0]); 1332 if ((n == -1) || (n >= name_len)) { 1333 return FALSE; 1334 } 1335 1336 break; 1337 } 1338 1339 case AF_INET6 : { 1340 int i; 1341 int s = 0; 1342 /* ALIGN: assume sa is aligned, cast ok. */ 1343 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(void *)sa; 1344 size_t x = name_len; 1345 1346 /* 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. 1349 */ 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)) { 1355 return FALSE; 1356 } 1357 1358 s += n; 1359 x -= n; 1360 } 1361 1362 n = snprintf(&name[s], x, "ip6.arpa."); 1363 if ((n == -1) || (n >= x)) { 1364 return FALSE; 1365 } 1366 1367 break; 1368 } 1369 1370 default : 1371 return FALSE; 1372 } 1373 1374 return TRUE; 1375} 1376 1377 1378CFStringRef 1379_SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target) 1380{ 1381 CFAllocatorRef allocator = CFGetAllocator(target); 1382 CFMutableStringRef str; 1383 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1384 1385 str = CFStringCreateMutable(allocator, 0); 1386 switch (targetPrivate->type) { 1387 case reachabilityTypeAddress : 1388 case reachabilityTypeAddressPair : { 1389 char buf[64]; 1390 1391 if (targetPrivate->localAddress != NULL) { 1392 _SC_sockaddr_to_string(targetPrivate->localAddress, buf, sizeof(buf)); 1393 CFStringAppendFormat(str, NULL, CFSTR("local address = %s"), 1394 buf); 1395 } 1396 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 " : "", 1402 buf); 1403 } 1404 break; 1405 } 1406 case reachabilityTypeName : { 1407 CFStringAppendFormat(str, NULL, CFSTR("name = %s"), targetPrivate->name); 1408 break; 1409 } 1410 case reachabilityTypePTR : { 1411 CFStringAppendFormat(str, NULL, CFSTR("ptr = %s"), targetPrivate->name); 1412 break; 1413 } 1414 } 1415 1416 return str; 1417} 1418 1419 1420CFStringRef 1421_SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target) 1422{ 1423 CFAllocatorRef allocator = CFGetAllocator(target); 1424 CFStringRef str; 1425 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1426 1427 str = CFStringCreateWithFormat(allocator, 1428 NULL, 1429 CFSTR("flags = 0x%08x, if_index = %u%s"), 1430 targetPrivate->info.flags, 1431 targetPrivate->info.if_index, 1432 targetPrivate->info.sleeping ? ", z" : ""); 1433 return str; 1434} 1435 1436 1437static CFStringRef 1438__SCNetworkReachabilityCopyDescription(CFTypeRef cf) 1439{ 1440 CFAllocatorRef allocator = CFGetAllocator(cf); 1441 CFMutableStringRef result; 1442 CFStringRef str; 1443 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf; 1444 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1445 1446 result = CFStringCreateMutable(allocator, 0); 1447 CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkReachability %p [%p]> {"), cf, allocator); 1448 1449 // add target description 1450 str = _SCNetworkReachabilityCopyTargetDescription(target); 1451 CFStringAppend(result, str); 1452 CFRelease(str); 1453 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)) { 1464 CFIndex i; 1465 CFIndex n = CFArrayGetCount(targetPrivate->resolvedAddresses); 1466 1467 CFStringAppendFormat(result, NULL, CFSTR(" (")); 1468 for (i = 0; i < n; i++) { 1469 CFDataRef address; 1470 1471 CFStringAppendFormat(result, NULL, CFSTR("%s"), 1472 i > 0 ? ", " : ""); 1473 1474 address = CFArrayGetValueAtIndex(targetPrivate->resolvedAddresses, i); 1475 if (isA_CFData(address)) { 1476 char buf[64]; 1477 struct sockaddr *sa; 1478 1479 sa = (struct sockaddr *)CFDataGetBytePtr(address); 1480 _SC_sockaddr_to_string(sa, buf, sizeof(buf)); 1481 CFStringAppendFormat(result, NULL, CFSTR("%s"), buf); 1482 } else { 1483 CFStringAppendFormat(result, NULL, CFSTR("%@"), address); 1484 } 1485 } 1486 CFStringAppendFormat(result, NULL, CFSTR(")")); 1487 } else if (CFEqual(targetPrivate->resolvedAddresses, kCFNull)) { 1488 CFStringAppendFormat(result, NULL, CFSTR(" (%s)"), 1489 gai_strerror(targetPrivate->resolvedError)); 1490 } else { 1491 CFStringAppendFormat(result, NULL, CFSTR(" (no addresses)")); 1492 } 1493 } else { 1494 CFStringAppendFormat(result, NULL, CFSTR(" (%s)"), 1495 gai_strerror(targetPrivate->resolvedError)); 1496 } 1497 } 1498 if (targetPrivate->dnsFlags != 0) { 1499 CFStringAppendFormat(result, NULL, CFSTR(", " DNS_FLAGS_FORMAT), 1500 DNS_FLAGS_VALUES(targetPrivate)); 1501 } 1502 } 1503 1504 if (targetPrivate->onDemandBypass) { 1505 CFStringAppendFormat(result, NULL, CFSTR(", !ondemand")); 1506 } 1507 1508 1509 if (targetPrivate->resolverBypass) { 1510 CFStringAppendFormat(result, NULL, CFSTR(", !resolve")); 1511 } 1512 1513 1514 // add flags 1515 if (targetPrivate->scheduled) { 1516 str = _SCNetworkReachabilityCopyTargetFlags(target); 1517 CFStringAppendFormat(result, NULL, CFSTR(", %@"), str); 1518 CFRelease(str); 1519 } 1520 1521 CFStringAppendFormat(result, NULL, CFSTR("}")); 1522 1523 return result; 1524} 1525 1526 1527static void 1528__SCNetworkReachabilityDeallocate(CFTypeRef cf) 1529{ 1530 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf; 1531 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1532 1533 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%srelease"), 1534 targetPrivate->log_prefix); 1535 1536 /* disconnect from the reachability server */ 1537 1538 if (targetPrivate->serverActive) { 1539 __SCNetworkReachabilityServer_targetRemove(target); 1540 } 1541 1542 /* release resources */ 1543 1544 pthread_mutex_destroy(&targetPrivate->lock); 1545 1546 if (targetPrivate->name != NULL) 1547 CFAllocatorDeallocate(NULL, (void *)targetPrivate->name); 1548 1549 if (targetPrivate->resolvedAddresses != NULL) 1550 CFRelease(targetPrivate->resolvedAddresses); 1551 1552 if (targetPrivate->localAddress != NULL) { 1553 if (targetPrivate->localAddress == targetPrivate->remoteAddress) { 1554 targetPrivate->remoteAddress = NULL; 1555 } 1556 CFAllocatorDeallocate(NULL, (void *)targetPrivate->localAddress); 1557 } 1558 1559 if (targetPrivate->remoteAddress != NULL) 1560 CFAllocatorDeallocate(NULL, (void *)targetPrivate->remoteAddress); 1561 1562 if (targetPrivate->rlsContext.release != NULL) { 1563 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info); 1564 } 1565 1566 if (targetPrivate->onDemandName != NULL) { 1567 CFRelease(targetPrivate->onDemandName); 1568 } 1569 1570 if (targetPrivate->onDemandRemoteAddress != NULL) { 1571 CFRelease(targetPrivate->onDemandRemoteAddress); 1572 } 1573 1574 if (targetPrivate->onDemandServer != NULL) { 1575 CFRelease(targetPrivate->onDemandServer); 1576 } 1577 1578 if (targetPrivate->onDemandServiceID != NULL) { 1579 CFRelease(targetPrivate->onDemandServiceID); 1580 } 1581 1582 if (targetPrivate->serverDigest != NULL) { 1583 CFRelease(targetPrivate->serverDigest); 1584 } 1585 1586 if (targetPrivate->serverGroup != NULL) { 1587 dispatch_release(targetPrivate->serverGroup); 1588 } 1589 1590 if (targetPrivate->serverQueue != NULL) { 1591 dispatch_release(targetPrivate->serverQueue); 1592 } 1593 1594 if (targetPrivate->serverWatchers != NULL) { 1595 CFRelease(targetPrivate->serverWatchers); 1596 } 1597 1598 if (targetPrivate->nePolicyResult) { 1599 free(targetPrivate->nePolicyResult); 1600 } 1601 1602 return; 1603} 1604 1605 1606static void 1607__SCNetworkReachabilityInitialize(void) 1608{ 1609 __kSCNetworkReachabilityTypeID = _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass); 1610 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, 1616 NULL))) { 1617 _sc_debug = TRUE; 1618 } 1619 1620 // set per-process "bypass" of the SCNetworkReachability server 1621 if (getenv("REACH_SERVER_BYPASS") != NULL) { 1622 D_serverBypass = TRUE; 1623 } 1624 1625 1626 pthread_mutexattr_init(&lock_attr); 1627 pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_ERRORCHECK); 1628 1629 return; 1630} 1631 1632 1633__private_extern__ 1634dispatch_queue_t 1635__SCNetworkReachability_concurrent_queue() 1636{ 1637 static dispatch_once_t once; 1638 static dispatch_queue_t q; 1639 1640 dispatch_once(&once, ^{ 1641 q = dispatch_queue_create("SCNetworkReachability.concurrent", 1642 DISPATCH_QUEUE_CONCURRENT); 1643 }); 1644 1645 return q; 1646} 1647 1648 1649/* 1650 * __SCNetworkReachabilityUpdateConcurrent 1651 * 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() 1656 */ 1657__private_extern__ 1658void 1659__SCNetworkReachabilityUpdateConcurrent(SCNetworkReachabilityRef target) 1660{ 1661 Boolean changed; 1662 unsigned int n; 1663 dispatch_queue_t queue; 1664 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1665 1666 changed = reachUpdate((void *)target); 1667 if (!changed) { 1668 return; 1669 } 1670 1671 n = _SC_ATOMIC_INC(&targetPrivate->pending); 1672 if (n > 0) { 1673 // if we already have a notification pending 1674 return; 1675 } 1676 1677 MUTEX_LOCK(&targetPrivate->lock); 1678 1679 queue = targetPrivate->dispatchQueue; 1680 if (queue != NULL) { 1681 dispatch_group_t group; 1682 1683 dispatch_retain(queue); 1684 1685 group = targetPrivate->dispatchGroup; 1686 dispatch_group_enter(group); 1687 1688 MUTEX_UNLOCK(&targetPrivate->lock); 1689 1690 dispatch_sync(queue, ^{ 1691 reachPerform((void *)target); 1692 dispatch_group_leave(group); 1693 }); 1694 1695 dispatch_release(queue); 1696 } else { 1697 if (targetPrivate->rls != NULL) { 1698 CFRunLoopSourceSignal(targetPrivate->rls); 1699 _SC_signalRunLoop(target, targetPrivate->rls, targetPrivate->rlList); 1700 } 1701 1702 MUTEX_UNLOCK(&targetPrivate->lock); 1703 } 1704 1705 return; 1706} 1707 1708 1709/* 1710 * __SCNetworkReachabilityUpdate 1711 * 1712 * Calls reachUpdate() [indirectly] 1713 * - caller can be holding the target lock 1714 * - caller can be running on any dispatch queue 1715 */ 1716__private_extern__ 1717void 1718__SCNetworkReachabilityUpdate(SCNetworkReachabilityRef target) 1719{ 1720 CFRetain(target); 1721 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{ 1722 __SCNetworkReachabilityUpdateConcurrent(target); 1723 CFRelease(target); 1724 }); 1725 1726 return; 1727} 1728 1729 1730static SCNetworkReachabilityPrivateRef 1731__SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator) 1732{ 1733 SCNetworkReachabilityPrivateRef targetPrivate; 1734 uint32_t size; 1735 1736 /* initialize runtime */ 1737 pthread_once(&initialized, __SCNetworkReachabilityInitialize); 1738 1739 /* allocate target */ 1740 size = sizeof(SCNetworkReachabilityPrivate) - sizeof(CFRuntimeBase); 1741 targetPrivate = (SCNetworkReachabilityPrivateRef)_CFRuntimeCreateInstance(allocator, 1742 __kSCNetworkReachabilityTypeID, 1743 size, 1744 NULL); 1745 if (targetPrivate == NULL) { 1746 return NULL; 1747 } 1748 1749 bzero((void *)targetPrivate + sizeof(CFRuntimeBase), size); 1750 1751 MUTEX_INIT(&targetPrivate->lock); 1752 1753 targetPrivate->cycle = 1; 1754 targetPrivate->last_notify = NOT_REPORTED; 1755 targetPrivate->serverBypass = D_serverBypass; 1756 1757 1758 1759 targetPrivate->log_prefix[0] = '\0'; 1760 if (_sc_log > 0) { 1761 snprintf(targetPrivate->log_prefix, 1762 sizeof(targetPrivate->log_prefix), 1763 "[%p] ", 1764 targetPrivate); 1765 } 1766 1767 return targetPrivate; 1768} 1769 1770 1771 1772 1773static const struct sockaddr * 1774is_valid_address(const struct sockaddr *address) 1775{ 1776 const struct sockaddr *valid = NULL; 1777 static Boolean warned = FALSE; 1778 1779 if ((address != NULL) && 1780 (address->sa_len <= sizeof(struct sockaddr_storage))) { 1781 switch (address->sa_family) { 1782 case AF_INET : 1783 if (address->sa_len >= sizeof(struct sockaddr_in)) { 1784 valid = address; 1785 } else { 1786 if (!warned) { 1787 SCLog(TRUE, LOG_ERR, 1788 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"), 1789 address->sa_len, 1790 sizeof(struct sockaddr_in)); 1791 warned = TRUE; 1792 } 1793 } 1794 break; 1795 case AF_INET6 : 1796 if (address->sa_len >= sizeof(struct sockaddr_in6)) { 1797 valid = address; 1798 } else if (!warned) { 1799 SCLog(TRUE, LOG_ERR, 1800 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu"), 1801 address->sa_len, 1802 sizeof(struct sockaddr_in6)); 1803 warned = TRUE; 1804 } 1805 break; 1806 default : 1807 if (!warned) { 1808 SCLog(TRUE, LOG_ERR, 1809 CFSTR("SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d"), 1810 address->sa_family); 1811 warned = TRUE; 1812 } 1813 } 1814 } 1815 1816 return valid; 1817} 1818 1819 1820 1821 1822 1823 1824SCNetworkReachabilityRef 1825SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator, 1826 const struct sockaddr *address) 1827{ 1828 SCNetworkReachabilityPrivateRef targetPrivate; 1829 1830 address = is_valid_address(address); 1831 if (address == NULL) { 1832 _SCErrorSet(kSCStatusInvalidArgument); 1833 return NULL; 1834 } 1835 1836 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator); 1837 if (targetPrivate == NULL) { 1838 return NULL; 1839 } 1840 1841 targetPrivate->type = reachabilityTypeAddress; 1842 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, address->sa_len, 0); 1843 bcopy(address, targetPrivate->remoteAddress, address->sa_len); 1844 1845 1846 1847 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"), 1848 targetPrivate->log_prefix, 1849 DEBUG_REACHABILITY_TYPE_ADDRESS, 1850 targetPrivate); 1851 1852 return (SCNetworkReachabilityRef)targetPrivate; 1853} 1854 1855 1856static Boolean 1857is_same_address(const struct sockaddr *a, const struct sockaddr *b) 1858{ 1859 const void *a_addr; 1860 const void *b_addr; 1861 size_t len; 1862 1863 if ((a == NULL) || 1864 (b == NULL) || 1865 (a->sa_family != b->sa_family) || 1866 (a->sa_len != b->sa_len )) { 1867 return FALSE; 1868 } 1869 1870 switch (a->sa_family) { 1871 case AF_INET : { 1872 struct sockaddr_in *a_sin = (struct sockaddr_in *)(void *)a; 1873 struct sockaddr_in *b_sin = (struct sockaddr_in *)(void *)b; 1874 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); 1879 break; 1880 } 1881 1882 case AF_INET6 : { 1883 struct sockaddr_in6 *a_sin6 = (struct sockaddr_in6 *)(void *)a; 1884 struct sockaddr_in6 *b_sin6 = (struct sockaddr_in6 *)(void *)b; 1885 1886 if (a_sin6->sin6_scope_id != b_sin6->sin6_scope_id) { 1887 return FALSE; 1888 } 1889 1890 a_addr = &a_sin6->sin6_addr; 1891 b_addr = &b_sin6->sin6_addr; 1892 len = sizeof(struct in6_addr); 1893 break; 1894 } 1895 1896 default : 1897 a_addr = a; 1898 b_addr = b; 1899 len = a->sa_len; 1900 break; 1901 } 1902 1903 return (bcmp(a_addr, b_addr, len) == 0); 1904} 1905 1906 1907SCNetworkReachabilityRef 1908SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator, 1909 const struct sockaddr *localAddress, 1910 const struct sockaddr *remoteAddress) 1911{ 1912 SCNetworkReachabilityPrivateRef targetPrivate; 1913 1914 if ((localAddress == NULL) && (remoteAddress == NULL)) { 1915 _SCErrorSet(kSCStatusInvalidArgument); 1916 return NULL; 1917 } 1918 1919 if (localAddress != NULL) { 1920 localAddress = is_valid_address(localAddress); 1921 if (localAddress == NULL) { 1922 _SCErrorSet(kSCStatusInvalidArgument); 1923 return NULL; 1924 } 1925 } 1926 1927 if (remoteAddress != NULL) { 1928 remoteAddress = is_valid_address(remoteAddress); 1929 if (remoteAddress == NULL) { 1930 _SCErrorSet(kSCStatusInvalidArgument); 1931 return NULL; 1932 } 1933 } 1934 1935 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator); 1936 if (targetPrivate == NULL) { 1937 return NULL; 1938 } 1939 1940 targetPrivate->type = reachabilityTypeAddressPair; 1941 1942 if (localAddress != NULL) { 1943 targetPrivate->localAddress = CFAllocatorAllocate(NULL, localAddress->sa_len, 0); 1944 bcopy(localAddress, targetPrivate->localAddress, localAddress->sa_len); 1945 } 1946 1947 if (remoteAddress != NULL) { 1948 if (is_same_address(localAddress, remoteAddress)) { 1949 targetPrivate->remoteAddress = targetPrivate->localAddress; 1950 } else { 1951 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, remoteAddress->sa_len, 0); 1952 bcopy(remoteAddress, targetPrivate->remoteAddress, remoteAddress->sa_len); 1953 } 1954 } 1955 1956 1957 1958 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"), 1959 targetPrivate->log_prefix, 1960 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR, 1961 targetPrivate); 1962 1963 return (SCNetworkReachabilityRef)targetPrivate; 1964} 1965 1966 1967SCNetworkReachabilityRef 1968SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator, 1969 const char *nodename) 1970{ 1971 union { 1972 struct sockaddr sa; 1973 struct sockaddr_in sin; 1974 struct sockaddr_in6 sin6; 1975 } addr; 1976 size_t nodenameLen; 1977 SCNetworkReachabilityPrivateRef targetPrivate; 1978 1979 if (nodename == NULL) { 1980 _SCErrorSet(kSCStatusInvalidArgument); 1981 return NULL; 1982 } 1983 1984 nodenameLen = strlen(nodename); 1985 if (nodenameLen == 0) { 1986 _SCErrorSet(kSCStatusInvalidArgument); 1987 return NULL; 1988 } 1989 1990 if (nodename[nodenameLen - 1] == '.') { 1991 int dots; 1992 size_t i; 1993 1994 // trim trailing "."s 1995 do { 1996 --nodenameLen; 1997 } while ((nodenameLen > 0) && (nodename[nodenameLen - 1] == '.')); 1998 1999 if (nodenameLen == 0) { 2000 // if only trailing "."s 2001 _SCErrorSet(kSCStatusInvalidArgument); 2002 return NULL; 2003 } 2004 2005 // count the remaining "."s 2006 dots = 0; 2007 for (i = 0; i < nodenameLen; i++) { 2008 if (nodename[i] == '.') dots++; 2009 } 2010 2011 if (dots == 0) { 2012 // if only a single-label, add back the FQDN "." 2013 nodenameLen++; 2014 } 2015 } 2016 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); 2020 } 2021 2022 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator); 2023 if (targetPrivate == NULL) { 2024 return NULL; 2025 } 2026 2027 targetPrivate->type = reachabilityTypeName; 2028 2029 targetPrivate->name = CFAllocatorAllocate(NULL, nodenameLen + 1, 0); 2030 strlcpy((char *)targetPrivate->name, nodename, nodenameLen + 1); 2031 2032 targetPrivate->needResolve = TRUE; 2033 targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending; 2034 targetPrivate->serverInfo.flags |= kSCNetworkReachabilityFlagsFirstResolvePending; 2035 2036 { 2037 /* make sure AppLayerVPN only is in client mode */ 2038 CFDictionaryRef appLayerVPNProperties; 2039 2040 appLayerVPNProperties = VPNAppLayerCopyCurrentAppProperties(); 2041 if (appLayerVPNProperties != NULL) { 2042 targetPrivate->serverBypassForVPN = TRUE; 2043 targetPrivate->serverBypass = YES; 2044 CFRelease(appLayerVPNProperties); 2045 } 2046 } 2047 2048 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%s%s %@"), 2049 targetPrivate->log_prefix, 2050 DEBUG_REACHABILITY_TYPE_NAME, 2051 targetPrivate); 2052 2053 return (SCNetworkReachabilityRef)targetPrivate; 2054} 2055 2056 2057static SCNetworkReachabilityRef 2058__SCNetworkReachabilityCreateWithPtr(CFAllocatorRef allocator, 2059 const char *ptrName, 2060 const struct sockaddr *ptrAddress) 2061{ 2062 SCNetworkReachabilityRef target; 2063 SCNetworkReachabilityPrivateRef targetPrivate; 2064 2065 target = SCNetworkReachabilityCreateWithName(NULL, ptrName); 2066 if (target == NULL) { 2067 return NULL; 2068 } 2069 2070 targetPrivate = (SCNetworkReachabilityPrivateRef)target; 2071 2072 // change type 2073 targetPrivate->type = reachabilityTypePTR; 2074 2075 // and keep the address 2076 targetPrivate->remoteAddress = CFAllocatorAllocate(NULL, ptrAddress->sa_len, 0); 2077 bcopy(ptrAddress, targetPrivate->remoteAddress, ptrAddress->sa_len); 2078 2079 return target; 2080} 2081 2082 2083 2084 2085SCNetworkReachabilityRef 2086SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator, 2087 CFDictionaryRef options) 2088{ 2089 const struct sockaddr *addr_l = NULL; 2090 const struct sockaddr *addr_p = NULL; 2091 const struct sockaddr *addr_r = NULL; 2092 CFDataRef data; 2093 CFStringRef interface = NULL; 2094 CFStringRef nodename; 2095 CFBooleanRef onDemandBypass; 2096 CFBooleanRef resolverBypass; 2097 CFBooleanRef serverBypass; 2098 SCNetworkReachabilityRef target; 2099 SCNetworkReachabilityPrivateRef targetPrivate; 2100 2101 if (!isA_CFDictionary(options)) { 2102 _SCErrorSet(kSCStatusInvalidArgument); 2103 return NULL; 2104 } 2105 2106 nodename = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionNodeName); 2107 if ((nodename != NULL) && 2108 (!isA_CFString(nodename) || (CFStringGetLength(nodename) == 0))) { 2109 _SCErrorSet(kSCStatusInvalidArgument); 2110 return NULL; 2111 } 2112 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionLocalAddress); 2113 if (data != NULL) { 2114 if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) { 2115 _SCErrorSet(kSCStatusInvalidArgument); 2116 return NULL; 2117 } 2118 addr_l = (const struct sockaddr *)CFDataGetBytePtr(data); 2119 } 2120 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionPTRAddress); 2121 if (data != NULL) { 2122 if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) { 2123 _SCErrorSet(kSCStatusInvalidArgument); 2124 return NULL; 2125 } 2126 addr_p = (const struct sockaddr *)CFDataGetBytePtr(data); 2127 } 2128 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionRemoteAddress); 2129 if (data != NULL) { 2130 if (!isA_CFData(data) || (CFDataGetLength(data) < sizeof(struct sockaddr_in))) { 2131 _SCErrorSet(kSCStatusInvalidArgument); 2132 return NULL; 2133 } 2134 addr_r = (const struct sockaddr *)CFDataGetBytePtr(data); 2135 } 2136 interface = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionInterface); 2137 if ((interface != NULL) && 2138 (!isA_CFString(interface) || (CFStringGetLength(interface) == 0))) { 2139 _SCErrorSet(kSCStatusInvalidArgument); 2140 return NULL; 2141 } 2142 onDemandBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandBypass); 2143 if ((onDemandBypass != NULL) && !isA_CFBoolean(onDemandBypass)) { 2144 _SCErrorSet(kSCStatusInvalidArgument); 2145 return NULL; 2146 } 2147 resolverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionResolverBypass); 2148 if ((resolverBypass != NULL) && !isA_CFBoolean(resolverBypass)) { 2149 _SCErrorSet(kSCStatusInvalidArgument); 2150 return NULL; 2151 } 2152 2153 2154 serverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionServerBypass); 2155 if ((serverBypass != NULL) && !isA_CFBoolean(serverBypass)) { 2156 _SCErrorSet(kSCStatusInvalidArgument); 2157 return NULL; 2158 } 2159 2160 2161 if (nodename != NULL) { 2162 const char *name; 2163 2164 if ((addr_l != NULL) || (addr_r != NULL) || (addr_p != NULL)) { 2165 // can't have both a nodename and an address 2166 _SCErrorSet(kSCStatusInvalidArgument); 2167 return NULL; 2168 } 2169 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]; 2175 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); 2180 return NULL; 2181 } 2182 2183 target = __SCNetworkReachabilityCreateWithPtr(NULL, name, addr_p); 2184 } else { 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); 2191 } else { 2192 _SCErrorSet(kSCStatusInvalidArgument); 2193 return NULL; 2194 } 2195 } 2196 if (target == NULL) { 2197 return NULL; 2198 } 2199 2200 targetPrivate = (SCNetworkReachabilityPrivateRef)target; 2201 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); 2210 return NULL; 2211 } 2212 } 2213 2214 2215 if (onDemandBypass != NULL) { 2216 targetPrivate->onDemandBypass = CFBooleanGetValue(onDemandBypass); 2217 } 2218 2219 if (resolverBypass != NULL) { 2220 targetPrivate->resolverBypass = CFBooleanGetValue(resolverBypass); 2221 } 2222 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); 2226 } 2227 2228 2229 if (_sc_debug && (_sc_log > 0)) { 2230 const char *opt = "???"; 2231 2232 switch (targetPrivate->type) { 2233 case reachabilityTypeAddress : 2234 opt = DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS; 2235 break; 2236 case reachabilityTypeAddressPair : 2237 opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS; 2238 break; 2239 case reachabilityTypeName : 2240 opt = DEBUG_REACHABILITY_TYPE_NAME_OPTIONS; 2241 break; 2242 case reachabilityTypePTR : 2243 opt = DEBUG_REACHABILITY_TYPE_PTR_OPTIONS; 2244 break; 2245 } 2246 2247 SCLog(TRUE, LOG_INFO, CFSTR("%s%s %@"), 2248 targetPrivate->log_prefix, 2249 opt, 2250 targetPrivate); 2251 } 2252 2253 return (SCNetworkReachabilityRef)targetPrivate; 2254} 2255 2256 2257static SCNetworkReachabilityRef 2258__SCNetworkReachabilityCreateCopy(SCNetworkReachabilityRef target) 2259{ 2260 SCNetworkReachabilityRef clone = NULL; 2261 SCNetworkReachabilityPrivateRef clonePrivate; 2262 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 2263 2264 switch (targetPrivate->type) { 2265 case reachabilityTypeAddress : 2266 clone = SCNetworkReachabilityCreateWithAddress(NULL, 2267 targetPrivate->remoteAddress); 2268 break; 2269 case reachabilityTypeAddressPair : 2270 clone = SCNetworkReachabilityCreateWithAddressPair(NULL, 2271 targetPrivate->localAddress, 2272 targetPrivate->remoteAddress); 2273 break; 2274 case reachabilityTypeName : 2275 clone = SCNetworkReachabilityCreateWithName(NULL, 2276 targetPrivate->name); 2277 break; 2278 case reachabilityTypePTR : 2279 clone = __SCNetworkReachabilityCreateWithPtr(NULL, 2280 targetPrivate->name, 2281 targetPrivate->remoteAddress); 2282 break; 2283 } 2284 if (clone == NULL) { 2285 return NULL; 2286 } 2287 2288 clonePrivate = (SCNetworkReachabilityPrivateRef)clone; 2289 2290 clonePrivate->quiet = TRUE; 2291 2292 clonePrivate->if_index = targetPrivate->if_index; 2293 bcopy(targetPrivate->if_name, clonePrivate->if_name, sizeof(clonePrivate->if_name)); 2294 2295 clonePrivate->onDemandBypass = targetPrivate->onDemandBypass; 2296 2297 2298 clonePrivate->serverBypass = targetPrivate->serverBypass; 2299 2300 clonePrivate->resolverBypass = targetPrivate->resolverBypass; 2301 2302 2303 if (_sc_debug && (_sc_log > 0)) { 2304 const char *opt = "???"; 2305 2306 switch (clonePrivate->type) { 2307 case reachabilityTypeAddress : 2308 opt = DEBUG_REACHABILITY_TYPE_ADDRESS_CLONE; 2309 break; 2310 case reachabilityTypeAddressPair : 2311 opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_CLONE; 2312 break; 2313 case reachabilityTypeName : 2314 opt = DEBUG_REACHABILITY_TYPE_NAME_CLONE; 2315 break; 2316 case reachabilityTypePTR : 2317 opt = DEBUG_REACHABILITY_TYPE_PTR_CLONE; 2318 break; 2319 } 2320 2321 SCLog(TRUE, LOG_INFO, CFSTR("%s%s %p %@"), 2322 clonePrivate->log_prefix, 2323 opt, 2324 targetPrivate, 2325 clone); 2326 } 2327 2328 return clone; 2329} 2330 2331 2332CFTypeID 2333SCNetworkReachabilityGetTypeID(void) 2334{ 2335 pthread_once(&initialized, __SCNetworkReachabilityInitialize); /* initialize runtime */ 2336 return __kSCNetworkReachabilityTypeID; 2337} 2338 2339 2340CFArrayRef /* CFArray[CFData], where each CFData is a (struct sockaddr *) */ 2341SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target, 2342 int *error_num) 2343{ 2344 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 2345 2346 if (!isA_SCNetworkReachability(target)) { 2347 _SCErrorSet(kSCStatusInvalidArgument); 2348 return NULL; 2349 } 2350 2351 if (!isReachabilityTypeName(targetPrivate->type)) { 2352 _SCErrorSet(kSCStatusInvalidArgument); 2353 return NULL; 2354 } 2355 2356 if (error_num) { 2357 *error_num = targetPrivate->resolvedError; 2358 } 2359 2360 if (targetPrivate->resolvedAddresses != NULL) { 2361 if (isA_CFArray(targetPrivate->resolvedAddresses)) { 2362 return CFRetain(targetPrivate->resolvedAddresses); 2363 } else { 2364 /* if status is known but no resolved addresses to return */ 2365 _SCErrorSet(kSCStatusOK); 2366 return NULL; 2367 } 2368 } 2369 2370 _SCErrorSet(kSCStatusReachabilityUnknown); 2371 return NULL; 2372} 2373 2374 2375static void 2376__SCNetworkReachabilitySetResolvedError(SCNetworkReachabilityRef target, 2377 int32_t status) 2378{ 2379 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 2380 2381 MUTEX_ASSERT_HELD(&targetPrivate->lock); 2382 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 2388 2389 if (targetPrivate->resolvedAddresses != NULL) { 2390 CFRelease(targetPrivate->resolvedAddresses); 2391 targetPrivate->resolvedAddresses = NULL; 2392 } 2393 2394 SCLog(_sc_debug, LOG_INFO, CFSTR("%scould not be resolved: %s"), 2395 targetPrivate->log_prefix, 2396 gai_strerror(status)); 2397 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; 2402 2403 return; 2404} 2405 2406 2407/* 2408 * rankReachability() 2409 * Not reachable == 0 2410 * Connection Required == 1 2411 * Reachable == 2 2412 */ 2413static int 2414rankReachability(SCNetworkReachabilityFlags flags) 2415{ 2416 int rank = 0; 2417 2418 if (flags & kSCNetworkReachabilityFlagsReachable) rank = 2; 2419 if (flags & kSCNetworkReachabilityFlagsConnectionRequired) rank = 1; 2420 return rank; 2421} 2422 2423 2424#pragma mark - 2425#pragma mark DNS name resolution 2426 2427 2428static void 2429update_resolver_reachability(ReachabilityStoreInfoRef store_info, 2430 dns_resolver_t *resolver, 2431 SCNetworkReachabilityFlags *flags, 2432 Boolean *haveDNS, 2433 uint32_t *resolver_if_index, 2434 const char *log_prefix) 2435{ 2436 if (resolver_if_index) *resolver_if_index = 0; 2437 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; 2442 } 2443 *haveDNS = TRUE; 2444 } else { 2445 *flags = kSCNetworkReachabilityFlagsReachable; 2446 *haveDNS = FALSE; 2447 } 2448 2449 return; 2450} 2451 2452 2453static Boolean 2454check_matching_resolvers(ReachabilityStoreInfoRef store_info, 2455 dns_config_t *dns_config, 2456 const char *fqdn, 2457 unsigned int if_index, 2458 SCNetworkReachabilityFlags *flags, 2459 Boolean *haveDNS, 2460 uint32_t *resolver_if_index, 2461 int *dns_config_index, 2462 const char *log_prefix) 2463{ 2464 int i; 2465 Boolean matched = FALSE; 2466 const char *name = fqdn; 2467 int32_t n_resolvers; 2468 dns_resolver_t **resolvers; 2469 2470 if (if_index == 0) { 2471 n_resolvers = dns_config->n_resolver; 2472 resolvers = dns_config->resolver; 2473 } else { 2474 n_resolvers = dns_config->n_scoped_resolver; 2475 resolvers = dns_config->scoped_resolver; 2476 } 2477 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; 2482 2483 while (!matched && (name != NULL)) { 2484 size_t len; 2485 2486 /* 2487 * check if the provided name (or sub-component) 2488 * matches one of our resolver configurations. 2489 */ 2490 len = strlen(name); 2491 for (i = 0; i < n_resolvers; i++) { 2492 char *domain; 2493 dns_resolver_t *resolver; 2494 2495 resolver = resolvers[i]; 2496 if ((if_index != 0) && (if_index != resolver->if_index)) { 2497 continue; 2498 } 2499 2500 domain = resolver->domain; 2501 if (domain != NULL && (len == strlen(domain))) { 2502 if (strcasecmp(name, domain) == 0) { 2503 /* 2504 * if name matches domain 2505 */ 2506 matched = TRUE; 2507 update_resolver_reachability(store_info, 2508 resolver, 2509 flags, 2510 haveDNS, 2511 resolver_if_index, 2512 log_prefix); 2513 if (dns_config_index != NULL) *dns_config_index = i; 2514 break; 2515 } 2516 } 2517 } 2518 2519 if (!matched) { 2520 /* 2521 * we have not found a matching resolver, try 2522 * a less qualified domain 2523 */ 2524 name = strchr(name, '.'); 2525 if ((name != NULL) && (*name != '\0')) { 2526 name++; 2527 } else { 2528 name = NULL; 2529 } 2530 } 2531 } 2532 2533 return matched; 2534} 2535 2536 2537static dns_resolver_t * 2538get_default_resolver(dns_config_t *dns_config, unsigned int if_index) 2539{ 2540 int i; 2541 int32_t n_resolvers; 2542 dns_resolver_t *resolver = NULL; 2543 dns_resolver_t **resolvers; 2544 2545 if (if_index == 0) { 2546 n_resolvers = dns_config->n_resolver; 2547 resolvers = dns_config->resolver; 2548 } else { 2549 n_resolvers = dns_config->n_scoped_resolver; 2550 resolvers = dns_config->scoped_resolver; 2551 } 2552 2553 for (i = 0; i < n_resolvers; i++) { 2554 if ((if_index != 0) && (if_index != resolvers[i]->if_index)) { 2555 continue; 2556 } 2557 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]; 2566 } 2567 } 2568 2569 return resolver; 2570} 2571 2572 2573static dns_configuration_t * 2574dns_configuration_retain() 2575{ 2576 dns_configuration_t *config; 2577 2578 pthread_mutex_lock(&dns_lock); 2579 2580 if (dns_configuration != NULL) { 2581 Boolean refresh = TRUE; 2582 2583 if (dns_token_valid) { 2584 int check = 0; 2585 uint32_t status; 2586 2587 /* 2588 * check if the global [DNS] configuration snapshot needs 2589 * to be updated 2590 */ 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 2596 refresh = FALSE; 2597 } 2598 } 2599 2600 if (refresh) { 2601 if (dns_configuration->refs == 0) { 2602 dns_configuration_free(dns_configuration->config); 2603 CFAllocatorDeallocate(NULL, dns_configuration); 2604 } 2605 dns_configuration = NULL; 2606 } 2607 } 2608 2609 if (dns_configuration == NULL) { 2610 dns_config_t *new_config; 2611 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; 2617 } 2618 } 2619 2620 if (dns_configuration != NULL) { 2621 dns_configuration->refs++; 2622 } 2623 2624 config = dns_configuration; 2625 pthread_mutex_unlock(&dns_lock); 2626 return config; 2627} 2628 2629 2630static void 2631dns_configuration_release(dns_configuration_t *config) 2632{ 2633 pthread_mutex_lock(&dns_lock); 2634 2635 config->refs--; 2636 if (config->refs == 0) { 2637 if (!dns_token_valid && (config == dns_configuration)) { 2638 dns_configuration = NULL; 2639 } 2640 2641 if (config != dns_configuration) { 2642 dns_configuration_free(config->config); 2643 CFAllocatorDeallocate(NULL, config); 2644 } 2645 } 2646 2647 pthread_mutex_unlock(&dns_lock); 2648 return; 2649} 2650 2651 2652static Boolean 2653dns_configuration_watch() 2654{ 2655 int dns_check = 0; 2656 const char *dns_key; 2657 Boolean ok = FALSE; 2658 uint32_t status; 2659 2660 pthread_mutex_lock(&dns_lock); 2661 2662 dns_key = dns_configuration_notify_key(); 2663 if (dns_key == NULL) { 2664 SCLog(TRUE, LOG_INFO, CFSTR("dns_configuration_notify_key() failed")); 2665 goto done; 2666 } 2667 2668 status = notify_register_check(dns_key, &dns_token); 2669 if (status == NOTIFY_STATUS_OK) { 2670 dns_token_valid = TRUE; 2671 } else { 2672 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_check() failed, status=%u"), status); 2673 goto done; 2674 } 2675 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; 2681 goto done; 2682 } 2683 2684 ok = TRUE; 2685 2686 done : 2687 2688 pthread_mutex_unlock(&dns_lock); 2689 return ok; 2690} 2691 2692 2693static void 2694dns_configuration_unwatch() 2695{ 2696 pthread_mutex_lock(&dns_lock); 2697 2698 (void)notify_cancel(dns_token); 2699 dns_token_valid = FALSE; 2700 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; 2705 } 2706 2707 pthread_mutex_unlock(&dns_lock); 2708 return; 2709} 2710 2711 2712static void 2713_SC_R_updateResolverReachability(ReachabilityStoreInfoRef store_info, 2714 SCNetworkReachabilityFlags *flags, 2715 Boolean *haveDNS, 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 2721 ) 2722{ 2723 dns_resolver_t *default_resolver; 2724 dns_configuration_t *dns; 2725 Boolean found = FALSE; 2726 char *fqdn = (char *)nodename; 2727 int i; 2728 Boolean isFQDN = FALSE; 2729 size_t len; 2730 const int ndots = 1; 2731 Boolean useDefault = FALSE; 2732 2733 if (resolver_if_index) *resolver_if_index = 0; 2734 if (dns_config_index) *dns_config_index = -1; 2735 2736 /* 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. 2742 */ 2743 2744 *flags = kSCNetworkReachabilityFlagsReachable; 2745 *haveDNS = FALSE; 2746 2747 len = (nodename != NULL) ? strlen(nodename) : 0; 2748 if (len == 0) { 2749 // if no nodename, return not reachable 2750 *flags = 0; 2751 return; 2752 } 2753 2754 dns = dns_configuration_retain(); 2755 if (dns == NULL) { 2756 // if error 2757 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS: no configuration"), log_prefix); 2758 goto done; 2759 } 2760 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); 2765 goto done; 2766 } 2767 2768 if (fqdn[len - 1] == '.') { 2769 isFQDN = TRUE; 2770 2771 // trim trailing '.''s 2772 while ((len > 0) && (fqdn[len-1] == '.')) { 2773 if (fqdn == nodename) { 2774 fqdn = strdup(nodename); 2775 assert(fqdn != nodename); 2776 } 2777 fqdn[--len] = '\0'; 2778 } 2779 } 2780 2781 /* 2782 * check if the provided name matches a supplemental domain 2783 */ 2784 found = check_matching_resolvers(store_info, dns->config, fqdn, if_index, 2785 flags, haveDNS, resolver_if_index, 2786 dns_config_index, log_prefix); 2787 2788 if (!found && !isFQDN) { 2789 /* 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. 2793 */ 2794 char *cp; 2795 int dots; 2796 2797 dots = 0; 2798 for (cp = fqdn; *cp != '\0'; cp++) { 2799 if (*cp == '.') dots++; 2800 } 2801 2802 /* Per KB: HT4845 */ 2803 if (dots >= ndots) { 2804 useDefault = TRUE; 2805 } 2806 } 2807 2808 if (!found && !isFQDN && !useDefault && (dns->config->n_resolver > 1)) { 2809 /* 2810 * FQDN not specified, try matching w/search domains 2811 */ 2812 if (default_resolver->n_search > 0) { 2813 for (i = 0; !found && (i < default_resolver->n_search); i++) { 2814 int ret; 2815 char *search_fqdn = NULL; 2816 2817 ret = asprintf(&search_fqdn, "%s.%s", fqdn, default_resolver->search[i]); 2818 if (ret == -1) { 2819 continue; 2820 } 2821 2822 // try the provided name with the search domain appended 2823 found = check_matching_resolvers(store_info, 2824 dns->config, 2825 search_fqdn, 2826 if_index, 2827 flags, 2828 haveDNS, 2829 resolver_if_index, 2830 dns_config_index, 2831 log_prefix); 2832 free(search_fqdn); 2833 } 2834 } else if (default_resolver->domain != NULL) { 2835 char *dp; 2836 int domain_parts = 0; 2837 2838 // count domain parts 2839 for (dp = default_resolver->domain; *dp != '\0'; dp++) { 2840 if (*dp == '.') { 2841 domain_parts++; 2842 } 2843 } 2844 2845 // remove trailing dots 2846 for (dp--; (dp >= default_resolver->domain) && (*dp == '.'); dp--) { 2847 *dp = '\0'; 2848 domain_parts--; 2849 } 2850 2851 if (dp >= default_resolver->domain) { 2852 // dots are separators, bump # of components 2853 domain_parts++; 2854 } 2855 2856 dp = default_resolver->domain; 2857 for (i = LOCALDOMAINPARTS; !found && (i <= (domain_parts - ndots)); i++) { 2858 int ret; 2859 char *search_fqdn = NULL; 2860 2861 ret = asprintf(&search_fqdn, "%s.%s", fqdn, dp); 2862 if (ret == -1) { 2863 continue; 2864 } 2865 2866 // try the provided name with the [default] domain appended 2867 found = check_matching_resolvers(store_info, 2868 dns->config, 2869 search_fqdn, 2870 if_index, 2871 flags, 2872 haveDNS, 2873 resolver_if_index, 2874 dns_config_index, 2875 log_prefix); 2876 free(search_fqdn); 2877 2878 // move to the next component of the [default] domain 2879 dp = strchr(dp, '.') + 1; 2880 } 2881 } 2882 } 2883 2884 if (!found) { 2885 // update the reachability of the default resolver 2886 update_resolver_reachability(store_info, 2887 default_resolver, 2888 flags, 2889 haveDNS, 2890 resolver_if_index, 2891 log_prefix); 2892 if (dns_config_index != NULL) *dns_config_index = 0; 2893 } 2894 2895 done : 2896 2897 if (fqdn != nodename) free(fqdn); 2898 2899 if (dns != NULL) { 2900 dns_configuration_release(dns); 2901 } 2902 2903 return; 2904} 2905 2906 2907Boolean 2908__SC_checkResolverReachabilityInternal(SCDynamicStoreRef *storeP, 2909 SCNetworkReachabilityFlags *flags, 2910 Boolean *haveDNS, 2911 const char *nodename, 2912 uint32_t *resolver_if_index, 2913 int *dns_config_index) 2914{ 2915 Boolean ok; 2916 ReachabilityStoreInfo store_info; 2917 2918 ReachabilityStoreInfo_init(&store_info); 2919 ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC); 2920 if (!ok) { 2921 goto done; 2922 } 2923 2924 _SC_R_updateResolverReachability(&store_info, 2925 flags, 2926 haveDNS, 2927 nodename, 2928 0, 2929 resolver_if_index, 2930 dns_config_index, 2931 ""); 2932 2933 done : 2934 2935 ReachabilityStoreInfo_free(&store_info); 2936 return ok; 2937} 2938 2939 2940/* 2941 * _SC_checkResolverReachabilityByAddress() 2942 * 2943 * Given an IP address, determine whether a reverse DNS query can be issued 2944 * using the current network configuration. 2945 */ 2946Boolean 2947_SC_checkResolverReachabilityByAddress(SCDynamicStoreRef *storeP, 2948 SCNetworkReachabilityFlags *flags, 2949 Boolean *haveDNS, 2950 struct sockaddr *sa) 2951{ 2952 Boolean ok; 2953 char ptr_name[128]; 2954 ReachabilityStoreInfo store_info; 2955 2956 ReachabilityStoreInfo_init(&store_info); 2957 ok = ReachabilityStoreInfo_update(&store_info, storeP, AF_UNSPEC); 2958 if (!ok) { 2959 goto done; 2960 } 2961 2962 /* 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. 2969 */ 2970 ok = addr_to_PTR_name(sa, ptr_name, sizeof(ptr_name)); 2971 if (!ok) { 2972 goto done; 2973 } 2974 2975 _SC_R_updateResolverReachability(&store_info, flags, haveDNS, ptr_name, 0, NULL, NULL, ""); 2976 2977 done : 2978 2979 ReachabilityStoreInfo_free(&store_info); 2980 return ok; 2981} 2982 2983 2984#pragma mark - 2985#pragma mark DNSServiceGetAddrInfo support 2986 2987 2988/* 2989 * DNS query handling 2990 * 2991 * Notes : 2992 * 2993 * 1. We have a "contract" with discoveryd that for EVERY network 2994 * or DNS configuration change that should warrant our [re-]starting 2995 * a query, discoveryd will acknowledge the latest DNS configuration. 2996 * 2997 * 2. IPMonitor also posts a notification AFTER every network or DNS 2998 * configuration change. 2999 * 3000 * 3. We use IPMonitor's "trailing edge" as a signal to restart any 3001 * by-name queries. 3002 */ 3003 3004 3005// Note: protected by _hn_target_queue() 3006static int dns_refresh_token; 3007static Boolean dns_refresh_token_valid = FALSE; 3008 3009 3010/* 3011 * dns_refresh_handler 3012 * 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 3015 * resolve the name. 3016 * - should be exec'd on the _hn_target_queue() 3017 */ 3018static void 3019dns_refresh_handler() 3020{ 3021 CFArrayRef changes; 3022 CFStringRef key; 3023 __block SCDynamicStoreRef store = NULL; 3024 3025 dispatch_sync(_hn_target_queue(), ^{ 3026 if (dns_refresh_token_valid && (hn_store != NULL)) { 3027 store = CFRetain(hn_store); 3028 } 3029 }); 3030 3031 if (store == NULL) { 3032 return; 3033 } 3034 3035 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 3036 kSCDynamicStoreDomainState, 3037 kSCEntNetDNS); 3038 changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks); 3039 __SCNetworkReachabilityHandleChanges(store, changes, NULL); 3040 CFRelease(changes); 3041 CFRelease(key); 3042 3043 CFRelease(store); 3044 return; 3045} 3046 3047 3048/* 3049 * dns_refresh_enable 3050 * 3051 * Called to monitor for network/DNS changes that should restart a DNS query. 3052 * - caller must be running on the _hn_target_queue() 3053 */ 3054static Boolean 3055dns_refresh_enable(dispatch_queue_t q) 3056{ 3057 uint32_t status; 3058 3059 status = notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE, 3060 &dns_refresh_token, 3061 q, 3062 ^(int token){ 3063 dns_refresh_handler(); 3064 }); 3065 if (status != NOTIFY_STATUS_OK) { 3066 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed, status=%u"), status); 3067 return FALSE; 3068 } 3069 3070 dns_refresh_token_valid = TRUE; 3071 3072 return TRUE; 3073} 3074 3075 3076/* 3077 * dns_refresh_disable 3078 * 3079 * Called to stop monitoring for network/DNS changes 3080 * - caller must be running on the _hn_target_queue() 3081 */ 3082static void 3083dns_refresh_disable() 3084{ 3085 (void)notify_cancel(dns_refresh_token); 3086 dns_refresh_token_valid = FALSE; 3087 return; 3088} 3089 3090 3091#pragma mark - 3092#pragma mark [m]DNS Queries 3093 3094 3095static void 3096dequeueDNSQuery(SCNetworkReachabilityRef target); 3097 3098 3099static dispatch_queue_t 3100_dns_queue() 3101{ 3102 static dispatch_once_t once; 3103 static dispatch_queue_t q; 3104 3105 dispatch_once(&once, ^{ 3106 q = dispatch_queue_create("SCNetworkReachability.DNSService", NULL); 3107 }); 3108 3109 return q; 3110} 3111 3112 3113/* 3114 * _dns_complete 3115 */ 3116static __inline__ Boolean 3117_dns_complete(SCNetworkReachabilityRef target) 3118{ 3119 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 3120 3121 if ((targetPrivate->dnsHaveV4 && targetPrivate->dnsHaveV6) || 3122 targetPrivate->dnsHavePTR || 3123 targetPrivate->dnsHaveError || 3124 targetPrivate->dnsHaveTimeout) { 3125 return TRUE; 3126 } 3127 3128 return FALSE; 3129} 3130 3131 3132/* 3133 * _dns_notify 3134 * 3135 * Called to push out a target's DNS changes 3136 * - caller must be running on the _dns_queue() 3137 */ 3138static void 3139_dns_notify(const void *value, void *context) 3140{ 3141 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)value; 3142 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 3143 3144 MUTEX_LOCK(&targetPrivate->lock); 3145 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 3152 3153 // update target info 3154 if (targetPrivate->resolvedAddresses != NULL) { 3155 CFRelease(targetPrivate->resolvedAddresses); 3156 } 3157 targetPrivate->resolvedAddresses = targetPrivate->dnsAddresses; 3158 targetPrivate->dnsAddresses = NULL; 3159 3160 targetPrivate->resolvedError = targetPrivate->dnsError; 3161 targetPrivate->dnsError = NETDB_SUCCESS; 3162 3163 dequeueDNSQuery(target); 3164 3165 targetPrivate->needResolve = FALSE; 3166 3167 if (targetPrivate->scheduled) { 3168 __SCNetworkReachabilityUpdate(target); 3169 } 3170 } 3171 3172 MUTEX_UNLOCK(&targetPrivate->lock); 3173 return; 3174} 3175 3176 3177typedef enum { 3178 MARK_NONE, 3179 MARK_ERROR, 3180 MARK_TIMEOUT, 3181 MARK_HAVE_V4, 3182 MARK_HAVE_V6, 3183 MARK_HAVE_PTR, 3184} _dns_mark_t; 3185 3186 3187/* 3188 * _dns_mark 3189 */ 3190static __inline__ void 3191_dns_mark(SCNetworkReachabilityRef target, _dns_mark_t mark) 3192{ 3193 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 3194 3195 switch (mark) { 3196 case MARK_NONE : 3197 break; 3198 case MARK_ERROR : 3199 targetPrivate->dnsHaveError = TRUE; 3200 break; 3201 case MARK_TIMEOUT : 3202 targetPrivate->dnsHaveTimeout = TRUE; 3203 break; 3204 case MARK_HAVE_V4 : 3205 targetPrivate->dnsHaveV4 = TRUE; 3206 break; 3207 case MARK_HAVE_V6 : 3208 targetPrivate->dnsHaveV6 = TRUE; 3209 break; 3210 case MARK_HAVE_PTR : 3211 targetPrivate->dnsHavePTR = TRUE; 3212 break; 3213 } 3214 3215 return; 3216} 3217 3218 3219/* 3220 * _dns_callback 3221 * 3222 * Called to process [m]DNS query updates 3223 * - caller must be running on the _dns_queue() 3224 */ 3225static void 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) 3231 void *context) 3232{ 3233 int failures = 0; 3234 Boolean restart = FALSE; 3235 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context; 3236 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 3237 3238 MUTEX_LOCK(&targetPrivate->lock); 3239 3240 if (sdRef != targetPrivate->dnsTarget) { 3241 // if this DNSServiceRef is no longer associated with the target 3242 MUTEX_UNLOCK(&targetPrivate->lock); 3243 return; 3244 } 3245 3246 switch (errorCode) { 3247 case kDNSServiceErr_NoError : 3248 if (dnsAddress != NULL) { 3249 CFMutableArrayRef addresses; 3250 CFIndex i; 3251 3252 _dns_mark(target, dnsMark); 3253 3254 if (targetPrivate->dnsAddresses != NULL) { 3255 if (isA_CFArray(targetPrivate->dnsAddresses)) { 3256 addresses = CFArrayCreateMutableCopy(NULL, 0, targetPrivate->dnsAddresses); 3257 } else { 3258 addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 3259 } 3260 3261 CFRelease(targetPrivate->dnsAddresses); 3262 targetPrivate->dnsAddresses = NULL; 3263 } else { 3264 addresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 3265 } 3266 3267 i = CFArrayGetFirstIndexOfValue(addresses, 3268 CFRangeMake(0, CFArrayGetCount(addresses)), 3269 dnsAddress); 3270 if (flags & kDNSServiceFlagsAdd) { 3271 // add address 3272 if (i == kCFNotFound) { 3273 CFArrayAppendValue(addresses, dnsAddress); 3274 } 3275#ifdef HANDLE_RMV_REQUESTS 3276 } else { 3277 // remove address 3278 if (i != kCFNotFound) { 3279 CFArrayRemoveValueAtIndex(addresses, i); 3280 } 3281#endif // HANDLE_RMV_REQUESTS 3282 } 3283 3284 if (CFArrayGetCount(addresses) > 0) { 3285 targetPrivate->dnsAddresses = addresses; 3286 targetPrivate->dnsError = NETDB_SUCCESS; 3287 } else { 3288 // if host not found 3289 targetPrivate->dnsAddresses = CFRetain(kCFNull); 3290 targetPrivate->dnsError = EAI_NONAME; 3291 CFRelease(addresses); 3292 } 3293 3294 } 3295 break; 3296 case kDNSServiceErr_BadParam : 3297 _dns_mark(target, MARK_ERROR); 3298 3299 if (targetPrivate->dnsAddresses != NULL) { 3300 CFRelease(targetPrivate->dnsAddresses); 3301 } 3302 targetPrivate->dnsAddresses = CFRetain(kCFNull); 3303 targetPrivate->dnsError = EAI_NONAME; 3304 break; 3305 case kDNSServiceErr_NoSuchRecord : 3306 _dns_mark(target, dnsMark); 3307 3308 if (targetPrivate->dnsAddresses == NULL) { 3309 targetPrivate->dnsAddresses = CFRetain(kCFNull); 3310 targetPrivate->dnsError = EAI_NONAME; 3311 } 3312 break; 3313 case kDNSServiceErr_Timeout : 3314 _dns_mark(target, MARK_TIMEOUT); 3315 3316 if (targetPrivate->dnsAddresses == NULL) { 3317 targetPrivate->dnsAddresses = CFRetain(kCFNull); 3318 targetPrivate->dnsError = EAI_NONAME; 3319 } 3320 break; 3321 default : 3322 SCLog(TRUE, LOG_ERR, 3323 CFSTR("%sSCNetworkReachability _dns_callback w/error=%d (n=%d)"), 3324 targetPrivate->log_prefix, 3325 errorCode, 3326 targetPrivate->dnsFailures + 1); 3327 // fall through 3328 case kDNSServiceErr_ServiceNotRunning : 3329 _dns_mark(target, MARK_ERROR); 3330 3331 // bump per-target failure count 3332 failures = ++targetPrivate->dnsFailures; 3333 3334 // Check to see if we've seen too many failures for this target 3335 if (failures > 2) { 3336 // if so, there's little point in retrying over 3337 // and over again so let's just return an error 3338 // and move on. 3339 if (targetPrivate->dnsAddresses != NULL) { 3340 CFRelease(targetPrivate->dnsAddresses); 3341 } 3342 targetPrivate->dnsAddresses = CFRetain(kCFNull); 3343 targetPrivate->dnsError = EAI_NONAME; 3344 } else if (targetPrivate->dnsGeneration == dnsGeneration) { 3345 // if not, then "discoveryd" 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); 3351 dnsMain = NULL; 3352 dnsCount = 0; 3353 dnsGeneration++; 3354 restart = TRUE; 3355 } 3356 } 3357 break; 3358 } 3359 3360 // update DNS failure count (and [re-]set to zero if we're OK) 3361 targetPrivate->dnsFailures = failures; 3362 3363 MUTEX_UNLOCK(&targetPrivate->lock); 3364 3365 if (restart) { 3366 SCLog(TRUE, LOG_DEBUG, 3367 CFSTR("%sreconnecting SCNetworkReachability w/\"discoveryd\" (%d)"), 3368 targetPrivate->log_prefix, 3369 dnsGeneration); 3370 3371 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC), 3372 _hn_changes_queue(), 3373 ^{ 3374 dns_refresh_handler(); 3375 }); 3376 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); 3381 dnsUpdated = NULL; 3382 } 3383 return; 3384 } 3385 3386 if (targetPrivate->dnsHaveTimeout) { 3387 targetPrivate->dnsNoAddressesSinceLastTimeout = TRUE; 3388 } else if (targetPrivate->dnsNoAddressesSinceLastTimeout && 3389 isA_CFArray(targetPrivate->dnsAddresses) && 3390 CFArrayGetCount(targetPrivate->dnsAddresses) > 0) 3391 { 3392 targetPrivate->dnsNoAddressesSinceLastTimeout = FALSE; 3393 } 3394 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. 3399 3400 if (dnsUpdated == NULL) { 3401 dnsUpdated = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); 3402 } 3403 CFSetAddValue(dnsUpdated, target); 3404 3405 if (!(flags & kDNSServiceFlagsMoreComing)) { 3406 CFSetApplyFunction(dnsUpdated, _dns_notify, NULL); 3407 CFRelease(dnsUpdated); 3408 dnsUpdated = NULL; 3409 } 3410 3411 return; 3412} 3413 3414 3415/* 3416 * _dns_getaddrinfo_callback 3417 * 3418 * Called to process [m]DNS query updates 3419 * - caller must be running on the _dns_queue() 3420 */ 3421static void 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, 3428 uint32_t ttl, 3429 void *context) 3430{ 3431 CFDataRef dnsAddress = NULL; 3432 _dns_mark_t dnsMark = MARK_NONE; 3433 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) { 3441 case AF_INET : 3442 dnsMark = MARK_HAVE_V4; 3443 break; 3444 case AF_INET6 : 3445 dnsMark = MARK_HAVE_V6; 3446 break; 3447 } 3448 break; 3449 default : 3450 break; 3451 } 3452 } 3453 3454 _dns_callback(sdRef, flags, errorCode, dnsMark, dnsAddress, context); 3455 3456 if (dnsAddress != NULL) { 3457 CFRelease(dnsAddress); 3458 } 3459 3460 return; 3461} 3462 3463 3464static CFStringRef 3465_dns_copy_domain_name(const uint8_t *rdata, uint16_t rdlen) 3466{ 3467 CFMutableStringRef domain; 3468 const uint8_t *label; 3469 uint8_t label_len; 3470 3471 domain = CFStringCreateMutable(NULL, 0); 3472 3473 label = rdata; 3474 label_len = *(label++); 3475 while (label_len != 0) { 3476 while (label_len-- > 0) { 3477 uint8_t byte = *label++; 3478 3479 if ((byte == '.') || (byte == '\\')) { 3480 // if escape needed 3481 CFStringAppendFormat(domain, NULL, CFSTR("\\%c"), byte); 3482 } else if (byte <= ' ') { 3483 CFStringAppendFormat(domain, NULL, CFSTR("\\%c%c%c"), 3484 '0' + (byte / 100), 3485 '0' + ((byte / 10) % 10), 3486 '0' + (byte % 10)); 3487 } else { 3488 CFStringAppendFormat(domain, NULL, CFSTR("%c"), byte); 3489 } 3490 } 3491 3492 label_len = *(label++); 3493 if (label_len != 0) { 3494 CFStringAppendFormat(domain, NULL, CFSTR(".")); 3495 } 3496 } 3497 3498 return domain; 3499} 3500 3501 3502static void 3503_dns_queryrecord_callback(DNSServiceRef sdRef, 3504 DNSServiceFlags flags, 3505 uint32_t interfaceIndex, 3506 DNSServiceErrorType errorCode, 3507 const char *fullname, 3508 uint16_t rrtype, 3509 uint16_t rrclass, 3510 uint16_t rdlen, 3511 const void *rdata, 3512 uint32_t ttl, 3513 void *context) 3514{ 3515 _dns_mark_t dnsMark = MARK_NONE; 3516 CFStringRef dnsPTRName = NULL; 3517 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)context; 3518 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 3519 3520 // for now, we only support using DNSServiceQueryRecord for PTR queries 3521 assert(targetPrivate->type == reachabilityTypePTR); 3522 3523 if (rdata != NULL) { 3524 switch (errorCode) { 3525 case kDNSServiceErr_NoError : 3526 if (rrtype == kDNSServiceType_PTR) { 3527 dnsPTRName = _dns_copy_domain_name(rdata, rdlen); 3528 } 3529 // ... and fall through 3530 case kDNSServiceErr_NoSuchRecord : 3531 dnsMark = MARK_HAVE_PTR; 3532 break; 3533 default : 3534 break; 3535 } 3536 } 3537 3538 _dns_callback(sdRef, flags, errorCode, dnsMark, dnsPTRName, context); 3539 3540 if (dnsPTRName != NULL) { 3541 CFRelease(dnsPTRName); 3542 } 3543 3544 return; 3545} 3546 3547 3548static Boolean 3549enqueueDNSQuery(SCNetworkReachabilityRef target) 3550{ 3551 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 3552 3553 MUTEX_ASSERT_HELD(&targetPrivate->lock); 3554 3555 // clear DNS flags, mark the query active 3556 targetPrivate->dnsFlags = 0; 3557 targetPrivate->dnsActive = TRUE; 3558 3559 // track the DNS resolution time 3560 __mark_operation_start(&targetPrivate->dnsQueryStart, &targetPrivate->dnsQueryEnd); 3561 3562 CFRetain(target); 3563 dispatch_async(_dns_queue(), ^{ 3564 DNSServiceErrorType err; 3565 const char *fcn = "???"; 3566 DNSServiceRef sdRef = NULL; 3567 3568 if (targetPrivate->dnsTarget != NULL) { 3569 // if already running 3570 CFRelease(target); 3571 return; 3572 } 3573 3574 // if needed, start interacting with "discoveryd" 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, 3581 err); 3582 goto done; 3583 } 3584 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, 3590 err); 3591 DNSServiceRefDeallocate(dnsMain); 3592 dnsMain = NULL; 3593 dnsGeneration++; 3594 goto done; 3595 } 3596 } 3597 3598 // start a query for this target 3599 sdRef = dnsMain; 3600 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 3610 0, // protocol 3611 targetPrivate->name, // hostname 3612 _dns_getaddrinfo_callback, // callback 3613 (void *)target); // context 3614 break; 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 3628 break; 3629 default : 3630 err = kDNSServiceErr_Unknown; 3631 break; 3632 } 3633 3634 switch (err) { 3635 case kDNSServiceErr_NoError : 3636 dnsCount++; 3637 break; 3638 3639 default : 3640 SCLog(TRUE, LOG_ERR, 3641 CFSTR("%s%s() failed, error = %d (%d)"), 3642 targetPrivate->log_prefix, 3643 fcn, 3644 err, 3645 dnsCount); 3646 // fall through 3647 3648 case kDNSServiceErr_BadParam : 3649 if (dnsCount == 0) { 3650 // if this was the first request 3651 DNSServiceRefDeallocate(dnsMain); 3652 dnsMain = NULL; 3653 dnsGeneration++; 3654 } 3655 sdRef = NULL; 3656 break; 3657 } 3658 3659 done : 3660 3661 MUTEX_LOCK(&targetPrivate->lock); 3662 3663 if (err == kDNSServiceErr_NoError) { 3664 targetPrivate->dnsGeneration = dnsGeneration; 3665 targetPrivate->dnsTarget = sdRef; 3666 } else { 3667 targetPrivate->dnsActive = FALSE; 3668 3669 // queue up the returned error 3670 dispatch_async(_dns_queue(), ^{ 3671 _dns_callback(NULL, // sdRef 3672 0, // flags 3673 err, // errorCode 3674 MARK_ERROR, // dnsMark 3675 NULL, // dnsAddress 3676 (void *)target); // context 3677 CFRelease(target); 3678 }); 3679 } 3680 3681 MUTEX_UNLOCK(&targetPrivate->lock); 3682 3683 return; 3684 }); 3685 3686 return TRUE; 3687} 3688 3689 3690static void 3691dequeueDNSQuery(SCNetworkReachabilityRef target) 3692{ 3693 DNSServiceRef sdRef; 3694 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 3695 3696 MUTEX_ASSERT_HELD(&targetPrivate->lock); 3697 3698 // terminate the [target] [m]DNS query 3699 sdRef = targetPrivate->dnsTarget; 3700 targetPrivate->dnsTarget = NULL; 3701 3702 // mark the query NOT active 3703 targetPrivate->dnsActive = FALSE; 3704 3705 // don't do anything if the sdRef is not valid 3706 if (sdRef != NULL) { 3707 int generation; 3708 3709 generation = targetPrivate->dnsGeneration; 3710 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3LL * NSEC_PER_SEC), 3711 _dns_queue(), 3712 ^{ 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); 3718 dnsCount--; 3719 if (dnsCount == 0) { 3720 // if no more queries active 3721 DNSServiceRefDeallocate(dnsMain); 3722 dnsMain = NULL; 3723 dnsGeneration++; 3724 } 3725 } 3726 3727 CFRelease(target); 3728 }); 3729 } 3730 3731 if (targetPrivate->dnsAddresses != NULL) { 3732 CFRelease(targetPrivate->dnsAddresses); 3733 targetPrivate->dnsAddresses = NULL; 3734 } 3735 targetPrivate->dnsError = NETDB_SUCCESS; 3736 3737 return; 3738} 3739 3740 3741#pragma mark - 3742#pragma mark Synchronous DNS query support 3743 3744 3745#define SYNC_DNS_QUERY_TIMEOUT_NSEC 35 * NSEC_PER_SEC // 35s 3746 3747 3748static void 3749sync_DNS_query_callback(SCNetworkReachabilityRef clone, 3750 SCNetworkReachabilityFlags cloneFlags, 3751 void *info) 3752{ 3753 dispatch_semaphore_t s = (dispatch_semaphore_t)info; 3754 3755 dispatch_semaphore_signal(s); 3756 return; 3757} 3758 3759 3760static void 3761sync_DNS_query(SCNetworkReachabilityRef target) 3762{ 3763 SCNetworkReachabilityRef clone; 3764 SCNetworkReachabilityPrivateRef clonePrivate; 3765 SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL }; 3766 dispatch_queue_t q; 3767 long ret; 3768 dispatch_semaphore_t s; 3769 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 3770 3771 clone = __SCNetworkReachabilityCreateCopy(target); 3772 if (clone == NULL) { 3773 return; 3774 } 3775 clonePrivate = (SCNetworkReachabilityPrivateRef)clone; 3776 3777 q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 3778 s = dispatch_semaphore_create(0); 3779 3780 // start async query 3781 context.info = (void *)s; 3782 SCNetworkReachabilitySetCallback(clone, sync_DNS_query_callback, &context); 3783 SCNetworkReachabilitySetDispatchQueue(clone, q); 3784 3785 // wait for reply (or timeout) 3786 ret = dispatch_semaphore_wait(s, dispatch_time(DISPATCH_TIME_NOW, 3787 SYNC_DNS_QUERY_TIMEOUT_NSEC)); 3788 if (ret != 0) { 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); 3793 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 3799 3800 MUTEX_LOCK(&clonePrivate->lock); 3801 3802 // update target info with what's available 3803 if (clonePrivate->resolvedAddresses != NULL) { 3804 CFRelease(clonePrivate->resolvedAddresses); 3805 clonePrivate->resolvedAddresses = NULL; 3806 } 3807 if ((clonePrivate->dnsAddresses != NULL) && 3808 isA_CFArray(clonePrivate->dnsAddresses) && 3809 (CFArrayGetCount(clonePrivate->dnsAddresses) > 0)) { 3810 clonePrivate->resolvedAddresses = CFArrayCreateMutableCopy(NULL, 3811 0, 3812 clonePrivate->dnsAddresses); 3813 } 3814 if (clonePrivate->resolvedAddresses != NULL) { 3815 // if timeout w/partial results 3816 clonePrivate->resolvedError = NETDB_SUCCESS; 3817 } else { 3818 // if timeout w/no results 3819 clonePrivate->resolvedAddresses = CFRetain(kCFNull); 3820 clonePrivate->resolvedError = EAI_NONAME; 3821 } 3822 3823 MUTEX_UNLOCK(&clonePrivate->lock); 3824 }); 3825 } 3826 3827 // cancel request 3828 SCNetworkReachabilitySetDispatchQueue(clone, NULL); 3829 SCNetworkReachabilitySetCallback(clone, NULL, NULL); 3830 3831 // transfer reply 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)); 3841 3842 CFRelease(clone); 3843 dispatch_release(s); 3844 3845 return; 3846} 3847 3848 3849#pragma mark - 3850#pragma mark Network Information support 3851 3852 3853// Note: protected by _hn_target_queue() 3854static int network_changed_token; 3855static Boolean network_changed_token_valid = FALSE; 3856 3857 3858/* 3859 * nwi_refresh_handler 3860 * 3861 * Called to notify/update network changed events 3862 * - should be exec'd on the _hn_changes_queue() 3863 */ 3864static void 3865nwi_refresh_handler() 3866{ 3867 CFArrayRef changes; 3868 CFStringRef key; 3869 __block SCDynamicStoreRef store = NULL; 3870 3871 dispatch_sync(_hn_target_queue(), ^{ 3872 if (network_changed_token_valid && (hn_store != NULL)) { 3873 store = CFRetain(hn_store); 3874 } 3875 }); 3876 3877 if (store == NULL) { 3878 return; 3879 } 3880 3881 // Fake a network change. 3882 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 3883 kSCDynamicStoreDomainState, 3884 kSCEntNetIPv4); 3885 changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks); 3886 __SCNetworkReachabilityHandleChanges(store, changes, NULL); 3887 CFRelease(changes); 3888 CFRelease(key); 3889 3890 CFRelease(store); 3891 return; 3892} 3893 3894 3895/* 3896 * nwi_refresh_enable 3897 * 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() 3901 */ 3902static Boolean 3903nwi_refresh_enable(dispatch_queue_t q) 3904{ 3905 uint32_t status; 3906 3907 status = notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE_NWI, // trailing nwi_state_get_notify_key() 3908 &network_changed_token, 3909 q, 3910 ^(int token){ 3911 nwi_refresh_handler(); 3912 }); 3913 if (status != NOTIFY_STATUS_OK) { 3914 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed for network changes, status=%u"), status); 3915 return FALSE; 3916 } 3917 3918 network_changed_token_valid = TRUE; 3919 3920 return TRUE; 3921} 3922 3923 3924/* 3925 * nwi_refresh_disable 3926 * 3927 * Called to stop monitoring for network changes 3928 * - caller must be running on the _hn_target_queue() 3929 */ 3930static void 3931nwi_refresh_disable() 3932{ 3933 if (network_changed_token_valid) { 3934 (void)notify_cancel(network_changed_token); 3935 network_changed_token_valid = FALSE; 3936 } 3937 3938 return; 3939} 3940 3941 3942#pragma mark - 3943#pragma mark Sleep/wake support 3944 3945 3946#if !TARGET_OS_IPHONE 3947 3948// Note: protected by _hn_target_queue() 3949static IOPMConnection power_changed_connection = NULL; 3950static const CFStringRef power_changed_key = CFSTR("*** EARLY WAKE ***"); 3951 3952 3953/* 3954 * power_refresh_handler 3955 * 3956 * Called to notify/update power capability changed events 3957 * - should be exec'd on the _hn_changes_queue() 3958 */ 3959static void 3960power_refresh_handler(void *param, 3961 IOPMConnection connection, 3962 IOPMConnectionMessageToken token, 3963 IOPMSystemPowerStateCapabilities capabilities) 3964{ 3965 Boolean change; 3966 IOReturn ret; 3967 __block SCDynamicStoreRef store = NULL; 3968 3969 dispatch_sync(_hn_target_queue(), ^{ 3970 if ((power_changed_connection != NULL) && (hn_store != NULL)) { 3971 store = CFRetain(hn_store); 3972 } 3973 }); 3974 3975 if (store == NULL) { 3976 return; 3977 } 3978 3979 // check for [relevant] changes 3980 change = ((power_capabilities ^ capabilities) & POWER_CAPABILITIES_NETWORK) != 0; 3981 3982 // update capabilities 3983 power_capabilities = capabilities; 3984 3985 if (change) { 3986 CFArrayRef changes; 3987 3988 // fake a network change. 3989 changes = CFArrayCreate(NULL, (const void **)&power_changed_key, 1, &kCFTypeArrayCallBacks); 3990 __SCNetworkReachabilityHandleChanges(store, changes, NULL); 3991 CFRelease(changes); 3992 } 3993 3994 ret = IOPMConnectionAcknowledgeEvent(connection, token); 3995 if (ret != kIOReturnSuccess) { 3996 SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionAcknowledgeEvent failed, 0x%08x"), ret); 3997 } 3998 3999 CFRelease(store); 4000 return; 4001} 4002 4003 4004/* 4005 * power_refresh_enable 4006 * 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() 4010 */ 4011static Boolean 4012power_refresh_enable(dispatch_queue_t q) 4013{ 4014 IOPMConnection connection = NULL; 4015 IOReturn ret; 4016 4017 ret = IOPMConnectionCreate(CFSTR("com.apple.SCNetworkReachability"), 4018 kIOPMEarlyWakeNotification | kIOPMSleepWakeInterest, 4019 &connection); 4020 if (ret != kIOReturnSuccess) { 4021 SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionCreate failed, 0x%08x"), ret); 4022 goto failed; 4023 } 4024 4025 ret = IOPMConnectionSetNotification(connection, NULL, power_refresh_handler); 4026 if (ret != kIOReturnSuccess) { 4027 SCLog(TRUE, LOG_ERR, CFSTR("IOPMConnectionSetNotification failed, 0x%08x"), ret); 4028 goto failed; 4029 } 4030 4031 power_changed_connection = connection; 4032 IOPMConnectionSetDispatchQueue(connection, q); 4033 power_capabilities = IOPMConnectionGetSystemCapabilities(); 4034 4035 return TRUE; 4036 4037 failed: 4038 4039 if (connection != NULL) { 4040 IOPMConnectionRelease(connection); 4041 } 4042 4043 return FALSE; 4044} 4045 4046 4047static void 4048power_refresh_disable() 4049{ 4050 if (power_changed_connection != NULL) { 4051 IOPMConnectionSetDispatchQueue(power_changed_connection, NULL); 4052 IOPMConnectionRelease(power_changed_connection); 4053 power_changed_connection = NULL; 4054 } 4055 4056 return; 4057} 4058 4059#endif // !TARGET_OS_IPHONE 4060 4061 4062 4063 4064 4065 4066#pragma mark - 4067#pragma mark OnDemand 4068 4069 4070SCNetworkServiceRef 4071SCNetworkReachabilityCopyOnDemandService(SCNetworkReachabilityRef target, 4072 CFDictionaryRef *userOptions) 4073{ 4074 SCNetworkServiceRef service = NULL; 4075 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 4076 4077 if (!isA_SCNetworkReachability(target)) { 4078 _SCErrorSet(kSCStatusInvalidArgument); 4079 return NULL; 4080 } 4081 4082 if (targetPrivate->onDemandServiceID != NULL) { 4083 service = _SCNetworkServiceCopyActive(NULL, targetPrivate->onDemandServiceID); 4084 } 4085 4086 if (userOptions != NULL) { 4087 if (targetPrivate->onDemandName != NULL) { 4088 *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 4089 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, targetPrivate->onDemandName); 4090 } else { 4091 *userOptions = NULL; 4092 } 4093 } 4094 4095 return service; 4096} 4097 4098 4099 4100 4101static void 4102__SCNetworkReachabilityOnDemandCheckCallback(SCNetworkReachabilityRef onDemandServer, 4103 SCNetworkReachabilityFlags onDemandFlags, 4104 void *info) 4105{ 4106 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info; 4107 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 4108 4109 MUTEX_LOCK(&targetPrivate->lock); 4110 4111 if (!targetPrivate->scheduled) { 4112 // if not currently scheduled 4113 MUTEX_UNLOCK(&targetPrivate->lock); 4114 return; 4115 } 4116 4117 SCLog(_sc_debug, LOG_INFO, CFSTR("%sOnDemand \"server\" status changed (now 0x%08x)"), 4118 targetPrivate->log_prefix, 4119 onDemandFlags); 4120 4121 if (targetPrivate->type == reachabilityTypeName) { 4122 // make sure that we resolve the name again 4123 targetPrivate->needResolve = TRUE; 4124 } 4125 4126 __SCNetworkReachabilityUpdate(target); 4127 4128 MUTEX_UNLOCK(&targetPrivate->lock); 4129 4130 return; 4131} 4132 4133 4134static Boolean 4135__SCNetworkReachabilityOnDemandCheck(ReachabilityStoreInfoRef store_info, 4136 SCNetworkReachabilityRef target, 4137 Boolean onDemandRetry, 4138 SCNetworkReachabilityFlags *flags) 4139{ 4140 SCNetworkConnectionRef connection = NULL; 4141 SCNetworkConnectionType connectionType = kSCNetworkConnectionTypeUnknown; 4142 Boolean isAppLayerVPN = FALSE; 4143 Boolean isOnDemandService = FALSE; 4144 Boolean ok = 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; 4151 4152 MUTEX_ASSERT_HELD(&targetPrivate->lock); 4153 4154 if (targetPrivate->onDemandName == NULL) { 4155 targetPrivate->onDemandName = CFStringCreateWithCString(NULL, targetPrivate->name, kCFStringEncodingUTF8); 4156 } 4157 4158 /* 4159 * check if an OnDemand VPN configuration matches the name. 4160 */ 4161 4162 connection = SCNetworkConnectionCreate(kCFAllocatorDefault, NULL, NULL); 4163 if (connection == NULL) { 4164 goto done; 4165 } 4166 4167 /* set select options */ 4168 selectOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 4169 if (selectOptions == NULL) { 4170 goto done; 4171 } 4172 4173 CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, targetPrivate->onDemandName); 4174 CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry, onDemandRetry ? kCFBooleanTrue : kCFBooleanFalse); 4175 CFDictionaryAddValue(selectOptions, kSCNetworkConnectionSelectionOptionNoUserPrefs, kCFBooleanTrue); 4176 4177 /* select service. May be On Demand or App Layer VPN */ 4178 if (!SCNetworkConnectionSelectServiceWithOptions(connection, selectOptions)) { 4179 goto done; 4180 } 4181 4182 /* get reachability flags (of VPN server) */ 4183 (void) SCNetworkConnectionGetReachabilityInfo(connection, flags, NULL); 4184 4185 connectionType = SCNetworkConnectionGetType(connection); 4186 if (connectionType == kSCNetworkConnectionTypeAppLayerVPN) { 4187 isAppLayerVPN = TRUE; 4188 } 4189 4190 /* get on-demand info */ 4191 onDemandServiceID = SCNetworkConnectionCopyServiceID(connection); 4192 if (SCNetworkConnectionCopyOnDemandInfo(connection, &onDemandRemoteAddress, &onDemandStatus)) { 4193 if (onDemandRemoteAddress != NULL) { 4194 isOnDemandService = TRUE; 4195 ok = TRUE; 4196 } 4197 } 4198 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, 4203 *flags); 4204 if (*flags & kSCNetworkReachabilityFlagsReachable) { 4205 // if VPN "server" is reachable 4206 4207 if (!(*flags & kSCNetworkReachabilityFlagsTransientConnection)) { 4208 // start w/clean flags if not already layered on a transient network 4209 *flags = kSCNetworkReachabilityFlagsReachable; 4210 } 4211 4212 *flags |= kSCNetworkReachabilityFlagsTransientConnection; 4213 if (onDemandStatus != kSCNetworkConnectionConnected) { 4214 *flags |= kSCNetworkReachabilityFlagsConnectionRequired; 4215 } 4216 4217 SCLog(_sc_debug, LOG_INFO, CFSTR("%s status = isReachable%s"), 4218 (onDemandStatus != kSCNetworkConnectionConnected) 4219 ? " (after App Layer connect)" : "", 4220 targetPrivate->log_prefix); 4221 } 4222 4223 success = TRUE; 4224 goto done; 4225 } 4226 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; 4232 } 4233 4234 if (targetPrivate->onDemandServer != NULL) { 4235 SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer, NULL, NULL); 4236 if (targetPrivate->dispatchQueue != NULL) { 4237 // unschedule 4238 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, NULL, NULL, TRUE); 4239 } else if (targetPrivate->rls != NULL) { 4240 CFIndex i; 4241 CFIndex n; 4242 4243 // unschedule 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); 4248 4249 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, rl, rlMode, TRUE); 4250 } 4251 } 4252 4253 CFRelease(targetPrivate->onDemandServer); 4254 targetPrivate->onDemandServer = NULL; 4255 } 4256 4257 if (targetPrivate->onDemandServiceID != NULL) { 4258 CFRelease(targetPrivate->onDemandServiceID); 4259 targetPrivate->onDemandServiceID = NULL; 4260 } 4261 } 4262 4263 if (ok) { 4264 if (onDemandStatus != kSCNetworkConnectionConnected) { 4265 /* 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. 4268 */ 4269 if (targetPrivate->onDemandServer == NULL) { 4270 SCNetworkReachabilityPrivateRef demandPrivate; 4271 CFMutableDictionaryRef options; 4272 4273 options = CFDictionaryCreateMutable(NULL, 4274 0, 4275 &kCFTypeDictionaryKeyCallBacks, 4276 &kCFTypeDictionaryValueCallBacks); 4277 CFDictionarySetValue(options, kSCNetworkReachabilityOptionNodeName, onDemandRemoteAddress); 4278 CFDictionarySetValue(options, kSCNetworkReachabilityOptionConnectionOnDemandBypass, kCFBooleanTrue); 4279 if (targetPrivate->serverBypass) { 4280 CFDictionarySetValue(options, kSCNetworkReachabilityOptionServerBypass, kCFBooleanTrue); 4281 } 4282 targetPrivate->onDemandServer = SCNetworkReachabilityCreateWithOptions(NULL, options); 4283 CFRelease(options); 4284 4285 // indent OnDemand target 4286 demandPrivate = (SCNetworkReachabilityPrivateRef)targetPrivate->onDemandServer; 4287 strlcat(demandPrivate->log_prefix, ".... ", sizeof(demandPrivate->log_prefix)); 4288 4289 if (targetPrivate->scheduled) { 4290 SCNetworkReachabilityContext context = { 0, NULL, CFRetain, CFRelease, CFCopyDescription }; 4291 4292 context.info = (void *)target; 4293 SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer, 4294 __SCNetworkReachabilityOnDemandCheckCallback, 4295 &context); 4296 4297 // schedule server reachability to match that of the target 4298 if (targetPrivate->dispatchQueue != NULL) { 4299 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, NULL, NULL, targetPrivate->dispatchQueue, TRUE); 4300 } else { 4301 CFIndex i; 4302 CFIndex n; 4303 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); 4308 4309 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, rl, rlMode, NULL, TRUE); 4310 } 4311 } 4312 } 4313 } 4314 4315 SCLog(_sc_debug, LOG_INFO, CFSTR("%s status * = 0x%08x"), 4316 targetPrivate->log_prefix, 4317 *flags); 4318 4319 4320 if ((*flags & kSCNetworkReachabilityFlagsReachable) && !(*flags & kSCNetworkReachabilityFlagsConnectionRequired)) { 4321 // if VPN "server" is [still] reachable 4322 4323 if (!(*flags & kSCNetworkReachabilityFlagsTransientConnection)) { 4324 // start w/clean flags if not already layered on a transient network 4325 *flags = kSCNetworkReachabilityFlagsReachable; 4326 } 4327 4328 *flags |= kSCNetworkReachabilityFlagsTransientConnection; 4329 *flags |= kSCNetworkReachabilityFlagsConnectionRequired; 4330 *flags |= kSCNetworkReachabilityFlagsConnectionOnDemand; 4331 4332 // set 'InterventionRequired' if the OnDemand connection is paused 4333 if (SCNetworkConnectionIsOnDemandSuspended(connection)) { 4334 *flags |= kSCNetworkReachabilityFlagsInterventionRequired; 4335 } 4336 4337 if (_sc_debug) { 4338 SCLog(TRUE, LOG_INFO, CFSTR("%s service * = %@"), 4339 targetPrivate->log_prefix, 4340 onDemandServiceID); 4341 SCLog(TRUE, LOG_INFO, CFSTR("%s status = isReachable (after OnDemand connect)"), 4342 targetPrivate->log_prefix); 4343 } 4344 4345 success = TRUE; 4346 } 4347 } 4348 4349 if (onDemandRemoteAddress != NULL) { 4350 if (targetPrivate->onDemandRemoteAddress == NULL) { 4351 targetPrivate->onDemandRemoteAddress = CFRetain(onDemandRemoteAddress); 4352 } 4353 } 4354 4355 if (onDemandServiceID != NULL) { 4356 if (targetPrivate->onDemandServiceID == NULL) { 4357 targetPrivate->onDemandServiceID = CFRetain(onDemandServiceID); 4358 } 4359 } 4360 } 4361 4362 done: 4363 4364 if (onDemandServiceID != NULL) { 4365 CFRelease(onDemandServiceID); 4366 } 4367 if (onDemandRemoteAddress != NULL) { 4368 CFRelease(onDemandRemoteAddress); 4369 } 4370 if (connection != NULL) { 4371 CFRelease(connection); 4372 } 4373 if (selectOptions != NULL) { 4374 CFRelease(selectOptions); 4375 } 4376 return success; 4377} 4378 4379 4380/* 4381 * OnDemand configuration handling 4382 * 4383 * Notes : 4384 * 4385 * 1. We have a "contract" with discoveryd that for EVERY network 4386 * or DNS configuration change that should warrant our [re-]starting 4387 * a query, discoveryd will acknowledge the latest DNS configuration. 4388 * 4389 * 2. IPMonitor also posts a notification AFTER every network or DNS 4390 * configuration change. 4391 * 4392 * 3. We use IPMonitor's "trailing edge" as a signal to restart any 4393 * by-name queries. 4394 */ 4395 4396 4397// Note: protected by _hn_target_queue() 4398static int onDemand_refresh_token; 4399static Boolean onDemand_refresh_token_valid = FALSE; 4400 4401 4402/* 4403 * onDemand_refresh_handler 4404 * 4405 * Called to notify/update all SCNetworkReachability targets of 4406 * OnDemand changes. 4407 * - should be exec'd on the _hn_changes_queue() 4408 */ 4409static void 4410onDemand_refresh_handler() 4411{ 4412 CFArrayRef changes; 4413 CFStringRef key; 4414 __block SCDynamicStoreRef store = NULL; 4415 4416 dispatch_sync(_hn_target_queue(), ^{ 4417 if (onDemand_refresh_token_valid && (hn_store != NULL)) { 4418 store = CFRetain(hn_store); 4419 } 4420 }); 4421 4422 if (store == NULL) { 4423 return; 4424 } 4425 4426 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 4427 kSCDynamicStoreDomainState, 4428 kSCEntNetOnDemand); 4429 changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks); 4430 __SCNetworkReachabilityHandleChanges(store, changes, NULL); 4431 CFRelease(changes); 4432 CFRelease(key); 4433 4434 CFRelease(store); 4435 return; 4436} 4437 4438 4439/* 4440 * onDemand_refresh_enable 4441 * 4442 * Called to monitor for OnDemand changes. 4443 * - caller must be running on the _hn_target_queue() 4444 */ 4445static Boolean 4446onDemand_refresh_enable(dispatch_queue_t q) 4447{ 4448 uint32_t status; 4449 4450 status = notify_register_dispatch(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY, 4451 &onDemand_refresh_token, 4452 q, 4453 ^(int token){ 4454 onDemand_refresh_handler(); 4455 }); 4456 if (status != NOTIFY_STATUS_OK) { 4457 SCLog(TRUE, LOG_INFO, CFSTR("notify_register_dispatch() failed, status=%u"), status); 4458 return FALSE; 4459 } 4460 4461 onDemand_refresh_token_valid = TRUE; 4462 4463 return TRUE; 4464} 4465 4466 4467/* 4468 * onDemand_refresh_disable 4469 * 4470 * Called to stop monitoring for OnDemand changes 4471 * - caller must be running on the _hn_target_queue() 4472 */ 4473static void 4474onDemand_refresh_disable() 4475{ 4476 (void)notify_cancel(onDemand_refresh_token); 4477 onDemand_refresh_token_valid = FALSE; 4478 return; 4479} 4480 4481 4482 4483 4484#pragma mark - 4485#pragma mark Reachability Flags 4486 4487 4488static Boolean 4489__SCNetworkReachabilityGetFlags(ReachabilityStoreInfoRef store_info, 4490 SCNetworkReachabilityRef target, 4491 ReachabilityInfo *reach_info, 4492 Boolean async) 4493{ 4494 CFMutableArrayRef addresses = NULL; 4495 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 4496 ReachabilityInfo my_info = NOT_REACHABLE; 4497 Boolean ok = TRUE; 4498 4499 MUTEX_ASSERT_HELD(&targetPrivate->lock); 4500 4501 _reach_set(reach_info, &NOT_REACHABLE, reach_info->cycle, targetPrivate->if_index, targetPrivate->if_name); 4502 4503 if (!isA_SCNetworkReachability(target)) { 4504 _SCErrorSet(kSCStatusInvalidArgument); 4505 return FALSE; 4506 } 4507 4508#if TARGET_OS_IPHONE 4509 if (isReachabilityTypeName(targetPrivate->type) && 4510 !async && 4511 pthread_is_threaded_np() && 4512 pthread_main_np()) { 4513 SCLog(TRUE, LOG_WARNING, CFSTR("Warning: sync SCNetworkReachability (by-name) query on main thread")); 4514 } 4515#endif // TARGET_OS_IPHONE 4516 4517 if (!targetPrivate->serverBypass) { 4518 if (!targetPrivate->serverActive) { 4519 4520 ok = __SCNetworkReachabilityServer_targetAdd(target); 4521 if (!ok) { 4522 targetPrivate->serverBypass = TRUE; 4523 } 4524 } 4525 4526 if (targetPrivate->serverActive) { 4527 ok = __SCNetworkReachabilityServer_targetStatus(target); 4528 if (!ok) { 4529 SCLog(TRUE, LOG_DEBUG, 4530 CFSTR("__SCNetworkReachabilityGetFlags _targetStatus() failed")); 4531 _SCErrorSet(kSCStatusFailed); 4532 goto done; 4533 } 4534 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); 4541 goto done; 4542 } 4543 } 4544 4545 4546 switch (targetPrivate->type) { 4547 case reachabilityTypeAddress : 4548 case reachabilityTypeAddressPair : { 4549 /* 4550 * Check "local" address 4551 */ 4552 if (targetPrivate->localAddress != NULL) { 4553 /* 4554 * Check "local" address 4555 */ 4556 ok = checkAddress(store_info, 4557 targetPrivate->localAddress, 4558 targetPrivate->if_index, 4559 &my_info, 4560 targetPrivate->log_prefix); 4561 if (!ok) { 4562 goto done2; /* not today */ 4563 } 4564 4565 if (!(my_info.flags & kSCNetworkReachabilityFlagsIsLocalAddress)) { 4566 goto done2; /* not reachable, non-"local" address */ 4567 } 4568 } 4569 4570 /* 4571 * Check "remote" address 4572 */ 4573 if ((targetPrivate->remoteAddress != NULL) && 4574 (targetPrivate->localAddress != targetPrivate->remoteAddress)) { 4575 /* 4576 * in cases where we have different "local" and "remote" addresses 4577 * we need to re-initialize the to-be-returned flags. 4578 */ 4579 my_info = NOT_REACHABLE; 4580 4581 /* 4582 * Check "remote" address 4583 */ 4584 ok = checkAddress(store_info, 4585 targetPrivate->remoteAddress, 4586 targetPrivate->if_index, 4587 &my_info, 4588 targetPrivate->log_prefix); 4589 if (!ok) { 4590 goto done2; /* not today */ 4591 } 4592 } 4593 4594 break; 4595 4596 } 4597 4598 case reachabilityTypeName : 4599 case reachabilityTypePTR : { 4600 int error; 4601 int ns_dns_config = -1; 4602 SCNetworkReachabilityFlags ns_flags = 0; 4603 uint32_t ns_if_index = 0; 4604 4605 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error); 4606 if ((addresses != NULL) || (error != NETDB_SUCCESS)) { 4607 /* if resolved or an error had been detected */ 4608 if (!async) { 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) { 4615 /* 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 4618 */ 4619 goto checkResolvedAddresses; 4620 } 4621 } 4622 4623 if (!targetPrivate->onDemandBypass) { 4624 Boolean onDemand; 4625 SCNetworkReachabilityFlags onDemandFlags = 0; 4626 4627 /* 4628 * before we attempt our initial DNS query, check if there is 4629 * an OnDemand configuration that we should be using. 4630 */ 4631 onDemand = __SCNetworkReachabilityOnDemandCheck(store_info, target, FALSE, &onDemandFlags); 4632 if (onDemand) { 4633 /* if OnDemand connection is needed */ 4634 my_info.flags = onDemandFlags; 4635 goto done; 4636 } 4637 } 4638 4639 targetPrivate->dnsBlocked = FALSE; 4640 4641 /* update the reachability of the DNS servers */ 4642 _SC_R_updateResolverReachability(store_info, 4643 &ns_flags, 4644 &targetPrivate->haveDNS, 4645 targetPrivate->name, 4646 targetPrivate->if_index, 4647 &ns_if_index, 4648 &ns_dns_config, 4649 targetPrivate->log_prefix); 4650 4651 4652 // save resolver reachability flags 4653 targetPrivate->resolverFlags = ns_flags; 4654 4655 if (rankReachability(ns_flags) < 2) { 4656 /* 4657 * if DNS servers are not (or are no longer) reachable, set 4658 * flags based on the availability of configured (but not 4659 * active) services. 4660 */ 4661 4662 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"), 4663 targetPrivate->log_prefix); 4664 4665 if (!targetPrivate->dnsBlocked) { 4666 ok = checkAddress(store_info, 4667 NULL, 4668 targetPrivate->if_index, 4669 &my_info, 4670 targetPrivate->log_prefix); 4671 if (!ok) { 4672 SCLog(_sc_debug, LOG_INFO, CFSTR("%sNo available networks"), 4673 targetPrivate->log_prefix); 4674 goto done2; 4675 } 4676 } else { 4677 // if not checking "available" networks 4678 my_info.flags = ns_flags; 4679 my_info.if_index = ns_if_index; 4680 } 4681 4682 if (async && targetPrivate->scheduled) { 4683 /* 4684 * return "host not found", set flags appropriately, 4685 * and schedule notification. 4686 */ 4687 __SCNetworkReachabilitySetResolvedError(target, EAI_NONAME); 4688 my_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending); 4689 4690 SCLog(_sc_debug, LOG_INFO, CFSTR("%sno DNS servers are reachable"), 4691 targetPrivate->log_prefix); 4692 __SCNetworkReachabilityUpdate(target); 4693 } 4694 4695 break; 4696 } 4697 4698 if (targetPrivate->resolverBypass) { 4699 if (targetPrivate->haveDNS) { 4700 /* 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). 4705 */ 4706 my_info.flags = ns_flags; 4707 my_info.if_index = ns_if_index; 4708 } 4709 break; 4710 } 4711 4712 if (async) { 4713 /* for async requests we return the last known status */ 4714 my_info = targetPrivate->info; 4715 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); 4722 } 4723 if ((addresses != NULL) || (error != NETDB_SUCCESS)) { 4724 /* updated reachability based on the previous reply */ 4725 goto checkResolvedAddresses; 4726 } 4727 break; 4728 } 4729 4730 SCLog(_sc_debug, LOG_INFO, 4731 CFSTR("%sstart DNS query for name = %s"), 4732 targetPrivate->log_prefix, 4733 targetPrivate->name); 4734 4735 /* 4736 * initiate an DNS query w/DNSServiceGetAddrInfo 4737 */ 4738 enqueueDNSQuery(target); 4739 break; 4740 } 4741 4742 SCLog(_sc_debug, LOG_INFO, 4743 CFSTR("%scheckName(%s)"), 4744 targetPrivate->log_prefix, 4745 targetPrivate->name); 4746 4747 /* 4748 * OK, all of the DNS name servers are available. Let's 4749 * resolve the nodename into an address. 4750 */ 4751 sync_DNS_query(target); 4752 4753 if (!(targetPrivate->dnsHaveTimeout && targetPrivate->dnsHaveError)) { 4754 // if target reach info is valid 4755 memcpy(reach_info, &targetPrivate->info, sizeof(ReachabilityInfo)); 4756 goto done2; 4757 } 4758 4759 if (addresses != NULL) CFRelease(addresses); 4760 addresses = (CFMutableArrayRef)SCNetworkReachabilityCopyResolvedAddress(target, &error); 4761 4762 checkResolvedAddresses : 4763 4764 /* 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. 4768 */ 4769 my_info = NOT_REACHABLE; 4770 4771 if ((targetPrivate->type == reachabilityTypeName) && isA_CFArray(addresses)) { 4772 CFIndex i; 4773 CFIndex n = CFArrayGetCount(addresses); 4774 struct sockaddr *sa; 4775 4776 for (i = 0; i < n; i++) { 4777 ReachabilityInfo ns_info = NOT_REACHABLE; 4778 4779 sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i)); 4780 4781 ok = checkAddress(store_info, 4782 sa, 4783 targetPrivate->if_index, 4784 &ns_info, 4785 targetPrivate->log_prefix); 4786 if (!ok) { 4787 goto done2; /* not today */ 4788 } 4789 4790 if (rankReachability(ns_info.flags) > rankReachability(my_info.flags)) { 4791 /* return the best case result */ 4792 my_info = ns_info; 4793 if (rankReachability(my_info.flags) == 2) { 4794 /* can't get any better than REACHABLE */ 4795 break; 4796 } 4797 } 4798 } 4799 4800 if (_sc_debug) { 4801 for (i++; i < n; i++) { 4802 sa = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addresses, i)); 4803 log_address("skipAddress", 4804 sa, 4805 targetPrivate->if_index, 4806 targetPrivate->log_prefix); 4807 } 4808 } 4809 } else if ((targetPrivate->type == reachabilityTypePTR) && isA_CFArray(addresses)) { 4810 CFIndex i; 4811 CFIndex n = CFArrayGetCount(addresses); 4812 4813 my_info = NOT_REACHABLE; 4814 4815 for (i = 0; i < n; i++) { 4816 if (i == 0) { 4817 my_info.flags = kSCNetworkReachabilityFlagsReachable; 4818 } 4819 4820 if (_sc_debug) { 4821 CFStringRef ptrName; 4822 4823 ptrName = CFArrayGetValueAtIndex(addresses, i); 4824 SCLog(TRUE, LOG_INFO, CFSTR("%sPTR name(%@)"), 4825 targetPrivate->log_prefix, 4826 ptrName); 4827 } 4828 } 4829 } else { 4830 if ((error == EAI_NONAME) 4831#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) 4832 || (error == EAI_NODATA) 4833#endif 4834 ) { 4835 /* 4836 * the target host name could not be resolved 4837 */ 4838 if (!targetPrivate->onDemandBypass) { 4839 Boolean onDemand; 4840 SCNetworkReachabilityFlags onDemandFlags = 0; 4841 4842 /* 4843 * our initial DNS query failed, check again to see if there 4844 * there is an OnDemand configuration that we should be using. 4845 */ 4846 onDemand = __SCNetworkReachabilityOnDemandCheck(store_info, target, TRUE, &onDemandFlags); 4847 if (onDemand) { 4848 /* if OnDemand connection is needed */ 4849 my_info.flags = onDemandFlags; 4850 goto done; 4851 } 4852 } 4853 4854 4855 if (!targetPrivate->haveDNS) { 4856 /* 4857 * No DNS servers are defined. Set flags based on 4858 * the availability of configured (but not active) 4859 * services. 4860 */ 4861 ok = checkAddress(store_info, 4862 NULL, 4863 targetPrivate->if_index, 4864 &my_info, 4865 targetPrivate->log_prefix); 4866 if (!ok) { 4867 goto done2; /* not today */ 4868 } 4869 4870 if ((my_info.flags & kSCNetworkReachabilityFlagsReachable) && 4871 (my_info.flags & kSCNetworkReachabilityFlagsConnectionRequired)) { 4872 /* 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. 4875 */ 4876 break; 4877 } 4878 4879 /* Host not found, not reachable! */ 4880 my_info = NOT_REACHABLE; 4881 } 4882 } 4883 } 4884 4885 break; 4886 } 4887 } 4888 4889 done: 4890 4891 4892 _reach_set(reach_info, &my_info, targetPrivate->cycle, targetPrivate->if_index, targetPrivate->if_name); 4893 4894 done2 : 4895 4896 if (addresses != NULL) CFRelease(addresses); 4897 return ok; 4898} 4899 4900int 4901SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target) 4902{ 4903 int if_index = -1; 4904 Boolean ok = TRUE; 4905 ReachabilityStoreInfo store_info; 4906 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 4907 4908 if (!isA_SCNetworkReachability(target)) { 4909 _SCErrorSet(kSCStatusInvalidArgument); 4910 return if_index; 4911 } 4912 4913 ReachabilityStoreInfo_init(&store_info); 4914 4915 MUTEX_LOCK(&targetPrivate->lock); 4916 4917 if (targetPrivate->scheduled) { 4918 // if being watched, return the last known (and what should be current) status 4919 goto done; 4920 } 4921 4922 4923 ok = __SCNetworkReachabilityGetFlags(&store_info, target, &targetPrivate->info, FALSE); 4924 4925 done : 4926 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; 4931 } 4932 4933 MUTEX_UNLOCK(&targetPrivate->lock); 4934 ReachabilityStoreInfo_free(&store_info); 4935 return if_index; 4936} 4937 4938 4939Boolean 4940SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target, 4941 SCNetworkReachabilityFlags *flags) 4942{ 4943 Boolean ok = TRUE; 4944 ReachabilityStoreInfo store_info; 4945 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 4946 4947 if (!isA_SCNetworkReachability(target)) { 4948 _SCErrorSet(kSCStatusInvalidArgument); 4949 return FALSE; 4950 } 4951 4952 ReachabilityStoreInfo_init(&store_info); 4953 4954 MUTEX_LOCK(&targetPrivate->lock); 4955 4956 if (targetPrivate->scheduled) { 4957 // if being watched, return the last known (and what should be current) status 4958 *flags = targetPrivate->info.flags & kSCNetworkReachabilityFlagsMask; 4959 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); 4964 } 4965 4966 goto done; 4967 } 4968 4969 4970 ok = __SCNetworkReachabilityGetFlags(&store_info, target, &targetPrivate->info, FALSE); 4971 if (_sc_debug) { 4972 SCLog(TRUE, LOG_INFO, CFSTR("%s flags = 0x%08x"), targetPrivate->log_prefix, targetPrivate->info.flags); 4973 } 4974 4975 *flags = targetPrivate->info.flags & kSCNetworkReachabilityFlagsMask; 4976 4977 done : 4978 4979 MUTEX_UNLOCK(&targetPrivate->lock); 4980 ReachabilityStoreInfo_free(&store_info); 4981 return ok; 4982} 4983 4984 4985#pragma mark - 4986#pragma mark Notifications 4987 4988 4989/* 4990 * __SCNetworkReachabilityHandleChanges 4991 * 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() 4995 */ 4996static void 4997__SCNetworkReachabilityHandleChanges(SCDynamicStoreRef store, 4998 CFArrayRef changedKeys, 4999 void *info) 5000{ 5001 Boolean dnsConfigChanged = FALSE; 5002 CFIndex i; 5003 Boolean forcedChange = FALSE; 5004 CFStringRef key; 5005 Boolean match; 5006 CFIndex nChanges; 5007 CFIndex nGlobals = 0; 5008 CFIndex nTargets; 5009 Boolean neChanged = FALSE; 5010 Boolean networkConfigChanged = FALSE; 5011 struct timeval now; 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; 5020 5021 nChanges = CFArrayGetCount(changedKeys); 5022 if (nChanges == 0) { 5023 /* if no changes */ 5024 return; 5025 } 5026 5027 5028 dispatch_sync(_hn_target_queue(), ^{ 5029 /* grab the currently watched targets */ 5030 if (hn_targets != NULL) { 5031 watchers = CFSetCreateCopy(NULL, hn_targets); 5032 } 5033 }); 5034 5035 nTargets = (watchers != NULL) ? CFSetGetCount(watchers) : 0; 5036 if (nTargets == 0) { 5037 /* if no addresses being monitored */ 5038 goto done; 5039 } 5040 5041 /* grab the current time */ 5042 (void)gettimeofday(&now, NULL); 5043 5044#if !TARGET_OS_IPHONE 5045 match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), power_changed_key); 5046 if (match) { 5047 /* handle "early" wake notification */ 5048 nGlobals++; 5049 powerStatusChanged = TRUE; 5050 } 5051#endif // !TARGET_OS_IPHONE 5052 5053 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 5054 kSCDynamicStoreDomainState, 5055 kSCEntNetDNS); 5056 match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key); 5057 CFRelease(key); 5058 if (match) { 5059 nGlobals++; 5060 dnsConfigChanged = TRUE; /* the DNS server(s) have changed */ 5061 } 5062 5063 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, 5064 kSCDynamicStoreDomainState, 5065 kSCEntNetOnDemand); 5066 match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), key); 5067 CFRelease(key); 5068 if (match) { 5069 nGlobals++; 5070 onDemandConfigChanged = TRUE; /* the OnDemand configuration has changed */ 5071 5072 // force OnDemand configuration refresh (if SC notification arrives before BSD notify) 5073 __SCNetworkConnectionForceOnDemandConfigurationRefresh(); 5074 } 5075 5076 5077 5078 match = CFArrayContainsValue(changedKeys, CFRangeMake(0, nChanges), SCNETWORKREACHABILITY_TRIGGER_KEY); 5079 if (match) { 5080 nGlobals++; 5081 forcedChange = TRUE; /* an SCDynamicStore driven "network" change */ 5082 } 5083 5084 if (nChanges > nGlobals) { 5085 networkConfigChanged = TRUE; 5086 } 5087 5088 if (_sc_debug) { 5089 unsigned int changes = 0; 5090 static const char *change_strings[] = { 5091 // with no "power" status change 5092 "", // 00000 5093 "network ", // 00001 5094 "DNS ", // 00010 5095 "network and DNS ", // 00011 5096 "OnDemand ", // 00100 5097 "network and OnDemand ", // 00101 5098 "DNS and OnDemand ", // 00110 5099 "network, DNS, and OnDemand ", // 00111 5100 "NE ", // 01000 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 5110 "power", // 10000 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 5127 }; 5128 5129#if !TARGET_OS_IPHONE 5130 #define PWR 16 5131 if (powerStatusChanged) { 5132 changes |= PWR; 5133 } 5134#endif // !TARGET_OS_IPHONE 5135 5136 #define NE 8 5137 if (neChanged) { 5138 changes |= NE; 5139 } 5140 5141 #define VOD 4 5142 if (onDemandConfigChanged) { 5143 changes |= VOD; 5144 } 5145 5146 #define DNS 2 5147 if (dnsConfigChanged) { 5148 changes |= DNS; 5149 } 5150 5151 #define NET 1 5152 if (networkConfigChanged) { 5153 changes |= NET; 5154 } 5155 5156 SCLog(TRUE, LOG_INFO, 5157 CFSTR("process %s%s%sconfiguration change"), 5158 forcedChange ? "[forced] " : "", 5159 change_strings[changes]); 5160 } 5161 5162 ReachabilityStoreInfo_init(&store_info); 5163 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; 5171 5172 MUTEX_LOCK(&targetPrivate->lock); 5173 5174 5175 if (dnsConfigChanged) { 5176 targetPrivate->last_dns = now; 5177 } 5178 5179 if (networkConfigChanged) { 5180 targetPrivate->last_network = now; 5181 } 5182 5183#if !TARGET_OS_IPHONE 5184 if (powerStatusChanged) { 5185 targetPrivate->last_power = now; 5186 } 5187#endif // !TARGET_OS_IPHONE 5188 5189 if (isReachabilityTypeName(targetPrivate->type)) { 5190 Boolean dnsChanged = (dnsConfigChanged | 5191 dnsNeedsUpdate | 5192 onDemandConfigChanged | 5193 neChanged); 5194 5195 if (!dnsChanged) { 5196 /* 5197 * if the DNS configuration didn't change we still need to 5198 * check that the DNS servers are accessible. 5199 */ 5200 Boolean ns_blocked = FALSE; 5201 int ns_dns_config = -1; 5202 SCNetworkReachabilityFlags ns_flags = 0; 5203 uint32_t ns_if_index = 0; 5204 Boolean ok; 5205 5206 /* update the reachability of the DNS servers */ 5207 ok = ReachabilityStoreInfo_update(&store_info, &store, AF_UNSPEC); 5208 if (ok) { 5209 _SC_R_updateResolverReachability(&store_info, 5210 &ns_flags, 5211 &targetPrivate->haveDNS, 5212 targetPrivate->name, 5213 targetPrivate->if_index, 5214 &ns_if_index, 5215 &ns_dns_config, 5216 targetPrivate->log_prefix); 5217 } else { 5218 ns_flags = kSCNetworkReachabilityFlagsReachable; 5219 dnsChanged = TRUE; 5220 } 5221 5222 5223 if (rankReachability(ns_flags) < 2) { 5224 /* 5225 * if DNS servers are not (or are no longer) reachable, set 5226 * flags based on the availability of configured (but not 5227 * active) services. 5228 */ 5229 SCLog(_sc_debug, LOG_INFO, CFSTR("%sDNS server(s) not available"), 5230 targetPrivate->log_prefix); 5231 dnsChanged = TRUE; 5232 } 5233 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; 5239 dnsChanged = TRUE; 5240 } 5241 } 5242 5243 if (dnsChanged) { 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); 5251 } 5252 5253 /* schedule request to resolve the name again */ 5254 targetPrivate->needResolve = TRUE; 5255 } 5256 } 5257 5258 if (forcedChange) { 5259 targetPrivate->cycle++; 5260 } 5261 5262 if (targetPrivate->scheduled) { 5263 __SCNetworkReachabilityUpdate(target); 5264 } 5265 5266 MUTEX_UNLOCK(&targetPrivate->lock); 5267 } 5268 if (targets != targets_q) CFAllocatorDeallocate(NULL, targets); 5269 5270 ReachabilityStoreInfo_free(&store_info); 5271 5272 done : 5273 5274 if (watchers != NULL) CFRelease(watchers); 5275 return; 5276} 5277 5278 5279/* 5280 * __SCNetworkReachabilityHandleStoreChanges 5281 * 5282 * Called to process SCDynamicStore network configuration changes. 5283 * - should be exec'd on the _hn_changes_queue() 5284 */ 5285static void 5286__SCNetworkReachabilityHandleStoreChanges(SCDynamicStoreRef store, 5287 CFArrayRef changedKeys, 5288 void *info) 5289{ 5290 nwi_state_t nwi_state; 5291 5292 if ((CFArrayGetCount(changedKeys) == 1) && 5293 CFArrayContainsValue(changedKeys, CFRangeMake(0, 1), SCNETWORKREACHABILITY_TRIGGER_KEY)) { 5294 goto update; 5295 } 5296 5297 /* "something" [else] changed, start fresh */ 5298 ReachabilityStoreInfo_save(NULL); 5299 5300 nwi_state = nwi_state_copy(); 5301 if (nwi_state != NULL) { 5302 // if we have some networking 5303 nwi_state_release(nwi_state); 5304 return; 5305 } 5306 5307 // if no networking, use the [SC] changes to add/update 5308 // the kSCNetworkReachabilityFlagsConnectionRequired flag 5309 5310 update : 5311 5312 __SCNetworkReachabilityHandleChanges(store, changedKeys, info); 5313 return; 5314} 5315 5316 5317#if !TARGET_OS_IPHONE 5318 5319static Boolean 5320darkWakeNotify(SCNetworkReachabilityRef target) 5321{ 5322 return FALSE; 5323} 5324 5325 5326static Boolean 5327systemIsAwake(IOPMSystemPowerStateCapabilities power_capabilities) 5328{ 5329 if ((power_capabilities & POWER_CAPABILITIES_NETWORK) != POWER_CAPABILITIES_NETWORK) { 5330 // if we're not fully awake (from a networking point of view). 5331 return FALSE; 5332 } 5333 5334 return TRUE; 5335} 5336 5337#endif // !TARGET_OS_IPHONE 5338 5339 5340static void 5341reachPerform(void *info) 5342{ 5343 void *context_info; 5344 void (*context_release)(const void *); 5345 unsigned int n; 5346 ReachabilityInfo reach_info; 5347 SCNetworkReachabilityCallBack rlsFunction; 5348 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info; 5349 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 5350 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, 5356 n); 5357 } 5358 5359 MUTEX_LOCK(&targetPrivate->lock); 5360 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); 5367 return; 5368 } 5369 5370 // capture current state 5371 memcpy(&reach_info, &targetPrivate->info, sizeof(ReachabilityInfo)); 5372 5373 // callout 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; 5378 } else { 5379 context_info = targetPrivate->rlsContext.info; 5380 context_release = NULL; 5381 } 5382 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); 5386 5387 MUTEX_UNLOCK(&targetPrivate->lock); 5388 5389 if (rlsFunction != NULL) { 5390 (*rlsFunction)(target, 5391 reach_info.flags & kSCNetworkReachabilityFlagsMask, 5392 context_info); 5393 } 5394 5395 if (context_release != NULL) { 5396 (*context_release)(context_info); 5397 } 5398 5399 return; 5400} 5401 5402 5403/* 5404 * reachUpdate 5405 * 5406 * - caller must *not* be holding the target lock 5407 * - caller must be running on the __SCNetworkReachability_concurrent_queue() 5408 */ 5409static Boolean 5410reachUpdate(SCNetworkReachabilityRef target) 5411{ 5412 uint64_t cycle; 5413 Boolean defer = FALSE; 5414 Boolean forced; 5415 Boolean ok; 5416 ReachabilityInfo reach_info = NOT_REACHABLE; 5417 ReachabilityStoreInfo store_info; 5418 Boolean target_debug; 5419 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 5420 5421 target_debug = (_sc_debug && !targetPrivate->quiet); 5422 5423 if (target_debug) { 5424 SCLog(TRUE, LOG_INFO, CFSTR("%schecking target reachability"), 5425 targetPrivate->log_prefix); 5426 } 5427 5428 5429 MUTEX_LOCK(&targetPrivate->lock); 5430 5431 if (!targetPrivate->scheduled) { 5432 // if not currently scheduled 5433 MUTEX_UNLOCK(&targetPrivate->lock); 5434 return FALSE; 5435 } 5436 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); 5441 if (!ok) { 5442 /* if reachability status not available */ 5443 if (target_debug) { 5444 SCLog(TRUE, LOG_INFO, CFSTR("%sflags not available"), 5445 targetPrivate->log_prefix); 5446 } 5447 reach_info = NOT_REACHABLE; 5448 } 5449 5450#if !TARGET_OS_IPHONE 5451 /* 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. 5455 */ 5456 if (!systemIsAwake(power_capabilities)) { 5457 /* if this is a maintenace wake */ 5458 reach_info.sleeping = TRUE; 5459 5460 if (rankReachability(reach_info.flags) >= rankReachability(targetPrivate->info.flags)) { 5461 /* 5462 * don't report the change if the new reachability flags are 5463 * the same or "better" 5464 */ 5465 defer = !darkWakeNotify(target); 5466 } else if (!__reach_changed(&targetPrivate->last_notify, &reach_info)) { 5467 /* 5468 * if we have already posted this change 5469 */ 5470 defer = !darkWakeNotify(target); 5471 } 5472 } 5473#endif // !TARGET_OS_IPHONE 5474 5475 cycle = targetPrivate->cycle; 5476 forced = ((cycle != 0) && (targetPrivate->info.cycle != cycle)); 5477 5478 /* 5479 * (A) (B) (C) 5480 * reach resolve 5481 * changed forced pending 5482 * ======= ======= ======= 5483 * N N N No change 5484 * N N Y No change 5485 * N Y N Change (forced && !resolve pending) 5486 * N Y Y No change (suppress forced w/resolve pending) 5487 * Y N/A N/A Change 5488 * Y N/A N/A Change 5489 * Y N/A N/A Change 5490 * Y N/A N/A Change 5491 * 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 5497 */ 5498 if ((!forced || (reach_info.flags == kSCNetworkReachabilityFlagsFirstResolvePending)) 5499 && !__reach_changed(&targetPrivate->info, &reach_info)) { 5500 if (target_debug) { 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, 5505 reach_info.flags, 5506 reach_info.if_index, 5507 reach_info.sleeping ? ", z" : "", 5508 defer ? ", deferred" : "", 5509 forced ? ", forced" : ""); 5510 } else { 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" : "", 5517 reach_info.flags, 5518 reach_info.if_index, 5519 reach_info.sleeping ? ", z" : "", 5520 defer ? ", deferred" : "", 5521 forced ? ", forced" : ""); 5522 } 5523 } 5524 MUTEX_UNLOCK(&targetPrivate->lock); 5525 return FALSE; 5526 } 5527 5528 if (target_debug) { 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" : "", 5535 reach_info.flags, 5536 reach_info.if_index, 5537 reach_info.sleeping ? ", z" : "", 5538 defer ? ", deferred" : "", 5539 forced ? ", forced" : ""); 5540 } 5541 5542 /* update flags / interface */ 5543 _reach_set(&targetPrivate->info, &reach_info, cycle, targetPrivate->if_index, targetPrivate->if_name); 5544 5545 /* as needed, defer the notification */ 5546 if (defer) { 5547 MUTEX_UNLOCK(&targetPrivate->lock); 5548 return FALSE; 5549 } 5550 5551 MUTEX_UNLOCK(&targetPrivate->lock); 5552 5553 return TRUE; 5554} 5555 5556 5557Boolean 5558SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target, 5559 SCNetworkReachabilityCallBack callout, 5560 SCNetworkReachabilityContext *context) 5561{ 5562 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 5563 5564 MUTEX_LOCK(&targetPrivate->lock); 5565 5566 if (targetPrivate->rlsContext.release != NULL) { 5567 /* let go of the current context */ 5568 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info); 5569 } 5570 5571 targetPrivate->rlsFunction = callout; 5572 targetPrivate->rlsContext.info = NULL; 5573 targetPrivate->rlsContext.retain = NULL; 5574 targetPrivate->rlsContext.release = NULL; 5575 targetPrivate->rlsContext.copyDescription = NULL; 5576 if (context) { 5577 bcopy(context, &targetPrivate->rlsContext, sizeof(SCNetworkReachabilityContext)); 5578 if (context->retain != NULL) { 5579 targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info); 5580 } 5581 } 5582 5583 MUTEX_UNLOCK(&targetPrivate->lock); 5584 5585 return TRUE; 5586} 5587 5588 5589static CFStringRef 5590reachRLSCopyDescription(const void *info) 5591{ 5592 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info; 5593 5594 return CFStringCreateWithFormat(NULL, 5595 NULL, 5596 CFSTR("<SCNetworkReachability RLS> {target = %p}"), 5597 target); 5598} 5599 5600 5601static Boolean 5602__SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target, 5603 CFRunLoopRef runLoop, 5604 CFStringRef runLoopMode, 5605 dispatch_queue_t queue, 5606 Boolean onDemand) 5607{ 5608 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 5609 Boolean init = FALSE; 5610 __block Boolean ok = FALSE; 5611 5612 MUTEX_LOCK(&targetPrivate->lock); 5613 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); 5617 goto done; 5618 } 5619 5620 if (!targetPrivate->serverBypass) { 5621 if (!targetPrivate->serverActive) { 5622 5623 ok = __SCNetworkReachabilityServer_targetAdd(target); 5624 if (!ok) { 5625 targetPrivate->serverBypass = TRUE; 5626 } 5627 } 5628 5629 if (targetPrivate->serverActive) { 5630 if (targetPrivate->scheduled) { 5631 // if already scheduled 5632 goto watch; 5633 } 5634 5635 ok = __SCNetworkReachabilityServer_targetSchedule(target); 5636 if (!ok) { 5637 SCLog(TRUE, LOG_DEBUG, 5638 CFSTR("__SCNetworkReachabilityScheduleWithRunLoop _targetMonitor() failed")); 5639 _SCErrorSet(kSCStatusFailed); 5640 goto done; 5641 } 5642 5643 goto watch; 5644 } 5645 } 5646 5647 /* schedule the SCNetworkReachability did-something-change handler */ 5648 5649 dispatch_sync(_hn_target_queue(), ^{ 5650 ok = FALSE; 5651 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 5662 5663 hn_store = SCDynamicStoreCreate(NULL, 5664 CFSTR("SCNetworkReachability"), 5665 __SCNetworkReachabilityHandleStoreChanges, 5666 NULL); 5667 if (hn_store == NULL) { 5668 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreCreate() failed")); 5669 return; 5670 } 5671 5672 ReachabilityStoreInfo_keys(&keys, &patterns); 5673 CFArrayAppendValue(keys, SCNETWORKREACHABILITY_TRIGGER_KEY); // force posting reach change 5674 ok = SCDynamicStoreSetNotificationKeys(hn_store, keys, patterns); 5675 CFRelease(keys); 5676 CFRelease(patterns); 5677 if (!ok) { 5678 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetNotificationKeys() failed")); 5679 CFRelease(hn_store); 5680 hn_store = NULL; 5681 return; 5682 } 5683 5684 ok = SCDynamicStoreSetDispatchQueue(hn_store, _hn_changes_queue()); 5685 if (!ok) { 5686 SCLog(TRUE, LOG_ERR, CFSTR("SCDynamicStoreSetDispatchQueue() failed")); 5687 CFRelease(hn_store); 5688 hn_store = NULL; 5689 return; 5690 } 5691 5692 // watch for network information changes 5693 watch_nwi = nwi_refresh_enable(_hn_changes_queue()); 5694 if (!watch_nwi) { 5695 goto fail; 5696 } 5697 5698 // watch for DNS configuration (resolver reachability) changes 5699 watch_dns_configuration = dns_configuration_watch(); 5700 if (!watch_dns_configuration) { 5701 goto fail; 5702 } 5703 5704 // watch for changes affecting DNS queries 5705 watch_dns_changes = dns_refresh_enable(_hn_changes_queue()); 5706 if (!watch_dns_changes) { 5707 goto fail; 5708 } 5709 5710#if !TARGET_OS_IPHONE 5711 // watch for power capabilities (sleep/wake) changes 5712 watch_power = power_refresh_enable(_hn_changes_queue()); 5713 if (!watch_power) { 5714 goto fail; 5715 } 5716#endif // !TARGET_OS_IPHONE 5717 5718 // watch for OnDemand network changes 5719 watch_onDemand_networking = onDemand_refresh_enable(_hn_changes_queue()); 5720 if (!watch_onDemand_networking) { 5721 goto fail; 5722 } 5723 5724 5725 5726 hn_targets = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); 5727 ReachabilityStoreInfo_enable(TRUE); 5728 5729 goto scheduled; 5730 5731 fail : 5732 5733 ok = FALSE; 5734 5735 5736 5737 if (watch_onDemand_networking) { 5738 onDemand_refresh_disable(); 5739 } 5740 5741#if !TARGET_OS_IPHONE 5742 if (watch_power) { 5743 power_refresh_disable(); 5744 } 5745#endif // !TARGET_OS_IPHONE 5746 5747 if (watch_dns_changes) { 5748 dns_refresh_disable(); 5749 } 5750 5751 if (watch_dns_configuration) { 5752 dns_configuration_unwatch(); 5753 } 5754 5755 if (watch_nwi) { 5756 nwi_refresh_disable(); 5757 } 5758 5759 SCDynamicStoreSetDispatchQueue(hn_store, NULL); 5760 CFRelease(hn_store); 5761 hn_store = NULL; 5762 5763 _SCErrorSet(kSCStatusFailed); 5764 5765 return; 5766 } 5767 5768 scheduled : 5769 5770 CFSetAddValue(hn_targets, target); 5771 5772 ok = TRUE; 5773 }); 5774 5775 if (!ok) { 5776 goto done; 5777 } 5778 5779 watch : 5780 5781 if (!targetPrivate->scheduled) { 5782 CFRunLoopSourceContext context = { 0 // version 5783 , (void *)target // info 5784 , CFRetain // retain 5785 , CFRelease // release 5786 , reachRLSCopyDescription // copyDescription 5787 , CFEqual // equal 5788 , CFHash // hash 5789 , NULL // schedule 5790 , NULL // cancel 5791 , reachPerform // perform 5792 }; 5793 5794 if (runLoop != NULL) { 5795 targetPrivate->rls = CFRunLoopSourceCreate(NULL, 0, &context); 5796 targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 5797 } 5798 5799 if (isReachabilityTypeName(targetPrivate->type)) { 5800 /* 5801 * we're now scheduled so let's ensure that we 5802 * are starting with a clean slate before we 5803 * resolve the name 5804 */ 5805 if (targetPrivate->resolvedAddresses != NULL) { 5806 CFRelease(targetPrivate->resolvedAddresses); 5807 targetPrivate->resolvedAddresses = NULL; 5808 } 5809 targetPrivate->resolvedError = NETDB_SUCCESS; 5810 targetPrivate->needResolve = TRUE; 5811 _reach_set(&targetPrivate->info, 5812 &NOT_REACHABLE, 5813 targetPrivate->info.cycle, 5814 targetPrivate->if_index, 5815 targetPrivate->if_name); 5816 targetPrivate->info.flags |= kSCNetworkReachabilityFlagsFirstResolvePending; 5817 _reach_set(&targetPrivate->serverInfo, 5818 &NOT_REACHABLE, 5819 targetPrivate->serverInfo.cycle, 5820 targetPrivate->if_index, 5821 targetPrivate->if_name); 5822 targetPrivate->serverInfo.flags |= kSCNetworkReachabilityFlagsFirstResolvePending; 5823 } 5824 5825 5826 targetPrivate->scheduled = TRUE; 5827 5828 init = TRUE; 5829 } 5830 5831 if (queue != NULL) { 5832 // retain dispatch queue 5833 dispatch_retain(queue); 5834 targetPrivate->dispatchQueue = queue; 5835 5836 // 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. 5844 // 5845 5846 // make sure that we have group to track any async requests 5847 targetPrivate->dispatchGroup = dispatch_group_create(); 5848 5849 // retain the target ... and release it when the group is released 5850 CFRetain(target); 5851 dispatch_set_context(targetPrivate->dispatchGroup, (void *)target); 5852 dispatch_set_finalizer_f(targetPrivate->dispatchGroup, (dispatch_function_t)CFRelease); 5853 } else { 5854 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) { 5855 /* 5856 * if we do not already have host notifications scheduled with 5857 * this runLoop / runLoopMode 5858 */ 5859 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode); 5860 } 5861 5862 _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList); 5863 } 5864 5865 if (init) { 5866 ReachabilityInfo reach_info = NOT_REACHABLE; 5867 ReachabilityStoreInfo store_info; 5868 5869 /* 5870 * if we have yet to schedule SC notifications for this address 5871 * - initialize current reachability status 5872 */ 5873 ReachabilityStoreInfo_init(&store_info); 5874 if (__SCNetworkReachabilityGetFlags(&store_info, target, &reach_info, TRUE)) { 5875 /* 5876 * if reachability status available 5877 * - set flags 5878 * - schedule notification to report status via callback 5879 */ 5880 reach_info.flags |= (targetPrivate->info.flags & kSCNetworkReachabilityFlagsFirstResolvePending); 5881 _reach_set(&targetPrivate->info, 5882 &reach_info, 5883 targetPrivate->cycle, 5884 targetPrivate->if_index, 5885 targetPrivate->if_name); 5886 __SCNetworkReachabilityUpdate(target); 5887 } else { 5888 /* if reachability status not available, async lookup started */ 5889 _reach_set(&targetPrivate->info, 5890 &NOT_REACHABLE, 5891 targetPrivate->cycle, 5892 targetPrivate->if_index, 5893 targetPrivate->if_name); 5894 _reach_set(&targetPrivate->serverInfo, 5895 &NOT_REACHABLE, 5896 targetPrivate->cycle, 5897 targetPrivate->if_index, 5898 targetPrivate->if_name); 5899 } 5900 ReachabilityStoreInfo_free(&store_info); 5901 } 5902 5903 if (targetPrivate->onDemandServer != NULL) { 5904 __SCNetworkReachabilityScheduleWithRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, queue, TRUE); 5905 } 5906 5907 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%sscheduled"), 5908 targetPrivate->log_prefix); 5909 5910 ok = TRUE; 5911 5912 done : 5913 5914 MUTEX_UNLOCK(&targetPrivate->lock); 5915 return ok; 5916} 5917 5918 5919static Boolean 5920__SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target, 5921 CFRunLoopRef runLoop, 5922 CFStringRef runLoopMode, 5923 Boolean onDemand) 5924{ 5925 dispatch_group_t drainGroup = NULL; 5926 dispatch_queue_t drainQueue = NULL; 5927 CFIndex n = 0; 5928 Boolean ok = FALSE; 5929 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 5930 5931 // hold a reference while we unschedule 5932 CFRetain(target); 5933 5934 MUTEX_LOCK(&targetPrivate->lock); 5935 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); 5939 goto done; 5940 } 5941 5942 if (!targetPrivate->scheduled) { 5943 // if not currently scheduled 5944 _SCErrorSet(kSCStatusInvalidArgument); 5945 goto done; 5946 } 5947 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); 5953 } 5954 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; 5960 } else { 5961 if (!_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) { 5962 // if not currently scheduled 5963 _SCErrorSet(kSCStatusInvalidArgument); 5964 goto done; 5965 } 5966 5967 if (targetPrivate->onDemandServer != NULL) { 5968 __SCNetworkReachabilityUnscheduleFromRunLoop(targetPrivate->onDemandServer, runLoop, runLoopMode, TRUE); 5969 } 5970 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); 5975 5976 if (n == 0) { 5977 // if *all* notifications have been unscheduled 5978 if (targetPrivate->onDemandServer != NULL) { 5979 SCNetworkReachabilitySetCallback(targetPrivate->onDemandServer, NULL, NULL); 5980 } 5981 CFRelease(targetPrivate->rlList); 5982 targetPrivate->rlList = NULL; 5983 CFRunLoopSourceInvalidate(targetPrivate->rls); 5984 CFRelease(targetPrivate->rls); 5985 targetPrivate->rls = NULL; 5986 } 5987 } 5988 } 5989 5990 if (n == 0) { 5991 // 5992 // Cancel our request for server monitoring 5993 // 5994 if (targetPrivate->serverActive) { 5995 ok = __SCNetworkReachabilityServer_targetUnschedule(target); 5996 if (!ok) { 5997 SCLog(TRUE, LOG_DEBUG, 5998 CFSTR("__SCNetworkReachabilityUnscheduleFromRunLoop _targetMonitor() failed")); 5999 _SCErrorSet(kSCStatusFailed); 6000 } 6001 } 6002 6003 // if *all* notifications have been unscheduled 6004 targetPrivate->scheduled = FALSE; 6005 } 6006 6007 if (targetPrivate->serverActive) { 6008 goto unwatch; 6009 } 6010 6011 if (n == 0) { 6012 if (targetPrivate->dnsActive) { 6013 // if we have an active [m]DNS query 6014 dequeueDNSQuery(target); 6015 } 6016 6017 dispatch_sync(_hn_target_queue(), ^{ 6018 CFSetRemoveValue(hn_targets, target); 6019 6020 if (onDemand) { 6021 return; 6022 } 6023 6024 if (CFSetGetCount(hn_targets) > 0) { 6025 return; 6026 } 6027 6028 // if we are no longer monitoring any targets 6029 SCDynamicStoreSetDispatchQueue(hn_store, NULL); 6030 CFRelease(hn_store); 6031 hn_store = NULL; 6032 CFRelease(hn_targets); 6033 hn_targets = NULL; 6034 6035 ReachabilityStoreInfo_enable(FALSE); 6036 ReachabilityStoreInfo_save(NULL); 6037 6038 /* 6039 * until we start monitoring again, ensure that 6040 * any resources associated with watching network 6041 * and configuration changes have been released. 6042 */ 6043 6044 6045 // OnDemand configuration 6046 onDemand_refresh_disable(); 6047 6048#if !TARGET_OS_IPHONE 6049 // sleep/wake & power capabilities 6050 power_refresh_disable(); 6051#endif // !TARGET_OS_IPHONE 6052 6053 // outstanding DNS queries 6054 dns_refresh_disable(); 6055 6056 // DNS configuration 6057 dns_configuration_unwatch(); 6058 6059 // nwi 6060 nwi_refresh_disable(); 6061 }); 6062 } 6063 6064 unwatch : 6065 6066 SCLog((_sc_debug && (_sc_log > 0)), LOG_INFO, CFSTR("%sunscheduled"), 6067 targetPrivate->log_prefix); 6068 6069 ok = TRUE; 6070 6071 done : 6072 6073 MUTEX_UNLOCK(&targetPrivate->lock); 6074 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 6080 }); 6081 } 6082 6083 // release our reference 6084 CFRelease(target); 6085 6086 return ok; 6087} 6088 6089Boolean 6090SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target, 6091 CFRunLoopRef runLoop, 6092 CFStringRef runLoopMode) 6093{ 6094 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) { 6095 _SCErrorSet(kSCStatusInvalidArgument); 6096 return FALSE; 6097 } 6098 6099 return __SCNetworkReachabilityScheduleWithRunLoop(target, runLoop, runLoopMode, NULL, FALSE); 6100} 6101 6102Boolean 6103SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target, 6104 CFRunLoopRef runLoop, 6105 CFStringRef runLoopMode) 6106{ 6107 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) { 6108 _SCErrorSet(kSCStatusInvalidArgument); 6109 return FALSE; 6110 } 6111 6112 return __SCNetworkReachabilityUnscheduleFromRunLoop(target, runLoop, runLoopMode, FALSE); 6113} 6114 6115Boolean 6116SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target, 6117 dispatch_queue_t queue) 6118{ 6119 Boolean ok = FALSE; 6120 6121 if (!isA_SCNetworkReachability(target)) { 6122 _SCErrorSet(kSCStatusInvalidArgument); 6123 return FALSE; 6124 } 6125 6126 if (queue != NULL) { 6127 ok = __SCNetworkReachabilityScheduleWithRunLoop(target, NULL, NULL, queue, FALSE); 6128 } else { 6129 ok = __SCNetworkReachabilityUnscheduleFromRunLoop(target, NULL, NULL, FALSE); 6130 } 6131 6132 return ok; 6133} 6134