]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
Unify hostmask parsing.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Wed, 18 Mar 2015 15:14:45 +0000 (15:14 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Wed, 18 Mar 2015 15:14:45 +0000 (15:14 +0000)
src/libserver/spf.c

index a93780123c20194010ad86901cb63ae0006ef70c..619f94fc40bd898c014fbe4925ef8f0fe361ae7d 100644 (file)
@@ -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,