Browse Source

Unify hostmask parsing.

tags/0.9.0
Vsevolod Stakhov 9 years ago
parent
commit
807bc3e2a8
1 changed files with 124 additions and 40 deletions
  1. 124
    40
      src/libserver/spf.c

+ 124
- 40
src/libserver/spf.c View 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,

Loading…
Cancel
Save