aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--src/evdns/evdns.c39
-rw-r--r--src/evdns/evdns.h16
-rw-r--r--src/map.c15
-rw-r--r--src/plugins/spf.c152
-rw-r--r--src/spf.c285
-rw-r--r--src/spf.h4
7 files changed, 451 insertions, 63 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cca84015a..5bada252c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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
diff --git a/src/evdns/evdns.c b/src/evdns/evdns.c
index 6db223790..4a8a7b650 100644
--- a/src/evdns/evdns.c
+++ b/src/evdns/evdns.c
@@ -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 */
diff --git a/src/evdns/evdns.h b/src/evdns/evdns.h
index 5d6df1872..ded48f2ac 100644
--- a/src/evdns/evdns.h
+++ b/src/evdns/evdns.h
@@ -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.
diff --git a/src/map.c b/src/map.c
index 6a96728e3..31a0569d9 100644
--- 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
index 000000000..1823f3407
--- /dev/null
+++ b/src/plugins/spf.c
@@ -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);
+ }
+}
diff --git a/src/spf.c b/src/spf.c
index 97199009e..b175d9498 100644
--- 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';
}
diff --git a/src/spf.h b/src/spf.h
index b31980958..9c0f872b7 100644
--- 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;