return spf_module_config (cfg);
}
-static void
-spf_plugin_callback (struct spf_record *record, struct worker_task *task)
+static gboolean
+spf_check_element (struct spf_addr *addr, struct worker_task *task)
{
- GList *cur;
- struct spf_addr *addr;
guint32 s, m;
- if (record) {
- cur = g_list_last (record->addrs);
- s = ntohl (task->from_addr.s_addr);
- while (cur) {
- addr = cur->data;
- if (addr != NULL) {
- if (addr->mask == 0) {
- m = 0;
- }
- else {
- m = G_MAXUINT32 << (32 - addr->mask);
- }
- if (addr->addr == 0 && cur->prev != NULL) {
- /*
- * In fact default record should be the last element in a record
- * so ignore such other records
- */
- cur = g_list_previous (cur);
- continue;
- }
- if ((s & m) == (addr->addr & m)) {
- switch (addr->mech) {
- case SPF_FAIL:
- insert_result (task, spf_module_ctx->symbol_fail, 1, g_list_prepend (NULL, addr->spf_string));
- task->messages = g_list_prepend (task->messages, "(SPF): spf fail");
- break;
- case SPF_SOFT_FAIL:
- case SPF_NEUTRAL:
- insert_result (task, spf_module_ctx->symbol_softfail, 1, g_list_prepend (NULL, addr->spf_string));
- task->messages = g_list_prepend (task->messages, "(SPF): spf softfail");
- break;
- default:
- insert_result (task, spf_module_ctx->symbol_allow, 1, g_list_prepend (NULL, addr->spf_string));
- task->messages = g_list_prepend (task->messages, "(SPF): spf allow");
- break;
- }
- /* Stop parsing */
- break;
- }
- }
- cur = g_list_previous (cur);
+ if (addr->data.normal.mask == 0) {
+ m = 0;
+ }
+ else {
+ m = G_MAXUINT32 << (32 - addr->data.normal.mask);
+ }
+ s = ntohl (task->from_addr.s_addr);
+
+ if ((s & m) == (addr->data.normal.addr & m)) {
+ switch (addr->mech) {
+ case SPF_FAIL:
+ insert_result (task, spf_module_ctx->symbol_fail, 1, g_list_prepend (NULL, addr->spf_string));
+ task->messages = g_list_prepend (task->messages, "(SPF): spf fail");
+ break;
+ case SPF_SOFT_FAIL:
+ case SPF_NEUTRAL:
+ insert_result (task, spf_module_ctx->symbol_softfail, 1, g_list_prepend (NULL, addr->spf_string));
+ task->messages = g_list_prepend (task->messages, "(SPF): spf softfail");
+ break;
+ default:
+ insert_result (task, spf_module_ctx->symbol_allow, 1, g_list_prepend (NULL, addr->spf_string));
+ task->messages = g_list_prepend (task->messages, "(SPF): spf allow");
+ break;
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+spf_check_list (GList *list, struct worker_task *task)
+{
+ GList *cur;
+ struct spf_addr *addr;
+
+ cur = list;
+
+ while (cur) {
+ addr = cur->data;
+ if (addr->is_list) {
+ /* Recursive call */
+ addr->data.list = g_list_reverse (addr->data.list);
+ if (spf_check_list (addr->data.list, task)) {
+ return TRUE;
+ }
+ }
+ else {
+ if (spf_check_element (addr, task)) {
+ return TRUE;
+ }
}
+ cur = g_list_next (cur);
+ }
+
+ return FALSE;
+}
+
+static void
+spf_plugin_callback (struct spf_record *record, struct worker_task *task)
+{
+ if (record) {
+ record->addrs = g_list_reverse (record->addrs);
+ spf_check_list (record->addrs, task);
}
if (task->save.saved == 0) {
while (cur) {
addr = cur->data;
- switch (addr->mech) {
- case SPF_FAIL:
- c = '-';
- break;
- case SPF_SOFT_FAIL:
- case SPF_NEUTRAL:
- c = '~';
- break;
- case SPF_PASS:
- c = '+';
- break;
+ if (!addr->is_list) {
+ switch (addr->mech) {
+ case SPF_FAIL:
+ c = '-';
+ break;
+ case SPF_SOFT_FAIL:
+ case SPF_NEUTRAL:
+ c = '~';
+ break;
+ case SPF_PASS:
+ c = '+';
+ break;
+ }
+ ina.s_addr = htonl (addr->data.normal.addr);
+ r += snprintf (logbuf + r, sizeof (logbuf) - r, "%c%s/%d; ", c, inet_ntoa (ina), addr->data.normal.mask);
+ }
+ else {
+ r += snprintf (logbuf + r, sizeof (logbuf) - r, "%s; ", addr->spf_string);
+ dump_spf_record (addr->data.list);
}
- ina.s_addr = htonl (addr->addr);
- r += snprintf (logbuf + r, sizeof (logbuf) - r, "%c%s/%d; ", c, inet_ntoa (ina), addr->mask);
cur = g_list_next (cur);
}
msg_info ("spf record: %s", logbuf);
}
+/* Find position of address inside addrs list */
+static GList *
+spf_addr_find (GList *addrs, gpointer to_find)
+{
+ struct spf_addr *addr;
+ GList *cur, *res = NULL;
+
+ cur = addrs;
+ while (cur) {
+ addr = cur->data;
+ if (addr->is_list) {
+ if ((res = spf_addr_find (addr->data.list, to_find)) != NULL) {
+ return res;
+ }
+ }
+ else {
+ if (cur->data == to_find) {
+ return cur;
+ }
+ }
+ cur = g_list_next (cur);
+ }
+
+ return res;
+}
+
/*
* Destructor for spf record
*/
spf_record_destructor (gpointer r)
{
struct spf_record *rec = r;
+ GList *cur;
+ struct spf_addr *addr;
if (rec->addrs) {
+ cur = rec->addrs;
+ while (cur) {
+ addr = cur->data;
+ if (addr->is_list && addr->data.list != NULL) {
+ g_list_free (addr->data.list);
+ }
+ cur = g_list_next (cur);
+ }
g_list_free (rec->addrs);
}
}
if (!inet_aton (ip_buf, &in)) {
return FALSE;
}
- addr->addr = ntohl (in.s_addr);
+ addr->data.normal.addr = ntohl (in.s_addr);
if (state == 2) {
/* Also parse mask */
- addr->mask = (mask_buf[0] - '0') * 10 + mask_buf[1] - '0';
- if (addr->mask > 32) {
+ 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->mask = 32;
+ addr->data.normal.mask = 32;
}
return TRUE;
if (p != NULL) {
/* Extract mask */
rspamd_strlcpy (mask_buf, p + 1, sizeof (mask_buf));
- addr->mask = mask_buf[0] * 10 + mask_buf[1];
- if (addr->mask > 32) {
+ addr->data.normal.mask = mask_buf[0] * 10 + mask_buf[1];
+ if (addr->data.normal.mask > 32) {
return FALSE;
}
if (host == NULL) {
}
}
else {
- addr->mask = 32;
+ addr->data.normal.mask = 32;
if (host == NULL) {
host = memory_pool_strdup (task->task_pool, begin);
}
struct spf_dns_cb *cb = arg;
gchar *begin;
union rspamd_reply_element *elt_data;
- GList *tmp = NULL, *tmp1,
- *elt, *last;
+ GList *tmp = NULL, *elt;
struct worker_task *task;
struct spf_addr *new_addr;
}
}
else if (reply->type == DNS_REQUEST_A) {
- if (cb->addr->addr == 0) {
- cb->addr->addr = ntohl (elt_data->a.addr[0].s_addr);
- cb->addr->mask = 32;
+ if (cb->addr->data.normal.addr == 0) {
+ cb->addr->data.normal.addr = ntohl (elt_data->a.addr[0].s_addr);
+ cb->addr->data.normal.mask = 32;
}
else {
/* Insert one more address */
- tmp = g_list_find (cb->rec->addrs, cb->addr);
+ 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->addr = ntohl (elt_data->a.addr[0].s_addr);
+ new_addr->data.normal.addr = ntohl (elt_data->a.addr[0].s_addr);
cb->rec->addrs = g_list_insert_before (cb->rec->addrs, tmp, new_addr);
}
else {
case SPF_RESOLVE_A:
if (reply->type == DNS_REQUEST_A) {
/* XXX: process only one record */
- cb->addr->addr = ntohl (elt_data->a.addr[0].s_addr);
- cb->addr->mask = 32;
+ cb->addr->data.normal.addr = ntohl (elt_data->a.addr[0].s_addr);
+ cb->addr->data.normal.mask = 32;
}
break;
case SPF_RESOLVE_PTR:
msg_info ("before include");
dump_spf_record (cb->rec->addrs);
#endif
- if (cb->rec->addrs) {
- tmp = cb->rec->addrs;
- cb->rec->addrs = NULL;
- }
+ tmp = cb->rec->addrs;
+ cb->rec->addrs = NULL;
cb->rec->in_include = TRUE;
start_spf_parse (cb->rec, begin);
cb->rec->in_include = FALSE;
- if (tmp && cb->rec->addrs) {
- tmp1 = g_list_find (tmp, cb->addr);
- if (tmp1) {
- /* Insert new list in place of include element */
- cb->rec->addrs->prev = tmp1->prev;
- last = g_list_last (cb->rec->addrs);
- last->next = tmp1;
- tmp1->prev = last;
- /* If tmp1 is at the beginning of the list, shift list */
- if (tmp == tmp1) {
- tmp = cb->rec->addrs;
- }
- else {
- tmp1->prev->next = cb->rec->addrs;
- }
- /* Remove self element */
- tmp = g_list_delete_link (tmp, tmp1);
#ifdef SPF_DEBUG
- msg_info ("after include");
- dump_spf_record (cb->rec->addrs);
+ msg_info ("after include");
+ dump_spf_record (cb->rec->addrs);
#endif
- }
- }
- if (tmp) {
- cb->rec->addrs = tmp;
- }
+ /* Insert new list */
+ cb->addr->is_list = TRUE;
+ cb->addr->data.list = cb->rec->addrs;
+ cb->rec->addrs = tmp;
}
break;
case SPF_RESOLVE_EXP:
case SPF_RESOLVE_EXISTS:
if (reply->type == DNS_REQUEST_A) {
/* If specified address resolves, we can accept connection from every IP */
- cb->addr->addr = ntohl (INADDR_ANY);
- cb->addr->mask = 0;
+ cb->addr->data.normal.addr = ntohl (INADDR_ANY);
+ cb->addr->data.normal.mask = 0;
}
break;
}
case SPF_RESOLVE_MX:
if (reply->type == DNS_REQUEST_MX) {
msg_info ("cannot find MX record for %s", cb->rec->cur_domain);
- cb->addr->addr = ntohl (INADDR_NONE);
- cb->addr->mask = 32;
+ cb->addr->data.normal.addr = ntohl (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->addr = ntohl (INADDR_NONE);
- cb->addr->mask = 32;
+ cb->addr->data.normal.addr = ntohl (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->addr = ntohl (INADDR_NONE);
- cb->addr->mask = 32;
+ cb->addr->data.normal.addr = ntohl (INADDR_NONE);
+ cb->addr->data.normal.mask = 32;
}
break;
case SPF_RESOLVE_PTR:
case SPF_RESOLVE_EXP:
break;
case SPF_RESOLVE_EXISTS:
- cb->addr->addr = ntohl (INADDR_NONE);
- cb->addr->mask = 32;
+ cb->addr->data.normal.addr = ntohl (INADDR_NONE);
+ cb->addr->data.normal.mask = 32;
break;
}
}
cb = memory_pool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
cb->rec = rec;
cb->addr = addr;
- addr->addr = 0;
+ addr->data.normal.addr = 0;
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)) {
/* All is 0/0 */
if (rec->in_include) {
/* Ignore all record in include */
- addr->addr = 0;
- addr->mask = 32;
+ addr->data.normal.addr = 0;
+ addr->data.normal.mask = 32;
}
else {
- addr->addr = 0;
- addr->mask = 0;
+ addr->data.normal.addr = 0;
+ addr->data.normal.mask = 0;
}
return TRUE;
cb->addr = addr;
cb->cur_action = SPF_RESOLVE_INCLUDE;
cb->in_include = rec->in_include;
+ addr->is_list = TRUE;
+ addr->data.list = NULL;
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->save.saved++;
begin ++;
rec->dns_requests ++;
- addr->mask = 32;
+ addr->data.normal.mask = 32;
cb = memory_pool_alloc (task->task_pool, sizeof (struct spf_dns_cb));
cb->rec = rec;
cb->addr = addr;
(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)->addr = 0; \
- (x)->mask = 32; \
+ (x)->data.normal.addr = 0; \
+ (x)->data.normal.mask = 32; \
+ (x)->is_list = FALSE; \
} while (0);
/* Read current element and try to parse record */
rec->cur_domain = memory_pool_strdup (task->task_pool, domains->data);
g_list_free (domains);
- if ((domain = strchr (rec->cur_domain, '@')) == NULL) {
+ if ((domain = strrchr (rec->cur_domain, '@')) == NULL) {
return FALSE;
}
rec->sender = memory_pool_strdup (task->task_pool, rec->cur_domain);