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.

monitored.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  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 <contrib/librdns/rdns.h>
  17. #include "rdns.h"
  18. #include "mem_pool.h"
  19. #include "cfg_file.h"
  20. #include "cryptobox.h"
  21. #include "logger.h"
  22. #include "contrib/uthash/utlist.h"
  23. static const gdouble default_monitoring_interval = 60.0;
  24. static const guint default_max_errors = 3;
  25. struct rspamd_monitored_methods {
  26. void * (*monitored_config) (struct rspamd_monitored *m,
  27. struct rspamd_monitored_ctx *ctx,
  28. const ucl_object_t *opts);
  29. gboolean (*monitored_update) (struct rspamd_monitored *m,
  30. struct rspamd_monitored_ctx *ctx, gpointer ud);
  31. void (*monitored_dtor) (struct rspamd_monitored *m,
  32. struct rspamd_monitored_ctx *ctx, gpointer ud);
  33. gpointer ud;
  34. };
  35. struct rspamd_monitored_ctx {
  36. struct rspamd_config *cfg;
  37. struct rdns_resolver *resolver;
  38. struct ev_loop *event_loop;
  39. GPtrArray *elts;
  40. GHashTable *helts;
  41. mon_change_cb change_cb;
  42. gpointer ud;
  43. gdouble monitoring_interval;
  44. guint max_errors;
  45. gboolean initialized;
  46. };
  47. struct rspamd_monitored {
  48. gchar *url;
  49. gdouble monitoring_mult;
  50. gdouble offline_time;
  51. gdouble total_offline_time;
  52. gdouble latency;
  53. guint nchecks;
  54. guint max_errors;
  55. guint cur_errors;
  56. gboolean alive;
  57. enum rspamd_monitored_type type;
  58. enum rspamd_monitored_flags flags;
  59. struct rspamd_monitored_ctx *ctx;
  60. struct rspamd_monitored_methods proc;
  61. ev_timer periodic;
  62. gchar tag[RSPAMD_MONITORED_TAG_LEN];
  63. };
  64. #define msg_err_mon(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \
  65. "monitored", m->tag, \
  66. G_STRFUNC, \
  67. __VA_ARGS__)
  68. #define msg_warn_mon(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \
  69. "monitored", m->tag, \
  70. G_STRFUNC, \
  71. __VA_ARGS__)
  72. #define msg_info_mon(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \
  73. "monitored", m->tag, \
  74. G_STRFUNC, \
  75. __VA_ARGS__)
  76. #define msg_notice_mon(...) rspamd_default_log_function (G_LOG_LEVEL_MESSAGE, \
  77. "monitored", m->tag, \
  78. G_STRFUNC, \
  79. __VA_ARGS__)
  80. #define msg_debug_mon(...) rspamd_conditional_debug_fast (NULL, NULL, \
  81. rspamd_monitored_log_id, "monitored", m->tag, \
  82. G_STRFUNC, \
  83. __VA_ARGS__)
  84. INIT_LOG_MODULE(monitored)
  85. static inline void
  86. rspamd_monitored_propagate_error (struct rspamd_monitored *m,
  87. const gchar *error)
  88. {
  89. if (m->alive) {
  90. if (m->cur_errors < m->max_errors) {
  91. msg_debug_mon ("%s on resolving %s, %d retries left",
  92. error, m->url, m->max_errors - m->cur_errors);
  93. m->cur_errors ++;
  94. /* Reduce timeout */
  95. rspamd_monitored_stop (m);
  96. if (m->monitoring_mult > 0.1) {
  97. m->monitoring_mult /= 2.0;
  98. }
  99. rspamd_monitored_start (m);
  100. }
  101. else {
  102. msg_notice_mon ("%s on resolving %s, disable object",
  103. error, m->url);
  104. m->alive = FALSE;
  105. m->offline_time = rspamd_get_calendar_ticks ();
  106. rspamd_monitored_stop (m);
  107. m->monitoring_mult = 1.0;
  108. rspamd_monitored_start (m);
  109. if (m->ctx->change_cb) {
  110. m->ctx->change_cb (m->ctx, m, FALSE, m->ctx->ud);
  111. }
  112. }
  113. }
  114. else {
  115. if (m->monitoring_mult < 8.0) {
  116. /* Increase timeout */
  117. rspamd_monitored_stop (m);
  118. m->monitoring_mult *= 2.0;
  119. rspamd_monitored_start (m);
  120. }
  121. else {
  122. rspamd_monitored_stop (m);
  123. m->monitoring_mult = 8.0;
  124. rspamd_monitored_start (m);
  125. }
  126. }
  127. }
  128. static inline void
  129. rspamd_monitored_propagate_success (struct rspamd_monitored *m, gdouble lat)
  130. {
  131. gdouble t;
  132. m->cur_errors = 0;
  133. m->monitoring_mult = 1.0;
  134. if (!m->alive) {
  135. t = rspamd_get_calendar_ticks ();
  136. m->total_offline_time += t - m->offline_time;
  137. m->alive = TRUE;
  138. msg_notice_mon ("restoring %s after %.1f seconds of downtime, "
  139. "total downtime: %.1f",
  140. m->url, t - m->offline_time, m->total_offline_time);
  141. m->offline_time = 0;
  142. m->nchecks = 1;
  143. m->latency = lat;
  144. rspamd_monitored_stop (m);
  145. rspamd_monitored_start (m);
  146. if (m->ctx->change_cb) {
  147. m->ctx->change_cb (m->ctx, m, TRUE, m->ctx->ud);
  148. }
  149. }
  150. else {
  151. m->latency = (lat + m->latency * m->nchecks) / (m->nchecks + 1);
  152. m->nchecks ++;
  153. }
  154. }
  155. static void
  156. rspamd_monitored_periodic (EV_P_ ev_timer *w, int revents)
  157. {
  158. struct rspamd_monitored *m = (struct rspamd_monitored *)w->data;
  159. gdouble jittered;
  160. gboolean ret = FALSE;
  161. jittered = rspamd_time_jitter (m->ctx->monitoring_interval * m->monitoring_mult,
  162. 0.0);
  163. if (m->proc.monitored_update) {
  164. ret = m->proc.monitored_update (m, m->ctx, m->proc.ud);
  165. }
  166. if (ret) {
  167. m->periodic.repeat = jittered;
  168. ev_timer_again (EV_A_ &m->periodic);
  169. }
  170. }
  171. struct rspamd_dns_monitored_conf {
  172. enum rdns_request_type rt;
  173. GString *request;
  174. radix_compressed_t *expected;
  175. struct rspamd_monitored *m;
  176. gint expected_code;
  177. gdouble check_tm;
  178. };
  179. static void
  180. rspamd_monitored_dns_random (struct rspamd_monitored *m,
  181. struct rspamd_dns_monitored_conf *conf)
  182. {
  183. gchar random_prefix[32];
  184. const gchar dns_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
  185. gint len;
  186. len = rspamd_random_uint64_fast () % sizeof (random_prefix);
  187. if (len < 8) {
  188. len = 8;
  189. }
  190. for (guint i = 0; i < len; i ++) {
  191. guint idx = rspamd_random_uint64_fast () % (sizeof (dns_chars) - 1);
  192. random_prefix[i] = dns_chars[idx];
  193. }
  194. conf->request->len = 0;
  195. rspamd_printf_gstring (conf->request, "%*.s.%s", len, random_prefix,
  196. m->url);
  197. }
  198. static void *
  199. rspamd_monitored_dns_conf (struct rspamd_monitored *m,
  200. struct rspamd_monitored_ctx *ctx,
  201. const ucl_object_t *opts)
  202. {
  203. struct rspamd_dns_monitored_conf *conf;
  204. const ucl_object_t *elt;
  205. gint rt;
  206. GString *req = g_string_sized_new (127);
  207. conf = g_malloc0 (sizeof (*conf));
  208. conf->rt = RDNS_REQUEST_A;
  209. conf->m = m;
  210. conf->expected_code = -1;
  211. if (opts) {
  212. elt = ucl_object_lookup (opts, "type");
  213. if (elt) {
  214. rt = rdns_type_fromstr (ucl_object_tostring (elt));
  215. if (rt != -1) {
  216. conf->rt = rt;
  217. }
  218. else {
  219. msg_err_mon ("invalid resolve type: %s",
  220. ucl_object_tostring (elt));
  221. }
  222. }
  223. if (!(m->flags & RSPAMD_MONITORED_RANDOM)) {
  224. /* Prefix is useless for random monitored */
  225. elt = ucl_object_lookup (opts, "prefix");
  226. if (elt && ucl_object_type (elt) == UCL_STRING) {
  227. rspamd_printf_gstring (req, "%s.", ucl_object_tostring (elt));
  228. }
  229. }
  230. elt = ucl_object_lookup (opts, "ipnet");
  231. if (elt) {
  232. if (ucl_object_type (elt) == UCL_STRING) {
  233. radix_add_generic_iplist (ucl_object_tostring (elt),
  234. &conf->expected, FALSE, NULL);
  235. }
  236. else if (ucl_object_type (elt) == UCL_ARRAY) {
  237. const ucl_object_t *cur;
  238. ucl_object_iter_t it = NULL;
  239. while ((cur = ucl_object_iterate (elt, &it, true)) != NULL) {
  240. radix_add_generic_iplist (ucl_object_tostring (elt),
  241. &conf->expected, FALSE, NULL);
  242. }
  243. }
  244. }
  245. elt = ucl_object_lookup (opts, "rcode");
  246. if (elt) {
  247. rt = rdns_rcode_fromstr (ucl_object_tostring (elt));
  248. if (rt != -1) {
  249. conf->expected_code = rt;
  250. }
  251. else {
  252. msg_err_mon ("invalid resolve rcode: %s",
  253. ucl_object_tostring (elt));
  254. }
  255. }
  256. }
  257. if (!(m->flags & RSPAMD_MONITORED_RANDOM)) {
  258. rspamd_printf_gstring (req, "%s", m->url);
  259. }
  260. conf->request = req;
  261. return conf;
  262. }
  263. static void
  264. rspamd_monitored_dns_cb (struct rdns_reply *reply, void *arg)
  265. {
  266. struct rspamd_dns_monitored_conf *conf = arg;
  267. struct rspamd_monitored *m;
  268. struct rdns_reply_entry *cur;
  269. gboolean is_special_reply = FALSE;
  270. gdouble lat;
  271. m = conf->m;
  272. lat = rspamd_get_calendar_ticks () - conf->check_tm;
  273. conf->check_tm = 0;
  274. msg_debug_mon ("dns callback for %s in %.2f: %s", m->url, lat,
  275. rdns_strerror (reply->code));
  276. if (reply->code == RDNS_RC_TIMEOUT) {
  277. rspamd_monitored_propagate_error (m, "timeout");
  278. }
  279. else if (reply->code == RDNS_RC_SERVFAIL) {
  280. rspamd_monitored_propagate_error (m, "servfail");
  281. }
  282. else if (reply->code == RDNS_RC_REFUSED) {
  283. rspamd_monitored_propagate_error (m, "refused");
  284. }
  285. else {
  286. if (conf->expected_code != -1) {
  287. if (reply->code != conf->expected_code) {
  288. if (reply->code == RDNS_RC_NOREC &&
  289. conf->expected_code == RDNS_RC_NXDOMAIN) {
  290. rspamd_monitored_propagate_success (m, lat);
  291. }
  292. else {
  293. LL_FOREACH (reply->entries, cur) {
  294. if (cur->type == RDNS_REQUEST_A) {
  295. if ((guint32)cur->content.a.addr.s_addr ==
  296. htonl (INADDR_LOOPBACK)) {
  297. is_special_reply = TRUE;
  298. }
  299. }
  300. }
  301. if (is_special_reply) {
  302. msg_notice_mon ("DNS query blocked on %s "
  303. "(127.0.0.1 returned), "
  304. "possibly due to high volume",
  305. m->url);
  306. }
  307. else {
  308. msg_notice_mon ("DNS reply returned '%s' for %s while '%s' "
  309. "was expected when querying for '%s'"
  310. "(likely DNS spoofing or BL internal issues)",
  311. rdns_strerror (reply->code),
  312. m->url,
  313. rdns_strerror (conf->expected_code),
  314. conf->request->str);
  315. }
  316. rspamd_monitored_propagate_error (m, "invalid return");
  317. }
  318. }
  319. else {
  320. rspamd_monitored_propagate_success (m, lat);
  321. }
  322. }
  323. else if (conf->expected) {
  324. /* We also need to check IP */
  325. if (reply->code != RDNS_RC_NOERROR) {
  326. rspamd_monitored_propagate_error (m, "no record");
  327. }
  328. else {
  329. rspamd_inet_addr_t *addr;
  330. addr = rspamd_inet_address_from_rnds (reply->entries);
  331. if (!addr) {
  332. rspamd_monitored_propagate_error (m,
  333. "unreadable address");
  334. }
  335. else if (radix_find_compressed_addr (conf->expected, addr)) {
  336. msg_notice_mon ("bad address %s is returned when monitoring %s",
  337. rspamd_inet_address_to_string (addr),
  338. conf->request->str);
  339. rspamd_monitored_propagate_error (m,
  340. "invalid address");
  341. rspamd_inet_address_free (addr);
  342. }
  343. else {
  344. rspamd_monitored_propagate_success (m, lat);
  345. rspamd_inet_address_free (addr);
  346. }
  347. }
  348. }
  349. else {
  350. rspamd_monitored_propagate_success (m, lat);
  351. }
  352. }
  353. }
  354. static gboolean
  355. rspamd_monitored_dns_mon (struct rspamd_monitored *m,
  356. struct rspamd_monitored_ctx *ctx, gpointer ud)
  357. {
  358. struct rspamd_dns_monitored_conf *conf = ud;
  359. if (m->flags & RSPAMD_MONITORED_RANDOM) {
  360. rspamd_monitored_dns_random (m, conf);
  361. }
  362. if (!rdns_make_request_full (ctx->resolver, rspamd_monitored_dns_cb,
  363. conf, ctx->cfg->dns_timeout, ctx->cfg->dns_retransmits,
  364. 1, conf->request->str, conf->rt)) {
  365. msg_notice_mon ("cannot make request to resolve %s (%s monitored url)",
  366. conf->request->str, conf->m->url);
  367. m->cur_errors ++;
  368. rspamd_monitored_propagate_error (m, "failed to make DNS request");
  369. return FALSE;
  370. }
  371. else {
  372. conf->check_tm = rspamd_get_calendar_ticks ();
  373. }
  374. return TRUE;
  375. }
  376. void
  377. rspamd_monitored_dns_dtor (struct rspamd_monitored *m,
  378. struct rspamd_monitored_ctx *ctx, gpointer ud)
  379. {
  380. struct rspamd_dns_monitored_conf *conf = ud;
  381. g_string_free (conf->request, TRUE);
  382. if (conf->expected) {
  383. radix_destroy_compressed (conf->expected);
  384. }
  385. g_free (conf);
  386. }
  387. struct rspamd_monitored_ctx *
  388. rspamd_monitored_ctx_init (void)
  389. {
  390. struct rspamd_monitored_ctx *ctx;
  391. ctx = g_malloc0 (sizeof (*ctx));
  392. ctx->monitoring_interval = default_monitoring_interval;
  393. ctx->max_errors = default_max_errors;
  394. ctx->elts = g_ptr_array_new ();
  395. ctx->helts = g_hash_table_new (g_str_hash, g_str_equal);
  396. return ctx;
  397. }
  398. void
  399. rspamd_monitored_ctx_config (struct rspamd_monitored_ctx *ctx,
  400. struct rspamd_config *cfg,
  401. struct ev_loop *ev_base,
  402. struct rdns_resolver *resolver,
  403. mon_change_cb change_cb,
  404. gpointer ud)
  405. {
  406. struct rspamd_monitored *m;
  407. guint i;
  408. g_assert (ctx != NULL);
  409. ctx->event_loop = ev_base;
  410. ctx->resolver = resolver;
  411. ctx->cfg = cfg;
  412. ctx->initialized = TRUE;
  413. ctx->change_cb = change_cb;
  414. ctx->ud = ud;
  415. if (cfg->monitored_interval != 0) {
  416. ctx->monitoring_interval = cfg->monitored_interval;
  417. }
  418. /* Start all events */
  419. for (i = 0; i < ctx->elts->len; i ++) {
  420. m = g_ptr_array_index (ctx->elts, i);
  421. m->monitoring_mult = 0;
  422. rspamd_monitored_start (m);
  423. m->monitoring_mult = 1.0;
  424. }
  425. }
  426. struct ev_loop *
  427. rspamd_monitored_ctx_get_ev_base (struct rspamd_monitored_ctx *ctx)
  428. {
  429. return ctx->event_loop;
  430. }
  431. struct rspamd_monitored *
  432. rspamd_monitored_create_ (struct rspamd_monitored_ctx *ctx,
  433. const gchar *line,
  434. enum rspamd_monitored_type type,
  435. enum rspamd_monitored_flags flags,
  436. const ucl_object_t *opts,
  437. const gchar *loc)
  438. {
  439. struct rspamd_monitored *m;
  440. rspamd_cryptobox_hash_state_t st;
  441. gchar *cksum_encoded, cksum[rspamd_cryptobox_HASHBYTES];
  442. g_assert (ctx != NULL);
  443. m = g_malloc0 (sizeof (*m));
  444. m->type = type;
  445. m->flags = flags;
  446. m->url = g_strdup (line);
  447. m->ctx = ctx;
  448. m->monitoring_mult = 1.0;
  449. m->max_errors = ctx->max_errors;
  450. m->alive = TRUE;
  451. if (type == RSPAMD_MONITORED_DNS) {
  452. m->proc.monitored_update = rspamd_monitored_dns_mon;
  453. m->proc.monitored_config = rspamd_monitored_dns_conf;
  454. m->proc.monitored_dtor = rspamd_monitored_dns_dtor;
  455. }
  456. else {
  457. g_free (m);
  458. return NULL;
  459. }
  460. if (opts) {
  461. const ucl_object_t *rnd_obj;
  462. rnd_obj = ucl_object_lookup (opts, "random");
  463. if (rnd_obj && ucl_object_type (rnd_obj) == UCL_BOOLEAN) {
  464. if (ucl_object_toboolean (rnd_obj)) {
  465. m->flags |= RSPAMD_MONITORED_RANDOM;
  466. }
  467. }
  468. }
  469. m->proc.ud = m->proc.monitored_config (m, ctx, opts);
  470. if (m->proc.ud == NULL) {
  471. g_free (m);
  472. return NULL;
  473. }
  474. /* Create a persistent tag */
  475. rspamd_cryptobox_hash_init (&st, NULL, 0);
  476. rspamd_cryptobox_hash_update (&st, m->url, strlen (m->url));
  477. rspamd_cryptobox_hash_update (&st, loc, strlen (loc));
  478. rspamd_cryptobox_hash_final (&st, cksum);
  479. cksum_encoded = rspamd_encode_base32 (cksum, sizeof (cksum), RSPAMD_BASE32_DEFAULT);
  480. rspamd_strlcpy (m->tag, cksum_encoded, sizeof (m->tag));
  481. if (g_hash_table_lookup (ctx->helts, m->tag) != NULL) {
  482. msg_err ("monitored error: tag collision detected for %s; "
  483. "url: %s", m->tag, m->url);
  484. }
  485. else {
  486. g_hash_table_insert (ctx->helts, m->tag, m);
  487. }
  488. g_free (cksum_encoded);
  489. g_ptr_array_add (ctx->elts, m);
  490. if (ctx->event_loop) {
  491. rspamd_monitored_start (m);
  492. }
  493. return m;
  494. }
  495. gboolean
  496. rspamd_monitored_alive (struct rspamd_monitored *m)
  497. {
  498. g_assert (m != NULL);
  499. return m->alive;
  500. }
  501. gboolean
  502. rspamd_monitored_set_alive (struct rspamd_monitored *m, gboolean alive)
  503. {
  504. gboolean st;
  505. g_assert (m != NULL);
  506. st = m->alive;
  507. m->alive = alive;
  508. return st;
  509. }
  510. gdouble
  511. rspamd_monitored_offline_time (struct rspamd_monitored *m)
  512. {
  513. g_assert (m != NULL);
  514. if (m->offline_time > 0) {
  515. return rspamd_get_calendar_ticks () - m->offline_time;
  516. }
  517. return 0;
  518. }
  519. gdouble
  520. rspamd_monitored_total_offline_time (struct rspamd_monitored *m)
  521. {
  522. g_assert (m != NULL);
  523. if (m->offline_time > 0) {
  524. return rspamd_get_calendar_ticks () - m->offline_time + m->total_offline_time;
  525. }
  526. return m->total_offline_time;
  527. }
  528. gdouble
  529. rspamd_monitored_latency (struct rspamd_monitored *m)
  530. {
  531. g_assert (m != NULL);
  532. return m->latency;
  533. }
  534. void
  535. rspamd_monitored_stop (struct rspamd_monitored *m)
  536. {
  537. g_assert (m != NULL);
  538. ev_timer_stop (m->ctx->event_loop, &m->periodic);
  539. }
  540. void
  541. rspamd_monitored_start (struct rspamd_monitored *m)
  542. {
  543. gdouble jittered;
  544. g_assert (m != NULL);
  545. msg_debug_mon ("started monitored object %s", m->url);
  546. jittered = rspamd_time_jitter (m->ctx->monitoring_interval * m->monitoring_mult,
  547. 0.0);
  548. if (ev_can_stop (&m->periodic)) {
  549. ev_timer_stop (m->ctx->event_loop, &m->periodic);
  550. }
  551. m->periodic.data = m;
  552. ev_timer_init (&m->periodic, rspamd_monitored_periodic, jittered, 0.0);
  553. ev_timer_start (m->ctx->event_loop, &m->periodic);
  554. }
  555. void
  556. rspamd_monitored_ctx_destroy (struct rspamd_monitored_ctx *ctx)
  557. {
  558. struct rspamd_monitored *m;
  559. guint i;
  560. g_assert (ctx != NULL);
  561. for (i = 0; i < ctx->elts->len; i ++) {
  562. m = g_ptr_array_index (ctx->elts, i);
  563. rspamd_monitored_stop (m);
  564. m->proc.monitored_dtor (m, m->ctx, m->proc.ud);
  565. g_free (m->url);
  566. g_free (m);
  567. }
  568. g_ptr_array_free (ctx->elts, TRUE);
  569. g_hash_table_unref (ctx->helts);
  570. g_free (ctx);
  571. }
  572. struct rspamd_monitored *
  573. rspamd_monitored_by_tag (struct rspamd_monitored_ctx *ctx,
  574. guchar tag[RSPAMD_MONITORED_TAG_LEN])
  575. {
  576. struct rspamd_monitored *res;
  577. gchar rtag[RSPAMD_MONITORED_TAG_LEN];
  578. rspamd_strlcpy (rtag, tag, sizeof (rtag));
  579. res = g_hash_table_lookup (ctx->helts, rtag);
  580. return res;
  581. }
  582. void
  583. rspamd_monitored_get_tag (struct rspamd_monitored *m,
  584. guchar tag_out[RSPAMD_MONITORED_TAG_LEN])
  585. {
  586. g_assert (m != NULL);
  587. rspamd_strlcpy (tag_out, m->tag, RSPAMD_MONITORED_TAG_LEN);
  588. }