Browse Source

* Fix parsing of include records in spf parser

tags/0.3.8
Vsevolod Stakhov 13 years ago
parent
commit
2a2bc886c1
3 changed files with 173 additions and 123 deletions
  1. 65
    45
      src/plugins/spf.c
  2. 100
    76
      src/spf.c
  3. 8
    2
      src/spf.h

+ 65
- 45
src/plugins/spf.c View File

@@ -128,55 +128,75 @@ spf_module_reconfig (struct config_file *cfg)
return spf_module_config (cfg);
}

static void
spf_plugin_callback (struct spf_record *record, struct worker_task *task)
static gboolean
spf_check_element (struct spf_addr *addr, struct worker_task *task)
{
GList *cur;
struct spf_addr *addr;
guint32 s, m;

if (record) {
cur = g_list_last (record->addrs);
s = ntohl (task->from_addr.s_addr);
while (cur) {
addr = cur->data;
if (addr != NULL) {
if (addr->mask == 0) {
m = 0;
}
else {
m = G_MAXUINT32 << (32 - addr->mask);
}
if (addr->addr == 0 && cur->prev != NULL) {
/*
* In fact default record should be the last element in a record
* so ignore such other records
*/
cur = g_list_previous (cur);
continue;
}
if ((s & m) == (addr->addr & m)) {
switch (addr->mech) {
case SPF_FAIL:
insert_result (task, spf_module_ctx->symbol_fail, 1, g_list_prepend (NULL, addr->spf_string));
task->messages = g_list_prepend (task->messages, "(SPF): spf fail");
break;
case SPF_SOFT_FAIL:
case SPF_NEUTRAL:
insert_result (task, spf_module_ctx->symbol_softfail, 1, g_list_prepend (NULL, addr->spf_string));
task->messages = g_list_prepend (task->messages, "(SPF): spf softfail");
break;
default:
insert_result (task, spf_module_ctx->symbol_allow, 1, g_list_prepend (NULL, addr->spf_string));
task->messages = g_list_prepend (task->messages, "(SPF): spf allow");
break;
}
/* Stop parsing */
break;
}
}
cur = g_list_previous (cur);
if (addr->data.normal.mask == 0) {
m = 0;
}
else {
m = G_MAXUINT32 << (32 - addr->data.normal.mask);
}
s = ntohl (task->from_addr.s_addr);

if ((s & m) == (addr->data.normal.addr & m)) {
switch (addr->mech) {
case SPF_FAIL:
insert_result (task, spf_module_ctx->symbol_fail, 1, g_list_prepend (NULL, addr->spf_string));
task->messages = g_list_prepend (task->messages, "(SPF): spf fail");
break;
case SPF_SOFT_FAIL:
case SPF_NEUTRAL:
insert_result (task, spf_module_ctx->symbol_softfail, 1, g_list_prepend (NULL, addr->spf_string));
task->messages = g_list_prepend (task->messages, "(SPF): spf softfail");
break;
default:
insert_result (task, spf_module_ctx->symbol_allow, 1, g_list_prepend (NULL, addr->spf_string));
task->messages = g_list_prepend (task->messages, "(SPF): spf allow");
break;
}
return TRUE;
}

return FALSE;
}

static gboolean
spf_check_list (GList *list, struct worker_task *task)
{
GList *cur;
struct spf_addr *addr;

cur = list;

while (cur) {
addr = cur->data;
if (addr->is_list) {
/* Recursive call */
addr->data.list = g_list_reverse (addr->data.list);
if (spf_check_list (addr->data.list, task)) {
return TRUE;
}
}
else {
if (spf_check_element (addr, task)) {
return TRUE;
}
}
cur = g_list_next (cur);
}

return FALSE;
}

static void
spf_plugin_callback (struct spf_record *record, struct worker_task *task)
{
if (record) {
record->addrs = g_list_reverse (record->addrs);
spf_check_list (record->addrs, task);
}

if (task->save.saved == 0) {

+ 100
- 76
src/spf.c View File

@@ -126,25 +126,57 @@ dump_spf_record (GList *addrs)

while (cur) {
addr = cur->data;
switch (addr->mech) {
case SPF_FAIL:
c = '-';
break;
case SPF_SOFT_FAIL:
case SPF_NEUTRAL:
c = '~';
break;
case SPF_PASS:
c = '+';
break;
if (!addr->is_list) {
switch (addr->mech) {
case SPF_FAIL:
c = '-';
break;
case SPF_SOFT_FAIL:
case SPF_NEUTRAL:
c = '~';
break;
case SPF_PASS:
c = '+';
break;
}
ina.s_addr = htonl (addr->data.normal.addr);
r += snprintf (logbuf + r, sizeof (logbuf) - r, "%c%s/%d; ", c, inet_ntoa (ina), addr->data.normal.mask);
}
else {
r += snprintf (logbuf + r, sizeof (logbuf) - r, "%s; ", addr->spf_string);
dump_spf_record (addr->data.list);
}
ina.s_addr = htonl (addr->addr);
r += snprintf (logbuf + r, sizeof (logbuf) - r, "%c%s/%d; ", c, inet_ntoa (ina), addr->mask);
cur = g_list_next (cur);
}
msg_info ("spf record: %s", logbuf);
}

/* Find position of address inside addrs list */
static GList *
spf_addr_find (GList *addrs, gpointer to_find)
{
struct spf_addr *addr;
GList *cur, *res = NULL;

cur = addrs;
while (cur) {
addr = cur->data;
if (addr->is_list) {
if ((res = spf_addr_find (addr->data.list, to_find)) != NULL) {
return res;
}
}
else {
if (cur->data == to_find) {
return cur;
}
}
cur = g_list_next (cur);
}

return res;
}

/*
* Destructor for spf record
*/
@@ -152,8 +184,18 @@ static void
spf_record_destructor (gpointer r)
{
struct spf_record *rec = r;
GList *cur;
struct spf_addr *addr;

if (rec->addrs) {
cur = rec->addrs;
while (cur) {
addr = cur->data;
if (addr->is_list && addr->data.list != NULL) {
g_list_free (addr->data.list);
}
cur = g_list_next (cur);
}
g_list_free (rec->addrs);
}
}
@@ -223,17 +265,17 @@ parse_spf_ipmask (const gchar *begin, struct spf_addr *addr)
if (!inet_aton (ip_buf, &in)) {
return FALSE;
}
addr->addr = ntohl (in.s_addr);
addr->data.normal.addr = ntohl (in.s_addr);
if (state == 2) {
/* Also parse mask */
addr->mask = (mask_buf[0] - '0') * 10 + mask_buf[1] - '0';
if (addr->mask > 32) {
addr->data.normal.mask = (mask_buf[0] - '0') * 10 + mask_buf[1] - '0';
if (addr->data.normal.mask > 32) {
msg_info ("bad ipmask value: '%s'", begin);
return FALSE;
}
}
else {
addr->mask = 32;
addr->data.normal.mask = 32;
}

return TRUE;
@@ -255,8 +297,8 @@ parse_spf_hostmask (struct worker_task *task, const gchar *begin, struct spf_add
if (p != NULL) {
/* Extract mask */
rspamd_strlcpy (mask_buf, p + 1, sizeof (mask_buf));
addr->mask = mask_buf[0] * 10 + mask_buf[1];
if (addr->mask > 32) {
addr->data.normal.mask = mask_buf[0] * 10 + mask_buf[1];
if (addr->data.normal.mask > 32) {
return FALSE;
}
if (host == NULL) {
@@ -266,7 +308,7 @@ parse_spf_hostmask (struct worker_task *task, const gchar *begin, struct spf_add
}
}
else {
addr->mask = 32;
addr->data.normal.mask = 32;
if (host == NULL) {
host = memory_pool_strdup (task->task_pool, begin);
}
@@ -281,8 +323,7 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
struct spf_dns_cb *cb = arg;
gchar *begin;
union rspamd_reply_element *elt_data;
GList *tmp = NULL, *tmp1,
*elt, *last;
GList *tmp = NULL, *elt;
struct worker_task *task;
struct spf_addr *new_addr;

@@ -303,17 +344,17 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
}
}
else if (reply->type == DNS_REQUEST_A) {
if (cb->addr->addr == 0) {
cb->addr->addr = ntohl (elt_data->a.addr[0].s_addr);
cb->addr->mask = 32;
if (cb->addr->data.normal.addr == 0) {
cb->addr->data.normal.addr = ntohl (elt_data->a.addr[0].s_addr);
cb->addr->data.normal.mask = 32;
}
else {
/* Insert one more address */
tmp = g_list_find (cb->rec->addrs, cb->addr);
tmp = spf_addr_find (cb->rec->addrs, cb->addr);
if (tmp) {
new_addr = memory_pool_alloc (task->task_pool, sizeof (struct spf_addr));
memcpy (new_addr, cb->addr, sizeof (struct spf_addr));
new_addr->addr = ntohl (elt_data->a.addr[0].s_addr);
new_addr->data.normal.addr = ntohl (elt_data->a.addr[0].s_addr);
cb->rec->addrs = g_list_insert_before (cb->rec->addrs, tmp, new_addr);
}
else {
@@ -326,8 +367,8 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
case SPF_RESOLVE_A:
if (reply->type == DNS_REQUEST_A) {
/* XXX: process only one record */
cb->addr->addr = ntohl (elt_data->a.addr[0].s_addr);
cb->addr->mask = 32;
cb->addr->data.normal.addr = ntohl (elt_data->a.addr[0].s_addr);
cb->addr->data.normal.mask = 32;
}
break;
case SPF_RESOLVE_PTR:
@@ -351,40 +392,20 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
msg_info ("before include");
dump_spf_record (cb->rec->addrs);
#endif
if (cb->rec->addrs) {
tmp = cb->rec->addrs;
cb->rec->addrs = NULL;
}
tmp = cb->rec->addrs;
cb->rec->addrs = NULL;
cb->rec->in_include = TRUE;
start_spf_parse (cb->rec, begin);
cb->rec->in_include = FALSE;

if (tmp && cb->rec->addrs) {
tmp1 = g_list_find (tmp, cb->addr);
if (tmp1) {
/* Insert new list in place of include element */
cb->rec->addrs->prev = tmp1->prev;
last = g_list_last (cb->rec->addrs);
last->next = tmp1;
tmp1->prev = last;
/* If tmp1 is at the beginning of the list, shift list */
if (tmp == tmp1) {
tmp = cb->rec->addrs;
}
else {
tmp1->prev->next = cb->rec->addrs;
}
/* Remove self element */
tmp = g_list_delete_link (tmp, tmp1);
#ifdef SPF_DEBUG
msg_info ("after include");
dump_spf_record (cb->rec->addrs);
msg_info ("after include");
dump_spf_record (cb->rec->addrs);
#endif
}
}
if (tmp) {
cb->rec->addrs = tmp;
}
/* Insert new list */
cb->addr->is_list = TRUE;
cb->addr->data.list = cb->rec->addrs;
cb->rec->addrs = tmp;
}
break;
case SPF_RESOLVE_EXP:
@@ -392,8 +413,8 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
case SPF_RESOLVE_EXISTS:
if (reply->type == DNS_REQUEST_A) {
/* If specified address resolves, we can accept connection from every IP */
cb->addr->addr = ntohl (INADDR_ANY);
cb->addr->mask = 0;
cb->addr->data.normal.addr = ntohl (INADDR_ANY);
cb->addr->data.normal.mask = 0;
}
break;
}
@@ -406,20 +427,20 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
case SPF_RESOLVE_MX:
if (reply->type == DNS_REQUEST_MX) {
msg_info ("cannot find MX record for %s", cb->rec->cur_domain);
cb->addr->addr = ntohl (INADDR_NONE);
cb->addr->mask = 32;
cb->addr->data.normal.addr = ntohl (INADDR_NONE);
cb->addr->data.normal.mask = 32;
}
else if (reply->type != DNS_REQUEST_MX) {
msg_info ("cannot resolve MX record for %s", cb->rec->cur_domain);
cb->addr->addr = ntohl (INADDR_NONE);
cb->addr->mask = 32;
cb->addr->data.normal.addr = ntohl (INADDR_NONE);
cb->addr->data.normal.mask = 32;
}
break;
case SPF_RESOLVE_A:
if (reply->type == DNS_REQUEST_A) {
/* XXX: process only one record */
cb->addr->addr = ntohl (INADDR_NONE);
cb->addr->mask = 32;
cb->addr->data.normal.addr = ntohl (INADDR_NONE);
cb->addr->data.normal.mask = 32;
}
break;
case SPF_RESOLVE_PTR:
@@ -433,8 +454,8 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
case SPF_RESOLVE_EXP:
break;
case SPF_RESOLVE_EXISTS:
cb->addr->addr = ntohl (INADDR_NONE);
cb->addr->mask = 32;
cb->addr->data.normal.addr = ntohl (INADDR_NONE);
cb->addr->data.normal.mask = 32;
break;
}
}
@@ -514,7 +535,7 @@ parse_spf_mx (struct worker_task *task, const gchar *begin, struct spf_record *r
cb = memory_pool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
cb->rec = rec;
cb->addr = addr;
addr->addr = 0;
addr->data.normal.addr = 0;
cb->cur_action = SPF_RESOLVE_MX;
cb->in_include = rec->in_include;
if (make_dns_request (task->resolver, task->s, task->task_pool, spf_record_dns_callback, (void *)cb, DNS_REQUEST_MX, host)) {
@@ -532,12 +553,12 @@ parse_spf_all (struct worker_task *task, const gchar *begin, struct spf_record *
/* All is 0/0 */
if (rec->in_include) {
/* Ignore all record in include */
addr->addr = 0;
addr->mask = 32;
addr->data.normal.addr = 0;
addr->data.normal.mask = 32;
}
else {
addr->addr = 0;
addr->mask = 0;
addr->data.normal.addr = 0;
addr->data.normal.mask = 0;
}

return TRUE;
@@ -571,6 +592,8 @@ parse_spf_include (struct worker_task *task, const gchar *begin, struct spf_reco
cb->addr = addr;
cb->cur_action = SPF_RESOLVE_INCLUDE;
cb->in_include = rec->in_include;
addr->is_list = TRUE;
addr->data.list = NULL;
domain = memory_pool_strdup (task->task_pool, begin);
if (make_dns_request (task->resolver, task->s, task->task_pool, spf_record_dns_callback, (void *)cb, DNS_REQUEST_TXT, domain)) {
task->save.saved++;
@@ -634,7 +657,7 @@ parse_spf_exists (struct worker_task *task, const gchar *begin, struct spf_recor
begin ++;
rec->dns_requests ++;

addr->mask = 32;
addr->data.normal.mask = 32;
cb = memory_pool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
cb->rec = rec;
cb->addr = addr;
@@ -910,8 +933,9 @@ expand_spf_macro (struct worker_task *task, struct spf_record *rec, gchar *begin
(x) = memory_pool_alloc (task->task_pool, sizeof (struct spf_addr)); \
(x)->mech = check_spf_mech (rec->cur_elt, &need_shift); \
(x)->spf_string = memory_pool_strdup (task->task_pool, begin); \
(x)->addr = 0; \
(x)->mask = 32; \
(x)->data.normal.addr = 0; \
(x)->data.normal.mask = 32; \
(x)->is_list = FALSE; \
} while (0);

/* Read current element and try to parse record */
@@ -1183,7 +1207,7 @@ resolve_spf (struct worker_task *task, spf_cb_t callback)
rec->cur_domain = memory_pool_strdup (task->task_pool, domains->data);
g_list_free (domains);

if ((domain = strchr (rec->cur_domain, '@')) == NULL) {
if ((domain = strrchr (rec->cur_domain, '@')) == NULL) {
return FALSE;
}
rec->sender = memory_pool_strdup (task->task_pool, rec->cur_domain);

+ 8
- 2
src/spf.h View File

@@ -26,8 +26,14 @@ typedef enum spf_action_e {
} spf_action_t;

struct spf_addr {
guint32 addr;
guint32 mask;
union {
struct {
guint32 addr;
guint32 mask;
} normal;
GList *list;
} data;
gboolean is_list;
spf_mech_t mech;
gchar *spf_string;
};

Loading…
Cancel
Save