From cd603ad1e87f01b8872b5b44b48ce3de51da9120 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Wed, 18 Mar 2015 16:13:21 +0000 Subject: [PATCH] Rework DNS callback. --- src/libserver/spf.c | 437 ++++++++++++-------------------------------- src/libserver/spf.h | 5 +- 2 files changed, 124 insertions(+), 318 deletions(-) diff --git a/src/libserver/spf.c b/src/libserver/spf.c index 619f94fc4..163ebf5c2 100644 --- a/src/libserver/spf.c +++ b/src/libserver/spf.c @@ -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/ - * a: - * a:/ - */ - 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); } diff --git a/src/libserver/spf.h b/src/libserver/spf.h index c491cd8b4..93098e941 100644 --- a/src/libserver/spf.h +++ b/src/libserver/spf.h @@ -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; -- 2.39.5