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.

fuzzy_storage.c 35KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420
  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. /*
  17. * Rspamd fuzzy storage server
  18. */
  19. #include <libserver/rspamd_control.h>
  20. #include "config.h"
  21. #include "util.h"
  22. #include "rspamd.h"
  23. #include "map.h"
  24. #include "fuzzy_storage.h"
  25. #include "fuzzy_backend.h"
  26. #include "ottery.h"
  27. #include "libserver/worker_util.h"
  28. #include "libserver/rspamd_control.h"
  29. #include "cryptobox.h"
  30. #include "keypairs_cache.h"
  31. #include "keypair_private.h"
  32. #include "ref.h"
  33. #include "xxhash.h"
  34. #include "libutil/hash.h"
  35. #include "unix-std.h"
  36. /* This number is used as expire time in seconds for cache items (2 days) */
  37. #define DEFAULT_EXPIRE 172800L
  38. /* Resync value in seconds */
  39. #define DEFAULT_SYNC_TIMEOUT 60.0
  40. #define DEFAULT_KEYPAIR_CACHE_SIZE 512
  41. #define INVALID_NODE_TIME (guint64) - 1
  42. /* Init functions */
  43. gpointer init_fuzzy (struct rspamd_config *cfg);
  44. void start_fuzzy (struct rspamd_worker *worker);
  45. worker_t fuzzy_worker = {
  46. "fuzzy", /* Name */
  47. init_fuzzy, /* Init function */
  48. start_fuzzy, /* Start function */
  49. TRUE, /* No socket */
  50. FALSE, /* Unique */
  51. FALSE, /* Threaded */
  52. FALSE, /* Non killable */
  53. SOCK_DGRAM /* UDP socket */
  54. };
  55. /* For evtimer */
  56. static struct timeval tmv;
  57. static struct event tev;
  58. struct fuzzy_global_stat {
  59. guint64 fuzzy_hashes;
  60. /**< number of fuzzy hashes stored */
  61. guint64 fuzzy_hashes_expired;
  62. /**< number of fuzzy hashes expired */
  63. guint64 fuzzy_hashes_checked[RSPAMD_FUZZY_EPOCH_MAX];
  64. /**< ammount of check requests for each epoch */
  65. guint64 fuzzy_shingles_checked[RSPAMD_FUZZY_EPOCH_MAX];
  66. /**< ammount of shingle check requests for each epoch */
  67. guint64 fuzzy_hashes_found[RSPAMD_FUZZY_EPOCH_MAX];
  68. /**< amount of hashes found by epoch */
  69. guint64 invalid_requests;
  70. };
  71. struct fuzzy_key_stat {
  72. guint64 checked;
  73. guint64 matched;
  74. guint64 added;
  75. guint64 deleted;
  76. guint64 errors;
  77. rspamd_lru_hash_t *last_ips;
  78. };
  79. struct rspamd_fuzzy_storage_ctx {
  80. struct fuzzy_global_stat stat;
  81. char *hashfile;
  82. gdouble expire;
  83. gdouble sync_timeout;
  84. radix_compressed_t *update_ips;
  85. gchar *update_map;
  86. guint keypair_cache_size;
  87. struct event_base *ev_base;
  88. gint peer_fd;
  89. struct event peer_ev;
  90. /* Local keypair */
  91. gpointer default_keypair; /* Bad clash, need for parse keypair */
  92. gpointer default_key;
  93. GHashTable *keys;
  94. gboolean encrypted_only;
  95. struct rspamd_keypair_cache *keypair_cache;
  96. rspamd_lru_hash_t *errors_ips;
  97. struct rspamd_fuzzy_backend *backend;
  98. GQueue *updates_pending;
  99. };
  100. enum fuzzy_cmd_type {
  101. CMD_NORMAL,
  102. CMD_SHINGLE,
  103. CMD_ENCRYPTED_NORMAL,
  104. CMD_ENCRYPTED_SHINGLE
  105. };
  106. struct fuzzy_session {
  107. struct rspamd_worker *worker;
  108. rspamd_inet_addr_t *addr;
  109. struct rspamd_fuzzy_storage_ctx *ctx;
  110. union {
  111. struct rspamd_fuzzy_encrypted_shingle_cmd enc_shingle;
  112. struct rspamd_fuzzy_encrypted_cmd enc_normal;
  113. struct rspamd_fuzzy_cmd normal;
  114. struct rspamd_fuzzy_shingle_cmd shingle;
  115. } cmd;
  116. struct rspamd_fuzzy_encrypted_reply reply;
  117. enum rspamd_fuzzy_epoch epoch;
  118. enum fuzzy_cmd_type cmd_type;
  119. gint fd;
  120. guint64 time;
  121. struct event io;
  122. ref_entry_t ref;
  123. struct fuzzy_key_stat *key_stat;
  124. guchar nm[rspamd_cryptobox_MAX_NMBYTES];
  125. };
  126. struct fuzzy_peer_cmd {
  127. gboolean is_shingle;
  128. union {
  129. struct rspamd_fuzzy_cmd normal;
  130. struct rspamd_fuzzy_shingle_cmd shingle;
  131. } cmd;
  132. };
  133. struct fuzzy_peer_request {
  134. struct event io_ev;
  135. struct fuzzy_peer_cmd cmd;
  136. };
  137. struct fuzzy_key {
  138. struct rspamd_http_keypair *key;
  139. struct fuzzy_key_stat *stat;
  140. };
  141. static void rspamd_fuzzy_write_reply (struct fuzzy_session *session);
  142. static gboolean
  143. rspamd_fuzzy_check_client (struct fuzzy_session *session)
  144. {
  145. if (session->ctx->update_ips != NULL) {
  146. if (radix_find_compressed_addr (session->ctx->update_ips,
  147. session->addr) == RADIX_NO_VALUE) {
  148. return FALSE;
  149. }
  150. }
  151. return TRUE;
  152. }
  153. static void
  154. fuzzy_key_stat_dtor (gpointer p)
  155. {
  156. struct fuzzy_key_stat *st = p;
  157. if (st->last_ips) {
  158. rspamd_lru_hash_destroy (st->last_ips);
  159. }
  160. g_slice_free1 (sizeof (*st), st);
  161. }
  162. static void
  163. fuzzy_key_dtor (gpointer p)
  164. {
  165. struct fuzzy_key *key = p;
  166. if (key->stat) {
  167. fuzzy_key_stat_dtor (key->stat);
  168. }
  169. if (key->key) {
  170. rspamd_http_connection_key_unref (key->key);
  171. }
  172. g_slice_free1 (sizeof (*key), key);
  173. }
  174. static void
  175. rspamd_fuzzy_process_updates_queue (struct rspamd_fuzzy_storage_ctx *ctx)
  176. {
  177. GList *cur;
  178. struct fuzzy_peer_cmd *io_cmd;
  179. struct rspamd_fuzzy_cmd *cmd;
  180. gpointer ptr;
  181. guint nupdates = 0;
  182. if (rspamd_fuzzy_backend_prepare_update (ctx->backend)) {
  183. cur = ctx->updates_pending->head;
  184. while (cur) {
  185. io_cmd = cur->data;
  186. if (io_cmd->is_shingle) {
  187. cmd = &io_cmd->cmd.shingle.basic;
  188. ptr = &io_cmd->cmd.shingle;
  189. }
  190. else {
  191. cmd = &io_cmd->cmd.normal;
  192. ptr = &io_cmd->cmd.normal;
  193. }
  194. if (cmd->cmd == FUZZY_WRITE) {
  195. rspamd_fuzzy_backend_add (ctx->backend, ptr);
  196. }
  197. else {
  198. rspamd_fuzzy_backend_del (ctx->backend, ptr);
  199. }
  200. nupdates++;
  201. cur = g_list_next (cur);
  202. }
  203. if (rspamd_fuzzy_backend_finish_update (ctx->backend)) {
  204. ctx->stat.fuzzy_hashes = rspamd_fuzzy_backend_count (ctx->backend);
  205. cur = ctx->updates_pending->head;
  206. while (cur) {
  207. io_cmd = cur->data;
  208. g_slice_free1 (sizeof (*io_cmd), io_cmd);
  209. cur = g_list_next (cur);
  210. }
  211. g_queue_clear (ctx->updates_pending);
  212. msg_info ("updated fuzzy storage: %ud updates processed", nupdates);
  213. }
  214. else {
  215. msg_err ("cannot commit update transaction to fuzzy backend, "
  216. "%ud updates are still pending",
  217. g_queue_get_length (ctx->updates_pending));
  218. }
  219. }
  220. else {
  221. msg_err ("cannot start transaction in fuzzy backend, "
  222. "%ud updates are still pending",
  223. g_queue_get_length (ctx->updates_pending));
  224. }
  225. }
  226. static void
  227. rspamd_fuzzy_reply_io (gint fd, gshort what, gpointer d)
  228. {
  229. struct fuzzy_session *session = d;
  230. rspamd_fuzzy_write_reply (session);
  231. REF_RELEASE (session);
  232. }
  233. static void
  234. rspamd_fuzzy_write_reply (struct fuzzy_session *session)
  235. {
  236. gssize r;
  237. gsize len;
  238. gconstpointer data;
  239. if (session->cmd_type == CMD_ENCRYPTED_NORMAL ||
  240. session->cmd_type == CMD_ENCRYPTED_SHINGLE) {
  241. /* Encrypted reply */
  242. data = &session->reply;
  243. len = sizeof (session->reply);
  244. }
  245. else {
  246. data = &session->reply.rep;
  247. len = sizeof (session->reply.rep);
  248. }
  249. r = rspamd_inet_address_sendto (session->fd, data, len, 0,
  250. session->addr);
  251. if (r == -1) {
  252. if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
  253. /* Grab reference to avoid early destruction */
  254. REF_RETAIN (session);
  255. event_set (&session->io, session->fd, EV_WRITE,
  256. rspamd_fuzzy_reply_io, session);
  257. event_base_set (session->ctx->ev_base, &session->io);
  258. event_add (&session->io, NULL);
  259. }
  260. else {
  261. msg_err ("error while writing reply: %s", strerror (errno));
  262. }
  263. }
  264. }
  265. static void
  266. fuzzy_peer_send_io (gint fd, gshort what, gpointer d)
  267. {
  268. struct fuzzy_peer_request *up_req = d;
  269. gssize r;
  270. r = write (fd, &up_req->cmd, sizeof (up_req->cmd));
  271. if (r != sizeof (up_req->cmd)) {
  272. msg_err ("cannot send update request to the peer: %s", strerror (errno));
  273. }
  274. event_del (&up_req->io_ev);
  275. g_slice_free1 (sizeof (*up_req), up_req);
  276. }
  277. static void
  278. rspamd_fuzzy_update_stats (struct rspamd_fuzzy_storage_ctx *ctx,
  279. enum rspamd_fuzzy_epoch epoch,
  280. gboolean matched,
  281. gboolean is_shingle,
  282. struct fuzzy_key_stat *key_stat,
  283. struct fuzzy_key_stat *ip_stat,
  284. guint cmd, guint reply)
  285. {
  286. ctx->stat.fuzzy_hashes_checked[epoch] ++;
  287. if (matched) {
  288. ctx->stat.fuzzy_hashes_found[epoch]++;
  289. }
  290. if (is_shingle) {
  291. ctx->stat.fuzzy_shingles_checked[epoch]++;
  292. }
  293. if (key_stat) {
  294. if (!matched && reply != 0) {
  295. key_stat->errors ++;
  296. }
  297. else {
  298. if (cmd == FUZZY_CHECK) {
  299. key_stat->checked++;
  300. if (matched) {
  301. key_stat->matched ++;
  302. }
  303. }
  304. else if (cmd == FUZZY_WRITE) {
  305. key_stat->added++;
  306. }
  307. else if (cmd == FUZZY_DEL) {
  308. key_stat->deleted++;
  309. }
  310. }
  311. }
  312. if (ip_stat) {
  313. if (!matched && reply != 0) {
  314. ip_stat->errors++;
  315. }
  316. else {
  317. if (cmd == FUZZY_CHECK) {
  318. ip_stat->checked++;
  319. if (matched) {
  320. ip_stat->matched++;
  321. }
  322. }
  323. else if (cmd == FUZZY_WRITE) {
  324. ip_stat->added++;
  325. }
  326. else if (cmd == FUZZY_DEL) {
  327. ip_stat->deleted++;
  328. }
  329. }
  330. }
  331. }
  332. static void
  333. rspamd_fuzzy_process_command (struct fuzzy_session *session)
  334. {
  335. gboolean encrypted = FALSE, is_shingle = FALSE;
  336. struct rspamd_fuzzy_cmd *cmd = NULL;
  337. struct rspamd_fuzzy_reply result;
  338. struct fuzzy_peer_cmd *up_cmd;
  339. struct fuzzy_peer_request *up_req;
  340. struct fuzzy_key_stat *ip_stat = NULL;
  341. rspamd_inet_addr_t *naddr;
  342. gpointer ptr;
  343. gsize up_len = 0;
  344. switch (session->cmd_type) {
  345. case CMD_NORMAL:
  346. cmd = &session->cmd.normal;
  347. up_len = sizeof (session->cmd.normal);
  348. break;
  349. case CMD_SHINGLE:
  350. cmd = &session->cmd.shingle.basic;
  351. up_len = sizeof (session->cmd.shingle);
  352. is_shingle = TRUE;
  353. break;
  354. case CMD_ENCRYPTED_NORMAL:
  355. cmd = &session->cmd.enc_normal.cmd;
  356. up_len = sizeof (session->cmd.normal);
  357. encrypted = TRUE;
  358. break;
  359. case CMD_ENCRYPTED_SHINGLE:
  360. cmd = &session->cmd.enc_shingle.cmd.basic;
  361. up_len = sizeof (session->cmd.shingle);
  362. encrypted = TRUE;
  363. is_shingle = TRUE;
  364. break;
  365. }
  366. if (G_UNLIKELY (cmd == NULL || up_len == 0)) {
  367. result.value = 500;
  368. result.prob = 0.0;
  369. goto reply;
  370. }
  371. if (session->ctx->encrypted_only && !encrypted) {
  372. /* Do not accept unencrypted commands */
  373. result.value = 403;
  374. result.prob = 0.0;
  375. goto reply;
  376. }
  377. if (session->key_stat) {
  378. ip_stat = rspamd_lru_hash_lookup (session->key_stat->last_ips,
  379. session->addr, -1);
  380. if (ip_stat == NULL) {
  381. naddr = rspamd_inet_address_copy (session->addr);
  382. ip_stat = g_slice_alloc0 (sizeof (*ip_stat));
  383. rspamd_lru_hash_insert (session->key_stat->last_ips,
  384. naddr, ip_stat, -1, 0);
  385. }
  386. }
  387. if (cmd->cmd == FUZZY_CHECK) {
  388. result = rspamd_fuzzy_backend_check (session->ctx->backend, cmd,
  389. session->ctx->expire);
  390. }
  391. else {
  392. result.flag = cmd->flag;
  393. if (rspamd_fuzzy_check_client (session)) {
  394. if (session->worker->index == 0 || session->ctx->peer_fd == -1) {
  395. /* Just add to the queue */
  396. up_cmd = g_slice_alloc0 (sizeof (*up_cmd));
  397. up_cmd->is_shingle = is_shingle;
  398. ptr = is_shingle ?
  399. (gpointer)&up_cmd->cmd.shingle :
  400. (gpointer)&up_cmd->cmd.normal;
  401. memcpy (ptr, cmd, up_len);
  402. g_queue_push_tail (session->ctx->updates_pending, up_cmd);
  403. }
  404. else {
  405. /* We need to send request to the peer */
  406. up_req = g_slice_alloc0 (sizeof (*up_req));
  407. up_req->cmd.is_shingle = is_shingle;
  408. ptr = is_shingle ?
  409. (gpointer)&up_req->cmd.cmd.shingle :
  410. (gpointer)&up_req->cmd.cmd.normal;
  411. memcpy (ptr, cmd, up_len);
  412. event_set (&up_req->io_ev, session->ctx->peer_fd, EV_WRITE,
  413. fuzzy_peer_send_io, up_req);
  414. event_base_set (session->ctx->ev_base, &up_req->io_ev);
  415. event_add (&up_req->io_ev, NULL);
  416. }
  417. result.value = 0;
  418. result.prob = 1.0;
  419. }
  420. else {
  421. result.value = 403;
  422. result.prob = 0.0;
  423. }
  424. }
  425. reply:
  426. result.tag = cmd->tag;
  427. memcpy (&session->reply.rep, &result, sizeof (result));
  428. rspamd_fuzzy_update_stats (session->ctx,
  429. session->epoch,
  430. result.prob > 0.5,
  431. is_shingle,
  432. session->key_stat,
  433. ip_stat, cmd->cmd,
  434. result.value);
  435. if (encrypted) {
  436. /* We need also to encrypt reply */
  437. ottery_rand_bytes (session->reply.hdr.nonce,
  438. sizeof (session->reply.hdr.nonce));
  439. rspamd_cryptobox_encrypt_nm_inplace ((guchar *)&session->reply.rep,
  440. sizeof (session->reply.rep),
  441. session->reply.hdr.nonce,
  442. session->nm,
  443. session->reply.hdr.mac);
  444. }
  445. rspamd_fuzzy_write_reply (session);
  446. }
  447. static enum rspamd_fuzzy_epoch
  448. rspamd_fuzzy_command_valid (struct rspamd_fuzzy_cmd *cmd, gint r)
  449. {
  450. enum rspamd_fuzzy_epoch ret = RSPAMD_FUZZY_EPOCH_MAX;
  451. if (cmd->version == RSPAMD_FUZZY_VERSION) {
  452. if (cmd->shingles_count > 0) {
  453. if (r == sizeof (struct rspamd_fuzzy_shingle_cmd)) {
  454. ret = RSPAMD_FUZZY_EPOCH9;
  455. }
  456. }
  457. else {
  458. if (r == sizeof (*cmd)) {
  459. ret = RSPAMD_FUZZY_EPOCH9;
  460. }
  461. }
  462. }
  463. else if (cmd->version == 2) {
  464. /*
  465. * rspamd 0.8 has slightly different tokenizer then it might be not
  466. * 100% compatible
  467. */
  468. if (cmd->shingles_count > 0) {
  469. if (r == sizeof (struct rspamd_fuzzy_shingle_cmd)) {
  470. ret = RSPAMD_FUZZY_EPOCH8;
  471. }
  472. }
  473. else {
  474. ret = RSPAMD_FUZZY_EPOCH8;
  475. }
  476. }
  477. return ret;
  478. }
  479. static gboolean
  480. rspamd_fuzzy_decrypt_command (struct fuzzy_session *s)
  481. {
  482. struct rspamd_fuzzy_encrypted_req_hdr *hdr;
  483. guchar *payload;
  484. gsize payload_len;
  485. struct rspamd_http_keypair rk;
  486. struct fuzzy_key *key;
  487. if (s->ctx->default_key == NULL) {
  488. msg_warn ("received encrypted request when encryption is not enabled");
  489. return FALSE;
  490. }
  491. if (s->cmd_type == CMD_ENCRYPTED_NORMAL) {
  492. hdr = &s->cmd.enc_normal.hdr;
  493. payload = (guchar *)&s->cmd.enc_normal.cmd;
  494. payload_len = sizeof (s->cmd.enc_normal.cmd);
  495. }
  496. else {
  497. hdr = &s->cmd.enc_shingle.hdr;
  498. payload = (guchar *) &s->cmd.enc_shingle.cmd;
  499. payload_len = sizeof (s->cmd.enc_shingle.cmd);
  500. }
  501. /* Compare magic */
  502. if (memcmp (hdr->magic, fuzzy_encrypted_magic, sizeof (hdr->magic)) != 0) {
  503. msg_debug ("invalid magic for the encrypted packet");
  504. return FALSE;
  505. }
  506. /* Try to find the desired key */
  507. key = g_hash_table_lookup (s->ctx->keys, hdr->key_id);
  508. if (key == NULL) {
  509. /* Unknown key, assume default one */
  510. key = s->ctx->default_key;
  511. }
  512. s->key_stat = key->stat;
  513. /* Now process keypair */
  514. memcpy (rk.pk, hdr->pubkey, sizeof (rk.pk));
  515. rspamd_keypair_cache_process (s->ctx->keypair_cache, key->key, &rk);
  516. /* Now decrypt request */
  517. if (!rspamd_cryptobox_decrypt_nm_inplace (payload, payload_len, hdr->nonce,
  518. rk.nm, hdr->mac)) {
  519. msg_debug ("decryption failed");
  520. rspamd_explicit_memzero (rk.nm, sizeof (rk.nm));
  521. return FALSE;
  522. }
  523. memcpy (s->nm, rk.nm, sizeof (s->nm));
  524. rspamd_explicit_memzero (rk.nm, sizeof (rk.nm));
  525. return TRUE;
  526. }
  527. static gboolean
  528. rspamd_fuzzy_cmd_from_wire (guchar *buf, guint buflen, struct fuzzy_session *s)
  529. {
  530. enum rspamd_fuzzy_epoch epoch;
  531. /* For now, we assume that recvfrom returns a complete datagramm */
  532. switch (buflen) {
  533. case sizeof (struct rspamd_fuzzy_cmd):
  534. s->cmd_type = CMD_NORMAL;
  535. memcpy (&s->cmd.normal, buf, sizeof (s->cmd.normal));
  536. epoch = rspamd_fuzzy_command_valid (&s->cmd.normal, buflen);
  537. if (epoch == RSPAMD_FUZZY_EPOCH_MAX) {
  538. msg_debug ("invalid fuzzy command of size %d received", buflen);
  539. return FALSE;
  540. }
  541. s->epoch = epoch;
  542. break;
  543. case sizeof (struct rspamd_fuzzy_shingle_cmd):
  544. s->cmd_type = CMD_SHINGLE;
  545. memcpy (&s->cmd.shingle, buf, sizeof (s->cmd.shingle));
  546. epoch = rspamd_fuzzy_command_valid (&s->cmd.shingle.basic, buflen);
  547. if (epoch == RSPAMD_FUZZY_EPOCH_MAX) {
  548. msg_debug ("invalid fuzzy command of size %d received", buflen);
  549. return FALSE;
  550. }
  551. s->epoch = epoch;
  552. break;
  553. case sizeof (struct rspamd_fuzzy_encrypted_cmd):
  554. s->cmd_type = CMD_ENCRYPTED_NORMAL;
  555. memcpy (&s->cmd.enc_normal, buf, sizeof (s->cmd.enc_normal));
  556. if (!rspamd_fuzzy_decrypt_command (s)) {
  557. return FALSE;
  558. }
  559. epoch = rspamd_fuzzy_command_valid (&s->cmd.enc_normal.cmd,
  560. sizeof (s->cmd.enc_normal.cmd));
  561. if (epoch == RSPAMD_FUZZY_EPOCH_MAX) {
  562. msg_debug ("invalid fuzzy command of size %d received", buflen);
  563. return FALSE;
  564. }
  565. /* Encrypted is epoch 10 at least */
  566. s->epoch = RSPAMD_FUZZY_EPOCH10;
  567. break;
  568. case sizeof (struct rspamd_fuzzy_encrypted_shingle_cmd):
  569. s->cmd_type = CMD_ENCRYPTED_SHINGLE;
  570. memcpy (&s->cmd.enc_shingle, buf, sizeof (s->cmd.enc_shingle));
  571. if (!rspamd_fuzzy_decrypt_command (s)) {
  572. return FALSE;
  573. }
  574. epoch = rspamd_fuzzy_command_valid (&s->cmd.enc_shingle.cmd.basic,
  575. sizeof (s->cmd.enc_shingle.cmd));
  576. if (epoch == RSPAMD_FUZZY_EPOCH_MAX) {
  577. msg_debug ("invalid fuzzy command of size %d received", buflen);
  578. return FALSE;
  579. }
  580. s->epoch = RSPAMD_FUZZY_EPOCH10;
  581. break;
  582. default:
  583. msg_debug ("invalid fuzzy command of size %d received", buflen);
  584. return FALSE;
  585. }
  586. return TRUE;
  587. }
  588. static void
  589. fuzzy_session_destroy (gpointer d)
  590. {
  591. struct fuzzy_session *session = d;
  592. rspamd_inet_address_destroy (session->addr);
  593. rspamd_explicit_memzero (session->nm, sizeof (session->nm));
  594. session->worker->nconns--;
  595. g_slice_free1 (sizeof (*session), session);
  596. }
  597. /*
  598. * Accept new connection and construct task
  599. */
  600. static void
  601. accept_fuzzy_socket (gint fd, short what, void *arg)
  602. {
  603. struct rspamd_worker *worker = (struct rspamd_worker *)arg;
  604. struct fuzzy_session *session;
  605. rspamd_inet_addr_t *addr;
  606. gssize r;
  607. guint8 buf[512];
  608. guint64 *nerrors;
  609. /* Got some data */
  610. if (what == EV_READ) {
  611. for (;;) {
  612. worker->nconns++;
  613. r = rspamd_inet_address_recvfrom (fd,
  614. buf,
  615. sizeof (buf),
  616. 0,
  617. &addr);
  618. if (r == -1) {
  619. if (errno == EINTR) {
  620. continue;
  621. }
  622. else if (errno == EAGAIN || errno == EWOULDBLOCK) {
  623. return;
  624. }
  625. msg_err ("got error while reading from socket: %d, %s",
  626. errno,
  627. strerror (errno));
  628. return;
  629. }
  630. session = g_slice_alloc0 (sizeof (*session));
  631. REF_INIT_RETAIN (session, fuzzy_session_destroy);
  632. session->worker = worker;
  633. session->fd = fd;
  634. session->ctx = worker->ctx;
  635. session->time = (guint64) time (NULL);
  636. session->addr = addr;
  637. if (rspamd_fuzzy_cmd_from_wire (buf, r, session)) {
  638. /* Check shingles count sanity */
  639. rspamd_fuzzy_process_command (session);
  640. }
  641. else {
  642. /* Discard input */
  643. session->ctx->stat.invalid_requests ++;
  644. msg_debug ("invalid fuzzy command of size %z received", r);
  645. nerrors = rspamd_lru_hash_lookup (session->ctx->errors_ips,
  646. addr, -1);
  647. if (nerrors == NULL) {
  648. nerrors = g_malloc (sizeof (*nerrors));
  649. *nerrors = 1;
  650. rspamd_lru_hash_insert (session->ctx->errors_ips,
  651. rspamd_inet_address_copy (addr),
  652. nerrors, -1, -1);
  653. }
  654. else {
  655. *nerrors = *nerrors + 1;
  656. }
  657. }
  658. REF_RELEASE (session);
  659. }
  660. }
  661. }
  662. static void
  663. sync_callback (gint fd, short what, void *arg)
  664. {
  665. struct rspamd_worker *worker = (struct rspamd_worker *)arg;
  666. struct rspamd_fuzzy_storage_ctx *ctx;
  667. gdouble next_check;
  668. guint64 old_expired, new_expired;
  669. ctx = worker->ctx;
  670. if (ctx->backend) {
  671. rspamd_fuzzy_process_updates_queue (ctx);
  672. /* Call backend sync */
  673. old_expired = rspamd_fuzzy_backend_expired (ctx->backend);
  674. rspamd_fuzzy_backend_sync (ctx->backend, ctx->expire, TRUE);
  675. new_expired = rspamd_fuzzy_backend_expired (ctx->backend);
  676. if (old_expired < new_expired) {
  677. ctx->stat.fuzzy_hashes_expired += new_expired - old_expired;
  678. }
  679. }
  680. /* Timer event */
  681. event_del (&tev);
  682. evtimer_set (&tev, sync_callback, worker);
  683. event_base_set (ctx->ev_base, &tev);
  684. /* Plan event with jitter */
  685. next_check = rspamd_time_jitter (ctx->sync_timeout, 0);
  686. double_to_tv (next_check, &tmv);
  687. evtimer_add (&tev, &tmv);
  688. }
  689. static gboolean
  690. rspamd_fuzzy_storage_reload (struct rspamd_main *rspamd_main,
  691. struct rspamd_worker *worker, gint fd,
  692. struct rspamd_control_command *cmd,
  693. gpointer ud)
  694. {
  695. struct rspamd_fuzzy_storage_ctx *ctx = ud;
  696. GError *err = NULL;
  697. struct rspamd_control_reply rep;
  698. msg_info ("reloading fuzzy storage after receiving reload command");
  699. if (ctx->backend) {
  700. /* Close backend and reopen it one more time */
  701. rspamd_fuzzy_backend_close (ctx->backend);
  702. }
  703. memset (&rep, 0, sizeof (rep));
  704. rep.type = RSPAMD_CONTROL_RELOAD;
  705. if ((ctx->backend = rspamd_fuzzy_backend_open (ctx->hashfile,
  706. TRUE,
  707. &err)) == NULL) {
  708. msg_err ("cannot open backend after reload: %e", err);
  709. g_error_free (err);
  710. rep.reply.reload.status = err->code;
  711. }
  712. else {
  713. rep.reply.reload.status = 0;
  714. }
  715. if (write (fd, &rep, sizeof (rep)) != sizeof (rep)) {
  716. msg_err ("cannot write reply to the control socket: %s",
  717. strerror (errno));
  718. }
  719. return TRUE;
  720. }
  721. static ucl_object_t *
  722. rspamd_fuzzy_storage_stat_key (struct fuzzy_key_stat *key_stat)
  723. {
  724. ucl_object_t *res;
  725. res = ucl_object_typed_new (UCL_OBJECT);
  726. ucl_object_insert_key (res, ucl_object_fromint (key_stat->checked),
  727. "checked", 0, false);
  728. ucl_object_insert_key (res, ucl_object_fromint (key_stat->matched),
  729. "matched", 0, false);
  730. ucl_object_insert_key (res, ucl_object_fromint (key_stat->added),
  731. "added", 0, false);
  732. ucl_object_insert_key (res, ucl_object_fromint (key_stat->deleted),
  733. "deleted", 0, false);
  734. ucl_object_insert_key (res, ucl_object_fromint (key_stat->errors),
  735. "errors", 0, false);
  736. return res;
  737. }
  738. static ucl_object_t *
  739. rspamd_fuzzy_stat_to_ucl (struct rspamd_fuzzy_storage_ctx *ctx, gboolean ip_stat)
  740. {
  741. GHashTableIter it, ip_it;
  742. GHashTable *ip_hash;
  743. struct fuzzy_key_stat *key_stat;
  744. struct fuzzy_key *key;
  745. rspamd_lru_element_t *lru_elt;
  746. ucl_object_t *obj, *keys_obj, *elt, *ip_elt, *ip_cur;
  747. gpointer k, v;
  748. gint i;
  749. gchar keyname[17];
  750. obj = ucl_object_typed_new (UCL_OBJECT);
  751. keys_obj = ucl_object_typed_new (UCL_OBJECT);
  752. g_hash_table_iter_init (&it, ctx->keys);
  753. while (g_hash_table_iter_next (&it, &k, &v)) {
  754. key = v;
  755. key_stat = key->stat;
  756. if (key_stat) {
  757. rspamd_snprintf (keyname, sizeof (keyname), "%8bs", k);
  758. elt = rspamd_fuzzy_storage_stat_key (key_stat);
  759. if (key_stat->last_ips && ip_stat) {
  760. ip_hash = rspamd_lru_hash_get_htable (key_stat->last_ips);
  761. if (ip_hash) {
  762. g_hash_table_iter_init (&ip_it, ip_hash);
  763. ip_elt = ucl_object_typed_new (UCL_OBJECT);
  764. while (g_hash_table_iter_next (&ip_it, &k, &v)) {
  765. lru_elt = v;
  766. ip_cur = rspamd_fuzzy_storage_stat_key (lru_elt->data);
  767. ucl_object_insert_key (ip_elt, ip_cur,
  768. rspamd_inet_address_to_string (k), 0, true);
  769. }
  770. ucl_object_insert_key (elt, ip_elt, "ips", 0, false);
  771. }
  772. }
  773. ucl_object_insert_key (keys_obj, elt, keyname, 0, true);
  774. }
  775. }
  776. ucl_object_insert_key (obj, keys_obj, "keys", 0, false);
  777. /* Now generic stats */
  778. ucl_object_insert_key (obj,
  779. ucl_object_fromint (ctx->stat.fuzzy_hashes),
  780. "fuzzy_stored",
  781. 0,
  782. false);
  783. ucl_object_insert_key (obj,
  784. ucl_object_fromint (ctx->stat.fuzzy_hashes_expired),
  785. "fuzzy_expired",
  786. 0,
  787. false);
  788. ucl_object_insert_key (obj,
  789. ucl_object_fromint (ctx->stat.invalid_requests),
  790. "invalid_requests",
  791. 0,
  792. false);
  793. if (ctx->errors_ips && ip_stat) {
  794. ip_hash = rspamd_lru_hash_get_htable (ctx->errors_ips);
  795. if (ip_hash) {
  796. g_hash_table_iter_init (&ip_it, ip_hash);
  797. ip_elt = ucl_object_typed_new (UCL_OBJECT);
  798. while (g_hash_table_iter_next (&ip_it, &k, &v)) {
  799. lru_elt = v;
  800. ucl_object_insert_key (ip_elt,
  801. ucl_object_fromint (*(guint64 *)lru_elt->data),
  802. rspamd_inet_address_to_string (k), 0, true);
  803. }
  804. ucl_object_insert_key (obj,
  805. ip_elt,
  806. "errors_ips",
  807. 0,
  808. false);
  809. }
  810. }
  811. /* Checked by epoch */
  812. elt = ucl_object_typed_new (UCL_ARRAY);
  813. for (i = RSPAMD_FUZZY_EPOCH6; i < RSPAMD_FUZZY_EPOCH_MAX; i++) {
  814. ucl_array_append (elt,
  815. ucl_object_fromint (ctx->stat.fuzzy_hashes_checked[i]));
  816. }
  817. ucl_object_insert_key (obj, elt, "fuzzy_checked", 0, false);
  818. /* Shingles by epoch */
  819. elt = ucl_object_typed_new (UCL_ARRAY);
  820. for (i = RSPAMD_FUZZY_EPOCH6; i < RSPAMD_FUZZY_EPOCH_MAX; i++) {
  821. ucl_array_append (elt,
  822. ucl_object_fromint (ctx->stat.fuzzy_shingles_checked[i]));
  823. }
  824. ucl_object_insert_key (obj, elt, "fuzzy_shingles", 0, false);
  825. /* Matched by epoch */
  826. elt = ucl_object_typed_new (UCL_ARRAY);
  827. for (i = RSPAMD_FUZZY_EPOCH6; i < RSPAMD_FUZZY_EPOCH_MAX; i++) {
  828. ucl_array_append (elt,
  829. ucl_object_fromint (ctx->stat.fuzzy_hashes_found[i]));
  830. }
  831. ucl_object_insert_key (obj, elt, "fuzzy_found", 0, false);
  832. return obj;
  833. }
  834. static gboolean
  835. rspamd_fuzzy_storage_stat (struct rspamd_main *rspamd_main,
  836. struct rspamd_worker *worker, gint fd,
  837. struct rspamd_control_command *cmd,
  838. gpointer ud)
  839. {
  840. struct rspamd_fuzzy_storage_ctx *ctx = ud;
  841. struct rspamd_control_reply rep;
  842. ucl_object_t *obj;
  843. struct ucl_emitter_functions *emit_subr;
  844. guchar fdspace[CMSG_SPACE(sizeof (int))];
  845. struct iovec iov;
  846. struct msghdr msg;
  847. struct cmsghdr *cmsg;
  848. gint outfd = -1;
  849. gchar tmppath[PATH_MAX];
  850. memset (&rep, 0, sizeof (rep));
  851. rep.type = RSPAMD_CONTROL_RELOAD;
  852. rspamd_snprintf (tmppath, sizeof (tmppath), "%s%c%s-XXXXXXXXXX",
  853. rspamd_main->cfg->temp_dir, G_DIR_SEPARATOR, "fuzzy-stat");
  854. if ((outfd = mkstemp (tmppath)) == -1) {
  855. rep.reply.fuzzy_stat.status = errno;
  856. msg_info_main ("cannot make temporary stat file for fuzzy stat: %s",
  857. strerror (errno));
  858. }
  859. else {
  860. rep.reply.fuzzy_stat.status = 0;
  861. memcpy (rep.reply.fuzzy_stat.storage_id,
  862. rspamd_fuzzy_backend_id (ctx->backend),
  863. sizeof (rep.reply.fuzzy_stat.storage_id));
  864. obj = rspamd_fuzzy_stat_to_ucl (ctx, TRUE);
  865. emit_subr = ucl_object_emit_fd_funcs (outfd);
  866. ucl_object_emit_full (obj, UCL_EMIT_JSON_COMPACT, emit_subr);
  867. ucl_object_emit_funcs_free (emit_subr);
  868. ucl_object_unref (obj);
  869. /* Rewind output file */
  870. close (outfd);
  871. outfd = open (tmppath, O_RDONLY);
  872. unlink (tmppath);
  873. }
  874. /* Now we can send outfd and status message */
  875. memset (&msg, 0, sizeof (msg));
  876. /* Attach fd to the message */
  877. if (outfd != -1) {
  878. memset (fdspace, 0, sizeof (fdspace));
  879. msg.msg_control = fdspace;
  880. msg.msg_controllen = sizeof (fdspace);
  881. cmsg = CMSG_FIRSTHDR (&msg);
  882. cmsg->cmsg_level = SOL_SOCKET;
  883. cmsg->cmsg_type = SCM_RIGHTS;
  884. cmsg->cmsg_len = CMSG_LEN (sizeof (int));
  885. memcpy (CMSG_DATA (cmsg), &outfd, sizeof (int));
  886. }
  887. iov.iov_base = &rep;
  888. iov.iov_len = sizeof (rep);
  889. msg.msg_iov = &iov;
  890. msg.msg_iovlen = 1;
  891. if (sendmsg (fd, &msg, 0) == -1) {
  892. msg_err_main ("cannot send fuzzy stat: %s", strerror (errno));
  893. }
  894. if (outfd != -1) {
  895. close (outfd);
  896. }
  897. return TRUE;
  898. }
  899. static gboolean
  900. fuzzy_parse_keypair (rspamd_mempool_t *pool,
  901. const ucl_object_t *obj,
  902. gpointer ud,
  903. struct rspamd_rcl_section *section,
  904. GError **err)
  905. {
  906. struct rspamd_rcl_struct_parser *pd = ud;
  907. struct rspamd_fuzzy_storage_ctx *ctx;
  908. struct rspamd_http_keypair *kp;
  909. struct fuzzy_key_stat *keystat;
  910. struct fuzzy_key *key;
  911. const ucl_object_t *cur;
  912. ucl_object_iter_t it = NULL;
  913. gboolean ret;
  914. ctx = pd->user_struct;
  915. pd->offset = G_STRUCT_OFFSET (struct rspamd_fuzzy_storage_ctx, default_keypair);
  916. /*
  917. * Single key
  918. */
  919. if (ucl_object_type (obj) == UCL_STRING || ucl_object_type (obj)
  920. == UCL_OBJECT) {
  921. ret = rspamd_rcl_parse_struct_keypair (pool, obj, pd, section, err);
  922. if (!ret) {
  923. return ret;
  924. }
  925. /* Insert key to the hash table */
  926. kp = ctx->default_keypair;
  927. if (kp == NULL) {
  928. return FALSE;
  929. }
  930. key = g_slice_alloc (sizeof (*key));
  931. key->key = kp;
  932. keystat = g_slice_alloc0 (sizeof (*keystat));
  933. /* Hash of ip -> fuzzy_key_stat */
  934. keystat->last_ips = rspamd_lru_hash_new_full (0, 1024,
  935. (GDestroyNotify)rspamd_inet_address_destroy, fuzzy_key_stat_dtor,
  936. rspamd_inet_address_hash, rspamd_inet_address_equal);
  937. key->stat = keystat;
  938. g_hash_table_insert (ctx->keys, kp->pk, key);
  939. ctx->default_key = key;
  940. msg_info_pool ("loaded keypair %8xs", kp->pk);
  941. }
  942. else if (ucl_object_type (obj) == UCL_ARRAY) {
  943. while ((cur = ucl_iterate_object (obj, &it, true)) != NULL) {
  944. if (!fuzzy_parse_keypair (pool, cur, pd, section, err)) {
  945. return FALSE;
  946. }
  947. }
  948. }
  949. return TRUE;
  950. }
  951. static guint
  952. fuzzy_kp_hash (gconstpointer p)
  953. {
  954. const guchar *pk = p;
  955. return XXH64 (pk, RSPAMD_FUZZY_KEYLEN, 0xdeadbabe);
  956. }
  957. static gboolean
  958. fuzzy_kp_equal (gconstpointer a, gconstpointer b)
  959. {
  960. const guchar *pa = a, *pb = b;
  961. return (memcmp (pa, pb, RSPAMD_FUZZY_KEYLEN) == 0);
  962. }
  963. gpointer
  964. init_fuzzy (struct rspamd_config *cfg)
  965. {
  966. struct rspamd_fuzzy_storage_ctx *ctx;
  967. GQuark type;
  968. type = g_quark_try_string ("fuzzy");
  969. ctx = g_malloc0 (sizeof (struct rspamd_fuzzy_storage_ctx));
  970. ctx->sync_timeout = DEFAULT_SYNC_TIMEOUT;
  971. ctx->expire = DEFAULT_EXPIRE;
  972. ctx->keypair_cache_size = DEFAULT_KEYPAIR_CACHE_SIZE;
  973. ctx->keys = g_hash_table_new_full (fuzzy_kp_hash, fuzzy_kp_equal,
  974. NULL, fuzzy_key_dtor);
  975. ctx->errors_ips = rspamd_lru_hash_new_full (0, 1024,
  976. (GDestroyNotify) rspamd_inet_address_destroy, g_free,
  977. rspamd_inet_address_hash, rspamd_inet_address_equal);
  978. rspamd_rcl_register_worker_option (cfg,
  979. type,
  980. "hashfile",
  981. rspamd_rcl_parse_struct_string,
  982. ctx,
  983. G_STRUCT_OFFSET (struct rspamd_fuzzy_storage_ctx, hashfile),
  984. 0,
  985. "Path to fuzzy database");
  986. rspamd_rcl_register_worker_option (cfg,
  987. type,
  988. "hash_file",
  989. rspamd_rcl_parse_struct_string,
  990. ctx,
  991. G_STRUCT_OFFSET (struct rspamd_fuzzy_storage_ctx, hashfile),
  992. 0,
  993. "Path to fuzzy database (alias for hashfile)");
  994. rspamd_rcl_register_worker_option (cfg,
  995. type,
  996. "file",
  997. rspamd_rcl_parse_struct_string,
  998. ctx,
  999. G_STRUCT_OFFSET (struct rspamd_fuzzy_storage_ctx, hashfile),
  1000. 0,
  1001. "Path to fuzzy database (alias for hashfile)");
  1002. rspamd_rcl_register_worker_option (cfg,
  1003. type,
  1004. "database",
  1005. rspamd_rcl_parse_struct_string,
  1006. ctx,
  1007. G_STRUCT_OFFSET (struct rspamd_fuzzy_storage_ctx, hashfile),
  1008. 0,
  1009. "Path to fuzzy database (alias for hashfile)");
  1010. rspamd_rcl_register_worker_option (cfg,
  1011. type,
  1012. "sync",
  1013. rspamd_rcl_parse_struct_time,
  1014. ctx,
  1015. G_STRUCT_OFFSET (struct rspamd_fuzzy_storage_ctx,
  1016. sync_timeout),
  1017. RSPAMD_CL_FLAG_TIME_FLOAT,
  1018. "Time to perform database sync, default: "
  1019. G_STRINGIFY (DEFAULT_SYNC_TIMEOUT) " seconds");
  1020. rspamd_rcl_register_worker_option (cfg,
  1021. type,
  1022. "expire",
  1023. rspamd_rcl_parse_struct_time,
  1024. ctx,
  1025. G_STRUCT_OFFSET (struct rspamd_fuzzy_storage_ctx,
  1026. expire),
  1027. RSPAMD_CL_FLAG_TIME_FLOAT,
  1028. "Default expire time for hashes, default: "
  1029. G_STRINGIFY (DEFAULT_EXPIRE) " seconds");
  1030. rspamd_rcl_register_worker_option (cfg,
  1031. type,
  1032. "allow_update",
  1033. rspamd_rcl_parse_struct_string,
  1034. ctx,
  1035. G_STRUCT_OFFSET (struct rspamd_fuzzy_storage_ctx, update_map),
  1036. 0,
  1037. "Allow modifications from the following IP addresses");
  1038. rspamd_rcl_register_worker_option (cfg,
  1039. type,
  1040. "keypair",
  1041. fuzzy_parse_keypair,
  1042. ctx,
  1043. 0,
  1044. RSPAMD_CL_FLAG_MULTIPLE,
  1045. "Encryption keypair (can be repeated for different keys)");
  1046. rspamd_rcl_register_worker_option (cfg,
  1047. type,
  1048. "keypair_cache_size",
  1049. rspamd_rcl_parse_struct_integer,
  1050. ctx,
  1051. G_STRUCT_OFFSET (struct rspamd_fuzzy_storage_ctx,
  1052. keypair_cache_size),
  1053. RSPAMD_CL_FLAG_UINT,
  1054. "Size of keypairs cache, default: "
  1055. G_STRINGIFY (DEFAULT_KEYPAIR_CACHE_SIZE));
  1056. rspamd_rcl_register_worker_option (cfg,
  1057. type,
  1058. "encrypted_only",
  1059. rspamd_rcl_parse_struct_boolean,
  1060. ctx,
  1061. G_STRUCT_OFFSET (struct rspamd_fuzzy_storage_ctx, encrypted_only),
  1062. 0,
  1063. "Allow encrypted requests only (and forbid all unknown keys or plaintext requests)");
  1064. return ctx;
  1065. }
  1066. static void
  1067. rspamd_fuzzy_peer_io (gint fd, gshort what, gpointer d)
  1068. {
  1069. struct fuzzy_peer_cmd cmd, *pcmd;
  1070. struct rspamd_fuzzy_storage_ctx *ctx = d;
  1071. gssize r;
  1072. r = read (fd, &cmd, sizeof (cmd));
  1073. if (r != sizeof (cmd)) {
  1074. msg_err ("cannot read command from peers: %s", strerror (errno));
  1075. }
  1076. else {
  1077. pcmd = g_slice_alloc (sizeof (*pcmd));
  1078. memcpy (pcmd, &cmd, sizeof (cmd));
  1079. g_queue_push_tail (ctx->updates_pending, pcmd);
  1080. }
  1081. }
  1082. static void
  1083. fuzzy_peer_rep (struct rspamd_worker *worker,
  1084. struct rspamd_srv_reply *rep, gint rep_fd,
  1085. gpointer ud)
  1086. {
  1087. struct rspamd_fuzzy_storage_ctx *ctx = ud;
  1088. GList *cur;
  1089. gint listen_socket;
  1090. struct event *accept_event;
  1091. gdouble next_check;
  1092. ctx->peer_fd = rep_fd;
  1093. if (rep_fd == -1) {
  1094. msg_err ("cannot receive peer fd from the main process");
  1095. exit (EXIT_FAILURE);
  1096. }
  1097. /* Start listening */
  1098. cur = worker->cf->listen_socks;
  1099. while (cur) {
  1100. listen_socket = GPOINTER_TO_INT (cur->data);
  1101. if (listen_socket != -1) {
  1102. accept_event = g_slice_alloc0 (sizeof (struct event));
  1103. event_set (accept_event, listen_socket, EV_READ | EV_PERSIST,
  1104. accept_fuzzy_socket, worker);
  1105. event_base_set (ctx->ev_base, accept_event);
  1106. event_add (accept_event, NULL);
  1107. worker->accept_events = g_list_prepend (worker->accept_events,
  1108. accept_event);
  1109. }
  1110. cur = g_list_next (cur);
  1111. }
  1112. if (worker->index == 0 && ctx->peer_fd != -1) {
  1113. /* Listen for peer requests */
  1114. event_set (&ctx->peer_ev, ctx->peer_fd, EV_READ | EV_PERSIST,
  1115. rspamd_fuzzy_peer_io, ctx);
  1116. event_base_set (ctx->ev_base, &ctx->peer_ev);
  1117. event_add (&ctx->peer_ev, NULL);
  1118. ctx->updates_pending = g_queue_new ();
  1119. /* Timer event */
  1120. evtimer_set (&tev, sync_callback, worker);
  1121. event_base_set (ctx->ev_base, &tev);
  1122. /* Plan event with jitter */
  1123. next_check = rspamd_time_jitter (ctx->sync_timeout, 0);
  1124. double_to_tv (next_check, &tmv);
  1125. evtimer_add (&tev, &tmv);
  1126. }
  1127. }
  1128. /*
  1129. * Start worker process
  1130. */
  1131. void
  1132. start_fuzzy (struct rspamd_worker *worker)
  1133. {
  1134. struct rspamd_fuzzy_storage_ctx *ctx = worker->ctx;
  1135. GError *err = NULL;
  1136. struct rspamd_srv_command srv_cmd;
  1137. ctx->ev_base = rspamd_prepare_worker (worker,
  1138. "fuzzy",
  1139. NULL);
  1140. ctx->peer_fd = -1;
  1141. /*
  1142. * Open DB and perform VACUUM
  1143. */
  1144. if ((ctx->backend = rspamd_fuzzy_backend_open (ctx->hashfile, TRUE, &err)) == NULL) {
  1145. msg_err ("cannot open backend: %e", err);
  1146. g_error_free (err);
  1147. exit (EXIT_SUCCESS);
  1148. }
  1149. ctx->stat.fuzzy_hashes = rspamd_fuzzy_backend_count (ctx->backend);
  1150. if (ctx->default_key && ctx->keypair_cache_size > 0) {
  1151. /* Create keypairs cache */
  1152. ctx->keypair_cache = rspamd_keypair_cache_new (ctx->keypair_cache_size);
  1153. }
  1154. if (worker->index == 0) {
  1155. rspamd_fuzzy_backend_sync (ctx->backend, ctx->expire, TRUE);
  1156. }
  1157. /* Register custom reload and stat commands for the control socket */
  1158. rspamd_control_worker_add_cmd_handler (worker, RSPAMD_CONTROL_RELOAD,
  1159. rspamd_fuzzy_storage_reload, ctx);
  1160. rspamd_control_worker_add_cmd_handler (worker, RSPAMD_CONTROL_FUZZY_STAT,
  1161. rspamd_fuzzy_storage_stat, ctx);
  1162. /* Create radix tree */
  1163. if (ctx->update_map != NULL) {
  1164. if (!rspamd_map_add (worker->srv->cfg, ctx->update_map,
  1165. "Allow fuzzy updates from specified addresses",
  1166. rspamd_radix_read, rspamd_radix_fin, (void **)&ctx->update_ips)) {
  1167. if (!radix_add_generic_iplist (ctx->update_map,
  1168. &ctx->update_ips)) {
  1169. msg_warn ("cannot load or parse ip list from '%s'",
  1170. ctx->update_map);
  1171. }
  1172. }
  1173. }
  1174. /* Maps events */
  1175. rspamd_map_watch (worker->srv->cfg, ctx->ev_base);
  1176. /* Get peer pipe */
  1177. memset (&srv_cmd, 0, sizeof (srv_cmd));
  1178. srv_cmd.type = RSPAMD_SRV_SOCKETPAIR;
  1179. srv_cmd.id = ottery_rand_uint64 ();
  1180. srv_cmd.cmd.spair.af = SOCK_DGRAM;
  1181. srv_cmd.cmd.spair.pair_num = worker->index;
  1182. memset (srv_cmd.cmd.spair.pair_id, 0, sizeof (srv_cmd.cmd.spair.pair_id));
  1183. memcpy (srv_cmd.cmd.spair.pair_id, "fuzzy", sizeof ("fuzzy"));
  1184. rspamd_srv_send_command (worker, ctx->ev_base, &srv_cmd, fuzzy_peer_rep, ctx);
  1185. event_base_loop (ctx->ev_base, 0);
  1186. rspamd_worker_block_signals ();
  1187. if (worker->index == 0) {
  1188. rspamd_fuzzy_process_updates_queue (ctx);
  1189. rspamd_fuzzy_backend_sync (ctx->backend, ctx->expire, TRUE);
  1190. }
  1191. rspamd_fuzzy_backend_close (ctx->backend);
  1192. rspamd_log_close (worker->srv->logger);
  1193. if (ctx->peer_fd != -1) {
  1194. if (worker->index == 0) {
  1195. event_del (&ctx->peer_ev);
  1196. }
  1197. close (ctx->peer_fd);
  1198. }
  1199. if (ctx->keypair_cache) {
  1200. rspamd_keypair_cache_destroy (ctx->keypair_cache);
  1201. }
  1202. rspamd_lru_hash_destroy (ctx->errors_ips);
  1203. g_hash_table_unref (ctx->keys);
  1204. exit (EXIT_SUCCESS);
  1205. }