Browse Source

Rework DNS callback.

tags/0.9.0
Vsevolod Stakhov 9 years ago
parent
commit
cd603ad1e8
2 changed files with 124 additions and 318 deletions
  1. 121
    316
      src/libserver/spf.c
  2. 3
    2
      src/libserver/spf.h

+ 121
- 316
src/libserver/spf.c View File

@@ -95,6 +95,7 @@ struct spf_record {
struct spf_dns_cb {
struct spf_record *rec;
struct spf_addr *addr;
struct spf_resolved_element *resolved;
gchar *ptr_host;
spf_action_t cur_action;
gboolean in_include;
@@ -190,95 +191,7 @@ rspamd_spf_new_addr_list (struct spf_record *rec, const gchar *domain)
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;
}

/*
@@ -312,11 +225,8 @@ parse_spf_ipmask (const gchar *begin,
const gchar *pos;
gchar mask_buf[5] = {'\0'}, *p;
gint state = 0, dots = 0;
#ifdef HAVE_INET_PTON
guint mask;
gchar ip_buf[INET6_ADDRSTRLEN];
#else
gchar ip_buf[INET_ADDRSTRLEN];
#endif

bzero (ip_buf, sizeof (ip_buf));
bzero (mask_buf, sizeof (mask_buf));
@@ -338,7 +248,6 @@ parse_spf_ipmask (const gchar *begin,
dots = 0;
break;
case 1:
#ifdef HAVE_INET_PTON
if (p - ip_buf >= (gint)sizeof (ip_buf)) {
return FALSE;
}
@@ -349,19 +258,6 @@ parse_spf_ipmask (const gchar *begin,
*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;
@@ -391,10 +287,9 @@ parse_spf_ipmask (const gchar *begin,
}
}

#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;
if (inet_pton (AF_INET, ip_buf, &addr->addr) != 1) {
if (inet_pton (AF_INET6, ip_buf, &addr->addr) == 1) {
addr->flags |= RSPAMD_SPF_FLAG_IPV6;
}
else {
msg_info ("<%s>: spf error for domain %s: invalid ip address",
@@ -402,109 +297,34 @@ parse_spf_ipmask (const gchar *begin,
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 (
mask = strtoul (mask_buf, NULL, 10);
if (mask > (addr->flags & RSPAMD_SPF_FLAG_IPV6) ? 128 : 32) {
msg_info (
"<%s>: spf error for domain %s: bad ipmask value: '%s'",
rec->task->message_id,
rec->sender_domain,
begin);
return FALSE;
}
return FALSE;
}
}
else {
addr->data.normal.mask = addr->data.normal.ipv6 ? 128 : 32;
}
addr->data.normal.parsed = TRUE;
return TRUE;

}

static void
spf_record_process_addr (struct rdns_reply_entry *elt,
struct spf_dns_cb *cb, struct rspamd_task *task)
{
struct spf_addr *addr = cb->addr, *new_addr;
GList *tmp = NULL;

if (elt->type == RDNS_REQUEST_A) {
if (!addr->data.normal.parsed) {
addr->data.normal.d.in4.s_addr = elt->content.a.addr.s_addr;
addr->data.normal.parsed = TRUE;
if (addr->flags & RSPAMD_SPF_FLAG_IPV6) {
addr->m.dual.mask_v6 = mask;
}
else {
/* Insert one more address */
tmp = spf_addr_find (cb->rec->addrs, addr);
if (tmp) {
new_addr = rspamd_mempool_alloc (task->task_pool,
sizeof (struct spf_addr));
memcpy (new_addr, addr, sizeof (struct spf_addr));
new_addr->data.normal.d.in4.s_addr = elt->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);
}
addr->m.dual.mask_v4 = mask;
}

}
else if (elt->type == RDNS_REQUEST_AAAA) {
if (!addr->data.normal.parsed) {
memcpy (&addr->data.normal.d.in6,
&elt->content.aaa.addr, sizeof (struct in6_addr));
addr->data.normal.mask = 32;
addr->data.normal.parsed = TRUE;
addr->data.normal.ipv6 = TRUE;
}
else {
/* Insert one more address */
tmp = spf_addr_find (cb->rec->addrs, addr);
if (tmp) {
new_addr =
rspamd_mempool_alloc (task->task_pool,
sizeof (struct spf_addr));
memcpy (new_addr, addr, sizeof (struct spf_addr));
memcpy (&new_addr->data.normal.d.in6,
&elt->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);
}
}
else {
addr->m.dual.mask_v6 = 128;
addr->m.dual.mask_v4 = 32;
}

return TRUE;

}

