You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 8 години
преди 15 години
преди 8 години
преди 15 години
преди 8 години
преди 15 години
преди 9 години
преди 9 години
преди 5 години
преди 10 години
преди 8 години
преди 10 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 4 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 7 години
преди 10 години
преди 8 години
преди 10 години
преди 10 години
преди 10 години
преди 6 години
преди 10 години
преди 10 години
преди 5 години
преди 9 години
преди 9 години
преди 8 години
преди 8 години
преди 9 години
преди 7 години
преди 8 години
преди 7 години
преди 8 години
преди 7 години
преди 7 години
преди 6 години
преди 7 години
преди 8 години
преди 8 години
преди 6 години
преди 8 години
преди 7 години
преди 8 години
преди 8 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 6 години
преди 10 години
преди 9 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 14 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 7 години
преди 9 години
преди 9 години
преди 10 години
преди 10 години
преди 6 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 9 години
преди 7 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 7 години
преди 9 години
преди 9 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 14 години
преди 10 години
преди 10 години
преди 14 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 5 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 6 години
преди 9 години
преди 9 години
преди 9 години
преди 8 години
преди 9 години
преди 8 години
преди 9 години
преди 9 години
преди 9 години
преди 8 години
преди 9 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 8 години
преди 9 години
преди 9 години
преди 7 години
преди 9 години
преди 10 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 10 години
преди 8 години
преди 6 години
преди 10 години
преди 10 години
преди 10 години
преди 6 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 6 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 9 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 10 години
преди 10 години
преди 6 години
преди 5 години
преди 7 години
преди 7 години
преди 7 години
преди 7 години
преди 10 години
преди 8 години
преди 8 години
преди 5 години
преди 8 години
преди 8 години
преди 5 години
преди 8 години
преди 5 години
преди 8 години
преди 5 години
преди 8 години
преди 8 години
преди 5 години
преди 8 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 8 години
преди 8 години
преди 9 години
преди 10 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 8 години
преди 5 години
преди 8 години
преди 8 години
преди 10 години
преди 8 години
преди 8 години
преди 9 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 14 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 9 години
преди 8 години
преди 9 години
преди 10 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 9 години
преди 10 години
преди 5 години
преди 5 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 10 години
преди 9 години
преди 6 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 7 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 6 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 4 години
преди 10 години
преди 4 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 4 години
преди 10 години
преди 4 години
преди 10 години
преди 8 години
преди 4 години
преди 10 години
преди 10 години
преди 4 години
преди 4 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 6 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 9 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 8 години
преди 8 години
преди 8 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 7 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 9 години
преди 8 години
преди 7 години
преди 10 години
преди 7 години
преди 10 години
преди 6 години
преди 10 години
преди 9 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 6 години
преди 4 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 10 години
преди 10 години
преди 7 години
преди 10 години
преди 5 години
преди 8 години
преди 8 години
преди 5 години
преди 7 години
преди 4 години
преди 6 години
преди 6 години
преди 6 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 10 години
преди 10 години
преди 5 години
преди 8 години
преди 10 години
преди 10 години
преди 4 години
преди 10 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 8 години
преди 10 години
преди 9 години
преди 9 години
преди 9 години
преди 10 години
преди 8 години
преди 9 години
преди 8 години
преди 8 години
преди 5 години
преди 7 години
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "config.h"
  17. #include "libserver/dynamic_cfg.h"
  18. #include "libserver/cfg_file_private.h"
  19. #include "libutil/rrd.h"
  20. #include "libserver/maps/map.h"
  21. #include "libserver/maps/map_helpers.h"
  22. #include "libserver/maps/map_private.h"
  23. #include "libserver/http/http_private.h"
  24. #include "libserver/http/http_router.h"
  25. #include "libstat/stat_api.h"
  26. #include "rspamd.h"
  27. #include "libserver/worker_util.h"
  28. #include "worker_private.h"
  29. #include "lua/lua_common.h"
  30. #include "cryptobox.h"
  31. #include "ottery.h"
  32. #include "fuzzy_wire.h"
  33. #include "unix-std.h"
  34. #include "utlist.h"
  35. #include "libmime/lang_detection.h"
  36. #include <math.h>
  37. /* 60 seconds for worker's IO */
  38. #define DEFAULT_WORKER_IO_TIMEOUT 60000
  39. /* HTTP paths */
  40. #define PATH_AUTH "/auth"
  41. #define PATH_SYMBOLS "/symbols"
  42. #define PATH_ACTIONS "/actions"
  43. #define PATH_MAPS "/maps"
  44. #define PATH_GET_MAP "/getmap"
  45. #define PATH_GRAPH "/graph"
  46. #define PATH_PIE_CHART "/pie"
  47. #define PATH_HISTORY "/history"
  48. #define PATH_HISTORY_RESET "/historyreset"
  49. #define PATH_LEARN_SPAM "/learnspam"
  50. #define PATH_LEARN_HAM "/learnham"
  51. #define PATH_SAVE_ACTIONS "/saveactions"
  52. #define PATH_SAVE_SYMBOLS "/savesymbols"
  53. #define PATH_SAVE_MAP "/savemap"
  54. #define PATH_SCAN "/scan"
  55. #define PATH_CHECK "/check"
  56. #define PATH_CHECKV2 "/checkv2"
  57. #define PATH_STAT "/stat"
  58. #define PATH_STAT_RESET "/statreset"
  59. #define PATH_COUNTERS "/counters"
  60. #define PATH_ERRORS "/errors"
  61. #define PATH_NEIGHBOURS "/neighbours"
  62. #define PATH_PLUGINS "/plugins"
  63. #define PATH_PING "/ping"
  64. #define msg_err_session(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
  65. session->pool->tag.tagname, session->pool->tag.uid, \
  66. G_STRFUNC, \
  67. __VA_ARGS__)
  68. #define msg_warn_session(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \
  69. session->pool->tag.tagname, session->pool->tag.uid, \
  70. G_STRFUNC, \
  71. __VA_ARGS__)
  72. #define msg_info_session(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \
  73. session->pool->tag.tagname, session->pool->tag.uid, \
  74. G_STRFUNC, \
  75. __VA_ARGS__)
  76. #define msg_err_ctx(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
  77. "controller", ctx->cfg->cfg_pool->tag.uid, \
  78. G_STRFUNC, \
  79. __VA_ARGS__)
  80. #define msg_warn_ctx(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \
  81. "controller", ctx->cfg->cfg_pool->tag.uid, \
  82. G_STRFUNC, \
  83. __VA_ARGS__)
  84. #define msg_info_ctx(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \
  85. "controller", ctx->cfg->cfg_pool->tag.uid, \
  86. G_STRFUNC, \
  87. __VA_ARGS__)
  88. #define msg_debug_session(...) rspamd_conditional_debug_fast (NULL, session->from_addr, \
  89. rspamd_controller_log_id, "controller", session->pool->tag.uid, \
  90. G_STRFUNC, \
  91. __VA_ARGS__)
  92. INIT_LOG_MODULE(controller)
  93. /* Graph colors */
  94. #define COLOR_CLEAN "#58A458"
  95. #define COLOR_PROBABLE_SPAM "#D67E7E"
  96. #define COLOR_GREYLIST "#A0A0A0"
  97. #define COLOR_REJECT "#CB4B4B"
  98. #define COLOR_TOTAL "#9440ED"
  99. static const guint64 rspamd_controller_ctx_magic = 0xf72697805e6941faULL;
  100. extern void fuzzy_stat_command (struct rspamd_task *task);
  101. gpointer init_controller_worker (struct rspamd_config *cfg);
  102. void start_controller_worker (struct rspamd_worker *worker);
  103. worker_t controller_worker = {
  104. "controller", /* Name */
  105. init_controller_worker, /* Init function */
  106. start_controller_worker, /* Start function */
  107. RSPAMD_WORKER_HAS_SOCKET | RSPAMD_WORKER_KILLABLE |
  108. RSPAMD_WORKER_SCANNER | RSPAMD_WORKER_CONTROLLER,
  109. RSPAMD_WORKER_SOCKET_TCP, /* TCP socket */
  110. RSPAMD_WORKER_VER /* Version info */
  111. };
  112. /*
  113. * Worker's context
  114. */
  115. struct rspamd_controller_worker_ctx {
  116. guint64 magic;
  117. /* Events base */
  118. struct ev_loop *event_loop;
  119. /* DNS resolver */
  120. struct rspamd_dns_resolver *resolver;
  121. /* Config */
  122. struct rspamd_config *cfg;
  123. /* END OF COMMON PART */
  124. ev_tstamp timeout;
  125. /* Whether we use ssl for this server */
  126. gboolean use_ssl;
  127. /* Webui password */
  128. gchar *password;
  129. /* Privilleged password */
  130. gchar *enable_password;
  131. /* Cached versions of the passwords */
  132. rspamd_ftok_t cached_password;
  133. rspamd_ftok_t cached_enable_password;
  134. /* HTTP server */
  135. struct rspamd_http_context *http_ctx;
  136. struct rspamd_http_connection_router *http;
  137. /* Server's start time */
  138. ev_tstamp start_time;
  139. /* Main server */
  140. struct rspamd_main *srv;
  141. /* SSL cert */
  142. gchar *ssl_cert;
  143. /* SSL private key */
  144. gchar *ssl_key;
  145. /* A map of secure IP */
  146. const ucl_object_t *secure_ip;
  147. struct rspamd_radix_map_helper *secure_map;
  148. /* Static files dir */
  149. gchar *static_files_dir;
  150. /* Custom commands registered by plugins */
  151. GHashTable *custom_commands;
  152. /* Plugins registered from lua */
  153. GHashTable *plugins;
  154. /* Worker */
  155. struct rspamd_worker *worker;
  156. /* Local keypair */
  157. gpointer key;
  158. struct rspamd_rrd_file *rrd;
  159. struct rspamd_lang_detector *lang_det;
  160. gdouble task_timeout;
  161. };
  162. struct rspamd_controller_plugin_cbdata {
  163. lua_State *L;
  164. struct rspamd_controller_worker_ctx *ctx;
  165. gchar *plugin;
  166. struct ucl_lua_funcdata *handler;
  167. ucl_object_t *obj;
  168. gboolean is_enable;
  169. gboolean need_task;
  170. guint version;
  171. };
  172. static gboolean
  173. rspamd_is_encrypted_password (const gchar *password,
  174. struct rspamd_controller_pbkdf const **pbkdf)
  175. {
  176. const gchar *start, *end;
  177. gint64 id;
  178. gsize size, i;
  179. gboolean ret = FALSE;
  180. const struct rspamd_controller_pbkdf *p;
  181. if (password[0] == '$') {
  182. /* Parse id */
  183. start = password + 1;
  184. end = start;
  185. size = 0;
  186. while (*end != '\0' && g_ascii_isdigit (*end)) {
  187. size++;
  188. end++;
  189. }
  190. if (size > 0) {
  191. gchar *endptr;
  192. id = strtoul (start, &endptr, 10);
  193. if ((endptr == NULL || *endptr == *end)) {
  194. for (i = 0; i < RSPAMD_PBKDF_ID_MAX - 1; i ++) {
  195. p = &pbkdf_list[i];
  196. if (p->id == id) {
  197. ret = TRUE;
  198. if (pbkdf != NULL) {
  199. *pbkdf = &pbkdf_list[i];
  200. }
  201. break;
  202. }
  203. }
  204. }
  205. }
  206. }
  207. return ret;
  208. }
  209. static const gchar *
  210. rspamd_encrypted_password_get_str (const gchar * password, gsize skip,
  211. gsize * length)
  212. {
  213. const gchar *str, *start, *end;
  214. gsize size;
  215. start = password + skip;
  216. end = start;
  217. size = 0;
  218. while (*end != '\0' && g_ascii_isalnum (*end)) {
  219. size++;
  220. end++;
  221. }
  222. if (size) {
  223. str = start;
  224. *length = size;
  225. }
  226. else {
  227. str = NULL;
  228. }
  229. return str;
  230. }
  231. static gboolean
  232. rspamd_check_encrypted_password (struct rspamd_controller_worker_ctx *ctx,
  233. const rspamd_ftok_t * password, const gchar * check,
  234. const struct rspamd_controller_pbkdf *pbkdf,
  235. gboolean is_enable)
  236. {
  237. const gchar *salt, *hash;
  238. gchar *salt_decoded, *key_decoded;
  239. gsize salt_len = 0, key_len = 0;
  240. gboolean ret = TRUE;
  241. guchar *local_key;
  242. rspamd_ftok_t *cache;
  243. gpointer m;
  244. /* First of all check cached versions to save resources */
  245. if (is_enable && ctx->cached_enable_password.len != 0) {
  246. if (password->len != ctx->cached_enable_password.len ||
  247. !rspamd_constant_memcmp (password->begin,
  248. ctx->cached_enable_password.begin, password->len)) {
  249. msg_info_ctx ("incorrect or absent enable password has been specified");
  250. return FALSE;
  251. }
  252. return TRUE;
  253. }
  254. else if (!is_enable && ctx->cached_password.len != 0) {
  255. if (password->len != ctx->cached_password.len ||
  256. !rspamd_constant_memcmp (password->begin,
  257. ctx->cached_password.begin, password->len)) {
  258. /* We still need to check enable password here */
  259. if (ctx->cached_enable_password.len != 0) {
  260. if (password->len != ctx->cached_enable_password.len ||
  261. !rspamd_constant_memcmp (password->begin,
  262. ctx->cached_enable_password.begin,
  263. password->len)) {
  264. msg_info_ctx (
  265. "incorrect or absent password has been specified");
  266. return FALSE;
  267. }
  268. else {
  269. /* Cached matched */
  270. return TRUE;
  271. }
  272. }
  273. else {
  274. /* We might want to check uncached version */
  275. goto check_uncached;
  276. }
  277. }
  278. else {
  279. /* Cached matched */
  280. return TRUE;
  281. }
  282. }
  283. check_uncached:
  284. g_assert (pbkdf != NULL);
  285. /* get salt */
  286. salt = rspamd_encrypted_password_get_str (check, 3, &salt_len);
  287. /* get hash */
  288. hash = rspamd_encrypted_password_get_str (check, 3 + salt_len + 1,
  289. &key_len);
  290. if (salt != NULL && hash != NULL) {
  291. /* decode salt */
  292. salt_decoded = rspamd_decode_base32 (salt, salt_len, &salt_len, RSPAMD_BASE32_DEFAULT);
  293. if (salt_decoded == NULL || salt_len != pbkdf->salt_len) {
  294. /* We have some unknown salt here */
  295. msg_info_ctx ("incorrect salt: %z, while %z expected",
  296. salt_len, pbkdf->salt_len);
  297. return FALSE;
  298. }
  299. key_decoded = rspamd_decode_base32 (hash, key_len, &key_len, RSPAMD_BASE32_DEFAULT);
  300. if (key_decoded == NULL || key_len != pbkdf->key_len) {
  301. /* We have some unknown salt here */
  302. msg_info_ctx ("incorrect key: %z, while %z expected",
  303. key_len, pbkdf->key_len);
  304. return FALSE;
  305. }
  306. local_key = g_alloca (pbkdf->key_len);
  307. rspamd_cryptobox_pbkdf (password->begin, password->len,
  308. salt_decoded, salt_len,
  309. local_key, pbkdf->key_len, pbkdf->complexity,
  310. pbkdf->type);
  311. if (!rspamd_constant_memcmp (key_decoded, local_key, pbkdf->key_len)) {
  312. msg_info_ctx ("incorrect or absent password has been specified");
  313. ret = FALSE;
  314. }
  315. g_free (salt_decoded);
  316. g_free (key_decoded);
  317. }
  318. if (ret) {
  319. /* Save cached version */
  320. cache = is_enable ? &ctx->cached_enable_password : &ctx->cached_password;
  321. if (cache->len == 0) {
  322. /* Mmap region */
  323. #ifdef MAP_NOCORE
  324. m = mmap (NULL, password->len, PROT_WRITE,
  325. MAP_PRIVATE | MAP_ANON | MAP_NOCORE, -1, 0);
  326. #else
  327. m = mmap (NULL, password->len, PROT_WRITE,
  328. MAP_PRIVATE | MAP_ANON, -1, 0);
  329. #endif
  330. if (m != MAP_FAILED) {
  331. memcpy (m, password->begin, password->len);
  332. (void)mprotect (m, password->len, PROT_READ);
  333. (void)mlock (m, password->len);
  334. cache->begin = m;
  335. cache->len = password->len;
  336. }
  337. else {
  338. msg_err_ctx ("cannot store cached password, mmap failed: %s",
  339. strerror (errno));
  340. }
  341. }
  342. }
  343. return ret;
  344. }
  345. /**
  346. * Checks for X-Forwarded-For header and update client's address if needed
  347. *
  348. * This function is intended to be called for a trusted client to ensure that
  349. * a request is not proxied through it
  350. * @return 0 if no forwarded found, 1 if forwarded found and it is yet trusted
  351. * and -1 if forwarded is denied
  352. */
  353. static gint
  354. rspamd_controller_check_forwarded (struct rspamd_controller_session *session,
  355. struct rspamd_http_message *msg,
  356. struct rspamd_controller_worker_ctx *ctx)
  357. {
  358. const rspamd_ftok_t *hdr;
  359. const gchar *comma;
  360. const char *hdr_name = "X-Forwarded-For", *alt_hdr_name = "X-Real-IP";
  361. char ip_buf[INET6_ADDRSTRLEN + 1];
  362. rspamd_inet_addr_t *addr = NULL;
  363. gint ret = 0;
  364. hdr = rspamd_http_message_find_header (msg, hdr_name);
  365. if (hdr) {
  366. /*
  367. * We need to parse and update the header
  368. * X-Forwarded-For: client, proxy1, proxy2
  369. */
  370. comma = rspamd_memrchr (hdr->begin, ',', hdr->len);
  371. if (comma != NULL) {
  372. while (comma < hdr->begin + hdr->len &&
  373. (*comma == ',' || g_ascii_isspace (*comma))) {
  374. comma ++;
  375. }
  376. }
  377. else {
  378. comma = hdr->begin;
  379. }
  380. if (rspamd_parse_inet_address (&addr, comma,
  381. (hdr->begin + hdr->len) - comma,
  382. RSPAMD_INET_ADDRESS_PARSE_NO_UNIX)) {
  383. /* We have addr now, so check if it is still trusted */
  384. if (ctx->secure_map &&
  385. rspamd_match_radix_map_addr (ctx->secure_map, addr) != NULL) {
  386. /* rspamd_inet_address_to_string is not reentrant */
  387. rspamd_strlcpy (ip_buf, rspamd_inet_address_to_string (addr),
  388. sizeof (ip_buf));
  389. msg_info_session ("allow unauthorized proxied connection "
  390. "from a trusted IP %s via %s",
  391. ip_buf,
  392. rspamd_inet_address_to_string (session->from_addr));
  393. ret = 1;
  394. }
  395. else {
  396. ret = -1;
  397. }
  398. rspamd_inet_address_free (addr);
  399. }
  400. else {
  401. msg_warn_session ("cannot parse forwarded IP: %T", hdr);
  402. ret = -1;
  403. }
  404. }
  405. else {
  406. /* Try also X-Real-IP */
  407. hdr = rspamd_http_message_find_header (msg, alt_hdr_name);
  408. if (hdr) {
  409. if (rspamd_parse_inet_address (&addr, hdr->begin, hdr->len,
  410. RSPAMD_INET_ADDRESS_PARSE_NO_UNIX)) {
  411. /* We have addr now, so check if it is still trusted */
  412. if (ctx->secure_map &&
  413. rspamd_match_radix_map_addr (ctx->secure_map, addr) != NULL) {
  414. /* rspamd_inet_address_to_string is not reentrant */
  415. rspamd_strlcpy (ip_buf, rspamd_inet_address_to_string (addr),
  416. sizeof (ip_buf));
  417. msg_info_session ("allow unauthorized proxied connection "
  418. "from a trusted IP %s via %s",
  419. ip_buf,
  420. rspamd_inet_address_to_string (session->from_addr));
  421. ret = 1;
  422. }
  423. else {
  424. ret = -1;
  425. }
  426. rspamd_inet_address_free (addr);
  427. }
  428. else {
  429. msg_warn_session ("cannot parse real IP: %T", hdr);
  430. ret = -1;
  431. }
  432. }
  433. }
  434. return ret;
  435. }
  436. /* Check for password if it is required by configuration */
  437. static gboolean
  438. rspamd_controller_check_password (struct rspamd_http_connection_entry *entry,
  439. struct rspamd_controller_session *session,
  440. struct rspamd_http_message *msg, gboolean is_enable)
  441. {
  442. const gchar *check;
  443. const rspamd_ftok_t *password;
  444. rspamd_ftok_t lookup;
  445. GHashTable *query_args = NULL;
  446. struct rspamd_controller_worker_ctx *ctx = session->ctx;
  447. gboolean check_normal = TRUE, check_enable = TRUE, ret = TRUE,
  448. use_enable = FALSE;
  449. const struct rspamd_controller_pbkdf *pbkdf = NULL;
  450. /* Access list logic */
  451. if (rspamd_inet_address_get_af (session->from_addr) == AF_UNIX) {
  452. ret = rspamd_controller_check_forwarded (session, msg, ctx);
  453. if (ret == 1) {
  454. session->is_enable = TRUE;
  455. return TRUE;
  456. }
  457. else if (ret == 0) {
  458. /* No forwarded found */
  459. msg_info_session ("allow unauthorized connection from a unix socket");
  460. session->is_enable = TRUE;
  461. return TRUE;
  462. }
  463. }
  464. else if (ctx->secure_map
  465. && rspamd_match_radix_map_addr (ctx->secure_map, session->from_addr)
  466. != NULL) {
  467. ret = rspamd_controller_check_forwarded (session, msg, ctx);
  468. if (ret == 1) {
  469. session->is_enable = TRUE;
  470. return TRUE;
  471. }
  472. else if (ret == 0) {
  473. /* No forwarded found */
  474. msg_info_session ("allow unauthorized connection from a trusted IP %s",
  475. rspamd_inet_address_to_string (session->from_addr));
  476. session->is_enable = TRUE;
  477. return TRUE;
  478. }
  479. }
  480. /* Password logic */
  481. password = rspamd_http_message_find_header (msg, "Password");
  482. if (password == NULL) {
  483. /* Try to get password from query args */
  484. query_args = rspamd_http_message_parse_query (msg);
  485. lookup.begin = (gchar *)"password";
  486. lookup.len = sizeof ("password") - 1;
  487. password = g_hash_table_lookup (query_args, &lookup);
  488. }
  489. if (password == NULL) {
  490. if (ctx->secure_map == NULL) {
  491. if (ctx->password == NULL && !is_enable) {
  492. return TRUE;
  493. }
  494. else if (is_enable && (ctx->password == NULL &&
  495. ctx->enable_password == NULL)) {
  496. session->is_enable = TRUE;
  497. return TRUE;
  498. }
  499. }
  500. msg_info_session ("absent password has been specified");
  501. ret = FALSE;
  502. }
  503. else {
  504. if (rspamd_ftok_cstr_equal (password, "q1", FALSE) ||
  505. rspamd_ftok_cstr_equal (password, "q2", FALSE)) {
  506. msg_info_session ("deny default password for remote access");
  507. ret = FALSE;
  508. goto end;
  509. }
  510. if (is_enable) {
  511. /* For privileged commands we strictly require enable password */
  512. if (ctx->enable_password != NULL) {
  513. check = ctx->enable_password;
  514. use_enable = TRUE;
  515. }
  516. else {
  517. /* Use just a password (legacy mode) */
  518. msg_info(
  519. "using password as enable_password for a privileged command");
  520. check = ctx->password;
  521. }
  522. if (check != NULL) {
  523. if (!rspamd_is_encrypted_password (check, &pbkdf)) {
  524. ret = FALSE;
  525. if (strlen (check) == password->len) {
  526. ret = rspamd_constant_memcmp (password->begin, check,
  527. password->len);
  528. }
  529. }
  530. else {
  531. ret = rspamd_check_encrypted_password (ctx, password, check,
  532. pbkdf, use_enable);
  533. }
  534. }
  535. else {
  536. msg_warn_session (
  537. "no password to check while executing a privileged command");
  538. ret = FALSE;
  539. }
  540. if (ret) {
  541. session->is_enable = TRUE;
  542. }
  543. }
  544. else {
  545. /* Accept both normal and enable passwords */
  546. if (ctx->password != NULL) {
  547. check = ctx->password;
  548. if (!rspamd_is_encrypted_password (check, &pbkdf)) {
  549. check_normal = FALSE;
  550. if (strlen (check) == password->len) {
  551. check_normal = rspamd_constant_memcmp (password->begin,
  552. check,
  553. password->len);
  554. }
  555. }
  556. else {
  557. check_normal = rspamd_check_encrypted_password (ctx,
  558. password,
  559. check, pbkdf, FALSE);
  560. }
  561. }
  562. else {
  563. check_normal = FALSE;
  564. }
  565. if (ctx->enable_password != NULL) {
  566. check = ctx->enable_password;
  567. if (!rspamd_is_encrypted_password (check, &pbkdf)) {
  568. check_enable = FALSE;
  569. if (strlen (check) == password->len) {
  570. check_enable = rspamd_constant_memcmp (password->begin,
  571. check,
  572. password->len);
  573. }
  574. }
  575. else {
  576. check_enable = rspamd_check_encrypted_password (ctx,
  577. password,
  578. check, pbkdf, TRUE);
  579. }
  580. if (check_enable) {
  581. session->is_enable = TRUE;
  582. }
  583. }
  584. else {
  585. check_enable = FALSE;
  586. if (check_normal) {
  587. /*
  588. * If no enable password is specified use normal password as
  589. * enable password
  590. */
  591. session->is_enable = TRUE;
  592. }
  593. }
  594. }
  595. }
  596. if (check_normal == FALSE && check_enable == FALSE) {
  597. msg_info ("absent or incorrect password has been specified");
  598. ret = FALSE;
  599. }
  600. end:
  601. if (query_args != NULL) {
  602. g_hash_table_unref (query_args);
  603. }
  604. if (!ret) {
  605. rspamd_controller_send_error (entry, 403, "Unauthorized");
  606. }
  607. return ret;
  608. }
  609. /* Command handlers */
  610. /*
  611. * Auth command handler:
  612. * request: /auth
  613. * headers: Password
  614. * reply: json {"auth": "ok", "version": "0.5.2", "uptime": "some uptime", "error": "none"}
  615. */
  616. static int
  617. rspamd_controller_handle_auth (struct rspamd_http_connection_entry *conn_ent,
  618. struct rspamd_http_message *msg)
  619. {
  620. struct rspamd_controller_session *session = conn_ent->ud;
  621. struct rspamd_stat *st;
  622. int64_t uptime;
  623. gulong data[5];
  624. ucl_object_t *obj;
  625. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  626. return 0;
  627. }
  628. obj = ucl_object_typed_new (UCL_OBJECT);
  629. st = session->ctx->srv->stat;
  630. data[0] = st->actions_stat[METRIC_ACTION_NOACTION];
  631. data[1] = st->actions_stat[METRIC_ACTION_ADD_HEADER] +
  632. st->actions_stat[METRIC_ACTION_REWRITE_SUBJECT];
  633. data[2] = st->actions_stat[METRIC_ACTION_GREYLIST];
  634. data[3] = st->actions_stat[METRIC_ACTION_REJECT];
  635. data[4] = st->actions_stat[METRIC_ACTION_SOFT_REJECT];
  636. /* Get uptime */
  637. uptime = ev_time () - session->ctx->start_time;
  638. ucl_object_insert_key (obj, ucl_object_fromstring (
  639. RVERSION), "version", 0, false);
  640. ucl_object_insert_key (obj, ucl_object_fromstring (
  641. "ok"), "auth", 0, false);
  642. ucl_object_insert_key (obj, ucl_object_fromint (
  643. uptime), "uptime", 0, false);
  644. ucl_object_insert_key (obj, ucl_object_fromint (
  645. data[0]), "clean", 0, false);
  646. ucl_object_insert_key (obj, ucl_object_fromint (
  647. data[1]), "probable", 0, false);
  648. ucl_object_insert_key (obj, ucl_object_fromint (
  649. data[2]), "greylist", 0, false);
  650. ucl_object_insert_key (obj, ucl_object_fromint (
  651. data[3]), "reject", 0, false);
  652. ucl_object_insert_key (obj, ucl_object_fromint (
  653. data[4]), "soft_reject", 0, false);
  654. ucl_object_insert_key (obj, ucl_object_fromint (
  655. st->messages_scanned), "scanned", 0, false);
  656. ucl_object_insert_key (obj, ucl_object_fromint (
  657. st->messages_learned), "learned", 0, false);
  658. ucl_object_insert_key (obj, ucl_object_frombool (!session->is_enable),
  659. "read_only", 0, false);
  660. ucl_object_insert_key (obj, ucl_object_fromstring (session->ctx->cfg->checksum),
  661. "config_id", 0, false);
  662. rspamd_controller_send_ucl (conn_ent, obj);
  663. ucl_object_unref (obj);
  664. return 0;
  665. }
  666. /*
  667. * Symbols command handler:
  668. * request: /symbols
  669. * reply: json [{
  670. * "name": "group_name",
  671. * "symbols": [
  672. * {
  673. * "name": "name",
  674. * "weight": 0.1,
  675. * "description": "description of symbol"
  676. * },
  677. * {...}
  678. * },
  679. * {...}]
  680. */
  681. static int
  682. rspamd_controller_handle_symbols (struct rspamd_http_connection_entry *conn_ent,
  683. struct rspamd_http_message *msg)
  684. {
  685. struct rspamd_controller_session *session = conn_ent->ud;
  686. GHashTableIter it, sit;
  687. struct rspamd_symbols_group *gr;
  688. struct rspamd_symbol *sym;
  689. ucl_object_t *obj, *top, *sym_obj, *group_symbols;
  690. gpointer k, v;
  691. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  692. return 0;
  693. }
  694. top = ucl_object_typed_new (UCL_ARRAY);
  695. /* Go through all symbols groups in the default metric */
  696. g_hash_table_iter_init (&it, session->cfg->groups);
  697. while (g_hash_table_iter_next (&it, &k, &v)) {
  698. gr = v;
  699. obj = ucl_object_typed_new (UCL_OBJECT);
  700. ucl_object_insert_key (obj, ucl_object_fromstring (
  701. gr->name), "group", 0, false);
  702. /* Iterate through all symbols */
  703. g_hash_table_iter_init (&sit, gr->symbols);
  704. group_symbols = ucl_object_typed_new (UCL_ARRAY);
  705. while (g_hash_table_iter_next (&sit, &k, &v)) {
  706. gdouble tm = 0.0, freq = 0, freq_dev = 0;
  707. sym = v;
  708. sym_obj = ucl_object_typed_new (UCL_OBJECT);
  709. ucl_object_insert_key (sym_obj, ucl_object_fromstring (sym->name),
  710. "symbol", 0, false);
  711. ucl_object_insert_key (sym_obj,
  712. ucl_object_fromdouble (*sym->weight_ptr),
  713. "weight", 0, false);
  714. if (sym->description) {
  715. ucl_object_insert_key (sym_obj,
  716. ucl_object_fromstring (sym->description),
  717. "description", 0, false);
  718. }
  719. if (rspamd_symcache_stat_symbol (session->ctx->cfg->cache,
  720. sym->name, &freq, &freq_dev, &tm, NULL)) {
  721. ucl_object_insert_key (sym_obj,
  722. ucl_object_fromdouble (freq),
  723. "frequency", 0, false);
  724. ucl_object_insert_key (sym_obj,
  725. ucl_object_fromdouble (freq_dev),
  726. "frequency_stddev", 0, false);
  727. ucl_object_insert_key (sym_obj,
  728. ucl_object_fromdouble (tm),
  729. "time", 0, false);
  730. }
  731. ucl_array_append (group_symbols, sym_obj);
  732. }
  733. ucl_object_insert_key (obj, group_symbols, "rules", 0, false);
  734. ucl_array_append (top, obj);
  735. }
  736. rspamd_controller_send_ucl (conn_ent, top);
  737. ucl_object_unref (top);
  738. return 0;
  739. }
  740. /*
  741. * Actions command handler:
  742. * request: /actions
  743. * reply: json [{
  744. * "action": "no action",
  745. * "value": 1.1
  746. * },
  747. * {...}]
  748. */
  749. static int
  750. rspamd_controller_handle_actions (struct rspamd_http_connection_entry *conn_ent,
  751. struct rspamd_http_message *msg)
  752. {
  753. struct rspamd_controller_session *session = conn_ent->ud;
  754. struct rspamd_action *act, *tmp;
  755. ucl_object_t *obj, *top;
  756. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  757. return 0;
  758. }
  759. top = ucl_object_typed_new (UCL_ARRAY);
  760. HASH_ITER (hh, session->cfg->actions, act, tmp) {
  761. obj = ucl_object_typed_new (UCL_OBJECT);
  762. ucl_object_insert_key (obj,
  763. ucl_object_fromstring (act->name),
  764. "action", 0, false);
  765. ucl_object_insert_key (obj,
  766. ucl_object_fromdouble (act->threshold),
  767. "value", 0, false);
  768. ucl_array_append (top, obj);
  769. }
  770. rspamd_controller_send_ucl (conn_ent, top);
  771. ucl_object_unref (top);
  772. return 0;
  773. }
  774. static gboolean
  775. rspamd_controller_can_edit_map (struct rspamd_map_backend *bk)
  776. {
  777. gchar *fpath;
  778. if (access (bk->uri, W_OK) == 0) {
  779. return TRUE;
  780. }
  781. else if (access (bk->uri, R_OK) == -1 && errno == ENOENT) {
  782. fpath = g_path_get_dirname (bk->uri);
  783. if (fpath) {
  784. if (access (fpath, W_OK) == 0) {
  785. g_free (fpath);
  786. return TRUE;
  787. }
  788. g_free (fpath);
  789. }
  790. }
  791. return FALSE;
  792. }
  793. /*
  794. * Maps command handler:
  795. * request: /maps
  796. * headers: Password
  797. * reply: json [
  798. * {
  799. * "map": "name",
  800. * "description": "description",
  801. * "editable": true
  802. * },
  803. * {...}
  804. * ]
  805. */
  806. static int
  807. rspamd_controller_handle_maps (struct rspamd_http_connection_entry *conn_ent,
  808. struct rspamd_http_message *msg)
  809. {
  810. struct rspamd_controller_session *session = conn_ent->ud;
  811. GList *cur;
  812. struct rspamd_map *map;
  813. struct rspamd_map_backend *bk;
  814. guint i;
  815. gboolean editable;
  816. ucl_object_t *obj, *top;
  817. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  818. return 0;
  819. }
  820. top = ucl_object_typed_new (UCL_ARRAY);
  821. /* Iterate over all maps */
  822. cur = session->ctx->cfg->maps;
  823. while (cur) {
  824. map = cur->data;
  825. PTR_ARRAY_FOREACH (map->backends, i, bk) {
  826. if (bk->protocol == MAP_PROTO_FILE) {
  827. editable = rspamd_controller_can_edit_map (bk);
  828. if (!editable && access (bk->uri, R_OK) == -1) {
  829. /* Skip unreadable and non-existing maps */
  830. continue;
  831. }
  832. obj = ucl_object_typed_new (UCL_OBJECT);
  833. ucl_object_insert_key (obj, ucl_object_fromint (bk->id),
  834. "map", 0, false);
  835. if (map->description) {
  836. ucl_object_insert_key (obj, ucl_object_fromstring (map->description),
  837. "description", 0, false);
  838. }
  839. ucl_object_insert_key (obj, ucl_object_fromstring (bk->uri),
  840. "uri", 0, false);
  841. ucl_object_insert_key (obj, ucl_object_frombool (editable),
  842. "editable", 0, false);
  843. ucl_array_append (top, obj);
  844. }
  845. }
  846. cur = g_list_next (cur);
  847. }
  848. rspamd_controller_send_ucl (conn_ent, top);
  849. ucl_object_unref (top);
  850. return 0;
  851. }
  852. /*
  853. * Get map command handler:
  854. * request: /getmap
  855. * headers: Password, Map
  856. * reply: plain-text
  857. */
  858. static int
  859. rspamd_controller_handle_get_map (struct rspamd_http_connection_entry *conn_ent,
  860. struct rspamd_http_message *msg)
  861. {
  862. struct rspamd_controller_session *session = conn_ent->ud;
  863. GList *cur;
  864. struct rspamd_map *map;
  865. struct rspamd_map_backend *bk = NULL;
  866. const rspamd_ftok_t *idstr;
  867. struct stat st;
  868. gint fd;
  869. gulong id, i;
  870. gboolean found = FALSE;
  871. struct rspamd_http_message *reply;
  872. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  873. return 0;
  874. }
  875. idstr = rspamd_http_message_find_header (msg, "Map");
  876. if (idstr == NULL) {
  877. msg_info_session ("absent map id");
  878. rspamd_controller_send_error (conn_ent, 400, "Id header missing");
  879. return 0;
  880. }
  881. if (!rspamd_strtoul (idstr->begin, idstr->len, &id)) {
  882. msg_info_session ("invalid map id");
  883. rspamd_controller_send_error (conn_ent, 400, "Invalid map id");
  884. return 0;
  885. }
  886. /* Now let's be sure that we have map defined in configuration */
  887. cur = session->ctx->cfg->maps;
  888. while (cur && !found) {
  889. map = cur->data;
  890. PTR_ARRAY_FOREACH (map->backends, i, bk) {
  891. if (bk->id == id && bk->protocol == MAP_PROTO_FILE) {
  892. found = TRUE;
  893. break;
  894. }
  895. }
  896. cur = g_list_next (cur);
  897. }
  898. if (!found || bk == NULL) {
  899. msg_info_session ("map not found");
  900. rspamd_controller_send_error (conn_ent, 404, "Map not found");
  901. return 0;
  902. }
  903. if (stat (bk->uri, &st) == -1 || (fd = open (bk->uri, O_RDONLY)) == -1) {
  904. reply = rspamd_http_new_message (HTTP_RESPONSE);
  905. reply->date = time (NULL);
  906. reply->code = 200;
  907. }
  908. else {
  909. reply = rspamd_http_new_message (HTTP_RESPONSE);
  910. reply->date = time (NULL);
  911. reply->code = 200;
  912. if (st.st_size > 0) {
  913. if (!rspamd_http_message_set_body_from_fd (reply, fd)) {
  914. close (fd);
  915. rspamd_http_message_unref (reply);
  916. msg_err_session ("cannot read map %s: %s", bk->uri, strerror (errno));
  917. rspamd_controller_send_error (conn_ent, 500, "Map read error");
  918. return 0;
  919. }
  920. }
  921. else {
  922. rspamd_fstring_t *empty_body = rspamd_fstring_new_init ("", 0);
  923. rspamd_http_message_set_body_from_fstring_steal (reply, empty_body);
  924. }
  925. close (fd);
  926. }
  927. rspamd_http_connection_reset (conn_ent->conn);
  928. rspamd_http_router_insert_headers (conn_ent->rt, reply);
  929. rspamd_http_connection_write_message (conn_ent->conn, reply, NULL,
  930. "text/plain", conn_ent,
  931. conn_ent->rt->timeout);
  932. conn_ent->is_reply = TRUE;
  933. return 0;
  934. }
  935. static ucl_object_t *
  936. rspamd_controller_pie_element (enum rspamd_action_type action,
  937. const char *label, gdouble data)
  938. {
  939. ucl_object_t *res = ucl_object_typed_new (UCL_OBJECT);
  940. const char *colors[METRIC_ACTION_MAX] = {
  941. [METRIC_ACTION_REJECT] = "#FF0000",
  942. [METRIC_ACTION_SOFT_REJECT] = "#cc9966",
  943. [METRIC_ACTION_REWRITE_SUBJECT] = "#ff6600",
  944. [METRIC_ACTION_ADD_HEADER] = "#FFD700",
  945. [METRIC_ACTION_GREYLIST] = "#436EEE",
  946. [METRIC_ACTION_NOACTION] = "#66cc00"
  947. };
  948. ucl_object_insert_key (res, ucl_object_fromstring (colors[action]),
  949. "color", 0, false);
  950. ucl_object_insert_key (res, ucl_object_fromstring (label), "label", 0, false);
  951. ucl_object_insert_key (res, ucl_object_fromdouble (data), "data", 0, false);
  952. ucl_object_insert_key (res, ucl_object_fromdouble (data), "value", 0, false);
  953. return res;
  954. }
  955. /*
  956. * Pie chart command handler:
  957. * request: /pie
  958. * headers: Password
  959. * reply: json [
  960. * { label: "Foo", data: 11 },
  961. * { label: "Bar", data: 20 },
  962. * {...}
  963. * ]
  964. */
  965. static int
  966. rspamd_controller_handle_pie_chart (
  967. struct rspamd_http_connection_entry *conn_ent,
  968. struct rspamd_http_message *msg)
  969. {
  970. struct rspamd_controller_session *session = conn_ent->ud;
  971. struct rspamd_controller_worker_ctx *ctx;
  972. gdouble data[5], total;
  973. ucl_object_t *top;
  974. ctx = session->ctx;
  975. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  976. return 0;
  977. }
  978. top = ucl_object_typed_new (UCL_ARRAY);
  979. total = ctx->srv->stat->messages_scanned;
  980. if (total != 0) {
  981. data[0] = ctx->srv->stat->actions_stat[METRIC_ACTION_NOACTION];
  982. data[1] = ctx->srv->stat->actions_stat[METRIC_ACTION_SOFT_REJECT];
  983. data[2] = (ctx->srv->stat->actions_stat[METRIC_ACTION_ADD_HEADER] +
  984. ctx->srv->stat->actions_stat[METRIC_ACTION_REWRITE_SUBJECT]);
  985. data[3] = ctx->srv->stat->actions_stat[METRIC_ACTION_GREYLIST];
  986. data[4] = ctx->srv->stat->actions_stat[METRIC_ACTION_REJECT];
  987. }
  988. else {
  989. memset (data, 0, sizeof (data));
  990. }
  991. ucl_array_append (top, rspamd_controller_pie_element (
  992. METRIC_ACTION_NOACTION, "Clean", data[0]));
  993. ucl_array_append (top, rspamd_controller_pie_element (
  994. METRIC_ACTION_SOFT_REJECT, "Temporarily rejected", data[1]));
  995. ucl_array_append (top, rspamd_controller_pie_element (
  996. METRIC_ACTION_ADD_HEADER, "Probable spam", data[2]));
  997. ucl_array_append (top, rspamd_controller_pie_element (
  998. METRIC_ACTION_GREYLIST, "Greylisted", data[3]));
  999. ucl_array_append (top, rspamd_controller_pie_element (
  1000. METRIC_ACTION_REJECT, "Rejected", data[4]));
  1001. rspamd_controller_send_ucl (conn_ent, top);
  1002. ucl_object_unref (top);
  1003. return 0;
  1004. }
  1005. void
  1006. rspamd_controller_graph_point (gulong t, gulong step,
  1007. struct rspamd_rrd_query_result* rrd_result,
  1008. gdouble *acc,
  1009. ucl_object_t **elt)
  1010. {
  1011. guint nan_cnt;
  1012. gdouble sum = 0.0, yval;
  1013. ucl_object_t* data_elt;
  1014. guint i, j;
  1015. for (i = 0; i < rrd_result->ds_count; i++) {
  1016. sum = 0.0;
  1017. nan_cnt = 0;
  1018. data_elt = ucl_object_typed_new (UCL_OBJECT);
  1019. ucl_object_insert_key (data_elt, ucl_object_fromint (t), "x", 1, false);
  1020. for (j = 0; j < step; j++) {
  1021. yval = acc[i + j * rrd_result->ds_count];
  1022. if (!isfinite (yval)) {
  1023. nan_cnt++;
  1024. }
  1025. else {
  1026. sum += yval;
  1027. }
  1028. }
  1029. if (nan_cnt == step) {
  1030. ucl_object_insert_key (data_elt, ucl_object_typed_new (UCL_NULL),
  1031. "y", 1, false);
  1032. }
  1033. else {
  1034. ucl_object_insert_key (data_elt,
  1035. ucl_object_fromdouble (sum / (gdouble) step), "y", 1,
  1036. false);
  1037. }
  1038. ucl_array_append (elt[i], data_elt);
  1039. }
  1040. }
  1041. /*
  1042. * Graph command handler:
  1043. * request: /graph?type=<day|week|month|year>
  1044. * headers: Password
  1045. * reply: json [
  1046. * { label: "Foo", data: 11 },
  1047. * { label: "Bar", data: 20 },
  1048. * {...}
  1049. * ]
  1050. */
  1051. static int
  1052. rspamd_controller_handle_graph (
  1053. struct rspamd_http_connection_entry *conn_ent,
  1054. struct rspamd_http_message *msg)
  1055. {
  1056. GHashTable *query;
  1057. struct rspamd_controller_session *session = conn_ent->ud;
  1058. struct rspamd_controller_worker_ctx *ctx;
  1059. rspamd_ftok_t srch, *value;
  1060. struct rspamd_rrd_query_result *rrd_result;
  1061. gulong i, k, start_row, cnt, t, ts, step;
  1062. gdouble *acc;
  1063. ucl_object_t *res, *elt[METRIC_ACTION_MAX];
  1064. enum {
  1065. rra_day = 0,
  1066. rra_week,
  1067. rra_month,
  1068. rra_year,
  1069. rra_invalid
  1070. } rra_num = rra_invalid;
  1071. /* How many points are we going to send to display */
  1072. static const guint desired_points = 500;
  1073. ctx = session->ctx;
  1074. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  1075. return 0;
  1076. }
  1077. if (ctx->rrd == NULL) {
  1078. msg_err_session ("no rrd configured");
  1079. rspamd_controller_send_error (conn_ent, 404, "No rrd configured for graphs");
  1080. return 0;
  1081. }
  1082. query = rspamd_http_message_parse_query (msg);
  1083. srch.begin = (gchar *)"type";
  1084. srch.len = 4;
  1085. if (query == NULL || (value = g_hash_table_lookup (query, &srch)) == NULL) {
  1086. msg_err_session ("absent graph type query");
  1087. rspamd_controller_send_error (conn_ent, 400, "Absent graph type");
  1088. if (query) {
  1089. g_hash_table_unref (query);
  1090. }
  1091. return 0;
  1092. }
  1093. if (value->len == 3 && rspamd_lc_cmp (value->begin, "day", value->len) == 0) {
  1094. rra_num = rra_day;
  1095. }
  1096. else if (value->len == 4 && rspamd_lc_cmp (value->begin, "week", value->len) == 0) {
  1097. rra_num = rra_week;
  1098. }
  1099. else if (value->len == 5 && rspamd_lc_cmp (value->begin, "month", value->len) == 0) {
  1100. rra_num = rra_month;
  1101. }
  1102. else if (value->len == 4 && rspamd_lc_cmp (value->begin, "year", value->len) == 0) {
  1103. rra_num = rra_year;
  1104. }
  1105. g_hash_table_unref (query);
  1106. if (rra_num == rra_invalid) {
  1107. msg_err_session ("invalid graph type query");
  1108. rspamd_controller_send_error (conn_ent, 400, "Invalid graph type");
  1109. return 0;
  1110. }
  1111. rrd_result = rspamd_rrd_query (ctx->rrd, rra_num);
  1112. if (rrd_result == NULL) {
  1113. msg_err_session ("cannot query rrd");
  1114. rspamd_controller_send_error (conn_ent, 500, "Cannot query rrd");
  1115. return 0;
  1116. }
  1117. g_assert (rrd_result->ds_count == G_N_ELEMENTS (elt));
  1118. res = ucl_object_typed_new (UCL_ARRAY);
  1119. /* How much full updates happened since the last update */
  1120. ts = rrd_result->last_update / rrd_result->pdp_per_cdp - rrd_result->rra_rows;
  1121. for (i = 0; i < rrd_result->ds_count; i ++) {
  1122. elt[i] = ucl_object_typed_new (UCL_ARRAY);
  1123. }
  1124. start_row = rrd_result->cur_row == rrd_result->rra_rows - 1 ?
  1125. 0 : rrd_result->cur_row;
  1126. t = ts * rrd_result->pdp_per_cdp;
  1127. k = 0;
  1128. /* Create window */
  1129. step = ceil (((gdouble)rrd_result->rra_rows) / desired_points);
  1130. g_assert (step >= 1);
  1131. acc = g_malloc0 (sizeof (double) * rrd_result->ds_count * step);
  1132. for (i = start_row, cnt = 0; cnt < rrd_result->rra_rows;
  1133. cnt ++) {
  1134. memcpy (&acc[k * rrd_result->ds_count],
  1135. &rrd_result->data[i * rrd_result->ds_count],
  1136. sizeof (gdouble) * rrd_result->ds_count);
  1137. if (k < step - 1) {
  1138. k ++;
  1139. }
  1140. else {
  1141. t = ts * rrd_result->pdp_per_cdp;
  1142. /* Need a fresh point */
  1143. rspamd_controller_graph_point (t, step, rrd_result, acc, elt);
  1144. k = 0;
  1145. }
  1146. if (i == rrd_result->rra_rows - 1) {
  1147. i = 0;
  1148. }
  1149. else {
  1150. i ++;
  1151. }
  1152. ts ++;
  1153. }
  1154. if (k > 0) {
  1155. rspamd_controller_graph_point (t, k, rrd_result, acc, elt);
  1156. }
  1157. for (i = 0; i < rrd_result->ds_count; i++) {
  1158. ucl_array_append (res, elt[i]);
  1159. }
  1160. rspamd_controller_send_ucl (conn_ent, res);
  1161. ucl_object_unref (res);
  1162. g_free (acc);
  1163. return 0;
  1164. }
  1165. static void
  1166. rspamd_controller_handle_legacy_history (
  1167. struct rspamd_controller_session *session,
  1168. struct rspamd_controller_worker_ctx *ctx,
  1169. struct rspamd_http_connection_entry *conn_ent,
  1170. struct rspamd_http_message *msg)
  1171. {
  1172. struct roll_history_row *row, *copied_rows;
  1173. guint i, rows_proc, row_num;
  1174. struct tm tm;
  1175. gchar timebuf[32], **syms;
  1176. ucl_object_t *top, *obj;
  1177. top = ucl_object_typed_new (UCL_ARRAY);
  1178. /* Set lock on history */
  1179. copied_rows = g_malloc (sizeof (*copied_rows) * ctx->srv->history->nrows);
  1180. memcpy (copied_rows, ctx->srv->history->rows,
  1181. sizeof (*copied_rows) * ctx->srv->history->nrows);
  1182. /* Go through all rows */
  1183. row_num = ctx->srv->history->cur_row;
  1184. for (i = 0, rows_proc = 0; i < ctx->srv->history->nrows; i++, row_num++) {
  1185. if (row_num == ctx->srv->history->nrows) {
  1186. row_num = 0;
  1187. }
  1188. row = &copied_rows[row_num];
  1189. /* Get only completed rows */
  1190. if (row->completed) {
  1191. rspamd_localtime (row->timestamp, &tm);
  1192. strftime (timebuf, sizeof (timebuf) - 1, "%Y-%m-%d %H:%M:%S", &tm);
  1193. obj = ucl_object_typed_new (UCL_OBJECT);
  1194. ucl_object_insert_key (obj, ucl_object_fromstring (
  1195. timebuf), "time", 0, false);
  1196. ucl_object_insert_key (obj, ucl_object_fromint (
  1197. row->timestamp), "unix_time", 0, false);
  1198. ucl_object_insert_key (obj, ucl_object_fromstring (
  1199. row->message_id), "id", 0, false);
  1200. ucl_object_insert_key (obj, ucl_object_fromstring (row->from_addr),
  1201. "ip", 0, false);
  1202. ucl_object_insert_key (obj,
  1203. ucl_object_fromstring (rspamd_action_to_str (
  1204. row->action)), "action", 0, false);
  1205. if (!isnan (row->score)) {
  1206. ucl_object_insert_key (obj, ucl_object_fromdouble (
  1207. row->score), "score", 0, false);
  1208. }
  1209. else {
  1210. ucl_object_insert_key (obj,
  1211. ucl_object_fromdouble (0.0), "score", 0, false);
  1212. }
  1213. if (!isnan (row->required_score)) {
  1214. ucl_object_insert_key (obj,
  1215. ucl_object_fromdouble (
  1216. row->required_score), "required_score", 0, false);
  1217. }
  1218. else {
  1219. ucl_object_insert_key (obj,
  1220. ucl_object_fromdouble (0.0), "required_score", 0, false);
  1221. }
  1222. syms = g_strsplit_set (row->symbols, ", ", -1);
  1223. if (syms) {
  1224. guint nelts = g_strv_length (syms);
  1225. ucl_object_t *syms_obj = ucl_object_typed_new (UCL_OBJECT);
  1226. ucl_object_reserve (syms_obj, nelts);
  1227. for (guint j = 0; j < nelts; j++) {
  1228. g_strstrip (syms[j]);
  1229. if (strlen (syms[j]) == 0) {
  1230. /* Empty garbadge */
  1231. continue;
  1232. }
  1233. ucl_object_t *cur = ucl_object_typed_new (UCL_OBJECT);
  1234. ucl_object_insert_key (cur, ucl_object_fromdouble (0.0),
  1235. "score", 0, false);
  1236. ucl_object_insert_key (syms_obj, cur, syms[j], 0, true);
  1237. }
  1238. ucl_object_insert_key (obj, syms_obj, "symbols", 0, false);
  1239. g_strfreev (syms);
  1240. }
  1241. ucl_object_insert_key (obj, ucl_object_fromint (row->len),
  1242. "size", 0, false);
  1243. ucl_object_insert_key (obj,
  1244. ucl_object_fromdouble (row->scan_time),
  1245. "scan_time", 0, false);
  1246. if (row->user[0] != '\0') {
  1247. ucl_object_insert_key (obj, ucl_object_fromstring (row->user),
  1248. "user", 0, false);
  1249. }
  1250. if (row->from_addr[0] != '\0') {
  1251. ucl_object_insert_key (obj, ucl_object_fromstring (
  1252. row->from_addr), "from", 0, false);
  1253. }
  1254. ucl_array_append (top, obj);
  1255. rows_proc++;
  1256. }
  1257. }
  1258. rspamd_controller_send_ucl (conn_ent, top);
  1259. ucl_object_unref (top);
  1260. g_free (copied_rows);
  1261. }
  1262. static gboolean
  1263. rspamd_controller_history_lua_fin_task (void *ud)
  1264. {
  1265. return TRUE;
  1266. }
  1267. static void
  1268. rspamd_controller_handle_lua_history (lua_State *L,
  1269. struct rspamd_controller_session *session,
  1270. struct rspamd_controller_worker_ctx *ctx,
  1271. struct rspamd_http_connection_entry *conn_ent,
  1272. struct rspamd_http_message *msg,
  1273. gboolean reset)
  1274. {
  1275. struct rspamd_task *task, **ptask;
  1276. struct rspamd_http_connection_entry **pconn_ent;
  1277. GHashTable *params;
  1278. rspamd_ftok_t srch, *found;
  1279. glong from = 0, to = -1;
  1280. params = rspamd_http_message_parse_query (msg);
  1281. if (params) {
  1282. /* Check from and to */
  1283. RSPAMD_FTOK_ASSIGN (&srch, "from");
  1284. found = g_hash_table_lookup (params, &srch);
  1285. if (found) {
  1286. rspamd_strtol (found->begin, found->len, &from);
  1287. }
  1288. RSPAMD_FTOK_ASSIGN (&srch, "to");
  1289. found = g_hash_table_lookup (params, &srch);
  1290. if (found) {
  1291. rspamd_strtol (found->begin, found->len, &to);
  1292. }
  1293. g_hash_table_unref (params);
  1294. }
  1295. lua_getglobal (L, "rspamd_plugins");
  1296. if (lua_istable (L, -1)) {
  1297. lua_pushstring (L, "history");
  1298. lua_gettable (L, -2);
  1299. if (lua_istable (L, -1)) {
  1300. lua_pushstring (L, "handler");
  1301. lua_gettable (L, -2);
  1302. if (lua_isfunction (L, -1)) {
  1303. task = rspamd_task_new (session->ctx->worker, session->cfg,
  1304. session->pool, ctx->lang_det, ctx->event_loop, FALSE);
  1305. task->resolver = ctx->resolver;
  1306. task->s = rspamd_session_create (session->pool,
  1307. rspamd_controller_history_lua_fin_task,
  1308. NULL,
  1309. (event_finalizer_t )rspamd_task_free,
  1310. task);
  1311. task->fin_arg = conn_ent;
  1312. ptask = lua_newuserdata (L, sizeof (*ptask));
  1313. *ptask = task;
  1314. rspamd_lua_setclass (L, "rspamd{task}", -1);
  1315. pconn_ent = lua_newuserdata (L, sizeof (*pconn_ent));
  1316. *pconn_ent = conn_ent;
  1317. rspamd_lua_setclass (L, "rspamd{csession}", -1);
  1318. lua_pushinteger (L, from);
  1319. lua_pushinteger (L, to);
  1320. lua_pushboolean (L, reset);
  1321. if (lua_pcall (L, 5, 0, 0) != 0) {
  1322. msg_err_session ("call to history function failed: %s",
  1323. lua_tostring (L, -1));
  1324. lua_settop (L, 0);
  1325. rspamd_task_free (task);
  1326. goto err;
  1327. }
  1328. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
  1329. task->sock = -1;
  1330. session->task = task;
  1331. rspamd_session_pending (task->s);
  1332. }
  1333. else {
  1334. msg_err_session ("rspamd_plugins.history.handler is not a function");
  1335. lua_settop (L, 0);
  1336. goto err;
  1337. }
  1338. }
  1339. else {
  1340. msg_err_session ("rspamd_plugins.history is not a table");
  1341. lua_settop (L, 0);
  1342. goto err;
  1343. }
  1344. }
  1345. else {
  1346. msg_err_session ("rspamd_plugins is absent or has incorrect type");
  1347. lua_settop (L, 0);
  1348. goto err;
  1349. }
  1350. lua_settop (L, 0);
  1351. return;
  1352. err:
  1353. rspamd_controller_send_error (conn_ent, 500, "Internal error");
  1354. }
  1355. /*
  1356. * History command handler:
  1357. * request: /history
  1358. * headers: Password
  1359. * reply: json [
  1360. * { label: "Foo", data: 11 },
  1361. * { label: "Bar", data: 20 },
  1362. * {...}
  1363. * ]
  1364. */
  1365. static int
  1366. rspamd_controller_handle_history (struct rspamd_http_connection_entry *conn_ent,
  1367. struct rspamd_http_message *msg)
  1368. {
  1369. struct rspamd_controller_session *session = conn_ent->ud;
  1370. struct rspamd_controller_worker_ctx *ctx;
  1371. lua_State *L;
  1372. ctx = session->ctx;
  1373. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  1374. return 0;
  1375. }
  1376. L = ctx->cfg->lua_state;
  1377. if (!ctx->srv->history->disabled) {
  1378. rspamd_controller_handle_legacy_history (session, ctx, conn_ent, msg);
  1379. }
  1380. else {
  1381. rspamd_controller_handle_lua_history (L, session, ctx, conn_ent, msg,
  1382. FALSE);
  1383. }
  1384. return 0;
  1385. }
  1386. /*
  1387. * Errors command handler:
  1388. * request: /errors
  1389. * headers: Password
  1390. * reply: json [
  1391. * { ts: 100500, type: normal, pid: 100, module: lua, message: bad things },
  1392. * {...}
  1393. * ]
  1394. */
  1395. static int
  1396. rspamd_controller_handle_errors (struct rspamd_http_connection_entry *conn_ent,
  1397. struct rspamd_http_message *msg)
  1398. {
  1399. struct rspamd_controller_session *session = conn_ent->ud;
  1400. struct rspamd_controller_worker_ctx *ctx;
  1401. ucl_object_t *top;
  1402. ctx = session->ctx;
  1403. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1404. return 0;
  1405. }
  1406. top = rspamd_log_errorbuf_export (ctx->worker->srv->logger);
  1407. rspamd_controller_send_ucl (conn_ent, top);
  1408. ucl_object_unref (top);
  1409. return 0;
  1410. }
  1411. /*
  1412. * Neighbours command handler:
  1413. * request: /neighbours
  1414. * headers: Password
  1415. * reply: json {name: {url: "http://...", host: "host"}}
  1416. */
  1417. static int
  1418. rspamd_controller_handle_neighbours (struct rspamd_http_connection_entry *conn_ent,
  1419. struct rspamd_http_message *msg)
  1420. {
  1421. struct rspamd_controller_session *session = conn_ent->ud;
  1422. struct rspamd_controller_worker_ctx *ctx;
  1423. ctx = session->ctx;
  1424. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  1425. return 0;
  1426. }
  1427. rspamd_controller_send_ucl (conn_ent, ctx->cfg->neighbours);
  1428. return 0;
  1429. }
  1430. static int
  1431. rspamd_controller_handle_history_reset (struct rspamd_http_connection_entry *conn_ent,
  1432. struct rspamd_http_message *msg)
  1433. {
  1434. struct rspamd_controller_session *session = conn_ent->ud;
  1435. struct rspamd_controller_worker_ctx *ctx;
  1436. struct roll_history_row *row;
  1437. guint completed_rows, i, t;
  1438. lua_State *L;
  1439. ctx = session->ctx;
  1440. L = ctx->cfg->lua_state;
  1441. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1442. return 0;
  1443. }
  1444. if (!ctx->srv->history->disabled) {
  1445. /* Clean from start to the current row */
  1446. completed_rows = g_atomic_int_get (&ctx->srv->history->cur_row);
  1447. completed_rows = MIN (completed_rows, ctx->srv->history->nrows - 1);
  1448. for (i = 0; i <= completed_rows; i ++) {
  1449. t = g_atomic_int_get (&ctx->srv->history->cur_row);
  1450. /* We somehow come to the race condition */
  1451. if (i > t) {
  1452. break;
  1453. }
  1454. row = &ctx->srv->history->rows[i];
  1455. memset (row, 0, sizeof (*row));
  1456. }
  1457. msg_info_session ("<%s> cleared %d entries from history",
  1458. rspamd_inet_address_to_string (session->from_addr),
  1459. completed_rows);
  1460. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1461. }
  1462. else {
  1463. rspamd_controller_handle_lua_history (L, session, ctx, conn_ent, msg,
  1464. TRUE);
  1465. }
  1466. return 0;
  1467. }
  1468. static gboolean
  1469. rspamd_controller_lua_fin_task (void *ud)
  1470. {
  1471. struct rspamd_task *task = ud;
  1472. struct rspamd_http_connection_entry *conn_ent;
  1473. conn_ent = task->fin_arg;
  1474. if (task->err != NULL) {
  1475. rspamd_controller_send_error (conn_ent, task->err->code, "%s",
  1476. task->err->message);
  1477. }
  1478. return TRUE;
  1479. }
  1480. static int
  1481. rspamd_controller_handle_lua (struct rspamd_http_connection_entry *conn_ent,
  1482. struct rspamd_http_message *msg)
  1483. {
  1484. struct rspamd_controller_session *session = conn_ent->ud;
  1485. struct rspamd_task *task, **ptask;
  1486. struct rspamd_http_connection_entry **pconn;
  1487. struct rspamd_controller_worker_ctx *ctx;
  1488. gchar filebuf[PATH_MAX], realbuf[PATH_MAX];
  1489. struct http_parser_url u;
  1490. rspamd_ftok_t lookup;
  1491. struct stat st;
  1492. lua_State *L;
  1493. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1494. return 0;
  1495. }
  1496. ctx = session->ctx;
  1497. L = ctx->cfg->lua_state;
  1498. /* Find lua script */
  1499. if (msg->url != NULL && msg->url->len != 0) {
  1500. http_parser_parse_url (RSPAMD_FSTRING_DATA (msg->url),
  1501. RSPAMD_FSTRING_LEN (msg->url), TRUE, &u);
  1502. if (u.field_set & (1 << UF_PATH)) {
  1503. lookup.begin = RSPAMD_FSTRING_DATA (msg->url) +
  1504. u.field_data[UF_PATH].off;
  1505. lookup.len = u.field_data[UF_PATH].len;
  1506. }
  1507. else {
  1508. lookup.begin = RSPAMD_FSTRING_DATA (msg->url);
  1509. lookup.len = RSPAMD_FSTRING_LEN (msg->url);
  1510. }
  1511. rspamd_snprintf (filebuf, sizeof (filebuf), "%s%c%T",
  1512. ctx->static_files_dir, G_DIR_SEPARATOR, &lookup);
  1513. if (realpath (filebuf, realbuf) == NULL ||
  1514. lstat (realbuf, &st) == -1) {
  1515. rspamd_controller_send_error (conn_ent, 404, "Cannot find path: %s",
  1516. strerror (errno));
  1517. return 0;
  1518. }
  1519. /* TODO: add caching here, should be trivial */
  1520. /* Now we load and execute the code fragment, which should return a function */
  1521. if (luaL_loadfile (L, realbuf) != 0) {
  1522. rspamd_controller_send_error (conn_ent, 500, "Cannot load path: %s",
  1523. lua_tostring (L, -1));
  1524. lua_settop (L, 0);
  1525. return 0;
  1526. }
  1527. if (lua_pcall (L, 0, 1, 0) != 0) {
  1528. rspamd_controller_send_error (conn_ent, 501, "Cannot run path: %s",
  1529. lua_tostring (L, -1));
  1530. lua_settop (L, 0);
  1531. return 0;
  1532. }
  1533. if (lua_type (L, -1) != LUA_TFUNCTION) {
  1534. rspamd_controller_send_error (conn_ent, 502, "Bad return type: %s",
  1535. lua_typename (L, lua_type (L, -1)));
  1536. lua_settop (L, 0);
  1537. return 0;
  1538. }
  1539. }
  1540. else {
  1541. rspamd_controller_send_error (conn_ent, 404, "Empty path is not permitted");
  1542. return 0;
  1543. }
  1544. task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
  1545. ctx->lang_det, ctx->event_loop, FALSE);
  1546. task->resolver = ctx->resolver;
  1547. task->s = rspamd_session_create (session->pool,
  1548. rspamd_controller_lua_fin_task,
  1549. NULL,
  1550. (event_finalizer_t )rspamd_task_free,
  1551. task);
  1552. task->fin_arg = conn_ent;
  1553. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
  1554. task->sock = -1;
  1555. session->task = task;
  1556. if (msg->body_buf.len > 0) {
  1557. if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
  1558. rspamd_controller_send_error (conn_ent, task->err->code, "%s",
  1559. task->err->message);
  1560. return 0;
  1561. }
  1562. }
  1563. ptask = lua_newuserdata (L, sizeof (*ptask));
  1564. rspamd_lua_setclass (L, "rspamd{task}", -1);
  1565. *ptask = task;
  1566. pconn = lua_newuserdata (L, sizeof (*pconn));
  1567. rspamd_lua_setclass (L, "rspamd{csession}", -1);
  1568. *pconn = conn_ent;
  1569. if (lua_pcall (L, 2, 0, 0) != 0) {
  1570. rspamd_controller_send_error (conn_ent, 503, "Cannot run callback: %s",
  1571. lua_tostring (L, -1));
  1572. lua_settop (L, 0);
  1573. return 0;
  1574. }
  1575. rspamd_session_pending (task->s);
  1576. return 0;
  1577. }
  1578. static gboolean
  1579. rspamd_controller_learn_fin_task (void *ud)
  1580. {
  1581. struct rspamd_task *task = ud;
  1582. struct rspamd_controller_session *session;
  1583. struct rspamd_http_connection_entry *conn_ent;
  1584. conn_ent = task->fin_arg;
  1585. session = conn_ent->ud;
  1586. if (task->err != NULL) {
  1587. msg_info_session ("cannot learn <%s>: %e",
  1588. MESSAGE_FIELD (task, message_id), task->err);
  1589. rspamd_controller_send_error (conn_ent, task->err->code, "%s",
  1590. task->err->message);
  1591. return TRUE;
  1592. }
  1593. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  1594. /* Successful learn */
  1595. msg_info_task ("<%s> learned message as %s: %s",
  1596. rspamd_inet_address_to_string (session->from_addr),
  1597. session->is_spam ? "spam" : "ham",
  1598. MESSAGE_FIELD (task, message_id));
  1599. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1600. return TRUE;
  1601. }
  1602. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_LEARN)) {
  1603. msg_info_task ("cannot learn <%s>: %e",
  1604. MESSAGE_FIELD (task, message_id), task->err);
  1605. if (task->err) {
  1606. rspamd_controller_send_error (conn_ent, task->err->code, "%s",
  1607. task->err->message);
  1608. }
  1609. else {
  1610. rspamd_controller_send_error (conn_ent, 500,
  1611. "Internal error");
  1612. }
  1613. }
  1614. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  1615. if (task->err) {
  1616. rspamd_controller_send_error (conn_ent, task->err->code, "%s",
  1617. task->err->message);
  1618. }
  1619. else {
  1620. msg_info_task ("<%s> learned message as %s: %s",
  1621. rspamd_inet_address_to_string (session->from_addr),
  1622. session->is_spam ? "spam" : "ham",
  1623. MESSAGE_FIELD (task, message_id));
  1624. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1625. }
  1626. return TRUE;
  1627. }
  1628. /* One more iteration */
  1629. return FALSE;
  1630. }
  1631. static void
  1632. rspamd_controller_scan_reply (struct rspamd_task *task)
  1633. {
  1634. struct rspamd_http_message *msg;
  1635. struct rspamd_http_connection_entry *conn_ent;
  1636. conn_ent = task->fin_arg;
  1637. msg = rspamd_http_new_message (HTTP_RESPONSE);
  1638. msg->date = time (NULL);
  1639. msg->code = 200;
  1640. rspamd_protocol_http_reply (msg, task, NULL);
  1641. rspamd_http_connection_reset (conn_ent->conn);
  1642. rspamd_http_router_insert_headers (conn_ent->rt, msg);
  1643. rspamd_http_connection_write_message (conn_ent->conn, msg, NULL,
  1644. "application/json", conn_ent, conn_ent->rt->timeout);
  1645. conn_ent->is_reply = TRUE;
  1646. }
  1647. static gboolean
  1648. rspamd_controller_check_fin_task (void *ud)
  1649. {
  1650. struct rspamd_task *task = ud;
  1651. struct rspamd_http_connection_entry *conn_ent;
  1652. msg_debug_task ("finish task");
  1653. conn_ent = task->fin_arg;
  1654. if (task->err) {
  1655. msg_info_task ("cannot check <%s>: %e",
  1656. MESSAGE_FIELD (task, message_id), task->err);
  1657. rspamd_controller_send_error (conn_ent, task->err->code, "%s",
  1658. task->err->message);
  1659. return TRUE;
  1660. }
  1661. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  1662. rspamd_controller_scan_reply (task);
  1663. return TRUE;
  1664. }
  1665. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_ALL)) {
  1666. rspamd_controller_scan_reply (task);
  1667. return TRUE;
  1668. }
  1669. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  1670. rspamd_controller_scan_reply (task);
  1671. return TRUE;
  1672. }
  1673. /* One more iteration */
  1674. return FALSE;
  1675. }
  1676. static int
  1677. rspamd_controller_handle_learn_common (
  1678. struct rspamd_http_connection_entry *conn_ent,
  1679. struct rspamd_http_message *msg,
  1680. gboolean is_spam)
  1681. {
  1682. struct rspamd_controller_session *session = conn_ent->ud;
  1683. struct rspamd_controller_worker_ctx *ctx;
  1684. struct rspamd_task *task;
  1685. const rspamd_ftok_t *cl_header;
  1686. ctx = session->ctx;
  1687. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1688. return 0;
  1689. }
  1690. if (rspamd_http_message_get_body (msg, NULL) == NULL) {
  1691. msg_err_session ("got zero length body, cannot continue");
  1692. rspamd_controller_send_error (conn_ent,
  1693. 400,
  1694. "Empty body is not permitted");
  1695. return 0;
  1696. }
  1697. task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
  1698. session->ctx->lang_det, ctx->event_loop, FALSE);
  1699. task->resolver = ctx->resolver;
  1700. task->s = rspamd_session_create (session->pool,
  1701. rspamd_controller_learn_fin_task,
  1702. NULL,
  1703. (event_finalizer_t )rspamd_task_free,
  1704. task);
  1705. task->fin_arg = conn_ent;
  1706. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
  1707. task->sock = -1;
  1708. session->task = task;
  1709. cl_header = rspamd_http_message_find_header (msg, "classifier");
  1710. if (cl_header) {
  1711. session->classifier = rspamd_mempool_ftokdup (session->pool, cl_header);
  1712. }
  1713. else {
  1714. session->classifier = NULL;
  1715. }
  1716. if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
  1717. goto end;
  1718. }
  1719. rspamd_learn_task_spam (task, is_spam, session->classifier, NULL);
  1720. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_LEARN)) {
  1721. msg_warn_session ("<%s> message cannot be processed",
  1722. MESSAGE_FIELD (task, message_id));
  1723. goto end;
  1724. }
  1725. end:
  1726. session->is_spam = is_spam;
  1727. rspamd_session_pending (task->s);
  1728. return 0;
  1729. }
  1730. /*
  1731. * Learn spam command handler:
  1732. * request: /learnspam
  1733. * headers: Password
  1734. * input: plaintext data
  1735. * reply: json {"success":true} or {"error":"error message"}
  1736. */
  1737. static int
  1738. rspamd_controller_handle_learnspam (
  1739. struct rspamd_http_connection_entry *conn_ent,
  1740. struct rspamd_http_message *msg)
  1741. {
  1742. return rspamd_controller_handle_learn_common (conn_ent, msg, TRUE);
  1743. }
  1744. /*
  1745. * Learn ham command handler:
  1746. * request: /learnham
  1747. * headers: Password
  1748. * input: plaintext data
  1749. * reply: json {"success":true} or {"error":"error message"}
  1750. */
  1751. static int
  1752. rspamd_controller_handle_learnham (
  1753. struct rspamd_http_connection_entry *conn_ent,
  1754. struct rspamd_http_message *msg)
  1755. {
  1756. return rspamd_controller_handle_learn_common (conn_ent, msg, FALSE);
  1757. }
  1758. /*
  1759. * Scan command handler:
  1760. * request: /scan
  1761. * headers: Password
  1762. * input: plaintext data
  1763. * reply: json {scan data} or {"error":"error message"}
  1764. */
  1765. static int
  1766. rspamd_controller_handle_scan (struct rspamd_http_connection_entry *conn_ent,
  1767. struct rspamd_http_message *msg)
  1768. {
  1769. struct rspamd_controller_session *session = conn_ent->ud;
  1770. struct rspamd_controller_worker_ctx *ctx;
  1771. struct rspamd_task *task;
  1772. ctx = session->ctx;
  1773. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  1774. return 0;
  1775. }
  1776. if (rspamd_http_message_get_body (msg, NULL) == NULL) {
  1777. msg_err_session ("got zero length body, cannot continue");
  1778. rspamd_controller_send_error (conn_ent,
  1779. 400,
  1780. "Empty body is not permitted");
  1781. return 0;
  1782. }
  1783. task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
  1784. ctx->lang_det, ctx->event_loop, FALSE);
  1785. task->resolver = ctx->resolver;
  1786. task->s = rspamd_session_create (session->pool,
  1787. rspamd_controller_check_fin_task,
  1788. NULL,
  1789. (event_finalizer_t )rspamd_task_free,
  1790. task);
  1791. task->fin_arg = conn_ent;
  1792. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);
  1793. task->sock = conn_ent->conn->fd;
  1794. task->flags |= RSPAMD_TASK_FLAG_MIME;
  1795. task->resolver = ctx->resolver;
  1796. if (!rspamd_protocol_handle_request (task, msg)) {
  1797. goto end;
  1798. }
  1799. if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
  1800. goto end;
  1801. }
  1802. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_ALL)) {
  1803. goto end;
  1804. }
  1805. if (ctx->task_timeout > 0.0) {
  1806. task->timeout_ev.data = task;
  1807. ev_timer_init (&task->timeout_ev, rspamd_task_timeout,
  1808. ctx->task_timeout, ctx->task_timeout);
  1809. ev_timer_start (task->event_loop, &task->timeout_ev);
  1810. ev_set_priority (&task->timeout_ev, EV_MAXPRI);
  1811. }
  1812. end:
  1813. session->task = task;
  1814. rspamd_session_pending (task->s);
  1815. return 0;
  1816. }
  1817. /*
  1818. * Save actions command handler:
  1819. * request: /saveactions
  1820. * headers: Password
  1821. * input: json array [<spam>,<probable spam>,<greylist>]
  1822. * reply: json {"success":true} or {"error":"error message"}
  1823. */
  1824. static int
  1825. rspamd_controller_handle_saveactions (
  1826. struct rspamd_http_connection_entry *conn_ent,
  1827. struct rspamd_http_message *msg)
  1828. {
  1829. struct rspamd_controller_session *session = conn_ent->ud;
  1830. struct ucl_parser *parser;
  1831. ucl_object_t *obj;
  1832. const ucl_object_t *cur;
  1833. struct rspamd_controller_worker_ctx *ctx;
  1834. const gchar *error;
  1835. gdouble score;
  1836. gint i, added = 0;
  1837. enum rspamd_action_type act;
  1838. ucl_object_iter_t it = NULL;
  1839. ctx = session->ctx;
  1840. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1841. return 0;
  1842. }
  1843. if (rspamd_http_message_get_body (msg, NULL) == NULL) {
  1844. msg_err_session ("got zero length body, cannot continue");
  1845. rspamd_controller_send_error (conn_ent,
  1846. 400,
  1847. "Empty body is not permitted");
  1848. return 0;
  1849. }
  1850. parser = ucl_parser_new (0);
  1851. if (!ucl_parser_add_chunk (parser, msg->body_buf.begin, msg->body_buf.len)) {
  1852. if ((error = ucl_parser_get_error (parser)) != NULL) {
  1853. msg_err_session ("cannot parse input: %s", error);
  1854. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1855. ucl_parser_free (parser);
  1856. return 0;
  1857. }
  1858. msg_err_session ("cannot parse input: unknown error");
  1859. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1860. ucl_parser_free (parser);
  1861. return 0;
  1862. }
  1863. obj = ucl_parser_get_object (parser);
  1864. ucl_parser_free (parser);
  1865. if (obj->type != UCL_ARRAY || obj->len != 4) {
  1866. msg_err_session ("input is not an array of 4 elements");
  1867. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1868. ucl_object_unref (obj);
  1869. return 0;
  1870. }
  1871. it = ucl_object_iterate_new (obj);
  1872. for (i = 0; i < 4; i++) {
  1873. cur = ucl_object_iterate_safe (it, TRUE);
  1874. switch (i) {
  1875. case 0:
  1876. default:
  1877. act = METRIC_ACTION_REJECT;
  1878. break;
  1879. case 1:
  1880. act = METRIC_ACTION_REWRITE_SUBJECT;
  1881. break;
  1882. case 2:
  1883. act = METRIC_ACTION_ADD_HEADER;
  1884. break;
  1885. case 3:
  1886. act = METRIC_ACTION_GREYLIST;
  1887. break;
  1888. }
  1889. if (ucl_object_type (cur) == UCL_NULL) {
  1890. /* Assume NaN */
  1891. score = NAN;
  1892. }
  1893. else {
  1894. score = ucl_object_todouble (cur);
  1895. }
  1896. if ((isnan (session->cfg->actions[act].threshold) != isnan (score)) ||
  1897. (session->cfg->actions[act].threshold != score)) {
  1898. add_dynamic_action (ctx->cfg, DEFAULT_METRIC, act, score);
  1899. added ++;
  1900. }
  1901. else {
  1902. if (remove_dynamic_action (ctx->cfg, DEFAULT_METRIC, act)) {
  1903. added ++;
  1904. }
  1905. }
  1906. }
  1907. ucl_object_iterate_free (it);
  1908. if (dump_dynamic_config (ctx->cfg)) {
  1909. msg_info_session ("<%s> modified %d actions",
  1910. rspamd_inet_address_to_string (session->from_addr),
  1911. added);
  1912. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1913. }
  1914. else {
  1915. rspamd_controller_send_error (conn_ent, 500, "Save error");
  1916. }
  1917. ucl_object_unref (obj);
  1918. return 0;
  1919. }
  1920. /*
  1921. * Save symbols command handler:
  1922. * request: /savesymbols
  1923. * headers: Password
  1924. * input: json data
  1925. * reply: json {"success":true} or {"error":"error message"}
  1926. */
  1927. static int
  1928. rspamd_controller_handle_savesymbols (
  1929. struct rspamd_http_connection_entry *conn_ent,
  1930. struct rspamd_http_message *msg)
  1931. {
  1932. struct rspamd_controller_session *session = conn_ent->ud;
  1933. struct ucl_parser *parser;
  1934. ucl_object_t *obj;
  1935. const ucl_object_t *cur, *jname, *jvalue;
  1936. ucl_object_iter_t iter = NULL;
  1937. struct rspamd_controller_worker_ctx *ctx;
  1938. const gchar *error;
  1939. gdouble val;
  1940. struct rspamd_symbol *sym;
  1941. int added = 0;
  1942. ctx = session->ctx;
  1943. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1944. return 0;
  1945. }
  1946. if (rspamd_http_message_get_body (msg, NULL) == NULL) {
  1947. msg_err_session ("got zero length body, cannot continue");
  1948. rspamd_controller_send_error (conn_ent,
  1949. 400,
  1950. "Empty body is not permitted");
  1951. return 0;
  1952. }
  1953. parser = ucl_parser_new (0);
  1954. if (!ucl_parser_add_chunk (parser, msg->body_buf.begin, msg->body_buf.len)) {
  1955. if ((error = ucl_parser_get_error (parser)) != NULL) {
  1956. msg_err_session ("cannot parse input: %s", error);
  1957. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1958. ucl_parser_free (parser);
  1959. return 0;
  1960. }
  1961. msg_err_session ("cannot parse input: unknown error");
  1962. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1963. ucl_parser_free (parser);
  1964. return 0;
  1965. }
  1966. obj = ucl_parser_get_object (parser);
  1967. ucl_parser_free (parser);
  1968. if (obj->type != UCL_ARRAY) {
  1969. msg_err_session ("input is not an array");
  1970. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1971. ucl_object_unref (obj);
  1972. return 0;
  1973. }
  1974. iter = ucl_object_iterate_new (obj);
  1975. while ((cur = ucl_object_iterate_safe (iter, true))) {
  1976. if (cur->type != UCL_OBJECT) {
  1977. msg_err_session ("json array data error");
  1978. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1979. ucl_object_unref (obj);
  1980. ucl_object_iterate_free (iter);
  1981. return 0;
  1982. }
  1983. jname = ucl_object_lookup (cur, "name");
  1984. jvalue = ucl_object_lookup (cur, "value");
  1985. val = ucl_object_todouble (jvalue);
  1986. sym = g_hash_table_lookup (session->cfg->symbols, ucl_object_tostring (jname));
  1987. if (sym && fabs (*sym->weight_ptr - val) > 0.01) {
  1988. if (!add_dynamic_symbol (ctx->cfg, DEFAULT_METRIC,
  1989. ucl_object_tostring (jname), val)) {
  1990. msg_err_session ("add symbol failed for %s",
  1991. ucl_object_tostring (jname));
  1992. rspamd_controller_send_error (conn_ent, 506,
  1993. "Add symbol failed");
  1994. ucl_object_unref (obj);
  1995. ucl_object_iterate_free (iter);
  1996. return 0;
  1997. }
  1998. added ++;
  1999. }
  2000. else if (sym && ctx->cfg->dynamic_conf) {
  2001. if (remove_dynamic_symbol (ctx->cfg, DEFAULT_METRIC,
  2002. ucl_object_tostring (jname))) {
  2003. added ++;
  2004. }
  2005. }
  2006. }
  2007. ucl_object_iterate_free (iter);
  2008. if (added > 0) {
  2009. if (ctx->cfg->dynamic_conf) {
  2010. if (dump_dynamic_config (ctx->cfg)) {
  2011. msg_info_session ("<%s> modified %d symbols",
  2012. rspamd_inet_address_to_string (session->from_addr),
  2013. added);
  2014. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  2015. }
  2016. else {
  2017. rspamd_controller_send_error (conn_ent, 500, "Save error");
  2018. }
  2019. }
  2020. else {
  2021. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  2022. }
  2023. }
  2024. else {
  2025. msg_err_session ("no symbols to save");
  2026. rspamd_controller_send_error (conn_ent, 404, "No symbols to save");
  2027. }
  2028. ucl_object_unref (obj);
  2029. return 0;
  2030. }
  2031. /*
  2032. * Save map command handler:
  2033. * request: /savemap
  2034. * headers: Password, Map
  2035. * input: plaintext data
  2036. * reply: json {"success":true} or {"error":"error message"}
  2037. */
  2038. static int
  2039. rspamd_controller_handle_savemap (struct rspamd_http_connection_entry *conn_ent,
  2040. struct rspamd_http_message *msg)
  2041. {
  2042. struct rspamd_controller_session *session = conn_ent->ud;
  2043. GList *cur;
  2044. struct rspamd_map *map = NULL;
  2045. struct rspamd_map_backend *bk;
  2046. struct rspamd_controller_worker_ctx *ctx;
  2047. const rspamd_ftok_t *idstr;
  2048. gulong id, i;
  2049. gboolean found = FALSE;
  2050. gchar tempname[PATH_MAX];
  2051. gint fd;
  2052. ctx = session->ctx;
  2053. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  2054. return 0;
  2055. }
  2056. if (rspamd_http_message_get_body (msg, NULL) == NULL) {
  2057. msg_err_session ("got zero length body, cannot continue");
  2058. rspamd_controller_send_error (conn_ent,
  2059. 400,
  2060. "Empty body is not permitted");
  2061. return 0;
  2062. }
  2063. idstr = rspamd_http_message_find_header (msg, "Map");
  2064. if (idstr == NULL) {
  2065. msg_info_session ("absent map id");
  2066. rspamd_controller_send_error (conn_ent, 400, "Map id not specified");
  2067. return 0;
  2068. }
  2069. if (!rspamd_strtoul (idstr->begin, idstr->len, &id)) {
  2070. msg_info_session ("invalid map id: %T", idstr);
  2071. rspamd_controller_send_error (conn_ent, 400, "Map id is invalid");
  2072. return 0;
  2073. }
  2074. /* Now let's be sure that we have map defined in configuration */
  2075. cur = ctx->cfg->maps;
  2076. while (cur && !found) {
  2077. map = cur->data;
  2078. PTR_ARRAY_FOREACH (map->backends, i, bk) {
  2079. if (bk->id == id && bk->protocol == MAP_PROTO_FILE) {
  2080. found = TRUE;
  2081. break;
  2082. }
  2083. }
  2084. cur = g_list_next (cur);
  2085. }
  2086. if (!found) {
  2087. msg_info_session ("map not found: %L", id);
  2088. rspamd_controller_send_error (conn_ent, 404, "Map id not found");
  2089. return 0;
  2090. }
  2091. rspamd_snprintf (tempname, sizeof (tempname), "%s.newXXXXXX", bk->uri);
  2092. fd = g_mkstemp_full (tempname, O_WRONLY, 00644);
  2093. if (fd == -1) {
  2094. msg_info_session ("map %s open error: %s", tempname, strerror (errno));
  2095. rspamd_controller_send_error (conn_ent, 404,
  2096. "Cannot open map: %s",
  2097. strerror (errno));
  2098. return 0;
  2099. }
  2100. if (write (fd, msg->body_buf.begin, msg->body_buf.len) == -1) {
  2101. msg_info_session ("map %s write error: %s", tempname, strerror (errno));
  2102. unlink (tempname);
  2103. close (fd);
  2104. rspamd_controller_send_error (conn_ent, 500, "Map write error: %s",
  2105. strerror (errno));
  2106. return 0;
  2107. }
  2108. /* Rename */
  2109. if (rename (tempname, bk->uri) == -1) {
  2110. msg_info_session ("map %s rename error: %s", tempname, strerror (errno));
  2111. unlink (tempname);
  2112. close (fd);
  2113. rspamd_controller_send_error (conn_ent, 500, "Map rename error: %s",
  2114. strerror (errno));
  2115. return 0;
  2116. }
  2117. msg_info_session ("<%s>, map %s saved",
  2118. rspamd_inet_address_to_string (session->from_addr),
  2119. bk->uri);
  2120. close (fd);
  2121. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  2122. return 0;
  2123. }
  2124. struct rspamd_stat_cbdata {
  2125. struct rspamd_http_connection_entry *conn_ent;
  2126. ucl_object_t *top;
  2127. ucl_object_t *stat;
  2128. struct rspamd_task *task;
  2129. guint64 learned;
  2130. };
  2131. static gboolean
  2132. rspamd_controller_stat_fin_task (void *ud)
  2133. {
  2134. struct rspamd_stat_cbdata *cbdata = ud;
  2135. struct rspamd_http_connection_entry *conn_ent;
  2136. ucl_object_t *top, *ar;
  2137. GList *fuzzy_elts, *cur;
  2138. struct rspamd_fuzzy_stat_entry *entry;
  2139. conn_ent = cbdata->conn_ent;
  2140. top = cbdata->top;
  2141. ucl_object_insert_key (top,
  2142. ucl_object_fromint (cbdata->learned), "total_learns", 0, false);
  2143. if (cbdata->stat) {
  2144. ucl_object_insert_key (top, cbdata->stat, "statfiles", 0, false);
  2145. }
  2146. fuzzy_elts = rspamd_mempool_get_variable (cbdata->task->task_pool, "fuzzy_stat");
  2147. if (fuzzy_elts) {
  2148. ar = ucl_object_typed_new (UCL_OBJECT);
  2149. for (cur = fuzzy_elts; cur != NULL; cur = g_list_next (cur)) {
  2150. entry = cur->data;
  2151. if (entry->name) {
  2152. ucl_object_insert_key (ar, ucl_object_fromint (entry->fuzzy_cnt),
  2153. entry->name, 0, true);
  2154. }
  2155. }
  2156. ucl_object_insert_key (top, ar, "fuzzy_hashes", 0, false);
  2157. }
  2158. rspamd_controller_send_ucl (conn_ent, top);
  2159. return TRUE;
  2160. }
  2161. static void
  2162. rspamd_controller_stat_cleanup_task (void *ud)
  2163. {
  2164. struct rspamd_stat_cbdata *cbdata = ud;
  2165. rspamd_task_free (cbdata->task);
  2166. ucl_object_unref (cbdata->top);
  2167. }
  2168. /*
  2169. * Stat command handler:
  2170. * request: /stat (/resetstat)
  2171. * headers: Password
  2172. * reply: json data
  2173. */
  2174. static int
  2175. rspamd_controller_handle_stat_common (
  2176. struct rspamd_http_connection_entry *conn_ent,
  2177. struct rspamd_http_message *msg,
  2178. gboolean do_reset)
  2179. {
  2180. struct rspamd_controller_session *session = conn_ent->ud;
  2181. ucl_object_t *top, *sub;
  2182. gint i;
  2183. guint64 spam = 0, ham = 0;
  2184. rspamd_mempool_stat_t mem_st;
  2185. struct rspamd_stat *stat, stat_copy;
  2186. struct rspamd_controller_worker_ctx *ctx;
  2187. struct rspamd_task *task;
  2188. struct rspamd_stat_cbdata *cbdata;
  2189. memset (&mem_st, 0, sizeof (mem_st));
  2190. rspamd_mempool_stat (&mem_st);
  2191. memcpy (&stat_copy, session->ctx->worker->srv->stat, sizeof (stat_copy));
  2192. stat = &stat_copy;
  2193. ctx = session->ctx;
  2194. task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
  2195. ctx->lang_det, ctx->event_loop, FALSE);
  2196. task->resolver = ctx->resolver;
  2197. cbdata = rspamd_mempool_alloc0 (session->pool, sizeof (*cbdata));
  2198. cbdata->conn_ent = conn_ent;
  2199. cbdata->task = task;
  2200. top = ucl_object_typed_new (UCL_OBJECT);
  2201. cbdata->top = top;
  2202. task->s = rspamd_session_create (session->pool,
  2203. rspamd_controller_stat_fin_task,
  2204. NULL,
  2205. rspamd_controller_stat_cleanup_task,
  2206. cbdata);
  2207. task->fin_arg = cbdata;
  2208. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
  2209. task->sock = conn_ent->conn->fd;
  2210. ucl_object_insert_key (top, ucl_object_frombool (!session->is_enable),
  2211. "read_only", 0, false);
  2212. ucl_object_insert_key (top, ucl_object_fromint (
  2213. stat->messages_scanned), "scanned", 0, false);
  2214. ucl_object_insert_key (top, ucl_object_fromint (
  2215. stat->messages_learned), "learned", 0, false);
  2216. if (stat->messages_scanned > 0) {
  2217. sub = ucl_object_typed_new (UCL_OBJECT);
  2218. for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
  2219. ucl_object_insert_key (sub,
  2220. ucl_object_fromint (stat->actions_stat[i]),
  2221. rspamd_action_to_str (i), 0, false);
  2222. if (i < METRIC_ACTION_GREYLIST) {
  2223. spam += stat->actions_stat[i];
  2224. }
  2225. else {
  2226. ham += stat->actions_stat[i];
  2227. }
  2228. if (do_reset) {
  2229. #ifndef HAVE_ATOMIC_BUILTINS
  2230. session->ctx->worker->srv->stat->actions_stat[i] = 0;
  2231. #else
  2232. __atomic_store_n(&session->ctx->worker->srv->stat->actions_stat[i],
  2233. 0, __ATOMIC_RELEASE);
  2234. #endif
  2235. }
  2236. }
  2237. ucl_object_insert_key (top, sub, "actions", 0, false);
  2238. }
  2239. ucl_object_insert_key (top, ucl_object_fromint (
  2240. spam), "spam_count", 0, false);
  2241. ucl_object_insert_key (top, ucl_object_fromint (
  2242. ham), "ham_count", 0, false);
  2243. ucl_object_insert_key (top,
  2244. ucl_object_fromint (stat->connections_count), "connections", 0, false);
  2245. ucl_object_insert_key (top,
  2246. ucl_object_fromint (stat->control_connections_count),
  2247. "control_connections", 0, false);
  2248. ucl_object_insert_key (top,
  2249. ucl_object_fromint (mem_st.pools_allocated), "pools_allocated", 0,
  2250. false);
  2251. ucl_object_insert_key (top,
  2252. ucl_object_fromint (mem_st.pools_freed), "pools_freed", 0, false);
  2253. ucl_object_insert_key (top,
  2254. ucl_object_fromint (mem_st.bytes_allocated), "bytes_allocated", 0,
  2255. false);
  2256. ucl_object_insert_key (top,
  2257. ucl_object_fromint (
  2258. mem_st.chunks_allocated), "chunks_allocated", 0, false);
  2259. ucl_object_insert_key (top,
  2260. ucl_object_fromint (mem_st.shared_chunks_allocated),
  2261. "shared_chunks_allocated", 0, false);
  2262. ucl_object_insert_key (top,
  2263. ucl_object_fromint (mem_st.chunks_freed), "chunks_freed", 0, false);
  2264. ucl_object_insert_key (top,
  2265. ucl_object_fromint (
  2266. mem_st.oversized_chunks), "chunks_oversized", 0, false);
  2267. ucl_object_insert_key (top,
  2268. ucl_object_fromint (mem_st.fragmented_size), "fragmented", 0, false);
  2269. if (do_reset) {
  2270. session->ctx->srv->stat->messages_scanned = 0;
  2271. session->ctx->srv->stat->messages_learned = 0;
  2272. session->ctx->srv->stat->connections_count = 0;
  2273. session->ctx->srv->stat->control_connections_count = 0;
  2274. rspamd_mempool_stat_reset ();
  2275. }
  2276. fuzzy_stat_command (task);
  2277. /* Now write statistics for each statfile */
  2278. rspamd_stat_statistics (task, session->ctx->cfg, &cbdata->learned,
  2279. &cbdata->stat);
  2280. session->task = task;
  2281. rspamd_session_pending (task->s);
  2282. return 0;
  2283. }
  2284. static int
  2285. rspamd_controller_handle_stat (struct rspamd_http_connection_entry *conn_ent,
  2286. struct rspamd_http_message *msg)
  2287. {
  2288. struct rspamd_controller_session *session = conn_ent->ud;
  2289. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  2290. return 0;
  2291. }
  2292. return rspamd_controller_handle_stat_common (conn_ent, msg, FALSE);
  2293. }
  2294. static int
  2295. rspamd_controller_handle_statreset (
  2296. struct rspamd_http_connection_entry *conn_ent,
  2297. struct rspamd_http_message *msg)
  2298. {
  2299. struct rspamd_controller_session *session = conn_ent->ud;
  2300. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  2301. return 0;
  2302. }
  2303. msg_info_session ("<%s> reset stat",
  2304. rspamd_inet_address_to_string (session->from_addr));
  2305. return rspamd_controller_handle_stat_common (conn_ent, msg, TRUE);
  2306. }
  2307. /*
  2308. * Counters command handler:
  2309. * request: /counters
  2310. * headers: Password
  2311. * reply: json array of all counters
  2312. */
  2313. static int
  2314. rspamd_controller_handle_counters (
  2315. struct rspamd_http_connection_entry *conn_ent,
  2316. struct rspamd_http_message *msg)
  2317. {
  2318. struct rspamd_controller_session *session = conn_ent->ud;
  2319. ucl_object_t *top;
  2320. struct rspamd_symcache *cache;
  2321. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  2322. return 0;
  2323. }
  2324. cache = session->ctx->cfg->cache;
  2325. if (cache != NULL) {
  2326. top = rspamd_symcache_counters (cache);
  2327. rspamd_controller_send_ucl (conn_ent, top);
  2328. ucl_object_unref (top);
  2329. }
  2330. else {
  2331. rspamd_controller_send_error (conn_ent, 500, "Invalid cache");
  2332. }
  2333. return 0;
  2334. }
  2335. static int
  2336. rspamd_controller_handle_custom (struct rspamd_http_connection_entry *conn_ent,
  2337. struct rspamd_http_message *msg)
  2338. {
  2339. struct rspamd_controller_session *session = conn_ent->ud;
  2340. struct rspamd_custom_controller_command *cmd;
  2341. gchar *url_str;
  2342. struct http_parser_url u;
  2343. rspamd_ftok_t lookup;
  2344. http_parser_parse_url (msg->url->str, msg->url->len, TRUE, &u);
  2345. if (u.field_set & (1 << UF_PATH)) {
  2346. guint unnorm_len;
  2347. lookup.begin = msg->url->str + u.field_data[UF_PATH].off;
  2348. lookup.len = u.field_data[UF_PATH].len;
  2349. rspamd_http_normalize_path_inplace ((gchar *)lookup.begin,
  2350. lookup.len,
  2351. &unnorm_len);
  2352. lookup.len = unnorm_len;
  2353. }
  2354. else {
  2355. lookup.begin = msg->url->str;
  2356. lookup.len = msg->url->len;
  2357. }
  2358. url_str = rspamd_ftok_cstr (&lookup);
  2359. cmd = g_hash_table_lookup (session->ctx->custom_commands, url_str);
  2360. g_free (url_str);
  2361. if (cmd == NULL || cmd->handler == NULL) {
  2362. msg_err_session ("custom command %T has not been found", &lookup);
  2363. rspamd_controller_send_error (conn_ent, 404, "No command associated");
  2364. return 0;
  2365. }
  2366. if (!rspamd_controller_check_password (conn_ent, session, msg,
  2367. cmd->privilleged)) {
  2368. return 0;
  2369. }
  2370. if (cmd->require_message && (rspamd_http_message_get_body (msg, NULL) == NULL)) {
  2371. msg_err_session ("got zero length body, cannot continue");
  2372. rspamd_controller_send_error (conn_ent,
  2373. 400,
  2374. "Empty body is not permitted");
  2375. return 0;
  2376. }
  2377. /* Transfer query arguments to headers */
  2378. if (u.field_set & (1u << UF_QUERY)) {
  2379. GHashTable *query_args;
  2380. GHashTableIter it;
  2381. gpointer k, v;
  2382. rspamd_ftok_t *key, *value;
  2383. /* In case if we have a query, we need to store it somewhere */
  2384. query_args = rspamd_http_message_parse_query (msg);
  2385. /* Insert the rest of query params as HTTP headers */
  2386. g_hash_table_iter_init (&it, query_args);
  2387. while (g_hash_table_iter_next (&it, &k, &v)) {
  2388. key = k;
  2389. value = v;
  2390. /* Steal strings */
  2391. g_hash_table_iter_steal (&it);
  2392. url_str = rspamd_ftok_cstr (key);
  2393. rspamd_http_message_add_header_len (msg, url_str,
  2394. value->begin, value->len);
  2395. g_free (url_str);
  2396. }
  2397. g_hash_table_unref (query_args);
  2398. }
  2399. return cmd->handler (conn_ent, msg, cmd->ctx);
  2400. }
  2401. static int
  2402. rspamd_controller_handle_plugins (struct rspamd_http_connection_entry *conn_ent,
  2403. struct rspamd_http_message *msg)
  2404. {
  2405. struct rspamd_controller_session *session = conn_ent->ud;
  2406. struct rspamd_controller_plugin_cbdata *cbd;
  2407. GHashTableIter it;
  2408. gpointer k, v;
  2409. ucl_object_t *plugins;
  2410. if (!rspamd_controller_check_password (conn_ent, session, msg,
  2411. FALSE)) {
  2412. return 0;
  2413. }
  2414. plugins = ucl_object_typed_new (UCL_OBJECT);
  2415. g_hash_table_iter_init (&it, session->ctx->plugins);
  2416. while (g_hash_table_iter_next (&it, &k, &v)) {
  2417. ucl_object_t *elt, *npath;
  2418. cbd = v;
  2419. elt = (ucl_object_t *)ucl_object_lookup (plugins, cbd->plugin);
  2420. if (elt == NULL) {
  2421. elt = ucl_object_typed_new (UCL_OBJECT);
  2422. ucl_object_insert_key (elt, ucl_object_fromint (cbd->version),
  2423. "version", 0, false);
  2424. npath = ucl_object_typed_new (UCL_ARRAY);
  2425. ucl_object_insert_key (elt, npath, "paths", 0, false);
  2426. ucl_object_insert_key (plugins, elt, cbd->plugin, 0, false);
  2427. }
  2428. else {
  2429. npath = (ucl_object_t *)ucl_object_lookup (elt, "paths");
  2430. }
  2431. g_assert (npath != NULL);
  2432. ucl_array_append (npath, ucl_object_fromstring (k));
  2433. }
  2434. rspamd_controller_send_ucl (conn_ent, plugins);
  2435. ucl_object_unref (plugins);
  2436. return 0;
  2437. }
  2438. static int
  2439. rspamd_controller_handle_ping (struct rspamd_http_connection_entry *conn_ent,
  2440. struct rspamd_http_message *msg)
  2441. {
  2442. struct rspamd_http_message *rep_msg;
  2443. rspamd_fstring_t *reply;
  2444. rep_msg = rspamd_http_new_message (HTTP_RESPONSE);
  2445. rep_msg->date = time (NULL);
  2446. rep_msg->code = 200;
  2447. rep_msg->status = rspamd_fstring_new_init ("OK", 2);
  2448. reply = rspamd_fstring_new_init ("pong" CRLF, strlen ("pong" CRLF));
  2449. rspamd_http_message_set_body_from_fstring_steal (rep_msg, reply);
  2450. rspamd_http_connection_reset (conn_ent->conn);
  2451. rspamd_http_router_insert_headers (conn_ent->rt, rep_msg);
  2452. rspamd_http_connection_write_message (conn_ent->conn,
  2453. rep_msg,
  2454. NULL,
  2455. "text/plain",
  2456. conn_ent,
  2457. conn_ent->rt->timeout);
  2458. conn_ent->is_reply = TRUE;
  2459. return 0;
  2460. }
  2461. /*
  2462. * Called on unknown methods and is used to deal with CORS as per
  2463. * https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
  2464. */
  2465. static int
  2466. rspamd_controller_handle_unknown (struct rspamd_http_connection_entry *conn_ent,
  2467. struct rspamd_http_message *msg)
  2468. {
  2469. struct rspamd_http_message *rep;
  2470. if (msg->method == HTTP_OPTIONS) {
  2471. /* Assume CORS request */
  2472. rep = rspamd_http_new_message (HTTP_RESPONSE);
  2473. rep->date = time (NULL);
  2474. rep->code = 200;
  2475. rep->status = rspamd_fstring_new_init ("OK", 2);
  2476. rspamd_http_message_add_header (rep, "Access-Control-Allow-Methods",
  2477. "POST, GET, OPTIONS");
  2478. rspamd_http_message_add_header (rep, "Access-Control-Allow-Headers",
  2479. "Content-Type,Password,Map,Weight,Flag");
  2480. rspamd_http_connection_reset (conn_ent->conn);
  2481. rspamd_http_router_insert_headers (conn_ent->rt, rep);
  2482. rspamd_http_connection_write_message (conn_ent->conn,
  2483. rep,
  2484. NULL,
  2485. "text/plain",
  2486. conn_ent,
  2487. conn_ent->rt->timeout);
  2488. conn_ent->is_reply = TRUE;
  2489. }
  2490. else {
  2491. rep = rspamd_http_new_message (HTTP_RESPONSE);
  2492. rep->date = time (NULL);
  2493. rep->code = 500;
  2494. rep->status = rspamd_fstring_new_init ("Invalid method",
  2495. strlen ("Invalid method"));
  2496. rspamd_http_connection_reset (conn_ent->conn);
  2497. rspamd_http_router_insert_headers (conn_ent->rt, rep);
  2498. rspamd_http_connection_write_message (conn_ent->conn,
  2499. rep,
  2500. NULL,
  2501. "text/plain",
  2502. conn_ent,
  2503. conn_ent->rt->timeout);
  2504. conn_ent->is_reply = TRUE;
  2505. }
  2506. return 0;
  2507. }
  2508. static int
  2509. rspamd_controller_handle_lua_plugin (struct rspamd_http_connection_entry *conn_ent,
  2510. struct rspamd_http_message *msg)
  2511. {
  2512. struct rspamd_controller_session *session = conn_ent->ud;
  2513. struct rspamd_controller_plugin_cbdata *cbd;
  2514. struct rspamd_task *task, **ptask;
  2515. struct rspamd_http_connection_entry **pconn;
  2516. struct rspamd_controller_worker_ctx *ctx;
  2517. lua_State *L;
  2518. gchar *url_str;
  2519. url_str = rspamd_fstring_cstr (msg->url);
  2520. cbd = g_hash_table_lookup (session->ctx->plugins, url_str);
  2521. g_free (url_str);
  2522. if (cbd == NULL || cbd->handler == NULL) {
  2523. msg_err_session ("plugin handler %V has not been found", msg->url);
  2524. rspamd_controller_send_error (conn_ent, 404, "No command associated");
  2525. return 0;
  2526. }
  2527. L = cbd->L;
  2528. ctx = cbd->ctx;
  2529. if (!rspamd_controller_check_password (conn_ent, session, msg,
  2530. cbd->is_enable)) {
  2531. return 0;
  2532. }
  2533. if (cbd->need_task && (rspamd_http_message_get_body (msg, NULL) == NULL)) {
  2534. msg_err_session ("got zero length body, cannot continue");
  2535. rspamd_controller_send_error (conn_ent,
  2536. 400,
  2537. "Empty body is not permitted");
  2538. return 0;
  2539. }
  2540. task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
  2541. ctx->lang_det, ctx->event_loop, FALSE);
  2542. task->resolver = ctx->resolver;
  2543. task->s = rspamd_session_create (session->pool,
  2544. rspamd_controller_lua_fin_task,
  2545. NULL,
  2546. (event_finalizer_t )rspamd_task_free,
  2547. task);
  2548. task->fin_arg = conn_ent;
  2549. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
  2550. task->sock = -1;
  2551. session->task = task;
  2552. if (msg->body_buf.len > 0) {
  2553. if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
  2554. rspamd_controller_send_error (conn_ent, task->err->code, "%s",
  2555. task->err->message);
  2556. return 0;
  2557. }
  2558. }
  2559. /* Callback */
  2560. lua_rawgeti (L, LUA_REGISTRYINDEX, cbd->handler->idx);
  2561. ptask = lua_newuserdata (L, sizeof (*ptask));
  2562. rspamd_lua_setclass (L, "rspamd{task}", -1);
  2563. *ptask = task;
  2564. pconn = lua_newuserdata (L, sizeof (*pconn));
  2565. rspamd_lua_setclass (L, "rspamd{csession}", -1);
  2566. *pconn = conn_ent;
  2567. if (lua_pcall (L, 2, 0, 0) != 0) {
  2568. rspamd_controller_send_error (conn_ent, 503, "Cannot run callback: %s",
  2569. lua_tostring (L, -1));
  2570. lua_settop (L, 0);
  2571. return 0;
  2572. }
  2573. rspamd_session_pending (task->s);
  2574. return 0;
  2575. }
  2576. static void
  2577. rspamd_controller_error_handler (struct rspamd_http_connection_entry *conn_ent,
  2578. GError *err)
  2579. {
  2580. struct rspamd_controller_session *session = conn_ent->ud;
  2581. msg_err_session ("http error occurred: %s", err->message);
  2582. }
  2583. static void
  2584. rspamd_controller_finish_handler (struct rspamd_http_connection_entry *conn_ent)
  2585. {
  2586. struct rspamd_controller_session *session = conn_ent->ud;
  2587. session->ctx->worker->srv->stat->control_connections_count++;
  2588. if (session->task != NULL) {
  2589. rspamd_session_destroy (session->task->s);
  2590. }
  2591. session->wrk->nconns --;
  2592. rspamd_inet_address_free (session->from_addr);
  2593. REF_RELEASE (session->cfg);
  2594. if (session->pool) {
  2595. msg_debug_session ("destroy session %p", session);
  2596. rspamd_mempool_delete (session->pool);
  2597. }
  2598. g_free (session);
  2599. }
  2600. static void
  2601. rspamd_controller_accept_socket (EV_P_ ev_io *w, int revents)
  2602. {
  2603. struct rspamd_worker *worker = (struct rspamd_worker *)w->data;
  2604. struct rspamd_controller_worker_ctx *ctx;
  2605. struct rspamd_controller_session *session;
  2606. rspamd_inet_addr_t *addr;
  2607. gint nfd;
  2608. ctx = worker->ctx;
  2609. if ((nfd =
  2610. rspamd_accept_from_socket (w->fd, &addr,
  2611. rspamd_worker_throttle_accept_events, worker->accept_events)) == -1) {
  2612. msg_warn_ctx ("accept failed: %s", strerror (errno));
  2613. return;
  2614. }
  2615. /* Check for EAGAIN */
  2616. if (nfd == 0) {
  2617. return;
  2618. }
  2619. session = g_malloc0 (sizeof (struct rspamd_controller_session));
  2620. session->pool = rspamd_mempool_new (rspamd_mempool_suggest_size (),
  2621. "csession", 0);
  2622. session->ctx = ctx;
  2623. session->cfg = ctx->cfg;
  2624. session->lang_det = ctx->lang_det;
  2625. REF_RETAIN (session->cfg);
  2626. session->from_addr = addr;
  2627. session->wrk = worker;
  2628. worker->nconns ++;
  2629. rspamd_http_router_handle_socket (ctx->http, nfd, session);
  2630. }
  2631. static void
  2632. rspamd_controller_password_sane (struct rspamd_controller_worker_ctx *ctx,
  2633. const gchar *password, const gchar *type)
  2634. {
  2635. const struct rspamd_controller_pbkdf *pbkdf = &pbkdf_list[0];
  2636. if (password == NULL) {
  2637. msg_warn_ctx ("%s is not set, so you should filter controller "
  2638. "availability "
  2639. "by using of firewall or `secure_ip` option", type);
  2640. return;
  2641. }
  2642. g_assert (pbkdf != NULL);
  2643. if (!rspamd_is_encrypted_password (password, NULL)) {
  2644. /* Suggest encryption to a user */
  2645. msg_warn_ctx ("your %s is not encrypted, we strongly "
  2646. "recommend to replace it with the encrypted one", type);
  2647. }
  2648. }
  2649. gpointer
  2650. init_controller_worker (struct rspamd_config *cfg)
  2651. {
  2652. struct rspamd_controller_worker_ctx *ctx;
  2653. GQuark type;
  2654. type = g_quark_try_string ("controller");
  2655. ctx = rspamd_mempool_alloc0 (cfg->cfg_pool,
  2656. sizeof (struct rspamd_controller_worker_ctx));
  2657. ctx->magic = rspamd_controller_ctx_magic;
  2658. ctx->timeout = DEFAULT_WORKER_IO_TIMEOUT;
  2659. ctx->task_timeout = NAN;
  2660. rspamd_rcl_register_worker_option (cfg,
  2661. type,
  2662. "password",
  2663. rspamd_rcl_parse_struct_string,
  2664. ctx,
  2665. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, password),
  2666. 0,
  2667. "Password for read-only commands");
  2668. rspamd_rcl_register_worker_option (cfg,
  2669. type,
  2670. "enable_password",
  2671. rspamd_rcl_parse_struct_string,
  2672. ctx,
  2673. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2674. enable_password),
  2675. 0,
  2676. "Password for read and write commands");
  2677. rspamd_rcl_register_worker_option (cfg,
  2678. type,
  2679. "ssl",
  2680. rspamd_rcl_parse_struct_boolean,
  2681. ctx,
  2682. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, use_ssl),
  2683. 0,
  2684. "Unimplemented");
  2685. rspamd_rcl_register_worker_option (cfg,
  2686. type,
  2687. "ssl_cert",
  2688. rspamd_rcl_parse_struct_string,
  2689. ctx,
  2690. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, ssl_cert),
  2691. 0,
  2692. "Unimplemented");
  2693. rspamd_rcl_register_worker_option (cfg,
  2694. type,
  2695. "ssl_key",
  2696. rspamd_rcl_parse_struct_string,
  2697. ctx,
  2698. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, ssl_key),
  2699. 0,
  2700. "Unimplemented");
  2701. rspamd_rcl_register_worker_option (cfg,
  2702. type,
  2703. "timeout",
  2704. rspamd_rcl_parse_struct_time,
  2705. ctx,
  2706. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2707. timeout),
  2708. RSPAMD_CL_FLAG_TIME_FLOAT,
  2709. "Protocol timeout");
  2710. rspamd_rcl_register_worker_option (cfg,
  2711. type,
  2712. "secure_ip",
  2713. rspamd_rcl_parse_struct_ucl,
  2714. ctx,
  2715. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, secure_ip),
  2716. 0,
  2717. "List of IP addresses that are allowed for password-less access");
  2718. rspamd_rcl_register_worker_option (cfg,
  2719. type,
  2720. "trusted_ips",
  2721. rspamd_rcl_parse_struct_ucl,
  2722. ctx,
  2723. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, secure_ip),
  2724. 0,
  2725. "List of IP addresses that are allowed for password-less access");
  2726. rspamd_rcl_register_worker_option (cfg,
  2727. type,
  2728. "static_dir",
  2729. rspamd_rcl_parse_struct_string,
  2730. ctx,
  2731. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2732. static_files_dir),
  2733. 0,
  2734. "Directory for static files served by controller's HTTP server");
  2735. rspamd_rcl_register_worker_option (cfg,
  2736. type,
  2737. "keypair",
  2738. rspamd_rcl_parse_struct_keypair,
  2739. ctx,
  2740. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2741. key),
  2742. 0,
  2743. "Encryption keypair");
  2744. rspamd_rcl_register_worker_option (cfg,
  2745. type,
  2746. "task_timeout",
  2747. rspamd_rcl_parse_struct_time,
  2748. ctx,
  2749. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2750. task_timeout),
  2751. RSPAMD_CL_FLAG_TIME_FLOAT,
  2752. "Maximum task processing time, default: 8.0 seconds");
  2753. return ctx;
  2754. }
  2755. /* Lua bindings */
  2756. LUA_FUNCTION_DEF (csession, get_ev_base);
  2757. LUA_FUNCTION_DEF (csession, get_cfg);
  2758. LUA_FUNCTION_DEF (csession, send_ucl);
  2759. LUA_FUNCTION_DEF (csession, send_string);
  2760. LUA_FUNCTION_DEF (csession, send_error);
  2761. static const struct luaL_reg lua_csessionlib_m[] = {
  2762. LUA_INTERFACE_DEF (csession, get_ev_base),
  2763. LUA_INTERFACE_DEF (csession, get_cfg),
  2764. LUA_INTERFACE_DEF (csession, send_ucl),
  2765. LUA_INTERFACE_DEF (csession, send_string),
  2766. LUA_INTERFACE_DEF (csession, send_error),
  2767. {"__tostring", rspamd_lua_class_tostring},
  2768. {NULL, NULL}
  2769. };
  2770. /* Basic functions of LUA API for worker object */
  2771. static void
  2772. luaopen_controller (lua_State * L)
  2773. {
  2774. rspamd_lua_new_class (L, "rspamd{csession}", lua_csessionlib_m);
  2775. lua_pop (L, 1);
  2776. }
  2777. struct rspamd_http_connection_entry *
  2778. lua_check_controller_entry (lua_State * L, gint pos)
  2779. {
  2780. void *ud = rspamd_lua_check_udata (L, pos, "rspamd{csession}");
  2781. luaL_argcheck (L, ud != NULL, pos, "'csession' expected");
  2782. return ud ? *((struct rspamd_http_connection_entry **)ud) : NULL;
  2783. }
  2784. static int
  2785. lua_csession_get_ev_base (lua_State *L)
  2786. {
  2787. struct rspamd_http_connection_entry *c = lua_check_controller_entry (L, 1);
  2788. struct ev_loop **pbase;
  2789. struct rspamd_controller_session *s;
  2790. if (c) {
  2791. s = c->ud;
  2792. pbase = lua_newuserdata (L, sizeof (struct ev_loop *));
  2793. rspamd_lua_setclass (L, "rspamd{ev_base}", -1);
  2794. *pbase = s->ctx->event_loop;
  2795. }
  2796. else {
  2797. return luaL_error (L, "invalid arguments");
  2798. }
  2799. return 1;
  2800. }
  2801. static int
  2802. lua_csession_get_cfg (lua_State *L)
  2803. {
  2804. struct rspamd_http_connection_entry *c = lua_check_controller_entry (L, 1);
  2805. struct rspamd_config **pcfg;
  2806. struct rspamd_controller_session *s;
  2807. if (c) {
  2808. s = c->ud;
  2809. pcfg = lua_newuserdata (L, sizeof (gpointer));
  2810. rspamd_lua_setclass (L, "rspamd{config}", -1);
  2811. *pcfg = s->ctx->cfg;
  2812. }
  2813. else {
  2814. return luaL_error (L, "invalid arguments");
  2815. }
  2816. return 1;
  2817. }
  2818. static int
  2819. lua_csession_send_ucl (lua_State *L)
  2820. {
  2821. struct rspamd_http_connection_entry *c = lua_check_controller_entry (L, 1);
  2822. ucl_object_t *obj = ucl_object_lua_import_escape (L, 2);
  2823. if (c) {
  2824. rspamd_controller_send_ucl (c, obj);
  2825. }
  2826. else {
  2827. ucl_object_unref (obj);
  2828. return luaL_error (L, "invalid arguments");
  2829. }
  2830. ucl_object_unref (obj);
  2831. return 0;
  2832. }
  2833. static int
  2834. lua_csession_send_error (lua_State *L)
  2835. {
  2836. struct rspamd_http_connection_entry *c = lua_check_controller_entry (L, 1);
  2837. guint err_code = lua_tonumber (L, 2);
  2838. const gchar *err_str = lua_tostring (L, 3);
  2839. if (c) {
  2840. rspamd_controller_send_error (c, err_code, "%s", err_str);
  2841. }
  2842. else {
  2843. return luaL_error (L, "invalid arguments");
  2844. }
  2845. return 0;
  2846. }
  2847. static int
  2848. lua_csession_send_string (lua_State *L)
  2849. {
  2850. struct rspamd_http_connection_entry *c = lua_check_controller_entry (L, 1);
  2851. const gchar *str = lua_tostring (L, 2);
  2852. if (c) {
  2853. rspamd_controller_send_string (c, str);
  2854. }
  2855. else {
  2856. return luaL_error (L, "invalid arguments");
  2857. }
  2858. return 0;
  2859. }
  2860. static void
  2861. rspamd_plugin_cbdata_dtor (gpointer p)
  2862. {
  2863. struct rspamd_controller_plugin_cbdata *cbd = p;
  2864. g_free (cbd->plugin);
  2865. ucl_object_unref (cbd->obj); /* This also releases lua references */
  2866. g_free (cbd);
  2867. }
  2868. static void
  2869. rspamd_controller_register_plugin_path (lua_State *L,
  2870. struct rspamd_controller_worker_ctx *ctx,
  2871. const ucl_object_t *webui_data,
  2872. const ucl_object_t *handler,
  2873. const gchar *path,
  2874. const gchar *plugin_name)
  2875. {
  2876. struct rspamd_controller_plugin_cbdata *cbd;
  2877. const ucl_object_t *elt;
  2878. GString *full_path;
  2879. cbd = g_malloc0 (sizeof (*cbd));
  2880. cbd->L = L;
  2881. cbd->ctx = ctx;
  2882. cbd->handler = ucl_object_toclosure (handler);
  2883. cbd->plugin = g_strdup (plugin_name);
  2884. cbd->obj = ucl_object_ref (webui_data);
  2885. elt = ucl_object_lookup (webui_data, "version");
  2886. if (elt) {
  2887. cbd->version = ucl_object_toint (elt);
  2888. }
  2889. elt = ucl_object_lookup (webui_data, "enable");
  2890. if (elt && ucl_object_toboolean (elt)) {
  2891. cbd->is_enable = TRUE;
  2892. }
  2893. elt = ucl_object_lookup (webui_data, "need_task");
  2894. if (elt && !!ucl_object_toboolean (elt)) {
  2895. cbd->need_task = TRUE;
  2896. }
  2897. full_path = g_string_new ("/plugins/");
  2898. rspamd_printf_gstring (full_path, "%s/%s",
  2899. plugin_name, path);
  2900. rspamd_http_router_add_path (ctx->http,
  2901. full_path->str,
  2902. rspamd_controller_handle_lua_plugin);
  2903. g_hash_table_insert (ctx->plugins, full_path->str, cbd);
  2904. g_string_free (full_path, FALSE); /* Do not free data */
  2905. }
  2906. static void
  2907. rspamd_controller_register_plugins_paths (struct rspamd_controller_worker_ctx *ctx)
  2908. {
  2909. lua_State *L = ctx->cfg->lua_state;
  2910. ucl_object_t *webui_data;
  2911. const ucl_object_t *handler_obj, *cur;
  2912. ucl_object_iter_t it = NULL;
  2913. lua_getglobal (L, "rspamd_plugins");
  2914. if (lua_istable (L, -1)) {
  2915. for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 2)) {
  2916. lua_pushvalue (L, -2); /* Store key */
  2917. lua_pushstring (L, "webui");
  2918. lua_gettable (L, -3); /* value is at -3 index */
  2919. if (lua_istable (L, -1)) {
  2920. webui_data = ucl_object_lua_import_escape (L, -1);
  2921. while ((cur = ucl_object_iterate (webui_data, &it, true)) != NULL) {
  2922. handler_obj = ucl_object_lookup (cur, "handler");
  2923. if (handler_obj && ucl_object_key (cur)) {
  2924. rspamd_controller_register_plugin_path (L, ctx,
  2925. cur, handler_obj, ucl_object_key (cur),
  2926. lua_tostring (L, -2));
  2927. }
  2928. else {
  2929. msg_err_ctx ("bad webui definition for plugin: %s",
  2930. lua_tostring (L, -2));
  2931. }
  2932. }
  2933. ucl_object_unref (webui_data);
  2934. }
  2935. lua_pop (L, 1); /* remove table value */
  2936. }
  2937. }
  2938. lua_pop (L, 1); /* rspamd_plugins global */
  2939. }
  2940. /*
  2941. * Start worker process
  2942. */
  2943. __attribute__((noreturn))
  2944. void
  2945. start_controller_worker (struct rspamd_worker *worker)
  2946. {
  2947. struct rspamd_controller_worker_ctx *ctx = worker->ctx;
  2948. struct module_ctx *mctx;
  2949. GHashTableIter iter;
  2950. gpointer key, value;
  2951. guint i;
  2952. gpointer m;
  2953. g_assert (rspamd_worker_check_context (worker->ctx, rspamd_controller_ctx_magic));
  2954. ctx->event_loop = rspamd_prepare_worker (worker,
  2955. "controller",
  2956. rspamd_controller_accept_socket);
  2957. ctx->start_time = ev_time ();
  2958. ctx->worker = worker;
  2959. ctx->cfg = worker->srv->cfg;
  2960. ctx->srv = worker->srv;
  2961. ctx->custom_commands = g_hash_table_new (rspamd_strcase_hash,
  2962. rspamd_strcase_equal);
  2963. ctx->plugins = g_hash_table_new_full (rspamd_strcase_hash,
  2964. rspamd_strcase_equal, g_free,
  2965. rspamd_plugin_cbdata_dtor);
  2966. if (isnan (ctx->task_timeout)) {
  2967. if (isnan (ctx->cfg->task_timeout)) {
  2968. ctx->task_timeout = 0;
  2969. }
  2970. else {
  2971. ctx->task_timeout = ctx->cfg->task_timeout;
  2972. }
  2973. }
  2974. if (ctx->secure_ip != NULL) {
  2975. rspamd_config_radix_from_ucl (ctx->cfg, ctx->secure_ip,
  2976. "Allow unauthenticated requests from these addresses",
  2977. &ctx->secure_map,
  2978. NULL,
  2979. worker);
  2980. }
  2981. ctx->lang_det = ctx->cfg->lang_det;
  2982. rspamd_controller_password_sane (ctx, ctx->password, "normal password");
  2983. rspamd_controller_password_sane (ctx, ctx->enable_password, "enable "
  2984. "password");
  2985. /* Accept event */
  2986. ctx->http_ctx = rspamd_http_context_create (ctx->cfg, ctx->event_loop,
  2987. ctx->cfg->ups_ctx);
  2988. rspamd_mempool_add_destructor (ctx->cfg->cfg_pool,
  2989. (rspamd_mempool_destruct_t)rspamd_http_context_free,
  2990. ctx->http_ctx);
  2991. ctx->http = rspamd_http_router_new (rspamd_controller_error_handler,
  2992. rspamd_controller_finish_handler, ctx->timeout,
  2993. ctx->static_files_dir, ctx->http_ctx);
  2994. /* Add callbacks for different methods */
  2995. rspamd_http_router_add_path (ctx->http,
  2996. PATH_AUTH,
  2997. rspamd_controller_handle_auth);
  2998. rspamd_http_router_add_path (ctx->http,
  2999. PATH_SYMBOLS,
  3000. rspamd_controller_handle_symbols);
  3001. rspamd_http_router_add_path (ctx->http,
  3002. PATH_ACTIONS,
  3003. rspamd_controller_handle_actions);
  3004. rspamd_http_router_add_path (ctx->http,
  3005. PATH_MAPS,
  3006. rspamd_controller_handle_maps);
  3007. rspamd_http_router_add_path (ctx->http,
  3008. PATH_GET_MAP,
  3009. rspamd_controller_handle_get_map);
  3010. rspamd_http_router_add_path (ctx->http,
  3011. PATH_PIE_CHART,
  3012. rspamd_controller_handle_pie_chart);
  3013. rspamd_http_router_add_path (ctx->http,
  3014. PATH_GRAPH,
  3015. rspamd_controller_handle_graph);
  3016. rspamd_http_router_add_path (ctx->http,
  3017. PATH_HISTORY,
  3018. rspamd_controller_handle_history);
  3019. rspamd_http_router_add_path (ctx->http,
  3020. PATH_HISTORY_RESET,
  3021. rspamd_controller_handle_history_reset);
  3022. rspamd_http_router_add_path (ctx->http,
  3023. PATH_LEARN_SPAM,
  3024. rspamd_controller_handle_learnspam);
  3025. rspamd_http_router_add_path (ctx->http,
  3026. PATH_LEARN_HAM,
  3027. rspamd_controller_handle_learnham);
  3028. rspamd_http_router_add_path (ctx->http,
  3029. PATH_SAVE_ACTIONS,
  3030. rspamd_controller_handle_saveactions);
  3031. rspamd_http_router_add_path (ctx->http,
  3032. PATH_SAVE_SYMBOLS,
  3033. rspamd_controller_handle_savesymbols);
  3034. rspamd_http_router_add_path (ctx->http,
  3035. PATH_SAVE_MAP,
  3036. rspamd_controller_handle_savemap);
  3037. rspamd_http_router_add_path (ctx->http,
  3038. PATH_SCAN,
  3039. rspamd_controller_handle_scan);
  3040. rspamd_http_router_add_path (ctx->http,
  3041. PATH_CHECK,
  3042. rspamd_controller_handle_scan);
  3043. rspamd_http_router_add_path (ctx->http,
  3044. PATH_CHECKV2,
  3045. rspamd_controller_handle_scan);
  3046. rspamd_http_router_add_path (ctx->http,
  3047. PATH_STAT,
  3048. rspamd_controller_handle_stat);
  3049. rspamd_http_router_add_path (ctx->http,
  3050. PATH_STAT_RESET,
  3051. rspamd_controller_handle_statreset);
  3052. rspamd_http_router_add_path (ctx->http,
  3053. PATH_COUNTERS,
  3054. rspamd_controller_handle_counters);
  3055. rspamd_http_router_add_path (ctx->http,
  3056. PATH_ERRORS,
  3057. rspamd_controller_handle_errors);
  3058. rspamd_http_router_add_path (ctx->http,
  3059. PATH_NEIGHBOURS,
  3060. rspamd_controller_handle_neighbours);
  3061. rspamd_http_router_add_path (ctx->http,
  3062. PATH_PLUGINS,
  3063. rspamd_controller_handle_plugins);
  3064. rspamd_http_router_add_path (ctx->http,
  3065. PATH_PING,
  3066. rspamd_controller_handle_ping);
  3067. rspamd_controller_register_plugins_paths (ctx);
  3068. #if 0
  3069. rspamd_regexp_t *lua_re = rspamd_regexp_new ("^/.*/.*\\.lua$", NULL, NULL);
  3070. rspamd_http_router_add_regexp (ctx->http, lua_re,
  3071. rspamd_controller_handle_lua);
  3072. rspamd_regexp_unref (lua_re);
  3073. #endif
  3074. luaopen_controller (ctx->cfg->lua_state);
  3075. if (ctx->key) {
  3076. rspamd_http_router_set_key (ctx->http, ctx->key);
  3077. }
  3078. PTR_ARRAY_FOREACH (ctx->cfg->c_modules, i, mctx) {
  3079. if (mctx->mod->module_attach_controller_func != NULL) {
  3080. mctx->mod->module_attach_controller_func (mctx,
  3081. ctx->custom_commands);
  3082. }
  3083. }
  3084. g_hash_table_iter_init (&iter, ctx->custom_commands);
  3085. while (g_hash_table_iter_next (&iter, &key, &value)) {
  3086. rspamd_http_router_add_path (ctx->http,
  3087. key,
  3088. rspamd_controller_handle_custom);
  3089. }
  3090. if (worker->srv->cfg->neighbours && worker->srv->cfg->neighbours->len > 0) {
  3091. rspamd_http_router_add_header (ctx->http,
  3092. "Access-Control-Allow-Origin", "*");
  3093. }
  3094. rspamd_http_router_set_unknown_handler (ctx->http,
  3095. rspamd_controller_handle_unknown);
  3096. ctx->resolver = rspamd_dns_resolver_init (worker->srv->logger,
  3097. ctx->event_loop,
  3098. worker->srv->cfg);
  3099. rspamd_upstreams_library_config (worker->srv->cfg, worker->srv->cfg->ups_ctx,
  3100. ctx->event_loop, ctx->resolver->r);
  3101. rspamd_symcache_start_refresh (worker->srv->cfg->cache, ctx->event_loop,
  3102. worker);
  3103. rspamd_stat_init (worker->srv->cfg, ctx->event_loop);
  3104. rspamd_worker_init_controller (worker, &ctx->rrd);
  3105. rspamd_lua_run_postloads (ctx->cfg->lua_state, ctx->cfg, ctx->event_loop, worker);
  3106. #ifdef WITH_HYPERSCAN
  3107. rspamd_control_worker_add_cmd_handler (worker,
  3108. RSPAMD_CONTROL_HYPERSCAN_LOADED,
  3109. rspamd_worker_hyperscan_ready,
  3110. NULL);
  3111. #endif
  3112. /* Start event loop */
  3113. ev_loop (ctx->event_loop, 0);
  3114. rspamd_worker_block_signals ();
  3115. rspamd_controller_on_terminate (worker, ctx->rrd);
  3116. rspamd_stat_close ();
  3117. rspamd_http_router_free (ctx->http);
  3118. if (ctx->cached_password.len > 0) {
  3119. m = (gpointer)ctx->cached_password.begin;
  3120. munmap (m, ctx->cached_password.len);
  3121. }
  3122. if (ctx->cached_enable_password.len > 0) {
  3123. m = (gpointer) ctx->cached_enable_password.begin;
  3124. munmap (m, ctx->cached_enable_password.len);
  3125. }
  3126. g_hash_table_unref (ctx->plugins);
  3127. g_hash_table_unref (ctx->custom_commands);
  3128. REF_RELEASE (ctx->cfg);
  3129. rspamd_log_close (worker->srv->logger);
  3130. exit (EXIT_SUCCESS);
  3131. }