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 15KB

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