diff options
author | Vsevolod Stakhov <vsevolod@rambler-co.ru> | 2009-10-07 07:54:02 +0400 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@rambler-co.ru> | 2009-10-07 07:54:02 +0400 |
commit | a7ea14b733201d1c9e8e38f716d315c489d9484d (patch) | |
tree | 71842896de00197d3ad8288fcadd628e134a1440 /src/spf.c | |
parent | 876e5f104227f855269daa0c2812f26a45b38cc7 (diff) | |
download | rspamd-a7ea14b733201d1c9e8e38f716d315c489d9484d.tar.gz rspamd-a7ea14b733201d1c9e8e38f716d315c489d9484d.zip |
* Port evdns with TXT patch into rspamd and adopt it to work separately from libevent
* Add skeleton for spf parser
Diffstat (limited to 'src/spf.c')
-rw-r--r-- | src/spf.c | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/src/spf.c b/src/spf.c new file mode 100644 index 000000000..857bcda99 --- /dev/null +++ b/src/spf.c @@ -0,0 +1,338 @@ +/* + * 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. + */ + +#include "config.h" +#include "evdns/evdns.h" +#include "spf.h" +#include "main.h" +#include "message.h" +#include "filter.h" + +#define SPF_VER_STR "spf=v1" +#define SPF_ALL "all" +#define SPF_A "a" +#define SPF_IP4 "ip4" +#define SPF_IP6 "ip6" +#define SPF_PTR "ptr" +#define SPF_MX "mx" +#define SPF_EXISTS "exists" +#define SPF_INCLUDE "include" +#define SPF_REDIRECT "redirect" +#define SPF_EXP "exp" + +/** + * State machine for SPF record: + * + * spf_mech ::= +|-|~|? + * + * spf_body ::= spf=v1 <spf_command> [<spf_command>] + * spf_command ::= [spf_mech]all|a|<ip4>|<ip6>|ptr|mx|<exists>|<include>|<redirect> + * + * spf_domain ::= [:domain][/mask] + * spf_ip4 ::= ip[/mask] + * ip4 ::= ip4:<spf_ip4> + * mx ::= mx<spf_domain> + * a ::= a<spf_domain> + * ptr ::= ptr[:domain] + * exists ::= exists:domain + * include ::= include:domain + * redirect ::= redirect:domain + * exp ::= exp:domain + * + */ + +struct spf_dns_cb { + struct spf_record *rec; + struct spf_addr *addr; +}; + +/* Determine spf mech */ +static spf_mech_t +check_spf_mech (const char *elt, gboolean *need_shift) +{ + g_assert (elt != NULL); + + *need_shift = TRUE; + + switch (*elt) { + case '-': + return SPF_FAIL; + case '~': + return SPF_SOFT_FAIL; + case '+': + return SPF_PASS; + case '?': + return SPF_NEUTRAL; + default: + *need_shift = FALSE; + return SPF_PASS; + } +} + +static void +parse_spf_a (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr) +{ + struct spf_dns_cb *cb; +} + +static void +parse_spf_ptr (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr) +{ + struct spf_dns_cb *cb; + +} + +static void +parse_spf_mx (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr) +{ + struct spf_dns_cb *cb; + +} + +static void +parse_spf_all (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr) +{ + +} + +static void +parse_spf_ip4 (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr) +{ + +} + +static void +parse_spf_include (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr) +{ + struct spf_dns_cb *cb; + +} + +static void +parse_spf_exp (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr) +{ + struct spf_dns_cb *cb; + +} + +static void +parse_spf_redirect (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr) +{ + struct spf_dns_cb *cb; + +} + +static void +parse_spf_exists (struct worker_task *task, const char *begin, struct spf_record *rec, struct spf_addr *addr) +{ + struct spf_dns_cb *cb; + +} + +/* Read current element and try to parse record */ +static gboolean +parse_spf_record (struct worker_task *task, struct spf_record *rec) +{ + struct spf_addr *new; + gboolean need_shift; + char *begin; + + 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); + if (need_shift) { + begin = (*rec->cur_elt)++; + } + else { + begin = *rec->cur_elt; + } + /* Now check what we have */ + switch (*begin) { + case 'a': + /* all or a */ + if (strncmp (begin, SPF_A, sizeof (SPF_A) - 1) == 0) { + begin += sizeof (SPF_A) - 1; + parse_spf_a (task, begin, rec, new); + } + else if (strncmp (begin, SPF_ALL, sizeof (SPF_ALL) - 1) == 0) { + begin += sizeof (SPF_ALL) - 1; + parse_spf_all (task, begin, rec, new); + } + else { + msg_info ("parse_spf_record: bad spf command"); + } + break; + case 'i': + /* include or ip4 */ + if (strncmp (begin, SPF_IP4, sizeof (SPF_IP4) - 1) == 0) { + begin += sizeof (SPF_IP4) - 1; + parse_spf_ip4 (task, begin, rec, new); + } + else if (strncmp (begin, SPF_INCLUDE, sizeof (SPF_INCLUDE) - 1) == 0) { + begin += sizeof (SPF_INCLUDE) - 1; + parse_spf_include (task, begin, rec, new); + } + else { + msg_info ("parse_spf_record: bad spf command"); + } + break; + case 'm': + /* mx */ + if (strncmp (begin, SPF_MX, sizeof (SPF_MX) - 1) == 0) { + begin += sizeof (SPF_MX) - 1; + parse_spf_mx (task, begin, rec, new); + } + else { + msg_info ("parse_spf_record: bad spf command"); + } + break; + case 'p': + /* ptr */ + if (strncmp (begin, SPF_PTR, sizeof (SPF_PTR) - 1) == 0) { + begin += sizeof (SPF_PTR) - 1; + parse_spf_ptr (task, begin, rec, new); + } + else { + msg_info ("parse_spf_record: bad spf command"); + } + break; + case 'e': + /* exp or exists */ + if (strncmp (begin, SPF_EXP, sizeof (SPF_EXP) - 1) == 0) { + begin += sizeof (SPF_EXP) - 1; + parse_spf_exp (task, begin, rec, new); + } + else if (strncmp (begin, SPF_EXISTS, sizeof (SPF_EXISTS) - 1) == 0) { + begin += sizeof (SPF_EXISTS) - 1; + parse_spf_exists (task, begin, rec, new); + } + else { + msg_info ("parse_spf_record: bad spf command"); + } + break; + case 'r': + /* redirect */ + if (strncmp (begin, SPF_REDIRECT, sizeof (SPF_REDIRECT) - 1) == 0) { + begin += sizeof (SPF_REDIRECT) - 1; + parse_spf_redirect (task, begin, rec, new); + } + else { + msg_info ("parse_spf_record: bad spf command"); + } + break; + default: + msg_info ("parse_spf_record: bad spf command"); + break; + } + rec->addrs = g_list_prepend (rec->addrs, new); + rec->cur_elt ++; + } +} + +static void +spf_dns_callback (int result, char type, int count, int ttl, void *addresses, void *data) +{ + struct spf_record *rec = data; + char *begin; + + 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)); + } + } + } + + rec->task->save.saved--; + if (rec->task->save.saved == 0 && rec->callback) { + rec->callback (rec, rec->task); + } + remove_forced_event (rec->task->s, (event_finalizer_t) spf_dns_callback); + +} + + +gboolean +resolve_spf (struct worker_task *task, spf_cb_t callback) +{ + struct spf_record *rec; + char *domain; + GList *domains; + + rec = memory_pool_alloc0 (task->task_pool, sizeof (struct spf_record)); + rec->task = task; + rec->callback = callback; + + domain = strchr (task->from, '@'); + if (domain != NULL) { + rec->cur_domain = memory_pool_strdup (task->task_pool, domain); + if ((domain = strchr (rec->cur_domain, '>')) != NULL) { + *domain = '\0'; + } + + if (evdns_resolve_txt (rec->cur_domain, DNS_QUERY_NO_SEARCH, spf_dns_callback, (void *)rec) == 0) { + task->save.saved++; + register_async_event (task->s, (event_finalizer_t) spf_dns_callback, NULL, TRUE); + + return TRUE; + } + } + else { + domains = message_get_header (task->task_pool, task->message, "From"); + + 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) { + *domain = '\0'; + } + if (evdns_resolve_txt (rec->cur_domain, DNS_QUERY_NO_SEARCH, spf_dns_callback, (void *)rec) == 0) { + task->save.saved++; + register_async_event (task->s, (event_finalizer_t) spf_dns_callback, NULL, TRUE); + + return TRUE; + } + } + } + + return FALSE; +} + +/* + * vi:ts=4 + */ |