static gboolean
@@ -520,7 +340,7 @@ spf_check_ptr_host (struct spf_dns_cb *cb, const char *name)

}
else {
dstart = cb->rec->cur_domain;
dstart = cb->resolved->cur_domain;
}

msg_debug ("check ptr %s vs %s", name, dstart);
@@ -544,7 +364,7 @@ spf_check_ptr_host (struct spf_dns_cb *cb, const char *name)
/* Now compare from end to start */
for (;;) {
if (g_ascii_tolower (*dend) != g_ascii_tolower (*nend)) {
msg_debug ("ptr records missmatch: %s and %s", dend, nend);
msg_debug ("ptr records mismatch: %s and %s", dend, nend);
return FALSE;
}
if (dend == dstart) {
@@ -565,6 +385,44 @@ spf_check_ptr_host (struct spf_dns_cb *cb, const char *name)
return TRUE;
}

static void
spf_record_process_addr (struct spf_addr *addr, struct rdns_reply_entry *reply)
{
if (reply->type == RDNS_REQUEST_AAAA) {
memcpy (addr->addr6, &reply->content.aaa.addr, sizeof (addr->addr6));
}
else if (reply->type == RDNS_REQUEST_A) {
memcpy (addr->addr4, &reply->content.a.addr, sizeof (addr->addr4));
}
else {
msg_err ("internal error, bad DNS reply is treated as address: %s",
rdns_strtype (reply->type));
}
}

static void
spf_record_addr_set (struct spf_addr *addr, gboolean allow_any)
{
guchar fill;
guint maskv4, maskv6;

if (allow_any) {
fill = 0;
maskv4 = 0;
maskv6 = 0;
}
else {
fill = 0xff;
maskv4 = 32;
maskv6 = 128;
}

memset (addr->addr4, fill, sizeof (addr->addr4));
memset (addr->addr6, fill, sizeof (addr->addr6));
addr->m.dual.mask_v4 = maskv4;
addr->m.dual.mask_v6 = maskv6;
}

static void
spf_record_dns_callback (struct rdns_reply *reply, gpointer arg)
{
@@ -573,11 +431,13 @@ spf_record_dns_callback (struct rdns_reply *reply, gpointer arg)
struct rdns_reply_entry *elt_data;
GList *tmp = NULL;
struct rspamd_task *task;
struct spf_addr *addr;
gboolean ret;

task = cb->rec->task;

cb->rec->requests_inflight--;
addr = cb->addr;

if (reply->code == RDNS_RC_NOERROR) {
/* Add all logic for all DNS states here */
@@ -605,12 +465,12 @@ spf_record_dns_callback (struct rdns_reply *reply, gpointer arg)
}
}
else {
spf_record_process_addr (elt_data, cb, task);
spf_record_process_addr (addr, elt_data);
}
break;
case SPF_RESOLVE_A:
case SPF_RESOLVE_AAA:
spf_record_process_addr (elt_data, cb, task);
spf_record_process_addr (addr, elt_data);
break;
case SPF_RESOLVE_PTR:
if (elt_data->type == RDNS_REQUEST_PTR) {
@@ -637,46 +497,19 @@ spf_record_dns_callback (struct rdns_reply *reply, gpointer arg)
}
}
else {
spf_record_process_addr (elt_data, cb, task);
spf_record_process_addr (addr, elt_data);
}
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);

ret = start_spf_parse (cb->rec, begin, 0);
}
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;
ret = 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

if (ret) {
/* Insert new list */
cb->addr->is_list = TRUE;
cb->addr->data.list = cb->rec->addrs;
}

