]> source.dussan.org Git - rspamd.git/commitdiff
* Updates to spf system:
authorVsevolod Stakhov <vsevolod@rambler-co.ru>
Fri, 4 Dec 2009 16:46:01 +0000 (19:46 +0300)
committerVsevolod Stakhov <vsevolod@rambler-co.ru>
Fri, 4 Dec 2009 16:46:01 +0000 (19:46 +0300)
  - add plugin to work with spf records (initial version)
  - make all spf types working (except include and redirect that still need some attention)
  - add support of MX records to evdns
  - some major fixes and testing of the whole spf subsystem

CMakeLists.txt
src/evdns/evdns.c
src/evdns/evdns.h
src/map.c
src/plugins/spf.c [new file with mode: 0644]
src/spf.c
src/spf.h

index cca84015a382a7355aee19528a292b1157314ecd..5bada252c1fc6fe8fdd113524215cae8bf92837b 100644 (file)
@@ -396,7 +396,8 @@ SET(PLUGINSSRC      src/plugins/surbl.c
                                src/plugins/regexp.c
                                src/plugins/chartable.c
                                src/plugins/emails.c
-                               src/plugins/fuzzy_check.c)
+                               src/plugins/fuzzy_check.c
+                               src/plugins/spf.c)
 
 SET(TESTSRC            test/rspamd_expression_test.c
                                test/rspamd_memcached_test.c
index 6db22379082d212cec6d4741416dbfcf524af943..4a8a7b650409870920de69ed155471c4a6fc91da 100644 (file)
@@ -71,6 +71,7 @@ typedef unsigned int uint;
 #define TYPE_A        EVDNS_TYPE_A
 #define TYPE_CNAME     5
 #define TYPE_TXT          EVDNS_TYPE_TXT
+#define TYPE_MX        EVDNS_TYPE_MX
 #define TYPE_PTR       EVDNS_TYPE_PTR
 #define TYPE_AAAA      EVDNS_TYPE_AAAA
 
@@ -133,6 +134,10 @@ struct reply {
                struct {
                        char name[HOST_NAME_MAX];
                } ptr;
+               struct {
+                       char name[HOST_NAME_MAX];
+                       u32 priority;
+               } mx;
                /* TXT field may be longer than 508 bytes, but UDP packets are limited to 512 octets */
                struct {
                        char data[508];
@@ -704,6 +709,18 @@ reply_callback(struct evdns_request *const req, u32 ttl, u32 err, struct reply *
                        req->user_callback(err, 0, 0, 0, NULL, req->user_pointer);
                }
                return; 
+       case TYPE_MX:
+               if (reply) {
+                       struct evdns_mx mx;
+                       mx.host = reply->data.mx.name;
+                       mx.priority = reply->data.mx.priority;
+                       req->user_callback(DNS_ERR_NONE, DNS_MX, 1, ttl,
+                                                               &mx, req->user_pointer);
+               }
+               else {
+                       req->user_callback(err, 0, 0, 0, NULL, req->user_pointer);
+               }
+               return; 
        }
        g_assert(0);
 }
@@ -990,6 +1007,18 @@ reply_parse(struct evdns_base *base, u8 *packet, int length) {
                        ttl_r = MIN(ttl_r, ttl);
                        reply.have_answer = 1;
                        break;
+               } else if (type == TYPE_MX) {
+                       if (req->request_type != TYPE_MX) {
+                               j += datalength;
+                               continue;
+                       }
+                       GET16(reply.data.mx.priority);
+                       if (name_parse(packet, length, &j, reply.data.mx.name,
+                                                  sizeof(reply.data.mx.name))<0)
+                               goto err;
+                       ttl_r = MIN(ttl_r, ttl);
+                       reply.have_answer = 1;
+                       break;
                } else {
                        /* skip over any other type of resource */
                        j += datalength;
@@ -2615,6 +2644,16 @@ evdns_resolve_txt(const char *in, int flags, evdns_callback_type callback, void
        return 0;
 }
 
+int
+evdns_resolve_mx(const char *in, int flags, evdns_callback_type callback, void *ptr) {
+       struct evdns_request *req;
+       g_assert(in);
+       req = request_new(current_base, TYPE_MX, in, flags, callback, ptr);
+       if (!req) return 1;
+       request_submit(req);
+       return 0;
+}
+
 
 /*/////////////////////////////////////////////////////////////////// */
 /* Search support */
index 5d6df1872014ebdb38e57f3a45f9bacaf29664b4..ded48f2ac7b4086d18d82cdf50241e22c883cafc 100644 (file)
@@ -58,6 +58,7 @@
 #define DNS_PTR 2
 #define DNS_IPv6_AAAA 3
 #define DNS_TXT 4
+#define DNS_MX  15
 
 #define DNS_QUERY_NO_SEARCH 1
 
@@ -78,6 +79,11 @@ typedef void (*evdns_callback_type) (int result, char type, int count, int ttl,
 struct evdns_base;
 struct event_base;
 
+struct evdns_mx {
+       char *host;
+       int priority;
+};
+
 /**
   Initialize the asynchronous DNS library.
 
@@ -664,6 +670,16 @@ int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callb
 */
 int evdns_resolve_txt(const char *in, int flags, evdns_callback_type callback, void *ptr);
 
+/**
+  Lookup a MX entry for a specified DNS name.
+  @param name a DNS name
+  @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
+  @param callback a callback function to invoke when the request is completed
+  @param ptr an argument to pass to the callback function
+  @return 0 if successful, or -1 if an error occurred
+*/
+int evdns_resolve_mx(const char *in, int flags, evdns_callback_type callback, void *ptr);
+
 
 /**
   Set the value of a configuration option.
index 6a96728e3b6efd244a149290cd3af68b6ac782fd..31a0569d99606ee9c4ca1202e8ec4f18b3ec3058 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -235,10 +235,21 @@ read_http_chunked (u_char * buf, size_t len, struct rspamd_map *map, struct http
 
                p = buf + (len - (data->chunk_read - data->chunk));
                if (*p != '\r') {
-                       msg_info ("read_http_chunked: invalid chunked reply");
-                       return FALSE;
+                       if (*p == '0') {
+                               return TRUE;
+                       }
+                       else {
+                               msg_info ("read_http_chunked: invalid chunked reply: %*s", len, buf);
+                               return FALSE;
+                       }
                }
                p += 2;
+               if (len == p - buf) {
+                       /* Next chunk data is not available */
+                       data->chunk = 0;
+                       return TRUE;
+               }
+
                len -= p - buf;
                skip = read_chunk_header (p, len, data);
                p += skip;
diff --git a/src/plugins/spf.c b/src/plugins/spf.c
new file mode 100644 (file)
index 0000000..1823f34
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2009, Rambler media
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Rambler media ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Rambler BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/***MODULE:spf
+ * rspamd module that checks spf records of incoming email
+ */
+
+#include "../config.h"
+#include "../main.h"
+#include "../message.h"
+#include "../modules.h"
+#include "../cfg_file.h"
+#include "../expressions.h"
+#include "../util.h"
+#include "../view.h"
+#include "../map.h"
+#include "../spf.h"
+
+#define DEFAULT_SYMBOL_FAIL "R_SPF_FAIL"
+#define DEFAULT_SYMBOL_SOFTFAIL "R_SPF_SOFTFAIL"
+#define DEFAULT_SYMBOL_ALLOW "R_SPF_ALLOW"
+
+struct spf_ctx {
+       int                             (*filter) (struct worker_task * task);
+       char                           *metric;
+       char                           *symbol_fail;
+       char                           *symbol_softfail;
+       char                           *symbol_allow;
+
+       memory_pool_t                  *spf_pool;
+};
+
+static struct spf_ctx        *spf_module_ctx = NULL;
+
+static void                   spf_symbol_callback (struct worker_task *task, void *unused);
+
+int
+spf_module_init (struct config_file *cfg, struct module_ctx **ctx)
+{
+       spf_module_ctx = g_malloc (sizeof (struct spf_ctx));
+
+       spf_module_ctx->spf_pool = memory_pool_new (memory_pool_get_size ());
+
+       *ctx = (struct module_ctx *)spf_module_ctx;
+
+       return 0;
+}
+
+
+int
+spf_module_config (struct config_file *cfg)
+{
+       char                           *value;
+       int                             res = TRUE;
+       struct metric                  *metric;
+       double                         *w;
+       
+       if ((value = get_module_opt (cfg, "spf", "metric")) != NULL) {
+               spf_module_ctx->metric = memory_pool_strdup (spf_module_ctx->spf_pool, value);
+               g_free (value);
+       }
+       else {
+               spf_module_ctx->metric = DEFAULT_METRIC;
+       }
+       if ((value = get_module_opt (cfg, "spf", "symbol_fail")) != NULL) {
+               spf_module_ctx->symbol_fail = memory_pool_strdup (spf_module_ctx->spf_pool, value);
+               g_free (value);
+       }
+       else {
+               spf_module_ctx->symbol_fail = DEFAULT_SYMBOL_FAIL;
+       }
+       if ((value = get_module_opt (cfg, "spf", "symbol_softfail")) != NULL) {
+               spf_module_ctx->symbol_softfail = memory_pool_strdup (spf_module_ctx->spf_pool, value);
+               g_free (value);
+       }
+       else {
+               spf_module_ctx->symbol_softfail = DEFAULT_SYMBOL_SOFTFAIL;
+       }
+       if ((value = get_module_opt (cfg, "spf", "symbol_allow")) != NULL) {
+               spf_module_ctx->symbol_allow = memory_pool_strdup (spf_module_ctx->spf_pool, value);
+               g_free (value);
+       }
+       else {
+               spf_module_ctx->symbol_allow = DEFAULT_SYMBOL_ALLOW;
+       }
+
+       metric = g_hash_table_lookup (cfg->metrics, spf_module_ctx->metric);
+       if (metric == NULL) {
+               msg_err ("spf_module_config: cannot find metric definition %s", spf_module_ctx->metric);
+               return FALSE;
+       }
+
+       /* Search in factors hash table */
+       w = g_hash_table_lookup (cfg->factors, spf_module_ctx->symbol_fail);
+       if (w == NULL) {
+               register_symbol (&metric->cache, spf_module_ctx->symbol_fail, 1, spf_symbol_callback, NULL);
+       }
+       else {
+               register_symbol (&metric->cache, spf_module_ctx->symbol_fail, *w, spf_symbol_callback, NULL);
+       }
+
+       return res;
+}
+
+int
+spf_module_reconfig (struct config_file *cfg)
+{
+       memory_pool_delete (spf_module_ctx->spf_pool);
+       spf_module_ctx->spf_pool = memory_pool_new (memory_pool_get_size ());
+
+       return spf_module_config (cfg);
+}
+
+static void 
+spf_plugin_callback (struct spf_record *record, struct worker_task *task)
+{
+       if (task->save.saved == 0) {
+               /* Call other filters */
+               task->save.saved = 1;
+               process_filters (task);
+       }
+}
+
+
+static void 
+spf_symbol_callback (struct worker_task *task, void *unused)
+{
+       if (!resolve_spf (task, spf_plugin_callback)) {
+               msg_info ("spf_symbol_callback: cannot make spf request for [%s]", task->message_id);
+       }
+}
index 97199009e07728341a7042cc246616f663d3ce9a..b175d949865770a138fca14403d351e383949724 100644 (file)
--- a/src/spf.c
+++ b/src/spf.c
@@ -29,7 +29,7 @@
 #include "message.h"
 #include "filter.h"
 
-#define SPF_VER_STR "spf=v1"
+#define SPF_VER_STR "v=spf1"
 #define SPF_ALL "all"
 #define SPF_A "a"
 #define SPF_IP4 "ip4"
@@ -79,7 +79,9 @@ do {                                                                                                          \
                return FALSE;                                                                           \
        }                                                                                                               \
 } while (0)                                                                                                    \
-       
+
+static gboolean parse_spf_record (struct worker_task *task, struct spf_record *rec);
+static void start_spf_parse (struct spf_record *rec, char *begin);
 
 /* Determine spf mech */
 static spf_mech_t
@@ -184,17 +186,64 @@ parse_spf_ipmask (const char *begin, struct spf_addr *addr)
 
 }
 
