diff options
author | Vsevolod Stakhov <vsevolod@rambler-co.ru> | 2012-04-20 20:02:28 +0400 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@rambler-co.ru> | 2012-04-20 20:02:28 +0400 |
commit | 4d4668a0d4022583208d20bac9b8a0bede6f073d (patch) | |
tree | 14fbacb9511c738b40919aecbf168151605d28cb /src/spf.c | |
parent | 6cc47586dbcbf21fb67be92f6736fd76ca8baffb (diff) | |
download | rspamd-4d4668a0d4022583208d20bac9b8a0bede6f073d.tar.gz rspamd-4d4668a0d4022583208d20bac9b8a0bede6f073d.zip |
* Fix spf plugin that was broken in 0.4.7
* Add partial ipv6 support for some rspamd modules.
Diffstat (limited to 'src/spf.c')
-rw-r--r-- | src/spf.c | 229 |
1 files changed, 198 insertions, 31 deletions
@@ -120,7 +120,11 @@ dump_spf_record (GList *addrs) 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; @@ -139,8 +143,19 @@ dump_spf_record (GList *addrs) c = '+'; break; } - ina.s_addr = htonl (addr->data.normal.addr); +#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); @@ -204,9 +219,13 @@ static gboolean parse_spf_ipmask (const gchar *begin, struct spf_addr *addr) { const gchar *pos; - gchar ip_buf[sizeof ("255.255.255.255")], mask_buf[3], *p; + gchar mask_buf[5] = {'\0'}, *p; gint state = 0, dots = 0; - struct in_addr in; +#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)); @@ -226,6 +245,18 @@ parse_spf_ipmask (const gchar *begin, struct spf_addr *addr) 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; @@ -237,6 +268,7 @@ parse_spf_ipmask (const gchar *begin, struct spf_addr *addr) *p ++ = *pos ++; dots ++; } +#endif else if (*pos == '/') { pos ++; p = mask_buf; @@ -249,7 +281,7 @@ parse_spf_ipmask (const gchar *begin, struct spf_addr *addr) break; case 2: /* Parse mask */ - if (p - mask_buf > 2) { + if (p - mask_buf >= (gint)sizeof (mask_buf)) { return FALSE; } if (g_ascii_isdigit (*pos)) { @@ -262,22 +294,44 @@ parse_spf_ipmask (const gchar *begin, struct spf_addr *addr) } } - if (!inet_aton (ip_buf, &in)) { +#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 { + return FALSE; + } + } + else { + addr->data.normal.ipv6 = FALSE; + } +#else + if (!inet_aton (ip_buf, &addr->data.normal.d.in4)) { return FALSE; } - addr->data.normal.addr = ntohl (in.s_addr); +#endif if (state == 2) { /* Also parse mask */ - addr->data.normal.mask = (mask_buf[0] - '0') * 10 + mask_buf[1] - '0'; - if (addr->data.normal.mask > 32) { - msg_info ("bad ipmask value: '%s'", begin); - return FALSE; + if (!addr->data.normal.ipv6) { + addr->data.normal.mask = (mask_buf[0] - '0') * 10 + mask_buf[1] - '0'; + if (addr->data.normal.mask > 32) { + msg_info ("bad ipmask value: '%s'", begin); + return FALSE; + } + } + else { + addr->data.normal.mask = strtoul (mask_buf, NULL, 10); + if (addr->data.normal.mask > 128) { + msg_info ("bad ipmask value: '%s'", begin); + return FALSE; + } } } else { - addr->data.normal.mask = 32; + addr->data.normal.mask = addr->data.normal.ipv6 ? 128 : 32; } - + addr->data.normal.parsed = TRUE; return TRUE; } @@ -329,6 +383,8 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) task = cb->rec->task; + cb->rec->requests_inflight --; + if (reply->code == DNS_RC_NOERROR) { if (reply->elements != NULL) { /* Add all logic for all DNS states here */ @@ -341,12 +397,38 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) /* Now resolve A record for this MX */ if (make_dns_request (task->resolver, task->s, task->task_pool, spf_record_dns_callback, (void *)cb, DNS_REQUEST_A, elt_data->mx.name)) { task->dns_requests ++; + cb->rec->requests_inflight ++; } } else if (reply->type == DNS_REQUEST_A) { - if (cb->addr->data.normal.addr == 0) { - cb->addr->data.normal.addr = ntohl (elt_data->a.addr[0].s_addr); + if (!cb->addr->data.normal.parsed) { + cb->addr->data.normal.d.in4.s_addr = elt_data->a.addr[0].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 = memory_pool_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->a.addr[0].s_addr; + new_addr->data.normal.parsed = TRUE; + cb->rec->addrs = g_list_insert_before (cb->rec->addrs, tmp, new_addr); + } + else { + msg_info ("wrong address list"); + } + } + + } +#ifdef HAVE_INET_PTON + else if (reply->type == DNS_REQUEST_AAA) { + if (!cb->addr->data.normal.parsed) { + memcpy (&cb->addr->data.normal.d.in6, &elt_data->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 */ @@ -354,7 +436,9 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) if (tmp) { new_addr = memory_pool_alloc (task->task_pool, sizeof (struct spf_addr)); memcpy (new_addr, cb->addr, sizeof (struct spf_addr)); - new_addr->data.normal.addr = ntohl (elt_data->a.addr[0].s_addr); + memcpy (&new_addr->data.normal.d.in6, &elt_data->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 { @@ -363,13 +447,39 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) } } +#endif break; case SPF_RESOLVE_A: if (reply->type == DNS_REQUEST_A) { /* XXX: process only one record */ - cb->addr->data.normal.addr = ntohl (elt_data->a.addr[0].s_addr); + cb->addr->data.normal.d.in4.s_addr = elt_data->a.addr[0].s_addr; + cb->addr->data.normal.mask = 32; + cb->addr->data.normal.parsed = TRUE; + } +#ifdef HAVE_INET_PTON + else if (reply->type == DNS_REQUEST_AAA) { + memcpy (&cb->addr->data.normal.d.in6, &elt_data->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 (reply->type == DNS_REQUEST_A) { + /* XXX: process only one record */ + cb->addr->data.normal.d.in4.s_addr = elt_data->a.addr[0].s_addr; + cb->addr->data.normal.mask = 32; + cb->addr->data.normal.parsed = TRUE; + } + else if (reply->type == DNS_REQUEST_AAA) { + memcpy (&cb->addr->data.normal.d.in6, &elt_data->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; @@ -413,7 +523,7 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) case SPF_RESOLVE_EXISTS: if (reply->type == DNS_REQUEST_A) { /* If specified address resolves, we can accept connection from every IP */ - cb->addr->data.normal.addr = ntohl (INADDR_ANY); + cb->addr->data.normal.d.in4.s_addr = INADDR_NONE; cb->addr->data.normal.mask = 0; } break; @@ -427,22 +537,31 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) case SPF_RESOLVE_MX: if (reply->type == DNS_REQUEST_MX) { msg_info ("cannot find MX record for %s", cb->rec->cur_domain); - cb->addr->data.normal.addr = ntohl (INADDR_NONE); + cb->addr->data.normal.d.in4.s_addr = INADDR_NONE; cb->addr->data.normal.mask = 32; } else if (reply->type != DNS_REQUEST_MX) { msg_info ("cannot resolve MX record for %s", cb->rec->cur_domain); - cb->addr->data.normal.addr = ntohl (INADDR_NONE); + cb->addr->data.normal.d.in4.s_addr = INADDR_NONE; cb->addr->data.normal.mask = 32; } break; case SPF_RESOLVE_A: if (reply->type == DNS_REQUEST_A) { /* XXX: process only one record */ - cb->addr->data.normal.addr = ntohl (INADDR_NONE); + 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 (reply->type == DNS_REQUEST_AAA) { + /* XXX: process only one record */ + 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: @@ -454,11 +573,15 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) case SPF_RESOLVE_EXP: break; case SPF_RESOLVE_EXISTS: - cb->addr->data.normal.addr = ntohl (INADDR_NONE); + 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 @@ -479,7 +602,6 @@ parse_spf_a (struct worker_task *task, const gchar *begin, struct spf_record *re if (!host) { return FALSE; } - rec->dns_requests ++; cb = memory_pool_alloc (task->task_pool, sizeof (struct spf_dns_cb)); cb->rec = rec; @@ -488,7 +610,7 @@ parse_spf_a (struct worker_task *task, const gchar *begin, struct spf_record *re cb->in_include = rec->in_include; if (make_dns_request (task->resolver, task->s, task->task_pool, spf_record_dns_callback, (void *)cb, DNS_REQUEST_A, host)) { task->dns_requests ++; - + rec->requests_inflight ++; return TRUE; } @@ -525,16 +647,16 @@ parse_spf_mx (struct worker_task *task, const gchar *begin, struct spf_record *r 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; - addr->data.normal.addr = 0; + 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, DNS_REQUEST_MX, host)) { task->dns_requests ++; + rec->requests_inflight ++; return TRUE; } @@ -546,14 +668,14 @@ static gboolean parse_spf_all (struct worker_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.addr = 0; addr->data.normal.mask = 32; } else { - addr->data.normal.addr = 0; addr->data.normal.mask = 0; + addr->data.normal.addr_any = TRUE; } return TRUE; @@ -568,6 +690,17 @@ parse_spf_ip4 (struct worker_task *task, const gchar *begin, struct spf_record * return parse_spf_ipmask (begin, addr); } +#ifdef HAVE_INET_PTON +static gboolean +parse_spf_ip6 (struct worker_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); +} +#endif + static gboolean parse_spf_include (struct worker_task *task, const gchar *begin, struct spf_record *rec, struct spf_addr *addr) { @@ -592,6 +725,7 @@ parse_spf_include (struct worker_task *task, const gchar *begin, struct spf_reco domain = memory_pool_strdup (task->task_pool, begin); if (make_dns_request (task->resolver, task->s, task->task_pool, spf_record_dns_callback, (void *)cb, DNS_REQUEST_TXT, domain)) { task->dns_requests ++; + rec->requests_inflight ++; return TRUE; } @@ -631,6 +765,7 @@ parse_spf_redirect (struct worker_task *task, const gchar *begin, struct spf_rec domain = memory_pool_strdup (task->task_pool, begin); if (make_dns_request (task->resolver, task->s, task->task_pool, spf_record_dns_callback, (void *)cb, DNS_REQUEST_TXT, domain)) { task->dns_requests ++; + rec->requests_inflight ++; return TRUE; } @@ -662,6 +797,7 @@ parse_spf_exists (struct worker_task *task, const gchar *begin, struct spf_recor if (make_dns_request (task->resolver, task->s, task->task_pool, spf_record_dns_callback, (void *)cb, DNS_REQUEST_A, host)) { task->dns_requests ++; + rec->requests_inflight ++; return TRUE; } @@ -706,7 +842,10 @@ expand_spf_macro (struct worker_task *task, struct spf_record *rec, gchar *begin { gchar *p, *c, *new, *tmp; gint len = 0, slen = 0, state = 0; - gboolean need_expand = FALSE; +#ifdef HAVE_INET_PTON + gchar ip_buf[INET6_ADDRSTRLEN]; +#endif + gboolean need_expand = FALSE; p = begin; /* Calculate length */ @@ -749,7 +888,11 @@ expand_spf_macro (struct worker_task *task, struct spf_record *rec, gchar *begin /* Read macro name */ switch (g_ascii_tolower (*p)) { case 'i': - len += sizeof ("255.255.255.255") - 1; +#ifdef HAVE_INET_PTON + len += sizeof (INET6_ADDRSTRLEN) - 1; +#else + len += sizeof (INET_ADDRSTRLEN) - 1; +#endif break; case 's': len += strlen (rec->sender); @@ -850,9 +993,20 @@ expand_spf_macro (struct worker_task *task, struct spf_record *rec, gchar *begin /* Read macro name */ switch (g_ascii_tolower (*p)) { case 'i': +#ifdef HAVE_INET_PTON + if (task->from_addr.ipv6) { + inet_ntop (AF_INET6, &task->from_addr.d.in6, ip_buf, sizeof (ip_buf)); + } + else { + inet_ntop (AF_INET, &task->from_addr.d.in4, ip_buf, sizeof (ip_buf)); + } + len = strlen (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': @@ -928,7 +1082,7 @@ expand_spf_macro (struct worker_task *task, struct spf_record *rec, gchar *begin (x) = memory_pool_alloc (task->task_pool, sizeof (struct spf_addr)); \ (x)->mech = check_spf_mech (rec->cur_elt, &need_shift); \ (x)->spf_string = memory_pool_strdup (task->task_pool, begin); \ - (x)->data.normal.addr = 0; \ + memset (&(x)->data.normal, 0, sizeof ((x)->data.normal)); \ (x)->data.normal.mask = 32; \ (x)->is_list = FALSE; \ } while (0); @@ -987,11 +1141,17 @@ parse_spf_record (struct worker_task *task, struct spf_record *rec) begin += sizeof (SPF_INCLUDE) - 1; res = parse_spf_include (task, begin, rec, new); } - else if (g_ascii_strncasecmp (begin, SPF_IP6, sizeof (SPF_IP4) - 1) == 0) { + 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 ("bad spf command: %s", begin); @@ -1145,6 +1305,7 @@ spf_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) union rspamd_reply_element *elt; GList *cur; + rec->requests_inflight --; if (reply->code == DNS_RC_NOERROR) { cur = reply->elements; while (cur) { @@ -1153,6 +1314,10 @@ spf_dns_callback (struct rspamd_dns_reply *reply, gpointer arg) cur = g_list_next (cur); } } + + if (rec->requests_inflight == 0) { + rec->callback (rec, rec->task); + } } gchar * @@ -1220,6 +1385,7 @@ resolve_spf (struct worker_task *task, spf_cb_t callback) if (make_dns_request (task->resolver, task->s, task->task_pool, spf_dns_callback, (void *)rec, DNS_REQUEST_TXT, rec->cur_domain)) { task->dns_requests ++; + rec->requests_inflight ++; return TRUE; } } @@ -1249,6 +1415,7 @@ resolve_spf (struct worker_task *task, spf_cb_t callback) rec->sender_domain = rec->cur_domain; if (make_dns_request (task->resolver, task->s, task->task_pool, spf_dns_callback, (void *)rec, DNS_REQUEST_TXT, rec->cur_domain)) { task->dns_requests ++; + rec->requests_inflight ++; return TRUE; } } |