From 4639b204c25a13638bee42eceb4004e6ba0c67ec Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Fri, 4 Dec 2009 19:46:01 +0300 Subject: [PATCH] * Updates to spf system: - 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 | 3 +- src/evdns/evdns.c | 39 +++++++ src/evdns/evdns.h | 16 +++ src/map.c | 15 ++- src/plugins/spf.c | 152 +++++++++++++++++++++++++ src/spf.c | 285 ++++++++++++++++++++++++++++++++++++---------- src/spf.h | 4 +- 7 files changed, 451 insertions(+), 63 deletions(-) create mode 100644 src/plugins/spf.c 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; -- 2.39.5