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