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 25#include <pthread.h> 26#include <notify.h> 27#include <stdlib.h> 28#include <stdio.h> 29#include <sys/socket.h> 30#include <dispatch/dispatch.h> 31#include <xpc/xpc.h> 32 33#include "libSystemConfiguration_client.h" 34#include "network_information.h" 35#include "network_information_priv.h" 36 37static nwi_state_t G_nwi_state = NULL; 38static pthread_mutex_t nwi_store_lock = PTHREAD_MUTEX_INITIALIZER; 39static boolean_t nwi_store_token_valid = FALSE; 40 41static pthread_once_t initialized = PTHREAD_ONCE_INIT; 42static int nwi_store_token; 43 44static boolean_t nwi_store_force_refresh = FALSE; 45 46#pragma mark - 47#pragma mark Network information [nwi] client support 48 49 50// Note: protected by __nwi_configuration_queue() 51static int nwi_active = 0; 52static libSC_info_client_t *nwi_client = NULL; 53 54 55static dispatch_queue_t 56__nwi_configuration_queue() 57{ 58 static dispatch_once_t once; 59 static dispatch_queue_t q; 60 61 dispatch_once(&once, ^{ 62 q = dispatch_queue_create(NWI_SERVICE_NAME, NULL); 63 }); 64 65 return q; 66} 67 68static 69void 70_nwi_state_initialize(void) 71{ 72 const char *nwi_key = nwi_state_get_notify_key(); 73 uint32_t status = notify_register_check(nwi_key, 74 &nwi_store_token); 75 76 if (status != NOTIFY_STATUS_OK) { 77 fprintf(stderr, "nwi_state: registration failed (%u)\n", status); 78 } 79 else { 80 nwi_store_token_valid = TRUE; 81 } 82} 83 84static 85void 86_nwi_set_alias(nwi_state* state, nwi_ifstate* ifstate) 87{ 88 nwi_ifstate* ifstate_alias; 89 int af = ifstate->af; 90 int af_alias; 91 92 af_alias = (af == AF_INET)?AF_INET6:AF_INET; 93 94 ifstate_alias = 95 nwi_state_get_ifstate_with_name(state, af_alias, 96 ifstate->ifname); 97 98 if (ifstate_alias != NULL) { 99 ifstate_alias->af_alias = ifstate; 100 } 101 ifstate->af_alias = ifstate_alias; 102 return; 103} 104 105static 106void 107_nwi_state_reset_alias(nwi_state_t state) { 108 int i; 109 110 for (i = 0; i < state->ipv4_count; i++) { 111 state->nwi_ifstates[i].af_alias = NULL; 112 } 113 114 for (i = state->ipv6_start; 115 i < state->ipv6_start + state->ipv6_count; i++) { 116 _nwi_set_alias(state, &state->nwi_ifstates[i]); 117 } 118} 119 120 121#pragma mark - 122#pragma mark Network information [nwi] APIs 123 124 125/* 126 * Function: nwi_state_get_notify_key 127 * Purpose: 128 * Returns the BSD notify key to use to monitor when the state changes. 129 * 130 * Note: 131 * The nwi_state_copy API uses this notify key to monitor when the state 132 * changes, so each invocation of nwi_state_copy returns the current 133 * information. 134 */ 135const char * 136nwi_state_get_notify_key() 137{ 138#if !TARGET_IPHONE_SIMULATOR 139 return "com.apple.system.SystemConfiguration.nwi"; 140#else // !TARGET_IPHONE_SIMULATOR 141 return "com.apple.iOS_Simulator.SystemConfiguration.nwi"; 142#endif // !TARGET_IPHONE_SIMULATOR 143} 144 145#define ATOMIC_CMPXCHG(p, o, n) __sync_bool_compare_and_swap((p), (o), (n)) 146#define ATOMIC_INC(p) __sync_fetch_and_add((p), 1) // return (n++); 147#define ATOMIC_DEC(p) __sync_sub_and_fetch((p), 1) // return (--n); 148 149void 150_nwi_state_force_refresh() 151{ 152 ATOMIC_CMPXCHG(&nwi_store_force_refresh, FALSE, TRUE); 153} 154 155static void 156nwi_state_retain(nwi_state_t state) 157{ 158 ATOMIC_INC(&state->ref); 159 return; 160} 161 162/* 163 * Function: nwi_state_release 164 * Purpose: 165 * Release the memory associated with the network state. 166 */ 167void 168nwi_state_release(nwi_state_t state) 169{ 170 if (ATOMIC_DEC(&state->ref) > 0) { 171 // if not last reference 172 return; 173 } 174 175 // release connection reference on 1-->0 transition 176 if (state->svr) { 177 dispatch_sync(__nwi_configuration_queue(), ^{ 178 if (--nwi_active == 0) { 179 // if last reference, drop connection 180 libSC_info_client_release(nwi_client); 181 nwi_client = NULL; 182 } 183 }); 184 } 185 186 // release nwi_state 187 free(state); 188 189 return; 190} 191 192static nwi_state * 193_nwi_state_copy_data() 194{ 195 nwi_state_t nwi_state = NULL; 196 static const char *proc_name = NULL; 197 xpc_object_t reqdict; 198 xpc_object_t reply; 199 200 dispatch_sync(__nwi_configuration_queue(), ^{ 201 if ((nwi_active++ == 0) || (nwi_client == NULL)) { 202 static dispatch_once_t once; 203 static const char *service_name = NWI_SERVICE_NAME; 204 205 dispatch_once(&once, ^{ 206 const char *name; 207 208 // get [XPC] service name 209 name = getenv(service_name); 210 if ((name != NULL) && (issetugid() == 0)) { 211 service_name = strdup(name); 212 } 213 214 // get process name 215 proc_name = getprogname(); 216 }); 217 218 nwi_client = 219 libSC_info_client_create(__nwi_configuration_queue(), // dispatch queue 220 service_name, // XPC service name 221 "Network information"); // service description 222 if (nwi_client == NULL) { 223 --nwi_active; 224 } 225 } 226 }); 227 228 if ((nwi_client == NULL) || !nwi_client->active) { 229 // if network information server not available 230 return NULL; 231 } 232 233 // create message 234 reqdict = xpc_dictionary_create(NULL, NULL, 0); 235 236 // set process name 237 if (proc_name != NULL) { 238 xpc_dictionary_set_string(reqdict, NWI_PROC_NAME, proc_name); 239 } 240 241 // set request 242 xpc_dictionary_set_int64(reqdict, NWI_REQUEST, NWI_REQUEST_COPY); 243 244 // send request to the DNS configuration server 245 reply = libSC_send_message_with_reply_sync(nwi_client, reqdict); 246 xpc_release(reqdict); 247 248 if (reply != NULL) { 249 const void *dataRef; 250 size_t dataLen = 0; 251 252 dataRef = xpc_dictionary_get_data(reply, NWI_CONFIGURATION, &dataLen); 253 if (dataRef != NULL) { 254 nwi_state = malloc(dataLen); 255 bcopy((void *)dataRef, nwi_state, dataLen); 256 nwi_state->ref = 0; 257 nwi_state->svr = TRUE; 258 } 259 260 xpc_release(reply); 261 } 262 263 return nwi_state; 264} 265 266/* 267 * Function: nwi_state_copy 268 * Purpose: 269 * Returns the current network state information. 270 * Release after use by calling nwi_state_release(). 271 */ 272nwi_state_t 273nwi_state_copy(void) 274{ 275 boolean_t force_refresh; 276 nwi_state_t nwi_state = NULL; 277 nwi_state_t old_state = NULL; 278 279 pthread_once(&initialized, _nwi_state_initialize); 280 pthread_mutex_lock(&nwi_store_lock); 281 282 force_refresh = ATOMIC_CMPXCHG(&nwi_store_force_refresh, TRUE, FALSE); 283 284 if (G_nwi_state != NULL) { 285 int check = 0; 286 uint32_t status; 287 288 if (nwi_store_token_valid == FALSE) { 289 /* have to throw cached copy away every time */ 290 check = 1; 291 } 292 else { 293 status = notify_check(nwi_store_token, &check); 294 if (status != NOTIFY_STATUS_OK) { 295 fprintf(stderr, "nwi notify_check: failed with %u\n", 296 status); 297 /* assume that it changed, throw cached copy away */ 298 check = 1; 299 } 300 } 301 if (check != 0 || force_refresh) { 302 /* new need snapshot */ 303 old_state = G_nwi_state; 304 G_nwi_state = NULL; 305 } 306 } 307 /* Let's populate the cache if it's empty */ 308 if (G_nwi_state == NULL) { 309 G_nwi_state = _nwi_state_copy_data(); 310 if (G_nwi_state != NULL) { 311 /* one reference for G_nwi_state */ 312 nwi_state_retain(G_nwi_state); 313 _nwi_state_reset_alias(G_nwi_state); 314 } 315 } 316 if (G_nwi_state != NULL) { 317 /* another reference for this caller */ 318 nwi_state_retain(G_nwi_state); 319 } 320 nwi_state = G_nwi_state; 321 pthread_mutex_unlock(&nwi_store_lock); 322 323 if (old_state != NULL) { 324 /* get rid of G_nwi_state reference */ 325 nwi_state_release(old_state); 326 } 327 return nwi_state; 328} 329 330/* 331 * Function: _nwi_state_ack 332 * Purpose: 333 * Acknowledge receipt and any changes associated with the [new or 334 * updated] network state. 335 */ 336void 337_nwi_state_ack(nwi_state_t state, const char *bundle_id) 338{ 339 xpc_object_t reqdict; 340 341 if (state == NULL) { 342 return; // ASSERT 343 } 344 345 if ((nwi_client == NULL) || !nwi_client->active) { 346 // if network information server not available 347 return; 348 } 349 350 dispatch_sync(__nwi_configuration_queue(), ^{ 351 nwi_active++; // keep connection active (for the life of the process) 352 }); 353 354 // create message 355 reqdict = xpc_dictionary_create(NULL, NULL, 0); 356 357 // set request 358 xpc_dictionary_set_int64(reqdict, NWI_REQUEST, NWI_REQUEST_ACKNOWLEDGE); 359 360 // set generation 361 xpc_dictionary_set_uint64(reqdict, NWI_GENERATION, state->generation_count); 362 363 // send acknowledgement to the DNS configuration server 364 xpc_connection_send_message(nwi_client->connection, reqdict); 365 366 xpc_release(reqdict); 367 return; 368} 369 370/* 371 * Function: nwi_state_get_generation 372 * Purpose: 373 * Returns the generation (mach_time) of the nwi_state data. 374 * Every time the data is updated due to changes 375 * in the network, this value will change. 376 */ 377uint64_t 378nwi_state_get_generation(nwi_state_t state) 379{ 380 return (state->generation_count); 381} 382 383/* 384 * Function: nwi_ifstate_get_generation 385 * Purpose: 386 * Returns the generation (mach_time) of the nwi_ifstate data. 387 */ 388uint64_t 389nwi_ifstate_get_generation(nwi_ifstate_t ifstate) 390{ 391 return (ifstate->if_generation_count); 392} 393 394/* 395 * Function: nwi_ifstate_get_ifname 396 * Purpose: 397 * Return the interface name of the specified ifstate. 398 */ 399const char * 400nwi_ifstate_get_ifname(nwi_ifstate_t ifstate) 401{ 402 return (ifstate != NULL?ifstate->ifname:NULL); 403 404} 405 406static uint64_t 407flags_from_af(int af) 408{ 409 return ((af == AF_INET) 410 ? NWI_IFSTATE_FLAGS_HAS_IPV4 411 : NWI_IFSTATE_FLAGS_HAS_IPV6); 412} 413/* 414 * Function: nwi_ifstate_get_flags 415 * Purpose: 416 * Return the flags for the given ifstate (see above for bit definitions). 417 */ 418nwi_ifstate_flags 419nwi_ifstate_get_flags(nwi_ifstate_t ifstate) 420{ 421 nwi_ifstate_t alias = ifstate->af_alias; 422 nwi_ifstate_flags flags = 0ULL; 423 424 flags |= flags_from_af(ifstate->af); 425 if ((ifstate->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0) { 426 flags |= NWI_IFSTATE_FLAGS_HAS_DNS; 427 428 } 429 if (alias != NULL) { 430 flags |= flags_from_af(alias->af); 431 if ((alias->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0) { 432 flags |= NWI_IFSTATE_FLAGS_HAS_DNS; 433 } 434 } 435 return flags; 436} 437 438/* 439 * Function: nwi_state_get_first_ifstate 440 * Purpose: 441 * Returns the first and highest priority interface that has connectivity 442 * for the specified address family 'af'. 'af' is either AF_INET or AF_INET6. 443 * The connectivity provided is for general networking. To get information 444 * about an interface that isn't available for general networking, use 445 * nwi_state_get_ifstate(). 446 * 447 * Use nwi_ifstate_get_next() to get the next, lower priority interface 448 * in the list. 449 * 450 * Returns NULL if no connectivity for the specified address family is 451 * available. 452 */ 453nwi_ifstate_t 454nwi_state_get_first_ifstate(nwi_state_t state, int af) 455{ 456 nwi_ifstate_t ifstate; 457 458 if (state == NULL) { 459 return NULL; 460 } 461 462 ifstate = nwi_state_get_ifstate_with_index(state, af, 0); 463 if ((ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) 464 != 0) { 465 ifstate = NULL; 466 } 467 468 return ifstate; 469 470} 471 472/* 473 * Function: nwi_state_get_ifstate 474 * Purpose: 475 * Return information for the specified interface 'ifname'. 476 * 477 * This API directly returns the ifstate for the specified interface. 478 * This is the only way to access information about an interface that isn't 479 * available for general networking. 480 * 481 * Returns NULL if no information is available for that interface. 482 */ 483nwi_ifstate_t 484nwi_state_get_ifstate(nwi_state_t state, const char * ifname) 485{ 486 nwi_ifstate_t ifstate = nwi_state_get_ifstate_with_name(state, AF_INET, ifname); 487 if (ifstate == NULL) { 488 ifstate = nwi_state_get_ifstate_with_name(state, AF_INET6, ifname); 489 } 490 return ifstate; 491 492} 493 494/* 495 * Function: nwi_ifstate_get_next 496 * Purpose: 497 * Returns the next, lower priority nwi_ifstate_t after the specified 498 * 'ifstate' for the protocol family 'af'. 499 * 500 * Returns NULL when the end of the list is reached. 501 */ 502nwi_ifstate_t 503nwi_ifstate_get_next(nwi_ifstate_t ifstate, int af) 504{ 505 nwi_ifstate_t alias, next; 506 507 alias = 508 (af == ifstate->af)?ifstate:ifstate->af_alias; 509 510 if (alias == NULL) { 511 return NULL; 512 } 513 514 /* We don't return interfaces marked rank never */ 515 if ((alias->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0) { 516 return NULL; 517 } 518 519 next = ++alias; 520 521 if ((next->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) == 0) { 522 return next; 523 } 524 return NULL; 525} 526 527/* 528 * Function: nwi_ifstate_compare_rank 529 * Purpose: 530 * Compare the relative rank of two nwi_ifstate_t objects. 531 * 532 * The "rank" indicates the importance of the underlying interface. 533 * 534 * Returns: 535 * 0 if ifstate1 and ifstate2 are ranked equally 536 * -1 if ifstate1 is ranked ahead of ifstate2 537 * 1 if ifstate2 is ranked ahead of ifstate1 538 */ 539int 540nwi_ifstate_compare_rank(nwi_ifstate_t ifstate1, nwi_ifstate_t ifstate2) 541{ 542 return RankCompare(ifstate1->rank, ifstate2->rank); 543} 544 545/* 546 * nwi_state_get_reachability_flags 547 * 548 * returns the global reachability flags for a given address family. 549 * If no address family is passed in, it returns the global reachability 550 * flags for either families. 551 * 552 * The reachability flags returned follow the definition of 553 * SCNetworkReachabilityFlags. 554 * 555 * If the flags are zero (i.e. do not contain kSCNetworkReachabilityFlagsReachable), there is no connectivity. 556 * 557 * Otherwise, at least kSCNetworkReachabilityFlagsReachable is set: 558 * Reachable only 559 * No other connection flags are set. 560 * Reachable and no ConnectionRequired 561 * If we have connectivity for the specified address family (and we'd 562 * be returning the reachability flags associated with the default route) 563 * Reachable and ConnectionRequired 564 * If we do not currently have an active/primary network but we may 565 * be able to establish connectivity. 566 * Reachable and OnDemand 567 * If we do not currently have an active/primary network but we may 568 * be able to establish connective on demand. 569 * Reachable and TransientConnection 570 * This connection is transient. 571 * Reachable and WWAN 572 * This connection will be going over the cellular network. 573 */ 574uint32_t 575nwi_state_get_reachability_flags(nwi_state_t nwi_state, int af) 576{ 577 if (nwi_state == NULL) { 578 return (0); 579 } 580 if (af == AF_INET || af == AF_INET6) { 581 nwi_ifstate_t ifstate; 582 583 ifstate = nwi_state_get_first_ifstate(nwi_state, af); 584 585 if (ifstate != NULL) { 586 return ifstate->reach_flags; 587 } 588 589 return (af == AF_INET) ? nwi_state->reach_flags_v4 : nwi_state->reach_flags_v6; 590 } else { 591 nwi_ifstate_t ifstate_v4; 592 nwi_ifstate_t ifstate_v6; 593 594 ifstate_v4 = nwi_state_get_first_ifstate(nwi_state, AF_INET); 595 ifstate_v6 = nwi_state_get_first_ifstate(nwi_state, AF_INET6); 596 597 if (ifstate_v4 != NULL) { 598 if (ifstate_v6 != NULL) { 599 if (nwi_ifstate_compare_rank(ifstate_v4, ifstate_v6) > 0) { 600 return ifstate_v6->reach_flags; 601 } else { 602 return ifstate_v4->reach_flags; 603 } 604 } else { 605 return ifstate_v4->reach_flags; 606 } 607 } else { 608 if (ifstate_v6 != NULL) { 609 return ifstate_v6->reach_flags; 610 } 611 } 612 613 if (nwi_state->reach_flags_v4 != 0) { 614 return nwi_state->reach_flags_v4; 615 } 616 // This is the case where both ifstate are NULL. 617 return nwi_state->reach_flags_v6; 618 } 619} 620 621/* 622 * nwi_ifstate_get_vpn_server 623 * 624 * returns a sockaddr representation of the vpn server address. 625 * NULL if PPP/VPN/IPSec server address does not exist. 626 */ 627const struct sockaddr * 628nwi_ifstate_get_vpn_server(nwi_ifstate_t ifstate) 629{ 630 const struct sockaddr * vpn_server_addr; 631 632 vpn_server_addr = (const struct sockaddr *)(void *) 633 &ifstate->vpn_server_address; 634 635 if (vpn_server_addr->sa_family == 0) { 636 return NULL; 637 } 638 return vpn_server_addr; 639} 640 641/* 642 * nwi_ifstate_get_reachability_flags 643 * 644 * returns the reachability flags for the interface given an address family. 645 * The flags returned are those determined outside of 646 * the routing table. [None, ConnectionRequired, OnDemand, 647 * Transient Connection, WWAN]. 648 */ 649uint32_t 650nwi_ifstate_get_reachability_flags(nwi_ifstate_t ifstate) 651{ 652 return ifstate->reach_flags; 653} 654 655/* 656 * nwi_ifstate_get_signature 657 * 658 * returns the signature and its length for an ifstate given an address family. 659 * If AF_UNSPEC is passed in, the signature for a given ifstate is returned. 660 * 661 * If the signature does not exist, NULL is returned. 662 */ 663const uint8_t * 664nwi_ifstate_get_signature(nwi_ifstate_t ifstate, int af, int * length) 665{ 666 nwi_ifstate_t i_state = NULL; 667 668 switch (af) { 669 case AF_UNSPEC: 670 i_state = ifstate; 671 break; 672 case AF_INET: 673 case AF_INET6: 674 i_state = (ifstate->af == af) ? ifstate : ifstate->af_alias; 675 break; 676 default: 677 break; 678 679 } 680 681 if (i_state != NULL) { 682 if ((i_state->flags & NWI_IFSTATE_FLAGS_HAS_SIGNATURE) != 0) { 683 *length = sizeof(i_state->signature); 684 return (i_state->signature); 685 } 686 } 687 688 *length = 0; 689 return NULL; 690} 691 692static inline 693boolean_t 694_nwi_ifstate_is_in_list(nwi_ifstate_t ifstate, int af) 695{ 696 nwi_ifstate_t i_state; 697 698 i_state = (ifstate->af == af) ? ifstate : ifstate->af_alias; 699 if (i_state == NULL) { 700 return FALSE; 701 } 702 703 if ((nwi_ifstate_get_flags(i_state) & NWI_IFSTATE_FLAGS_NOT_IN_LIST) == 0) { 704 return TRUE; 705 } 706 707 return FALSE; 708} 709 710/* 711 * nwi_ifstate_get_dns_signature 712 * 713 * returns the signature and its length for given 714 * ifstate with a valid dns configuration. 715 * 716 * If the signature does not exist, NULL is returned. 717 * 718 */ 719const uint8_t * 720nwi_ifstate_get_dns_signature(nwi_ifstate_t ifstate, int * length) 721{ 722 const uint8_t * signature = NULL; 723 const uint8_t * v4_signature; 724 int v4_signature_len; 725 const uint8_t * v6_signature; 726 int v6_signature_len; 727 728 *length = 0; 729 730 if ((nwi_ifstate_get_flags(ifstate) & NWI_IFSTATE_FLAGS_HAS_DNS) == 0) { 731 return NULL; 732 } 733 734 v4_signature = nwi_ifstate_get_signature(ifstate, AF_INET, &v4_signature_len); 735 v6_signature = nwi_ifstate_get_signature(ifstate, AF_INET6, &v6_signature_len); 736 if (v4_signature == NULL && v6_signature == NULL) { 737 return NULL; 738 } 739 740 if (_nwi_ifstate_is_in_list(ifstate, AF_INET) == TRUE) { 741 signature = v4_signature; 742 *length = v4_signature_len; 743 } else { 744 if (_nwi_ifstate_is_in_list(ifstate, AF_INET6) != TRUE && v4_signature_len > 0) { 745 /* v6 is ranked never, v4 is ranked never but has a valid signature */ 746 signature = v4_signature; 747 *length = v4_signature_len; 748 } else { 749 /* v6 is not ranked never or v4 has no signature */ 750 signature = v6_signature; 751 *length = v6_signature_len; 752 } 753 } 754 755 return signature; 756} 757 758 759#pragma mark - 760#pragma mark Network information [nwi] test code 761 762 763#ifdef MAIN 764 765int 766main(int argc, char **argv) 767{ 768 dns_config_t *config; 769 770 config = dns_configuration_copy(); 771 if (config != NULL) { 772 dns_configuration_free(&config); 773 } 774 775 exit(0); 776} 777 778#endif 779