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