]> source.dussan.org Git - rspamd.git/commitdiff
Fix parsing routines.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Wed, 18 Mar 2015 15:13:04 +0000 (15:13 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Wed, 18 Mar 2015 15:13:04 +0000 (15:13 +0000)
src/libserver/spf.c

index 6e4e55a86ad975fe825440bb65ecb9e8da34bcbd..a93780123c20194010ad86901cb63ae0006ef70c 100644 (file)
@@ -911,10 +911,7 @@ parse_spf_ptr (struct rspamd_task *task,
 }
 
 static gboolean
-parse_spf_mx (struct rspamd_task *task,
-       const gchar *begin,
-       struct spf_record *rec,
-       struct spf_addr *addr)
+parse_spf_mx (struct spf_record *rec, struct spf_addr *addr)
 {
        struct spf_dns_cb *cb;
        gchar *host;
@@ -952,76 +949,66 @@ parse_spf_mx (struct rspamd_task *task,
 }
 
 static gboolean
-parse_spf_all (struct rspamd_task *task,
-       const gchar *begin,
-       struct spf_record *rec,
-       struct spf_addr *addr)
+parse_spf_all (struct spf_record *rec, struct spf_addr *addr)
 {
        /* All is 0/0 */
-       memset (&addr->data.normal.d, 0, sizeof (addr->data.normal.d));
-       if (rec->in_include) {
-               /* Ignore all record in include */
-               addr->data.normal.mask = 32;
-       }
-       else {
-               addr->data.normal.mask = 0;
-               addr->data.normal.addr_any = TRUE;
-       }
+       memset (&addr->addr, 0, sizeof (addr->addr));
+       addr->m.mask = 0;
+       addr->flags |= RSPAMD_SPF_FLAG_ANY;
 
        return TRUE;
 }
 
 static gboolean
-parse_spf_ip4 (struct rspamd_task *task,
-       const gchar *begin,
-       struct spf_record *rec,
-       struct spf_addr *addr)
+parse_spf_ip4 (struct spf_record *rec, struct spf_addr *addr)
 {
        /* ip4:addr[/mask] */
 
        CHECK_REC (rec);
-       return parse_spf_ipmask (begin, addr, rec);
+       return parse_spf_ipmask (addr->spf_string, addr, rec);
 }
 
 #ifdef HAVE_INET_PTON
 static gboolean
-parse_spf_ip6 (struct rspamd_task *task,
-       const gchar *begin,
-       struct spf_record *rec,
-       struct spf_addr *addr)
+parse_spf_ip6 (struct spf_record *rec, struct spf_addr *addr)
 {
        /* ip6:addr[/mask] */
 
        CHECK_REC (rec);
-       return parse_spf_ipmask (begin, addr, rec);
+       return parse_spf_ipmask (addr->spf_string, addr, rec);
 }
 #endif
 
 static gboolean
-parse_spf_include (struct rspamd_task *task,
-       const gchar *begin,
-       struct spf_record *rec,
-       struct spf_addr *addr)
+parse_spf_include (struct spf_record *rec, struct spf_addr *addr)
 {
        struct spf_dns_cb *cb;
        gchar *domain;
+       struct spf_resolved_element *resolved;
+       struct rspamd_task *task = rec->task;
+
+       resolved = &g_array_index (rec->resolved, struct spf_resolved_element,
+                       rec->resolved->len - 1);
 
        CHECK_REC (rec);
+       domain = strchr (addr->spf_string, '=');
 
-       if (begin == NULL || *begin != ':') {
+       if (domain == NULL) {
                return FALSE;
        }
-       begin++;
+
+       domain++;
+
        rec->dns_requests++;
 
        cb = rspamd_mempool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
        cb->rec = rec;
        cb->addr = addr;
        cb->cur_action = SPF_RESOLVE_INCLUDE;
-       cb->in_include = rec->in_include;
-       addr->is_list = TRUE;
-       addr->data.list = NULL;
-       domain = rspamd_mempool_strdup (task->task_pool, begin);
+       /* Set reference */
+       addr->flags |= RSPAMD_SPF_FLAG_REFRENCE;
+       addr->m.mask = rec->resolved->len;
+       rspamd_spf_new_addr_list (rec, domain);
        msg_debug ("resolve include %s", domain);
 
        if (make_dns_request (task->resolver, task->s, task->task_pool,
@@ -1037,10 +1024,7 @@ parse_spf_include (struct rspamd_task *task,
 }
 
 static gboolean
-parse_spf_exp (struct rspamd_task *task,
-       const gchar *begin,
-       struct spf_record *rec,
-       struct spf_addr *addr)
+parse_spf_exp (struct spf_record *rec, struct spf_addr *addr)
 {
        CHECK_REC (rec);
 
@@ -1049,29 +1033,51 @@ parse_spf_exp (struct rspamd_task *task,
 }
 
 static gboolean
-parse_spf_redirect (struct rspamd_task *task,
-       const gchar *begin,
-       struct spf_record *rec,
-       struct spf_addr *addr)
+parse_spf_redirect (struct spf_record *rec, struct spf_addr *addr)
 {
        struct spf_dns_cb *cb;
-       gchar *domain;
+       const gchar *domain;
+       struct spf_resolved_element *resolved;
+       struct spf_addr *cur;
+       struct rspamd_task *task = rec->task;
+       guint i;
+
+       resolved = &g_array_index (rec->resolved, struct spf_resolved_element,
+                       rec->resolved->len - 1);
 
        CHECK_REC (rec);
 
-       if (begin == NULL || *begin != '=') {
+       domain = strchr (addr->spf_string, '=');
+
+       if (domain == NULL) {
                return FALSE;
        }
-       begin++;
+
+       domain++;
+
        rec->dns_requests++;
+       resolved->redirected = TRUE;
+
+       /* Now clear all elements but this one */
+       for (i = 0; i < resolved->elts->len; i ++) {
+               cur = g_ptr_array_index (resolved->elts, i);
+
+               if (cur != addr) {
+                       g_ptr_array_remove_index_fast (resolved->elts, i);
+               }
+       }
 
        cb = rspamd_mempool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
+       /* Set reference */
+       addr->flags |= RSPAMD_SPF_FLAG_REFRENCE;
+       addr->m.mask = rec->resolved->len;
+       rspamd_spf_new_addr_list (rec, domain);
+
        cb->rec = rec;
        cb->addr = addr;
        cb->cur_action = SPF_RESOLVE_REDIRECT;
-       cb->in_include = rec->in_include;
-       domain = rspamd_mempool_strdup (task->task_pool, begin);
        msg_debug ("resolve redirect %s", domain);
+
        if (make_dns_request (task->resolver, task->s, task->task_pool,
                spf_record_dns_callback, (void *)cb, RDNS_REQUEST_TXT, domain)) {
                task->dns_requests++;
@@ -1084,29 +1090,27 @@ parse_spf_redirect (struct rspamd_task *task,
 }
 
 static gboolean
-parse_spf_exists (struct rspamd_task *task,
-       const gchar *begin,
-       struct spf_record *rec,
-       struct spf_addr *addr)
+parse_spf_exists (struct spf_record *rec, struct spf_addr *addr)
 {
        struct spf_dns_cb *cb;
-       gchar *host;
+       const gchar *host;
+       struct rspamd_task *task = rec->task;
 
        CHECK_REC (rec);
 
-       if (begin == NULL || *begin != ':') {
+       host = strchr (addr->spf_string, ':');
+       if (host == NULL) {
+               msg_info ("bad SPF exist record: %s", addr->spf_string);
                return FALSE;
        }
-       begin++;
+
+       host ++;
        rec->dns_requests++;
 
-       addr->data.normal.mask = 32;
        cb = rspamd_mempool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
        cb->rec = rec;
        cb->addr = addr;
        cb->cur_action = SPF_RESOLVE_EXISTS;
-       cb->in_include = rec->in_include;
-       host = rspamd_mempool_strdup (task->task_pool, begin);
 
        msg_debug ("resolve exists %s", host);
        if (make_dns_request (task->resolver, task->s, task->task_pool,
@@ -1152,17 +1156,26 @@ reverse_spf_ip (gchar *ip, gint len)
        memcpy (ip,        ipbuf, len);
 }
 
-static gchar *
-expand_spf_macro (struct rspamd_task *task, struct spf_record *rec,
-       gchar *begin)
+static const gchar *
+expand_spf_macro (struct spf_record *rec,
+       const gchar *begin)
 {
-       gchar *p, *c, *new, *tmp;
+       const gchar *p, *c;
+       gchar *new, *tmp;
        gint len = 0, slen = 0, state = 0;
 #ifdef HAVE_INET_PTON
        gchar ip_buf[INET6_ADDRSTRLEN];
 #endif
        gboolean need_expand = FALSE;
+       struct rspamd_task *task;
+       struct spf_resolved_element *resolved;
+
+       g_assert (rec != NULL);
+       g_assert (begin != NULL);
 
+       task = rec->task;
+       resolved = &g_array_index (rec->resolved, struct spf_resolved_element,
+                       rec->resolved->len - 1);
        p = begin;
        /* Calculate length */
        while (*p) {
@@ -1223,7 +1236,7 @@ expand_spf_macro (struct rspamd_task *task, struct spf_record *rec,
                                len += strlen (rec->sender_domain);
                                break;
                        case 'd':
-                               len += strlen (rec->cur_domain);
+                               len += strlen (resolved->cur_domain);
                                break;
                        case 'v':
                                len += sizeof ("in-addr") - 1;
@@ -1348,8 +1361,8 @@ expand_spf_macro (struct rspamd_task *task, struct spf_record *rec,
                                c += len;
                                break;
                        case 'd':
-                               len = strlen (rec->cur_domain);
-                               memcpy (c, rec->cur_domain, len);
+                               len = strlen (resolved->cur_domain);
+                               memcpy (c, resolved->cur_domain, len);
                                c += len;
                                break;
                        case 'v':
@@ -1411,174 +1424,138 @@ expand_spf_macro (struct rspamd_task *task, struct spf_record *rec,
 
 }
 
-#define NEW_ADDR(x) do {                                                        \
-               (x) = rspamd_mempool_alloc (task->task_pool, sizeof (struct spf_addr));     \
-               (x)->mech = check_spf_mech (rec->cur_elt, &need_shift);                     \
-               (x)->spf_string = rspamd_mempool_strdup (task->task_pool, begin);               \
-               memset (&(x)->data.normal, 0, sizeof ((x)->data.normal));                   \
-               (x)->data.normal.mask = 32;                                                 \
-               (x)->is_list = FALSE;                                                       \
-} while (0);
-
 /* Read current element and try to parse record */
 static gboolean
-parse_spf_record (struct rspamd_task *task, struct spf_record *rec)
+parse_spf_record (struct spf_record *rec, const gchar *elt)
 {
-       struct spf_addr *new = NULL;
-       gboolean need_shift, res = FALSE;
-       gchar *begin;
+       struct spf_addr *addr = NULL;
+       gboolean res = FALSE;
+       const gchar *begin;
+       struct rspamd_task *task;
+       struct spf_resolved_element *resolved;
+       gchar t;
 
-       rec->cur_elt = rec->elts[rec->elt_num];
-       if (rec->cur_elt == NULL) {
-               return FALSE;
-       }
-       else if (*rec->cur_elt == '\0') {
-               /* Silently skip empty elements */
-               rec->elt_num++;
+       resolved = &g_array_index (rec->resolved, struct spf_resolved_element,
+               rec->resolved->len - 1);
+
+       g_assert (elt != NULL);
+       g_assert (rec != NULL);
+
+       if (*elt == '\0' || resolved->redirected) {
                return TRUE;
        }
-       else {
-               begin = expand_spf_macro (task, rec, rec->cur_elt);
-               if (*begin == '?' || *begin == '+' || *begin == '-' || *begin == '~') {
-                       begin++;
-               }
 
+       task = rec->task;
+       begin = expand_spf_macro (rec, elt);
+       addr = rspamd_spf_new_addr (rec, begin);
+       g_assert (addr != NULL);
+       t = g_ascii_tolower (addr->spf_string[0]);
+       begin = addr->spf_string;
 
-               /* Now check what we have */
-               switch (g_ascii_tolower (*begin)) {
-               case 'a':
-                       /* all or a */
-                       if (g_ascii_strncasecmp (begin, SPF_ALL,
+       /* Now check what we have */
+       switch (t) {
+       case 'a':
+               /* all or a */
+               if (g_ascii_strncasecmp (begin, SPF_ALL,
                                sizeof (SPF_ALL) - 1) == 0) {
-                               NEW_ADDR (new);
-                               begin += sizeof (SPF_ALL) - 1;
-                               res = parse_spf_all (task, begin, rec, new);
-                       }
-                       else if (g_ascii_strncasecmp (begin, SPF_A,
+                       res = parse_spf_all (rec, addr);
+               }
+               else if (g_ascii_strncasecmp (begin, SPF_A,
                                sizeof (SPF_A) - 1) == 0) {
-                               NEW_ADDR (new);
-                               begin += sizeof (SPF_A) - 1;
-                               res = parse_spf_a (task, begin, rec, new);
-                       }
-                       else {
-                               msg_info ("<%s>: spf error for domain %s: bad spf command %s",
+                       res = parse_spf_a (rec, addr);
+               }
+               else {
+                       msg_info ("<%s>: spf error for domain %s: bad spf command %s",
                                        task->message_id, rec->sender_domain, begin);
-                       }
-                       break;
-               case 'i':
-                       /* include or ip4 */
-                       if (g_ascii_strncasecmp (begin, SPF_IP4,
+               }
+               break;
+       case 'i':
+               /* include or ip4 */
+               if (g_ascii_strncasecmp (begin, SPF_IP4,
                                sizeof (SPF_IP4) - 1) == 0) {
-                               NEW_ADDR (new);
-                               begin += sizeof (SPF_IP4) - 1;
-                               res = parse_spf_ip4 (task, begin, rec, new);
-                       }
-                       else if (g_ascii_strncasecmp (begin, SPF_INCLUDE,
+                       res = parse_spf_ip4 (rec, addr);
+               }
+               else if (g_ascii_strncasecmp (begin, SPF_INCLUDE,
                                sizeof (SPF_INCLUDE) - 1) == 0) {
-                               NEW_ADDR (new);
-                               begin += sizeof (SPF_INCLUDE) - 1;
-                               res = parse_spf_include (task, begin, rec, new);
-                       }
-                       else if (g_ascii_strncasecmp (begin, SPF_IP6, sizeof (SPF_IP6) -
+                       res = parse_spf_include (rec, addr);
+               }
+               else if (g_ascii_strncasecmp (begin, SPF_IP6, sizeof (SPF_IP6) -
                                1) == 0) {
-#ifdef HAVE_INET_PTON
-                               NEW_ADDR (new);
-                               begin += sizeof (SPF_IP6) - 1;
-                               res = parse_spf_ip6 (task, begin, rec, new);
-#else
-                               msg_info (
-                                       "ignoring ip6 spf command as IPv6 is not supported: %s",
-                                       begin);
-                               new = NULL;
-                               res = TRUE;
-                               begin += sizeof (SPF_IP6) - 1;
-#endif
-                       }
-                       else {
-                               msg_info ("<%s>: spf error for domain %s: bad spf command %s",
+                       res = parse_spf_ip6 (rec, addr);
+               }
+               else {
+                       msg_info ("<%s>: spf error for domain %s: bad spf command %s",
                                        task->message_id, rec->sender_domain, begin);
-                       }
-                       break;
-               case 'm':
-                       /* mx */
-                       if (g_ascii_strncasecmp (begin, SPF_MX, sizeof (SPF_MX) - 1) == 0) {
-                               NEW_ADDR (new);
-                               begin += sizeof (SPF_MX) - 1;
-                               res = parse_spf_mx (task, begin, rec, new);
-                       }
-                       else {
-                               msg_info ("<%s>: spf error for domain %s: bad spf command %s",
+               }
+               break;
+       case 'm':
+               /* mx */
+               if (g_ascii_strncasecmp (begin, SPF_MX, sizeof (SPF_MX) - 1) == 0) {
+                       res = parse_spf_mx (rec, addr);
+               }
+               else {
+                       msg_info ("<%s>: spf error for domain %s: bad spf command %s",
                                        task->message_id, rec->sender_domain, begin);
-                       }
-                       break;
-               case 'p':
-                       /* ptr */
-                       if (g_ascii_strncasecmp (begin, SPF_PTR,
+               }
+               break;
+       case 'p':
+               /* ptr */
+               if (g_ascii_strncasecmp (begin, SPF_PTR,
                                sizeof (SPF_PTR) - 1) == 0) {
-                               NEW_ADDR (new);
-                               begin += sizeof (SPF_PTR) - 1;
-                               res = parse_spf_ptr (task, begin, rec, new);
-                       }
-                       else {
-                               msg_info ("<%s>: spf error for domain %s: bad spf command %s",
+                       res = parse_spf_ptr (rec, addr);
+               }
+               else {
+                       msg_info ("<%s>: spf error for domain %s: bad spf command %s",
                                        task->message_id, rec->sender_domain, begin);
-                       }
-                       break;
-               case 'e':
-                       /* exp or exists */
-                       if (g_ascii_strncasecmp (begin, SPF_EXP,
+               }
+               break;
+       case 'e':
+               /* exp or exists */
+               if (g_ascii_strncasecmp (begin, SPF_EXP,
                                sizeof (SPF_EXP) - 1) == 0) {
-                               begin += sizeof (SPF_EXP) - 1;
-                               res = parse_spf_exp (task, begin, rec, NULL);
-                       }
-                       else if (g_ascii_strncasecmp (begin, SPF_EXISTS,
+                       res = parse_spf_exp (rec, addr);
+               }
+               else if (g_ascii_strncasecmp (begin, SPF_EXISTS,
                                sizeof (SPF_EXISTS) - 1) == 0) {
-                               NEW_ADDR (new);
-                               begin += sizeof (SPF_EXISTS) - 1;
-                               res = parse_spf_exists (task, begin, rec, new);
-                       }
-                       else {
-                               msg_info ("<%s>: spf error for domain %s: bad spf command %s",
+                       res = parse_spf_exists (rec, addr);
+               }
+               else {
+                       msg_info ("<%s>: spf error for domain %s: bad spf command %s",
                                        task->message_id, rec->sender_domain, begin);
-                       }
-                       break;
-               case 'r':
-                       /* redirect */
-                       if (g_ascii_strncasecmp (begin, SPF_REDIRECT,
+               }
+               break;
+       case 'r':
+               /* redirect */
+               if (g_ascii_strncasecmp (begin, SPF_REDIRECT,
                                sizeof (SPF_REDIRECT) - 1) == 0) {
-                               begin += sizeof (SPF_REDIRECT) - 1;
-                               res = parse_spf_redirect (task, begin, rec, NULL);
-                       }
-                       else {
-                               msg_info ("<%s>: spf error for domain %s: bad spf command %s",
-                                       task->message_id, rec->sender_domain, begin);
-                       }
-                       break;
-               case 'v':
-                       if (g_ascii_strncasecmp (begin, "v=spf",
-                               sizeof ("v=spf") - 1) == 0) {
-                               /* Skip this element till the end of record */
-                               while (*begin && !g_ascii_isspace (*begin)) {
-                                       begin++;
-                               }
-                       }
-                       break;
-               default:
+                       res = parse_spf_redirect (rec, addr);
+               }
+               else {
                        msg_info ("<%s>: spf error for domain %s: bad spf command %s",
-                               task->message_id, rec->sender_domain, begin);
-                       break;
+                                       task->message_id, rec->sender_domain, begin);
                }
-               if (res) {
-                       if (new != NULL) {
-                               rec->addrs = g_list_prepend (rec->addrs, new);
+               break;
+       case 'v':
+               if (g_ascii_strncasecmp (begin, "v=spf",
+                               sizeof ("v=spf") - 1) == 0) {
+                       /* Skip this element till the end of record */
+                       while (*begin && !g_ascii_isspace (*begin)) {
+                               begin++;
                        }
-                       rec->elt_num++;
                }
+               break;
+       default:
+               msg_info ("<%s>: spf error for domain %s: bad spf command %s",
+                               task->message_id, rec->sender_domain, begin);
+               break;
+       }
+
+       if (res) {
+               addr->flags |= RSPAMD_SPF_FLAG_VALID;
        }
 
        return res;
 }
-#undef NEW_ADDR
 
 static void
 parse_spf_scopes (struct spf_record *rec, gchar **begin)