1/* 2 * Copyright (c) 2011-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#include <CoreFoundation/CoreFoundation.h> 25#include <SystemConfiguration/SystemConfiguration.h> 26#include <SystemConfiguration/SCPrivate.h> 27#include "SCNetworkReachabilityInternal.h" 28 29#include <xpc/xpc.h> 30#include <xpc/private.h> 31#include <sys/rbtree.h> 32 33 34#pragma mark - 35#pragma mark Globals 36 37 38static Boolean serverAvailable = TRUE; 39 40 41#pragma mark - 42#pragma mark Support functions 43 44 45static void 46log_xpc_object(const char *msg, xpc_object_t obj) 47{ 48 char *desc; 49 50 desc = xpc_copy_description(obj); 51 SCLog(TRUE, LOG_DEBUG, CFSTR("%s = %s"), msg, desc); 52 free(desc); 53} 54 55 56#pragma mark - 57#pragma mark Reachability [RBT] client support 58 59 60typedef struct { 61 rb_node_t rbn; 62 SCNetworkReachabilityRef target; 63} reach_request_t; 64 65 66static int 67_rbt_compare_transaction_nodes(void *context, const void *n1, const void *n2) 68{ 69 uint64_t a = (uintptr_t)(((reach_request_t *)n1)->target); 70 uint64_t b = (uintptr_t)(((reach_request_t *)n2)->target); 71 72 if (a == b) { 73 return 0; 74 } else if (a < b) { 75 return -1; 76 } else { 77 return 1; 78 } 79} 80 81 82static int 83_rbt_compare_transaction_key(void *context, const void *n1, const void *key) 84{ 85 uint64_t a = (uintptr_t)(((reach_request_t *)n1)->target); 86 uint64_t b = *(uint64_t *)key; 87 88 if (a == b) { 89 return 0; 90 } else if (a < b) { 91 return -1; 92 } else { 93 return 1; 94 } 95} 96 97 98static rb_tree_t * 99_reach_requests_rbt() 100{ 101 static dispatch_once_t once; 102 static const rb_tree_ops_t ops = { 103 .rbto_compare_nodes = _rbt_compare_transaction_nodes, 104 .rbto_compare_key = _rbt_compare_transaction_key, 105 .rbto_node_offset = offsetof(reach_request_t, rbn), 106 .rbto_context = NULL 107 }; 108 static rb_tree_t rbt; 109 110 dispatch_once(&once, ^{ 111 rb_tree_init(&rbt, &ops); 112 }); 113 114 return &rbt; 115} 116 117 118static dispatch_queue_t 119_reach_requests_rbt_queue() 120{ 121 static dispatch_once_t once; 122 static dispatch_queue_t q; 123 124 dispatch_once(&once, ^{ 125 q = dispatch_queue_create(REACH_SERVICE_NAME ".requests.rbt", NULL); 126 }); 127 128 return q; 129} 130 131 132static reach_request_t * 133_reach_request_create(SCNetworkReachabilityRef target) 134{ 135 reach_request_t *request; 136 137 request = calloc(1, sizeof(*request)); 138 request->target = CFRetain(target); 139 140 return request; 141} 142 143 144static void 145_reach_request_release(reach_request_t *request) 146{ 147 SCNetworkReachabilityRef target = request->target; 148 149 CFRelease(target); 150 free(request); 151 152 return; 153} 154 155 156static void 157_reach_request_add(SCNetworkReachabilityRef target) 158{ 159 uint64_t target_id = (uintptr_t)target; 160 161 dispatch_sync(_reach_requests_rbt_queue(), ^{ 162 rb_tree_t *rbt = _reach_requests_rbt(); 163 reach_request_t *request; 164 165 request = rb_tree_find_node(rbt, &target_id); 166 if (request == NULL) { 167 request = _reach_request_create(target); 168 rb_tree_insert_node(rbt, request); 169 } 170 }); 171 172 return; 173} 174 175 176static void 177_reach_request_remove(SCNetworkReachabilityRef target) 178{ 179 uint64_t target_id = (uintptr_t)target; 180 181 dispatch_sync(_reach_requests_rbt_queue(), ^{ // FIXME ?? use dispatch_async? 182 rb_tree_t *rbt = _reach_requests_rbt(); 183 reach_request_t *request; 184 185 request = rb_tree_find_node(rbt, &target_id); 186 if (request != NULL) { 187 rb_tree_remove_node(rbt, request); 188 _reach_request_release(request); 189 } 190 }); 191 192 return; 193} 194 195 196static SCNetworkReachabilityRef 197_reach_request_copy_target(uint64_t target_id) 198{ 199 __block SCNetworkReachabilityRef target = NULL; 200 201 dispatch_sync(_reach_requests_rbt_queue(), ^{ 202 rb_tree_t *rbt = _reach_requests_rbt(); 203 reach_request_t *request; 204 205 request = rb_tree_find_node(rbt, &target_id); 206 if (request != NULL) { 207 target = request->target; 208 CFRetain(target); 209 } 210 }); 211 212 return target; 213} 214 215 216#pragma mark - 217#pragma mark Reachability [XPC] client support 218 219 220static void 221handle_reachability_status(SCNetworkReachabilityRef target, xpc_object_t dict) 222{ 223 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 224 225 if (_sc_debug) { 226 SCLog(TRUE, LOG_INFO, CFSTR("%sgot [async] notification"), 227 targetPrivate->log_prefix); 228// log_xpc_object(" status", dict); 229 } 230 231 __SCNetworkReachabilityUpdateConcurrent(target); 232 233 return; 234} 235 236 237static void 238handle_async_notification(SCNetworkReachabilityRef target, xpc_object_t dict) 239{ 240 int64_t op; 241 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 242 243 op = xpc_dictionary_get_int64(dict, MESSAGE_NOTIFY); 244 switch (op) { 245 case MESSAGE_REACHABILITY_STATUS : 246 handle_reachability_status(target, dict); 247 break; 248 default : 249 SCLog(TRUE, LOG_ERR, CFSTR("%sgot [async] unknown reply : %lld"), 250 targetPrivate->log_prefix, 251 op); 252 log_xpc_object(" reply", dict); 253 break; 254 } 255 256 return; 257} 258 259 260static dispatch_queue_t 261_reach_xpc_queue() 262{ 263 static dispatch_once_t once; 264 static dispatch_queue_t q; 265 266 dispatch_once(&once, ^{ 267 q = dispatch_queue_create(REACH_SERVICE_NAME ".xpc", NULL); 268 }); 269 270 return q; 271} 272 273 274static void 275_reach_connection_reconnect(xpc_connection_t connection); 276 277 278static xpc_connection_t 279_reach_connection_create() 280{ 281 xpc_connection_t c; 282#if !TARGET_IPHONE_SIMULATOR 283 const uint64_t flags = XPC_CONNECTION_MACH_SERVICE_PRIVILEGED; 284#else // !TARGET_IPHONE_SIMULATOR 285 const uint64_t flags = 0; 286#endif // !TARGET_IPHONE_SIMULATOR 287 const char *name; 288 dispatch_queue_t q = _reach_xpc_queue(); 289 290 // create XPC connection 291 name = getenv("REACH_SERVER"); 292 if ((name == NULL) || (issetugid() != 0)) { 293 name = REACH_SERVICE_NAME; 294 } 295 296 c = xpc_connection_create_mach_service(name, q, flags); 297 298 xpc_connection_set_event_handler(c, ^(xpc_object_t xobj) { 299 xpc_type_t type; 300 301 type = xpc_get_type(xobj); 302 if (type == XPC_TYPE_DICTIONARY) { 303 SCNetworkReachabilityRef target; 304 uint64_t target_id; 305 306 target_id = xpc_dictionary_get_uint64(xobj, REACH_CLIENT_TARGET_ID); 307 if (target_id == 0) { 308 SCLog(TRUE, LOG_ERR, 309 CFSTR("reach client %p: async reply with no target [ID]"), 310 c); 311 log_xpc_object(" reply", xobj); 312 return; 313 } 314 315 target = _reach_request_copy_target(target_id); 316 if (target == NULL) { 317// SCLog(TRUE, LOG_ERR, 318// CFSTR("received unexpected target [ID] from SCNetworkReachability server")); 319// log_xpc_object(" reply", xobj); 320 return; 321 } 322 323 xpc_retain(xobj); 324 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{ 325 handle_async_notification(target, xobj); 326 CFRelease(target); 327 xpc_release(xobj); 328 }); 329 330 } else if (type == XPC_TYPE_ERROR) { 331 if (xobj == XPC_ERROR_CONNECTION_INVALID) { 332 SCLog(TRUE, LOG_ERR, 333 CFSTR("SCNetworkReachability server not available")); 334 serverAvailable = FALSE; 335 } else if (xobj == XPC_ERROR_CONNECTION_INTERRUPTED) { 336 SCLog(TRUE, LOG_DEBUG, 337 CFSTR("SCNetworkReachability server failure, reconnecting")); 338 _reach_connection_reconnect(c); 339 } else { 340 const char *desc; 341 342 desc = xpc_dictionary_get_string(xobj, XPC_ERROR_KEY_DESCRIPTION); 343 SCLog(TRUE, LOG_ERR, 344 CFSTR("reach client %p: Connection error: %s"), 345 c, 346 desc); 347 } 348 349 } else { 350 SCLog(TRUE, LOG_ERR, 351 CFSTR("reach client %p: unknown event type : %p"), 352 c, 353 type); 354 } 355 }); 356 xpc_connection_resume(c); 357 358 return c; 359} 360 361 362static xpc_connection_t 363_reach_connection() 364{ 365 static xpc_connection_t c; 366 static dispatch_once_t once; 367 static dispatch_queue_t q; 368 369 if (!serverAvailable) { 370 // if SCNetworkReachability [XPC] server not available 371 return NULL; 372 } 373 374 dispatch_once(&once, ^{ 375 q = dispatch_queue_create(REACH_SERVICE_NAME ".connection", NULL); 376 }); 377 378 dispatch_sync(q, ^{ 379 if (c == NULL) { 380 c = _reach_connection_create(); 381 } 382 }); 383 384 return c; 385} 386 387 388typedef void (^reach_server_reply_handler_t)(xpc_object_t reply); 389 390 391static void 392add_proc_name(xpc_object_t reqdict) 393{ 394 static const char *name = NULL; 395 static dispatch_once_t once; 396 397 // add the process name 398 dispatch_once(&once, ^{ 399 name = getprogname(); 400 }); 401 xpc_dictionary_set_string(reqdict, REACH_CLIENT_PROC_NAME, name); 402 403 return; 404} 405 406 407static void 408_reach_server_target_reconnect(xpc_connection_t connection, SCNetworkReachabilityRef target, Boolean disconnect); 409 410 411static Boolean 412_reach_server_target_add(xpc_connection_t connection, SCNetworkReachabilityRef target) 413{ 414 Boolean ok = FALSE; 415 xpc_object_t reply; 416 xpc_object_t reqdict; 417 Boolean retry = FALSE; 418 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 419 420 // create message 421 reqdict = xpc_dictionary_create(NULL, NULL, 0); 422 423 // set request 424 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_CREATE); 425 426 // add reachability target info 427 switch (targetPrivate->type) { 428 case reachabilityTypeName : 429 xpc_dictionary_set_string(reqdict, 430 REACH_TARGET_NAME, 431 targetPrivate->name); 432 break; 433 case reachabilityTypeAddress : 434 case reachabilityTypeAddressPair : 435 if (targetPrivate->localAddress != NULL) { 436 xpc_dictionary_set_data(reqdict, 437 REACH_TARGET_LOCAL_ADDR, 438 targetPrivate->localAddress, 439 targetPrivate->localAddress->sa_len); 440 } 441 if (targetPrivate->remoteAddress != NULL) { 442 xpc_dictionary_set_data(reqdict, 443 REACH_TARGET_REMOTE_ADDR, 444 targetPrivate->remoteAddress, 445 targetPrivate->remoteAddress->sa_len); 446 } 447 break; 448 case reachabilityTypePTR : 449 xpc_dictionary_set_data(reqdict, 450 REACH_TARGET_PTR_ADDR, 451 targetPrivate->remoteAddress, 452 targetPrivate->remoteAddress->sa_len); 453 break; 454 } 455 if (targetPrivate->if_index != 0) { 456 xpc_dictionary_set_int64(reqdict, 457 REACH_TARGET_IF_INDEX, 458 targetPrivate->if_index); 459 xpc_dictionary_set_string(reqdict, 460 REACH_TARGET_IF_NAME, 461 targetPrivate->if_name); 462 } 463 if (targetPrivate->onDemandBypass) { 464 xpc_dictionary_set_bool(reqdict, 465 REACH_TARGET_ONDEMAND_BYPASS, 466 TRUE); 467 } 468 if (targetPrivate->resolverBypass) { 469 xpc_dictionary_set_bool(reqdict, 470 REACH_TARGET_RESOLVER_BYPASS, 471 TRUE); 472 } 473 474 475 476 // add the target [ID] 477 xpc_dictionary_set_uint64(reqdict, REACH_CLIENT_TARGET_ID, (uintptr_t)target); 478 479 // add the process name (for debugging) 480 add_proc_name(reqdict); 481 482 retry : 483 484 // send request to the SCNetworkReachability server 485 reply = xpc_connection_send_message_with_reply_sync(connection, reqdict); 486 if (reply != NULL) { 487 xpc_type_t type; 488 489 type = xpc_get_type(reply); 490 if (type == XPC_TYPE_DICTIONARY) { 491 int64_t status; 492 493 status = xpc_dictionary_get_int64(reply, REACH_REQUEST_REPLY); 494 ok = (status == REACH_REQUEST_REPLY_OK); 495 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INVALID)) { 496 SCLog(TRUE, LOG_ERR, 497 CFSTR("SCNetworkReachability server not available")); 498 serverAvailable = FALSE; 499 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INTERRUPTED)) { 500 SCLog(TRUE, LOG_DEBUG, 501 CFSTR("reach target %p: SCNetworkReachability server failure, retrying"), 502 target); 503 retry = TRUE; 504 } else { 505 SCLog(TRUE, LOG_ERR, 506 CFSTR("reach target %p: _targetAdd with unexpected reply"), 507 target); 508 log_xpc_object(" reply", reply); 509 } 510 511 xpc_release(reply); 512 } 513 514 if (retry) { 515 retry = FALSE; 516 goto retry; 517 } 518 519 xpc_release(reqdict); 520 return ok; 521} 522 523 524static Boolean 525_reach_server_target_remove(xpc_connection_t connection, SCNetworkReachabilityRef target) 526{ 527 Boolean ok = FALSE; 528 xpc_object_t reply; 529 xpc_object_t reqdict; 530 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 531 532 // create message 533 reqdict = xpc_dictionary_create(NULL, NULL, 0); 534 535 // set request 536 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_REMOVE); 537 538 // add the target [ID] 539 xpc_dictionary_set_uint64(reqdict, REACH_CLIENT_TARGET_ID, (uintptr_t)target); 540 541 reply = xpc_connection_send_message_with_reply_sync(connection, reqdict); 542 if (reply != NULL) { 543 xpc_type_t type; 544 545 type = xpc_get_type(reply); 546 if (type == XPC_TYPE_DICTIONARY) { 547 int64_t status; 548 549 status = xpc_dictionary_get_int64(reply, REACH_REQUEST_REPLY); 550 switch (status) { 551 case REACH_REQUEST_REPLY_OK : 552 ok = TRUE; 553 break; 554 case REACH_REQUEST_REPLY_UNKNOWN : 555 // target not known by the server (most likely due to a 556 // SCNetworkReachability server failure), no need to 557 // remove. 558 ok = TRUE; 559 break; 560 default : { 561 SCLog(TRUE, LOG_ERR, CFSTR("%s target remove failed"), 562 targetPrivate->log_prefix); 563 log_xpc_object(" reply", reply); 564 } 565 } 566 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INVALID)) { 567 SCLog(TRUE, LOG_ERR, 568 CFSTR("SCNetworkReachability server not available")); 569 serverAvailable = FALSE; 570 ok = TRUE; 571 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INTERRUPTED)) { 572 SCLog(TRUE, LOG_DEBUG, 573 CFSTR("reach target %p: SCNetworkReachability server failure, no need to remove"), 574 target); 575 ok = TRUE; 576 } else { 577 SCLog(TRUE, LOG_ERR, 578 CFSTR("reach target %p: _targetRemove with unexpected reply"), 579 target); 580 log_xpc_object(" reply", reply); 581 } 582 583 xpc_release(reply); 584 } 585 586 xpc_release(reqdict); 587 return ok; 588} 589 590 591static Boolean 592_reach_server_target_schedule(xpc_connection_t connection, SCNetworkReachabilityRef target) 593{ 594 Boolean ok = FALSE; 595 xpc_object_t reply; 596 xpc_object_t reqdict; 597 Boolean retry = FALSE; 598 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 599 600 // create message 601 reqdict = xpc_dictionary_create(NULL, NULL, 0); 602 603 // set request 604 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_SCHEDULE); 605 606 // add the target [ID] 607 xpc_dictionary_set_uint64(reqdict, REACH_CLIENT_TARGET_ID, (uintptr_t)target); 608 609 retry : 610 611 reply = xpc_connection_send_message_with_reply_sync(connection, reqdict); 612 if (reply != NULL) { 613 xpc_type_t type; 614 615 type = xpc_get_type(reply); 616 if (type == XPC_TYPE_DICTIONARY) { 617 int64_t status; 618 619 status = xpc_dictionary_get_int64(reply, REACH_REQUEST_REPLY); 620 switch (status) { 621 case REACH_REQUEST_REPLY_OK : 622 ok = TRUE; 623 break; 624 case REACH_REQUEST_REPLY_UNKNOWN : 625 // target not known by the server (most likely due to a 626 // SCNetworkReachability server failure), re-establish 627 // and retry scheduling. 628 retry = TRUE; 629 break; 630 default : { 631 SCLog(TRUE, LOG_ERR, CFSTR("%s target schedule failed"), 632 targetPrivate->log_prefix); 633 log_xpc_object(" reply", reply); 634 } 635 } 636 637 if (ok) { 638 CFRetain(target); 639 } 640 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INVALID)) { 641 SCLog(TRUE, LOG_ERR, 642 CFSTR("SCNetworkReachability server not available")); 643 serverAvailable = FALSE; 644 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INTERRUPTED)) { 645 SCLog(TRUE, LOG_DEBUG, 646 CFSTR("reach target %p: SCNetworkReachability server failure, retry schedule"), 647 target); 648 retry = TRUE; 649 } else { 650 SCLog(TRUE, LOG_ERR, 651 CFSTR("reach target %p: _targetSchedule with unexpected reply"), 652 target); 653 log_xpc_object(" reply", reply); 654 } 655 656 xpc_release(reply); 657 } 658 659 if (retry) { 660 // reconnect 661 _reach_server_target_reconnect(connection, target, FALSE); 662 663 // and retry 664 retry = FALSE; 665 goto retry; 666 } 667 668 xpc_release(reqdict); 669 return ok; 670} 671 672 673static void 674_reach_reply_set_reachability(SCNetworkReachabilityRef target, 675 xpc_object_t reply) 676{ 677 char *if_name; 678 size_t len = 0; 679 680 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 681 682 targetPrivate->serverInfo.cycle = xpc_dictionary_get_uint64(reply, 683 REACH_STATUS_CYCLE); 684 685 targetPrivate->serverInfo.flags = (SCNetworkReachabilityFlags)xpc_dictionary_get_uint64(reply, 686 REACH_STATUS_FLAGS); 687 688 targetPrivate->serverInfo.if_index = (unsigned int)xpc_dictionary_get_uint64(reply, 689 REACH_STATUS_IF_INDEX); 690 691 bzero(&targetPrivate->serverInfo.if_name, sizeof(targetPrivate->serverInfo.if_name)); 692 if_name = (void *)xpc_dictionary_get_data(reply, 693 REACH_STATUS_IF_NAME, 694 &len); 695 if ((if_name != NULL) && (len > 0)) { 696 if (len > sizeof(targetPrivate->serverInfo.if_name)) { 697 len = sizeof(targetPrivate->serverInfo.if_name); 698 } 699 700 bcopy(if_name, targetPrivate->serverInfo.if_name, len); 701 } 702 703 targetPrivate->serverInfo.sleeping = xpc_dictionary_get_bool(reply, 704 REACH_STATUS_SLEEPING); 705 706 if (isReachabilityTypeName(targetPrivate->type)) { 707 xpc_object_t addresses; 708 709 if (targetPrivate->resolvedAddresses != NULL) { 710 CFRelease(targetPrivate->resolvedAddresses); 711 targetPrivate->resolvedAddresses = NULL; 712 } 713 714 targetPrivate->resolvedError = (int)xpc_dictionary_get_int64(reply, 715 REACH_STATUS_RESOLVED_ERROR); 716 717 addresses = xpc_dictionary_get_value(reply, REACH_STATUS_RESOLVED_ADDRESSES); 718 if ((addresses != NULL) && (xpc_get_type(addresses) != XPC_TYPE_ARRAY)) { 719 addresses = NULL; 720 } 721 722 if ((targetPrivate->resolvedError == NETDB_SUCCESS) && (addresses != NULL)) { 723 int i; 724 size_t n; 725 CFMutableArrayRef newAddresses; 726 727 newAddresses = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 728 729 n = xpc_array_get_count(addresses); 730 for (i = 0; i < n; i++) { 731 if (targetPrivate->type == reachabilityTypeName) { 732 struct sockaddr *sa; 733 size_t len; 734 CFDataRef newAddress; 735 736 sa = (struct sockaddr *)xpc_array_get_data(addresses, i, &len); 737 newAddress = CFDataCreate(NULL, (const UInt8 *)sa, len); 738 CFArrayAppendValue(newAddresses, newAddress); 739 CFRelease(newAddress); 740 } else if (targetPrivate->type == reachabilityTypePTR) { 741 const char *str; 742 CFStringRef newName; 743 744 str = xpc_array_get_string(addresses, i); 745 newName = CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8); 746 CFArrayAppendValue(newAddresses, newName); 747 CFRelease(newName); 748 } 749 } 750 751 targetPrivate->resolvedAddresses = newAddresses; 752 } else { 753 /* save the error associated with the attempt to resolve the name */ 754 targetPrivate->resolvedAddresses = CFRetain(kCFNull); 755 } 756 757 targetPrivate->dnsFlags = (uint32_t)xpc_dictionary_get_uint64(reply, 758 REACH_STATUS_DNS_FLAGS); 759 760 targetPrivate->needResolve = FALSE; 761 } 762 763 return; 764} 765 766 767static Boolean 768_reach_server_target_status(xpc_connection_t connection, SCNetworkReachabilityRef target) 769{ 770 Boolean ok = FALSE; 771 xpc_object_t reply; 772 xpc_object_t reqdict; 773 Boolean retry = FALSE; 774 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 775 776 if (_sc_debug) { 777 CFStringRef str; 778 779 str = _SCNetworkReachabilityCopyTargetDescription(target); 780 SCLog(TRUE, LOG_INFO, CFSTR("%scheckReachability(%@)"), 781 targetPrivate->log_prefix, 782 str); 783 CFRelease(str); 784 } 785 786 // create message 787 reqdict = xpc_dictionary_create(NULL, NULL, 0); 788 789 // set request 790 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_STATUS); 791 792 // add the target [ID] 793 xpc_dictionary_set_uint64(reqdict, REACH_CLIENT_TARGET_ID, (uintptr_t)target); 794 795 retry : 796 797 reply = xpc_connection_send_message_with_reply_sync(connection, reqdict); 798 if (reply != NULL) { 799 xpc_type_t type; 800 801 type = xpc_get_type(reply); 802 if (type == XPC_TYPE_DICTIONARY) { 803 int64_t status; 804 805 status = xpc_dictionary_get_int64(reply, REACH_REQUEST_REPLY); 806 switch (status) { 807 case REACH_REQUEST_REPLY_OK : 808 ok = TRUE; 809 break; 810 case REACH_REQUEST_REPLY_UNKNOWN : 811 // target not known by the server (most likely due to a 812 // SCNetworkReachability server failure), re-establish 813 // and retry status. 814 retry = TRUE; 815 break; 816 default : 817 SCLog(TRUE, LOG_INFO, CFSTR("%s target status failed"), 818 targetPrivate->log_prefix); 819 log_xpc_object(" reply", reply); 820 } 821 822 if (ok) { 823 _reach_reply_set_reachability(target, reply); 824 825 if (_sc_debug) { 826 SCLog(TRUE, LOG_INFO, CFSTR("%s flags = 0x%08x"), 827 targetPrivate->log_prefix, 828 targetPrivate->serverInfo.flags); 829 if (targetPrivate->serverInfo.if_index != 0) { 830 SCLog(TRUE, LOG_INFO, CFSTR("%s device = %s (%u%s)"), 831 targetPrivate->log_prefix, 832 targetPrivate->serverInfo.if_name, 833 targetPrivate->serverInfo.if_index, 834 targetPrivate->serverInfo.sleeping ? ", z" : ""); 835 } 836 if (targetPrivate->serverInfo.cycle != targetPrivate->cycle) { 837 SCLog(TRUE, LOG_INFO, CFSTR("%s forced"), 838 targetPrivate->log_prefix); 839 } 840 } 841 } 842 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INVALID)) { 843 SCLog(TRUE, LOG_ERR, 844 CFSTR("SCNetworkReachability server not available")); 845 serverAvailable = FALSE; 846 ok = TRUE; 847 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INTERRUPTED)) { 848 SCLog(TRUE, LOG_DEBUG, 849 CFSTR("reach target %p: SCNetworkReachability server failure, retry status"), 850 target); 851 retry = TRUE; 852 } else { 853 SCLog(TRUE, LOG_ERR, 854 CFSTR("reach target %p: _targetStatus with unexpected reply"), 855 target); 856 log_xpc_object(" reply", reply); 857 } 858 859 xpc_release(reply); 860 } 861 862 if (retry) { 863 // reconnect 864 _reach_server_target_reconnect(connection, target, FALSE); 865 866 // and retry 867 retry = FALSE; 868 goto retry; 869 } 870 871 xpc_release(reqdict); 872 return ok; 873} 874 875 876static Boolean 877_reach_server_target_unschedule(xpc_connection_t connection, SCNetworkReachabilityRef target) 878{ 879 Boolean ok = FALSE; 880 xpc_object_t reply; 881 xpc_object_t reqdict; 882 Boolean retry = FALSE; 883 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 884 885 // create message 886 reqdict = xpc_dictionary_create(NULL, NULL, 0); 887 888 // set request 889 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_UNSCHEDULE); 890 891 // add the target [ID] 892 xpc_dictionary_set_uint64(reqdict, REACH_CLIENT_TARGET_ID, (uintptr_t)target); 893 894 reply = xpc_connection_send_message_with_reply_sync(connection, reqdict); 895 if (reply != NULL) { 896 xpc_type_t type; 897 898 type = xpc_get_type(reply); 899 if (type == XPC_TYPE_DICTIONARY) { 900 int64_t status; 901 902 status = xpc_dictionary_get_int64(reply, REACH_REQUEST_REPLY); 903 switch (status) { 904 case REACH_REQUEST_REPLY_OK : 905 ok = TRUE; 906 break; 907 case REACH_REQUEST_REPLY_UNKNOWN : 908 // target not known by the server (most likely due to a 909 // SCNetworkReachability server failure), re-establish 910 // but no need to unschedule. 911 retry = TRUE; 912 break; 913 default : 914 SCLog(TRUE, LOG_INFO, CFSTR("%s target unschedule failed"), 915 targetPrivate->log_prefix); 916 log_xpc_object(" reply", reply); 917 } 918 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INVALID)) { 919 SCLog(TRUE, LOG_ERR, 920 CFSTR("SCNetworkReachability server not available")); 921 serverAvailable = FALSE; 922 ok = TRUE; 923 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INTERRUPTED)) { 924 SCLog(TRUE, LOG_DEBUG, 925 CFSTR("reach target %p: SCNetworkReachability server failure, re-establish (but do not re-schedule)"), 926 target); 927 retry = TRUE; 928 } else { 929 SCLog(TRUE, LOG_ERR, 930 CFSTR("reach target %p: _targetUnschedule with unexpected reply"), 931 target); 932 log_xpc_object(" reply", reply); 933 } 934 935 xpc_release(reply); 936 } 937 938 if (retry) { 939 // reconnect 940 targetPrivate->serverScheduled = FALSE; 941 _reach_server_target_reconnect(connection, target, FALSE); 942 ok = TRUE; 943 } 944 945 if (ok) { 946 CFRelease(target); 947 } 948 949 xpc_release(reqdict); 950 return ok; 951} 952 953 954#pragma mark - 955#pragma mark Reconnect 956 957 958static void 959_reach_server_target_reconnect(xpc_connection_t connection, SCNetworkReachabilityRef target, Boolean disconnect) 960{ 961 Boolean ok; 962 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 963 964 if (!targetPrivate->serverActive) { 965 // if target already removed 966 return; 967 } 968 969 if (disconnect) { 970 // if we should first disconnect (unschedule, remove) 971 if (targetPrivate->serverScheduled) { 972 (void) _reach_server_target_unschedule(connection, target); 973 } 974 975 (void) _reach_server_target_remove(connection, target); 976 } else { 977 // server has been restarted 978 targetPrivate->cycle = 0; 979 } 980 981 // re-associate with server 982 ok = _reach_server_target_add(connection, target); 983 if (!ok) { 984 // if we could not add the target 985 return; 986 } 987 988 if (!targetPrivate->serverScheduled) { 989 // if not scheduled 990 return; 991 } 992 993 // ... and re-schedule with server 994 ok = _reach_server_target_schedule(connection, target); 995 if (!ok) { 996 // if we could not reschedule the target 997 return; 998 } 999 1000 // For addresses, update our status now. For names, queries will 1001 // be updated with a callback 1002 if (isReachabilityTypeAddress(targetPrivate->type)) { 1003 __SCNetworkReachabilityUpdate(target); 1004 } 1005 1006 return; 1007} 1008 1009 1010static void 1011_reach_connection_reconnect(xpc_connection_t connection) 1012{ 1013 dispatch_sync(_reach_requests_rbt_queue(), ^{ 1014 rb_tree_t *rbt = _reach_requests_rbt(); 1015 reach_request_t *request; 1016 1017 RB_TREE_FOREACH(request, rbt) { 1018 SCNetworkReachabilityRef target; 1019 1020 xpc_retain(connection); 1021 target = request->target; 1022 CFRetain(target); 1023 dispatch_async(__SCNetworkReachability_concurrent_queue(), ^{ 1024 _reach_server_target_reconnect(connection, target, FALSE); 1025 CFRelease(target); 1026 xpc_release(connection); 1027 }); 1028 } 1029 }); 1030 1031 return; 1032} 1033 1034 1035#pragma mark - 1036#pragma mark SPI (exposed) 1037 1038 1039Boolean 1040_SCNetworkReachabilityServer_snapshot(void) 1041{ 1042 xpc_connection_t c; 1043 Boolean ok = FALSE; 1044 xpc_object_t reply; 1045 xpc_object_t reqdict; 1046 1047 // initialize connection with SCNetworkReachability server 1048 c = _reach_connection(); 1049 if (c == NULL) { 1050 return FALSE; 1051 } 1052 1053 // create message 1054 reqdict = xpc_dictionary_create(NULL, NULL, 0); 1055 1056 // set request 1057 xpc_dictionary_set_int64(reqdict, REACH_REQUEST, REACH_REQUEST_SNAPSHOT); 1058 1059 // add the process name (for debugging) 1060 add_proc_name(reqdict); 1061 1062 retry : 1063 1064 // send request 1065 reply = xpc_connection_send_message_with_reply_sync(c, reqdict); 1066 if (reply != NULL) { 1067 xpc_type_t type; 1068 1069 type = xpc_get_type(reply); 1070 if (type == XPC_TYPE_DICTIONARY) { 1071 int64_t status; 1072 1073 status = xpc_dictionary_get_int64(reply, REACH_REQUEST_REPLY); 1074 ok = (status == REACH_REQUEST_REPLY_OK); 1075 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INVALID)) { 1076 SCLog(TRUE, LOG_ERR, 1077 CFSTR("SCNetworkReachability server not available")); 1078 serverAvailable = FALSE; 1079 } else if ((type == XPC_TYPE_ERROR) && (reply == XPC_ERROR_CONNECTION_INTERRUPTED)) { 1080 SCLog(TRUE, LOG_DEBUG, 1081 CFSTR("SCNetworkReachability server failure, retrying")); 1082 xpc_release(reply); 1083 goto retry; 1084 } else { 1085 SCLog(TRUE, LOG_ERR, 1086 CFSTR("_snapshot with unexpected reply")); 1087 log_xpc_object(" reply", reply); 1088 } 1089 1090 xpc_release(reply); 1091 } 1092 1093 xpc_release(reqdict); 1094 return ok; 1095} 1096 1097 1098__private_extern__ 1099Boolean 1100__SCNetworkReachabilityServer_targetAdd(SCNetworkReachabilityRef target) 1101{ 1102 xpc_connection_t c; 1103 Boolean ok; 1104 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1105 1106 c = _reach_connection(); 1107 if (c == NULL) { 1108 return FALSE; 1109 } 1110 1111 ok = _reach_server_target_add(c, target); 1112 if (ok) { 1113 _SC_ATOMIC_CMPXCHG(&targetPrivate->serverActive, FALSE, TRUE); 1114 } 1115 1116 return ok; 1117} 1118 1119 1120__private_extern__ 1121void 1122__SCNetworkReachabilityServer_targetRemove(SCNetworkReachabilityRef target) 1123{ 1124 xpc_connection_t c; 1125 Boolean ok; 1126 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1127 1128 if (!targetPrivate->serverActive) { 1129 // if not active 1130 return; 1131 } 1132 1133 c = _reach_connection(); 1134 if (c == NULL) { 1135 return; 1136 } 1137 1138 ok = _reach_server_target_remove(c, target); 1139 if (ok) { 1140 _SC_ATOMIC_CMPXCHG(&targetPrivate->serverActive, TRUE, FALSE); 1141 } 1142 1143 return; 1144} 1145 1146 1147__private_extern__ 1148Boolean 1149__SCNetworkReachabilityServer_targetSchedule(SCNetworkReachabilityRef target) 1150{ 1151 xpc_connection_t c; 1152 Boolean ok; 1153 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1154 1155 c = _reach_connection(); 1156 if (c == NULL) { 1157 return FALSE; 1158 } 1159 1160 _reach_request_add(target); 1161 ok = _reach_server_target_schedule(c, target); 1162 if (ok) { 1163 _SC_ATOMIC_CMPXCHG(&targetPrivate->serverScheduled, FALSE, TRUE); 1164 } else { 1165 _reach_request_remove(target); 1166 } 1167 1168 return ok; 1169} 1170 1171 1172__private_extern__ 1173Boolean 1174__SCNetworkReachabilityServer_targetStatus(SCNetworkReachabilityRef target) 1175{ 1176 xpc_connection_t c; 1177 Boolean ok; 1178 1179 c = _reach_connection(); 1180 if (c == NULL) { 1181 return FALSE; 1182 } 1183 1184 ok = _reach_server_target_status(c, target); 1185 return ok; 1186} 1187 1188 1189__private_extern__ 1190Boolean 1191__SCNetworkReachabilityServer_targetUnschedule(SCNetworkReachabilityRef target) 1192{ 1193 xpc_connection_t c; 1194 Boolean ok; 1195 SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; 1196 1197 if (!targetPrivate->serverScheduled) { 1198 // if not scheduled 1199 return TRUE; 1200 } 1201 1202 c = _reach_connection(); 1203 if (c == NULL) { 1204 return FALSE; 1205 } 1206 1207 ok = _reach_server_target_unschedule(c, target); 1208 if (ok) { 1209 _SC_ATOMIC_CMPXCHG(&targetPrivate->serverScheduled, TRUE, FALSE); 1210 _reach_request_remove(target); 1211 } else { 1212 // if unschedule failed 1213 } 1214 1215 return ok; 1216} 1217 1218 1219