+static char *
+parse_spf_hostmask (struct worker_task *task, const char *begin, struct spf_addr *addr, struct spf_record *rec)
+{
+       char *host = NULL, *p,  mask_buf[3];
+       int hostlen;
+
+       bzero (mask_buf, sizeof (mask_buf));
+       if (*begin == '\0' || *begin == '/') {
+               /* Assume host as host to resolve from record */
+               host = rec->cur_domain;
+       }
+       p = strchr (begin, '/');
+       if (p != NULL) {
+               /* Extract mask */
+               g_strlcpy (mask_buf, p + 1, sizeof (mask_buf));
+               addr->mask = mask_buf[0] * 10 + mask_buf[1];
+               if (addr->mask > 32) {
+                       return FALSE;
+               }
+               if (host == NULL) {
+                       hostlen = p - begin;
+                       host = memory_pool_alloc (task->task_pool, hostlen);
+                       g_strlcpy (host, begin, hostlen);
+               }
+       }
+       else {
+               addr->mask = 32;
+               if (host == NULL) {
+                       g_strlcpy (host, begin, strlen (begin));
+               }
+       }
+
+       return host;
+}
+
 static void
 spf_record_dns_callback (int result, char type, int count, int ttl, void *addresses, void *data)
 {
        struct spf_dns_cb *cb = data;
        char *begin;
+       struct evdns_mx *mx;
 
        if (result == DNS_ERR_NONE) {
                if (addresses != NULL) {
                        /* Add all logic for all DNS states here */
                        switch (cb->cur_action) {
                                case SPF_RESOLVE_MX:
+                                       if (type == DNS_MX) {
+                                               mx = (struct evdns_mx *)addresses;
+                                               /* Now resolve A record for this MX */
+                                               if (evdns_resolve_ipv4 (mx->host, DNS_QUERY_NO_SEARCH, spf_record_dns_callback, (void *)cb) == 0) {
+                                                       return;
+                                               }
+                                       }
+                                       else if (type == DNS_IPv4_A) {
+                                               /* XXX: process only one record */
+                                               cb->addr->addr = *((uint32_t *)addresses);
+                                       }
                                        break;
                                case SPF_RESOLVE_A:
                                        if (type == DNS_IPv4_A) {
@@ -205,11 +254,29 @@ spf_record_dns_callback (int result, char type, int count, int ttl, void *addres
                                case SPF_RESOLVE_PTR:
                                        break;
                                case SPF_RESOLVE_REDIRECT:
+                                       if (type == DNS_TXT) {
+                                               if (addresses != NULL) {
+                                                       begin = *(char **)addresses;
+                                                       start_spf_parse (cb->rec, begin);
+                                               }
+                                       }
                                        break;
                                case SPF_RESOLVE_INCLUDE:
+                                       if (type == DNS_TXT) {
+                                               if (addresses != NULL) {
+                                                       begin = *(char **)addresses;
+                                                       start_spf_parse (cb->rec, begin);
+                                               }
+                                       }
                                        break;
                                case SPF_RESOLVE_EXP:
                                        break;
+                               case SPF_RESOLVE_EXISTS:
+                                       if (type == DNS_IPv4_A) {
+                                               /* If specified address resolves, we can accept connection from every IP */
+                                               cb->addr->addr = INADDR_ANY;
+                                       }
+                                       break;
                        }
                }
        }
@@ -226,8 +293,7 @@ static gboolean
 parse_spf_a (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr)
 {
        struct spf_dns_cb *cb;
-       char *host, *p,  mask_buf[3];
-       int hostlen;
+       char *host;
        
        CHECK_REC (rec);
        
@@ -235,25 +301,13 @@ parse_spf_a (struct worker_task *task, const char *begin, struct spf_record *rec
                return FALSE;
        }
        begin ++;
-
-       bzero (mask_buf, sizeof (mask_buf));
-       p = strchr (begin, '/');
-       if (p != NULL) {
-               /* Extract mask */
-               g_strlcpy (mask_buf, p + 1, sizeof (mask_buf));
-               addr->mask = mask_buf[0] * 10 + mask_buf[1];
-               if (addr->mask > 32) {
-                       return FALSE;
-               }
-               hostlen = p - begin;
-               host = memory_pool_alloc (task->task_pool, hostlen);
-               g_strlcpy (host, begin, hostlen);
-       }
-       else {
-               addr->mask = 32;
-               g_strlcpy (host, begin, strlen (begin));
-       }
        
+       host = parse_spf_hostmask (task, begin, addr, rec);
+       
+       if (!host) {
+               return FALSE;
+       }
+
        rec->dns_requests ++;
        cb = memory_pool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
        cb->rec = rec;
@@ -274,19 +328,47 @@ parse_spf_a (struct worker_task *task, const char *begin, struct spf_record *rec
 static gboolean
 parse_spf_ptr (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr)
 {
-       struct spf_dns_cb *cb;
-
        CHECK_REC (rec);
-
+       
+       msg_info ("parse_spf_ptr: ptr parsing is unimplemented");
+       return FALSE;
 }
 
 static gboolean
 parse_spf_mx (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr)
 {
        struct spf_dns_cb *cb;
-
+       char *host;
+       
        CHECK_REC (rec);
+       
+       if (begin == NULL) {
+               return FALSE;
+       }
+       if (*begin == ':') {
+               begin ++;
+       }
+       
+       host = parse_spf_hostmask (task, begin, addr, rec);
+       
+       if (!host) {
+               return FALSE;
+       }
+
+       rec->dns_requests ++;
+       cb = memory_pool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
+       cb->rec = rec;
+       cb->addr = addr;
+       cb->cur_action = SPF_RESOLVE_MX;
+
+       if (evdns_resolve_mx (host, DNS_QUERY_NO_SEARCH, spf_record_dns_callback, (void *)cb) == 0) {
+               task->save.saved++;
+               register_async_event (task->s, (event_finalizer_t) spf_record_dns_callback, NULL, TRUE);
+               
+               return TRUE;
+       }
 
+       return FALSE;
 }
 
 static gboolean
@@ -295,6 +377,8 @@ parse_spf_all (struct worker_task *task, const char *begin, struct spf_record *r
        /* All is 0/0 */
        addr->addr = 0;
        addr->mask = 0;
+
+       return TRUE;
 }
 
 static gboolean
@@ -310,33 +394,99 @@ static gboolean
 parse_spf_include (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr)
 {
        struct spf_dns_cb *cb;
+       char              *domain;
 
        CHECK_REC (rec);
 
+       if (begin == NULL || *begin != ':') {
+               return FALSE;
+       }
+       begin ++;
+       rec->dns_requests ++;
+
+       cb = memory_pool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
+       cb->rec = rec;
+       cb->addr = addr;
+       cb->cur_action = SPF_RESOLVE_REDIRECT;
+       domain = memory_pool_strdup (task->task_pool, begin);
+
+       if (evdns_resolve_txt (domain, DNS_QUERY_NO_SEARCH, spf_record_dns_callback, (void *)cb) == 0) {
+               task->save.saved++;
+               register_async_event (task->s, (event_finalizer_t) spf_record_dns_callback, NULL, TRUE);
+               
+               return TRUE;
+       }
+
+       return FALSE;
 }
 
 static gboolean
 parse_spf_exp (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr)
 {
-       struct spf_dns_cb *cb;
-
        CHECK_REC (rec);
+
+       msg_info ("parse_spf_exp: exp record is ignored");
+       return TRUE;
 }
 
 static gboolean
 parse_spf_redirect (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr)
 {
        struct spf_dns_cb *cb;
+       char              *domain;
 
        CHECK_REC (rec);
+
+       if (begin == NULL || *begin != ':') {
+               return FALSE;
+       }
+       begin ++;
+       rec->dns_requests ++;
+
+       cb = memory_pool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
+       cb->rec = rec;
+       cb->addr = addr;
+       cb->cur_action = SPF_RESOLVE_INCLUDE;
+       domain = memory_pool_strdup (task->task_pool, begin);
+
+       if (evdns_resolve_txt (domain, DNS_QUERY_NO_SEARCH, spf_record_dns_callback, (void *)cb) == 0) {
+               task->save.saved++;
+               register_async_event (task->s, (event_finalizer_t) spf_record_dns_callback, NULL, TRUE);
+               
+               return TRUE;
+       }
+
+       return FALSE;
 }
 
 static gboolean
 parse_spf_exists (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr)
 {
        struct spf_dns_cb *cb;
+       char              *host;
 
        CHECK_REC (rec);
+       
+       if (begin == NULL || *begin != ':') {
+               return FALSE;
+       }
+       begin ++;
+       rec->dns_requests ++;
+
+       cb = memory_pool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
+       cb->rec = rec;
+       cb->addr = addr;
+       cb->cur_action = SPF_RESOLVE_EXISTS;
+       host = memory_pool_strdup (task->task_pool, begin);
+
+       if (evdns_resolve_ipv4 (host, DNS_QUERY_NO_SEARCH, spf_record_dns_callback, (void *)cb) == 0) {
+               task->save.saved++;
+               register_async_event (task->s, (event_finalizer_t) spf_record_dns_callback, NULL, TRUE);
+               
+               return TRUE;
+       }
+
+       return FALSE;
 }
 
 /* Read current element and try to parse record */
@@ -346,20 +496,21 @@ parse_spf_record (struct worker_task *task, struct spf_record *rec)
        struct spf_addr *new;
        gboolean need_shift, res = FALSE;
        char *begin;
-
-       if (*rec->cur_elt == NULL) {
+       
+       rec->cur_elt = rec->elts[rec->elt_num];
+       if (rec->cur_elt == NULL) {
                return TRUE;
        }
        else {
                /* Check spf mech */
                new = memory_pool_alloc (task->task_pool, sizeof (struct spf_addr));
-               new->mech = check_spf_mech (*rec->cur_elt, &need_shift);
+               new->mech = check_spf_mech (rec->cur_elt, &need_shift);
                if (need_shift) {
-                       begin = (*rec->cur_elt)++;
-               }
-               else {
-                       begin = *rec->cur_elt;
+                       rec->cur_elt ++;
                }
+               begin = rec->cur_elt; 
+
+
                /* Now check what we have */
                switch (*begin) {
                        case 'a':
@@ -373,7 +524,7 @@ parse_spf_record (struct worker_task *task, struct spf_record *rec)
                                        res = parse_spf_all (task, begin, rec, new);
                                }
                                else {
-                                       msg_info ("parse_spf_record: bad spf command");
+                                       msg_info ("parse_spf_record: bad spf command: %s", begin);
                                }
                                break;
                        case 'i':
@@ -387,7 +538,7 @@ parse_spf_record (struct worker_task *task, struct spf_record *rec)
                                        res = parse_spf_include (task, begin, rec, new);
                                }
                                else {
-                                       msg_info ("parse_spf_record: bad spf command");
+                                       msg_info ("parse_spf_record: bad spf command: %s", begin);
                                }
                                break;
                        case 'm':
@@ -397,7 +548,7 @@ parse_spf_record (struct worker_task *task, struct spf_record *rec)
                                        res = parse_spf_mx (task, begin, rec, new);
                                }
                                else {
-                                       msg_info ("parse_spf_record: bad spf command");
+                                       msg_info ("parse_spf_record: bad spf command: %s", begin);
                                }
                                break;
                        case 'p':
@@ -407,7 +558,7 @@ parse_spf_record (struct worker_task *task, struct spf_record *rec)
                                        res = parse_spf_ptr (task, begin, rec, new);
                                }
                                else {
-                                       msg_info ("parse_spf_record: bad spf command");
+                                       msg_info ("parse_spf_record: bad spf command: %s", begin);
                                }
                                break;
                        case 'e':
@@ -421,7 +572,7 @@ parse_spf_record (struct worker_task *task, struct spf_record *rec)
                                        res = parse_spf_exists (task, begin, rec, new);
                                }
                                else {
-                                       msg_info ("parse_spf_record: bad spf command");
+                                       msg_info ("parse_spf_record: bad spf command: %s", begin);
                                }
                                break;
                        case 'r':
@@ -431,18 +582,41 @@ parse_spf_record (struct worker_task *task, struct spf_record *rec)
                                        res = parse_spf_redirect (task, begin, rec, new);
                                }
                                else {
-                                       msg_info ("parse_spf_record: bad spf command");
+                                       msg_info ("parse_spf_record: bad spf command: %s", begin);
                                }
                                break;
                        default:
-                               msg_info ("parse_spf_record: bad spf command");
+                               msg_info ("parse_spf_record: bad spf command: %s", begin);
                                break;
                }
                if (res) {
                        rec->addrs = g_list_prepend (rec->addrs, new);
-                       rec->cur_elt ++;
+                       rec->elt_num ++;
+               }
+       }
+
+       return res;
+}
+
+static void
+start_spf_parse (struct spf_record *rec, char *begin)
+{
+       if (strncmp (begin, SPF_VER_STR, sizeof (SPF_VER_STR) - 1) == 0) {
+               begin += sizeof (SPF_VER_STR) - 1;
+               while (g_ascii_isspace (*begin) && *begin) {
+                       begin ++;
+               }
+               rec->elts = g_strsplit (begin, " ", 0);
+               rec->elt_num = 0;
+               if (rec->elts) {
+                       memory_pool_add_destructor (rec->task->task_pool, (pool_destruct_func)g_strfreev, rec->elts);
+                       rec->cur_elt = rec->elts[0];
+                       while (parse_spf_record (rec->task, rec));
                }
        }
+       else {
+               msg_info ("start_spf_parse: bad spf record version: %*s", sizeof (SPF_VER_STR) - 1, begin);
+       }
 }
 
 static void
