aboutsummaryrefslogtreecommitdiffstats
path: root/src/spf.c
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2014-04-21 16:25:51 +0100
committerVsevolod Stakhov <vsevolod@highsecure.ru>2014-04-21 16:25:51 +0100
commit61555065f3d1c8badcc9573691232f1b6e42988c (patch)
tree563d5b7cb8c468530f7e79c4da0a75267b1184e1 /src/spf.c
parentad5bf825b7f33bc10311673991f0cc888e69c0b1 (diff)
downloadrspamd-61555065f3d1c8badcc9573691232f1b6e42988c.tar.gz
rspamd-61555065f3d1c8badcc9573691232f1b6e42988c.zip
Rework project structure, remove trash files.
Diffstat (limited to 'src/spf.c')
-rw-r--r--src/spf.c1465
1 files changed, 0 insertions, 1465 deletions
diff --git a/src/spf.c b/src/spf.c
deleted file mode 100644
index 12f1513d4..000000000
--- a/src/spf.c
+++ /dev/null
@@ -1,1465 +0,0 @@
-/*
- * Copyright (c) 2009-2012, Vsevolod Stakhov
- * 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 AUTHOR ''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 AUTHOR 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 "dns.h"
-#include "spf.h"
-#include "main.h"
-#include "message.h"
-#include "filter.h"
-
-#define SPF_VER1_STR "v=spf1"
-#define SPF_VER2_STR "spf2."
-#define SPF_SCOPE_PRA "pra"
-#define SPF_SCOPE_MFROM "mfrom"
-#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"
-
-/** SPF limits for avoiding abuse **/
-#define SPF_MAX_NESTING 10
-#define SPF_MAX_DNS_REQUESTS 30
-
-/**
- * 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
- *
- */
-
-#undef SPF_DEBUG
-
-struct spf_dns_cb {
- struct spf_record *rec;
- struct spf_addr *addr;
- spf_action_t cur_action;
- gboolean in_include;
-};
-
-#define CHECK_REC(rec) \
-do { \
- if ((rec)->nested > SPF_MAX_NESTING || \
- (rec)->dns_requests > SPF_MAX_DNS_REQUESTS) { \
- msg_info ("<%s> spf recursion limit %d is reached, domain: %s", \
- (rec)->task->message_id, (rec)->dns_requests, \
- (rec)->sender_domain); \
- return FALSE; \
- } \
-} while (0) \
-
-static gboolean parse_spf_record (struct rspamd_task *task, struct spf_record *rec);
-static void start_spf_parse (struct spf_record *rec, gchar *begin, guint ttl);
-
-/* Determine spf mech */
-static spf_mech_t
-check_spf_mech (const gchar *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;
- }
-}
-
-/* Debugging function that dumps spf record in log */
-static void
-dump_spf_record (GList *addrs)
-{
- struct spf_addr *addr;
- GList *cur;
- gint r = 0;
- gchar logbuf[BUFSIZ], c;
-#ifdef HAVE_INET_PTON
- gchar ipbuf[INET6_ADDRSTRLEN];
-#else
- struct in_addr ina;
-#endif
-
- cur = addrs;
-
- while (cur) {
- addr = cur->data;
- if (!addr->is_list) {
- switch (addr->mech) {
- case SPF_FAIL:
- c = '-';
- break;
- case SPF_SOFT_FAIL:
- case SPF_NEUTRAL:
- c = '~';
- break;
- case SPF_PASS:
- c = '+';
- break;
- }
-#ifdef HAVE_INET_PTON
- if (addr->data.normal.ipv6) {
- inet_ntop (AF_INET6, &addr->data.normal.d.in6, ipbuf, sizeof (ipbuf));
-
- }
- else {
- inet_ntop (AF_INET, &addr->data.normal.d.in4, ipbuf, sizeof (ipbuf));
- }
- r += snprintf (logbuf + r, sizeof (logbuf) - r, "%c%s/%d; ", c, ipbuf, addr->data.normal.mask);
-#else
- ina.s_addr = addr->data.normal.d.in4.s_addr;
- r += snprintf (logbuf + r, sizeof (logbuf) - r, "%c%s/%d; ", c, inet_ntoa (ina), addr->data.normal.mask);
-#endif
- }
- else {
- r += snprintf (logbuf + r, sizeof (logbuf) - r, "%s; ", addr->spf_string);
- dump_spf_record (addr->data.list);
- }
- cur = g_list_next (cur);
- }
- msg_info ("spf record: %s", logbuf);
-}
-
-/* Find position of address inside addrs list */
-static GList *
-spf_addr_find (GList *addrs, gpointer to_find)
-{
- struct spf_addr *addr;
- GList *cur, *res = NULL;
-
- cur = addrs;
- while (cur) {
- addr = cur->data;
- if (addr->is_list) {
- if ((res = spf_addr_find (addr->data.list, to_find)) != NULL) {
- return cur;
- }
- }
- else {
- if (cur->data == to_find) {
- return cur;
- }
- }
- cur = g_list_next (cur);
- }
-
- return res;
-}
-
-/*
- * Destructor for spf record
- */
-static void
-spf_record_destructor (gpointer r)
-{
- struct spf_record *rec = r;
- GList *cur;
- struct spf_addr *addr;
-
- if (rec->addrs) {
- cur = rec->addrs;
- while (cur) {
- addr = cur->data;
- if (addr->is_list && addr->data.list != NULL) {
- g_list_free (addr->data.list);
- }
- cur = g_list_next (cur);
- }
- g_list_free (rec->addrs);
- }
-}
-
-static gboolean
-parse_spf_ipmask (const gchar *begin, struct spf_addr *addr, struct spf_record *rec)
-{
- const gchar *pos;
- gchar mask_buf[5] = {'\0'}, *p;
- gint state = 0, dots = 0;
-#ifdef HAVE_INET_PTON
- gchar ip_buf[INET6_ADDRSTRLEN];
-#else
- gchar ip_buf[INET_ADDRSTRLEN];
-#endif
-
- bzero (ip_buf, sizeof (ip_buf));
- bzero (mask_buf, sizeof (mask_buf));
- pos = begin;
- p = ip_buf;
-
- while (*pos) {
- switch (state) {
- case 0:
- /* Require ':' */
- if (*pos != ':') {
- msg_info ("<%s>: spf error for domain %s: semicolon missing",
- rec->task->message_id, rec->sender_domain);
- return FALSE;
- }
- state = 1;
- pos ++;
- p = ip_buf;
- dots = 0;
- break;
- case 1:
-#ifdef HAVE_INET_PTON
- if (p - ip_buf >= (gint)sizeof (ip_buf)) {
- return FALSE;
- }
- if (g_ascii_isxdigit (*pos)) {
- *p ++ = *pos ++;
- }
- else if (*pos == '.' || *pos == ':') {
- *p ++ = *pos ++;
- dots ++;
- }
-#else
- /* Begin parse ip */
- if (p - ip_buf >= (gint)sizeof (ip_buf) || dots > 3) {
- return FALSE;
- }
- if (g_ascii_isdigit (*pos)) {
- *p ++ = *pos ++;
- }
- else if (*pos == '.') {
- *p ++ = *pos ++;
- dots ++;
- }
-#endif
- else if (*pos == '/') {
- pos ++;
- p = mask_buf;
- state = 2;
- }
- else {
- /* Invalid character */
- msg_info ("<%s>: spf error for domain %s: invalid ip address",
- rec->task->message_id, rec->sender_domain);
- return FALSE;
- }
- break;
- case 2:
- /* Parse mask */
- if (p - mask_buf >= (gint)sizeof (mask_buf)) {
- msg_info ("<%s>: spf error for domain %s: too long mask",
- rec->task->message_id, rec->sender_domain);
- return FALSE;
- }
- if (g_ascii_isdigit (*pos)) {
- *p ++ = *pos ++;
- }
- else {
- return FALSE;
- }
- break;
- }
- }
-
-#ifdef HAVE_INET_PTON
- if (inet_pton (AF_INET, ip_buf, &addr->data.normal.d.in4) != 1) {
- if (inet_pton (AF_INET6, ip_buf, &addr->data.normal.d.in6) == 1) {
- addr->data.normal.ipv6 = TRUE;
- }
- else {
- msg_info ("<%s>: spf error for domain %s: invalid ip address",
- rec->task->message_id, rec->sender_domain);
- return FALSE;
- }
- }
- else {
- addr->data.normal.ipv6 = FALSE;
- }
-#else
- if (!inet_aton (ip_buf, &addr->data.normal.d.in4)) {
- return FALSE;
- }
-#endif
- if (state == 2) {
- /* Also parse mask */
- if (!addr->data.normal.ipv6) {
- addr->data.normal.mask = strtoul (mask_buf, NULL, 10);
- if (addr->data.normal.mask > 32) {
- msg_info ("<%s>: spf error for domain %s: bad ipmask value: '%s'",
- rec->task->message_id, rec->sender_domain, begin);
- return FALSE;
- }
- }
- else {
- addr->data.normal.mask = strtoul (mask_buf, NULL, 10);
- if (addr->data.normal.mask > 128) {
- msg_info ("<%s>: spf error for domain %s: bad ipmask value: '%s'",
- rec->task->message_id, rec->sender_domain, begin);
- return FALSE;
- }
- }
- }
- else {
- addr->data.normal.mask = addr->data.normal.ipv6 ? 128 : 32;
- }
- addr->data.normal.parsed = TRUE;
- return TRUE;
-
-}
-
-static gchar *
-parse_spf_hostmask (struct rspamd_task *task, const gchar *begin, struct spf_addr *addr, struct spf_record *rec)
-{
- gchar *host = NULL, *p, mask_buf[3];
- gint 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 */
- rspamd_strlcpy (mask_buf, p + 1, sizeof (mask_buf));
- addr->data.normal.mask = strtoul (mask_buf, NULL, 10);
- if (addr->data.normal.mask > 32) {
- msg_info ("<%s>: spf error for domain %s: too long mask",
- rec->task->message_id, rec->sender_domain);
- return FALSE;
- }
- if (host == NULL) {
- hostlen = p - begin;
- host = rspamd_mempool_alloc (task->task_pool, hostlen);
- rspamd_strlcpy (host, begin, hostlen);
- }
- }
- else {
- addr->data.normal.mask = 32;
- if (host == NULL) {
- host = rspamd_mempool_strdup (task->task_pool, begin);
- }
- }
-
- return host;
-}
-
-static void
-spf_record_dns_callback (struct rdns_reply *reply, gpointer arg)
-{
- struct spf_dns_cb *cb = arg;
- gchar *begin;
- struct rdns_reply_entry *elt_data;
- GList *tmp = NULL;
- struct rspamd_task *task;
- struct spf_addr *new_addr;
-
- task = cb->rec->task;
-
- cb->rec->requests_inflight --;
-
- if (reply->code == RDNS_RC_NOERROR) {
- /* Add all logic for all DNS states here */
- LL_FOREACH (reply->entries, elt_data) {
- switch (cb->cur_action) {
- case SPF_RESOLVE_MX:
- if (elt_data->type == RDNS_REQUEST_MX) {
- /* Now resolve A record for this MX */
- if (make_dns_request (task->resolver, task->s, task->task_pool,
- spf_record_dns_callback, (void *)cb, RDNS_REQUEST_A, elt_data->content.mx.name)) {
- task->dns_requests ++;
- cb->rec->requests_inflight ++;
- }
- }
- else if (elt_data->type == RDNS_REQUEST_A) {
- if (!cb->addr->data.normal.parsed) {
- cb->addr->data.normal.d.in4.s_addr = elt_data->content.a.addr.s_addr;
- cb->addr->data.normal.mask = 32;
- cb->addr->data.normal.parsed = TRUE;
- }
- else {
- /* Insert one more address */
- tmp = spf_addr_find (cb->rec->addrs, cb->addr);
- if (tmp) {
- new_addr = rspamd_mempool_alloc (task->task_pool, sizeof (struct spf_addr));
- memcpy (new_addr, cb->addr, sizeof (struct spf_addr));
- new_addr->data.normal.d.in4.s_addr = elt_data->content.a.addr.s_addr;
- new_addr->data.normal.parsed = TRUE;
- cb->rec->addrs = g_list_insert_before (cb->rec->addrs, tmp, new_addr);
- }
- else {
- msg_info ("<%s>: spf error for domain %s: addresses mismatch",
- task->message_id, cb->rec->sender_domain);
- }
- }
-
- }
-#ifdef HAVE_INET_PTON
- else if (elt_data->type == RDNS_REQUEST_AAAA) {
- if (!cb->addr->data.normal.parsed) {
- memcpy (&cb->addr->data.normal.d.in6, &elt_data->content.aaa.addr, sizeof (struct in6_addr));
- cb->addr->data.normal.mask = 32;
- cb->addr->data.normal.parsed = TRUE;
- cb->addr->data.normal.ipv6 = TRUE;
- }
- else {
- /* Insert one more address */
- tmp = spf_addr_find (cb->rec->addrs, cb->addr);
- if (tmp) {
- new_addr = rspamd_mempool_alloc (task->task_pool, sizeof (struct spf_addr));
- memcpy (new_addr, cb->addr, sizeof (struct spf_addr));
- memcpy (&new_addr->data.normal.d.in6, &elt_data->content.aaa.addr, sizeof (struct in6_addr));
- new_addr->data.normal.parsed = TRUE;
- new_addr->data.normal.ipv6 = TRUE;
- cb->rec->addrs = g_list_insert_before (cb->rec->addrs, tmp, new_addr);
- }
- else {
- msg_info ("<%s>: spf error for domain %s: addresses mismatch",
- task->message_id, cb->rec->sender_domain);
- }
- }
-
- }
-#endif
- break;
- case SPF_RESOLVE_A:
- if (elt_data->type == RDNS_REQUEST_A) {
- /* XXX: process only one record */
- cb->addr->data.normal.d.in4.s_addr = elt_data->content.a.addr.s_addr;
- cb->addr->data.normal.mask = 32;
- cb->addr->data.normal.parsed = TRUE;
- }
-#ifdef HAVE_INET_PTON
- else if (elt_data->type == RDNS_REQUEST_AAAA) {
- memcpy (&cb->addr->data.normal.d.in6, &elt_data->content.aaa.addr, sizeof (struct in6_addr));
- cb->addr->data.normal.mask = 32;
- cb->addr->data.normal.parsed = TRUE;
- cb->addr->data.normal.ipv6 = TRUE;
- }
-#endif
- break;
-#ifdef HAVE_INET_PTON
- case SPF_RESOLVE_AAA:
- if (elt_data->type == RDNS_REQUEST_A) {
- /* XXX: process only one record */
- cb->addr->data.normal.d.in4.s_addr = elt_data->content.a.addr.s_addr;
- cb->addr->data.normal.mask = 32;
- cb->addr->data.normal.parsed = TRUE;
- }
- else if (elt_data->type == RDNS_REQUEST_AAAA) {
- memcpy (&cb->addr->data.normal.d.in6, &elt_data->content.aaa.addr, sizeof (struct in6_addr));
- cb->addr->data.normal.mask = 32;
- cb->addr->data.normal.parsed = TRUE;
- cb->addr->data.normal.ipv6 = TRUE;
- }
-#endif
- break;
- case SPF_RESOLVE_PTR:
- break;
- case SPF_RESOLVE_REDIRECT:
- if (elt_data->type == RDNS_REQUEST_TXT) {
- begin = elt_data->content.txt.data;
-
- if (!cb->in_include && cb->rec->addrs) {
- g_list_free (cb->rec->addrs);
- cb->rec->addrs = NULL;
- }
- start_spf_parse (cb->rec, begin, elt_data->ttl);
-
- }
- break;
- case SPF_RESOLVE_INCLUDE:
- if (elt_data->type == RDNS_REQUEST_TXT) {
- begin = elt_data->content.txt.data;
-#ifdef SPF_DEBUG
- msg_info ("before include");
- dump_spf_record (cb->rec->addrs);
-#endif
- tmp = cb->rec->addrs;
- cb->rec->addrs = NULL;
- cb->rec->in_include = TRUE;
- start_spf_parse (cb->rec, begin, 0);
- cb->rec->in_include = FALSE;
-
-#ifdef SPF_DEBUG
- msg_info ("after include");
- dump_spf_record (cb->rec->addrs);
-#endif
- /* Insert new list */
- cb->addr->is_list = TRUE;
- cb->addr->data.list = cb->rec->addrs;
- cb->rec->addrs = tmp;
- }
- break;
- case SPF_RESOLVE_EXP:
- break;
- case SPF_RESOLVE_EXISTS:
- if (elt_data->type == RDNS_REQUEST_A) {
- /* If specified address resolves, we can accept connection from every IP */
- cb->addr->data.normal.d.in4.s_addr = INADDR_NONE;
- cb->addr->data.normal.mask = 0;
- }
- break;
- }
- }
- }
- else if (reply->code == RDNS_RC_NXDOMAIN) {
- switch (cb->cur_action) {
- case SPF_RESOLVE_MX:
- if (rdns_request_has_type (reply->request, RDNS_REQUEST_MX)) {
- msg_info ("<%s>: spf error for domain %s: cannot find MX record for %s",
- task->message_id, cb->rec->sender_domain, cb->rec->cur_domain);
- cb->addr->data.normal.d.in4.s_addr = INADDR_NONE;
- cb->addr->data.normal.mask = 32;
- }
- else {
- msg_info ("<%s>: spf error for domain %s: cannot resolve MX record for %s",
- task->message_id, cb->rec->sender_domain, cb->rec->cur_domain);
- cb->addr->data.normal.d.in4.s_addr = INADDR_NONE;
- cb->addr->data.normal.mask = 32;
- }
- break;
- case SPF_RESOLVE_A:
- if (rdns_request_has_type (reply->request, RDNS_REQUEST_A)) {
- cb->addr->data.normal.d.in4.s_addr = INADDR_NONE;
- cb->addr->data.normal.mask = 32;
- }
- break;
-#ifdef HAVE_INET_PTON
- case SPF_RESOLVE_AAA:
- if (rdns_request_has_type (reply->request, RDNS_REQUEST_AAAA)) {
- memset (&cb->addr->data.normal.d.in6, 0xff, sizeof (struct in6_addr));
- cb->addr->data.normal.mask = 32;
- }
- break;
-#endif
- case SPF_RESOLVE_PTR:
- break;
- case SPF_RESOLVE_REDIRECT:
- msg_info ("<%s>: spf error for domain %s: cannot resolve TXT record for %s",
- task->message_id, cb->rec->sender_domain, cb->rec->cur_domain);
- break;
- case SPF_RESOLVE_INCLUDE:
- msg_info ("<%s>: spf error for domain %s: cannot resolve TXT record for %s",
- task->message_id, cb->rec->sender_domain, cb->rec->cur_domain);
- break;
- case SPF_RESOLVE_EXP:
- break;
- case SPF_RESOLVE_EXISTS:
- cb->addr->data.normal.d.in4.s_addr = INADDR_NONE;
- cb->addr->data.normal.mask = 32;
- break;
- }
- }
-
- if (cb->rec->requests_inflight == 0) {
- cb->rec->callback (cb->rec, cb->rec->task);
- }
-}
-
-static gboolean
-parse_spf_a (struct rspamd_task *task, const gchar *begin, struct spf_record *rec, struct spf_addr *addr)
-{
- struct spf_dns_cb *cb;
- gchar *host;
-
- CHECK_REC (rec);
-
- if (begin == NULL || *begin != ':') {
- return FALSE;
- }
- begin ++;
-
- host = parse_spf_hostmask (task, begin, addr, rec);
-
- if (!host) {
- return FALSE;
- }
- 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_A;
- cb->in_include = rec->in_include;
- if (make_dns_request (task->resolver, task->s, task->task_pool,
- spf_record_dns_callback, (void *)cb, RDNS_REQUEST_A, host)) {
- task->dns_requests ++;
- rec->requests_inflight ++;
- return TRUE;
- }
-
- return FALSE;
-
-}
-
-static gboolean
-parse_spf_ptr (struct rspamd_task *task, const gchar *begin, struct spf_record *rec, struct spf_addr *addr)
-{
- CHECK_REC (rec);
-
- msg_info ("<%s>: spf error for domain %s: ptr elements are not implemented",
- rec->task->message_id, rec->sender_domain);
- return FALSE;
-}
-
-static gboolean
-parse_spf_mx (struct rspamd_task *task, const gchar *begin, struct spf_record *rec, struct spf_addr *addr)
-{
- struct spf_dns_cb *cb;
- gchar *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 = rspamd_mempool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
- cb->rec = rec;
- cb->addr = addr;
- memset (&addr->data.normal, 0, sizeof (addr->data.normal));
- cb->cur_action = SPF_RESOLVE_MX;
- cb->in_include = rec->in_include;
- if (make_dns_request (task->resolver, task->s, task->task_pool,
- spf_record_dns_callback, (void *)cb, RDNS_REQUEST_MX, host)) {
- task->dns_requests ++;
- rec->requests_inflight ++;
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-parse_spf_all (struct rspamd_task *task, const gchar *begin, 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;
- }
-
- return TRUE;
-}
-
-static gboolean
-parse_spf_ip4 (struct rspamd_task *task, const gchar *begin, struct spf_record *rec, struct spf_addr *addr)
-{
- /* ip4:addr[/mask] */
-
- CHECK_REC (rec);
- return parse_spf_ipmask (begin, 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)
-{
- /* ip6:addr[/mask] */
-
- CHECK_REC (rec);
- return parse_spf_ipmask (begin, addr, rec);
-}
-#endif
-
-static gboolean
-parse_spf_include (struct rspamd_task *task, const gchar *begin, struct spf_record *rec, struct spf_addr *addr)
-{
- struct spf_dns_cb *cb;
- gchar *domain;
-
- CHECK_REC (rec);
-
- if (begin == NULL || *begin != ':') {
- return FALSE;
- }
- begin ++;
- 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);
- if (make_dns_request (task->resolver, task->s, task->task_pool,
- spf_record_dns_callback, (void *)cb, RDNS_REQUEST_TXT, domain)) {
- task->dns_requests ++;
- rec->requests_inflight ++;
-
- return TRUE;
- }
-
-
- return FALSE;
-}
-
-static gboolean
-parse_spf_exp (struct rspamd_task *task, const gchar *begin, struct spf_record *rec, struct spf_addr *addr)
-{
- CHECK_REC (rec);
-
- msg_info ("exp record is ignored");
- return TRUE;
-}
-
-static gboolean
-parse_spf_redirect (struct rspamd_task *task, const gchar *begin, struct spf_record *rec, struct spf_addr *addr)
-{
- struct spf_dns_cb *cb;
- gchar *domain;
-
- CHECK_REC (rec);
-
- if (begin == NULL || *begin != '=') {
- return FALSE;
- }
- begin ++;
- 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_REDIRECT;
- cb->in_include = rec->in_include;
- domain = rspamd_mempool_strdup (task->task_pool, begin);
- if (make_dns_request (task->resolver, task->s, task->task_pool,
- spf_record_dns_callback, (void *)cb, RDNS_REQUEST_TXT, domain)) {
- task->dns_requests ++;
- rec->requests_inflight ++;
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-parse_spf_exists (struct rspamd_task *task, const gchar *begin, struct spf_record *rec, struct spf_addr *addr)
-{
- struct spf_dns_cb *cb;
- gchar *host;
-
- CHECK_REC (rec);
-
- if (begin == NULL || *begin != ':') {
- return FALSE;
- }
- begin ++;
- 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);
-
- if (make_dns_request (task->resolver, task->s, task->task_pool,
- spf_record_dns_callback, (void *)cb, RDNS_REQUEST_A, host)) {
- task->dns_requests ++;
- rec->requests_inflight ++;
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-reverse_spf_ip (gchar *ip, gint len)
-{
- gchar ipbuf[sizeof("255.255.255.255") - 1], *p, *c;
- gint t = 0, l = len;
-
- if (len > (gint)sizeof (ipbuf)) {
- msg_info ("cannot reverse string of length %d", len);
- return;
- }
-
- p = ipbuf + len;
- c = ip;
- while (-- l) {
- if (*c == '.') {
- memcpy (p, c - t, t);
- *--p = '.';
- c ++;
- t = 0;
- continue;
- }
-
- t ++;
- c ++;
- p --;
- }
-
- memcpy (p - 1, c - t, t + 1);
-
- memcpy (ip, ipbuf, len);
-}
-
-static gchar *
-expand_spf_macro (struct rspamd_task *task, struct spf_record *rec, gchar *begin)
-{
- gchar *p, *c, *new, *tmp;
- gint len = 0, slen = 0, state = 0;
-#ifdef HAVE_INET_PTON
- gchar ip_buf[INET6_ADDRSTRLEN];
-#endif
- gboolean need_expand = FALSE;
-
- p = begin;
- /* Calculate length */
- while (*p) {
- switch (state) {
- case 0:
- /* Skip any character and wait for % in input */
- if (*p == '%') {
- state = 1;
- }
- else {
- len ++;
- }
-
- slen ++;
- p ++;
- break;
- case 1:
- /* We got % sign, so we should whether wait for { or for - or for _ or for % */
- if (*p == '%' || *p == '-') {
- /* Just a single % sign or space */
- len ++;
- }
- else if (*p == '_') {
- /* %20 */
- len += sizeof ("%20") - 1;
- }
- else if (*p == '{') {
- state = 2;
- }
- else {
- /* Something unknown */
- msg_info ("<%s>: spf error for domain %s: unknown spf element",
- task->message_id, rec->sender_domain);
- return begin;
- }
- p ++;
- slen ++;
- break;
- case 2:
- /* Read macro name */
- switch (g_ascii_tolower (*p)) {
- case 'i':
-#ifdef HAVE_INET_PTON
- len += sizeof (INET6_ADDRSTRLEN) - 1;
-#else
- len += sizeof (INET_ADDRSTRLEN) - 1;
-#endif
- break;
- case 's':
- len += strlen (rec->sender);
- break;
- case 'l':
- len += strlen (rec->local_part);
- break;
- case 'o':
- len += strlen (rec->sender_domain);
- break;
- case 'd':
- len += strlen (rec->cur_domain);
- break;
- case 'v':
- len += sizeof ("in-addr") - 1;
- break;
- case 'h':
- if (task->helo) {
- len += strlen (task->helo);
- }
- break;
- default:
- msg_info ("<%s>: spf error for domain %s: unknown or unsupported spf macro %c in %s",
- task->message_id, rec->sender_domain, *p, begin);
- return begin;
- }
- p ++;
- slen ++;
- state = 3;
- break;
- case 3:
- /* Read modifier */
- if (*p == '}') {
- state = 0;
- need_expand = TRUE;
- }
- else if (*p != 'r' && !g_ascii_isdigit (*p)) {
- msg_info ("<%s>: spf error for domain %s: unknown or unsupported spf modifier %c in %s",
- task->message_id, rec->sender_domain, *p, begin);
- return begin;
- }
- p ++;
- slen ++;
- break;
- }
- }
-
- if (!need_expand) {
- /* No expansion needed */
- return begin;
- }
-
- new = rspamd_mempool_alloc (task->task_pool, len + 1);
-
- c = new;
- p = begin;
- state = 0;
- /* Begin macro expansion */
-
- while (*p) {
- switch (state) {
- case 0:
- /* Skip any character and wait for % in input */
- if (*p == '%') {
- state = 1;
- }
- else {
- *c = *p;
- c ++;
- }
-
- p ++;
- break;
- case 1:
- /* We got % sign, so we should whether wait for { or for - or for _ or for % */
- if (*p == '%') {
- /* Just a single % sign or space */
- *c++ = '%';
- }
- else if (*p == '-') {
- *c++ = ' ';
- }
- else if (*p == '_') {
- /* %20 */
- *c++ = '%';
- *c++ = '2';
- *c++ = '0';
- }
- else if (*p == '{') {
- state = 2;
- }
- else {
- /* Something unknown */
- msg_info ("<%s>: spf error for domain %s: unknown spf element",
- task->message_id, rec->sender_domain);
- return begin;
- }
- p ++;
- break;
- case 2:
- /* Read macro name */
- switch (g_ascii_tolower (*p)) {
- case 'i':
-#ifdef HAVE_INET_PTON
- len = rspamd_strlcpy (ip_buf,
- rspamd_inet_address_to_string (&task->from_addr),
- sizeof (ip_buf));
- memcpy (c, ip_buf, len);
-#else
- tmp = inet_ntoa (task->from_addr);
- len = strlen (tmp);
- memcpy (c, tmp, len);
-#endif
- c += len;
- break;
- case 's':
- len = strlen (rec->sender);
- memcpy (c, rec->sender, len);
- c += len;
- break;
- case 'l':
- len = strlen (rec->local_part);
- memcpy (c, rec->local_part, len);
- c += len;
- break;
- case 'o':
- len = strlen (rec->sender_domain);
- memcpy (c, rec->sender_domain, len);
- c += len;
- break;
- case 'd':
- len = strlen (rec->cur_domain);
- memcpy (c, rec->cur_domain, len);
- c += len;
- break;
- case 'v':
- len = sizeof ("in-addr") - 1;
- memcpy (c, "in-addr", len);
- c += len;
- break;
- case 'h':
- if (task->helo) {
- tmp = strchr (task->helo, '@');
- if (tmp) {
- len = strlen (tmp + 1);
- memcpy (c, tmp + 1, len);
- c += len;
- }
- }
- break;
- default:
- msg_info ("<%s>: spf error for domain %s: unknown or unsupported spf macro %c in %s",
- task->message_id, rec->sender_domain, *p, begin);
- return begin;
- }
- p ++;
- state = 3;
- break;
- case 3:
- /* Read modifier */
- if (*p == '}') {
- state = 0;
- }
- else if (*p == 'r' && len != 0) {
- reverse_spf_ip (c - len, len);
- len = 0;
- }
- else if (g_ascii_isdigit (*p)) {
- /*XXX: try to implement domain strimming */
- }
- else {
- msg_info ("<%s>: spf error for domain %s: unknown or unsupported spf macro %c in %s",
- task->message_id, rec->sender_domain, *p, begin);
- return begin;
- }
- p ++;
- break;
- }
- }
- /* Null terminate */
- *c = '\0';
-
- return new;
-
-}
-
-#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)
-{
- struct spf_addr *new = NULL;
- gboolean need_shift, res = FALSE;
- gchar *begin;
-
- 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 ++;
- return TRUE;
- }
- else {
- begin = expand_spf_macro (task, rec, rec->cur_elt);
- if (*begin == '?' || *begin == '+' || *begin == '-' || *begin == '~') {
- begin ++;
- }
-
-
- /* Now check what we have */
- switch (g_ascii_tolower (*begin)) {
- 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, 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",
- task->message_id, rec->sender_domain, begin);
- }
- 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, 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) - 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",
- 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",
- task->message_id, rec->sender_domain, begin);
- }
- 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",
- task->message_id, rec->sender_domain, begin);
- }
- 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, 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",
- task->message_id, rec->sender_domain, begin);
- }
- 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:
- msg_info ("<%s>: spf error for domain %s: bad spf command %s",
- task->message_id, rec->sender_domain, begin);
- break;
- }
- if (res) {
- if (new != NULL) {
- rec->addrs = g_list_prepend (rec->addrs, new);
- }
- rec->elt_num ++;
- }
- }
-
- return res;
-}
-#undef NEW_ADDR
-
-static void
-parse_spf_scopes (struct spf_record *rec, gchar **begin)
-{
- for (;;) {
- if (g_ascii_strncasecmp (*begin, SPF_SCOPE_PRA, sizeof (SPF_SCOPE_PRA) - 1) == 0) {
- *begin += sizeof (SPF_SCOPE_PRA) - 1;
- /* XXX: Implement actual PRA check */
- /* extract_pra_info (rec); */
- continue;
- }
- else if (g_ascii_strncasecmp (*begin, SPF_SCOPE_MFROM, sizeof (SPF_SCOPE_MFROM) - 1) == 0) {
- /* mfrom is standart spf1 check */
- *begin += sizeof (SPF_SCOPE_MFROM) - 1;
- continue;
- }
- else if (**begin != ',') {
- break;
- }
- (*begin) ++;
- }
-}
-
-static void
-start_spf_parse (struct spf_record *rec, gchar *begin, guint ttl)
-{
- /* Skip spaces */
- while (g_ascii_isspace (*begin)) {
- begin ++;
- }
-
- if (g_ascii_strncasecmp (begin, SPF_VER1_STR, sizeof (SPF_VER1_STR) - 1) == 0) {
- begin += sizeof (SPF_VER1_STR) - 1;
- while (g_ascii_isspace (*begin) && *begin) {
- begin ++;
- }
- rec->elts = g_strsplit_set (begin, " ", 0);
- rec->elt_num = 0;
- if (rec->elts) {
- rspamd_mempool_add_destructor (rec->task->task_pool, (rspamd_mempool_destruct_t)g_strfreev, rec->elts);
- rec->cur_elt = rec->elts[0];
- while (parse_spf_record (rec->task, rec));
- if (ttl != 0) {
- rec->ttl = ttl;
- }
- }
- }
- else if (g_ascii_strncasecmp (begin, SPF_VER2_STR, sizeof (SPF_VER2_STR) - 1) == 0) {
- /* Skip one number of record, so no we are here spf2.0/ */
- begin += sizeof (SPF_VER2_STR);
- if (*begin != '/') {
- msg_info ("<%s>: spf error for domain %s: sender id is invalid",
- rec->task->message_id, rec->sender_domain);
- }
- else {
- begin ++;
- parse_spf_scopes (rec, &begin);
- }
- /* Now common spf record */
- while (g_ascii_isspace (*begin) && *begin) {
- begin ++;
- }
- rec->elts = g_strsplit_set (begin, " ", 0);
- rec->elt_num = 0;
- if (rec->elts) {
- rspamd_mempool_add_destructor (rec->task->task_pool, (rspamd_mempool_destruct_t)g_strfreev, rec->elts);
- rec->cur_elt = rec->elts[0];
- while (parse_spf_record (rec->task, rec));
- if (ttl != 0) {
- rec->ttl = ttl;
- }
- }
- }
- else {
- msg_debug ("<%s>: spf error for domain %s: bad spf record version: %*s",
- rec->task->message_id, rec->sender_domain, sizeof (SPF_VER1_STR) - 1, begin);
- }
-}
-
-static void
-spf_dns_callback (struct rdns_reply *reply, gpointer arg)
-{
- struct spf_record *rec = arg;
- struct rdns_reply_entry *elt;
-
- rec->requests_inflight --;
- if (reply->code == RDNS_RC_NOERROR) {
- LL_FOREACH (reply->entries, elt) {
- start_spf_parse (rec, elt->content.txt.data, elt->ttl);
- }
- }
-
- if (rec->requests_inflight == 0) {
- rec->callback (rec, rec->task);
- }
-}
-
-gchar *
-get_spf_domain (struct rspamd_task *task)
-{
- gchar *domain, *res = NULL;
- GList *domains;
-
- if (task->from && (domain = strchr (task->from, '@')) != NULL && *domain == '@') {
- res = rspamd_mempool_strdup (task->task_pool, domain + 1);
- if ((domain = strchr (res, '>')) != NULL) {
- *domain = '\0';
- }
- }
- else {
- /* Extract from header */
- domains = message_get_header (task->task_pool, task->message, "From", FALSE);
-
- if (domains != NULL) {
- res = rspamd_mempool_strdup (task->task_pool, domains->data);
-
- if ((domain = strrchr (res, '@')) == NULL) {
- g_list_free (domains);
- return NULL;
- }
- res = rspamd_mempool_strdup (task->task_pool, domain + 1);
- g_list_free (domains);
-
- if ((domain = strchr (res, '>')) != NULL) {
- *domain = '\0';
- }
- }
- }
-
- return res;
-}
-
-gboolean
-resolve_spf (struct rspamd_task *task, spf_cb_t callback)
-{
- struct spf_record *rec;
- gchar *domain;
- GList *domains;
-
- rec = rspamd_mempool_alloc0 (task->task_pool, sizeof (struct spf_record));
- rec->task = task;
- rec->callback = callback;
- /* Add destructor */
- rspamd_mempool_add_destructor (task->task_pool, (rspamd_mempool_destruct_t)spf_record_destructor, rec);
-
- /* Extract from data */
- if (task->from && (domain = strchr (task->from, '@')) != NULL && *domain == '@') {
- rec->sender = task->from;
-
- rec->local_part = rspamd_mempool_strdup (task->task_pool, task->from);
- *(rec->local_part + (domain - task->from)) = '\0';
- if (*rec->local_part == '<') {
- memmove (rec->local_part, rec->local_part + 1, strlen (rec->local_part));
- }
- rec->cur_domain = rspamd_mempool_strdup (task->task_pool, domain + 1);
- if ((domain = strchr (rec->cur_domain, '>')) != NULL) {
- *domain = '\0';
- }
- rec->sender_domain = rec->cur_domain;
-
- if (make_dns_request (task->resolver, task->s, task->task_pool, spf_dns_callback,
- (void *)rec, RDNS_REQUEST_TXT, rec->cur_domain)) {
- task->dns_requests ++;
- rec->requests_inflight ++;
- return TRUE;
- }
- }
- else {
- /* Extract from header */
- domains = message_get_header (task->task_pool, task->message, "From", FALSE);
-
- if (domains != NULL) {
- rec->cur_domain = rspamd_mempool_strdup (task->task_pool, domains->data);
- g_list_free (domains);
-
- if ((domain = strrchr (rec->cur_domain, '@')) == NULL) {
- return FALSE;
- }
- rec->sender = rspamd_mempool_strdup (task->task_pool, rec->cur_domain);
- rec->local_part = rec->cur_domain;
- *domain = '\0';
- rec->cur_domain = domain + 1;
-
- if ((domain = strchr (rec->local_part, '<')) != NULL) {
- memmove (rec->local_part, domain + 1, strlen (domain));
- }
-
- if ((domain = strchr (rec->cur_domain, '>')) != NULL) {
- *domain = '\0';
- }
- rec->sender_domain = rec->cur_domain;
- if (make_dns_request (task->resolver, task->s, task->task_pool,
- spf_dns_callback, (void *)rec, RDNS_REQUEST_TXT, rec->cur_domain)) {
- task->dns_requests ++;
- rec->requests_inflight ++;
- return TRUE;
- }
- }
- }
-
- return FALSE;
-}
-
-/*
- * vi:ts=4
- */