session->cmd_type == CMD_ENCRYPTED_SHINGLE) {
/* Encrypted reply */
data = &session->reply;
- len = sizeof (session->reply);
+
+ if (session->epoch > RSPAMD_FUZZY_EPOCH10) {
+ len = sizeof (session->reply);
+ }
+ else {
+ len = sizeof (session->reply.hdr + session->reply.rep.v1);
+ }
}
else {
data = &session->reply.rep;
- len = sizeof (session->reply.rep);
+
+ if (session->epoch > RSPAMD_FUZZY_EPOCH10) {
+ len = sizeof (session->reply.rep);
+ }
+ else {
+ len = sizeof (session->reply.rep.v1);
+ }
}
r = rspamd_inet_address_sendto (session->fd, data, len, 0,
gboolean encrypted, gboolean is_shingle)
{
if (cmd) {
- result->tag = cmd->tag;
+ result->v1.tag = cmd->tag;
memcpy (&session->reply.rep, result, sizeof (*result));
rspamd_fuzzy_update_stats (session->ctx,
session->epoch,
- result->prob > 0.5,
+ result->v1.prob > 0.5,
is_shingle,
session->key_stat,
session->ip_stat,
cmd->cmd,
- result->value);
+ result->v1.value);
if (encrypted) {
/* We need also to encrypt reply */
session->ip_stat = ip_stat;
}
- result.flag = cmd->flag;
+ result.v1.flag = cmd->flag;
if (cmd->cmd == FUZZY_CHECK) {
if (G_UNLIKELY (session->ctx->collection_mode)) {
- result.prob = 0;
- result.value = 500;
- result.flag = 0;
+ result.v1.prob = 0;
+ result.v1.value = 500;
+ result.v1.flag = 0;
rspamd_fuzzy_make_reply (cmd, &result, session, encrypted, is_shingle);
}
else {
}
else if (cmd->cmd == FUZZY_STAT) {
if (G_UNLIKELY (session->ctx->collection_mode)) {
- result.prob = 0;
- result.value = 500;
- result.flag = 0;
+ result.v1.prob = 0;
+ result.v1.value = 500;
+ result.v1.flag = 0;
rspamd_fuzzy_make_reply (cmd, &result, session, encrypted, is_shingle);
}
else {
- result.prob = 1.0;
- result.value = 0;
- result.flag = session->ctx->stat.fuzzy_hashes;
+ result.v1.prob = 1.0;
+ result.v1.value = 0;
+ result.v1.flag = session->ctx->stat.fuzzy_hashes;
rspamd_fuzzy_make_reply (cmd, &result, session, encrypted, is_shingle);
}
}
hexbuf[sizeof (hexbuf) - 1] = '\0';
if (g_hash_table_lookup (session->ctx->skip_hashes, hexbuf)) {
- result.value = 401;
- result.prob = 0.0;
+ result.v1.value = 401;
+ result.v1.prob = 0.0;
goto reply;
}
event_add (&up_req->io_ev, NULL);
}
- result.value = 0;
- result.prob = 1.0;
+ result.v1.value = 0;
+ result.v1.prob = 1.0;
}
else {
- result.value = 403;
- result.prob = 0.0;
+ result.v1.value = 403;
+ result.v1.prob = 0.0;
}
reply:
rspamd_fuzzy_make_reply (cmd, &result, session, encrypted, is_shingle);
enum rspamd_fuzzy_epoch ret = RSPAMD_FUZZY_EPOCH_MAX;
switch (cmd->version) {
+ case 4:
+ if (cmd->shingles_count > 0) {
+ if (r == sizeof (struct rspamd_fuzzy_shingle_cmd)) {
+ ret = RSPAMD_FUZZY_EPOCH11;
+ }
+ }
+ else {
+ if (r == sizeof (*cmd)) {
+ ret = RSPAMD_FUZZY_EPOCH11;
+ }
+ }
+ break;
case 3:
if (cmd->shingles_count > 0) {
if (r == sizeof (struct rspamd_fuzzy_shingle_cmd)) {
if (max_found > RSPAMD_SHINGLE_SIZE / 2) {
session->prob = ((float)max_found) / RSPAMD_SHINGLE_SIZE;
- rep.prob = session->prob;
+ rep.v1.prob = session->prob;
g_assert (sel != NULL);
/* Prepare new check command */
rspamd_fuzzy_redis_session_free_args (session);
- session->nargs = 4;
+ session->nargs = 5;
session->argv = g_malloc (sizeof (gchar *) * session->nargs);
session->argv_lens = g_malloc (sizeof (gsize) * session->nargs);
session->argv_lens[2] = 1;
session->argv[3] = g_strdup ("F");
session->argv_lens[3] = 1;
+ session->argv[3] = g_strdup ("C");
+ session->argv_lens[3] = 1;
g_string_free (key, FALSE); /* Do not free underlying array */
+ memcpy (rep.digest, sel->digest, sizeof (rep.digest));
g_assert (session->ctx != NULL);
if (redisAsyncCommandArgv (session->ctx,
if (c->err == 0) {
rspamd_upstream_ok (session->up);
- if (reply->type == REDIS_REPLY_ARRAY && reply->elements == 2) {
+ if (reply->type == REDIS_REPLY_ARRAY && reply->elements >= 2) {
cur = reply->element[0];
if (cur->type == REDIS_REPLY_STRING) {
value = strtoul (cur->str, NULL, 10);
- rep.value = value;
+ rep.v1.value = value;
found_elts ++;
}
if (cur->type == REDIS_REPLY_STRING) {
value = strtoul (cur->str, NULL, 10);
- rep.flag = value;
+ rep.v1.flag = value;
found_elts ++;
}
- if (found_elts == 2) {
- rep.prob = session->prob;
+ if (found_elts >= 2) {
+ rep.v1.prob = session->prob;
+ memcpy (rep.digest, session->cmd->digest, sizeof (rep.digest));
+ }
+
+ rep.ts = 0;
+
+ if (reply->elements > 2) {
+ cur = reply->element[2];
+
+ if (cur->type == REDIS_REPLY_STRING) {
+ rep.ts = strtoul (cur->str, NULL, 10);
+ }
}
}
session->ev_base = rspamd_fuzzy_backend_event_base (bk);
/* First of all check digest */
- session->nargs = 4;
+ session->nargs = 5;
session->argv = g_malloc (sizeof (gchar *) * session->nargs);
session->argv_lens = g_malloc (sizeof (gsize) * session->nargs);
session->argv_lens[2] = 1;
session->argv[3] = g_strdup ("F");
session->argv_lens[3] = 1;
+ session->argv[4] = g_strdup ("C");
+ session->argv_lens[4] = 1;
g_string_free (key, FALSE); /* Do not free underlying array */
up = rspamd_upstream_get (backend->read_servers,
if (cmd->cmd == FUZZY_WRITE) {
/*
- * For each normal hash addition we do 3 redis commands:
+ * For each normal hash addition we do 5 redis commands:
* HSET <key> F <flag>
+ * HSETNX <key> C <time>
* HINCRBY <key> V <weight>
* EXPIRE <key> <expire>
* Where <key> is <prefix> || <digest>
return FALSE;
}
+ /* HSETNX */
+ klen = strlen (session->backend->redis_object) +
+ sizeof (cmd->digest) + 1;
+ key = g_string_sized_new (klen);
+ g_string_append (key, session->backend->redis_object);
+ g_string_append_len (key, cmd->digest, sizeof (cmd->digest));
+ value = g_string_sized_new (30);
+ rspamd_printf_gstring (value, "%L", (gint64)rspamd_get_calendar_ticks ());
+ session->argv[cur_shift] = g_strdup ("HSETNX");
+ session->argv_lens[cur_shift++] = sizeof ("HSETNX") - 1;
+ session->argv[cur_shift] = key->str;
+ session->argv_lens[cur_shift++] = key->len;
+ session->argv[cur_shift] = g_strdup ("C");
+ session->argv_lens[cur_shift++] = sizeof ("C") - 1;
+ session->argv[cur_shift] = value->str;
+ session->argv_lens[cur_shift++] = value->len;
+ g_string_free (key, FALSE);
+ g_string_free (value, FALSE);
+
+ if (redisAsyncCommandArgv (session->ctx, NULL, NULL,
+ 4,
+ (const gchar **)&session->argv[cur_shift - 4],
+ &session->argv_lens[cur_shift - 4]) != REDIS_OK) {
+
+ return FALSE;
+ }
+
/* HINCRBY */
key = g_string_sized_new (klen);
g_string_append (key, session->backend->redis_object);
}
if (cmd->cmd == FUZZY_WRITE) {
- ncommands += 4;
- nargs += 13;
+ ncommands += 5;
+ nargs += 17;
if (io_cmd->is_shingle) {
ncommands += RSPAMD_SHINGLE_SIZE;
rspamd_fuzzy_backend_sqlite_check (struct rspamd_fuzzy_backend_sqlite *backend,
const struct rspamd_fuzzy_cmd *cmd, gint64 expire)
{
- struct rspamd_fuzzy_reply rep = {0, 0, 0, 0.0};
+ struct rspamd_fuzzy_reply rep;
const struct rspamd_fuzzy_shingle_cmd *shcmd;
int rc;
gint64 timestamp;
gint64 shingle_values[RSPAMD_SHINGLE_SIZE], i, sel_id, cur_id,
cur_cnt, max_cnt;
+ memset (&rep, 0, sizeof (rep));
+ memcpy (rep.digest, cmd->digest, sizeof (rep.digest));
+
if (backend == NULL) {
return rep;
}
msg_debug_fuzzy_backend ("requested hash has been expired");
}
else {
- rep.value = sqlite3_column_int64 (
+ rep.v1.value = sqlite3_column_int64 (
prepared_stmts[RSPAMD_FUZZY_BACKEND_CHECK].stmt, 0);
- rep.prob = 1.0;
- rep.flag = sqlite3_column_int (
+ rep.v1.prob = 1.0;
+ rep.v1.flag = sqlite3_column_int (
prepared_stmts[RSPAMD_FUZZY_BACKEND_CHECK].stmt, 2);
}
}
if (sel_id != -1) {
/* We have some id selected here */
- rep.prob = (float)max_cnt / (float)RSPAMD_SHINGLE_SIZE;
+ rep.v1.prob = (float)max_cnt / (float)RSPAMD_SHINGLE_SIZE;
- if (rep.prob > 0.5) {
+ if (rep.v1.prob > 0.5) {
msg_debug_fuzzy_backend (
"found fuzzy hash with probability %.2f",
- rep.prob);
+ rep.v1.prob);
rc = rspamd_fuzzy_backend_sqlite_run_stmt (backend, FALSE,
RSPAMD_FUZZY_BACKEND_GET_DIGEST_BY_ID, sel_id);
if (rc == SQLITE_OK) {
/* Expire element */
msg_debug_fuzzy_backend (
"requested hash has been expired");
- rep.prob = 0.0;
+ rep.v1.prob = 0.0;
}
else {
- rep.value = sqlite3_column_int64 (
+ rep.ts = timestamp;
+ memcpy (rep.digest, sqlite3_column_blob (
+ prepared_stmts[RSPAMD_FUZZY_BACKEND_GET_DIGEST_BY_ID].stmt,
+ 0), sizeof (rep.digest));
+ rep.v1.value = sqlite3_column_int64 (
prepared_stmts[RSPAMD_FUZZY_BACKEND_GET_DIGEST_BY_ID].stmt,
1);
- rep.flag = sqlite3_column_int (
+ rep.v1.flag = sqlite3_column_int (
prepared_stmts[RSPAMD_FUZZY_BACKEND_GET_DIGEST_BY_ID].stmt,
3);
}
}
else {
/* Otherwise we assume that as error */
- rep.value = 0;
+ rep.v1.value = 0;
}
rspamd_fuzzy_backend_sqlite_cleanup_stmt (backend,
#include "shingles.h"
#include "cryptobox.h"
-#define RSPAMD_FUZZY_VERSION 3
+#define RSPAMD_FUZZY_VERSION 4
#define RSPAMD_FUZZY_KEYLEN 8
/* Commands for fuzzy storage */
RSPAMD_FUZZY_EPOCH8, /**< 0.8 till 0.9 */
RSPAMD_FUZZY_EPOCH9, /**< 0.9 + */
RSPAMD_FUZZY_EPOCH10, /**< 1.0+ encryption */
+ RSPAMD_FUZZY_EPOCH11, /**< 1.7+ extended reply */
RSPAMD_FUZZY_EPOCH_MAX
};
struct rspamd_shingle sgl;
};
-RSPAMD_PACKED(rspamd_fuzzy_reply) {
+RSPAMD_PACKED(rspamd_fuzzy_reply_v1) {
gint32 value;
guint32 flag;
guint32 tag;
float prob;
};
+RSPAMD_PACKED(rspamd_fuzzy_reply) {
+ struct rspamd_fuzzy_reply_v1 v1;
+ gchar digest[rspamd_cryptobox_HASHBYTES];
+ guint32 ts;
+ guchar reserved[12];
+};
+
RSPAMD_PACKED(rspamd_fuzzy_encrypted_req_hdr) {
guchar magic[4];
guchar key_id[RSPAMD_FUZZY_KEYLEN];