cb->rec->addrs = tmp;
}
break;
case SPF_RESOLVE_EXP:
@@ -685,8 +518,7 @@ spf_record_dns_callback (struct rdns_reply *reply, gpointer arg)
if (elt_data->type == RDNS_REQUEST_A || elt_data->type ==
RDNS_REQUEST_AAAA) {
/* 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;
spf_record_addr_set (addr, TRUE);
}
break;
}
@@ -700,56 +532,51 @@ spf_record_dns_callback (struct rdns_reply *reply, gpointer arg)
"<%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;
cb->resolved->cur_domain);
spf_record_addr_set (addr, FALSE);
}
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;
cb->resolved->cur_domain);
spf_record_addr_set (addr, FALSE);
}
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;
spf_record_addr_set (addr, FALSE);
}
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;
spf_record_addr_set (addr, FALSE);
}
break;
#endif
case SPF_RESOLVE_PTR:
spf_record_addr_set (addr, FALSE);
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);
cb->resolved->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);
cb->resolved->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;
spf_record_addr_set (addr, FALSE);
break;
}
}
@@ -779,7 +606,8 @@ parse_spf_domain_mask (struct spf_record *rec, struct spf_addr *addr,
parse_slash,
parse_ipv4_mask,
parse_second_slash,
parse_ipv6_mask
parse_ipv6_mask,
skip_garbadge
} state = 0;
const gchar *p = addr->spf_string, *host, *c;
gchar *hostbuf;
@@ -823,7 +651,12 @@ parse_spf_domain_mask (struct spf_record *rec, struct spf_addr *addr,
break;
case parse_slash:
c = p;
state = parse_ipv4_mask;
if (allow_mask) {
state = parse_ipv4_mask;
}
else {
state = skip_garbadge;
}
cur_mask = 0;
break;
case parse_ipv4_mask:
@@ -854,6 +687,9 @@ parse_spf_domain_mask (struct spf_record *rec, struct spf_addr *addr,
}
p ++;
break;
case skip_garbadge:
p++;
break;
}
}

@@ -884,41 +720,18 @@ parse_spf_domain_mask (struct spf_record *rec, struct spf_addr *addr,
}

static gboolean
parse_spf_a (struct rspamd_task *task,
const gchar *begin,
struct spf_record *rec,
struct spf_addr *addr)
parse_spf_a (struct spf_record *rec, struct spf_addr *addr)
{
struct spf_dns_cb *cb;
gchar *host = NULL;
const gchar *host = NULL;
struct rspamd_task *task = rec->task;
struct spf_resolved_element *resolved;

resolved = &g_array_index (rec->resolved, struct spf_resolved_element,
rec->resolved->len - 1);
CHECK_REC (rec);

/*
* a
* a/<prefix-length>
* a:<domain>
* a:<domain>/<prefix-length>
*/
if (begin == NULL) {
return FALSE;
}
if (*begin == '\0') {
/* Use current domain only */
host = rec->cur_domain;
addr->data.normal.mask = 32;
}
else if (*begin == ':') {
begin++;
}
else if (*begin != '/') {
/* Invalid A record */
return FALSE;
}

if (host == NULL) {
host = parse_spf_hostmask (task, begin, addr, rec);
}
host = parse_spf_domain_mask (rec, addr, TRUE);

if (host == NULL) {
return FALSE;
@@ -929,8 +742,9 @@ parse_spf_a (struct rspamd_task *task,
cb->rec = rec;
cb->addr = addr;
cb->cur_action = SPF_RESOLVE_A;
cb->in_include = rec->in_include;
cb->resolved = resolved;
msg_debug ("resolve a %s", host);

if (make_dns_request (task->resolver, task->s, task->task_pool,
spf_record_dns_callback, (void *)cb, RDNS_REQUEST_A, host)) {
task->dns_requests++;
@@ -943,43 +757,34 @@ parse_spf_a (struct rspamd_task *task,
}

static gboolean
parse_spf_ptr (struct rspamd_task *task,
const gchar *begin,
struct spf_record *rec,
struct spf_addr *addr)
parse_spf_ptr (struct spf_record *rec, struct spf_addr *addr)
{
struct spf_dns_cb *cb;
gchar *host, *ptr;
struct rspamd_task *task = rec->task;
struct spf_resolved_element *resolved;

resolved = &g_array_index (rec->resolved, struct spf_resolved_element,
rec->resolved->len - 1);
CHECK_REC (rec);

if (begin == NULL) {
return FALSE;
}
if (*begin == ':') {
begin++;
host = rspamd_mempool_strdup (task->task_pool, begin);
}
else if (*begin == '\0') {
host = NULL;
}
else {
return FALSE;
}
host = parse_spf_domain_mask (rec, addr, 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_PTR;
cb->in_include = rec->in_include;
cb->resolved = resolved;
cb->ptr_host = host;
ptr =
rdns_generate_ptr_from_str (rspamd_inet_address_to_string (
task->from_addr));

if (ptr == NULL) {
return FALSE;
}

rspamd_mempool_add_destructor (task->task_pool, free, ptr);
msg_debug ("resolve ptr %s for %s", ptr, host);
if (make_dns_request (task->resolver, task->s, task->task_pool,
@@ -991,7 +796,6 @@ parse_spf_ptr (struct rspamd_task *task,
}

return FALSE;
return TRUE;
}

static gboolean
@@ -999,27 +803,27 @@ parse_spf_mx (struct spf_record *rec, struct spf_addr *addr)
{
struct spf_dns_cb *cb;
gchar *host;
struct rspamd_task *task = rec->task;
struct spf_resolved_element *resolved;

CHECK_REC (rec);
resolved = &g_array_index (rec->resolved, struct spf_resolved_element,
rec->resolved->len - 1);

if (begin == NULL) {
return FALSE;
}
if (*begin == ':') {
begin++;
}
CHECK_REC (rec);

host = parse_spf_hostmask (task, begin, addr, rec);
host = parse_spf_domain_mask (rec, addr, TRUE);

if (host == NULL) {
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_MX;
cb->in_include = rec->in_include;
cb->resolved = resolved;

msg_debug ("resolve mx for %s", host);
if (make_dns_request (task->resolver, task->s, task->task_pool,
spf_record_dns_callback, (void *)cb, RDNS_REQUEST_MX, host)) {
@@ -1036,8 +840,10 @@ static gboolean
parse_spf_all (struct spf_record *rec, struct spf_addr *addr)
{
/* All is 0/0 */
memset (&addr->addr, 0, sizeof (addr->addr));
addr->m.mask = 0;
memset (&addr->addr4, 0, sizeof (addr->addr4));
memset (&addr->addr6, 0, sizeof (addr->addr6));
/* Here we set all masks to 0 */
addr->m.idx = 0;
addr->flags |= RSPAMD_SPF_FLAG_ANY;

return TRUE;
@@ -1068,8 +874,8 @@ parse_spf_include (struct spf_record *rec, struct spf_addr *addr)
{
struct spf_dns_cb *cb;
gchar *domain;
struct spf_resolved_element *resolved;
struct rspamd_task *task = rec->task;
struct spf_resolved_element *resolved;

resolved = &g_array_index (rec->resolved, struct spf_resolved_element,
rec->resolved->len - 1);
@@ -1091,7 +897,7 @@ parse_spf_include (struct spf_record *rec, struct spf_addr *addr)
cb->cur_action = SPF_RESOLVE_INCLUDE;
/* Set reference */
addr->flags |= RSPAMD_SPF_FLAG_REFRENCE;
addr->m.mask = rec->resolved->len;
addr->m.idx = rec->resolved->len;
rspamd_spf_new_addr_list (rec, domain);
msg_debug ("resolve include %s", domain);

@@ -1154,7 +960,7 @@ parse_spf_redirect (struct spf_record *rec, struct spf_addr *addr)
cb = rspamd_mempool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
/* Set reference */
addr->flags |= RSPAMD_SPF_FLAG_REFRENCE;
addr->m.mask = rec->resolved->len;
addr->m.idx = rec->resolved->len;
rspamd_spf_new_addr_list (rec, domain);

cb->rec = rec;
@@ -1236,7 +1042,6 @@ reverse_spf_ip (gchar *ip, gint len)
}

memcpy (p - 1, c - t, t + 1);

memcpy (ip, ipbuf, len);
}


+ 3
- 2
src/libserver/spf.h View File

@@ -34,13 +34,14 @@ typedef enum spf_action_e {
#define RSPAMD_SPF_FLAG_REFRENCE (1 << 4)

struct spf_addr {
guchar addr[sizeof (struct in6_addr)];
guchar addr6[sizeof (struct in6_addr)];
guchar addr4[sizeof (struct in_addr)];
union {
struct {
guint16 mask_v4;
guint16 mask_v6;
} dual;
guint32 mask;
guint32 idx;
} m;
guint flags;
spf_mech_t mech;

Loading…
Cancel
Save