Something went wrong. Try again.
Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
145 kB · 4808 lines
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809/* * 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 14, 2004 Christophe Allie <callie@apple.com> * - use mach messages
* December 20, 2002 Christophe Allie <callie@apple.com> * - initial revision */
//#define DEBUG_MACH_PORT_ALLOCATIONS
#include <TargetConditionals.h>#include <sys/cdefs.h>#include <dispatch/dispatch.h>#include <CoreFoundation/CFXPCBridge.h>
#include "SCNetworkConnectionInternal.h"#include "SCNetworkConfigurationInternal.h"#include "SCD.h"
#include <SystemConfiguration/VPNAppLayerPrivate.h>#include <SystemConfiguration/VPNTunnel.h>
#if !TARGET_OS_IPHONE#include <Security/Security.h>#include "dy_framework.h"#endif // !TARGET_OS_IPHONE
#include <bootstrap.h>
#include <pthread.h>#include <notify.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#include <unistd.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <net/if.h>#include <mach/mach.h>#include <bsm/audit.h>#include <bsm/libbsm.h>#include <sandbox.h>#include <sys/proc_info.h>#include <libproc.h>
#include <ppp/ppp_msg.h>#include "pppcontroller.h"#include <ppp/pppcontroller_types.h>
#ifndef PPPCONTROLLER_SERVER_PRIV#define PPPCONTROLLER_SERVER_PRIV PPPCONTROLLER_SERVER#endif // !PPPCONTROLLER_SERVER_PRIV
static int debug = 0;static pthread_once_t initialized = PTHREAD_ONCE_INIT;static pthread_mutex_t scnc_lock = PTHREAD_MUTEX_INITIALIZER;static mach_port_t scnc_server = MACH_PORT_NULL;static char *scnc_server_name = NULL;
typedef struct {
/* base CFType information */ CFRuntimeBase cfBase;
/* lock */ pthread_mutex_t lock;
/* service */ SCNetworkServiceRef service;
/* client info (if we are proxying for another process */ mach_port_t client_audit_session; audit_token_t client_audit_token; mach_port_t client_bootstrap_port; uid_t client_uid; gid_t client_gid; pid_t client_pid; uuid_t client_uuid; CFStringRef client_bundle_id;
/* ref to PPP controller for control messages */ mach_port_t session_port;
/* ref to PPP controller for notification messages */ CFMachPortRef notify_port;
/* keep track of whether we're acquired the initial status */ Boolean haveStatus;
/* run loop source, callout, context, rl scheduling info */ Boolean scheduled; CFRunLoopSourceRef rls; SCNetworkConnectionCallBack rlsFunction; SCNetworkConnectionContext rlsContext; CFMutableArrayRef rlList;
/* SCNetworkConnectionSetDispatchQueue */ dispatch_group_t dispatchGroup; dispatch_queue_t dispatchQueue; dispatch_source_t dispatchSource;
SCNetworkConnectionType type; Boolean on_demand; CFDictionaryRef on_demand_info; CFDictionaryRef on_demand_user_options; CFStringRef on_demand_required_probe;
/* Flow Divert support info */ CFDictionaryRef flow_divert_token_params;
#if !TARGET_OS_SIMULATOR /* NetworkExtension data structures */ ne_session_t ne_session;#endif /* !TARGET_OS_SIMULATOR */} SCNetworkConnectionPrivate, *SCNetworkConnectionPrivateRef;
__private_extern__ os_log_t__log_SCNetworkConnection(void){ static os_log_t log = NULL;
if (log == NULL) { log = os_log_create("com.apple.SystemConfiguration", "SCNetworkConnection"); }
return log;}
static __inline__ CFTypeRefisA_SCNetworkConnection(CFTypeRef obj){ return (isA_CFType(obj, SCNetworkConnectionGetTypeID()));}
#if !TARGET_OS_SIMULATORstatic Boolean__SCNetworkConnectionUseNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate){ Boolean result = FALSE;
if (ne_session_use_as_system_vpn() && connectionPrivate->service != NULL) { _SCErrorSet(kSCStatusOK); result = _SCNetworkServiceIsVPN(connectionPrivate->service); /* * SCNetworkServiceGetInterface (called by _SCNetworkServiceIsVPN) will set the SC error to kSCStatusInvalidArgument if the service does not have an associated prefs object. * In that case, we try to get the service type/subtype from the dynamic store. */ if (!result && SCError() == kSCStatusInvalidArgument) { CFStringRef interfaceKey; CFDictionaryRef interfaceDict; CFStringRef serviceID;
serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); interfaceKey = SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault, kSCDynamicStoreDomainSetup, serviceID, kSCEntNetInterface); interfaceDict = SCDynamicStoreCopyValue(NULL, interfaceKey); if (isA_CFDictionary(interfaceDict)) { CFStringRef interfaceType = CFDictionaryGetValue(interfaceDict, kSCPropNetInterfaceType); if (isA_CFString(interfaceType)) { if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP)) { CFStringRef interfaceSubType = CFDictionaryGetValue(interfaceDict, kSCPropNetInterfaceSubType);#pragma GCC diagnostic push#pragma GCC diagnostic ignored "-Wdeprecated" result = (isA_CFString(interfaceSubType) && (CFEqual(interfaceSubType, kSCValNetInterfaceSubTypePPTP) || CFEqual(interfaceSubType, kSCValNetInterfaceSubTypeL2TP)));#pragma GCC diagnostic pop } else { result = (CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) || CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec)); } } } if (interfaceDict != NULL) { CFRelease(interfaceDict); } CFRelease(interfaceKey); } }
return result;}#endif /* !TARGET_OS_SIMULATOR */
static Boolean__SCNetworkConnectionUsingNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate){#if !TARGET_OS_SIMULATOR return (connectionPrivate->ne_session != NULL);#else#pragma unused(connectionPrivate) return FALSE;#endif /* !TARGET_OS_SIMULATOR */}
static CFStringRef__SCNetworkConnectionCopyDescription(CFTypeRef cf){ CFAllocatorRef allocator = CFGetAllocator(cf); SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)cf; CFMutableStringRef result;
result = CFStringCreateMutable(allocator, 0); CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf, allocator); CFStringAppendFormat(result, NULL, CFSTR("service = %p"), connectionPrivate->service); if (connectionPrivate->session_port != MACH_PORT_NULL) { CFStringAppendFormat(result, NULL, CFSTR(", server port = 0x%x"), connectionPrivate->session_port); } CFStringAppendFormat(result, NULL, CFSTR("using NetworkExtension = %s"), (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate) ? "yes" : "no")); CFStringAppendFormat(result, NULL, CFSTR("}"));
return result;}
static void__SCNetworkConnectionDeallocate(CFTypeRef cf){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)cf;
/* release resources */ pthread_mutex_destroy(&connectionPrivate->lock);
if (connectionPrivate->client_audit_session != MACH_PORT_NULL) { mach_port_deallocate(mach_task_self(), connectionPrivate->client_audit_session); }
if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) { mach_port_deallocate(mach_task_self(), connectionPrivate->client_bootstrap_port); }
if (connectionPrivate->client_bundle_id != NULL) { CFRelease(connectionPrivate->client_bundle_id); }
if (connectionPrivate->rls != NULL) { CFRunLoopSourceInvalidate(connectionPrivate->rls); CFRelease(connectionPrivate->rls); }
if (connectionPrivate->rlList != NULL) { CFRelease(connectionPrivate->rlList); }
if (connectionPrivate->notify_port != NULL) { mach_port_t mp = CFMachPortGetPort(connectionPrivate->notify_port);
__MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate notify_port", mp); CFMachPortInvalidate(connectionPrivate->notify_port); CFRelease(connectionPrivate->notify_port); mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1); }
if (connectionPrivate->session_port != MACH_PORT_NULL) { __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate->session_port); (void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port); }
if (connectionPrivate->rlsContext.release != NULL) (*connectionPrivate->rlsContext.release)(connectionPrivate->rlsContext.info);
if (connectionPrivate->service != NULL) { CFRelease(connectionPrivate->service); }
if (connectionPrivate->on_demand_info != NULL) { CFRelease(connectionPrivate->on_demand_info); }
if (connectionPrivate->on_demand_user_options != NULL) { CFRelease(connectionPrivate->on_demand_user_options); }
if (connectionPrivate->on_demand_required_probe != NULL) { CFRelease(connectionPrivate->on_demand_required_probe); }
if (connectionPrivate->flow_divert_token_params != NULL) { CFRelease(connectionPrivate->flow_divert_token_params); }
#if !TARGET_OS_SIMULATOR if (connectionPrivate->ne_session != NULL) { ne_session_set_event_handler(connectionPrivate->ne_session, NULL, NULL); ne_session_release(connectionPrivate->ne_session); }#endif /* !TARGET_OS_SIMULATOR */
return;}
static CFTypeID __kSCNetworkConnectionTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __SCNetworkConnectionClass = { 0, // version "SCNetworkConnection", // className NULL, // init NULL, // copy __SCNetworkConnectionDeallocate, // dealloc NULL, // equal NULL, // hash NULL, // copyFormattingDesc __SCNetworkConnectionCopyDescription // copyDebugDesc};
static voidchildForkHandler(){ /* the process has forked (and we are the child process) */
scnc_server = MACH_PORT_NULL; scnc_server_name = NULL; return;}
static void__SCNetworkConnectionInitialize(void){ char *env;
/* get the debug environment variable */ env = getenv("PPPDebug"); if (env != NULL) { if (sscanf(env, "%d", &debug) != 1) { /* PPPDebug value is not valid (or non-numeric), set debug to 1 */ debug = 1; } }
/* register with CoreFoundation */ __kSCNetworkConnectionTypeID = _CFRuntimeRegisterClass(&__SCNetworkConnectionClass);
/* add handler to cleanup after fork() */ (void) pthread_atfork(NULL, NULL, childForkHandler);
return;}
static Boolean__SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection);
#define SC_NETWORK_CONNECTION_QUEUE "SCNetworkConnectionQueue"
static dispatch_queue_t__SCNetworkConnectionQueue(){ static dispatch_once_t once; static dispatch_queue_t q;
dispatch_once(&once, ^{ q = dispatch_queue_create(SC_NETWORK_CONNECTION_QUEUE, NULL); });
return q;}
static void__SCNetworkConnectionNotify(SCNetworkConnectionRef connection, SCNetworkConnectionCallBack rlsFunction, SCNetworkConnectionStatus nc_status, void (*context_release)(const void *), void *context_info){ SC_log(LOG_DEBUG, "exec SCNetworkConnection callout"); (*rlsFunction)(connection, nc_status, context_info); if ((context_release != NULL) && (context_info != NULL)) { (*context_release)(context_info); }
return;}
static void__SCNetworkConnectionCallBackRunLoopPerform(SCNetworkConnectionRef connection, CFRunLoopRef rl, CFStringRef rl_mode, SCNetworkConnectionCallBack rlsFunction, void (*context_release)(const void *), void *context_info){ SCNetworkConnectionStatus nc_status;
nc_status = SCNetworkConnectionGetStatus(connection); CFRunLoopPerformBlock(rl, rl_mode, ^{ __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info); CFRelease(connection); }); CFRunLoopWakeUp(rl); return;}
static void__SCNetworkConnectionCallBackDispatchPerform(SCNetworkConnectionRef connection, dispatch_queue_t q, SCNetworkConnectionCallBack rlsFunction, void (*context_release)(const void *), void *context_info){ SCNetworkConnectionStatus nc_status;
nc_status = SCNetworkConnectionGetStatus(connection); dispatch_async(q, ^{ __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info); CFRelease(connection); }); return;}
static void__SCNetworkConnectionCallBack(void *connection){ boolean_t exec_async = FALSE; SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; void *context_info; void (*context_release)(const void *); CFRunLoopRef rl = NULL; CFStringRef rl_mode; SCNetworkConnectionCallBack rlsFunction = NULL; dispatch_queue_t q = NULL; SCNetworkConnectionStatus nc_status = kSCNetworkConnectionInvalid;
pthread_mutex_lock(&connectionPrivate->lock);
if (!connectionPrivate->scheduled) { // if not currently scheduled pthread_mutex_unlock(&connectionPrivate->lock); return; }
rlsFunction = connectionPrivate->rlsFunction; if (rlsFunction == NULL) { pthread_mutex_unlock(&connectionPrivate->lock); return; }
if ((connectionPrivate->rlsContext.retain != NULL) && (connectionPrivate->rlsContext.info != NULL)) { context_info = (void *)(*connectionPrivate->rlsContext.retain)(connectionPrivate->rlsContext.info); context_release = connectionPrivate->rlsContext.release; } else { context_info = connectionPrivate->rlsContext.info; context_release = NULL; }
#if !TARGET_OS_SIMULATOR if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { pthread_mutex_unlock(&connectionPrivate->lock);
nc_status = SCNetworkConnectionGetStatus(connection); __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info); CFRelease(connection); /* This releases the reference that we took in the NESessionEventStatusChanged event handler */ return; }#endif /* !TARGET_OS_SIMULATOR */
// Do we need to spin a new thread? (either we are running on the main // dispatch queue or main runloop) if (connectionPrivate->rlList == NULL) { // if we are performing the callback on a dispatch queue q = connectionPrivate->dispatchQueue; if (q == dispatch_get_main_queue()) { exec_async = TRUE; } } else { rl = CFRunLoopGetCurrent(); if (rl == CFRunLoopGetMain()) { exec_async = TRUE; } }
CFRetain(connection); pthread_mutex_unlock(&connectionPrivate->lock);
if (!exec_async) { nc_status = SCNetworkConnectionGetStatus(connection); __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info); CFRelease(connection); return; }
if (connectionPrivate->rlList == NULL) { assert(q != NULL); dispatch_retain(q); dispatch_async(__SCNetworkConnectionQueue(), ^{ __SCNetworkConnectionCallBackDispatchPerform(connection, q, rlsFunction, context_release, context_info); dispatch_release(q); }); } else { assert(rl != NULL); CFRetain(rl); rl_mode = CFRunLoopCopyCurrentMode(rl); dispatch_async(__SCNetworkConnectionQueue(), ^{ __SCNetworkConnectionCallBackRunLoopPerform(connection, rl, rl_mode, rlsFunction, context_release, context_info); CFRelease(rl); CFRelease(rl_mode); }); }
return;}
static void__SCNetworkConnectionMachCallBack(CFMachPortRef port, void * msg, CFIndex size, void * info){#pragma unused(port)#pragma unused(size) mach_no_senders_notification_t *buf = msg; mach_msg_id_t msgid = buf->not_header.msgh_id; SCNetworkConnectionRef connection = (SCNetworkConnectionRef)info;
if (msgid == MACH_NOTIFY_NO_SENDERS) { // re-establish notification SC_log(LOG_INFO, "PPPController server died"); (void)__SCNetworkConnectionReconnectNotifications(connection); }
__SCNetworkConnectionCallBack(info);
return;}
#pragma mark -#pragma mark SCNetworkConnection APIs
static CFStringRefpppMPCopyDescription(const void *info){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)info;
return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"), connectionPrivate, connectionPrivate->service, connectionPrivate->rlsFunction);}
static SCNetworkConnectionPrivateRef__SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator, SCNetworkServiceRef service, SCNetworkConnectionCallBack callout, SCNetworkConnectionContext *context){ SCNetworkConnectionPrivateRef connectionPrivate = NULL; uint32_t size;
/* initialize runtime */ pthread_once(&initialized, __SCNetworkConnectionInitialize);
/* allocate NetworkConnection */ size = sizeof(SCNetworkConnectionPrivate) - sizeof(CFRuntimeBase); connectionPrivate = (SCNetworkConnectionPrivateRef)_CFRuntimeCreateInstance(allocator, __kSCNetworkConnectionTypeID, size, NULL); if (connectionPrivate == NULL) { goto fail; }
/* initialize non-zero/NULL members */ pthread_mutex_init(&connectionPrivate->lock, NULL); if (service != NULL) { connectionPrivate->service = CFRetain(service); } connectionPrivate->client_uid = geteuid(); connectionPrivate->client_gid = getegid(); connectionPrivate->client_pid = getpid(); connectionPrivate->rlsFunction = callout; if (context) { memcpy(&connectionPrivate->rlsContext, context, sizeof(SCNetworkConnectionContext)); if (context->retain != NULL) { connectionPrivate->rlsContext.info = (void *)(*context->retain)(context->info); } } connectionPrivate->type = kSCNetworkConnectionTypeUnknown;
#if !TARGET_OS_SIMULATOR if (__SCNetworkConnectionUseNetworkExtension(connectionPrivate)) { CFStringRef serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); if (serviceID != NULL) { uuid_string_t service_uuid_str; if (CFStringGetCString(serviceID, service_uuid_str, sizeof(service_uuid_str), kCFStringEncodingUTF8)) { uuid_t config_id; if (uuid_parse(service_uuid_str, config_id) == 0) { connectionPrivate->ne_session = ne_session_create(config_id, NESessionTypeVPN); } } }
if (connectionPrivate->ne_session == NULL) { SC_log(LOG_NOTICE, "SCNetworkConnection failed to create an ne_session: service ID %@ is not a valid UUID", serviceID); goto fail; } }#endif /* !TARGET_OS_SIMULATOR */
/* success, return the connection reference */ return connectionPrivate;
fail:
/* failure, clean up and leave */ if (connectionPrivate != NULL) { CFRelease(connectionPrivate); }
_SCErrorSet(kSCStatusFailed); return NULL;}
static mach_port_t__SCNetworkConnectionServerPort(kern_return_t *status){ mach_port_t server = MACH_PORT_NULL;
#ifdef BOOTSTRAP_PRIVILEGED_SERVER *status = bootstrap_look_up2(bootstrap_port, __SCNetworkConnectionGetControllerPortName(), &server, 0, BOOTSTRAP_PRIVILEGED_SERVER);#else // BOOTSTRAP_PRIVILEGED_SERVER *status = bootstrap_look_up(bootstrap_port, __SCNetworkConnectionGetControllerPortName(), &server);#endif // BOOTSTRAP_PRIVILEGED_SERVER
switch (*status) { case BOOTSTRAP_SUCCESS : // service currently registered, "a good thing" (tm) return server; case BOOTSTRAP_NOT_PRIVILEGED : // the service is not privileged break; case BOOTSTRAP_UNKNOWN_SERVICE : // service not currently registered, try again later break; default :#ifdef DEBUG SC_log(LOG_DEBUG, "bootstrap_look_up() failed: status=%s", bootstrap_strerror(*status));#endif // DEBUG break; }
scnc_server_name = NULL; /* reset pppcontroller server */ return MACH_PORT_NULL;}
static mach_port_t__SCNetworkConnectionGetCurrentServerPort(void){ return scnc_server;}
static mach_port_t__SCNetworkConnectionRefreshServerPort(mach_port_t current_server, int *mach_result){ mach_port_t new_server;
pthread_mutex_lock(&scnc_lock); if (scnc_server != MACH_PORT_NULL) { if (current_server == scnc_server) { scnc_server_name = NULL; // if the server we tried returned the error (void)mach_port_deallocate(mach_task_self(), scnc_server); scnc_server = __SCNetworkConnectionServerPort(mach_result); } else { // another thread has refreshed the server port } } else { scnc_server = __SCNetworkConnectionServerPort(mach_result); } new_server = scnc_server; pthread_mutex_unlock(&scnc_lock);
return new_server;}
#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && (!TARGET_OS_SIMULATOR || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000))#define HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION#endif
#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_OS_SIMULATOR#define HAVE_PPPCONTROLLER_ATTACHWITHPROXY#endif
static mach_port_t__SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate){ void *data = NULL; CFIndex dataLen = 0; CFDataRef dataRef = NULL; mach_port_t notify_port = MACH_PORT_NULL; mach_port_t oldNotify = MACH_PORT_NULL; int retry = 0; int sc_status = kSCStatusFailed; mach_port_t server = __SCNetworkConnectionGetCurrentServerPort(); kern_return_t status = KERN_SUCCESS;
#ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION mach_port_t au_session = MACH_PORT_NULL;#endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
if (connectionPrivate->session_port != MACH_PORT_NULL) { return connectionPrivate->session_port; }
if (connectionPrivate->service == NULL) { sc_status = kSCStatusConnectionNoService; goto done; }
if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate->service), &dataRef, &data, &dataLen)) { goto done; }
if (connectionPrivate->notify_port != NULL) { mach_port_t mp = CFMachPortGetPort(connectionPrivate->notify_port);
__MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort mp", mp); CFMachPortInvalidate(connectionPrivate->notify_port); CFRelease(connectionPrivate->notify_port); connectionPrivate->notify_port = NULL; mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1); }
#ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION au_session = audit_session_self();#endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
// open a new session with the server while (TRUE) { if ((connectionPrivate->rlsFunction != NULL) && (notify_port == MACH_PORT_NULL)) { // allocate port (for server response) status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, ¬ify_port); if (status != KERN_SUCCESS) { SC_log(LOG_ERR, "mach_port_allocate() failed: %s", mach_error_string(status)); sc_status = status; goto done; }
// add send right (passed to the server) status = mach_port_insert_right(mach_task_self(), notify_port, notify_port, MACH_MSG_TYPE_MAKE_SEND); if (status != KERN_SUCCESS) { SC_log(LOG_NOTICE, "mach_port_insert_right() failed: %s", mach_error_string(status)); mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1); sc_status = status; goto done; } }
if (server != MACH_PORT_NULL) {#ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY if ((connectionPrivate->client_audit_session == MACH_PORT_NULL) && (connectionPrivate->client_bootstrap_port == MACH_PORT_NULL) && (connectionPrivate->client_uid == geteuid()) && (connectionPrivate->client_gid == getegid()) && (connectionPrivate->client_pid == getpid()) ) {#endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY status = pppcontroller_attach(server, data, (mach_msg_type_number_t)dataLen, bootstrap_port, notify_port,#ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION au_session,#endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION &connectionPrivate->session_port, &sc_status);#ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY } else { mach_port_t client_au_session; mach_port_t client_bootstrap_port;
if (connectionPrivate->client_audit_session == MACH_PORT_NULL) { client_au_session = au_session; } else { client_au_session = connectionPrivate->client_audit_session; }
if (connectionPrivate->client_bootstrap_port == MACH_PORT_NULL) { client_bootstrap_port = bootstrap_port; } else { client_bootstrap_port = connectionPrivate->client_bootstrap_port; }
status = pppcontroller_attach_proxy(server, data, (mach_msg_type_number_t)dataLen, client_bootstrap_port, notify_port, client_au_session, connectionPrivate->client_uid, connectionPrivate->client_gid, connectionPrivate->client_pid, &connectionPrivate->session_port, &sc_status); }#endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY if (status == KERN_SUCCESS) { if (sc_status != kSCStatusOK) { SC_log(LOG_DEBUG, "attach w/error, sc_status=%s%s", SCErrorString(sc_status), (connectionPrivate->session_port != MACH_PORT_NULL) ? ", w/session_port!=MACH_PORT_NULL" : "");
if (connectionPrivate->session_port != MACH_PORT_NULL) { __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)", connectionPrivate->session_port); mach_port_deallocate(mach_task_self(), connectionPrivate->session_port); connectionPrivate->session_port = MACH_PORT_NULL; }
if (notify_port != MACH_PORT_NULL) { __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)", notify_port); (void) mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1); notify_port = MACH_PORT_NULL; } } break; }
// our [cached] server port is not valid SC_log(LOG_INFO, "!attach: %s", SCErrorString(status)); if (status == MACH_SEND_INVALID_DEST) { // the server is not yet available __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port); } else if (status == MIG_SERVER_DIED) { __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port); // the server we were using is gone and we've lost our send right mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1); notify_port = MACH_PORT_NULL; } else { // if we got an unexpected error, don't retry sc_status = status; break; } }
server = __SCNetworkConnectionRefreshServerPort(server, &sc_status); if (server == MACH_PORT_NULL) { // if server not available if (sc_status == BOOTSTRAP_UNKNOWN_SERVICE) { // if first retry attempt, wait for SCDynamicStore server if (retry == 0) { SCDynamicStoreRef store;
store = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkConnection connect"), NULL, NULL); if (store != NULL) { CFRelease(store); } }
// wait up to 2.5 seconds for the [SCNetworkConnection] server // to startup if ((retry += 50) < 2500) { usleep(50 * 1000); // sleep 50ms between attempts continue; } } break; } }
if (notify_port != MACH_PORT_NULL) { if (connectionPrivate->session_port != MACH_PORT_NULL) { CFMachPortContext context = { 0 , (void *)connectionPrivate , NULL , NULL , pppMPCopyDescription };
// request a notification when/if the server dies status = mach_port_request_notification(mach_task_self(), notify_port, MACH_NOTIFY_NO_SENDERS, 1, notify_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldNotify); if (status != KERN_SUCCESS) { SC_log(LOG_NOTICE, "mach_port_request_notification() failed: %s", mach_error_string(status)); mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1); sc_status = status; goto done; }
if (oldNotify != MACH_PORT_NULL) { SC_log(LOG_NOTICE, "oldNotify != MACH_PORT_NULL"); }
// create CFMachPort for SCNetworkConnection notification callback connectionPrivate->notify_port = _SC_CFMachPortCreateWithPort("SCNetworkConnection", notify_port, __SCNetworkConnectionMachCallBack, &context);
// we need to try a bit harder to acquire the initial status connectionPrivate->haveStatus = FALSE; } else { // with no server port, release the notification port we allocated __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!server)", notify_port); (void) mach_port_mod_refs (mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1); (void) mach_port_deallocate(mach_task_self(), notify_port); notify_port = MACH_PORT_NULL; } }
done :
// clean up
#ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION if (au_session != MACH_PORT_NULL) { (void)mach_port_deallocate(mach_task_self(), au_session); }#endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
if (dataRef != NULL) CFRelease(dataRef);
switch (sc_status) { case kSCStatusOK : __MACH_PORT_DEBUG(connectionPrivate->session_port != MACH_PORT_NULL, "*** __SCNetworkConnectionSessionPort session_port", connectionPrivate->session_port); __MACH_PORT_DEBUG(notify_port != MACH_PORT_NULL, "*** __SCNetworkConnectionSessionPort notify_port", notify_port); break; case BOOTSTRAP_UNKNOWN_SERVICE : SC_log((status == KERN_SUCCESS) ? LOG_NOTICE : LOG_ERR, "PPPController not available"); break; default : SC_log((status == KERN_SUCCESS) ? LOG_NOTICE : LOG_ERR, "pppcontroller_attach() failed: %s", SCErrorString(sc_status)); break; }
if (sc_status != kSCStatusOK) { _SCErrorSet(sc_status); }
return connectionPrivate->session_port;}
static Boolean__SCNetworkConnectionReconnect(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; mach_port_t port;
port = __SCNetworkConnectionSessionPort(connectionPrivate); return (port != MACH_PORT_NULL);}
static Boolean__SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; dispatch_group_t dispatchGroup = NULL; dispatch_queue_t dispatchQueue = NULL; Boolean ok = TRUE; CFArrayRef rlList = NULL;
// Before we fully tearing down our [old] notifications, make sure // we have retained any information that is needed to re-register the // [new] notifications.
pthread_mutex_lock(&connectionPrivate->lock);
// save and cancel [old] notifications if (connectionPrivate->rlList != NULL) { rlList = connectionPrivate->rlList; connectionPrivate->rlList = NULL; } if (connectionPrivate->rls != NULL) { CFRunLoopSourceInvalidate(connectionPrivate->rls); CFRelease(connectionPrivate->rls); connectionPrivate->rls = NULL; } if (connectionPrivate->dispatchSource != NULL) { dispatch_source_cancel(connectionPrivate->dispatchSource); connectionPrivate->dispatchSource = NULL; }
// make sure dispatchSource is cancelled before removing group/queue if (connectionPrivate->dispatchQueue != NULL) { // save dispatchQueue, release reference when we've queue'd blocks // complete, allow re-scheduling dispatchGroup = connectionPrivate->dispatchGroup; connectionPrivate->dispatchGroup = NULL; dispatchQueue = connectionPrivate->dispatchQueue; connectionPrivate->dispatchQueue = NULL;
// and take an extra reference for rescheduling dispatch_retain(dispatchQueue); }
connectionPrivate->scheduled = FALSE;
pthread_mutex_unlock(&connectionPrivate->lock);
if (dispatchGroup != NULL) { dispatch_group_notify(dispatchGroup, dispatchQueue, ^{ // release group/queue references dispatch_release(dispatchQueue); dispatch_release(dispatchGroup); // releases our connection reference }); }
// re-schedule if (rlList != NULL) { CFIndex i; CFIndex n;
n = CFArrayGetCount(rlList); for (i = 0; i < n; i += 3) { CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1); CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(rlList, i+2);
ok = SCNetworkConnectionScheduleWithRunLoop(connection, rl, rlMode); if (!ok) { if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE) { SC_log(LOG_NOTICE, "SCNetworkConnectionScheduleWithRunLoop() failed"); } goto done; } } } else if (dispatchQueue != NULL) { ok = SCNetworkConnectionSetDispatchQueue(connection, dispatchQueue); if (!ok) { if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE) { SC_log(LOG_NOTICE, "SCNetworkConnectionSetDispatchQueue() failed"); } goto done; } } else { ok = FALSE; }
done :
// cleanup if (rlList != NULL) { CFRelease(rlList); } if (dispatchQueue != NULL) { dispatch_release(dispatchQueue); }
if (!ok) { SC_log(LOG_NOTICE, "SCNetworkConnection server %s, notification not restored", (SCError() == BOOTSTRAP_UNKNOWN_SERVICE) ? "shutdown" : "failed"); }
return ok;}
static Boolean__SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection, const char *error_label, kern_return_t status, int *sc_status){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
if (status == KERN_SUCCESS) { return FALSE; }
if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) { // the server's gone and our session port's dead, remove the dead name right (void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port); } else { // we got an unexpected error, leave the [session] port alone SC_log(LOG_NOTICE, "%s: %s", error_label, mach_error_string(status)); } connectionPrivate->session_port = MACH_PORT_NULL; if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) { if (__SCNetworkConnectionReconnect(connection)) { return TRUE; } } *sc_status = status;
return FALSE;}
CFTypeIDSCNetworkConnectionGetTypeID(void) { pthread_once(&initialized, __SCNetworkConnectionInitialize); /* initialize runtime */ return __kSCNetworkConnectionTypeID;}
CFArrayRef /* of SCNetworkServiceRef's */SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set){ CFMutableArrayRef available; Boolean tempSet = FALSE;
if (set == NULL) { SCPreferencesRef prefs;
prefs = SCPreferencesCreate(NULL, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL); if (prefs != NULL) { set = SCNetworkSetCopyCurrent(prefs); CFRelease(prefs); } tempSet = TRUE; }
available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (set != NULL) { CFArrayRef services;
services = SCNetworkSetCopyServices(set); if (services != NULL) { CFIndex i; CFIndex n;
n = CFArrayGetCount(services); for (i = 0; i < n; i++) { SCNetworkInterfaceRef interface; CFStringRef interfaceType; SCNetworkServiceRef service;
service = CFArrayGetValueAtIndex(services, i); interface = SCNetworkServiceGetInterface(service); if (interface == NULL) { continue; }
interfaceType = SCNetworkInterfaceGetInterfaceType(interface); if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP) || CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) || CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec)) { CFArrayAppendValue(available, service); } }
CFRelease(services); } }
if (tempSet && (set != NULL)) { CFRelease(set); } return available;}
SCNetworkConnectionRefSCNetworkConnectionCreateWithService(CFAllocatorRef allocator, SCNetworkServiceRef service, SCNetworkConnectionCallBack callout, SCNetworkConnectionContext *context){ SCNetworkConnectionPrivateRef connectionPrivate;
if (!isA_SCNetworkService(service)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
if (__SCNetworkServiceIsPPTP(service)) { SC_log(LOG_INFO, "PPTP VPNs are no longer supported"); _SCErrorSet(kSCStatusConnectionIgnore); return FALSE; }
connectionPrivate = __SCNetworkConnectionCreatePrivate(allocator, service, callout, context); return (SCNetworkConnectionRef)connectionPrivate;}
SCNetworkConnectionRefSCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator, CFStringRef serviceID, SCNetworkConnectionCallBack callout, SCNetworkConnectionContext *context){ SCNetworkConnectionRef connection; SCNetworkServiceRef service;
if (!isA_CFString(serviceID)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
service = _SCNetworkServiceCopyActive(NULL, serviceID); if (service == NULL) { return NULL; }
connection = SCNetworkConnectionCreateWithService(allocator, service, callout, context); CFRelease(service);
return connection;}
SCNetworkConnectionRefSCNetworkConnectionCreate(CFAllocatorRef allocator, SCNetworkConnectionCallBack callout, SCNetworkConnectionContext *context){ SCNetworkConnectionPrivateRef connectionPrivate = __SCNetworkConnectionCreatePrivate(allocator, NULL, callout, context); return (SCNetworkConnectionRef)connectionPrivate;}
CFStringRefSCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; CFStringRef serviceID;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
if (connectionPrivate->service == NULL) { _SCErrorSet(kSCStatusConnectionNoService); return NULL; }
serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); return CFRetain(serviceID);}
BooleanSCNetworkConnectionSetClientInfo(SCNetworkConnectionRef connection, mach_port_t client_audit_session, uid_t client_uid, gid_t client_gid, pid_t client_pid){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
// save client audit session port if (connectionPrivate->client_audit_session != MACH_PORT_NULL) { mach_port_deallocate(mach_task_self(), connectionPrivate->client_audit_session); connectionPrivate->client_audit_session = MACH_PORT_NULL; } connectionPrivate->client_audit_session = client_audit_session; if (connectionPrivate->client_audit_session != MACH_PORT_NULL) { mach_port_mod_refs(mach_task_self(), connectionPrivate->client_audit_session, MACH_PORT_RIGHT_SEND, +1); }
// save client UID, GID, and PID connectionPrivate->client_uid = client_uid; connectionPrivate->client_gid = client_gid; connectionPrivate->client_pid = client_pid;
return TRUE;}
BooleanSCNetworkConnectionSetClientAuditInfo(SCNetworkConnectionRef connection, audit_token_t client_audit_token, mach_port_t audit_session, mach_port_t bootstrap_port, pid_t client_pid, const uuid_t uuid, const char *bundle_id){ const audit_token_t null_audit = KERNEL_AUDIT_TOKEN_VALUE; SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; gid_t gid = 0; pid_t pid = 0; uid_t uid = 0;
if (memcmp(&client_audit_token, &null_audit, sizeof(client_audit_token))) {#if TARGET_OS_IPHONE audit_token_to_au32(client_audit_token, NULL, &uid, &gid, NULL, NULL, &pid, NULL, NULL);#else // TARGET_OS_IPHONE uid = audit_token_to_euid(client_audit_token); gid = audit_token_to_egid(client_audit_token); pid = audit_token_to_pid(client_audit_token);#endif // TARGET_OS_IPHONE } else { pid = client_pid; }
if (!SCNetworkConnectionSetClientInfo(connection, audit_session, uid, gid, pid)) { return FALSE; }
if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) { mach_port_deallocate(mach_task_self(), connectionPrivate->client_bootstrap_port); connectionPrivate->client_bootstrap_port = MACH_PORT_NULL; }
connectionPrivate->client_bootstrap_port = bootstrap_port; if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) { mach_port_mod_refs(mach_task_self(), connectionPrivate->client_bootstrap_port, MACH_PORT_RIGHT_SEND, +1); }
memcpy(&connectionPrivate->client_audit_token, &client_audit_token, sizeof(connectionPrivate->client_audit_token));
if (uuid != NULL && !uuid_is_null(uuid)) { uuid_copy(connectionPrivate->client_uuid, uuid); }
if (connectionPrivate->client_bundle_id != NULL) { CFRelease(connectionPrivate->client_bundle_id); connectionPrivate->client_bundle_id = NULL; }
if (bundle_id != NULL) { connectionPrivate->client_bundle_id = CFStringCreateWithCString(kCFAllocatorDefault, bundle_id, kCFStringEncodingUTF8); }
return TRUE;}
CFDictionaryRefSCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; xmlDataOut_t data = NULL; mach_msg_type_number_t datalen = 0; int sc_status = kSCStatusFailed; mach_port_t session_port; CFPropertyListRef statistics = NULL; kern_return_t status;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
pthread_mutex_lock(&connectionPrivate->lock);
#if !TARGET_OS_SIMULATOR if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { __block xpc_object_t xstats = NULL; ne_session_t ne_session = connectionPrivate->ne_session;
ne_session_retain(ne_session); pthread_mutex_unlock(&connectionPrivate->lock);
dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0); ne_session_get_info(ne_session, NESessionInfoTypeStatistics, __SCNetworkConnectionQueue(), ^(xpc_object_t result) { if (result != NULL) { xstats = xpc_retain(result); } ne_session_release(ne_session); dispatch_semaphore_signal(ne_sema); }); dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER); dispatch_release(ne_sema);
if (xstats != NULL) { statistics = _CFXPCCreateCFObjectFromXPCObject(xstats); xpc_release(xstats); } else { _SCErrorSet(kSCStatusFailed); }
return statistics; }#endif /* !TARGET_OS_SIMULATOR */
retry :
session_port = __SCNetworkConnectionSessionPort(connectionPrivate); if (session_port == MACH_PORT_NULL) { goto done; }
status = pppcontroller_copystatistics(session_port, &data, &datalen, &sc_status); if (__SCNetworkConnectionNeedsRetry(connection, "SCNetworkConnectionCopyStatistics()", status, &sc_status)) { goto retry; }
if (data != NULL) { if (!_SCUnserialize(&statistics, NULL, data, datalen)) { if (sc_status != kSCStatusOK) sc_status = SCError(); } if ((sc_status == kSCStatusOK) && !isA_CFDictionary(statistics)) { sc_status = kSCStatusFailed; } }
if (sc_status != kSCStatusOK) { if (statistics != NULL) { CFRelease(statistics); statistics = NULL; } _SCErrorSet(sc_status); }
done :
pthread_mutex_unlock(&connectionPrivate->lock); return statistics;}
SCNetworkServiceRefSCNetworkConnectionGetService(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
return connectionPrivate->service;}
SCNetworkConnectionStatusSCNetworkConnectionGetStatus(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; SCNetworkConnectionStatus nc_status = kSCNetworkConnectionInvalid; int retry = 0; int sc_status = kSCStatusFailed; mach_port_t session_port; kern_return_t status; CFStringRef serviceID;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return kSCNetworkConnectionInvalid; }
if (connectionPrivate->service == NULL) { _SCErrorSet(kSCStatusConnectionNoService); return kSCNetworkConnectionInvalid; }
// skip retry and return immediately if we know no service is to be found. serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); if (CFStringGetLength(serviceID) == 0) { _SCErrorSet(kSCStatusConnectionNoService); return kSCNetworkConnectionInvalid; }
pthread_mutex_lock(&connectionPrivate->lock);
#if !TARGET_OS_SIMULATOR if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { __block ne_session_status_t ne_status; ne_session_t ne_session = connectionPrivate->ne_session;
ne_session_retain(ne_session); pthread_mutex_unlock(&connectionPrivate->lock);
dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0); ne_session_get_status(ne_session, __SCNetworkConnectionQueue(), ^(ne_session_status_t status) { ne_status = status; ne_session_release(ne_session); dispatch_semaphore_signal(ne_sema); }); dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER); dispatch_release(ne_sema);
return SCNetworkConnectionGetStatusFromNEStatus(ne_status); }#endif /* !TARGET_OS_SIMULATOR */
retry :
session_port = __SCNetworkConnectionSessionPort(connectionPrivate); if (session_port == MACH_PORT_NULL) { nc_status = kSCNetworkConnectionInvalid; goto done; }
status = pppcontroller_getstatus(session_port, &nc_status, &sc_status); if (__SCNetworkConnectionNeedsRetry(connection, "SCNetworkConnectionGetStatus()", status, &sc_status)) { goto retry; }
// wait up to 250 ms for the network service to become available if (!connectionPrivate->haveStatus && (sc_status == kSCStatusConnectionNoService) && ((retry += 10) < 250)) { usleep(10 * 1000); // sleep 10ms between attempts goto retry; }
if (sc_status == kSCStatusOK) { connectionPrivate->haveStatus = TRUE; } else { _SCErrorSet(sc_status); nc_status = kSCNetworkConnectionInvalid; }
done :
pthread_mutex_unlock(&connectionPrivate->lock); return nc_status;}
CFDictionaryRefSCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; xmlDataOut_t data = NULL; mach_msg_type_number_t datalen = 0; CFPropertyListRef extstatus = NULL; int retry = 0; int sc_status = kSCStatusFailed; mach_port_t session_port; kern_return_t status; CFStringRef serviceID;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
if (connectionPrivate->service == NULL) { _SCErrorSet(kSCStatusConnectionNoService); return NULL; }
// skip retry and return immediately if we know no service is to be found. serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); if (CFStringGetLength(serviceID) == 0) { _SCErrorSet(kSCStatusConnectionNoService); return NULL; }
pthread_mutex_lock(&connectionPrivate->lock);
#if !TARGET_OS_SIMULATOR if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { __block CFDictionaryRef statusDictionary = NULL; ne_session_t ne_session = connectionPrivate->ne_session;
ne_session_retain(ne_session); pthread_mutex_unlock(&connectionPrivate->lock);
dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0); ne_session_get_info(ne_session, NESessionInfoTypeExtendedStatus, __SCNetworkConnectionQueue(), ^(xpc_object_t extended_status) { if (extended_status != NULL) { statusDictionary = _CFXPCCreateCFObjectFromXPCObject(extended_status); ne_session_release(ne_session); dispatch_semaphore_signal(ne_sema); } else { ne_session_get_status(ne_session, __SCNetworkConnectionQueue(), ^(ne_session_status_t ne_status) { SCNetworkConnectionStatus status = SCNetworkConnectionGetStatusFromNEStatus(ne_status); if (status != kSCNetworkConnectionInvalid) { CFStringRef keys[1] = { kSCNetworkConnectionStatus }; CFNumberRef values[1] = { NULL }; values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &status); statusDictionary = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, sizeof(values) / sizeof(values[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease(values[0]); } ne_session_release(ne_session); dispatch_semaphore_signal(ne_sema); }); } }); dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER); dispatch_release(ne_sema);
if (statusDictionary != NULL) { extstatus = (CFPropertyListRef)statusDictionary; } else { _SCErrorSet(kSCStatusFailed); }
return extstatus; }#endif
retry :
session_port = __SCNetworkConnectionSessionPort(connectionPrivate); if (session_port == MACH_PORT_NULL) { goto done; }
status = pppcontroller_copyextendedstatus(session_port, &data, &datalen, &sc_status); if (__SCNetworkConnectionNeedsRetry(connection, "SCNetworkConnectionCopyExtendedStatus()", status, &sc_status)) { goto retry; }
if (data != NULL) { if (!_SCUnserialize(&extstatus, NULL, data, datalen)) { if (sc_status != kSCStatusOK) sc_status = SCError(); } if ((sc_status == kSCStatusOK) && !isA_CFDictionary(extstatus)) { sc_status = kSCStatusFailed; } }
// wait up to 250 ms for the network service to become available if (!connectionPrivate->haveStatus && (sc_status == kSCStatusConnectionNoService) && ((retry += 10) < 250)) { usleep(10 * 1000); // sleep 10ms between attempts goto retry; }
if (sc_status == kSCStatusOK) { connectionPrivate->haveStatus = TRUE; } else { if (extstatus != NULL) { CFRelease(extstatus); extstatus = NULL; } _SCErrorSet(sc_status); }
done :
pthread_mutex_unlock(&connectionPrivate->lock); return extstatus;}
static void_SCNetworkConnectionMergeDictionaries (const void *key, const void *value, void *context){ /* Add value only if not present */ CFDictionaryAddValue((CFMutableDictionaryRef)context, (CFStringRef)key, (CFTypeRef)value);}
BooleanSCNetworkConnectionStart(SCNetworkConnectionRef connection, CFDictionaryRef userOptions, Boolean linger){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; CFDataRef dataref = NULL; void *data = NULL; CFIndex datalen = 0; Boolean ok = FALSE; int sc_status = kSCStatusFailed; mach_port_t session_port; kern_return_t status;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
if ((userOptions != NULL) && !isA_CFDictionary(userOptions)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
if (userOptions == NULL) { userOptions = connectionPrivate->on_demand_user_options; } else if (connectionPrivate->on_demand_user_options != NULL) { CFDictionaryRef localUserOptions = NULL;
localUserOptions = CFDictionaryCreateMutableCopy(NULL, 0, userOptions); if (localUserOptions) { CFDictionaryApplyFunction(connectionPrivate->on_demand_user_options, _SCNetworkConnectionMergeDictionaries, (void *)localUserOptions); CFRelease(connectionPrivate->on_demand_user_options); userOptions = connectionPrivate->on_demand_user_options = localUserOptions; } }
if (debug > 0) { CFMutableDictionaryRef mdict = NULL;
SC_log(LOG_INFO, "SCNetworkConnectionStart (%p)", connectionPrivate);
if (userOptions != NULL) { CFDictionaryRef dict; CFStringRef encryption; CFMutableDictionaryRef new_dict;
/* special code to remove secret information */ mdict = CFDictionaryCreateMutableCopy(NULL, 0, userOptions);
dict = CFDictionaryGetValue(mdict, kSCEntNetPPP); if (isA_CFDictionary(dict)) { encryption = CFDictionaryGetValue(dict, kSCPropNetPPPAuthPasswordEncryption); if (!isA_CFString(encryption) || !CFEqual(encryption, kSCValNetPPPAuthPasswordEncryptionKeychain)) { new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict); CFDictionaryReplaceValue(new_dict, kSCPropNetPPPAuthPassword, CFSTR("******")); CFDictionarySetValue(mdict, kSCEntNetPPP, new_dict); CFRelease(new_dict); } }
dict = CFDictionaryGetValue(mdict, kSCEntNetL2TP); if (isA_CFDictionary(dict)) { encryption = CFDictionaryGetValue(dict, kSCPropNetL2TPIPSecSharedSecretEncryption); if (!isA_CFString(encryption) || !CFEqual(encryption, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain)) { new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict); CFDictionaryReplaceValue(new_dict, kSCPropNetL2TPIPSecSharedSecret, CFSTR("******")); CFDictionarySetValue(mdict, kSCEntNetL2TP, new_dict); CFRelease(new_dict); } }
dict = CFDictionaryGetValue(mdict, kSCEntNetIPSec); if (isA_CFDictionary(dict)) { encryption = CFDictionaryGetValue(dict, kSCPropNetIPSecSharedSecretEncryption); if (!isA_CFString(encryption) || !CFEqual(encryption, kSCValNetIPSecSharedSecretEncryptionKeychain)) { new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict); CFDictionaryReplaceValue(new_dict, kSCPropNetIPSecSharedSecret, CFSTR("******")); CFDictionarySetValue(mdict, kSCEntNetIPSec, new_dict); CFRelease(new_dict); } } }
SC_log(LOG_INFO, "User options: %@", mdict); if (mdict != NULL) CFRelease(mdict); }
pthread_mutex_lock(&connectionPrivate->lock);
/* Clear out any cached flow divert token parameters */ if (connectionPrivate->flow_divert_token_params != NULL) { CFRelease(connectionPrivate->flow_divert_token_params); connectionPrivate->flow_divert_token_params = NULL; }
#if !TARGET_OS_SIMULATOR if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { xpc_object_t xuser_options = NULL;
if (userOptions != NULL) { xuser_options = _CFXPCCreateXPCObjectFromCFObject(userOptions); }
if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) {#if NE_SESSION_VERSION > 2 ne_session_start_on_behalf_of(connectionPrivate->ne_session, xuser_options, connectionPrivate->client_bootstrap_port, connectionPrivate->client_audit_session, connectionPrivate->client_uid, connectionPrivate->client_gid, connectionPrivate->client_pid);#else ne_session_start_on_behalf_of(connectionPrivate->ne_session, xuser_options, connectionPrivate->client_bootstrap_port, connectionPrivate->client_audit_session, connectionPrivate->client_uid, connectionPrivate->client_gid);#endif } else { ne_session_start_with_options(connectionPrivate->ne_session, xuser_options); }
/* make sure the xpc_message goes through */ ne_session_send_barrier(connectionPrivate->ne_session);
if (xuser_options != NULL) { xpc_release(xuser_options); }
ok = TRUE; goto done; }#endif /* !TARGET_OS_SIMULATOR */
if (userOptions && !_SCSerialize(userOptions, &dataref, &data, &datalen)) { goto done; }
retry :
session_port = __SCNetworkConnectionSessionPort(connectionPrivate); if (session_port == MACH_PORT_NULL) { if (dataref) CFRelease(dataref); goto done; }
status = pppcontroller_start(session_port, data, (mach_msg_type_number_t)datalen, linger, &sc_status); if (__SCNetworkConnectionNeedsRetry(connection, "SCNetworkConnectionStart()", status, &sc_status)) { goto retry; }
if (dataref) CFRelease(dataref);
if (debug > 0) { SC_log(LOG_INFO, "SCNetworkConnectionStart (%p), return: %d", connectionPrivate, sc_status); }
if (sc_status != kSCStatusOK) { _SCErrorSet(sc_status); goto done; }
/* connection is now started */ ok = TRUE;
done: pthread_mutex_unlock(&connectionPrivate->lock); return ok;}
BooleanSCNetworkConnectionStop(SCNetworkConnectionRef connection, Boolean forceDisconnect){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; Boolean ok = FALSE; int sc_status = kSCStatusFailed; mach_port_t session_port; kern_return_t status;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
if (debug > 0) { SC_log(LOG_INFO, "SCNetworkConnectionStop (%p)", connectionPrivate); }
pthread_mutex_lock(&connectionPrivate->lock);
#if !TARGET_OS_SIMULATOR if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { ne_session_stop(connectionPrivate->ne_session); /* make sure the xpc_message goes through */ ne_session_send_barrier(connectionPrivate->ne_session); ok = TRUE; goto done; }#endif /* !TARGET_OS_SIMULATOR */
retry :
session_port = __SCNetworkConnectionSessionPort(connectionPrivate); if (session_port == MACH_PORT_NULL) { goto done; }
status = pppcontroller_stop(session_port, forceDisconnect, &sc_status); if (__SCNetworkConnectionNeedsRetry(connection, "SCNetworkConnectionStop()", status, &sc_status)) { goto retry; }
if (debug > 0) { SC_log(LOG_INFO, "SCNetworkConnectionStop (%p), return: %d", connectionPrivate, sc_status); }
if (sc_status != kSCStatusOK) { _SCErrorSet(sc_status); goto done; }
/* connection is now disconnecting */ ok = TRUE;
done :
pthread_mutex_unlock(&connectionPrivate->lock); return ok;}
BooleanSCNetworkConnectionSuspend(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; Boolean ok = FALSE; int sc_status = kSCStatusFailed; mach_port_t session_port; kern_return_t status;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
if (debug > 0) { SC_log(LOG_INFO, "SCNetworkConnectionSuspend (%p)", connectionPrivate); }
pthread_mutex_lock(&connectionPrivate->lock);
#if !!TARGET_OS_SIMULATOR if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { /* Suspend only applies to PPPSerial and PPPoE */ ok = TRUE; goto done; }#endif /* !TARGET_OS_SIMULATOR */
retry :
session_port = __SCNetworkConnectionSessionPort(connectionPrivate); if (session_port == MACH_PORT_NULL) { goto done; }
status = pppcontroller_suspend(session_port, &sc_status); if (__SCNetworkConnectionNeedsRetry(connection, "SCNetworkConnectionSuspend()", status, &sc_status)) { goto retry; }
if (debug > 0) { SC_log(LOG_INFO, "SCNetworkConnectionSuspend (%p), return: %d", connectionPrivate, sc_status); }
if (sc_status != kSCStatusOK) { _SCErrorSet(sc_status); goto done; }
/* connection is now suspended */ ok = TRUE;
done :
pthread_mutex_unlock(&connectionPrivate->lock); return ok;}
BooleanSCNetworkConnectionResume(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; Boolean ok = FALSE; int sc_status = kSCStatusFailed; mach_port_t session_port; kern_return_t status;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
if (debug > 0) { SC_log(LOG_INFO, "SCNetworkConnectionResume (%p)", connectionPrivate); }
pthread_mutex_lock(&connectionPrivate->lock);
#if !TARGET_OS_SIMULATOR if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { /* Resume only applies to PPPSerial and PPPoE */ ok = TRUE; goto done; }#endif /* !TARGET_OS_SIMULATOR */
retry :
session_port = __SCNetworkConnectionSessionPort(connectionPrivate); if (session_port == MACH_PORT_NULL) { goto done; }
status = pppcontroller_resume(session_port, &sc_status); if (__SCNetworkConnectionNeedsRetry(connection, "SCNetworkConnectionResume()", status, &sc_status)) { goto retry; }
if (debug > 0) { SC_log(LOG_INFO, "SCNetworkConnectionResume (%p), return: %d", connectionPrivate, sc_status); }
if (sc_status != kSCStatusOK) { _SCErrorSet(sc_status); goto done; }
/* connection is now resume */ ok = TRUE;
done :
pthread_mutex_unlock(&connectionPrivate->lock); return ok;}
#if !TARGET_OS_SIMULATORBooleanSCNetworkConnectionRefreshOnDemandState(__unused SCNetworkConnectionRef connection){ return FALSE;}#endif /* !TARGET_OS_SIMULATOR */
CFDictionaryRefSCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; xmlDataOut_t data = NULL; mach_msg_type_number_t datalen = 0; int sc_status = kSCStatusFailed; mach_port_t session_port; kern_return_t status; CFPropertyListRef userOptions = NULL;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return NULL; }
pthread_mutex_lock(&connectionPrivate->lock);
#if !TARGET_OS_SIMULATOR if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { __block xpc_object_t config = NULL; ne_session_t ne_session = connectionPrivate->ne_session;
ne_session_retain(ne_session); pthread_mutex_unlock(&connectionPrivate->lock);
dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0); ne_session_get_info(ne_session, NESessionInfoTypeConfiguration, __SCNetworkConnectionQueue(), ^(xpc_object_t result) { if (result != NULL) { config = xpc_retain(result); } ne_session_release(ne_session); dispatch_semaphore_signal(ne_sema); }); dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER); dispatch_release(ne_sema);
if (config != NULL) { xpc_object_t xoptions = xpc_dictionary_get_value(config, NESMSessionLegacyUserConfigurationKey); if (xoptions != NULL) { userOptions = _CFXPCCreateCFObjectFromXPCObject(xoptions); } xpc_release(config); } return userOptions; }#endif /* !TARGET_OS_SIMULATOR */
retry :
session_port = __SCNetworkConnectionSessionPort(connectionPrivate); if (session_port == MACH_PORT_NULL) { goto done; }
status = pppcontroller_copyuseroptions(session_port, &data, &datalen, &sc_status); if (__SCNetworkConnectionNeedsRetry(connection, "SCNetworkConnectionCopyUserOptions()", status, &sc_status)) { goto retry; }
if (data != NULL) { if (!_SCUnserialize(&userOptions, NULL, data, datalen)) { if (sc_status != kSCStatusOK) sc_status = SCError(); } if ((sc_status == kSCStatusOK) && (userOptions != NULL) && !isA_CFDictionary(userOptions)) { sc_status = kSCStatusFailed; } }
if (sc_status == kSCStatusOK) { if (userOptions == NULL) { // if no user options, return an empty dictionary userOptions = CFDictionaryCreate(NULL, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } } else { if (userOptions) { CFRelease(userOptions); userOptions = NULL; } _SCErrorSet(sc_status); }
done :
pthread_mutex_unlock(&connectionPrivate->lock); return userOptions;}
static Boolean__SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection, CFRunLoopRef runLoop, CFStringRef runLoopMode, dispatch_queue_t queue){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; Boolean ok = FALSE; int sc_status = kSCStatusFailed; mach_port_t session_port; kern_return_t status;
pthread_mutex_lock(&connectionPrivate->lock);
if (connectionPrivate->rlsFunction == NULL) { _SCErrorSet(kSCStatusInvalidArgument); goto done; }
if ((connectionPrivate->dispatchQueue != NULL) || // if we are already scheduled on a dispatch queue ((queue != NULL) && connectionPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop _SCErrorSet(kSCStatusInvalidArgument); goto done; }
if (!connectionPrivate->scheduled) {
retry :
if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { session_port = __SCNetworkConnectionSessionPort(connectionPrivate); if (session_port == MACH_PORT_NULL) { goto done; }
status = pppcontroller_notification(session_port, 1, &sc_status); if (__SCNetworkConnectionNeedsRetry(connection, "__SCNetworkConnectionScheduleWithRunLoop()", status, &sc_status)) { goto retry; }
if (sc_status != kSCStatusOK) { _SCErrorSet(sc_status); goto done; }
if (runLoop != NULL) { connectionPrivate->rls = CFMachPortCreateRunLoopSource(NULL, connectionPrivate->notify_port, 0); connectionPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); } } else if (runLoop != NULL) { CFRunLoopSourceContext rlsContext = { 0, // version (void *)connection, // info NULL, // retain NULL, // release NULL, // copy description NULL, // equal NULL, // hash NULL, // schedule NULL, // cancel __SCNetworkConnectionCallBack, // perform };
connectionPrivate->rls = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &rlsContext); connectionPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); }
connectionPrivate->scheduled = TRUE; }
if (queue != NULL) { // retain the dispatch queue connectionPrivate->dispatchQueue = queue; dispatch_retain(connectionPrivate->dispatchQueue);
if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { dispatch_group_t group = NULL; mach_port_t mp; dispatch_source_t source;
// // We've taken a reference to the caller's dispatch_queue and we // want to hold on to that reference until we've processed any/all // notifications. To facilitate this we create a group, dispatch // any notification blocks via that group, and when the caller // has told us to stop the notifications (unschedule) we wait for // the group to empty and use the group's finalizer to release // our reference to the SCNetworkConnection. // group = dispatch_group_create(); connectionPrivate->dispatchGroup = group; CFRetain(connection); dispatch_set_context(connectionPrivate->dispatchGroup, (void *)connection); dispatch_set_finalizer_f(connectionPrivate->dispatchGroup, (dispatch_function_t)CFRelease);
mp = CFMachPortGetPort(connectionPrivate->notify_port); source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue); if (source == NULL) { SC_log(LOG_NOTICE, "dispatch_source_create() failed"); _SCErrorSet(kSCStatusFailed); goto done; }
// have our dispatch source hold a reference to the notification CFMachPort CFRetain(connectionPrivate->notify_port); dispatch_set_context(source, (void *)connectionPrivate->notify_port); dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease);
dispatch_source_set_event_handler(source, ^{ kern_return_t kr; typedef union { u_int8_t buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE]; mach_msg_empty_rcv_t msg; mach_no_senders_notification_t no_senders; } *notify_message_t; CFMachPortRef notify_port; notify_message_t notify_msg;
notify_msg = (notify_message_t)malloc(sizeof(*notify_msg));
kr = mach_msg(¬ify_msg->msg.header, // msg MACH_RCV_MSG, // options 0, // send_size sizeof(*notify_msg), // rcv_size mp, // rcv_name MACH_MSG_TIMEOUT_NONE, // timeout MACH_PORT_NULL); // notify if (kr != KERN_SUCCESS) { SC_log(LOG_NOTICE, "SCDynamicStore notification handler, kr=0x%x", kr); free(notify_msg); return; }
CFRetain(connection); notify_port = dispatch_get_context(source);
dispatch_group_async(group, queue, ^{ __SCNetworkConnectionMachCallBack(notify_port, (void *)notify_msg, sizeof(*notify_msg), (void *)connection); free(notify_msg); CFRelease(connection); }); });
dispatch_source_set_cancel_handler(source, ^{ dispatch_release(source); });
connectionPrivate->dispatchSource = source; dispatch_resume(source); } } else { if (!_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) { /* * if we do not already have notifications scheduled with * this runLoop / runLoopMode */ CFRunLoopAddSource(runLoop, connectionPrivate->rls, runLoopMode); }
_SC_schedule(connection, runLoop, runLoopMode, connectionPrivate->rlList); }
#if !TARGET_OS_SIMULATOR if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { CFRetain(connection); ne_session_set_event_handler(connectionPrivate->ne_session, __SCNetworkConnectionQueue(), ^(ne_session_event_t event, void *event_data) { #pragma unused(event_data) if (event == NESessionEventStatusChanged) { CFRetain(connection); /* Released in __SCNetworkConnectionCallBack */ pthread_mutex_lock(&connectionPrivate->lock); if (connectionPrivate->rls != NULL) { CFRunLoopSourceSignal(connectionPrivate->rls); _SC_signalRunLoop(connection, connectionPrivate->rls, connectionPrivate->rlList); } else if (connectionPrivate->dispatchQueue != NULL) { dispatch_async(connectionPrivate->dispatchQueue, ^{ __SCNetworkConnectionCallBack((void *)connection); }); } pthread_mutex_unlock(&connectionPrivate->lock); } else if (event == NESessionEventCanceled) { CFRelease(connection); } }); }#endif /* !TARGET_OS_SIMULATOR */
ok = TRUE;
done :
pthread_mutex_unlock(&connectionPrivate->lock); return ok;}
static Boolean__SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection, CFRunLoopRef runLoop, CFStringRef runLoopMode, dispatch_queue_t queue){#pragma unused(queue) SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; dispatch_group_t drainGroup = NULL; dispatch_queue_t drainQueue = NULL; int sc_status = kSCStatusFailed; CFIndex n = 0; Boolean ok = FALSE; kern_return_t status;
// hold a reference while we unschedule CFRetain(connection);
pthread_mutex_lock(&connectionPrivate->lock);
if ((runLoop != NULL) && !connectionPrivate->scheduled) { // if we should be scheduled (but are not) _SCErrorSet(kSCStatusInvalidArgument); goto done; }
if (((runLoop == NULL) && (connectionPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not) ((runLoop != NULL) && (connectionPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue) _SCErrorSet(kSCStatusInvalidArgument); goto done; }
if (connectionPrivate->dispatchQueue != NULL) { if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { // cancel dispatchSource if (connectionPrivate->dispatchSource != NULL) { dispatch_source_cancel(connectionPrivate->dispatchSource); connectionPrivate->dispatchSource = NULL; }
// save dispatchQueue/group, release reference when all queue'd blocks // have been processed, allow re-scheduling drainGroup = connectionPrivate->dispatchGroup; connectionPrivate->dispatchGroup = NULL; drainQueue = connectionPrivate->dispatchQueue; connectionPrivate->dispatchQueue = NULL; } else { dispatch_release(connectionPrivate->dispatchQueue); connectionPrivate->dispatchQueue = NULL; } } else { if (!_SC_unschedule(connection, runLoop, runLoopMode, connectionPrivate->rlList, FALSE)) { // if not currently scheduled on this runLoop / runLoopMode _SCErrorSet(kSCStatusFailed); goto done; }
n = CFArrayGetCount(connectionPrivate->rlList); if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) { /* * if we are no longer scheduled to receive notifications for * this runLoop / runLoopMode */ CFRunLoopRemoveSource(runLoop, connectionPrivate->rls, runLoopMode);
if (n == 0) { // if *all* notifications have been unscheduled CFRelease(connectionPrivate->rlList); connectionPrivate->rlList = NULL; CFRunLoopSourceInvalidate(connectionPrivate->rls); CFRelease(connectionPrivate->rls); connectionPrivate->rls = NULL; } } }
if (n == 0) { // if *all* notifications have been unscheduled connectionPrivate->scheduled = FALSE;
if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) {#if !TARGET_OS_SIMULATOR ne_session_cancel(connectionPrivate->ne_session);#endif /* !TARGET_OS_SIMULATOR */ } else { mach_port_t session_port = __SCNetworkConnectionSessionPort(connectionPrivate); if (session_port == MACH_PORT_NULL) { goto done; }
status = pppcontroller_notification(session_port, 0, &sc_status); if (__SCNetworkConnectionNeedsRetry(connection, "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()", status, &sc_status)) { sc_status = kSCStatusOK; status = KERN_SUCCESS; }
if ((status != KERN_SUCCESS) || (sc_status != kSCStatusOK)) { _SCErrorSet(sc_status); goto done; } } }
ok = TRUE;
done :
pthread_mutex_unlock(&connectionPrivate->lock);
if (drainGroup != NULL) { dispatch_group_notify(drainGroup, drainQueue, ^{ // release group/queue references dispatch_release(drainQueue); dispatch_release(drainGroup); // releases our connection reference }); }
// release our reference CFRelease(connection);
return ok;}
BooleanSCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection, CFRunLoopRef runLoop, CFStringRef runLoopMode){ if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
return __SCNetworkConnectionScheduleWithRunLoop(connection, runLoop, runLoopMode, NULL);}
BooleanSCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection, CFRunLoopRef runLoop, CFStringRef runLoopMode){ if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
return __SCNetworkConnectionUnscheduleFromRunLoop(connection, runLoop, runLoopMode, NULL);}
BooleanSCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection, dispatch_queue_t queue){ Boolean ok = FALSE;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
if (queue != NULL) { ok = __SCNetworkConnectionScheduleWithRunLoop(connection, NULL, NULL, queue); } else { ok = __SCNetworkConnectionUnscheduleFromRunLoop(connection, NULL, NULL, NULL); }
return ok;}
/* Requires having called SCNetworkConnectionSelectServiceWithOptions previously */BooleanSCNetworkConnectionIsOnDemandSuspended(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
if (connectionPrivate->on_demand_info != NULL) { uint32_t isSuspended = 0; CFNumberRef num = NULL;
num = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCPropNetVPNOnDemandSuspended); if (isA_CFNumber(num) && CFNumberGetValue(num, kCFNumberSInt32Type, &isSuspended) && (isSuspended != 0)) { return TRUE; } }
_SCErrorSet(kSCStatusOK); return FALSE;}
BooleanSCNetworkConnectionTriggerOnDemandIfNeeded (CFStringRef hostName, Boolean afterDNSFail, int timeout, int trafficClass){#if !TARGET_OS_SIMULATOR __block Boolean triggeredOnDemand = FALSE; struct proc_uniqidentifierinfo procu; void *policy_match = NULL; char *hostname = NULL; pid_t pid = getpid(); uid_t uid = geteuid();
/* Require hostName, require non-root user */ if (hostName == NULL || geteuid() == 0) { goto done; }
hostname = _SC_cfstring_to_cstring(hostName, NULL, 0, kCFStringEncodingUTF8);
if (proc_pidinfo(pid, PROC_PIDUNIQIDENTIFIERINFO, 1, &procu, sizeof(procu)) != sizeof(procu)) { goto done; }
policy_match = ne_session_copy_policy_match(hostname, NULL, NULL, procu.p_uuid, procu.p_uuid, pid, uid, 0, trafficClass);
NEPolicyServiceActionType action_type = ne_session_policy_match_get_service_action(policy_match); if (action_type == NESessionPolicyActionTrigger || (afterDNSFail && action_type == NESessionPolicyActionTriggerIfNeeded)) { uuid_t config_id; if (ne_session_policy_match_get_service(policy_match, config_id)) { xpc_object_t start_options = xpc_dictionary_create(NULL, NULL, 0); if (start_options != NULL) { xpc_dictionary_set_bool(start_options, NESessionStartOptionIsOnDemandKey, true); xpc_dictionary_set_string(start_options, NESessionStartOptionMatchHostnameKey, hostname);
ne_session_t new_session = ne_session_create(config_id, ne_session_policy_match_get_service_type(policy_match)); if (new_session != NULL) { dispatch_semaphore_t wait_for_session = dispatch_semaphore_create(0); dispatch_retain(wait_for_session); xpc_retain(start_options); ne_session_get_status(new_session, __SCNetworkConnectionQueue(), ^(ne_session_status_t status) { if (status == NESessionStatusDisconnected) { dispatch_retain(wait_for_session); ne_session_set_event_handler(new_session, __SCNetworkConnectionQueue(), ^(ne_session_event_t event, void *event_data) {#pragma unused(event_data) if (event == NESessionEventStatusChanged) { dispatch_retain(wait_for_session); ne_session_get_status(new_session, __SCNetworkConnectionQueue(), ^(ne_session_status_t new_status) { if (new_status != NESessionStatusConnecting) { if (status == NESessionStatusConnected) { triggeredOnDemand = TRUE; } ne_session_cancel(new_session); } dispatch_release(wait_for_session); }); } else if (event == NESessionEventCanceled) { dispatch_semaphore_signal(wait_for_session); dispatch_release(wait_for_session); } }); ne_session_start_with_options(new_session, start_options); } else { dispatch_semaphore_signal(wait_for_session); } dispatch_release(wait_for_session); xpc_release(start_options); }); dispatch_semaphore_wait(wait_for_session, timeout ? dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout * NSEC_PER_SEC) : DISPATCH_TIME_FOREVER); dispatch_release(wait_for_session); ne_session_release(new_session); }
xpc_release(start_options); } } }done: if (hostname) { CFAllocatorDeallocate(NULL, hostname); }
if (policy_match) { free(policy_match); }
return triggeredOnDemand;#else#pragma unused(hostName, afterDNSFail, timeout, trafficClass) return FALSE;#endif}
BooleanSCNetworkConnectionCopyOnDemandInfo(SCNetworkConnectionRef connection, CFStringRef *onDemandRemoteAddress, SCNetworkConnectionStatus *onDemandConnectionStatus){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
if (connectionPrivate->service == NULL) { _SCErrorSet(kSCStatusConnectionNoService); return FALSE; }
if (onDemandRemoteAddress != NULL) { *onDemandRemoteAddress = NULL; }
if (onDemandConnectionStatus != NULL) { *onDemandConnectionStatus = kSCNetworkConnectionInvalid; }
if (connectionPrivate->on_demand_info != NULL) { if (onDemandRemoteAddress != NULL) { CFStringRef address = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandRemoteAddress); if (isA_CFString(address)) { *onDemandRemoteAddress = address; CFRetain(*onDemandRemoteAddress); } }
if (onDemandConnectionStatus != NULL) { int num; CFNumberRef status_num = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandStatus); if (isA_CFNumber(status_num) && CFNumberGetValue(status_num, kCFNumberIntType, &num)) { *onDemandConnectionStatus = num; } } }
return connectionPrivate->on_demand;}
BooleanSCNetworkConnectionGetReachabilityInfo(SCNetworkConnectionRef connection, SCNetworkReachabilityFlags *reach_flags, unsigned int *reach_if_index){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return FALSE; }
if (connectionPrivate->service == NULL) { _SCErrorSet(kSCStatusConnectionNoService); return FALSE; }
if (reach_flags != NULL) { *reach_flags = 0; }
if (reach_if_index != NULL) { *reach_if_index = 0; }
if (connectionPrivate->on_demand_info != NULL) { if (reach_flags != NULL) { int num; CFNumberRef flags_num = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandReachFlags); if (isA_CFNumber(flags_num) && CFNumberGetValue(flags_num, kCFNumberIntType, &num)) { *reach_flags = num; } }
if (reach_if_index != NULL) { int num; CFNumberRef if_index_num = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandReachInterfaceIndex); if (isA_CFNumber(if_index_num) && CFNumberGetValue(if_index_num, kCFNumberIntType, &num)) { *reach_if_index = num; } } }
return TRUE;}
SCNetworkConnectionTypeSCNetworkConnectionGetType(SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); return kSCNetworkConnectionTypeUnknown; }
if (connectionPrivate->service == NULL) { _SCErrorSet(kSCStatusConnectionNoService); return kSCNetworkConnectionTypeUnknown; }
_SCErrorSet(kSCStatusOK);
return connectionPrivate->type;}
CFDataRefSCNetworkConnectionCopyFlowDivertToken(SCNetworkConnectionRef connection, CFDictionaryRef flowProperties){#pragma unused(connection, flowProperties) _SCErrorSet(kSCStatusFailed); return NULL;}
intSCNetworkConnectionGetServiceIdentifier (SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; int service_identifier = -1;
if (connectionPrivate->service != NULL) { service_identifier = 0; if (connectionPrivate->on_demand_info != NULL) { CFNumberRef id_num = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCPropNetDNSServiceIdentifier);
if (isA_CFNumber(id_num)) { CFNumberGetValue(id_num, kCFNumberIntType, &service_identifier); } } }
return service_identifier;}
#if !TARGET_OS_SIMULATORSCNetworkConnectionStatusSCNetworkConnectionGetStatusFromNEStatus(ne_session_status_t status){ switch (status) { case NESessionStatusInvalid: return kSCNetworkConnectionInvalid; case NESessionStatusDisconnected: return kSCNetworkConnectionDisconnected; case NESessionStatusConnecting: case NESessionStatusReasserting: return kSCNetworkConnectionConnecting; case NESessionStatusConnected: return kSCNetworkConnectionConnected; case NESessionStatusDisconnecting: return kSCNetworkConnectionDisconnecting; }
return kSCNetworkConnectionInvalid;}#endif /* !TARGET_OS_SIMULATOR */
#pragma mark -#pragma mark User level "dial" API
#define k_NetworkConnect_Notification "com.apple.networkConnect"#define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")#define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
#define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC#define k_Last_Service_Id_Key CFSTR("ServiceID")#define k_Unique_Id_Key CFSTR("UniqueIdentifier")
/* Private Prototypes */static Boolean SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (CFStringRef *serviceID);static Boolean SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (CFStringRef *serviceID);static Boolean SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions);static Boolean SCNetworkConnectionPrivateIsPPPService (CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2);static void addPasswordFromKeychain (CFStringRef serviceID, CFDictionaryRef *userOptions);static CFStringRef copyPasswordFromKeychain (CFStringRef uniqueID);
static int notify_userprefs_token = -1;
static CFDictionaryRef onDemand_configuration = NULL;static Boolean onDemand_force_refresh = FALSE;static pthread_mutex_t onDemand_notify_lock = PTHREAD_MUTEX_INITIALIZER;static int onDemand_notify_token = -1;
/* * return TRUE if domain1 ends with domain2, and will check for trailing "." */#define WILD_CARD_MATCH_STR CFSTR("*")Boolean_SC_domainEndsWithDomain(CFStringRef compare_domain, CFStringRef match_domain){ CFRange range; Boolean ret = FALSE; CFStringRef s1 = NULL; Boolean s1_created = FALSE; CFStringRef s2 = NULL; Boolean s2_created = FALSE; CFStringRef s3 = NULL;
if (CFEqual(match_domain, WILD_CARD_MATCH_STR)) { return TRUE; }
if (CFStringHasSuffix(compare_domain, CFSTR("."))) { range.location = 0; range.length = CFStringGetLength(compare_domain) - 1; s1 = CFStringCreateWithSubstring(NULL, compare_domain, range); if (s1 == NULL) { goto done; } s1_created = TRUE; } else { s1 = compare_domain; }
if (CFStringHasSuffix(match_domain, CFSTR("."))) { range.location = 0; range.length = CFStringGetLength(match_domain) - 1; s2 = CFStringCreateWithSubstring(NULL, match_domain, range); if (s2 == NULL) { goto done; } s2_created = TRUE; } else { s2 = match_domain; }
if (CFStringHasPrefix(s2, CFSTR("*."))) { range.location = 2; range.length = CFStringGetLength(s2)-2; s3 = CFStringCreateWithSubstring(NULL, s2, range); if (s3 == NULL) { goto done; } if (s2_created) { CFRelease(s2); } s2 = s3; s2_created = TRUE; }
ret = CFStringHasSuffix(s1, s2);
done :
if (s1_created) CFRelease(s1); if (s2_created) CFRelease(s2); return ret;}
static CFCharacterSetRef_SC_getNotDotOrStarCharacterSet (void){ static CFCharacterSetRef notDotOrStar = NULL; if (notDotOrStar == NULL) { CFCharacterSetRef dotOrStar = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, CFSTR(".*")); if (dotOrStar) { notDotOrStar = CFCharacterSetCreateInvertedSet(kCFAllocatorDefault, dotOrStar); CFRelease(dotOrStar); } } return notDotOrStar;}
static CFMutableStringRef_SC_createStringByTrimmingDotsAndStars (CFStringRef string){ CFCharacterSetRef notDotOrStar = _SC_getNotDotOrStarCharacterSet(); CFRange entireString = CFRangeMake(0, CFStringGetLength(string)); CFMutableStringRef result = CFStringCreateMutableCopy(kCFAllocatorDefault, entireString.length, string); CFRange start; CFRange end = CFRangeMake(entireString.length, 0);
if (CFStringFindCharacterFromSet(string, notDotOrStar, entireString, 0, &start) && CFStringFindCharacterFromSet(string, notDotOrStar, entireString, kCFCompareBackwards, &end)) { if (start.location == kCFNotFound || end.location == kCFNotFound || start.location > end.location) { CFRelease(result); return NULL; } }
if ((end.location + 1) < entireString.length) { CFStringReplace(result, CFRangeMake(end.location + 1, entireString.length - (end.location + 1)), CFSTR("")); } if (start.location > 0) { CFStringReplace(result, CFRangeMake(0, start.location), CFSTR("")); }
return result;}
static CFIndex_SC_getCountOfStringInString (CFStringRef string, CFStringRef substring){ CFIndex count = 0; CFArrayRef ranges = CFStringCreateArrayWithFindResults(kCFAllocatorDefault, string, substring, CFRangeMake(0, CFStringGetLength(string)), 0); if (ranges != NULL) { count = CFArrayGetCount(ranges); CFRelease(ranges); } return count;}
Boolean_SC_hostMatchesDomain(CFStringRef hostname, CFStringRef domain){ Boolean result = FALSE; CFMutableStringRef trimmedHostname = NULL; CFMutableStringRef trimmedDomain = NULL;
if (!isA_CFString(hostname) || !isA_CFString(domain)) { goto done; }
trimmedHostname = _SC_createStringByTrimmingDotsAndStars(hostname); trimmedDomain = _SC_createStringByTrimmingDotsAndStars(domain);
if (!isA_CFString(trimmedHostname) || !isA_CFString(trimmedDomain)) { goto done; }
CFIndex numHostnameDots = _SC_getCountOfStringInString(trimmedHostname, CFSTR(".")); CFIndex numDomainDots = _SC_getCountOfStringInString(trimmedDomain, CFSTR(".")); if (numHostnameDots == numDomainDots) { result = CFEqual(trimmedHostname, trimmedDomain); } else if (numDomainDots > 0 && numDomainDots < numHostnameDots) { CFStringReplace(trimmedDomain, CFRangeMake(0, 0), CFSTR(".")); result = CFStringHasSuffix(trimmedHostname, trimmedDomain); } else { result = FALSE; }
done: if (trimmedHostname) { CFRelease(trimmedHostname); } if (trimmedDomain) { CFRelease(trimmedDomain); } return result;}
/* VPN On Demand */
static CFDictionaryRef__SCNetworkConnectionCopyOnDemandConfiguration(void){ int changed = 1; int status; uint64_t triggersCount = 0; CFDictionaryRef configuration;
pthread_mutex_lock(&onDemand_notify_lock); if (onDemand_notify_token == -1) { status = notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY, &onDemand_notify_token); if (status != NOTIFY_STATUS_OK) { SC_log(LOG_NOTICE, "notify_register_check() failed, status=%d", status); onDemand_notify_token = -1; } }
if (onDemand_notify_token != -1) { status = notify_check(onDemand_notify_token, &changed); if (status != NOTIFY_STATUS_OK) { SC_log(LOG_NOTICE, "notify_check() failed, status=%d", status); (void)notify_cancel(onDemand_notify_token); onDemand_notify_token = -1; } }
if (changed && (onDemand_notify_token != -1)) { status = notify_get_state(onDemand_notify_token, &triggersCount); if (status != NOTIFY_STATUS_OK) { SC_log(LOG_NOTICE, "notify_get_state() failed, status=%d", status); (void)notify_cancel(onDemand_notify_token); onDemand_notify_token = -1; } }
if (changed || onDemand_force_refresh) { CFStringRef key;
SC_log(LOG_INFO, "OnDemand information %s", (onDemand_configuration == NULL) ? "fetched" : "updated");
if (onDemand_configuration != NULL) { CFRelease(onDemand_configuration); onDemand_configuration = NULL; }
if ((triggersCount > 0) || onDemand_force_refresh) { key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand); onDemand_configuration = SCDynamicStoreCopyValue(NULL, key); CFRelease(key); if ((onDemand_configuration != NULL) && !isA_CFDictionary(onDemand_configuration)) { CFRelease(onDemand_configuration); onDemand_configuration = NULL; } }
onDemand_force_refresh = FALSE; }
configuration = (onDemand_configuration != NULL) ? CFRetain(onDemand_configuration) : NULL; pthread_mutex_unlock(&onDemand_notify_lock);
return configuration;}
__private_extern__void__SCNetworkConnectionForceOnDemandConfigurationRefresh(void){ pthread_mutex_lock(&onDemand_notify_lock); onDemand_force_refresh = TRUE; pthread_mutex_unlock(&onDemand_notify_lock);
return;}
static Boolean__SCNetworkConnectionShouldNeverMatch(CFDictionaryRef trigger, CFStringRef hostName, pid_t client_pid){ CFArrayRef exceptedProcesses; CFIndex exceptedProcessesCount; CFIndex exceptedProcessesIndex; CFArrayRef exceptions; CFIndex exceptionsCount; int exceptionsIndex;
// we have a matching domain, check against exception list exceptions = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandMatchDomainsNever); exceptionsCount = isA_CFArray(exceptions) ? CFArrayGetCount(exceptions) : 0; for (exceptionsIndex = 0; exceptionsIndex < exceptionsCount; exceptionsIndex++) { CFStringRef exception;
exception = CFArrayGetValueAtIndex(exceptions, exceptionsIndex); if (isA_CFString(exception) && _SC_domainEndsWithDomain(hostName, exception)) { // found matching exception SC_log(LOG_INFO, "OnDemand match exception"); return TRUE; } }
if (client_pid != 0) { exceptedProcesses = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandPluginPIDs); exceptedProcessesCount = isA_CFArray(exceptedProcesses) ? CFArrayGetCount(exceptedProcesses) : 0; for (exceptedProcessesIndex = 0; exceptedProcessesIndex < exceptedProcessesCount; exceptedProcessesIndex++) { int pid; CFNumberRef pidRef;
pidRef = CFArrayGetValueAtIndex(exceptedProcesses, exceptedProcessesIndex); if (isA_CFNumber(pidRef) && CFNumberGetValue(pidRef, kCFNumberIntType, &pid)) { if (pid == client_pid) { return TRUE; } } } }
return FALSE;}
static CFStringRef__SCNetworkConnectionDomainGetMatchWithParameters(CFStringRef action, CFPropertyListRef actionParameters, CFStringRef hostName, CFStringRef *probeString){ CFArrayRef actionArray = NULL; CFIndex actionArraySize = 0; CFIndex i; CFStringRef matchDomain = NULL;
/* For now, only support EvaluateConnection, which takes a CFArray */ if (!CFEqual(action, kSCValNetVPNOnDemandRuleActionEvaluateConnection) || !isA_CFArray(actionParameters)) { return NULL; }
actionArray = (CFArrayRef)actionParameters; actionArraySize = CFArrayGetCount(actionArray);
/* Process domain rules, with actions of ConnectIfNeeded and NeverConnect */ for (i = 0; i < actionArraySize; i++) { CFStringRef domainAction = NULL; CFDictionaryRef domainRule = CFArrayGetValueAtIndex(actionArray, i); CFArrayRef domains = NULL; CFIndex domainsCount = 0; CFIndex domainsIndex;
if (!isA_CFDictionary(domainRule)) { continue; }
domains = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomains); if (!isA_CFArray(domains)) { continue; }
domainsCount = CFArrayGetCount(domains); for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) { CFStringRef domain; domain = CFArrayGetValueAtIndex(domains, domainsIndex); if (isA_CFString(domain) && _SC_domainEndsWithDomain(hostName, domain)) { matchDomain = domain; break; } }
if (matchDomain) { domainAction = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomainAction); if (isA_CFString(domainAction) && CFEqual(domainAction, kSCValNetVPNOnDemandRuleActionParametersDomainActionNeverConnect)) { return NULL; } else { /* If we found a match, save the optional probe string as well */ if (probeString) { *probeString = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe); } break; } } }
return matchDomain;}
static CFStringRef__SCNetworkConnectionDomainGetMatch(CFDictionaryRef trigger, CFStringRef hostName, Boolean onDemandRetry){ CFArrayRef domains; CFIndex domainsCount; int domainsIndex; CFStringRef key; CFStringRef match_domain = NULL;
/* Old configuration: always, never, on retry lists */ key = onDemandRetry ? kSCNetworkConnectionOnDemandMatchDomainsOnRetry : kSCNetworkConnectionOnDemandMatchDomainsAlways;
domains = CFDictionaryGetValue(trigger, key); domainsCount = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0; for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) { CFStringRef domain;
domain = CFArrayGetValueAtIndex(domains, domainsIndex); if (isA_CFString(domain) && _SC_domainEndsWithDomain(hostName, domain)) { match_domain = domain; break; } }
return match_domain;}
static Boolean__SCNetworkConnectionShouldAlwaysConnect(CFDictionaryRef trigger){ CFStringRef action = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction); return (isA_CFString(action) && CFEqual(action, kSCValNetVPNOnDemandRuleActionConnect));}
static Boolean__SCNetworkConnectionShouldIgnoreTrigger(CFDictionaryRef trigger){ CFStringRef action = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction);
if (isA_CFString(action) && (CFEqual(action, kSCValNetVPNOnDemandRuleActionIgnore) || CFEqual(action, kSCValNetVPNOnDemandRuleActionDisconnect))) { return TRUE; }
return FALSE;}
static CFDictionaryRef__SCNetworkConnectionCopyMatchingTriggerWithName(CFDictionaryRef configuration, CFStringRef hostName, pid_t client_pid, Boolean onDemandRetry, CFDictionaryRef *match_info, Boolean *triggerNow, CFStringRef *probe_string){ CFDictionaryRef result = NULL; int sc_status = kSCStatusOK; CFArrayRef triggers; CFIndex triggersCount = 0; Boolean usedOnDemandRetry = FALSE;
if (triggerNow != NULL) { *triggerNow = FALSE; }
if (match_info != NULL) { *match_info = NULL; }
triggers = CFDictionaryGetValue(configuration, kSCNetworkConnectionOnDemandTriggers); triggersCount = isA_CFArray(triggers) ? CFArrayGetCount(triggers) : 0; for (CFIndex triggersIndex = 0; triggersIndex < triggersCount; triggersIndex++) { CFStringRef matched_domain = NULL; CFStringRef matched_probe_string = NULL; CFDictionaryRef trigger; Boolean trigger_matched = FALSE;
usedOnDemandRetry = FALSE;
trigger = CFArrayGetValueAtIndex(triggers, triggersIndex); if (!isA_CFDictionary(trigger)) { // if not a valid "OnDemand" configuration continue; }
if (__SCNetworkConnectionShouldAlwaysConnect(trigger)) { /* If the trigger action is 'Connect', always match this trigger */ /* First check the never match list */ if (__SCNetworkConnectionShouldNeverMatch(trigger, hostName, client_pid)) { continue; } trigger_matched = TRUE; } else if (__SCNetworkConnectionShouldIgnoreTrigger(trigger)) { /* If the trigger action is 'Ignore' or 'Disconnect', skip this trigger */ sc_status = kSCStatusConnectionIgnore; continue; } else { CFStringRef action = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction); CFArrayRef actionParameters = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleActionParameters); if (action && actionParameters) { matched_domain = __SCNetworkConnectionDomainGetMatchWithParameters(action, actionParameters, hostName, &matched_probe_string); usedOnDemandRetry = TRUE; } else { if (onDemandRetry) { matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, TRUE); usedOnDemandRetry = TRUE; } else { matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, FALSE); if (matched_domain == NULL && result == NULL) { /* Check the retry list if Always failed */ matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, TRUE); usedOnDemandRetry = TRUE; } } }
if (matched_domain) { if (__SCNetworkConnectionShouldNeverMatch(trigger, hostName, client_pid)) { matched_domain = NULL; continue; } else { trigger_matched = TRUE; } } }
if (trigger_matched) { // if we have a matching domain and there were no exceptions // then we pass back the OnDemand info if (match_info != NULL) { CFMutableDictionaryRef minfo; SCNetworkConnectionType type = kSCNetworkConnectionTypeIPLayerVPN; CFNumberRef type_num;
if (*match_info != NULL) { CFRelease(*match_info); *match_info = NULL; }
minfo = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
type_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &type); CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoVPNType, type_num); CFRelease(type_num); if (matched_domain) { CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoDomain, matched_domain); } CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoOnRetry, (usedOnDemandRetry ? kCFBooleanTrue : kCFBooleanFalse));
*match_info = minfo; }
if (probe_string != NULL) { if (*probe_string != NULL) { CFRelease(*probe_string); *probe_string = NULL; }
if (matched_probe_string) { *probe_string = CFRetain(matched_probe_string); } }
result = trigger;
/* If retry was requested, or we found Always match, trigger now */ if (onDemandRetry || !usedOnDemandRetry) { if (triggerNow != NULL) { *triggerNow = TRUE; } break; }
/* If we matched the Retry list, but Always was requested, keep going through triggers in case one matches an Always */ } }
if (result) { CFRetain(result); }
_SCErrorSet(sc_status); return result;}
static CFDictionaryRef__SCNetworkConnectionCopyTriggerWithService(CFDictionaryRef configuration, CFStringRef service_id){ CFArrayRef triggers; CFIndex triggersCount;
triggers = CFDictionaryGetValue(configuration, kSCNetworkConnectionOnDemandTriggers); triggersCount = isA_CFArray(triggers) ? CFArrayGetCount(triggers) : 0; for (CFIndex triggersIndex = 0; triggersIndex < triggersCount; triggersIndex++) { CFDictionaryRef trigger; CFStringRef trigger_service_id;
trigger = CFArrayGetValueAtIndex(triggers, triggersIndex); if (!isA_CFDictionary(trigger)) { // if not a valid "OnDemand" configuration continue; }
trigger_service_id = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandServiceID); if (isA_CFString(trigger_service_id) && CFEqual(trigger_service_id, service_id)) { CFRetain(trigger); return trigger; } }
return NULL;}
Boolean__SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef *storeP, CFStringRef hostName, Boolean onDemandRetry, CFStringRef *connectionServiceID, SCNetworkConnectionStatus *connectionStatus, CFStringRef *vpnRemoteAddress) /* CFDictionaryRef *info */{#pragma unused(storeP) CFDictionaryRef configuration; Boolean ok = FALSE; int sc_status = kSCStatusOK; CFDictionaryRef trigger; Boolean trigger_now = FALSE;
configuration = __SCNetworkConnectionCopyOnDemandConfiguration(); if (configuration == NULL) { _SCErrorSet(sc_status); return ok; }
trigger = __SCNetworkConnectionCopyMatchingTriggerWithName(configuration, hostName, 0, onDemandRetry, NULL, &trigger_now, NULL); if (trigger != NULL && trigger_now) { CFNumberRef num; SCNetworkConnectionStatus onDemandStatus = kSCNetworkConnectionDisconnected;
ok = TRUE;
if (!CFDictionaryGetValueIfPresent(trigger, kSCNetworkConnectionOnDemandStatus, (const void **)&num) || !isA_CFNumber(num) || !CFNumberGetValue(num, kCFNumberSInt32Type, &onDemandStatus)) { onDemandStatus = kSCNetworkConnectionDisconnected; } if (connectionStatus != NULL) { *connectionStatus = onDemandStatus; }
if (connectionServiceID != NULL) { *connectionServiceID = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandServiceID); *connectionServiceID = isA_CFString(*connectionServiceID); if ((*connectionServiceID != NULL) && (CFStringGetLength(*connectionServiceID) > 0)) { CFRetain(*connectionServiceID); } else { SC_log(LOG_INFO, "OnDemand%s configuration error, no serviceID", onDemandRetry ? " (on retry)" : ""); *connectionServiceID = NULL; ok = FALSE; } }
if (vpnRemoteAddress != NULL) { *vpnRemoteAddress = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandRemoteAddress); *vpnRemoteAddress = isA_CFString(*vpnRemoteAddress); if ((*vpnRemoteAddress != NULL) && (CFStringGetLength(*vpnRemoteAddress) > 0)) { CFRetain(*vpnRemoteAddress); } else { SC_log(LOG_INFO, "OnDemand%s configuration error, no server address", onDemandRetry ? " (on retry)" : ""); *vpnRemoteAddress = NULL; ok = FALSE; } }
if (!ok) { if ((connectionServiceID != NULL) && (*connectionServiceID != NULL)) { CFRelease(*connectionServiceID); *connectionServiceID = NULL; } if ((vpnRemoteAddress != NULL) && (*vpnRemoteAddress != NULL)) { CFRelease(*vpnRemoteAddress); *vpnRemoteAddress = NULL; } sc_status = kSCStatusFailed; } else { SC_log(LOG_INFO, "OnDemand%s match, connection status = %d", onDemandRetry ? " (on retry)" : "", onDemandStatus); } }
if (trigger) { CFRelease(trigger); }
// SC_log(LOG_INFO, "OnDemand domain name(s) not matched");
if (configuration != NULL) CFRelease(configuration); if (!ok) { _SCErrorSet(sc_status); } return ok;}
static Boolean__SCNetworkConnectionCopyUserPreferencesInternal(CFDictionaryRef selectionOptions, CFStringRef *serviceID, CFDictionaryRef *userOptions){ int prefsChanged = 1; int status; Boolean success = FALSE;
if (notify_userprefs_token == -1) { status = notify_register_check(k_NetworkConnect_Notification, ¬ify_userprefs_token); if (status != NOTIFY_STATUS_OK) { SC_log(LOG_NOTICE, "notify_register_check() failed, status=%d", status); (void)notify_cancel(notify_userprefs_token); notify_userprefs_token = -1; } else { // clear the "something has changed" state (void) notify_check(notify_userprefs_token, &prefsChanged); prefsChanged = 1; } } if (notify_userprefs_token != -1) { status = notify_check(notify_userprefs_token, &prefsChanged); if (status != NOTIFY_STATUS_OK) { SC_log(LOG_NOTICE, "notify_check() failed, status=%d", status); (void)notify_cancel(notify_userprefs_token); notify_userprefs_token = -1; } }
*serviceID = NULL; *userOptions = NULL;
if (selectionOptions != NULL) { Boolean catchAllFound = FALSE; CFIndex catchAllService = 0; CFIndex catchAllConfig = 0; CFStringRef hostName = NULL; CFStringRef priority = NULL; CFArrayRef serviceNames = NULL; CFDictionaryRef services = NULL; CFIndex serviceIndex; CFIndex servicesCount;
hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName); if (hostName == NULL) { hostName = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandHostName); } hostName = isA_CFString(hostName); if (hostName == NULL) goto done_selection; // if no hostname for matching
priority = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandPriority); if (!isA_CFString(priority)) priority = kSCValNetPPPOnDemandPriorityDefault;
if (!isA_CFArray(serviceNames)) goto done_selection;
if (!isA_CFDictionary(services)) { goto done_selection; }
servicesCount = CFArrayGetCount(serviceNames); for (serviceIndex = 0; serviceIndex < servicesCount; serviceIndex++) { CFIndex configIndex; CFIndex configsCount; CFArrayRef serviceConfigs; CFStringRef serviceName; int val;
serviceName = CFArrayGetValueAtIndex(serviceNames, serviceIndex); if (!isA_CFString(serviceName)) { continue; }
serviceConfigs = CFDictionaryGetValue(services, serviceName); if (!isA_CFArray(serviceConfigs)) { continue; }
configsCount = CFArrayGetCount(serviceConfigs); for (configIndex = 0; configIndex < configsCount; configIndex++) { CFNumberRef autodial; CFDictionaryRef config; CFDictionaryRef pppConfig;
config = CFArrayGetValueAtIndex(serviceConfigs, configIndex); if (!isA_CFDictionary(config)) { continue; }
pppConfig = CFDictionaryGetValue(config, kSCEntNetPPP); if (!isA_CFDictionary(pppConfig)) { continue; }
autodial = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandEnabled); if (!isA_CFNumber(autodial)) { continue; }
CFNumberGetValue(autodial, kCFNumberIntType, &val); if (val) { CFArrayRef domains; CFIndex domainsCount; CFIndex domainsIndex;
/* we found an conditional connection enabled configuration */
/* check domain */ domains = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandDomains); if (!isA_CFArray(domains)) { continue; }
domainsCount = CFArrayGetCount(domains); for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) { CFStringRef domain;
domain = CFArrayGetValueAtIndex(domains, domainsIndex); if (!isA_CFString(domain)) { continue; }
if (!catchAllFound && (CFStringCompare(domain, CFSTR(""), 0) == kCFCompareEqualTo || CFStringCompare(domain, CFSTR("."), 0) == kCFCompareEqualTo)) { // found a catch all catchAllFound = TRUE; catchAllService = serviceIndex; catchAllConfig = configIndex; }
if (_SC_domainEndsWithDomain(hostName, domain)) { // found matching configuration *serviceID = serviceName; CFRetain(*serviceID); *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config); CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName); CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority); addPasswordFromKeychain(*serviceID, userOptions); success = TRUE; goto done_selection; } } } } }
// config not found, do we have a catchall ? if (catchAllFound) { CFDictionaryRef config; CFArrayRef serviceConfigs; CFStringRef serviceName;
serviceName = CFArrayGetValueAtIndex(serviceNames, catchAllService); serviceConfigs = CFDictionaryGetValue(services, serviceName); config = CFArrayGetValueAtIndex(serviceConfigs, catchAllConfig);
*serviceID = serviceName; CFRetain(*serviceID); *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config); CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName); CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority); addPasswordFromKeychain(*serviceID, userOptions); success = TRUE; goto done_selection; }
done_selection:
if (serviceNames) { CFRelease(serviceNames); } if (services) { CFRelease(services); }
if (debug > 1) { SC_log(LOG_INFO, "SCNetworkConnectionCopyUserPreferences %s", success ? "succeeded" : "failed"); SC_log(LOG_INFO, "Selection options: %@", selectionOptions); }
return success; }
/* we don't have selection options */
// (1) Figure out which service ID we care about, allocate it into passed "serviceID" success = SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(serviceID);
if (success && (*serviceID != NULL)) { // (2) Get the list of user data for this service ID CFPropertyListRef userServices = NULL;
// (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't if (userServices != NULL) { if (isA_CFArray(userServices)) { // (4) Get the default set of user options for this service success = SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef)userServices, userOptions); if (success) { addPasswordFromKeychain(*serviceID, userOptions); } } else { SC_log(LOG_INFO, "Error, userServices are not of type CFArray!"); }
CFRelease(userServices); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL } }
if (debug > 1) { SC_log(LOG_INFO, "SCNetworkConnectionCopyUserPreferences %@, no selection options", success ? CFSTR("succeeded") : CFSTR("failed")); }
return success;}
BooleanSCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions, CFStringRef *serviceID, CFDictionaryRef *userOptions){ Boolean success = FALSE;
/* initialize runtime */ pthread_once(&initialized, __SCNetworkConnectionInitialize);
/* first check for new VPN OnDemand style */ if (selectionOptions != NULL) { CFStringRef hostName;
hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName); if (isA_CFString(hostName)) { CFStringRef connectionServiceID = NULL; SCNetworkConnectionStatus connectionStatus = kSCNetworkConnectionInvalid; Boolean onDemandRetry; CFTypeRef val;
val = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry); onDemandRetry = isA_CFBoolean(val) ? CFBooleanGetValue(val) : TRUE;
success = __SCNetworkConnectionCopyOnDemandInfoWithName(NULL, hostName, onDemandRetry, &connectionServiceID, &connectionStatus, NULL); if (debug > 1) { SC_log(LOG_INFO, "__SCNetworkConnectionCopyOnDemandInfoWithName: return %d, status %d", success, connectionStatus); }
if (success) { // if the hostname matches an OnDemand domain if (connectionStatus == kSCNetworkConnectionConnected) { // if we are already connected if (connectionServiceID != NULL) { CFRelease(connectionServiceID); } return FALSE; }
*serviceID = connectionServiceID; *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName); return TRUE; } else if (!onDemandRetry) { // if the hostname does not match an OnDemand domain and we have // not yet issued an initial DNS query (i.e. it's not a query // being retried after the VPN has been established) then we're // done return FALSE; } } }
return __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions, serviceID, userOptions);}
BooleanSCNetworkConnectionOnDemandShouldRetryOnFailure (SCNetworkConnectionRef connection){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; CFDictionaryRef match_info = NULL;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); goto fail; }
if (connectionPrivate->service == NULL) { _SCErrorSet(kSCStatusConnectionNoService); goto fail; }
if (isA_CFDictionary(connectionPrivate->on_demand_user_options)) { match_info = CFDictionaryGetValue(connectionPrivate->on_demand_user_options, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo); if (isA_CFDictionary(match_info)) { CFBooleanRef onRetry = CFDictionaryGetValue(match_info, kSCNetworkConnectionOnDemandMatchInfoOnRetry); if (isA_CFBoolean(onRetry)) { return CFBooleanGetValue(onRetry); } } }
fail: return FALSE;}
// Mask is optional in routes dictionary; if not present, whole addresses are matchedstatic Boolean__SCNetworkConnectionIPv4AddressMatchesRoutes (struct sockaddr_in *addr_in, CFDictionaryRef routes){ CFIndex count; CFIndex i; CFDataRef maskData = NULL; struct in_addr *maskDataArray; CFDataRef routeaddrData = NULL; struct in_addr *routeaddrDataArray;
if (!isA_CFDictionary(routes)) { return FALSE; }
routeaddrData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoAddresses); maskData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoMasks);
/* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */ if (!isA_CFData(routeaddrData) || (maskData && (!isA_CFData(maskData) || CFDataGetLength(routeaddrData) != CFDataGetLength(maskData)))) { return FALSE; }
routeaddrDataArray = (struct in_addr*)(void*)CFDataGetBytePtr(routeaddrData); if (maskData) { maskDataArray = (struct in_addr*)(void*)CFDataGetBytePtr(maskData); }
count = CFDataGetLength(routeaddrData) / sizeof(struct in_addr); for (i=0; i<count; i++) { struct in_addr routeAddr = *routeaddrDataArray;
if (maskData) { struct in_addr mask = *maskDataArray;
if ((addr_in->sin_addr.s_addr & mask.s_addr) == (routeAddr.s_addr & mask.s_addr)) { return TRUE; } maskDataArray++; } else { if (addr_in->sin_addr.s_addr == routeAddr.s_addr) { return TRUE; } } routeaddrDataArray++; } return FALSE;}
static void__SCNetworkConnectionMaskIPv6Address(struct in6_addr *addr, struct in6_addr *mask){ for (size_t i = 0; i < sizeof(struct in6_addr); i++) addr->s6_addr[i] &= mask->s6_addr[i];}
// Mask is optional in routes dictionary; if not present, whole addresses are matchedstatic Boolean__SCNetworkConnectionIPv6AddressMatchesRoutes (struct sockaddr_in6 *addr_in6, CFDictionaryRef routes){ CFIndex count; CFIndex i; CFDataRef maskData = NULL; struct in6_addr *maskDataArray; CFDataRef routeaddrData = NULL; struct in6_addr *routeaddrDataArray;
if (!isA_CFDictionary(routes)) { return FALSE; }
routeaddrData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoAddresses); maskData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoMasks);
/* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */ if (!isA_CFData(routeaddrData) || (maskData && (!isA_CFData(maskData) || CFDataGetLength(routeaddrData) != CFDataGetLength(maskData)))) { return FALSE; }
routeaddrDataArray = (struct in6_addr*)(void*)CFDataGetBytePtr(routeaddrData); if (maskData) { maskDataArray = (struct in6_addr*)(void*)CFDataGetBytePtr(maskData); }
count = CFDataGetLength(routeaddrData) / sizeof(struct in6_addr); for (i=0; i<count; i++) { if (maskData) { struct in6_addr cmpAddr; struct in6_addr *mask = maskDataArray; struct in6_addr routeAddr;
memcpy(&routeAddr, routeaddrDataArray, sizeof(routeAddr)); memcpy(&cmpAddr, &addr_in6->sin6_addr, sizeof(cmpAddr)); __SCNetworkConnectionMaskIPv6Address(&routeAddr, mask); __SCNetworkConnectionMaskIPv6Address(&cmpAddr, mask); maskDataArray++; if (!memcmp(&routeAddr, &cmpAddr, sizeof(routeAddr))) { return TRUE; } } else { if (!memcmp(routeaddrDataArray, &addr_in6->sin6_addr, sizeof(struct in6_addr))) { return TRUE; } }
routeaddrDataArray++; } return FALSE;}
static Boolean__SCNetworkConnectionAddressMatchesRedirectedDNS(CFDictionaryRef trigger, const struct sockaddr *input_addr){ CFBooleanRef redirectedRef = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandDNSRedirectDetected);
if (isA_CFBoolean(redirectedRef) && CFBooleanGetValue(redirectedRef)) { /* DNS is redirected. Look for address list. */ CFDictionaryRef redirectedAddressesRef = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandDNSRedirectedAddresses);
if (isA_CFDictionary(redirectedAddressesRef)) { if (input_addr->sa_family == AF_INET) { return __SCNetworkConnectionIPv4AddressMatchesRoutes((struct sockaddr_in*)(void*)input_addr, CFDictionaryGetValue(redirectedAddressesRef, kSCNetworkConnectionNetworkInfoIPv4)); } else if (input_addr->sa_family == AF_INET6) { return __SCNetworkConnectionIPv6AddressMatchesRoutes((struct sockaddr_in6*)(void*)input_addr, CFDictionaryGetValue(redirectedAddressesRef, kSCNetworkConnectionNetworkInfoIPv6)); } } }
return FALSE;}
/* If the required probe has failed, we need to tunnel the address. Equivalent to redirected DNS. */static Boolean__SCNetworkConnectionRequiredProbeFailed (CFDictionaryRef trigger, CFStringRef probeString){ CFDictionaryRef probeResults = NULL;
if (!isA_CFString(probeString)) { return FALSE; }
probeResults = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandProbeResults); if (!isA_CFDictionary(probeResults)) { return TRUE; }
CFBooleanRef result = CFDictionaryGetValue(probeResults, probeString);
/* Only a value of kCFBooleanFalse marks the probe as failed */ return (isA_CFBoolean(result) && !CFBooleanGetValue(result));}
BooleanSCNetworkConnectionCanTunnelAddress (SCNetworkConnectionRef connection, const struct sockaddr *address, Boolean *startImmediately){ SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; CFStringRef serviceID = NULL; CFDictionaryRef configuration = NULL; CFDictionaryRef trigger = NULL; CFDictionaryRef tunneledNetworks = NULL; sa_family_t address_family = AF_UNSPEC; Boolean success = FALSE;
if (startImmediately) { *startImmediately = FALSE; }
if (address == NULL) { goto done; }
address_family = address->sa_family; if (address_family != AF_INET && address_family != AF_INET6) { goto done; }
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); goto done; }
if (connectionPrivate->service == NULL) { _SCErrorSet(kSCStatusConnectionNoService); goto done; }
serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); if (!isA_CFString(serviceID)) { goto done; }
configuration = __SCNetworkConnectionCopyOnDemandConfiguration(); if (configuration == NULL) { goto done; }
trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, serviceID); if (trigger == NULL) { goto done; }
if (__SCNetworkConnectionRequiredProbeFailed(trigger, connectionPrivate->on_demand_required_probe)) { /* If probe failed, we can't trust DNS - connect now */ if (startImmediately) { *startImmediately = TRUE; } success = TRUE; goto done; }
if (__SCNetworkConnectionAddressMatchesRedirectedDNS(trigger, address)) { if (startImmediately) { *startImmediately = TRUE; } success = TRUE; goto done; }
tunneledNetworks = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandTunneledNetworks); if (!isA_CFDictionary(tunneledNetworks)) { goto done; }
if (address_family == AF_INET) { CFDictionaryRef ip_dict; Boolean matches = FALSE; struct sockaddr_in *addr_in = (struct sockaddr_in *)(void*)address;
ip_dict = CFDictionaryGetValue(tunneledNetworks, kSCNetworkConnectionNetworkInfoIPv4); if (!isA_CFDictionary(ip_dict)) { goto done; }
matches = __SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in, CFDictionaryGetValue(ip_dict, kSCNetworkConnectionNetworkInfoIncludedRoutes));
if (matches) { if (!__SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in, CFDictionaryGetValue(ip_dict, kSCNetworkConnectionNetworkInfoExcludedRoutes))) { success = TRUE; goto done; } } } else { CFDictionaryRef ip6_dict; Boolean matches = FALSE; struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)(void*)address;
ip6_dict = CFDictionaryGetValue(tunneledNetworks, kSCNetworkConnectionNetworkInfoIPv6); if (!isA_CFDictionary(ip6_dict)) { goto done; }
matches = __SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6, CFDictionaryGetValue(ip6_dict, kSCNetworkConnectionNetworkInfoIncludedRoutes));
if (matches) { if (!__SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6, CFDictionaryGetValue(ip6_dict, kSCNetworkConnectionNetworkInfoExcludedRoutes))) { success = TRUE; goto done; } } }done: if (configuration) { CFRelease(configuration); } if (trigger) { CFRelease(trigger); } return success;}
BooleanSCNetworkConnectionSelectServiceWithOptions(SCNetworkConnectionRef connection, CFDictionaryRef selectionOptions){ CFStringRef account_identifier = NULL; CFDictionaryRef configuration = NULL; SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; CFDictionaryRef found_trigger = NULL; CFStringRef host_name = NULL; Boolean is_retry = TRUE; CFDictionaryRef match_info = NULL; CFMutableDictionaryRef new_user_options = NULL; SCNetworkConnectionStatus on_demand_status = kSCNetworkConnectionInvalid; CFStringRef requiredProbe = NULL; CFStringRef service_id = NULL; Boolean skip_prefs = FALSE; Boolean success = TRUE; CFDictionaryRef user_options = NULL;
if (!isA_SCNetworkConnection(connection)) { _SCErrorSet(kSCStatusInvalidArgument); success = FALSE; goto done; }
/* Can't call this on a connection that is already associated with a service */ if (connectionPrivate->service != NULL) { _SCErrorSet(kSCStatusInvalidArgument); success = FALSE; goto done; }
if (isA_CFDictionary(selectionOptions)) { CFBooleanRef no_user_prefs = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionNoUserPrefs); CFBooleanRef retry = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry);
account_identifier = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandAccountIdentifier); host_name = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName); skip_prefs = (isA_CFBoolean(no_user_prefs) && CFBooleanGetValue(no_user_prefs));
if (isA_CFBoolean(retry)) { is_retry = CFBooleanGetValue(retry); } }
configuration = __SCNetworkConnectionCopyOnDemandConfiguration();
/* First, check for a match with the App Layer rules */ service_id = VPNAppLayerCopyMatchingService(connectionPrivate->client_audit_token, connectionPrivate->client_pid, connectionPrivate->client_uuid, connectionPrivate->client_bundle_id, host_name, account_identifier, &match_info); if (service_id != NULL) { Boolean use_app_layer = TRUE;
if (isA_CFDictionary(configuration)) { found_trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, service_id); if (found_trigger != NULL) { CFNumberRef status_num;
if (!CFDictionaryGetValueIfPresent(found_trigger, kSCNetworkConnectionOnDemandStatus, (const void **) &status_num) || !isA_CFNumber(status_num) || !CFNumberGetValue(status_num, kCFNumberSInt32Type, &on_demand_status)) { on_demand_status = kSCNetworkConnectionInvalid; }
/* * If the trigger should be ignored, still use App Layer VPN if it is already connected or * is in the process of connecting. */ if (__SCNetworkConnectionShouldIgnoreTrigger(found_trigger) && (on_demand_status != kSCNetworkConnectionConnecting) && (on_demand_status != kSCNetworkConnectionConnected)) { use_app_layer = FALSE; } } }
if (use_app_layer) { /* If this is not the 'OnRetry' call, and the service has not yet started, the match may need to return false */ if (!is_retry && match_info != NULL && on_demand_status != kSCNetworkConnectionConnecting && on_demand_status != kSCNetworkConnectionConnected) { CFBooleanRef matchedOnRetry = CFDictionaryGetValue(match_info, kSCNetworkConnectionOnDemandMatchInfoOnRetry); if (matchedOnRetry && CFBooleanGetValue(matchedOnRetry)) { /* Don't return that we matched always; wait for SCNetworkConnectionOnDemandShouldRetryOnFailure */ success = FALSE; } } connectionPrivate->type = kSCNetworkConnectionTypeAppLayerVPN; goto search_done; } else { CFRelease(service_id); service_id = NULL; if (match_info != NULL) { CFRelease(match_info); match_info = NULL; } if (found_trigger != NULL) { CFRelease(found_trigger); found_trigger = NULL; } } }
/* Next, check the IP layer rules */ if (isA_CFDictionary(configuration) && host_name != NULL) { Boolean triggerNow = FALSE;
found_trigger = __SCNetworkConnectionCopyMatchingTriggerWithName(configuration, host_name, connectionPrivate->client_pid, is_retry, &match_info, &triggerNow, &requiredProbe); if (found_trigger != NULL) { service_id = CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandServiceID); if (isA_CFString(service_id)) { CFRetain(service_id); connectionPrivate->type = kSCNetworkConnectionTypeIPLayerVPN; } else { service_id = NULL; } if (!triggerNow) { success = FALSE; } goto search_done; } else if (!is_retry) { goto search_done; }
if (match_info != NULL) { CFRelease(match_info); match_info = NULL; } }
/* Next, check the user preferences */ if (!skip_prefs && __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions, &service_id, &user_options)) { CFMutableDictionaryRef minfo; CFNumberRef type_num;
if (isA_CFDictionary(configuration)) { found_trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, service_id); } connectionPrivate->type = kSCNetworkConnectionTypePPP;
minfo = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); type_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &connectionPrivate->type); CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoVPNType, type_num); CFRelease(type_num); match_info = minfo; goto search_done; }
search_done: if (service_id == NULL) { _SCErrorSet(kSCStatusOK); success = FALSE; goto done; }
connectionPrivate->service = _SCNetworkServiceCopyActive(NULL, service_id); if (connectionPrivate->service == NULL) { _SCErrorSet(kSCStatusOK); success = FALSE; goto done; }
if (found_trigger != NULL) { if (connectionPrivate->on_demand_info) { CFRelease(connectionPrivate->on_demand_info); } connectionPrivate->on_demand_info = found_trigger; CFRetain(connectionPrivate->on_demand_info);
if (on_demand_status == kSCNetworkConnectionInvalid) { CFNumberRef status_num;
if (!CFDictionaryGetValueIfPresent(found_trigger, kSCNetworkConnectionOnDemandStatus, (const void **) &status_num) || !isA_CFNumber(status_num) || !CFNumberGetValue(status_num, kCFNumberSInt32Type, &on_demand_status)) { on_demand_status = kSCNetworkConnectionInvalid; } }
if (on_demand_status != kSCNetworkConnectionConnected) { if (connectionPrivate->type == kSCNetworkConnectionTypeAppLayerVPN) { /* Check App Layer OnDemand flag */ CFBooleanRef app_on_demand_enabled = CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandMatchAppEnabled); if (isA_CFBoolean(app_on_demand_enabled) && CFBooleanGetValue(app_on_demand_enabled)) { connectionPrivate->on_demand = TRUE; } } else { connectionPrivate->on_demand = TRUE; } } } else if (connectionPrivate->type == kSCNetworkConnectionTypePPP) { /* If we got the service from __SCNetworkConnectionCopyUserPreferencesInternal, then it's on demand */ connectionPrivate->on_demand = TRUE; }
if (user_options == NULL) { new_user_options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } else { new_user_options = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, user_options); }
if (host_name != NULL) { CFDictionarySetValue(new_user_options, kSCNetworkConnectionSelectionOptionOnDemandHostName, host_name); }
if (connectionPrivate->on_demand && match_info != NULL) { CFDictionarySetValue(new_user_options, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo, match_info); }
connectionPrivate->on_demand_user_options = new_user_options; CFRetain(connectionPrivate->on_demand_user_options);
if (requiredProbe) { connectionPrivate->on_demand_required_probe = requiredProbe; CFRetain(connectionPrivate->on_demand_required_probe); }
done: if (service_id != NULL) { CFRelease(service_id); }
if (configuration != NULL) { CFRelease(configuration); }
if (found_trigger != NULL) { CFRelease(found_trigger); }
if (user_options != NULL) { CFRelease(user_options); }
if (new_user_options != NULL) { CFRelease(new_user_options); }
if (match_info != NULL) { CFRelease(match_info); }
if (requiredProbe != NULL) { CFRelease(requiredProbe); }
return success;}
//*******************************************************************************************// SCNetworkConnectionPrivateCopyDefaultServiceIDForDial// ----------------------------------------------------// Try to find the service id to connect// (1) Start by looking at the last service used in Network Pref / Network menu extra// (2) If Network Pref / Network menu extra has not been used, find the PPP service// with the highest ordering//********************************************************************************************static BooleanSCNetworkConnectionPrivateCopyDefaultServiceIDForDial(CFStringRef *serviceID){ Boolean foundService = FALSE; CFPropertyListRef lastServiceSelectedInIC = NULL;
// we found the service the user last had open in IC if (lastServiceSelectedInIC != NULL) { // make sure its a PPP service if (SCNetworkConnectionPrivateIsPPPService(lastServiceSelectedInIC, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) { // make sure the service that we found is valid CFDictionaryRef dict; CFStringRef key;
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, lastServiceSelectedInIC, kSCEntNetInterface); dict = SCDynamicStoreCopyValue(NULL, key); CFRelease(key); if (dict != NULL) { CFRelease(dict); *serviceID = CFRetain(lastServiceSelectedInIC); foundService = TRUE; } } CFRelease(lastServiceSelectedInIC); }
if (!foundService) { foundService = SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(serviceID); }
return foundService;}
//********************************************************************************// SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore// -------------------------------------------------------// Find the highest ordered PPP service in the dynamic store//********************************************************************************static BooleanSCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(CFStringRef *serviceID){ CFDictionaryRef dict = NULL; CFStringRef key = NULL; CFArrayRef serviceIDs = NULL; Boolean success = FALSE;
*serviceID = NULL;
do { CFIndex count; CFIndex i;
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainSetup, kSCEntNetIPv4); if (key == NULL) { fprintf(stderr, "Error, Setup Key == NULL!\n"); break; }
dict = SCDynamicStoreCopyValue(NULL, key); if (!isA_CFDictionary(dict)) { fprintf(stderr, "no global IPv4 entity\n"); break; }
serviceIDs = CFDictionaryGetValue(dict, kSCPropNetServiceOrder); // array of service id's if (!isA_CFArray(serviceIDs)) { fprintf(stderr, "service order not specified\n"); break; }
count = CFArrayGetCount(serviceIDs); for (i = 0; i < count; i++) { CFStringRef service = CFArrayGetValueAtIndex(serviceIDs, i);
if (SCNetworkConnectionPrivateIsPPPService(service, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) { *serviceID = CFRetain(service); success = TRUE; break; } } } while (FALSE);
if (key != NULL) CFRelease(key); if (dict != NULL) CFRelease(dict);
return success;}
//********************************************************************************// SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray// ---------------------------------------------------------// Copy over user preferences for a particular service if they exist//********************************************************************************static BooleanSCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions){ CFIndex count = CFArrayGetCount(userOptionsArray); int i;
for (i = 0; i < count; i++) { // (1) Find the dictionary CFPropertyListRef propertyList = CFArrayGetValueAtIndex(userOptionsArray, i);
if (isA_CFDictionary(propertyList) != NULL) { // See if there's a value for dial on demand CFPropertyListRef value;
value = CFDictionaryGetValue((CFDictionaryRef)propertyList, k_Dial_Default_Key); if (isA_CFBoolean(value) != NULL) { if (CFBooleanGetValue(value)) { // we found the default user options *userOptions = CFDictionaryCreateCopy(NULL, (CFDictionaryRef)propertyList); break; } } } }
return TRUE;}
//********************************************************************************// SCNetworkConnectionPrivateIsServiceType// --------------------------------------// Check and see if the service is a PPP service of the given types//********************************************************************************static BooleanSCNetworkConnectionPrivateIsPPPService(CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2){ CFStringRef entityKey; Boolean isPPPService = FALSE; Boolean isMatchingSubType = FALSE; CFDictionaryRef serviceDict;
entityKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceID, kSCEntNetInterface); if (entityKey == NULL) { return FALSE; }
serviceDict = SCDynamicStoreCopyValue(NULL, entityKey); if (serviceDict != NULL) { if (isA_CFDictionary(serviceDict)) { CFStringRef type; CFStringRef subtype;
type = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceType); if (isA_CFString(type)) { isPPPService = CFEqual(type, kSCValNetInterfaceTypePPP); }
subtype = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceSubType); if (isA_CFString(subtype)) { isMatchingSubType = CFEqual(subtype, subType1); if (!isMatchingSubType && subType2) isMatchingSubType = CFEqual(subtype, subType2); } } CFRelease(serviceDict); } CFRelease(entityKey);
return (isPPPService && isMatchingSubType);}
//********************************************************************************// addPasswordFromKeychain// --------------------------------------// Get the password and shared secret out of the keychain and add// them to the PPP and IPSec dictionaries//********************************************************************************static voidaddPasswordFromKeychain(CFStringRef serviceID, CFDictionaryRef *userOptions){ CFPropertyListRef uniqueID; CFStringRef password; CFStringRef sharedsecret = NULL;
/* user options must exist */ if (*userOptions == NULL) return;
/* first, get the unique identifier used to store passwords in the keychain */ uniqueID = CFDictionaryGetValue(*userOptions, k_Unique_Id_Key); if (!isA_CFString(uniqueID)) return;
/* first, get the PPP password */ password = copyPasswordFromKeychain(uniqueID);
/* then, if necessary, get the IPSec Shared Secret */ if (SCNetworkConnectionPrivateIsPPPService(serviceID, kSCValNetInterfaceSubTypeL2TP, 0)) { CFMutableStringRef uniqueIDSS;
uniqueIDSS = CFStringCreateMutableCopy(NULL, 0, uniqueID); CFStringAppend(uniqueIDSS, CFSTR(".SS")); sharedsecret = copyPasswordFromKeychain(uniqueIDSS); CFRelease(uniqueIDSS); }
/* did we find our information in the key chain ? */ if ((password != NULL) || (sharedsecret != NULL)) { CFMutableDictionaryRef newOptions;
newOptions = CFDictionaryCreateMutableCopy(NULL, 0, *userOptions);
/* PPP password */ if (password != NULL) { CFDictionaryRef entity; CFMutableDictionaryRef newEntity;
entity = CFDictionaryGetValue(*userOptions, kSCEntNetPPP); if (isA_CFDictionary(entity)) newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity); else newEntity = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
/* set the PPP password */ CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPassword, uniqueID); CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPasswordEncryption, kSCValNetPPPAuthPasswordEncryptionKeychain); CFRelease(password);
/* update the PPP entity */ CFDictionarySetValue(newOptions, kSCEntNetPPP, newEntity); CFRelease(newEntity); }
/* IPSec Shared Secret */ if (sharedsecret != NULL) { CFDictionaryRef entity; CFMutableDictionaryRef newEntity;
entity = CFDictionaryGetValue(*userOptions, kSCEntNetIPSec); if (isA_CFDictionary(entity)) newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity); else newEntity = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
/* set the IPSec Shared Secret */ CFDictionarySetValue(newEntity, kSCPropNetIPSecSharedSecret, sharedsecret); CFRelease(sharedsecret);
/* update the IPSec entity */ CFDictionarySetValue(newOptions, kSCEntNetIPSec, newEntity); CFRelease(newEntity); }
/* update the userOptions dictionary */ CFRelease(*userOptions); *userOptions = CFDictionaryCreateCopy(NULL, newOptions); CFRelease(newOptions); }
}
#if !TARGET_OS_IPHONE//********************************************************************************// copyKeychainEnumerator// --------------------------------------// Gather Keychain Enumerator//********************************************************************************static CFArrayRefcopyKeychainEnumerator(CFStringRef uniqueIdentifier){ CFArrayRef itemArray = NULL; CFMutableDictionaryRef query; OSStatus result;
query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(query, kSecClass , kSecClassGenericPassword); CFDictionarySetValue(query, kSecAttrService, uniqueIdentifier); CFDictionarySetValue(query, kSecReturnRef , kCFBooleanTrue); CFDictionarySetValue(query, kSecMatchLimit , kSecMatchLimitAll); result = SecItemCopyMatching(query, (CFTypeRef *)&itemArray); CFRelease(query); if ((result != noErr) && (itemArray != NULL)) { CFRelease(itemArray); itemArray = NULL; }
return itemArray;}#endif // !TARGET_OS_IPHONE
//********************************************************************************// copyPasswordFromKeychain// --------------------------------------// Given a uniqueID, retrieve the password from the keychain//********************************************************************************static CFStringRefcopyPasswordFromKeychain(CFStringRef uniqueID){#if !TARGET_OS_IPHONE CFArrayRef enumerator; CFIndex n; CFStringRef password = NULL;
enumerator = copyKeychainEnumerator(uniqueID); if (enumerator == NULL) { return NULL; // if no keychain enumerator }
n = CFArrayGetCount(enumerator); if (n > 0) { void *data = NULL; UInt32 dataLen = 0; SecKeychainItemRef itemRef; OSStatus result;
itemRef = (SecKeychainItemRef)CFArrayGetValueAtIndex(enumerator, 0); result = SecKeychainItemCopyContent(itemRef, // itemRef NULL, // itemClass NULL, // attrList &dataLen, // length (void *)&data); // outData if ((result == noErr) && (data != NULL) && (dataLen > 0)) { password = CFStringCreateWithBytes(NULL, data, dataLen, kCFStringEncodingUTF8, TRUE); (void) SecKeychainItemFreeContent(NULL, data); }
}
CFRelease(enumerator);
return password;#else // !TARGET_OS_IPHONE#pragma unused(uniqueID) return NULL;#endif // !TARGET_OS_IPHONE}
__private_extern__char *__SCNetworkConnectionGetControllerPortName(void){#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000)) && !TARGET_OS_SIMULATOR if (scnc_server_name == NULL){ if (!(sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME | SANDBOX_CHECK_NO_REPORT, PPPCONTROLLER_SERVER_PRIV))){ scnc_server_name = PPPCONTROLLER_SERVER_PRIV; } else { scnc_server_name = PPPCONTROLLER_SERVER; } }#else scnc_server_name = PPPCONTROLLER_SERVER;#endif return scnc_server_name;}