this repo has no description
at fixPythonPipStalling 1952 lines 61 kB view raw
1/* 2 * Copyright (c) 2003-2019 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 <TargetConditionals.h> 38#include <sys/cdefs.h> 39#include <dispatch/dispatch.h> 40#include <dispatch/private.h> 41#include <pthread.h> 42#include <libkern/OSAtomic.h> 43#include <notify.h> 44#include <dnsinfo.h> 45#include <netinet/in.h> 46#include <arpa/inet.h> 47#include <netdb.h> 48#include <resolv.h> 49#include <unistd.h> 50#include <sys/ioctl.h> 51#include <sys/socket.h> 52#include <net/if.h> 53#include <net/if_dl.h> 54#include <net/if_types.h> 55#include <net/network_agent.h> 56 57#include <CoreFoundation/CoreFoundation.h> 58#include <CoreFoundation/CFRuntime.h> 59 60#define SC_LOG_HANDLE __log_SCNetworkReachability 61#define SC_LOG_HANDLE_TYPE static 62#include <SystemConfiguration/SystemConfiguration.h> 63#include <SystemConfiguration/SCValidation.h> 64#include <SystemConfiguration/SCPrivate.h> 65#include "SCD.h" 66#include "SCNetworkReachabilityInternal.h" 67 68 69 70#include <nw/private.h> 71 72#define DEBUG_REACHABILITY_TYPE_NAME "create w/name" 73#define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options" 74 75#define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address" 76#define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options" 77 78#define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair" 79#define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options" 80 81#define DEBUG_REACHABILITY_TYPE_PTR "create w/ptr" 82#define DEBUG_REACHABILITY_TYPE_PTR_OPTIONS " + options" 83 84static pthread_mutexattr_t lock_attr; 85 86#define MUTEX_INIT(m) { \ 87 int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \ 88 assert(_lock_); \ 89} 90 91#define MUTEX_LOCK(m) { \ 92 int _lock_ = (pthread_mutex_lock(m) == 0); \ 93 assert(_lock_); \ 94} 95 96#define MUTEX_UNLOCK(m) { \ 97 int _unlock_ = (pthread_mutex_unlock(m) == 0); \ 98 assert(_unlock_); \ 99} 100 101#define MUTEX_ASSERT_HELD(m) { \ 102 int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \ 103 assert(_locked_); \ 104} 105 106 107#define N_QUICK 64 108 109#define REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN "NetworkExtension" 110#define REACHABILITY_AGENT_DATA_KEY "data" 111 112 113static CFStringRef __SCNetworkReachabilityCopyDescription (CFTypeRef cf); 114static void __SCNetworkReachabilityDeallocate (CFTypeRef cf); 115 116static SCNetworkReachabilityFlags 117__SCNetworkReachabilityGetFlagsFromPath(nw_path_t path, 118 ReachabilityAddressType type, 119 nw_resolver_status_t resolverStatus, 120 nw_array_t resolvedEndpoints, 121 Boolean resolvedEndpointUseFlags, 122 SCNetworkReachabilityFlags resolvedEndpointFlags); 123 124static Boolean 125__SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate, 126 dispatch_queue_t queue); 127 128static CFTypeID __kSCNetworkReachabilityTypeID = _kCFRuntimeNotATypeID; 129 130 131static const CFRuntimeClass __SCNetworkReachabilityClass = { 132 0, // version 133 "SCNetworkReachability", // className 134 NULL, // init 135 NULL, // copy 136 __SCNetworkReachabilityDeallocate, // dealloc 137 NULL, // equal 138 NULL, // hash 139 NULL, // copyFormattingDesc 140 __SCNetworkReachabilityCopyDescription // copyDebugDesc 141}; 142 143 144static pthread_once_t initialized = PTHREAD_ONCE_INIT; 145 146static dispatch_queue_t 147_callback_queue() 148{ 149 static dispatch_once_t once; 150 static dispatch_queue_t q; 151 152 dispatch_once(&once, ^{ 153 q = dispatch_queue_create("SCNetworkReachability.callback", NULL); 154 }); 155 156 return q; 157} 158 159static os_log_t 160__log_SCNetworkReachability(void) 161{ 162 static os_log_t log = NULL; 163 164 if (log == NULL) { 165 log = os_log_create("com.apple.SystemConfiguration", "SCNetworkReachability"); 166 } 167 168 return log; 169} 170 171 172#pragma mark - 173#pragma mark SCNetworkReachability APIs 174 175 176static __inline__ CFTypeRef 177isA_SCNetworkReachability(CFTypeRef obj) 178{ 179 return (isA_CFType(obj, SCNetworkReachabilityGetTypeID())); 180} 181 182static CFStringRef 183_SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target) 184{ 185 CFAllocatorRef allocator = CFGetAllocator(target); 186 CFMutableStringRef str; 187 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 188 189 str = CFStringCreateMutable(allocator, 0); 190 switch (targetPrivate->type) { 191 case reachabilityTypeAddress : 192 case reachabilityTypeAddressPair : { 193 char buf[64]; 194 if (targetPrivate->localAddressEndpoint != NULL) { 195 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate->localAddressEndpoint), buf, sizeof(buf)); 196 CFStringAppendFormat(str, NULL, CFSTR("local address = %s"), 197 buf); 198 } 199 if (targetPrivate->remoteAddressEndpoint != NULL) { 200 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate->remoteAddressEndpoint), buf, sizeof(buf)); 201 CFStringAppendFormat(str, NULL, CFSTR("%s%saddress = %s"), 202 targetPrivate->localAddressEndpoint ? ", " : "", 203 (targetPrivate->type == reachabilityTypeAddressPair) ? "remote " : "", 204 buf); 205 } else { 206 CFStringAppendFormat(str, NULL, CFSTR("default path")); 207 } 208 break; 209 } 210 case reachabilityTypeName : { 211 CFStringAppendFormat(str, NULL, CFSTR("name = %s"), nw_endpoint_get_hostname(targetPrivate->hostnameEndpoint)); 212 break; 213 } 214 case reachabilityTypePTR : { 215 char buf[64]; 216 if (targetPrivate->remoteAddressEndpoint != NULL) { 217 _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate->remoteAddressEndpoint), buf, sizeof(buf)); 218 CFStringAppendFormat(str, NULL, CFSTR("ptr = %s"), 219 buf); 220 } 221 break; 222 } 223 } 224 225 if (targetPrivate->parameters != NULL) { 226 unsigned int if_index; 227 228 if_index = nw_parameters_get_required_interface_index(targetPrivate->parameters); 229 if (if_index != 0) { 230 CFStringAppendFormat(str, NULL, CFSTR(", if_index = %u"), if_index); 231 } 232 } 233 234 return str; 235} 236 237 238static CFStringRef 239__SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target) 240{ 241 CFAllocatorRef allocator = CFGetAllocator(target); 242 CFStringRef str; 243 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 244 245 str = CFStringCreateWithFormat(allocator, 246 NULL, 247 CFSTR("flags = 0x%08x, if_index = %u"), 248 __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, 249 targetPrivate->type, 250 targetPrivate->lastResolverStatus, 251 targetPrivate->lastResolvedEndpoints, 252 targetPrivate->lastResolvedEndpointHasFlags, 253 targetPrivate->lastResolvedEndpointFlags), 254 targetPrivate->lastResolvedEndpointHasFlags ? targetPrivate->lastResolvedEndpointInterfaceIndex 255 : nw_path_get_interface_index(targetPrivate->lastPath)); 256 return str; 257} 258 259 260static CFStringRef 261__SCNetworkReachabilityCopyDescription(CFTypeRef cf) 262{ 263 CFAllocatorRef allocator = CFGetAllocator(cf); 264 CFMutableStringRef result; 265 CFStringRef str; 266 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf; 267 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 268 269 MUTEX_LOCK(&targetPrivate->lock); 270 271 result = CFStringCreateMutable(allocator, 0); 272 CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkReachability %p [%p]> {"), cf, allocator); 273 274 // add target description 275 str = _SCNetworkReachabilityCopyTargetDescription(target); 276 CFStringAppend(result, str); 277 CFRelease(str); 278 279 // add additional "name" info 280 if (isReachabilityTypeName(targetPrivate->type)) { 281 if (targetPrivate->resolver && targetPrivate->lastResolverStatus == nw_resolver_status_invalid) { 282 CFStringAppendFormat(result, NULL, CFSTR(" (DNS query active)")); 283 } else if (targetPrivate->lastResolverStatus != nw_resolver_status_invalid) { 284 CFStringAppendFormat(result, NULL, CFSTR(" (%s"), (targetPrivate->lastResolverStatus == nw_resolver_status_complete) ? "complete" : "in progress"); 285 if (nw_array_get_count(targetPrivate->lastResolvedEndpoints) > 0) { 286 nw_array_apply(targetPrivate->lastResolvedEndpoints, ^bool(size_t index, nw_object_t object) { 287#pragma unused(index) 288 nw_endpoint_t endpoint = (nw_endpoint_t)object; 289 nw_endpoint_type_t endpoint_type = nw_endpoint_get_type(endpoint); 290 if (endpoint_type == nw_endpoint_type_address) { 291 char buf[64]; 292 const struct sockaddr *sa = nw_endpoint_get_address(endpoint); 293 _SC_sockaddr_to_string(sa, buf, sizeof(buf)); 294 CFStringAppendFormat(result, NULL, CFSTR(", %s"), buf); 295 } else if (endpoint_type == nw_endpoint_type_host) { 296 CFStringAppendFormat(result, NULL, CFSTR(", %s"), nw_endpoint_get_hostname(endpoint)); 297 } else { 298 CFStringAppendFormat(result, NULL, CFSTR(", unexpected nw_endpoint type: %d"), endpoint_type); 299 } 300 return TRUE; 301 }); 302 } else { 303 CFStringAppendFormat(result, NULL, CFSTR(", no addresses")); 304 } 305 CFStringAppendFormat(result, NULL, CFSTR(")")); 306 } 307 } 308 309 if (targetPrivate->resolverBypass) { 310 CFStringAppendFormat(result, NULL, CFSTR(", !resolve")); 311 } 312 313 // add flags 314 if (targetPrivate->scheduled) { 315 str = __SCNetworkReachabilityCopyTargetFlags(target); 316 CFStringAppendFormat(result, NULL, CFSTR(", %@"), str); 317 CFRelease(str); 318 } 319 320 CFStringAppendFormat(result, NULL, CFSTR("}")); 321 322 MUTEX_UNLOCK(&targetPrivate->lock); 323 324 return result; 325} 326 327 328static void 329__SCNetworkReachabilityDeallocate(CFTypeRef cf) 330{ 331 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf; 332 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 333 334 SC_log(LOG_DEBUG, "%srelease", targetPrivate->log_prefix); 335 336 /* release resources */ 337 MUTEX_LOCK(&targetPrivate->lock); 338 targetPrivate->scheduled = FALSE; 339 340 if (targetPrivate->hostnameEndpoint) { 341 nw_release(targetPrivate->hostnameEndpoint); 342 targetPrivate->hostnameEndpoint = NULL; 343 } 344 if (targetPrivate->localAddressEndpoint) { 345 nw_release(targetPrivate->localAddressEndpoint); 346 targetPrivate->localAddressEndpoint = NULL; 347 } 348 if (targetPrivate->remoteAddressEndpoint) { 349 nw_release(targetPrivate->remoteAddressEndpoint); 350 targetPrivate->remoteAddressEndpoint = NULL; 351 } 352 if (targetPrivate->parameters) { 353 nw_release(targetPrivate->parameters); 354 targetPrivate->parameters = NULL; 355 } 356 if (targetPrivate->lastPath) { 357 nw_release(targetPrivate->lastPath); 358 targetPrivate->lastPath = NULL; 359 } 360 if (targetPrivate->lastPathParameters) { 361 nw_release(targetPrivate->lastPathParameters); 362 targetPrivate->lastPathParameters = NULL; 363 } 364 if (targetPrivate->lastResolvedEndpoints) { 365 nw_release(targetPrivate->lastResolvedEndpoints); 366 targetPrivate->lastResolvedEndpoints = NULL; 367 } 368 369 if (targetPrivate->rlsContext.release != NULL) { 370 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info); 371 } 372 373 MUTEX_UNLOCK(&targetPrivate->lock); 374 375 pthread_mutex_destroy(&targetPrivate->lock); 376 377 return; 378} 379 380 381static void 382__SCNetworkReachabilityInitialize(void) 383{ 384 __kSCNetworkReachabilityTypeID = _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass); 385 386 pthread_mutexattr_init(&lock_attr); 387 pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_ERRORCHECK); 388 389 return; 390} 391 392static SCNetworkReachabilityPrivateRef 393__SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator) 394{ 395 SCNetworkReachabilityPrivateRef targetPrivate; 396 uint32_t size; 397 398 /* initialize runtime */ 399 pthread_once(&initialized, __SCNetworkReachabilityInitialize); 400 401 /* allocate target */ 402 size = sizeof(SCNetworkReachabilityPrivate) - sizeof(CFRuntimeBase); 403 targetPrivate = (SCNetworkReachabilityPrivateRef)_CFRuntimeCreateInstance(allocator, 404 __kSCNetworkReachabilityTypeID, 405 size, 406 NULL); 407 if (targetPrivate == NULL) { 408 return NULL; 409 } 410 411 /* initialize non-zero/NULL members */ 412 MUTEX_INIT(&targetPrivate->lock); 413 if (_sc_log > 0) { 414 snprintf(targetPrivate->log_prefix, 415 sizeof(targetPrivate->log_prefix), 416 "[%p] ", 417 targetPrivate); 418 } 419 420 return targetPrivate; 421} 422 423static const struct sockaddr * 424is_valid_address(const struct sockaddr *address) 425{ 426 const struct sockaddr *valid = NULL; 427 static Boolean warned = FALSE; 428 429 if ((address != NULL) && 430 (address->sa_len <= sizeof(struct sockaddr_storage))) { 431 switch (address->sa_family) { 432 case AF_INET : 433 if (address->sa_len >= sizeof(struct sockaddr_in)) { 434 valid = address; 435 } else if (!warned) { 436 SC_log(LOG_WARNING, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu", 437 address->sa_len, 438 sizeof(struct sockaddr_in)); 439 warned = TRUE; 440 } 441 break; 442 case AF_INET6 : 443 if (address->sa_len >= sizeof(struct sockaddr_in6)) { 444 valid = address; 445 } else if (!warned) { 446 SC_log(LOG_WARNING, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu", 447 address->sa_len, 448 sizeof(struct sockaddr_in6)); 449 warned = TRUE; 450 } 451 break; 452 default : 453 if (!warned) { 454 SC_log(LOG_WARNING, "SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d", 455 address->sa_family); 456 warned = TRUE; 457 } 458 } 459 } 460 461 return valid; 462} 463 464static bool 465__SCNetworkReachabilityAddressIsEmpty(const struct sockaddr *address) 466{ 467 if (address == NULL) { 468 return TRUE; 469 } 470 471 if (address->sa_family == AF_INET) { 472 return (((struct sockaddr_in *)(void *)address)->sin_addr.s_addr == 0); 473 } else if (address->sa_family == AF_INET6) { 474 return IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)(void *)address)->sin6_addr); 475 } else { 476 return FALSE; 477 } 478} 479 480SCNetworkReachabilityRef 481SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator, 482 const struct sockaddr *address) 483{ 484 const struct sockaddr *targetAddress; 485 SCNetworkReachabilityPrivateRef targetPrivate; 486 487 targetAddress = is_valid_address(address); 488 if (targetAddress == NULL) { 489 _SCErrorSet(kSCStatusInvalidArgument); 490 return NULL; 491 } 492 493 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator); 494 if (targetPrivate == NULL) { 495 return NULL; 496 } 497 498 targetPrivate->type = reachabilityTypeAddress; 499 500 if (!__SCNetworkReachabilityAddressIsEmpty(targetAddress)) { 501 targetPrivate->remoteAddressEndpoint = nw_endpoint_create_address(targetAddress); 502 } 503 504 SC_log(LOG_DEBUG, "%s%s %@", 505 targetPrivate->log_prefix, 506 DEBUG_REACHABILITY_TYPE_ADDRESS, 507 targetPrivate); 508 509 return (SCNetworkReachabilityRef)targetPrivate; 510} 511 512 513#if !TARGET_OS_IPHONE 514static Boolean 515is_ipv4_loopback(const struct sockaddr *sa) 516{ 517 uint32_t addr; 518 struct sockaddr_in *sin = (struct sockaddr_in *)(void *)sa; 519 520 if ((sa == NULL) || 521 (sa->sa_len < sizeof(struct sockaddr_in)) || 522 (sa->sa_family != AF_INET)) { 523 return FALSE; 524 } 525 526 addr = ntohl(sin->sin_addr.s_addr); 527 return IN_LOOPBACK(addr) ? TRUE : FALSE; 528} 529#endif // !TARGET_OS_IPHONE 530 531 532static Boolean 533is_same_address(const struct sockaddr *a, const struct sockaddr *b) 534{ 535 const void *a_addr; 536 const void *b_addr; 537 size_t len; 538 539 if ((a == NULL) || 540 (b == NULL) || 541 (a->sa_family != b->sa_family) || 542 (a->sa_len != b->sa_len )) { 543 return FALSE; 544 } 545 546 switch (a->sa_family) { 547 case AF_INET : { 548 struct sockaddr_in *a_sin = (struct sockaddr_in *)(void *)a; 549 struct sockaddr_in *b_sin = (struct sockaddr_in *)(void *)b; 550 551 /* ALIGN: assuming a (and b) are aligned, then cast ok. */ 552 a_addr = &a_sin->sin_addr; 553 b_addr = &b_sin->sin_addr; 554 len = sizeof(struct in_addr); 555 break; 556 } 557 558 case AF_INET6 : { 559 struct sockaddr_in6 *a_sin6 = (struct sockaddr_in6 *)(void *)a; 560 struct sockaddr_in6 *b_sin6 = (struct sockaddr_in6 *)(void *)b; 561 562 if (a_sin6->sin6_scope_id != b_sin6->sin6_scope_id) { 563 return FALSE; 564 } 565 566 a_addr = &a_sin6->sin6_addr; 567 b_addr = &b_sin6->sin6_addr; 568 len = sizeof(struct in6_addr); 569 break; 570 } 571 572 default : 573 a_addr = a; 574 b_addr = b; 575 len = a->sa_len; 576 break; 577 } 578 579 return (bcmp(a_addr, b_addr, len) == 0); 580} 581 582 583SCNetworkReachabilityRef 584SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator, 585 const struct sockaddr *localAddress, 586 const struct sockaddr *remoteAddress) 587{ 588 SCNetworkReachabilityPrivateRef targetPrivate; 589 590 if ((localAddress == NULL) && (remoteAddress == NULL)) { 591 _SCErrorSet(kSCStatusInvalidArgument); 592 return NULL; 593 } 594 595 if (localAddress != NULL) { 596 localAddress = is_valid_address(localAddress); 597 if (localAddress == NULL) { 598 _SCErrorSet(kSCStatusInvalidArgument); 599 return NULL; 600 } 601 } 602 603 if (remoteAddress != NULL) { 604 remoteAddress = is_valid_address(remoteAddress); 605 if (remoteAddress == NULL) { 606 _SCErrorSet(kSCStatusInvalidArgument); 607 return NULL; 608 } 609 } 610 611#if !TARGET_OS_IPHONE 612 // Check/fix for loopback IP --> remote IP (rdar://26561383) 613 if ((localAddress != NULL) && (remoteAddress != NULL) && 614 is_ipv4_loopback(localAddress) && !is_ipv4_loopback(remoteAddress)) { 615 static Boolean warned = FALSE; 616 617 if (!warned) { 618 SC_log(LOG_WARNING, "BUG: SCNetworkReachabilityCreateWithAddressPair() called with local <loopback>"); 619 SC_log(LOG_WARNING, "address and remote <non-loopback> address. To return the expected (but actually"); 620 SC_log(LOG_WARNING, "incorrect) result, switched to SCNetworkReachabilityCreateWithAddress() with"); 621 SC_log(LOG_WARNING, "the remote address."); 622 warned = TRUE; 623 } 624 625 return SCNetworkReachabilityCreateWithAddress(allocator, remoteAddress); 626 } 627#endif // !TARGET_OS_IPHONE 628 629 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator); 630 if (targetPrivate == NULL) { 631 return NULL; 632 } 633 634 targetPrivate->type = reachabilityTypeAddressPair; 635 636 if (localAddress != NULL) { 637 targetPrivate->localAddressEndpoint = nw_endpoint_create_address(localAddress); 638 } 639 640 if (remoteAddress != NULL) { 641 if (is_same_address(localAddress, remoteAddress)) { 642 targetPrivate->remoteAddressEndpoint = nw_retain(targetPrivate->localAddressEndpoint); 643 } else { 644 targetPrivate->remoteAddressEndpoint = nw_endpoint_create_address(remoteAddress); 645 } 646 } 647 648 targetPrivate->parameters = nw_parameters_create(); 649 nw_parameters_set_local_address(targetPrivate->parameters, targetPrivate->localAddressEndpoint); 650 651 SC_log(LOG_DEBUG, "%s%s %@", 652 targetPrivate->log_prefix, 653 DEBUG_REACHABILITY_TYPE_ADDRESSPAIR, 654 targetPrivate); 655 656 return (SCNetworkReachabilityRef)targetPrivate; 657} 658 659 660SCNetworkReachabilityRef 661SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator, 662 const char *nodename) 663{ 664 union { 665 struct sockaddr sa; 666 struct sockaddr_in sin; 667 struct sockaddr_in6 sin6; 668 } addr; 669 size_t nodenameLen; 670 SCNetworkReachabilityPrivateRef targetPrivate; 671 672 if (nodename == NULL) { 673 _SCErrorSet(kSCStatusInvalidArgument); 674 return NULL; 675 } 676 677 nodenameLen = strlen(nodename); 678 if (nodenameLen == 0) { 679 _SCErrorSet(kSCStatusInvalidArgument); 680 return NULL; 681 } 682 683 if (_SC_string_to_sockaddr(nodename, AF_UNSPEC, (void *)&addr, sizeof(addr)) != NULL) { 684 /* if this "nodename" is really an IP[v6] address in disguise */ 685 return SCNetworkReachabilityCreateWithAddress(allocator, &addr.sa); 686 } 687 688 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator); 689 if (targetPrivate == NULL) { 690 return NULL; 691 } 692 693 targetPrivate->type = reachabilityTypeName; 694 695 targetPrivate->hostnameEndpoint = nw_endpoint_create_host(nodename, "0"); 696 697 SC_log(LOG_DEBUG, "%s%s %@", 698 targetPrivate->log_prefix, 699 DEBUG_REACHABILITY_TYPE_NAME, 700 targetPrivate); 701 702 return (SCNetworkReachabilityRef)targetPrivate; 703} 704 705 706static SCNetworkReachabilityRef 707__SCNetworkReachabilityCreateWithPTR(CFAllocatorRef allocator, 708 const struct sockaddr *ptrAddress) 709{ 710 SCNetworkReachabilityPrivateRef targetPrivate; 711 712 ptrAddress = is_valid_address(ptrAddress); 713 if (ptrAddress == NULL) { 714 _SCErrorSet(kSCStatusInvalidArgument); 715 return NULL; 716 } 717 718 targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator); 719 if (targetPrivate == NULL) { 720 return NULL; 721 } 722 723 targetPrivate->type = reachabilityTypePTR; 724 725 targetPrivate->remoteAddressEndpoint = nw_endpoint_create_address(ptrAddress); 726 727 targetPrivate->parameters = nw_parameters_create(); 728 nw_parameters_set_resolve_ptr(targetPrivate->parameters, TRUE); 729 730 SC_log(LOG_DEBUG, "%s%s %@", 731 targetPrivate->log_prefix, 732 DEBUG_REACHABILITY_TYPE_PTR, 733 targetPrivate); 734 735 return (SCNetworkReachabilityRef)targetPrivate; 736} 737 738SCNetworkReachabilityRef 739SCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator, 740 CFDictionaryRef options) 741{ 742 const struct sockaddr *addr_l = NULL; 743 const struct sockaddr *addr_p = NULL; 744 const struct sockaddr *addr_r = NULL; 745 CFDataRef data; 746 Boolean haveOpt = FALSE; 747 CFStringRef interface = NULL; 748 CFStringRef nodename; 749 CFBooleanRef resolverBypass; 750 SCNetworkReachabilityRef target; 751 SCNetworkReachabilityPrivateRef targetPrivate; 752 unsigned int if_index = 0; 753 char if_name[IFNAMSIZ]; 754 CFDataRef sourceAppAuditToken = NULL; 755 CFStringRef sourceAppBundleID = NULL; 756 757 if (!isA_CFDictionary(options)) { 758 _SCErrorSet(kSCStatusInvalidArgument); 759 return NULL; 760 } 761 762 nodename = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionNodeName); 763 if ((nodename != NULL) && 764 (!isA_CFString(nodename) || (CFStringGetLength(nodename) == 0))) { 765 _SCErrorSet(kSCStatusInvalidArgument); 766 return NULL; 767 } 768 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionLocalAddress); 769 if (data != NULL) { 770 if (!isA_CFData(data) || ((size_t)CFDataGetLength(data) < sizeof(struct sockaddr_in))) { 771 _SCErrorSet(kSCStatusInvalidArgument); 772 return NULL; 773 } 774 addr_l = (const struct sockaddr *)CFDataGetBytePtr(data); 775 } 776 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionPTRAddress); 777 if (data != NULL) { 778 if (!isA_CFData(data) || ((size_t)CFDataGetLength(data) < sizeof(struct sockaddr_in))) { 779 _SCErrorSet(kSCStatusInvalidArgument); 780 return NULL; 781 } 782 addr_p = (const struct sockaddr *)CFDataGetBytePtr(data); 783 } 784 data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionRemoteAddress); 785 if (data != NULL) { 786 if (!isA_CFData(data) || ((size_t)CFDataGetLength(data) < sizeof(struct sockaddr_in))) { 787 _SCErrorSet(kSCStatusInvalidArgument); 788 return NULL; 789 } 790 addr_r = (const struct sockaddr *)CFDataGetBytePtr(data); 791 } 792 interface = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionInterface); 793 if ((interface != NULL) && 794 (!isA_CFString(interface) || (CFStringGetLength(interface) == 0))) { 795 _SCErrorSet(kSCStatusInvalidArgument); 796 return NULL; 797 } 798 resolverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionResolverBypass); 799 if ((resolverBypass != NULL) && !isA_CFBoolean(resolverBypass)) { 800 _SCErrorSet(kSCStatusInvalidArgument); 801 return NULL; 802 } 803 sourceAppAuditToken = 804 CFDictionaryGetValue(options, kSCNetworkReachabilityOptionSourceAppAuditToken); 805 if ((sourceAppAuditToken != NULL) && 806 (!isA_CFData(sourceAppAuditToken) || 807 (CFDataGetLength(sourceAppAuditToken) != sizeof(audit_token_t)))) { 808 _SCErrorSet(kSCStatusInvalidArgument); 809 return NULL; 810 } 811 sourceAppBundleID = 812 CFDictionaryGetValue(options, kSCNetworkReachabilityOptionSourceAppBundleIdentifier); 813 if ((sourceAppBundleID != NULL) && 814 (!isA_CFString(sourceAppBundleID) || 815 (CFStringGetLength(sourceAppBundleID) == 0))) { 816 _SCErrorSet(kSCStatusInvalidArgument); 817 return NULL; 818 } 819 820 if (nodename != NULL) { 821 const char *name; 822 823 if ((addr_l != NULL) || (addr_r != NULL) || (addr_p != NULL)) { 824 // can't have both a nodename and an address 825 _SCErrorSet(kSCStatusInvalidArgument); 826 return NULL; 827 } 828 829 name = _SC_cfstring_to_cstring(nodename, NULL, 0, kCFStringEncodingUTF8); 830 target = SCNetworkReachabilityCreateWithName(allocator, name); 831 CFAllocatorDeallocate(NULL, (void *)name); 832 } else if (addr_p != NULL) { 833 if ((addr_l != NULL) || // can't have PTR and target address 834 (addr_r != NULL)) { 835 _SCErrorSet(kSCStatusInvalidArgument); 836 return NULL; 837 } 838 839 target = __SCNetworkReachabilityCreateWithPTR(NULL, addr_p); 840 } else { 841 if ((addr_l != NULL) && (addr_r != NULL)) { 842 target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, addr_r); 843 } else if (addr_r != NULL) { 844 target = SCNetworkReachabilityCreateWithAddress(NULL, addr_r); 845 } else if (addr_l != NULL) { 846 target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, NULL); 847 } else { 848 _SCErrorSet(kSCStatusInvalidArgument); 849 return NULL; 850 } 851 } 852 if (target == NULL) { 853 return NULL; 854 } 855 856 targetPrivate = (SCNetworkReachabilityPrivateRef)target; 857 858 if (interface != NULL) { 859 if ((_SC_cfstring_to_cstring(interface, 860 if_name, 861 sizeof(if_name), 862 kCFStringEncodingASCII) == NULL) || 863 ((if_index = if_nametoindex(if_name)) == 0)) { 864 CFRelease(targetPrivate); 865 _SCErrorSet(kSCStatusInvalidArgument); 866 return NULL; 867 } 868 } 869 870 if (targetPrivate->parameters == NULL) { 871 targetPrivate->parameters = nw_parameters_create(); 872 } 873 874 if (if_index != 0) { 875 nw_interface_t interfaceObject = nw_interface_create_with_index(if_index); 876 nw_parameters_require_interface(targetPrivate->parameters, interfaceObject); 877 nw_release(interfaceObject); 878 haveOpt = TRUE; 879 } 880 881 if (resolverBypass != NULL) { 882 targetPrivate->resolverBypass = CFBooleanGetValue(resolverBypass); 883 haveOpt = TRUE; 884 } 885 886 if (sourceAppAuditToken != NULL) { 887 audit_token_t atoken; 888 CFDataGetBytes(sourceAppAuditToken, 889 CFRangeMake(0, CFDataGetLength(sourceAppAuditToken)), 890 (UInt8 *)&atoken); 891 nw_parameters_set_source_application(targetPrivate->parameters, atoken); 892 haveOpt = TRUE; 893 } else if (sourceAppBundleID != NULL) { 894 char *cBundleID = _SC_cfstring_to_cstring(sourceAppBundleID, 895 NULL, 896 0, 897 kCFStringEncodingUTF8); 898 if (cBundleID != NULL) { 899 nw_parameters_set_source_application_by_bundle_id(targetPrivate->parameters, 900 cBundleID); 901 CFAllocatorDeallocate(NULL, (void *)cBundleID); 902 } else { 903 SC_log(LOG_WARNING, "failed to convert %@ to a C string", sourceAppBundleID); 904 } 905 haveOpt = TRUE; 906 } 907 908 if (haveOpt) { 909 const char *opt = "???"; 910 911 switch (targetPrivate->type) { 912 case reachabilityTypeAddress : 913 opt = DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS; 914 break; 915 case reachabilityTypeAddressPair : 916 opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS; 917 break; 918 case reachabilityTypeName : 919 opt = DEBUG_REACHABILITY_TYPE_NAME_OPTIONS; 920 break; 921 case reachabilityTypePTR : 922 opt = DEBUG_REACHABILITY_TYPE_PTR_OPTIONS; 923 break; 924 } 925 926 SC_log(LOG_DEBUG, "%s%s %@", 927 targetPrivate->log_prefix, 928 opt, 929 targetPrivate); 930 } 931 932 return (SCNetworkReachabilityRef)targetPrivate; 933} 934 935 936CFTypeID 937SCNetworkReachabilityGetTypeID(void) 938{ 939 pthread_once(&initialized, __SCNetworkReachabilityInitialize); /* initialize runtime */ 940 return __kSCNetworkReachabilityTypeID; 941} 942 943 944CFArrayRef /* CFArray[CFData], where each CFData is a (struct sockaddr *) */ 945SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target, 946 int *error_num) 947{ 948 CFMutableArrayRef array = NULL; 949 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 950 951 if (!isA_SCNetworkReachability(target)) { 952 _SCErrorSet(kSCStatusInvalidArgument); 953 return NULL; 954 } 955 956 if (!isReachabilityTypeName(targetPrivate->type)) { 957 _SCErrorSet(kSCStatusInvalidArgument); 958 return NULL; 959 } 960 961 if (error_num) { 962 *error_num = 0; 963 } 964 965 MUTEX_LOCK(&targetPrivate->lock); 966 967 if (nw_array_get_count(targetPrivate->lastResolvedEndpoints) > 0) { 968 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 969 nw_array_apply(targetPrivate->lastResolvedEndpoints, ^bool(size_t index, nw_object_t object) { 970#pragma unused(index) 971 nw_endpoint_type_t endpoint_type = nw_endpoint_get_type((nw_endpoint_t)object); 972 if (endpoint_type == nw_endpoint_type_address) { 973 const struct sockaddr *address = nw_endpoint_get_address((nw_endpoint_t)object); 974 if (address == NULL) { 975 SC_log(LOG_ERR, "nw_endpoint_type_address w/no address"); 976 return TRUE; 977 } 978 979 CFDataRef addressData = CFDataCreate(kCFAllocatorDefault, (const uint8_t *)address, address->sa_len); 980 CFArrayAppendValue(array, addressData); 981 CFRelease(addressData); 982 } else if (endpoint_type == nw_endpoint_type_host) { 983 const char *host = nw_endpoint_get_hostname((nw_endpoint_t)object); 984 if (host == NULL) { 985 SC_log(LOG_ERR, "nw_endpoint_type_host w/no host"); 986 return TRUE; 987 } 988 989 CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, host, kCFStringEncodingASCII); 990 if (string == NULL) { 991 SC_log(LOG_ERR, "nw_endpoint_type_host w/non-ASCII host"); 992 return TRUE; 993 } 994 995 if (CFStringHasPrefix(string, CFSTR(".")) || CFStringHasSuffix(string, CFSTR("."))) { 996 CFMutableStringRef mutableString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, string); 997 CFRelease(string); 998 string = mutableString; 999 CFStringTrim(mutableString, CFSTR(".")); 1000 } 1001 CFArrayAppendValue(array, string); 1002 CFRelease(string); 1003 } else { 1004 SC_log(LOG_ERR, "unexpected nw_endpoint type: %d", endpoint_type); 1005 } 1006 return TRUE; 1007 }); 1008 } 1009 1010 MUTEX_UNLOCK(&targetPrivate->lock); 1011 _SCErrorSet(kSCStatusOK); 1012 return array; 1013} 1014 1015#pragma mark - 1016#pragma mark Reachability Flags 1017 1018static void 1019__SCNetworkReachabilityGetAgentVPNFlags(xpc_object_t dictionary, Boolean *vpn, Boolean *onDemand) 1020{ 1021 const struct netagent *agent = NULL; 1022 size_t length = 0; 1023 1024 if (dictionary == NULL || vpn == NULL || onDemand == NULL) { 1025 return; 1026 } 1027 1028 *vpn = FALSE; 1029 *onDemand = FALSE; 1030 1031 agent = xpc_dictionary_get_data(dictionary, REACHABILITY_AGENT_DATA_KEY, &length); 1032 if (agent == NULL || length < sizeof(struct netagent) || length != (sizeof(struct netagent) + agent->netagent_data_size)) { 1033 return; 1034 } 1035 1036 if (strncmp(REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN, agent->netagent_domain, NETAGENT_DOMAINSIZE) == 0) { 1037 *vpn = TRUE; 1038 if ((agent->netagent_flags & NETAGENT_FLAG_VOLUNTARY) && 1039 !(agent->netagent_flags & NETAGENT_FLAG_ACTIVE)) { 1040 *onDemand = TRUE; 1041 } 1042 } 1043} 1044 1045static bool 1046nw_path_is_linklocal_direct(nw_path_t path) 1047{ 1048 bool is_linklocal_direct = false; 1049 1050 nw_endpoint_t endpoint = nw_path_copy_endpoint(path); 1051 if (endpoint == NULL) { 1052 return false; 1053 } 1054 1055 if (nw_endpoint_get_type(endpoint) == nw_endpoint_type_address) { 1056 const struct sockaddr_in *sin; 1057 1058 sin = (const struct sockaddr_in *)(void *)nw_endpoint_get_address(endpoint);; 1059 if ((sin != NULL) && 1060 (sin->sin_family == AF_INET) && 1061 IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr))) { 1062 nw_interface_t interface = nw_path_copy_interface(path); 1063 1064 if (interface != NULL) { 1065 nw_interface_type_t type = nw_interface_get_type(interface); 1066 if ((type == nw_interface_type_wired) || 1067 ((type == nw_interface_type_wifi) && 1068 (nw_interface_get_subtype(interface) != nw_interface_subtype_wifi_awdl))) { 1069 is_linklocal_direct = true; 1070 } 1071 1072 nw_release(interface); 1073 } 1074 } 1075 } 1076 1077 nw_release(endpoint); 1078 return is_linklocal_direct; 1079} 1080 1081static SCNetworkReachabilityFlags 1082__SCNetworkReachabilityGetFlagsFromPath(nw_path_t path, 1083 ReachabilityAddressType type, 1084 nw_resolver_status_t resolverStatus, 1085 nw_array_t resolvedEndpoints, 1086 Boolean resolvedEndpointUseFlags, 1087 SCNetworkReachabilityFlags resolvedEndpointFlags) 1088{ 1089 __block SCNetworkReachabilityFlags flags = kSCNetworkReachabilityFlagsReachable; 1090 __block const char *why = "???"; 1091 if (path != NULL) { 1092 nw_path_status_t status = nw_path_get_status(path); 1093 if (status == nw_path_status_satisfied) { 1094 __block bool checkDNSFlags = TRUE; 1095 flags = kSCNetworkReachabilityFlagsReachable; 1096 why = "nw_path_status_satisfied"; 1097#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR 1098 if (nw_path_uses_interface_type(path, nw_interface_type_cellular)) { 1099 flags |= (kSCNetworkReachabilityFlagsTransientConnection | kSCNetworkReachabilityFlagsIsWWAN); 1100 why = "nw_path_status_satisfied, cellular"; 1101 } 1102#endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR 1103 xpc_object_t agent_dictionary = nw_path_copy_netagent_dictionary(path); 1104 if (agent_dictionary != NULL) { 1105 if (xpc_dictionary_get_count(agent_dictionary) > 0) { 1106 xpc_dictionary_apply(agent_dictionary, ^bool(const char *key, xpc_object_t value) { 1107#pragma unused(key) 1108 Boolean vpn = FALSE; 1109 Boolean onDemand = FALSE; 1110 __SCNetworkReachabilityGetAgentVPNFlags(value, &vpn, &onDemand); 1111 if (vpn) { 1112 // VPN flows are transient 1113 flags |= kSCNetworkReachabilityFlagsTransientConnection; 1114 why = "nw_path_status_satisfied, VPN"; 1115 } 1116 if (onDemand && 1117 type == reachabilityTypeName && 1118 resolverStatus == nw_resolver_status_complete && 1119 nw_array_get_count(resolvedEndpoints) == 0) { 1120 // On Demand by hostname, when no address has been resolved 1121 flags |= (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsConnectionOnDemand); 1122 why = "nw_path_status_satisfied, OnDemand"; 1123 checkDNSFlags = FALSE; 1124 } 1125 return TRUE; 1126 }); 1127 1128 } 1129 xpc_release(agent_dictionary); 1130 } 1131 if (isReachabilityTypeName(type)) { 1132 if (checkDNSFlags) { 1133 if (resolverStatus == nw_resolver_status_complete && 1134 nw_array_get_count(resolvedEndpoints) == 0) { 1135 // DNS didn't resolve, as a final answer for now. Not reachable! 1136 flags = 0; 1137 why = "nw_path_status_satisfied, DNS not reachable"; 1138 } else if (resolvedEndpointUseFlags) { 1139 flags = resolvedEndpointFlags; 1140 why = "nw_path_status_satisfied, resolved endpoint flags"; 1141 } 1142 } 1143 } else { 1144 if (nw_path_is_direct(path) || nw_path_is_linklocal_direct(path)) { 1145 flags |= kSCNetworkReachabilityFlagsIsDirect; 1146 why = "nw_path_status_satisfied, by address, direct"; 1147 } 1148 if (nw_path_is_local(path)) { 1149 flags |= kSCNetworkReachabilityFlagsIsLocalAddress; 1150 why = "nw_path_status_satisfied, by address, local"; 1151 } 1152 } 1153 } else if (status == nw_path_status_unsatisfied) { 1154 flags = 0; 1155 why = "nw_path_status_unsatisfied"; 1156#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR 1157 if (nw_path_uses_interface_type(path, nw_interface_type_cellular)) { 1158 flags |= kSCNetworkReachabilityFlagsIsWWAN; 1159 why = "nw_path_status_unsatisfied, WWAN"; 1160 } 1161#endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR 1162 } else if (status == nw_path_status_satisfiable) { 1163 flags = (kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection); 1164 why = "nw_path_status_satisfiable"; 1165 uuid_t vpn_uuid; 1166 if (nw_path_get_vpn_config_id(path, &vpn_uuid)) { 1167 flags |= kSCNetworkReachabilityFlagsConnectionOnDemand; 1168 why = "nw_path_status_satisfiable, OnDemand"; 1169 } 1170#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR 1171 else if (nw_path_uses_interface_type(path, nw_interface_type_cellular)) { 1172 flags |= kSCNetworkReachabilityFlagsIsWWAN; 1173 why = "nw_path_status_satisfiable, WWAN"; 1174 } 1175#endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR 1176 } 1177 } 1178 SC_log(LOG_DEBUG, "__SCNetworkReachabilityGetFlagsFromPath, flags = 0x%08x, %s", flags, why); 1179 return (flags & kSCNetworkReachabilityFlagsMask); 1180} 1181 1182static nw_endpoint_t 1183__SCNetworkReachabilityGetPrimaryEndpoint(SCNetworkReachabilityPrivateRef targetPrivate) 1184{ 1185 if (targetPrivate->type == reachabilityTypeName) { 1186 return targetPrivate->hostnameEndpoint; 1187 } else if (targetPrivate->type == reachabilityTypeAddress || 1188 targetPrivate->type == reachabilityTypeAddressPair || 1189 targetPrivate->type == reachabilityTypePTR) { 1190 return targetPrivate->remoteAddressEndpoint; 1191 } 1192 return NULL; 1193} 1194 1195int 1196SCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target) 1197{ 1198 int if_index = -1; 1199 Boolean ok = TRUE; 1200 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1201 SCNetworkReachabilityFlags flags = 0; 1202 1203 if (!isA_SCNetworkReachability(target)) { 1204 _SCErrorSet(kSCStatusInvalidArgument); 1205 return if_index; 1206 } 1207 1208 MUTEX_LOCK(&targetPrivate->lock); 1209 1210 flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, 1211 targetPrivate->type, 1212 nw_resolver_status_invalid, 1213 NULL, 1214 targetPrivate->lastResolvedEndpointHasFlags, 1215 targetPrivate->lastResolvedEndpointFlags); 1216 1217 /* Only return the if_index if the connection is reachable not for reachable connection 1218 * required etc ... */ 1219 if (ok && __SCNetworkReachabilityRank(flags) == ReachabilityRankReachable) { 1220 if (targetPrivate->lastResolvedEndpointHasFlags) { 1221 if_index = targetPrivate->lastResolvedEndpointInterfaceIndex; 1222 } else { 1223 if_index = nw_path_get_interface_index(targetPrivate->lastPath); 1224 } 1225 } 1226 1227 MUTEX_UNLOCK(&targetPrivate->lock); 1228 return if_index; 1229} 1230 1231// CrazyIvan46 is the feature that allows connections to IPv4 literals on IPv6-only (NAT64+DNS64) networks to work 1232// This function replaces the path when the initial one isn't satisfied and our target is an IPv4 literal 1233// It tries IPv6 reachability instead in case we could synthesize another address to connect to 1234static OS_OBJECT_RETURNS_RETAINED nw_path_t 1235__SCNetworkReachabilityCreateCrazyIvan46Path(nw_path_t path, nw_endpoint_t endpoint, 1236 nw_parameters_t parameters, Boolean allow_resolution) 1237{ 1238 nw_path_t retPath = NULL; 1239 const struct sockaddr *sa; 1240 1241 if ((nw_path_get_status(path) != nw_path_status_unsatisfied) || 1242 (NULL == endpoint) || (nw_endpoint_get_type(endpoint) != nw_endpoint_type_address)) { 1243 return NULL; 1244 } 1245 1246 sa = nw_endpoint_get_address(endpoint); 1247 1248 if (sa->sa_family != AF_INET) { 1249 return NULL; 1250 } 1251 1252 if (allow_resolution) { 1253 uint32_t ifIndex = 0; 1254 int32_t numPrefixes; 1255 nw_nat64_prefix_t *prefixes = NULL; 1256 struct sockaddr_in sin; 1257 1258 memcpy(&sin, sa, MIN(sizeof(sin), sa->sa_len)); 1259 if (NULL != parameters) { 1260 ifIndex = nw_parameters_get_required_interface_index(parameters); 1261 } 1262 numPrefixes = nw_nat64_copy_prefixes(&ifIndex, &prefixes); 1263 if (numPrefixes > 0) { 1264 struct sockaddr_in6 synthesizedAddress = { 1265 .sin6_len = sizeof(struct sockaddr_in6), 1266 .sin6_family = AF_INET6, 1267 .sin6_port = htons(nw_endpoint_get_port(endpoint)), 1268 .sin6_flowinfo = 0, 1269 .sin6_scope_id = 0 1270 }; 1271 nw_endpoint_t synthesizedEndpoint; 1272 nw_path_evaluator_t synthesizedEvaluator; 1273 nw_path_t synthesizedPath; 1274 1275 nw_nat64_synthesize_v6(&prefixes[0], &sin.sin_addr, &synthesizedAddress.sin6_addr); 1276 synthesizedEndpoint = nw_endpoint_create_address((const struct sockaddr *)&synthesizedAddress); 1277 synthesizedEvaluator = nw_path_create_evaluator_for_endpoint(synthesizedEndpoint, parameters); 1278 synthesizedPath = nw_path_evaluator_copy_path(synthesizedEvaluator); 1279 if (nw_path_get_status(synthesizedPath) != nw_path_status_unsatisfied) { 1280 retPath = synthesizedPath; 1281 SC_log(LOG_INFO, "Using CrazyIvan46 synthesized reachability result"); 1282 } else { 1283 nw_release(synthesizedPath); 1284 } 1285 nw_release(synthesizedEvaluator); 1286 nw_release(synthesizedEndpoint); 1287 } 1288 } else { 1289 // Don't synthesize in non-scheduled mode to avoid generating DNS traffic 1290 nw_path_t v6Path; 1291 nw_path_evaluator_t v6PathEvaluator; 1292 nw_parameters_t v6Parameters; 1293 1294 if (NULL != parameters) { 1295 v6Parameters = nw_parameters_copy(parameters); 1296 } else { 1297 v6Parameters = nw_parameters_create(); 1298 } 1299 nw_parameters_set_required_address_family(v6Parameters, AF_INET6); 1300 v6PathEvaluator = nw_path_create_evaluator_for_endpoint(NULL, v6Parameters); 1301 v6Path = nw_path_evaluator_copy_path(v6PathEvaluator); 1302 if (nw_path_get_status(v6Path) != nw_path_status_unsatisfied) { 1303 retPath = v6Path; 1304 SC_log(LOG_INFO, "Using CrazyIvan46 simple reachability result"); 1305 } else { 1306 nw_release(v6Path); 1307 } 1308 nw_release(v6PathEvaluator); 1309 nw_release(v6Parameters); 1310 } 1311 return retPath; 1312} 1313 1314Boolean 1315SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target, 1316 SCNetworkReachabilityFlags *flags) 1317{ 1318 nw_path_t crazyIvanPath; 1319 nw_endpoint_t endpoint; 1320 Boolean ok = TRUE; 1321 nw_path_t path; 1322 nw_path_evaluator_t pathEvaluator; 1323 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1324 1325 if (!isA_SCNetworkReachability(target)) { 1326 _SCErrorSet(kSCStatusInvalidArgument); 1327 return FALSE; 1328 } 1329 1330 MUTEX_LOCK(&targetPrivate->lock); 1331 1332 if (targetPrivate->scheduled) { 1333 // if being watched, return the last known (and what should be current) status 1334 *flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, 1335 targetPrivate->type, 1336 targetPrivate->lastResolverStatus, 1337 targetPrivate->lastResolvedEndpoints, 1338 targetPrivate->lastResolvedEndpointHasFlags, 1339 targetPrivate->lastResolvedEndpointFlags); 1340 // because we have synchronously captured the current status, we no longer 1341 // need our by-name required callback 1342 targetPrivate->sentFirstUpdate = TRUE; 1343 goto done; 1344 } 1345 1346 // Not being watched, so run a one-shot path evaluator 1347 // We don't care about DNS resolution in this case, since we only need to have the 1348 // DNS resolution to support clients watching reachability to get updates 1349 endpoint = __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate); 1350 pathEvaluator = nw_path_create_evaluator_for_endpoint(endpoint, targetPrivate->parameters); 1351 path = nw_path_evaluator_copy_path(pathEvaluator); 1352 1353 if (isReachabilityTypeAddress(targetPrivate->type)) { 1354 crazyIvanPath = __SCNetworkReachabilityCreateCrazyIvan46Path(path, endpoint, 1355 targetPrivate->parameters, FALSE); 1356 if (NULL != crazyIvanPath) { 1357 nw_release(path); 1358 path = crazyIvanPath; 1359 } 1360 } 1361 1362 *flags = __SCNetworkReachabilityGetFlagsFromPath(path, 0, nw_resolver_status_invalid, NULL, FALSE, 0); 1363 nw_release(path); 1364 nw_release(pathEvaluator); 1365 1366 done : 1367 1368 MUTEX_UNLOCK(&targetPrivate->lock); 1369 return ok; 1370} 1371 1372 1373#pragma mark - 1374#pragma mark Notifications 1375 1376static void 1377reachPerformAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate) 1378{ 1379 void *context_info; 1380 void (*context_release)(const void *); 1381 SCNetworkReachabilityCallBack rlsFunction; 1382 SCNetworkReachabilityFlags flags = 0; 1383 1384 if (!targetPrivate->scheduled) { 1385 // if no longer scheduled 1386 SC_log(LOG_INFO, "%sskipping SCNetworkReachability callback, no longer scheduled", 1387 targetPrivate->log_prefix); 1388 MUTEX_UNLOCK(&targetPrivate->lock); 1389 return; 1390 } 1391 1392 // callout 1393 rlsFunction = targetPrivate->rlsFunction; 1394 if (targetPrivate->rlsContext.retain != NULL) { 1395 context_info = (void *)(*targetPrivate->rlsContext.retain)(targetPrivate->rlsContext.info); 1396 context_release = targetPrivate->rlsContext.release; 1397 } else { 1398 context_info = targetPrivate->rlsContext.info; 1399 context_release = NULL; 1400 } 1401 1402 flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, 1403 targetPrivate->type, 1404 targetPrivate->lastResolverStatus, 1405 targetPrivate->lastResolvedEndpoints, 1406 targetPrivate->lastResolvedEndpointHasFlags, 1407 targetPrivate->lastResolvedEndpointFlags); 1408 1409 MUTEX_UNLOCK(&targetPrivate->lock); 1410 1411 if (rlsFunction != NULL) { 1412 SC_log(LOG_DEBUG, "%sexec SCNetworkReachability callout w/flags = 0x%08x", 1413 targetPrivate->log_prefix, 1414 flags); 1415 (*rlsFunction)((SCNetworkReachabilityRef)targetPrivate, 1416 flags, 1417 context_info); 1418 } 1419 1420 if (context_release != NULL) { 1421 (*context_release)(context_info); 1422 } 1423 1424 return; 1425} 1426 1427static void 1428reachPerform(void *info) 1429{ 1430 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)info; 1431 1432 MUTEX_LOCK(&targetPrivate->lock); 1433 reachPerformAndUnlock(targetPrivate); 1434 1435} 1436 1437static void 1438reachUpdateAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate) 1439{ 1440 targetPrivate->sentFirstUpdate = TRUE; 1441 if (targetPrivate->rls != NULL) { 1442 if (targetPrivate->rlList != NULL) { 1443 CFRunLoopSourceSignal(targetPrivate->rls); 1444 _SC_signalRunLoop(targetPrivate, targetPrivate->rls, targetPrivate->rlList); 1445 } 1446 MUTEX_UNLOCK(&targetPrivate->lock); 1447 } else { 1448 reachPerformAndUnlock(targetPrivate); 1449 } 1450} 1451 1452Boolean 1453SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target, 1454 SCNetworkReachabilityCallBack callout, 1455 SCNetworkReachabilityContext *context) 1456{ 1457 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1458 1459 MUTEX_LOCK(&targetPrivate->lock); 1460 1461 if (targetPrivate->rlsContext.release != NULL) { 1462 /* let go of the current context */ 1463 (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info); 1464 } 1465 1466 targetPrivate->rlsFunction = callout; 1467 targetPrivate->rlsContext.info = NULL; 1468 targetPrivate->rlsContext.retain = NULL; 1469 targetPrivate->rlsContext.release = NULL; 1470 targetPrivate->rlsContext.copyDescription = NULL; 1471 if (context) { 1472 memcpy(&targetPrivate->rlsContext, context, sizeof(SCNetworkReachabilityContext)); 1473 if (context->retain != NULL) { 1474 targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info); 1475 } 1476 } 1477 1478 MUTEX_UNLOCK(&targetPrivate->lock); 1479 1480 return TRUE; 1481} 1482 1483 1484static CFStringRef 1485reachRLSCopyDescription(const void *info) 1486{ 1487 SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info; 1488 1489 return CFStringCreateWithFormat(NULL, 1490 NULL, 1491 CFSTR("<SCNetworkReachability RLS> {target = %p}"), 1492 target); 1493} 1494 1495Boolean 1496SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target, 1497 CFRunLoopRef runLoop, 1498 CFStringRef runLoopMode) 1499{ 1500 Boolean success = FALSE; 1501 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1502 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) { 1503 _SCErrorSet(kSCStatusInvalidArgument); 1504 return FALSE; 1505 } 1506 1507 MUTEX_LOCK(&targetPrivate->lock); 1508 1509 if (targetPrivate->scheduled) { 1510 if (targetPrivate->rls != NULL && targetPrivate->rlList != NULL) { 1511 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) { 1512 /* 1513 * if we do not already have host notifications scheduled with 1514 * this runLoop / runLoopMode 1515 */ 1516 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode); 1517 } 1518 1519 _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList); 1520 1521 MUTEX_UNLOCK(&targetPrivate->lock); 1522 return TRUE; 1523 } else { 1524 MUTEX_UNLOCK(&targetPrivate->lock); 1525 _SCErrorSet(kSCStatusInvalidArgument); 1526 return FALSE; 1527 } 1528 } 1529 1530 CFRunLoopSourceContext context = { 1531 0 // version 1532 , (void *)target // info 1533 , CFRetain // retain 1534 , CFRelease // release 1535 , reachRLSCopyDescription // copyDescription 1536 , CFEqual // equal 1537 , CFHash // hash 1538 , NULL // schedule 1539 , NULL // cancel 1540 , reachPerform // perform 1541 }; 1542 1543 targetPrivate->rls = CFRunLoopSourceCreate(NULL, 0, &context); 1544 targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1545 1546 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) { 1547 /* 1548 * if we do not already have host notifications scheduled with 1549 * this runLoop / runLoopMode 1550 */ 1551 CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode); 1552 } 1553 1554 _SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList); 1555 1556 success = __SCNetworkReachabilitySetDispatchQueue(targetPrivate, _callback_queue()); 1557 if (!success) { 1558 if (_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) { 1559 CFIndex n = CFArrayGetCount(targetPrivate->rlList); 1560 if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) { 1561 // if target is no longer scheduled for this runLoop / runLoopMode 1562 CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode); 1563 if (n == 0) { 1564 // if *all* notifications have been unscheduled 1565 CFRelease(targetPrivate->rlList); 1566 targetPrivate->rlList = NULL; 1567 CFRunLoopSourceInvalidate(targetPrivate->rls); 1568 CFRelease(targetPrivate->rls); 1569 targetPrivate->rls = NULL; 1570 } 1571 } 1572 } 1573 } 1574 1575 MUTEX_UNLOCK(&targetPrivate->lock); 1576 return success; 1577} 1578 1579Boolean 1580SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target, 1581 CFRunLoopRef runLoop, 1582 CFStringRef runLoopMode) 1583{ 1584 Boolean success = FALSE; 1585 Boolean unscheduleDispatchQueue = FALSE; 1586 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1587 1588 if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) { 1589 _SCErrorSet(kSCStatusInvalidArgument); 1590 return FALSE; 1591 } 1592 1593 MUTEX_LOCK(&targetPrivate->lock); 1594 1595 if (targetPrivate->rlList == NULL || targetPrivate->rls == NULL || !targetPrivate->scheduled) { 1596 MUTEX_UNLOCK(&targetPrivate->lock); 1597 _SCErrorSet(kSCStatusInvalidArgument); 1598 return FALSE; 1599 } 1600 1601 if (_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) { 1602 CFIndex n = CFArrayGetCount(targetPrivate->rlList); 1603 if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) { 1604 // if target is no longer scheduled for this runLoop / runLoopMode 1605 CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode); 1606 if (n == 0) { 1607 // if *all* notifications have been unscheduled 1608 unscheduleDispatchQueue = TRUE; 1609 CFRelease(targetPrivate->rlList); 1610 targetPrivate->rlList = NULL; 1611 CFRunLoopSourceInvalidate(targetPrivate->rls); 1612 CFRelease(targetPrivate->rls); 1613 targetPrivate->rls = NULL; 1614 } 1615 } 1616 } 1617 1618 if (unscheduleDispatchQueue) { 1619 success = __SCNetworkReachabilitySetDispatchQueue(targetPrivate, NULL); 1620 } else { 1621 success = TRUE; 1622 } 1623 MUTEX_UNLOCK(&targetPrivate->lock); 1624 return success; 1625} 1626 1627static __inline__ void 1628__SCNetworkReachabilityCopyPathStatus(SCNetworkReachabilityPrivateRef targetPrivate, SCNetworkReachabilityFlags *flags, uint *ifIndex, size_t *endpointCount) 1629{ 1630 if (flags) { 1631 *flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, 1632 targetPrivate->type, 1633 targetPrivate->lastResolverStatus, 1634 targetPrivate->lastResolvedEndpoints, 1635 targetPrivate->lastResolvedEndpointHasFlags, 1636 targetPrivate->lastResolvedEndpointFlags); 1637 } 1638 if (ifIndex) { 1639 *ifIndex = nw_path_get_interface_index(targetPrivate->lastPath); 1640 } 1641 if (endpointCount) { 1642 *endpointCount = nw_array_get_count(targetPrivate->lastResolvedEndpoints); 1643 } 1644 return; 1645} 1646 1647static __inline__ Boolean 1648__SCNetworkReachabilityShouldUpdateClient(SCNetworkReachabilityPrivateRef targetPrivate, SCNetworkReachabilityFlags oldFlags, uint oldIFIndex, size_t oldEndpointCount) 1649{ 1650 SCNetworkReachabilityFlags newFlags = 0; 1651 uint newIFIndex = 0; 1652 size_t newEndpointCount = 0; 1653 __SCNetworkReachabilityCopyPathStatus(targetPrivate, &newFlags, &newIFIndex, &newEndpointCount); 1654 return (!targetPrivate->sentFirstUpdate || 1655 oldFlags != newFlags || 1656 oldIFIndex != newIFIndex || 1657 oldEndpointCount != newEndpointCount); 1658} 1659 1660static void 1661__SCNetworkReachabilityRestartResolver(SCNetworkReachabilityPrivateRef targetPrivate) 1662{ 1663 if (targetPrivate && 1664 !targetPrivate->resolverBypass && 1665 isReachabilityTypeName(targetPrivate->type)) { 1666 nw_resolver_t resolver; 1667 CFRetain(targetPrivate); 1668 if (NULL != targetPrivate->resolver) { 1669 nw_resolver_cancel(targetPrivate->resolver); 1670 } 1671 if (targetPrivate->lastPath != NULL) { 1672 resolver = nw_resolver_create_with_path(targetPrivate->lastPath); 1673 } else { 1674 resolver = nw_resolver_create_with_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate), targetPrivate->lastPathParameters ? targetPrivate->lastPathParameters : targetPrivate->parameters); 1675 } 1676 if (resolver == NULL) { 1677 SC_log(LOG_ERR, "%sfailed to create a nw_resolver", targetPrivate->log_prefix); 1678 targetPrivate->resolver = NULL; 1679 CFRelease(targetPrivate); 1680 return; 1681 } 1682 targetPrivate->resolver = resolver; 1683 nw_resolver_set_cancel_handler(resolver, ^(void) { 1684 MUTEX_LOCK(&targetPrivate->lock); 1685 if (resolver == targetPrivate->resolver) { 1686 targetPrivate->resolver = NULL; 1687 } 1688 nw_release(resolver); 1689 MUTEX_UNLOCK(&targetPrivate->lock); 1690 CFRelease(targetPrivate); 1691 }); 1692 if (!nw_resolver_set_update_handler(resolver, targetPrivate->dispatchQueue, ^(nw_resolver_status_t status, nw_array_t resolved_endpoints) { 1693 MUTEX_LOCK(&targetPrivate->lock); 1694 if (targetPrivate->scheduled) { 1695 SCNetworkReachabilityFlags oldFlags = 0; 1696 uint oldIFIndex = 0; 1697 size_t oldEndpointCount = 0; 1698 __SCNetworkReachabilityCopyPathStatus(targetPrivate, &oldFlags, &oldIFIndex, &oldEndpointCount); 1699 1700 targetPrivate->lastResolverStatus = status; 1701 nw_release(targetPrivate->lastResolvedEndpoints); 1702 targetPrivate->lastResolvedEndpoints = nw_retain(resolved_endpoints); 1703 1704 // Run path evaluation on the resolved endpoints 1705 __block Boolean hasFlags = FALSE; 1706 targetPrivate->lastResolvedEndpointHasFlags = FALSE; 1707 targetPrivate->lastResolvedEndpointFlags = 0; 1708 targetPrivate->lastResolvedEndpointInterfaceIndex = 0; 1709 nw_array_apply(targetPrivate->lastResolvedEndpoints, ^bool(size_t index, nw_object_t object) { 1710#pragma unused(index) 1711 SCNetworkReachabilityFlags flags = 0; 1712 uint interfaceIndex = 0; 1713 ReachabilityRankType rank; 1714 nw_endpoint_t resolvedEndpoint = (nw_endpoint_t)object; 1715 nw_path_evaluator_t pathEvaluator = nw_path_create_evaluator_for_endpoint(resolvedEndpoint, targetPrivate->lastPathParameters ? targetPrivate->lastPathParameters : targetPrivate->parameters); 1716 nw_path_t path = nw_path_evaluator_copy_path(pathEvaluator); 1717 if (path != NULL) { 1718 flags = __SCNetworkReachabilityGetFlagsFromPath(path, 0, nw_resolver_status_invalid, NULL, FALSE, 0); 1719 hasFlags = TRUE; 1720 } 1721 interfaceIndex = nw_path_get_interface_index(path); 1722 nw_release(path); 1723 nw_release(pathEvaluator); 1724 1725 rank = __SCNetworkReachabilityRank(flags); 1726 if (rank > __SCNetworkReachabilityRank(targetPrivate->lastResolvedEndpointFlags)) { 1727 // Return the best case result 1728 targetPrivate->lastResolvedEndpointFlags = flags; 1729 targetPrivate->lastResolvedEndpointInterfaceIndex = interfaceIndex; 1730 if (rank == ReachabilityRankReachable) { 1731 // Can't get any better than REACHABLE 1732 return FALSE; 1733 } 1734 } 1735 return TRUE; 1736 }); 1737 targetPrivate->lastResolvedEndpointHasFlags = hasFlags; 1738 1739 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate, oldFlags, oldIFIndex, oldEndpointCount)) { 1740 reachUpdateAndUnlock(targetPrivate); 1741 } else { 1742 MUTEX_UNLOCK(&targetPrivate->lock); 1743 } 1744 } else { 1745 MUTEX_UNLOCK(&targetPrivate->lock); 1746 } 1747 })) { 1748 nw_release(resolver); 1749 targetPrivate->resolver = NULL; 1750 CFRelease(targetPrivate); 1751 } 1752 } 1753} 1754 1755static Boolean 1756__SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate, 1757 dispatch_queue_t queue) 1758{ 1759 Boolean ok = FALSE; 1760 1761 if (queue != NULL) { 1762 nw_path_t crazyIvanPath; 1763 nw_endpoint_t endpoint; 1764 nw_path_evaluator_t pathEvaluator; 1765 1766 if ((targetPrivate->dispatchQueue != NULL) || // if we are already scheduled with a dispatch queue 1767 ((queue != NULL) && targetPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop 1768 _SCErrorSet(kSCStatusInvalidArgument); 1769 goto done; 1770 } 1771 1772 SC_log(LOG_DEBUG, "%sscheduled", targetPrivate->log_prefix); 1773 1774 // retain dispatch queue 1775 dispatch_retain(queue); 1776 nw_path_evaluator_cancel(targetPrivate->pathEvaluator); 1777 endpoint = __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate); 1778 pathEvaluator = nw_path_create_evaluator_for_endpoint(endpoint, targetPrivate->parameters); 1779 targetPrivate->pathEvaluator = pathEvaluator; 1780 targetPrivate->dispatchQueue = queue; 1781 targetPrivate->scheduled = TRUE; 1782 if (isReachabilityTypeName(targetPrivate->type)) { 1783 // we must have at least one callback for by-name queries 1784 targetPrivate->sentFirstUpdate = FALSE; 1785 } else { 1786 targetPrivate->sentFirstUpdate = TRUE; 1787 } 1788 1789 nw_release(targetPrivate->lastPath); 1790 targetPrivate->lastPath = nw_path_evaluator_copy_path(pathEvaluator); 1791 1792 if (isReachabilityTypeAddress(targetPrivate->type)) { 1793 crazyIvanPath = __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate->lastPath, endpoint, 1794 targetPrivate->parameters, FALSE); 1795 if (NULL != crazyIvanPath) { 1796 nw_release(targetPrivate->lastPath); 1797 targetPrivate->lastPath = crazyIvanPath; 1798 } 1799 } 1800 1801 nw_release(targetPrivate->lastPathParameters); 1802 targetPrivate->lastPathParameters = nw_path_copy_derived_parameters(targetPrivate->lastPath); 1803 1804 targetPrivate->lastResolverStatus = nw_resolver_status_invalid; 1805 nw_release(targetPrivate->lastResolvedEndpoints); 1806 targetPrivate->lastResolvedEndpoints = NULL; 1807 __SCNetworkReachabilityRestartResolver(targetPrivate); 1808 1809 CFRetain(targetPrivate); 1810 nw_path_evaluator_set_cancel_handler(pathEvaluator, ^(void) { 1811 MUTEX_LOCK(&targetPrivate->lock); 1812 if (pathEvaluator == targetPrivate->pathEvaluator) { 1813 targetPrivate->pathEvaluator = NULL; 1814 } 1815 nw_release(pathEvaluator); 1816 MUTEX_UNLOCK(&targetPrivate->lock); 1817 CFRelease(targetPrivate); 1818 }); 1819 1820 if (!nw_path_evaluator_set_update_handler(pathEvaluator, targetPrivate->dispatchQueue, ^(nw_path_t path) { 1821 MUTEX_LOCK(&targetPrivate->lock); 1822 if (targetPrivate->scheduled) { 1823 nw_path_t crazyIvanPath; 1824 SCNetworkReachabilityFlags oldFlags = 0; 1825 uint oldIFIndex = 0; 1826 size_t oldEndpointCount = 0; 1827 __SCNetworkReachabilityCopyPathStatus(targetPrivate, &oldFlags, &oldIFIndex, &oldEndpointCount); 1828 1829 nw_release(targetPrivate->lastPath); 1830 targetPrivate->lastPath = nw_retain(path); 1831 1832 if (isReachabilityTypeAddress(targetPrivate->type)) { 1833 crazyIvanPath = 1834 __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate->lastPath, 1835 endpoint, 1836 targetPrivate->parameters, 1837 TRUE); 1838 if (NULL != crazyIvanPath) { 1839 nw_release(targetPrivate->lastPath); 1840 targetPrivate->lastPath = crazyIvanPath; 1841 } 1842 } 1843 1844 if (targetPrivate->lastResolverStatus == nw_resolver_status_complete) { 1845 targetPrivate->lastResolverStatus = nw_resolver_status_invalid; 1846 __SCNetworkReachabilityRestartResolver(targetPrivate); 1847 } 1848 1849 if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate, oldFlags, oldIFIndex, oldEndpointCount)) { 1850 reachUpdateAndUnlock(targetPrivate); 1851 } else { 1852 MUTEX_UNLOCK(&targetPrivate->lock); 1853 } 1854 } else { 1855 MUTEX_UNLOCK(&targetPrivate->lock); 1856 } 1857 })) { 1858 targetPrivate->pathEvaluator = NULL; 1859 nw_release(pathEvaluator); 1860 CFRelease(targetPrivate); 1861 } 1862 } else { 1863 if (targetPrivate->dispatchQueue == NULL) { // if we should be scheduled on a dispatch queue (but are not) 1864 _SCErrorSet(kSCStatusInvalidArgument); 1865 goto done; 1866 } 1867 1868 if (!targetPrivate->scheduled) { 1869 // if not currently scheduled 1870 _SCErrorSet(kSCStatusInvalidArgument); 1871 goto done; 1872 } 1873 1874 targetPrivate->scheduled = FALSE; 1875 targetPrivate->sentFirstUpdate = FALSE; 1876 nw_path_evaluator_cancel(targetPrivate->pathEvaluator); 1877 targetPrivate->pathEvaluator = NULL; 1878 nw_release(targetPrivate->lastPath); 1879 targetPrivate->lastPath = NULL; 1880 nw_release(targetPrivate->lastPathParameters); 1881 targetPrivate->lastPathParameters = NULL; 1882 nw_release(targetPrivate->lastResolvedEndpoints); 1883 targetPrivate->lastResolvedEndpoints = NULL; 1884 if (NULL != targetPrivate->resolver) { 1885 nw_resolver_cancel(targetPrivate->resolver); 1886 targetPrivate->resolver = NULL; 1887 } 1888 if (targetPrivate->dispatchQueue != NULL) { 1889 dispatch_release(targetPrivate->dispatchQueue); 1890 targetPrivate->dispatchQueue = NULL; 1891 } 1892 1893 SC_log(LOG_DEBUG, "%sunscheduled", targetPrivate->log_prefix); 1894 } 1895 ok = TRUE; 1896done: 1897 return ok; 1898} 1899 1900Boolean 1901SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target, 1902 dispatch_queue_t queue) 1903{ 1904 if (!isA_SCNetworkReachability(target)) { 1905 _SCErrorSet(kSCStatusInvalidArgument); 1906 return FALSE; 1907 } 1908 1909 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1910 MUTEX_LOCK(&targetPrivate->lock); 1911 Boolean success = __SCNetworkReachabilitySetDispatchQueue(targetPrivate, queue); 1912 MUTEX_UNLOCK(&targetPrivate->lock); 1913 return success; 1914} 1915 1916/* 1917 * _SC_checkResolverReachabilityByAddress() 1918 * 1919 * Given an IP address, determine whether a reverse DNS query can be issued 1920 * using the current network configuration. 1921 */ 1922Boolean 1923_SC_checkResolverReachabilityByAddress(SCDynamicStoreRef *storeP, 1924 SCNetworkReachabilityFlags *flags, 1925 Boolean *haveDNS, 1926 struct sockaddr *sa) 1927{ 1928#pragma unused(storeP) 1929#pragma unused(sa) 1930 nw_path_evaluator_t evaluator = nw_path_create_default_evaluator(); 1931 nw_path_t path = nw_path_evaluator_copy_path(evaluator); 1932 if (nw_path_get_status(path) == nw_path_status_unsatisfied_network) { 1933 if (flags) { 1934 *flags = 0; 1935 } 1936 if (haveDNS) { 1937 *haveDNS = FALSE; 1938 } 1939 } else { 1940 if (flags) { 1941 *flags = kSCNetworkReachabilityFlagsReachable; 1942 } 1943 if (haveDNS) { 1944 *haveDNS = TRUE; 1945 } 1946 } 1947 nw_release(evaluator); 1948 nw_release(path); 1949 1950 return TRUE; 1951} 1952