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.

task.c 46KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "task.h"
  17. #include "rspamd.h"
  18. #include "scan_result.h"
  19. #include "libserver/protocol.h"
  20. #include "libserver/protocol_internal.h"
  21. #include "message.h"
  22. #include "lua/lua_common.h"
  23. #include "email_addr.h"
  24. #include "composites.h"
  25. #include "stat_api.h"
  26. #include "unix-std.h"
  27. #include "utlist.h"
  28. #include "contrib/zstd/zstd.h"
  29. #include "libserver/mempool_vars_internal.h"
  30. #include "libserver/cfg_file_private.h"
  31. #include "libmime/lang_detection.h"
  32. #include "libmime/scan_result_private.h"
  33. #ifdef WITH_JEMALLOC
  34. #include <jemalloc/jemalloc.h>
  35. #else
  36. # if defined(__GLIBC__) && defined(_GNU_SOURCE)
  37. # include <malloc.h>
  38. # endif
  39. #endif
  40. #include <math.h>
  41. __KHASH_IMPL (rspamd_req_headers_hash, static inline,
  42. rspamd_ftok_t *, struct rspamd_request_header_chain *, 1,
  43. rspamd_ftok_icase_hash, rspamd_ftok_icase_equal)
  44. /*
  45. * Do not print more than this amount of elts
  46. */
  47. static const int max_log_elts = 7;
  48. static GQuark
  49. rspamd_task_quark (void)
  50. {
  51. return g_quark_from_static_string ("task-error");
  52. }
  53. /*
  54. * Create new task
  55. */
  56. struct rspamd_task *
  57. rspamd_task_new (struct rspamd_worker *worker,
  58. struct rspamd_config *cfg,
  59. rspamd_mempool_t *pool,
  60. struct rspamd_lang_detector *lang_det,
  61. struct ev_loop *event_loop,
  62. gboolean debug_mem)
  63. {
  64. struct rspamd_task *new_task;
  65. rspamd_mempool_t *task_pool;
  66. guint flags = 0;
  67. if (pool == NULL) {
  68. task_pool = rspamd_mempool_new (rspamd_mempool_suggest_size (),
  69. "task", debug_mem ? RSPAMD_MEMPOOL_DEBUG : 0);
  70. flags |= RSPAMD_TASK_FLAG_OWN_POOL;
  71. }
  72. else {
  73. task_pool = pool;
  74. }
  75. new_task = rspamd_mempool_alloc0 (task_pool, sizeof (struct rspamd_task));
  76. new_task->task_pool = task_pool;
  77. new_task->flags = flags;
  78. new_task->worker = worker;
  79. new_task->lang_det = lang_det;
  80. if (cfg) {
  81. new_task->cfg = cfg;
  82. REF_RETAIN (cfg);
  83. if (cfg->check_all_filters) {
  84. new_task->flags |= RSPAMD_TASK_FLAG_PASS_ALL;
  85. }
  86. if (cfg->re_cache) {
  87. new_task->re_rt = rspamd_re_cache_runtime_new (cfg->re_cache);
  88. }
  89. if (new_task->lang_det == NULL && cfg->lang_det != NULL) {
  90. new_task->lang_det = cfg->lang_det;
  91. }
  92. }
  93. new_task->event_loop = event_loop;
  94. new_task->task_timestamp = ev_time ();
  95. new_task->time_real_finish = NAN;
  96. new_task->request_headers = kh_init (rspamd_req_headers_hash);
  97. new_task->sock = -1;
  98. new_task->flags |= (RSPAMD_TASK_FLAG_MIME);
  99. /* Default results chain */
  100. rspamd_create_metric_result (new_task, NULL, -1);
  101. new_task->queue_id = "undef";
  102. new_task->messages = ucl_object_typed_new (UCL_OBJECT);
  103. new_task->lua_cache = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  104. return new_task;
  105. }
  106. static void
  107. rspamd_task_reply (struct rspamd_task *task)
  108. {
  109. const ev_tstamp write_timeout = 5.0;
  110. if (task->fin_callback) {
  111. task->fin_callback (task, task->fin_arg);
  112. }
  113. else {
  114. if (!(task->processed_stages & RSPAMD_TASK_STAGE_REPLIED)) {
  115. rspamd_protocol_write_reply (task, write_timeout);
  116. }
  117. }
  118. }
  119. /*
  120. * Called if all filters are processed
  121. * @return TRUE if session should be terminated
  122. */
  123. gboolean
  124. rspamd_task_fin (void *arg)
  125. {
  126. struct rspamd_task *task = (struct rspamd_task *) arg;
  127. /* Task is already finished or skipped */
  128. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  129. rspamd_task_reply (task);
  130. return TRUE;
  131. }
  132. if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_ALL)) {
  133. rspamd_task_reply (task);
  134. return TRUE;
  135. }
  136. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  137. rspamd_task_reply (task);
  138. return TRUE;
  139. }
  140. /* One more iteration */
  141. return FALSE;
  142. }
  143. /*
  144. * Called if session was restored inside fin callback
  145. */
  146. void
  147. rspamd_task_restore (void *arg)
  148. {
  149. /* XXX: not needed now ? */
  150. }
  151. /*
  152. * Free all structures of worker_task
  153. */
  154. void
  155. rspamd_task_free (struct rspamd_task *task)
  156. {
  157. struct rspamd_email_address *addr;
  158. struct rspamd_lua_cached_entry *entry;
  159. static guint free_iters = 0;
  160. GHashTableIter it;
  161. gpointer k, v;
  162. guint i;
  163. if (task) {
  164. debug_task ("free pointer %p", task);
  165. if (task->rcpt_envelope) {
  166. for (i = 0; i < task->rcpt_envelope->len; i ++) {
  167. addr = g_ptr_array_index (task->rcpt_envelope, i);
  168. rspamd_email_address_free (addr);
  169. }
  170. g_ptr_array_free (task->rcpt_envelope, TRUE);
  171. }
  172. if (task->from_envelope) {
  173. rspamd_email_address_free (task->from_envelope);
  174. }
  175. if (task->from_envelope_orig) {
  176. rspamd_email_address_free (task->from_envelope_orig);
  177. }
  178. if (task->meta_words) {
  179. g_array_free (task->meta_words, TRUE);
  180. }
  181. ucl_object_unref (task->messages);
  182. if (task->re_rt) {
  183. rspamd_re_cache_runtime_destroy (task->re_rt);
  184. }
  185. if (task->http_conn != NULL) {
  186. rspamd_http_connection_reset (task->http_conn);
  187. rspamd_http_connection_unref (task->http_conn);
  188. }
  189. if (task->settings != NULL) {
  190. ucl_object_unref (task->settings);
  191. }
  192. if (task->settings_elt != NULL) {
  193. REF_RELEASE (task->settings_elt);
  194. }
  195. if (task->client_addr) {
  196. rspamd_inet_address_free (task->client_addr);
  197. }
  198. if (task->from_addr) {
  199. rspamd_inet_address_free (task->from_addr);
  200. }
  201. if (task->err) {
  202. g_error_free (task->err);
  203. }
  204. ev_timer_stop (task->event_loop, &task->timeout_ev);
  205. ev_io_stop (task->event_loop, &task->guard_ev);
  206. if (task->sock != -1) {
  207. close (task->sock);
  208. }
  209. if (task->cfg) {
  210. if (task->lua_cache) {
  211. g_hash_table_iter_init (&it, task->lua_cache);
  212. while (g_hash_table_iter_next (&it, &k, &v)) {
  213. entry = (struct rspamd_lua_cached_entry *)v;
  214. luaL_unref (task->cfg->lua_state,
  215. LUA_REGISTRYINDEX, entry->ref);
  216. }
  217. g_hash_table_unref (task->lua_cache);
  218. }
  219. if (task->cfg->full_gc_iters && (++free_iters > task->cfg->full_gc_iters)) {
  220. /* Perform more expensive cleanup cycle */
  221. gsize allocated = 0, active = 0, metadata = 0,
  222. resident = 0, mapped = 0, old_lua_mem = 0;
  223. gdouble t1, t2;
  224. old_lua_mem = lua_gc (task->cfg->lua_state, LUA_GCCOUNT, 0);
  225. t1 = rspamd_get_ticks (FALSE);
  226. #ifdef WITH_JEMALLOC
  227. gsize sz = sizeof (gsize);
  228. mallctl ("stats.allocated", &allocated, &sz, NULL, 0);
  229. mallctl ("stats.active", &active, &sz, NULL, 0);
  230. mallctl ("stats.metadata", &metadata, &sz, NULL, 0);
  231. mallctl ("stats.resident", &resident, &sz, NULL, 0);
  232. mallctl ("stats.mapped", &mapped, &sz, NULL, 0);
  233. #else
  234. # if defined(__GLIBC__) && defined(_GNU_SOURCE)
  235. malloc_trim (0);
  236. # endif
  237. #endif
  238. lua_gc (task->cfg->lua_state, LUA_GCCOLLECT, 0);
  239. t2 = rspamd_get_ticks (FALSE);
  240. msg_notice_task ("perform full gc cycle; memory stats: "
  241. "%Hz allocated, %Hz active, %Hz metadata, %Hz resident, %Hz mapped;"
  242. " lua memory: %z kb -> %d kb; %f ms for gc iter",
  243. allocated, active, metadata, resident, mapped,
  244. old_lua_mem, lua_gc (task->cfg->lua_state, LUA_GCCOUNT, 0),
  245. (t2 - t1) * 1000.0);
  246. free_iters = rspamd_time_jitter (0,
  247. (gdouble)task->cfg->full_gc_iters / 2);
  248. }
  249. REF_RELEASE (task->cfg);
  250. }
  251. kh_destroy (rspamd_req_headers_hash, task->request_headers);
  252. rspamd_message_unref (task->message);
  253. if (task->flags & RSPAMD_TASK_FLAG_OWN_POOL) {
  254. rspamd_mempool_delete (task->task_pool);
  255. }
  256. }
  257. }
  258. struct rspamd_task_map {
  259. gpointer begin;
  260. gulong len;
  261. gint fd;
  262. };
  263. static void
  264. rspamd_task_unmapper (gpointer ud)
  265. {
  266. struct rspamd_task_map *m = ud;
  267. munmap (m->begin, m->len);
  268. close (m->fd);
  269. }
  270. gboolean
  271. rspamd_task_load_message (struct rspamd_task *task,
  272. struct rspamd_http_message *msg, const gchar *start, gsize len)
  273. {
  274. guint control_len, r;
  275. struct ucl_parser *parser;
  276. ucl_object_t *control_obj;
  277. gchar filepath[PATH_MAX], *fp;
  278. gint fd, flen;
  279. gulong offset = 0, shmem_size = 0;
  280. rspamd_ftok_t *tok;
  281. gpointer map;
  282. struct stat st;
  283. struct rspamd_task_map *m;
  284. const gchar *ft;
  285. #ifdef HAVE_SANE_SHMEM
  286. ft = "shm";
  287. #else
  288. ft = "file";
  289. #endif
  290. if (msg) {
  291. rspamd_protocol_handle_headers (task, msg);
  292. }
  293. tok = rspamd_task_get_request_header (task, "shm");
  294. if (tok) {
  295. /* Shared memory part */
  296. r = rspamd_strlcpy (filepath, tok->begin,
  297. MIN (sizeof (filepath), tok->len + 1));
  298. rspamd_url_decode (filepath, filepath, r + 1);
  299. flen = strlen (filepath);
  300. if (filepath[0] == '"' && flen > 2) {
  301. /* We need to unquote filepath */
  302. fp = &filepath[1];
  303. fp[flen - 2] = '\0';
  304. }
  305. else {
  306. fp = &filepath[0];
  307. }
  308. #ifdef HAVE_SANE_SHMEM
  309. fd = shm_open (fp, O_RDONLY, 00600);
  310. #else
  311. fd = open (fp, O_RDONLY, 00600);
  312. #endif
  313. if (fd == -1) {
  314. g_set_error (&task->err, rspamd_task_quark(), RSPAMD_PROTOCOL_ERROR,
  315. "Cannot open %s segment (%s): %s", ft, fp, strerror (errno));
  316. return FALSE;
  317. }
  318. if (fstat (fd, &st) == -1) {
  319. g_set_error (&task->err, rspamd_task_quark(), RSPAMD_PROTOCOL_ERROR,
  320. "Cannot stat %s segment (%s): %s", ft, fp, strerror (errno));
  321. close (fd);
  322. return FALSE;
  323. }
  324. map = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
  325. if (map == MAP_FAILED) {
  326. close (fd);
  327. g_set_error (&task->err, rspamd_task_quark(), RSPAMD_PROTOCOL_ERROR,
  328. "Cannot mmap %s (%s): %s", ft, fp, strerror (errno));
  329. return FALSE;
  330. }
  331. tok = rspamd_task_get_request_header (task, "shm-offset");
  332. if (tok) {
  333. rspamd_strtoul (tok->begin, tok->len, &offset);
  334. if (offset > (gulong)st.st_size) {
  335. msg_err_task ("invalid offset %ul (%ul available) for shm "
  336. "segment %s", offset, (gulong)st.st_size, fp);
  337. munmap (map, st.st_size);
  338. close (fd);
  339. return FALSE;
  340. }
  341. }
  342. tok = rspamd_task_get_request_header (task, "shm-length");
  343. shmem_size = st.st_size;
  344. if (tok) {
  345. rspamd_strtoul (tok->begin, tok->len, &shmem_size);
  346. if (shmem_size > (gulong)st.st_size) {
  347. msg_err_task ("invalid length %ul (%ul available) for %s "
  348. "segment %s", shmem_size, (gulong)st.st_size, ft, fp);
  349. munmap (map, st.st_size);
  350. close (fd);
  351. return FALSE;
  352. }
  353. }
  354. task->msg.begin = ((guchar *)map) + offset;
  355. task->msg.len = shmem_size;
  356. m = rspamd_mempool_alloc (task->task_pool, sizeof (*m));
  357. m->begin = map;
  358. m->len = st.st_size;
  359. m->fd = fd;
  360. msg_info_task ("loaded message from shared memory %s (%ul size, %ul offset), fd=%d",
  361. fp, shmem_size, offset, fd);
  362. rspamd_mempool_add_destructor (task->task_pool, rspamd_task_unmapper, m);
  363. return TRUE;
  364. }
  365. tok = rspamd_task_get_request_header (task, "file");
  366. if (tok == NULL) {
  367. tok = rspamd_task_get_request_header (task, "path");
  368. }
  369. if (tok) {
  370. debug_task ("want to scan file %T", tok);
  371. r = rspamd_strlcpy (filepath, tok->begin,
  372. MIN (sizeof (filepath), tok->len + 1));
  373. rspamd_url_decode (filepath, filepath, r + 1);
  374. flen = strlen (filepath);
  375. if (filepath[0] == '"' && flen > 2) {
  376. /* We need to unquote filepath */
  377. fp = &filepath[1];
  378. fp[flen - 2] = '\0';
  379. }
  380. else {
  381. fp = &filepath[0];
  382. }
  383. if (stat (fp, &st) == -1) {
  384. g_set_error (&task->err, rspamd_task_quark(), RSPAMD_PROTOCOL_ERROR,
  385. "Invalid file (%s): %s", fp, strerror (errno));
  386. return FALSE;
  387. }
  388. if (G_UNLIKELY (st.st_size == 0)) {
  389. /* Empty file */
  390. task->flags |= RSPAMD_TASK_FLAG_EMPTY;
  391. task->msg.begin = rspamd_mempool_strdup (task->task_pool, "");
  392. task->msg.len = 0;
  393. }
  394. else {
  395. fd = open (fp, O_RDONLY);
  396. if (fd == -1) {
  397. g_set_error (&task->err, rspamd_task_quark (),
  398. RSPAMD_PROTOCOL_ERROR,
  399. "Cannot open file (%s): %s", fp, strerror (errno));
  400. return FALSE;
  401. }
  402. map = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
  403. if (map == MAP_FAILED) {
  404. close (fd);
  405. g_set_error (&task->err, rspamd_task_quark (),
  406. RSPAMD_PROTOCOL_ERROR,
  407. "Cannot mmap file (%s): %s", fp, strerror (errno));
  408. return FALSE;
  409. }
  410. task->msg.begin = map;
  411. task->msg.len = st.st_size;
  412. m = rspamd_mempool_alloc (task->task_pool, sizeof (*m));
  413. m->begin = map;
  414. m->len = st.st_size;
  415. m->fd = fd;
  416. rspamd_mempool_add_destructor (task->task_pool, rspamd_task_unmapper, m);
  417. }
  418. task->msg.fpath = rspamd_mempool_strdup (task->task_pool, fp);
  419. task->flags |= RSPAMD_TASK_FLAG_FILE;
  420. msg_info_task ("loaded message from file %s", fp);
  421. return TRUE;
  422. }
  423. /* Plain data */
  424. debug_task ("got input of length %z", task->msg.len);
  425. /* Check compression */
  426. tok = rspamd_task_get_request_header (task, "compression");
  427. if (tok) {
  428. /* Need to uncompress */
  429. rspamd_ftok_t t;
  430. t.begin = "zstd";
  431. t.len = 4;
  432. if (rspamd_ftok_casecmp (tok, &t) == 0) {
  433. ZSTD_DStream *zstream;
  434. ZSTD_inBuffer zin;
  435. ZSTD_outBuffer zout;
  436. guchar *out;
  437. gsize outlen, r;
  438. gulong dict_id;
  439. if (!rspamd_libs_reset_decompression (task->cfg->libs_ctx)) {
  440. g_set_error (&task->err, rspamd_task_quark(),
  441. RSPAMD_PROTOCOL_ERROR,
  442. "Cannot decompress, decompressor init failed");
  443. return FALSE;
  444. }
  445. tok = rspamd_task_get_request_header (task, "dictionary");
  446. if (tok != NULL) {
  447. /* We need to use custom dictionary */
  448. if (!rspamd_strtoul (tok->begin, tok->len, &dict_id)) {
  449. g_set_error (&task->err, rspamd_task_quark(), RSPAMD_PROTOCOL_ERROR,
  450. "Non numeric dictionary");
  451. return FALSE;
  452. }
  453. if (!task->cfg->libs_ctx->in_dict) {
  454. g_set_error (&task->err, rspamd_task_quark(), RSPAMD_PROTOCOL_ERROR,
  455. "Unknown dictionary, undefined locally");
  456. return FALSE;
  457. }
  458. if (task->cfg->libs_ctx->in_dict->id != dict_id) {
  459. g_set_error (&task->err, rspamd_task_quark(), RSPAMD_PROTOCOL_ERROR,
  460. "Unknown dictionary, invalid dictionary id");
  461. return FALSE;
  462. }
  463. }
  464. zstream = task->cfg->libs_ctx->in_zstream;
  465. zin.pos = 0;
  466. zin.src = start;
  467. zin.size = len;
  468. if ((outlen = ZSTD_getDecompressedSize (start, len)) == 0) {
  469. outlen = ZSTD_DStreamOutSize ();
  470. }
  471. out = g_malloc (outlen);
  472. zout.dst = out;
  473. zout.pos = 0;
  474. zout.size = outlen;
  475. while (zin.pos < zin.size) {
  476. r = ZSTD_decompressStream (zstream, &zout, &zin);
  477. if (ZSTD_isError (r)) {
  478. g_set_error (&task->err, rspamd_task_quark(),
  479. RSPAMD_PROTOCOL_ERROR,
  480. "Decompression error: %s", ZSTD_getErrorName (r));
  481. return FALSE;
  482. }
  483. if (zout.pos == zout.size) {
  484. /* We need to extend output buffer */
  485. zout.size = zout.size * 2 + 1;
  486. zout.dst = g_realloc (zout.dst, zout.size);
  487. }
  488. }
  489. rspamd_mempool_add_destructor (task->task_pool, g_free, zout.dst);
  490. task->msg.begin = zout.dst;
  491. task->msg.len = zout.pos;
  492. task->protocol_flags |= RSPAMD_TASK_PROTOCOL_FLAG_COMPRESSED;
  493. msg_info_task ("loaded message from zstd compressed stream; "
  494. "compressed: %ul; uncompressed: %ul",
  495. (gulong)zin.size, (gulong)zout.pos);
  496. }
  497. else {
  498. g_set_error (&task->err, rspamd_task_quark(), RSPAMD_PROTOCOL_ERROR,
  499. "Invalid compression method");
  500. return FALSE;
  501. }
  502. }
  503. else {
  504. task->msg.begin = start;
  505. task->msg.len = len;
  506. }
  507. if (task->msg.len == 0) {
  508. task->flags |= RSPAMD_TASK_FLAG_EMPTY;
  509. }
  510. if (task->protocol_flags & RSPAMD_TASK_PROTOCOL_FLAG_HAS_CONTROL) {
  511. rspamd_ftok_t *hv = rspamd_task_get_request_header (task, MLEN_HEADER);
  512. gulong message_len = 0;
  513. if (!hv || !rspamd_strtoul (hv->begin, hv->len, &message_len) ||
  514. task->msg.len < message_len) {
  515. msg_warn_task ("message has invalid message length: %ul and total len: %ul",
  516. message_len, task->msg.len);
  517. g_set_error (&task->err, rspamd_task_quark(), RSPAMD_PROTOCOL_ERROR,
  518. "Invalid length");
  519. return FALSE;
  520. }
  521. control_len = task->msg.len - message_len;
  522. if (control_len > 0) {
  523. parser = ucl_parser_new (UCL_PARSER_KEY_LOWERCASE);
  524. if (!ucl_parser_add_chunk (parser, task->msg.begin, control_len)) {
  525. msg_warn_task ("processing of control chunk failed: %s",
  526. ucl_parser_get_error (parser));
  527. ucl_parser_free (parser);
  528. }
  529. else {
  530. control_obj = ucl_parser_get_object (parser);
  531. ucl_parser_free (parser);
  532. rspamd_protocol_handle_control (task, control_obj);
  533. ucl_object_unref (control_obj);
  534. }
  535. task->msg.begin += control_len;
  536. task->msg.len -= control_len;
  537. }
  538. }
  539. return TRUE;
  540. }
  541. static gint
  542. rspamd_task_select_processing_stage (struct rspamd_task *task, guint stages)
  543. {
  544. gint st, mask;
  545. mask = task->processed_stages;
  546. if (mask == 0) {
  547. st = 0;
  548. }
  549. else {
  550. for (st = 1; mask != 1; st ++) {
  551. mask = (unsigned int)mask >> 1;
  552. }
  553. }
  554. st = 1 << st;
  555. if (stages & st) {
  556. return st;
  557. }
  558. else if (st < RSPAMD_TASK_STAGE_DONE) {
  559. /* We assume that the stage that was not requested is done */
  560. task->processed_stages |= st;
  561. return rspamd_task_select_processing_stage (task, stages);
  562. }
  563. /* We are done */
  564. return RSPAMD_TASK_STAGE_DONE;
  565. }
  566. gboolean
  567. rspamd_task_process (struct rspamd_task *task, guint stages)
  568. {
  569. gint st;
  570. gboolean ret = TRUE, all_done = TRUE;
  571. GError *stat_error = NULL;
  572. /* Avoid nested calls */
  573. if (task->flags & RSPAMD_TASK_FLAG_PROCESSING) {
  574. return TRUE;
  575. }
  576. if (RSPAMD_TASK_IS_PROCESSED (task)) {
  577. return TRUE;
  578. }
  579. task->flags |= RSPAMD_TASK_FLAG_PROCESSING;
  580. st = rspamd_task_select_processing_stage (task, stages);
  581. switch (st) {
  582. case RSPAMD_TASK_STAGE_CONNFILTERS:
  583. all_done = rspamd_symcache_process_symbols (task, task->cfg->cache, st);
  584. break;
  585. case RSPAMD_TASK_STAGE_READ_MESSAGE:
  586. if (!rspamd_message_parse (task)) {
  587. ret = FALSE;
  588. }
  589. break;
  590. case RSPAMD_TASK_STAGE_PROCESS_MESSAGE:
  591. if (!(task->flags & RSPAMD_TASK_FLAG_SKIP_PROCESS)) {
  592. rspamd_message_process (task);
  593. }
  594. break;
  595. case RSPAMD_TASK_STAGE_PRE_FILTERS:
  596. case RSPAMD_TASK_STAGE_FILTERS:
  597. all_done = rspamd_symcache_process_symbols (task, task->cfg->cache, st);
  598. break;
  599. case RSPAMD_TASK_STAGE_CLASSIFIERS:
  600. case RSPAMD_TASK_STAGE_CLASSIFIERS_PRE:
  601. case RSPAMD_TASK_STAGE_CLASSIFIERS_POST:
  602. if (!RSPAMD_TASK_IS_EMPTY (task)) {
  603. if (rspamd_stat_classify (task, task->cfg->lua_state, st, &stat_error) ==
  604. RSPAMD_STAT_PROCESS_ERROR) {
  605. msg_err_task ("classify error: %e", stat_error);
  606. g_error_free (stat_error);
  607. }
  608. }
  609. break;
  610. case RSPAMD_TASK_STAGE_COMPOSITES:
  611. rspamd_composites_process_task (task);
  612. task->result->nresults_postfilters = task->result->nresults;
  613. break;
  614. case RSPAMD_TASK_STAGE_POST_FILTERS:
  615. all_done = rspamd_symcache_process_symbols (task, task->cfg->cache,
  616. st);
  617. if (all_done && (task->flags & RSPAMD_TASK_FLAG_LEARN_AUTO) &&
  618. !RSPAMD_TASK_IS_EMPTY (task) &&
  619. !(task->flags & (RSPAMD_TASK_FLAG_LEARN_SPAM|RSPAMD_TASK_FLAG_LEARN_HAM))) {
  620. rspamd_stat_check_autolearn (task);
  621. }
  622. break;
  623. case RSPAMD_TASK_STAGE_LEARN:
  624. case RSPAMD_TASK_STAGE_LEARN_PRE:
  625. case RSPAMD_TASK_STAGE_LEARN_POST:
  626. if (task->flags & (RSPAMD_TASK_FLAG_LEARN_SPAM|RSPAMD_TASK_FLAG_LEARN_HAM)) {
  627. if (task->err == NULL) {
  628. if (!rspamd_stat_learn (task,
  629. task->flags & RSPAMD_TASK_FLAG_LEARN_SPAM,
  630. task->cfg->lua_state, task->classifier,
  631. st, &stat_error)) {
  632. if (stat_error == NULL) {
  633. g_set_error (&stat_error,
  634. g_quark_from_static_string ("stat"), 500,
  635. "Unknown statistics error, found on stage %s;"
  636. " classifier: %s",
  637. rspamd_task_stage_name (st), task->classifier);
  638. }
  639. if (stat_error->code >= 400) {
  640. msg_err_task ("learn error: %e", stat_error);
  641. }
  642. else {
  643. msg_notice_task ("skip learning: %e", stat_error);
  644. }
  645. if (!(task->flags & RSPAMD_TASK_FLAG_LEARN_AUTO)) {
  646. task->err = stat_error;
  647. task->processed_stages |= RSPAMD_TASK_STAGE_DONE;
  648. }
  649. else {
  650. /* Do not skip idempotent in case of learn error */
  651. if (stat_error) {
  652. g_error_free (stat_error);
  653. }
  654. task->processed_stages |= RSPAMD_TASK_STAGE_LEARN|
  655. RSPAMD_TASK_STAGE_LEARN_PRE|
  656. RSPAMD_TASK_STAGE_LEARN_POST;
  657. }
  658. }
  659. }
  660. }
  661. break;
  662. case RSPAMD_TASK_STAGE_COMPOSITES_POST:
  663. /* Second run of composites processing before idempotent filters (if needed) */
  664. if (task->result->nresults_postfilters != task->result->nresults) {
  665. rspamd_composites_process_task (task);
  666. }
  667. else {
  668. msg_debug_task ("skip second run of composites as the result has not been changed");
  669. }
  670. break;
  671. case RSPAMD_TASK_STAGE_IDEMPOTENT:
  672. /* Stop task timeout */
  673. if (ev_can_stop (&task->timeout_ev)) {
  674. ev_timer_stop (task->event_loop, &task->timeout_ev);
  675. }
  676. all_done = rspamd_symcache_process_symbols (task, task->cfg->cache, st);
  677. break;
  678. case RSPAMD_TASK_STAGE_DONE:
  679. task->processed_stages |= RSPAMD_TASK_STAGE_DONE;
  680. break;
  681. default:
  682. /* TODO: not implemented stage */
  683. break;
  684. }
  685. if (RSPAMD_TASK_IS_SKIPPED (task)) {
  686. /* Set all bits except idempotent filters */
  687. task->processed_stages |= 0x7FFF;
  688. }
  689. task->flags &= ~RSPAMD_TASK_FLAG_PROCESSING;
  690. if (!ret || RSPAMD_TASK_IS_PROCESSED (task)) {
  691. if (!ret) {
  692. /* Set processed flags */
  693. task->processed_stages |= RSPAMD_TASK_STAGE_DONE;
  694. }
  695. msg_debug_task ("task is processed");
  696. return ret;
  697. }
  698. if (ret) {
  699. if (rspamd_session_events_pending (task->s) != 0) {
  700. /* We have events pending, so we consider this stage as incomplete */
  701. msg_debug_task ("need more work on stage %d", st);
  702. }
  703. else {
  704. if (all_done) {
  705. /* Mark the current stage as done and go to the next stage */
  706. msg_debug_task ("completed stage %d", st);
  707. task->processed_stages |= st;
  708. }
  709. else {
  710. msg_debug_task ("need more processing on stage %d", st);
  711. }
  712. /* Tail recursion */
  713. return rspamd_task_process (task, stages);
  714. }
  715. }
  716. return ret;
  717. }
  718. struct rspamd_email_address*
  719. rspamd_task_get_sender (struct rspamd_task *task)
  720. {
  721. return task->from_envelope;
  722. }
  723. static const gchar *
  724. rspamd_task_cache_principal_recipient (struct rspamd_task *task,
  725. const gchar *rcpt, gsize len)
  726. {
  727. gchar *rcpt_lc;
  728. if (rcpt == NULL) {
  729. return NULL;
  730. }
  731. rcpt_lc = rspamd_mempool_alloc (task->task_pool, len + 1);
  732. rspamd_strlcpy (rcpt_lc, rcpt, len + 1);
  733. rspamd_str_lc (rcpt_lc, len);
  734. rspamd_mempool_set_variable (task->task_pool,
  735. RSPAMD_MEMPOOL_PRINCIPAL_RECIPIENT, rcpt_lc, NULL);
  736. return rcpt_lc;
  737. }
  738. const gchar *
  739. rspamd_task_get_principal_recipient (struct rspamd_task *task)
  740. {
  741. const gchar *val;
  742. struct rspamd_email_address *addr;
  743. guint i;
  744. val = rspamd_mempool_get_variable (task->task_pool,
  745. RSPAMD_MEMPOOL_PRINCIPAL_RECIPIENT);
  746. if (val) {
  747. return val;
  748. }
  749. if (task->deliver_to) {
  750. return rspamd_task_cache_principal_recipient (task, task->deliver_to,
  751. strlen (task->deliver_to));
  752. }
  753. if (task->rcpt_envelope != NULL) {
  754. PTR_ARRAY_FOREACH (task->rcpt_envelope, i, addr) {
  755. if (addr->addr && !(addr->flags & RSPAMD_EMAIL_ADDR_ORIGINAL)) {
  756. return rspamd_task_cache_principal_recipient (task, addr->addr,
  757. addr->addr_len);
  758. }
  759. }
  760. }
  761. GPtrArray *rcpt_mime = MESSAGE_FIELD_CHECK (task, rcpt_mime);
  762. if (rcpt_mime != NULL && rcpt_mime->len > 0) {
  763. PTR_ARRAY_FOREACH (rcpt_mime, i, addr) {
  764. if (addr->addr && !(addr->flags & RSPAMD_EMAIL_ADDR_ORIGINAL)) {
  765. return rspamd_task_cache_principal_recipient (task, addr->addr,
  766. addr->addr_len);
  767. }
  768. }
  769. }
  770. return NULL;
  771. }
  772. gboolean
  773. rspamd_learn_task_spam (struct rspamd_task *task,
  774. gboolean is_spam,
  775. const gchar *classifier,
  776. GError **err)
  777. {
  778. if (is_spam) {
  779. task->flags |= RSPAMD_TASK_FLAG_LEARN_SPAM;
  780. }
  781. else {
  782. task->flags |= RSPAMD_TASK_FLAG_LEARN_HAM;
  783. }
  784. task->classifier = classifier;
  785. return TRUE;
  786. }
  787. static gboolean
  788. rspamd_task_log_check_condition (struct rspamd_task *task,
  789. struct rspamd_log_format *lf)
  790. {
  791. gboolean ret = FALSE;
  792. switch (lf->type) {
  793. case RSPAMD_LOG_MID:
  794. if (MESSAGE_FIELD_CHECK (task, message_id) &&
  795. strcmp (MESSAGE_FIELD (task, message_id) , "undef") != 0) {
  796. ret = TRUE;
  797. }
  798. break;
  799. case RSPAMD_LOG_QID:
  800. if (task->queue_id && strcmp (task->queue_id, "undef") != 0) {
  801. ret = TRUE;
  802. }
  803. break;
  804. case RSPAMD_LOG_USER:
  805. if (task->user) {
  806. ret = TRUE;
  807. }
  808. break;
  809. case RSPAMD_LOG_IP:
  810. if (task->from_addr && rspamd_ip_is_valid (task->from_addr)) {
  811. ret = TRUE;
  812. }
  813. break;
  814. case RSPAMD_LOG_SMTP_RCPT:
  815. case RSPAMD_LOG_SMTP_RCPTS:
  816. if (task->rcpt_envelope && task->rcpt_envelope->len > 0) {
  817. ret = TRUE;
  818. }
  819. break;
  820. case RSPAMD_LOG_MIME_RCPT:
  821. case RSPAMD_LOG_MIME_RCPTS:
  822. if (MESSAGE_FIELD_CHECK (task, rcpt_mime) &&
  823. MESSAGE_FIELD (task, rcpt_mime)->len > 0) {
  824. ret = TRUE;
  825. }
  826. break;
  827. case RSPAMD_LOG_SMTP_FROM:
  828. if (task->from_envelope) {
  829. ret = TRUE;
  830. }
  831. break;
  832. case RSPAMD_LOG_MIME_FROM:
  833. if (MESSAGE_FIELD_CHECK (task, from_mime) &&
  834. MESSAGE_FIELD (task, from_mime)->len > 0) {
  835. ret = TRUE;
  836. }
  837. break;
  838. case RSPAMD_LOG_FILENAME:
  839. if (task->msg.fpath) {
  840. ret = TRUE;
  841. }
  842. break;
  843. case RSPAMD_LOG_FORCED_ACTION:
  844. if (task->result->passthrough_result) {
  845. ret = TRUE;
  846. }
  847. break;
  848. case RSPAMD_LOG_SETTINGS_ID:
  849. if (task->settings_elt) {
  850. ret = TRUE;
  851. }
  852. break;
  853. default:
  854. ret = TRUE;
  855. break;
  856. }
  857. return ret;
  858. }
  859. /*
  860. * Sort by symbol's score -> name
  861. */
  862. static gint
  863. rspamd_task_compare_log_sym (gconstpointer a, gconstpointer b)
  864. {
  865. const struct rspamd_symbol_result *s1 = *(const struct rspamd_symbol_result **)a,
  866. *s2 = *(const struct rspamd_symbol_result **)b;
  867. gdouble w1, w2;
  868. w1 = fabs (s1->score);
  869. w2 = fabs (s2->score);
  870. if (w1 == w2 && s1->name && s2->name) {
  871. return strcmp (s1->name, s2->name);
  872. }
  873. return (w2 - w1) * 1000.0;
  874. }
  875. static gint
  876. rspamd_task_compare_log_group (gconstpointer a, gconstpointer b)
  877. {
  878. const struct rspamd_symbols_group *s1 = *(const struct rspamd_symbols_group **)a,
  879. *s2 = *(const struct rspamd_symbols_group **)b;
  880. return strcmp (s1->name, s2->name);
  881. }
  882. static rspamd_ftok_t
  883. rspamd_task_log_metric_res (struct rspamd_task *task,
  884. struct rspamd_log_format *lf)
  885. {
  886. static gchar scorebuf[32];
  887. rspamd_ftok_t res = {.begin = NULL, .len = 0};
  888. struct rspamd_scan_result *mres;
  889. gboolean first = TRUE;
  890. rspamd_fstring_t *symbuf;
  891. struct rspamd_symbol_result *sym;
  892. GPtrArray *sorted_symbols;
  893. struct rspamd_action *act;
  894. struct rspamd_symbols_group *gr;
  895. guint i, j;
  896. khiter_t k;
  897. mres = task->result;
  898. act = rspamd_check_action_metric (task, NULL, NULL);
  899. if (mres != NULL) {
  900. switch (lf->type) {
  901. case RSPAMD_LOG_ISSPAM:
  902. if (RSPAMD_TASK_IS_SKIPPED (task)) {
  903. res.begin = "S";
  904. }
  905. else if (!(act->flags & RSPAMD_ACTION_HAM)) {
  906. res.begin = "T";
  907. }
  908. else {
  909. res.begin = "F";
  910. }
  911. res.len = 1;
  912. break;
  913. case RSPAMD_LOG_ACTION:
  914. res.begin = act->name;
  915. res.len = strlen (res.begin);
  916. break;
  917. case RSPAMD_LOG_SCORES:
  918. res.len = rspamd_snprintf (scorebuf, sizeof (scorebuf), "%.2f/%.2f",
  919. mres->score, rspamd_task_get_required_score (task, mres));
  920. res.begin = scorebuf;
  921. break;
  922. case RSPAMD_LOG_SYMBOLS:
  923. symbuf = rspamd_fstring_sized_new (128);
  924. sorted_symbols = g_ptr_array_sized_new (kh_size (mres->symbols));
  925. kh_foreach_value (mres->symbols, sym, {
  926. if (!(sym->flags & RSPAMD_SYMBOL_RESULT_IGNORED)) {
  927. g_ptr_array_add (sorted_symbols, (gpointer)sym);
  928. }
  929. });
  930. g_ptr_array_sort (sorted_symbols, rspamd_task_compare_log_sym);
  931. for (i = 0; i < sorted_symbols->len; i ++) {
  932. sym = g_ptr_array_index (sorted_symbols, i);
  933. if (first) {
  934. rspamd_printf_fstring (&symbuf, "%s", sym->name);
  935. }
  936. else {
  937. rspamd_printf_fstring (&symbuf, ",%s", sym->name);
  938. }
  939. if (lf->flags & RSPAMD_LOG_FMT_FLAG_SYMBOLS_SCORES) {
  940. rspamd_printf_fstring (&symbuf, "(%.2f)", sym->score);
  941. }
  942. if (lf->flags & RSPAMD_LOG_FMT_FLAG_SYMBOLS_PARAMS) {
  943. rspamd_printf_fstring (&symbuf, "{");
  944. if (sym->options) {
  945. struct rspamd_symbol_option *opt;
  946. j = 0;
  947. DL_FOREACH (sym->opts_head, opt) {
  948. rspamd_printf_fstring (&symbuf, "%*s;",
  949. (gint)opt->optlen, opt->option);
  950. if (j >= max_log_elts) {
  951. rspamd_printf_fstring (&symbuf, "...;");
  952. break;
  953. }
  954. j ++;
  955. }
  956. }
  957. rspamd_printf_fstring (&symbuf, "}");
  958. }
  959. first = FALSE;
  960. }
  961. g_ptr_array_free (sorted_symbols, TRUE);
  962. rspamd_mempool_add_destructor (task->task_pool,
  963. (rspamd_mempool_destruct_t)rspamd_fstring_free,
  964. symbuf);
  965. rspamd_mempool_notify_alloc (task->task_pool, symbuf->len);
  966. res.begin = symbuf->str;
  967. res.len = symbuf->len;
  968. break;
  969. case RSPAMD_LOG_GROUPS:
  970. case RSPAMD_LOG_PUBLIC_GROUPS:
  971. symbuf = rspamd_fstring_sized_new (128);
  972. sorted_symbols = g_ptr_array_sized_new (kh_size (mres->sym_groups));
  973. kh_foreach_key (mres->sym_groups, gr,{
  974. if (!(gr->flags & RSPAMD_SYMBOL_GROUP_PUBLIC)) {
  975. if (lf->type == RSPAMD_LOG_PUBLIC_GROUPS) {
  976. continue;
  977. }
  978. }
  979. g_ptr_array_add (sorted_symbols, gr);
  980. });
  981. g_ptr_array_sort (sorted_symbols, rspamd_task_compare_log_group);
  982. for (i = 0; i < sorted_symbols->len; i++) {
  983. gr = g_ptr_array_index (sorted_symbols, i);
  984. if (first) {
  985. rspamd_printf_fstring (&symbuf, "%s", gr->name);
  986. }
  987. else {
  988. rspamd_printf_fstring (&symbuf, ",%s", gr->name);
  989. }
  990. k = kh_get (rspamd_symbols_group_hash, mres->sym_groups, gr);
  991. rspamd_printf_fstring (&symbuf, "(%.2f)",
  992. kh_value (mres->sym_groups, k));
  993. first = FALSE;
  994. }
  995. g_ptr_array_free (sorted_symbols, TRUE);
  996. rspamd_mempool_add_destructor (task->task_pool,
  997. (rspamd_mempool_destruct_t) rspamd_fstring_free,
  998. symbuf);
  999. rspamd_mempool_notify_alloc (task->task_pool, symbuf->len);
  1000. res.begin = symbuf->str;
  1001. res.len = symbuf->len;
  1002. break;
  1003. default:
  1004. break;
  1005. }
  1006. }
  1007. return res;
  1008. }
  1009. static rspamd_fstring_t *
  1010. rspamd_task_log_write_var (struct rspamd_task *task, rspamd_fstring_t *logbuf,
  1011. const rspamd_ftok_t *var, const rspamd_ftok_t *content)
  1012. {
  1013. rspamd_fstring_t *res = logbuf;
  1014. const gchar *p, *c, *end;
  1015. if (content == NULL) {
  1016. /* Just output variable */
  1017. res = rspamd_fstring_append (res, var->begin, var->len);
  1018. }
  1019. else {
  1020. /* Replace $ with variable value */
  1021. p = content->begin;
  1022. c = p;
  1023. end = p + content->len;
  1024. while (p < end) {
  1025. if (*p == '$') {
  1026. if (p > c) {
  1027. res = rspamd_fstring_append (res, c, p - c);
  1028. }
  1029. res = rspamd_fstring_append (res, var->begin, var->len);
  1030. p ++;
  1031. c = p;
  1032. }
  1033. else {
  1034. p ++;
  1035. }
  1036. }
  1037. if (p > c) {
  1038. res = rspamd_fstring_append (res, c, p - c);
  1039. }
  1040. }
  1041. return res;
  1042. }
  1043. static rspamd_fstring_t *
  1044. rspamd_task_write_ialist (struct rspamd_task *task,
  1045. GPtrArray *addrs, gint lim,
  1046. struct rspamd_log_format *lf,
  1047. rspamd_fstring_t *logbuf)
  1048. {
  1049. rspamd_fstring_t *res = logbuf, *varbuf;
  1050. rspamd_ftok_t var = {.begin = NULL, .len = 0};
  1051. struct rspamd_email_address *addr;
  1052. gint i, nchars = 0, wr = 0, cur_chars;
  1053. gboolean has_orig = FALSE;
  1054. if (addrs && lim <= 0) {
  1055. lim = addrs->len;
  1056. }
  1057. PTR_ARRAY_FOREACH (addrs, i, addr) {
  1058. if (addr->flags & RSPAMD_EMAIL_ADDR_ORIGINAL) {
  1059. has_orig = TRUE;
  1060. break;
  1061. }
  1062. }
  1063. varbuf = rspamd_fstring_new ();
  1064. PTR_ARRAY_FOREACH (addrs, i, addr) {
  1065. if (wr >= lim) {
  1066. break;
  1067. }
  1068. if (has_orig) {
  1069. /* Report merely original addresses */
  1070. if (!(addr->flags & RSPAMD_EMAIL_ADDR_ORIGINAL)) {
  1071. continue;
  1072. }
  1073. }
  1074. cur_chars = addr->addr_len;
  1075. varbuf = rspamd_fstring_append (varbuf, addr->addr,
  1076. cur_chars);
  1077. nchars += cur_chars;
  1078. wr ++;
  1079. if (varbuf->len > 0) {
  1080. if (i != lim - 1) {
  1081. varbuf = rspamd_fstring_append (varbuf, ",", 1);
  1082. }
  1083. }
  1084. if (wr >= max_log_elts || nchars >= max_log_elts * 10) {
  1085. varbuf = rspamd_fstring_append (varbuf, "...", 3);
  1086. break;
  1087. }
  1088. }
  1089. if (varbuf->len > 0) {
  1090. var.begin = varbuf->str;
  1091. var.len = varbuf->len;
  1092. res = rspamd_task_log_write_var (task, logbuf,
  1093. &var, (const rspamd_ftok_t *) lf->data);
  1094. }
  1095. rspamd_fstring_free (varbuf);
  1096. return res;
  1097. }
  1098. static rspamd_fstring_t *
  1099. rspamd_task_write_addr_list (struct rspamd_task *task,
  1100. GPtrArray *addrs, gint lim,
  1101. struct rspamd_log_format *lf,
  1102. rspamd_fstring_t *logbuf)
  1103. {
  1104. rspamd_fstring_t *res = logbuf, *varbuf;
  1105. rspamd_ftok_t var = {.begin = NULL, .len = 0};
  1106. struct rspamd_email_address *addr;
  1107. gint i;
  1108. if (lim <= 0) {
  1109. lim = addrs->len;
  1110. }
  1111. varbuf = rspamd_fstring_new ();
  1112. for (i = 0; i < lim; i++) {
  1113. addr = g_ptr_array_index (addrs, i);
  1114. if (addr->addr) {
  1115. varbuf = rspamd_fstring_append (varbuf, addr->addr, addr->addr_len);
  1116. }
  1117. if (varbuf->len > 0) {
  1118. if (i != lim - 1) {
  1119. varbuf = rspamd_fstring_append (varbuf, ",", 1);
  1120. }
  1121. }
  1122. if (i >= max_log_elts) {
  1123. varbuf = rspamd_fstring_append (varbuf, "...", 3);
  1124. break;
  1125. }
  1126. }
  1127. if (varbuf->len > 0) {
  1128. var.begin = varbuf->str;
  1129. var.len = varbuf->len;
  1130. res = rspamd_task_log_write_var (task, logbuf,
  1131. &var, (const rspamd_ftok_t *) lf->data);
  1132. }
  1133. rspamd_fstring_free (varbuf);
  1134. return res;
  1135. }
  1136. static rspamd_fstring_t *
  1137. rspamd_task_log_variable (struct rspamd_task *task,
  1138. struct rspamd_log_format *lf, rspamd_fstring_t *logbuf)
  1139. {
  1140. rspamd_fstring_t *res = logbuf;
  1141. rspamd_ftok_t var = {.begin = NULL, .len = 0};
  1142. static gchar numbuf[128];
  1143. static const gchar undef[] = "undef";
  1144. switch (lf->type) {
  1145. /* String vars */
  1146. case RSPAMD_LOG_MID:
  1147. if (MESSAGE_FIELD_CHECK (task, message_id)) {
  1148. var.begin = MESSAGE_FIELD (task, message_id);
  1149. var.len = strlen (var.begin);
  1150. }
  1151. else {
  1152. var.begin = undef;
  1153. var.len = sizeof (undef) - 1;
  1154. }
  1155. break;
  1156. case RSPAMD_LOG_QID:
  1157. if (task->queue_id) {
  1158. var.begin = task->queue_id;
  1159. var.len = strlen (var.begin);
  1160. }
  1161. else {
  1162. var.begin = undef;
  1163. var.len = sizeof (undef) - 1;
  1164. }
  1165. break;
  1166. case RSPAMD_LOG_USER:
  1167. if (task->user) {
  1168. var.begin = task->user;
  1169. var.len = strlen (var.begin);
  1170. }
  1171. else {
  1172. var.begin = undef;
  1173. var.len = sizeof (undef) - 1;
  1174. }
  1175. break;
  1176. case RSPAMD_LOG_IP:
  1177. if (task->from_addr && rspamd_ip_is_valid (task->from_addr)) {
  1178. var.begin = rspamd_inet_address_to_string (task->from_addr);
  1179. var.len = strlen (var.begin);
  1180. }
  1181. else {
  1182. var.begin = undef;
  1183. var.len = sizeof (undef) - 1;
  1184. }
  1185. break;
  1186. /* Numeric vars */
  1187. case RSPAMD_LOG_LEN:
  1188. var.len = rspamd_snprintf (numbuf, sizeof (numbuf), "%uz",
  1189. task->msg.len);
  1190. var.begin = numbuf;
  1191. break;
  1192. case RSPAMD_LOG_DNS_REQ:
  1193. var.len = rspamd_snprintf (numbuf, sizeof (numbuf), "%uD",
  1194. task->dns_requests);
  1195. var.begin = numbuf;
  1196. break;
  1197. case RSPAMD_LOG_TIME_REAL:
  1198. var.begin = rspamd_log_check_time (task->task_timestamp,
  1199. task->time_real_finish,
  1200. task->cfg->clock_res);
  1201. var.len = strlen (var.begin);
  1202. break;
  1203. case RSPAMD_LOG_TIME_VIRTUAL:
  1204. var.begin = rspamd_log_check_time (task->task_timestamp,
  1205. task->time_real_finish,
  1206. task->cfg->clock_res);
  1207. var.len = strlen (var.begin);
  1208. break;
  1209. /* InternetAddress vars */
  1210. case RSPAMD_LOG_SMTP_FROM:
  1211. if (task->from_envelope) {
  1212. var.begin = task->from_envelope->addr;
  1213. var.len = task->from_envelope->addr_len;
  1214. }
  1215. break;
  1216. case RSPAMD_LOG_MIME_FROM:
  1217. if (MESSAGE_FIELD_CHECK (task, from_mime)) {
  1218. return rspamd_task_write_ialist (task,
  1219. MESSAGE_FIELD (task, from_mime),
  1220. 1,
  1221. lf,
  1222. logbuf);
  1223. }
  1224. break;
  1225. case RSPAMD_LOG_SMTP_RCPT:
  1226. if (task->rcpt_envelope) {
  1227. return rspamd_task_write_addr_list (task, task->rcpt_envelope, 1, lf,
  1228. logbuf);
  1229. }
  1230. break;
  1231. case RSPAMD_LOG_MIME_RCPT:
  1232. if (MESSAGE_FIELD_CHECK (task, rcpt_mime)) {
  1233. return rspamd_task_write_ialist (task,
  1234. MESSAGE_FIELD (task, rcpt_mime),
  1235. 1,
  1236. lf,
  1237. logbuf);
  1238. }
  1239. break;
  1240. case RSPAMD_LOG_SMTP_RCPTS:
  1241. if (task->rcpt_envelope) {
  1242. return rspamd_task_write_addr_list (task, task->rcpt_envelope, -1, lf,
  1243. logbuf);
  1244. }
  1245. break;
  1246. case RSPAMD_LOG_MIME_RCPTS:
  1247. if (MESSAGE_FIELD_CHECK (task, rcpt_mime)) {
  1248. return rspamd_task_write_ialist (task,
  1249. MESSAGE_FIELD (task, rcpt_mime),
  1250. -1, /* All addresses */
  1251. lf,
  1252. logbuf);
  1253. }
  1254. break;
  1255. case RSPAMD_LOG_DIGEST:
  1256. if (task->message) {
  1257. var.len = rspamd_snprintf (numbuf, sizeof (numbuf), "%*xs",
  1258. (gint) sizeof (MESSAGE_FIELD (task, digest)),
  1259. MESSAGE_FIELD (task, digest));
  1260. var.begin = numbuf;
  1261. }
  1262. else {
  1263. var.begin = undef;
  1264. var.len = sizeof (undef) - 1;
  1265. }
  1266. break;
  1267. case RSPAMD_LOG_FILENAME:
  1268. if (task->msg.fpath) {
  1269. var.len = strlen (task->msg.fpath);
  1270. var.begin = task->msg.fpath;
  1271. }
  1272. else {
  1273. var.begin = undef;
  1274. var.len = sizeof (undef) - 1;
  1275. }
  1276. break;
  1277. case RSPAMD_LOG_FORCED_ACTION:
  1278. if (task->result->passthrough_result) {
  1279. struct rspamd_passthrough_result *pr = task->result->passthrough_result;
  1280. if (!isnan (pr->target_score)) {
  1281. var.len = rspamd_snprintf (numbuf, sizeof (numbuf),
  1282. "%s \"%s\"; score=%.2f (set by %s)",
  1283. pr->action->name,
  1284. pr->message,
  1285. pr->target_score,
  1286. pr->module);
  1287. }
  1288. else {
  1289. var.len = rspamd_snprintf (numbuf, sizeof (numbuf),
  1290. "%s \"%s\"; score=nan (set by %s)",
  1291. pr->action->name,
  1292. pr->message,
  1293. pr->module);
  1294. }
  1295. var.begin = numbuf;
  1296. }
  1297. else {
  1298. var.begin = undef;
  1299. var.len = sizeof (undef) - 1;
  1300. }
  1301. break;
  1302. case RSPAMD_LOG_SETTINGS_ID:
  1303. if (task->settings_elt) {
  1304. var.begin = task->settings_elt->name;
  1305. var.len = strlen (task->settings_elt->name);
  1306. }
  1307. else {
  1308. var.begin = undef;
  1309. var.len = sizeof (undef) - 1;
  1310. }
  1311. break;
  1312. case RSPAMD_LOG_MEMPOOL_SIZE:
  1313. var.len = rspamd_snprintf (numbuf, sizeof (numbuf),
  1314. "%Hz",
  1315. rspamd_mempool_get_used_size (task->task_pool));
  1316. var.begin = numbuf;
  1317. break;
  1318. case RSPAMD_LOG_MEMPOOL_WASTE:
  1319. var.len = rspamd_snprintf (numbuf, sizeof (numbuf),
  1320. "%Hz",
  1321. rspamd_mempool_get_wasted_size (task->task_pool));
  1322. var.begin = numbuf;
  1323. break;
  1324. default:
  1325. var = rspamd_task_log_metric_res (task, lf);
  1326. break;
  1327. }
  1328. if (var.len > 0) {
  1329. res = rspamd_task_log_write_var (task, logbuf,
  1330. &var, (const rspamd_ftok_t *)lf->data);
  1331. }
  1332. return res;
  1333. }
  1334. void
  1335. rspamd_task_write_log (struct rspamd_task *task)
  1336. {
  1337. rspamd_fstring_t *logbuf;
  1338. struct rspamd_log_format *lf;
  1339. struct rspamd_task **ptask;
  1340. const gchar *lua_str;
  1341. gsize lua_str_len;
  1342. lua_State *L;
  1343. g_assert (task != NULL);
  1344. if (task->cfg->log_format == NULL ||
  1345. (task->flags & RSPAMD_TASK_FLAG_NO_LOG)) {
  1346. msg_debug_task ("skip logging due to no log flag");
  1347. return;
  1348. }
  1349. logbuf = rspamd_fstring_sized_new (1000);
  1350. DL_FOREACH (task->cfg->log_format, lf) {
  1351. switch (lf->type) {
  1352. case RSPAMD_LOG_STRING:
  1353. logbuf = rspamd_fstring_append (logbuf, lf->data, lf->len);
  1354. break;
  1355. case RSPAMD_LOG_LUA:
  1356. L = task->cfg->lua_state;
  1357. lua_rawgeti (L, LUA_REGISTRYINDEX, GPOINTER_TO_INT (lf->data));
  1358. ptask = lua_newuserdata (L, sizeof (*ptask));
  1359. rspamd_lua_setclass (L, "rspamd{task}", -1);
  1360. *ptask = task;
  1361. if (lua_pcall (L, 1, 1, 0) != 0) {
  1362. msg_err_task ("call to log function failed: %s",
  1363. lua_tostring (L, -1));
  1364. lua_pop (L, 1);
  1365. }
  1366. else {
  1367. lua_str = lua_tolstring (L, -1, &lua_str_len);
  1368. if (lua_str != NULL) {
  1369. logbuf = rspamd_fstring_append (logbuf, lua_str, lua_str_len);
  1370. }
  1371. lua_pop (L, 1);
  1372. }
  1373. break;
  1374. default:
  1375. /* We have a variable in log format */
  1376. if (lf->flags & RSPAMD_LOG_FMT_FLAG_CONDITION) {
  1377. if (!rspamd_task_log_check_condition (task, lf)) {
  1378. continue;
  1379. }
  1380. }
  1381. logbuf = rspamd_task_log_variable (task, lf, logbuf);
  1382. break;
  1383. }
  1384. }
  1385. msg_notice_task ("%V", logbuf);
  1386. rspamd_fstring_free (logbuf);
  1387. }
  1388. gdouble
  1389. rspamd_task_get_required_score (struct rspamd_task *task, struct rspamd_scan_result *m)
  1390. {
  1391. gint i;
  1392. if (m == NULL) {
  1393. m = task->result;
  1394. if (m == NULL) {
  1395. return NAN;
  1396. }
  1397. }
  1398. for (i = m->nactions - 1; i >= 0; i --) {
  1399. struct rspamd_action_result *action_lim = &m->actions_limits[i];
  1400. if (!isnan (action_lim->cur_limit) &&
  1401. !(action_lim->action->flags & (RSPAMD_ACTION_NO_THRESHOLD|RSPAMD_ACTION_HAM))) {
  1402. return m->actions_limits[i].cur_limit;
  1403. }
  1404. }
  1405. return NAN;
  1406. }
  1407. rspamd_ftok_t *
  1408. rspamd_task_get_request_header (struct rspamd_task *task,
  1409. const gchar *name)
  1410. {
  1411. struct rspamd_request_header_chain *ret =
  1412. rspamd_task_get_request_header_multiple (task, name);
  1413. if (ret) {
  1414. return ret->hdr;
  1415. }
  1416. return NULL;
  1417. }
  1418. struct rspamd_request_header_chain *
  1419. rspamd_task_get_request_header_multiple (struct rspamd_task *task,
  1420. const gchar *name)
  1421. {
  1422. struct rspamd_request_header_chain *ret = NULL;
  1423. rspamd_ftok_t srch;
  1424. khiter_t k;
  1425. srch.begin = (gchar *)name;
  1426. srch.len = strlen (name);
  1427. k = kh_get (rspamd_req_headers_hash, task->request_headers,
  1428. &srch);
  1429. if (k != kh_end (task->request_headers)) {
  1430. ret = kh_value (task->request_headers, k);
  1431. }
  1432. return ret;
  1433. }
  1434. void
  1435. rspamd_task_add_request_header (struct rspamd_task *task,
  1436. rspamd_ftok_t *name, rspamd_ftok_t *value)
  1437. {
  1438. khiter_t k;
  1439. gint res;
  1440. struct rspamd_request_header_chain *chain, *nchain;
  1441. k = kh_put (rspamd_req_headers_hash, task->request_headers,
  1442. name, &res);
  1443. if (res == 0) {
  1444. /* Existing name */
  1445. nchain = rspamd_mempool_alloc (task->task_pool, sizeof (*nchain));
  1446. nchain->hdr = value;
  1447. nchain->next = NULL;
  1448. chain = kh_value (task->request_headers, k);
  1449. /* Slow but OK here */
  1450. LL_APPEND (chain, nchain);
  1451. }
  1452. else {
  1453. nchain = rspamd_mempool_alloc (task->task_pool, sizeof (*nchain));
  1454. nchain->hdr = value;
  1455. nchain->next = NULL;
  1456. kh_value (task->request_headers, k) = nchain;
  1457. }
  1458. }
  1459. void
  1460. rspamd_task_profile_set (struct rspamd_task *task, const gchar *key,
  1461. gdouble value)
  1462. {
  1463. GHashTable *tbl;
  1464. gdouble *pval;
  1465. if (key == NULL) {
  1466. return;
  1467. }
  1468. tbl = rspamd_mempool_get_variable (task->task_pool, RSPAMD_MEMPOOL_PROFILE);
  1469. if (tbl == NULL) {
  1470. tbl = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
  1471. rspamd_mempool_set_variable (task->task_pool, RSPAMD_MEMPOOL_PROFILE,
  1472. tbl, (rspamd_mempool_destruct_t)g_hash_table_unref);
  1473. }
  1474. pval = g_hash_table_lookup (tbl, key);
  1475. if (pval == NULL) {
  1476. pval = rspamd_mempool_alloc (task->task_pool, sizeof (*pval));
  1477. *pval = value;
  1478. g_hash_table_insert (tbl, (void *)key, pval);
  1479. }
  1480. else {
  1481. *pval = value;
  1482. }
  1483. }
  1484. gdouble*
  1485. rspamd_task_profile_get (struct rspamd_task *task, const gchar *key)
  1486. {
  1487. GHashTable *tbl;
  1488. gdouble *pval = NULL;
  1489. tbl = rspamd_mempool_get_variable (task->task_pool, RSPAMD_MEMPOOL_PROFILE);
  1490. if (tbl != NULL) {
  1491. pval = g_hash_table_lookup (tbl, key);
  1492. }
  1493. return pval;
  1494. }
  1495. gboolean
  1496. rspamd_task_set_finish_time (struct rspamd_task *task)
  1497. {
  1498. if (isnan (task->time_real_finish)) {
  1499. task->time_real_finish = ev_time ();
  1500. return TRUE;
  1501. }
  1502. return FALSE;
  1503. }
  1504. const gchar *
  1505. rspamd_task_stage_name (enum rspamd_task_stage stg)
  1506. {
  1507. const gchar *ret = "unknown stage";
  1508. switch (stg) {
  1509. case RSPAMD_TASK_STAGE_CONNECT:
  1510. ret = "connect";
  1511. break;
  1512. case RSPAMD_TASK_STAGE_CONNFILTERS:
  1513. ret = "connection_filter";
  1514. break;
  1515. case RSPAMD_TASK_STAGE_READ_MESSAGE:
  1516. ret = "read_message";
  1517. break;
  1518. case RSPAMD_TASK_STAGE_PRE_FILTERS:
  1519. ret = "prefilters";
  1520. break;
  1521. case RSPAMD_TASK_STAGE_PROCESS_MESSAGE:
  1522. ret = "process_message";
  1523. break;
  1524. case RSPAMD_TASK_STAGE_FILTERS:
  1525. ret = "filters";
  1526. break;
  1527. case RSPAMD_TASK_STAGE_CLASSIFIERS_PRE:
  1528. ret = "classifiers_pre";
  1529. break;
  1530. case RSPAMD_TASK_STAGE_CLASSIFIERS:
  1531. ret = "classifiers";
  1532. break;
  1533. case RSPAMD_TASK_STAGE_CLASSIFIERS_POST:
  1534. ret = "classifiers_post";
  1535. break;
  1536. case RSPAMD_TASK_STAGE_COMPOSITES:
  1537. ret = "composites";
  1538. break;
  1539. case RSPAMD_TASK_STAGE_POST_FILTERS:
  1540. ret = "postfilters";
  1541. break;
  1542. case RSPAMD_TASK_STAGE_LEARN_PRE:
  1543. ret = "learn_pre";
  1544. break;
  1545. case RSPAMD_TASK_STAGE_LEARN:
  1546. ret = "learn";
  1547. break;
  1548. case RSPAMD_TASK_STAGE_LEARN_POST:
  1549. ret = "learn_post";
  1550. break;
  1551. case RSPAMD_TASK_STAGE_COMPOSITES_POST:
  1552. ret = "composites_post";
  1553. break;
  1554. case RSPAMD_TASK_STAGE_IDEMPOTENT:
  1555. ret = "idempotent";
  1556. break;
  1557. case RSPAMD_TASK_STAGE_DONE:
  1558. ret = "done";
  1559. break;
  1560. case RSPAMD_TASK_STAGE_REPLIED:
  1561. ret = "replied";
  1562. break;
  1563. default:
  1564. break;
  1565. }
  1566. return ret;
  1567. }
  1568. void
  1569. rspamd_task_timeout (EV_P_ ev_timer *w, int revents)
  1570. {
  1571. struct rspamd_task *task = (struct rspamd_task *)w->data;
  1572. if (!(task->processed_stages & RSPAMD_TASK_STAGE_FILTERS)) {
  1573. ev_now_update_if_cheap (task->event_loop);
  1574. msg_info_task ("processing of task time out: %.1fs spent; %.1fs limit; "
  1575. "forced processing",
  1576. ev_now (task->event_loop) - task->task_timestamp,
  1577. w->repeat);
  1578. if (task->cfg->soft_reject_on_timeout) {
  1579. struct rspamd_action *action, *soft_reject;
  1580. action = rspamd_check_action_metric (task, NULL, NULL);
  1581. if (action->action_type != METRIC_ACTION_REJECT) {
  1582. soft_reject = rspamd_config_get_action_by_type (task->cfg,
  1583. METRIC_ACTION_SOFT_REJECT);
  1584. rspamd_add_passthrough_result (task,
  1585. soft_reject,
  1586. 0,
  1587. NAN,
  1588. "timeout processing message",
  1589. "task timeout",
  1590. 0, NULL);
  1591. }
  1592. }
  1593. ev_timer_again (EV_A_ w);
  1594. task->processed_stages |= RSPAMD_TASK_STAGE_FILTERS;
  1595. rspamd_session_cleanup (task->s);
  1596. rspamd_task_process (task, RSPAMD_TASK_PROCESS_ALL);
  1597. rspamd_session_pending (task->s);
  1598. }
  1599. else {
  1600. /* Postprocessing timeout */
  1601. msg_info_task ("post-processing of task time out: %.1f second spent; forced processing",
  1602. ev_now (task->event_loop) - task->task_timestamp);
  1603. if (task->cfg->soft_reject_on_timeout) {
  1604. struct rspamd_action *action, *soft_reject;
  1605. action = rspamd_check_action_metric (task, NULL, NULL);
  1606. if (action->action_type != METRIC_ACTION_REJECT) {
  1607. soft_reject = rspamd_config_get_action_by_type (task->cfg,
  1608. METRIC_ACTION_SOFT_REJECT);
  1609. rspamd_add_passthrough_result (task,
  1610. soft_reject,
  1611. 0,
  1612. NAN,
  1613. "timeout post-processing message",
  1614. "task timeout",
  1615. 0, NULL);
  1616. }
  1617. }
  1618. ev_timer_stop (EV_A_ w);
  1619. task->processed_stages |= RSPAMD_TASK_STAGE_DONE;
  1620. rspamd_session_cleanup (task->s);
  1621. rspamd_task_process (task, RSPAMD_TASK_PROCESS_ALL);
  1622. rspamd_session_pending (task->s);
  1623. }
  1624. }
  1625. void
  1626. rspamd_worker_guard_handler (EV_P_ ev_io *w, int revents)
  1627. {
  1628. struct rspamd_task *task = (struct rspamd_task *)w->data;
  1629. gchar fake_buf[1024];
  1630. gssize r;
  1631. r = read (w->fd, fake_buf, sizeof (fake_buf));
  1632. if (r > 0) {
  1633. msg_warn_task ("received extra data after task is loaded, ignoring");
  1634. }
  1635. else {
  1636. if (r == 0) {
  1637. /*
  1638. * Poor man approach, that might break things in case of
  1639. * shutdown (SHUT_WR) but sockets are so bad that there's no
  1640. * reliable way to distinguish between shutdown(SHUT_WR) and
  1641. * close.
  1642. */
  1643. if (task->cmd != CMD_CHECK_V2 && task->cfg->enable_shutdown_workaround) {
  1644. msg_info_task ("workaround for shutdown enabled, please update "
  1645. "your client, this support might be removed in future");
  1646. shutdown (w->fd, SHUT_RD);
  1647. ev_io_stop (task->event_loop, &task->guard_ev);
  1648. }
  1649. else {
  1650. msg_err_task ("the peer has closed connection unexpectedly");
  1651. rspamd_session_destroy (task->s);
  1652. }
  1653. }
  1654. else if (errno != EAGAIN) {
  1655. msg_err_task ("the peer has closed connection unexpectedly: %s",
  1656. strerror (errno));
  1657. rspamd_session_destroy (task->s);
  1658. }
  1659. else {
  1660. return;
  1661. }
  1662. }
  1663. }