@@ -453,19 +627,8 @@ spf_dns_callback (int result, char type, int count, int ttl, void *addresses, vo
 
        if (result == DNS_ERR_NONE && type == DNS_TXT) {
                if (addresses != NULL) {
-                       begin = (char *)addresses;
-                       if (strncmp (begin, SPF_VER_STR, sizeof (SPF_VER_STR) - 1) == 0) {
-                               begin += sizeof (SPF_VER_STR) - 1;
-                               while (g_ascii_isspace (*begin) && *begin) {
-                                       begin ++;
-                               }
-                       }
-                       rec->elts = g_strsplit (begin, " ", 0);
-                       if (rec->elts) {
-                               memory_pool_add_destructor (rec->task->task_pool, (pool_destruct_func)g_strfreev, rec->elts);
-                               rec->cur_elt = rec->elts;
-                               while (!parse_spf_record (rec->task, rec));
-                       }
+                       begin = *(char **)addresses;
+                       start_spf_parse (rec, begin);
                }
        }
 
@@ -489,9 +652,8 @@ resolve_spf (struct worker_task *task, spf_cb_t callback)
        rec->task = task;
        rec->callback = callback;
 
-       domain = strchr (task->from, '@');
-       if (domain != NULL) {
-               rec->cur_domain = memory_pool_strdup (task->task_pool, domain);
+       if (task->from && (domain = strchr (task->from, '@'))) {
+               rec->cur_domain = memory_pool_strdup (task->task_pool, domain + 1);
                if ((domain = strchr (rec->cur_domain, '>')) != NULL) {
                        *domain = '\0';
                }
@@ -509,6 +671,11 @@ resolve_spf (struct worker_task *task, spf_cb_t callback)
                if (domains != NULL) {
                        rec->cur_domain = memory_pool_strdup (task->task_pool, domains->data);
                        g_list_free (domains);
+                       if ((domain = strchr (rec->cur_domain, '@')) == NULL) {
+                               return FALSE;
+                       }
+                       rec->cur_domain = domain + 1;
+
                        if ((domain = strchr (rec->cur_domain, '>')) != NULL) {
                                *domain = '\0';
                        }
index b3198095865b3273b2abbaca3a44649074d6dbb0..9c0f872b7f5255842b5239376747287053295b88 100644 (file)
--- a/src/spf.h
+++ b/src/spf.h
@@ -21,6 +21,7 @@ typedef enum spf_action_e {
        SPF_RESOLVE_PTR,
        SPF_RESOLVE_REDIRECT,
        SPF_RESOLVE_INCLUDE,
+       SPF_RESOLVE_EXISTS,
        SPF_RESOLVE_EXP
 } spf_action_t;
 
@@ -33,7 +34,8 @@ struct spf_addr {
 struct spf_record {
        char **elts;
 
-       char **cur_elt;
+       char *cur_elt;
+       int elt_num;
        int nested;
        int dns_requests;