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.

controller.c 69KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581
  1. /* Copyright (c) 2010-2012, Vsevolod Stakhov
  2. * All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. * * Redistributions of source code must retain the above copyright
  7. * notice, this list of conditions and the following disclaimer.
  8. * * Redistributions in binary form must reproduce the above copyright
  9. * notice, this list of conditions and the following disclaimer in the
  10. * documentation and/or other materials provided with the distribution.
  11. *
  12. * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
  13. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  14. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  15. * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
  16. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  17. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  18. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  19. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  20. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  21. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22. */
  23. #include "config.h"
  24. #include "libserver/dynamic_cfg.h"
  25. #include "libutil/rrd.h"
  26. #include "libutil/map.h"
  27. #include "libstat/stat_api.h"
  28. #include "rspamd.h"
  29. #include "libserver/worker_util.h"
  30. #include "cryptobox.h"
  31. #include "ottery.h"
  32. #include "libutil/rrd.h"
  33. #include "unix-std.h"
  34. #include <math.h>
  35. /* 60 seconds for worker's IO */
  36. #define DEFAULT_WORKER_IO_TIMEOUT 60000
  37. #define DEFAULT_STATS_PATH RSPAMD_DBDIR "/stats.ucl"
  38. /* HTTP paths */
  39. #define PATH_AUTH "/auth"
  40. #define PATH_SYMBOLS "/symbols"
  41. #define PATH_ACTIONS "/actions"
  42. #define PATH_MAPS "/maps"
  43. #define PATH_GET_MAP "/getmap"
  44. #define PATH_GRAPH "/graph"
  45. #define PATH_PIE_CHART "/pie"
  46. #define PATH_HISTORY "/history"
  47. #define PATH_HISTORY_RESET "/historyreset"
  48. #define PATH_LEARN_SPAM "/learnspam"
  49. #define PATH_LEARN_HAM "/learnham"
  50. #define PATH_SAVE_ACTIONS "/saveactions"
  51. #define PATH_SAVE_SYMBOLS "/savesymbols"
  52. #define PATH_SAVE_MAP "/savemap"
  53. #define PATH_SCAN "/scan"
  54. #define PATH_CHECK "/check"
  55. #define PATH_STAT "/stat"
  56. #define PATH_STAT_RESET "/statreset"
  57. #define PATH_COUNTERS "/counters"
  58. #define msg_err_session(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
  59. session->pool->tag.tagname, session->pool->tag.uid, \
  60. G_STRFUNC, \
  61. __VA_ARGS__)
  62. #define msg_warn_session(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \
  63. session->pool->tag.tagname, session->pool->tag.uid, \
  64. G_STRFUNC, \
  65. __VA_ARGS__)
  66. #define msg_info_session(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \
  67. session->pool->tag.tagname, session->pool->tag.uid, \
  68. G_STRFUNC, \
  69. __VA_ARGS__)
  70. #define msg_debug_session(...) rspamd_default_log_function (G_LOG_LEVEL_DEBUG, \
  71. session->pool->tag.tagname, session->pool->tag.uid, \
  72. G_STRFUNC, \
  73. __VA_ARGS__)
  74. #define msg_err_ctx(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
  75. "controller", ctx->cfg->cfg_pool->tag.uid, \
  76. G_STRFUNC, \
  77. __VA_ARGS__)
  78. #define msg_warn_ctx(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \
  79. "controller", ctx->cfg->cfg_pool->tag.uid, \
  80. G_STRFUNC, \
  81. __VA_ARGS__)
  82. #define msg_info_ctx(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \
  83. "controller", ctx->cfg->cfg_pool->tag.uid, \
  84. G_STRFUNC, \
  85. __VA_ARGS__)
  86. #define msg_debug_ctx(...) rspamd_default_log_function (G_LOG_LEVEL_DEBUG, \
  87. "controller", ctx->cfg->cfg_pool->tag.uid, \
  88. G_STRFUNC, \
  89. __VA_ARGS__)
  90. /* Graph colors */
  91. #define COLOR_CLEAN "#58A458"
  92. #define COLOR_PROBABLE_SPAM "#D67E7E"
  93. #define COLOR_GREYLIST "#A0A0A0"
  94. #define COLOR_REJECT "#CB4B4B"
  95. #define COLOR_TOTAL "#9440ED"
  96. const struct timeval rrd_update_time = {
  97. .tv_sec = 1,
  98. .tv_usec = 0
  99. };
  100. gpointer init_controller_worker (struct rspamd_config *cfg);
  101. void start_controller_worker (struct rspamd_worker *worker);
  102. worker_t controller_worker = {
  103. "controller", /* Name */
  104. init_controller_worker, /* Init function */
  105. start_controller_worker, /* Start function */
  106. TRUE, /* Has socket */
  107. TRUE, /* Non unique */
  108. FALSE, /* Non threaded */
  109. TRUE, /* Killable */
  110. SOCK_STREAM /* TCP socket */
  111. };
  112. /*
  113. * Worker's context
  114. */
  115. struct rspamd_controller_worker_ctx {
  116. guint32 timeout;
  117. struct timeval io_tv;
  118. /* DNS resolver */
  119. struct rspamd_dns_resolver *resolver;
  120. /* Events base */
  121. struct event_base *ev_base;
  122. /* Whether we use ssl for this server */
  123. gboolean use_ssl;
  124. /* Webui password */
  125. gchar *password;
  126. /* Privilleged password */
  127. gchar *enable_password;
  128. /* Cached versions of the passwords */
  129. rspamd_ftok_t cached_password;
  130. rspamd_ftok_t cached_enable_password;
  131. /* HTTP server */
  132. struct rspamd_http_connection_router *http;
  133. /* Server's start time */
  134. time_t start_time;
  135. /* Main server */
  136. struct rspamd_main *srv;
  137. /* Configuration */
  138. struct rspamd_config *cfg;
  139. /* SSL cert */
  140. gchar *ssl_cert;
  141. /* SSL private key */
  142. gchar *ssl_key;
  143. /* A map of secure IP */
  144. GList *secure_ip;
  145. radix_compressed_t *secure_map;
  146. /* Static files dir */
  147. gchar *static_files_dir;
  148. /* Saved statistics path */
  149. gchar *saved_stats_path;
  150. /* Custom commands registered by plugins */
  151. GHashTable *custom_commands;
  152. /* Worker */
  153. struct rspamd_worker *worker;
  154. /* Local keypair */
  155. gpointer key;
  156. struct event *rrd_event;
  157. struct rspamd_rrd_file *rrd;
  158. };
  159. static gboolean
  160. rspamd_is_encrypted_password (const gchar *password,
  161. struct rspamd_controller_pbkdf const **pbkdf)
  162. {
  163. const gchar *start, *end;
  164. gint64 id;
  165. gsize size;
  166. gboolean ret = FALSE;
  167. if (password[0] == '$') {
  168. /* Parse id */
  169. start = password + 1;
  170. end = start;
  171. size = 0;
  172. while (*end != '\0' && g_ascii_isdigit (*end)) {
  173. size++;
  174. end++;
  175. }
  176. if (size > 0) {
  177. gchar *endptr;
  178. id = strtoul (start, &endptr, 10);
  179. if ((endptr == NULL || *endptr == *end) && id == RSPAMD_PBKDF_ID_V1) {
  180. ret = TRUE;
  181. if (pbkdf != NULL) {
  182. *pbkdf = &pbkdf_list[0];
  183. }
  184. }
  185. }
  186. }
  187. return ret;
  188. }
  189. static const gchar *
  190. rspamd_encrypted_password_get_str (const gchar * password, gsize skip,
  191. gsize * length)
  192. {
  193. const gchar *str, *start, *end;
  194. gsize size;
  195. start = password + skip;
  196. end = start;
  197. size = 0;
  198. while (*end != '\0' && g_ascii_isalnum (*end)) {
  199. size++;
  200. end++;
  201. }
  202. if (size) {
  203. str = start;
  204. *length = size;
  205. }
  206. else {
  207. str = NULL;
  208. }
  209. return str;
  210. }
  211. static gboolean
  212. rspamd_check_encrypted_password (struct rspamd_controller_worker_ctx *ctx,
  213. const rspamd_ftok_t * password, const gchar * check,
  214. const struct rspamd_controller_pbkdf *pbkdf,
  215. gboolean is_enable)
  216. {
  217. const gchar *salt, *hash;
  218. gchar *salt_decoded, *key_decoded;
  219. gsize salt_len, key_len;
  220. gboolean ret = TRUE;
  221. guchar *local_key;
  222. rspamd_ftok_t *cache;
  223. gpointer m;
  224. /* First of all check cached versions to save resources */
  225. if (is_enable && ctx->cached_enable_password.len != 0) {
  226. if (password->len != ctx->cached_enable_password.len ||
  227. !rspamd_constant_memcmp (password->begin,
  228. ctx->cached_enable_password.begin, password->len)) {
  229. msg_info_ctx ("incorrect or absent enable password has been specified");
  230. return FALSE;
  231. }
  232. return TRUE;
  233. }
  234. else if (!is_enable && ctx->cached_password.len != 0) {
  235. if (password->len != ctx->cached_password.len ||
  236. !rspamd_constant_memcmp (password->begin,
  237. ctx->cached_password.begin, password->len)) {
  238. msg_info_ctx ("incorrect or absent password has been specified");
  239. return FALSE;
  240. }
  241. return TRUE;
  242. }
  243. g_assert (pbkdf != NULL);
  244. /* get salt */
  245. salt = rspamd_encrypted_password_get_str (check, 3, &salt_len);
  246. /* get hash */
  247. hash = rspamd_encrypted_password_get_str (check, 3 + salt_len + 1,
  248. &key_len);
  249. if (salt != NULL && hash != NULL) {
  250. /* decode salt */
  251. salt_decoded = rspamd_decode_base32 (salt, salt_len, &salt_len);
  252. if (salt_decoded == NULL || salt_len != pbkdf->salt_len) {
  253. /* We have some unknown salt here */
  254. msg_info_ctx ("incorrect salt: %z, while %z expected",
  255. salt_len, pbkdf->salt_len);
  256. return FALSE;
  257. }
  258. key_decoded = rspamd_decode_base32 (hash, key_len, &key_len);
  259. if (key_decoded == NULL || key_len != pbkdf->key_len) {
  260. /* We have some unknown salt here */
  261. msg_info_ctx ("incorrect key: %z, while %z expected",
  262. key_len, pbkdf->key_len);
  263. return FALSE;
  264. }
  265. local_key = g_alloca (pbkdf->key_len);
  266. rspamd_cryptobox_pbkdf (password->begin, password->len,
  267. salt_decoded, salt_len,
  268. local_key, pbkdf->key_len, pbkdf->rounds);
  269. if (!rspamd_constant_memcmp (key_decoded, local_key, pbkdf->key_len)) {
  270. msg_info_ctx ("incorrect or absent password has been specified");
  271. ret = FALSE;
  272. }
  273. g_free (salt_decoded);
  274. g_free (key_decoded);
  275. }
  276. if (ret) {
  277. /* Save cached version */
  278. cache = is_enable ? &ctx->cached_enable_password : &ctx->cached_password;
  279. if (cache->len == 0) {
  280. /* Mmap region */
  281. m = mmap (NULL, password->len, PROT_WRITE,
  282. MAP_PRIVATE | MAP_ANON, -1, 0);
  283. memcpy (m, password->begin, password->len);
  284. (void)mprotect (m, password->len, PROT_READ);
  285. (void)mlock (m, password->len);
  286. cache->begin = m;
  287. cache->len = password->len;
  288. }
  289. }
  290. return ret;
  291. }
  292. /* Check for password if it is required by configuration */
  293. static gboolean rspamd_controller_check_password(
  294. struct rspamd_http_connection_entry *entry,
  295. struct rspamd_controller_session *session,
  296. struct rspamd_http_message *msg, gboolean is_enable)
  297. {
  298. const gchar *check;
  299. const rspamd_ftok_t *password;
  300. rspamd_ftok_t lookup;
  301. GHashTable *query_args = NULL;
  302. struct rspamd_controller_worker_ctx *ctx = session->ctx;
  303. gboolean check_normal = TRUE, check_enable = TRUE, ret = TRUE,
  304. use_enable = FALSE;
  305. const struct rspamd_controller_pbkdf *pbkdf = NULL;
  306. /* Access list logic */
  307. if (rspamd_inet_address_get_af (session->from_addr) == AF_UNIX) {
  308. msg_info_session ("allow unauthorized connection from a unix socket");
  309. return TRUE;
  310. }
  311. else if (ctx->secure_map
  312. && radix_find_compressed_addr (ctx->secure_map, session->from_addr)
  313. != RADIX_NO_VALUE) {
  314. msg_info_session ("allow unauthorized connection from a trusted IP %s",
  315. rspamd_inet_address_to_string (session->from_addr));
  316. return TRUE;
  317. }
  318. /* Password logic */
  319. password = rspamd_http_message_find_header (msg, "Password");
  320. if (password == NULL) {
  321. /* Try to get password from query args */
  322. query_args = rspamd_http_message_parse_query (msg);
  323. lookup.begin = (gchar *)"password";
  324. lookup.len = sizeof ("password") - 1;
  325. password = g_hash_table_lookup (query_args, &lookup);
  326. }
  327. if (password == NULL) {
  328. if (ctx->secure_map == NULL) {
  329. if (ctx->password == NULL && !is_enable) {
  330. return TRUE;
  331. }
  332. else if (is_enable && (ctx->password == NULL &&
  333. ctx->enable_password == NULL)) {
  334. return TRUE;
  335. }
  336. }
  337. msg_info_session ("absent password has been specified");
  338. ret = FALSE;
  339. }
  340. else {
  341. if (is_enable) {
  342. /* For privileged commands we strictly require enable password */
  343. if (ctx->enable_password != NULL) {
  344. check = ctx->enable_password;
  345. use_enable = TRUE;
  346. }
  347. else {
  348. /* Use just a password (legacy mode) */
  349. msg_info(
  350. "using password as enable_password for a privileged command");
  351. check = ctx->password;
  352. }
  353. if (check != NULL) {
  354. if (!rspamd_is_encrypted_password (check, &pbkdf)) {
  355. ret = rspamd_constant_memcmp (password->begin, check, password->len);
  356. }
  357. else {
  358. ret = rspamd_check_encrypted_password (ctx, password, check,
  359. pbkdf, use_enable);
  360. }
  361. }
  362. else {
  363. msg_warn_session (
  364. "no password to check while executing a privileged command");
  365. if (ctx->secure_map) {
  366. msg_info("deny unauthorized connection");
  367. ret = FALSE;
  368. }
  369. ret = FALSE;
  370. }
  371. }
  372. else {
  373. /* Accept both normal and enable passwords */
  374. if (ctx->password != NULL) {
  375. check = ctx->password;
  376. if (!rspamd_is_encrypted_password (check, &pbkdf)) {
  377. check_normal = rspamd_constant_memcmp (password->begin, check,
  378. password->len);
  379. }
  380. else {
  381. check_normal = rspamd_check_encrypted_password (ctx,
  382. password,
  383. check, pbkdf, FALSE);
  384. }
  385. }
  386. else {
  387. check_normal = FALSE;
  388. }
  389. if (ctx->enable_password != NULL) {
  390. check = ctx->enable_password;
  391. if (!rspamd_is_encrypted_password (check, &pbkdf)) {
  392. check_enable = rspamd_constant_memcmp (password->begin, check,
  393. password->len);
  394. }
  395. else {
  396. check_enable = rspamd_check_encrypted_password (ctx,
  397. password,
  398. check, pbkdf, TRUE);
  399. }
  400. }
  401. else {
  402. check_enable = FALSE;
  403. }
  404. }
  405. }
  406. if (query_args != NULL) {
  407. g_hash_table_unref (query_args);
  408. }
  409. if (check_normal == FALSE && check_enable == FALSE) {
  410. msg_info("absent or incorrect password has been specified");
  411. ret = FALSE;
  412. }
  413. if (!ret) {
  414. rspamd_controller_send_error (entry, 403, "Unauthorized");
  415. }
  416. return ret;
  417. }
  418. /* Command handlers */
  419. /*
  420. * Auth command handler:
  421. * request: /auth
  422. * headers: Password
  423. * reply: json {"auth": "ok", "version": "0.5.2", "uptime": "some uptime", "error": "none"}
  424. */
  425. static int
  426. rspamd_controller_handle_auth (struct rspamd_http_connection_entry *conn_ent,
  427. struct rspamd_http_message *msg)
  428. {
  429. struct rspamd_controller_session *session = conn_ent->ud;
  430. struct rspamd_stat *st;
  431. int64_t uptime;
  432. gulong data[4];
  433. ucl_object_t *obj;
  434. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  435. return 0;
  436. }
  437. obj = ucl_object_typed_new (UCL_OBJECT);
  438. st = session->ctx->srv->stat;
  439. data[0] = st->actions_stat[METRIC_ACTION_NOACTION];
  440. data[1] = st->actions_stat[METRIC_ACTION_ADD_HEADER] +
  441. st->actions_stat[METRIC_ACTION_REWRITE_SUBJECT];
  442. data[2] = st->actions_stat[METRIC_ACTION_GREYLIST];
  443. data[3] = st->actions_stat[METRIC_ACTION_REJECT];
  444. /* Get uptime */
  445. uptime = time (NULL) - session->ctx->start_time;
  446. ucl_object_insert_key (obj, ucl_object_fromstring (
  447. RVERSION), "version", 0, false);
  448. ucl_object_insert_key (obj, ucl_object_fromstring (
  449. "ok"), "auth", 0, false);
  450. ucl_object_insert_key (obj, ucl_object_fromint (
  451. uptime), "uptime", 0, false);
  452. ucl_object_insert_key (obj, ucl_object_fromint (
  453. data[0]), "clean", 0, false);
  454. ucl_object_insert_key (obj, ucl_object_fromint (
  455. data[1]), "probable", 0, false);
  456. ucl_object_insert_key (obj, ucl_object_fromint (
  457. data[2]), "greylist", 0, false);
  458. ucl_object_insert_key (obj, ucl_object_fromint (
  459. data[3]), "reject", 0, false);
  460. ucl_object_insert_key (obj, ucl_object_fromint (
  461. st->messages_scanned), "scanned", 0, false);
  462. ucl_object_insert_key (obj, ucl_object_fromint (
  463. st->messages_learned), "learned", 0, false);
  464. rspamd_controller_send_ucl (conn_ent, obj);
  465. ucl_object_unref (obj);
  466. return 0;
  467. }
  468. /*
  469. * Symbols command handler:
  470. * request: /symbols
  471. * reply: json [{
  472. * "name": "group_name",
  473. * "symbols": [
  474. * {
  475. * "name": "name",
  476. * "weight": 0.1,
  477. * "description": "description of symbol"
  478. * },
  479. * {...}
  480. * },
  481. * {...}]
  482. */
  483. static int
  484. rspamd_controller_handle_symbols (struct rspamd_http_connection_entry *conn_ent,
  485. struct rspamd_http_message *msg)
  486. {
  487. struct rspamd_controller_session *session = conn_ent->ud;
  488. GHashTableIter it, sit;
  489. struct rspamd_symbols_group *gr;
  490. struct rspamd_symbol_def *sym;
  491. struct metric *metric;
  492. ucl_object_t *obj, *top, *sym_obj, *group_symbols;
  493. gpointer k, v;
  494. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  495. return 0;
  496. }
  497. top = ucl_object_typed_new (UCL_ARRAY);
  498. /* Go through all symbols groups in the default metric */
  499. metric = g_hash_table_lookup (session->ctx->cfg->metrics, DEFAULT_METRIC);
  500. g_assert (metric != NULL);
  501. g_hash_table_iter_init (&it, metric->groups);
  502. while (g_hash_table_iter_next (&it, &k, &v)) {
  503. gr = v;
  504. obj = ucl_object_typed_new (UCL_OBJECT);
  505. ucl_object_insert_key (obj, ucl_object_fromstring (
  506. gr->name), "group", 0, false);
  507. /* Iterate through all symbols */
  508. g_hash_table_iter_init (&sit, gr->symbols);
  509. group_symbols = ucl_object_typed_new (UCL_ARRAY);
  510. while (g_hash_table_iter_next (&sit, &k, &v)) {
  511. sym = v;
  512. sym_obj = ucl_object_typed_new (UCL_OBJECT);
  513. ucl_object_insert_key (sym_obj, ucl_object_fromstring (sym->name),
  514. "symbol", 0, false);
  515. ucl_object_insert_key (sym_obj,
  516. ucl_object_fromdouble (*sym->weight_ptr),
  517. "weight", 0, false);
  518. if (sym->description) {
  519. ucl_object_insert_key (sym_obj,
  520. ucl_object_fromstring (sym->description),
  521. "description", 0, false);
  522. }
  523. ucl_array_append (group_symbols, sym_obj);
  524. }
  525. ucl_object_insert_key (obj, group_symbols, "rules", 0, false);
  526. ucl_array_append (top, obj);
  527. }
  528. rspamd_controller_send_ucl (conn_ent, top);
  529. ucl_object_unref (top);
  530. return 0;
  531. }
  532. /*
  533. * Actions command handler:
  534. * request: /actions
  535. * reply: json [{
  536. * "action": "no action",
  537. * "value": 1.1
  538. * },
  539. * {...}]
  540. */
  541. static int
  542. rspamd_controller_handle_actions (struct rspamd_http_connection_entry *conn_ent,
  543. struct rspamd_http_message *msg)
  544. {
  545. struct rspamd_controller_session *session = conn_ent->ud;
  546. struct metric *metric;
  547. struct metric_action *act;
  548. gint i;
  549. ucl_object_t *obj, *top;
  550. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  551. return 0;
  552. }
  553. top = ucl_object_typed_new (UCL_ARRAY);
  554. /* Get actions for default metric */
  555. metric = g_hash_table_lookup (session->ctx->cfg->metrics, DEFAULT_METRIC);
  556. if (metric != NULL) {
  557. for (i = METRIC_ACTION_REJECT; i < METRIC_ACTION_MAX; i++) {
  558. act = &metric->actions[i];
  559. if (act->score >= 0) {
  560. obj = ucl_object_typed_new (UCL_OBJECT);
  561. ucl_object_insert_key (obj,
  562. ucl_object_fromstring (rspamd_action_to_str (
  563. act->action)), "action", 0, false);
  564. ucl_object_insert_key (obj, ucl_object_fromdouble (
  565. act->score), "value", 0, false);
  566. ucl_array_append (top, obj);
  567. }
  568. }
  569. }
  570. rspamd_controller_send_ucl (conn_ent, top);
  571. ucl_object_unref (top);
  572. return 0;
  573. }
  574. /*
  575. * Maps command handler:
  576. * request: /maps
  577. * headers: Password
  578. * reply: json [
  579. * {
  580. * "map": "name",
  581. * "description": "description",
  582. * "editable": true
  583. * },
  584. * {...}
  585. * ]
  586. */
  587. static int
  588. rspamd_controller_handle_maps (struct rspamd_http_connection_entry *conn_ent,
  589. struct rspamd_http_message *msg)
  590. {
  591. struct rspamd_controller_session *session = conn_ent->ud;
  592. GList *cur, *tmp = NULL;
  593. struct rspamd_map *map;
  594. gboolean editable;
  595. ucl_object_t *obj, *top;
  596. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  597. return 0;
  598. }
  599. top = ucl_object_typed_new (UCL_ARRAY);
  600. /* Iterate over all maps */
  601. cur = session->ctx->cfg->maps;
  602. while (cur) {
  603. map = cur->data;
  604. if (map->protocol == MAP_PROTO_FILE) {
  605. if (access (map->uri, R_OK) == 0) {
  606. tmp = g_list_prepend (tmp, map);
  607. }
  608. }
  609. cur = g_list_next (cur);
  610. }
  611. /* Iterate over selected maps */
  612. cur = tmp;
  613. while (cur) {
  614. map = cur->data;
  615. editable = (access (map->uri, W_OK) == 0);
  616. obj = ucl_object_typed_new (UCL_OBJECT);
  617. ucl_object_insert_key (obj, ucl_object_fromint (map->id),
  618. "map", 0, false);
  619. if (map->description) {
  620. ucl_object_insert_key (obj, ucl_object_fromstring (map->description),
  621. "description", 0, false);
  622. }
  623. ucl_object_insert_key (obj, ucl_object_frombool (editable),
  624. "editable", 0, false);
  625. ucl_array_append (top, obj);
  626. cur = g_list_next (cur);
  627. }
  628. if (tmp) {
  629. g_list_free (tmp);
  630. }
  631. rspamd_controller_send_ucl (conn_ent, top);
  632. ucl_object_unref (top);
  633. return 0;
  634. }
  635. /*
  636. * Get map command handler:
  637. * request: /getmap
  638. * headers: Password, Map
  639. * reply: plain-text
  640. */
  641. static int
  642. rspamd_controller_handle_get_map (struct rspamd_http_connection_entry *conn_ent,
  643. struct rspamd_http_message *msg)
  644. {
  645. struct rspamd_controller_session *session = conn_ent->ud;
  646. GList *cur;
  647. struct rspamd_map *map;
  648. const rspamd_ftok_t *idstr;
  649. struct stat st;
  650. gint fd;
  651. gulong id;
  652. gboolean found = FALSE;
  653. struct rspamd_http_message *reply;
  654. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  655. return 0;
  656. }
  657. idstr = rspamd_http_message_find_header (msg, "Map");
  658. if (idstr == NULL) {
  659. msg_info_session ("absent map id");
  660. rspamd_controller_send_error (conn_ent, 400, "400 id header missing");
  661. return 0;
  662. }
  663. if (!rspamd_strtoul (idstr->begin, idstr->len, &id)) {
  664. msg_info_session ("invalid map id");
  665. rspamd_controller_send_error (conn_ent, 400, "400 invalid map id");
  666. return 0;
  667. }
  668. /* Now let's be sure that we have map defined in configuration */
  669. cur = session->ctx->cfg->maps;
  670. while (cur) {
  671. map = cur->data;
  672. if (map->id == id && map->protocol == MAP_PROTO_FILE) {
  673. found = TRUE;
  674. break;
  675. }
  676. cur = g_list_next (cur);
  677. }
  678. if (!found) {
  679. msg_info_session ("map not found");
  680. rspamd_controller_send_error (conn_ent, 404, "404 map not found");
  681. return 0;
  682. }
  683. if (stat (map->uri, &st) == -1 || (fd = open (map->uri, O_RDONLY)) == -1) {
  684. msg_err_session ("cannot open map %s: %s", map->uri, strerror (errno));
  685. rspamd_controller_send_error (conn_ent, 500, "500 map open error");
  686. return 0;
  687. }
  688. reply = rspamd_http_new_message (HTTP_RESPONSE);
  689. reply->date = time (NULL);
  690. reply->code = 200;
  691. reply->body = rspamd_fstring_sized_new (st.st_size);
  692. /* Read the whole buffer */
  693. if (read (fd, reply->body->str, st.st_size) == -1) {
  694. close (fd);
  695. rspamd_http_message_free (reply);
  696. msg_err_session ("cannot read map %s: %s", map->uri, strerror (errno));
  697. rspamd_controller_send_error (conn_ent, 500, "500 map read error");
  698. return 0;
  699. }
  700. reply->body->len = st.st_size;
  701. close (fd);
  702. rspamd_http_connection_reset (conn_ent->conn);
  703. rspamd_http_connection_write_message (conn_ent->conn, reply, NULL,
  704. "text/plain", conn_ent, conn_ent->conn->fd,
  705. conn_ent->rt->ptv, conn_ent->rt->ev_base);
  706. conn_ent->is_reply = TRUE;
  707. return 0;
  708. }
  709. static ucl_object_t *
  710. rspamd_controller_pie_element (enum rspamd_metric_action action,
  711. const char *label, gdouble data)
  712. {
  713. ucl_object_t *res = ucl_object_typed_new (UCL_OBJECT);
  714. const char *colors[METRIC_ACTION_MAX] = {
  715. [METRIC_ACTION_REJECT] = "#FF0000",
  716. [METRIC_ACTION_SOFT_REJECT] = "#cc9966",
  717. [METRIC_ACTION_REWRITE_SUBJECT] = "#ff6600",
  718. [METRIC_ACTION_ADD_HEADER] = "#FFD700",
  719. [METRIC_ACTION_GREYLIST] = "#436EEE",
  720. [METRIC_ACTION_NOACTION] = "#66cc00"
  721. };
  722. ucl_object_insert_key (res, ucl_object_fromstring (colors[action]),
  723. "color", 0, false);
  724. ucl_object_insert_key (res, ucl_object_fromstring (label), "label", 0, false);
  725. ucl_object_insert_key (res, ucl_object_fromdouble (data), "data", 0, false);
  726. ucl_object_insert_key (res, ucl_object_fromdouble (data), "value", 0, false);
  727. return res;
  728. }
  729. /*
  730. * Pie chart command handler:
  731. * request: /pie
  732. * headers: Password
  733. * reply: json [
  734. * { label: "Foo", data: 11 },
  735. * { label: "Bar", data: 20 },
  736. * {...}
  737. * ]
  738. */
  739. static int
  740. rspamd_controller_handle_pie_chart (
  741. struct rspamd_http_connection_entry *conn_ent,
  742. struct rspamd_http_message *msg)
  743. {
  744. struct rspamd_controller_session *session = conn_ent->ud;
  745. struct rspamd_controller_worker_ctx *ctx;
  746. gdouble data[5], total;
  747. ucl_object_t *top;
  748. ctx = session->ctx;
  749. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  750. return 0;
  751. }
  752. top = ucl_object_typed_new (UCL_ARRAY);
  753. total = ctx->srv->stat->messages_scanned;
  754. if (total != 0) {
  755. data[0] = ctx->srv->stat->actions_stat[METRIC_ACTION_NOACTION];
  756. data[1] = ctx->srv->stat->actions_stat[METRIC_ACTION_SOFT_REJECT];
  757. data[2] = (ctx->srv->stat->actions_stat[METRIC_ACTION_ADD_HEADER] +
  758. ctx->srv->stat->actions_stat[METRIC_ACTION_REWRITE_SUBJECT]);
  759. data[3] = ctx->srv->stat->actions_stat[METRIC_ACTION_GREYLIST];
  760. data[4] = ctx->srv->stat->actions_stat[METRIC_ACTION_REJECT];
  761. }
  762. else {
  763. memset (data, 0, sizeof (data));
  764. }
  765. ucl_array_append (top, rspamd_controller_pie_element (
  766. METRIC_ACTION_NOACTION, "Clean", data[0]));
  767. ucl_array_append (top, rspamd_controller_pie_element (
  768. METRIC_ACTION_SOFT_REJECT, "Temporary rejected", data[1]));
  769. ucl_array_append (top, rspamd_controller_pie_element (
  770. METRIC_ACTION_ADD_HEADER, "Probable spam", data[2]));
  771. ucl_array_append (top, rspamd_controller_pie_element (
  772. METRIC_ACTION_GREYLIST, "Greylisted", data[3]));
  773. ucl_array_append (top, rspamd_controller_pie_element (
  774. METRIC_ACTION_REJECT, "Rejected", data[4]));
  775. rspamd_controller_send_ucl (conn_ent, top);
  776. ucl_object_unref (top);
  777. return 0;
  778. }
  779. /*
  780. * Graph command handler:
  781. * request: /graph?type=<hourly|daily|weekly|monthly>
  782. * headers: Password
  783. * reply: json [
  784. * { label: "Foo", data: 11 },
  785. * { label: "Bar", data: 20 },
  786. * {...}
  787. * ]
  788. */
  789. static int
  790. rspamd_controller_handle_graph (
  791. struct rspamd_http_connection_entry *conn_ent,
  792. struct rspamd_http_message *msg)
  793. {
  794. GHashTable *query;
  795. struct rspamd_controller_session *session = conn_ent->ud;
  796. struct rspamd_controller_worker_ctx *ctx;
  797. rspamd_ftok_t srch, *value;
  798. struct rspamd_rrd_query_result *rrd_result;
  799. gulong i, j, ts, start_row, cnt, t;
  800. ucl_object_t *res, *elt[4], *data_elt;
  801. enum {
  802. rra_hourly = 0,
  803. rra_daily,
  804. rra_weekly,
  805. rra_monthly,
  806. rra_invalid
  807. } rra_num = rra_invalid;
  808. ctx = session->ctx;
  809. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  810. return 0;
  811. }
  812. if (ctx->rrd == NULL) {
  813. msg_err_session ("no rrd configured");
  814. rspamd_controller_send_error (conn_ent, 404, "no rrd configured for graphs");
  815. return 0;
  816. }
  817. query = rspamd_http_message_parse_query (msg);
  818. srch.begin = (gchar *)"type";
  819. srch.len = 4;
  820. if (query == NULL || (value = g_hash_table_lookup (query, &srch)) == NULL) {
  821. msg_err_session ("absent graph type query");
  822. rspamd_controller_send_error (conn_ent, 400, "absent graph type");
  823. if (query) {
  824. g_hash_table_unref (query);
  825. }
  826. return 0;
  827. }
  828. if (strncmp (value->begin, "hourly", value->len) == 0) {
  829. rra_num = rra_hourly;
  830. }
  831. else if (strncmp (value->begin, "daily", value->len) == 0) {
  832. rra_num = rra_hourly;
  833. }
  834. else if (strncmp (value->begin, "weekly", value->len) == 0) {
  835. rra_num = rra_hourly;
  836. }
  837. else if (strncmp (value->begin, "monthly", value->len) == 0) {
  838. rra_num = rra_monthly;
  839. }
  840. g_hash_table_unref (query);
  841. if (rra_num == rra_invalid) {
  842. msg_err_session ("invalid graph type query");
  843. rspamd_controller_send_error (conn_ent, 400, "invalid graph type");
  844. return 0;
  845. }
  846. rrd_result = rspamd_rrd_query (ctx->rrd, rra_num);
  847. if (rrd_result == NULL) {
  848. msg_err_session ("cannot query rrd");
  849. rspamd_controller_send_error (conn_ent, 500, "cannot query rrd");
  850. return 0;
  851. }
  852. g_assert (rrd_result->ds_count == G_N_ELEMENTS (elt));
  853. res = ucl_object_typed_new (UCL_ARRAY);
  854. /* How much full updates happened since the last update */
  855. ts = rrd_result->last_update / rrd_result->pdp_per_cdp - rrd_result->rra_rows;
  856. for (i = 0; i < rrd_result->ds_count; i ++) {
  857. elt[i] = ucl_object_typed_new (UCL_ARRAY);
  858. }
  859. start_row = rrd_result->cur_row == rrd_result->rra_rows - 1 ?
  860. 0 : rrd_result->cur_row;
  861. for (i = start_row, cnt = 0; cnt < rrd_result->rra_rows; cnt ++) {
  862. for (j = 0; j < rrd_result->ds_count; j++) {
  863. data_elt = ucl_object_typed_new (UCL_OBJECT);
  864. t = ts * rrd_result->pdp_per_cdp;
  865. ucl_object_insert_key (data_elt,
  866. ucl_object_fromint (t),
  867. "x", 1,
  868. false);
  869. ucl_object_insert_key (data_elt,
  870. ucl_object_fromdouble (rrd_result->data[i *
  871. rrd_result->ds_count + j]),
  872. "y", 1,
  873. false);
  874. ucl_array_append (elt[j], data_elt);
  875. }
  876. i = start_row == 0 ? i + 1 : (i + 1) % start_row;
  877. ts ++;
  878. }
  879. for (i = 0; i < rrd_result->ds_count; i++) {
  880. ucl_array_append (res, elt[i]);
  881. }
  882. rspamd_controller_send_ucl (conn_ent, res);
  883. ucl_object_unref (res);
  884. return 0;
  885. }
  886. /*
  887. * History command handler:
  888. * request: /history
  889. * headers: Password
  890. * reply: json [
  891. * { label: "Foo", data: 11 },
  892. * { label: "Bar", data: 20 },
  893. * {...}
  894. * ]
  895. */
  896. static int
  897. rspamd_controller_handle_history (struct rspamd_http_connection_entry *conn_ent,
  898. struct rspamd_http_message *msg)
  899. {
  900. struct rspamd_controller_session *session = conn_ent->ud;
  901. struct rspamd_controller_worker_ctx *ctx;
  902. struct roll_history_row *row, *copied_rows;
  903. guint i, rows_proc, row_num;
  904. struct tm *tm;
  905. gchar timebuf[32];
  906. ucl_object_t *top, *obj;
  907. ctx = session->ctx;
  908. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  909. return 0;
  910. }
  911. top = ucl_object_typed_new (UCL_ARRAY);
  912. /* Set lock on history */
  913. copied_rows = g_slice_alloc (sizeof (*copied_rows) * ctx->srv->history->nrows);
  914. memcpy (copied_rows, ctx->srv->history->rows,
  915. sizeof (*copied_rows) * ctx->srv->history->nrows);
  916. /* Go through all rows */
  917. row_num = ctx->srv->history->cur_row;
  918. for (i = 0, rows_proc = 0; i < ctx->srv->history->nrows; i++, row_num++) {
  919. if (row_num == ctx->srv->history->nrows) {
  920. row_num = 0;
  921. }
  922. row = &copied_rows[row_num];
  923. /* Get only completed rows */
  924. if (row->completed) {
  925. tm = localtime (&row->tv.tv_sec);
  926. strftime (timebuf, sizeof (timebuf) - 1, "%Y-%m-%d %H:%M:%S", tm);
  927. obj = ucl_object_typed_new (UCL_OBJECT);
  928. ucl_object_insert_key (obj, ucl_object_fromstring (
  929. timebuf), "time", 0, false);
  930. ucl_object_insert_key (obj, ucl_object_fromint (
  931. row->tv.tv_sec), "unix_time", 0, false);
  932. ucl_object_insert_key (obj, ucl_object_fromstring (
  933. row->message_id), "id", 0, false);
  934. ucl_object_insert_key (obj, ucl_object_fromstring (row->from_addr),
  935. "ip", 0, false);
  936. ucl_object_insert_key (obj,
  937. ucl_object_fromstring (rspamd_action_to_str (
  938. row->action)), "action", 0, false);
  939. ucl_object_insert_key (obj, ucl_object_fromdouble (
  940. row->score), "score", 0, false);
  941. ucl_object_insert_key (obj,
  942. ucl_object_fromdouble (
  943. row->required_score), "required_score", 0, false);
  944. ucl_object_insert_key (obj, ucl_object_fromstring (
  945. row->symbols), "symbols", 0, false);
  946. ucl_object_insert_key (obj, ucl_object_fromint (
  947. row->len), "size", 0, false);
  948. ucl_object_insert_key (obj, ucl_object_fromdouble (
  949. row->scan_time), "scan_time", 0, false);
  950. if (row->user[0] != '\0') {
  951. ucl_object_insert_key (obj, ucl_object_fromstring (
  952. row->user), "user", 0, false);
  953. }
  954. if (row->from_addr[0] != '\0') {
  955. ucl_object_insert_key (obj, ucl_object_fromstring (
  956. row->from_addr), "from", 0, false);
  957. }
  958. ucl_array_append (top, obj);
  959. rows_proc++;
  960. }
  961. }
  962. rspamd_controller_send_ucl (conn_ent, top);
  963. ucl_object_unref (top);
  964. return 0;
  965. }
  966. static int
  967. rspamd_controller_handle_history_reset (struct rspamd_http_connection_entry *conn_ent,
  968. struct rspamd_http_message *msg)
  969. {
  970. struct rspamd_controller_session *session = conn_ent->ud;
  971. struct rspamd_controller_worker_ctx *ctx;
  972. struct roll_history_row *row;
  973. guint start_row, i, t;
  974. ctx = session->ctx;
  975. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  976. return 0;
  977. }
  978. /* Clean from start to the current row */
  979. start_row = g_atomic_int_get (&ctx->srv->history->cur_row);
  980. for (i = 0; i < start_row; i ++) {
  981. t = g_atomic_int_get (&ctx->srv->history->cur_row);
  982. /* We somehow come to the race condition */
  983. if (i >= t) {
  984. break;
  985. }
  986. row = &ctx->srv->history->rows[i];
  987. memset (row, 0, sizeof (*row));
  988. }
  989. start_row = g_atomic_int_get (&ctx->srv->history->cur_row);
  990. /* Optimistically set all bytes to zero (might cause race) */
  991. memset (ctx->srv->history->rows,
  992. 0,
  993. sizeof (*row) * (ctx->srv->history->nrows - start_row));
  994. msg_info_session ("<%s> reseted history",
  995. rspamd_inet_address_to_string (session->from_addr));
  996. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  997. return 0;
  998. }
  999. static gboolean
  1000. rspamd_controller_learn_fin_task (void *ud)
  1001. {
  1002. struct rspamd_task *task = ud;
  1003. struct rspamd_controller_session *session;
  1004. struct rspamd_http_connection_entry *conn_ent;
  1005. GError *err = NULL;
  1006. conn_ent = task->fin_arg;
  1007. session = conn_ent->ud;
  1008. if (rspamd_learn_task_spam (session->cl, task, session->is_spam, &err) ==
  1009. RSPAMD_STAT_PROCESS_ERROR) {
  1010. msg_info_session ("cannot learn <%s>: %e", task->message_id, err);
  1011. rspamd_controller_send_error (conn_ent, err->code, err->message);
  1012. return TRUE;
  1013. }
  1014. /* Successful learn */
  1015. msg_info_session ("<%s> learned message as %s: %s",
  1016. rspamd_inet_address_to_string (session->from_addr),
  1017. session->is_spam ? "spam" : "ham",
  1018. task->message_id);
  1019. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1020. return TRUE;
  1021. }
  1022. static void
  1023. rspamd_controller_scan_reply (struct rspamd_task *task)
  1024. {
  1025. struct rspamd_http_message *msg;
  1026. struct rspamd_http_connection_entry *conn_ent = task->fin_arg;
  1027. conn_ent = task->fin_arg;
  1028. msg = rspamd_http_new_message (HTTP_RESPONSE);
  1029. msg->date = time (NULL);
  1030. msg->code = 200;
  1031. rspamd_protocol_http_reply (msg, task);
  1032. rspamd_http_connection_reset (conn_ent->conn);
  1033. rspamd_http_connection_write_message (conn_ent->conn, msg, NULL,
  1034. "application/json", conn_ent, conn_ent->conn->fd, conn_ent->rt->ptv,
  1035. conn_ent->rt->ev_base);
  1036. conn_ent->is_reply = TRUE;
  1037. }
  1038. static gboolean
  1039. rspamd_controller_check_fin_task (void *ud)
  1040. {
  1041. struct rspamd_task *task = ud;
  1042. msg_debug_task ("finish task");
  1043. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  1044. rspamd_controller_scan_reply (task);
  1045. return TRUE;
  1046. }
  1047. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_ALL)) {
  1048. rspamd_controller_scan_reply (task);
  1049. return TRUE;
  1050. }
  1051. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  1052. rspamd_controller_scan_reply (task);
  1053. return TRUE;
  1054. }
  1055. /* One more iteration */
  1056. return FALSE;
  1057. }
  1058. static int
  1059. rspamd_controller_handle_learn_common (
  1060. struct rspamd_http_connection_entry *conn_ent,
  1061. struct rspamd_http_message *msg,
  1062. gboolean is_spam)
  1063. {
  1064. struct rspamd_controller_session *session = conn_ent->ud;
  1065. struct rspamd_controller_worker_ctx *ctx;
  1066. struct rspamd_classifier_config *cl;
  1067. struct rspamd_task *task;
  1068. ctx = session->ctx;
  1069. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1070. return 0;
  1071. }
  1072. if (msg->body == NULL || msg->body->len == 0) {
  1073. msg_err_session ("got zero length body, cannot continue");
  1074. rspamd_controller_send_error (conn_ent,
  1075. 400,
  1076. "Empty body is not permitted");
  1077. return 0;
  1078. }
  1079. /* XXX: now work with only bayes */
  1080. cl = rspamd_config_find_classifier (ctx->cfg, "bayes");
  1081. if (cl == NULL) {
  1082. rspamd_controller_send_error (conn_ent, 400, "Classifier not found");
  1083. return 0;
  1084. }
  1085. task = rspamd_task_new (session->ctx->worker);
  1086. task->resolver = ctx->resolver;
  1087. task->ev_base = ctx->ev_base;
  1088. task->s = rspamd_session_create (session->pool,
  1089. rspamd_controller_learn_fin_task,
  1090. NULL,
  1091. rspamd_task_free_hard,
  1092. task);
  1093. task->fin_arg = conn_ent;
  1094. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
  1095. task->sock = -1;
  1096. session->task = task;
  1097. session->cl = cl;
  1098. if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
  1099. rspamd_controller_send_error (conn_ent, task->err->code, task->err->message);
  1100. return 0;
  1101. }
  1102. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_LEARN)) {
  1103. msg_warn_session ("<%s> message cannot be processed", task->message_id);
  1104. rspamd_controller_send_error (conn_ent, task->err->code, task->err->message);
  1105. return 0;
  1106. }
  1107. session->is_spam = is_spam;
  1108. rspamd_session_pending (task->s);
  1109. return 0;
  1110. }
  1111. /*
  1112. * Learn spam command handler:
  1113. * request: /learnspam
  1114. * headers: Password
  1115. * input: plaintext data
  1116. * reply: json {"success":true} or {"error":"error message"}
  1117. */
  1118. static int
  1119. rspamd_controller_handle_learnspam (
  1120. struct rspamd_http_connection_entry *conn_ent,
  1121. struct rspamd_http_message *msg)
  1122. {
  1123. return rspamd_controller_handle_learn_common (conn_ent, msg, TRUE);
  1124. }
  1125. /*
  1126. * Learn ham command handler:
  1127. * request: /learnham
  1128. * headers: Password
  1129. * input: plaintext data
  1130. * reply: json {"success":true} or {"error":"error message"}
  1131. */
  1132. static int
  1133. rspamd_controller_handle_learnham (
  1134. struct rspamd_http_connection_entry *conn_ent,
  1135. struct rspamd_http_message *msg)
  1136. {
  1137. return rspamd_controller_handle_learn_common (conn_ent, msg, FALSE);
  1138. }
  1139. /*
  1140. * Scan command handler:
  1141. * request: /scan
  1142. * headers: Password
  1143. * input: plaintext data
  1144. * reply: json {scan data} or {"error":"error message"}
  1145. */
  1146. static int
  1147. rspamd_controller_handle_scan (struct rspamd_http_connection_entry *conn_ent,
  1148. struct rspamd_http_message *msg)
  1149. {
  1150. struct rspamd_controller_session *session = conn_ent->ud;
  1151. struct rspamd_controller_worker_ctx *ctx;
  1152. struct rspamd_task *task;
  1153. ctx = session->ctx;
  1154. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  1155. return 0;
  1156. }
  1157. if (msg->body == NULL || msg->body->len == 0) {
  1158. msg_err_session ("got zero length body, cannot continue");
  1159. rspamd_controller_send_error (conn_ent,
  1160. 400,
  1161. "Empty body is not permitted");
  1162. return 0;
  1163. }
  1164. task = rspamd_task_new (session->ctx->worker);
  1165. task->ev_base = session->ctx->ev_base;
  1166. task->resolver = ctx->resolver;
  1167. task->ev_base = ctx->ev_base;
  1168. task->s = rspamd_session_create (session->pool,
  1169. rspamd_controller_check_fin_task,
  1170. NULL,
  1171. rspamd_task_free_hard,
  1172. task);
  1173. task->fin_arg = conn_ent;
  1174. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);
  1175. task->sock = conn_ent->conn->fd;
  1176. task->flags |= RSPAMD_TASK_FLAG_MIME;
  1177. task->resolver = ctx->resolver;
  1178. if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
  1179. rspamd_controller_send_error (conn_ent, task->err->code, task->err->message);
  1180. rspamd_session_destroy (task->s);
  1181. return 0;
  1182. }
  1183. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_ALL)) {
  1184. msg_warn_session ("message cannot be processed for %s", task->message_id);
  1185. rspamd_controller_send_error (conn_ent, task->err->code, task->err->message);
  1186. rspamd_session_destroy (task->s);
  1187. return 0;
  1188. }
  1189. session->task = task;
  1190. rspamd_session_pending (task->s);
  1191. return 0;
  1192. }
  1193. /*
  1194. * Save actions command handler:
  1195. * request: /saveactions
  1196. * headers: Password
  1197. * input: json array [<spam>,<probable spam>,<greylist>]
  1198. * reply: json {"success":true} or {"error":"error message"}
  1199. */
  1200. static int
  1201. rspamd_controller_handle_saveactions (
  1202. struct rspamd_http_connection_entry *conn_ent,
  1203. struct rspamd_http_message *msg)
  1204. {
  1205. struct rspamd_controller_session *session = conn_ent->ud;
  1206. struct ucl_parser *parser;
  1207. struct metric *metric;
  1208. ucl_object_t *obj;
  1209. const ucl_object_t *cur;
  1210. struct rspamd_controller_worker_ctx *ctx;
  1211. const gchar *error;
  1212. gdouble score;
  1213. gint i, added = 0;
  1214. enum rspamd_metric_action act;
  1215. ucl_object_iter_t it = NULL;
  1216. ctx = session->ctx;
  1217. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1218. return 0;
  1219. }
  1220. if (msg->body == NULL || msg->body->len == 0) {
  1221. msg_err_session ("got zero length body, cannot continue");
  1222. rspamd_controller_send_error (conn_ent,
  1223. 400,
  1224. "Empty body is not permitted");
  1225. return 0;
  1226. }
  1227. metric = g_hash_table_lookup (ctx->cfg->metrics, DEFAULT_METRIC);
  1228. if (metric == NULL) {
  1229. msg_err_session ("cannot find default metric");
  1230. rspamd_controller_send_error (conn_ent, 500,
  1231. "Default metric is absent");
  1232. return 0;
  1233. }
  1234. /* Now check for dynamic config */
  1235. if (!ctx->cfg->dynamic_conf) {
  1236. msg_err_session ("dynamic conf has not been defined");
  1237. rspamd_controller_send_error (conn_ent,
  1238. 500,
  1239. "No dynamic_rules setting defined");
  1240. return 0;
  1241. }
  1242. parser = ucl_parser_new (0);
  1243. ucl_parser_add_chunk (parser, msg->body_buf.begin, msg->body_buf.len);
  1244. if ((error = ucl_parser_get_error (parser)) != NULL) {
  1245. msg_err_session ("cannot parse input: %s", error);
  1246. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1247. ucl_parser_free (parser);
  1248. return 0;
  1249. }
  1250. obj = ucl_parser_get_object (parser);
  1251. ucl_parser_free (parser);
  1252. if (obj->type != UCL_ARRAY || obj->len != 3) {
  1253. msg_err_session ("input is not an array of 3 elements");
  1254. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1255. ucl_object_unref (obj);
  1256. return 0;
  1257. }
  1258. for (i = 0; i < 3; i++) {
  1259. cur = ucl_iterate_object (obj, &it, TRUE);
  1260. if (cur == NULL) {
  1261. break;
  1262. }
  1263. switch (i) {
  1264. case 0:
  1265. act = METRIC_ACTION_REJECT;
  1266. break;
  1267. case 1:
  1268. act = METRIC_ACTION_ADD_HEADER;
  1269. break;
  1270. case 2:
  1271. act = METRIC_ACTION_GREYLIST;
  1272. break;
  1273. }
  1274. score = ucl_object_todouble (cur);
  1275. if (metric->actions[act].score != score) {
  1276. add_dynamic_action (ctx->cfg, DEFAULT_METRIC, act, score);
  1277. added ++;
  1278. }
  1279. }
  1280. if (dump_dynamic_config (ctx->cfg)) {
  1281. msg_info_session ("<%s> modified %d actions",
  1282. rspamd_inet_address_to_string (session->from_addr),
  1283. added);
  1284. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1285. }
  1286. else {
  1287. rspamd_controller_send_error (conn_ent, 500, "Save error");
  1288. }
  1289. ucl_object_unref (obj);
  1290. return 0;
  1291. }
  1292. /*
  1293. * Save symbols command handler:
  1294. * request: /savesymbols
  1295. * headers: Password
  1296. * input: json data
  1297. * reply: json {"success":true} or {"error":"error message"}
  1298. */
  1299. static int
  1300. rspamd_controller_handle_savesymbols (
  1301. struct rspamd_http_connection_entry *conn_ent,
  1302. struct rspamd_http_message *msg)
  1303. {
  1304. struct rspamd_controller_session *session = conn_ent->ud;
  1305. struct ucl_parser *parser;
  1306. struct metric *metric;
  1307. ucl_object_t *obj;
  1308. const ucl_object_t *cur, *jname, *jvalue;
  1309. ucl_object_iter_t iter = NULL;
  1310. struct rspamd_controller_worker_ctx *ctx;
  1311. const gchar *error;
  1312. gdouble val;
  1313. struct rspamd_symbol_def *sym;
  1314. int added = 0;
  1315. ctx = session->ctx;
  1316. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1317. return 0;
  1318. }
  1319. if (msg->body == NULL || msg->body->len == 0) {
  1320. msg_err_session ("got zero length body, cannot continue");
  1321. rspamd_controller_send_error (conn_ent,
  1322. 400,
  1323. "Empty body is not permitted");
  1324. return 0;
  1325. }
  1326. metric = g_hash_table_lookup (ctx->cfg->metrics, DEFAULT_METRIC);
  1327. if (metric == NULL) {
  1328. msg_err_session ("cannot find default metric");
  1329. rspamd_controller_send_error (conn_ent, 500,
  1330. "Default metric is absent");
  1331. return 0;
  1332. }
  1333. /* Now check for dynamic config */
  1334. if (!ctx->cfg->dynamic_conf) {
  1335. msg_err_session ("dynamic conf has not been defined");
  1336. rspamd_controller_send_error (conn_ent,
  1337. 500,
  1338. "No dynamic_rules setting defined");
  1339. return 0;
  1340. }
  1341. parser = ucl_parser_new (0);
  1342. ucl_parser_add_chunk (parser, msg->body_buf.begin, msg->body_buf.len);
  1343. if ((error = ucl_parser_get_error (parser)) != NULL) {
  1344. msg_err_session ("cannot parse input: %s", error);
  1345. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1346. ucl_parser_free (parser);
  1347. return 0;
  1348. }
  1349. obj = ucl_parser_get_object (parser);
  1350. ucl_parser_free (parser);
  1351. if (obj->type != UCL_ARRAY) {
  1352. msg_err_session ("input is not an array");
  1353. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1354. ucl_object_unref (obj);
  1355. return 0;
  1356. }
  1357. while ((cur = ucl_iterate_object (obj, &iter, true))) {
  1358. if (cur->type != UCL_OBJECT) {
  1359. msg_err_session ("json array data error");
  1360. rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
  1361. ucl_object_unref (obj);
  1362. return 0;
  1363. }
  1364. jname = ucl_object_find_key (cur, "name");
  1365. jvalue = ucl_object_find_key (cur, "value");
  1366. val = ucl_object_todouble (jvalue);
  1367. sym =
  1368. g_hash_table_lookup (metric->symbols, ucl_object_tostring (jname));
  1369. if (sym && fabs (*sym->weight_ptr - val) > 0.01) {
  1370. if (!add_dynamic_symbol (ctx->cfg, DEFAULT_METRIC,
  1371. ucl_object_tostring (jname), val)) {
  1372. msg_err_session ("add symbol failed for %s",
  1373. ucl_object_tostring (jname));
  1374. rspamd_controller_send_error (conn_ent, 506,
  1375. "Add symbol failed");
  1376. ucl_object_unref (obj);
  1377. return 0;
  1378. }
  1379. added ++;
  1380. }
  1381. }
  1382. if (added > 0) {
  1383. if (dump_dynamic_config (ctx->cfg)) {
  1384. msg_info_session ("<%s> modified %d symbols",
  1385. rspamd_inet_address_to_string (session->from_addr),
  1386. added);
  1387. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1388. }
  1389. else {
  1390. rspamd_controller_send_error (conn_ent, 500, "Save error");
  1391. }
  1392. }
  1393. else {
  1394. msg_err_session ("no symbols to save");
  1395. rspamd_controller_send_error (conn_ent, 404, "No symbols to save");
  1396. }
  1397. ucl_object_unref (obj);
  1398. return 0;
  1399. }
  1400. /*
  1401. * Save map command handler:
  1402. * request: /savemap
  1403. * headers: Password, Map
  1404. * input: plaintext data
  1405. * reply: json {"success":true} or {"error":"error message"}
  1406. */
  1407. static int
  1408. rspamd_controller_handle_savemap (struct rspamd_http_connection_entry *conn_ent,
  1409. struct rspamd_http_message *msg)
  1410. {
  1411. struct rspamd_controller_session *session = conn_ent->ud;
  1412. GList *cur;
  1413. struct rspamd_map *map;
  1414. struct rspamd_controller_worker_ctx *ctx;
  1415. const rspamd_ftok_t *idstr;
  1416. gulong id;
  1417. gboolean found = FALSE;
  1418. gint fd;
  1419. ctx = session->ctx;
  1420. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1421. return 0;
  1422. }
  1423. if (msg->body == NULL || msg->body->len == 0) {
  1424. msg_err_session ("got zero length body, cannot continue");
  1425. rspamd_controller_send_error (conn_ent,
  1426. 400,
  1427. "Empty body is not permitted");
  1428. return 0;
  1429. }
  1430. idstr = rspamd_http_message_find_header (msg, "Map");
  1431. if (idstr == NULL) {
  1432. msg_info_session ("absent map id");
  1433. rspamd_controller_send_error (conn_ent, 400, "Map id not specified");
  1434. return 0;
  1435. }
  1436. if (!rspamd_strtoul (idstr->begin, idstr->len, &id)) {
  1437. msg_info_session ("invalid map id: %T", idstr);
  1438. rspamd_controller_send_error (conn_ent, 400, "Map id is invalid");
  1439. return 0;
  1440. }
  1441. /* Now let's be sure that we have map defined in configuration */
  1442. cur = ctx->cfg->maps;
  1443. while (cur) {
  1444. map = cur->data;
  1445. if (map->id == id && map->protocol == MAP_PROTO_FILE) {
  1446. found = TRUE;
  1447. break;
  1448. }
  1449. cur = g_list_next (cur);
  1450. }
  1451. if (!found) {
  1452. msg_info_session ("map not found: %d", id);
  1453. rspamd_controller_send_error (conn_ent, 404, "Map id not found");
  1454. return 0;
  1455. }
  1456. if (g_atomic_int_get (map->locked)) {
  1457. msg_info_session ("map locked: %s", map->uri);
  1458. rspamd_controller_send_error (conn_ent, 404, "Map is locked");
  1459. return 0;
  1460. }
  1461. /* Set lock */
  1462. g_atomic_int_set (map->locked, 1);
  1463. fd = open (map->uri, O_WRONLY | O_TRUNC);
  1464. if (fd == -1) {
  1465. g_atomic_int_set (map->locked, 0);
  1466. msg_info_session ("map %s open error: %s", map->uri, strerror (errno));
  1467. rspamd_controller_send_error (conn_ent, 404, "Map id not found");
  1468. return 0;
  1469. }
  1470. if (write (fd, msg->body_buf.begin, msg->body_buf.len) == -1) {
  1471. msg_info_session ("map %s write error: %s", map->uri, strerror (errno));
  1472. close (fd);
  1473. g_atomic_int_set (map->locked, 0);
  1474. rspamd_controller_send_error (conn_ent, 500, "Map write error");
  1475. return 0;
  1476. }
  1477. msg_info_session ("<%s>, map %s saved",
  1478. rspamd_inet_address_to_string (session->from_addr),
  1479. map->uri);
  1480. /* Close and unlock */
  1481. close (fd);
  1482. g_atomic_int_set (map->locked, 0);
  1483. rspamd_controller_send_string (conn_ent, "{\"success\":true}");
  1484. return 0;
  1485. }
  1486. struct rspamd_stat_cbdata {
  1487. struct rspamd_http_connection_entry *conn_ent;
  1488. ucl_object_t *top;
  1489. ucl_object_t *stat;
  1490. struct rspamd_task *task;
  1491. guint64 learned;
  1492. };
  1493. static gboolean
  1494. rspamd_controller_stat_fin_task (void *ud)
  1495. {
  1496. struct rspamd_stat_cbdata *cbdata = ud;
  1497. struct rspamd_http_connection_entry *conn_ent;
  1498. ucl_object_t *top;
  1499. conn_ent = cbdata->conn_ent;
  1500. top = cbdata->top;
  1501. ucl_object_insert_key (top,
  1502. ucl_object_fromint (cbdata->learned), "total_learns", 0, false);
  1503. if (cbdata->stat) {
  1504. ucl_object_insert_key (top, cbdata->stat, "statfiles", 0, false);
  1505. }
  1506. rspamd_controller_send_ucl (conn_ent, top);
  1507. return TRUE;
  1508. }
  1509. static void
  1510. rspamd_controller_stat_cleanup_task (void *ud)
  1511. {
  1512. struct rspamd_stat_cbdata *cbdata = ud;
  1513. rspamd_task_free_hard (cbdata->task);
  1514. ucl_object_unref (cbdata->top);
  1515. }
  1516. /*
  1517. * Stat command handler:
  1518. * request: /stat (/resetstat)
  1519. * headers: Password
  1520. * reply: json data
  1521. */
  1522. static int
  1523. rspamd_controller_handle_stat_common (
  1524. struct rspamd_http_connection_entry *conn_ent,
  1525. struct rspamd_http_message *msg,
  1526. gboolean do_reset)
  1527. {
  1528. struct rspamd_controller_session *session = conn_ent->ud;
  1529. ucl_object_t *top, *sub;
  1530. gint i;
  1531. guint64 spam = 0, ham = 0;
  1532. rspamd_mempool_stat_t mem_st;
  1533. struct rspamd_stat *stat, stat_copy;
  1534. struct rspamd_controller_worker_ctx *ctx;
  1535. struct rspamd_task *task;
  1536. struct rspamd_stat_cbdata *cbdata;
  1537. rspamd_mempool_stat (&mem_st);
  1538. memcpy (&stat_copy, session->ctx->worker->srv->stat, sizeof (stat_copy));
  1539. stat = &stat_copy;
  1540. task = rspamd_task_new (session->ctx->worker);
  1541. ctx = session->ctx;
  1542. task->resolver = ctx->resolver;
  1543. task->ev_base = ctx->ev_base;
  1544. cbdata = rspamd_mempool_alloc0 (session->pool, sizeof (*cbdata));
  1545. cbdata->conn_ent = conn_ent;
  1546. cbdata->task = task;
  1547. top = ucl_object_typed_new (UCL_OBJECT);
  1548. cbdata->top = top;
  1549. task->s = rspamd_session_create (session->pool,
  1550. rspamd_controller_stat_fin_task,
  1551. NULL,
  1552. rspamd_controller_stat_cleanup_task,
  1553. cbdata);
  1554. task->fin_arg = cbdata;
  1555. task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
  1556. task->sock = conn_ent->conn->fd;
  1557. ucl_object_insert_key (top, ucl_object_fromint (
  1558. stat->messages_scanned), "scanned", 0, false);
  1559. ucl_object_insert_key (top, ucl_object_fromint (
  1560. stat->messages_learned), "learned", 0, false);
  1561. if (stat->messages_scanned > 0) {
  1562. sub = ucl_object_typed_new (UCL_OBJECT);
  1563. for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
  1564. ucl_object_insert_key (sub,
  1565. ucl_object_fromint (stat->actions_stat[i]),
  1566. rspamd_action_to_str (i), 0, false);
  1567. if (i < METRIC_ACTION_GREYLIST) {
  1568. spam += stat->actions_stat[i];
  1569. }
  1570. else {
  1571. ham += stat->actions_stat[i];
  1572. }
  1573. if (do_reset) {
  1574. session->ctx->worker->srv->stat->actions_stat[i] = 0;
  1575. }
  1576. }
  1577. ucl_object_insert_key (top, sub, "actions", 0, false);
  1578. }
  1579. ucl_object_insert_key (top, ucl_object_fromint (
  1580. spam), "spam_count", 0, false);
  1581. ucl_object_insert_key (top, ucl_object_fromint (
  1582. ham), "ham_count", 0, false);
  1583. ucl_object_insert_key (top,
  1584. ucl_object_fromint (stat->connections_count), "connections", 0, false);
  1585. ucl_object_insert_key (top,
  1586. ucl_object_fromint (stat->control_connections_count),
  1587. "control_connections", 0, false);
  1588. ucl_object_insert_key (top,
  1589. ucl_object_fromint (mem_st.pools_allocated), "pools_allocated", 0,
  1590. false);
  1591. ucl_object_insert_key (top,
  1592. ucl_object_fromint (mem_st.pools_freed), "pools_freed", 0, false);
  1593. ucl_object_insert_key (top,
  1594. ucl_object_fromint (mem_st.bytes_allocated), "bytes_allocated", 0,
  1595. false);
  1596. ucl_object_insert_key (top,
  1597. ucl_object_fromint (
  1598. mem_st.chunks_allocated), "chunks_allocated", 0, false);
  1599. ucl_object_insert_key (top,
  1600. ucl_object_fromint (mem_st.shared_chunks_allocated),
  1601. "shared_chunks_allocated", 0, false);
  1602. ucl_object_insert_key (top,
  1603. ucl_object_fromint (mem_st.chunks_freed), "chunks_freed", 0, false);
  1604. ucl_object_insert_key (top,
  1605. ucl_object_fromint (
  1606. mem_st.oversized_chunks), "chunks_oversized", 0, false);
  1607. ucl_object_insert_key (top,
  1608. ucl_object_fromint (stat->fuzzy_hashes), "fuzzy_stored", 0, false);
  1609. ucl_object_insert_key (top,
  1610. ucl_object_fromint (
  1611. stat->fuzzy_hashes_expired), "fuzzy_expired", 0, false);
  1612. /* Fuzzy epoch statistics */
  1613. sub = ucl_object_typed_new (UCL_ARRAY);
  1614. for (i = RSPAMD_FUZZY_EPOCH6; i < RSPAMD_FUZZY_EPOCH_MAX; i ++) {
  1615. ucl_array_append (sub, ucl_object_fromint (stat->fuzzy_hashes_checked[i]));
  1616. }
  1617. ucl_object_insert_key (top, sub, "fuzzy_checked", 0, false);
  1618. sub = ucl_object_typed_new (UCL_ARRAY);
  1619. for (i = RSPAMD_FUZZY_EPOCH6; i < RSPAMD_FUZZY_EPOCH_MAX; i ++) {
  1620. ucl_array_append (sub, ucl_object_fromint (stat->fuzzy_hashes_found[i]));
  1621. }
  1622. ucl_object_insert_key (top, sub, "fuzzy_found", 0, false);
  1623. if (do_reset) {
  1624. session->ctx->srv->stat->messages_scanned = 0;
  1625. session->ctx->srv->stat->messages_learned = 0;
  1626. session->ctx->srv->stat->connections_count = 0;
  1627. session->ctx->srv->stat->control_connections_count = 0;
  1628. memset (stat->fuzzy_hashes_checked, 0,
  1629. sizeof (stat->fuzzy_hashes_checked));
  1630. memset (stat->fuzzy_hashes_found, 0,
  1631. sizeof (stat->fuzzy_hashes_found));
  1632. rspamd_mempool_stat_reset ();
  1633. }
  1634. /* Now write statistics for each statfile */
  1635. rspamd_stat_statistics (task, session->ctx->cfg, &cbdata->learned,
  1636. &cbdata->stat);
  1637. session->task = task;
  1638. rspamd_session_pending (task->s);
  1639. return 0;
  1640. }
  1641. static int
  1642. rspamd_controller_handle_stat (struct rspamd_http_connection_entry *conn_ent,
  1643. struct rspamd_http_message *msg)
  1644. {
  1645. struct rspamd_controller_session *session = conn_ent->ud;
  1646. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  1647. return 0;
  1648. }
  1649. return rspamd_controller_handle_stat_common (conn_ent, msg, FALSE);
  1650. }
  1651. static int
  1652. rspamd_controller_handle_statreset (
  1653. struct rspamd_http_connection_entry *conn_ent,
  1654. struct rspamd_http_message *msg)
  1655. {
  1656. struct rspamd_controller_session *session = conn_ent->ud;
  1657. if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
  1658. return 0;
  1659. }
  1660. msg_info_session ("<%s> reset stat",
  1661. rspamd_inet_address_to_string (session->from_addr));
  1662. return rspamd_controller_handle_stat_common (conn_ent, msg, TRUE);
  1663. }
  1664. /*
  1665. * Counters command handler:
  1666. * request: /counters
  1667. * headers: Password
  1668. * reply: json array of all counters
  1669. */
  1670. static int
  1671. rspamd_controller_handle_counters (
  1672. struct rspamd_http_connection_entry *conn_ent,
  1673. struct rspamd_http_message *msg)
  1674. {
  1675. struct rspamd_controller_session *session = conn_ent->ud;
  1676. ucl_object_t *top;
  1677. struct symbols_cache *cache;
  1678. if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
  1679. return 0;
  1680. }
  1681. cache = session->ctx->cfg->cache;
  1682. if (cache != NULL) {
  1683. top = rspamd_symbols_cache_counters (cache);
  1684. rspamd_controller_send_ucl (conn_ent, top);
  1685. ucl_object_unref (top);
  1686. }
  1687. else {
  1688. rspamd_controller_send_error (conn_ent, 500, "Invalid cache");
  1689. }
  1690. return 0;
  1691. }
  1692. static int
  1693. rspamd_controller_handle_custom (struct rspamd_http_connection_entry *conn_ent,
  1694. struct rspamd_http_message *msg)
  1695. {
  1696. struct rspamd_controller_session *session = conn_ent->ud;
  1697. struct rspamd_custom_controller_command *cmd;
  1698. gchar *url_str;
  1699. url_str = rspamd_fstring_cstr (msg->url);
  1700. cmd = g_hash_table_lookup (session->ctx->custom_commands, url_str);
  1701. g_free (url_str);
  1702. if (cmd == NULL || cmd->handler == NULL) {
  1703. msg_err_session ("custom command %V has not been found", msg->url);
  1704. rspamd_controller_send_error (conn_ent, 404, "No command associated");
  1705. return 0;
  1706. }
  1707. if (!rspamd_controller_check_password (conn_ent, session, msg,
  1708. cmd->privilleged)) {
  1709. return 0;
  1710. }
  1711. if (cmd->require_message && (msg->body == NULL || msg->body->len == 0)) {
  1712. msg_err_session ("got zero length body, cannot continue");
  1713. rspamd_controller_send_error (conn_ent,
  1714. 400,
  1715. "Empty body is not permitted");
  1716. return 0;
  1717. }
  1718. return cmd->handler (conn_ent, msg, cmd->ctx);
  1719. }
  1720. static void
  1721. rspamd_controller_error_handler (struct rspamd_http_connection_entry *conn_ent,
  1722. GError *err)
  1723. {
  1724. struct rspamd_controller_session *session = conn_ent->ud;
  1725. msg_err_session ("http error occurred: %s", err->message);
  1726. }
  1727. static void
  1728. rspamd_controller_finish_handler (struct rspamd_http_connection_entry *conn_ent)
  1729. {
  1730. struct rspamd_controller_session *session = conn_ent->ud;
  1731. session->ctx->worker->srv->stat->control_connections_count++;
  1732. msg_debug_session ("destroy session %p", session);
  1733. if (session->task != NULL) {
  1734. rspamd_session_destroy (session->task->s);
  1735. }
  1736. if (session->pool) {
  1737. rspamd_mempool_delete (session->pool);
  1738. }
  1739. session->wrk->nconns --;
  1740. rspamd_inet_address_destroy (session->from_addr);
  1741. g_slice_free1 (sizeof (struct rspamd_controller_session), session);
  1742. }
  1743. static void
  1744. rspamd_controller_accept_socket (gint fd, short what, void *arg)
  1745. {
  1746. struct rspamd_worker *worker = (struct rspamd_worker *) arg;
  1747. struct rspamd_controller_worker_ctx *ctx;
  1748. struct rspamd_controller_session *session;
  1749. rspamd_inet_addr_t *addr;
  1750. gint nfd;
  1751. ctx = worker->ctx;
  1752. if ((nfd =
  1753. rspamd_accept_from_socket (fd, &addr)) == -1) {
  1754. msg_warn_ctx ("accept failed: %s", strerror (errno));
  1755. return;
  1756. }
  1757. /* Check for EAGAIN */
  1758. if (nfd == 0) {
  1759. return;
  1760. }
  1761. session = g_slice_alloc0 (sizeof (struct rspamd_controller_session));
  1762. session->pool = rspamd_mempool_new (rspamd_mempool_suggest_size (),
  1763. "csession");
  1764. session->ctx = ctx;
  1765. session->from_addr = addr;
  1766. session->wrk = worker;
  1767. worker->nconns ++;
  1768. rspamd_http_router_handle_socket (ctx->http, nfd, session);
  1769. }
  1770. static void
  1771. rspamd_controller_rrd_update (gint fd, short what, void *arg)
  1772. {
  1773. struct rspamd_controller_worker_ctx *ctx = arg;
  1774. struct rspamd_stat *stat;
  1775. GArray ar;
  1776. gdouble points[4];
  1777. GError *err = NULL;
  1778. guint i, j;
  1779. gdouble val;
  1780. g_assert (ctx->rrd != NULL);
  1781. stat = ctx->srv->stat;
  1782. for (i = METRIC_ACTION_REJECT, j = 0;
  1783. i <= METRIC_ACTION_NOACTION && j < G_N_ELEMENTS (points);
  1784. i++) {
  1785. switch (i) {
  1786. case METRIC_ACTION_SOFT_REJECT:
  1787. break;
  1788. case METRIC_ACTION_REWRITE_SUBJECT:
  1789. val = stat->actions_stat[i];
  1790. break;
  1791. case METRIC_ACTION_ADD_HEADER:
  1792. val += stat->actions_stat[i];
  1793. points[j++] = val;
  1794. break;
  1795. default:
  1796. val = stat->actions_stat[i];
  1797. points[j++] = val;
  1798. }
  1799. }
  1800. ar.data = (gchar *)points;
  1801. ar.len = sizeof (points);
  1802. if (!rspamd_rrd_add_record (ctx->rrd, &ar, rspamd_get_calendar_ticks (),
  1803. &err)) {
  1804. msg_err_ctx ("cannot update rrd file: %e", err);
  1805. g_error_free (err);
  1806. }
  1807. /* Plan new event */
  1808. event_del (ctx->rrd_event);
  1809. evtimer_add (ctx->rrd_event, &rrd_update_time);
  1810. }
  1811. static void
  1812. rspamd_controller_load_saved_stats (struct rspamd_controller_worker_ctx *ctx)
  1813. {
  1814. struct ucl_parser *parser;
  1815. ucl_object_t *obj;
  1816. const ucl_object_t *elt, *subelt;
  1817. struct rspamd_stat *stat, stat_copy;
  1818. gint i;
  1819. g_assert (ctx->saved_stats_path != NULL);
  1820. if (access (ctx->saved_stats_path, R_OK) == -1) {
  1821. msg_err_ctx ("cannot load controller stats from %s: %s",
  1822. ctx->saved_stats_path, strerror (errno));
  1823. return;
  1824. }
  1825. parser = ucl_parser_new (0);
  1826. if (!ucl_parser_add_file (parser, ctx->saved_stats_path)) {
  1827. msg_err_ctx ("cannot parse controller stats from %s: %s",
  1828. ctx->saved_stats_path, ucl_parser_get_error (parser));
  1829. ucl_parser_free (parser);
  1830. return;
  1831. }
  1832. obj = ucl_parser_get_object (parser);
  1833. ucl_parser_free (parser);
  1834. stat = ctx->srv->stat;
  1835. memcpy (&stat_copy, stat, sizeof (stat_copy));
  1836. elt = ucl_object_find_key (obj, "scanned");
  1837. if (elt != NULL && ucl_object_type (elt) == UCL_INT) {
  1838. stat_copy.messages_scanned = ucl_object_toint (elt);
  1839. }
  1840. elt = ucl_object_find_key (obj, "learned");
  1841. if (elt != NULL && ucl_object_type (elt) == UCL_INT) {
  1842. stat_copy.messages_learned = ucl_object_toint (elt);
  1843. }
  1844. elt = ucl_object_find_key (obj, "actions");
  1845. if (elt != NULL) {
  1846. for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
  1847. subelt = ucl_object_find_key (elt, rspamd_action_to_str (i));
  1848. if (subelt && ucl_object_type (subelt) == UCL_INT) {
  1849. stat_copy.actions_stat[i] = ucl_object_toint (subelt);
  1850. }
  1851. }
  1852. }
  1853. elt = ucl_object_find_key (obj, "connections_count");
  1854. if (elt != NULL && ucl_object_type (elt) == UCL_INT) {
  1855. stat_copy.connections_count = ucl_object_toint (elt);
  1856. }
  1857. elt = ucl_object_find_key (obj, "control_connections_count");
  1858. if (elt != NULL && ucl_object_type (elt) == UCL_INT) {
  1859. stat_copy.control_connections_count = ucl_object_toint (elt);
  1860. }
  1861. elt = ucl_object_find_key (obj, "fuzzy_stored");
  1862. if (elt != NULL && ucl_object_type (elt) == UCL_INT) {
  1863. stat_copy.fuzzy_hashes = ucl_object_toint (elt);
  1864. }
  1865. elt = ucl_object_find_key (obj, "fuzzy_expired");
  1866. if (elt != NULL && ucl_object_type (elt) == UCL_INT) {
  1867. stat_copy.fuzzy_hashes_expired = ucl_object_toint (elt);
  1868. }
  1869. elt = ucl_object_find_key (obj, "fuzzy_checked");
  1870. if (elt && ucl_object_type (elt) == UCL_ARRAY) {
  1871. for (i = 0; i < RSPAMD_FUZZY_EPOCH_MAX; i++) {
  1872. subelt = ucl_array_find_index (elt, i);
  1873. if (subelt && ucl_object_type (subelt) == UCL_INT) {
  1874. stat_copy.fuzzy_hashes_checked[i] = ucl_object_toint (subelt);
  1875. }
  1876. }
  1877. }
  1878. elt = ucl_object_find_key (obj, "fuzzy_found");
  1879. if (elt && ucl_object_type (elt) == UCL_ARRAY) {
  1880. for (i = 0; i < RSPAMD_FUZZY_EPOCH_MAX; i++) {
  1881. subelt = ucl_array_find_index (elt, i);
  1882. if (subelt && ucl_object_type (subelt) == UCL_INT) {
  1883. stat_copy.fuzzy_hashes_found[i] = ucl_object_toint (subelt);
  1884. }
  1885. }
  1886. }
  1887. ucl_object_unref (obj);
  1888. memcpy (stat, &stat_copy, sizeof (stat_copy));
  1889. }
  1890. static void
  1891. rspamd_controller_store_saved_stats (struct rspamd_controller_worker_ctx *ctx)
  1892. {
  1893. struct rspamd_stat *stat;
  1894. ucl_object_t *top, *sub;
  1895. gint i, fd;
  1896. g_assert (ctx->saved_stats_path != NULL);
  1897. fd = open (ctx->saved_stats_path, O_WRONLY|O_CREAT|O_TRUNC, 00644);
  1898. if (fd == -1) {
  1899. msg_err_ctx ("cannot load controller stats from %s: %s",
  1900. ctx->saved_stats_path, strerror (errno));
  1901. return;
  1902. }
  1903. if (rspamd_file_lock (fd, FALSE) == -1) {
  1904. msg_err_ctx ("cannot load controller stats from %s: %s",
  1905. ctx->saved_stats_path, strerror (errno));
  1906. close (fd);
  1907. return;
  1908. }
  1909. stat = ctx->srv->stat;
  1910. top = ucl_object_typed_new (UCL_OBJECT);
  1911. ucl_object_insert_key (top, ucl_object_fromint (
  1912. stat->messages_scanned), "scanned", 0, false);
  1913. ucl_object_insert_key (top, ucl_object_fromint (
  1914. stat->messages_learned), "learned", 0, false);
  1915. if (stat->messages_scanned > 0) {
  1916. sub = ucl_object_typed_new (UCL_OBJECT);
  1917. for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
  1918. ucl_object_insert_key (sub,
  1919. ucl_object_fromint (stat->actions_stat[i]),
  1920. rspamd_action_to_str (i), 0, false);
  1921. }
  1922. ucl_object_insert_key (top, sub, "actions", 0, false);
  1923. }
  1924. ucl_object_insert_key (top,
  1925. ucl_object_fromint (stat->connections_count), "connections", 0, false);
  1926. ucl_object_insert_key (top,
  1927. ucl_object_fromint (stat->control_connections_count),
  1928. "control_connections", 0, false);
  1929. ucl_object_insert_key (top,
  1930. ucl_object_fromint (stat->fuzzy_hashes), "fuzzy_stored", 0, false);
  1931. ucl_object_insert_key (top,
  1932. ucl_object_fromint (
  1933. stat->fuzzy_hashes_expired), "fuzzy_expired", 0, false);
  1934. /* Fuzzy epoch statistics */
  1935. sub = ucl_object_typed_new (UCL_ARRAY);
  1936. for (i = RSPAMD_FUZZY_EPOCH6; i < RSPAMD_FUZZY_EPOCH_MAX; i ++) {
  1937. ucl_array_append (sub, ucl_object_fromint (stat->fuzzy_hashes_checked[i]));
  1938. }
  1939. ucl_object_insert_key (top, sub, "fuzzy_checked", 0, false);
  1940. sub = ucl_object_typed_new (UCL_ARRAY);
  1941. for (i = RSPAMD_FUZZY_EPOCH6; i < RSPAMD_FUZZY_EPOCH_MAX; i ++) {
  1942. ucl_array_append (sub, ucl_object_fromint (stat->fuzzy_hashes_found[i]));
  1943. }
  1944. ucl_object_insert_key (top, sub, "fuzzy_found", 0, false);
  1945. ucl_object_emit_full (top, UCL_EMIT_JSON_COMPACT,
  1946. ucl_object_emit_fd_funcs (fd));
  1947. rspamd_file_unlock (fd, FALSE);
  1948. close (fd);
  1949. }
  1950. static void
  1951. rspamd_controller_password_sane (struct rspamd_controller_worker_ctx *ctx,
  1952. const gchar *password, const gchar *type)
  1953. {
  1954. const struct rspamd_controller_pbkdf *pbkdf = &pbkdf_list[0];
  1955. if (password == NULL) {
  1956. msg_warn_ctx ("%s is not set, so you should filter controller "
  1957. "availability "
  1958. "by using of firewall or `secure_ip` option", type);
  1959. return;
  1960. }
  1961. g_assert (pbkdf != NULL);
  1962. if (!rspamd_is_encrypted_password (password, NULL)) {
  1963. /* Suggest encryption to a user */
  1964. msg_warn_ctx ("your %s is not encrypted, we strongly "
  1965. "recommend to replace it with the encrypted one", type);
  1966. }
  1967. }
  1968. gpointer
  1969. init_controller_worker (struct rspamd_config *cfg)
  1970. {
  1971. struct rspamd_controller_worker_ctx *ctx;
  1972. GQuark type;
  1973. type = g_quark_try_string ("controller");
  1974. ctx = g_malloc0 (sizeof (struct rspamd_controller_worker_ctx));
  1975. ctx->timeout = DEFAULT_WORKER_IO_TIMEOUT;
  1976. rspamd_rcl_register_worker_option (cfg, type, "password",
  1977. rspamd_rcl_parse_struct_string, ctx,
  1978. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, password), 0);
  1979. rspamd_rcl_register_worker_option (cfg, type, "enable_password",
  1980. rspamd_rcl_parse_struct_string, ctx,
  1981. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, enable_password), 0);
  1982. rspamd_rcl_register_worker_option (cfg, type, "ssl",
  1983. rspamd_rcl_parse_struct_boolean, ctx,
  1984. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, use_ssl), 0);
  1985. rspamd_rcl_register_worker_option (cfg, type, "ssl_cert",
  1986. rspamd_rcl_parse_struct_string, ctx,
  1987. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, ssl_cert), 0);
  1988. rspamd_rcl_register_worker_option (cfg, type, "ssl_key",
  1989. rspamd_rcl_parse_struct_string, ctx,
  1990. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, ssl_key), 0);
  1991. rspamd_rcl_register_worker_option (cfg, type, "timeout",
  1992. rspamd_rcl_parse_struct_time, ctx,
  1993. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  1994. timeout), RSPAMD_CL_FLAG_TIME_INTEGER);
  1995. rspamd_rcl_register_worker_option (cfg, type, "secure_ip",
  1996. rspamd_rcl_parse_struct_string_list, ctx,
  1997. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, secure_ip), 0);
  1998. rspamd_rcl_register_worker_option (cfg, type, "static_dir",
  1999. rspamd_rcl_parse_struct_string, ctx,
  2000. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2001. static_files_dir), 0);
  2002. rspamd_rcl_register_worker_option (cfg, type, "keypair",
  2003. rspamd_rcl_parse_struct_keypair, ctx,
  2004. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
  2005. key), 0);
  2006. rspamd_rcl_register_worker_option (cfg, type, "stats_path",
  2007. rspamd_rcl_parse_struct_string, ctx,
  2008. G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, saved_stats_path), 0);
  2009. return ctx;
  2010. }
  2011. /*
  2012. * Start worker process
  2013. */
  2014. void
  2015. start_controller_worker (struct rspamd_worker *worker)
  2016. {
  2017. struct rspamd_controller_worker_ctx *ctx = worker->ctx;
  2018. GList *cur;
  2019. struct module_ctx *mctx;
  2020. GHashTableIter iter;
  2021. gpointer key, value;
  2022. struct rspamd_keypair_cache *cache;
  2023. gchar *secure_ip;
  2024. gpointer m;
  2025. ctx->ev_base = rspamd_prepare_worker (worker,
  2026. "controller",
  2027. rspamd_controller_accept_socket);
  2028. msec_to_tv (ctx->timeout, &ctx->io_tv);
  2029. ctx->start_time = time (NULL);
  2030. ctx->worker = worker;
  2031. ctx->cfg = worker->srv->cfg;
  2032. ctx->srv = worker->srv;
  2033. ctx->custom_commands = g_hash_table_new (rspamd_strcase_hash,
  2034. rspamd_strcase_equal);
  2035. if (ctx->secure_ip != NULL) {
  2036. cur = ctx->secure_ip;
  2037. while (cur) {
  2038. secure_ip = cur->data;
  2039. /* Try map syntax */
  2040. if (!rspamd_map_add (worker->srv->cfg, secure_ip,
  2041. "Allow webui access from the specified IP",
  2042. rspamd_radix_read, rspamd_radix_fin, (void **)&ctx->secure_map)) {
  2043. /* Fallback to the plain IP */
  2044. if (!radix_add_generic_iplist (secure_ip,
  2045. &ctx->secure_map)) {
  2046. msg_warn_ctx ("cannot load or parse ip list from '%s'",
  2047. secure_ip);
  2048. }
  2049. }
  2050. cur = g_list_next (cur);
  2051. }
  2052. }
  2053. if (ctx->saved_stats_path == NULL) {
  2054. /* Assume default path */
  2055. ctx->saved_stats_path = rspamd_mempool_strdup (worker->srv->cfg->cfg_pool,
  2056. DEFAULT_STATS_PATH);
  2057. }
  2058. rspamd_controller_load_saved_stats (ctx);
  2059. /* RRD collector */
  2060. if (ctx->cfg->rrd_file && worker->index == 0) {
  2061. ctx->rrd = rspamd_rrd_file_default (ctx->cfg->rrd_file, NULL);
  2062. if (ctx->rrd) {
  2063. ctx->rrd_event = g_slice_alloc0 (sizeof (*ctx->rrd_event));
  2064. evtimer_set (ctx->rrd_event, rspamd_controller_rrd_update, ctx);
  2065. event_base_set (ctx->ev_base, ctx->rrd_event);
  2066. event_add (ctx->rrd_event, &rrd_update_time);
  2067. }
  2068. }
  2069. else {
  2070. ctx->rrd = NULL;
  2071. }
  2072. rspamd_controller_password_sane (ctx, ctx->password, "normal password");
  2073. rspamd_controller_password_sane (ctx, ctx->enable_password, "enable "
  2074. "password");
  2075. /* Accept event */
  2076. cache = rspamd_keypair_cache_new (256);
  2077. ctx->http = rspamd_http_router_new (rspamd_controller_error_handler,
  2078. rspamd_controller_finish_handler, &ctx->io_tv, ctx->ev_base,
  2079. ctx->static_files_dir, cache);
  2080. /* Add callbacks for different methods */
  2081. rspamd_http_router_add_path (ctx->http,
  2082. PATH_AUTH,
  2083. rspamd_controller_handle_auth);
  2084. rspamd_http_router_add_path (ctx->http,
  2085. PATH_SYMBOLS,
  2086. rspamd_controller_handle_symbols);
  2087. rspamd_http_router_add_path (ctx->http,
  2088. PATH_ACTIONS,
  2089. rspamd_controller_handle_actions);
  2090. rspamd_http_router_add_path (ctx->http,
  2091. PATH_MAPS,
  2092. rspamd_controller_handle_maps);
  2093. rspamd_http_router_add_path (ctx->http,
  2094. PATH_GET_MAP,
  2095. rspamd_controller_handle_get_map);
  2096. rspamd_http_router_add_path (ctx->http,
  2097. PATH_PIE_CHART,
  2098. rspamd_controller_handle_pie_chart);
  2099. rspamd_http_router_add_path (ctx->http,
  2100. PATH_GRAPH,
  2101. rspamd_controller_handle_graph);
  2102. rspamd_http_router_add_path (ctx->http,
  2103. PATH_HISTORY,
  2104. rspamd_controller_handle_history);
  2105. rspamd_http_router_add_path (ctx->http,
  2106. PATH_HISTORY_RESET,
  2107. rspamd_controller_handle_history_reset);
  2108. rspamd_http_router_add_path (ctx->http,
  2109. PATH_LEARN_SPAM,
  2110. rspamd_controller_handle_learnspam);
  2111. rspamd_http_router_add_path (ctx->http,
  2112. PATH_LEARN_HAM,
  2113. rspamd_controller_handle_learnham);
  2114. rspamd_http_router_add_path (ctx->http,
  2115. PATH_SAVE_ACTIONS,
  2116. rspamd_controller_handle_saveactions);
  2117. rspamd_http_router_add_path (ctx->http,
  2118. PATH_SAVE_SYMBOLS,
  2119. rspamd_controller_handle_savesymbols);
  2120. rspamd_http_router_add_path (ctx->http,
  2121. PATH_SAVE_MAP,
  2122. rspamd_controller_handle_savemap);
  2123. rspamd_http_router_add_path (ctx->http,
  2124. PATH_SCAN,
  2125. rspamd_controller_handle_scan);
  2126. rspamd_http_router_add_path (ctx->http,
  2127. PATH_CHECK,
  2128. rspamd_controller_handle_scan);
  2129. rspamd_http_router_add_path (ctx->http,
  2130. PATH_STAT,
  2131. rspamd_controller_handle_stat);
  2132. rspamd_http_router_add_path (ctx->http,
  2133. PATH_STAT_RESET,
  2134. rspamd_controller_handle_statreset);
  2135. rspamd_http_router_add_path (ctx->http,
  2136. PATH_COUNTERS,
  2137. rspamd_controller_handle_counters);
  2138. if (ctx->key) {
  2139. rspamd_http_router_set_key (ctx->http, ctx->key);
  2140. }
  2141. g_hash_table_iter_init (&iter, ctx->cfg->c_modules);
  2142. while (g_hash_table_iter_next (&iter, &key, &value)) {
  2143. mctx = value;
  2144. if (mctx->mod->module_attach_controller_func != NULL) {
  2145. mctx->mod->module_attach_controller_func (mctx,
  2146. ctx->custom_commands);
  2147. }
  2148. }
  2149. g_hash_table_iter_init (&iter, ctx->custom_commands);
  2150. while (g_hash_table_iter_next (&iter, &key, &value)) {
  2151. rspamd_http_router_add_path (ctx->http,
  2152. key,
  2153. rspamd_controller_handle_custom);
  2154. }
  2155. ctx->resolver = dns_resolver_init (worker->srv->logger,
  2156. ctx->ev_base,
  2157. worker->srv->cfg);
  2158. rspamd_upstreams_library_init (ctx->resolver->r, ctx->ev_base);
  2159. rspamd_upstreams_library_config (worker->srv->cfg);
  2160. /* Maps events */
  2161. rspamd_map_watch (worker->srv->cfg, ctx->ev_base);
  2162. rspamd_symbols_cache_start_refresh (worker->srv->cfg->cache, ctx->ev_base);
  2163. rspamd_stat_init (worker->srv->cfg);
  2164. event_base_loop (ctx->ev_base, 0);
  2165. rspamd_worker_block_signals ();
  2166. g_mime_shutdown ();
  2167. rspamd_stat_close ();
  2168. rspamd_http_router_free (ctx->http);
  2169. rspamd_log_close (worker->srv->logger);
  2170. rspamd_controller_store_saved_stats (ctx);
  2171. if (ctx->rrd) {
  2172. rspamd_rrd_close (ctx->rrd);
  2173. }
  2174. if (ctx->cached_password.len > 0) {
  2175. m = (gpointer)ctx->cached_password.begin;
  2176. munmap (m, ctx->cached_password.len);
  2177. }
  2178. if (ctx->cached_enable_password.len > 0) {
  2179. m = (gpointer) ctx->cached_enable_password.begin;
  2180. munmap (m, ctx->cached_enable_password.len);
  2181. }
  2182. exit (EXIT_SUCCESS);
  2183. }