From 4d4668a0d4022583208d20bac9b8a0bede6f073d Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Fri, 20 Apr 2012 20:02:28 +0400 Subject: [PATCH] * Fix spf plugin that was broken in 0.4.7 * Add partial ipv6 support for some rspamd modules. --- CMakeLists.txt | 1 + config.h.in | 2 + src/dns.c | 50 ++++++++- src/dns.h | 8 +- src/logger.h | 7 +- src/lua/lua_task.c | 23 ++++ src/main.h | 11 ++ src/plugins/fuzzy_check.c | 11 +- src/plugins/spf.c | 72 +++++++++++- src/protocol.c | 17 +++ src/spf.c | 229 ++++++++++++++++++++++++++++++++------ src/spf.h | 12 +- src/symbols_cache.c | 15 ++- src/view.c | 19 ++++ src/worker.c | 1 - 15 files changed, 432 insertions(+), 46 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 565cabe04..fc15f86c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -745,6 +745,7 @@ CHECK_FUNCTION_EXISTS(tanhl HAVE_TANHL) CHECK_FUNCTION_EXISTS(sendfile HAVE_SENDFILE) CHECK_FUNCTION_EXISTS(mkstemp HAVE_MKSTEMP) CHECK_FUNCTION_EXISTS(setitimer HAVE_SETITIMER) +CHECK_FUNCTION_EXISTS(inet_pton HAVE_INET_PTON) CHECK_FUNCTION_EXISTS(clock_gettime HAVE_CLOCK_GETTIME) # diff --git a/config.h.in b/config.h.in index 8ad00557d..186021446 100644 --- a/config.h.in +++ b/config.h.in @@ -153,6 +153,8 @@ #cmakedefine HAVE_SETITIMER 1 +#cmakedefine HAVE_INET_PTON 1 + #define WITHOUT_PERL 1 #cmakedefine WITH_LUA 1 diff --git a/src/dns.c b/src/dns.c index fe087da08..f39d47843 100644 --- a/src/dns.c +++ b/src/dns.c @@ -663,6 +663,24 @@ make_a_req (struct rspamd_dns_request *req, const gchar *name) req->requested_name = name; } +#ifdef HAVE_INET_PTON +static void +make_aaa_req (struct rspamd_dns_request *req, const gchar *name) +{ + guint16 *p; + + allocate_packet (req, strlen (name)); + make_dns_header (req); + format_dns_name (req, name, 0); + p = (guint16 *)(req->packet + req->pos); + *p++ = htons (DNS_T_AAAA); + *p = htons (DNS_C_IN); + req->pos += sizeof (guint16) * 2; + req->type = DNS_REQUEST_AAA; + req->requested_name = name; +} +#endif + static void make_txt_req (struct rspamd_dns_request *req, const gchar *name) { @@ -1001,6 +1019,24 @@ dns_parse_rr (guint8 *in, union rspamd_reply_element *elt, guint8 **pos, struct } } break; +#ifdef HAVE_INET_PTON + case DNS_T_AAAA: + if (rep->request->type != DNS_REQUEST_AAA) { + p += datalen; + } + else { + if (datalen == sizeof (struct in6_addr) && datalen <= *remain) { + memcpy (&elt->aaa.addr, p, sizeof (struct in6_addr)); + p += datalen; + parsed = TRUE; + } + else { + msg_info ("corrupted AAAA record"); + return -1; + } + } + break; +#endif case DNS_T_PTR: if (rep->request->type != DNS_REQUEST_PTR) { p += datalen; @@ -1387,6 +1423,15 @@ make_dns_request (struct rspamd_dns_resolver *resolver, name = va_arg (args, const gchar *); make_a_req (req, name); break; + case DNS_REQUEST_AAA: +#ifdef HAVE_INET_PTON + name = va_arg (args, const gchar *); + make_aaa_req (req, name); + break; +#else + msg_err ("your system has no ipv6 support, cannot make aaa request"); + break; +#endif case DNS_REQUEST_TXT: name = va_arg (args, const gchar *); make_txt_req (req, name); @@ -1649,13 +1694,14 @@ dns_strerror (enum dns_rcode rcode) return dns_rcodes[rcode]; } -static gchar dns_types[6][16] = { +static gchar dns_types[7][16] = { [DNS_REQUEST_A] = "A request", [DNS_REQUEST_PTR] = "PTR request", [DNS_REQUEST_MX] = "MX request", [DNS_REQUEST_TXT] = "TXT request", [DNS_REQUEST_SRV] = "SRV request", - [DNS_REQUEST_SPF] = "SPF request" + [DNS_REQUEST_SPF] = "SPF request", + [DNS_REQUEST_AAA] = "AAA request" }; const gchar * diff --git a/src/dns.h b/src/dns.h index f174f5026..8914ae5b1 100644 --- a/src/dns.h +++ b/src/dns.h @@ -68,7 +68,8 @@ enum rspamd_request_type { DNS_REQUEST_MX, DNS_REQUEST_TXT, DNS_REQUEST_SRV, - DNS_REQUEST_SPF + DNS_REQUEST_SPF, + DNS_REQUEST_AAA }; struct rspamd_dns_request { @@ -100,6 +101,11 @@ union rspamd_reply_element { struct in_addr addr[MAX_ADDRS]; guint16 addrcount; } a; +#ifdef HAVE_INET_PTON + struct { + struct in6_addr addr; + } aaa; +#endif struct { gchar *name; } ptr; diff --git a/src/logger.h b/src/logger.h index 6d0cac4b8..ec7a23af5 100644 --- a/src/logger.h +++ b/src/logger.h @@ -84,8 +84,11 @@ void rspamd_log_nodebug (rspamd_logger_t *logger); #define msg_warn(...) rspamd_common_log_function(rspamd_main->logger, G_LOG_LEVEL_WARNING, __FUNCTION__, __VA_ARGS__) #define msg_info(...) rspamd_common_log_function(rspamd_main->logger, G_LOG_LEVEL_INFO, __FUNCTION__, __VA_ARGS__) #define msg_debug(...) rspamd_conditional_debug(rspamd_main->logger, -1, __FUNCTION__, __VA_ARGS__) -#define debug_task(...) rspamd_conditional_debug(rspamd_main->logger, task->from_addr.s_addr, __FUNCTION__, __VA_ARGS__) - +#ifdef HAVE_INET_PTON +# define debug_task(...) rspamd_conditional_debug(rspamd_main->logger, task->from_addr.d.in4.s_addr, __FUNCTION__, __VA_ARGS__) +#else +# define debug_task(...) rspamd_conditional_debug(rspamd_main->logger, task->from_addr.s_addr, __FUNCTION__, __VA_ARGS__) +#endif #else #define msg_err(...) rspamd_fprintf(stderr, __VA_ARGS__) #define msg_warn(...) rspamd_fprintf(stderr, __VA_ARGS__) diff --git a/src/lua/lua_task.c b/src/lua/lua_task.c index 7ca1c58df..5695b6f42 100644 --- a/src/lua/lua_task.c +++ b/src/lua/lua_task.c @@ -981,12 +981,26 @@ static gint lua_task_get_from_ip (lua_State *L) { struct worker_task *task = lua_check_task (L); +#ifdef HAVE_INET_PTON + gchar ipbuf[INET6_ADDRSTRLEN]; +#endif if (task) { +#ifdef HAVE_INET_PTON + if (task->from_addr.ipv6) { + inet_ntop (AF_INET6, &task->from_addr.d.in6, ipbuf, sizeof (ipbuf)); + } + else { + inet_ntop (AF_INET, &task->from_addr.d.in4, ipbuf, sizeof (ipbuf)); + } + lua_pushstring (L, ipbuf); + return 1; +#else if (task->from_addr.s_addr != INADDR_NONE && task->from_addr.s_addr != INADDR_ANY) { lua_pushstring (L, inet_ntoa (task->from_addr)); return 1; } +#endif } lua_pushnil (L); @@ -999,10 +1013,19 @@ lua_task_get_from_ip_num (lua_State *L) struct worker_task *task = lua_check_task (L); if (task) { +#ifdef HAVE_INET_PTON + if (!task->from_addr.ipv6 && task->from_addr.d.in4.s_addr != INADDR_NONE) { + lua_pushinteger (L, ntohl (task->from_addr.d.in4.s_addr)); + return 1; + } + /* TODO: do something with ipv6 numeric representation */ +#else if (task->from_addr.s_addr != INADDR_NONE && task->from_addr.s_addr != INADDR_ANY) { lua_pushinteger (L, ntohl (task->from_addr.s_addr)); return 1; } + +#endif } lua_pushnil (L); diff --git a/src/main.h b/src/main.h index 22b2f7584..1d8fab134 100644 --- a/src/main.h +++ b/src/main.h @@ -203,7 +203,18 @@ struct worker_task { const gchar *message_id; /**< message id */ GList *rcpt; /**< recipients list */ guint nrcpt; /**< number of recipients */ +#ifdef HAVE_INET_PTON + struct { + union { + struct in_addr in4; + struct in6_addr in6; + } d; + gboolean ipv6; + gboolean has_addr; + } from_addr; +#else struct in_addr from_addr; /**< client addr in numeric form */ +#endif struct in_addr client_addr; /**< client addr in numeric form */ gchar *deliver_to; /**< address to deliver */ gchar *user; /**< user to deliver */ diff --git a/src/plugins/fuzzy_check.c b/src/plugins/fuzzy_check.c index 4f66a5731..7d400a0c4 100644 --- a/src/plugins/fuzzy_check.c +++ b/src/plugins/fuzzy_check.c @@ -658,6 +658,15 @@ fuzzy_symbol_callback (struct worker_task *task, void *unused) /* Check whitelist */ +#ifdef HAVE_INET_PTON + if (fuzzy_module_ctx->whitelist && !task->from_addr.ipv6 && task->from_addr.d.in4.s_addr != INADDR_NONE) { + if (radix32tree_find (fuzzy_module_ctx->whitelist, ntohl ((guint32) task->from_addr.d.in4.s_addr)) != RADIX_NO_VALUE) { + msg_info ("<%s>, address %s is whitelisted, skip fuzzy check", + task->message_id, inet_ntoa (task->from_addr.d.in4)); + return; + } + } +#else if (fuzzy_module_ctx->whitelist && task->from_addr.s_addr != 0) { if (radix32tree_find (fuzzy_module_ctx->whitelist, ntohl ((guint32) task->from_addr.s_addr)) != RADIX_NO_VALUE) { msg_info ("<%s>, address %s is whitelisted, skip fuzzy check", @@ -665,7 +674,7 @@ fuzzy_symbol_callback (struct worker_task *task, void *unused) return; } } - +#endif cur = task->text_parts; while (cur) { diff --git a/src/plugins/spf.c b/src/plugins/spf.c index 093c57703..26b3c1903 100644 --- a/src/plugins/spf.c +++ b/src/plugins/spf.c @@ -166,17 +166,75 @@ spf_module_reconfig (struct config_file *cfg) static gboolean spf_check_element (struct spf_addr *addr, struct worker_task *task) { + gboolean res = FALSE; +#ifdef HAVE_INET_PTON + guint8 *s, *d, t; + guint nbits, addrlen; + struct in_addr in4s, in4d; + struct in6_addr in6s, in6d; + + /* Basic comparing algorithm */ + if (addr->data.normal.ipv6 == task->from_addr.ipv6) { + if (addr->data.normal.ipv6) { + addrlen = sizeof (struct in6_addr); + memcpy (&in6s, &addr->data.normal.d.in6, sizeof (struct in6_addr)); + memcpy (&in6d, &task->from_addr.d.in6, sizeof (struct in6_addr)); + s = (guint8 *)&in6s; + d = (guint8 *)&in6d; + } + else { + addrlen = sizeof (struct in_addr); + memcpy (&in4s, &addr->data.normal.d.in4, sizeof (struct in_addr)); + memcpy (&in4d, &task->from_addr.d.in4, sizeof (struct in_addr)); + s = (guint8 *)&in4s; + d = (guint8 *)&in4d; + } + /* Move pointers to the less significant byte */ + t = 0x1; + s += addrlen - 1; + d += addrlen - 1; + /* TODO: improve this cycle by masking by words */ + for (nbits = 0; nbits < addrlen * CHAR_BIT - addr->data.normal.mask; nbits ++) { + /* Skip bits from the beginning as we know that data is in network byte order */ + if (nbits != 0 && nbits % 8 == 0) { + /* Move pointer to the next byte */ + s --; + d --; + t = 0x1; + } + *s |= t; + *d |= t; + t <<= 1; + } + if (addr->data.normal.ipv6) { + res = memcmp (&in6d, &in6s, sizeof (struct in6_addr)) == 0; + } + else { + res = memcmp (&in4d, &in4s, sizeof (struct in_addr)) == 0; + } + } + else { + if (addr->data.normal.addr_any) { + res = TRUE; + } + else { + res = FALSE; + } + } +#else guint32 s, m; if (addr->data.normal.mask == 0) { m = 0; } else { - m = G_MAXUINT32 << (32 - addr->data.normal.mask); + m = htonl (G_MAXUINT32 << (32 - addr->data.normal.mask)); } - s = ntohl (task->from_addr.s_addr); + s = task->from_addr.s_addr; + res = (s & m) == (addr->data.normal.d.in4.s_addr & m); +#endif - if ((s & m) == (addr->data.normal.addr & m)) { + if (res) { switch (addr->mech) { case SPF_FAIL: insert_result (task, spf_module_ctx->symbol_fail, 1, g_list_prepend (NULL, addr->spf_string)); @@ -248,9 +306,13 @@ spf_symbol_callback (struct worker_task *task, void *unused) gchar *domain; GList *l; +#ifdef HAVE_INET_PTON + if (task->from_addr.has_addr) { + if (TRUE) { +#else if (task->from_addr.s_addr != INADDR_NONE && task->from_addr.s_addr != INADDR_ANY) { if (radix32tree_find (spf_module_ctx->whitelist_ip, ntohl (task->from_addr.s_addr)) == RADIX_NO_VALUE) { - +#endif domain = get_spf_domain (task); if (domain) { if ((l = rspamd_lru_hash_lookup (spf_module_ctx->spf_hash, domain, task->tv.tv_sec)) != NULL) { @@ -261,9 +323,11 @@ spf_symbol_callback (struct worker_task *task, void *unused) } } } +#ifndef HAVE_INET_PTON else { msg_info ("ip %s is whitelisted for spf checks", inet_ntoa (task->from_addr)); } +#endif } } diff --git a/src/protocol.c b/src/protocol.c index 40bd01e0c..fc03acacb 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -537,10 +537,27 @@ parse_header (struct worker_task *task, f_str_t * line) /* ip_addr */ if (g_ascii_strncasecmp (headern, IP_ADDR_HEADER, sizeof (IP_ADDR_HEADER) - 1) == 0) { tmp = memory_pool_fstrdup (task->task_pool, line); +#ifdef HAVE_INET_PTON + if (inet_pton (AF_INET, tmp, &task->from_addr.d.in4) != 1) { + /* Try ipv6 */ + if (inet_pton (AF_INET6, tmp, &task->from_addr.d.in6) == 1) { + task->from_addr.ipv6 = TRUE; + } + else { + msg_info ("bad ip header: '%s'", tmp); + return FALSE; + } + } + else { + task->from_addr.ipv6 = FALSE; + } + task->from_addr.has_addr = TRUE; +#else if (!inet_aton (tmp, &task->from_addr)) { msg_info ("bad ip header: '%s'", tmp); return FALSE; } +#endif debug_task ("read IP header, value: %s", tmp); } else { diff --git a/src/spf.c b/src/spf.c index 137ca0f5e..738b56ee8 100644 --- a/src/spf.c +++ b/src/spf.c @@ -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; } } diff --git a/src/spf.h b/src/spf.h index 90a21c0b3..a9631e733 100644 --- a/src/spf.h +++ b/src/spf.h @@ -19,6 +19,7 @@ typedef enum spf_action_e { SPF_RESOLVE_MX, SPF_RESOLVE_A, SPF_RESOLVE_PTR, + SPF_RESOLVE_AAA, SPF_RESOLVE_REDIRECT, SPF_RESOLVE_INCLUDE, SPF_RESOLVE_EXISTS, @@ -28,8 +29,16 @@ typedef enum spf_action_e { struct spf_addr { union { struct { - guint32 addr; + union { + struct in_addr in4; +#ifdef HAVE_INET_PTON + struct in6_addr in6; +#endif + } d; guint32 mask; + gboolean ipv6; + gboolean parsed; + gboolean addr_any; } normal; GList *list; } data; @@ -45,6 +54,7 @@ struct spf_record { gint elt_num; gint nested; gint dns_requests; + gint requests_inflight; GList *addrs; gchar *cur_domain; diff --git a/src/symbols_cache.c b/src/symbols_cache.c index 49934b6d2..ed8fa73f6 100644 --- a/src/symbols_cache.c +++ b/src/symbols_cache.c @@ -634,9 +634,12 @@ init_symbols_cache (memory_pool_t * pool, struct symbols_cache *cache, struct co static GList * check_dynamic_item (struct worker_task *task, struct symbols_cache *cache) { +#ifdef HAVE_INET_PTON + /* TODO: radix doesn't support ipv6 addrs */ + return NULL; +#else GList *res = NULL; uintptr_t r; - if (cache->dynamic_map != NULL && task->from_addr.s_addr != INADDR_NONE) { if ((r = radix32tree_find (cache->dynamic_map, ntohl (task->from_addr.s_addr))) != RADIX_NO_VALUE) { res = (GList *)((gpointer)r); @@ -646,13 +649,18 @@ check_dynamic_item (struct worker_task *task, struct symbols_cache *cache) return NULL; } } - return res; +#endif } static gboolean check_negative_dynamic_item (struct worker_task *task, struct symbols_cache *cache, struct cache_item *item) { + +#ifdef HAVE_INET_PTON + /* TODO: radix doesn't support ipv6 addrs */ + return FALSE; +#else GList *res = NULL; uintptr_t r; @@ -667,8 +675,9 @@ check_negative_dynamic_item (struct worker_task *task, struct symbols_cache *cac } } } - return FALSE; +#endif + } static gboolean diff --git a/src/view.c b/src/view.c index 7b70a07ca..6c67fce95 100644 --- a/src/view.c +++ b/src/view.c @@ -135,6 +135,24 @@ find_view_by_ip (GList * views, struct worker_task *task) GList *cur; struct rspamd_view *v; +#ifdef HAVE_INET_PTON + + if (task->from_addr.ipv6 || task->from_addr.d.in4.s_addr == INADDR_NONE) { + return NULL; + } + + cur = views; + while (cur) { + v = cur->data; + if (radix32tree_find (v->ip_tree, ntohl (task->from_addr.d.in4.s_addr)) != RADIX_NO_VALUE) { + return v; + } + cur = g_list_next (cur); + } + + return NULL; +#else + if (task->from_addr.s_addr == INADDR_NONE) { return NULL; } @@ -149,6 +167,7 @@ find_view_by_ip (GList * views, struct worker_task *task) } return NULL; +#endif } static struct rspamd_view * diff --git a/src/worker.c b/src/worker.c index eecaf45dc..250bf7d31 100644 --- a/src/worker.c +++ b/src/worker.c @@ -205,7 +205,6 @@ construct_task (struct rspamd_worker *worker) new_task->worker = worker; new_task->state = READ_COMMAND; new_task->cfg = worker->srv->cfg; - new_task->from_addr.s_addr = INADDR_NONE; new_task->view_checked = FALSE; #ifdef HAVE_CLOCK_GETTIME # ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID -- 2.39.5