]> source.dussan.org Git - rspamd.git/commitdiff
* Fix parsing of include records in spf parser
authorVsevolod Stakhov <vsevolod@rambler-co.ru>
Thu, 3 Mar 2011 14:08:37 +0000 (17:08 +0300)
committerVsevolod Stakhov <vsevolod@rambler-co.ru>
Thu, 3 Mar 2011 14:08:37 +0000 (17:08 +0300)
src/plugins/spf.c
src/spf.c
src/spf.h

index 7b7dac6050e5fde656f5fb0d1395116d9b5e4e87..f5cbbe7b3b6b1ccc8e1f760c1281cc3138a34d07 100644 (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) {
index f19665e79dda96fc41ad4b002b22322918873ca2..233025ca3f013b9b78456688d72eb8faceccc559 100644 (file)
--- a/src/spf.c
+++ b/src/spf.c
@@ -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);
index c65a1bf15101e310b1ccfffa7cad5b1081bbcbd2..7a801254e2d91252e3f46c6378bb373b30b3ce5a 100644 (file)
--- a/src/spf.h
+++ b/src/spf.h
@@ -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;
 };