From 807bc3e2a88a562b7276ac8a15a77ddd3dd373c2 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Wed, 18 Mar 2015 15:14:45 +0000 Subject: [PATCH] Unify hostmask parsing. --- src/libserver/spf.c | 164 +++++++++++++++++++++++++++++++++----------- 1 file changed, 124 insertions(+), 40 deletions(-) diff --git a/src/libserver/spf.c b/src/libserver/spf.c index a93780123..619f94fc4 100644 --- a/src/libserver/spf.c +++ b/src/libserver/spf.c @@ -443,46 +443,6 @@ parse_spf_ipmask (const gchar *begin, } -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_process_addr (struct rdns_reply_entry *elt, struct spf_dns_cb *cb, struct rspamd_task *task) @@ -799,6 +759,130 @@ spf_record_dns_callback (struct rdns_reply *reply, gpointer arg) } } +/* + * The syntax defined by the following BNF: + * [ ":" domain-spec ] [ dual-cidr-length ] + * ip4-cidr-length = "/" 1*DIGIT + * ip6-cidr-length = "/" 1*DIGIT + * dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ] + */ +static const gchar * +parse_spf_domain_mask (struct spf_record *rec, struct spf_addr *addr, + gboolean allow_mask) +{ + struct spf_resolved_element *resolved; + struct rspamd_task *task = rec->task; + enum { + parse_spf_elt = 0, + parse_semicolon, + parse_domain, + parse_slash, + parse_ipv4_mask, + parse_second_slash, + parse_ipv6_mask + } state = 0; + const gchar *p = addr->spf_string, *host, *c; + gchar *hostbuf; + gchar t; + guint16 cur_mask = 0; + + resolved = &g_array_index (rec->resolved, struct spf_resolved_element, + rec->resolved->len - 1); + host = resolved->cur_domain; + + while (*p) { + t = *p; + + switch (state) { + case parse_spf_elt: + if (t == ':') { + state = parse_semicolon; + } + else if (t == '/') { + /* No domain but mask */ + state = parse_slash; + } + p ++; + break; + case parse_semicolon: + if (t == '/') { + /* Empty domain, technically an error */ + state = parse_slash; + } + c = p; + state = parse_domain; + break; + case parse_domain: + if (t == '/') { + hostbuf = rspamd_mempool_alloc (task->task_pool, p - c + 1); + rspamd_strlcpy (hostbuf, c, p - c + 1); + host = hostbuf; + state = parse_slash; + } + p ++; + break; + case parse_slash: + c = p; + state = parse_ipv4_mask; + cur_mask = 0; + break; + case parse_ipv4_mask: + if (g_ascii_isdigit (t)) { + /* Ignore errors here */ + cur_mask = cur_mask * 10 + (t - '0'); + } + else if (t == '/') { + if (cur_mask <= 32) { + addr->m.dual.mask_v4 = cur_mask; + } + else { + msg_info ("bad ipv4 mask: %d", cur_mask); + } + state = parse_second_slash; + } + p ++; + break; + case parse_second_slash: + c = p; + state = parse_ipv6_mask; + cur_mask = 0; + break; + case parse_ipv6_mask: + if (g_ascii_isdigit (t)) { + /* Ignore errors here */ + cur_mask = cur_mask * 10 + (t - '0'); + } + p ++; + break; + } + } + + /* Process end states */ + if (state == parse_ipv4_mask) { + if (cur_mask <= 32) { + addr->m.dual.mask_v4 = cur_mask; + } + else { + msg_info ("bad ipv4 mask: %d", cur_mask); + } + } + else if (state == parse_ipv6_mask) { + if (cur_mask <= 128) { + addr->m.dual.mask_v6 = cur_mask; + } + else { + msg_info ("bad ipv6 mask: %d", cur_mask); + } + } + else if (state == parse_domain && p - c > 0) { + hostbuf = rspamd_mempool_alloc (task->task_pool, p - c + 1); + rspamd_strlcpy (hostbuf, c, p - c + 1); + host = hostbuf; + } + + return host; +} + static gboolean parse_spf_a (struct rspamd_task *task, const gchar *begin, -- 2.39.5