aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2015-03-18 15:14:45 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2015-03-18 15:14:45 +0000
commit807bc3e2a88a562b7276ac8a15a77ddd3dd373c2 (patch)
tree4150d9e41f57df4e0a6d07bc2135d1af754e6f41 /src
parentd6cbb11077073c8796ce34afce74daf7ce6f4bb2 (diff)
downloadrspamd-807bc3e2a88a562b7276ac8a15a77ddd3dd373c2.tar.gz
rspamd-807bc3e2a88a562b7276ac8a15a77ddd3dd373c2.zip
Unify hostmask parsing.
Diffstat (limited to 'src')
-rw-r--r--src/libserver/spf.c164
1 files 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,