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.

dkim_check.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  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. /***MODULE:dkim
  17. * rspamd module that checks dkim records of incoming email
  18. *
  19. * Allowed options:
  20. * - symbol_allow (string): symbol to insert in case of allow (default: 'R_DKIM_ALLOW')
  21. * - symbol_reject (string): symbol to insert (default: 'R_DKIM_REJECT')
  22. * - symbol_tempfail (string): symbol to insert in case of temporary fail (default: 'R_DKIM_TEMPFAIL')
  23. * - whitelist (map): map of whitelisted networks
  24. * - domains (map): map of domains to check
  25. * - strict_multiplier (number): multiplier for strict domains
  26. * - time_jitter (number): jitter in seconds to allow time diff while checking
  27. * - trusted_only (flag): check signatures only for domains in 'domains' map
  28. * - skip_mutli (flag): skip messages with multiply dkim signatures
  29. */
  30. #include "config.h"
  31. #include "libmime/message.h"
  32. #include "libserver/dkim.h"
  33. #include "libutil/hash.h"
  34. #include "libutil/map.h"
  35. #include "rspamd.h"
  36. #include "utlist.h"
  37. #define DEFAULT_SYMBOL_REJECT "R_DKIM_REJECT"
  38. #define DEFAULT_SYMBOL_TEMPFAIL "R_DKIM_TEMPFAIL"
  39. #define DEFAULT_SYMBOL_ALLOW "R_DKIM_ALLOW"
  40. #define DEFAULT_CACHE_SIZE 2048
  41. #define DEFAULT_CACHE_MAXAGE 86400
  42. #define DEFAULT_TIME_JITTER 60
  43. struct dkim_ctx {
  44. struct module_ctx ctx;
  45. const gchar *symbol_reject;
  46. const gchar *symbol_tempfail;
  47. const gchar *symbol_allow;
  48. rspamd_mempool_t *dkim_pool;
  49. radix_compressed_t *whitelist_ip;
  50. GHashTable *dkim_domains;
  51. guint strict_multiplier;
  52. guint time_jitter;
  53. rspamd_lru_hash_t *dkim_hash;
  54. gboolean trusted_only;
  55. gboolean skip_multi;
  56. };
  57. struct dkim_check_result {
  58. rspamd_dkim_context_t *ctx;
  59. rspamd_dkim_key_t *key;
  60. struct rspamd_task *task;
  61. gint res;
  62. gint mult_allow, mult_deny;
  63. struct rspamd_async_watcher *w;
  64. struct dkim_check_result *next, *prev, *first;
  65. };
  66. static struct dkim_ctx *dkim_module_ctx = NULL;
  67. static void dkim_symbol_callback (struct rspamd_task *task, void *unused);
  68. /* Initialization */
  69. gint dkim_module_init (struct rspamd_config *cfg, struct module_ctx **ctx);
  70. gint dkim_module_config (struct rspamd_config *cfg);
  71. gint dkim_module_reconfig (struct rspamd_config *cfg);
  72. module_t dkim_module = {
  73. "dkim",
  74. dkim_module_init,
  75. dkim_module_config,
  76. dkim_module_reconfig,
  77. NULL,
  78. RSPAMD_MODULE_VER
  79. };
  80. static void
  81. dkim_module_key_dtor (gpointer k)
  82. {
  83. rspamd_dkim_key_t *key = k;
  84. REF_RELEASE (key);
  85. }
  86. gint
  87. dkim_module_init (struct rspamd_config *cfg, struct module_ctx **ctx)
  88. {
  89. dkim_module_ctx = g_malloc0 (sizeof (struct dkim_ctx));
  90. dkim_module_ctx->dkim_pool = rspamd_mempool_new (rspamd_mempool_suggest_size (), NULL);
  91. *ctx = (struct module_ctx *)dkim_module_ctx;
  92. rspamd_rcl_add_doc_by_path (cfg,
  93. NULL,
  94. "DKIM check plugin",
  95. "dkim",
  96. UCL_OBJECT,
  97. NULL,
  98. 0,
  99. NULL,
  100. 0);
  101. rspamd_rcl_add_doc_by_path (cfg,
  102. "dkim",
  103. "Map of IP addresses that should be excluded from DKIM checks",
  104. "whitelist",
  105. UCL_STRING,
  106. NULL,
  107. 0,
  108. NULL,
  109. 0);
  110. rspamd_rcl_add_doc_by_path (cfg,
  111. "dkim",
  112. "Symbol that is added if DKIM check is successful",
  113. "symbol_allow",
  114. UCL_STRING,
  115. NULL,
  116. 0,
  117. NULL,
  118. 0);
  119. rspamd_rcl_add_doc_by_path (cfg,
  120. "dkim",
  121. "Symbol that is added if DKIM check is unsuccessful",
  122. "symbol_reject",
  123. UCL_STRING,
  124. NULL,
  125. 0,
  126. NULL,
  127. 0);
  128. rspamd_rcl_add_doc_by_path (cfg,
  129. "dkim",
  130. "Symbol that is added if DKIM check can't be completed (e.g. DNS failure)",
  131. "symbol_tempfail",
  132. UCL_STRING,
  133. NULL,
  134. 0,
  135. NULL,
  136. 0);
  137. rspamd_rcl_add_doc_by_path (cfg,
  138. "dkim",
  139. "Size of DKIM keys cache",
  140. "dkim_cache_size",
  141. UCL_INT,
  142. NULL,
  143. 0,
  144. NULL,
  145. 0);
  146. rspamd_rcl_add_doc_by_path (cfg,
  147. "dkim",
  148. "Allow this time difference when checking DKIM signature time validity",
  149. "time_jitter",
  150. UCL_TIME,
  151. NULL,
  152. 0,
  153. NULL,
  154. 0);
  155. rspamd_rcl_add_doc_by_path (cfg,
  156. "dkim",
  157. "Domains to check DKIM for (check all domains if this option is empty)",
  158. "domains",
  159. UCL_STRING,
  160. NULL,
  161. 0,
  162. NULL,
  163. 0);
  164. rspamd_rcl_add_doc_by_path (cfg,
  165. "dkim",
  166. "Map of domains that are treated as 'trusted' meaning that DKIM policy failure has more significant score",
  167. "trusted_domains",
  168. UCL_STRING,
  169. NULL,
  170. 0,
  171. NULL,
  172. 0);
  173. rspamd_rcl_add_doc_by_path (cfg,
  174. "dkim",
  175. "Multiply dkim score by this factor for trusted domains",
  176. "strict_multiplier",
  177. UCL_FLOAT,
  178. NULL,
  179. 0,
  180. NULL,
  181. 0);
  182. rspamd_rcl_add_doc_by_path (cfg,
  183. "dkim",
  184. "Check DKIM policies merely for `trusted_domains`",
  185. "trusted_only",
  186. UCL_BOOLEAN,
  187. NULL,
  188. 0,
  189. NULL,
  190. 0);
  191. rspamd_rcl_add_doc_by_path (cfg,
  192. "dkim",
  193. "Do not check messages with multiple DKIM signatures",
  194. "skip_multi",
  195. UCL_BOOLEAN,
  196. NULL,
  197. 0,
  198. NULL,
  199. 0);
  200. return 0;
  201. }
  202. gint
  203. dkim_module_config (struct rspamd_config *cfg)
  204. {
  205. const ucl_object_t *value;
  206. gint res = TRUE, cb_id;
  207. guint cache_size, cache_expire;
  208. gboolean got_trusted = FALSE;
  209. if (!rspamd_config_is_module_enabled (cfg, "dkim")) {
  210. return TRUE;
  211. }
  212. dkim_module_ctx->whitelist_ip = radix_create_compressed ();
  213. if ((value =
  214. rspamd_config_get_module_opt (cfg, "dkim", "symbol_reject")) != NULL) {
  215. dkim_module_ctx->symbol_reject = ucl_obj_tostring (value);
  216. }
  217. else {
  218. dkim_module_ctx->symbol_reject = DEFAULT_SYMBOL_REJECT;
  219. }
  220. if ((value =
  221. rspamd_config_get_module_opt (cfg, "dkim",
  222. "symbol_tempfail")) != NULL) {
  223. dkim_module_ctx->symbol_tempfail = ucl_obj_tostring (value);
  224. }
  225. else {
  226. dkim_module_ctx->symbol_tempfail = DEFAULT_SYMBOL_TEMPFAIL;
  227. }
  228. if ((value =
  229. rspamd_config_get_module_opt (cfg, "dkim", "symbol_allow")) != NULL) {
  230. dkim_module_ctx->symbol_allow = ucl_obj_tostring (value);
  231. }
  232. else {
  233. dkim_module_ctx->symbol_allow = DEFAULT_SYMBOL_ALLOW;
  234. }
  235. if ((value =
  236. rspamd_config_get_module_opt (cfg, "dkim",
  237. "dkim_cache_size")) != NULL) {
  238. cache_size = ucl_obj_toint (value);
  239. }
  240. else {
  241. cache_size = DEFAULT_CACHE_SIZE;
  242. }
  243. if ((value =
  244. rspamd_config_get_module_opt (cfg, "dkim",
  245. "dkim_cache_expire")) != NULL) {
  246. cache_expire = ucl_obj_todouble (value);
  247. }
  248. else {
  249. cache_expire = DEFAULT_CACHE_MAXAGE;
  250. }
  251. if ((value =
  252. rspamd_config_get_module_opt (cfg, "dkim", "time_jitter")) != NULL) {
  253. dkim_module_ctx->time_jitter = ucl_obj_todouble (value);
  254. }
  255. else {
  256. dkim_module_ctx->time_jitter = DEFAULT_TIME_JITTER;
  257. }
  258. if ((value =
  259. rspamd_config_get_module_opt (cfg, "dkim", "whitelist")) != NULL) {
  260. if (!rspamd_map_add (cfg, ucl_obj_tostring (value),
  261. "DKIM whitelist", rspamd_radix_read, rspamd_radix_fin,
  262. (void **)&dkim_module_ctx->whitelist_ip)) {
  263. radix_add_generic_iplist (ucl_obj_tostring (value),
  264. &dkim_module_ctx->whitelist_ip);
  265. }
  266. }
  267. if ((value =
  268. rspamd_config_get_module_opt (cfg, "dkim", "domains")) != NULL) {
  269. if (!rspamd_map_add (cfg, ucl_obj_tostring (value),
  270. "DKIM domains", rspamd_kv_list_read, rspamd_kv_list_fin,
  271. (void **)&dkim_module_ctx->dkim_domains)) {
  272. msg_warn_config ("cannot load dkim domains list from %s",
  273. ucl_obj_tostring (value));
  274. }
  275. else {
  276. got_trusted = TRUE;
  277. }
  278. }
  279. if (!got_trusted && (value =
  280. rspamd_config_get_module_opt (cfg, "dkim", "trusted_domains")) != NULL) {
  281. if (!rspamd_map_add (cfg, ucl_obj_tostring (value),
  282. "DKIM domains", rspamd_kv_list_read, rspamd_kv_list_fin,
  283. (void **)&dkim_module_ctx->dkim_domains)) {
  284. msg_warn_config ("cannot load dkim domains list from %s",
  285. ucl_obj_tostring (value));
  286. }
  287. else {
  288. got_trusted = TRUE;
  289. }
  290. }
  291. if ((value =
  292. rspamd_config_get_module_opt (cfg, "dkim",
  293. "strict_multiplier")) != NULL) {
  294. dkim_module_ctx->strict_multiplier = ucl_obj_toint (value);
  295. }
  296. else {
  297. dkim_module_ctx->strict_multiplier = 1;
  298. }
  299. if ((value =
  300. rspamd_config_get_module_opt (cfg, "dkim", "trusted_only")) != NULL) {
  301. dkim_module_ctx->trusted_only = ucl_obj_toboolean (value);
  302. }
  303. else {
  304. dkim_module_ctx->trusted_only = FALSE;
  305. }
  306. if ((value =
  307. rspamd_config_get_module_opt (cfg, "dkim", "skip_multi")) != NULL) {
  308. dkim_module_ctx->skip_multi = ucl_obj_toboolean (value);
  309. }
  310. else {
  311. dkim_module_ctx->skip_multi = FALSE;
  312. }
  313. if (dkim_module_ctx->trusted_only && !got_trusted) {
  314. msg_err_config (
  315. "trusted_only option is set and no trusted domains are defined; disabling dkim module completely as it is useless in this case");
  316. }
  317. else {
  318. cb_id = rspamd_symbols_cache_add_symbol (cfg->cache,
  319. dkim_module_ctx->symbol_reject,
  320. 0,
  321. dkim_symbol_callback,
  322. NULL,
  323. SYMBOL_TYPE_NORMAL|SYMBOL_TYPE_FINE,
  324. -1);
  325. rspamd_symbols_cache_add_symbol (cfg->cache,
  326. dkim_module_ctx->symbol_tempfail,
  327. 0,
  328. NULL, NULL,
  329. SYMBOL_TYPE_VIRTUAL|SYMBOL_TYPE_FINE,
  330. cb_id);
  331. rspamd_symbols_cache_add_symbol (cfg->cache,
  332. dkim_module_ctx->symbol_allow,
  333. 0,
  334. NULL, NULL,
  335. SYMBOL_TYPE_VIRTUAL|SYMBOL_TYPE_FINE,
  336. cb_id);
  337. dkim_module_ctx->dkim_hash = rspamd_lru_hash_new (
  338. cache_size,
  339. cache_expire,
  340. g_free, /* Keys are just C-strings */
  341. dkim_module_key_dtor);
  342. msg_info_config ("init internal dkim module");
  343. #ifndef HAVE_OPENSSL
  344. msg_warn_config (
  345. "openssl is not found so dkim rsa check is disabled, only check body hash, it is NOT safe to trust these results");
  346. #endif
  347. }
  348. return res;
  349. }
  350. gint
  351. dkim_module_reconfig (struct rspamd_config *cfg)
  352. {
  353. struct module_ctx saved_ctx;
  354. saved_ctx = dkim_module_ctx->ctx;
  355. rspamd_mempool_delete (dkim_module_ctx->dkim_pool);
  356. radix_destroy_compressed (dkim_module_ctx->whitelist_ip);
  357. if (dkim_module_ctx->dkim_domains) {
  358. g_hash_table_destroy (dkim_module_ctx->dkim_domains);
  359. }
  360. memset (dkim_module_ctx, 0, sizeof (*dkim_module_ctx));
  361. dkim_module_ctx->ctx = saved_ctx;
  362. dkim_module_ctx->dkim_pool = rspamd_mempool_new (rspamd_mempool_suggest_size (), NULL);
  363. return dkim_module_config (cfg);
  364. }
  365. /*
  366. * Parse strict value for domain in format: 'reject_multiplier:deny_multiplier'
  367. */
  368. static gboolean
  369. dkim_module_parse_strict (const gchar *value, gint *allow, gint *deny)
  370. {
  371. const gchar *colon;
  372. gulong val;
  373. colon = strchr (value, ':');
  374. if (colon) {
  375. if (rspamd_strtoul (value, colon - value, &val)) {
  376. *deny = val;
  377. colon++;
  378. if (rspamd_strtoul (colon, strlen (colon), &val)) {
  379. *allow = val;
  380. return TRUE;
  381. }
  382. }
  383. }
  384. return FALSE;
  385. }
  386. static void
  387. dkim_module_check (struct dkim_check_result *res)
  388. {
  389. gboolean all_done = TRUE, got_allow = FALSE;
  390. const gchar *strict_value;
  391. struct dkim_check_result *first, *cur, *sel = NULL;
  392. struct rspamd_task *task = res->task;
  393. first = res->first;
  394. DL_FOREACH (first, cur) {
  395. if (cur->ctx == NULL) {
  396. continue;
  397. }
  398. if (cur->key != NULL && cur->res == -1) {
  399. msg_debug_task ("check dkim signature for %s domain from %s",
  400. cur->ctx->domain,
  401. cur->ctx->dns_key);
  402. cur->res = rspamd_dkim_check (cur->ctx, cur->key, cur->task);
  403. if (dkim_module_ctx->dkim_domains != NULL) {
  404. /* Perform strict check */
  405. if ((strict_value =
  406. g_hash_table_lookup (dkim_module_ctx->dkim_domains,
  407. cur->ctx->domain)) != NULL) {
  408. if (!dkim_module_parse_strict (strict_value, &cur->mult_allow,
  409. &cur->mult_deny)) {
  410. cur->mult_allow = dkim_module_ctx->strict_multiplier;
  411. cur->mult_deny = dkim_module_ctx->strict_multiplier;
  412. }
  413. }
  414. }
  415. }
  416. if (cur->res == -1 || cur->key == NULL) {
  417. /* Still need a key */
  418. all_done = FALSE;
  419. }
  420. }
  421. if (all_done) {
  422. DL_FOREACH (first, cur) {
  423. if (cur->ctx == NULL) {
  424. continue;
  425. }
  426. if (cur->res == DKIM_CONTINUE) {
  427. rspamd_task_insert_result (cur->task,
  428. dkim_module_ctx->symbol_allow,
  429. cur->mult_allow * 1.0,
  430. g_list_prepend (NULL,
  431. rspamd_mempool_strdup (cur->task->task_pool,
  432. cur->ctx->domain)));
  433. got_allow = TRUE;
  434. sel = NULL;
  435. }
  436. else if (!got_allow) {
  437. if (sel == NULL) {
  438. sel = cur;
  439. }
  440. else if (sel->res == DKIM_TRYAGAIN && cur->res != DKIM_TRYAGAIN) {
  441. sel = cur;
  442. }
  443. }
  444. }
  445. }
  446. if (sel != NULL) {
  447. if (sel->res == DKIM_REJECT) {
  448. rspamd_task_insert_result (sel->task,
  449. dkim_module_ctx->symbol_reject,
  450. sel->mult_deny * 1.0,
  451. g_list_prepend (NULL,
  452. rspamd_mempool_strdup (sel->task->task_pool,
  453. sel->ctx->domain)));
  454. }
  455. else {
  456. rspamd_task_insert_result (sel->task,
  457. dkim_module_ctx->symbol_tempfail,
  458. 1.0,
  459. g_list_prepend (NULL,
  460. rspamd_mempool_strdup (sel->task->task_pool,
  461. sel->ctx->domain)));
  462. }
  463. }
  464. if (all_done) {
  465. rspamd_session_watcher_pop (res->task->s, res->w);
  466. }
  467. }
  468. static void
  469. dkim_module_key_handler (rspamd_dkim_key_t *key,
  470. gsize keylen,
  471. rspamd_dkim_context_t *ctx,
  472. gpointer ud,
  473. GError *err)
  474. {
  475. struct dkim_check_result *res = ud;
  476. if (key != NULL) {
  477. /*
  478. * We actually receive key with refcount = 1, so we just assume that
  479. * lru hash owns this object now
  480. */
  481. rspamd_lru_hash_insert (dkim_module_ctx->dkim_hash,
  482. g_strdup (ctx->dns_key),
  483. key, res->task->tv.tv_sec, key->ttl);
  484. /* Another ref belongs to the check context */
  485. res->key = key;
  486. REF_RETAIN (res->key);
  487. /* Release key when task is processed */
  488. rspamd_mempool_add_destructor (res->task->task_pool,
  489. dkim_module_key_dtor, res->key);
  490. }
  491. else {
  492. /* Insert tempfail symbol */
  493. msg_info ("cannot get key for domain %s", ctx->dns_key);
  494. if (err != NULL) {
  495. res->res = DKIM_TRYAGAIN;
  496. }
  497. }
  498. if (err) {
  499. g_error_free (err);
  500. }
  501. dkim_module_check (res);
  502. }
  503. static void
  504. dkim_symbol_callback (struct rspamd_task *task, void *unused)
  505. {
  506. GList *hlist;
  507. rspamd_dkim_context_t *ctx;
  508. rspamd_dkim_key_t *key;
  509. GError *err = NULL;
  510. struct raw_header *rh;
  511. struct dkim_check_result *res = NULL, *cur;
  512. /* First check if a message has its signature */
  513. hlist = rspamd_message_get_header (task,
  514. DKIM_SIGNHEADER,
  515. FALSE);
  516. if (hlist != NULL) {
  517. /* Check whitelist */
  518. msg_debug_task ("dkim signature found");
  519. if (radix_find_compressed_addr (dkim_module_ctx->whitelist_ip,
  520. task->from_addr) == RADIX_NO_VALUE) {
  521. /* Parse signature */
  522. msg_debug_task ("create dkim signature");
  523. while (hlist != NULL) {
  524. rh = (struct raw_header *)hlist->data;
  525. if (res == NULL) {
  526. res = rspamd_mempool_alloc0 (task->task_pool, sizeof (*res));
  527. res->prev = res;
  528. res->w = rspamd_session_get_watcher (task->s);
  529. cur = res;
  530. }
  531. else {
  532. cur = rspamd_mempool_alloc0 (task->task_pool, sizeof (*res));
  533. }
  534. cur->first = res;
  535. cur->res = -1;
  536. cur->task = task;
  537. cur->mult_allow = 1.0;
  538. cur->mult_deny = 1.0;
  539. ctx = rspamd_create_dkim_context (rh->decoded,
  540. task->task_pool,
  541. dkim_module_ctx->time_jitter,
  542. &err);
  543. if (ctx == NULL) {
  544. if (err != NULL) {
  545. msg_info_task ("<%s> cannot parse DKIM context: %s",
  546. task->message_id, err->message);
  547. g_error_free (err);
  548. err = NULL;
  549. }
  550. else {
  551. msg_info_task ("<%s> cannot parse DKIM context: "
  552. "unknown error",
  553. task->message_id);
  554. }
  555. hlist = g_list_next (hlist);
  556. continue;
  557. }
  558. else {
  559. /* Get key */
  560. cur->ctx = ctx;
  561. if (dkim_module_ctx->trusted_only &&
  562. (dkim_module_ctx->dkim_domains == NULL ||
  563. g_hash_table_lookup (dkim_module_ctx->dkim_domains,
  564. ctx->domain) == NULL)) {
  565. msg_debug_task ("skip dkim check for %s domain",
  566. ctx->domain);
  567. hlist = g_list_next (hlist);
  568. continue;
  569. }
  570. key = rspamd_lru_hash_lookup (dkim_module_ctx->dkim_hash,
  571. ctx->dns_key,
  572. task->tv.tv_sec);
  573. if (key != NULL) {
  574. debug_task ("found key for %s in cache", ctx->dns_key);
  575. cur->key = key;
  576. REF_RETAIN (cur->key);
  577. /* Release key when task is processed */
  578. rspamd_mempool_add_destructor (task->task_pool,
  579. dkim_module_key_dtor, cur->key);
  580. }
  581. else {
  582. debug_task ("request key for %s from DNS", ctx->dns_key);
  583. rspamd_get_dkim_key (ctx,
  584. task,
  585. dkim_module_key_handler,
  586. cur);
  587. }
  588. }
  589. if (res != cur) {
  590. DL_APPEND (res, cur);
  591. }
  592. hlist = g_list_next (hlist);
  593. }
  594. }
  595. }
  596. if (res != NULL) {
  597. rspamd_session_watcher_push (task->s);
  598. dkim_module_check (res);
  599. }
  600. }