meshcore.js 339 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258
  1. /*
  2. Copyright 2018-2022 Intel Corporation
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. process.on('uncaughtException', function (ex) {
  14. require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException1: " + ex });
  15. });
  16. if (process.platform == 'win32' && require('user-sessions').getDomain == null) {
  17. require('user-sessions').getDomain = function getDomain(uid) {
  18. return (this.getSessionAttribute(uid, this.InfoClass.WTSDomainName));
  19. };
  20. }
  21. var promise = require('promise');
  22. // Mesh Rights
  23. var MNG_ERROR = 65;
  24. var MESHRIGHT_EDITMESH = 1;
  25. var MESHRIGHT_MANAGEUSERS = 2;
  26. var MESHRIGHT_MANAGECOMPUTERS = 4;
  27. var MESHRIGHT_REMOTECONTROL = 8;
  28. var MESHRIGHT_AGENTCONSOLE = 16;
  29. var MESHRIGHT_SERVERFILES = 32;
  30. var MESHRIGHT_WAKEDEVICE = 64;
  31. var MESHRIGHT_SETNOTES = 128;
  32. var MESHRIGHT_REMOTEVIEW = 256; // Remote View Only
  33. var MESHRIGHT_NOTERMINAL = 512;
  34. var MESHRIGHT_NOFILES = 1024;
  35. var MESHRIGHT_NOAMT = 2048;
  36. var MESHRIGHT_LIMITEDINPUT = 4096;
  37. var MESHRIGHT_LIMITEVENTS = 8192;
  38. var MESHRIGHT_CHATNOTIFY = 16384;
  39. var MESHRIGHT_UNINSTALL = 32768;
  40. var MESHRIGHT_NODESKTOP = 65536;
  41. var pendingSetClip = false; // This is a temporary hack to prevent multiple setclips at the same time to stop the agent from crashing.
  42. //
  43. // This is a helper function used by the 32 bit Windows Agent, when running on 64 bit windows. It will check if the agent is already patched for this
  44. // and will use this helper if it is not. This helper will inject 'sysnative' into the results when calling readdirSync() on %windir%.
  45. //
  46. function __readdirSync_fix(path)
  47. {
  48. var sysnative = false;
  49. pathstr = require('fs')._fixwinpath(path);
  50. if (pathstr.split('\\*').join('').toLowerCase() == process.env['windir'].toLowerCase()) { sysnative = true; }
  51. var ret = require('fs').__readdirSync_old(path);
  52. if (sysnative) { ret.push('sysnative'); }
  53. return (ret);
  54. }
  55. if (process.platform == 'win32' && require('_GenericMarshal').PointerSize == 4 && require('os').arch() == 'x64')
  56. {
  57. if (require('fs').readdirSync.version == null)
  58. {
  59. //
  60. // 32 Bit Windows Agent on 64 bit Windows has not been patched for sysnative issue, so lets use our own solution
  61. //
  62. require('fs').__readdirSync_old = require('fs').readdirSync;
  63. require('fs').readdirSync = __readdirSync_fix;
  64. }
  65. }
  66. function bcdOK() {
  67. if (process.platform != 'win32') { return (false); }
  68. if (require('os').arch() == 'x64') {
  69. return (require('_GenericMarshal').PointerSize == 8);
  70. }
  71. return (true);
  72. }
  73. function getDomainInfo() {
  74. var hostname = require('os').hostname();
  75. var ret = { Name: hostname, Domain: "", PartOfDomain: false };
  76. switch (process.platform) {
  77. case 'win32':
  78. try {
  79. ret = require('win-wmi').query('ROOT\\CIMV2', 'SELECT * FROM Win32_ComputerSystem', ['Name', 'Domain', 'PartOfDomain'])[0];
  80. }
  81. catch (x) {
  82. }
  83. break;
  84. case 'linux':
  85. var hasrealm = false;
  86. try {
  87. hasrealm = require('lib-finder').hasBinary('realm');
  88. }
  89. catch (x) {
  90. }
  91. if (hasrealm) {
  92. var child = require('child_process').execFile('/bin/sh', ['sh']);
  93. child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
  94. child.stdin.write("realm list | grep domain-name: | tr '\\n' '`' | ");
  95. child.stdin.write("awk -F'`' '{ ");
  96. child.stdin.write(' printf("[");');
  97. child.stdin.write(' ST="";');
  98. child.stdin.write(' for(i=1;i<NF;++i)');
  99. child.stdin.write(' {');
  100. child.stdin.write(' match($i,/domain-name: /);');
  101. child.stdin.write(' printf("%s\\"%s\\"", ST, substr($i, RSTART+RLENGTH));');
  102. child.stdin.write(' ST=",";');
  103. child.stdin.write(' }');
  104. child.stdin.write(' printf("]");');
  105. child.stdin.write(" }'");
  106. child.stdin.write('\nexit\n');
  107. child.waitExit();
  108. var names = [];
  109. try {
  110. names = JSON.parse(child.stdout.str);
  111. }
  112. catch (e) {
  113. }
  114. while (names.length > 0) {
  115. if (hostname.endsWith('.' + names.peek())) {
  116. ret = { Name: hostname.substring(0, hostname.length - names.peek().length - 1), Domain: names.peek(), PartOfDomain: true };
  117. break;
  118. }
  119. names.pop();
  120. }
  121. }
  122. break;
  123. }
  124. return (ret);
  125. }
  126. try {
  127. Object.defineProperty(Array.prototype, 'findIndex', {
  128. value: function (func) {
  129. var i = 0;
  130. for (i = 0; i < this.length; ++i) {
  131. if (func(this[i], i, this)) {
  132. return (i);
  133. }
  134. }
  135. return (-1);
  136. }
  137. });
  138. } catch (ex) { }
  139. if (require('MeshAgent').ARCHID == null) {
  140. var id = null;
  141. switch (process.platform) {
  142. case 'win32':
  143. id = require('_GenericMarshal').PointerSize == 4 ? 3 : 4;
  144. break;
  145. case 'freebsd':
  146. id = require('_GenericMarshal').PointerSize == 4 ? 31 : 30;
  147. break;
  148. case 'darwin':
  149. try {
  150. id = require('os').arch() == 'x64' ? 16 : 29;
  151. } catch (ex) { id = 16; }
  152. break;
  153. }
  154. if (id != null) { Object.defineProperty(require('MeshAgent'), 'ARCHID', { value: id }); }
  155. }
  156. function setDefaultCoreTranslation(obj, field, value) {
  157. if (obj[field] == null || obj[field] == '') { obj[field] = value; }
  158. }
  159. function getCoreTranslation() {
  160. var ret = {};
  161. if (global.coretranslations != null) {
  162. try {
  163. var lang = require('util-language').current;
  164. if (coretranslations[lang] == null) { lang = lang.split('-')[0]; }
  165. if (coretranslations[lang] == null) { lang = 'en'; }
  166. if (coretranslations[lang] != null) { ret = coretranslations[lang]; }
  167. }
  168. catch (ex) { }
  169. }
  170. setDefaultCoreTranslation(ret, 'allow', 'Allow');
  171. setDefaultCoreTranslation(ret, 'deny', 'Deny');
  172. setDefaultCoreTranslation(ret, 'autoAllowForFive', 'Auto accept all connections for next 5 minutes');
  173. setDefaultCoreTranslation(ret, 'terminalConsent', '{0} requesting remote terminal access. Grant access?');
  174. setDefaultCoreTranslation(ret, 'desktopConsent', '{0} requesting remote desktop access. Grant access?');
  175. setDefaultCoreTranslation(ret, 'fileConsent', '{0} requesting remote file Access. Grant access?');
  176. setDefaultCoreTranslation(ret, 'terminalNotify', '{0} started a remote terminal session.');
  177. setDefaultCoreTranslation(ret, 'desktopNotify', '{0} started a remote desktop session.');
  178. setDefaultCoreTranslation(ret, 'fileNotify', '{0} started a remote file session.');
  179. setDefaultCoreTranslation(ret, 'privacyBar', 'Sharing desktop with: {0}');
  180. return (ret);
  181. }
  182. var currentTranslation = getCoreTranslation();
  183. try {
  184. require('kvm-helper');
  185. }
  186. catch (e) {
  187. var j =
  188. {
  189. users: function () {
  190. var r = {};
  191. require('user-sessions').Current(function (c) { r = c; });
  192. if (process.platform != 'win32') {
  193. for (var i in r) {
  194. r[i].SessionId = r[i].uid;
  195. }
  196. }
  197. return (r);
  198. }
  199. };
  200. addModuleObject('kvm-helper', j);
  201. }
  202. function lockDesktop(uid) {
  203. switch (process.platform) {
  204. case 'linux':
  205. if (uid != null) {
  206. var name = require('user-sessions').getUsername(uid);
  207. var child = require('child_process').execFile('/bin/sh', ['sh']);
  208. child.stdout.str = ''; child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
  209. child.stderr.str = ''; child.stderr.on('data', function (chunk) { this.str += chunk.toString(); });
  210. child.stdin.write('loginctl show-user -p Sessions ' + name + " | awk '{");
  211. child.stdin.write('gsub(/^Sessions=/,"",$0);');
  212. child.stdin.write('cmd = sprintf("loginctl lock-session %s",$0);');
  213. child.stdin.write('system(cmd);');
  214. child.stdin.write("}'\nexit\n");
  215. child.waitExit();
  216. }
  217. else {
  218. var child = require('child_process').execFile('/bin/sh', ['sh']);
  219. child.stdout.str = ''; child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
  220. child.stderr.str = ''; child.stderr.on('data', function (chunk) { this.str += chunk.toString(); });
  221. child.stdin.write('loginctl lock-sessions\nexit\n');
  222. child.waitExit();
  223. }
  224. break;
  225. case 'win32':
  226. {
  227. var options = { type: 1, uid: uid };
  228. var child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], options);
  229. child.waitExit();
  230. }
  231. break;
  232. default:
  233. break;
  234. }
  235. }
  236. var writable = require('stream').Writable;
  237. function destopLockHelper_pipe(httprequest) {
  238. if (process.platform != 'linux' && process.platform != 'freebsd') { return; }
  239. if (httprequest.unlockerHelper == null && httprequest.desktop != null && httprequest.desktop.kvm != null) {
  240. httprequest.unlockerHelper = new writable(
  241. {
  242. 'write': function (chunk, flush) {
  243. if (chunk.readUInt16BE(0) == 65) {
  244. delete this.request.autolock;
  245. }
  246. flush();
  247. return (true);
  248. },
  249. 'final': function (flush) {
  250. flush();
  251. }
  252. });
  253. httprequest.unlockerHelper.request = httprequest;
  254. httprequest.desktop.kvm.pipe(httprequest.unlockerHelper);
  255. }
  256. }
  257. var obj = { serverInfo: {} };
  258. var agentFileHttpRequests = {}; // Currently active agent HTTPS GET requests from the server.
  259. var agentFileHttpPendingRequests = []; // Pending HTTPS GET requests from the server.
  260. var debugConsole = (global._MSH && (_MSH().debugConsole == 1));
  261. var color_options =
  262. {
  263. background: (global._MSH != null) ? global._MSH().background : '0,54,105',
  264. foreground: (global._MSH != null) ? global._MSH().foreground : '255,255,255'
  265. };
  266. if (process.platform == 'win32' && require('user-sessions').isRoot()) {
  267. // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
  268. try {
  269. var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
  270. var serviceName = (_MSH().serviceName ? _MSH().serviceName : (require('_agentNodeId').serviceName() ? require('_agentNodeId').serviceName() : 'Mesh Agent'));
  271. try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'EstimatedSize'); } catch (ex) { }
  272. if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'EstimatedSize', actualSize); } catch (ex) { } }
  273. } catch (ex) { }
  274. // Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode
  275. var svcname = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
  276. try {
  277. svcname = require('MeshAgent').serviceName;
  278. } catch (ex) { }
  279. try {
  280. var meshCheck = false;
  281. try { meshCheck = require('service-manager').manager.getService(svcname).isMe(); } catch (ex) { }
  282. if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService(svcname)) { require('win-bcd').enableSafeModeService(svcname); }
  283. } catch (ex) { }
  284. // Check the Agent Uninstall MetaData for DisplayVersion and update if not the same and only on windows
  285. if (process.platform == 'win32') {
  286. try {
  287. var writtenDisplayVersion = 0, actualDisplayVersion = process.versions.commitDate.toString();
  288. var serviceName = (_MSH().serviceName ? _MSH().serviceName : (require('_agentNodeId').serviceName() ? require('_agentNodeId').serviceName() : 'Mesh Agent'));
  289. try { writtenDisplayVersion = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'DisplayVersion'); } catch (ex) { }
  290. if (writtenDisplayVersion != actualDisplayVersion) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'DisplayVersion', actualDisplayVersion); } catch (ex) { } }
  291. } catch (ex) { }
  292. }
  293. }
  294. if (process.platform != 'win32') {
  295. var ch = require('child_process');
  296. ch._execFile = ch.execFile;
  297. ch.execFile = function execFile(path, args, options) {
  298. if (options && options.type && options.type == ch.SpawnTypes.TERM && options.env) {
  299. options.env['TERM'] = 'xterm-256color';
  300. }
  301. return (this._execFile(path, args, options));
  302. };
  303. }
  304. if (process.platform == 'darwin' && !process.versions) {
  305. // This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
  306. var child = require('child_process').execFile('/bin/sh', ['sh']);
  307. child.stdout.str = '';
  308. child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
  309. child.stdin.write("cat /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist | tr '\n' '\.' | awk '{split($0, a, \"<key>KeepAlive</key>\"); split(a[2], b, \"<\"); split(b[2], c, \">\"); ");
  310. child.stdin.write(" if(c[1]==\"dict\"){ split(a[2], d, \"</dict>\"); if(split(d[1], truval, \"<true/>\")>1) { split(truval[1], kn1, \"<key>\"); split(kn1[2], kn2, \"</key>\"); print kn2[1]; } }");
  311. child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
  312. child.waitExit();
  313. if (child.stdout.str.trim() == 'Crashed') {
  314. child = require('child_process').execFile('/bin/sh', ['sh']);
  315. child.stdout.str = '';
  316. child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
  317. child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
  318. child.waitExit();
  319. if (parseInt(child.stdout.str.trim()) == process.pid) {
  320. // The currently running MeshAgent is us, so we can continue with the update
  321. var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
  322. var tokens = plist.split('<key>KeepAlive</key>');
  323. if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
  324. var tmp = tokens[1].split('</dict>');
  325. tmp.shift();
  326. tokens[1] = '\n <true/>' + tmp.join('</dict>');
  327. tokens = tokens.join('<key>KeepAlive</key>');
  328. require('fs').writeFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist', tokens);
  329. var fix = '';
  330. fix += ("function macosRepair()\n");
  331. fix += ("{\n");
  332. fix += (" var child = require('child_process').execFile('/bin/sh', ['sh']);\n");
  333. fix += (" child.stdout.str = '';\n");
  334. fix += (" child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });\n");
  335. fix += (" child.stderr.on('data', function (chunk) { });\n");
  336. fix += (" child.stdin.write('launchctl unload /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
  337. fix += (" child.stdin.write('launchctl load /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
  338. fix += (" child.stdin.write('rm /Library/LaunchDaemons/meshagentRepair.plist\\n');\n");
  339. fix += (" child.stdin.write('rm " + process.cwd() + "/macosRepair.js\\n');\n");
  340. fix += (" child.stdin.write('launchctl stop meshagentRepair\\nexit\\n');\n");
  341. fix += (" child.waitExit();\n");
  342. fix += ("}\n");
  343. fix += ("macosRepair();\n");
  344. fix += ("process.exit();\n");
  345. require('fs').writeFileSync(process.cwd() + '/macosRepair.js', fix);
  346. var plist = '<?xml version="1.0" encoding="UTF-8"?>\n';
  347. plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n';
  348. plist += '<plist version="1.0">\n';
  349. plist += ' <dict>\n';
  350. plist += ' <key>Label</key>\n';
  351. plist += (' <string>meshagentRepair</string>\n');
  352. plist += ' <key>ProgramArguments</key>\n';
  353. plist += ' <array>\n';
  354. plist += (' <string>' + process.execPath + '</string>\n');
  355. plist += ' <string>macosRepair.js</string>\n';
  356. plist += ' </array>\n';
  357. plist += ' <key>WorkingDirectory</key>\n';
  358. plist += (' <string>' + process.cwd() + '</string>\n');
  359. plist += ' <key>RunAtLoad</key>\n';
  360. plist += ' <true/>\n';
  361. plist += ' </dict>\n';
  362. plist += '</plist>';
  363. require('fs').writeFileSync('/Library/LaunchDaemons/meshagentRepair.plist', plist);
  364. child = require('child_process').execFile('/bin/sh', ['sh']);
  365. child.stdout.str = '';
  366. child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
  367. child.stdin.write("launchctl load /Library/LaunchDaemons/meshagentRepair.plist\nexit\n");
  368. child.waitExit();
  369. }
  370. }
  371. }
  372. }
  373. // Add an Intel AMT event to the log
  374. function addAmtEvent(msg) {
  375. if (obj.amtevents == null) { obj.amtevents = []; }
  376. var d = new Date(), e = zeroPad(d.getHours(), 2) + ':' + zeroPad(d.getMinutes(), 2) + ':' + zeroPad(d.getSeconds(), 2) + ', ' + msg;
  377. obj.amtevents.push(e);
  378. if (obj.amtevents.length > 100) { obj.amtevents.splice(0, obj.amtevents.length - 100); }
  379. if (obj.showamtevent) { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: e }); }
  380. }
  381. function zeroPad(num, size) { var s = '000000000' + num; return s.substr(s.length - size); }
  382. // Create Secure IPC for Diagnostic Agent Communications
  383. obj.DAIPC = require('net').createServer();
  384. if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (ex) { } }
  385. obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
  386. try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH, writableAll: true, maxConnections: 5 }); } catch (ex) { }
  387. obj.DAIPC._daipc = [];
  388. obj.DAIPC.on('connection', function (c) {
  389. c._send = function (j) {
  390. var data = JSON.stringify(j);
  391. var packet = Buffer.alloc(data.length + 4);
  392. packet.writeUInt32LE(data.length + 4, 0);
  393. Buffer.from(data).copy(packet, 4);
  394. this.write(packet);
  395. };
  396. this._daipc.push(c);
  397. c.parent = this;
  398. c.on('end', function () { removeRegisteredApp(this); });
  399. c.on('data', function (chunk) {
  400. if (chunk.length < 4) { this.unshift(chunk); return; }
  401. var len = chunk.readUInt32LE(0);
  402. if (len > 8192) { removeRegisteredApp(this); this.end(); return; }
  403. if (chunk.length < len) { this.unshift(chunk); return; }
  404. var data = chunk.slice(4, len);
  405. try { data = JSON.parse(data.toString()); } catch (ex) { }
  406. if ((data == null) || (typeof data.cmd != 'string')) return;
  407. try {
  408. switch (data.cmd) {
  409. case 'requesthelp':
  410. if (this._registered == null) return;
  411. sendConsoleText('Request Help (' + this._registered + '): ' + data.value);
  412. var help = {};
  413. help[this._registered] = data.value;
  414. try { mesh.SendCommand({ action: 'sessions', type: 'help', value: help }); } catch (ex) { }
  415. MeshServerLogEx(98, [this._registered, data.value], "Help Requested, user: " + this._registered + ", details: " + data.value, null);
  416. break;
  417. case 'cancelhelp':
  418. if (this._registered == null) return;
  419. sendConsoleText('Cancel Help (' + this._registered + ')');
  420. try { mesh.SendCommand({ action: 'sessions', type: 'help', value: {} }); } catch (ex) { }
  421. break;
  422. case 'register':
  423. if (typeof data.value == 'string') {
  424. this._registered = data.value;
  425. var apps = {};
  426. apps[data.value] = 1;
  427. try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (ex) { }
  428. this._send({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer, amt: (amt != null) });
  429. }
  430. break;
  431. case 'query':
  432. switch (data.value) {
  433. case 'connection':
  434. data.result = require('MeshAgent').ConnectedServer;
  435. this._send(data);
  436. break;
  437. case 'descriptors':
  438. require('ChainViewer').getSnapshot().then(function (f) {
  439. this.tag.payload.result = f;
  440. this.tag.ipc._send(this.tag.payload);
  441. }).parentPromise.tag = { ipc: this, payload: data };
  442. break;
  443. case 'timerinfo':
  444. data.result = require('ChainViewer').getTimerInfo();
  445. this._send(data);
  446. break;
  447. }
  448. break;
  449. case 'amtstate':
  450. if (amt == null) return;
  451. var func = function amtStateFunc(state) { if (state != null) { amtStateFunc.pipe._send({ cmd: 'amtstate', value: state }); } }
  452. func.pipe = this;
  453. amt.getMeiState(11, func);
  454. break;
  455. case 'sessions':
  456. this._send({ cmd: 'sessions', sessions: tunnelUserCount });
  457. break;
  458. case 'meshToolInfo':
  459. try { mesh.SendCommand({ action: 'meshToolInfo', name: data.name, hash: data.hash, cookie: data.cookie ? true : false, pipe: true }); } catch (ex) { }
  460. break;
  461. case 'getUserImage':
  462. try { mesh.SendCommand({ action: 'getUserImage', userid: data.userid, pipe: true }); } catch (ex) { }
  463. break;
  464. case 'console':
  465. if (debugConsole) {
  466. var args = splitArgs(data.value);
  467. processConsoleCommand(args[0].toLowerCase(), parseArgs(args), 0, 'pipe');
  468. }
  469. break;
  470. }
  471. }
  472. catch (ex) { removeRegisteredApp(this); this.end(); return; }
  473. });
  474. });
  475. // Send current sessions to registered apps
  476. function broadcastSessionsToRegisteredApps(x) {
  477. var p = {}, i;
  478. for (i = 0; sendAgentMessage.messages != null && i < sendAgentMessage.messages.length; ++i) {
  479. p[i] = sendAgentMessage.messages[i];
  480. }
  481. tunnelUserCount.msg = p;
  482. broadcastToRegisteredApps({ cmd: 'sessions', sessions: tunnelUserCount });
  483. tunnelUserCount.msg = {};
  484. }
  485. // Send this object to all registered local applications
  486. function broadcastToRegisteredApps(x) {
  487. if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
  488. for (var i in obj.DAIPC._daipc) {
  489. if (obj.DAIPC._daipc[i]._registered != null) { obj.DAIPC._daipc[i]._send(x); }
  490. }
  491. }
  492. // Send this object to a specific registered local applications
  493. function sendToRegisteredApp(appid, x) {
  494. if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
  495. for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered == appid) { obj.DAIPC._daipc[i]._send(x); } }
  496. }
  497. // Send list of registered apps to the server
  498. function updateRegisteredAppsToServer() {
  499. if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
  500. var apps = {};
  501. for (var i in obj.DAIPC._daipc) { if (apps[obj.DAIPC._daipc[i]._registered] == null) { apps[obj.DAIPC._daipc[i]._registered] = 1; } else { apps[obj.DAIPC._daipc[i]._registered]++; } }
  502. try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (ex) { }
  503. }
  504. // Remove a registered app
  505. function removeRegisteredApp(pipe) {
  506. for (var i = obj.DAIPC._daipc.length - 1; i >= 0; i--) { if (obj.DAIPC._daipc[i] === pipe) { obj.DAIPC._daipc.splice(i, 1); } }
  507. if (pipe._registered != null) updateRegisteredAppsToServer();
  508. }
  509. function diagnosticAgent_uninstall() {
  510. require('service-manager').manager.uninstallService('meshagentDiagnostic');
  511. require('task-scheduler').delete('meshagentDiagnostic/periodicStart'); // TODO: Using "delete" here breaks the minifier since this is a reserved keyword
  512. }
  513. function diagnosticAgent_installCheck(install) {
  514. try {
  515. var diag = require('service-manager').manager.getService('meshagentDiagnostic');
  516. return (diag);
  517. } catch (ex) { }
  518. if (!install) { return null; }
  519. var svc = null;
  520. try {
  521. require('service-manager').manager.installService(
  522. {
  523. name: 'meshagentDiagnostic',
  524. displayName: "Mesh Agent Diagnostic Service",
  525. description: "Mesh Agent Diagnostic Service",
  526. servicePath: process.execPath,
  527. parameters: ['-recovery']
  528. //files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
  529. });
  530. svc = require('service-manager').manager.getService('meshagentDiagnostic');
  531. }
  532. catch (ex) { return null; }
  533. var proxyConfig = require('global-tunnel').proxyConfig;
  534. var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate');
  535. var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64');
  536. ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db');
  537. ddb.Put('disableUpdate', '1');
  538. ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex'));
  539. ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID);
  540. ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
  541. if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
  542. if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
  543. if (proxyConfig) {
  544. ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
  545. } else {
  546. ddb.Put('ignoreProxyFile', '1');
  547. }
  548. require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } });
  549. require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "Diagnostic Agent Registered [" + nodeid.length + "/" + nodeid + "]" });
  550. delete ddb;
  551. // Set a recurrent task, to run the Diagnostic Agent every 2 days
  552. require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: 2, time: require('tls').generateRandomInteger('0', '23') + ':' + require('tls').generateRandomInteger('0', '59').padStart(2, '0'), service: 'meshagentDiagnostic' });
  553. //require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
  554. return (svc);
  555. }
  556. // Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed.
  557. if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null)) {
  558. // Setup manual battery monitoring
  559. require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function () {
  560. if (require('MeshAgent')._batteryFileTimer != null) return;
  561. require('MeshAgent')._batteryFileTimer = setTimeout(function () {
  562. try {
  563. require('MeshAgent')._batteryFileTimer = null;
  564. var data = null;
  565. try { data = require('fs').readFileSync(process.cwd() + 'batterystate.txt').toString(); } catch (ex) { }
  566. if ((data != null) && (data.length < 10)) {
  567. data = data.split(',');
  568. if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc'))) {
  569. var level = parseInt(data[1]);
  570. if ((level >= 0) && (level <= 100)) { require('MeshAgent').SendCommand({ action: 'battery', state: data[0], level: level }); }
  571. }
  572. }
  573. } catch (ex) { }
  574. }, 1000);
  575. });
  576. }
  577. else {
  578. try {
  579. // Setup normal battery monitoring
  580. if (require('computer-identifiers').isBatteryPowered && require('computer-identifiers').isBatteryPowered()) {
  581. require('MeshAgent')._battLevelChanged = function _battLevelChanged(val) {
  582. _battLevelChanged.self._currentBatteryLevel = val;
  583. _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val });
  584. };
  585. require('MeshAgent')._battLevelChanged.self = require('MeshAgent');
  586. require('MeshAgent')._powerChanged = function _powerChanged(val) {
  587. _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc');
  588. _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel });
  589. };
  590. require('MeshAgent')._powerChanged.self = require('MeshAgent');
  591. require('MeshAgent').on('Connected', function (status) {
  592. if (status == 0) {
  593. require('power-monitor').removeListener('acdc', this._powerChanged);
  594. require('power-monitor').removeListener('batteryLevel', this._battLevelChanged);
  595. } else {
  596. require('power-monitor').on('acdc', this._powerChanged);
  597. require('power-monitor').on('batteryLevel', this._battLevelChanged);
  598. }
  599. });
  600. }
  601. }
  602. catch (ex) { }
  603. }
  604. // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
  605. var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ((process.versions.compileTime ? process.versions.compileTime : '').split(', ')[1].replace(' ', ' ') + ', ' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14, root: require('user-sessions').isRoot() }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
  606. // Get the operating system description string
  607. try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; meshCoreObjChanged(); }); } catch (ex) { }
  608. // Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
  609. function onUserSessionChanged(user, locked) {
  610. userSession.enumerateUsers().then(function (users) {
  611. if (process.platform == 'linux') {
  612. if (userSession._startTime == null) {
  613. userSession._startTime = Date.now();
  614. userSession._count = users.length;
  615. }
  616. else if (Date.now() - userSession._startTime < 10000 && users.length == userSession._count) {
  617. userSession.removeAllListeners('changed');
  618. return;
  619. }
  620. }
  621. var u = [], a = users.Active;
  622. if(meshCoreObj.lusers == null) { meshCoreObj.lusers = []; }
  623. if(meshCoreObj.upnusers == null) { meshCoreObj.upnusers = []; }
  624. var ret = getDomainInfo();
  625. for (var i = 0; i < a.length; i++) {
  626. var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
  627. if (user && locked && (JSON.stringify(a[i]) === JSON.stringify(user))) { if (meshCoreObj.lusers.indexOf(un) == -1) { meshCoreObj.lusers.push(un); } }
  628. else if (user && !locked && (JSON.stringify(a[i]) === JSON.stringify(user))) { meshCoreObj.lusers.splice(meshCoreObj.lusers.indexOf(un), 1); }
  629. if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
  630. if (a[i].Domain != null && a[i].Domain == 'AzureAD'){
  631. // AzureAD to-do
  632. // var userGUID = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Control\\CloudDomainJoin\\JoinInfo');
  633. // if (userGUID != null){
  634. // var user = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Control\\CloudDomainJoin\\JoinInfo\\' + userGUID.subkeys[0], 'UserEmail');
  635. // if (user != null) u[i].UPN = user;
  636. // }
  637. } else if (a[i].Domain != null) {
  638. if (ret != null && ret.PartOfDomain === true) {
  639. meshCoreObj.upnusers.push(a[i].Username + '@' + ret.Domain);
  640. }
  641. }
  642. }
  643. meshCoreObj.lusers = meshCoreObj.lusers;
  644. meshCoreObj.users = u;
  645. meshCoreObjChanged();
  646. });
  647. }
  648. try {
  649. var userSession = require('user-sessions');
  650. userSession.on('changed', function () { onUserSessionChanged(null, false); });
  651. userSession.emit('changed');
  652. userSession.on('locked', function (user) { if(user != undefined && user != null) { onUserSessionChanged(user, true); } });
  653. userSession.on('unlocked', function (user) { if(user != undefined && user != null) { onUserSessionChanged(user, false); } });
  654. } catch (ex) { }
  655. var meshServerConnectionState = 0;
  656. var tunnels = {};
  657. var lastNetworkInfo = null;
  658. var lastPublicLocationInfo = null;
  659. var selfInfoUpdateTimer = null;
  660. var http = require('http');
  661. var net = require('net');
  662. var fs = require('fs');
  663. var rtc = require('ILibWebRTC');
  664. var amt = null;
  665. var processManager = require('process-manager');
  666. var wifiScannerLib = null;
  667. var wifiScanner = null;
  668. var networkMonitor = null;
  669. var nextTunnelIndex = 1;
  670. var apftunnel = null;
  671. var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing
  672. // Add to the server event log
  673. function MeshServerLog(msg, state) {
  674. if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
  675. if (state) {
  676. if (state.userid) { msg.userid = state.userid; }
  677. if (state.username) { msg.username = state.username; }
  678. if (state.sessionid) { msg.sessionid = state.sessionid; }
  679. if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
  680. if (state.guestname) { msg.guestname = state.guestname; }
  681. }
  682. mesh.SendCommand(msg);
  683. }
  684. // Add to the server event log, use internationalized events
  685. function MeshServerLogEx(id, args, msg, state) {
  686. var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg };
  687. if (state) {
  688. if (state.userid) { msg.userid = state.userid; }
  689. if (state.xuserid) { msg.xuserid = state.xuserid; }
  690. if (state.username) { msg.username = state.username; }
  691. if (state.sessionid) { msg.sessionid = state.sessionid; }
  692. if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
  693. if (state.guestname) { msg.guestname = state.guestname; }
  694. }
  695. mesh.SendCommand(msg);
  696. }
  697. // Import libraries
  698. db = require('SimpleDataStore').Shared();
  699. sha = require('SHA256Stream');
  700. mesh = require('MeshAgent');
  701. childProcess = require('child_process');
  702. if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
  703. // Check if this computer supports a desktop
  704. try {
  705. if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support)) {
  706. meshCoreObj.caps |= 1; meshCoreObjChanged();
  707. } else if (process.platform == 'linux' || process.platform == 'freebsd') {
  708. require('monitor-info').on('kvmSupportDetected', function (value) { meshCoreObj.caps |= 1; meshCoreObjChanged(); });
  709. }
  710. } catch (ex) { }
  711. }
  712. mesh.DAIPC = obj.DAIPC;
  713. /*
  714. // Try to load up the network monitor
  715. try {
  716. networkMonitor = require('NetworkMonitor');
  717. networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
  718. networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
  719. networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
  720. } catch (ex) { networkMonitor = null; }
  721. */
  722. // Fetch the SMBios Tables
  723. var SMBiosTables = null;
  724. var SMBiosTablesRaw = null;
  725. try {
  726. var SMBiosModule = null;
  727. try { SMBiosModule = require('smbios'); } catch (ex) { }
  728. if (SMBiosModule != null) {
  729. SMBiosModule.get(function (data) {
  730. if (data != null) {
  731. SMBiosTablesRaw = data;
  732. SMBiosTables = require('smbios').parse(data)
  733. if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
  734. // If SMBios tables say that Intel AMT is present, try to connect MEI
  735. if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true)) {
  736. var amtmodule = require('amt-manage');
  737. amt = new amtmodule(mesh, db, false);
  738. amt.on('portBinding_LMS', function (map) { mesh.SendCommand({ action: 'lmsinfo', value: { ports: map.keys() } }); });
  739. amt.on('stateChange_LMS', function (v) { if (!meshCoreObj.intelamt) { meshCoreObj.intelamt = {}; } meshCoreObj.intelamt.microlms = v; meshCoreObjChanged(); }); // 0 = Disabled, 1 = Connecting, 2 = Connected
  740. amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } } // MEI State
  741. amt.reset();
  742. }
  743. }
  744. });
  745. }
  746. } catch (ex) { sendConsoleText("ex1: " + ex); }
  747. // Try to load up the WIFI scanner
  748. try {
  749. var wifiScannerLib = require('wifi-scanner');
  750. wifiScanner = new wifiScannerLib();
  751. wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); });
  752. } catch (ex) { wifiScannerLib = null; wifiScanner = null; }
  753. // Get our location (lat/long) using our public IP address
  754. var getIpLocationDataExInProgress = false;
  755. var getIpLocationDataExCounts = [0, 0];
  756. function getIpLocationDataEx(func) {
  757. if (getIpLocationDataExInProgress == true) { return false; }
  758. try {
  759. getIpLocationDataExInProgress = true;
  760. getIpLocationDataExCounts[0]++;
  761. var options = http.parseUri("http://ipinfo.io/json");
  762. options.method = 'GET';
  763. http.request(options, function (resp) {
  764. if (resp.statusCode == 200) {
  765. var geoData = '';
  766. resp.data = function (geoipdata) { geoData += geoipdata; };
  767. resp.end = function () {
  768. var location = null;
  769. try {
  770. if (typeof geoData == 'string') {
  771. var result = JSON.parse(geoData);
  772. if (result.ip && result.loc) { location = result; }
  773. }
  774. } catch (ex) { }
  775. if (func) { getIpLocationDataExCounts[1]++; func(location); }
  776. }
  777. } else
  778. { func(null); }
  779. getIpLocationDataExInProgress = false;
  780. }).end();
  781. return true;
  782. }
  783. catch (ex) { return false; }
  784. }
  785. // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
  786. function clearGatewayMac(str) {
  787. if (typeof str != 'string') return null;
  788. var x = JSON.parse(str);
  789. for (var i in x.netif) { try { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } } catch (ex) { } }
  790. return JSON.stringify(x);
  791. }
  792. function getIpLocationData(func) {
  793. // Get the location information for the cache if possible
  794. var publicLocationInfo = db.Get('publicLocationInfo');
  795. if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
  796. if (publicLocationInfo == null) {
  797. // Nothing in the cache, fetch the data
  798. getIpLocationDataEx(function (locationData) {
  799. if (locationData != null) {
  800. publicLocationInfo = {};
  801. publicLocationInfo.netInfoStr = lastNetworkInfo;
  802. publicLocationInfo.locationData = locationData;
  803. var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
  804. if (func) func(locationData); // Report the new location
  805. }
  806. else {
  807. if (func) func(null); // Report no location
  808. }
  809. });
  810. }
  811. else {
  812. // Check the cache
  813. if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) {
  814. // Cache match
  815. if (func) func(publicLocationInfo.locationData);
  816. }
  817. else {
  818. // Cache mismatch
  819. getIpLocationDataEx(function (locationData) {
  820. if (locationData != null) {
  821. publicLocationInfo = {};
  822. publicLocationInfo.netInfoStr = lastNetworkInfo;
  823. publicLocationInfo.locationData = locationData;
  824. var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
  825. if (func) func(locationData); // Report the new location
  826. }
  827. else {
  828. if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
  829. }
  830. });
  831. }
  832. }
  833. }
  834. // Polyfill String.endsWith
  835. if (!String.prototype.endsWith) {
  836. String.prototype.endsWith = function (searchString, position) {
  837. var subjectString = this.toString();
  838. if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
  839. position -= searchString.length;
  840. var lastIndex = subjectString.lastIndexOf(searchString, position);
  841. return lastIndex !== -1 && lastIndex === position;
  842. };
  843. }
  844. // Polyfill path.join
  845. obj.path =
  846. {
  847. join: function () {
  848. var x = [];
  849. for (var i in arguments) {
  850. var w = arguments[i];
  851. if (w != null) {
  852. while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
  853. if (i != 0) {
  854. while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
  855. }
  856. x.push(w);
  857. }
  858. }
  859. if (x.length == 0) return '/';
  860. return x.join('/');
  861. }
  862. };
  863. // Replace a string with a number if the string is an exact number
  864. function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
  865. // Convert decimal to hex
  866. function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
  867. // Convert a raw string to a hex string
  868. function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
  869. // Convert a buffer into a string
  870. function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
  871. // Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
  872. function hex2rstr(d) {
  873. if (typeof d != "string" || d.length == 0) return '';
  874. var r = '', m = ('' + d).match(/../g), t;
  875. while (t = m.shift()) r += String.fromCharCode('0x' + t);
  876. return r
  877. }
  878. // Convert an object to string with all functions
  879. function objToString(x, p, pad, ret) {
  880. if (ret == undefined) ret = '';
  881. if (p == undefined) p = 0;
  882. if (x == null) { return '[null]'; }
  883. if (p > 8) { return '[...]'; }
  884. if (x == undefined) { return '[undefined]'; }
  885. if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
  886. if (typeof x == 'buffer') { return '[buffer]'; }
  887. if (typeof x != 'object') { return x; }
  888. var r = '{' + (ret ? '\r\n' : ' ');
  889. for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
  890. return r + addPad(p, pad) + '}';
  891. }
  892. // Return p number of spaces
  893. function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
  894. // Split a string taking into account the quoats. Used for command line parsing
  895. function splitArgs(str) {
  896. var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
  897. do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
  898. return myArray;
  899. }
  900. // Parse arguments string array into an object
  901. function parseArgs(argv) {
  902. var results = { '_': [] }, current = null;
  903. for (var i = 1, len = argv.length; i < len; i++) {
  904. var x = argv[i];
  905. if (x.length > 2 && x[0] == '-' && x[1] == '-') {
  906. if (current != null) { results[current] = true; }
  907. current = x.substring(2);
  908. } else {
  909. if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
  910. }
  911. }
  912. if (current != null) { results[current] = true; }
  913. return results;
  914. }
  915. // Get server target url with a custom path
  916. function getServerTargetUrl(path) {
  917. var x = mesh.ServerUrl;
  918. //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
  919. if (x == null) { return null; }
  920. if (path == null) { path = ''; }
  921. x = http.parseUri(x);
  922. if (x == null) return null;
  923. return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
  924. }
  925. // Get server url. If the url starts with "*/..." change it, it not use the url as is.
  926. function getServerTargetUrlEx(url) {
  927. if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
  928. return url;
  929. }
  930. function sendWakeOnLanEx_interval() {
  931. var t = require('MeshAgent').wakesockets;
  932. if (t.list.length == 0) {
  933. clearInterval(t);
  934. delete require('MeshAgent').wakesockets;
  935. return;
  936. }
  937. var mac = t.list.shift().split(':').join('')
  938. var magic = 'FFFFFFFFFFFF';
  939. for (var x = 1; x <= 16; ++x) { magic += mac; }
  940. var magicbin = Buffer.from(magic, 'hex');
  941. for (var i in t.sockets) {
  942. t.sockets[i].send(magicbin, 7, '255.255.255.255');
  943. //sendConsoleText('Sending wake packet on ' + JSON.stringify(t.sockets[i].address()));
  944. }
  945. }
  946. function sendWakeOnLanEx(hexMacList) {
  947. var ret = 0;
  948. if (require('MeshAgent').wakesockets == null) {
  949. // Create a new interval timer
  950. require('MeshAgent').wakesockets = setInterval(sendWakeOnLanEx_interval, 10);
  951. require('MeshAgent').wakesockets.sockets = [];
  952. require('MeshAgent').wakesockets.list = hexMacList;
  953. var interfaces = require('os').networkInterfaces();
  954. for (var adapter in interfaces) {
  955. if (interfaces.hasOwnProperty(adapter)) {
  956. for (var i = 0; i < interfaces[adapter].length; ++i) {
  957. var addr = interfaces[adapter][i];
  958. if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00')) {
  959. try {
  960. var socket = require('dgram').createSocket({ type: 'udp4' });
  961. socket.bind({ address: addr.address });
  962. socket.setBroadcast(true);
  963. socket.setMulticastInterface(addr.address);
  964. socket.setMulticastTTL(1);
  965. socket.descriptorMetadata = 'WoL (' + addr.address + ')';
  966. require('MeshAgent').wakesockets.sockets.push(socket);
  967. ++ret;
  968. }
  969. catch (ex) { }
  970. }
  971. }
  972. }
  973. }
  974. }
  975. else {
  976. // Append to an existing interval timer
  977. for (var i in hexMacList) {
  978. require('MeshAgent').wakesockets.list.push(hexMacList[i]);
  979. }
  980. ret = require('MeshAgent').wakesockets.sockets.length;
  981. }
  982. return ret;
  983. }
  984. function server_promise_default(res, rej) {
  985. this.resolve = res;
  986. this.reject = rej;
  987. }
  988. function server_getUserImage(userid) {
  989. var xpromise = require('promise');
  990. var ret = new xpromise(server_promise_default);
  991. if (require('MeshAgent')._promises == null) { require('MeshAgent')._promises = {}; }
  992. require('MeshAgent')._promises[ret._hashCode()] = ret;
  993. require('MeshAgent').SendCommand({ action: 'getUserImage', userid: userid, promise: ret._hashCode(), sentDefault: true });
  994. return ret;
  995. }
  996. require('MeshAgent')._consentTimers = {};
  997. function server_set_consentTimer(id) {
  998. require('MeshAgent')._consentTimers[id] = new Date();
  999. }
  1000. function server_check_consentTimer(id) {
  1001. if (require('MeshAgent')._consentTimers[id] != null) {
  1002. if ((new Date()) - require('MeshAgent')._consentTimers[id] < (60000 * 5)) return true;
  1003. require('MeshAgent')._consentTimers[id] = null;
  1004. }
  1005. return false;
  1006. }
  1007. function tunnel_finalized()
  1008. {
  1009. console.info1('Tunnel Request Finalized');
  1010. }
  1011. function tunnel_checkServerIdentity(certs)
  1012. {
  1013. /*
  1014. try { sendConsoleText("certs[0].digest: " + certs[0].digest); } catch (ex) { sendConsoleText(ex); }
  1015. try { sendConsoleText("certs[0].fingerprint: " + certs[0].fingerprint); } catch (ex) { sendConsoleText(ex); }
  1016. try { sendConsoleText("control-digest: " + require('MeshAgent').ServerInfo.ControlChannelCertificate.digest); } catch (ex) { sendConsoleText(ex); }
  1017. try { sendConsoleText("control-fingerprint: " + require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint); } catch (ex) { sendConsoleText(ex); }
  1018. */
  1019. // Check if this is an old agent, no certificate checks are possible in this situation. Display a warning.
  1020. if ((require('MeshAgent').ServerInfo == null) || (require('MeshAgent').ServerInfo.ControlChannelCertificate == null) || (certs[0].digest == null)) { sendAgentMessage("This agent is using insecure tunnels, consider updating.", 3, 119, true); return; }
  1021. // If the tunnel certificate matches the control channel certificate, accept the connection
  1022. if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; // Control channel certificate matches using full cert hash
  1023. if ((certs[0].fingerprint != null) && (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint)) return; // Control channel certificate matches using public key hash
  1024. // Check that the certificate is the one expected by the server, fail if not.
  1025. if ((tunnel_checkServerIdentity.servertlshash != null) && (tunnel_checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
  1026. }
  1027. function tunnel_onError()
  1028. {
  1029. sendConsoleText("ERROR: Unable to connect relay tunnel to: " + this.url + ", " + JSON.stringify(e));
  1030. }
  1031. // Handle a mesh agent command
  1032. function handleServerCommand(data) {
  1033. if (typeof data == 'object') {
  1034. // If this is a console command, parse it and call the console handler
  1035. switch (data.action) {
  1036. case 'agentupdate':
  1037. agentUpdate_Start(data.url, { hash: data.hash, tlshash: data.servertlshash, sessionid: data.sessionid });
  1038. break;
  1039. case 'msg': {
  1040. switch (data.type) {
  1041. case 'console': { // Process a console command
  1042. if ((typeof data.rights != 'number') || ((data.rights & 8) == 0) || ((data.rights & 16) == 0)) break; // Check console rights (Remote Control and Console)
  1043. if (data.value && data.sessionid) {
  1044. MeshServerLogEx(17, [data.value], "Processing console command: " + data.value, data);
  1045. var args = splitArgs(data.value);
  1046. processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
  1047. }
  1048. break;
  1049. }
  1050. case 'tunnel':
  1051. {
  1052. if (data.value != null) { // Process a new tunnel connection request
  1053. // Create a new tunnel object
  1054. var xurl = getServerTargetUrlEx(data.value);
  1055. if (xurl != null) {
  1056. xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
  1057. var woptions = http.parseUri(xurl);
  1058. woptions.perMessageDeflate = false;
  1059. if (typeof data.perMessageDeflate == 'boolean') { woptions.perMessageDeflate = data.perMessageDeflate; }
  1060. // Perform manual server TLS certificate checking based on the certificate hash given by the server.
  1061. woptions.rejectUnauthorized = 0;
  1062. woptions.checkServerIdentity = tunnel_checkServerIdentity;
  1063. woptions.checkServerIdentity.servertlshash = data.servertlshash;
  1064. //sendConsoleText(JSON.stringify(woptions));
  1065. //sendConsoleText('TUNNEL: ' + JSON.stringify(data, null, 2));
  1066. var tunnel = http.request(woptions);
  1067. tunnel.upgrade = onTunnelUpgrade;
  1068. tunnel.on('error', tunnel_onError);
  1069. tunnel.sessionid = data.sessionid;
  1070. tunnel.rights = data.rights;
  1071. tunnel.consent = data.consent;
  1072. if (global._MSH && _MSH().LocalConsent != null) { tunnel.consent |= parseInt(_MSH().LocalConsent); }
  1073. tunnel.privacybartext = data.privacybartext ? data.privacybartext : currentTranslation['privacyBar'];
  1074. tunnel.username = data.username + (data.guestname ? (' - ' + data.guestname) : '');
  1075. tunnel.realname = (data.realname ? data.realname : data.username) + (data.guestname ? (' - ' + data.guestname) : '');
  1076. tunnel.guestuserid = data.guestuserid;
  1077. tunnel.guestname = data.guestname;
  1078. tunnel.userid = data.userid;
  1079. if (server_check_consentTimer(tunnel.userid)) { tunnel.consent = (tunnel.consent & -57); } // Deleting Consent Requirement
  1080. tunnel.desktopviewonly = data.desktopviewonly;
  1081. tunnel.remoteaddr = data.remoteaddr;
  1082. tunnel.state = 0;
  1083. tunnel.url = xurl;
  1084. tunnel.protocol = 0;
  1085. tunnel.soptions = data.soptions;
  1086. tunnel.consentTimeout = (tunnel.soptions && tunnel.soptions.consentTimeout) ? tunnel.soptions.consentTimeout : 30;
  1087. tunnel.consentAutoAccept = (tunnel.soptions && (tunnel.soptions.consentAutoAccept === true));
  1088. tunnel.consentAutoAcceptIfNoUser = (tunnel.soptions && (tunnel.soptions.consentAutoAcceptIfNoUser === true));
  1089. tunnel.consentAutoAcceptIfDesktopNoUser = (tunnel.soptions && (tunnel.soptions.consentAutoAcceptIfDesktopNoUser === true));
  1090. tunnel.consentAutoAcceptIfTerminalNoUser = (tunnel.soptions && (tunnel.soptions.consentAutoAcceptIfTerminalNoUser === true));
  1091. tunnel.consentAutoAcceptIfFileNoUser = (tunnel.soptions && (tunnel.soptions.consentAutoAcceptIfFileNoUser === true));
  1092. tunnel.consentAutoAcceptIfLocked = (tunnel.soptions && (tunnel.soptions.consentAutoAcceptIfLocked === true));
  1093. tunnel.consentAutoAcceptIfDesktopLocked = (tunnel.soptions && (tunnel.soptions.consentAutoAcceptIfDesktopLocked === true));
  1094. tunnel.consentAutoAcceptIfTerminalLocked = (tunnel.soptions && (tunnel.soptions.consentAutoAcceptIfTerminalLocked === true));
  1095. tunnel.consentAutoAcceptIfFileLocked = (tunnel.soptions && (tunnel.soptions.consentAutoAcceptIfFileLocked === true));
  1096. tunnel.oldStyle = (tunnel.soptions && tunnel.soptions.oldStyle) ? tunnel.soptions.oldStyle : false;
  1097. tunnel.tcpaddr = data.tcpaddr;
  1098. tunnel.tcpport = data.tcpport;
  1099. tunnel.udpaddr = data.udpaddr;
  1100. tunnel.udpport = data.udpport;
  1101. // Put the tunnel in the tunnels list
  1102. var index = nextTunnelIndex++;
  1103. tunnel.index = index;
  1104. tunnels[index] = tunnel;
  1105. tunnel.once('~', tunnel_finalized);
  1106. tunnel.end();
  1107. //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
  1108. }
  1109. }
  1110. break;
  1111. }
  1112. case 'endtunnel': {
  1113. // Terminate one or more tunnels
  1114. if ((data.rights != 4294967295) && (data.xuserid != data.userid)) return; // This command requires full admin rights on the device or user self-closes it's own sessions
  1115. for (var i in tunnels) {
  1116. if ((tunnels[i].userid == data.xuserid) && (tunnels[i].guestname == data.guestname)) {
  1117. var disconnect = false, msgid = 0;
  1118. if ((data.protocol == 'kvm') && (tunnels[i].protocol == 2)) { msgid = 134; disconnect = true; }
  1119. else if ((data.protocol == 'terminal') && (tunnels[i].protocol == 1)) { msgid = 135; disconnect = true; }
  1120. else if ((data.protocol == 'files') && (tunnels[i].protocol == 5)) { msgid = 136; disconnect = true; }
  1121. else if ((data.protocol == 'tcp') && (tunnels[i].tcpport != null)) { msgid = 137; disconnect = true; }
  1122. else if ((data.protocol == 'udp') && (tunnels[i].udpport != null)) { msgid = 137; disconnect = true; }
  1123. if (disconnect) {
  1124. if (tunnels[i].s != null) { tunnels[i].s.end(); } else { tunnels[i].end(); }
  1125. // Log tunnel disconnection
  1126. var xusername = data.xuserid.split('/')[2];
  1127. if (data.guestname != null) { xusername += '/' + guestname; }
  1128. MeshServerLogEx(msgid, [xusername], "Forcibly disconnected session of user: " + xusername, data);
  1129. }
  1130. }
  1131. }
  1132. break;
  1133. }
  1134. case 'messagebox': {
  1135. // Display a message box
  1136. if (data.title && data.msg) {
  1137. MeshServerLogEx(18, [data.title, data.msg], "Displaying message box, title=" + data.title + ", message=" + data.msg, data);
  1138. if (process.platform == 'win32') {
  1139. if (global._clientmessage) {
  1140. global._clientmessage.addMessage(data.msg);
  1141. }
  1142. else {
  1143. try {
  1144. require('win-dialog');
  1145. var ipr = server_getUserImage(data.userid);
  1146. ipr.title = data.title;
  1147. ipr.message = data.msg;
  1148. ipr.username = data.username;
  1149. if (data.realname && (data.realname != '')) { ipr.username = data.realname; }
  1150. ipr.timeout = (typeof data.timeout === 'number' ? data.timeout : 120000);
  1151. global._clientmessage = ipr.then(function (img) {
  1152. var options = { b64Image: img.split(',').pop(), background: color_options.background, foreground: color_options.foreground }
  1153. if (this.timeout != 0) { options.timeout = this.timeout; }
  1154. this.messagebox = require('win-dialog').create(this.title, this.message, this.username, options);
  1155. this.__childPromise.addMessage = this.messagebox.addMessage.bind(this.messagebox);
  1156. return (this.messagebox);
  1157. });
  1158. global._clientmessage.then(function () { global._clientmessage = null; });
  1159. }
  1160. catch (z) {
  1161. try { require('message-box').create(data.title, data.msg, 120).then(function () { }).catch(function () { }); } catch (ex) { }
  1162. }
  1163. }
  1164. }
  1165. else {
  1166. try { require('message-box').create(data.title, data.msg, 120).then(function () { }).catch(function () { }); } catch (ex) { }
  1167. }
  1168. }
  1169. break;
  1170. }
  1171. case 'ps': {
  1172. // Return the list of running processes
  1173. if (data.sessionid) {
  1174. processManager.getProcesses(function (plist) {
  1175. mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid });
  1176. });
  1177. }
  1178. break;
  1179. }
  1180. case 'psinfo': {
  1181. // Requestion details information about a process
  1182. if (data.pid) {
  1183. var info = {}; // TODO: Replace with real data. Feel free not to give all values if not available.
  1184. try {
  1185. info = processManager.getProcessInfo(data.pid);
  1186. }catch(e){ }
  1187. /*
  1188. info.processUser = "User"; // String
  1189. info.processDomain = "Domain"; // String
  1190. info.cmd = "abc"; // String
  1191. info.processName = "dummydata";
  1192. info.privateMemorySize = 123; // Bytes
  1193. info.virtualMemorySize = 123; // Bytes
  1194. info.workingSet = 123; // Bytes
  1195. info.totalProcessorTime = 123; // Seconds
  1196. info.userProcessorTime = 123; // Seconds
  1197. info.startTime = "2012-12-30T23:59:59.000Z"; // Time in UTC ISO format
  1198. info.sessionId = 123; // Number
  1199. info.privilegedProcessorTime = 123; // Seconds
  1200. info.PriorityBoostEnabled = true; // Boolean
  1201. info.peakWorkingSet = 123; // Bytes
  1202. info.peakVirtualMemorySize = 123; // Bytes
  1203. info.peakPagedMemorySize = 123; // Bytes
  1204. info.pagedSystemMemorySize = 123; // Bytes
  1205. info.pagedMemorySize = 123; // Bytes
  1206. info.nonpagedSystemMemorySize = 123; // Bytes
  1207. info.mainWindowTitle = "dummydata"; // String
  1208. info.machineName = "dummydata"; // Only set this if machine name is not "."
  1209. info.handleCount = 123; // Number
  1210. */
  1211. mesh.SendCommand({ action: 'msg', type: 'psinfo', pid: data.pid, sessionid: data.sessionid, value: info });
  1212. }
  1213. break;
  1214. }
  1215. case 'pskill': {
  1216. // Kill a process
  1217. if (data.value) {
  1218. var info = data.value.split('|'), pid = data.value, msg = " (Unknown)";
  1219. if (info.length > 1) { pid = info[0]; msg = " (" + info[1] + ")"; } else { info[1] = "Unknown"; }
  1220. MeshServerLogEx(19, info, "Killing process " + pid + msg, data);
  1221. try { process.kill(parseInt(pid)); } catch (ex) { sendConsoleText("pskill: " + JSON.stringify(ex)); }
  1222. }
  1223. break;
  1224. }
  1225. case 'service': {
  1226. // return information about the service
  1227. try {
  1228. var service = require('service-manager').manager.getService(data.serviceName);
  1229. if (service != null) {
  1230. var reply = {
  1231. name: (service.name ? service.name : ''),
  1232. status: (service.status ? service.status : ''),
  1233. startType: (service.startType ? service.startType : ''),
  1234. failureActions: (service.failureActions ? service.failureActions : ''),
  1235. installedDate: (service.installedDate ? service.installedDate : ''),
  1236. installedBy: (service.installedBy ? service.installedBy : '') ,
  1237. user: (service.user ? service.user : '')
  1238. };
  1239. if(reply.installedBy.indexOf('S-1-5') != -1) {
  1240. var cmd = "(Get-WmiObject -Class win32_userAccount -Filter \"SID='"+service.installedBy+"'\").Caption";
  1241. var replydata = "";
  1242. var pws = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], {});
  1243. pws.descriptorMetadata = 'UserSIDPowerShell';
  1244. pws.stdout.on('data', function (c) { replydata += c.toString(); });
  1245. pws.stderr.on('data', function (c) { replydata += c.toString(); });
  1246. pws.stdin.write(cmd + '\r\nexit\r\n');
  1247. pws.on('exit', function () {
  1248. if (replydata != "") reply.installedBy = replydata;
  1249. mesh.SendCommand({ action: 'msg', type: 'service', value: JSON.stringify(reply), sessionid: data.sessionid });
  1250. delete pws;
  1251. });
  1252. } else {
  1253. mesh.SendCommand({ action: 'msg', type: 'service', value: JSON.stringify(reply), sessionid: data.sessionid });
  1254. }
  1255. }
  1256. } catch (ex) {
  1257. mesh.SendCommand({ action: 'msg', type: 'service', error: ex, sessionid: data.sessionid })
  1258. }
  1259. }
  1260. case 'services': {
  1261. // Return the list of installed services
  1262. var services = null;
  1263. try { services = require('service-manager').manager.enumerateService(); } catch (ex) { }
  1264. if (services != null) { mesh.SendCommand({ action: 'msg', type: 'services', value: JSON.stringify(services), sessionid: data.sessionid }); }
  1265. break;
  1266. }
  1267. case 'serviceStop': {
  1268. // Stop a service
  1269. try {
  1270. var service = require('service-manager').manager.getService(data.serviceName);
  1271. if (service != null) { service.stop(); }
  1272. } catch (ex) { }
  1273. break;
  1274. }
  1275. case 'serviceStart': {
  1276. // Start a service
  1277. try {
  1278. var service = require('service-manager').manager.getService(data.serviceName);
  1279. if (service != null) { service.start(); }
  1280. } catch (ex) { }
  1281. break;
  1282. }
  1283. case 'serviceRestart': {
  1284. // Restart a service
  1285. try {
  1286. var service = require('service-manager').manager.getService(data.serviceName);
  1287. if (service != null) { service.restart(); }
  1288. } catch (ex) { }
  1289. break;
  1290. }
  1291. case 'deskBackground':
  1292. {
  1293. // Toggle desktop background
  1294. try {
  1295. if (process.platform == 'win32') {
  1296. var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
  1297. var sid = undefined;
  1298. if (stype == 1) {
  1299. if (require('MeshAgent')._tsid != null) {
  1300. stype = 5;
  1301. sid = require('MeshAgent')._tsid;
  1302. }
  1303. }
  1304. var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
  1305. var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: stype, uid: sid });
  1306. child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
  1307. child.stderr.on('data', function () { });
  1308. child.waitExit();
  1309. var current = child.stdout.str.trim();
  1310. if (current != '') { require('MeshAgent')._wallpaper = current; }
  1311. child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: stype, uid: sid });
  1312. child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
  1313. child.stderr.on('data', function () { });
  1314. child.waitExit();
  1315. mesh.SendCommand({ action: 'msg', type: 'deskBackground', sessionid: data.sessionid, data: (current != '' ? "" : require('MeshAgent')._wallpaper), });
  1316. } else {
  1317. var id = require('user-sessions').consoleUid();
  1318. var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
  1319. if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; }
  1320. require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
  1321. mesh.SendCommand({ action: 'msg', type: 'deskBackground', sessionid: data.sessionid, data: (current != '/dev/null' ? "" : require('MeshAgent')._wallpaper), });
  1322. }
  1323. } catch (ex) {
  1324. sendConsoleText(ex);
  1325. }
  1326. break;
  1327. }
  1328. case 'openUrl': {
  1329. // Open a local web browser and return success/fail
  1330. MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
  1331. sendConsoleText("OpenURL: " + data.url);
  1332. if (data.url) { mesh.SendCommand({ action: 'msg', type: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
  1333. break;
  1334. }
  1335. case 'getclip': {
  1336. // Send the load clipboard back to the user
  1337. //sendConsoleText('getClip: ' + JSON.stringify(data));
  1338. if (require('MeshAgent').isService) {
  1339. require('clipboard').dispatchRead().then(function (str) {
  1340. if (str) {
  1341. MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
  1342. mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
  1343. }
  1344. });
  1345. } else {
  1346. require('clipboard').read().then(function (str) {
  1347. if (str) {
  1348. MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
  1349. mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
  1350. }
  1351. });
  1352. }
  1353. break;
  1354. }
  1355. case 'setclip': {
  1356. if (pendingSetClip) return;
  1357. // Set the load clipboard to a user value
  1358. if (typeof data.data == 'string') {
  1359. MeshServerLogEx(22, [data.data.length], "Setting clipboard content, " + data.data.length + " byte(s)", data);
  1360. if (require('MeshAgent').isService) {
  1361. if (process.platform != 'win32') {
  1362. require('clipboard').dispatchWrite(data.data);
  1363. mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
  1364. }
  1365. else {
  1366. var clipargs = data.data;
  1367. var uid = require('user-sessions').consoleUid();
  1368. var user = require('user-sessions').getUsername(uid);
  1369. var domain = require('user-sessions').getDomain(uid);
  1370. user = (domain + '\\' + user);
  1371. if (this._dispatcher) { this._dispatcher.close(); }
  1372. this._dispatcher = require('win-dispatcher').dispatch({ user: user, modules: [{ name: 'clip-dispatch', script: "module.exports = { dispatch: function dispatch(val) { require('clipboard')(val); process.exit(); } };" }], launch: { module: 'clip-dispatch', method: 'dispatch', args: [clipargs] } });
  1373. this._dispatcher.parent = this;
  1374. //require('events').setFinalizerMetadata.call(this._dispatcher, 'clip-dispatch');
  1375. pendingSetClip = true;
  1376. this._dispatcher.on('connection', function (c) {
  1377. this._c = c;
  1378. this._c.root = this.parent;
  1379. this._c.on('end', function ()
  1380. {
  1381. pendingSetClip = false;
  1382. try { this.root._dispatcher.close(); } catch (ex) { }
  1383. this.root._dispatcher = null;
  1384. this.root = null;
  1385. mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
  1386. });
  1387. });
  1388. }
  1389. }
  1390. else {
  1391. require('clipboard')(data.data);
  1392. mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
  1393. } // Set the clipboard
  1394. }
  1395. break;
  1396. }
  1397. case 'userSessions': {
  1398. mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: data.sessionid, data: require('kvm-helper').users(), tag: data.tag });
  1399. break;
  1400. }
  1401. case 'cpuinfo':
  1402. // CPU & memory utilization
  1403. var cpuuse = require('sysinfo').cpuUtilization();
  1404. cpuuse.sessionid = data.sessionid;
  1405. cpuuse.tag = data.tag;
  1406. cpuuse.then(function (data) {
  1407. mesh.SendCommand(JSON.stringify(
  1408. {
  1409. action: 'msg',
  1410. type: 'cpuinfo',
  1411. cpu: data,
  1412. memory: require('sysinfo').memUtilization(),
  1413. thermals: require('sysinfo').thermals == null ? [] : require('sysinfo').thermals(),
  1414. sessionid: this.sessionid,
  1415. tag: this.tag
  1416. }));
  1417. }, function (ex) { });
  1418. break;
  1419. case 'localapp':
  1420. // Send a message to a local application
  1421. sendConsoleText('localappMsg: ' + data.appid + ', ' + JSON.stringify(data.value));
  1422. if (data.appid != null) { sendToRegisteredApp(data.appid, data.value); } else { broadcastToRegisteredApps(data.value); }
  1423. break;
  1424. case 'alertbox': {
  1425. // Display an old style alert box
  1426. if (data.title && data.msg) {
  1427. MeshServerLogEx(158, [data.title, data.msg], "Displaying alert box, title=" + data.title + ", message=" + data.msg, data);
  1428. try { require('message-box').create(data.title, data.msg, 9999, 1).then(function () { }).catch(function () { }); } catch (ex) { }
  1429. }
  1430. break;
  1431. }
  1432. case 'sysinfo': {
  1433. // Send system information
  1434. getSystemInformation(function (results) {
  1435. if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
  1436. });
  1437. break;
  1438. }
  1439. default:
  1440. // Unknown action, ignore it.
  1441. break;
  1442. }
  1443. break;
  1444. }
  1445. case 'acmactivate': {
  1446. if (amt != null) {
  1447. MeshServerLogEx(23, null, "Attempting Intel AMT ACM mode activation", data);
  1448. amt.setAcmResponse(data);
  1449. }
  1450. break;
  1451. }
  1452. case 'wakeonlan': {
  1453. // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
  1454. //sendConsoleText("Server requesting wake-on-lan for: " + data.macs.join(', '));
  1455. sendWakeOnLanEx(data.macs);
  1456. sendWakeOnLanEx(data.macs);
  1457. sendWakeOnLanEx(data.macs);
  1458. break;
  1459. }
  1460. case 'runcommands': {
  1461. if (mesh.cmdchild != null) { sendConsoleText("Run commands can't execute, already busy."); break; }
  1462. if (!data.reply) sendConsoleText("Run commands (" + data.runAsUser + "): " + data.cmds);
  1463. // data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly
  1464. var options = {};
  1465. if (data.runAsUser > 0) {
  1466. try { options.uid = require('user-sessions').consoleUid(); } catch (ex) { }
  1467. options.type = require('child_process').SpawnTypes.TERM;
  1468. }
  1469. if (data.runAsUser == 2) {
  1470. if (options.uid == null) break;
  1471. if (((require('user-sessions').minUid != null) && (options.uid < require('user-sessions').minUid()))) break; // This command can only run as user.
  1472. }
  1473. var replydata = "";
  1474. if (process.platform == 'win32') {
  1475. if (data.type == 1) {
  1476. // Windows command shell
  1477. mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd'], options);
  1478. mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
  1479. mesh.cmdchild.stdout.on('data', function (c) { replydata += c.toString(); sendConsoleText(c.toString()); });
  1480. mesh.cmdchild.stderr.on('data', function (c) { replydata += c.toString(); sendConsoleText(c.toString()); });
  1481. mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
  1482. mesh.cmdchild.on('exit', function () {
  1483. if (data.reply) {
  1484. mesh.SendCommand({ action: 'msg', type: 'runcommands', result: replydata, sessionid: data.sessionid, responseid: data.responseid });
  1485. } else {
  1486. sendConsoleText("Run commands completed.");
  1487. }
  1488. delete mesh.cmdchild;
  1489. });
  1490. } else if (data.type == 2) {
  1491. // Windows Powershell
  1492. mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], options);
  1493. mesh.cmdchild.descriptorMetadata = 'UserCommandsPowerShell';
  1494. mesh.cmdchild.stdout.on('data', function (c) { replydata += c.toString(); sendConsoleText(c.toString()); });
  1495. mesh.cmdchild.stderr.on('data', function (c) { replydata += c.toString(); sendConsoleText(c.toString()); });
  1496. mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
  1497. mesh.cmdchild.on('exit', function () {
  1498. if (data.reply) {
  1499. mesh.SendCommand({ action: 'msg', type: 'runcommands', result: replydata, sessionid: data.sessionid, responseid: data.responseid });
  1500. } else {
  1501. sendConsoleText("Run commands completed.");
  1502. }
  1503. delete mesh.cmdchild;
  1504. });
  1505. }
  1506. } else if (data.type == 3) {
  1507. // Linux shell
  1508. mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh'], options);
  1509. mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
  1510. mesh.cmdchild.stdout.on('data', function (c) { replydata += c.toString(); sendConsoleText(c.toString()); });
  1511. mesh.cmdchild.stderr.on('data', function (c) { replydata += c.toString(); sendConsoleText(c.toString()); });
  1512. mesh.cmdchild.stdin.write(data.cmds.split('\r').join('') + '\nexit\n');
  1513. mesh.cmdchild.on('exit', function () {
  1514. if (data.reply) {
  1515. mesh.SendCommand({ action: 'msg', type: 'runcommands', result: replydata, sessionid: data.sessionid, responseid: data.responseid });
  1516. } else {
  1517. sendConsoleText("Run commands completed.");
  1518. }
  1519. delete mesh.cmdchild;
  1520. });
  1521. }
  1522. break;
  1523. }
  1524. case 'uninstallagent':
  1525. // Uninstall this agent
  1526. var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
  1527. try {
  1528. agentName = require('MeshAgent').serviceName;
  1529. } catch (ex) { }
  1530. if (require('service-manager').manager.getService(agentName).isMe()) {
  1531. try { diagnosticAgent_uninstall(); } catch (ex) { }
  1532. var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
  1533. this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
  1534. }
  1535. break;
  1536. case 'poweraction': {
  1537. // Server telling us to execute a power action
  1538. if ((mesh.ExecPowerState != undefined) && (data.actiontype)) {
  1539. var forced = 0;
  1540. if (data.forced == 1) { forced = 1; }
  1541. data.actiontype = parseInt(data.actiontype);
  1542. MeshServerLogEx(25, [data.actiontype, forced], "Performing power action=" + data.actiontype + ", forced=" + forced, data);
  1543. sendConsoleText("Performing power action=" + data.actiontype + ", forced=" + forced + '.');
  1544. var r = mesh.ExecPowerState(data.actiontype, forced);
  1545. sendConsoleText("ExecPowerState returned code: " + r);
  1546. }
  1547. break;
  1548. }
  1549. case 'iplocation': {
  1550. // Update the IP location information of this node. Only do this when requested by the server since we have a limited amount of time we can call this per day
  1551. getIpLocationData(function (location) { mesh.SendCommand({ action: 'iplocation', type: 'publicip', value: location }); });
  1552. break;
  1553. }
  1554. case 'toast': {
  1555. // Display a toast message
  1556. if (data.title && data.msg) {
  1557. MeshServerLogEx(26, [data.title, data.msg], "Displaying toast message, title=" + data.title + ", message=" + data.msg, data);
  1558. data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
  1559. try { require('toaster').Toast(data.title, data.msg); } catch (ex) { }
  1560. }
  1561. break;
  1562. }
  1563. case 'openUrl': {
  1564. // Open a local web browser and return success/fail
  1565. //sendConsoleText('OpenURL: ' + data.url);
  1566. MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
  1567. if (data.url) { mesh.SendCommand({ action: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
  1568. break;
  1569. }
  1570. case 'amtconfig': {
  1571. // Perform Intel AMT activation and/or configuration
  1572. if ((apftunnel != null) || (amt == null) || (typeof data.user != 'string') || (typeof data.pass != 'string')) break;
  1573. amt.getMeiState(15, function (state) {
  1574. if ((apftunnel != null) || (amt == null)) return;
  1575. if ((state == null) || (state.ProvisioningState == null)) return;
  1576. if ((state.UUID == null) || (state.UUID.length != 36)) return; // Bad UUID
  1577. getAmtOsDnsSuffix(state, function () {
  1578. var apfarg = {
  1579. mpsurl: mesh.ServerUrl.replace('/agent.ashx', '/apf.ashx'),
  1580. mpsuser: data.user, // Agent user name
  1581. mpspass: data.pass, // Encrypted login cookie
  1582. mpskeepalive: 60000,
  1583. clientname: state.OsHostname,
  1584. clientaddress: '127.0.0.1',
  1585. clientuuid: state.UUID,
  1586. conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
  1587. meiState: state // MEI state will be passed to MPS server
  1588. };
  1589. addAmtEvent('LMS tunnel start.');
  1590. apftunnel = require('amt-apfclient')({ debug: false }, apfarg);
  1591. apftunnel.onJsonControl = handleApfJsonControl;
  1592. apftunnel.onChannelClosed = function () { addAmtEvent('LMS tunnel closed.'); apftunnel = null; }
  1593. try { apftunnel.connect(); } catch (ex) { }
  1594. });
  1595. });
  1596. break;
  1597. }
  1598. case 'getScript': {
  1599. // Received a configuration script from the server
  1600. sendConsoleText('getScript: ' + JSON.stringify(data));
  1601. break;
  1602. }
  1603. case 'sysinfo': {
  1604. // Fetch system information
  1605. getSystemInformation(function (results) {
  1606. if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
  1607. });
  1608. break;
  1609. }
  1610. case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
  1611. case 'pong': { break; }
  1612. case 'plugin': {
  1613. try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (ex) { throw ex; }
  1614. break;
  1615. }
  1616. case 'coredump':
  1617. // Set the current agent coredump situation.s
  1618. if (data.value === true) {
  1619. if (process.platform == 'win32') {
  1620. // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
  1621. process.coreDumpLocation = process.execPath.replace('.exe', '.dmp');
  1622. } else {
  1623. process.coreDumpLocation = (process.cwd() != '//') ? (process.cwd() + 'core') : null;
  1624. }
  1625. } else if (data.value === false) {
  1626. process.coreDumpLocation = null;
  1627. }
  1628. break;
  1629. case 'getcoredump':
  1630. // Ask the agent if a core dump is currently available, if yes, also return the hash of the agent.
  1631. var r = { action: 'getcoredump', value: (process.coreDumpLocation != null) };
  1632. var coreDumpPath = null;
  1633. if (process.platform == 'win32') { coreDumpPath = process.coreDumpLocation; } else { coreDumpPath = (process.cwd() != '//') ? fs.existsSync(process.cwd() + 'core') : null; }
  1634. if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath))) {
  1635. try {
  1636. var coredate = fs.statSync(coreDumpPath).mtime;
  1637. var coretime = new Date(coredate).getTime();
  1638. var agenttime = new Date(fs.statSync(process.execPath).mtime).getTime();
  1639. if (coretime > agenttime) { r.exists = (db.Get('CoreDumpTime') != coredate); }
  1640. } catch (ex) { }
  1641. }
  1642. if (r.exists == true) {
  1643. r.agenthashhex = getSHA384FileHash(process.execPath).toString('hex'); // Hash of current agent
  1644. r.corehashhex = getSHA384FileHash(coreDumpPath).toString('hex'); // Hash of core dump file
  1645. }
  1646. mesh.SendCommand(JSON.stringify(r));
  1647. break;
  1648. case 'meshToolInfo':
  1649. if (data.pipe == true) { delete data.pipe; delete data.action; data.cmd = 'meshToolInfo'; broadcastToRegisteredApps(data); }
  1650. if (data.tag == 'info') { sendConsoleText(JSON.stringify(data, null, 2)); }
  1651. if (data.tag == 'install') {
  1652. data.func = function (options, success) {
  1653. sendConsoleText('Download of MeshCentral Assistant ' + (success ? 'succeed' : 'failed'));
  1654. if (success) {
  1655. // TODO: Install & Run
  1656. }
  1657. }
  1658. data.filename = 'MeshAssistant.exe';
  1659. downloadFile(data);
  1660. }
  1661. break;
  1662. case 'getUserImage':
  1663. if (data.pipe == true) { delete data.pipe; delete data.action; data.cmd = 'getUserImage'; broadcastToRegisteredApps(data); }
  1664. if (data.tag == 'info') { sendConsoleText(JSON.stringify(data, null, 2)); }
  1665. if (data.promise != null && require('MeshAgent')._promises[data.promise] != null) {
  1666. var p = require('MeshAgent')._promises[data.promise];
  1667. delete require('MeshAgent')._promises[data.promise];
  1668. p.resolve(data.image);
  1669. }
  1670. break;
  1671. case 'wget': // Server uses this command to tell the agent to download a file using HTTPS/GET and place it in a given path. This is used for one-to-many file uploads.
  1672. agentFileHttpPendingRequests.push(data);
  1673. serverFetchFile();
  1674. break;
  1675. case 'serverInfo': // Server information
  1676. obj.serverInfo = data;
  1677. delete obj.serverInfo.action;
  1678. break;
  1679. case 'errorlog': // Return agent error log
  1680. try { mesh.SendCommand(JSON.stringify({ action: 'errorlog', log: require('util-agentlog').read(data.startTime) })); } catch (ex) { }
  1681. break;
  1682. default:
  1683. // Unknown action, ignore it.
  1684. break;
  1685. }
  1686. }
  1687. }
  1688. // On non-Windows platforms, we need to query the DHCP server for the DNS suffix
  1689. function getAmtOsDnsSuffix(mestate, func) {
  1690. if ((process.platform == 'win32') || (mestate.net0 == null) || (mestate.net0.mac == null)) { func(mestate); return; }
  1691. try { require('linux-dhcp') } catch (ex) { func(mestate); return; }
  1692. require('linux-dhcp').client.info(mestate.net0.mac).then(function (d) {
  1693. if ((typeof d.options == 'object') && (typeof d.options.domainname == 'string')) { mestate.OsDnsSuffix = d.options.domainname; }
  1694. func(mestate);
  1695. }, function (e) {
  1696. console.log('DHCP error', e);
  1697. func(mestate);
  1698. });
  1699. }
  1700. // Download a file from the server and check the hash.
  1701. // This download is similar to the one used for meshcore self-update.
  1702. var trustedDownloads = {};
  1703. function downloadFile(downloadoptions) {
  1704. var options = require('http').parseUri(downloadoptions.url);
  1705. options.rejectUnauthorized = false;
  1706. options.checkServerIdentity = function checkServerIdentity(certs) {
  1707. // If the tunnel certificate matches the control channel certificate, accept the connection
  1708. try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
  1709. try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
  1710. // Check that the certificate is the one expected by the server, fail if not.
  1711. if (checkServerIdentity.servertlshash == null) { if (require('MeshAgent').ServerInfo == null || require('MeshAgent').ServerInfo.ControlChannelCertificate == null) return; throw new Error('BadCert'); }
  1712. if (certs[0].digest == null) return;
  1713. if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
  1714. }
  1715. //options.checkServerIdentity.servertlshash = downloadoptions.serverhash;
  1716. trustedDownloads[downloadoptions.name] = downloadoptions;
  1717. trustedDownloads[downloadoptions.name].dl = require('https').get(options);
  1718. trustedDownloads[downloadoptions.name].dl.on('error', function (e) { downloadoptions.func(downloadoptions, false); delete trustedDownloads[downloadoptions.name]; });
  1719. trustedDownloads[downloadoptions.name].dl.on('response', function (img) {
  1720. this._file = require('fs').createWriteStream(trustedDownloads[downloadoptions.name].filename, { flags: 'wb' });
  1721. this._filehash = require('SHA384Stream').create();
  1722. this._filehash.on('hash', function (h) { if ((downloadoptions.hash != null) && (downloadoptions.hash.toLowerCase() != h.toString('hex').toLowerCase())) { downloadoptions.func(downloadoptions, false); delete trustedDownloads[downloadoptions.name]; return; } downloadoptions.func(downloadoptions, true); });
  1723. img.pipe(this._file);
  1724. img.pipe(this._filehash);
  1725. });
  1726. }
  1727. // Handle APF JSON control commands
  1728. function handleApfJsonControl(data) {
  1729. if (data.action == 'console') { addAmtEvent(data.msg); } // Add console message to AMT event log
  1730. if (data.action == 'mestate') { amt.getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
  1731. if (data.action == 'close') { try { apftunnel.disconnect(); } catch (ex) { } apftunnel = null; } // Close the CIRA-LMS connection
  1732. if (amt.amtMei != null) {
  1733. if (data.action == 'deactivate') { // Request CCM deactivation
  1734. amt.amtMei.unprovision(1, function (status) { if (apftunnel) apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
  1735. }
  1736. if (data.action == 'startTlsHostConfig') { // Request start of host based TLS ACM activation
  1737. amt.amtMei.startConfigurationHBased(Buffer.from(data.hash, 'hex'), data.hostVpn, data.dnsSuffixList, function (response) { apftunnel.sendStartTlsHostConfigResponse(response); });
  1738. }
  1739. if (data.action == 'stopConfiguration') { // Request Intel AMT stop configuration.
  1740. amt.amtMei.stopConfiguration(function (status) { apftunnel.sendStopConfigurationResponse(status); });
  1741. }
  1742. }
  1743. }
  1744. // Agent just get a file from the server and save it locally.
  1745. function serverFetchFile() {
  1746. if ((Object.keys(agentFileHttpRequests).length > 4) || (agentFileHttpPendingRequests.length == 0)) return; // No more than 4 active HTTPS requests to the server.
  1747. var data = agentFileHttpPendingRequests.shift();
  1748. if ((data.overwrite !== true) && fs.existsSync(data.path)) return; // Don't overwrite an existing file.
  1749. if (data.createFolder) { try { fs.mkdirSync(data.folder); } catch (ex) { } } // If requested, create the local folder.
  1750. data.url = 'http' + getServerTargetUrlEx('*/').substring(2);
  1751. var agentFileHttpOptions = http.parseUri(data.url);
  1752. agentFileHttpOptions.path = data.urlpath;
  1753. // Perform manual server TLS certificate checking based on the certificate hash given by the server.
  1754. agentFileHttpOptions.rejectUnauthorized = 0;
  1755. agentFileHttpOptions.checkServerIdentity = function checkServerIdentity(certs) {
  1756. // If the tunnel certificate matches the control channel certificate, accept the connection
  1757. try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
  1758. try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
  1759. // Check that the certificate is the one expected by the server, fail if not.
  1760. if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
  1761. }
  1762. agentFileHttpOptions.checkServerIdentity.servertlshash = data.servertlshash;
  1763. if (agentFileHttpOptions == null) return;
  1764. var agentFileHttpRequest = http.request(agentFileHttpOptions,
  1765. function (response) {
  1766. response.xparent = this;
  1767. try {
  1768. response.xfile = fs.createWriteStream(this.xpath, { flags: 'wbN' })
  1769. response.pipe(response.xfile);
  1770. response.end = function () { delete agentFileHttpRequests[this.xparent.xurlpath]; delete this.xparent; serverFetchFile(); }
  1771. } catch (ex) { delete agentFileHttpRequests[this.xurlpath]; delete response.xparent; serverFetchFile(); return; }
  1772. }
  1773. );
  1774. agentFileHttpRequest.on('error', function (ex) { sendConsoleText(ex); delete agentFileHttpRequests[this.xurlpath]; serverFetchFile(); });
  1775. agentFileHttpRequest.end();
  1776. agentFileHttpRequest.xurlpath = data.urlpath;
  1777. agentFileHttpRequest.xpath = data.path;
  1778. agentFileHttpRequests[data.urlpath] = agentFileHttpRequest;
  1779. }
  1780. // Called when a file changed in the file system
  1781. /*
  1782. function onFileWatcher(a, b) {
  1783. console.log('onFileWatcher', a, b, this.path);
  1784. var response = getDirectoryInfo(this.path);
  1785. if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
  1786. }
  1787. */
  1788. // Replace all key name spaces with _ in an object recursively.
  1789. // This is a workaround since require('computer-identifiers').get() returns key names with spaces in them on Linux.
  1790. function replaceSpacesWithUnderscoresRec(o) {
  1791. if (typeof o != 'object') return;
  1792. for (var i in o) { if (i.indexOf(' ') >= 0) { o[i.split(' ').join('_')] = o[i]; delete o[i]; } replaceSpacesWithUnderscoresRec(o[i]); }
  1793. }
  1794. function getSystemInformation(func) {
  1795. try {
  1796. var results = { hardware: require('computer-identifiers').get() }; // Hardware info
  1797. if (results.hardware && results.hardware.windows) {
  1798. // Remove extra entries and things that change quickly
  1799. var x = results.hardware.windows.osinfo;
  1800. try { delete x.FreePhysicalMemory; } catch (ex) { }
  1801. try { delete x.FreeSpaceInPagingFiles; } catch (ex) { }
  1802. try { delete x.FreeVirtualMemory; } catch (ex) { }
  1803. try { delete x.LocalDateTime; } catch (ex) { }
  1804. try { delete x.MaxProcessMemorySize; } catch (ex) { }
  1805. try { delete x.TotalVirtualMemorySize; } catch (ex) { }
  1806. try { delete x.TotalVisibleMemorySize; } catch (ex) { }
  1807. try {
  1808. if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } }
  1809. if (results.hardware.windows.osinfo) {
  1810. delete results.hardware.windows.osinfo.Node;
  1811. results.hardware.windows.osinfo.Domain = getDomainInfo().Domain;
  1812. results.hardware.windows.osinfo.PartOfDomain = getDomainInfo().PartOfDomain;
  1813. }
  1814. if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } }
  1815. } catch (ex) { }
  1816. if (x.LastBootUpTime) { // detect windows uptime
  1817. var thedate = {
  1818. year: parseInt(x.LastBootUpTime.substring(0, 4)),
  1819. month: parseInt(x.LastBootUpTime.substring(4, 6)) - 1, // Months are 0-based in JavaScript (0 - January, 11 - December)
  1820. day: parseInt(x.LastBootUpTime.substring(6, 8)),
  1821. hours: parseInt(x.LastBootUpTime.substring(8, 10)),
  1822. minutes: parseInt(x.LastBootUpTime.substring(10, 12)),
  1823. seconds: parseInt(x.LastBootUpTime.substring(12, 14)),
  1824. };
  1825. var thelastbootuptime = new Date(thedate.year, thedate.month, thedate.day, thedate.hours, thedate.minutes, thedate.seconds);
  1826. meshCoreObj.lastbootuptime = thelastbootuptime.getTime(); // store the last boot up time in coreinfo for columns
  1827. meshCoreObjChanged();
  1828. var nowtime = new Date();
  1829. var differenceInMilliseconds = Math.abs(thelastbootuptime - nowtime);
  1830. if (differenceInMilliseconds < 300000) { // computer uptime less than 5 minutes
  1831. MeshServerLogEx(159, [thelastbootuptime.toString()], "Device Powered On", null);
  1832. }
  1833. }
  1834. }
  1835. if(results.hardware && results.hardware.linux) {
  1836. if(results.hardware.linux.LastBootUpTime) {
  1837. var thelastbootuptime = new Date(results.hardware.linux.LastBootUpTime);
  1838. meshCoreObj.lastbootuptime = thelastbootuptime.getTime(); // store the last boot up time in coreinfo for columns
  1839. meshCoreObjChanged();
  1840. var nowtime = new Date();
  1841. var differenceInMilliseconds = Math.abs(thelastbootuptime - nowtime);
  1842. if (differenceInMilliseconds < 300000) { // computer uptime less than 5 minutes
  1843. MeshServerLogEx(159, [thelastbootuptime.toString()], "Device Powered On", null);
  1844. }
  1845. }
  1846. }
  1847. if(results.hardware && results.hardware.darwin){
  1848. if(results.hardware.darwin.LastBootUpTime) {
  1849. var thelastbootuptime = new Date(results.hardware.darwin.LastBootUpTime * 1000); // must times by 1000 even tho timestamp is correct?
  1850. meshCoreObj.lastbootuptime = thelastbootuptime.getTime(); // store the last boot up time in coreinfo for columns
  1851. meshCoreObjChanged();
  1852. var nowtime = new Date();
  1853. var differenceInMilliseconds = Math.abs(thelastbootuptime - nowtime);
  1854. if (differenceInMilliseconds < 300000) { // computer uptime less than 5 minutes
  1855. MeshServerLogEx(159, [thelastbootuptime.toString()], "Device Powered On", null);
  1856. }
  1857. }
  1858. }
  1859. results.hardware.agentvers = process.versions;
  1860. results.hardware.network = { dns: require('os').dns() };
  1861. replaceSpacesWithUnderscoresRec(results);
  1862. var hasher = require('SHA384Stream').create();
  1863. // On Windows platforms, get volume information - Needs more testing.
  1864. if (process.platform == 'win32')
  1865. {
  1866. results.pendingReboot = require('win-info').pendingReboot(); // Pending reboot
  1867. if (require('win-volumes').volumes_promise != null)
  1868. {
  1869. var p = require('win-volumes').volumes_promise();
  1870. p.then(function (res)
  1871. {
  1872. results.hardware.windows.volumes = cleanGetBitLockerVolumeInfo(res);
  1873. results.hash = hasher.syncHash(JSON.stringify(results)).toString('hex');
  1874. func(results);
  1875. });
  1876. }
  1877. else
  1878. {
  1879. results.hash = hasher.syncHash(JSON.stringify(results)).toString('hex');
  1880. func(results);
  1881. }
  1882. }
  1883. else
  1884. {
  1885. results.hash = hasher.syncHash(JSON.stringify(results)).toString('hex');
  1886. func(results);
  1887. }
  1888. } catch (ex) { func(null, ex); }
  1889. }
  1890. // Get a formated response for a given directory path
  1891. function getDirectoryInfo(reqpath) {
  1892. var response = { path: reqpath, dir: [] };
  1893. if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
  1894. // List all the drives in the root, or the root itself
  1895. var results = null;
  1896. try { results = fs.readDrivesSync(); } catch (ex) { }
  1897. if (results != null) {
  1898. for (var i = 0; i < results.length; ++i) {
  1899. var drive = { n: results[i].name, t: 1, dt: results[i].type, s: (results[i].size ? results[i].size : 0), f: (results[i].free ? results[i].free : 0) };
  1900. response.dir.push(drive);
  1901. }
  1902. }
  1903. } else {
  1904. // List all the files and folders in this path
  1905. if (reqpath == '') { reqpath = '/'; }
  1906. var results = null, xpath = obj.path.join(reqpath, '*');
  1907. //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
  1908. try { results = fs.readdirSync(xpath); } catch (ex) { }
  1909. try { if ((results != null) && (results.length == 0) && (fs.existsSync(reqpath) == false)) { results = null; } } catch (ex) { }
  1910. if (results != null) {
  1911. for (var i = 0; i < results.length; ++i) {
  1912. if ((results[i] != '.') && (results[i] != '..')) {
  1913. var stat = null, p = obj.path.join(reqpath, results[i]);
  1914. //if (process.platform == "win32") { p = p.split('/').join('\\'); }
  1915. try { stat = fs.statSync(p); } catch (ex) { } // TODO: Get file size/date
  1916. if ((stat != null) && (stat != undefined)) {
  1917. if (stat.isDirectory() == true) {
  1918. response.dir.push({ n: results[i], t: 2, d: stat.mtime });
  1919. } else {
  1920. response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
  1921. }
  1922. }
  1923. }
  1924. }
  1925. } else {
  1926. response.dir = null;
  1927. }
  1928. }
  1929. return response;
  1930. }
  1931. function tunnel_s_finalized()
  1932. {
  1933. console.info1('Tunnel Socket Finalized');
  1934. }
  1935. function tunnel_onIdleTimeout()
  1936. {
  1937. this.ping();
  1938. this.setTimeout(require('MeshAgent').idleTimeout * 1000);
  1939. }
  1940. // Tunnel callback operations
  1941. function onTunnelUpgrade(response, s, head)
  1942. {
  1943. this.s = s;
  1944. s.once('~', tunnel_s_finalized);
  1945. s.httprequest = this;
  1946. s.end = onTunnelClosed;
  1947. s.tunnel = this;
  1948. s.descriptorMetadata = "MeshAgent_relayTunnel";
  1949. if (require('MeshAgent').idleTimeout != null)
  1950. {
  1951. s.setTimeout(require('MeshAgent').idleTimeout * 1000);
  1952. s.on('timeout', tunnel_onIdleTimeout);
  1953. }
  1954. //sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
  1955. if (this.tcpport != null) {
  1956. // This is a TCP relay connection, pause now and try to connect to the target.
  1957. s.pause();
  1958. s.data = onTcpRelayServerTunnelData;
  1959. var connectionOptions = { port: parseInt(this.tcpport) };
  1960. if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
  1961. s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
  1962. s.tcprelay.peerindex = this.index;
  1963. // Add the TCP session to the count and update the server
  1964. if (s.httprequest.userid != null) {
  1965. var userid = getUserIdAndGuestNameFromHttpRequest(s.httprequest);
  1966. if (tunnelUserCount.tcp[userid] == null) { tunnelUserCount.tcp[userid] = 1; } else { tunnelUserCount.tcp[userid]++; }
  1967. try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (ex) { }
  1968. broadcastSessionsToRegisteredApps();
  1969. }
  1970. }
  1971. if (this.udpport != null) {
  1972. // This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
  1973. s.data = onUdpRelayServerTunnelData;
  1974. s.udprelay = require('dgram').createSocket({ type: 'udp4' });
  1975. s.udprelay.bind({ port: 0 });
  1976. s.udprelay.peerindex = this.index;
  1977. s.udprelay.on('message', onUdpRelayTargetTunnelConnect);
  1978. s.udprelay.udpport = this.udpport;
  1979. s.udprelay.udpaddr = this.udpaddr;
  1980. s.udprelay.first = true;
  1981. // Add the UDP session to the count and update the server
  1982. if (s.httprequest.userid != null) {
  1983. var userid = getUserIdAndGuestNameFromHttpRequest(s.httprequest);
  1984. if (tunnelUserCount.udp[userid] == null) { tunnelUserCount.udp[userid] = 1; } else { tunnelUserCount.udp[userid]++; }
  1985. try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.tcp }); } catch (ex) { }
  1986. broadcastSessionsToRegisteredApps();
  1987. }
  1988. }
  1989. else {
  1990. // This is a normal connect for KVM/Terminal/Files
  1991. s.data = onTunnelData;
  1992. }
  1993. }
  1994. // If the HTTP Request has a guest name, we need to form a userid that includes the guest name in hex.
  1995. // This is so we can tell the server that a session is for a given userid/guest sharing pair.
  1996. function getUserIdAndGuestNameFromHttpRequest(request) {
  1997. if (request.guestname == null) return request.userid; else return request.guestuserid + '/guest:' + Buffer.from(request.guestname).toString('base64');
  1998. }
  1999. // Called when UDP relay data is received // TODO****
  2000. function onUdpRelayTargetTunnelConnect(data) {
  2001. var peerTunnel = tunnels[this.peerindex];
  2002. peerTunnel.s.write(data);
  2003. }
  2004. // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
  2005. function onUdpRelayServerTunnelData(data) {
  2006. if (this.udprelay.first === true) {
  2007. delete this.udprelay.first; // Skip the first 'c' that is received.
  2008. } else {
  2009. this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1');
  2010. }
  2011. }
  2012. // Called when the TCP relay target is connected
  2013. function onTcpRelayTargetTunnelConnect() {
  2014. var peerTunnel = tunnels[this.peerindex];
  2015. this.pipe(peerTunnel.s); // Pipe Target --> Server
  2016. peerTunnel.s.first = true;
  2017. peerTunnel.s.resume();
  2018. }
  2019. // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
  2020. function onTcpRelayServerTunnelData(data) {
  2021. if (this.first == true) {
  2022. this.first = false;
  2023. this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames)
  2024. }
  2025. }
  2026. function onTunnelClosed()
  2027. {
  2028. if (this.httprequest._dispatcher != null && this.httprequest.term == null)
  2029. {
  2030. // Windows Dispatcher was created to spawn a child connection, but the child didn't connect yet, so we have to shutdown the dispatcher, otherwise the child may end up hanging
  2031. if (this.httprequest._dispatcher.close) { this.httprequest._dispatcher.close(); }
  2032. this.httprequest._dispatcher = null;
  2033. }
  2034. if (this.tunnel)
  2035. {
  2036. if (tunnels[this.httprequest.index] == null)
  2037. {
  2038. this.tunnel.s = null;
  2039. this.tunnel = null;
  2040. return;
  2041. }
  2042. }
  2043. var tunnel = tunnels[this.httprequest.index];
  2044. if (tunnel == null) return; // Stop duplicate calls.
  2045. // Perform display locking on disconnect
  2046. if ((this.httprequest.protocol == 2) && (this.httprequest.autolock === true)) {
  2047. // Look for a TSID
  2048. var tsid = null;
  2049. if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
  2050. // Lock the current user out of the desktop
  2051. MeshServerLogEx(53, null, "Locking remote user out of desktop", this.httprequest);
  2052. lockDesktop(tsid);
  2053. }
  2054. // If this is a routing session, clean up and send the new session counts.
  2055. if (this.httprequest.userid != null) {
  2056. if (this.httprequest.tcpport != null) {
  2057. var userid = getUserIdAndGuestNameFromHttpRequest(this.httprequest);
  2058. if (tunnelUserCount.tcp[userid] != null) { tunnelUserCount.tcp[userid]--; if (tunnelUserCount.tcp[userid] <= 0) { delete tunnelUserCount.tcp[userid]; } }
  2059. try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (ex) { }
  2060. broadcastSessionsToRegisteredApps();
  2061. } else if (this.httprequest.udpport != null) {
  2062. var userid = getUserIdAndGuestNameFromHttpRequest(this.httprequest);
  2063. if (tunnelUserCount.udp[userid] != null) { tunnelUserCount.udp[userid]--; if (tunnelUserCount.udp[userid] <= 0) { delete tunnelUserCount.udp[userid]; } }
  2064. try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.udp }); } catch (ex) { }
  2065. broadcastSessionsToRegisteredApps();
  2066. }
  2067. }
  2068. try {
  2069. // Sent tunnel statistics to the server, only send this if compression was used.
  2070. if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString())) {
  2071. mesh.SendCommand({
  2072. action: 'tunnelCloseStats',
  2073. url: tunnel.url,
  2074. userid: tunnel.userid,
  2075. protocol: tunnel.protocol,
  2076. sessionid: tunnel.sessionid,
  2077. sent: this.bytesSent_uncompressed.toString(),
  2078. sentActual: this.bytesSent_actual.toString(),
  2079. sentRatio: this.bytesSent_ratio,
  2080. received: this.bytesReceived_uncompressed.toString(),
  2081. receivedActual: this.bytesReceived_actual.toString(),
  2082. receivedRatio: this.bytesReceived_ratio
  2083. });
  2084. }
  2085. } catch (ex) { }
  2086. //sendConsoleText("Tunnel #" + this.httprequest.index + " closed. Sent -> " + this.bytesSent_uncompressed + ' bytes (uncompressed), ' + this.bytesSent_actual + ' bytes (actual), ' + this.bytesSent_ratio + '% compression', this.httprequest.sessionid);
  2087. /*
  2088. // Close the watcher if required
  2089. if (this.httprequest.watcher != undefined) {
  2090. //console.log('Closing watcher: ' + this.httprequest.watcher.path);
  2091. //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
  2092. delete this.httprequest.watcher;
  2093. }
  2094. */
  2095. // If there is a upload or download active on this connection, close the file
  2096. if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; delete this.httprequest.uploadFileid; delete this.httprequest.uploadFilePath; delete this.httprequest.uploadFileSize; }
  2097. if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; }
  2098. // Clean up WebRTC
  2099. if (this.webrtc != null) {
  2100. if (this.webrtc.rtcchannel) { try { this.webrtc.rtcchannel.close(); } catch (ex) { } this.webrtc.rtcchannel.removeAllListeners('data'); this.webrtc.rtcchannel.removeAllListeners('end'); delete this.webrtc.rtcchannel; }
  2101. if (this.webrtc.websocket) { delete this.webrtc.websocket; }
  2102. try { this.webrtc.close(); } catch (ex) { }
  2103. this.webrtc.removeAllListeners('connected');
  2104. this.webrtc.removeAllListeners('disconnected');
  2105. this.webrtc.removeAllListeners('dataChannel');
  2106. delete this.webrtc;
  2107. }
  2108. // Clean up WebSocket
  2109. delete tunnels[this.httprequest.index];
  2110. tunnel = null;
  2111. this.tunnel.s = null;
  2112. this.tunnel = null;
  2113. this.removeAllListeners('data');
  2114. }
  2115. function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
  2116. function terminal_onconnection (c)
  2117. {
  2118. if (this.httprequest.connectionPromise.completed)
  2119. {
  2120. c.end();
  2121. }
  2122. else
  2123. {
  2124. this.httprequest.connectionPromise._res(c);
  2125. }
  2126. }
  2127. function terminal_user_onconnection(c)
  2128. {
  2129. console.info1('completed-2: ' + this.connectionPromise.completed);
  2130. if (this.connectionPromise.completed)
  2131. {
  2132. c.end();
  2133. }
  2134. else
  2135. {
  2136. this.connectionPromise._res(c);
  2137. }
  2138. }
  2139. function terminal_stderr_ondata(c)
  2140. {
  2141. this.stdout.write(c);
  2142. }
  2143. function terminal_onend()
  2144. {
  2145. this.httprequest.process.kill();
  2146. }
  2147. function terminal_onexit()
  2148. {
  2149. this.tunnel.end();
  2150. }
  2151. function terminal_onfinalized()
  2152. {
  2153. this.httprequest = null;
  2154. console.info1('Dispatcher Finalized');
  2155. }
  2156. function terminal_end()
  2157. {
  2158. if (this.httprequest == null) { return; }
  2159. if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); }
  2160. if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); }
  2161. // Remove the terminal session to the count to update the server
  2162. if (this.httprequest.userid != null)
  2163. {
  2164. var userid = getUserIdAndGuestNameFromHttpRequest(this.httprequest);
  2165. if (tunnelUserCount.terminal[userid] != null) { tunnelUserCount.terminal[userid]--; if (tunnelUserCount.terminal[userid] <= 0) { delete tunnelUserCount.terminal[userid]; } }
  2166. try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (ex) { }
  2167. broadcastSessionsToRegisteredApps();
  2168. }
  2169. if (process.platform == 'win32')
  2170. {
  2171. // Unpipe the web socket
  2172. this.unpipe(this.httprequest._term);
  2173. if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
  2174. // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
  2175. if (this.rtcchannel)
  2176. {
  2177. this.rtcchannel.unpipe(this.httprequest._term);
  2178. if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
  2179. }
  2180. // Clean up
  2181. if (this.httprequest._term) { this.httprequest._term.end(); }
  2182. this.httprequest._term = null;
  2183. this.httprequest._dispatcher = null;
  2184. }
  2185. this.httprequest = null;
  2186. }
  2187. function terminal_consent_ask(ws) {
  2188. ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
  2189. var consentMessage = currentTranslation['terminalConsent'].replace(/\{0\}/g, ws.httprequest.realname).replace(/\{1\}/g, ws.httprequest.username);
  2190. var consentTitle = 'MeshCentral';
  2191. if (ws.httprequest.soptions != null) {
  2192. if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
  2193. if (ws.httprequest.soptions.consentMsgTerminal != null) { consentMessage = ws.httprequest.soptions.consentMsgTerminal.replace(/\{0\}/g, ws.httprequest.realname).replace(/\{1\}/g, ws.httprequest.username); }
  2194. }
  2195. if (process.platform == 'win32') {
  2196. var enhanced = false;
  2197. if (ws.httprequest.oldStyle === false) {
  2198. try { require('win-userconsent'); enhanced = true; } catch (ex) { }
  2199. }
  2200. if (enhanced) {
  2201. var ipr = server_getUserImage(ws.httprequest.userid);
  2202. ipr.consentTitle = consentTitle;
  2203. ipr.consentMessage = consentMessage;
  2204. ipr.consentTimeout = ws.httprequest.consentTimeout;
  2205. ipr.consentAutoAccept = ws.httprequest.consentAutoAccept;
  2206. ipr.username = ws.httprequest.realname;
  2207. ipr.tsid = ws.tsid;
  2208. ipr.translations = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
  2209. ws.httprequest.tpromise._consent = ipr.then(function (img) {
  2210. this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), uid: this.tsid, timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translations, background: color_options.background, foreground: color_options.foreground });
  2211. this.__childPromise.close = this.consent.close.bind(this.consent);
  2212. return (this.consent);
  2213. });
  2214. } else {
  2215. ws.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout);
  2216. }
  2217. } else {
  2218. ws.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout);
  2219. }
  2220. ws.httprequest.tpromise._consent.retPromise = ws.httprequest.tpromise;
  2221. ws.httprequest.tpromise._consent.then(function (always) {
  2222. if (always && process.platform == 'win32') { server_set_consentTimer(this.retPromise.httprequest.userid); }
  2223. // Success
  2224. MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
  2225. this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
  2226. this.retPromise._consent = null;
  2227. this.retPromise._res();
  2228. }, function (e) {
  2229. if (this.retPromise.that) {
  2230. if(this.retPromise.that.httprequest){ // User Consent Denied
  2231. MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
  2232. } else { } // Connection was closed server side, maybe log some messages somewhere?
  2233. this.retPromise._consent = null;
  2234. this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
  2235. } else { } // no websocket, maybe log some messages somewhere?
  2236. this.retPromise._rej(e.toString());
  2237. });
  2238. }
  2239. function terminal_promise_connection_rejected(e)
  2240. {
  2241. // FAILED to connect terminal
  2242. this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
  2243. this.ws.end();
  2244. }
  2245. function terminal_promise_connection_resolved(term)
  2246. {
  2247. this._internal.completedArgs = [];
  2248. // SUCCESS
  2249. var stdoutstream;
  2250. var stdinstream;
  2251. if (process.platform == 'win32')
  2252. {
  2253. this.ws.httprequest._term = term;
  2254. this.ws.httprequest._term.tunnel = this.ws;
  2255. stdoutstream = stdinstream = term;
  2256. }
  2257. else
  2258. {
  2259. term.descriptorMetadata = 'Remote Terminal';
  2260. this.ws.httprequest.process = term;
  2261. this.ws.httprequest.process.tunnel = this.ws;
  2262. term.stderr.stdout = term.stdout;
  2263. term.stderr.on('data', terminal_stderr_ondata);
  2264. stdoutstream = term.stdout;
  2265. stdinstream = term.stdin;
  2266. this.ws.prependListener('end', terminal_onend);
  2267. term.prependListener('exit', terminal_onexit);
  2268. }
  2269. this.ws.removeAllListeners('data');
  2270. this.ws.on('data', onTunnelControlData);
  2271. stdoutstream.pipe(this.ws, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
  2272. this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
  2273. // Add the terminal session to the count to update the server
  2274. if (this.ws.httprequest.userid != null)
  2275. {
  2276. var userid = getUserIdAndGuestNameFromHttpRequest(this.ws.httprequest);
  2277. if (tunnelUserCount.terminal[userid] == null) { tunnelUserCount.terminal[userid] = 1; } else { tunnelUserCount.terminal[userid]++; }
  2278. try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (ex) { }
  2279. broadcastSessionsToRegisteredApps();
  2280. }
  2281. // Toast Notification, if required
  2282. if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2))
  2283. {
  2284. // User Notifications is required
  2285. var notifyMessage = currentTranslation['terminalNotify'].replace(/\{0\}/g, this.ws.httprequest.realname ? this.ws.httprequest.realname : this.ws.httprequest.username);
  2286. var notifyTitle = "MeshCentral";
  2287. if (this.ws.httprequest.soptions != null)
  2288. {
  2289. if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
  2290. if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace(/\{0\}/g, this.ws.httprequest.realname).replace(/\{1\}/g, this.ws.httprequest.username); }
  2291. }
  2292. try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
  2293. }
  2294. this.ws = null;
  2295. }
  2296. function terminal_promise_consent_rejected(e)
  2297. {
  2298. // DO NOT start terminal
  2299. if (this.that) {
  2300. if(this.that.httprequest){ // User Consent Denied
  2301. if ((this.that.httprequest.oldStyle === true) && (this.that.httprequest.consentAutoAccept === true) && (e.toString() != "7")) {
  2302. terminal_promise_consent_resolved.call(this); // oldStyle prompt timed out and User Consent is not required so connect anyway
  2303. return;
  2304. }
  2305. } else { } // Connection was closed server side, maybe log some messages somewhere?
  2306. this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
  2307. this.that.end();
  2308. this.that = null;
  2309. this.httprequest = null;
  2310. } else { } // no websocket, maybe log some messages somewhere?
  2311. }
  2312. function promise_init(res, rej) { this._res = res; this._rej = rej; }
  2313. function terminal_userpromise_resolved(u)
  2314. {
  2315. var that = this.that;
  2316. if (u.Active.length > 0)
  2317. {
  2318. var tmp;
  2319. var username = '"' + u.Active[0].Domain + '\\' + u.Active[0].Username + '"';
  2320. if (require('win-virtual-terminal').supported)
  2321. {
  2322. // ConPTY PseudoTerminal
  2323. tmp = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [this.cols, this.rows] } });
  2324. }
  2325. else
  2326. {
  2327. // Legacy Terminal
  2328. tmp = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-terminal', script: getJSModule('win-terminal') }], launch: { module: 'win-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [this.cols, this.rows] } });
  2329. }
  2330. that.httprequest._dispatcher = tmp;
  2331. that.httprequest._dispatcher.connectionPromise = that.httprequest.connectionPromise;
  2332. that.httprequest._dispatcher.on('connection', terminal_user_onconnection);
  2333. that.httprequest._dispatcher.on('~', terminal_onfinalized);
  2334. }
  2335. this.that = null;
  2336. that = null;
  2337. }
  2338. function terminal_promise_consent_resolved()
  2339. {
  2340. this.httprequest.connectionPromise = new promise(promise_init);
  2341. this.httprequest.connectionPromise.ws = this.that;
  2342. // Start Terminal
  2343. if (process.platform == 'win32')
  2344. {
  2345. try
  2346. {
  2347. var cols = 80, rows = 25;
  2348. if (this.httprequest.xoptions)
  2349. {
  2350. if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
  2351. if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
  2352. }
  2353. if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6))
  2354. {
  2355. // Admin Terminal
  2356. if (require('win-virtual-terminal').supported)
  2357. {
  2358. // ConPTY PseudoTerminal
  2359. // this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
  2360. // The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
  2361. this.httprequest._dispatcher = require('win-dispatcher').dispatch({ modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
  2362. this.httprequest._dispatcher.httprequest = this.httprequest;
  2363. this.httprequest._dispatcher.on('connection', terminal_onconnection);
  2364. this.httprequest._dispatcher.on('~', terminal_onfinalized);
  2365. }
  2366. else
  2367. {
  2368. // Legacy Terminal
  2369. this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows));
  2370. }
  2371. }
  2372. else
  2373. {
  2374. // Logged in user
  2375. var userPromise = require('user-sessions').enumerateUsers();
  2376. userPromise.that = this;
  2377. userPromise.cols = cols;
  2378. userPromise.rows = rows;
  2379. userPromise.then(terminal_userpromise_resolved);
  2380. }
  2381. } catch (ex)
  2382. {
  2383. this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + ex.toString());
  2384. }
  2385. }
  2386. else
  2387. {
  2388. try
  2389. {
  2390. var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
  2391. var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
  2392. var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login';
  2393. var env = { HISTCONTROL: 'ignoreboth' };
  2394. if (process.env['LANG']) { env['LANG'] = process.env['LANG']; }
  2395. if (process.env['PATH']) { env['PATH'] = process.env['PATH']; }
  2396. if (this.httprequest.xoptions)
  2397. {
  2398. if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
  2399. if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
  2400. }
  2401. var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
  2402. if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin)
  2403. {
  2404. if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); }
  2405. this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell
  2406. }
  2407. else if (bash)
  2408. {
  2409. var p = childProcess.execFile(bash, ['bash'], options); // Start bash
  2410. // Spaces at the beginning of lines are needed to hide commands from the command history
  2411. if ((obj.serverInfo.termlaunchcommand != null) && (typeof obj.serverInfo.termlaunchcommand[process.platform] == 'string'))
  2412. {
  2413. if (obj.serverInfo.termlaunchcommand[process.platform] != '') { p.stdin.write(obj.serverInfo.termlaunchcommand[process.platform]); }
  2414. } else if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
  2415. this.httprequest.connectionPromise._res(p);
  2416. }
  2417. else if (sh)
  2418. {
  2419. var p = childProcess.execFile(sh, ['sh'], options); // Start sh
  2420. // Spaces at the beginning of lines are needed to hide commands from the command history
  2421. if ((obj.serverInfo.termlaunchcommand != null) && (typeof obj.serverInfo.termlaunchcommand[process.platform] == 'string'))
  2422. {
  2423. if (obj.serverInfo.termlaunchcommand[process.platform] != '') { p.stdin.write(obj.serverInfo.termlaunchcommand[process.platform]); }
  2424. } else if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
  2425. this.httprequest.connectionPromise._res(p);
  2426. }
  2427. else
  2428. {
  2429. this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found');
  2430. }
  2431. } catch (ex)
  2432. {
  2433. this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + ex.toString());
  2434. }
  2435. }
  2436. this.httprequest.connectionPromise.then(terminal_promise_connection_resolved, terminal_promise_connection_rejected);
  2437. this.that = null;
  2438. this.httprequest = null;
  2439. }
  2440. function tunnel_kvm_end()
  2441. {
  2442. --this.desktop.kvm.connectionCount;
  2443. // Remove ourself from the list of remote desktop session
  2444. var i = this.desktop.kvm.tunnels.indexOf(this);
  2445. if (i >= 0) { this.desktop.kvm.tunnels.splice(i, 1); }
  2446. // Send a metadata update to all desktop sessions
  2447. var users = {};
  2448. if (this.httprequest.desktop.kvm.tunnels != null)
  2449. {
  2450. for (var i in this.httprequest.desktop.kvm.tunnels)
  2451. {
  2452. try
  2453. {
  2454. var userid = getUserIdAndGuestNameFromHttpRequest(this.httprequest.desktop.kvm.tunnels[i].httprequest);
  2455. if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; }
  2456. } catch (ex) { sendConsoleText(ex); }
  2457. }
  2458. for (var i in this.httprequest.desktop.kvm.tunnels)
  2459. {
  2460. try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (ex) { }
  2461. }
  2462. tunnelUserCount.desktop = users;
  2463. try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (ex) { }
  2464. broadcastSessionsToRegisteredApps();
  2465. }
  2466. // Unpipe the web socket
  2467. try
  2468. {
  2469. this.unpipe(this.httprequest.desktop.kvm);
  2470. this.httprequest.desktop.kvm.unpipe(this);
  2471. } catch (ex) { }
  2472. // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
  2473. if (this.rtcchannel)
  2474. {
  2475. try
  2476. {
  2477. this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
  2478. this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
  2479. }
  2480. catch (ex) { }
  2481. }
  2482. // Place wallpaper back if needed
  2483. // TODO
  2484. if (this.desktop.kvm.connectionCount == 0)
  2485. {
  2486. // Display a toast message. This may not be supported on all platforms.
  2487. // try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (ex) { }
  2488. this.httprequest.desktop.kvm.end();
  2489. if (this.httprequest.desktop.kvm.connectionBar)
  2490. {
  2491. this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
  2492. this.httprequest.desktop.kvm.connectionBar.close();
  2493. this.httprequest.desktop.kvm.connectionBar = null;
  2494. }
  2495. } else
  2496. {
  2497. for (var i in this.httprequest.desktop.kvm.users)
  2498. {
  2499. if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar)
  2500. {
  2501. for (var j in this.httprequest.desktop.kvm.rusers) { if (this.httprequest.desktop.kvm.rusers[j] == this.httprequest.realname) { this.httprequest.desktop.kvm.rusers.splice(j, 1); break; } }
  2502. this.httprequest.desktop.kvm.users.splice(i, 1);
  2503. this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
  2504. this.httprequest.desktop.kvm.connectionBar.close();
  2505. this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace(/\{0\}/g, this.httprequest.desktop.kvm.rusers.join(', ')).replace(/\{1\}/g, this.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
  2506. this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
  2507. this.httprequest.desktop.kvm.connectionBar.on('close', function ()
  2508. {
  2509. MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
  2510. for (var i in this.httprequest.desktop.kvm._pipedStreams)
  2511. {
  2512. this.httprequest.desktop.kvm._pipedStreams[i].end();
  2513. }
  2514. this.httprequest.desktop.kvm.end();
  2515. });
  2516. break;
  2517. }
  2518. }
  2519. }
  2520. if(this.httprequest.desktop.kvm.connectionBar)
  2521. {
  2522. console.info1('Setting ConnectionBar request to NULL');
  2523. this.httprequest.desktop.kvm.connectionBar.httprequest = null;
  2524. }
  2525. this.httprequest = null;
  2526. this.desktop.tunnel = null;
  2527. }
  2528. function kvm_tunnel_consentpromise_closehandler()
  2529. {
  2530. if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }
  2531. }
  2532. function kvm_consent_ok(ws) {
  2533. // User Consent Prompt is not required because no user is present
  2534. if (ws.httprequest.consent && (ws.httprequest.consent & 1)){
  2535. // User Notifications is required
  2536. MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
  2537. var notifyMessage = currentTranslation['desktopNotify'].replace(/\{0\}/g, ws.httprequest.realname);
  2538. var notifyTitle = "MeshCentral";
  2539. if (ws.httprequest.soptions != null) {
  2540. if (ws.httprequest.soptions.notifyTitle != null) { notifyTitle = ws.httprequest.soptions.notifyTitle; }
  2541. if (ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = ws.httprequest.soptions.notifyMsgDesktop.replace(/\{0\}/g, ws.httprequest.realname).replace(/\{1\}/g, ws.httprequest.username); }
  2542. }
  2543. try { require('toaster').Toast(notifyTitle, notifyMessage, ws.tsid); } catch (ex) { }
  2544. } else {
  2545. MeshServerLogEx(36, null, "Started remote desktop without notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
  2546. }
  2547. if (ws.httprequest.consent && (ws.httprequest.consent & 0x40)) {
  2548. // Connection Bar is required
  2549. if (ws.httprequest.desktop.kvm.connectionBar) {
  2550. ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
  2551. ws.httprequest.desktop.kvm.connectionBar.close();
  2552. }
  2553. try {
  2554. ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(ws.httprequest.privacybartext.replace(/\{0\}/g, ws.httprequest.desktop.kvm.rusers.join(', ')).replace(/\{1\}/g, ws.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
  2555. MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
  2556. } catch (ex) {
  2557. MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or not Supported (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
  2558. }
  2559. if (ws.httprequest.desktop.kvm.connectionBar) {
  2560. ws.httprequest.desktop.kvm.connectionBar.state = {
  2561. userid: ws.httprequest.userid,
  2562. xuserid: ws.httprequest.xuserid,
  2563. username: ws.httprequest.username,
  2564. sessionid: ws.httprequest.sessionid,
  2565. remoteaddr: ws.httprequest.remoteaddr,
  2566. guestname: ws.httprequest.guestname,
  2567. desktop: ws.httprequest.desktop
  2568. };
  2569. ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
  2570. console.info1('Connection Bar Forcefully closed');
  2571. MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.state.remoteaddr + ")", this.state);
  2572. for (var i in this.state.desktop.kvm._pipedStreams) {
  2573. this.state.desktop.kvm._pipedStreams[i].end();
  2574. }
  2575. this.state.desktop.kvm.end();
  2576. });
  2577. }
  2578. }
  2579. ws.httprequest.desktop.kvm.pipe(ws, { dataTypeSkip: 1 });
  2580. if (ws.httprequest.autolock) {
  2581. destopLockHelper_pipe(ws.httprequest);
  2582. }
  2583. }
  2584. function kvm_consent_ask(ws){
  2585. // Send a console message back using the console channel, "\n" is supported.
  2586. ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
  2587. var consentMessage = currentTranslation['desktopConsent'].replace(/\{0\}/g, ws.httprequest.realname).replace(/\{1\}/g, ws.httprequest.username);
  2588. var consentTitle = 'MeshCentral';
  2589. if (ws.httprequest.soptions != null) {
  2590. if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
  2591. if (ws.httprequest.soptions.consentMsgDesktop != null) { consentMessage = ws.httprequest.soptions.consentMsgDesktop.replace(/\{0\}/g, ws.httprequest.realname).replace(/\{1\}/g, ws.httprequest.username); }
  2592. }
  2593. var pr;
  2594. if (process.platform == 'win32') {
  2595. var enhanced = false;
  2596. if (ws.httprequest.oldStyle === false) {
  2597. try { require('win-userconsent'); enhanced = true; } catch (ex) { }
  2598. }
  2599. if (enhanced) {
  2600. var ipr = server_getUserImage(ws.httprequest.userid);
  2601. ipr.consentTitle = consentTitle;
  2602. ipr.consentMessage = consentMessage;
  2603. ipr.consentTimeout = ws.httprequest.consentTimeout;
  2604. ipr.consentAutoAccept = ws.httprequest.consentAutoAccept;
  2605. ipr.tsid = ws.tsid;
  2606. ipr.username = ws.httprequest.realname;
  2607. ipr.translation = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
  2608. pr = ipr.then(function (img) {
  2609. this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), uid: this.tsid, timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translation, background: color_options.background, foreground: color_options.foreground });
  2610. this.__childPromise.close = this.consent.close.bind(this.consent);
  2611. return (this.consent);
  2612. });
  2613. } else {
  2614. pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null, ws.tsid);
  2615. }
  2616. } else {
  2617. pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null, ws.tsid);
  2618. }
  2619. pr.ws = ws;
  2620. ws.pause();
  2621. ws._consentpromise = pr;
  2622. ws.prependOnceListener('end', kvm_tunnel_consentpromise_closehandler);
  2623. pr.then(kvm_consentpromise_resolved, kvm_consentpromise_rejected);
  2624. }
  2625. function kvm_consentpromise_rejected(e)
  2626. {
  2627. if (this.ws) {
  2628. if(this.ws.httprequest){ // User Consent Denied
  2629. if ((this.ws.httprequest.oldStyle === true) && (this.ws.httprequest.consentAutoAccept === true) && (e.toString() != "7")) {
  2630. kvm_consentpromise_resolved.call(this); // oldStyle prompt timed out and User Consent is not required so connect anyway
  2631. return;
  2632. }
  2633. MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
  2634. } else { } // Connection was closed server side, maybe log some messages somewhere?
  2635. this.ws._consentpromise = null;
  2636. this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
  2637. this.ws = null;
  2638. } else { } // no websocket, maybe log some messages somewhere?
  2639. }
  2640. function kvm_consentpromise_resolved(always)
  2641. {
  2642. if (always && process.platform=='win32') { server_set_consentTimer(this.ws.httprequest.userid); }
  2643. // Success
  2644. this.ws._consentpromise = null;
  2645. MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
  2646. this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
  2647. if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1))
  2648. {
  2649. // User Notifications is required
  2650. var notifyMessage = currentTranslation['desktopNotify'].replace(/\{0\}/g, this.ws.httprequest.realname);
  2651. var notifyTitle = "MeshCentral";
  2652. if (this.ws.httprequest.soptions != null)
  2653. {
  2654. if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
  2655. if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replace(/\{0\}/g, this.ws.httprequest.realname).replace(/\{1\}/g, this.ws.httprequest.username); }
  2656. }
  2657. try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (ex) { }
  2658. }
  2659. if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40))
  2660. {
  2661. // Connection Bar is required
  2662. if (this.ws.httprequest.desktop.kvm.connectionBar)
  2663. {
  2664. this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
  2665. this.ws.httprequest.desktop.kvm.connectionBar.close();
  2666. }
  2667. try
  2668. {
  2669. this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replace(/\{0\}/g, this.ws.httprequest.desktop.kvm.rusers.join(', ')).replace(/\{1\}/g, this.ws.httprequest.desktop.kvm.users.join(', ')).replace(/'/g, "\\'\\"), require('MeshAgent')._tsid, color_options);
  2670. MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
  2671. } catch (ex)
  2672. {
  2673. if (process.platform != 'darwin')
  2674. {
  2675. MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
  2676. }
  2677. }
  2678. try {
  2679. if (this.ws.httprequest.desktop.kvm.connectionBar) {
  2680. this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest;
  2681. this.ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
  2682. MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
  2683. for (var i in this.httprequest.desktop.kvm._pipedStreams) {
  2684. this.httprequest.desktop.kvm._pipedStreams[i].end();
  2685. }
  2686. this.httprequest.desktop.kvm.end();
  2687. });
  2688. }
  2689. }
  2690. catch (ex)
  2691. {
  2692. if (process.platform != 'darwin')
  2693. {
  2694. MeshServerLogEx(32, null, "Failed2(" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
  2695. }
  2696. }
  2697. }
  2698. this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
  2699. if (this.ws.httprequest.autolock)
  2700. {
  2701. destopLockHelper_pipe(this.ws.httprequest);
  2702. }
  2703. this.ws.resume();
  2704. this.ws = null;
  2705. }
  2706. function files_consent_ok(ws){
  2707. // User Consent Prompt is not required
  2708. if (ws.httprequest.consent && (ws.httprequest.consent & 4)) {
  2709. // User Notifications is required
  2710. MeshServerLogEx(42, null, "Started remote files with toast notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
  2711. var notifyMessage = currentTranslation['fileNotify'].replace(/\{0\}/g, ws.httprequest.realname);
  2712. var notifyTitle = "MeshCentral";
  2713. if (ws.httprequest.soptions != null) {
  2714. if (ws.httprequest.soptions.notifyTitle != null) { notifyTitle = ws.httprequest.soptions.notifyTitle; }
  2715. if (ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = ws.httprequest.soptions.notifyMsgFiles.replace(/\{0\}/g, ws.httprequest.realname).replace(/\{1\}/g, ws.httprequest.username); }
  2716. }
  2717. try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
  2718. } else {
  2719. MeshServerLogEx(43, null, "Started remote files without notification (" + ws.httprequest.remoteaddr + ")", ws.httprequest);
  2720. }
  2721. ws.resume();
  2722. }
  2723. function files_consent_ask(ws){
  2724. // Send a console message back using the console channel, "\n" is supported.
  2725. ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
  2726. var consentMessage = currentTranslation['fileConsent'].replace(/\{0\}/g, ws.httprequest.realname).replace(/\{1\}/g, ws.httprequest.username);
  2727. var consentTitle = 'MeshCentral';
  2728. if (ws.httprequest.soptions != null) {
  2729. if (ws.httprequest.soptions.consentTitle != null) { consentTitle = ws.httprequest.soptions.consentTitle; }
  2730. if (ws.httprequest.soptions.consentMsgFiles != null) { consentMessage = ws.httprequest.soptions.consentMsgFiles.replace(/\{0\}/g, ws.httprequest.realname).replace(/\{1\}/g, ws.httprequest.username); }
  2731. }
  2732. var pr;
  2733. if (process.platform == 'win32') {
  2734. var enhanced = false;
  2735. if (ws.httprequest.oldStyle === false) {
  2736. try { require('win-userconsent'); enhanced = true; } catch (ex) { }
  2737. }
  2738. if (enhanced) {
  2739. var ipr = server_getUserImage(ws.httprequest.userid);
  2740. ipr.consentTitle = consentTitle;
  2741. ipr.consentMessage = consentMessage;
  2742. ipr.consentTimeout = ws.httprequest.consentTimeout;
  2743. ipr.consentAutoAccept = ws.httprequest.consentAutoAccept;
  2744. ipr.username = ws.httprequest.realname;
  2745. ipr.tsid = ws.tsid;
  2746. ipr.translations = { Allow: currentTranslation['allow'], Deny: currentTranslation['deny'], Auto: currentTranslation['autoAllowForFive'], Caption: consentMessage };
  2747. pr = ipr.then(function (img) {
  2748. this.consent = require('win-userconsent').create(this.consentTitle, this.consentMessage, this.username, { b64Image: img.split(',').pop(), uid: this.tsid, timeout: this.consentTimeout * 1000, timeoutAutoAccept: this.consentAutoAccept, translations: this.translations, background: color_options.background, foreground: color_options.foreground });
  2749. this.__childPromise.close = this.consent.close.bind(this.consent);
  2750. return (this.consent);
  2751. });
  2752. } else {
  2753. pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null);
  2754. }
  2755. } else {
  2756. pr = require('message-box').create(consentTitle, consentMessage, ws.httprequest.consentTimeout, null);
  2757. }
  2758. pr.ws = ws;
  2759. ws.pause();
  2760. ws._consentpromise = pr;
  2761. ws.prependOnceListener('end', files_tunnel_endhandler);
  2762. pr.then(files_consentpromise_resolved, files_consentpromise_rejected);
  2763. }
  2764. function files_consentpromise_resolved(always)
  2765. {
  2766. if (always && process.platform == 'win32') { server_set_consentTimer(this.ws.httprequest.userid); }
  2767. // Success
  2768. this.ws._consentpromise = null;
  2769. MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
  2770. this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
  2771. if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4))
  2772. {
  2773. // User Notifications is required
  2774. var notifyMessage = currentTranslation['fileNotify'].replace(/\{0\}/g, this.ws.httprequest.realname);
  2775. var notifyTitle = "MeshCentral";
  2776. if (this.ws.httprequest.soptions != null)
  2777. {
  2778. if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
  2779. if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replace(/\{0\}/g, this.ws.httprequest.realname).replace(/\{1\}/g, this.ws.httprequest.username); }
  2780. }
  2781. try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
  2782. }
  2783. this.ws.resume();
  2784. this.ws = null;
  2785. }
  2786. function files_consentpromise_rejected(e)
  2787. {
  2788. if (this.ws) {
  2789. if(this.ws.httprequest){ // User Consent Denied
  2790. if ((this.ws.httprequest.oldStyle === true) && (this.ws.httprequest.consentAutoAccept === true) && (e.toString() != "7")) {
  2791. files_consentpromise_resolved.call(this); // oldStyle prompt timed out and User Consent is not required so connect anyway
  2792. return;
  2793. }
  2794. MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
  2795. } else { } // Connection was closed server side, maybe log some messages somewhere?
  2796. this.ws._consentpromise = null;
  2797. this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
  2798. this.ws = null;
  2799. } else { } // no websocket, maybe log some messages somewhere?
  2800. }
  2801. function files_tunnel_endhandler()
  2802. {
  2803. if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }
  2804. }
  2805. function onTunnelData(data)
  2806. {
  2807. //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
  2808. // If this is upload data, save it to file
  2809. if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123)) {
  2810. // Save the data to file being uploaded.
  2811. if (data[0] == 0) {
  2812. // If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
  2813. this.httprequest.uploadFileSize += (data.length - 1);
  2814. try { fs.writeSync(this.httprequest.uploadFile, data, 1, data.length - 1); } catch (ex) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
  2815. } else {
  2816. // If data does not start with zero, save as-is.
  2817. this.httprequest.uploadFileSize += data.length;
  2818. try { fs.writeSync(this.httprequest.uploadFile, data); } catch (ex) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
  2819. }
  2820. this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data.
  2821. return;
  2822. }
  2823. if (this.httprequest.state == 0) {
  2824. // Check if this is a relay connection
  2825. if ((data == 'c') || (data == 'cr')) {
  2826. this.httprequest.state = 1;
  2827. //sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);
  2828. }
  2829. }
  2830. else {
  2831. // Handle tunnel data
  2832. if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
  2833. // Take a look at the protocol
  2834. if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
  2835. this.httprequest.protocol = parseInt(data);
  2836. if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
  2837. // See if this protocol request is allowed.
  2838. if ((this.httprequest.soptions != null) && (this.httprequest.soptions.usages != null) && (this.httprequest.soptions.usages.indexOf(this.httprequest.protocol) == -1)) { this.httprequest.protocol = 0; }
  2839. if (this.httprequest.protocol == 10) {
  2840. //
  2841. // Basic file transfer
  2842. //
  2843. var stats = null;
  2844. if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; }
  2845. try { stats = require('fs').statSync(this.httprequest.xoptions.file) } catch (ex) { }
  2846. try { if (stats) { this.httprequest.downloadFile = fs.createReadStream(this.httprequest.xoptions.file, { flags: 'rbN' }); } } catch (ex) { }
  2847. if (this.httprequest.downloadFile) {
  2848. MeshServerLogEx(106, [this.httprequest.xoptions.file, stats.size], 'Download: \"' + this.httprequest.xoptions.file + '\", Size: ' + stats.size, this.httprequest);
  2849. //sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
  2850. this.write(JSON.stringify({ op: 'ok', size: stats.size }));
  2851. this.httprequest.downloadFile.pipe(this);
  2852. this.httprequest.downloadFile.end = function () { }
  2853. } else {
  2854. //sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
  2855. this.write(JSON.stringify({ op: 'cancel' }));
  2856. }
  2857. }
  2858. else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9)) {
  2859. //
  2860. // Remote Terminal
  2861. //
  2862. // Check user access rights for terminal
  2863. if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0)))
  2864. {
  2865. // Disengage this tunnel, user does not have the rights to do this!!
  2866. this.httprequest.protocol = 999999;
  2867. this.httprequest.s.end();
  2868. sendConsoleText("Error: No Terminal Control Rights.");
  2869. return;
  2870. }
  2871. this.descriptorMetadata = "Remote Terminal";
  2872. // Look for a TSID
  2873. var tsid = null;
  2874. if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
  2875. require('MeshAgent')._tsid = tsid;
  2876. this.tsid = tsid;
  2877. if (process.platform == 'win32')
  2878. {
  2879. if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9)) {
  2880. this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 }));
  2881. this.httprequest.s.end();
  2882. return;
  2883. }
  2884. }
  2885. var prom = require('promise');
  2886. this.httprequest.tpromise = new prom(promise_init);
  2887. this.httprequest.tpromise.that = this;
  2888. this.httprequest.tpromise.httprequest = this.httprequest;
  2889. this.end = terminal_end;
  2890. // Perform User-Consent if needed.
  2891. if (this.httprequest.consent && (this.httprequest.consent & 16)) {
  2892. // User asked for consent so now we check if we can auto accept if no user is present/loggedin
  2893. if (this.httprequest.consentAutoAcceptIfNoUser || this.httprequest.consentAutoAcceptIfTerminalNoUser || this.httprequest.consentAutoAcceptIfLocked || this.httprequest.consentAutoAcceptIfTerminalLocked) {
  2894. var p = require('user-sessions').enumerateUsers();
  2895. p.sessionid = this.httprequest.sessionid;
  2896. p.ws = this;
  2897. p.then(function (u) {
  2898. var v = [];
  2899. for (var i in u) {
  2900. if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
  2901. }
  2902. var autoAccept = false;
  2903. // Check if we should auto-accept because no user is present
  2904. if ((this.ws.httprequest.consentAutoAcceptIfNoUser || this.ws.httprequest.consentAutoAcceptIfTerminalNoUser) && (v.length == 0)) {
  2905. autoAccept = true;
  2906. }
  2907. // Check if we should auto-accept because all users are locked
  2908. if ((this.ws.httprequest.consentAutoAcceptIfLocked || this.ws.httprequest.consentAutoAcceptIfTerminalLocked) && (v.length > 0)) {
  2909. var allUsersLocked = true;
  2910. if (!meshCoreObj.lusers || meshCoreObj.lusers.length == 0) {
  2911. // No locked users list available, assume users are not locked
  2912. allUsersLocked = false;
  2913. } else {
  2914. for (var i in v) {
  2915. var username = v[i].domain ? (v[i].domain + '\\' + v[i].user) : v[i].user;
  2916. if (meshCoreObj.lusers.indexOf(username) == -1) {
  2917. allUsersLocked = false;
  2918. break;
  2919. }
  2920. }
  2921. }
  2922. if (allUsersLocked) { autoAccept = true; }
  2923. }
  2924. if (autoAccept) {
  2925. this.ws.httprequest.tpromise._res();
  2926. } else {
  2927. // User is present and not all locked, so we still need consent
  2928. terminal_consent_ask(this.ws);
  2929. }
  2930. });
  2931. } else {
  2932. terminal_consent_ask(this);
  2933. }
  2934. } else {
  2935. // User-Consent is not required, so just resolve this promise
  2936. this.httprequest.tpromise._res();
  2937. }
  2938. this.httprequest.tpromise.then(terminal_promise_consent_resolved, terminal_promise_consent_rejected);
  2939. }
  2940. else if (this.httprequest.protocol == 2)
  2941. {
  2942. //
  2943. // Remote Desktop
  2944. //
  2945. // Check user access rights for desktop
  2946. if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) {
  2947. // Disengage this tunnel, user does not have the rights to do this!!
  2948. this.httprequest.protocol = 999999;
  2949. this.httprequest.s.end();
  2950. sendConsoleText("Error: No Desktop Control Rights.");
  2951. return;
  2952. }
  2953. this.descriptorMetadata = "Remote KVM";
  2954. // Look for a TSID
  2955. var tsid = null;
  2956. if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
  2957. require('MeshAgent')._tsid = tsid;
  2958. this.tsid = tsid;
  2959. // If MacOS, Wake up device with caffeinate
  2960. if(process.platform == 'darwin'){
  2961. try {
  2962. var options = {};
  2963. try { options.uid = require('user-sessions').consoleUid(); } catch (ex) { }
  2964. options.type = require('child_process').SpawnTypes.TERM;
  2965. var replydata = "";
  2966. var cmdchild = require('child_process').execFile('/usr/bin/caffeinate', ['caffeinate', '-u', '-t', '10'], options);
  2967. cmdchild.descriptorMetadata = 'UserCommandsShell';
  2968. cmdchild.stdout.on('data', function (c) { replydata += c.toString(); });
  2969. cmdchild.stderr.on('data', function (c) { replydata + c.toString(); });
  2970. cmdchild.on('exit', function () { delete cmdchild; });
  2971. } catch(err) { }
  2972. }
  2973. // Remote desktop using native pipes
  2974. this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(tsid), tunnel: this };
  2975. this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
  2976. this.desktop = this.httprequest.desktop;
  2977. // Add ourself to the list of remote desktop sessions
  2978. if (this.httprequest.desktop.kvm.tunnels == null) { this.httprequest.desktop.kvm.tunnels = []; }
  2979. this.httprequest.desktop.kvm.tunnels.push(this);
  2980. // Send a metadata update to all desktop sessions
  2981. var users = {};
  2982. if (this.httprequest.desktop.kvm.tunnels != null)
  2983. {
  2984. for (var i in this.httprequest.desktop.kvm.tunnels)
  2985. {
  2986. try {
  2987. var userid = getUserIdAndGuestNameFromHttpRequest(this.httprequest.desktop.kvm.tunnels[i].httprequest);
  2988. if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; }
  2989. } catch (ex) { sendConsoleText(ex); }
  2990. }
  2991. for (var i in this.httprequest.desktop.kvm.tunnels)
  2992. {
  2993. try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (ex) { }
  2994. }
  2995. tunnelUserCount.desktop = users;
  2996. try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (ex) { }
  2997. broadcastSessionsToRegisteredApps();
  2998. }
  2999. this.end = tunnel_kvm_end;
  3000. if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) {
  3001. this.httprequest.desktop.kvm.connectionCount++;
  3002. this.httprequest.desktop.kvm.rusers.push(this.httprequest.realname);
  3003. this.httprequest.desktop.kvm.users.push(this.httprequest.username);
  3004. this.httprequest.desktop.kvm.rusers.sort();
  3005. this.httprequest.desktop.kvm.users.sort();
  3006. } else {
  3007. this.httprequest.desktop.kvm.connectionCount = 1;
  3008. this.httprequest.desktop.kvm.rusers = [this.httprequest.realname];
  3009. this.httprequest.desktop.kvm.users = [this.httprequest.username];
  3010. }
  3011. if ((this.httprequest.desktopviewonly != true) && ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0))))
  3012. {
  3013. // If we have remote control rights, pipe the KVM input
  3014. this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
  3015. }
  3016. else
  3017. {
  3018. // We need to only pipe non-mouse & non-keyboard inputs.
  3019. // sendConsoleText('Warning: No Remote Desktop Input Rights.');
  3020. // TODO!!!
  3021. }
  3022. // Perform notification if needed. Toast messages may not be supported on all platforms.
  3023. if (this.httprequest.consent && (this.httprequest.consent & 8)) {
  3024. // User asked for consent but now we check if can auto accept if no user is present
  3025. if (this.httprequest.consentAutoAcceptIfNoUser || this.httprequest.consentAutoAcceptIfDesktopNoUser || this.httprequest.consentAutoAcceptIfLocked || this.httprequest.consentAutoAcceptIfDesktopLocked) {
  3026. // Get list of users to check if we any actual users logged in, and if users logged in, we still need consent
  3027. var p = require('user-sessions').enumerateUsers();
  3028. p.sessionid = this.httprequest.sessionid;
  3029. p.ws = this;
  3030. p.then(function (u) {
  3031. var v = [];
  3032. for (var i in u) {
  3033. if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
  3034. }
  3035. var autoAccept = false;
  3036. // Check if we can auto-accept because no user is present
  3037. if ((this.ws.httprequest.consentAutoAcceptIfNoUser || this.ws.httprequest.consentAutoAcceptIfDesktopNoUser) && (v.length == 0)) {
  3038. // No user is present, auto accept
  3039. autoAccept = true;
  3040. }
  3041. // Check if we can auto-accept because all users are locked
  3042. if ((this.ws.httprequest.consentAutoAcceptIfLocked || this.ws.httprequest.consentAutoAcceptIfDesktopLocked) && (v.length > 0)) {
  3043. var allUsersLocked = true;
  3044. if (!meshCoreObj.lusers || meshCoreObj.lusers.length == 0) {
  3045. // No locked users list available, assume users are not locked
  3046. allUsersLocked = false;
  3047. } else {
  3048. for (var i in v) {
  3049. var username = v[i].domain ? (v[i].domain + '\\' + v[i].user) : v[i].user;
  3050. if (meshCoreObj.lusers.indexOf(username) == -1) {
  3051. allUsersLocked = false;
  3052. break;
  3053. }
  3054. }
  3055. }
  3056. if (allUsersLocked) { autoAccept = true; }
  3057. }
  3058. if (autoAccept) {
  3059. kvm_consent_ok(this.ws);
  3060. } else {
  3061. // User is present and not all locked, so we still need consent
  3062. kvm_consent_ask(this.ws);
  3063. }
  3064. });
  3065. } else {
  3066. // User Consent Prompt is required
  3067. kvm_consent_ask(this);
  3068. }
  3069. } else {
  3070. // User Consent Prompt is not required
  3071. kvm_consent_ok(this);
  3072. }
  3073. this.removeAllListeners('data');
  3074. this.on('data', onTunnelControlData);
  3075. //this.write('MeshCore KVM Hello!1');
  3076. } else if (this.httprequest.protocol == 5) {
  3077. //
  3078. // Remote Files
  3079. //
  3080. // Check user access rights for files
  3081. if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) {
  3082. // Disengage this tunnel, user does not have the rights to do this!!
  3083. this.httprequest.protocol = 999999;
  3084. this.httprequest.s.end();
  3085. sendConsoleText("Error: No files control rights.");
  3086. return;
  3087. }
  3088. this.descriptorMetadata = "Remote Files";
  3089. // Look for a TSID
  3090. var tsid = null;
  3091. if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
  3092. require('MeshAgent')._tsid = tsid;
  3093. this.tsid = tsid;
  3094. // Add the files session to the count to update the server
  3095. if (this.httprequest.userid != null) {
  3096. var userid = getUserIdAndGuestNameFromHttpRequest(this.httprequest);
  3097. if (tunnelUserCount.files[userid] == null) { tunnelUserCount.files[userid] = 1; } else { tunnelUserCount.files[userid]++; }
  3098. try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (ex) { }
  3099. broadcastSessionsToRegisteredApps();
  3100. }
  3101. this.end = function ()
  3102. {
  3103. // Remove the files session from the count to update the server
  3104. if (this.httprequest.userid != null) {
  3105. var userid = getUserIdAndGuestNameFromHttpRequest(this.httprequest);
  3106. if (tunnelUserCount.files[userid] != null) { tunnelUserCount.files[userid]--; if (tunnelUserCount.files[userid] <= 0) { delete tunnelUserCount.files[userid]; } }
  3107. try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (ex) { }
  3108. broadcastSessionsToRegisteredApps();
  3109. }
  3110. };
  3111. // Perform notification if needed. Toast messages may not be supported on all platforms.
  3112. if (this.httprequest.consent && (this.httprequest.consent & 32)) {
  3113. // User asked for consent so now we check if we can auto accept if no user is present/loggedin
  3114. if (this.httprequest.consentAutoAcceptIfNoUser || this.httprequest.consentAutoAcceptIfFileNoUser || this.httprequest.consentAutoAcceptIfLocked || this.httprequest.consentAutoAcceptIfFileLocked) {
  3115. var p = require('user-sessions').enumerateUsers();
  3116. p.sessionid = this.httprequest.sessionid;
  3117. p.ws = this;
  3118. p.then(function (u) {
  3119. var v = [];
  3120. for (var i in u) {
  3121. if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
  3122. }
  3123. var autoAccept = false;
  3124. // Check if we should auto-accept because no user is present
  3125. if ((this.ws.httprequest.consentAutoAcceptIfNoUser || this.ws.httprequest.consentAutoAcceptIfFileNoUser) && (v.length == 0)) {
  3126. autoAccept = true;
  3127. }
  3128. // Check if we should auto-accept because all users are locked
  3129. if ((this.ws.httprequest.consentAutoAcceptIfLocked || this.ws.httprequest.consentAutoAcceptIfFileLocked) && (v.length > 0)) {
  3130. var allUsersLocked = true;
  3131. if (!meshCoreObj.lusers || meshCoreObj.lusers.length == 0) {
  3132. // No locked users list available, assume users are not locked
  3133. allUsersLocked = false;
  3134. } else {
  3135. for (var i in v) {
  3136. var username = v[i].domain ? (v[i].domain + '\\' + v[i].user) : v[i].user;
  3137. if (meshCoreObj.lusers.indexOf(username) == -1) {
  3138. allUsersLocked = false;
  3139. break;
  3140. }
  3141. }
  3142. }
  3143. if (allUsersLocked) { autoAccept = true; }
  3144. }
  3145. if (autoAccept) {
  3146. // User Consent Prompt is not required
  3147. files_consent_ok(this.ws);
  3148. } else {
  3149. // User is present and not all locked, so we still need consent
  3150. files_consent_ask(this.ws);
  3151. }
  3152. });
  3153. } else {
  3154. // User Consent Prompt is required
  3155. files_consent_ask(this);
  3156. }
  3157. } else {
  3158. // User Consent Prompt is not required
  3159. files_consent_ok(this);
  3160. }
  3161. // Setup files
  3162. // NOP
  3163. }
  3164. } else if (this.httprequest.protocol == 1) {
  3165. // Send data into terminal stdin
  3166. //this.write(data); // Echo back the keys (Does not seem to be a good idea)
  3167. } else if (this.httprequest.protocol == 2) {
  3168. // Send data into remote desktop
  3169. if (this.httprequest.desktop.state == 0) {
  3170. this.write(Buffer.from(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
  3171. this.httprequest.desktop.state = 1;
  3172. } else {
  3173. this.httprequest.desktop.write(data);
  3174. }
  3175. } else if (this.httprequest.protocol == 5) {
  3176. // Process files commands
  3177. var cmd = null;
  3178. try { cmd = JSON.parse(data); } catch (ex) { };
  3179. if (cmd == null) { return; }
  3180. if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
  3181. if (cmd.action == undefined) { return; }
  3182. //sendConsoleText('CMD: ' + JSON.stringify(cmd));
  3183. if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
  3184. //console.log(objToString(cmd, 0, ' '));
  3185. switch (cmd.action) {
  3186. case 'ls': {
  3187. /*
  3188. // Close the watcher if required
  3189. var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
  3190. if ((this.httprequest.watcher != undefined) && (samepath == false)) {
  3191. //console.log('Closing watcher: ' + this.httprequest.watcher.path);
  3192. //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
  3193. delete this.httprequest.watcher;
  3194. }
  3195. */
  3196. // Send the folder content to the browser
  3197. var response = getDirectoryInfo(cmd.path);
  3198. response.reqid = cmd.reqid;
  3199. this.write(Buffer.from(JSON.stringify(response)));
  3200. /*
  3201. // Start the directory watcher
  3202. if ((cmd.path != '') && (samepath == false)) {
  3203. var watcher = fs.watch(cmd.path, onFileWatcher);
  3204. watcher.tunnel = this.httprequest;
  3205. watcher.path = cmd.path;
  3206. this.httprequest.watcher = watcher;
  3207. //console.log('Starting watcher: ' + this.httprequest.watcher.path);
  3208. }
  3209. */
  3210. break;
  3211. }
  3212. case 'mkdir': {
  3213. // Create a new empty folder
  3214. fs.mkdirSync(cmd.path);
  3215. MeshServerLogEx(44, [cmd.path], "Create folder: \"" + cmd.path + "\"", this.httprequest);
  3216. break;
  3217. }
  3218. case 'mkfile': {
  3219. // Create a new empty file
  3220. fs.closeSync(fs.openSync(cmd.path, 'w'));
  3221. MeshServerLogEx(164, [cmd.path], "Create file: \"" + cmd.path + "\"", this.httprequest);
  3222. break;
  3223. }
  3224. case 'rm': {
  3225. // Delete, possibly recursive delete
  3226. for (var i in cmd.delfiles) {
  3227. var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0;
  3228. try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (ex) { }
  3229. if ((delcount == 1) && !cmd.rec) {
  3230. MeshServerLogEx(45, [p], "Delete: \"" + p + "\"", this.httprequest);
  3231. } else {
  3232. if (cmd.rec) {
  3233. MeshServerLogEx(46, [p, delcount], "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
  3234. } else {
  3235. MeshServerLogEx(47, [p, delcount], "Delete: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
  3236. }
  3237. }
  3238. }
  3239. break;
  3240. }
  3241. case 'open': {
  3242. // Open the local file/folder on the users desktop
  3243. if (cmd.path) {
  3244. MeshServerLogEx(20, [cmd.path], "Opening: " + cmd.path, cmd);
  3245. openFileOnDesktop(cmd.path);
  3246. }
  3247. }
  3248. case 'markcoredump': {
  3249. // If we are asking for the coredump file, set the right path.
  3250. var coreDumpPath = null;
  3251. if (process.platform == 'win32') {
  3252. if (fs.existsSync(process.coreDumpLocation)) { coreDumpPath = process.coreDumpLocation; }
  3253. } else {
  3254. if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { coreDumpPath = process.cwd() + 'core'; }
  3255. }
  3256. if (coreDumpPath != null) { db.Put('CoreDumpTime', require('fs').statSync(coreDumpPath).mtime); }
  3257. break;
  3258. }
  3259. case 'rename':
  3260. {
  3261. // Rename a file or folder
  3262. var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
  3263. var newfullpath = obj.path.join(cmd.path, cmd.newname);
  3264. MeshServerLogEx(48, [oldfullpath, cmd.newname], 'Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
  3265. try { fs.renameSync(oldfullpath, newfullpath); } catch (ex) { console.log(ex); }
  3266. break;
  3267. }
  3268. case 'findfile':
  3269. {
  3270. // Search for files
  3271. var r = require('file-search').find('"' + cmd.path + '"', cmd.filter);
  3272. if (!r.cancel) { r.cancel = function cancel() { this.child.kill(); }; }
  3273. this._search = r;
  3274. r.socket = this;
  3275. r.socket.reqid = cmd.reqid; // Search request id. This is used to send responses and cancel the request.
  3276. r.socket.path = cmd.path; // Search path
  3277. r.on('result', function (str) { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: str.substring(this.socket.path.length), reqid: this.socket.reqid }))); } catch (ex) { } });
  3278. r.then(function () { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: null, reqid: this.socket.reqid }))); } catch (ex) { } });
  3279. break;
  3280. }
  3281. case 'cancelfindfile':
  3282. {
  3283. if (this._search) { this._search.cancel(); this._search = null; }
  3284. break;
  3285. }
  3286. case 'download':
  3287. {
  3288. // Download a file
  3289. var sendNextBlock = 0;
  3290. if (cmd.sub == 'start') { // Setup the download
  3291. if ((cmd.path == null) && (cmd.ask == 'coredump')) { // If we are asking for the coredump file, set the right path.
  3292. if (process.platform == 'win32') {
  3293. if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
  3294. } else {
  3295. if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
  3296. }
  3297. }
  3298. MeshServerLogEx((cmd.ask == 'coredump') ? 104 : 49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
  3299. if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
  3300. this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
  3301. try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (ex) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
  3302. if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
  3303. } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands
  3304. if (cmd.sub == 'startack') { sendNextBlock = ((typeof cmd.ack == 'number') ? cmd.ack : 8); } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
  3305. }
  3306. // Send the next download block(s)
  3307. if (sendNextBlock > 0) {
  3308. sendNextBlock--;
  3309. var buf = Buffer.alloc(16384);
  3310. var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
  3311. this.filedownload.ptr += len;
  3312. if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
  3313. this.write(buf.slice(0, len + 4)); // Write as binary
  3314. }
  3315. break;
  3316. }
  3317. case 'upload':
  3318. {
  3319. // Upload a file, browser to agent
  3320. if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
  3321. if (cmd.path == undefined) break;
  3322. var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
  3323. this.httprequest.uploadFilePath = filepath;
  3324. this.httprequest.uploadFileSize = 0;
  3325. try { this.httprequest.uploadFile = fs.openSync(filepath, cmd.append ? 'abN' : 'wbN'); } catch (ex) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
  3326. this.httprequest.uploadFileid = cmd.reqid;
  3327. if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
  3328. break;
  3329. }
  3330. case 'uploaddone':
  3331. {
  3332. // Indicates that an upload is done
  3333. if (this.httprequest.uploadFile) {
  3334. MeshServerLogEx(105, [this.httprequest.uploadFilePath, this.httprequest.uploadFileSize], 'Upload: \"' + this.httprequest.uploadFilePath + '\", Size: ' + this.httprequest.uploadFileSize, this.httprequest);
  3335. fs.closeSync(this.httprequest.uploadFile);
  3336. this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
  3337. delete this.httprequest.uploadFile;
  3338. delete this.httprequest.uploadFileid;
  3339. delete this.httprequest.uploadFilePath;
  3340. delete this.httprequest.uploadFileSize;
  3341. }
  3342. break;
  3343. }
  3344. case 'uploadcancel':
  3345. {
  3346. // Indicates that an upload is canceled
  3347. if (this.httprequest.uploadFile) {
  3348. fs.closeSync(this.httprequest.uploadFile);
  3349. fs.unlinkSync(this.httprequest.uploadFilePath);
  3350. this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
  3351. delete this.httprequest.uploadFile;
  3352. delete this.httprequest.uploadFileid;
  3353. delete this.httprequest.uploadFilePath;
  3354. delete this.httprequest.uploadFileSize;
  3355. }
  3356. break;
  3357. }
  3358. case 'uploadhash':
  3359. {
  3360. // Hash a file
  3361. var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
  3362. var h = null;
  3363. try { h = getSHA384FileHash(filepath); } catch (ex) { sendConsoleText(ex); }
  3364. this.write(Buffer.from(JSON.stringify({ action: 'uploadhash', reqid: cmd.reqid, path: cmd.path, name: cmd.name, tag: cmd.tag, hash: (h ? h.toString('hex') : null) })));
  3365. break
  3366. }
  3367. case 'copy':
  3368. {
  3369. // Copy a bunch of files from scpath to dspath
  3370. for (var i in cmd.names) {
  3371. var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
  3372. MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
  3373. if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (ex) { } }
  3374. }
  3375. break;
  3376. }
  3377. case 'move':
  3378. {
  3379. // Move a bunch of files from scpath to dspath
  3380. for (var i in cmd.names) {
  3381. var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
  3382. MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
  3383. if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (ex) { } }
  3384. }
  3385. break;
  3386. }
  3387. case 'zip':
  3388. // Zip a bunch of files
  3389. if (this.zip != null) return; // Zip operating is currently running, exit now.
  3390. // Check that the specified files exist & build full paths
  3391. var fp, stat, p = [];
  3392. for (var i in cmd.files) { fp = cmd.path + '/' + cmd.files[i]; stat = null; try { stat = fs.statSync(fp); } catch (ex) { } if (stat != null) { p.push(fp); } }
  3393. if (p.length == 0) return; // No files, quit now.
  3394. // Setup file compression
  3395. var ofile = cmd.path + '/' + cmd.output;
  3396. this.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zipping' })));
  3397. this.zipfile = ofile;
  3398. delete this.zipcancel;
  3399. var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
  3400. out.xws = this;
  3401. out.on('close', function () {
  3402. this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: null })));
  3403. this.xws.write(Buffer.from(JSON.stringify({ action: 'refresh' })));
  3404. if (this.xws.zipcancel === true) { fs.unlinkSync(this.xws.zipfile); } // Delete the complete file.
  3405. delete this.xws.zipcancel;
  3406. delete this.xws.zipfile;
  3407. delete this.xws.zip;
  3408. });
  3409. this.zip = require('zip-writer').write({ files: p, basePath: cmd.path });
  3410. this.zip.xws = this;
  3411. this.zip.on('progress', require('events').moderated(function (name, p) { this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zippingFile', file: ((process.platform == 'win32') ? (name.split('/').join('\\')) : name), progress: p }))); }, 1000));
  3412. this.zip.pipe(out);
  3413. break;
  3414. case 'unzip':
  3415. if (this.unzip != null) return; // Unzip operating is currently running, exit now.
  3416. this.unzip = require('zip-reader').read(cmd.input);
  3417. this.unzip._dest = cmd.dest;
  3418. this.unzip.xws = this;
  3419. this.unzip.then(function (zipped) {
  3420. this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'unzipping' })));
  3421. zipped.xws = this.xws;
  3422. zipped.extractAll(this._dest).then(function () { // finished extracting
  3423. zipped.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: null })));
  3424. zipped.xws.write(Buffer.from(JSON.stringify({ action: 'refresh' })));
  3425. delete zipped.xws.unzip;
  3426. }, function (e) { // error extracting
  3427. zipped.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'unziperror', error: e })));
  3428. delete zipped.xws.unzip;
  3429. });
  3430. }, function (e) { this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'unziperror', error: e }))); delete this.xws.unzip });
  3431. break;
  3432. case 'cancel':
  3433. // Cancel zip operation if present
  3434. try { this.zipcancel = true; this.zip.cancel(function () { }); } catch (ex) { }
  3435. this.zip = null;
  3436. break;
  3437. default:
  3438. // Unknown action, ignore it.
  3439. break;
  3440. }
  3441. } else if (this.httprequest.protocol == 7) { // Plugin data exchange
  3442. var cmd = null;
  3443. try { cmd = JSON.parse(data); } catch (ex) { };
  3444. if (cmd == null) { return; }
  3445. if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
  3446. if (cmd.action == undefined) return;
  3447. switch (cmd.action) {
  3448. case 'plugin': {
  3449. try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (ex) { throw ex; }
  3450. break;
  3451. }
  3452. default: {
  3453. // probably shouldn't happen, but just in case this feature is expanded
  3454. }
  3455. }
  3456. }
  3457. //sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
  3458. }
  3459. }
  3460. // Delete a directory with a files and directories within it
  3461. function deleteFolderRecursive(path, rec) {
  3462. var count = 0;
  3463. if (fs.existsSync(path)) {
  3464. if (rec == true) {
  3465. fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) {
  3466. var curPath = obj.path.join(path, file);
  3467. if (fs.statSync(curPath).isDirectory()) { // recurse
  3468. count += deleteFolderRecursive(curPath, true);
  3469. } else { // delete file
  3470. fs.unlinkSync(curPath);
  3471. count++;
  3472. }
  3473. });
  3474. }
  3475. fs.unlinkSync(path);
  3476. count++;
  3477. }
  3478. return count;
  3479. }
  3480. // Called when receiving control data on WebRTC
  3481. function onTunnelWebRTCControlData(data) {
  3482. if (typeof data != 'string') return;
  3483. var obj;
  3484. try { obj = JSON.parse(data); } catch (ex) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
  3485. if (obj.type == 'close') {
  3486. //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
  3487. try { this.close(); } catch (ex) { }
  3488. try { this.xrtc.close(); } catch (ex) { }
  3489. }
  3490. }
  3491. function tunnel_webrtc_onEnd()
  3492. {
  3493. // The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
  3494. //sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
  3495. if (this.websocket.desktop && this.websocket.desktop.kvm)
  3496. {
  3497. try
  3498. {
  3499. this.unpipe(this.websocket.desktop.kvm);
  3500. this.websocket.httprequest.desktop.kvm.unpipe(this);
  3501. } catch (ex) { }
  3502. }
  3503. this.httprequest = null;
  3504. this.websocket = null;
  3505. }
  3506. function tunnel_webrtc_DataChannel_OnFinalized()
  3507. {
  3508. console.info1('WebRTC DataChannel Finalized');
  3509. }
  3510. function tunnel_webrtc_OnDataChannel(rtcchannel)
  3511. {
  3512. //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
  3513. //rtcchannel.maxFragmentSize = 32768;
  3514. rtcchannel.xrtc = this;
  3515. rtcchannel.websocket = this.websocket;
  3516. this.rtcchannel = rtcchannel;
  3517. this.rtcchannel.once('~', tunnel_webrtc_DataChannel_OnFinalized);
  3518. this.websocket.rtcchannel = rtcchannel;
  3519. this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
  3520. this.websocket.rtcchannel.on('end', tunnel_webrtc_onEnd);
  3521. this.websocket.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}'); // Indicate we are ready for WebRTC switch-over.
  3522. }
  3523. function tunnel_webrtc_OnFinalized()
  3524. {
  3525. console.info1('WebRTC Connection Finalized');
  3526. }
  3527. // Called when receiving control data on websocket
  3528. function onTunnelControlData(data, ws) {
  3529. var obj;
  3530. if (ws == null) { ws = this; }
  3531. if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (ex) { sendConsoleText('Invalid control JSON: ' + data); return; } }
  3532. else if (typeof data == 'object') { obj = data; } else { return; }
  3533. //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
  3534. //console.log('onTunnelControlData: ' + JSON.stringify(data));
  3535. switch (obj.type) {
  3536. case 'lock': {
  3537. // Look for a TSID
  3538. var tsid = null;
  3539. if ((ws.httprequest.xoptions != null) && (typeof ws.httprequest.xoptions.tsid == 'number')) { tsid = ws.httprequest.xoptions.tsid; }
  3540. // Lock the current user out of the desktop
  3541. MeshServerLogEx(53, null, "Locking remote user out of desktop", ws.httprequest);
  3542. lockDesktop(tsid);
  3543. break;
  3544. }
  3545. case 'autolock': {
  3546. // Set the session to auto lock on disconnect
  3547. if (obj.value === true) {
  3548. ws.httprequest.autolock = true;
  3549. if (ws.httprequest.unlockerHelper == null) {
  3550. destopLockHelper_pipe(ws.httprequest);
  3551. }
  3552. }
  3553. else {
  3554. delete ws.httprequest.autolock;
  3555. }
  3556. break;
  3557. }
  3558. case 'options': {
  3559. // These are additional connection options passed in the control channel.
  3560. //sendConsoleText('options: ' + JSON.stringify(obj));
  3561. delete obj.type;
  3562. ws.httprequest.xoptions = obj;
  3563. // Set additional user consent options if present
  3564. if ((obj != null) && (typeof obj.consent == 'number')) { ws.httprequest.consent |= obj.consent; }
  3565. // Set autolock
  3566. if ((obj != null) && (obj.autolock === true)) {
  3567. ws.httprequest.autolock = true;
  3568. if (ws.httprequest.unlockerHelper == null) {
  3569. destopLockHelper_pipe(ws.httprequest);
  3570. }
  3571. }
  3572. break;
  3573. }
  3574. case 'close': {
  3575. // We received the close on the websocket
  3576. //sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
  3577. try { ws.close(); } catch (ex) { }
  3578. break;
  3579. }
  3580. case 'termsize': {
  3581. // Indicates a change in terminal size
  3582. if (process.platform == 'win32') {
  3583. if (ws.httprequest._dispatcher == null) return;
  3584. //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
  3585. if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); }
  3586. } else {
  3587. if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
  3588. //sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
  3589. if (ws.httprequest.process.tcsetsize) { ws.httprequest.process.tcsetsize(obj.rows, obj.cols); }
  3590. }
  3591. break;
  3592. }
  3593. case 'webrtc0': { // Browser indicates we can start WebRTC switch-over.
  3594. if (ws.httprequest.protocol == 1)
  3595. { // Terminal
  3596. // This is a terminal data stream, unpipe the terminal now and indicate to the other side that terminal data will no longer be received over WebSocket
  3597. if (process.platform == 'win32') {
  3598. ws.httprequest._term.unpipe(ws);
  3599. } else {
  3600. ws.httprequest.process.stdout.unpipe(ws);
  3601. ws.httprequest.process.stderr.unpipe(ws);
  3602. }
  3603. } else if (ws.httprequest.protocol == 2) { // Desktop
  3604. // This is a KVM data stream, unpipe the KVM now and indicate to the other side that KVM data will no longer be received over WebSocket
  3605. ws.httprequest.desktop.kvm.unpipe(ws);
  3606. } else
  3607. {
  3608. // Switch things around so all WebRTC data goes to onTunnelData().
  3609. ws.rtcchannel.httprequest = ws.httprequest;
  3610. ws.rtcchannel.removeAllListeners('data');
  3611. ws.rtcchannel.on('data', onTunnelData);
  3612. }
  3613. ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker
  3614. break;
  3615. }
  3616. case 'webrtc1':
  3617. {
  3618. if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6))
  3619. { // Terminal
  3620. // Switch the user input from websocket to webrtc at this point.
  3621. if (process.platform == 'win32') {
  3622. ws.unpipe(ws.httprequest._term);
  3623. ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
  3624. } else {
  3625. ws.unpipe(ws.httprequest.process.stdin);
  3626. ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
  3627. }
  3628. ws.resume(); // Resume the websocket to keep receiving control data
  3629. }
  3630. else if (ws.httprequest.protocol == 2)
  3631. { // Desktop
  3632. // Switch the user input from websocket to webrtc at this point.
  3633. ws.unpipe(ws.httprequest.desktop.kvm);
  3634. if ((ws.httprequest.desktopviewonly != true) && ((ws.httprequest.rights == 0xFFFFFFFF) || (((ws.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((ws.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)))) {
  3635. // If we have remote control rights, pipe the KVM input
  3636. try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (ex) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
  3637. } else {
  3638. // We need to only pipe non-mouse & non-keyboard inputs.
  3639. // sendConsoleText('Warning: No Remote Desktop Input Rights.');
  3640. // TODO!!!
  3641. }
  3642. ws.resume(); // Resume the websocket to keep receiving control data
  3643. }
  3644. ws.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}'); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
  3645. break;
  3646. }
  3647. case 'webrtc2': {
  3648. // Other side received websocket end of data marker, start sending data on WebRTC channel
  3649. if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
  3650. if (process.platform == 'win32') {
  3651. ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
  3652. } else {
  3653. ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
  3654. ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
  3655. }
  3656. } else if (ws.httprequest.protocol == 2) { // Desktop
  3657. ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
  3658. }
  3659. break;
  3660. }
  3661. case 'offer': {
  3662. // This is a WebRTC offer.
  3663. if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
  3664. ws.webrtc = rtc.createConnection();
  3665. ws.webrtc.once('~', tunnel_webrtc_OnFinalized);
  3666. ws.webrtc.websocket = ws;
  3667. //ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });
  3668. //ws.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ });
  3669. ws.webrtc.on('dataChannel', tunnel_webrtc_OnDataChannel);
  3670. var sdp = null;
  3671. try { sdp = ws.webrtc.setOffer(obj.sdp); } catch (ex) { }
  3672. if (sdp != null) { ws.write({ type: 'answer', ctrlChannel: '102938', sdp: sdp }); }
  3673. break;
  3674. }
  3675. case 'ping': {
  3676. ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"pong\"}"); // Send pong response
  3677. break;
  3678. }
  3679. case 'pong': { // NOP
  3680. break;
  3681. }
  3682. case 'rtt': {
  3683. ws.write({ type: 'rtt', ctrlChannel: '102938', time: obj.time });
  3684. break;
  3685. }
  3686. }
  3687. }
  3688. // Console state
  3689. var consoleWebSockets = {};
  3690. var consoleHttpRequest = null;
  3691. // Console HTTP response
  3692. function consoleHttpResponse(response) {
  3693. response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
  3694. response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
  3695. }
  3696. // Open a local file on current user's desktop
  3697. function openFileOnDesktop(file) {
  3698. var child = null;
  3699. try {
  3700. switch (process.platform) {
  3701. case 'win32':
  3702. var uid = require('user-sessions').consoleUid();
  3703. var user = require('user-sessions').getUsername(uid);
  3704. var domain = require('user-sessions').getDomain(uid);
  3705. var task = { name: 'MeshChatTask', user: user, domain: domain, execPath: (require('fs').statSync(file).isDirectory() ? process.env['windir'] + '\\explorer.exe' : file) };
  3706. if (require('fs').statSync(file).isDirectory()) task.arguments = [file];
  3707. try {
  3708. require('win-tasks').addTask(task);
  3709. require('win-tasks').getTask({ name: 'MeshChatTask' }).run();
  3710. require('win-tasks').deleteTask('MeshChatTask');
  3711. return (true);
  3712. }
  3713. catch (ex) {
  3714. var taskoptions = { env: { _target: (require('fs').statSync(file).isDirectory() ? process.env['windir'] + '\\explorer.exe' : file), _user: '"' + domain + '\\' + user + '"' }, _args: "" };
  3715. if (require('fs').statSync(file).isDirectory()) taskoptions.env._args = file;
  3716. for (var c1e in process.env) {
  3717. taskoptions.env[c1e] = process.env[c1e];
  3718. }
  3719. var child = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], taskoptions);
  3720. child.stderr.on('data', function (c) { });
  3721. child.stdout.on('data', function (c) { });
  3722. child.stdin.write('SCHTASKS /CREATE /F /TN MeshChatTask /SC ONCE /ST 00:00 ');
  3723. if (user) { child.stdin.write('/RU $env:_user '); }
  3724. child.stdin.write('/TR "$env:_target $env:_args"\r\n');
  3725. child.stdin.write('$ts = New-Object -ComObject Schedule.service\r\n');
  3726. child.stdin.write('$ts.connect()\r\n');
  3727. child.stdin.write('$tsfolder = $ts.getfolder("\\")\r\n');
  3728. child.stdin.write('$task = $tsfolder.GetTask("MeshChatTask")\r\n');
  3729. child.stdin.write('$taskdef = $task.Definition\r\n');
  3730. child.stdin.write('$taskdef.Settings.StopIfGoingOnBatteries = $false\r\n');
  3731. child.stdin.write('$taskdef.Settings.DisallowStartIfOnBatteries = $false\r\n');
  3732. child.stdin.write('$taskdef.Actions.Item(1).Path = $env:_target\r\n');
  3733. child.stdin.write('$taskdef.Actions.Item(1).Arguments = $env:_args\r\n');
  3734. child.stdin.write('$tsfolder.RegisterTaskDefinition($task.Name, $taskdef, 4, $null, $null, $null)\r\n');
  3735. child.stdin.write('SCHTASKS /RUN /TN MeshChatTask\r\n');
  3736. child.stdin.write('SCHTASKS /DELETE /F /TN MeshChatTask\r\nexit\r\n');
  3737. child.waitExit();
  3738. }
  3739. break;
  3740. case 'linux':
  3741. child = require('child_process').execFile('/usr/bin/xdg-open', ['xdg-open', file], { uid: require('user-sessions').consoleUid() });
  3742. break;
  3743. case 'darwin':
  3744. child = require('child_process').execFile('/usr/bin/open', ['open', file]);
  3745. break;
  3746. default:
  3747. // Unknown platform, ignore this command.
  3748. break;
  3749. }
  3750. } catch (ex) { }
  3751. return child;
  3752. }
  3753. // Open a web browser to a specified URL on current user's desktop
  3754. function openUserDesktopUrl(url) {
  3755. if ((url.toLowerCase().startsWith('http://') == false) && (url.toLowerCase().startsWith('https://') == false)) { return null; }
  3756. var child = null;
  3757. try {
  3758. switch (process.platform) {
  3759. case 'win32':
  3760. var uid = require('user-sessions').consoleUid();
  3761. var user = require('user-sessions').getUsername(uid);
  3762. var domain = require('user-sessions').getDomain(uid);
  3763. var task = { name: 'MeshChatTask', user: user, domain: domain, execPath: process.env['windir'] + '\\system32\\cmd.exe', arguments: ['/C START ' + url.split('&').join('^&')] };
  3764. try {
  3765. require('win-tasks').addTask(task);
  3766. require('win-tasks').getTask({ name: 'MeshChatTask' }).run();
  3767. require('win-tasks').deleteTask('MeshChatTask');
  3768. return (true);
  3769. }
  3770. catch (ex) {
  3771. var taskoptions = { env: { _target: process.env['windir'] + '\\system32\\cmd.exe', _args: '/C START ' + url.split('&').join('^&'), _user: '"' + domain + '\\' + user + '"' } };
  3772. for (var c1e in process.env) {
  3773. taskoptions.env[c1e] = process.env[c1e];
  3774. }
  3775. var child = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], taskoptions);
  3776. child.stderr.on('data', function (c) { });
  3777. child.stdout.on('data', function (c) { });
  3778. child.stdin.write('SCHTASKS /CREATE /F /TN MeshChatTask /SC ONCE /ST 00:00 ');
  3779. if (user) { child.stdin.write('/RU $env:_user '); }
  3780. child.stdin.write('/TR "$env:_target $env:_args"\r\n');
  3781. child.stdin.write('$ts = New-Object -ComObject Schedule.service\r\n');
  3782. child.stdin.write('$ts.connect()\r\n');
  3783. child.stdin.write('$tsfolder = $ts.getfolder("\\")\r\n');
  3784. child.stdin.write('$task = $tsfolder.GetTask("MeshChatTask")\r\n');
  3785. child.stdin.write('$taskdef = $task.Definition\r\n');
  3786. child.stdin.write('$taskdef.Settings.StopIfGoingOnBatteries = $false\r\n');
  3787. child.stdin.write('$taskdef.Settings.DisallowStartIfOnBatteries = $false\r\n');
  3788. child.stdin.write('$taskdef.Actions.Item(1).Path = $env:_target\r\n');
  3789. child.stdin.write('$taskdef.Actions.Item(1).Arguments = $env:_args\r\n');
  3790. child.stdin.write('$tsfolder.RegisterTaskDefinition($task.Name, $taskdef, 4, $null, $null, $null)\r\n');
  3791. child.stdin.write('SCHTASKS /RUN /TN MeshChatTask\r\n');
  3792. child.stdin.write('SCHTASKS /DELETE /F /TN MeshChatTask\r\nexit\r\n');
  3793. child.waitExit();
  3794. }
  3795. break;
  3796. case 'linux':
  3797. child = require('child_process').execFile('/usr/bin/xdg-open', ['xdg-open', url], { uid: require('user-sessions').consoleUid() });
  3798. break;
  3799. case 'darwin':
  3800. child = require('child_process').execFile('/usr/bin/open', ['open', url], { uid: require('user-sessions').consoleUid() });
  3801. break;
  3802. default:
  3803. // Unknown platform, ignore this command.
  3804. break;
  3805. }
  3806. } catch (ex) { }
  3807. return child;
  3808. }
  3809. // Process a mesh agent console command
  3810. function processConsoleCommand(cmd, args, rights, sessionid) {
  3811. try {
  3812. var response = null;
  3813. switch (cmd) {
  3814. case 'help': { // Displays available commands
  3815. var fin = '', f = '', availcommands = 'domain,translations,agentupdate,errorlog,msh,timerinfo,coreinfo,coreinfoupdate,coredump,service,fdsnapshot,fdcount,startupoptions,';
  3816. availcommands += 'alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,wslist,plugin,wsconnect,wssend,wsclose,notify,';
  3817. availcommands += 'ls,ps,kill,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,openurl,getscript,getclip,setclip,log,cpuinfo,sysinfo';
  3818. availcommands += 'apf,scanwifi,wallpaper,agentmsg,task,uninstallagent,display,openfile';
  3819. if (require('os').dns != null) { availcommands += ',dnsinfo'; }
  3820. try { require('linux-dhcp'); availcommands += ',dhcp'; } catch (ex) { }
  3821. if (process.platform == 'win32') {
  3822. availcommands += ',bitlocker,cs,wpfhwacceleration,uac,volumes,rdpport,deskbackground,domaininfo';
  3823. if (bcdOK()) { availcommands += ',safemode'; }
  3824. if (require('notifybar-desktop').DefaultPinned != null) { availcommands += ',privacybar'; }
  3825. try { require('win-utils'); availcommands += ',taskbar'; } catch (ex) { }
  3826. try { require('win-info'); availcommands += ',installedapps,qfe,defender,av,installedstoreapps'; } catch (ex) { }
  3827. }
  3828. if (amt != null) { availcommands += ',amt,amtconfig,amtevents'; }
  3829. if (process.platform != 'freebsd') { availcommands += ',vm'; }
  3830. if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
  3831. try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (ex) { }
  3832. availcommands = availcommands.split(',').sort();
  3833. while (availcommands.length > 0) {
  3834. if (f.length > 90) { fin += (f + ',\r\n'); f = ''; }
  3835. f += (((f != '') ? ', ' : ' ') + availcommands.shift());
  3836. }
  3837. if (f != '') { fin += f; }
  3838. response = "Available commands: \r\n" + fin + ".";
  3839. break;
  3840. }
  3841. case 'mousetrails':
  3842. try { require('win-deskutils'); } catch (ex) { response = 'Unknown command "mousetrails", type "help" for list of available commands.'; break; }
  3843. var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : null;
  3844. switch (args['_'].length)
  3845. {
  3846. case 0:
  3847. var trails = require('win-deskutils').mouse.getTrails(id);
  3848. response = trails == 0 ? 'MouseTrails Disabled' : ('MouseTrails enabled (' + trails + ')');
  3849. response += '\nTo change setting, specify a positive integer, where 0 is disable: mousetrails [n]';
  3850. break;
  3851. case 1:
  3852. var trails = parseInt(args['_'][0]);
  3853. require('win-deskutils').mouse.setTrails(trails, id);
  3854. trails = require('win-deskutils').mouse.getTrails(id);
  3855. response = trails == 0 ? 'MouseTrails Disabled' : ('MouseTrails enabled (' + trails + ')');
  3856. break;
  3857. default:
  3858. response = 'Proper usage: mousetrails [n]';
  3859. break;
  3860. }
  3861. break;
  3862. case 'deskbackground':
  3863. try { require('win-deskutils'); } catch (ex) { response = 'Unknown command "deskbackground", type "help" for list of available commands.'; break; }
  3864. var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : null;
  3865. switch (args['_'].length)
  3866. {
  3867. case 0:
  3868. response = 'Desktop Background: ' + require('win-deskutils').background.get(id);
  3869. break;
  3870. case 1:
  3871. require('win-deskutils').background.set(args['_'][0], id);
  3872. response = 'Desktop Background: ' + require('win-deskutils').background.get(id);
  3873. break;
  3874. default:
  3875. response = 'Proper usage: deskbackground [path]';
  3876. break;
  3877. }
  3878. break;
  3879. case 'taskbar':
  3880. try { require('win-utils'); } catch (ex) { response = 'Unknown command "taskbar", type "help" for list of available commands.'; break; }
  3881. switch (args['_'].length) {
  3882. case 1:
  3883. case 2:
  3884. {
  3885. var tsid = parseInt(args['_'][1]);
  3886. if (isNaN(tsid)) { tsid = require('user-sessions').consoleUid(); }
  3887. sendConsoleText('Changing TaskBar AutoHide status. Please wait...', sessionid);
  3888. try {
  3889. var result = require('win-utils').taskBar.autoHide(tsid, args['_'][0].toLowerCase() == 'hide');
  3890. response = 'Current Status of TaskBar AutoHide: ' + result;
  3891. } catch (ex) { response = 'Unable to change TaskBar settings'; }
  3892. }
  3893. break;
  3894. default:
  3895. {
  3896. response = 'Proper usage: taskbar HIDE|SHOW [TSID]';
  3897. break;
  3898. }
  3899. }
  3900. break;
  3901. case 'privacybar':
  3902. if (process.platform != 'win32' || require('notifybar-desktop').DefaultPinned == null) {
  3903. response = 'Unknown command "privacybar", type "help" for list of available commands.';
  3904. }
  3905. else {
  3906. switch (args['_'].length) {
  3907. default:
  3908. // Show Help
  3909. response = "Current Default Pinned State: " + (require('notifybar-desktop').DefaultPinned ? "PINNED" : "UNPINNED") + '\r\n';
  3910. response += "To set default pinned state:\r\n privacybar [PINNED|UNPINNED]\r\n";
  3911. break;
  3912. case 1:
  3913. switch (args['_'][0].toUpperCase()) {
  3914. case 'PINNED':
  3915. require('notifybar-desktop').DefaultPinned = true;
  3916. response = "privacybar default pinned state is: PINNED";
  3917. break;
  3918. case 'UNPINNED':
  3919. require('notifybar-desktop').DefaultPinned = false;
  3920. response = "privacybar default pinned state is: UNPINNED";
  3921. break;
  3922. default:
  3923. response = "INVALID parameter: " + args['_'][0].toUpperCase();
  3924. break;
  3925. }
  3926. break;
  3927. }
  3928. }
  3929. break;
  3930. case 'domain':
  3931. response = getDomainInfo();
  3932. break;
  3933. case 'domaininfo':
  3934. {
  3935. if (process.platform != 'win32') {
  3936. response = 'Unknown command "domaininfo", type "help" for list of available commands.';
  3937. break;
  3938. }
  3939. if (global._domainQuery != null) {
  3940. response = "There is already an outstanding Domain Controller Query... Please try again later...";
  3941. break;
  3942. }
  3943. sendConsoleText('Querying Domain Controller... This can take up to 60 seconds. Please wait...', sessionid);
  3944. global._domainQuery = require('win-wmi').queryAsync('ROOT\\CIMV2', 'SELECT * FROM Win32_NTDomain');
  3945. global._domainQuery.session = sessionid;
  3946. global._domainQuery.then(function (v) {
  3947. var results = [];
  3948. if (Array.isArray(v)) {
  3949. var i;
  3950. var r;
  3951. for (i = 0; i < v.length; ++i) {
  3952. r = {};
  3953. if (v[i].DomainControllerAddress != null) { r.DomainControllerAddress = v[i].DomainControllerAddress.split('\\').pop(); }
  3954. if (r.DomainControllerName != null) { r.DomainControllerName = v[i].DomainControllerName.split('\\').pop(); }
  3955. r.DomainGuid = v[i].DomainGuid;
  3956. r.DomainName = v[i].DomainName;
  3957. if (r.DomainGuid != null) {
  3958. results.push(r);
  3959. }
  3960. }
  3961. }
  3962. if (results.length > 0) {
  3963. sendConsoleText('Domain Controller Results:', this.session);
  3964. sendConsoleText(JSON.stringify(results, null, 1), this.session);
  3965. sendConsoleText('End of results...', this.session);
  3966. }
  3967. else {
  3968. sendConsoleText('Domain Controller: No results returned. Is the domain controller reachable?', this.session);
  3969. }
  3970. global._domainQuery = null;
  3971. });
  3972. break;
  3973. }
  3974. case 'translations': {
  3975. response = JSON.stringify(coretranslations, null, 2);
  3976. break;
  3977. }
  3978. case 'volumes':
  3979. response = JSON.stringify(require('win-volumes').getVolumes(), null, 1);
  3980. break;
  3981. case 'bitlocker':
  3982. if (process.platform == 'win32') {
  3983. if (require('win-volumes').volumes_promise != null) {
  3984. var p = require('win-volumes').volumes_promise();
  3985. p.then(function (res) { sendConsoleText(JSON.stringify(cleanGetBitLockerVolumeInfo(res), null, 1), this.session); });
  3986. }
  3987. }
  3988. break;
  3989. case 'dhcp': // This command is only supported on Linux, this is because Linux does not give us the DNS suffix for each network adapter independently so we have to ask the DHCP server.
  3990. {
  3991. try { require('linux-dhcp'); } catch (ex) { response = 'Unknown command "dhcp", type "help" for list of available commands.'; break; }
  3992. if (args['_'].length == 0) {
  3993. var j = require('os').networkInterfaces();
  3994. var ifcs = [];
  3995. for (var i in j) {
  3996. for (var z in j[i]) {
  3997. if (j[i][z].status == 'up' && j[i][z].type != 'loopback' && j[i][z].address != null) {
  3998. ifcs.push('"' + i + '"');
  3999. break;
  4000. }
  4001. }
  4002. }
  4003. response = 'Proper usage: dhcp [' + ifcs.join(' | ') + ']';
  4004. }
  4005. else {
  4006. require('linux-dhcp').client.info(args['_'][0]).
  4007. then(function (d) {
  4008. sendConsoleText(JSON.stringify(d, null, 1), sessionid);
  4009. },
  4010. function (e) {
  4011. sendConsoleText(e, sessionid);
  4012. });
  4013. }
  4014. break;
  4015. }
  4016. case 'cs':
  4017. if (process.platform != 'win32') {
  4018. response = 'Unknown command "cs", type "help" for list of available commands.';
  4019. break;
  4020. }
  4021. switch (args['_'].length) {
  4022. case 0:
  4023. try {
  4024. var cs = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'System\\CurrentControlSet\\Control\\Power', 'CsEnabled');
  4025. response = "Connected Standby: " + (cs == 1 ? "ENABLED" : "DISABLED");
  4026. } catch (ex) {
  4027. response = "This machine does not support Connected Standby";
  4028. }
  4029. break;
  4030. case 1:
  4031. if ((args['_'][0].toUpperCase() != 'ENABLE' && args['_'][0].toUpperCase() != 'DISABLE')) {
  4032. response = "Proper usage:\r\n cs [ENABLE|DISABLE]";
  4033. }
  4034. else {
  4035. try {
  4036. var cs = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'System\\CurrentControlSet\\Control\\Power', 'CsEnabled');
  4037. require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'System\\CurrentControlSet\\Control\\Power', 'CsEnabled', args['_'][0].toUpperCase() == 'ENABLE' ? 1 : 0);
  4038. cs = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'System\\CurrentControlSet\\Control\\Power', 'CsEnabled');
  4039. response = "Connected Standby: " + (cs == 1 ? "ENABLED" : "DISABLED");
  4040. } catch (ex) {
  4041. response = "This machine does not support Connected Standby";
  4042. }
  4043. }
  4044. break;
  4045. default:
  4046. response = "Proper usage:\r\n cs [ENABLE|DISABLE]";
  4047. break;
  4048. }
  4049. break;
  4050. case 'assistant':
  4051. if (process.platform == 'win32') {
  4052. // Install MeshCentral Assistant on this device
  4053. response = "Usage: Assistant [info|install|uninstall]";
  4054. if (args['_'].length == 1) {
  4055. if ((args['_'][0] == 'install') || (args['_'][0] == 'info')) { response = ''; require('MeshAgent').SendCommand({ action: 'meshToolInfo', sessionid: sessionid, name: 'MeshCentralAssistant', cookie: true, tag: args['_'][0] }); }
  4056. // TODO: Uninstall
  4057. }
  4058. } else {
  4059. response = "MeshCentral Assistant is not supported on this platform.";
  4060. }
  4061. break;
  4062. case 'userimage':
  4063. require('MeshAgent').SendCommand({ action: 'getUserImage', sessionid: sessionid, userid: args['_'][0], tag: 'info' });
  4064. response = 'ok';
  4065. break;
  4066. case 'agentupdate':
  4067. require('MeshAgent').SendCommand({ action: 'agentupdate', sessionid: sessionid });
  4068. break;
  4069. case 'agentupdateex':
  4070. // Perform an direct agent update without requesting any information from the server, this should not typically be used.
  4071. if (args['_'].length == 1) {
  4072. if (args['_'][0].startsWith('https://')) { agentUpdate_Start(args['_'][0], { sessionid: sessionid }); } else { response = "Usage: agentupdateex https://server/path"; }
  4073. } else {
  4074. agentUpdate_Start(null, { sessionid: sessionid });
  4075. }
  4076. break;
  4077. case 'errorlog':
  4078. switch (args['_'].length) {
  4079. case 0:
  4080. // All Error Logs
  4081. response = JSON.stringify(require('util-agentlog').read(), null, 1);
  4082. break;
  4083. case 1:
  4084. // Error Logs, by either count or timestamp
  4085. response = JSON.stringify(require('util-agentlog').read(parseInt(args['_'][0])), null, 1);
  4086. break;
  4087. default:
  4088. response = "Proper usage:\r\n errorlog [lastCount|linuxEpoch]";
  4089. break;
  4090. }
  4091. break;
  4092. case 'msh':
  4093. if (args['_'].length == 0) {
  4094. response = JSON.stringify(_MSH(), null, 2);
  4095. } else if (args['_'].length > 3) {
  4096. response = 'Proper usage: msh [get|set|delete]\r\nmsh get MeshServer\r\nmsh set abc "xyz"\r\nmsh delete abc';
  4097. } else {
  4098. var mshFileName = process.execPath.replace('.exe','') + '.msh';
  4099. switch (args['_'][0].toLocaleLowerCase()) {
  4100. case 'get':
  4101. if (typeof args['_'][1] != 'string' || args['_'].length > 2) {
  4102. response = 'Proper usage: msh get MeshServer';
  4103. } else if(_MSH()[args['_'][1]]) {
  4104. response = _MSH()[args['_'][1]];
  4105. } else {
  4106. response = "Unknown Value: " + args['_'][1];
  4107. }
  4108. break;
  4109. case 'set':
  4110. if (typeof args['_'][1] != 'string' || typeof args['_'][2] != 'string') {
  4111. response = 'Proper usage: msh set abc "xyz"';
  4112. } else {
  4113. var jsonToSave = _MSH();
  4114. jsonToSave[args['_'][1]] = args['_'][2];
  4115. var updatedContent = '';
  4116. for (var key in jsonToSave) {
  4117. if (jsonToSave.hasOwnProperty(key)) {
  4118. updatedContent += key + '=' + jsonToSave[key] + '\n';
  4119. }
  4120. }
  4121. try {
  4122. require('fs').writeFileSync(mshFileName, updatedContent);
  4123. response = "msh set " + args['_'][1] + " successful"
  4124. } catch (ex) {
  4125. response = "msh set " + args['_'][1] + " unsuccessful";
  4126. }
  4127. }
  4128. break;
  4129. case 'delete':
  4130. if (typeof args['_'][1] != 'string') {
  4131. response = 'Proper usage: msh delete abc';
  4132. } else {
  4133. var jsonToSave = _MSH();
  4134. delete jsonToSave[args['_'][1]];
  4135. var updatedContent = '';
  4136. for (var key in jsonToSave) {
  4137. if (jsonToSave.hasOwnProperty(key)) {
  4138. updatedContent += key + '=' + jsonToSave[key] + '\n';
  4139. }
  4140. }
  4141. try {
  4142. require('fs').writeFileSync(mshFileName, updatedContent);
  4143. response = "msh delete " + args['_'][1] + " successful"
  4144. } catch (ex) {
  4145. response = "msh delete " + args['_'][1] + " unsuccessful";
  4146. }
  4147. }
  4148. break;
  4149. default:
  4150. response = 'Proper usage: msh [get|set|delete]\r\nmsh get MeshServer\r\nmsh set abc "xyz"\r\nmsh delete abc';
  4151. break;
  4152. }
  4153. }
  4154. break;
  4155. case 'dnsinfo':
  4156. if (require('os').dns == null) {
  4157. response = "Unknown command \"" + cmd + "\", type \"help\" for list of available commands.";
  4158. }
  4159. else {
  4160. response = 'DNS Servers: ';
  4161. var dns = require('os').dns();
  4162. for (var i = 0; i < dns.length; ++i) {
  4163. if (i > 0) { response += ', '; }
  4164. response += dns[i];
  4165. }
  4166. }
  4167. break;
  4168. case 'timerinfo':
  4169. response = require('ChainViewer').getTimerInfo();
  4170. break;
  4171. case 'rdpport':
  4172. if (process.platform != 'win32') {
  4173. response = 'Unknown command "rdpport", type "help" for list of available commands.';
  4174. return;
  4175. }
  4176. if (args['_'].length == 0) {
  4177. response = 'Proper usage: rdpport [get|default|PORTNUMBER]';
  4178. } else {
  4179. switch (args['_'][0].toLocaleLowerCase()) {
  4180. case 'get':
  4181. var rdpport = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'System\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp', 'PortNumber');
  4182. response = "Current RDP Port Set To: " + rdpport + '\r\n';
  4183. break;
  4184. case 'default':
  4185. try {
  4186. require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'System\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp', 'PortNumber', 3389);
  4187. response = 'RDP Port Set To 3389, Please Dont Forget To Restart Your Computer To Fully Apply';
  4188. } catch (ex) {
  4189. response = 'Unable to Set RDP Port To: 3389';
  4190. }
  4191. break;
  4192. default:
  4193. if (isNaN(parseFloat(args['_'][0]))){
  4194. response = 'Proper usage: rdpport [get|default|PORTNUMBER]';
  4195. } else if(parseFloat(args['_'][0]) < 0 || args['_'][0] > 65535) {
  4196. response = 'RDP Port Must Be More Than 0 And Less Than 65535';
  4197. } else {
  4198. try {
  4199. require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'System\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp', 'PortNumber', parseFloat(args['_'][0]));
  4200. response = 'RDP Port Set To ' + args['_'][0] + ', Please Dont Forget To Restart Your Computer To Fully Apply';
  4201. } catch (ex) {
  4202. response = 'Unable to Set RDP Port To: '+args['_'][0];
  4203. }
  4204. }
  4205. break;
  4206. }
  4207. }
  4208. break;
  4209. case 'find':
  4210. if (args['_'].length <= 1) {
  4211. response = "Proper usage:\r\n find root criteria [criteria2] [criteria n...]";
  4212. }
  4213. else {
  4214. var root = args['_'][0];
  4215. var p = args['_'].slice(1);
  4216. var r = require('file-search').find(root, p);
  4217. r.sid = sessionid;
  4218. r.on('result', function (str) { sendConsoleText(str, this.sid); });
  4219. r.then(function () { sendConsoleText('*** End Results ***', this.sid); });
  4220. response = "Find: [" + root + "] " + JSON.stringify(p);
  4221. }
  4222. break;
  4223. case 'coreinfo': {
  4224. response = JSON.stringify(meshCoreObj, null, 2);
  4225. break;
  4226. }
  4227. case 'coreinfoupdate': {
  4228. sendPeriodicServerUpdate(null, true);
  4229. response = "Core Info Update Requested"
  4230. break;
  4231. }
  4232. case 'agentmsg': {
  4233. if (args['_'].length == 0) {
  4234. response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [id]\r\n agentmsg list"; // Display usage
  4235. } else {
  4236. if ((args['_'][0] == 'add') && (args['_'].length > 1)) {
  4237. var msgID, iconIndex = 0;
  4238. if (args['_'].length >= 3) { try { iconIndex = parseInt(args['_'][2]); } catch (ex) { } }
  4239. if (typeof iconIndex != 'number') { iconIndex = 0; }
  4240. msgID = sendAgentMessage(args['_'][1], iconIndex);
  4241. response = 'Agent message: ' + msgID + ' added.';
  4242. } else if ((args['_'][0] == 'remove') && (args['_'].length > 1)) {
  4243. var r = removeAgentMessage(args['_'][1]);
  4244. response = 'Message ' + (r ? 'removed' : 'NOT FOUND');
  4245. } else if (args['_'][0] == 'list') {
  4246. response = JSON.stringify(sendAgentMessage(), null, 2);
  4247. }
  4248. broadcastSessionsToRegisteredApps();
  4249. }
  4250. break;
  4251. }
  4252. case 'clearagentmsg': {
  4253. removeAgentMessage();
  4254. broadcastSessionsToRegisteredApps();
  4255. break;
  4256. }
  4257. case 'coredump':
  4258. if (args['_'].length != 1) {
  4259. response = "Proper usage: coredump on|off|status|clear"; // Display usage
  4260. } else {
  4261. switch (args['_'][0].toLowerCase()) {
  4262. case 'on':
  4263. process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
  4264. response = 'coredump is now on';
  4265. break;
  4266. case 'off':
  4267. process.coreDumpLocation = null;
  4268. response = 'coredump is now off';
  4269. break;
  4270. case 'status':
  4271. response = 'coredump is: ' + ((process.coreDumpLocation == null) ? 'off' : 'on');
  4272. if (process.coreDumpLocation != null) {
  4273. if (process.platform == 'win32') {
  4274. if (fs.existsSync(process.coreDumpLocation)) {
  4275. response += '\r\n CoreDump present at: ' + process.coreDumpLocation;
  4276. response += '\r\n CoreDump Time: ' + new Date(fs.statSync(process.coreDumpLocation).mtime).getTime();
  4277. response += '\r\n Agent Time : ' + new Date(fs.statSync(process.execPath).mtime).getTime();
  4278. }
  4279. } else {
  4280. if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) {
  4281. response += '\r\n CoreDump present at: ' + process.cwd() + 'core';
  4282. response += '\r\n CoreDump Time: ' + new Date(fs.statSync(process.cwd() + 'core').mtime).getTime();
  4283. response += '\r\n Agent Time : ' + new Date(fs.statSync(process.execPath).mtime).getTime();
  4284. }
  4285. }
  4286. }
  4287. break;
  4288. case 'clear':
  4289. db.Put('CoreDumpTime', null);
  4290. response = 'coredump db cleared';
  4291. break;
  4292. default:
  4293. response = "Proper usage: coredump on|off|status"; // Display usage
  4294. break;
  4295. }
  4296. }
  4297. break;
  4298. case 'service':
  4299. if (args['_'].length != 1) {
  4300. response = "Proper usage: service status|restart"; // Display usage
  4301. } else {
  4302. var svcname = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
  4303. try {
  4304. svcname = require('MeshAgent').serviceName;
  4305. } catch (ex) { }
  4306. var s = require('service-manager').manager.getService(svcname);
  4307. switch (args['_'][0].toLowerCase()) {
  4308. case 'status':
  4309. response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]'));
  4310. break;
  4311. case 'restart':
  4312. if (s.isMe()) {
  4313. s.restart();
  4314. } else {
  4315. response = 'Restarting another agent instance is not allowed';
  4316. }
  4317. break;
  4318. default:
  4319. response = "Proper usage: service status|restart"; // Display usage
  4320. break;
  4321. }
  4322. if (process.platform == 'win32') { s.close(); }
  4323. }
  4324. break;
  4325. case 'zip':
  4326. if (args['_'].length == 0) {
  4327. response = "Proper usage: zip (output file name), input1 [, input n]"; // Display usage
  4328. } else {
  4329. var p = args['_'].join(' ').split(',');
  4330. var ofile = p.shift();
  4331. sendConsoleText('Writing ' + ofile + '...');
  4332. var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
  4333. out.fname = ofile;
  4334. out.sessionid = sessionid;
  4335. out.on('close', function () { sendConsoleText('DONE writing ' + this.fname, this.sessionid); });
  4336. var zip = require('zip-writer').write({ files: p });
  4337. zip.pipe(out);
  4338. }
  4339. break;
  4340. case 'unzip':
  4341. if (args['_'].length == 0) {
  4342. response = "Proper usage: unzip input,destination"; // Display usage
  4343. } else {
  4344. var p = args['_'].join(' ').split(',');
  4345. if (p.length != 2) { response = "Proper usage: unzip input,destination"; break; } // Display usage
  4346. var prom = require('zip-reader').read(p[0].trim());
  4347. prom._dest = p[1].trim();
  4348. prom.self = this;
  4349. prom.sessionid = sessionid;
  4350. prom.then(function (zipped) {
  4351. sendConsoleText('Extracting to ' + this._dest + '...', this.sessionid);
  4352. zipped.extractAll(this._dest).then(function () { sendConsoleText('finished unzipping', this.sessionid); }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); }).parentPromise.sessionid = this.sessionid;
  4353. }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); });
  4354. }
  4355. break;
  4356. case 'setbattery':
  4357. // require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 });
  4358. if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc'))) {
  4359. var b = { action: 'battery', state: args['_'][0] };
  4360. if (args['_'].length == 2) { b.level = parseInt(args['_'][1]); }
  4361. require('MeshAgent').SendCommand(b);
  4362. } else {
  4363. require('MeshAgent').SendCommand({ action: 'battery' });
  4364. }
  4365. break;
  4366. case 'fdsnapshot':
  4367. require('ChainViewer').getSnapshot().then(function (c) { sendConsoleText(c, this.sessionid); }).parentPromise.sessionid = sessionid;
  4368. break;
  4369. case 'fdcount':
  4370. require('DescriptorEvents').getDescriptorCount().then(
  4371. function (c) {
  4372. sendConsoleText('Descriptor Count: ' + c, this.sessionid);
  4373. }, function (e) {
  4374. sendConsoleText('Error fetching descriptor count: ' + e, this.sessionid);
  4375. }).parentPromise.sessionid = sessionid;
  4376. break;
  4377. case 'uac':
  4378. if (process.platform != 'win32') {
  4379. response = 'Unknown command "uac", type "help" for list of available commands.';
  4380. break;
  4381. }
  4382. if (args['_'].length != 1) {
  4383. response = 'Proper usage: uac [get|interactive|secure]';
  4384. }
  4385. else {
  4386. switch (args['_'][0].toUpperCase()) {
  4387. case 'GET':
  4388. var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop');
  4389. response = "UAC mode: " + (secd == 0 ? "Interactive Desktop" : "Secure Desktop");
  4390. break;
  4391. case 'INTERACTIVE':
  4392. try {
  4393. require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 0);
  4394. response = 'UAC mode changed to: Interactive Desktop';
  4395. } catch (ex) {
  4396. response = "Unable to change UAC Mode";
  4397. }
  4398. break;
  4399. case 'SECURE':
  4400. try {
  4401. require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1);
  4402. response = 'UAC mode changed to: Secure Desktop';
  4403. } catch (ex) {
  4404. response = "Unable to change UAC Mode";
  4405. }
  4406. break;
  4407. default:
  4408. response = 'Proper usage: uac [get|interactive|secure]';
  4409. break;
  4410. }
  4411. }
  4412. break;
  4413. case 'vm':
  4414. response = 'Virtual Machine = ' + require('computer-identifiers').isVM();
  4415. break;
  4416. case 'startupoptions':
  4417. response = JSON.stringify(require('MeshAgent').getStartupOptions());
  4418. break;
  4419. case 'kvmmode':
  4420. if (require('MeshAgent').maxKvmTileSize == null) {
  4421. response = "Unknown command \"kvmmode\", type \"help\" for list of available commands.";
  4422. }
  4423. else {
  4424. if (require('MeshAgent').maxKvmTileSize == 0) {
  4425. response = 'KVM Mode: Full JUMBO';
  4426. }
  4427. else {
  4428. response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO');
  4429. response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize / 1024) + ' Kbytes')));
  4430. }
  4431. }
  4432. break;
  4433. case 'alert':
  4434. if (args['_'].length == 0) {
  4435. response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
  4436. }
  4437. else {
  4438. var p = args['_'].join(' ').split(',');
  4439. if (p.length < 2) {
  4440. response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
  4441. }
  4442. else {
  4443. this._alert = require('message-box').create(p[0], p[1], p.length == 3 ? parseInt(p[2]) : 9999, 1);
  4444. }
  4445. }
  4446. break;
  4447. case 'agentsize':
  4448. var actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
  4449. if (process.platform == 'win32') {
  4450. // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
  4451. var writtenSize = 0;
  4452. var serviceName = (_MSH().serviceName ? _MSH().serviceName : (require('_agentNodeId').serviceName() ? require('_agentNodeId').serviceName() : 'Mesh Agent'));
  4453. try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'EstimatedSize'); } catch (ex) { response = ex; }
  4454. if (writtenSize != actualSize) {
  4455. response = "Size updated from: " + writtenSize + " to: " + actualSize;
  4456. try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\' + serviceName, 'EstimatedSize', actualSize); } catch (ex) { response = ex; }
  4457. } else
  4458. { response = "Agent Size: " + actualSize + " kb"; }
  4459. } else
  4460. { response = "Agent Size: " + actualSize + " kb"; }
  4461. break;
  4462. case 'versions':
  4463. response = JSON.stringify(process.versions, null, ' ');
  4464. break;
  4465. case 'wpfhwacceleration':
  4466. if (process.platform != 'win32') { throw ("wpfhwacceleration setting is only supported on Windows"); }
  4467. if (args['_'].length != 1) {
  4468. response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)"; // Display usage
  4469. }
  4470. else {
  4471. var reg = require('win-registry');
  4472. var uname = require('user-sessions').getUsername(require('user-sessions').consoleUid());
  4473. var key = reg.usernameToUserKey(uname);
  4474. switch (args['_'][0].toUpperCase()) {
  4475. default:
  4476. response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)"; // Display usage
  4477. break;
  4478. case 'ON':
  4479. try {
  4480. reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 0);
  4481. response = "OK";
  4482. } catch (ex) { response = "FAILED"; }
  4483. break;
  4484. case 'OFF':
  4485. try {
  4486. reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 1);
  4487. response = 'OK';
  4488. } catch (ex) { response = 'FAILED'; }
  4489. break;
  4490. case 'STATUS':
  4491. var s;
  4492. try { s = reg.QueryKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration') == 1 ? 'DISABLED' : 'ENABLED'; } catch (ex) { s = 'DEFAULT'; }
  4493. response = "WPF Hardware Acceleration: " + s;
  4494. break;
  4495. case 'DEFAULT':
  4496. try { reg.DeleteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration'); } catch (ex) { }
  4497. response = 'OK';
  4498. break;
  4499. }
  4500. }
  4501. break;
  4502. case 'tsid':
  4503. if (process.platform == 'win32') {
  4504. if (args['_'].length != 1) {
  4505. response = "TSID: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
  4506. } else {
  4507. var i = parseInt(args['_'][0]);
  4508. require('MeshAgent')._tsid = (isNaN(i) ? null : i);
  4509. response = "TSID set to: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
  4510. }
  4511. } else
  4512. { response = "TSID command only supported on Windows"; }
  4513. break;
  4514. case 'activeusers':
  4515. if (process.platform == 'win32') {
  4516. var p = require('user-sessions').enumerateUsers();
  4517. p.sessionid = sessionid;
  4518. p.then(function (u) {
  4519. var v = [];
  4520. var ret = getDomainInfo();
  4521. for (var i in u) {
  4522. if (u[i].State == 'Active') {
  4523. if (u[i].Domain != null && u[i].Domain == 'AzureAD'){
  4524. // AzureAD to-do
  4525. // var userGUID = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Control\\CloudDomainJoin\\JoinInfo');
  4526. // if (userGUID != null){
  4527. // var user = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Control\\CloudDomainJoin\\JoinInfo\\' + userGUID.subkeys[0], 'UserEmail');
  4528. // if (user != null) u[i].UPN = user;
  4529. // }
  4530. } else if (u[i].Domain != null) {
  4531. if (ret != null && ret.PartOfDomain === true) {
  4532. u[i].UPN = u[i].Username + '@' + ret.Domain;
  4533. }
  4534. }
  4535. v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain, upn: u[i].UPN });
  4536. }
  4537. }
  4538. sendConsoleText(JSON.stringify(v, null, 1), this.sessionid);
  4539. });
  4540. } else
  4541. { response = "activeusers command only supported on Windows"; }
  4542. break;
  4543. case 'wallpaper':
  4544. if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available)) {
  4545. response = "wallpaper command not supported on this platform";
  4546. }
  4547. else {
  4548. if (args['_'].length != 1) {
  4549. response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
  4550. }
  4551. else {
  4552. switch (args['_'][0].toUpperCase()) {
  4553. default:
  4554. response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
  4555. break;
  4556. case 'GET':
  4557. case 'TOGGLE':
  4558. if (process.platform == 'win32') {
  4559. var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
  4560. var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: id });
  4561. child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
  4562. child.stderr.on('data', function () { });
  4563. child.waitExit();
  4564. var current = child.stdout.str.trim();
  4565. if (args['_'][0].toUpperCase() == 'GET') {
  4566. response = current;
  4567. break;
  4568. }
  4569. if (current != '') {
  4570. require('MeshAgent')._wallpaper = current;
  4571. response = 'Wallpaper cleared';
  4572. } else {
  4573. response = 'Wallpaper restored';
  4574. }
  4575. child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: id });
  4576. child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
  4577. child.stderr.on('data', function () { });
  4578. child.waitExit();
  4579. }
  4580. else {
  4581. var id = require('user-sessions').consoleUid();
  4582. var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
  4583. if (args['_'][0].toUpperCase() == 'GET') {
  4584. response = current;
  4585. break;
  4586. }
  4587. if (current != '/dev/null') {
  4588. require('MeshAgent')._wallpaper = current;
  4589. response = 'Wallpaper cleared';
  4590. } else {
  4591. response = 'Wallpaper restored';
  4592. }
  4593. require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
  4594. }
  4595. break;
  4596. }
  4597. }
  4598. }
  4599. break;
  4600. case 'safemode':
  4601. if (process.platform != 'win32') {
  4602. response = 'safemode only supported on Windows Platforms'
  4603. }
  4604. else {
  4605. if (!bcdOK()) {
  4606. response = 'safemode not supported on 64 bit Windows from a 32 bit process'
  4607. break;
  4608. }
  4609. if (args['_'].length != 1) {
  4610. response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
  4611. }
  4612. else {
  4613. var svcname = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
  4614. try {
  4615. svcname = require('MeshAgent').serviceName;
  4616. } catch (ex) { }
  4617. switch (args['_'][0].toUpperCase()) {
  4618. default:
  4619. response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
  4620. break;
  4621. case 'ON':
  4622. require('win-bcd').setKey('safeboot', 'Network');
  4623. require('win-bcd').enableSafeModeService(svcname);
  4624. break;
  4625. case 'OFF':
  4626. require('win-bcd').deleteKey('safeboot');
  4627. break;
  4628. case 'STATUS':
  4629. var nextboot = require('win-bcd').getKey('safeboot');
  4630. if (nextboot) {
  4631. switch (nextboot) {
  4632. case 'Network':
  4633. case 'network':
  4634. nextboot = 'SAFE_MODE_NETWORK';
  4635. break;
  4636. default:
  4637. nextboot = 'SAFE_MODE';
  4638. break;
  4639. }
  4640. }
  4641. response = 'Current: ' + require('win-bcd').bootMode + ', NextBoot: ' + (nextboot ? nextboot : 'NORMAL');
  4642. break;
  4643. }
  4644. }
  4645. }
  4646. break;
  4647. /*
  4648. case 'border':
  4649. {
  4650. if ((args['_'].length == 1) && (args['_'][0] == 'on')) {
  4651. if (meshCoreObj.users.length > 0) {
  4652. obj.borderManager.Start(meshCoreObj.users[0]);
  4653. response = 'Border blinking is on.';
  4654. } else {
  4655. response = 'Cannot turn on border blinking, no logged in users.';
  4656. }
  4657. } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) {
  4658. obj.borderManager.Stop();
  4659. response = 'Border blinking is off.';
  4660. } else {
  4661. response = 'Proper usage: border "on|off"'; // Display correct command usage
  4662. }
  4663. }
  4664. break;
  4665. */
  4666. case 'av':
  4667. if (process.platform == 'win32') {
  4668. // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
  4669. response = JSON.stringify(require('win-info').av(), null, 1);
  4670. } else {
  4671. response = 'Not supported on the platform';
  4672. }
  4673. break;
  4674. case 'defender':
  4675. if (process.platform == 'win32') {
  4676. // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
  4677. response = JSON.stringify(require('win-info').defender(), null, 1);
  4678. } else {
  4679. response = 'Not supported on the platform';
  4680. }
  4681. break;
  4682. case 'log':
  4683. if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; }
  4684. break;
  4685. case 'getclip':
  4686. if (require('MeshAgent').isService) {
  4687. require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); });
  4688. } else {
  4689. require('clipboard').read().then(function (str) { sendConsoleText(str, sessionid); });
  4690. }
  4691. break;
  4692. case 'setclip': {
  4693. if (pendingSetClip) {
  4694. response = 'Busy';
  4695. } else if (args['_'].length != 1) {
  4696. response = 'Proper usage: setclip "sample text"';
  4697. } else {
  4698. if (require('MeshAgent').isService) {
  4699. if (process.platform != 'win32') {
  4700. require('clipboard').dispatchWrite(args['_'][0]);
  4701. }
  4702. else {
  4703. var clipargs = args['_'][0];
  4704. var uid = require('user-sessions').consoleUid();
  4705. var user = require('user-sessions').getUsername(uid);
  4706. var domain = require('user-sessions').getDomain(uid);
  4707. user = (domain + '\\' + user);
  4708. if (this._dispatcher) { this._dispatcher.close(); }
  4709. this._dispatcher = require('win-dispatcher').dispatch({ user: user, modules: [{ name: 'clip-dispatch', script: "module.exports = { dispatch: function dispatch(val) { require('clipboard')(val); process.exit(); } };" }], launch: { module: 'clip-dispatch', method: 'dispatch', args: [clipargs] } });
  4710. this._dispatcher.parent = this;
  4711. //require('events').setFinalizerMetadata.call(this._dispatcher, 'clip-dispatch');
  4712. pendingSetClip = true;
  4713. this._dispatcher.on('connection', function (c) {
  4714. this._c = c;
  4715. this._c.root = this.parent;
  4716. this._c.on('end', function ()
  4717. {
  4718. pendingSetClip = false;
  4719. try { this.root._dispatcher.close(); } catch (ex) { }
  4720. this.root._dispatcher = null;
  4721. this.root = null;
  4722. });
  4723. });
  4724. }
  4725. response = 'Setting clipboard to: "' + args['_'][0] + '"';
  4726. }
  4727. else {
  4728. require('clipboard')(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"';
  4729. }
  4730. }
  4731. break;
  4732. }
  4733. case 'openurl': {
  4734. if (args['_'].length != 1) { response = 'Proper usage: openurl (url)'; } // Display usage
  4735. else { if (openUserDesktopUrl(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
  4736. break;
  4737. }
  4738. case 'openfile': {
  4739. if (args['_'].length != 1) { response = 'Proper usage: openfile (filepath)'; } // Display usage
  4740. else { if (openFileOnDesktop(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
  4741. break;
  4742. }
  4743. case 'users': {
  4744. if (meshCoreObj.users == null) { response = 'Active users are unknown.'; } else { response = 'Active Users: ' + meshCoreObj.users.join(', ') + '.'; }
  4745. require('user-sessions').enumerateUsers().then(function (u) {
  4746. var ret = getDomainInfo();
  4747. for (var i in u) {
  4748. if (u[i].Domain != null && u[i].Domain == 'AzureAD'){
  4749. // AzureAD to-do
  4750. // var userGUID = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Control\\CloudDomainJoin\\JoinInfo');
  4751. // if (userGUID != null){
  4752. // var user = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Control\\CloudDomainJoin\\JoinInfo\\' + userGUID.subkeys[0], 'UserEmail');
  4753. // if (user != null) u[i].UPN = user;
  4754. // }
  4755. } else if (u[i].Domain != null) {
  4756. if (ret != null && ret.PartOfDomain === true) {
  4757. u[i].UPN = u[i].Username + '@' + ret.Domain;
  4758. }
  4759. }
  4760. sendConsoleText(u[i]);
  4761. }
  4762. });
  4763. break;
  4764. }
  4765. case 'kvmusers':
  4766. response = JSON.stringify(require('kvm-helper').users(), null, 1);
  4767. break;
  4768. case 'toast': {
  4769. if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
  4770. if (require('MeshAgent')._tsid == null) {
  4771. require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
  4772. }
  4773. else {
  4774. require('toaster').Toast('MeshCentral', args['_'][0], require('MeshAgent')._tsid).then(sendConsoleText, sendConsoleText);
  4775. }
  4776. }
  4777. break;
  4778. }
  4779. case 'setdebug': {
  4780. if (args['_'].length < 1) { response = 'Proper usage: setdebug (target), 0 = Disabled, 1 = StdOut, 2 = This Console, * = All Consoles, 4 = WebLog, 8 = Logfile'; } // Display usage
  4781. else { if (args['_'][0] == '*') { console.setDestination(2); } else { console.setDestination(parseInt(args['_'][0]), sessionid); } }
  4782. break;
  4783. }
  4784. case 'ps': {
  4785. processManager.getProcesses(function (plist) {
  4786. var x = '';
  4787. for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; }
  4788. sendConsoleText(x, sessionid);
  4789. });
  4790. break;
  4791. }
  4792. case 'kill': {
  4793. if ((args['_'].length < 1)) {
  4794. response = 'Proper usage: kill [pid]'; // Display correct command usage
  4795. } else {
  4796. process.kill(parseInt(args['_'][0]));
  4797. response = 'Killed process ' + args['_'][0] + '.';
  4798. }
  4799. break;
  4800. }
  4801. case 'smbios': {
  4802. if (SMBiosTables == null) { response = 'SMBios tables not available.'; } else { response = objToString(SMBiosTables, 0, ' ', true); }
  4803. break;
  4804. }
  4805. case 'rawsmbios': {
  4806. if (SMBiosTablesRaw == null) { response = 'SMBios tables not available.'; } else {
  4807. response = '';
  4808. for (var i in SMBiosTablesRaw) {
  4809. var header = false;
  4810. for (var j in SMBiosTablesRaw[i]) {
  4811. if (SMBiosTablesRaw[i][j].length > 0) {
  4812. if (header == false) { response += ('Table type #' + i + ((require('smbios').smTableTypes[i] == null) ? '' : (', ' + require('smbios').smTableTypes[i]))) + '\r\n'; header = true; }
  4813. response += (' ' + SMBiosTablesRaw[i][j].toString('hex')) + '\r\n';
  4814. }
  4815. }
  4816. }
  4817. }
  4818. break;
  4819. }
  4820. case 'eval': { // Eval JavaScript
  4821. if (args['_'].length < 1) {
  4822. response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
  4823. } else {
  4824. response = JSON.stringify(mesh.eval(args['_'][0])); // This can only be run by trusted administrator.
  4825. }
  4826. break;
  4827. }
  4828. case 'uninstallagent': // Uninstall this agent
  4829. var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
  4830. try {
  4831. agentName = require('MeshAgent').serviceName;
  4832. } catch (ex) { }
  4833. if (!require('service-manager').manager.getService(agentName).isMe()) {
  4834. response = 'Uininstall failed, this instance is not the service instance';
  4835. } else {
  4836. try { diagnosticAgent_uninstall(); } catch (ex) { }
  4837. var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
  4838. this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
  4839. }
  4840. break;
  4841. case 'notify': { // Send a notification message to the mesh
  4842. if (args['_'].length != 1) {
  4843. response = 'Proper usage: notify "message" [--session]'; // Display correct command usage
  4844. } else {
  4845. var notification = { action: 'msg', type: 'notify', value: args['_'][0], tag: 'console' };
  4846. if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
  4847. mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
  4848. response = "ok";
  4849. }
  4850. break;
  4851. }
  4852. case 'cpuinfo': { // Return system information
  4853. // CPU & memory utilization
  4854. pr = require('sysinfo').cpuUtilization();
  4855. pr.sessionid = sessionid;
  4856. pr.then(function (data) {
  4857. sendConsoleText(JSON.stringify(
  4858. {
  4859. cpu: data,
  4860. memory: require('sysinfo').memUtilization(),
  4861. thermals: require('sysinfo').thermals == null ? [] : require('sysinfo').thermals()
  4862. }, null, 1), this.sessionid);
  4863. }, function (e) {
  4864. sendConsoleText(e);
  4865. });
  4866. break;
  4867. }
  4868. case 'sysinfo': { // Return system information
  4869. getSystemInformation(function (results, err) {
  4870. if (results == null) {
  4871. sendConsoleText(err, this.sessionid);
  4872. } else {
  4873. sendConsoleText(JSON.stringify(results, null, 1), this.sessionid);
  4874. mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results });
  4875. }
  4876. });
  4877. break;
  4878. }
  4879. case 'info': { // Return information about the agent and agent core module
  4880. response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
  4881. if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; }
  4882. if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
  4883. response += '\r\nModules: ' + addedModules.join(', ') + '.';
  4884. response += '\r\nServer Connection: ' + mesh.isControlChannelConnected + ', State: ' + meshServerConnectionState + '.';
  4885. var oldNodeId = db.Get('OldNodeId');
  4886. if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; }
  4887. response += '\r\nNode ID: ' + Buffer.from(require('_agentNodeId')(), 'hex').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
  4888. if (process.platform == 'linux' || process.platform == 'freebsd') { response += '\r\nX11 support: ' + require('monitor-info').kvm_x11_support + '.'; }
  4889. response += '\r\nApplication Location: ' + process.cwd();
  4890. //response += '\r\Debug Console: ' + debugConsole + '.';
  4891. break;
  4892. }
  4893. case 'osinfo': { // Return the operating system information
  4894. var i = 1;
  4895. if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
  4896. for (var j = 0; j < i; j++) {
  4897. var pr = require('os').name();
  4898. pr.sessionid = sessionid;
  4899. pr.then(function (v) {
  4900. sendConsoleText("OS: " + v + (process.platform == 'win32' ? (require('win-virtual-terminal').supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]') : ''), this.sessionid);
  4901. });
  4902. }
  4903. break;
  4904. }
  4905. case 'args': { // Displays parsed command arguments
  4906. response = 'args ' + objToString(args, 0, ' ', true);
  4907. break;
  4908. }
  4909. case 'print': { // Print a message on the mesh agent console, does nothing when running in the background
  4910. var r = [];
  4911. for (var i in args['_']) { r.push(args['_'][i]); }
  4912. console.log(r.join(' '));
  4913. response = 'Message printed on agent console.';
  4914. break;
  4915. }
  4916. case 'type': { // Returns the content of a file
  4917. if (args['_'].length == 0) {
  4918. response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage
  4919. } else {
  4920. var max = 4096;
  4921. if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; }
  4922. if (max > 4096) max = 4096;
  4923. var buf = Buffer.alloc(max), fd = fs.openSync(args['_'][0], "r"), r = fs.readSync(fd, buf, 0, max); // Read the file content
  4924. response = buf.toString();
  4925. var i = response.indexOf('\n');
  4926. if ((i > 0) && (response[i - 1] != '\r')) { response = response.split('\n').join('\r\n'); }
  4927. if (r == max) response += '...';
  4928. fs.closeSync(fd);
  4929. }
  4930. break;
  4931. }
  4932. case 'dbkeys': { // Return all data store keys
  4933. response = JSON.stringify(db.Keys);
  4934. break;
  4935. }
  4936. case 'dbget': { // Return the data store value for a given key
  4937. if (db == null) { response = 'Database not accessible.'; break; }
  4938. if (args['_'].length != 1) {
  4939. response = 'Proper usage: dbget (key)'; // Display the value for a given database key
  4940. } else {
  4941. response = db.Get(args['_'][0]);
  4942. }
  4943. break;
  4944. }
  4945. case 'dbset': { // Set a data store key and value pair
  4946. if (db == null) { response = 'Database not accessible.'; break; }
  4947. if (args['_'].length != 2) {
  4948. response = 'Proper usage: dbset (key) (value)'; // Set a database key
  4949. } else {
  4950. var r = db.Put(args['_'][0], args['_'][1]);
  4951. response = 'Key set: ' + r;
  4952. }
  4953. break;
  4954. }
  4955. case 'dbcompact': { // Compact the data store
  4956. if (db == null) { response = 'Database not accessible.'; break; }
  4957. var r = db.Compact();
  4958. response = 'Database compacted: ' + r;
  4959. break;
  4960. }
  4961. case 'httpget': {
  4962. if (consoleHttpRequest != null) {
  4963. response = 'HTTP operation already in progress.';
  4964. } else {
  4965. if (args['_'].length != 1) {
  4966. response = 'Proper usage: httpget (url)';
  4967. } else {
  4968. var options = http.parseUri(args['_'][0]);
  4969. options.method = 'GET';
  4970. if (options == null) {
  4971. response = 'Invalid url.';
  4972. } else {
  4973. try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (ex) { response = 'Invalid HTTP GET request'; }
  4974. consoleHttpRequest.sessionid = sessionid;
  4975. if (consoleHttpRequest != null) {
  4976. consoleHttpRequest.end();
  4977. response = 'HTTPGET ' + options.protocol + '//' + options.host + ':' + options.port + options.path;
  4978. }
  4979. }
  4980. }
  4981. }
  4982. break;
  4983. }
  4984. case 'wslist': { // List all web sockets
  4985. response = '';
  4986. for (var i in consoleWebSockets) {
  4987. var httprequest = consoleWebSockets[i];
  4988. response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
  4989. }
  4990. if (response == '') { response = 'no websocket sessions.'; }
  4991. break;
  4992. }
  4993. case 'wsconnect': { // Setup a web socket
  4994. if (args['_'].length == 0) {
  4995. response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage
  4996. } else {
  4997. var httprequest = null;
  4998. try {
  4999. var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL
  5000. options.rejectUnauthorized = 0;
  5001. httprequest = http.request(options);
  5002. } catch (ex) { response = 'Invalid HTTP websocket request'; }
  5003. if (httprequest != null) {
  5004. httprequest.upgrade = onWebSocketUpgrade;
  5005. httprequest.on('error', function (e) { sendConsoleText("ERROR: Unable to connect to: " + this.url + ", " + JSON.stringify(e)); });
  5006. var index = 1;
  5007. while (consoleWebSockets[index]) { index++; }
  5008. httprequest.sessionid = sessionid;
  5009. httprequest.index = index;
  5010. httprequest.url = args['_'][0];
  5011. consoleWebSockets[index] = httprequest;
  5012. response = 'New websocket session #' + index;
  5013. }
  5014. }
  5015. break;
  5016. }
  5017. case 'wssend': { // Send data on a web socket
  5018. if (args['_'].length == 0) {
  5019. response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage
  5020. for (var i in consoleWebSockets) {
  5021. var httprequest = consoleWebSockets[i];
  5022. response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
  5023. }
  5024. } else {
  5025. var i = parseInt(args['_'][0]);
  5026. var httprequest = consoleWebSockets[i];
  5027. if (httprequest != undefined) {
  5028. httprequest.s.write(args['_'][1]);
  5029. response = 'ok';
  5030. } else {
  5031. response = 'Invalid web socket number';
  5032. }
  5033. }
  5034. break;
  5035. }
  5036. case 'wsclose': { // Close a websocket
  5037. if (args['_'].length == 0) {
  5038. response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage
  5039. } else {
  5040. var i = parseInt(args['_'][0]);
  5041. var httprequest = consoleWebSockets[i];
  5042. if (httprequest != undefined) {
  5043. if (httprequest.s != null) { httprequest.s.end(); } else { httprequest.end(); }
  5044. response = 'ok';
  5045. } else {
  5046. response = 'Invalid web socket number';
  5047. }
  5048. }
  5049. break;
  5050. }
  5051. case 'tunnels': { // Show the list of current tunnels
  5052. response = '';
  5053. for (var i in tunnels) {
  5054. response += 'Tunnel #' + i + ', ' + tunnels[i].protocol; //tunnels[i].url
  5055. if (tunnels[i].userid) { response += ', ' + tunnels[i].userid; }
  5056. if (tunnels[i].guestname) { response += '/' + tunnels[i].guestname; }
  5057. response += '\r\n'
  5058. }
  5059. if (response == '') { response = 'No websocket sessions.'; }
  5060. break;
  5061. }
  5062. case 'ls': { // Show list of files and folders
  5063. response = '';
  5064. var xpath = '*';
  5065. if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); }
  5066. response = 'List of ' + xpath + '\r\n';
  5067. var results = fs.readdirSync(xpath);
  5068. for (var i = 0; i < results.length; ++i) {
  5069. var stat = null, p = obj.path.join(args['_'][0], results[i]);
  5070. try { stat = fs.statSync(p); } catch (ex) { }
  5071. if ((stat == null) || (stat == undefined)) {
  5072. response += (results[i] + "\r\n");
  5073. } else {
  5074. response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n");
  5075. }
  5076. }
  5077. break;
  5078. }
  5079. case 'lsx': { // Show list of files and folders
  5080. response = objToString(getDirectoryInfo(args['_'][0]), 0, ' ', true);
  5081. break;
  5082. }
  5083. case 'lock': { // Lock the current user out of the desktop
  5084. lockDesktop();
  5085. break;
  5086. }
  5087. case 'amt': { // Show Intel AMT status
  5088. if (amt != null) {
  5089. amt.getMeiState(9, function (state) {
  5090. var resp = "Intel AMT not detected.";
  5091. if (state != null) { resp = objToString(state, 0, ' ', true); }
  5092. sendConsoleText(resp, sessionid);
  5093. });
  5094. } else {
  5095. response = "Intel AMT not detected.";
  5096. }
  5097. break;
  5098. }
  5099. case 'netinfo': { // Show network interface information
  5100. var interfaces = require('os').networkInterfaces();
  5101. if (process.platform == 'win32') {
  5102. try {
  5103. var ret = require('win-wmi').query('ROOT\\CIMV2', 'SELECT InterfaceIndex,NetConnectionID,Speed FROM Win32_NetworkAdapter', ['InterfaceIndex','NetConnectionID','Speed']);
  5104. if (ret[0]) {
  5105. var speedMap = {};
  5106. for (var i = 0; i < ret.length; i++) speedMap[ret[i].InterfaceIndex] = ret[i].Speed;
  5107. var adapterNames = Object.keys(interfaces);
  5108. for (var j = 0; j < adapterNames.length; j++) {
  5109. var theinterfaces = interfaces[adapterNames[j]];
  5110. for (var k = 0; k < theinterfaces.length; k++) {
  5111. var iface = theinterfaces[k], speed = speedMap[iface.index] || 0;
  5112. iface.speed = parseInt(speed); // bits per seconds
  5113. }
  5114. }
  5115. }
  5116. } catch(ex) { }
  5117. } else if (process.platform == 'linux') {
  5118. var adapterNames = Object.keys(interfaces);
  5119. for (var i = 0; i < adapterNames.length; i++) {
  5120. var ifaceName = adapterNames[i];
  5121. try {
  5122. var speedStr = require('fs').readFileSync('/sys/class/net/' + ifaceName + '/speed').toString();
  5123. if ((speedStr.trim() != "") && (speedStr.trim() != "-1")) {
  5124. var theinterfaces = interfaces[ifaceName];
  5125. for (var k = 0; k < theinterfaces.length; k++) {
  5126. var iface = theinterfaces[k];
  5127. iface.speed = parseInt(speedStr) * 1000000; // bits per seconds
  5128. }
  5129. }
  5130. } catch(ex) { }
  5131. }
  5132. }
  5133. response = objToString(interfaces, 0, ' ', true);
  5134. break;
  5135. }
  5136. case 'wakeonlan': { // Send wake-on-lan
  5137. if ((args['_'].length != 1) || (args['_'][0].length != 12)) {
  5138. response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".';
  5139. } else {
  5140. var count = sendWakeOnLanEx([args['_'][0]]);
  5141. sendWakeOnLanEx([args['_'][0]]);
  5142. sendWakeOnLanEx([args['_'][0]]);
  5143. response = 'Sending wake-on-lan on ' + count + ' interface(s).';
  5144. }
  5145. break;
  5146. }
  5147. case 'display': {
  5148. if (args['_'].length != 1) {
  5149. response = 'Proper usage: display (sleep | awake)';
  5150. } else {
  5151. var sleepawake = [args['_'][0]];
  5152. if(sleepawake=='sleep'){
  5153. require('power-monitor').sleepDisplay()
  5154. }else if(sleepawake=='awake'){
  5155. require('power-monitor').wakeDisplay()
  5156. }
  5157. response = 'Setting Display To ' + sleepawake;
  5158. }
  5159. break;
  5160. }
  5161. case 'sendall': { // Send a message to all consoles on this mesh
  5162. sendConsoleText(args['_'].join(' '));
  5163. break;
  5164. }
  5165. case 'power': { // Execute a power action on this computer
  5166. if (mesh.ExecPowerState == undefined) {
  5167. response = 'Power command not supported on this agent.';
  5168. } else {
  5169. if ((args['_'].length == 0) || isNaN(Number(args['_'][0]))) {
  5170. response = 'Proper usage: power (actionNumber), where actionNumber is:\r\n LOGOFF = 1\r\n SHUTDOWN = 2\r\n REBOOT = 3\r\n SLEEP = 4\r\n HIBERNATE = 5\r\n DISPLAYON = 6\r\n KEEPAWAKE = 7\r\n BEEP = 8\r\n CTRLALTDEL = 9\r\n VIBRATE = 13\r\n FLASH = 14'; // Display correct command usage
  5171. } else {
  5172. var r = mesh.ExecPowerState(Number(args['_'][0]), Number(args['_'][1]));
  5173. response = 'Power action executed with return code: ' + r + '.';
  5174. }
  5175. }
  5176. break;
  5177. }
  5178. case 'location': {
  5179. getIpLocationData(function (location) {
  5180. sendConsoleText(objToString({ action: 'iplocation', type: 'publicip', value: location }, 0, ' '));
  5181. });
  5182. break;
  5183. }
  5184. case 'parseuri': {
  5185. response = JSON.stringify(http.parseUri(args['_'][0]));
  5186. break;
  5187. }
  5188. case 'scanwifi': {
  5189. if (wifiScanner != null) {
  5190. var wifiPresent = wifiScanner.hasWireless;
  5191. if (wifiPresent) { response = "Perfoming Wifi scan..."; wifiScanner.Scan(); } else { response = "Wifi absent."; }
  5192. } else
  5193. { response = "Wifi module not present."; }
  5194. break;
  5195. }
  5196. case 'modules': {
  5197. response = JSON.stringify(addedModules);
  5198. break;
  5199. }
  5200. case 'listservices': {
  5201. var services = require('service-manager').manager.enumerateService();
  5202. response = JSON.stringify(services, null, 1);
  5203. break;
  5204. }
  5205. case 'getscript': {
  5206. if (args['_'].length != 1) {
  5207. response = "Proper usage: getscript [scriptNumber].";
  5208. } else {
  5209. mesh.SendCommand({ action: 'getScript', type: args['_'][0] });
  5210. }
  5211. break;
  5212. }
  5213. case 'diagnostic':
  5214. {
  5215. if (!mesh.DAIPC.listening) {
  5216. response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
  5217. break;
  5218. }
  5219. var diag = diagnosticAgent_installCheck();
  5220. if (diag) {
  5221. if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
  5222. diagnosticAgent_uninstall();
  5223. response = 'Diagnostic Agent uninstalled';
  5224. }
  5225. else {
  5226. response = 'Diagnostic Agent installed at: ' + diag.appLocation();
  5227. }
  5228. }
  5229. else {
  5230. if (args['_'].length == 1 && args['_'][0] == 'install') {
  5231. diag = diagnosticAgent_installCheck(true);
  5232. if (diag) {
  5233. response = 'Diagnostic agent was installed at: ' + diag.appLocation();
  5234. }
  5235. else {
  5236. response = 'Diagnostic agent installation failed';
  5237. }
  5238. }
  5239. else {
  5240. response = 'Diagnostic Agent Not installed. To install: diagnostic install';
  5241. }
  5242. }
  5243. if (diag) { diag.close(); diag = null; }
  5244. break;
  5245. }
  5246. case 'amtevents': {
  5247. if ((args['_'].length == 1) && (args['_'][0] == 'on')) { obj.showamtevent = true; response = 'Intel AMT configuration events live view enabled.'; }
  5248. else if ((args['_'].length == 1) && (args['_'][0] == 'off')) { delete obj.showamtevent; response = 'Intel AMT configuration events live view disabled.'; }
  5249. else if (obj.amtevents == null) { response = 'No events.'; } else { response = obj.amtevents.join('\r\n'); }
  5250. break;
  5251. }
  5252. case 'amtconfig': {
  5253. if (amt == null) { response = 'Intel AMT not detected.'; break; }
  5254. if (!obj.showamtevent) { obj.showamtevent = true; require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: 'Enabled live view of Intel AMT configuration events, \"amtevents off\" to disable.' }); }
  5255. if (apftunnel != null) { response = 'Intel AMT server tunnel already active'; break; }
  5256. require('MeshAgent').SendCommand({ action: 'amtconfig' }); // Request that the server give us a server authentication cookie to start the APF session.
  5257. break;
  5258. }
  5259. case 'apf': {
  5260. if (meshCoreObj.intelamt !== null) {
  5261. if (args['_'].length == 1) {
  5262. var connType = -1, connTypeStr = args['_'][0].toLowerCase();
  5263. if (connTypeStr == 'lms') { connType = 2; }
  5264. if (connTypeStr == 'relay') { connType = 1; }
  5265. if (connTypeStr == 'cira') { connType = 0; }
  5266. if (connTypeStr == 'off') { connType = -2; }
  5267. if (connType >= 0) { // Connect
  5268. var apfarg = {
  5269. mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
  5270. mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16).replace(/\+/g, '@').replace(/\//g, '$'),
  5271. mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16).replace(/\+/g, '@').replace(/\//g, '$'),
  5272. mpskeepalive: 60000,
  5273. clientname: require('os').hostname(),
  5274. clientaddress: '127.0.0.1',
  5275. clientuuid: meshCoreObj.intelamt.UUID,
  5276. conntype: connType // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
  5277. };
  5278. if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) {
  5279. response = "Unable to get Intel AMT UUID: " + apfarg.clientuuid;
  5280. } else {
  5281. apftunnel = require('amt-apfclient')({ debug: false }, apfarg);
  5282. apftunnel.onJsonControl = handleApfJsonControl;
  5283. apftunnel.onChannelClosed = function () { apftunnel = null; }
  5284. try {
  5285. apftunnel.connect();
  5286. response = "Started APF tunnel";
  5287. } catch (ex) {
  5288. response = JSON.stringify(ex);
  5289. }
  5290. }
  5291. } else if (connType == -2) { // Disconnect
  5292. try {
  5293. apftunnel.disconnect();
  5294. response = "Stopped APF tunnel";
  5295. } catch (ex) {
  5296. response = JSON.stringify(ex);
  5297. }
  5298. apftunnel = null;
  5299. } else {
  5300. response = "Invalid command.\r\nUse: apf lms|relay|cira|off";
  5301. }
  5302. } else {
  5303. response = "APF tunnel is " + (apftunnel == null ? "off" : "on") + "\r\nUse: apf lms|relay|cira|off";
  5304. }
  5305. } else {
  5306. response = "APF tunnel requires Intel AMT";
  5307. }
  5308. break;
  5309. }
  5310. case 'plugin': {
  5311. if (typeof args['_'][0] == 'string') {
  5312. try {
  5313. // Pass off the action to the plugin
  5314. // for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
  5315. // to control the output / actions here.
  5316. response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
  5317. } catch (ex) {
  5318. response = "There was an error in the plugin (" + ex + ")";
  5319. }
  5320. } else {
  5321. response = "Proper usage: plugin [pluginName] [args].";
  5322. }
  5323. break;
  5324. }
  5325. case 'installedapps': {
  5326. if(process.platform == 'win32'){
  5327. require('win-info').installedApps().then(function (apps){ sendConsoleText(JSON.stringify(apps,null,1)); });
  5328. }
  5329. break;
  5330. }
  5331. case 'installedstoreapps': {
  5332. if(process.platform == 'win32'){
  5333. var apps = require('win-info').installedStoreApps();
  5334. if (apps[0]) {
  5335. sendConsoleText(JSON.stringify(apps,null,1));
  5336. };
  5337. }
  5338. break;
  5339. }
  5340. case 'qfe': {
  5341. if(process.platform == 'win32'){
  5342. var qfe = require('win-info').qfe();
  5343. sendConsoleText(JSON.stringify(qfe,null,1));
  5344. }
  5345. break;
  5346. }
  5347. default: { // This is an unknown command, return an error message
  5348. response = "Unknown command \"" + cmd + "\", type \"help\" for list of available commands.";
  5349. break;
  5350. }
  5351. }
  5352. } catch (ex) { response = "Command returned an exception error: " + ex; console.log(ex); }
  5353. if (response != null) { sendConsoleText(response, sessionid); }
  5354. }
  5355. // Send a mesh agent console command
  5356. function sendConsoleText(text, sessionid) {
  5357. if (typeof text == 'object') { text = JSON.stringify(text); }
  5358. if (debugConsole && ((sessionid == null) || (sessionid == 'pipe'))) { broadcastToRegisteredApps({ cmd: 'console', value: text }); }
  5359. if (sessionid != 'pipe') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: text, sessionid: sessionid }); }
  5360. }
  5361. function removeAgentMessage(msgid) {
  5362. var ret = false;
  5363. if (msgid == null) {
  5364. // Delete all messages
  5365. sendAgentMessage.messages = [];
  5366. ret = true;
  5367. }
  5368. else {
  5369. var i = sendAgentMessage.messages.findIndex(function (v) { return (v.id == msgid); });
  5370. if (i >= 0) {
  5371. sendAgentMessage.messages.splice(i, 1);
  5372. ret = true;
  5373. }
  5374. }
  5375. if (ret) { sendAgentMessage(); }
  5376. return (ret);
  5377. }
  5378. // Send a mesh agent message to server, placing a bubble/badge on the agent device
  5379. function sendAgentMessage(msg, icon, serverid, first) {
  5380. if (sendAgentMessage.messages == null) {
  5381. sendAgentMessage.messages = [];
  5382. }
  5383. if (arguments.length > 0) {
  5384. if (first == null || (serverid && first && sendAgentMessage.messages.findIndex(function (v) { return (v.msgid == serverid); }) < 0)) {
  5385. sendAgentMessage.messages.push({ msg: msg, icon: icon, msgid: serverid });
  5386. sendAgentMessage.messages.peek().id = sendAgentMessage.messages.peek()._hashCode();
  5387. }
  5388. }
  5389. var p = {}, i;
  5390. for (i = 0; i < sendAgentMessage.messages.length; ++i) {
  5391. p[i] = sendAgentMessage.messages[i];
  5392. }
  5393. try {
  5394. require('MeshAgent').SendCommand({ action: 'sessions', type: 'msg', value: p });
  5395. } catch (ex) { }
  5396. return (arguments.length > 0 ? sendAgentMessage.messages.peek().id : sendAgentMessage.messages);
  5397. }
  5398. function getOpenDescriptors() {
  5399. var r = [];
  5400. switch (process.platform) {
  5401. case "freebsd": {
  5402. var child = require('child_process').execFile('/bin/sh', ['sh']);
  5403. child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
  5404. child.stderr.on('data', function (c) { });
  5405. child.stdin.write("procstat -f " + process.pid + " | tr '\\n' '`' | awk -F'`' '");
  5406. child.stdin.write('{');
  5407. child.stdin.write(' DEL="";');
  5408. child.stdin.write(' printf "[";');
  5409. child.stdin.write(' for(i=1;i<NF;++i)');
  5410. child.stdin.write(' {');
  5411. child.stdin.write(' A=split($i,B," ");');
  5412. child.stdin.write(' if(B[3] ~ /^[0-9]/)');
  5413. child.stdin.write(' {');
  5414. child.stdin.write(' printf "%s%s", DEL, B[3];');
  5415. child.stdin.write(' DEL=",";');
  5416. child.stdin.write(' }');
  5417. child.stdin.write(' }');
  5418. child.stdin.write(' printf "]";');
  5419. child.stdin.write("}'");
  5420. child.stdin.write('\nexit\n');
  5421. child.waitExit();
  5422. try { r = JSON.parse(child.stdout.str.trim()); } catch (ex) { }
  5423. break;
  5424. }
  5425. case "linux": {
  5426. var child = require('child_process').execFile('/bin/sh', ['sh']);
  5427. child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
  5428. child.stderr.on('data', function (c) { });
  5429. child.stdin.write("ls /proc/" + process.pid + "/fd | tr '\\n' '`' | awk -F'`' '");
  5430. child.stdin.write('{');
  5431. child.stdin.write(' printf "[";');
  5432. child.stdin.write(' DEL="";');
  5433. child.stdin.write(' for(i=1;i<NF;++i)');
  5434. child.stdin.write(' {');
  5435. child.stdin.write(' printf "%s%s",DEL,$i;');
  5436. child.stdin.write(' DEL=",";');
  5437. child.stdin.write(' }');
  5438. child.stdin.write(' printf "]";');
  5439. child.stdin.write("}'");
  5440. child.stdin.write('\nexit\n');
  5441. child.waitExit();
  5442. try { r = JSON.parse(child.stdout.str.trim()); } catch (ex) { }
  5443. break;
  5444. }
  5445. }
  5446. return r;
  5447. }
  5448. function closeDescriptors(libc, descriptors) {
  5449. var fd = null;
  5450. while (descriptors.length > 0) {
  5451. fd = descriptors.pop();
  5452. if (fd > 2) {
  5453. libc.close(fd);
  5454. }
  5455. }
  5456. }
  5457. function linux_execv(name, agentfilename, sessionid) {
  5458. var libs = require('monitor-info').getLibInfo('libc');
  5459. var libc = null;
  5460. if ((libs.length == 0 || libs.length == null) && require('MeshAgent').ARCHID == 33) {
  5461. var child = require('child_process').execFile('/bin/sh', ['sh']);
  5462. child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
  5463. child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
  5464. child.stdin.write("ls /lib/libc.* | tr '\\n' '`' | awk -F'`' '{ " + ' printf "["; DEL=""; for(i=1;i<NF;++i) { printf "%s{\\"path\\":\\"%s\\"}",DEL,$i; DEL=""; } printf "]"; }\'\nexit\n');
  5465. child.waitExit();
  5466. try {
  5467. libs = JSON.parse(child.stdout.str.trim());
  5468. } catch (ex) { }
  5469. }
  5470. while (libs.length > 0) {
  5471. try {
  5472. libc = require('_GenericMarshal').CreateNativeProxy(libs.pop().path);
  5473. break;
  5474. } catch (ex) {
  5475. libc = null;
  5476. continue;
  5477. }
  5478. }
  5479. if (libc != null) {
  5480. try {
  5481. libc.CreateMethod('execv');
  5482. libc.CreateMethod('close');
  5483. } catch (ex) {
  5484. libc = null;
  5485. }
  5486. }
  5487. if (libc == null) {
  5488. // Couldn't find libc.so, fallback to using service manager to restart agent
  5489. if (sessionid != null) { sendConsoleText('Restarting service via service-manager...', sessionid) }
  5490. try {
  5491. // restart service
  5492. var s = require('service-manager').manager.getService(name);
  5493. s.restart();
  5494. } catch (ex) {
  5495. sendConsoleText('Self Update encountered an error trying to restart service', sessionid);
  5496. sendAgentMessage('Self Update encountered an error trying to restart service', 3);
  5497. }
  5498. return;
  5499. }
  5500. if (sessionid != null) { sendConsoleText('Restarting service via execv()...', sessionid) }
  5501. var i;
  5502. var args;
  5503. var argarr = [process.execPath];
  5504. var argtmp = [];
  5505. var path = require('_GenericMarshal').CreateVariable(process.execPath);
  5506. if (require('MeshAgent').getStartupOptions != null) {
  5507. var options = require('MeshAgent').getStartupOptions();
  5508. for (i in options) {
  5509. argarr.push('--' + i + '="' + options[i] + '"');
  5510. }
  5511. }
  5512. args = require('_GenericMarshal').CreateVariable((1 + argarr.length) * require('_GenericMarshal').PointerSize);
  5513. for (i = 0; i < argarr.length; ++i) {
  5514. var arg = require('_GenericMarshal').CreateVariable(argarr[i]);
  5515. argtmp.push(arg);
  5516. arg.pointerBuffer().copy(args.toBuffer(), i * require('_GenericMarshal').PointerSize);
  5517. }
  5518. var descriptors = getOpenDescriptors();
  5519. closeDescriptors(libc, descriptors);
  5520. libc.execv(path, args);
  5521. if (sessionid != null) { sendConsoleText('Self Update failed because execv() failed', sessionid) }
  5522. sendAgentMessage('Self Update failed because execv() failed', 3);
  5523. }
  5524. function bsd_execv(name, agentfilename, sessionid) {
  5525. var child = require('child_process').execFile('/bin/sh', ['sh']);
  5526. child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
  5527. child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
  5528. child.stdin.write("cat /usr/lib/libc.so | awk '");
  5529. child.stdin.write('{');
  5530. child.stdin.write(' a=split($0, tok, "(");');
  5531. child.stdin.write(' if(a>1)');
  5532. child.stdin.write(' {');
  5533. child.stdin.write(' split(tok[2], b, ")");');
  5534. child.stdin.write(' split(b[1], c, " ");');
  5535. child.stdin.write(' print c[1];');
  5536. child.stdin.write(' }');
  5537. child.stdin.write("}'\nexit\n");
  5538. child.waitExit();
  5539. if (child.stdout.str.trim() == '') {
  5540. if (sessionid != null) { sendConsoleText('Self Update failed because cannot find libc.so', sessionid) }
  5541. sendAgentMessage('Self Update failed because cannot find libc.so', 3);
  5542. return;
  5543. }
  5544. var libc = null;
  5545. try {
  5546. libc = require('_GenericMarshal').CreateNativeProxy(child.stdout.str.trim());
  5547. libc.CreateMethod('execv');
  5548. libc.CreateMethod('close');
  5549. } catch (ex) {
  5550. if (sessionid != null) { sendConsoleText('Self Update failed: ' + ex.toString(), sessionid) }
  5551. sendAgentMessage('Self Update failed: ' + ex.toString(), 3);
  5552. return;
  5553. }
  5554. var path = require('_GenericMarshal').CreateVariable(process.execPath);
  5555. var argarr = [process.execPath];
  5556. var args, i, argtmp = [];
  5557. var options = require('MeshAgent').getStartupOptions();
  5558. for (i in options) {
  5559. argarr.push('--' + i + '="' + options[i] + '"');
  5560. }
  5561. args = require('_GenericMarshal').CreateVariable((1 + argarr.length) * require('_GenericMarshal').PointerSize);
  5562. for (i = 0; i < argarr.length; ++i) {
  5563. var arg = require('_GenericMarshal').CreateVariable(argarr[i]);
  5564. argtmp.push(arg);
  5565. arg.pointerBuffer().copy(args.toBuffer(), i * require('_GenericMarshal').PointerSize);
  5566. }
  5567. if (sessionid != null) { sendConsoleText('Restarting service via execv()', sessionid) }
  5568. var descriptors = getOpenDescriptors();
  5569. closeDescriptors(libc, descriptors);
  5570. libc.execv(path, args);
  5571. if (sessionid != null) { sendConsoleText('Self Update failed because execv() failed', sessionid) }
  5572. sendAgentMessage('Self Update failed because execv() failed', 3);
  5573. }
  5574. function windows_execve(name, agentfilename, sessionid) {
  5575. var libc;
  5576. try {
  5577. libc = require('_GenericMarshal').CreateNativeProxy('msvcrt.dll');
  5578. libc.CreateMethod('_wexecve');
  5579. } catch (ex) {
  5580. sendConsoleText('Self Update failed because msvcrt.dll is missing', sessionid);
  5581. sendAgentMessage('Self Update failed because msvcrt.dll is missing', 3);
  5582. return;
  5583. }
  5584. var cmd = require('_GenericMarshal').CreateVariable(process.env['windir'] + '\\system32\\cmd.exe', { wide: true });
  5585. var args = require('_GenericMarshal').CreateVariable(3 * require('_GenericMarshal').PointerSize);
  5586. var arg1 = require('_GenericMarshal').CreateVariable('cmd.exe', { wide: true });
  5587. var arg2 = require('_GenericMarshal').CreateVariable('/C net stop "' + name + '" & "' + process.cwd() + agentfilename + '.update.exe" -b64exec ' + 'dHJ5CnsKICAgIHZhciBzZXJ2aWNlTG9jYXRpb24gPSBwcm9jZXNzLmFyZ3YucG9wKCkudG9Mb3dlckNhc2UoKTsKICAgIHJlcXVpcmUoJ3Byb2Nlc3MtbWFuYWdlcicpLmVudW1lcmF0ZVByb2Nlc3NlcygpLnRoZW4oZnVuY3Rpb24gKHByb2MpCiAgICB7CiAgICAgICAgZm9yICh2YXIgcCBpbiBwcm9jKQogICAgICAgIHsKICAgICAgICAgICAgaWYgKHByb2NbcF0ucGF0aCAmJiAocHJvY1twXS5wYXRoLnRvTG93ZXJDYXNlKCkgPT0gc2VydmljZUxvY2F0aW9uKSkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgcHJvY2Vzcy5raWxsKHByb2NbcF0ucGlkKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBwcm9jZXNzLmV4aXQoKTsKICAgIH0pOwp9CmNhdGNoIChlKQp7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQ==' +
  5588. ' "' + process.execPath + '" & copy "' + process.cwd() + agentfilename + '.update.exe" "' + process.execPath + '" & net start "' + name + '" & erase "' + process.cwd() + agentfilename + '.update.exe"', { wide: true });
  5589. arg1.pointerBuffer().copy(args.toBuffer());
  5590. arg2.pointerBuffer().copy(args.toBuffer(), require('_GenericMarshal').PointerSize);
  5591. libc._wexecve(cmd, args, 0);
  5592. }
  5593. // Start a JavaScript based Agent Self-Update
  5594. function agentUpdate_Start(updateurl, updateoptions) {
  5595. // If this value is null
  5596. var sessionid = (updateoptions != null) ? updateoptions.sessionid : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
  5597. // If the url starts with *, switch it to use the same protoco, host and port as the control channel.
  5598. if (updateurl != null) {
  5599. updateurl = getServerTargetUrlEx(updateurl);
  5600. if (updateurl.startsWith("wss://")) { updateurl = "https://" + updateurl.substring(6); }
  5601. }
  5602. if (agentUpdate_Start._selfupdate != null) {
  5603. // We were already called, so we will ignore this duplicate request
  5604. if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); }
  5605. }
  5606. else {
  5607. if (agentUpdate_Start._retryCount == null) { agentUpdate_Start._retryCount = 0; }
  5608. if (require('MeshAgent').ARCHID == null && updateurl == null) {
  5609. // This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull
  5610. sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid);
  5611. }
  5612. else {
  5613. var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); // Local File Name, ie: MeshAgent.exe
  5614. var name = require('MeshAgent').serviceName;
  5615. if (name == null) { name = (process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'); } // This is an older agent that doesn't expose the service name, so use the default
  5616. try {
  5617. var s = require('service-manager').manager.getService(name);
  5618. if (!s.isMe()) {
  5619. if (process.platform == 'win32') { s.close(); }
  5620. sendConsoleText('Self Update cannot continue, this agent is not an instance of (' + name + ')', sessionid);
  5621. return;
  5622. }
  5623. if (process.platform == 'win32') { s.close(); }
  5624. }
  5625. catch (ex) {
  5626. sendConsoleText('Self Update Failed because this agent is not an instance of (' + name + ')', sessionid);
  5627. sendAgentMessage('Self Update Failed because this agent is not an instance of (' + name + ')', 3);
  5628. return;
  5629. }
  5630. if ((sessionid != null) && (updateurl != null)) { sendConsoleText('Downloading update from: ' + updateurl, sessionid); }
  5631. var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl);
  5632. options.protocol = 'https:';
  5633. if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); sendConsoleText('Downloading update from: ' + options.path, sessionid); }
  5634. options.rejectUnauthorized = false;
  5635. options.checkServerIdentity = function checkServerIdentity(certs) {
  5636. // If the tunnel certificate matches the control channel certificate, accept the connection
  5637. try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
  5638. try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
  5639. // Check that the certificate is the one expected by the server, fail if not.
  5640. if (checkServerIdentity.servertlshash == null) {
  5641. if (require('MeshAgent').ServerInfo == null || require('MeshAgent').ServerInfo.ControlChannelCertificate == null) { return; }
  5642. sendConsoleText('Self Update failed, because the url cannot be verified: ' + updateurl, sessionid);
  5643. sendAgentMessage('Self Update failed, because the url cannot be verified: ' + updateurl, 3);
  5644. throw new Error('BadCert');
  5645. }
  5646. if (certs[0].digest == null) { return; }
  5647. if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) {
  5648. sendConsoleText('Self Update failed, because the supplied certificate does not match', sessionid);
  5649. sendAgentMessage('Self Update failed, because the supplied certificate does not match', 3);
  5650. throw new Error('BadCert')
  5651. }
  5652. }
  5653. options.checkServerIdentity.servertlshash = (updateoptions != null ? updateoptions.tlshash : null);
  5654. agentUpdate_Start._selfupdate = require('https').get(options);
  5655. agentUpdate_Start._selfupdate.on('error', function (e) {
  5656. sendConsoleText('Self Update failed, because there was a problem trying to download the update from ' + updateurl, sessionid);
  5657. sendAgentMessage('Self Update failed, because there was a problem trying to download the update from ' + updateurl, 3);
  5658. agentUpdate_Start._selfupdate = null;
  5659. });
  5660. agentUpdate_Start._selfupdate.on('response', function (img) {
  5661. this._file = require('fs').createWriteStream(agentfilename + (process.platform == 'win32' ? '.update.exe' : '.update'), { flags: 'wb' });
  5662. this._filehash = require('SHA384Stream').create();
  5663. this._filehash.on('hash', function (h) {
  5664. if (updateoptions != null && updateoptions.hash != null) {
  5665. if (updateoptions.hash.toLowerCase() == h.toString('hex').toLowerCase()) {
  5666. if (sessionid != null) { sendConsoleText('Download complete. HASH verified.', sessionid); }
  5667. } else {
  5668. agentUpdate_Start._retryCount++;
  5669. sendConsoleText('Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate_Start._retryCount + '), URL: ' + updateurl, sessionid);
  5670. sendConsoleText(updateoptions.hash + " != " + h.toString('hex'));
  5671. sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate_Start._retryCount + '), URL: ' + updateurl, 3);
  5672. agentUpdate_Start._selfupdate = null;
  5673. if (agentUpdate_Start._retryCount < 4) {
  5674. // Retry the download again
  5675. sendConsoleText('Self Update will try again in 60 seconds...', sessionid);
  5676. agentUpdate_Start._timeout = setTimeout(agentUpdate_Start, 60000, updateurl, updateoptions);
  5677. }
  5678. else {
  5679. sendConsoleText('Self Update giving up, too many failures...', sessionid);
  5680. sendAgentMessage('Self Update giving up, too many failures...', 3);
  5681. }
  5682. return;
  5683. }
  5684. }
  5685. else {
  5686. sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid);
  5687. }
  5688. // Send an indication to the server that we got the update download correctly.
  5689. try { require('MeshAgent').SendCommand({ action: 'agentupdatedownloaded' }); } catch (ex) { }
  5690. if (sessionid != null) { sendConsoleText('Updating and restarting agent...', sessionid); }
  5691. if (process.platform == 'win32') {
  5692. // Use _wexecve() equivalent to perform the update
  5693. windows_execve(name, agentfilename, sessionid);
  5694. }
  5695. else {
  5696. var m = require('fs').statSync(process.execPath).mode;
  5697. require('fs').chmodSync(process.cwd() + agentfilename + '.update', m);
  5698. // remove binary
  5699. require('fs').unlinkSync(process.execPath);
  5700. // copy update
  5701. require('fs').copyFileSync(process.cwd() + agentfilename + '.update', process.execPath);
  5702. require('fs').chmodSync(process.execPath, m);
  5703. // erase update
  5704. require('fs').unlinkSync(process.cwd() + agentfilename + '.update');
  5705. switch (process.platform) {
  5706. case 'freebsd':
  5707. bsd_execv(name, agentfilename, sessionid);
  5708. break;
  5709. case 'linux':
  5710. linux_execv(name, agentfilename, sessionid);
  5711. break;
  5712. default:
  5713. try {
  5714. // restart service
  5715. var s = require('service-manager').manager.getService(name);
  5716. s.restart();
  5717. }
  5718. catch (ex) {
  5719. sendConsoleText('Self Update encountered an error trying to restart service', sessionid);
  5720. sendAgentMessage('Self Update encountered an error trying to restart service', 3);
  5721. }
  5722. break;
  5723. }
  5724. }
  5725. });
  5726. img.pipe(this._file);
  5727. img.pipe(this._filehash);
  5728. });
  5729. }
  5730. }
  5731. }
  5732. // Called before the process exits
  5733. //process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
  5734. // Called when the server connection state changes
  5735. function handleServerConnection(state) {
  5736. meshServerConnectionState = state;
  5737. if (meshServerConnectionState == 0) {
  5738. // Server disconnected
  5739. if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
  5740. lastSelfInfo = null;
  5741. } else {
  5742. // Server connected, send mesh core information
  5743. if (require('MeshAgent').ServerInfo == null || require('MeshAgent').ServerInfo.ControlChannelCertificate == null) {
  5744. // Outdated Agent, will have insecure tunnels
  5745. sendAgentMessage("This agent has an outdated certificate validation mechanism, consider updating.", 3, 118);
  5746. }
  5747. else if (global._MSH == null) {
  5748. sendAgentMessage("This is an old agent version, consider updating.", 3, 117);
  5749. }
  5750. var oldNodeId = db.Get('OldNodeId');
  5751. if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); }
  5752. // Send SMBios tables if present
  5753. if (SMBiosTablesRaw != null) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
  5754. // Update the server on with basic info, logged in users and more advanced stuff, like Intel ME and Network Settings
  5755. meInfoStr = null;
  5756. LastPeriodicServerUpdate = null;
  5757. sendPeriodicServerUpdate(null, true);
  5758. if (selfInfoUpdateTimer == null) {
  5759. selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); // 20 minutes
  5760. selfInfoUpdateTimer.metadata = 'meshcore (InfoUpdate Timer)';
  5761. }
  5762. // Send any state messages
  5763. if (Object.keys(tunnelUserCount.msg).length > 0) {
  5764. sendAgentMessage();
  5765. broadcastSessionsToRegisteredApps();
  5766. }
  5767. // Send update of registered applications to the server
  5768. updateRegisteredAppsToServer();
  5769. }
  5770. // Send server state update to registered applications
  5771. broadcastToRegisteredApps({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer });
  5772. }
  5773. // Update the server with the latest network interface information
  5774. var sendNetworkUpdateNagleTimer = null;
  5775. function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
  5776. function sendNetworkUpdate(force) {
  5777. sendNetworkUpdateNagleTimer = null;
  5778. try {
  5779. // Update the network interfaces information data
  5780. var netInfo = { netif2: require('os').networkInterfaces() };
  5781. if (process.platform == 'win32') {
  5782. try {
  5783. var ret = require('win-wmi').query('ROOT\\CIMV2', 'SELECT InterfaceIndex,NetConnectionID,Speed FROM Win32_NetworkAdapter', ['InterfaceIndex','NetConnectionID','Speed']);
  5784. if (ret[0]) {
  5785. var speedMap = {};
  5786. for (var i = 0; i < ret.length; i++) speedMap[ret[i].InterfaceIndex] = ret[i].Speed;
  5787. var adapterNames = Object.keys(netInfo.netif2);
  5788. for (var j = 0; j < adapterNames.length; j++) {
  5789. var interfaces = netInfo.netif2[adapterNames[j]];
  5790. for (var k = 0; k < interfaces.length; k++) {
  5791. var iface = interfaces[k], speed = speedMap[iface.index] || 0;
  5792. iface.speed = parseInt(speed); // bits per seconds
  5793. }
  5794. }
  5795. }
  5796. } catch(ex) { }
  5797. } else if (process.platform == 'linux') {
  5798. var adapterNames = Object.keys(netInfo.netif2);
  5799. for (var i = 0; i < adapterNames.length; i++) {
  5800. var ifaceName = adapterNames[i];
  5801. try {
  5802. var speedStr = require('fs').readFileSync('/sys/class/net/' + ifaceName + '/speed').toString();
  5803. if ((speedStr.trim() != "") && (speedStr.trim() != "-1")) {
  5804. var theinterfaces = netInfo.netif2[ifaceName];
  5805. for (var k = 0; k < theinterfaces.length; k++) {
  5806. var iface = theinterfaces[k];
  5807. iface.speed = parseInt(speedStr) * 1000000; // bits per seconds
  5808. }
  5809. }
  5810. } catch(ex) { }
  5811. }
  5812. }
  5813. if (netInfo.netif2) {
  5814. netInfo.action = 'netinfo';
  5815. var netInfoStr = JSON.stringify(netInfo);
  5816. if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
  5817. }
  5818. } catch (ex) { }
  5819. }
  5820. // Called periodically to check if we need to send updates to the server
  5821. function sendPeriodicServerUpdate(flags, force) {
  5822. if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
  5823. if (!flags) { flags = 0xFFFFFFFF; }
  5824. if (!force) { force = false; }
  5825. // If we have a connected MEI, get Intel ME information
  5826. if ((flags & 1) && (amt != null) && (amt.state == 2)) {
  5827. delete meshCoreObj.intelamt;
  5828. amt.getMeiState(9, function (meinfo) {
  5829. meshCoreObj.intelamt = meinfo;
  5830. meshCoreObj.intelamt.microlms = amt.lmsstate;
  5831. meshCoreObjChanged();
  5832. });
  5833. }
  5834. // Update network information
  5835. if (flags & 2) { sendNetworkUpdateNagle(false); }
  5836. // Update anti-virus information
  5837. if ((flags & 4) && (process.platform == 'win32')) {
  5838. // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
  5839. try { meshCoreObj.av = require('win-info').av(); meshCoreObjChanged(); } catch (ex) { av = null; } // Antivirus
  5840. //if (process.platform == 'win32') { try { meshCoreObj.pr = require('win-info').pendingReboot(); meshCoreObjChanged(); } catch (ex) { meshCoreObj.pr = null; } } // Pending reboot
  5841. }
  5842. if (process.platform == 'win32') {
  5843. if (require('MeshAgent')._securitycenter == null) {
  5844. try {
  5845. require('MeshAgent')._securitycenter = require('win-securitycenter').status();
  5846. meshCoreObj['wsc'] = require('MeshAgent')._securitycenter; // Windows Security Central (WSC)
  5847. require('win-securitycenter').on('changed', function () {
  5848. require('MeshAgent')._securitycenter = require('win-securitycenter').status();
  5849. meshCoreObj['wsc'] = require('MeshAgent')._securitycenter; // Windows Security Central (WSC)
  5850. require('MeshAgent').SendCommand({ action: 'coreinfo', wsc: require('MeshAgent')._securitycenter });
  5851. });
  5852. } catch (ex) { }
  5853. }
  5854. // Get Defender Information
  5855. try {
  5856. meshCoreObj.defender = require('win-info').defender();
  5857. meshCoreObjChanged();
  5858. } catch (ex) { }
  5859. }
  5860. // Send available data right now
  5861. if (force) {
  5862. meshCoreObj = sortObjRec(meshCoreObj);
  5863. var x = JSON.stringify(meshCoreObj);
  5864. if (x != LastPeriodicServerUpdate) {
  5865. LastPeriodicServerUpdate = x;
  5866. mesh.SendCommand(meshCoreObj);
  5867. }
  5868. }
  5869. }
  5870. // Sort the names in an object
  5871. function sortObject(obj) { return Object.keys(obj).sort().reduce(function(a, v) { a[v] = obj[v]; return a; }, {}); }
  5872. // Fix the incoming data and cut down how much data we use
  5873. function cleanGetBitLockerVolumeInfo(volumes) {
  5874. for (var i in volumes) {
  5875. const v = volumes[i];
  5876. if (typeof v.size == 'string') { v.size = parseInt(v.size); }
  5877. if (typeof v.sizeremaining == 'string') { v.sizeremaining = parseInt(v.sizeremaining); }
  5878. if (v.identifier == '') { delete v.identifier; }
  5879. if (v.name == '') { delete v.name; }
  5880. if (v.removable != true) { delete v.removable; }
  5881. if (v.cdrom != true) { delete v.cdrom; }
  5882. if (v.protectionStatus == 'On') { v.protectionStatus = true; } else { delete v.protectionStatus; }
  5883. if (v.volumeStatus == 'FullyDecrypted') { delete v.volumeStatus; }
  5884. if (v.recoveryPassword == '') { delete v.recoveryPassword; }
  5885. }
  5886. return sortObject(volumes);
  5887. }
  5888. // Once we are done collecting all the data, send to server if needed
  5889. var LastPeriodicServerUpdate = null;
  5890. var PeriodicServerUpdateNagleTimer = null;
  5891. function meshCoreObjChanged() {
  5892. if (PeriodicServerUpdateNagleTimer == null) {
  5893. PeriodicServerUpdateNagleTimer = setTimeout(meshCoreObjChangedEx, 500);
  5894. }
  5895. }
  5896. function meshCoreObjChangedEx() {
  5897. PeriodicServerUpdateNagleTimer = null;
  5898. meshCoreObj = sortObjRec(meshCoreObj);
  5899. var x = JSON.stringify(meshCoreObj);
  5900. if (x != LastPeriodicServerUpdate) {
  5901. try { LastPeriodicServerUpdate = x; mesh.SendCommand(meshCoreObj); } catch (ex) { }
  5902. }
  5903. }
  5904. function sortObjRec(o) { if ((typeof o != 'object') || (Array.isArray(o))) return o; for (var i in o) { if (typeof o[i] == 'object') { o[i] = sortObjRec(o[i]); } } return sortObj(o); }
  5905. function sortObj(o) { return Object.keys(o).sort().reduce(function (result, key) { result[key] = o[key]; return result; }, {}); }
  5906. function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
  5907. function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
  5908. function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
  5909. function onWebSocketUpgrade(response, s, head) {
  5910. sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
  5911. this.s = s;
  5912. s.httprequest = this;
  5913. s.end = onWebSocketClosed;
  5914. s.data = onWebSocketData;
  5915. }
  5916. mesh.AddCommandHandler(handleServerCommand);
  5917. mesh.AddConnectHandler(handleServerConnection);