Something went wrong. Try again.
Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
61 kB · 1952 lines
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953/* * Copyright (c) 2003-2019 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */
/* * Modification History * * April 12, 2011 Allan Nathanson <ajn@apple.com> * - add SCNetworkReachability "server" * * March 31, 2004 Allan Nathanson <ajn@apple.com> * - use [SC] DNS configuration information * * January 19, 2003 Allan Nathanson <ajn@apple.com> * - add advanced reachability APIs */
#include <TargetConditionals.h>#include <sys/cdefs.h>#include <dispatch/dispatch.h>#include <dispatch/private.h>#include <pthread.h>#include <libkern/OSAtomic.h>#include <notify.h>#include <dnsinfo.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#include <resolv.h>#include <unistd.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <net/if.h>#include <net/if_dl.h>#include <net/if_types.h>#include <net/network_agent.h>
#include <CoreFoundation/CoreFoundation.h>#include <CoreFoundation/CFRuntime.h>
#define SC_LOG_HANDLE __log_SCNetworkReachability#define SC_LOG_HANDLE_TYPE static#include <SystemConfiguration/SystemConfiguration.h>#include <SystemConfiguration/SCValidation.h>#include <SystemConfiguration/SCPrivate.h>#include "SCD.h"#include "SCNetworkReachabilityInternal.h"
#include <nw/private.h>
#define DEBUG_REACHABILITY_TYPE_NAME "create w/name"#define DEBUG_REACHABILITY_TYPE_NAME_OPTIONS " + options"
#define DEBUG_REACHABILITY_TYPE_ADDRESS "create w/address"#define DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS " + options"
#define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR "create w/address pair"#define DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS " + options"
#define DEBUG_REACHABILITY_TYPE_PTR "create w/ptr"#define DEBUG_REACHABILITY_TYPE_PTR_OPTIONS " + options"
static pthread_mutexattr_t lock_attr;
#define MUTEX_INIT(m) { \ int _lock_ = (pthread_mutex_init(m, &lock_attr) == 0); \ assert(_lock_); \}
#define MUTEX_LOCK(m) { \ int _lock_ = (pthread_mutex_lock(m) == 0); \ assert(_lock_); \}
#define MUTEX_UNLOCK(m) { \ int _unlock_ = (pthread_mutex_unlock(m) == 0); \ assert(_unlock_); \}
#define MUTEX_ASSERT_HELD(m) { \ int _locked_ = (pthread_mutex_lock(m) == EDEADLK); \ assert(_locked_); \}
#define N_QUICK 64
#define REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN "NetworkExtension"#define REACHABILITY_AGENT_DATA_KEY "data"
static CFStringRef __SCNetworkReachabilityCopyDescription (CFTypeRef cf);static void __SCNetworkReachabilityDeallocate (CFTypeRef cf);
static SCNetworkReachabilityFlags__SCNetworkReachabilityGetFlagsFromPath(nw_path_t path, ReachabilityAddressType type, nw_resolver_status_t resolverStatus, nw_array_t resolvedEndpoints, Boolean resolvedEndpointUseFlags, SCNetworkReachabilityFlags resolvedEndpointFlags);
static Boolean__SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate, dispatch_queue_t queue);
static CFTypeID __kSCNetworkReachabilityTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __SCNetworkReachabilityClass = { 0, // version "SCNetworkReachability", // className NULL, // init NULL, // copy __SCNetworkReachabilityDeallocate, // dealloc NULL, // equal NULL, // hash NULL, // copyFormattingDesc __SCNetworkReachabilityCopyDescription // copyDebugDesc};
static pthread_once_t initialized = PTHREAD_ONCE_INIT;
static dispatch_queue_t_callback_queue(){ static dispatch_once_t once; static dispatch_queue_t q;
dispatch_once(&once, ^{ q = dispatch_queue_create("SCNetworkReachability.callback", NULL); });
return q;}
static os_log_t__log_SCNetworkReachability(void){ static os_log_t log = NULL;
if (log == NULL) { log = os_log_create("com.apple.SystemConfiguration", "SCNetworkReachability"); }
return log;}
#pragma mark -#pragma mark SCNetworkReachability APIs
static __inline__ CFTypeRefisA_SCNetworkReachability(CFTypeRef obj){ return (isA_CFType(obj, SCNetworkReachabilityGetTypeID()));}
static CFStringRef_SCNetworkReachabilityCopyTargetDescription(SCNetworkReachabilityRef target){ CFAllocatorRef allocator = CFGetAllocator(target); CFMutableStringRef str; SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
str = CFStringCreateMutable(allocator, 0); switch (targetPrivate->type) { case reachabilityTypeAddress : case reachabilityTypeAddressPair : { char buf[64]; if (targetPrivate->localAddressEndpoint != NULL) { _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate->localAddressEndpoint), buf, sizeof(buf)); CFStringAppendFormat(str, NULL, CFSTR("local address = %s"), buf); } if (targetPrivate->remoteAddressEndpoint != NULL) { _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate->remoteAddressEndpoint), buf, sizeof(buf)); CFStringAppendFormat(str, NULL, CFSTR("%s%saddress = %s"), targetPrivate->localAddressEndpoint ? ", " : "", (targetPrivate->type == reachabilityTypeAddressPair) ? "remote " : "", buf); } else { CFStringAppendFormat(str, NULL, CFSTR("default path")); } break; } case reachabilityTypeName : { CFStringAppendFormat(str, NULL, CFSTR("name = %s"), nw_endpoint_get_hostname(targetPrivate->hostnameEndpoint)); break; } case reachabilityTypePTR : { char buf[64]; if (targetPrivate->remoteAddressEndpoint != NULL) { _SC_sockaddr_to_string(nw_endpoint_get_address(targetPrivate->remoteAddressEndpoint), buf, sizeof(buf)); CFStringAppendFormat(str, NULL, CFSTR("ptr = %s"), buf); } break; } }
if (targetPrivate->parameters != NULL) { unsigned int if_index;
if_index = nw_parameters_get_required_interface_index(targetPrivate->parameters); if (if_index != 0) { CFStringAppendFormat(str, NULL, CFSTR(", if_index = %u"), if_index); } }
return str;}
static CFStringRef__SCNetworkReachabilityCopyTargetFlags(SCNetworkReachabilityRef target){ CFAllocatorRef allocator = CFGetAllocator(target); CFStringRef str; SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
str = CFStringCreateWithFormat(allocator, NULL, CFSTR("flags = 0x%08x, if_index = %u"), __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, targetPrivate->type, targetPrivate->lastResolverStatus, targetPrivate->lastResolvedEndpoints, targetPrivate->lastResolvedEndpointHasFlags, targetPrivate->lastResolvedEndpointFlags), targetPrivate->lastResolvedEndpointHasFlags ? targetPrivate->lastResolvedEndpointInterfaceIndex : nw_path_get_interface_index(targetPrivate->lastPath)); return str;}
static CFStringRef__SCNetworkReachabilityCopyDescription(CFTypeRef cf){ CFAllocatorRef allocator = CFGetAllocator(cf); CFMutableStringRef result; CFStringRef str; SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf; SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
MUTEX_LOCK(&targetPrivate->lock);
result = CFStringCreateMutable(allocator, 0); CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkReachability %p [%p]> {"), cf, allocator);
// add target description str = _SCNetworkReachabilityCopyTargetDescription(target); CFStringAppend(result, str); CFRelease(str);
// add additional "name" info if (isReachabilityTypeName(targetPrivate->type)) { if (targetPrivate->resolver && targetPrivate->lastResolverStatus == nw_resolver_status_invalid) { CFStringAppendFormat(result, NULL, CFSTR(" (DNS query active)")); } else if (targetPrivate->lastResolverStatus != nw_resolver_status_invalid) { CFStringAppendFormat(result, NULL, CFSTR(" (%s"), (targetPrivate->lastResolverStatus == nw_resolver_status_complete) ? "complete" : "in progress"); if (nw_array_get_count(targetPrivate->lastResolvedEndpoints) > 0) { nw_array_apply(targetPrivate->lastResolvedEndpoints, ^bool(size_t index, nw_object_t object) {#pragma unused(index) nw_endpoint_t endpoint = (nw_endpoint_t)object; nw_endpoint_type_t endpoint_type = nw_endpoint_get_type(endpoint); if (endpoint_type == nw_endpoint_type_address) { char buf[64]; const struct sockaddr *sa = nw_endpoint_get_address(endpoint); _SC_sockaddr_to_string(sa, buf, sizeof(buf)); CFStringAppendFormat(result, NULL, CFSTR(", %s"), buf); } else if (endpoint_type == nw_endpoint_type_host) { CFStringAppendFormat(result, NULL, CFSTR(", %s"), nw_endpoint_get_hostname(endpoint)); } else { CFStringAppendFormat(result, NULL, CFSTR(", unexpected nw_endpoint type: %d"), endpoint_type); } return TRUE; }); } else { CFStringAppendFormat(result, NULL, CFSTR(", no addresses")); } CFStringAppendFormat(result, NULL, CFSTR(")")); } }
if (targetPrivate->resolverBypass) { CFStringAppendFormat(result, NULL, CFSTR(", !resolve")); }
// add flags if (targetPrivate->scheduled) { str = __SCNetworkReachabilityCopyTargetFlags(target); CFStringAppendFormat(result, NULL, CFSTR(", %@"), str); CFRelease(str); }
CFStringAppendFormat(result, NULL, CFSTR("}"));
MUTEX_UNLOCK(&targetPrivate->lock);
return result;}
static void__SCNetworkReachabilityDeallocate(CFTypeRef cf){ SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)cf; SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
SC_log(LOG_DEBUG, "%srelease", targetPrivate->log_prefix);
/* release resources */ MUTEX_LOCK(&targetPrivate->lock); targetPrivate->scheduled = FALSE;
if (targetPrivate->hostnameEndpoint) { nw_release(targetPrivate->hostnameEndpoint); targetPrivate->hostnameEndpoint = NULL; } if (targetPrivate->localAddressEndpoint) { nw_release(targetPrivate->localAddressEndpoint); targetPrivate->localAddressEndpoint = NULL; } if (targetPrivate->remoteAddressEndpoint) { nw_release(targetPrivate->remoteAddressEndpoint); targetPrivate->remoteAddressEndpoint = NULL; } if (targetPrivate->parameters) { nw_release(targetPrivate->parameters); targetPrivate->parameters = NULL; } if (targetPrivate->lastPath) { nw_release(targetPrivate->lastPath); targetPrivate->lastPath = NULL; } if (targetPrivate->lastPathParameters) { nw_release(targetPrivate->lastPathParameters); targetPrivate->lastPathParameters = NULL; } if (targetPrivate->lastResolvedEndpoints) { nw_release(targetPrivate->lastResolvedEndpoints); targetPrivate->lastResolvedEndpoints = NULL; }
if (targetPrivate->rlsContext.release != NULL) { (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info); }
MUTEX_UNLOCK(&targetPrivate->lock);
pthread_mutex_destroy(&targetPrivate->lock);
return;}
static void__SCNetworkReachabilityInitialize(void){ __kSCNetworkReachabilityTypeID = _CFRuntimeRegisterClass(&__SCNetworkReachabilityClass);
pthread_mutexattr_init(&lock_attr); pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_ERRORCHECK);
return;}
static SCNetworkReachabilityPrivateRef__SCNetworkReachabilityCreatePrivate(CFAllocatorRef allocator){ SCNetworkReachabilityPrivateRef targetPrivate; uint32_t size;
/* initialize runtime */ pthread_once(&initialized, __SCNetworkReachabilityInitialize);
/* allocate target */ size = sizeof(SCNetworkReachabilityPrivate) - sizeof(CFRuntimeBase); targetPrivate = (SCNetworkReachabilityPrivateRef)_CFRuntimeCreateInstance(allocator, __kSCNetworkReachabilityTypeID, size, NULL); if (targetPrivate == NULL) { return NULL; }
/* initialize non-zero/NULL members */ MUTEX_INIT(&targetPrivate->lock); if (_sc_log > 0) { snprintf(targetPrivate->log_prefix, sizeof(targetPrivate->log_prefix), "[%p] ", targetPrivate); }
return targetPrivate;}
static const struct sockaddr *is_valid_address(const struct sockaddr *address){ const struct sockaddr *valid = NULL; static Boolean warned = FALSE;
if ((address != NULL) && (address->sa_len <= sizeof(struct sockaddr_storage))) { switch (address->sa_family) { case AF_INET : if (address->sa_len >= sizeof(struct sockaddr_in)) { valid = address; } else if (!warned) { SC_log(LOG_WARNING, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu", address->sa_len, sizeof(struct sockaddr_in)); warned = TRUE; } break; case AF_INET6 : if (address->sa_len >= sizeof(struct sockaddr_in6)) { valid = address; } else if (!warned) { SC_log(LOG_WARNING, "SCNetworkReachabilityCreateWithAddress[Pair] called with \"struct sockaddr *\" len %d < %zu", address->sa_len, sizeof(struct sockaddr_in6)); warned = TRUE; } break; default : if (!warned) { SC_log(LOG_WARNING, "SCNetworkReachabilityCreateWithAddress[Pair] called with invalid address family %d", address->sa_family); warned = TRUE; } } }
return valid;}
static bool__SCNetworkReachabilityAddressIsEmpty(const struct sockaddr *address){ if (address == NULL) { return TRUE; }
if (address->sa_family == AF_INET) { return (((struct sockaddr_in *)(void *)address)->sin_addr.s_addr == 0); } else if (address->sa_family == AF_INET6) { return IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)(void *)address)->sin6_addr); } else { return FALSE; }}
SCNetworkReachabilityRefSCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator, const struct sockaddr *address){ const struct sockaddr *targetAddress; SCNetworkReachabilityPrivateRef targetPrivate;
targetAddress = is_valid_address(address); if (targetAddress == NULL) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator); if (targetPrivate == NULL) { return NULL; }
targetPrivate->type = reachabilityTypeAddress;
if (!__SCNetworkReachabilityAddressIsEmpty(targetAddress)) { targetPrivate->remoteAddressEndpoint = nw_endpoint_create_address(targetAddress); }
SC_log(LOG_DEBUG, "%s%s %@", targetPrivate->log_prefix, DEBUG_REACHABILITY_TYPE_ADDRESS, targetPrivate);
return (SCNetworkReachabilityRef)targetPrivate;}
#if !TARGET_OS_IPHONEstatic Booleanis_ipv4_loopback(const struct sockaddr *sa){ uint32_t addr; struct sockaddr_in *sin = (struct sockaddr_in *)(void *)sa;
if ((sa == NULL) || (sa->sa_len < sizeof(struct sockaddr_in)) || (sa->sa_family != AF_INET)) { return FALSE; }
addr = ntohl(sin->sin_addr.s_addr); return IN_LOOPBACK(addr) ? TRUE : FALSE;}#endif // !TARGET_OS_IPHONE
static Booleanis_same_address(const struct sockaddr *a, const struct sockaddr *b){ const void *a_addr; const void *b_addr; size_t len;
if ((a == NULL) || (b == NULL) || (a->sa_family != b->sa_family) || (a->sa_len != b->sa_len )) { return FALSE; }
switch (a->sa_family) { case AF_INET : { struct sockaddr_in *a_sin = (struct sockaddr_in *)(void *)a; struct sockaddr_in *b_sin = (struct sockaddr_in *)(void *)b;
/* ALIGN: assuming a (and b) are aligned, then cast ok. */ a_addr = &a_sin->sin_addr; b_addr = &b_sin->sin_addr; len = sizeof(struct in_addr); break; }
case AF_INET6 : { struct sockaddr_in6 *a_sin6 = (struct sockaddr_in6 *)(void *)a; struct sockaddr_in6 *b_sin6 = (struct sockaddr_in6 *)(void *)b;
if (a_sin6->sin6_scope_id != b_sin6->sin6_scope_id) { return FALSE; }
a_addr = &a_sin6->sin6_addr; b_addr = &b_sin6->sin6_addr; len = sizeof(struct in6_addr); break; }
default : a_addr = a; b_addr = b; len = a->sa_len; break; }
return (bcmp(a_addr, b_addr, len) == 0);}
SCNetworkReachabilityRefSCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator, const struct sockaddr *localAddress, const struct sockaddr *remoteAddress){ SCNetworkReachabilityPrivateRef targetPrivate;
if ((localAddress == NULL) && (remoteAddress == NULL)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
if (localAddress != NULL) { localAddress = is_valid_address(localAddress); if (localAddress == NULL) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; } }
if (remoteAddress != NULL) { remoteAddress = is_valid_address(remoteAddress); if (remoteAddress == NULL) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; } }
#if !TARGET_OS_IPHONE // Check/fix for loopback IP --> remote IP (rdar://26561383) if ((localAddress != NULL) && (remoteAddress != NULL) && is_ipv4_loopback(localAddress) && !is_ipv4_loopback(remoteAddress)) { static Boolean warned = FALSE;
if (!warned) { SC_log(LOG_WARNING, "BUG: SCNetworkReachabilityCreateWithAddressPair() called with local <loopback>"); SC_log(LOG_WARNING, "address and remote <non-loopback> address. To return the expected (but actually"); SC_log(LOG_WARNING, "incorrect) result, switched to SCNetworkReachabilityCreateWithAddress() with"); SC_log(LOG_WARNING, "the remote address."); warned = TRUE; }
return SCNetworkReachabilityCreateWithAddress(allocator, remoteAddress); }#endif // !TARGET_OS_IPHONE
targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator); if (targetPrivate == NULL) { return NULL; }
targetPrivate->type = reachabilityTypeAddressPair;
if (localAddress != NULL) { targetPrivate->localAddressEndpoint = nw_endpoint_create_address(localAddress); }
if (remoteAddress != NULL) { if (is_same_address(localAddress, remoteAddress)) { targetPrivate->remoteAddressEndpoint = nw_retain(targetPrivate->localAddressEndpoint); } else { targetPrivate->remoteAddressEndpoint = nw_endpoint_create_address(remoteAddress); } }
targetPrivate->parameters = nw_parameters_create(); nw_parameters_set_local_address(targetPrivate->parameters, targetPrivate->localAddressEndpoint);
SC_log(LOG_DEBUG, "%s%s %@", targetPrivate->log_prefix, DEBUG_REACHABILITY_TYPE_ADDRESSPAIR, targetPrivate);
return (SCNetworkReachabilityRef)targetPrivate;}
SCNetworkReachabilityRefSCNetworkReachabilityCreateWithName(CFAllocatorRef allocator, const char *nodename){ union { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; } addr; size_t nodenameLen; SCNetworkReachabilityPrivateRef targetPrivate;
if (nodename == NULL) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
nodenameLen = strlen(nodename); if (nodenameLen == 0) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
if (_SC_string_to_sockaddr(nodename, AF_UNSPEC, (void *)&addr, sizeof(addr)) != NULL) { /* if this "nodename" is really an IP[v6] address in disguise */ return SCNetworkReachabilityCreateWithAddress(allocator, &addr.sa); }
targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator); if (targetPrivate == NULL) { return NULL; }
targetPrivate->type = reachabilityTypeName;
targetPrivate->hostnameEndpoint = nw_endpoint_create_host(nodename, "0");
SC_log(LOG_DEBUG, "%s%s %@", targetPrivate->log_prefix, DEBUG_REACHABILITY_TYPE_NAME, targetPrivate);
return (SCNetworkReachabilityRef)targetPrivate;}
static SCNetworkReachabilityRef__SCNetworkReachabilityCreateWithPTR(CFAllocatorRef allocator, const struct sockaddr *ptrAddress){ SCNetworkReachabilityPrivateRef targetPrivate;
ptrAddress = is_valid_address(ptrAddress); if (ptrAddress == NULL) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
targetPrivate = __SCNetworkReachabilityCreatePrivate(allocator); if (targetPrivate == NULL) { return NULL; }
targetPrivate->type = reachabilityTypePTR;
targetPrivate->remoteAddressEndpoint = nw_endpoint_create_address(ptrAddress);
targetPrivate->parameters = nw_parameters_create(); nw_parameters_set_resolve_ptr(targetPrivate->parameters, TRUE);
SC_log(LOG_DEBUG, "%s%s %@", targetPrivate->log_prefix, DEBUG_REACHABILITY_TYPE_PTR, targetPrivate);
return (SCNetworkReachabilityRef)targetPrivate;}
SCNetworkReachabilityRefSCNetworkReachabilityCreateWithOptions(CFAllocatorRef allocator, CFDictionaryRef options){ const struct sockaddr *addr_l = NULL; const struct sockaddr *addr_p = NULL; const struct sockaddr *addr_r = NULL; CFDataRef data; Boolean haveOpt = FALSE; CFStringRef interface = NULL; CFStringRef nodename; CFBooleanRef resolverBypass; SCNetworkReachabilityRef target; SCNetworkReachabilityPrivateRef targetPrivate; unsigned int if_index = 0; char if_name[IFNAMSIZ]; CFDataRef sourceAppAuditToken = NULL; CFStringRef sourceAppBundleID = NULL;
if (!isA_CFDictionary(options)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
nodename = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionNodeName); if ((nodename != NULL) && (!isA_CFString(nodename) || (CFStringGetLength(nodename) == 0))) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; } data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionLocalAddress); if (data != NULL) { if (!isA_CFData(data) || ((size_t)CFDataGetLength(data) < sizeof(struct sockaddr_in))) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; } addr_l = (const struct sockaddr *)CFDataGetBytePtr(data); } data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionPTRAddress); if (data != NULL) { if (!isA_CFData(data) || ((size_t)CFDataGetLength(data) < sizeof(struct sockaddr_in))) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; } addr_p = (const struct sockaddr *)CFDataGetBytePtr(data); } data = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionRemoteAddress); if (data != NULL) { if (!isA_CFData(data) || ((size_t)CFDataGetLength(data) < sizeof(struct sockaddr_in))) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; } addr_r = (const struct sockaddr *)CFDataGetBytePtr(data); } interface = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionInterface); if ((interface != NULL) && (!isA_CFString(interface) || (CFStringGetLength(interface) == 0))) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; } resolverBypass = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionResolverBypass); if ((resolverBypass != NULL) && !isA_CFBoolean(resolverBypass)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; } sourceAppAuditToken = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionSourceAppAuditToken); if ((sourceAppAuditToken != NULL) && (!isA_CFData(sourceAppAuditToken) || (CFDataGetLength(sourceAppAuditToken) != sizeof(audit_token_t)))) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; } sourceAppBundleID = CFDictionaryGetValue(options, kSCNetworkReachabilityOptionSourceAppBundleIdentifier); if ((sourceAppBundleID != NULL) && (!isA_CFString(sourceAppBundleID) || (CFStringGetLength(sourceAppBundleID) == 0))) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
if (nodename != NULL) { const char *name;
if ((addr_l != NULL) || (addr_r != NULL) || (addr_p != NULL)) { // can't have both a nodename and an address _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
name = _SC_cfstring_to_cstring(nodename, NULL, 0, kCFStringEncodingUTF8); target = SCNetworkReachabilityCreateWithName(allocator, name); CFAllocatorDeallocate(NULL, (void *)name); } else if (addr_p != NULL) { if ((addr_l != NULL) || // can't have PTR and target address (addr_r != NULL)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
target = __SCNetworkReachabilityCreateWithPTR(NULL, addr_p); } else { if ((addr_l != NULL) && (addr_r != NULL)) { target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, addr_r); } else if (addr_r != NULL) { target = SCNetworkReachabilityCreateWithAddress(NULL, addr_r); } else if (addr_l != NULL) { target = SCNetworkReachabilityCreateWithAddressPair(NULL, addr_l, NULL); } else { _SCErrorSet(kSCStatusInvalidArgument); return NULL; } } if (target == NULL) { return NULL; }
targetPrivate = (SCNetworkReachabilityPrivateRef)target;
if (interface != NULL) { if ((_SC_cfstring_to_cstring(interface, if_name, sizeof(if_name), kCFStringEncodingASCII) == NULL) || ((if_index = if_nametoindex(if_name)) == 0)) { CFRelease(targetPrivate); _SCErrorSet(kSCStatusInvalidArgument); return NULL; } }
if (targetPrivate->parameters == NULL) { targetPrivate->parameters = nw_parameters_create(); }
if (if_index != 0) { nw_interface_t interfaceObject = nw_interface_create_with_index(if_index); nw_parameters_require_interface(targetPrivate->parameters, interfaceObject); nw_release(interfaceObject); haveOpt = TRUE; }
if (resolverBypass != NULL) { targetPrivate->resolverBypass = CFBooleanGetValue(resolverBypass); haveOpt = TRUE; }
if (sourceAppAuditToken != NULL) { audit_token_t atoken; CFDataGetBytes(sourceAppAuditToken, CFRangeMake(0, CFDataGetLength(sourceAppAuditToken)), (UInt8 *)&atoken); nw_parameters_set_source_application(targetPrivate->parameters, atoken); haveOpt = TRUE; } else if (sourceAppBundleID != NULL) { char *cBundleID = _SC_cfstring_to_cstring(sourceAppBundleID, NULL, 0, kCFStringEncodingUTF8); if (cBundleID != NULL) { nw_parameters_set_source_application_by_bundle_id(targetPrivate->parameters, cBundleID); CFAllocatorDeallocate(NULL, (void *)cBundleID); } else { SC_log(LOG_WARNING, "failed to convert %@ to a C string", sourceAppBundleID); } haveOpt = TRUE; }
if (haveOpt) { const char *opt = "???";
switch (targetPrivate->type) { case reachabilityTypeAddress : opt = DEBUG_REACHABILITY_TYPE_ADDRESS_OPTIONS; break; case reachabilityTypeAddressPair : opt = DEBUG_REACHABILITY_TYPE_ADDRESSPAIR_OPTIONS; break; case reachabilityTypeName : opt = DEBUG_REACHABILITY_TYPE_NAME_OPTIONS; break; case reachabilityTypePTR : opt = DEBUG_REACHABILITY_TYPE_PTR_OPTIONS; break; }
SC_log(LOG_DEBUG, "%s%s %@", targetPrivate->log_prefix, opt, targetPrivate); }
return (SCNetworkReachabilityRef)targetPrivate;}
CFTypeIDSCNetworkReachabilityGetTypeID(void){ pthread_once(&initialized, __SCNetworkReachabilityInitialize); /* initialize runtime */ return __kSCNetworkReachabilityTypeID;}
CFArrayRef /* CFArray[CFData], where each CFData is a (struct sockaddr *) */SCNetworkReachabilityCopyResolvedAddress(SCNetworkReachabilityRef target, int *error_num){ CFMutableArrayRef array = NULL; SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
if (!isA_SCNetworkReachability(target)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
if (!isReachabilityTypeName(targetPrivate->type)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
if (error_num) { *error_num = 0; }
MUTEX_LOCK(&targetPrivate->lock);
if (nw_array_get_count(targetPrivate->lastResolvedEndpoints) > 0) { array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); nw_array_apply(targetPrivate->lastResolvedEndpoints, ^bool(size_t index, nw_object_t object) {#pragma unused(index) nw_endpoint_type_t endpoint_type = nw_endpoint_get_type((nw_endpoint_t)object); if (endpoint_type == nw_endpoint_type_address) { const struct sockaddr *address = nw_endpoint_get_address((nw_endpoint_t)object); if (address == NULL) { SC_log(LOG_ERR, "nw_endpoint_type_address w/no address"); return TRUE; }
CFDataRef addressData = CFDataCreate(kCFAllocatorDefault, (const uint8_t *)address, address->sa_len); CFArrayAppendValue(array, addressData); CFRelease(addressData); } else if (endpoint_type == nw_endpoint_type_host) { const char *host = nw_endpoint_get_hostname((nw_endpoint_t)object); if (host == NULL) { SC_log(LOG_ERR, "nw_endpoint_type_host w/no host"); return TRUE; }
CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, host, kCFStringEncodingASCII); if (string == NULL) { SC_log(LOG_ERR, "nw_endpoint_type_host w/non-ASCII host"); return TRUE; }
if (CFStringHasPrefix(string, CFSTR(".")) || CFStringHasSuffix(string, CFSTR("."))) { CFMutableStringRef mutableString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, string); CFRelease(string); string = mutableString; CFStringTrim(mutableString, CFSTR(".")); } CFArrayAppendValue(array, string); CFRelease(string); } else { SC_log(LOG_ERR, "unexpected nw_endpoint type: %d", endpoint_type); } return TRUE; }); }
MUTEX_UNLOCK(&targetPrivate->lock); _SCErrorSet(kSCStatusOK); return array;}
#pragma mark -#pragma mark Reachability Flags
static void__SCNetworkReachabilityGetAgentVPNFlags(xpc_object_t dictionary, Boolean *vpn, Boolean *onDemand){ const struct netagent *agent = NULL; size_t length = 0;
if (dictionary == NULL || vpn == NULL || onDemand == NULL) { return; }
*vpn = FALSE; *onDemand = FALSE;
agent = xpc_dictionary_get_data(dictionary, REACHABILITY_AGENT_DATA_KEY, &length); if (agent == NULL || length < sizeof(struct netagent) || length != (sizeof(struct netagent) + agent->netagent_data_size)) { return; }
if (strncmp(REACHABILITY_NETWORK_EXTENSION_AGENT_DOMAIN, agent->netagent_domain, NETAGENT_DOMAINSIZE) == 0) { *vpn = TRUE; if ((agent->netagent_flags & NETAGENT_FLAG_VOLUNTARY) && !(agent->netagent_flags & NETAGENT_FLAG_ACTIVE)) { *onDemand = TRUE; } }}
static boolnw_path_is_linklocal_direct(nw_path_t path){ bool is_linklocal_direct = false;
nw_endpoint_t endpoint = nw_path_copy_endpoint(path); if (endpoint == NULL) { return false; }
if (nw_endpoint_get_type(endpoint) == nw_endpoint_type_address) { const struct sockaddr_in *sin;
sin = (const struct sockaddr_in *)(void *)nw_endpoint_get_address(endpoint);; if ((sin != NULL) && (sin->sin_family == AF_INET) && IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr))) { nw_interface_t interface = nw_path_copy_interface(path);
if (interface != NULL) { nw_interface_type_t type = nw_interface_get_type(interface); if ((type == nw_interface_type_wired) || ((type == nw_interface_type_wifi) && (nw_interface_get_subtype(interface) != nw_interface_subtype_wifi_awdl))) { is_linklocal_direct = true; }
nw_release(interface); } } }
nw_release(endpoint); return is_linklocal_direct;}
static SCNetworkReachabilityFlags__SCNetworkReachabilityGetFlagsFromPath(nw_path_t path, ReachabilityAddressType type, nw_resolver_status_t resolverStatus, nw_array_t resolvedEndpoints, Boolean resolvedEndpointUseFlags, SCNetworkReachabilityFlags resolvedEndpointFlags){ __block SCNetworkReachabilityFlags flags = kSCNetworkReachabilityFlagsReachable; __block const char *why = "???"; if (path != NULL) { nw_path_status_t status = nw_path_get_status(path); if (status == nw_path_status_satisfied) { __block bool checkDNSFlags = TRUE; flags = kSCNetworkReachabilityFlagsReachable; why = "nw_path_status_satisfied";#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR if (nw_path_uses_interface_type(path, nw_interface_type_cellular)) { flags |= (kSCNetworkReachabilityFlagsTransientConnection | kSCNetworkReachabilityFlagsIsWWAN); why = "nw_path_status_satisfied, cellular"; }#endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR xpc_object_t agent_dictionary = nw_path_copy_netagent_dictionary(path); if (agent_dictionary != NULL) { if (xpc_dictionary_get_count(agent_dictionary) > 0) { xpc_dictionary_apply(agent_dictionary, ^bool(const char *key, xpc_object_t value) {#pragma unused(key) Boolean vpn = FALSE; Boolean onDemand = FALSE; __SCNetworkReachabilityGetAgentVPNFlags(value, &vpn, &onDemand); if (vpn) { // VPN flows are transient flags |= kSCNetworkReachabilityFlagsTransientConnection; why = "nw_path_status_satisfied, VPN"; } if (onDemand && type == reachabilityTypeName && resolverStatus == nw_resolver_status_complete && nw_array_get_count(resolvedEndpoints) == 0) { // On Demand by hostname, when no address has been resolved flags |= (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsConnectionOnDemand); why = "nw_path_status_satisfied, OnDemand"; checkDNSFlags = FALSE; } return TRUE; });
} xpc_release(agent_dictionary); } if (isReachabilityTypeName(type)) { if (checkDNSFlags) { if (resolverStatus == nw_resolver_status_complete && nw_array_get_count(resolvedEndpoints) == 0) { // DNS didn't resolve, as a final answer for now. Not reachable! flags = 0; why = "nw_path_status_satisfied, DNS not reachable"; } else if (resolvedEndpointUseFlags) { flags = resolvedEndpointFlags; why = "nw_path_status_satisfied, resolved endpoint flags"; } } } else { if (nw_path_is_direct(path) || nw_path_is_linklocal_direct(path)) { flags |= kSCNetworkReachabilityFlagsIsDirect; why = "nw_path_status_satisfied, by address, direct"; } if (nw_path_is_local(path)) { flags |= kSCNetworkReachabilityFlagsIsLocalAddress; why = "nw_path_status_satisfied, by address, local"; } } } else if (status == nw_path_status_unsatisfied) { flags = 0; why = "nw_path_status_unsatisfied";#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR if (nw_path_uses_interface_type(path, nw_interface_type_cellular)) { flags |= kSCNetworkReachabilityFlagsIsWWAN; why = "nw_path_status_unsatisfied, WWAN"; }#endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR } else if (status == nw_path_status_satisfiable) { flags = (kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection); why = "nw_path_status_satisfiable"; uuid_t vpn_uuid; if (nw_path_get_vpn_config_id(path, &vpn_uuid)) { flags |= kSCNetworkReachabilityFlagsConnectionOnDemand; why = "nw_path_status_satisfiable, OnDemand"; }#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR else if (nw_path_uses_interface_type(path, nw_interface_type_cellular)) { flags |= kSCNetworkReachabilityFlagsIsWWAN; why = "nw_path_status_satisfiable, WWAN"; }#endif // TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR } } SC_log(LOG_DEBUG, "__SCNetworkReachabilityGetFlagsFromPath, flags = 0x%08x, %s", flags, why); return (flags & kSCNetworkReachabilityFlagsMask);}
static nw_endpoint_t__SCNetworkReachabilityGetPrimaryEndpoint(SCNetworkReachabilityPrivateRef targetPrivate){ if (targetPrivate->type == reachabilityTypeName) { return targetPrivate->hostnameEndpoint; } else if (targetPrivate->type == reachabilityTypeAddress || targetPrivate->type == reachabilityTypeAddressPair || targetPrivate->type == reachabilityTypePTR) { return targetPrivate->remoteAddressEndpoint; } return NULL;}
intSCNetworkReachabilityGetInterfaceIndex(SCNetworkReachabilityRef target){ int if_index = -1; Boolean ok = TRUE; SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; SCNetworkReachabilityFlags flags = 0;
if (!isA_SCNetworkReachability(target)) { _SCErrorSet(kSCStatusInvalidArgument); return if_index; }
MUTEX_LOCK(&targetPrivate->lock);
flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, targetPrivate->type, nw_resolver_status_invalid, NULL, targetPrivate->lastResolvedEndpointHasFlags, targetPrivate->lastResolvedEndpointFlags);
/* Only return the if_index if the connection is reachable not for reachable connection * required etc ... */ if (ok && __SCNetworkReachabilityRank(flags) == ReachabilityRankReachable) { if (targetPrivate->lastResolvedEndpointHasFlags) { if_index = targetPrivate->lastResolvedEndpointInterfaceIndex; } else { if_index = nw_path_get_interface_index(targetPrivate->lastPath); } }
MUTEX_UNLOCK(&targetPrivate->lock); return if_index;}
// CrazyIvan46 is the feature that allows connections to IPv4 literals on IPv6-only (NAT64+DNS64) networks to work// This function replaces the path when the initial one isn't satisfied and our target is an IPv4 literal// It tries IPv6 reachability instead in case we could synthesize another address to connect tostatic OS_OBJECT_RETURNS_RETAINED nw_path_t__SCNetworkReachabilityCreateCrazyIvan46Path(nw_path_t path, nw_endpoint_t endpoint, nw_parameters_t parameters, Boolean allow_resolution){ nw_path_t retPath = NULL; const struct sockaddr *sa;
if ((nw_path_get_status(path) != nw_path_status_unsatisfied) || (NULL == endpoint) || (nw_endpoint_get_type(endpoint) != nw_endpoint_type_address)) { return NULL; }
sa = nw_endpoint_get_address(endpoint);
if (sa->sa_family != AF_INET) { return NULL; }
if (allow_resolution) { uint32_t ifIndex = 0; int32_t numPrefixes; nw_nat64_prefix_t *prefixes = NULL; struct sockaddr_in sin;
memcpy(&sin, sa, MIN(sizeof(sin), sa->sa_len)); if (NULL != parameters) { ifIndex = nw_parameters_get_required_interface_index(parameters); } numPrefixes = nw_nat64_copy_prefixes(&ifIndex, &prefixes); if (numPrefixes > 0) { struct sockaddr_in6 synthesizedAddress = { .sin6_len = sizeof(struct sockaddr_in6), .sin6_family = AF_INET6, .sin6_port = htons(nw_endpoint_get_port(endpoint)), .sin6_flowinfo = 0, .sin6_scope_id = 0 }; nw_endpoint_t synthesizedEndpoint; nw_path_evaluator_t synthesizedEvaluator; nw_path_t synthesizedPath;
nw_nat64_synthesize_v6(&prefixes[0], &sin.sin_addr, &synthesizedAddress.sin6_addr); synthesizedEndpoint = nw_endpoint_create_address((const struct sockaddr *)&synthesizedAddress); synthesizedEvaluator = nw_path_create_evaluator_for_endpoint(synthesizedEndpoint, parameters); synthesizedPath = nw_path_evaluator_copy_path(synthesizedEvaluator); if (nw_path_get_status(synthesizedPath) != nw_path_status_unsatisfied) { retPath = synthesizedPath; SC_log(LOG_INFO, "Using CrazyIvan46 synthesized reachability result"); } else { nw_release(synthesizedPath); } nw_release(synthesizedEvaluator); nw_release(synthesizedEndpoint); } } else { // Don't synthesize in non-scheduled mode to avoid generating DNS traffic nw_path_t v6Path; nw_path_evaluator_t v6PathEvaluator; nw_parameters_t v6Parameters;
if (NULL != parameters) { v6Parameters = nw_parameters_copy(parameters); } else { v6Parameters = nw_parameters_create(); } nw_parameters_set_required_address_family(v6Parameters, AF_INET6); v6PathEvaluator = nw_path_create_evaluator_for_endpoint(NULL, v6Parameters); v6Path = nw_path_evaluator_copy_path(v6PathEvaluator); if (nw_path_get_status(v6Path) != nw_path_status_unsatisfied) { retPath = v6Path; SC_log(LOG_INFO, "Using CrazyIvan46 simple reachability result"); } else { nw_release(v6Path); } nw_release(v6PathEvaluator); nw_release(v6Parameters); } return retPath;}
BooleanSCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags *flags){ nw_path_t crazyIvanPath; nw_endpoint_t endpoint; Boolean ok = TRUE; nw_path_t path; nw_path_evaluator_t pathEvaluator; SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
if (!isA_SCNetworkReachability(target)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
MUTEX_LOCK(&targetPrivate->lock);
if (targetPrivate->scheduled) { // if being watched, return the last known (and what should be current) status *flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, targetPrivate->type, targetPrivate->lastResolverStatus, targetPrivate->lastResolvedEndpoints, targetPrivate->lastResolvedEndpointHasFlags, targetPrivate->lastResolvedEndpointFlags); // because we have synchronously captured the current status, we no longer // need our by-name required callback targetPrivate->sentFirstUpdate = TRUE; goto done; }
// Not being watched, so run a one-shot path evaluator // We don't care about DNS resolution in this case, since we only need to have the // DNS resolution to support clients watching reachability to get updates endpoint = __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate); pathEvaluator = nw_path_create_evaluator_for_endpoint(endpoint, targetPrivate->parameters); path = nw_path_evaluator_copy_path(pathEvaluator);
if (isReachabilityTypeAddress(targetPrivate->type)) { crazyIvanPath = __SCNetworkReachabilityCreateCrazyIvan46Path(path, endpoint, targetPrivate->parameters, FALSE); if (NULL != crazyIvanPath) { nw_release(path); path = crazyIvanPath; } }
*flags = __SCNetworkReachabilityGetFlagsFromPath(path, 0, nw_resolver_status_invalid, NULL, FALSE, 0); nw_release(path); nw_release(pathEvaluator);
done :
MUTEX_UNLOCK(&targetPrivate->lock); return ok;}
#pragma mark -#pragma mark Notifications
static voidreachPerformAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate){ void *context_info; void (*context_release)(const void *); SCNetworkReachabilityCallBack rlsFunction; SCNetworkReachabilityFlags flags = 0;
if (!targetPrivate->scheduled) { // if no longer scheduled SC_log(LOG_INFO, "%sskipping SCNetworkReachability callback, no longer scheduled", targetPrivate->log_prefix); MUTEX_UNLOCK(&targetPrivate->lock); return; }
// callout rlsFunction = targetPrivate->rlsFunction; if (targetPrivate->rlsContext.retain != NULL) { context_info = (void *)(*targetPrivate->rlsContext.retain)(targetPrivate->rlsContext.info); context_release = targetPrivate->rlsContext.release; } else { context_info = targetPrivate->rlsContext.info; context_release = NULL; }
flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, targetPrivate->type, targetPrivate->lastResolverStatus, targetPrivate->lastResolvedEndpoints, targetPrivate->lastResolvedEndpointHasFlags, targetPrivate->lastResolvedEndpointFlags);
MUTEX_UNLOCK(&targetPrivate->lock);
if (rlsFunction != NULL) { SC_log(LOG_DEBUG, "%sexec SCNetworkReachability callout w/flags = 0x%08x", targetPrivate->log_prefix, flags); (*rlsFunction)((SCNetworkReachabilityRef)targetPrivate, flags, context_info); }
if (context_release != NULL) { (*context_release)(context_info); }
return;}
static voidreachPerform(void *info){ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)info;
MUTEX_LOCK(&targetPrivate->lock); reachPerformAndUnlock(targetPrivate);
}
static voidreachUpdateAndUnlock(SCNetworkReachabilityPrivateRef targetPrivate){ targetPrivate->sentFirstUpdate = TRUE; if (targetPrivate->rls != NULL) { if (targetPrivate->rlList != NULL) { CFRunLoopSourceSignal(targetPrivate->rls); _SC_signalRunLoop(targetPrivate, targetPrivate->rls, targetPrivate->rlList); } MUTEX_UNLOCK(&targetPrivate->lock); } else { reachPerformAndUnlock(targetPrivate); }}
BooleanSCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityCallBack callout, SCNetworkReachabilityContext *context){ SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
MUTEX_LOCK(&targetPrivate->lock);
if (targetPrivate->rlsContext.release != NULL) { /* let go of the current context */ (*targetPrivate->rlsContext.release)(targetPrivate->rlsContext.info); }
targetPrivate->rlsFunction = callout; targetPrivate->rlsContext.info = NULL; targetPrivate->rlsContext.retain = NULL; targetPrivate->rlsContext.release = NULL; targetPrivate->rlsContext.copyDescription = NULL; if (context) { memcpy(&targetPrivate->rlsContext, context, sizeof(SCNetworkReachabilityContext)); if (context->retain != NULL) { targetPrivate->rlsContext.info = (void *)(*context->retain)(context->info); } }
MUTEX_UNLOCK(&targetPrivate->lock);
return TRUE;}
static CFStringRefreachRLSCopyDescription(const void *info){ SCNetworkReachabilityRef target = (SCNetworkReachabilityRef)info;
return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCNetworkReachability RLS> {target = %p}"), target);}
BooleanSCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef target, CFRunLoopRef runLoop, CFStringRef runLoopMode){ Boolean success = FALSE; SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
MUTEX_LOCK(&targetPrivate->lock);
if (targetPrivate->scheduled) { if (targetPrivate->rls != NULL && targetPrivate->rlList != NULL) { if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) { /* * if we do not already have host notifications scheduled with * this runLoop / runLoopMode */ CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode); }
_SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
MUTEX_UNLOCK(&targetPrivate->lock); return TRUE; } else { MUTEX_UNLOCK(&targetPrivate->lock); _SCErrorSet(kSCStatusInvalidArgument); return FALSE; } }
CFRunLoopSourceContext context = { 0 // version , (void *)target // info , CFRetain // retain , CFRelease // release , reachRLSCopyDescription // copyDescription , CFEqual // equal , CFHash // hash , NULL // schedule , NULL // cancel , reachPerform // perform };
targetPrivate->rls = CFRunLoopSourceCreate(NULL, 0, &context); targetPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (!_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) { /* * if we do not already have host notifications scheduled with * this runLoop / runLoopMode */ CFRunLoopAddSource(runLoop, targetPrivate->rls, runLoopMode); }
_SC_schedule(target, runLoop, runLoopMode, targetPrivate->rlList);
success = __SCNetworkReachabilitySetDispatchQueue(targetPrivate, _callback_queue()); if (!success) { if (_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) { CFIndex n = CFArrayGetCount(targetPrivate->rlList); if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) { // if target is no longer scheduled for this runLoop / runLoopMode CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode); if (n == 0) { // if *all* notifications have been unscheduled CFRelease(targetPrivate->rlList); targetPrivate->rlList = NULL; CFRunLoopSourceInvalidate(targetPrivate->rls); CFRelease(targetPrivate->rls); targetPrivate->rls = NULL; } } } }
MUTEX_UNLOCK(&targetPrivate->lock); return success;}
BooleanSCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef target, CFRunLoopRef runLoop, CFStringRef runLoopMode){ Boolean success = FALSE; Boolean unscheduleDispatchQueue = FALSE; SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target;
if (!isA_SCNetworkReachability(target) || (runLoop == NULL) || (runLoopMode == NULL)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
MUTEX_LOCK(&targetPrivate->lock);
if (targetPrivate->rlList == NULL || targetPrivate->rls == NULL || !targetPrivate->scheduled) { MUTEX_UNLOCK(&targetPrivate->lock); _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
if (_SC_unschedule(target, runLoop, runLoopMode, targetPrivate->rlList, FALSE)) { CFIndex n = CFArrayGetCount(targetPrivate->rlList); if ((n == 0) || !_SC_isScheduled(NULL, runLoop, runLoopMode, targetPrivate->rlList)) { // if target is no longer scheduled for this runLoop / runLoopMode CFRunLoopRemoveSource(runLoop, targetPrivate->rls, runLoopMode); if (n == 0) { // if *all* notifications have been unscheduled unscheduleDispatchQueue = TRUE; CFRelease(targetPrivate->rlList); targetPrivate->rlList = NULL; CFRunLoopSourceInvalidate(targetPrivate->rls); CFRelease(targetPrivate->rls); targetPrivate->rls = NULL; } } }
if (unscheduleDispatchQueue) { success = __SCNetworkReachabilitySetDispatchQueue(targetPrivate, NULL); } else { success = TRUE; } MUTEX_UNLOCK(&targetPrivate->lock); return success;}
static __inline__ void__SCNetworkReachabilityCopyPathStatus(SCNetworkReachabilityPrivateRef targetPrivate, SCNetworkReachabilityFlags *flags, uint *ifIndex, size_t *endpointCount){ if (flags) { *flags = __SCNetworkReachabilityGetFlagsFromPath(targetPrivate->lastPath, targetPrivate->type, targetPrivate->lastResolverStatus, targetPrivate->lastResolvedEndpoints, targetPrivate->lastResolvedEndpointHasFlags, targetPrivate->lastResolvedEndpointFlags); } if (ifIndex) { *ifIndex = nw_path_get_interface_index(targetPrivate->lastPath); } if (endpointCount) { *endpointCount = nw_array_get_count(targetPrivate->lastResolvedEndpoints); } return;}
static __inline__ Boolean__SCNetworkReachabilityShouldUpdateClient(SCNetworkReachabilityPrivateRef targetPrivate, SCNetworkReachabilityFlags oldFlags, uint oldIFIndex, size_t oldEndpointCount){ SCNetworkReachabilityFlags newFlags = 0; uint newIFIndex = 0; size_t newEndpointCount = 0; __SCNetworkReachabilityCopyPathStatus(targetPrivate, &newFlags, &newIFIndex, &newEndpointCount); return (!targetPrivate->sentFirstUpdate || oldFlags != newFlags || oldIFIndex != newIFIndex || oldEndpointCount != newEndpointCount);}
static void__SCNetworkReachabilityRestartResolver(SCNetworkReachabilityPrivateRef targetPrivate){ if (targetPrivate && !targetPrivate->resolverBypass && isReachabilityTypeName(targetPrivate->type)) { nw_resolver_t resolver; CFRetain(targetPrivate); if (NULL != targetPrivate->resolver) { nw_resolver_cancel(targetPrivate->resolver); } if (targetPrivate->lastPath != NULL) { resolver = nw_resolver_create_with_path(targetPrivate->lastPath); } else { resolver = nw_resolver_create_with_endpoint(__SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate), targetPrivate->lastPathParameters ? targetPrivate->lastPathParameters : targetPrivate->parameters); } if (resolver == NULL) { SC_log(LOG_ERR, "%sfailed to create a nw_resolver", targetPrivate->log_prefix); targetPrivate->resolver = NULL; CFRelease(targetPrivate); return; } targetPrivate->resolver = resolver; nw_resolver_set_cancel_handler(resolver, ^(void) { MUTEX_LOCK(&targetPrivate->lock); if (resolver == targetPrivate->resolver) { targetPrivate->resolver = NULL; } nw_release(resolver); MUTEX_UNLOCK(&targetPrivate->lock); CFRelease(targetPrivate); }); if (!nw_resolver_set_update_handler(resolver, targetPrivate->dispatchQueue, ^(nw_resolver_status_t status, nw_array_t resolved_endpoints) { MUTEX_LOCK(&targetPrivate->lock); if (targetPrivate->scheduled) { SCNetworkReachabilityFlags oldFlags = 0; uint oldIFIndex = 0; size_t oldEndpointCount = 0; __SCNetworkReachabilityCopyPathStatus(targetPrivate, &oldFlags, &oldIFIndex, &oldEndpointCount);
targetPrivate->lastResolverStatus = status; nw_release(targetPrivate->lastResolvedEndpoints); targetPrivate->lastResolvedEndpoints = nw_retain(resolved_endpoints);
// Run path evaluation on the resolved endpoints __block Boolean hasFlags = FALSE; targetPrivate->lastResolvedEndpointHasFlags = FALSE; targetPrivate->lastResolvedEndpointFlags = 0; targetPrivate->lastResolvedEndpointInterfaceIndex = 0; nw_array_apply(targetPrivate->lastResolvedEndpoints, ^bool(size_t index, nw_object_t object) {#pragma unused(index) SCNetworkReachabilityFlags flags = 0; uint interfaceIndex = 0; ReachabilityRankType rank; nw_endpoint_t resolvedEndpoint = (nw_endpoint_t)object; nw_path_evaluator_t pathEvaluator = nw_path_create_evaluator_for_endpoint(resolvedEndpoint, targetPrivate->lastPathParameters ? targetPrivate->lastPathParameters : targetPrivate->parameters); nw_path_t path = nw_path_evaluator_copy_path(pathEvaluator); if (path != NULL) { flags = __SCNetworkReachabilityGetFlagsFromPath(path, 0, nw_resolver_status_invalid, NULL, FALSE, 0); hasFlags = TRUE; } interfaceIndex = nw_path_get_interface_index(path); nw_release(path); nw_release(pathEvaluator);
rank = __SCNetworkReachabilityRank(flags); if (rank > __SCNetworkReachabilityRank(targetPrivate->lastResolvedEndpointFlags)) { // Return the best case result targetPrivate->lastResolvedEndpointFlags = flags; targetPrivate->lastResolvedEndpointInterfaceIndex = interfaceIndex; if (rank == ReachabilityRankReachable) { // Can't get any better than REACHABLE return FALSE; } } return TRUE; }); targetPrivate->lastResolvedEndpointHasFlags = hasFlags;
if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate, oldFlags, oldIFIndex, oldEndpointCount)) { reachUpdateAndUnlock(targetPrivate); } else { MUTEX_UNLOCK(&targetPrivate->lock); } } else { MUTEX_UNLOCK(&targetPrivate->lock); } })) { nw_release(resolver); targetPrivate->resolver = NULL; CFRelease(targetPrivate); } }}
static Boolean__SCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityPrivateRef targetPrivate, dispatch_queue_t queue){ Boolean ok = FALSE;
if (queue != NULL) { nw_path_t crazyIvanPath; nw_endpoint_t endpoint; nw_path_evaluator_t pathEvaluator;
if ((targetPrivate->dispatchQueue != NULL) || // if we are already scheduled with a dispatch queue ((queue != NULL) && targetPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop _SCErrorSet(kSCStatusInvalidArgument); goto done; }
SC_log(LOG_DEBUG, "%sscheduled", targetPrivate->log_prefix);
// retain dispatch queue dispatch_retain(queue); nw_path_evaluator_cancel(targetPrivate->pathEvaluator); endpoint = __SCNetworkReachabilityGetPrimaryEndpoint(targetPrivate); pathEvaluator = nw_path_create_evaluator_for_endpoint(endpoint, targetPrivate->parameters); targetPrivate->pathEvaluator = pathEvaluator; targetPrivate->dispatchQueue = queue; targetPrivate->scheduled = TRUE; if (isReachabilityTypeName(targetPrivate->type)) { // we must have at least one callback for by-name queries targetPrivate->sentFirstUpdate = FALSE; } else { targetPrivate->sentFirstUpdate = TRUE; }
nw_release(targetPrivate->lastPath); targetPrivate->lastPath = nw_path_evaluator_copy_path(pathEvaluator);
if (isReachabilityTypeAddress(targetPrivate->type)) { crazyIvanPath = __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate->lastPath, endpoint, targetPrivate->parameters, FALSE); if (NULL != crazyIvanPath) { nw_release(targetPrivate->lastPath); targetPrivate->lastPath = crazyIvanPath; } }
nw_release(targetPrivate->lastPathParameters); targetPrivate->lastPathParameters = nw_path_copy_derived_parameters(targetPrivate->lastPath);
targetPrivate->lastResolverStatus = nw_resolver_status_invalid; nw_release(targetPrivate->lastResolvedEndpoints); targetPrivate->lastResolvedEndpoints = NULL; __SCNetworkReachabilityRestartResolver(targetPrivate);
CFRetain(targetPrivate); nw_path_evaluator_set_cancel_handler(pathEvaluator, ^(void) { MUTEX_LOCK(&targetPrivate->lock); if (pathEvaluator == targetPrivate->pathEvaluator) { targetPrivate->pathEvaluator = NULL; } nw_release(pathEvaluator); MUTEX_UNLOCK(&targetPrivate->lock); CFRelease(targetPrivate); });
if (!nw_path_evaluator_set_update_handler(pathEvaluator, targetPrivate->dispatchQueue, ^(nw_path_t path) { MUTEX_LOCK(&targetPrivate->lock); if (targetPrivate->scheduled) { nw_path_t crazyIvanPath; SCNetworkReachabilityFlags oldFlags = 0; uint oldIFIndex = 0; size_t oldEndpointCount = 0; __SCNetworkReachabilityCopyPathStatus(targetPrivate, &oldFlags, &oldIFIndex, &oldEndpointCount);
nw_release(targetPrivate->lastPath); targetPrivate->lastPath = nw_retain(path);
if (isReachabilityTypeAddress(targetPrivate->type)) { crazyIvanPath = __SCNetworkReachabilityCreateCrazyIvan46Path(targetPrivate->lastPath, endpoint, targetPrivate->parameters, TRUE); if (NULL != crazyIvanPath) { nw_release(targetPrivate->lastPath); targetPrivate->lastPath = crazyIvanPath; } }
if (targetPrivate->lastResolverStatus == nw_resolver_status_complete) { targetPrivate->lastResolverStatus = nw_resolver_status_invalid; __SCNetworkReachabilityRestartResolver(targetPrivate); }
if (__SCNetworkReachabilityShouldUpdateClient(targetPrivate, oldFlags, oldIFIndex, oldEndpointCount)) { reachUpdateAndUnlock(targetPrivate); } else { MUTEX_UNLOCK(&targetPrivate->lock); } } else { MUTEX_UNLOCK(&targetPrivate->lock); } })) { targetPrivate->pathEvaluator = NULL; nw_release(pathEvaluator); CFRelease(targetPrivate); } } else { if (targetPrivate->dispatchQueue == NULL) { // if we should be scheduled on a dispatch queue (but are not) _SCErrorSet(kSCStatusInvalidArgument); goto done; }
if (!targetPrivate->scheduled) { // if not currently scheduled _SCErrorSet(kSCStatusInvalidArgument); goto done; }
targetPrivate->scheduled = FALSE; targetPrivate->sentFirstUpdate = FALSE; nw_path_evaluator_cancel(targetPrivate->pathEvaluator); targetPrivate->pathEvaluator = NULL; nw_release(targetPrivate->lastPath); targetPrivate->lastPath = NULL; nw_release(targetPrivate->lastPathParameters); targetPrivate->lastPathParameters = NULL; nw_release(targetPrivate->lastResolvedEndpoints); targetPrivate->lastResolvedEndpoints = NULL; if (NULL != targetPrivate->resolver) { nw_resolver_cancel(targetPrivate->resolver); targetPrivate->resolver = NULL; } if (targetPrivate->dispatchQueue != NULL) { dispatch_release(targetPrivate->dispatchQueue); targetPrivate->dispatchQueue = NULL; }
SC_log(LOG_DEBUG, "%sunscheduled", targetPrivate->log_prefix); } ok = TRUE;done: return ok;}
BooleanSCNetworkReachabilitySetDispatchQueue(SCNetworkReachabilityRef target, dispatch_queue_t queue){ if (!isA_SCNetworkReachability(target)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
SCNetworkReachabilityPrivateRef targetPrivate = (SCNetworkReachabilityPrivateRef)target; MUTEX_LOCK(&targetPrivate->lock); Boolean success = __SCNetworkReachabilitySetDispatchQueue(targetPrivate, queue); MUTEX_UNLOCK(&targetPrivate->lock); return success;}
/* * _SC_checkResolverReachabilityByAddress() * * Given an IP address, determine whether a reverse DNS query can be issued * using the current network configuration. */Boolean_SC_checkResolverReachabilityByAddress(SCDynamicStoreRef *storeP, SCNetworkReachabilityFlags *flags, Boolean *haveDNS, struct sockaddr *sa){#pragma unused(storeP)#pragma unused(sa) nw_path_evaluator_t evaluator = nw_path_create_default_evaluator(); nw_path_t path = nw_path_evaluator_copy_path(evaluator); if (nw_path_get_status(path) == nw_path_status_unsatisfied_network) { if (flags) { *flags = 0; } if (haveDNS) { *haveDNS = FALSE; } } else { if (flags) { *flags = kSCNetworkReachabilityFlagsReachable; } if (haveDNS) { *haveDNS = TRUE; } } nw_release(evaluator); nw_release(path);
return TRUE;}