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.

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