]> source.dussan.org Git - rspamd.git/commitdiff
Rework resolver library.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Mon, 27 Jan 2014 16:12:27 +0000 (16:12 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 18 Feb 2014 15:07:59 +0000 (15:07 +0000)
Conflicts:

src/dns.c

CMakeLists.txt
src/dkim.c
src/dns.c
src/dns.h
src/dns_private.h [new file with mode: 0644]
src/lua/lua_dns.c
src/lua/lua_http.c
src/lua/lua_redis.c
src/plugins/surbl.c
src/smtp_proxy.c
src/spf.c

index d962997ba8bb0f885cd5dd550a23743773ccc632..e85e1febbaf3ecd6cd470579b02ee411c012a45b 100644 (file)
@@ -885,7 +885,6 @@ SET(RSPAMDSRC       src/modules.c
                                src/lua_worker.c
                                src/main.c
                                src/map.c
-                               src/smtp.c
                                src/smtp_proxy.c
                                src/webui.c
                                src/worker.c)
@@ -898,7 +897,7 @@ SET(PLUGINSSRC      src/plugins/surbl.c
                                src/plugins/dkim_check.c)
                                
 SET(MODULES_LIST surbl regexp chartable fuzzy_check spf dkim)
-SET(WORKERS_LIST normal controller smtp smtp_proxy fuzzy lua webui)
+SET(WORKERS_LIST normal controller smtp_proxy fuzzy lua webui)
 
 AddModules(MODULES_LIST WORKERS_LIST)
 
index bd57cd2321c563c0063c2ebf481cf934c0d0b958..72a14c1798467a07ba6eafb364858f9544d48343 100644 (file)
@@ -754,8 +754,7 @@ rspamd_dkim_dns_cb (struct rspamd_dns_reply *reply, gpointer arg)
        struct rspamd_dkim_key_cbdata                           *cbdata = arg;
        rspamd_dkim_key_t                                                       *key = NULL;
        GError                                                                          *err = NULL;
-       GList                                                                           *cur;
-       union rspamd_reply_element                                      *elt;
+       struct rspamd_reply_entry                                       *elt;
        gsize                                                                            keylen = 0;
 
        if (reply->code != DNS_RC_NOERROR) {
@@ -764,14 +763,13 @@ rspamd_dkim_dns_cb (struct rspamd_dns_reply *reply, gpointer arg)
                cbdata->handler (NULL, 0, cbdata->ctx, cbdata->ud, err);
        }
        else {
-               cur = reply->elements;
-               while (cur) {
-                       elt = cur->data;
-                       key = rspamd_dkim_parse_key (elt->txt.data, &keylen, &err);
-                       if (key) {
-                               break;
+               LL_FOREACH (reply->entries, elt) {
+                       if (elt->type == DNS_REQUEST_TXT) {
+                               key = rspamd_dkim_parse_key (elt->content.txt.data, &keylen, &err);
+                               if (key) {
+                                       break;
+                               }
                        }
-                       cur = g_list_next (cur);
                }
                if (key != NULL && err != NULL) {
                        /* Free error as it is insignificant */
index 5ec372f17ef6b2dd91cd32b08d20cd08198fde73..e5e69b71bd0798fae67c4093a9989c0b4f948ff0 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
 
 #include "config.h"
 #include "dns.h"
+#include "dns_private.h"
 #include "main.h"
 #include "utlist.h"
-#include "chacha_private.h"
+#include "uthash.h"
+
 #ifdef HAVE_OPENSSL
 #include <openssl/rand.h>
 #endif
 
-/* Upstream timeouts */
-#define DEFAULT_UPSTREAM_ERROR_TIME 10
-#define DEFAULT_UPSTREAM_DEAD_TIME 300
-#define DEFAULT_UPSTREAM_MAXERRORS 10
-
-static const unsigned base         = 36;
-static const unsigned t_min        = 1;
-static const unsigned t_max        = 26;
-static const unsigned skew         = 38;
-static const unsigned damp         = 700;
-static const unsigned initial_n    = 128;
-static const unsigned initial_bias = 72;
-
-static const gint dns_port = 53;
-
-#define UDP_PACKET_SIZE 4096
-
-#define DNS_COMPRESSION_BITS 0xC0
-
 static void dns_retransmit_handler (gint fd, short what, void *arg);
 
 /*
  * DNS permutor utilities
  */
 
-#define PERMUTOR_BUF_SIZE 32768
-#define PERMUTOR_KSIZE 32
-#define PERMUTOR_IVSIZE 8
-
-struct dns_permutor {
-       chacha_ctx ctx;
-       guchar perm_buf[PERMUTOR_BUF_SIZE];
-       guint pos;
-};
-
 /**
  * Init chacha20 context
  * @param p
@@ -87,11 +60,11 @@ dns_permutor_init (struct dns_permutor *p)
 }
 
 static struct dns_permutor *
-dns_permutor_new (memory_pool_t *pool)
+dns_permutor_new (void)
 {
        struct dns_permutor *new;
 
-       new = memory_pool_alloc0 (pool, sizeof (struct dns_permutor));
+       new = g_slice_alloc0 (sizeof (struct dns_permutor));
        dns_permutor_init (new);
 
        return new;
@@ -112,12 +85,14 @@ dns_permutor_generate_id (struct dns_permutor *p)
 }
 
 /* Punycode utility */
-static guint digit(unsigned n)
+static guint
+digit (unsigned n)
 {
        return "abcdefghijklmnopqrstuvwxyz0123456789"[n];
 }
 
-static guint adapt(guint delta, guint numpoints, gint first)
+static guint
+adapt (guint delta, guint numpoints, gint first)
 {
        guint k;
 
@@ -256,34 +231,30 @@ struct dns_name_table {
        guint8 off;
        guint8 *label;
        guint8 len;
+       UT_hash_handle hh;
 };
 
 static gboolean
-try_compress_label (memory_pool_t *pool, guint8 *target, guint8 *start, guint8 len, guint8 *label, GList **table)
+try_compress_label (memory_pool_t *pool, guint8 *target, guint8 *start, guint8 len,
+               guint8 *label, struct dns_name_table **table)
 {
-       GList *cur;
-       struct dns_name_table *tbl;
+       struct dns_name_table *found = NULL;
        guint16 pointer;
 
-       cur = *table;
-       while (cur) {
-               tbl = cur->data;
-               if (tbl->len == len) {
-                       if (memcmp (label, tbl->label, len) == 0) {
-                               pointer = htons ((guint16)tbl->off | 0xC0);
-                               memcpy (target, &pointer, sizeof (pointer));
-                               return TRUE;
-                       }
-               }
-               cur = g_list_next (cur);
+       HASH_FIND (hh, *table, label, len, found);
+       if (found != NULL) {
+               pointer = htons ((guint16)found->off | 0xC0);
+               memcpy (target, &pointer, sizeof (pointer));
+               return TRUE;
+       }
+       else {
+               /* Insert label to list */
+               found = memory_pool_alloc (pool, sizeof (struct dns_name_table));
+               found->off = target - start;
+               found->label = label;
+               found->len = len;
+               HASH_ADD_KEYPTR (hh, *table, found->label, len, found);
        }
-
-       /* Insert label to list */
-       tbl = memory_pool_alloc (pool, sizeof (struct dns_name_table));
-       tbl->off = target - start;
-       tbl->label = label;
-       tbl->len = len;
-       *table = g_list_prepend (*table, tbl);
 
        return FALSE;
 }
@@ -353,7 +324,7 @@ format_dns_name (struct rspamd_dns_request *req, const gchar *name, guint namele
 {
        guint8 *pos = req->packet + req->pos, *end, *dot, *name_pos, *begin;
        guint remain = req->packet_len - req->pos - 5, label_len;
-       GList *table = NULL;
+       struct dns_name_table *table = NULL;
        gunichar *uclabel;
        glong uclabel_len;
        gsize punylabel_len;
@@ -448,7 +419,7 @@ format_dns_name (struct rspamd_dns_request *req, const gchar *name, guint namele
        *pos = '\0';
        req->pos += pos - (req->packet + req->pos) + 1;
        if (table != NULL) {
-               g_list_free (table);
+               HASH_CLEAR (hh, table);
        }
 }
 
@@ -849,12 +820,13 @@ end:
 
 #define GET16(x) do {(x) = ((*p) << 8) + *(p + 1); p += sizeof (guint16); *remain -= sizeof (guint16); } while(0)
 #define GET32(x) do {(x) = ((*p) << 24) + ((*(p + 1)) << 16) + ((*(p + 2)) << 8) + *(p + 3); p += sizeof (guint32); *remain -= sizeof (guint32); } while(0)
+#define SKIP(type) do { p += sizeof(type); *remain -= sizeof(type); } while (0)
 
 static gint
-dns_parse_rr (guint8 *in, union rspamd_reply_element *elt, guint8 **pos, struct rspamd_dns_reply *rep, gint *remain)
+dns_parse_rr (guint8 *in, struct rspamd_reply_entry *elt, guint8 **pos, struct rspamd_dns_reply *rep, gint *remain)
 {
        guint8 *p = *pos, parts;
-       guint16 type, datalen, txtlen, copied;
+       guint16 type, datalen, txtlen, copied, ttl;
        gboolean parsed = FALSE;
 
        /* Skip the whole name */
@@ -867,146 +839,94 @@ dns_parse_rr (guint8 *in, union rspamd_reply_element *elt, guint8 **pos, struct
                return -1;
        }
        GET16 (type);
-       /* Skip ttl and class */
-       p += sizeof (guint16) + sizeof (guint32);
-       *remain -= sizeof (guint16) + sizeof (guint32);
+       GET16 (ttl);
+       /* Skip class */
+       SKIP (guint32);
        GET16 (datalen);
        /* Now p points to RR data */
        switch (type) {
        case DNS_T_A:
-               if (rep->request->type != DNS_REQUEST_A) {
+               if (!(datalen & 0x3) && datalen <= *remain) {
+                       memcpy (&elt->content.a.addr, p, sizeof (struct in_addr));
                        p += datalen;
+                       *remain -= datalen;
+                       parsed = TRUE;
+                       elt->type = DNS_REQUEST_A;
                }
                else {
-                       if (!(datalen & 0x3) && datalen <= *remain) {
-                               memcpy (&elt->a.addr[0], p, sizeof (struct in_addr));
-                               p += datalen;
-                               *remain -= datalen;
-                               parsed = TRUE;
-                       }
-                       else {
-                               msg_info ("corrupted A record");
-                               return -1;
-                       }
+                       msg_info ("corrupted A record");
+                       return -1;
                }
                break;
 #ifdef HAVE_INET_PTON
        case DNS_T_AAAA:
-               if (rep->request->type != DNS_REQUEST_AAA) {
+               if (datalen == sizeof (struct in6_addr) && datalen <= *remain) {
+                       memcpy (&elt->content.aaa.addr, p, sizeof (struct in6_addr));
                        p += datalen;
                        *remain -= datalen;
+                       parsed = TRUE;
+                       elt->type = DNS_REQUEST_AAA;
                }
                else {
-                       if (datalen == sizeof (struct in6_addr) && datalen <= *remain) {
-                               memcpy (&elt->aaa.addr, p, sizeof (struct in6_addr));
-                               p += datalen;
-                               *remain -= datalen;
-                               parsed = TRUE;
-                       }
-                       else {
-                               msg_info ("corrupted AAAA record");
-                               return -1;
-                       }
+                       msg_info ("corrupted AAAA record");
+                       return -1;
                }
                break;
 #endif
        case DNS_T_PTR:
-               if (rep->request->type != DNS_REQUEST_PTR) {
-                       p += datalen;
-                       *remain -= datalen;
-               }
-               else {
-                       if (! dns_parse_labels (in, &elt->ptr.name, &p, rep, remain, TRUE)) {
-                               msg_info ("invalid labels in PTR record");
-                               return -1;
-                       }
-                       parsed = TRUE;
+               if (! dns_parse_labels (in, &elt->content.ptr.name, &p, rep, remain, TRUE)) {
+                       msg_info ("invalid labels in PTR record");
+                       return -1;
                }
+               parsed = TRUE;
+               elt->type = DNS_REQUEST_PTR;
                break;
        case DNS_T_MX:
-               if (rep->request->type != DNS_REQUEST_MX) {
-                       p += datalen;
-                       *remain -= datalen;
-               }
-               else {
-                       GET16 (elt->mx.priority);
-                       if (! dns_parse_labels (in, &elt->mx.name, &p, rep, remain, TRUE)) {
-                               msg_info ("invalid labels in MX record");
-                               return -1;
-                       }
-                       parsed = TRUE;
+               GET16 (elt->content.mx.priority);
+               if (! dns_parse_labels (in, &elt->content.mx.name, &p, rep, remain, TRUE)) {
+                       msg_info ("invalid labels in MX record");
+                       return -1;
                }
+               parsed = TRUE;
+               elt->type = DNS_REQUEST_MX;
                break;
        case DNS_T_TXT:
-               if (rep->request->type != DNS_REQUEST_TXT) {
-                       p += datalen;
-                       *remain -= datalen;
-               }
-               else {
-                       elt->txt.data = memory_pool_alloc (rep->request->pool, datalen + 1);
-                       /* Now we should compose data from parts */
-                       copied = 0;
-                       parts = 0;
-                       while (copied + parts < datalen) {
-                               txtlen = *p;
-                               if (txtlen + copied + parts <= datalen) {
-                                       parts ++;
-                                       memcpy (elt->txt.data + copied, p + 1, txtlen);
-                                       copied += txtlen;
-                                       p += txtlen + 1;
-                                       *remain -= txtlen + 1;
-                               }
-                               else {
-                                       break;
-                               }
-                       }
-                       *(elt->txt.data + copied) = '\0';
-                       parsed = TRUE;
-               }
-               break;
        case DNS_T_SPF:
-               if (rep->request->type != DNS_REQUEST_SPF) {
-                       p += datalen;
-                       *remain -= datalen;
-               }
-               else {
-                       copied = 0;
-                       elt->txt.data = memory_pool_alloc (rep->request->pool, datalen + 1);
-                       while (copied < datalen) {
-                               txtlen = *p;
-                               if (txtlen + copied < datalen) {
-                                       memcpy (elt->txt.data + copied, p + 1, txtlen);
-                                       copied += txtlen;
-                                       p += txtlen + 1;
-                                       *remain -= txtlen + 1;
-                               }
-                               else {
-                                       break;
-                               }
+               elt->content.txt.data = memory_pool_alloc (rep->request->pool, datalen + 1);
+               /* Now we should compose data from parts */
+               copied = 0;
+               parts = 0;
+               while (copied + parts < datalen) {
+                       txtlen = *p;
+                       if (txtlen + copied + parts <= datalen) {
+                               parts ++;
+                               memcpy (elt->content.txt.data + copied, p + 1, txtlen);
+                               copied += txtlen;
+                               p += txtlen + 1;
+                               *remain -= txtlen + 1;
+                       }
+                       else {
+                               break;
                        }
-                       *(elt->spf.data + copied) = '\0';
-                       parsed = TRUE;
                }
+               *(elt->content.txt.data + copied) = '\0';
+               parsed = TRUE;
+               elt->type = DNS_REQUEST_TXT;
                break;
        case DNS_T_SRV:
-               if (rep->request->type != DNS_REQUEST_SRV) {
-                       p += datalen;
-                       *remain -= datalen;
+               if (p - *pos > (gint)(*remain - sizeof (guint16) * 3)) {
+                       msg_info ("stripped dns reply while reading SRV record");
+                       return -1;
                }
-               else {
-                       if (p - *pos > (gint)(*remain - sizeof (guint16) * 3)) {
-                               msg_info ("stripped dns reply while reading SRV record");
-                               return -1;
-                       }
-                       GET16 (elt->srv.priority);
-                       GET16 (elt->srv.weight);
-                       GET16 (elt->srv.port);
-                       if (! dns_parse_labels (in, &elt->srv.target, &p, rep, remain, TRUE)) {
-                               msg_info ("invalid labels in SRV record");
-                               return -1;
-                       }
-                       parsed = TRUE;
+               GET16 (elt->content.srv.priority);
+               GET16 (elt->content.srv.weight);
+               GET16 (elt->content.srv.port);
+               if (! dns_parse_labels (in, &elt->content.srv.target, &p, rep, remain, TRUE)) {
+                       msg_info ("invalid labels in SRV record");
+                       return -1;
                }
+               parsed = TRUE;
+               elt->type = DNS_REQUEST_SRV;
                break;
        case DNS_T_CNAME:
                /* Skip cname records */
@@ -1022,6 +942,7 @@ dns_parse_rr (guint8 *in, union rspamd_reply_element *elt, guint8 **pos, struct
        *pos = p;
 
        if (parsed) {
+               elt->ttl = ttl;
                return 1;
        }
        return 0;
@@ -1035,7 +956,7 @@ dns_parse_reply (gint sock, guint8 *in, gint r, struct rspamd_dns_resolver *reso
        struct rspamd_dns_request      *req;
        struct rspamd_dns_reply        *rep;
        struct rspamd_dns_io_channel   *ioc;
-       union rspamd_reply_element     *elt;
+       struct rspamd_reply_entry      *elt;
        guint8                         *pos;
        guint16                         id;
        gint                            i, t;
@@ -1074,27 +995,23 @@ dns_parse_reply (gint sock, guint8 *in, gint r, struct rspamd_dns_resolver *reso
         */
        rep = memory_pool_alloc (req->pool, sizeof (struct rspamd_dns_reply));
        rep->request = req;
-       rep->type = req->type;
-       rep->elements = NULL;
+       rep->entries = NULL;
        rep->code = header->rcode;
 
        if (rep->code == DNS_RC_NOERROR) {
                r -= pos - in;
                /* Extract RR records */
                for (i = 0; i < ntohs (header->ancount); i ++) {
-                       elt = memory_pool_alloc (req->pool, sizeof (union rspamd_reply_element));
+                       elt = memory_pool_alloc (req->pool, sizeof (struct rspamd_reply_entry));
                        t = dns_parse_rr (in, elt, &pos, rep, &r);
                        if (t == -1) {
                                msg_info ("incomplete reply");
                                break;
                        }
                        else if (t == 1) {
-                               rep->elements = g_list_prepend (rep->elements, elt);
+                               DL_APPEND (rep->entries, elt);
                        }
                }
-               if (rep->elements) {
-                       memory_pool_add_destructor (req->pool, (pool_destruct_func)g_list_free, rep->elements);
-               }
        }
        
        *_rep = rep;
@@ -1387,8 +1304,6 @@ make_dns_request (struct rspamd_dns_resolver *resolver,
        return TRUE;
 }
 
-#define RESOLV_CONF "/etc/resolv.conf"
-
 static gboolean
 parse_resolv_conf (struct rspamd_dns_resolver *resolver)
 {
@@ -1419,7 +1334,7 @@ parse_resolv_conf (struct rspamd_dns_resolver *resolver)
                                        if (inet_pton (AF_INET6, p, addr_holder) == 1 ||
                                                        inet_pton (AF_INET, p, addr_holder) == 1) {
                                                new = &resolver->servers[resolver->servers_num];
-                                               new->name = memory_pool_strdup (resolver->static_pool, p);
+                                               new->name = strdup (p);
                                                resolver->servers_num ++;
                                        }
                                        else {
@@ -1459,11 +1374,10 @@ dns_resolver_init (struct event_base *ev_base, struct config_file *cfg)
        struct rspamd_dns_server       *serv;
        struct rspamd_dns_io_channel   *ioc;
        
-       new = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct rspamd_dns_resolver));
+       new = g_slice_alloc0 (sizeof (struct rspamd_dns_resolver));
        new->ev_base = ev_base;
-       new->permutor = dns_permutor_new (cfg->cfg_pool);
+       new->permutor = dns_permutor_new ();
        new->io_channels = g_hash_table_new (g_direct_hash, g_direct_equal);
-       new->static_pool = cfg->cfg_pool;
        new->request_timeout = cfg->dns_timeout;
        new->max_retransmits = cfg->dns_retransmits;
        new->max_errors = cfg->dns_throttling_errors;
@@ -1514,7 +1428,7 @@ dns_resolver_init (struct event_base *ev_base, struct config_file *cfg)
                        serv = &new->servers[new->servers_num];
                        if (inet_pton (AF_INET6, begin, addr_holder) == 1 ||
                                inet_pton (AF_INET, begin, addr_holder) == 1) {
-                               serv->name = memory_pool_strdup (new->static_pool, begin);
+                               serv->name = strdup (begin);
                                serv->up.priority = priority;
                                serv->up.weight = priority;
                                new->servers_num ++;
@@ -1540,15 +1454,13 @@ dns_resolver_init (struct event_base *ev_base, struct config_file *cfg)
        for (i = 0; i < new->servers_num; i ++) {
                serv = &new->servers[i];
                for (j = 0; j < (gint)cfg->dns_io_per_server; j ++) {
-                       ioc = memory_pool_alloc (new->static_pool, sizeof (struct rspamd_dns_io_channel));
+                       ioc = g_slice_alloc0 (sizeof (struct rspamd_dns_io_channel));
                        ioc->sock = make_universal_socket (serv->name, dns_port, SOCK_DGRAM, TRUE, FALSE, FALSE);
                        if (ioc->sock == -1) {
                                msg_warn ("cannot create socket to server %s", serv->name);
                        }
                        else {
                                ioc->requests = g_hash_table_new (dns_id_hash, dns_id_equal);
-                               memory_pool_add_destructor (new->static_pool, (pool_destruct_func)g_hash_table_unref,
-                                               ioc->requests);
                                ioc->srv = serv;
                                ioc->resolver = new;
                                event_set (&ioc->ev, ioc->sock, EV_READ | EV_PERSIST, dns_read_cb, new);
@@ -1564,20 +1476,6 @@ dns_resolver_init (struct event_base *ev_base, struct config_file *cfg)
        return new;
 }
 
-static gchar dns_rcodes[16][16] = {
-       [DNS_RC_NOERROR]  = "NOERROR",
-       [DNS_RC_FORMERR]  = "FORMERR",
-       [DNS_RC_SERVFAIL] = "SERVFAIL",
-       [DNS_RC_NXDOMAIN] = "NXDOMAIN",
-       [DNS_RC_NOTIMP]   = "NOTIMP",
-       [DNS_RC_REFUSED]  = "REFUSED",
-       [DNS_RC_YXDOMAIN] = "YXDOMAIN",
-       [DNS_RC_YXRRSET]  = "YXRRSET",
-       [DNS_RC_NXRRSET]  = "NXRRSET",
-       [DNS_RC_NOTAUTH]  = "NOTAUTH",
-       [DNS_RC_NOTZONE]  = "NOTZONE",
-};
-
 const gchar *
 dns_strerror (enum dns_rcode rcode)
 {
@@ -1591,16 +1489,6 @@ dns_strerror (enum dns_rcode rcode)
        return dns_rcodes[rcode];
 }
 
-static gchar dns_types[7][16] = {
-               [DNS_REQUEST_A] = "A request",
-               [DNS_REQUEST_PTR] = "PTR request",
-               [DNS_REQUEST_MX] = "MX request",
-               [DNS_REQUEST_TXT] = "TXT request",
-               [DNS_REQUEST_SRV] = "SRV request",
-               [DNS_REQUEST_SPF] = "SPF request",
-               [DNS_REQUEST_AAA] = "AAA request"
-};
-
 const gchar *
 dns_strtype (enum rspamd_request_type type)
 {
index 1c88e5a187afc87cb787e6451d57bc003c5abe10..ee52ba13c4381cdb252e0211acce3ae36ba907ca 100644 (file)
--- a/src/dns.h
+++ b/src/dns.h
 #include "events.h"
 #include "upstream.h"
 
-#define MAX_SERVERS 16
-
-#define DNS_D_MAXLABEL 63      /* + 1 '\0' */
-#define DNS_D_MAXNAME  255     /* + 1 '\0' */
-
-#define MAX_ADDRS 10
-
 struct rspamd_dns_reply;
 struct config_file;
 
 typedef void (*dns_callback_type) (struct rspamd_dns_reply *reply, gpointer arg);
 
-/**
- * Represents DNS server
- */
-struct rspamd_dns_server {
-       struct upstream up;                                     /**< upstream structure                                         */
-       gchar *name;                                                    /**< name of DNS server                                         */
-       struct rspamd_dns_io_channel *io_channels;
-       struct rspamd_dns_io_channel *cur_io_channel;
-};
-
-/**
- * IO channel for a specific DNS server
- */
-struct rspamd_dns_io_channel {
-       struct rspamd_dns_server *srv;
-       struct rspamd_dns_resolver *resolver;
-       gint sock;                                                      /**< persistent socket                                          */
-       struct event ev;
-       GHashTable *requests;                           /**< requests in flight                                         */
-       struct rspamd_dns_io_channel *prev, *next;
-};
-
-struct dns_permutor;
-
-struct rspamd_dns_resolver {
-       struct rspamd_dns_server servers[MAX_SERVERS];
-       gint servers_num;                                       /**< number of DNS servers registered           */
-       struct dns_permutor *permutor;  /**< permutor for randomizing request id        */
-       guint request_timeout;
-       guint max_retransmits;
-       guint max_errors;
-       GHashTable *io_channels;                        /**< hash of io chains indexed by socket        */
-       memory_pool_t *static_pool;                     /**< permament pool (cfg_pool)                          */
-       gboolean throttling;                            /**< dns servers are busy                                       */
-       gboolean is_master_slave;                       /**< if this is true, then select upstreams as master/slave */
-       guint errors;                                           /**< resolver errors                                            */
-       struct timeval throttling_time;         /**< throttling time                                            */
-       struct event throttling_event;          /**< throttling event                                           */
-       struct event_base *ev_base;                     /**< base for event ops                                         */
-};
-
-struct dns_header;
-struct dns_query;
-
 enum rspamd_request_type {
        DNS_REQUEST_A = 0,
        DNS_REQUEST_PTR,
@@ -117,13 +66,12 @@ struct rspamd_dns_request {
        gint sock;
        enum rspamd_request_type type;
        time_t time;
+       struct rspamd_dns_request *next;
 };
 
-
-
-union rspamd_reply_element {
+union rspamd_reply_element_un {
        struct {
-               struct in_addr addr[MAX_ADDRS];
+               struct in_addr addr;
                guint16 addrcount;
        } a;
 #ifdef HAVE_INET_PTON
@@ -141,9 +89,6 @@ union rspamd_reply_element {
        struct {
                gchar *data;
        } txt;
-       struct {
-               gchar *data;
-       } spf;
        struct {
                guint16 priority;
                guint16 weight;
@@ -152,6 +97,14 @@ union rspamd_reply_element {
        } srv;
 };
 
+struct rspamd_reply_entry {
+       union rspamd_reply_element_un content;
+       guint16 type;
+       guint16 ttl;
+       struct rspamd_reply_entry *prev, *next;
+};
+
+
 enum dns_rcode {
        DNS_RC_NOERROR  = 0,
        DNS_RC_FORMERR  = 1,
@@ -167,97 +120,11 @@ enum dns_rcode {
 };
        
 struct rspamd_dns_reply {
-       enum rspamd_request_type type;
        struct rspamd_dns_request *request;
        enum dns_rcode code;
-       GList *elements;
+       struct rspamd_reply_entry *entries;
 };
 
-/* Internal DNS structs */
-
-struct dns_header {
-               guint qid:16;
-
-#if BYTE_ORDER == BIG_ENDIAN
-               guint qr:1;
-               guint opcode:4;
-               guint aa:1;
-               guint tc:1;
-               guint rd:1;
-
-               guint ra:1;
-               guint unused:3;
-               guint rcode:4;
-#else
-               guint rd:1;
-               guint tc:1;
-               guint aa:1;
-               guint opcode:4;
-               guint qr:1;
-
-               guint rcode:4;
-               guint unused:3;
-               guint ra:1;
-#endif
-
-               guint qdcount:16;
-               guint ancount:16;
-               guint nscount:16;
-               guint arcount:16;
-};
-
-enum dns_section {
-       DNS_S_QD                = 0x01,
-#define DNS_S_QUESTION         DNS_S_QD
-
-       DNS_S_AN                = 0x02,
-#define DNS_S_ANSWER           DNS_S_AN
-
-       DNS_S_NS                = 0x04,
-#define DNS_S_AUTHORITY                DNS_S_NS
-
-       DNS_S_AR                = 0x08,
-#define DNS_S_ADDITIONAL       DNS_S_AR
-
-       DNS_S_ALL               = 0x0f
-}; /* enum dns_section */
-
-enum dns_opcode {
-       DNS_OP_QUERY    = 0,
-       DNS_OP_IQUERY   = 1,
-       DNS_OP_STATUS   = 2,
-       DNS_OP_NOTIFY   = 4,
-       DNS_OP_UPDATE   = 5,
-}; /* dns_opcode */
-
-enum dns_type {
-       DNS_T_A         = 1,
-       DNS_T_NS        = 2,
-       DNS_T_CNAME     = 5,
-       DNS_T_SOA       = 6,
-       DNS_T_PTR       = 12,
-       DNS_T_MX        = 15,
-       DNS_T_TXT       = 16,
-       DNS_T_AAAA      = 28,
-       DNS_T_SRV       = 33,
-       DNS_T_OPT       = 41,
-       DNS_T_SSHFP     = 44,
-       DNS_T_SPF       = 99,
-
-       DNS_T_ALL       = 255
-}; /* enum dns_type */
-
-enum dns_class {
-       DNS_C_IN        = 1,
-
-       DNS_C_ANY       = 255
-}; /* enum dns_class */
-
-struct dns_query {
-       gchar *qname;
-       guint qtype:16;
-       guint qclass:16;
-};
 
 /* Rspamd DNS API */
 
diff --git a/src/dns_private.h b/src/dns_private.h
new file mode 100644 (file)
index 0000000..4648391
--- /dev/null
@@ -0,0 +1,222 @@
+/* Copyright (c) 2014, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *       * Redistributions of source code must retain the above copyright
+ *         notice, this list of conditions and the following disclaimer.
+ *       * Redistributions in binary form must reproduce the above copyright
+ *         notice, this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DNS_PRIVATE_H_
+#define DNS_PRIVATE_H_
+
+#include "config.h"
+#include "chacha_private.h"
+
+#define MAX_SERVERS 16
+/* Upstream timeouts */
+#define DEFAULT_UPSTREAM_ERROR_TIME 10
+#define DEFAULT_UPSTREAM_DEAD_TIME 300
+#define DEFAULT_UPSTREAM_MAXERRORS 10
+
+static const unsigned base = 36;
+static const unsigned t_min = 1;
+static const unsigned t_max = 26;
+static const unsigned skew = 38;
+static const unsigned damp = 700;
+static const unsigned initial_n = 128;
+static const unsigned initial_bias = 72;
+
+static const gint dns_port = 53;
+
+#define UDP_PACKET_SIZE 4096
+
+#define DNS_COMPRESSION_BITS 0xC0
+
+#define DNS_D_MAXLABEL  63      /* + 1 '\0' */
+#define DNS_D_MAXNAME   255     /* + 1 '\0' */
+
+#define PERMUTOR_BUF_SIZE 32768
+#define PERMUTOR_KSIZE 32
+#define PERMUTOR_IVSIZE 8
+
+#define RESOLV_CONF "/etc/resolv.conf"
+
+/**
+ * Represents DNS server
+ */
+struct rspamd_dns_server {
+       struct upstream up; /**< upstream structure                                         */
+       gchar *name; /**< name of DNS server                                         */
+       struct rspamd_dns_io_channel *io_channels;
+       struct rspamd_dns_io_channel *cur_io_channel;
+};
+
+/**
+ * IO channel for a specific DNS server
+ */
+struct rspamd_dns_io_channel {
+       struct rspamd_dns_server *srv;
+       struct rspamd_dns_resolver *resolver;
+       gint sock; /**< persistent socket                                          */
+       struct event ev;
+       GHashTable *requests; /**< requests in flight                                         */
+       struct rspamd_dns_io_channel *prev, *next;
+};
+
+struct dns_permutor;
+
+struct rspamd_dns_resolver {
+       struct rspamd_dns_server servers[MAX_SERVERS];
+       gint servers_num; /**< number of DNS servers registered           */
+       struct dns_permutor *permutor; /**< permutor for randomizing request id        */
+       guint request_timeout;
+       guint max_retransmits;
+       guint max_errors;
+       GHashTable *io_channels; /**< hash of io chains indexed by socket        */
+       gboolean throttling; /**< dns servers are busy                                       */
+       gboolean is_master_slave; /**< if this is true, then select upstreams as master/slave */
+       guint errors; /**< resolver errors                                            */
+       struct timeval throttling_time; /**< throttling time                                            */
+       struct event throttling_event; /**< throttling event                                           */
+       struct event_base *ev_base; /**< base for event ops                                         */
+};
+
+struct dns_header;
+struct dns_query;
+
+/* Internal DNS structs */
+
+struct dns_header {
+       guint qid :16;
+
+#if BYTE_ORDER == BIG_ENDIAN
+       guint qr:1;
+       guint opcode:4;
+       guint aa:1;
+       guint tc:1;
+       guint rd:1;
+
+       guint ra:1;
+       guint unused:3;
+       guint rcode:4;
+#else
+       guint rd :1;
+       guint tc :1;
+       guint aa :1;
+       guint opcode :4;
+       guint qr :1;
+
+       guint rcode :4;
+       guint unused :3;
+       guint ra :1;
+#endif
+
+       guint qdcount :16;
+       guint ancount :16;
+       guint nscount :16;
+       guint arcount :16;
+};
+
+enum dns_section {
+       DNS_S_QD = 0x01,
+#define DNS_S_QUESTION          DNS_S_QD
+
+       DNS_S_AN = 0x02,
+#define DNS_S_ANSWER            DNS_S_AN
+
+       DNS_S_NS = 0x04,
+#define DNS_S_AUTHORITY         DNS_S_NS
+
+       DNS_S_AR = 0x08,
+#define DNS_S_ADDITIONAL        DNS_S_AR
+
+       DNS_S_ALL = 0x0f
+};
+/* enum dns_section */
+
+enum dns_opcode {
+       DNS_OP_QUERY = 0,
+       DNS_OP_IQUERY = 1,
+       DNS_OP_STATUS = 2,
+       DNS_OP_NOTIFY = 4,
+       DNS_OP_UPDATE = 5,
+};
+/* dns_opcode */
+
+enum dns_class {
+       DNS_C_IN = 1,
+
+       DNS_C_ANY = 255
+};
+/* enum dns_class */
+
+struct dns_query {
+       gchar *qname;
+       guint qtype :16;
+       guint qclass :16;
+};
+
+enum dns_type {
+       DNS_T_A = 1,
+       DNS_T_NS = 2,
+       DNS_T_CNAME = 5,
+       DNS_T_SOA = 6,
+       DNS_T_PTR = 12,
+       DNS_T_MX = 15,
+       DNS_T_TXT = 16,
+       DNS_T_AAAA = 28,
+       DNS_T_SRV = 33,
+       DNS_T_OPT = 41,
+       DNS_T_SSHFP = 44,
+       DNS_T_SPF = 99,
+
+       DNS_T_ALL = 255
+};
+/* enum dns_type */
+
+struct dns_permutor {
+       chacha_ctx ctx;
+       guchar perm_buf[PERMUTOR_BUF_SIZE];
+       guint pos;
+};
+
+static const gchar dns_rcodes[16][16] = {
+       [DNS_RC_NOERROR]  = "NOERROR",
+       [DNS_RC_FORMERR]  = "FORMERR",
+       [DNS_RC_SERVFAIL] = "SERVFAIL",
+       [DNS_RC_NXDOMAIN] = "NXDOMAIN",
+       [DNS_RC_NOTIMP]   = "NOTIMP",
+       [DNS_RC_REFUSED]  = "REFUSED",
+       [DNS_RC_YXDOMAIN] = "YXDOMAIN",
+       [DNS_RC_YXRRSET]  = "YXRRSET",
+       [DNS_RC_NXRRSET]  = "NXRRSET",
+       [DNS_RC_NOTAUTH]  = "NOTAUTH",
+       [DNS_RC_NOTZONE]  = "NOTZONE",
+};
+
+static const gchar dns_types[7][16] = {
+               [DNS_REQUEST_A] = "A request",
+               [DNS_REQUEST_PTR] = "PTR request",
+               [DNS_REQUEST_MX] = "MX request",
+               [DNS_REQUEST_TXT] = "TXT request",
+               [DNS_REQUEST_SRV] = "SRV request",
+               [DNS_REQUEST_SPF] = "SPF request",
+               [DNS_REQUEST_AAA] = "AAA request"
+};
+
+#endif /* DNS_PRIVATE_H_ */
index 540fc77130b4e1c35e9d0d6d0b3f78026d4363d7..2bfbe582f125a8960ef39f2baa8e10e23487b58b 100644 (file)
@@ -71,8 +71,7 @@ lua_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
        struct lua_dns_cbdata              *cd = arg;
        gint                            i = 0;
        struct rspamd_dns_resolver    **presolver;
-       union rspamd_reply_element     *elt;
-       GList                          *cur;
+       struct rspamd_reply_entry     *elt;
 
        lua_rawgeti (cd->L, LUA_REGISTRYINDEX, cd->cbref);
        presolver = lua_newuserdata (cd->L, sizeof (gpointer));
@@ -81,77 +80,50 @@ lua_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
        *presolver = cd->resolver;
        lua_pushstring (cd->L, cd->to_resolve);
 
+       /*
+        * XXX: rework to handle different request types
+        */
        if (reply->code == DNS_RC_NOERROR) {
-               if (reply->type == DNS_REQUEST_A) {
-
-                       lua_newtable (cd->L);
-                       cur = reply->elements;
-                       while (cur) {
-                               elt = cur->data;
-                               lua_ip_push (cd->L, AF_INET, &elt->a.addr);
-                               lua_rawseti (cd->L, -2, ++i);
-                               cur = g_list_next (cur);
+               lua_newtable (cd->L);
+               LL_FOREACH (reply->entries, elt) {
+                       if (elt->type != reply->request->type) {
+                               /*
+                                * XXX: Skip additional record here to be compatible
+                                * with the existing plugins
+                                */
+                               continue;
                        }
-                       lua_pushnil (cd->L);
-               }
-               else if (reply->type == DNS_REQUEST_AAA) {
-
-                       lua_newtable (cd->L);
-                       cur = reply->elements;
-                       while (cur) {
-                               elt = cur->data;
-                               lua_ip_push (cd->L, AF_INET6, &elt->aaa.addr);
+                       switch (elt->type) {
+                       case DNS_REQUEST_A:
+                               lua_ip_push (cd->L, AF_INET, &elt->content.a.addr);
                                lua_rawseti (cd->L, -2, ++i);
-                               cur = g_list_next (cur);
-                       }
-                       lua_pushnil (cd->L);
-               }
-               else if (reply->type == DNS_REQUEST_PTR) {
-                       lua_newtable (cd->L);
-                       cur = reply->elements;
-                       while (cur) {
-                               elt = cur->data;
-                               lua_pushstring (cd->L, elt->ptr.name);
+                               break;
+                       case DNS_REQUEST_AAA:
+                               lua_ip_push (cd->L, AF_INET6, &elt->content.aaa.addr);
                                lua_rawseti (cd->L, -2, ++i);
-                               cur = g_list_next (cur);
-                       }
-                       lua_pushnil (cd->L);
-
-               }
-               else if (reply->type == DNS_REQUEST_TXT) {
-                       lua_newtable (cd->L);
-                       cur = reply->elements;
-                       while (cur) {
-                               elt = cur->data;
-                               lua_pushstring (cd->L, elt->txt.data);
+                               break;
+                       case DNS_REQUEST_PTR:
+                               lua_pushstring (cd->L, elt->content.ptr.name);
                                lua_rawseti (cd->L, -2, ++i);
-                               cur = g_list_next (cur);
-                       }
-                       lua_pushnil (cd->L);
-
-               }
-               else if (reply->type == DNS_REQUEST_MX) {
-                       lua_newtable (cd->L);
-                       cur = reply->elements;
-                       while (cur) {
-                               elt = cur->data;
+                               break;
+                       case DNS_REQUEST_TXT:
+                       case DNS_REQUEST_SPF:
+                               lua_pushstring (cd->L, elt->content.txt.data);
+                               lua_rawseti (cd->L, -2, ++i);
+                               break;
+                       case DNS_REQUEST_MX:
                                /* mx['name'], mx['priority'] */
                                lua_newtable (cd->L);
-                               lua_set_table_index (cd->L, "name", elt->mx.name);
+                               lua_set_table_index (cd->L, "name", elt->content.mx.name);
                                lua_pushstring (cd->L, "priority");
-                               lua_pushnumber (cd->L, elt->mx.priority);
+                               lua_pushnumber (cd->L, elt->content.mx.priority);
                                lua_settable (cd->L, -3);
 
                                lua_rawseti (cd->L, -2, ++i);
-                               cur = g_list_next (cur);
+                               break;
                        }
-                       lua_pushnil (cd->L);
-
-               }
-               else {
-                       lua_pushnil (cd->L);
-                       lua_pushstring (cd->L, "Unknown reply type");
                }
+               lua_pushnil (cd->L);
        }
        else {
                lua_pushnil (cd->L);
index 26f699b785cd1c57b686985bf61296bc640335b8..324807005bc1cc97e39dee1e7ffe89d3578052ac 100644 (file)
@@ -302,7 +302,7 @@ static void
 lua_http_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
 {
        struct lua_http_ud             *ud = arg;
-       union rspamd_reply_element     *elt;
+       struct rspamd_reply_entry      *elt;
        struct in_addr                  ina;
        struct timeval                  tv;
 
@@ -312,8 +312,8 @@ lua_http_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
        }
 
        /* Create socket to server */
-       elt = reply->elements->data;
-       memcpy (&ina, &elt->a.addr[0], sizeof (struct in_addr));
+       elt = reply->entries;
+       memcpy (&ina, &elt->content.a.addr, sizeof (struct in_addr));
 
        ud->fd = make_universal_socket (inet_ntoa (ina), ud->port, SOCK_STREAM, TRUE, FALSE, FALSE);
 
index a9006b773ded027b9b7affa4ba185056bd14a478..c0b5c7d52c18c970875df5083a6de03f7ecbe47c 100644 (file)
@@ -243,7 +243,7 @@ static void
 lua_redis_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
 {
        struct lua_redis_userdata                       *ud = arg;
-       union rspamd_reply_element                      *elt;
+       struct rspamd_reply_entry                       *elt;
 
 
        if (reply->code != DNS_RC_NOERROR) {
@@ -251,8 +251,8 @@ lua_redis_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
                return;
        }
        else {
-               elt = reply->elements->data;
-               memcpy (&ud->ina, &elt->a.addr[0], sizeof (struct in_addr));
+               elt = reply->entries;
+               memcpy (&ud->ina, &elt->content.a.addr, sizeof (struct in_addr));
                /* Make real request */
                lua_redis_make_request_real (ud);
        }
index 397a26ba4d79f8e95b27b707e44c8c8ddc581f0e..f42d0259d865d7cbf323c51c2832dcdea6638f45 100644 (file)
@@ -679,14 +679,16 @@ dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
 {
        struct dns_param               *param = (struct dns_param *)arg;
        struct worker_task             *task = param->task;
-       union rspamd_reply_element     *elt;
+       struct rspamd_reply_entry      *elt;
 
        debug_task ("in surbl request callback");
        /* If we have result from DNS server, this url exists in SURBL, so increase score */
-       if (reply->code == DNS_RC_NOERROR && reply->elements) {
+       if (reply->code == DNS_RC_NOERROR && reply->entries) {
                msg_info ("<%s> domain [%s] is in surbl %s", param->task->message_id, param->host_resolve, param->suffix->suffix);
-               elt = reply->elements->data;
-               process_dns_results (param->task, param->suffix, param->host_resolve, (guint32)elt->a.addr[0].s_addr);
+               elt = reply->entries;
+               if (elt->type == DNS_REQUEST_A) {
+                       process_dns_results (param->task, param->suffix, param->host_resolve, (guint32)elt->content.a.addr.s_addr);
+               }
        }
        else {
                debug_task ("<%s> domain [%s] is not in surbl %s", param->task->message_id, param->host_resolve, param->suffix->suffix);
index e468086b44345ea6194b295dd9428605954f41e9..423a9b0bd5d529741ca37ae63cbb6bfea16d171c 100644 (file)
@@ -652,7 +652,7 @@ smtp_dns_cb (struct rspamd_dns_reply *reply, void *arg)
 {
        struct smtp_proxy_session                                               *session = arg;
        gint                                                                                     res = 0;
-       union rspamd_reply_element                                              *elt;
+       struct rspamd_reply_entry                                               *elt;
        GList                                                                                   *cur;
 
        switch (session->state)
@@ -676,10 +676,10 @@ smtp_dns_cb (struct rspamd_dns_reply *reply, void *arg)
                        smtp_make_delay (session);
                }
                else {
-                       if (reply->elements) {
-                               elt = reply->elements->data;
+                       if (reply->entries) {
+                               elt = reply->entries;
                                session->hostname = memory_pool_strdup (session->pool,
-                                               elt->ptr.name);
+                                               elt->content.ptr.name);
                                session->state = SMTP_PROXY_STATE_RESOLVE_NORMAL;
                                make_dns_request (session->resolver, session->s, session->pool,
                                                smtp_dns_cb, session, DNS_REQUEST_A, session->hostname);
@@ -706,10 +706,8 @@ smtp_dns_cb (struct rspamd_dns_reply *reply, void *arg)
                }
                else {
                        res = 0;
-                       cur = reply->elements;
-                       while (cur) {
-                               elt = cur->data;
-                               if (memcmp (&session->client_addr, &elt->a.addr[0],
+                       LL_FOREACH (reply->entries, elt) {
+                               if (memcmp (&session->client_addr, &elt->content.a.addr,
                                                sizeof(struct in_addr)) == 0) {
                                        res = 1;
                                        session->resolved = TRUE;
index e394005b89e89b9669f013f6a6cc3327ec235173..f04395da5e9e5139026e468ee998d695da551f88 100644 (file)
--- a/src/spf.c
+++ b/src/spf.c
@@ -391,8 +391,8 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
 {
        struct spf_dns_cb               *cb = arg;
        gchar                           *begin;
-       union rspamd_reply_element      *elt_data;
-       GList                           *tmp = NULL, *elt;
+       struct rspamd_reply_entry      *elt_data;
+       GList                           *tmp = NULL;
        struct worker_task              *task;
        struct spf_addr                 *new_addr;
 
@@ -401,165 +401,160 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
        cb->rec->requests_inflight --;
 
        if (reply->code == DNS_RC_NOERROR) {
-               if (reply->elements != NULL) {
-                       /* Add all logic for all DNS states here */
-                       elt = reply->elements;
-                       while (elt) {
-                               elt_data = elt->data;
-                               switch (cb->cur_action) {
-                               case SPF_RESOLVE_MX:
-                                       if (reply->type == DNS_REQUEST_MX) {
-                                               /* Now resolve A record for this MX */
-                                               if (make_dns_request (task->resolver, task->s, task->task_pool,
-                                                               spf_record_dns_callback, (void *)cb, DNS_REQUEST_A, elt_data->mx.name)) {
-                                                       task->dns_requests ++;
-                                                       cb->rec->requests_inflight ++;
-                                               }
+               /* Add all logic for all DNS states here */
+               LL_FOREACH (reply->entries, elt_data) {
+                       switch (cb->cur_action) {
+                       case SPF_RESOLVE_MX:
+                               if (elt_data->type == DNS_REQUEST_MX) {
+                                       /* Now resolve A record for this MX */
+                                       if (make_dns_request (task->resolver, task->s, task->task_pool,
+                                                       spf_record_dns_callback, (void *)cb, DNS_REQUEST_A, elt_data->content.mx.name)) {
+                                               task->dns_requests ++;
+                                               cb->rec->requests_inflight ++;
+                                       }
+                               }
+                               else if (elt_data->type == DNS_REQUEST_A) {
+                                       if (!cb->addr->data.normal.parsed) {
+                                               cb->addr->data.normal.d.in4.s_addr = elt_data->content.a.addr.s_addr;
+                                               cb->addr->data.normal.mask = 32;
+                                               cb->addr->data.normal.parsed = TRUE;
                                        }
-                                       else if (reply->type == DNS_REQUEST_A) {
-                                               if (!cb->addr->data.normal.parsed) {
-                                                       cb->addr->data.normal.d.in4.s_addr = elt_data->a.addr[0].s_addr;
-                                                       cb->addr->data.normal.mask = 32;
-                                                       cb->addr->data.normal.parsed = TRUE;
+                                       else {
+                                               /* Insert one more address */
+                                               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->data.normal.d.in4.s_addr = elt_data->content.a.addr.s_addr;
+                                                       new_addr->data.normal.parsed = TRUE;
+                                                       cb->rec->addrs = g_list_insert_before (cb->rec->addrs, tmp, new_addr);
                                                }
                                                else {
-                                                       /* Insert one more address */
-                                                       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->data.normal.d.in4.s_addr = elt_data->a.addr[0].s_addr;
-                                                               new_addr->data.normal.parsed = TRUE;
-                                                               cb->rec->addrs = g_list_insert_before (cb->rec->addrs, tmp, new_addr);
-                                                       }
-                                                       else {
-                                                               msg_info ("<%s>: spf error for domain %s: addresses mismatch",
-                                                                               task->message_id, cb->rec->sender_domain);
-                                                       }
+                                                       msg_info ("<%s>: spf error for domain %s: addresses mismatch",
+                                                                       task->message_id, cb->rec->sender_domain);
                                                }
-
                                        }
+
+                               }
 #ifdef HAVE_INET_PTON
-                                       else if (reply->type == DNS_REQUEST_AAA) {
-                                               if (!cb->addr->data.normal.parsed) {
-                                                       memcpy (&cb->addr->data.normal.d.in6, &elt_data->aaa.addr, sizeof (struct in6_addr));
-                                                       cb->addr->data.normal.mask = 32;
-                                                       cb->addr->data.normal.parsed = TRUE;
-                                                       cb->addr->data.normal.ipv6 = TRUE;
+                               else if (elt_data->type == DNS_REQUEST_AAA) {
+                                       if (!cb->addr->data.normal.parsed) {
+                                               memcpy (&cb->addr->data.normal.d.in6, &elt_data->content.aaa.addr, sizeof (struct in6_addr));
+                                               cb->addr->data.normal.mask = 32;
+                                               cb->addr->data.normal.parsed = TRUE;
+                                               cb->addr->data.normal.ipv6 = TRUE;
+                                       }
+                                       else {
+                                               /* Insert one more address */
+                                               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));
+                                                       memcpy (&new_addr->data.normal.d.in6, &elt_data->content.aaa.addr, sizeof (struct in6_addr));
+                                                       new_addr->data.normal.parsed = TRUE;
+                                                       new_addr->data.normal.ipv6 = TRUE;
+                                                       cb->rec->addrs = g_list_insert_before (cb->rec->addrs, tmp, new_addr);
                                                }
                                                else {
-                                                       /* Insert one more address */
-                                                       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));
-                                                               memcpy (&new_addr->data.normal.d.in6, &elt_data->aaa.addr, sizeof (struct in6_addr));
-                                                               new_addr->data.normal.parsed = TRUE;
-                                                               new_addr->data.normal.ipv6 = TRUE;
-                                                               cb->rec->addrs = g_list_insert_before (cb->rec->addrs, tmp, new_addr);
-                                                       }
-                                                       else {
-                                                               msg_info ("<%s>: spf error for domain %s: addresses mismatch",
-                                                                               task->message_id, cb->rec->sender_domain);
-                                                       }
+                                                       msg_info ("<%s>: spf error for domain %s: addresses mismatch",
+                                                                       task->message_id, cb->rec->sender_domain);
                                                }
-
                                        }
+
+                               }
 #endif
-                                       break;
-                               case SPF_RESOLVE_A:
-                                       if (reply->type == DNS_REQUEST_A) {
-                                               /* XXX: process only one record */
-                                               cb->addr->data.normal.d.in4.s_addr = elt_data->a.addr[0].s_addr;
-                                               cb->addr->data.normal.mask = 32;
-                                               cb->addr->data.normal.parsed = TRUE;
-                                       }
+                               break;
+                       case SPF_RESOLVE_A:
+                               if (elt_data->type == DNS_REQUEST_A) {
+                                       /* XXX: process only one record */
+                                       cb->addr->data.normal.d.in4.s_addr = elt_data->content.a.addr.s_addr;
+                                       cb->addr->data.normal.mask = 32;
+                                       cb->addr->data.normal.parsed = TRUE;
+                               }
 #ifdef HAVE_INET_PTON
-                                       else if (reply->type == DNS_REQUEST_AAA) {
-                                               memcpy (&cb->addr->data.normal.d.in6, &elt_data->aaa.addr, sizeof (struct in6_addr));
-                                               cb->addr->data.normal.mask = 32;
-                                               cb->addr->data.normal.parsed = TRUE;
-                                               cb->addr->data.normal.ipv6 = TRUE;
-                                       }
+                               else if (elt_data->type == DNS_REQUEST_AAA) {
+                                       memcpy (&cb->addr->data.normal.d.in6, &elt_data->content.aaa.addr, sizeof (struct in6_addr));
+                                       cb->addr->data.normal.mask = 32;
+                                       cb->addr->data.normal.parsed = TRUE;
+                                       cb->addr->data.normal.ipv6 = TRUE;
+                               }
 #endif
-                                       break;
+                               break;
 #ifdef HAVE_INET_PTON
-                               case SPF_RESOLVE_AAA:
-                                       if (reply->type == DNS_REQUEST_A) {
-                                               /* XXX: process only one record */
-                                               cb->addr->data.normal.d.in4.s_addr = elt_data->a.addr[0].s_addr;
-                                               cb->addr->data.normal.mask = 32;
-                                               cb->addr->data.normal.parsed = TRUE;
-                                       }
-                                       else if (reply->type == DNS_REQUEST_AAA) {
-                                               memcpy (&cb->addr->data.normal.d.in6, &elt_data->aaa.addr, sizeof (struct in6_addr));
-                                               cb->addr->data.normal.mask = 32;
-                                               cb->addr->data.normal.parsed = TRUE;
-                                               cb->addr->data.normal.ipv6 = TRUE;
-                                       }
+                       case SPF_RESOLVE_AAA:
+                               if (elt_data->type == DNS_REQUEST_A) {
+                                       /* XXX: process only one record */
+                                       cb->addr->data.normal.d.in4.s_addr = elt_data->content.a.addr.s_addr;
+                                       cb->addr->data.normal.mask = 32;
+                                       cb->addr->data.normal.parsed = TRUE;
+                               }
+                               else if (elt_data->type == DNS_REQUEST_AAA) {
+                                       memcpy (&cb->addr->data.normal.d.in6, &elt_data->content.aaa.addr, sizeof (struct in6_addr));
+                                       cb->addr->data.normal.mask = 32;
+                                       cb->addr->data.normal.parsed = TRUE;
+                                       cb->addr->data.normal.ipv6 = TRUE;
+                               }
 #endif
-                                       break;
-                               case SPF_RESOLVE_PTR:
-                                       break;
-                               case SPF_RESOLVE_REDIRECT:
-                                       if (reply->type == DNS_REQUEST_TXT) {
-                                               begin = elt_data->txt.data;
-
-                                               if (!cb->in_include && cb->rec->addrs) {
-                                                       g_list_free (cb->rec->addrs);
-                                                       cb->rec->addrs = NULL;
-                                               }
-                                               start_spf_parse (cb->rec, begin);
+                               break;
+                       case SPF_RESOLVE_PTR:
+                               break;
+                       case SPF_RESOLVE_REDIRECT:
+                               if (elt_data->type == DNS_REQUEST_TXT) {
+                                       begin = elt_data->content.txt.data;
 
+                                       if (!cb->in_include && cb->rec->addrs) {
+                                               g_list_free (cb->rec->addrs);
+                                               cb->rec->addrs = NULL;
                                        }
-                                       break;
-                               case SPF_RESOLVE_INCLUDE:
-                                       if (reply->type == DNS_REQUEST_TXT) {
-                                               begin = elt_data->txt.data;
+                                       start_spf_parse (cb->rec, begin);
+
+                               }
+                               break;
+                       case SPF_RESOLVE_INCLUDE:
+                               if (elt_data->type == DNS_REQUEST_TXT) {
+                                       begin = elt_data->content.txt.data;
 #ifdef SPF_DEBUG
-                                               msg_info ("before include");
-                                               dump_spf_record (cb->rec->addrs);
+                                       msg_info ("before include");
+                                       dump_spf_record (cb->rec->addrs);
 #endif
-                                               tmp = cb->rec->addrs;
-                                               cb->rec->addrs = NULL;
-                                               cb->rec->in_include = TRUE;
-                                               start_spf_parse (cb->rec, begin);
-                                               cb->rec->in_include = FALSE;
+                                       tmp = cb->rec->addrs;
+                                       cb->rec->addrs = NULL;
+                                       cb->rec->in_include = TRUE;
+                                       start_spf_parse (cb->rec, begin);
+                                       cb->rec->in_include = FALSE;
 
 #ifdef SPF_DEBUG
-                                               msg_info ("after include");
-                                               dump_spf_record (cb->rec->addrs);
+                                       msg_info ("after include");
+                                       dump_spf_record (cb->rec->addrs);
 #endif
-                                               /* Insert new list */
-                                               cb->addr->is_list = TRUE;
-                                               cb->addr->data.list = cb->rec->addrs;
-                                               cb->rec->addrs = tmp;
-                                       }
-                                       break;
-                               case SPF_RESOLVE_EXP:
-                                       break;
-                               case SPF_RESOLVE_EXISTS:
-                                       if (reply->type == DNS_REQUEST_A) {
-                                               /* If specified address resolves, we can accept connection from every IP */
-                                               cb->addr->data.normal.d.in4.s_addr = INADDR_NONE;
-                                               cb->addr->data.normal.mask = 0;
-                                       }
-                                       break;
+                                       /* Insert new list */
+                                       cb->addr->is_list = TRUE;
+                                       cb->addr->data.list = cb->rec->addrs;
+                                       cb->rec->addrs = tmp;
+                               }
+                               break;
+                       case SPF_RESOLVE_EXP:
+                               break;
+                       case SPF_RESOLVE_EXISTS:
+                               if (elt_data->type == DNS_REQUEST_A) {
+                                       /* If specified address resolves, we can accept connection from every IP */
+                                       cb->addr->data.normal.d.in4.s_addr = INADDR_NONE;
+                                       cb->addr->data.normal.mask = 0;
                                }
-                               elt = g_list_next (elt);
+                               break;
                        }
                }
        }
        else if (reply->code == DNS_RC_NXDOMAIN) {
                switch (cb->cur_action) {
                                case SPF_RESOLVE_MX:
-                                       if (reply->type == DNS_REQUEST_MX) {
+                                       if (reply->request->type == DNS_REQUEST_MX) {
                                                msg_info ("<%s>: spf error for domain %s: cannot find MX record for %s",
                                                                task->message_id, cb->rec->sender_domain, cb->rec->cur_domain);
                                                cb->addr->data.normal.d.in4.s_addr = INADDR_NONE;
                                                cb->addr->data.normal.mask = 32;
                                        }
-                                       else if (reply->type != DNS_REQUEST_MX) {
+                                       else if (reply->request->type != DNS_REQUEST_MX) {
                                                msg_info ("<%s>: spf error for domain %s: cannot resolve MX record for %s",
                                                                task->message_id, cb->rec->sender_domain, cb->rec->cur_domain);
                                                cb->addr->data.normal.d.in4.s_addr = INADDR_NONE;
@@ -567,7 +562,7 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
                                        }
                                        break;
                                case SPF_RESOLVE_A:
-                                       if (reply->type == DNS_REQUEST_A) {
+                                       if (reply->request->type == DNS_REQUEST_A) {
                                                /* XXX: process only one record */
                                                cb->addr->data.normal.d.in4.s_addr = INADDR_NONE;
                                                cb->addr->data.normal.mask = 32;
@@ -575,7 +570,7 @@ spf_record_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
                                        break;
 #ifdef HAVE_INET_PTON
                                case SPF_RESOLVE_AAA:
-                                       if (reply->type == DNS_REQUEST_AAA) {
+                                       if (reply->request->type == DNS_REQUEST_AAA) {
                                                /* XXX: process only one record */
                                                memset (&cb->addr->data.normal.d.in6, 0xff, sizeof (struct in6_addr));
                                                cb->addr->data.normal.mask = 32;
@@ -1345,16 +1340,12 @@ static void
 spf_dns_callback (struct rspamd_dns_reply *reply, gpointer arg)
 {
        struct spf_record *rec = arg;
-       union rspamd_reply_element *elt;
-       GList *cur;
+       struct rspamd_reply_entry *elt;
 
        rec->requests_inflight --;
        if (reply->code == DNS_RC_NOERROR) {
-               cur = reply->elements;
-               while (cur) {
-                       elt = cur->data;
-                       start_spf_parse (rec, elt->txt.data);
-                       cur = g_list_next (cur);
+               LL_FOREACH (reply->entries, elt) {
+                       start_spf_parse (rec, elt->content.txt.data);
                }
        }