MAJOR: dns: Refactor the DNS code

This is a huge patch with many changes, all about the DNS. Initially, the idea
was to update the DNS part to ease the threads support integration. But quickly,
I started to refactor some parts. And after several iterations, it was
impossible for me to commit the different parts atomically. So, instead of
adding tens of patches, often reworking the same parts, it was easier to merge
all my changes in a uniq patch. Here are all changes made on the DNS.

First, the DNS initialization has been refactored. The DNS configuration parsing
remains untouched, in cfgparse.c. But all checks have been moved in a post-check
callback. In the function dns_finalize_config, for each resolvers, the
nameservers configuration is tested and the task used to manage DNS resolutions
is created. The links between the backend's servers and the resolvers are also
created at this step. Here no connection are kept alive. So there is no needs
anymore to reopen them after HAProxy fork. Connections used to send DNS queries
will be opened on demand.

Then, the way DNS requesters are linked to a DNS resolution has been
reworked. The resolution used by a requester is now referenced into the
dns_requester structure and the resolution pointers in server and dns_srvrq
structures have been removed. wait and curr list of requesters, for a DNS
resolution, have been replaced by a uniq list. And Finally, the way a requester
is removed from a DNS resolution has been simplified. Now everything is done in
dns_unlink_resolution.

srv_set_fqdn function has been simplified. Now, there is only 1 way to set the
server's FQDN, independently it is done by the CLI or when a SRV record is
resolved.

The static DNS resolutions pool has been replaced by a dynamoc pool. The part
has been modified by Baptiste Assmann.

The way the DNS resolutions are triggered by the task or by a health-check has
been totally refactored. Now, all timeouts are respected. Especially
hold.valid. The default frequency to wake up a resolvers is now configurable
using "timeout resolve" parameter.

Now, as documented, as long as invalid repsonses are received, we really wait
all name servers responses before retrying.

As far as possible, resources allocated during DNS configuration parsing are
releases when HAProxy is shutdown.

Beside all these changes, the code has been cleaned to ease code review and the
doc has been updated.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index f5cf603..59bbc56 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -11475,10 +11475,6 @@
 resolvers <id>
   Points to an existing "resolvers" section to resolve current server's
   hostname.
-  In order to be operational, DNS resolution requires that health check is
-  enabled on the server. Actually, health checks triggers the DNS resolution.
-  You must precise one 'resolvers' parameter on each server line where DNS
-  resolution is required.
 
   Example:
 
@@ -11712,21 +11708,20 @@
      host name. It uses libc functions to get the host name resolved. This
      resolution relies on /etc/resolv.conf file.
 
-  2. at run time, when HAProxy gets prepared to run a health check on a server,
-     it verifies if the current name resolution is still considered as valid.
-     If not, it processes a new resolution, in parallel of the health check.
+  2. at run time, HAProxy performs periodically name resolutions for servers
+     requiring DNS resolutions.
 
 A few other events can trigger a name resolution at run time:
   - when a server's health check ends up in a connection timeout: this may be
     because the server has a new IP address. So we need to trigger a name
     resolution to know this new IP.
 
-When using resolvers, the server name can either be a hostname, or s SRV label.
-HAProxy considers anything that starts with an underscore a SRV label.
-If a SRV label is specified, then the corresponding SRV records will be
-retrieved from the DNS server, and the provided hostnames will be used. The
-SRV label will be checked periodically, and if any server are added or removed,
-haproxy will automatically do the same.
+When using resolvers, the server name can either be a hostname, or a SRV label.
+HAProxy considers anything that starts with an underscore as a SRV label.  If a
+SRV label is specified, then the corresponding SRV records will be retrieved
+from the DNS server, and the provided hostnames will be used. The SRV label
+will be checked periodically, and if any server are added or removed, haproxy
+will automatically do the same.
 
 A few things important to notice:
   - all the name servers are queried in the mean time. HAProxy will process the
@@ -11740,9 +11735,8 @@
 ----------------------------
 
 This section is dedicated to host information related to name resolution in
-HAProxy.
-There can be as many as resolvers section as needed. Each section can contain
-many name servers.
+HAProxy. There can be as many as resolvers section as needed. Each section can
+contain many name servers.
 
 When multiple name servers are configured in a resolvers section, then HAProxy
 uses the first valid response. In case of invalid responses, only the last one
@@ -11750,43 +11744,40 @@
 answer after a fast faulty or outdated server.
 
 When each server returns a different error type, then only the last error is
-used by HAProxy to decide what type of behavior to apply.
+used by HAProxy. The following processing is applied on this error:
 
-Two types of behavior can be applied:
- 1. stop DNS resolution
- 2. replay the DNS query with a new query type
-      In such case, the following types are applied in this exact order:
-        1. ANY query type
-        2. query type corresponding to family pointed by resolve-prefer
-           server's parameter
-        3. remaining family type
+  1. HAProxy retries the same DNS query with a new query type. The A queries are
+     switch to AAAA or the opposite. SRV queries are not concerned here. Timeout
+     errors are also excluded.
 
-HAProxy stops DNS resolution when the following errors occur:
- - invalid DNS response packet
- - wrong name in the query section of the response
- - NX domain
- - Query refused by server
- - CNAME not pointing to an IP address
+  2. When the fallback on the query type was done (or not applicable), HAProxy
+     retries the original DNS query, with the preferred query type.
 
-HAProxy tries a new query type when the following errors occur:
- - no Answer records in the response
- - DNS response truncated
- - Error in DNS response
- - No expected DNS records found in the response
- - name server timeout
+  3. HAProxy retries previous steps <resolve_retires> times. If no valid
+     response is received after that, it stops the DNS resolution and reports
+     the error.
 
-For example, with 2 name servers configured in a resolvers section:
- - first response is valid and is applied directly, second response is ignored
- - first response is invalid and second one is valid, then second response is
-   applied;
- - first response is a NX domain and second one a truncated response, then
-   HAProxy replays the query with a new type;
- - first response is truncated and second one is a NX Domain, then HAProxy
-   stops resolution.
+For example, with 2 name servers configured in a resolvers section, the
+following scenarios are possible:
+
+  - First response is valid and is applied directly, second response is
+    ignored
+
+  - First response is invalid and second one is valid, then second response is
+    applied
+
+  - First response is a NX domain and second one a truncated response, then
+    HAProxy retries the query with a new type
+
+  - First response is a NX domain and second one is a timeout, then HAProxy
+    retries the query with a new type
+
+  - Query timed out for both name servers, then HAProxy retries it with the
+    same query type
 
 As a DNS server may not answer all the IPs in one DNS request, haproxy keeps
 a cache of previous answers, an answer will be considered obsolete after
-"hold obsolete" seconds without the IP returned.
+<hold obsolete> seconds without the IP returned.
 
 
 resolvers <resolvers id>
@@ -11796,7 +11787,7 @@
 
 accepted_payload_size <nb>
   Defines the maxium payload size accepted by HAProxy and announced to all the
-  naeservers configured in this resolvers section.
+  name servers configured in this resolvers section.
   <nb> is in bytes. If not set, HAProxy announces 512. (minimal value defined
        by RFC 6891)
 
@@ -11822,11 +11813,7 @@
 
   Default value is 10s for "valid", 0s for "obsolete" and 30s for others.
 
-  Note: since the name resolution is triggered by the health checks, a new
-        resolution is triggered after <period> modulo the <inter> parameter of
-        the healch check.
-
-resolution_pool_size <nb>
+resolution_pool_size <nb>  (deprecated)
   Defines the number of resolutions available in the pool for this resolvers.
   If not defines, it defaults to 64. If your configuration requires more than
   <nb>, then HAProxy will return an error when parsing the configuration.
@@ -11844,9 +11831,12 @@
   Defines timeouts related to name resolution
      <event> : the event on which the <time> timeout period applies to.
                events available are:
-               - retry: time between two DNS queries, when no response have
-                        been received.
-                        Default value: 1s
+	       - resolve : default time to trigger name resolutions when no
+	                   other time applied.
+                           Default value: 1s
+               - retry   : time between two DNS queries, when no valid response
+	                   have been received.
+                           Default value: 1s
      <time>  : time related to the event. It follows the HAProxy time format.
                <time> is expressed in milliseconds.
 
@@ -11856,6 +11846,7 @@
      nameserver dns1 10.0.0.1:53
      nameserver dns2 10.0.0.2:53
      resolve_retries       3
+     timeout resolve       1s
      timeout retry         1s
      hold other           30s
      hold refused         30s
diff --git a/include/proto/dns.h b/include/proto/dns.h
index aa063c7..c3e3846 100644
--- a/include/proto/dns.h
+++ b/include/proto/dns.h
@@ -23,46 +23,26 @@
 #define _PROTO_DNS_H
 
 #include <types/dns.h>
-#include <types/proto_udp.h>
 
-char *dns_str_to_dn_label(const char *string, char *dn, int dn_len);
-int dns_str_to_dn_label_len(const char *string);
-void dns_dn_label_to_str(char *dn, char *str, int dn_len);
+extern struct list dns_resolvers;
+
+struct dns_resolvers *find_resolvers_by_id(const char *id);
+struct dns_srvrq *find_srvrq_by_name(const char *name, struct proxy *px);
+struct dns_srvrq *new_dns_srvrq(struct server *srv, char *fqdn);
+
+int dns_str_to_dn_label(const char *str, int str_len, char *dn, int dn_len);
+int dns_dn_label_to_str(const char *dn, int dn_len, char *str, int str_len);
+
 int dns_hostname_validation(const char *string, char **err);
-int dns_build_query(int query_id, int query_type, unsigned int accepted_payload_size, char *hostname_dn, int hostname_dn_len, char *buf, int bufsize);
-struct task *dns_process_resolve(struct task *t);
-int dns_init_resolvers(int close_socket);
-uint16_t dns_rnd16(void);
-int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct dns_resolution *resolution, int max_answer_records);
 int dns_get_ip_from_response(struct dns_response_packet *dns_p,
                              struct dns_options *dns_opts, void *currentip,
                              short currentip_sin_family,
                              void **newip, short *newip_sin_family,
                              void *owner);
-void dns_resolve_send(struct dgram_conn *dgram);
-void dns_resolve_recv(struct dgram_conn *dgram);
-int dns_send_query(struct dns_resolution *resolution);
-void dns_print_current_resolutions(struct dns_resolvers *resolvers);
-void dns_update_resolvers_timeout(struct dns_resolvers *resolvers);
-void dns_reset_resolution(struct dns_resolution *resolution);
-void dns_resolution_free(struct dns_resolvers *resolvers, struct dns_resolution *resolution);
-void dns_rm_requester_from_resolution(struct dns_requester *requester, struct dns_resolution *resolution);
-int dns_check_resolution_queue(struct dns_resolvers *resolvers);
-unsigned short dns_response_get_query_id(unsigned char *resp);
-struct dns_resolution *dns_alloc_resolution(void);
-void dns_free_resolution(struct dns_resolution *resolution);
-struct chunk *dns_cache_key(int query_type, char *hostname_dn, int hostname_dn_len, struct chunk *buf);
-struct lru64 *dns_cache_lookup(int query_type, char *hostname_dn, int hostname_dn_len, int valid_period, void *cache_domain);
-int dns_link_resolution(void *requester, int requester_type, struct dns_resolution *resolution);
-struct dns_resolution *dns_resolution_list_get(struct dns_resolvers *resolvers, char *hostname_dn, int query_type);
-int dns_trigger_resolution(struct dns_resolution *resolution);
-int dns_alloc_resolution_pool(struct dns_resolvers *resolvers);
 
-void dump_dns_config(void);
+int dns_link_resolution(void *requester, int requester_type);
+void dns_unlink_resolution(struct dns_requester *requester);
+void dns_trigger_resolution(struct dns_requester *requester);
 
-/*
- * erases all information of a dns_requester structure
- */
-#define		dns_clear_requester(requester)	memset(requester, '\0', sizeof(*requester));
 
 #endif // _PROTO_DNS_H
diff --git a/include/proto/server.h b/include/proto/server.h
index b521995..b101b3e 100644
--- a/include/proto/server.h
+++ b/include/proto/server.h
@@ -60,7 +60,6 @@
 int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver *nameserver);
 int snr_resolution_error_cb(struct dns_requester *requester, int error_code);
 struct server *snr_check_ip_callback(struct server *srv, void *ip, unsigned char *ip_family);
-void srv_free_from_resolution(struct server *srv);
 
 /* increase the number of cumulated connections on the designated server */
 static void inline srv_inc_sess_ctr(struct server *s)
diff --git a/include/types/dns.h b/include/types/dns.h
index 06e014c..1c68551 100644
--- a/include/types/dns.h
+++ b/include/types/dns.h
@@ -22,21 +22,32 @@
 #ifndef _TYPES_DNS_H
 #define _TYPES_DNS_H
 
+#include <eb32tree.h>
+
+#include <common/mini-clist.h>
+
+#include <types/connection.h>
+#include <types/obj_type.h>
+#include <types/proto_udp.h>
+#include <types/proxy.h>
+#include <types/server.h>
+#include <types/task.h>
+
 /*DNS maximum values */
 /*
  * Maximum issued from RFC:
  *  RFC 1035: https://www.ietf.org/rfc/rfc1035.txt chapter 2.3.4
  *  RFC 2671: http://tools.ietf.org/html/rfc2671
  */
-#define DNS_MAX_LABEL_SIZE	63
-#define DNS_MAX_NAME_SIZE	255
-#define DNS_MAX_UDP_MESSAGE	8192
+#define DNS_MAX_LABEL_SIZE   63
+#define DNS_MAX_NAME_SIZE    255
+#define DNS_MAX_UDP_MESSAGE  8192
 
 /* DNS minimun record size: 1 char + 1 NULL + type + class */
-#define DNS_MIN_RECORD_SIZE	( 1 + 1 + 2 + 2 )
+#define DNS_MIN_RECORD_SIZE  (1 + 1 + 2 + 2)
 
 /* DNS smallest fqdn 'a.gl' size */
-# define DNS_SMALLEST_FQDN_SIZE	4
+# define DNS_SMALLEST_FQDN_SIZE 4
 
 /* maximum number of query records in a DNS response
  * For now, we allow only one */
@@ -52,29 +63,29 @@
 #define DNS_ANALYZE_BUFFER_SIZE DNS_MAX_UDP_MESSAGE + DNS_MAX_NAME_SIZE
 
 /* DNS error messages */
-#define DNS_TOO_LONG_FQDN	"hostname too long"
-#define DNS_LABEL_TOO_LONG	"one label too long"
-#define DNS_INVALID_CHARACTER	"found an invalid character"
+#define DNS_TOO_LONG_FQDN       "hostname too long"
+#define DNS_LABEL_TOO_LONG      "one label too long"
+#define DNS_INVALID_CHARACTER   "found an invalid character"
 
 /* dns query class */
-#define DNS_RCLASS_IN		1	/* internet class */
+#define DNS_RCLASS_IN           1      /* internet class */
 
 /* dns record types (non exhaustive list) */
-#define DNS_RTYPE_A		1	/* IPv4 address */
-#define DNS_RTYPE_CNAME		5	/* canonical name */
-#define DNS_RTYPE_AAAA		28	/* IPv6 address */
-#define DNS_RTYPE_SRV		33	/* SRV record */
-#define DNS_RTYPE_OPT		41	/* OPT */
-#define DNS_RTYPE_ANY		255	/* all records */
+#define DNS_RTYPE_A             1       /* IPv4 address */
+#define DNS_RTYPE_CNAME         5       /* canonical name */
+#define DNS_RTYPE_AAAA          28      /* IPv6 address */
+#define DNS_RTYPE_SRV           33      /* SRV record */
+#define DNS_RTYPE_OPT           41      /* OPT */
+#define DNS_RTYPE_ANY           255     /* all records */
 
 /* dns rcode values */
-#define DNS_RCODE_NO_ERROR	0	/* no error */
-#define DNS_RCODE_NX_DOMAIN	3	/* non existent domain */
-#define DNS_RCODE_REFUSED	5	/* query refused */
+#define DNS_RCODE_NO_ERROR      0       /* no error */
+#define DNS_RCODE_NX_DOMAIN     3       /* non existent domain */
+#define DNS_RCODE_REFUSED       5       /* query refused */
 
 /* dns flags masks */
-#define DNS_FLAG_TRUNCATED	0x0200	/* mask for truncated flag */
-#define DNS_FLAG_REPLYCODE	0x000F	/* mask for reply code */
+#define DNS_FLAG_TRUNCATED      0x0200  /* mask for truncated flag */
+#define DNS_FLAG_REPLYCODE      0x000F  /* mask for reply code */
 
 /* max number of network preference entries are avalaible from the
  * configuration file.
@@ -82,10 +93,7 @@
 #define SRV_MAX_PREF_NET 5
 
 /* DNS header size */
-#define DNS_HEADER_SIZE		((int)sizeof(struct dns_header))
-
-/* DNS resolution pool size, per resolvers section */
-#define DNS_DEFAULT_RESOLUTION_POOL_SIZE	64
+#define DNS_HEADER_SIZE  ((int)sizeof(struct dns_header))
 
 /* DNS request or response header structure */
 struct dns_header {
@@ -100,254 +108,244 @@
 /* short structure to describe a DNS question */
 /* NOTE: big endian structure */
 struct dns_question {
-	unsigned short	qtype;		/* question type */
-	unsigned short	qclass;		/* query class */
+	unsigned short qtype;   /* question type */
+	unsigned short qclass;  /* query class */
 };
 
 /* NOTE: big endian structure */
 struct dns_query_item {
-	struct list list;
-	char name[DNS_MAX_NAME_SIZE];		/* query name */
-	unsigned short type;			/* question type */
-	unsigned short class;			/* query class */
+	char           name[DNS_MAX_NAME_SIZE]; /* query name */
+	unsigned short type;                    /* question type */
+	unsigned short class;                   /* query class */
+	struct list    list;
 };
 
 /* NOTE: big endian structure */
 struct dns_additional_record {
-	uint8_t name;                           /* domain name, must be 0 (RFC 6891) */
-	uint16_t type;			/* record type DNS_RTYPE_OPT (41) */
-	uint16_t udp_payload_size;	/* maximum size accepted for the response */
-	uint32_t extension;				/* extended rcode and flags, not used for now */
-	uint16_t data_length;		/* data length */
-/* as of today, we don't support yet edns options, that said I already put a placeholder here
- * for this purpose. We may need to define a dns_option_record structure which itself should
- * point to different type of data, based on the extension set (client subnet, tcp keepalive,
- * etc...)*/
-//	struct list options;			/* list of option records */
+	uint8_t  name;             /* domain name, must be 0 (RFC 6891) */
+	uint16_t type;             /* record type DNS_RTYPE_OPT (41) */
+	uint16_t udp_payload_size; /* maximum size accepted for the response */
+	uint32_t extension;        /* extended rcode and flags, not used for now */
+	uint16_t data_length;      /* data length */
+/* as of today, we don't support yet edns options, that said I already put a
+ * placeholder here for this purpose. We may need to define a dns_option_record
+ * structure which itself should point to different type of data, based on the
+ * extension set (client subnet, tcp keepalive, etc...)*/
+//	struct list options;       /* list of option records */
 } __attribute__ ((packed));
 
 /* NOTE: big endian structure */
 struct dns_answer_item {
-	struct list list;
-	char name[DNS_MAX_NAME_SIZE];		/* answer name
-						 * For SRV type, name also includes service
-						 * and protocol value */
-	int16_t type;				/* question type */
-	int16_t class;				/* query class */
-	int32_t ttl;				/* response TTL */
-	int16_t priority;			/* SRV type priority */
-	int16_t weight;				/* SRV type weight */
-	int16_t port;				/* SRV type port */
-	int16_t data_len;			/* number of bytes in target below */
-	struct sockaddr address;		/* IPv4 or IPv6, network format */
-	char target[DNS_MAX_NAME_SIZE];		/* Response data: SRV or CNAME type target */
-	time_t last_seen;			/* When was the answer was last seen */
+	/*For SRV type, name also includes service and protocol value */
+	char            name[DNS_MAX_NAME_SIZE];   /* answer name */
+	int16_t         type;                      /* question type */
+	int16_t         class;                     /* query class */
+	int32_t         ttl;                       /* response TTL */
+	int16_t         priority;                  /* SRV type priority */
+	int16_t         weight;                    /* SRV type weight */
+	int16_t         port;                      /* SRV type port */
+	int16_t         data_len;                  /* number of bytes in target below */
+	struct sockaddr address;                   /* IPv4 or IPv6, network format */
+	char            target[DNS_MAX_NAME_SIZE]; /* Response data: SRV or CNAME type target */
+	time_t          last_seen;                 /* When was the answer was last seen */
+	struct list     list;
 };
 
 struct dns_response_packet {
 	struct dns_header header;
-	struct list query_list;
-	struct list answer_list;
+	struct list       query_list;
+	struct list       answer_list;
 	/* authority and additional_information ignored for now */
 };
 
-/*
- * resolvers section and parameters. It is linked to the name servers
+/* Resolvers section and parameters. It is linked to the name servers
  * servers points to it.
  * current resolution are stored in a FIFO list.
  */
 struct dns_resolvers {
-	struct list list;		/* resolvers list */
-	char *id;			/* resolvers unique identifier */
+	char      *id;                      /* resolvers unique identifier */
 	struct {
-		const char *file;	/* file where the section appears */
-		int line;		/* line where the section appears */
-	} conf;				/* config information */
-	struct list nameserver_list;	/* dns server list */
-	unsigned int accepted_payload_size;	/* maximum payload size we accept for responses */
-	int count_nameservers;			/* total number of nameservers in a resolvers section */
-	int resolve_retries;		/* number of retries before giving up */
-	struct {			/* time to: */
-		int retry;		/*   wait for a response before retrying */
+		const char *file;           /* file where the section appears */
+		int         line;           /* line where the section appears */
+	} conf;                             /* config information */
+	struct list  nameservers;           /* dns server list */
+	unsigned int accepted_payload_size; /* maximum payload size we accept for responses */
+	int          nb_nameservers;        /* total number of active nameservers in a resolvers section */
+	int          resolve_retries;       /* number of retries before giving up */
+	struct {                            /* time to: */
+		int resolve;                /*     wait between 2 queries for the same resolution */
+		int retry;                  /*     wait for a response before retrying */
 	} timeout;
-	struct {			/* time to hold current data when */
-		int valid;		/*   a response is valid */
-		int nx;                 /*   a response doesn't exist */
-		int timeout;            /*   no answer was delivered */
-		int refused;            /*   dns server refused to answer */
-		int other;              /*   other dns response errors */
-		int obsolete;		/*   an answer hasn't been seen */
+	struct {                            /* time to hold current data when */
+		int valid;                  /*     a response is valid */
+		int nx;                     /*     a response doesn't exist */
+		int timeout;                /*     no answer was delivered */
+		int refused;                /*     dns server refused to answer */
+		int other;                  /*     other dns response errors */
+		int obsolete;               /*     an answer hasn't been seen */
 	} hold;
-	struct task *t;			/* timeout management */
-	int resolution_pool_size;	/* size of the resolution pool associated to this resolvers section */
+	struct task *t;                     /* timeout management */
 	struct {
-		struct list pool;	/* resolution pool dedicated to this resolvers section */
-		struct list wait;	/* resolutions managed to this resolvers section */
-		struct list curr;	/* current running resolutions */
-	} resolution;
-	struct eb_root query_ids;	/* tree to quickly lookup/retrieve query ids currently in use */
-					/* used by each nameserver, but stored in resolvers since there must */
-					/* be a unique relation between an eb_root and an eb_node (resolution) */
+		struct list wait;           /* resolutions managed to this resolvers section */
+		struct list curr;           /* current running resolutions */
+	} resolutions;
+	struct eb_root query_ids;           /* tree to quickly lookup/retrieve query ids currently in use
+                                             * used by each nameserver, but stored in resolvers since there must
+                                             * be a unique relation between an eb_root and an eb_node (resolution) */
+	struct list list;                   /* resolvers list */
 };
 
-/*
- * structure describing a name server used during name resolution.
+/* Structure describing a name server used during name resolution.
  * A name server belongs to a resolvers section.
  */
 struct dns_nameserver {
-	struct list list;		/* nameserver chained list */
-	char *id;			/* nameserver unique identifier */
+	char *id;                       /* nameserver unique identifier */
 	struct {
-		const char *file;	/* file where the section appears */
-		int line;		/* line where the section appears */
-	} conf;				/* config information */
-	struct dns_resolvers *resolvers;
-	struct dgram_conn *dgram;		/* transport layer */
-	struct sockaddr_storage addr;	/* IP address */
-	struct {			/* numbers relted to this name server: */
-		long int sent;		/* - queries sent */
-		long int valid;		/* - valid response */
-		long int update;	/* - valid response used to update server's IP */
-		long int cname;		/* - CNAME response requiring new resolution */
-		long int cname_error;	/* - error when resolving CNAMEs */
-		long int any_err;	/* - void response (usually because ANY qtype) */
-		long int nx;		/* - NX response */
-		long int timeout;	/* - queries which reached timeout */
-		long int refused;	/* - queries refused */
-		long int other;		/* - other type of response */
-		long int invalid;	/* - malformed DNS response */
-		long int too_big;	/* - too big response */
-		long int outdated;	/* - outdated response (server slower than the other ones) */
-		long int truncated;	/* - truncated response */
+		const char *file;       /* file where the section appears */
+		int         line;       /* line where the section appears */
+	} conf;                         /* config information */
+
+	struct dns_resolvers   *resolvers;
+	struct dgram_conn      *dgram;  /* transport layer */
+	struct sockaddr_storage addr;   /* IP address */
+
+	struct {                        /* numbers relted to this name server: */
+		long long sent;         /* - queries sent */
+		long long snd_error;    /* - sending errors */
+		long long valid;        /* - valid response */
+		long long update;       /* - valid response used to update server's IP */
+		long long cname;        /* - CNAME response requiring new resolution */
+		long long cname_error;  /* - error when resolving CNAMEs */
+		long long any_err;      /* - void response (usually because ANY qtype) */
+		long long nx;           /* - NX response */
+		long long timeout;      /* - queries which reached timeout */
+		long long refused;      /* - queries refused */
+		long long other;        /* - other type of response */
+		long long invalid;      /* - malformed DNS response */
+		long long too_big;      /* - too big response */
+		long long outdated;     /* - outdated response (server slower than the other ones) */
+		long long truncated;    /* - truncated response */
 	} counters;
+	struct list list;               /* nameserver chained list */
 };
 
 struct dns_options {
-	int family_prio;	/* which IP family should the resolver use when both are returned */
+	int family_prio; /* which IP family should the resolver use when both are returned */
 	struct {
 		int family;
 		union {
-			struct in_addr in4;
+			struct in_addr  in4;
 			struct in6_addr in6;
 		} addr;
 		union {
-			struct in_addr in4;
+			struct in_addr  in4;
 			struct in6_addr in6;
 		} mask;
 	} pref_net[SRV_MAX_PREF_NET];
-	int pref_net_nb;               /* The number of registered prefered networks. */
+	int pref_net_nb; /* The number of registered prefered networks. */
 };
 
-/*
- * resolution structure associated to single server and used to manage name resolution for
- * this server.
- * The only link between the resolution and a nameserver is through the query_id.
+/* Resolution structure associated to single server and used to manage name
+ * resolution for this server.
+ * The only link between the resolution and a nameserver is through the
+ * query_id.
  */
 struct dns_resolution {
-	struct list list;		/* resolution list */
-	struct {
-		struct list wait;	/* list of standby requesters for this resolution */
-		struct list curr;	/* list of requesters currently active on this resolution */
-	} requester;
-	int (*requester_cb)(struct dns_resolution *, struct dns_nameserver *);
-					/* requester callback for valid response */
-	int (*requester_error_cb)(struct dns_resolution *, int);
-					/* requester callback, for error management */
-	int uuid;			/* unique id (used for debugging purpose) */
-	char *hostname_dn;		/* server hostname in domain name label format */
-	int hostname_dn_len;		/* server domain name label len */
-	unsigned int last_resolution;	/* time of the lastest valid resolution */
-	unsigned int last_sent_packet;	/* time of the latest DNS packet sent */
-	unsigned int last_status_change;	/* time of the latest DNS resolution status change */
-	int query_id;			/* DNS query ID dedicated for this resolution */
-	struct eb32_node qid;		/* ebtree query id */
-	int query_type;
-		/* query type to send. By default DNS_RTYPE_A or DNS_RTYPE_AAAA depending on resolver_family_priority */
-	int status;			/* status of the resolution being processed RSLV_STATUS_* */
-	int step;			/* */
-	int try;			/* current resolution try */
-	int try_cname;			/* number of CNAME requests sent */
-	int nb_responses;		/* count number of responses received */
-	unsigned long long revision;    /* updated for each update */
-	struct dns_response_packet response;	/* structure hosting the DNS response */
-	struct dns_query_item response_query_records[DNS_MAX_QUERY_RECORDS];		/* <response> query records */
+	struct dns_resolvers *resolvers;           /* pointer to the resolvers structure owning the resolution */
+	struct list           requesters;          /* list of requesters using this resolution */
+	int                   uuid;                /* unique id (used for debugging purpose) */
+	char                 *hostname_dn;         /* server hostname in domain name label format */
+	int                   hostname_dn_len;     /* server domain name label len */
+	unsigned int          last_resolution;     /* time of the last resolution */
+	unsigned int          last_query;          /* time of the last query sent */
+	unsigned int          last_valid;          /* time of the last valid response */
+	int                   query_id;            /* DNS query ID dedicated for this resolution */
+	struct eb32_node      qid;                 /* ebtree query id */
+	int                   prefered_query_type; /* prefered query type */
+	int                   query_type;          /* current query type  */
+	int                   status;              /* status of the resolution being processed RSLV_STATUS_* */
+	int                   step;                /* RSLV_STEP_* */
+	int                   try;                 /* current resolution try */
+	int                   nb_queries;          /* count number of queries sent */
+	int                   nb_responses;        /* count number of responses received */
+
+	struct dns_response_packet response; /* structure hosting the DNS response */
+	struct dns_query_item response_query_records[DNS_MAX_QUERY_RECORDS]; /* <response> query records */
+
+	struct list list; /* resolution list */
 };
 
-/*
- * structure used to describe the owner of a DNS resolution.
- */
+/* Structure used to describe the owner of a DNS resolution. */
 struct dns_requester {
-	struct list list;		/* requester list */
-	enum obj_type *requester;	/* pointer to the requester */
-	int prefered_query_type;	/* prefered query type */
-	int (*requester_cb)(struct dns_requester *, struct dns_nameserver *);
-					/* requester callback for valid response */
-	int (*requester_error_cb)(struct dns_requester *, int);
-					/* requester callback, for error management */
+	enum obj_type         *owner;       /* pointer to the owner (server or dns_srvrq) */
+	struct dns_resolution *resolution;  /* pointer to the owned DNS resolution */
+
+	int (*requester_cb)(struct dns_requester *, struct dns_nameserver *); /* requester callback for valid response */
+	int (*requester_error_cb)(struct dns_requester *, int);               /* requester callback, for error management */
+
+	struct list list; /* requester list */
 };
 
-/* last resolution status code */
+/* Last resolution status code */
 enum {
-	RSLV_STATUS_NONE	= 0,	/* no resolution occured yet */
-	RSLV_STATUS_VALID,		/* no error */
-	RSLV_STATUS_INVALID,		/* invalid responses */
-	RSLV_STATUS_ERROR,		/* error */
-	RSLV_STATUS_NX,			/* NXDOMAIN */
-	RSLV_STATUS_REFUSED,		/* server refused our query */
-	RSLV_STATUS_TIMEOUT,		/* no response from DNS servers */
-	RSLV_STATUS_OTHER,		/* other errors */
+	RSLV_STATUS_NONE = 0,  /* no resolution occured yet */
+	RSLV_STATUS_VALID,     /* no error */
+	RSLV_STATUS_INVALID,   /* invalid responses */
+	RSLV_STATUS_ERROR,     /* error */
+	RSLV_STATUS_NX,        /* NXDOMAIN */
+	RSLV_STATUS_REFUSED,   /* server refused our query */
+	RSLV_STATUS_TIMEOUT,   /* no response from DNS servers */
+	RSLV_STATUS_OTHER,     /* other errors */
 };
 
-/* current resolution step */
+/* Current resolution step */
 enum {
-	RSLV_STEP_NONE		= 0,	/* nothing happening currently */
-	RSLV_STEP_RUNNING,		/* resolution is running */
+	RSLV_STEP_NONE = 0,    /* nothing happening currently */
+	RSLV_STEP_RUNNING,     /* resolution is running */
 };
 
-/* return codes after analyzing a DNS response */
+/* Return codes after analyzing a DNS response */
 enum {
-	DNS_RESP_VALID		= 0,	/* valid response */
-	DNS_RESP_INVALID,		/* invalid response (various type of errors can trigger it) */
-	DNS_RESP_ERROR,			/* DNS error code */
-	DNS_RESP_NX_DOMAIN,		/* resolution unsuccessful */
-	DNS_RESP_REFUSED,		/* DNS server refused to answer */
-	DNS_RESP_ANCOUNT_ZERO,		/* no answers in the response */
-	DNS_RESP_WRONG_NAME,		/* response does not match query name */
-	DNS_RESP_CNAME_ERROR,		/* error when resolving a CNAME in an atomic response */
-	DNS_RESP_TIMEOUT,		/* DNS server has not answered in time */
-	DNS_RESP_TRUNCATED,		/* DNS response is truncated */
-	DNS_RESP_NO_EXPECTED_RECORD,	/* No expected records were found in the response */
-	DNS_RESP_QUERY_COUNT_ERROR,	/* we did not get the expected number of queries in the response */
-	DNS_RESP_INTERNAL,		/* internal resolver error */
+	DNS_RESP_VALID = 0,          /* valid response */
+	DNS_RESP_INVALID,            /* invalid response (various type of errors can trigger it) */
+	DNS_RESP_ERROR,              /* DNS error code */
+	DNS_RESP_NX_DOMAIN,          /* resolution unsuccessful */
+	DNS_RESP_REFUSED,            /* DNS server refused to answer */
+	DNS_RESP_ANCOUNT_ZERO,       /* no answers in the response */
+	DNS_RESP_WRONG_NAME,         /* response does not match query name */
+	DNS_RESP_CNAME_ERROR,        /* error when resolving a CNAME in an atomic response */
+	DNS_RESP_TIMEOUT,            /* DNS server has not answered in time */
+	DNS_RESP_TRUNCATED,          /* DNS response is truncated */
+	DNS_RESP_NO_EXPECTED_RECORD, /* No expected records were found in the response */
+        DNS_RESP_QUERY_COUNT_ERROR,  /* we did not get the expected number of queries in the response */
+        DNS_RESP_INTERNAL,           /* internal resolver error */
 };
 
-/* return codes after searching an IP in a DNS response buffer, using a family preference */
+/* Return codes after searching an IP in a DNS response buffer, using a family
+ * preference
+ */
 enum {
-	DNS_UPD_NO 		= 1,	/* provided IP was found and preference is matched
-					 * OR provided IP found and preference is not matched, but no IP
-					 *    matching preference was found */
-	DNS_UPD_SRVIP_NOT_FOUND,	/* provided IP not found
-					 * OR provided IP found and preference is not match and an IP
-					 *    matching preference was found */
-	DNS_UPD_CNAME,			/* CNAME without any IP provided in the response */
-	DNS_UPD_NAME_ERROR,		/* name in the response did not match the query */
-	DNS_UPD_NO_IP_FOUND,		/* no IP could be found in the response */
-	DNS_UPD_OBSOLETE_IP,		/* The server IP was obsolete, and no other IP was found */
+	DNS_UPD_NO = 1,           /* provided IP was found and preference is matched
+                                   * OR provided IP found and preference is not matched, but no IP
+                                   * matching preference was found */
+	DNS_UPD_SRVIP_NOT_FOUND,  /* provided IP not found
+                                   * OR provided IP found and preference is not match and an IP
+                                   * matching preference was found */
+	DNS_UPD_CNAME,            /* CNAME without any IP provided in the response */
+	DNS_UPD_NAME_ERROR,       /* name in the response did not match the query */
+	DNS_UPD_NO_IP_FOUND,      /* no IP could be found in the response */
+	DNS_UPD_OBSOLETE_IP,      /* The server IP was obsolete, and no other IP was found */
 };
 
 struct dns_srvrq {
-	enum obj_type obj_type;			/* object type == OBJ_TYPE_SRVRQ */
-	struct dns_resolvers *resolvers;	/* pointer to the resolvers structure used for this server template */
-
-	struct dns_resolution *resolution;	/* server name resolution */
-
-	struct proxy *proxy;			/* associated proxy */
-	char *name;
-	char *hostname_dn;			/* server hostname in Domain Name format */
-	int hostname_dn_len;			/* string length of the server hostname in Domain Name format */
-	struct dns_requester *dns_requester;	/* used to link to its DNS resolution */
-	int inter;				/* time in ms */
-	struct list list;			/* Next SRV RQ for the same proxy */
+	enum obj_type         obj_type;         /* object type == OBJ_TYPE_SRVRQ */
+	struct dns_resolvers *resolvers;        /* pointer to the resolvers structure used for this server template */
+	struct proxy         *proxy;            /* associated proxy */
+	char                 *name;
+	char                 *hostname_dn;      /* server hostname in Domain Name format */
+	int                   hostname_dn_len;  /* string length of the server hostname in Domain Name format */
+	struct dns_requester *dns_requester;    /* used to link to its DNS resolution */
+	struct list list;                       /* Next SRV RQ for the same proxy */
 };
 
 #endif /* _TYPES_DNS_H */
diff --git a/include/types/global.h b/include/types/global.h
index 37fbaca..03a7282 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -183,7 +183,6 @@
 extern struct list global_listener_queue; /* list of the temporarily limited listeners */
 extern struct task *global_listener_queue_task;
 extern unsigned int warned;     /* bitfield of a few warnings to emit just once */
-extern struct list dns_resolvers;
 
 /* bit values to go with "warned" above */
 #define WARN_BLOCK_DEPRECATED       0x00000001
diff --git a/include/types/proxy.h b/include/types/proxy.h
index a4f3b9e..5306a3b 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -438,7 +438,6 @@
 						 * name is used
 						 */
 	struct list filter_configs;		/* list of the filters that are declared on this proxy */
-	struct list srvrq_list;			/* List of SRV requests associated with this proxy */
 };
 
 struct switching_rule {
diff --git a/include/types/server.h b/include/types/server.h
index cdc3359..69397c1 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -262,7 +262,6 @@
 	char *hostname_dn;			/* server hostname in Domain Name format */
 	int hostname_dn_len;			/* sting lenght of the server hostname in Domain Name format */
 	char *lastaddr;				/* the address string provided by the server-state file */
-	struct dns_resolution *resolution;	/* server name resolution */
 	struct dns_options dns_opts;
 	struct sockaddr_storage init_addr;	/* plain IP address specified on the init-addr line */
 	unsigned int init_addr_methods;		/* initial address setting, 3-bit per method, ends at 0, enough to store 10 entries */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index e7bca18..8b1d8f8 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2149,14 +2149,13 @@
 		curr_resolvers->hold.obsolete = 0;
 		/* default hold period for valid is 10s */
 		curr_resolvers->hold.valid = 10000;
-		curr_resolvers->timeout.retry = 1000;
+		curr_resolvers->timeout.resolve = 1000;
+		curr_resolvers->timeout.retry   = 1000;
 		curr_resolvers->resolve_retries = 3;
-		/* default resolution pool size */
-		curr_resolvers->resolution_pool_size = DNS_DEFAULT_RESOLUTION_POOL_SIZE;
-		LIST_INIT(&curr_resolvers->nameserver_list);
-		LIST_INIT(&curr_resolvers->resolution.curr);
-		LIST_INIT(&curr_resolvers->resolution.wait);
-		LIST_INIT(&curr_resolvers->resolution.pool);
+		curr_resolvers->nb_nameservers  = 0;
+		LIST_INIT(&curr_resolvers->nameservers);
+		LIST_INIT(&curr_resolvers->resolutions.curr);
+		LIST_INIT(&curr_resolvers->resolutions.wait);
 	}
 	else if (strcmp(args[0], "nameserver") == 0) { /* nameserver definition */
 		struct sockaddr_storage *sk;
@@ -2178,7 +2177,7 @@
 			goto out;
 		}
 
-		list_for_each_entry(newnameserver, &curr_resolvers->nameserver_list, list) {
+		list_for_each_entry(newnameserver, &curr_resolvers->nameservers, list) {
 			/* Error if two resolvers owns the same name */
 			if (strcmp(newnameserver->id, args[1]) == 0) {
 				Alert("Parsing [%s:%d]: nameserver '%s' has same name as another nameserver (declared at %s:%d).\n",
@@ -2194,8 +2193,7 @@
 		}
 
 		/* the nameservers are linked backward first */
-		LIST_ADDQ(&curr_resolvers->nameserver_list, &newnameserver->list);
-		curr_resolvers->count_nameservers++;
+		LIST_ADDQ(&curr_resolvers->nameservers, &newnameserver->list);
 		newnameserver->resolvers = curr_resolvers;
 		newnameserver->conf.file = strdup(file);
 		newnameserver->conf.line = linenum;
@@ -2291,13 +2289,10 @@
 		curr_resolvers->accepted_payload_size = i;
 	}
 	else if (strcmp(args[0], "resolution_pool_size") == 0) {
-		if (!*args[1]) {
-			Alert("parsing [%s:%d] : '%s' expects <nb> as argument.\n",
-				file, linenum, args[0]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-		curr_resolvers->resolution_pool_size = atoi(args[1]);
+		Warning("parsing [%s:%d] : '%s' directive is now deprecated and ignored.\n",
+			file, linenum, args[0]);
+		err_code |= ERR_WARN;
+		goto out;
 	}
 	else if (strcmp(args[0], "resolve_retries") == 0) {
 		if (!*args[1]) {
@@ -2310,14 +2305,15 @@
 	}
 	else if (strcmp(args[0], "timeout") == 0) {
 		if (!*args[1]) {
-			Alert("parsing [%s:%d] : '%s' expects 'retry' and <time> as arguments.\n",
+			Alert("parsing [%s:%d] : '%s' expects 'retry' or 'resolve' and <time> as arguments.\n",
 				file, linenum, args[0]);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
-		else if (strcmp(args[1], "retry") == 0) {
+		else if (strcmp(args[1], "retry") == 0 ||
+			 strcmp(args[1], "resolve") == 0) {
 			const char *res;
-			unsigned int timeout_retry;
+			unsigned int tout;
 
 			if (!*args[2]) {
 				Alert("parsing [%s:%d] : '%s %s' expects <time> as argument.\n",
@@ -2325,17 +2321,20 @@
 				err_code |= ERR_ALERT | ERR_FATAL;
 				goto out;
 			}
-			res = parse_time_err(args[2], &timeout_retry, TIME_UNIT_MS);
+			res = parse_time_err(args[2], &tout, TIME_UNIT_MS);
 			if (res) {
 				Alert("parsing [%s:%d]: unexpected character '%c' in argument to <%s %s>.\n",
 					file, linenum, *res, args[0], args[1]);
 				err_code |= ERR_ALERT | ERR_FATAL;
 				goto out;
 			}
-			curr_resolvers->timeout.retry = timeout_retry;
+			if (args[1][2] == 't')
+				curr_resolvers->timeout.retry = tout;
+			else
+				curr_resolvers->timeout.resolve = tout;
 		}
 		else {
-			Alert("parsing [%s:%d] : '%s' expects 'retry' and <time> as arguments got '%s'.\n",
+			Alert("parsing [%s:%d] : '%s' expects 'retry' or 'resolve' and <time> as arguments got '%s'.\n",
 				file, linenum, args[0], args[1]);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
@@ -7394,7 +7393,6 @@
 	unsigned int next_pxid = 1;
 	struct bind_conf *bind_conf;
 	char *err;
-	struct dns_resolvers *curr_resolvers;
 	struct cfg_postparser *postparser;
 
 	bind_conf = NULL;
@@ -7418,15 +7416,6 @@
 
 	pool2_capture = create_pool("capture", global.tune.cookie_len, MEM_F_SHARED);
 
-	/* allocate pool of resolution per resolvers */
-	list_for_each_entry(curr_resolvers, &dns_resolvers, list) {
-		if (dns_alloc_resolution_pool(curr_resolvers) != 0) {
-			/* error message is already displayed by dns_alloc_resolution_pool() */
-			err_code |= ERR_ALERT | ERR_ABORT;
-			goto out;
-		}
-	}
-
 	/* Post initialisation of the users and groups lists. */
 	err_code = userlist_postinit();
 	if (err_code != ERR_NONE)
@@ -8362,64 +8351,6 @@
 				newsrv->trackit = NULL;
 			}
 
-			/*
-			 * resolve server's resolvers name and update the resolvers pointer
-			 * accordingly
-			 */
-			if (newsrv->resolvers_id) {
-				struct dns_resolvers *curr_resolvers;
-				int found;
-
-				found = 0;
-				list_for_each_entry(curr_resolvers, &dns_resolvers, list) {
-					if (!strcmp(curr_resolvers->id, newsrv->resolvers_id)) {
-						found = 1;
-						break;
-					}
-				}
-
-				if (!found) {
-					Alert("config : %s '%s', server '%s': unable to find required resolvers '%s'\n",
-					proxy_type_str(curproxy), curproxy->id,
-					newsrv->id, newsrv->resolvers_id);
-					cfgerr++;
-				} else {
-					if (newsrv->srvrq) {
-						if (!newsrv->srvrq->resolvers) {
-							newsrv->srvrq->resolvers = curr_resolvers;
-							if (dns_link_resolution(newsrv->srvrq,
-							    OBJ_TYPE_SRVRQ, NULL) != 0) {
-								Alert("config : %s '%s', server '%s': unable to set DNS resolution\n",
-								    proxy_type_str(curproxy), curproxy->id,
-								    newsrv->id);
-								cfgerr++;
-							}
-						}
-
-					}
-					if (newsrv->srvrq || newsrv->hostname_dn) {
-						newsrv->resolvers = curr_resolvers;
-						if (dns_link_resolution(newsrv, OBJ_TYPE_SERVER, NULL) != 0) {
-							Alert("config : %s '%s', server '%s': unable to set DNS resolution\n",
-							proxy_type_str(curproxy), curproxy->id,
-							newsrv->id);
-							cfgerr++;
-						}
-					}
-				}
-			}
-			else {
-				/* if no resolvers section associated to this server
-				 * we can clean up the associated resolution structure
-				 */
-				if (newsrv->resolution) {
-					free(newsrv->resolution->hostname_dn);
-					newsrv->resolution->hostname_dn = NULL;
-					free(newsrv->resolution);
-					newsrv->resolution = NULL;
-				}
-			}
-
 		next_srv:
 			newsrv = newsrv->next;
 		}
diff --git a/src/checks.c b/src/checks.c
index 880b47b..ed9a49a 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -667,8 +667,7 @@
 		 * might be due to a server IP change.
 		 * Let's trigger a DNS resolution if none are currently running.
 		 */
-		if ((check->server->resolution)	&& (check->server->resolution->step == RSLV_STEP_NONE))
-			dns_trigger_resolution(check->server->resolution);
+		dns_trigger_resolution(check->server->dns_requester);
 
 	}
 	else if ((conn->flags & (CO_FL_CONNECTED|CO_FL_WAIT_L6_CONN)) == CO_FL_WAIT_L6_CONN) {
diff --git a/src/dns.c b/src/dns.c
index aa17846..2d684b0 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -19,18 +19,15 @@
 
 #include <sys/types.h>
 
+#include <common/errors.h>
 #include <common/time.h>
 #include <common/ticks.h>
 #include <common/net_helper.h>
 
-#include <import/lru.h>
-#include <import/xxhash.h>
-
 #include <types/applet.h>
 #include <types/cli.h>
 #include <types/global.h>
 #include <types/dns.h>
-#include <types/proto_udp.h>
 #include <types/stats.h>
 
 #include <proto/channel.h>
@@ -42,955 +39,359 @@
 #include <proto/server.h>
 #include <proto/task.h>
 #include <proto/proto_udp.h>
+#include <proto/proxy.h>
 #include <proto/stream_interface.h>
 
-struct list dns_resolvers = LIST_HEAD_INIT(dns_resolvers);
-struct dns_resolution *resolution = NULL;
-
-static int64_t dns_query_id_seed;	/* random seed */
-
-static struct lru64_head *dns_lru_tree;
-static int dns_cache_size = 1024;       /* arbitrary DNS cache size */
-
-static struct pool_head *dns_answer_item_pool;
-
-/* proto_udp callback functions for a DNS resolution */
-struct dgram_data_cb resolve_dgram_cb = {
-	.recv = dns_resolve_recv,
-	.send = dns_resolve_send,
-};
+struct list dns_resolvers  = LIST_HEAD_INIT(dns_resolvers);
+struct list dns_srvrq_list = LIST_HEAD_INIT(dns_srvrq_list);
 
-/* local function prototypes */
-static int dns_run_resolution(struct dns_requester *requester);
+static int64_t dns_query_id_seed = 0;	/* random seed */
+static struct pool_head *dns_answer_item_pool = NULL;
+static struct pool_head *dns_resolution_pool  = NULL;
+static unsigned int resolution_uuid = 1;
 
-#if DEBUG
-/*
- * go through the resolutions associated to a resolvers section and print the ID and hostname in
- * domain name format
- * should be used for debug purpose only
+/* Returns a pointer to the resolvers matching the id <id>. NULL is returned if
+ * no match is found.
  */
-void dns_print_current_resolutions(struct dns_resolvers *resolvers)
-{
-	list_for_each_entry(resolution, &resolvers->resolution.curr, list) {
-		printf("  resolution %d for %s\n", resolution->query_id, resolution->hostname_dn);
-	}
-}
-#endif
-
-void dump_dns_config()
+struct dns_resolvers *find_resolvers_by_id(const char *id)
 {
-	struct dns_resolvers *curr_resolvers = NULL;
-	struct dns_nameserver *curr_nameserver = NULL;
-	struct dns_resolution *curr_resolution = NULL;
-	struct dns_requester *curr_requester = NULL;
+	struct dns_resolvers *res;
 
-	printf("===============\n");
-	list_for_each_entry(curr_resolvers, &dns_resolvers, list) {
-		printf("Resolvers: %s\n", curr_resolvers->id);
-
-		printf("  nameservers:\n");
-		list_for_each_entry(curr_nameserver, &curr_resolvers->nameserver_list, list) {
-			printf("    %s\n", curr_nameserver->id);
-		}
-
-/*
-		printf("  resolution.pool list:\n");
-		list_for_each_entry(curr_resolution, &curr_resolvers->resolution.pool, list) {
-			printf("    %p\n", curr_resolution);
-		}
-*/
-
-		printf("  resolution.wait list:\n");
-		list_for_each_entry(curr_resolution, &curr_resolvers->resolution.wait, list) {
-			printf("    %p %s\n", curr_resolution, curr_resolution->hostname_dn);
-			printf("      requester.wait list:\n");
-			list_for_each_entry(curr_requester, &curr_resolution->requester.wait, list) {
-				switch (obj_type(curr_requester->requester)) {
-					case OBJ_TYPE_SERVER:
-						printf("        %p SRV   %s %d\n", curr_requester, objt_server(curr_requester->requester)->id, curr_requester->prefered_query_type);
-						break;
-					case OBJ_TYPE_SRVRQ:
-						printf("        %p SRVRQ %s %d\n", curr_requester, objt_dns_srvrq(curr_requester->requester)->name, curr_requester->prefered_query_type);
-						break;
-					case OBJ_TYPE_NONE:
-					default:
-						;;
-				}
-			}
-			printf("      requester.curr list:\n");
-			list_for_each_entry(curr_requester, &curr_resolution->requester.curr, list) {
-				switch (obj_type(curr_requester->requester)) {
-					case OBJ_TYPE_SERVER:
-						printf("        %p SRV   %s %d\n", curr_requester, objt_server(curr_requester->requester)->id, curr_requester->prefered_query_type);
-						break;
-					case OBJ_TYPE_SRVRQ:
-						printf("        %p SRVRQ %s %d\n", curr_requester, objt_dns_srvrq(curr_requester->requester)->name, curr_requester->prefered_query_type);
-						break;
-					case OBJ_TYPE_NONE:
-					default:
-						;;
-				}
-			}
-		}
-		printf("  resolution.curr list:\n");
-		list_for_each_entry(curr_resolution, &curr_resolvers->resolution.curr, list) {
-			printf("    %p %s\n", curr_resolution, curr_resolution->hostname_dn);
-			printf("      requester.wait list:\n");
-			list_for_each_entry(curr_requester, &curr_resolution->requester.wait, list) {
-				switch (obj_type(curr_requester->requester)) {
-					case OBJ_TYPE_SERVER:
-						printf("        %p SRV   %s %d\n", curr_requester, objt_server(curr_requester->requester)->id, curr_requester->prefered_query_type);
-						break;
-					case OBJ_TYPE_SRVRQ:
-						printf("        %p SRVRQ %s %d\n", curr_requester, objt_dns_srvrq(curr_requester->requester)->name, curr_requester->prefered_query_type);
-						break;
-					case OBJ_TYPE_NONE:
-					default:
-						;;
-				}
-			}
-			printf("      requester.curr list:\n");
-			list_for_each_entry(curr_requester, &curr_resolution->requester.curr, list) {
-				switch (obj_type(curr_requester->requester)) {
-					case OBJ_TYPE_SERVER:
-						printf("        %p SRV   %s %d\n", curr_requester, objt_server(curr_requester->requester)->id, curr_requester->prefered_query_type);
-						break;
-					case OBJ_TYPE_SRVRQ:
-						printf("        %p SRVRQ %s %d\n", curr_requester, objt_dns_srvrq(curr_requester->requester)->name, curr_requester->prefered_query_type);
-						break;
-					case OBJ_TYPE_NONE:
-					default:
-						;;
-				}
-			}
-		}
+	list_for_each_entry(res, &dns_resolvers, list) {
+		if (!strcmp(res->id, id))
+			return res;
 	}
-
-	printf("===============\n");
+	return NULL;
 }
 
-/*
- * Initiates a new name resolution:
- *  - generates a query id
- *  - configure the resolution structure
- *  - startup the resolvers task if required
- *
- * returns:
- *  -  0 if everything started properly
- *  - -1 in case of error or if resolution already running
+/* Returns a pointer on the SRV request matching the name <name> for the proxy
+ * <px>. NULL is returned if no match is found.
  */
-int dns_trigger_resolution(struct dns_resolution *resolution)
+struct dns_srvrq *find_srvrq_by_name(const char *name, struct proxy *px)
 {
-	struct dns_requester *requester = NULL, *tmprequester;
-	struct dns_resolvers *resolvers = NULL;
-	int inter, valid_period;
+	struct dns_srvrq *srvrq;
 
-	/* process the element of the wait queue */
-	list_for_each_entry_safe(requester, tmprequester, &resolution->requester.wait, list) {
-		inter = 0;
-
-		switch (obj_type(requester->requester)) {
-			case OBJ_TYPE_SERVER:
-				valid_period = objt_server(requester->requester)->check.inter;
-				resolvers = objt_server(requester->requester)->resolvers;
-				break;
-			case OBJ_TYPE_SRVRQ:
-				valid_period = objt_dns_srvrq(requester->requester)->inter;
-				resolvers = objt_dns_srvrq(requester->requester)->resolvers;
-				break;
-			case OBJ_TYPE_NONE:
-			default:
-				return -1;
-		}
-
-		if (resolvers->hold.valid < valid_period)
-			inter = resolvers->hold.valid;
-		else
-			inter = valid_period;
-
-		/* if data is fresh enough, let's use it */
-		if (!tick_is_expired(tick_add(resolution->last_resolution, inter), now_ms)) {
-			/* we only use cache if the response there is valid.
-			 * If not valid, we run the resolution and move the requester to
-			 * the run queue. */
-			if (resolution->status != RSLV_STATUS_VALID) {
-				LIST_DEL(&requester->list);
-				LIST_ADDQ(&resolution->requester.curr, &requester->list);
-				dns_run_resolution(requester);
-				continue;
-			}
-
-			requester->requester_cb(requester, NULL);
-			resolvers = NULL;
-		}
-		else {
-			LIST_DEL(&requester->list);
-			LIST_ADDQ(&resolution->requester.curr, &requester->list);
-			dns_run_resolution(requester);
-		}
+	list_for_each_entry(srvrq, &dns_srvrq_list, list) {
+		if (srvrq->proxy == px && !strcmp(srvrq->name, name))
+			return srvrq;
 	}
-
-	if (resolvers)
-		dns_update_resolvers_timeout(resolvers);
-
-	return 0;
+	return NULL;
 }
 
-/*
- * Prepare and send a DNS resolution.
- *
- * Return code:
- * -  0 if no error occured
- * - -1 in case of error
- */
-static int
-dns_run_resolution(struct dns_requester *requester)
+/* Allocates a new SRVRQ for the given server with the name <fqdn>. It returns
+ * NULL if an error occurred. */
+struct dns_srvrq *new_dns_srvrq(struct server *srv, char *fqdn)
 {
-	struct dns_resolution *resolution;
-	struct dns_resolvers *resolvers;
-	int query_id, query_type, i;
-	struct proxy *proxy;
+	struct proxy     *px    = srv->proxy;
+	struct dns_srvrq *srvrq = NULL;
+	int fqdn_len, hostname_dn_len;
 
-	resolution = NULL;
-	resolvers = NULL;
-	proxy = NULL;
-	query_type = -1;
-	switch (obj_type(requester->requester)) {
-		case OBJ_TYPE_SERVER:
-			resolution = objt_server(requester->requester)->resolution;
-			resolvers = objt_server(requester->requester)->resolvers;
-			proxy = objt_server(requester->requester)->proxy;
-			query_type = requester->prefered_query_type;
-			break;
-		case OBJ_TYPE_SRVRQ:
-			resolution = objt_dns_srvrq(requester->requester)->resolution;
-			resolvers = objt_dns_srvrq(requester->requester)->resolvers;
-			proxy = objt_dns_srvrq(requester->requester)->proxy;
-			query_type = DNS_RTYPE_SRV;
-			break;
-		case OBJ_TYPE_NONE:
-		default:
-			return -1;
+	fqdn_len = strlen(fqdn);
+	hostname_dn_len = dns_str_to_dn_label(fqdn, fqdn_len + 1, trash.str, trash.size);
+	if (hostname_dn_len == -1) {
+		Alert("config : %s '%s', server '%s': failed to parse FQDN '%s'\n",
+		      proxy_type_str(px), px->id, srv->id, fqdn);
+		goto err;
 	}
 
-	/*
-	 * Avoid sending requests for resolutions that don't yet have
-	 * an hostname, ie resolutions linked to servers that do not yet
-	 * have an fqdn
-	 */
-	if (!resolution->hostname_dn)
-		return 0;
-
-	/*
-	 * check if a resolution has already been started for this server
-	 * return directly to avoid resolution pill up.
-	 */
-	if (resolution->step != RSLV_STEP_NONE)
-		return 0;
-
-	/* generates a query id */
-	i = 0;
-	do {
-		query_id = dns_rnd16();
-		/* we do try only 100 times to find a free query id */
-		if (i++ > 100) {
-			chunk_printf(&trash, "could not generate a query id for %s, in resolvers %s",
-						resolution->hostname_dn, resolvers->id);
+	if ((srvrq = calloc(1, sizeof(*srvrq))) == NULL) {
+		Alert("config : %s '%s', server '%s': out of memory\n",
+		      proxy_type_str(px), px->id, srv->id);
+		goto err;
+	}
+	srvrq->obj_type        = OBJ_TYPE_SRVRQ;
+	srvrq->proxy           = px;
+	srvrq->name            = strdup(fqdn);
+	srvrq->hostname_dn     = strdup(trash.str);
+	srvrq->hostname_dn_len = hostname_dn_len;
+	if (!srvrq->name || !srvrq->hostname_dn) {
+		Alert("config : %s '%s', server '%s': out of memory\n",
+		      proxy_type_str(px), px->id, srv->id);
+		goto err;
+	}
+	LIST_ADDQ(&dns_srvrq_list, &srvrq->list);
+	return srvrq;
 
-			if (proxy)
-				send_log(proxy, LOG_NOTICE, "%s.\n", trash.str);
-			return -1;
-		}
-	} while (eb32_lookup(&resolvers->query_ids, query_id));
+  err:
+	if (srvrq) {
+		free(srvrq->name);
+		free(srvrq->hostname_dn);
+		free(srvrq);
+	}
+	return NULL;
+}
 
-	/* move the resolution into the run queue */
-	LIST_DEL(&resolution->list);
-	LIST_ADDQ(&resolvers->resolution.curr, &resolution->list);
 
-	/* now update resolution parameters */
-	resolution->query_id = query_id;
-	resolution->qid.key = query_id;
-	resolution->step = RSLV_STEP_RUNNING;
-	resolution->query_type = query_type;
-	resolution->try = resolvers->resolve_retries;
-	resolution->try_cname = 0;
-	resolution->nb_responses = 0;
-	eb32_insert(&resolvers->query_ids, &resolution->qid);
+/* 2 bytes random generator to generate DNS query ID */
+static inline uint16_t dns_rnd16(void)
+{
+	dns_query_id_seed ^= dns_query_id_seed << 13;
+	dns_query_id_seed ^= dns_query_id_seed >> 7;
+	dns_query_id_seed ^= dns_query_id_seed << 17;
+	return dns_query_id_seed;
+}
 
-	dns_send_query(resolution);
-	resolution->try -= 1;
 
-	/* update wakeup date if this resolution is the only one in the FIFO list */
-	if (dns_check_resolution_queue(resolvers) == 1) {
-		/* update task timeout */
-		dns_update_resolvers_timeout(resolvers);
-		task_queue(resolvers->t);
+static inline int dns_resolution_timeout(struct dns_resolution *res)
+{
+	switch (res->status) {
+		case RSLV_STATUS_VALID: return res->resolvers->hold.valid;
+		default:                return res->resolvers->timeout.resolve;
 	}
-
-	return 0;
 }
 
-/*
- * check if there is more than 1 resolution in the resolver's resolution list
- * return value:
- * 0: empty list
- * 1: exactly one entry in the list
- * 2: more than one entry in the list
- */
-int dns_check_resolution_queue(struct dns_resolvers *resolvers)
+/* Updates a resolvers' task timeout for next wake up and queue it */
+static void dns_update_resolvers_timeout(struct dns_resolvers *resolvers)
 {
-
-	if (LIST_ISEMPTY(&resolvers->resolution.curr))
-		return 0;
+	struct dns_resolution *res;
+	int next;
 
-	if ((resolvers->resolution.curr.n) && (resolvers->resolution.curr.n == resolvers->resolution.curr.p))
-		return 1;
+	next = tick_add(now_ms, resolvers->timeout.resolve);
+	if (!LIST_ISEMPTY(&resolvers->resolutions.curr)) {
+		res  = LIST_NEXT(&resolvers->resolutions.curr, struct dns_resolution *, list);
+		next = MIN(next, tick_add(res->last_query, resolvers->timeout.retry));
+	}
 
-	if (! ((resolvers->resolution.curr.n == resolvers->resolution.curr.p)
-			&& (&resolvers->resolution.curr != resolvers->resolution.curr.n)))
-		return 2;
+	list_for_each_entry(res, &resolvers->resolutions.wait, list)
+		next = MIN(next, tick_add(res->last_resolution, dns_resolution_timeout(res)));
 
-	return 0;
+	resolvers->t->expire = next;
+	task_queue(resolvers->t);
 }
 
-/*
- * reset some resolution parameters to initial values and also delete the
- * query ID from the resolver's tree.
+/* Opens an UDP socket on the namesaver's IP/Port, if required. Returns 0 on
+ * success, -1 otherwise.
  */
-void dns_reset_resolution(struct dns_resolution *resolution)
+static int dns_connect_namesaver(struct dns_nameserver *ns)
 {
-	/* update resolution status */
-	resolution->step = RSLV_STEP_NONE;
+	struct dgram_conn *dgram = ns->dgram;
+	int fd;
 
-	resolution->try = 0;
-	resolution->try_cname = 0;
-	resolution->last_resolution = now_ms;
-	resolution->nb_responses = 0;
+	/* Already connected */
+	if (dgram->t.sock.fd != -1)
+		return 0;
 
-	/* clean up query id */
-	eb32_delete(&resolution->qid);
-	resolution->query_id = 0;
-	resolution->qid.key = 0;
-}
+	/* Create an UDP socket and connect it on the nameserver's IP/Port */
+	if ((fd = socket(ns->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+		send_log(NULL, LOG_WARNING,
+			 "DNS : resolvers '%s': can't create socket for nameserver '%s'.\n",
+			 ns->resolvers->id, ns->id);
+		return -1;
+	}
+	if (connect(fd, (struct sockaddr*)&ns->addr, get_addr_len(&ns->addr)) == -1) {
+		send_log(NULL, LOG_WARNING,
+			 "DNS : resolvers '%s': can't connect socket for nameserver '%s'.\n",
+			 ns->resolvers->id, ns->id);
+		close(fd);
+		return -1;
+	}
 
-static inline void free_dns_answer_item(struct dns_answer_item *item)
-{
-	pool_free2(dns_answer_item_pool, item);
-}
+	/* Make the socket non blocking */
+	fcntl(fd, F_SETFL, O_NONBLOCK);
 
+	/* Add the fd in the fd list and update its parameters */
+	dgram->t.sock.fd = fd;
+	fdtab[fd].owner  = dgram;
+	fdtab[fd].iocb   = dgram_fd_handler;
+	fd_insert(fd);
+	fd_want_recv(fd);
+	return 0;
+}
 
-/*
- * function called when a network IO is generated on a name server socket for an incoming packet
- * It performs the following actions:
- *  - check if the packet requires processing (not outdated resolution)
- *  - ensure the DNS packet received is valid and call requester's callback
- *  - call requester's error callback if invalid response
- *  - check the dn_name in the packet against the one sent
+/* Forges a DNS query. It needs the following information from the caller:
+ *  - <query_id>        : the DNS query id corresponding to this query
+ *  - <query_type>      : DNS_RTYPE_* request DNS record type (A, AAAA, ANY...)
+ *  - <hostname_dn>     : hostname in domain name format
+ *  - <hostname_dn_len> : length of <hostname_dn>
+ *
+ * To store the query, the caller must pass a buffer <buf> and its size
+ * <bufsize>. It returns the number of written bytes in success, -1 if <buf> is
+ * too short.
  */
-void dns_resolve_recv(struct dgram_conn *dgram)
+static int dns_build_query(int query_id, int query_type, unsigned int accepted_payload_size,
+			   char *hostname_dn, int hostname_dn_len, char *buf, int bufsize)
 {
-	struct dns_nameserver *nameserver, *tmpnameserver;
-	struct dns_resolvers *resolvers;
-	struct dns_resolution *resolution = NULL;
-	struct dns_query_item *query;
-	unsigned char buf[DNS_MAX_UDP_MESSAGE + 1];
-	unsigned char *bufend;
-	int fd, buflen, dns_resp, need_resend = 0;
-	int max_answer_records = 0;
-	unsigned short query_id;
-	struct eb32_node *eb;
-	struct lru64 *lru = NULL;
-	struct dns_requester *requester = NULL, *tmprequester = NULL;
-	struct dns_answer_item *item1, *item2 = NULL;
-
-	fd = dgram->t.sock.fd;
-
-	/* check if ready for reading */
-	if (!fd_recv_ready(fd))
-		return;
-
-	/* no need to go further if we can't retrieve the nameserver */
-	if ((nameserver = dgram->owner) == NULL)
-		return;
-
-	resolvers = nameserver->resolvers;
-
-	/* process all pending input messages */
-	while (1) {
-		int removed_reso = 0;
-		/* read message received */
-		memset(buf, '\0', resolvers->accepted_payload_size + 1);
-		if ((buflen = recv(fd, (char*)buf , resolvers->accepted_payload_size + 1, 0)) < 0) {
-			/* FIXME : for now we consider EAGAIN only */
-			fd_cant_recv(fd);
-			break;
-		}
-
-		/* message too big */
-		if (buflen > resolvers->accepted_payload_size) {
-			nameserver->counters.too_big += 1;
-			continue;
-		}
-
-		/* initializing variables */
-		bufend = buf + buflen;	/* pointer to mark the end of the buffer */
-
-		/* read the query id from the packet (16 bits) */
-		if (buf + 2 > bufend) {
-			nameserver->counters.invalid += 1;
-			continue;
-		}
-		query_id = dns_response_get_query_id(buf);
-
-		/* search the query_id in the pending resolution tree */
-		eb = eb32_lookup(&resolvers->query_ids, query_id);
-		if (eb == NULL) {
-			/* unknown query id means an outdated response and can be safely ignored */
-			nameserver->counters.outdated += 1;
-			continue;
-		}
-
-		/* known query id means a resolution in prgress */
-		resolution = eb32_entry(eb, struct dns_resolution, qid);
-
-		if (!resolution) {
-			nameserver->counters.outdated += 1;
-			continue;
-		}
-
-		/* number of responses received */
-		resolution->nb_responses += 1;
-
-
-		max_answer_records = (resolvers->accepted_payload_size - DNS_HEADER_SIZE) / DNS_MIN_RECORD_SIZE;
-		dns_resp = dns_validate_dns_response(buf, bufend, resolution, max_answer_records);
-
-		switch (dns_resp) {
-			case DNS_RESP_VALID:
-				need_resend = 0;
-				break;
-
-			case DNS_RESP_INVALID:
-			case DNS_RESP_QUERY_COUNT_ERROR:
-			case DNS_RESP_WRONG_NAME:
-				if (resolution->status != RSLV_STATUS_INVALID) {
-					resolution->status = RSLV_STATUS_INVALID;
-					resolution->last_status_change = now_ms;
-				}
-				nameserver->counters.invalid += 1;
-				need_resend = 0;
-				break;
-
-			case DNS_RESP_ANCOUNT_ZERO:
-				if (resolution->status != RSLV_STATUS_OTHER) {
-					resolution->status = RSLV_STATUS_OTHER;
-					resolution->last_status_change = now_ms;
-				}
-				nameserver->counters.any_err += 1;
-				need_resend = 1;
-				break;
-
-			case DNS_RESP_NX_DOMAIN:
-				if (resolution->status != RSLV_STATUS_NX) {
-					resolution->status = RSLV_STATUS_NX;
-					resolution->last_status_change = now_ms;
-				}
-				nameserver->counters.nx += 1;
-				need_resend = 0;
-				break;
-
-			case DNS_RESP_REFUSED:
-				if (resolution->status != RSLV_STATUS_REFUSED) {
-					resolution->status = RSLV_STATUS_REFUSED;
-					resolution->last_status_change = now_ms;
-				}
-				nameserver->counters.refused += 1;
-				need_resend = 0;
-				break;
-
-			case DNS_RESP_CNAME_ERROR:
-				if (resolution->status != RSLV_STATUS_OTHER) {
-					resolution->status = RSLV_STATUS_OTHER;
-					resolution->last_status_change = now_ms;
-				}
-				nameserver->counters.cname_error += 1;
-				need_resend = 1;
-				break;
-
-			case DNS_RESP_TRUNCATED:
-				if (resolution->status != RSLV_STATUS_OTHER) {
-					resolution->status = RSLV_STATUS_OTHER;
-					resolution->last_status_change = now_ms;
-				}
-				nameserver->counters.truncated += 1;
-				need_resend = 1;
-				break;
-
-			case DNS_RESP_NO_EXPECTED_RECORD:
-				if (resolution->status != RSLV_STATUS_OTHER) {
-					resolution->status = RSLV_STATUS_OTHER;
-					resolution->last_status_change = now_ms;
-				}
-				nameserver->counters.other += 1;
-				need_resend = 1;
-				break;
-
-			case DNS_RESP_ERROR:
-			case DNS_RESP_INTERNAL:
-				if (resolution->status != RSLV_STATUS_OTHER) {
-					resolution->status = RSLV_STATUS_OTHER;
-					resolution->last_status_change = now_ms;
-				}
-				nameserver->counters.other += 1;
-				need_resend = 1;
-				break;
-		}
-
-		/* Check for any obsolete record, also identify any SRV request, and try to find a corresponding server */
-		list_for_each_entry_safe(item1, item2, &resolution->response.answer_list,
-		    list) {
-			if (item1->last_seen + nameserver->resolvers->hold.obsolete / 1000 < now.tv_sec) {
-				LIST_DEL(&item1->list);
-				if (item1->type == DNS_RTYPE_SRV && !LIST_ISEMPTY(&resolution->requester.curr)) {
-					struct dns_srvrq *srvrq;
-
-					list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
-						srvrq = objt_dns_srvrq(requester->requester);
-						/* We're removing an obsolete entry, remove any associated server */
-						if (srvrq) {
-							struct server *srv;
-
-							for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
-								if (srv->srvrq == srvrq &&
-								    item1->data_len ==
-								    srv->hostname_dn_len &&
-								    !memcmp(srv->hostname_dn, item1->target, item1->data_len) &&
-								    srv->svc_port == item1->port) {
-									snr_update_srv_status(srv, 1);
-									free(srv->hostname);
-									srv->hostname = NULL;
-									srv->hostname_dn_len = 0;
-									free(srv->hostname_dn);
-									srv->hostname_dn = NULL;
-									srv_free_from_resolution(srv);
-								}
-							}
-						}
-					} /* end of list_for_each(requester) */
-				}
-				free_dns_answer_item(item1);
-				continue;
-			}
-			if (item1->type == DNS_RTYPE_SRV) {
-				struct server *srv = NULL;
-				struct dns_srvrq *srvrq = NULL;
-
-				if (LIST_ISEMPTY(&resolution->requester.curr))
-					continue;
-
-				list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
-					srvrq = objt_dns_srvrq(requester->requester);
-					if (!srvrq)
-						continue;
-					/* Check if a server already uses that hostname */
-					for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
-						if (srv->srvrq == srvrq &&
-						    item1->data_len == srv->hostname_dn_len &&
-						    !memcmp(srv->hostname_dn, item1->target, item1->data_len) &&
-						    srv->svc_port == item1->port) {
-							if (srv->uweight != item1->weight) {
-								char weight[9];
-
-								snprintf(weight, sizeof(weight),
-								    "%d", item1->weight);
-								server_parse_weight_change_request(srv, weight);
-
-							}
-
-							break;
-						}
-					}
-					/* If not, try to find a server that is down */
-					if (!srv) {
-						for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
-
-							if (srv->srvrq == srvrq &&
-							    !srv->hostname_dn)
-								break;
-						}
-						if (srv) {
-							char weight[9];
-							const char *msg = NULL;
-							char hostname[DNS_MAX_NAME_SIZE];
-
-							if (item1->data_len > DNS_MAX_NAME_SIZE)
-								continue;
-							dns_dn_label_to_str(item1->target, hostname, item1->data_len);
-							msg = update_server_fqdn(srv, hostname, "SRV record");
-							if (msg)
-								send_log(srv->proxy, LOG_NOTICE, "%s", msg);
-
-							srv->svc_port = item1->port;
-							srv->flags &= ~SRV_F_MAPPORTS;
-							if ((srv->check.state & CHK_ST_CONFIGURED) && !(srv->flags & SRV_F_CHECKPORT))
-								srv->check.port = item1->port;
-							snprintf(weight, sizeof(weight),
-							    "%d", item1->weight);
-							server_parse_weight_change_request(srv, weight);
-						}
-					}
-				}
-			}
-		}
-		if (removed_reso)
-			goto next_packet;
-
-		/* some error codes trigger a re-send of the query, but switching the
-		 * query type.
-		 * This is the case for the following error codes:
-		 *   DNS_RESP_ANCOUNT_ZERO
-		 *   DNS_RESP_TRUNCATED
-		 *   DNS_RESP_ERROR
-		 *   DNS_RESP_INTERNAL
-		 *   DNS_RESP_NO_EXPECTED_RECORD
-		 *   DNS_RESP_CNAME_ERROR
-		 */
-		if (need_resend) {
-			int family_prio;
-			int res_preferred_afinet, res_preferred_afinet6;
-
-			requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
-			switch (obj_type(requester->requester)) {
-				case OBJ_TYPE_SERVER:
-					family_prio = objt_server(requester->requester)->dns_opts.family_prio;
-					break;
-				case OBJ_TYPE_NONE:
-				default:
-					family_prio = AF_INET6;
-			}
-			res_preferred_afinet = family_prio == AF_INET && resolution->query_type == DNS_RTYPE_A;
-			res_preferred_afinet6 = family_prio == AF_INET6 && resolution->query_type == DNS_RTYPE_AAAA;
-			if ((res_preferred_afinet || res_preferred_afinet6)
-					|| (resolution->try > 0)) {
-				/* let's change the query type */
-				if (res_preferred_afinet6) {
-                                        /* fallback from AAAA to A */
-                                        resolution->query_type = DNS_RTYPE_A;
-                                }
-                                else if (res_preferred_afinet) {
-                                        /* fallback from A to AAAA */
-                                        resolution->query_type = DNS_RTYPE_AAAA;
-                                }
-                                else {
-                                        resolution->try -= 1;
-                                        if (family_prio == AF_INET) {
-                                                resolution->query_type = DNS_RTYPE_A;
-                                        } else {
-                                                resolution->query_type = DNS_RTYPE_AAAA;
-                                        }
-                                }
-
-				dns_send_query(resolution);
-                                /*
-				 * move the resolution to the last element of the FIFO queue
-				 * and update timeout wakeup based on the new first entry
-				 */
-				if (dns_check_resolution_queue(resolvers) > 1) {
-				/* second resolution becomes first one */
-					LIST_DEL(&resolution->list);
-					/* ex first resolution goes to the end of the queue */
-					LIST_ADDQ(&resolvers->resolution.curr, &resolution->list);
-				}
-
-				dns_update_resolvers_timeout(resolvers);
-				goto next_packet;
-			}
-
-			/* if we're there, this means that we already ran out of chances to re-send
-			 * the query */
-			list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
-				requester->requester_error_cb(requester, dns_resp);
-			}
-			goto next_packet;
-		}
-
-		/* now processing those error codes only:
-		 *   DNS_RESP_NX_DOMAIN
-		 *   DNS_RESP_REFUSED
-		 */
-		if (dns_resp != DNS_RESP_VALID) {
-			/* now parse list of requesters currently waiting for this resolution */
-			list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
-				requester->requester_error_cb(requester, dns_resp);
-
-				/* we can move the requester the wait queue */
-				LIST_DEL(&requester->list);
-				LIST_ADDQ(&resolution->requester.wait, &requester->list);
-			}
-			goto next_packet;
-		}
-
-		/* Now let's check the query's dname corresponds to the one we sent.
-		 * We can check only the first query of the list. We send one query at a time
-		 * so we get one query in the response */
-		query = LIST_NEXT(&resolution->response.query_list, struct dns_query_item *, list);
-		if (!resolution->hostname_dn)
-			abort();
-		if (query && memcmp(query->name, resolution->hostname_dn, resolution->hostname_dn_len) != 0) {
-			nameserver->counters.other += 1;
-			/* now parse list of requesters currently waiting for this resolution */
-			list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
-				requester->requester_error_cb(requester, DNS_RESP_WRONG_NAME);
-				/* we can move the requester the wait queue */
-				LIST_DEL(&requester->list);
-				LIST_ADDQ(&resolution->requester.wait, &requester->list);
-			}
-			goto next_packet;
-		}
-
-		/* no errors, we can save the response in the cache */
-		if (dns_lru_tree) {
-			unsigned long long seed = 1;
-			struct chunk *buf = get_trash_chunk();
-			struct chunk *tmp = NULL;
-
-			chunk_reset(buf);
-			tmp = dns_cache_key(resolution->query_type, resolution->hostname_dn,
-					    resolution->hostname_dn_len, buf);
-			if (!tmp) {
-				nameserver->counters.other += 1;
-				/* now parse list of requesters currently waiting for this resolution */
-				list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
-					requester->requester_error_cb(requester, DNS_RESP_ERROR);
-					/* we can move the requester the wait queue */
-					LIST_DEL(&requester->list);
-					LIST_ADDQ(&resolution->requester.wait, &requester->list);
-				}
-				goto next_packet;
-			}
+	struct dns_header            dns_hdr;
+	struct dns_question          qinfo;
+	struct dns_additional_record edns;
+	char *p = buf;
 
-			lru = lru64_get(XXH64(buf->str, buf->len, seed),
-					dns_lru_tree, nameserver->resolvers, 1);
+	if (sizeof(dns_hdr) + sizeof(qinfo) +  sizeof(edns) + hostname_dn_len >= bufsize)
+		return -1;
 
-			lru64_commit(lru, resolution, nameserver->resolvers, 1, NULL);
-		}
+	memset(buf, 0, bufsize);
 
-		if (resolution->status != RSLV_STATUS_VALID) {
-			resolution->status = RSLV_STATUS_VALID;
-			resolution->last_status_change = now_ms;
-		}
+	/* Set dns query headers */
+	dns_hdr.id      = (unsigned short) htons(query_id);
+	dns_hdr.flags   = htons(0x0100); /* qr=0, opcode=0, aa=0, tc=0, rd=1, ra=0, z=0, rcode=0 */
+	dns_hdr.qdcount = htons(1);      /* 1 question */
+	dns_hdr.ancount = 0;
+	dns_hdr.nscount = 0;
+	dns_hdr.arcount = htons(1);
+	memcpy(p, &dns_hdr, sizeof(dns_hdr));
+	p += sizeof(dns_hdr);
 
-		nameserver->counters.valid += 1;
-		/* now parse list of requesters currently waiting for this resolution */
-		tmpnameserver = nameserver;
-		list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
-			requester->requester_cb(requester, tmpnameserver);
-			/* we can move the requester the wait queue */
-			LIST_DEL(&requester->list);
-			LIST_ADDQ(&resolution->requester.wait, &requester->list);
-			/* first response is managed by the server, others are from the cache */
-			tmpnameserver = NULL;
-		}
+	/* Set up query hostname */
+	memcpy(p, hostname_dn, hostname_dn_len);
+	p += hostname_dn_len;
+	*p++ = 0;
 
- next_packet:
-		/* resolution may be NULL when we receive an ICMP unreachable packet */
-		if (resolution && LIST_ISEMPTY(&resolution->requester.curr)) {
-			/* move the resolution into the wait queue */
-			LIST_DEL(&resolution->list);
-			LIST_ADDQ(&resolvers->resolution.wait, &resolution->list);
-			/* update last resolution date and time */
-			resolution->last_resolution = now_ms;
-			/* reset current status flag */
-			resolution->step = RSLV_STEP_NONE;
-			/* reset values */
-			dns_reset_resolution(resolution);
-		}
+	/* Set up query info (type and class) */
+	qinfo.qtype  = htons(query_type);
+	qinfo.qclass = htons(DNS_RCLASS_IN);
+	memcpy(p, &qinfo, sizeof(qinfo));
+	p += sizeof(qinfo);
 
-	} // end of while "packets" loop
+	/* Set the DNS extension */
+	edns.name             = 0;
+	edns.type             = htons(DNS_RTYPE_OPT);
+	edns.udp_payload_size = htons(accepted_payload_size);
+	edns.extension        = 0;
+	edns.data_length      = 0;
+	memcpy(p, &edns, sizeof(edns));
+	p += sizeof(edns);
 
-	dns_update_resolvers_timeout(nameserver->resolvers);
+	return (p - buf);
 }
 
-/*
- * function called when a resolvers network socket is ready to send data
- * It performs the following actions:
+/* Sends a DNS query to resolvers associated to a resolution. It returns 0 on
+ * success, -1 otherwise.
  */
-void dns_resolve_send(struct dgram_conn *dgram)
+static int dns_send_query(struct dns_resolution *resolution)
 {
-	int fd;
-	struct dns_nameserver *nameserver;
-	struct dns_resolvers *resolvers;
-	struct dns_resolution *resolution;
-
-	fd = dgram->t.sock.fd;
-
-	/* check if ready for sending */
-	if (!fd_send_ready(fd))
-		return;
+	struct dns_resolvers  *resolvers = resolution->resolvers;
+	struct dns_nameserver *ns;
 
-	/* we don't want/need to be waked up any more for sending */
-	fd_stop_send(fd);
-
-	/* no need to go further if we can't retrieve the nameserver */
-	if ((nameserver = dgram->owner) == NULL)
-		return;
+	list_for_each_entry(ns, &resolvers->nameservers, list) {
+		int fd = ns->dgram->t.sock.fd;
+		if (fd == -1) {
+			if (dns_connect_namesaver(ns) == -1)
+				continue;
+			fd = ns->dgram->t.sock.fd;
+			resolvers->nb_nameservers++;
+		}
+		fd_want_send(fd);
+	}
 
-	resolvers = nameserver->resolvers;
-	resolution = LIST_NEXT(&resolvers->resolution.curr, struct dns_resolution *, list);
+	/* Update resolution */
+	resolution->nb_queries   = 0;
+	resolution->nb_responses = 0;
+	resolution->last_query   = now_ms;
 
-	dns_send_query(resolution);
-	dns_update_resolvers_timeout(resolvers);
+	/* Push the resolution at the end of the active list */
+	LIST_DEL(&resolution->list);
+	LIST_ADDQ(&resolvers->resolutions.curr, &resolution->list);
+	return 0;
 }
 
-/*
- * forge and send a DNS query to resolvers associated to a resolution
- * It performs the following actions:
- * returns:
- *  0 in case of error or safe ignorance
- *  1 if no error
+/* Prepares and sends a DNS resolution. It returns 1 if the query was sent, 0 if
+ * skipped and -1 if an error occurred.
  */
-int dns_send_query(struct dns_resolution *resolution)
+static int
+dns_run_resolution(struct dns_resolution *resolution)
 {
-	struct dns_resolvers *resolvers = NULL;
-	struct dns_nameserver *nameserver;
-	struct dns_requester *requester = NULL;
-	int ret, bufsize, fd;
+	struct dns_resolvers  *resolvers = resolution->resolvers;
+	int query_id, i;
 
-	/* nothing to do */
-	if (LIST_ISEMPTY(&resolution->requester.curr))
+	/* Avoid sending requests for resolutions that don't yet have an
+	 * hostname, ie resolutions linked to servers that do not yet have an
+	 * fqdn */
+	if (!resolution->hostname_dn)
 		return 0;
 
-	requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
+	/* Check if a resolution has already been started for this server return
+	 * directly to avoid resolution pill up. */
+	if (resolution->step != RSLV_STEP_NONE)
+		return 0;
 
-	switch (obj_type(requester->requester)) {
-		case OBJ_TYPE_SERVER:
-			resolvers = objt_server(requester->requester)->resolvers;
-			break;
-		case OBJ_TYPE_SRVRQ:
-			resolvers = objt_dns_srvrq(requester->requester)->resolvers;
+	/* Generates a new query id. We try at most 100 times to find a free
+	 * query id */
+	for (i = 0; i < 100; ++i) {
+		query_id = dns_rnd16();
+		if (!eb32_lookup(&resolvers->query_ids, query_id))
 			break;
-		case OBJ_TYPE_NONE:
-		default:
-			return 0;
+		query_id = -1;
 	}
-
-	if (!resolvers)
-		return 0;
-
-	bufsize = dns_build_query(resolution->query_id, resolution->query_type, resolvers->accepted_payload_size,
-			resolution->hostname_dn, resolution->hostname_dn_len, trash.str, trash.size);
-
-	if (bufsize == -1)
-		return 0;
-
-	list_for_each_entry(nameserver, &resolvers->nameserver_list, list) {
-		fd = nameserver->dgram->t.sock.fd;
-		errno = 0;
-
-		ret = send(fd, trash.str, bufsize, 0);
-
-		if (ret > 0)
-			nameserver->counters.sent += 1;
-
-		if (ret == 0 || errno == EAGAIN) {
-			/* nothing written, let's update the poller that we wanted to send
-			 * but we were not able to */
-			fd_want_send(fd);
-			fd_cant_send(fd);
-		}
+	if (query_id == -1) {
+		send_log(NULL, LOG_NOTICE,
+			 "could not generate a query id for %s, in resolvers %s.\n",
+			 resolution->hostname_dn, resolvers->id);
+		return -1;
 	}
 
-	/* update resolution */
-	resolution->nb_responses = 0;
-	resolution->last_sent_packet = now_ms;
+	/* Update resolution parameters */
+	resolution->query_id     = query_id;
+	resolution->qid.key      = query_id;
+	resolution->step         = RSLV_STEP_RUNNING;
+	resolution->query_type   = resolution->prefered_query_type;
+	resolution->try          = resolvers->resolve_retries;
+	eb32_insert(&resolvers->query_ids, &resolution->qid);
 
+	/* Send the DNS query */
+	resolution->try -= 1;
+	dns_send_query(resolution);
 	return 1;
 }
 
-/*
- * update a resolvers' task timeout for next wake up
- */
-void dns_update_resolvers_timeout(struct dns_resolvers *resolvers)
+/* Performs a name resolution for the requester <req> */
+void dns_trigger_resolution(struct dns_requester *req)
 {
-	struct dns_resolution *resolution;
-	struct dns_requester *requester;
+	struct dns_resolvers  *resolvers;
+	struct dns_resolution *res;
+	int exp;
 
-	if ((LIST_ISEMPTY(&resolvers->resolution.curr)) && (LIST_ISEMPTY(&resolvers->resolution.wait))) {
-		resolvers->t->expire = TICK_ETERNITY;
-	}
-	else if (!LIST_ISEMPTY(&resolvers->resolution.curr)) {
-		resolution = LIST_NEXT(&resolvers->resolution.curr, struct dns_resolution *, list);
-		if (!resolvers->t->expire || tick_is_le(resolvers->t->expire, tick_add(resolution->last_sent_packet, resolvers->timeout.retry))) {
-			resolvers->t->expire = tick_add(resolution->last_sent_packet, resolvers->timeout.retry);
-		}
-	}
-	else if (!LIST_ISEMPTY(&resolvers->resolution.wait)) {
-		int valid_period, inter, need_wakeup;
-		struct dns_resolution *res_back;
-		need_wakeup = 0;
-		list_for_each_entry_safe(resolution, res_back, &resolvers->resolution.wait, list) {
-			valid_period = 0;
-			inter = 0;
-
-			requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
+	if (!req || !req->resolution)
+		return;
+	res       = req->resolution;
+	resolvers = res->resolvers;
 
-			switch (obj_type(requester->requester)) {
-				case OBJ_TYPE_SERVER:
-					valid_period = objt_server(requester->requester)->check.inter;
-					break;
-				case OBJ_TYPE_SRVRQ:
-					valid_period = objt_dns_srvrq(requester->requester)->inter;
-					break;
-				case OBJ_TYPE_NONE:
-				default:
-					continue;
-			}
+	/* The resolution must not be triggered yet. Use the cached response, if
+	 * valid */
+	exp = tick_add(res->last_resolution, resolvers->hold.valid);
+	if (res->status == RSLV_STATUS_VALID &&
+	    tick_isset(res->last_resolution) && !tick_is_expired(exp, now_ms))
+		req->requester_cb(req, NULL);
+	else
+		dns_run_resolution(res);
+}
 
-			if (resolvers->hold.valid < valid_period)
-				inter = resolvers->hold.valid;
-			else
-				inter = valid_period;
 
-			if (tick_is_expired(tick_add(resolution->last_resolution, inter), now_ms)) {
-				switch (obj_type(requester->requester)) {
-					case OBJ_TYPE_SERVER:
-						dns_trigger_resolution(objt_server(requester->requester)->resolution);
-						break;
-					case OBJ_TYPE_SRVRQ:
-						dns_trigger_resolution(objt_dns_srvrq(requester->requester)->resolution);
-						break;
-					case OBJ_TYPE_NONE:
-					default:
-						;;
-				}
-			}
-			else {
-				need_wakeup = 1;
-			}
-		}
-		/* in such case, we wake up in 1s */
-		if (need_wakeup) {
-			int r = 1000;
+/* Resets some resolution parameters to initial values and also delete the query
+ * ID from the resolver's tree.
+ */
+static void dns_reset_resolution(struct dns_resolution *resolution)
+{
+	/* update resolution status */
+	resolution->step            = RSLV_STEP_NONE;
+	resolution->try             = 0;
+	resolution->last_resolution = now_ms;
+	resolution->nb_queries      = 0;
+	resolution->nb_responses    = 0;
+	resolution->query_type      = resolution->prefered_query_type;
 
-			resolution = LIST_NEXT(&resolvers->resolution.wait, struct dns_resolution *, list);
-			if (tick_is_le(resolvers->t->expire, tick_add(now_ms, r)))
-				resolvers->t->expire = tick_add(now_ms, r);
-			resolvers->t->expire = tick_add(now_ms, 1000);
-		}
-	}
+	/* clean up query id */
+	eb32_delete(&resolution->qid);
+	resolution->query_id = 0;
+	resolution->qid.key   = 0;
+}
 
-	task_queue(resolvers->t);
+/* Returns the query id contained in a DNS response */
+static inline unsigned short dns_response_get_query_id(unsigned char *resp)
+{
+	return resp[0] * 256 + resp[1];
 }
 
-/*
- * Analyse, re-build and copy the name <name> from the DNS response packet <buffer>.
- * <name> must point to the 'data_len' information or pointer 'c0' for compressed data.
- * The result is copied into <dest>, ensuring we don't overflow using <dest_len>
- * Returns the number of bytes the caller can move forward. If 0 it means an error occured
- * while parsing the name.
- * <offset> is the number of bytes the caller could move forward.
+
+/* Analyses, re-builds and copies the name <name> from the DNS response packet
+ * <buffer>.  <name> must point to the 'data_len' information or pointer 'c0'
+ * for compressed data.  The result is copied into <dest>, ensuring we don't
+ * overflow using <dest_len> Returns the number of bytes the caller can move
+ * forward. If 0 it means an error occurred while parsing the name. <offset> is
+ * the number of bytes the caller could move forward.
  */
-int dns_read_name(unsigned char *buffer, unsigned char *bufend, unsigned char *name, char *destination, int dest_len, int *offset)
+int dns_read_name(unsigned char *buffer, unsigned char *bufend,
+		  unsigned char *name, char *destination, int dest_len,
+		  int *offset)
 {
 	int nb_bytes = 0, n = 0;
 	int label_len;
@@ -998,18 +399,18 @@
 	char *dest = destination;
 
 	while (1) {
-		/* name compression is in use */
+		/* Name compression is in use */
 		if ((*reader & 0xc0) == 0xc0) {
-			/* a pointer must point BEFORE current position */
-			if ((buffer + reader[1]) > reader) {
-				goto out_error;
-			}
+			/* Must point BEFORE current position */
+			if ((buffer + reader[1]) > reader)
+				goto err;
 
-			n = dns_read_name(buffer, bufend, buffer + reader[1], dest, dest_len - nb_bytes, offset);
+			n = dns_read_name(buffer, bufend, buffer + reader[1],
+					  dest, dest_len - nb_bytes, offset);
 			if (n == 0)
-				goto out_error;
+				goto err;
 
-			dest += n;
+			dest     += n;
 			nb_bytes += n;
 			goto out;
 		}
@@ -1017,28 +418,29 @@
 		label_len = *reader;
 		if (label_len == 0)
 			goto out;
+
 		/* Check if:
 		 *  - we won't read outside the buffer
 		 *  - there is enough place in the destination
 		 */
 		if ((reader + label_len >= bufend) || (nb_bytes + label_len >= dest_len))
-			goto out_error;
+			goto err;
 
 		/* +1 to take label len + label string */
-		label_len += 1;
+		label_len++;
 
 		memcpy(dest, reader, label_len);
 
-		dest += label_len;
+		dest     += label_len;
 		nb_bytes += label_len;
-		reader += label_len;
+		reader   += label_len;
 	}
 
- out:
+  out:
 	/* offset computation:
 	 * parse from <name> until finding either NULL or a pointer "c0xx"
 	 */
-	reader = name;
+	reader  = name;
 	*offset = 0;
 	while (reader < bufend) {
 		if ((reader[0] & 0xc0) == 0xc0) {
@@ -1052,24 +454,122 @@
 		*offset += 1;
 		++reader;
 	}
-
 	return nb_bytes;
 
- out_error:
+  err:
 	return 0;
 }
 
+/* Checks for any obsolete record, also identify any SRV request, and try to
+ * find a corresponding server.
+*/
+static void dns_check_dns_response(struct dns_resolution *res)
+{
+	struct dns_resolvers   *resolvers = res->resolvers;
+	struct dns_requester   *req, *reqback;
+	struct dns_answer_item *item, *itemback;
+	struct server          *srv;
+	struct dns_srvrq       *srvrq;
+
+	list_for_each_entry_safe(item, itemback, &res->response.answer_list, list) {
+
+		/* Remove obsolete items */
+		if ((item->last_seen + resolvers->hold.obsolete / 1000) < now.tv_sec) {
+			if (item->type != DNS_RTYPE_SRV)
+				goto rm_obselete_item;
+
+			list_for_each_entry_safe(req, reqback, &res->requesters, list) {
+				if ((srvrq = objt_dns_srvrq(req->owner)) == NULL)
+					continue;
+
+				/* Remove any associated server */
+				for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
+					if (srv->srvrq == srvrq && srv->svc_port == item->port &&
+					    item->data_len == srv->hostname_dn_len &&
+					    !memcmp(srv->hostname_dn, item->target, item->data_len)) {
+						snr_update_srv_status(srv, 1);
+						free(srv->hostname);
+						free(srv->hostname_dn);
+						srv->hostname        = NULL;
+						srv->hostname_dn     = NULL;
+						srv->hostname_dn_len = 0;
+						dns_unlink_resolution(srv->dns_requester);
+					}
+				}
+			}
+
+		  rm_obselete_item:
+			LIST_DEL(&item->list);
+			pool_free2(dns_answer_item_pool, item);
+			continue;
+		}
+
+		if (item->type != DNS_RTYPE_SRV)
+			continue;
+
+		/* Now process SRV records */
+		list_for_each_entry_safe(req, reqback, &res->requesters, list) {
+			if ((srvrq = objt_dns_srvrq(req->owner)) == NULL)
+				continue;
+
+			/* Check if a server already uses that hostname */
+			for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
+				if (srv->srvrq == srvrq && srv->svc_port == item->port &&
+				    item->data_len == srv->hostname_dn_len &&
+				    !memcmp(srv->hostname_dn, item->target, item->data_len)) {
+					if (srv->uweight != item->weight) {
+						char weight[9];
+
+						snprintf(weight, sizeof(weight), "%d", item->weight);
+						server_parse_weight_change_request(srv, weight);
+					}
+					break;
+				}
+			}
+			if (srv)
+				continue;
+
+			/* If not, try to find a server with undefined hostname */
+			for (srv = srvrq->proxy->srv; srv != NULL; srv = srv->next) {
+				if (srv->srvrq == srvrq && !srv->hostname_dn)
+					break;
+			}
+			/* And update this server, if found */
+			if (srv) {
+				const char *msg = NULL;
+				char weight[9];
+				char hostname[DNS_MAX_NAME_SIZE];
+
+				if (dns_dn_label_to_str(item->target, item->data_len+1,
+							hostname, DNS_MAX_NAME_SIZE) == -1)
+					continue;
+				msg = update_server_fqdn(srv, hostname, "SRV record");
+				if (msg)
+					send_log(srv->proxy, LOG_NOTICE, "%s", msg);
+
+				srv->svc_port = item->port;
+				srv->flags   &= ~SRV_F_MAPPORTS;
+				if ((srv->check.state & CHK_ST_CONFIGURED) &&
+				    !(srv->flags & SRV_F_CHECKPORT))
+					srv->check.port = item->port;
+				snprintf(weight, sizeof(weight), "%d", item->weight);
+				server_parse_weight_change_request(srv, weight);
+			}
+		}
+	}
+}
+
-/*
- * Function to validate that the buffer DNS response provided in <resp> and
- * finishing before <bufend> is valid from a DNS protocol point of view.
+/* Validates that the buffer DNS response provided in <resp> and finishing
+ * before <bufend> is valid from a DNS protocol point of view.
  *
- * The result is stored in <resolution>' response, buf_response, response_query_records
- * and response_answer_records members.
+ * The result is stored in <resolution>' response, buf_response,
+ * response_query_records and response_answer_records members.
  *
  * This function returns one of the DNS_RESP_* code to indicate the type of
  * error found.
  */
-int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct dns_resolution *resolution, int max_answer_records)
+static int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend,
+				     struct dns_resolution *resolution, int max_answer_records)
 {
 	unsigned char *reader;
 	char *previous_dname, tmpname[DNS_MAX_NAME_SIZE];
@@ -1079,15 +579,14 @@
 	struct dns_query_item *dns_query;
 	struct dns_answer_item *dns_answer_record, *tmp_record;
 	struct dns_response_packet *dns_p;
-	int found = 0;
-	int i;
+	int i, found = 0;
 
-	reader = resp;
-	len = 0;
+	reader         = resp;
+	len            = 0;
 	previous_dname = NULL;
-	dns_query = NULL;
+	dns_query      = NULL;
 
-	/* initialization of response buffer and structure */
+	/* Initialization of response buffer and structure */
 	dns_p = &resolution->response;
 
 	/* query id */
@@ -1096,8 +595,7 @@
 	dns_p->header.id = reader[0] * 256 + reader[1];
 	reader += 2;
 
-	/*
-	 * flags and rcode are stored over 2 bytes
+	/* Flags and rcode are stored over 2 bytes
 	 * First byte contains:
 	 *  - response flag (1 bit)
 	 *  - opcode (4 bits)
@@ -1115,18 +613,18 @@
 			return DNS_RESP_NX_DOMAIN;
 		else if ((flags & DNS_FLAG_REPLYCODE) == DNS_RCODE_REFUSED)
 			return DNS_RESP_REFUSED;
-
 		return DNS_RESP_ERROR;
 	}
 
-	/* move forward 2 bytes for flags */
+	/* Move forward 2 bytes for flags */
 	reader += 2;
 
 	/* 2 bytes for question count */
 	if (reader + 2 >= bufend)
 		return DNS_RESP_INVALID;
 	dns_p->header.qdcount = reader[0] * 256 + reader[1];
-	/* (for now) we send one query only, so we expect only one in the response too */
+	/* (for now) we send one query only, so we expect only one in the
+	 * response too */
 	if (dns_p->header.qdcount != 1)
 		return DNS_RESP_QUERY_COUNT_ERROR;
 	if (dns_p->header.qdcount > DNS_MAX_QUERY_RECORDS)
@@ -1139,7 +637,7 @@
 	dns_p->header.ancount = reader[0] * 256 + reader[1];
 	if (dns_p->header.ancount == 0)
 		return DNS_RESP_ANCOUNT_ZERO;
-	/* check if too many records are announced */
+	/* Check if too many records are announced */
 	if (dns_p->header.ancount > max_answer_records)
 		return DNS_RESP_INVALID;
 	reader += 2;
@@ -1156,22 +654,20 @@
 	dns_p->header.arcount = reader[0] * 256 + reader[1];
 	reader += 2;
 
-	/* parsing dns queries */
+	/* Parsing dns queries */
 	LIST_INIT(&dns_p->query_list);
 	for (dns_query_record_id = 0; dns_query_record_id < dns_p->header.qdcount; dns_query_record_id++) {
-		/* use next pre-allocated dns_query_item after ensuring there is
+		/* Use next pre-allocated dns_query_item after ensuring there is
 		 * still one available.
-		 * It's then added to our packet query list.
-		 */
+		 * It's then added to our packet query list. */
 		if (dns_query_record_id > DNS_MAX_QUERY_RECORDS)
 			return DNS_RESP_INVALID;
 		dns_query = &resolution->response_query_records[dns_query_record_id];
 		LIST_ADDQ(&dns_p->query_list, &dns_query->list);
 
-		/* name is a NULL terminated string in our case, since we have
+		/* Name is a NULL terminated string in our case, since we have
 		 * one query per response and the first one can't be compressed
-		 * (using the 0x0c format)
-		 */
+		 * (using the 0x0c format) */
 		offset = 0;
 		len = dns_read_name(resp, bufend, reader, dns_query->name, DNS_MAX_NAME_SIZE, &offset);
 
@@ -1195,8 +691,7 @@
 	}
 
 	/* TRUNCATED flag must be checked after we could read the query type
-	 * because a TRUNCATED SRV query type response can still be exploited
-	 */
+	 * because a TRUNCATED SRV query type response can still be exploited */
 	if (dns_query->type != DNS_RTYPE_SRV && flags & DNS_FLAG_TRUNCATED)
 		return DNS_RESP_TRUNCATED;
 
@@ -1214,22 +709,23 @@
 		len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
 
 		if (len == 0) {
-			free_dns_answer_item(dns_answer_record);
+			pool_free2(dns_answer_item_pool, dns_answer_record);
 			return DNS_RESP_INVALID;
 		}
 
-		/* check if the current record dname is valid.
-		 * previous_dname points either to queried dname or last CNAME target
-		 */
+		/* Check if the current record dname is valid.  previous_dname
+		 * points either to queried dname or last CNAME target */
 		if (dns_query->type != DNS_RTYPE_SRV && memcmp(previous_dname, tmpname, len) != 0) {
-			free_dns_answer_item(dns_answer_record);
+			pool_free2(dns_answer_item_pool, dns_answer_record);
 			if (i == 0) {
-				/* first record, means a mismatch issue between queried dname
-				 * and dname found in the first record */
+				/* First record, means a mismatch issue between
+				 * queried dname and dname found in the first
+				 * record */
 				return DNS_RESP_INVALID;
-			} else {
-				/* if not the first record, this means we have a CNAME resolution
-				 * error */
+			}
+			else {
+				/* If not the first record, this means we have a
+				 * CNAME resolution error */
 				return DNS_RESP_CNAME_ERROR;
 			}
 
@@ -1240,13 +736,13 @@
 
 		reader += offset;
 		if (reader >= bufend) {
-			free_dns_answer_item(dns_answer_record);
+			pool_free2(dns_answer_item_pool, dns_answer_record);
 			return DNS_RESP_INVALID;
 		}
 
 		/* 2 bytes for record type (A, AAAA, CNAME, etc...) */
 		if (reader + 2 > bufend) {
-			free_dns_answer_item(dns_answer_record);
+			pool_free2(dns_answer_item_pool, dns_answer_record);
 			return DNS_RESP_INVALID;
 		}
 		dns_answer_record->type = reader[0] * 256 + reader[1];
@@ -1254,7 +750,7 @@
 
 		/* 2 bytes for class (2) */
 		if (reader + 2 > bufend) {
-			free_dns_answer_item(dns_answer_record);
+			pool_free2(dns_answer_item_pool, dns_answer_record);
 			return DNS_RESP_INVALID;
 		}
 		dns_answer_record->class = reader[0] * 256 + reader[1];
@@ -1262,29 +758,29 @@
 
 		/* 4 bytes for ttl (4) */
 		if (reader + 4 > bufend) {
-			free_dns_answer_item(dns_answer_record);
+			pool_free2(dns_answer_item_pool, dns_answer_record);
 			return DNS_RESP_INVALID;
 		}
 		dns_answer_record->ttl =   reader[0] * 16777216 + reader[1] * 65536
 			                 + reader[2] * 256 + reader[3];
 		reader += 4;
 
-		/* now reading data len */
+		/* Now reading data len */
 		if (reader + 2 > bufend) {
-			free_dns_answer_item(dns_answer_record);
+			pool_free2(dns_answer_item_pool, dns_answer_record);
 			return DNS_RESP_INVALID;
 		}
 		dns_answer_record->data_len = reader[0] * 256 + reader[1];
 
-		/* move forward 2 bytes for data len */
+		/* Move forward 2 bytes for data len */
 		reader += 2;
 
-		/* analyzing record content */
+		/* Analyzing record content */
 		switch (dns_answer_record->type) {
 			case DNS_RTYPE_A:
 				/* ipv4 is stored on 4 bytes */
 				if (dns_answer_record->data_len != 4) {
-					free_dns_answer_item(dns_answer_record);
+					pool_free2(dns_answer_item_pool, dns_answer_record);
 					return DNS_RESP_INVALID;
 				}
 				dns_answer_record->address.sa_family = AF_INET;
@@ -1293,44 +789,41 @@
 				break;
 
 			case DNS_RTYPE_CNAME:
-				/* check if this is the last record and update the caller about the status:
+				/* Check if this is the last record and update the caller about the status:
 				 * no IP could be found and last record was a CNAME. Could be triggered
 				 * by a wrong query type
 				 *
-				 * + 1 because dns_answer_record_id starts at 0 while number of answers
-				 * is an integer and starts at 1.
+				 * + 1 because dns_answer_record_id starts at 0
+				 * while number of answers is an integer and
+				 * starts at 1.
 				 */
 				if (i + 1 == dns_p->header.ancount) {
-					free_dns_answer_item(dns_answer_record);
+					pool_free2(dns_answer_item_pool, dns_answer_record);
 					return DNS_RESP_CNAME_ERROR;
 				}
 
 				offset = 0;
 				len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
-
 				if (len == 0) {
-					free_dns_answer_item(dns_answer_record);
+					pool_free2(dns_answer_item_pool, dns_answer_record);
 					return DNS_RESP_INVALID;
 				}
 
 				memcpy(dns_answer_record->target, tmpname, len);
 				dns_answer_record->target[len] = 0;
-
 				previous_dname = dns_answer_record->target;
-
 				break;
 
 
 			case DNS_RTYPE_SRV:
-				/*
-				 * Answer must contain :
+				/* Answer must contain :
 				 * - 2 bytes for the priority
 				 * - 2 bytes for the weight
 				 * - 2 bytes for the port
 				 * - the target hostname
 				 */
 				if (dns_answer_record->data_len <= 6) {
-					free_dns_answer_item(dns_answer_record);
+					pool_free2(dns_answer_item_pool, dns_answer_record);
 					return DNS_RESP_INVALID;
 				}
 				dns_answer_record->priority = read_n16(reader);
@@ -1342,17 +835,18 @@
 				offset = 0;
 				len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
 				if (len == 0) {
-					free_dns_answer_item(dns_answer_record);
+					pool_free2(dns_answer_item_pool, dns_answer_record);
 					return DNS_RESP_INVALID;
 				}
 				dns_answer_record->data_len = len;
 				memcpy(dns_answer_record->target, tmpname, len);
 				dns_answer_record->target[len] = 0;
 				break;
+
 			case DNS_RTYPE_AAAA:
 				/* ipv6 is stored on 16 bytes */
 				if (dns_answer_record->data_len != 16) {
-					free_dns_answer_item(dns_answer_record);
+					pool_free2(dns_answer_item_pool, dns_answer_record);
 					return DNS_RESP_INVALID;
 				}
 				dns_answer_record->address.sa_family = AF_INET6;
@@ -1362,66 +856,71 @@
 
 		} /* switch (record type) */
 
-		/* increment the counter for number of records saved into our local response */
-		nb_saved_records += 1;
+		/* Increment the counter for number of records saved into our
+		 * local response */
+		nb_saved_records++;
 
-		/* move forward dns_answer_record->data_len for analyzing next record in the response */
-		if (dns_answer_record->type == DNS_RTYPE_SRV)
-			reader += offset;
-		else
-			reader += dns_answer_record->data_len;
+		/* Move forward dns_answer_record->data_len for analyzing next
+		 * record in the response */
+		reader += ((dns_answer_record->type == DNS_RTYPE_SRV)
+			   ? offset
+			   : dns_answer_record->data_len);
 
 		/* Lookup to see if we already had this entry */
-
 		found = 0;
 		list_for_each_entry(tmp_record, &dns_p->answer_list, list) {
 			if (tmp_record->type != dns_answer_record->type)
 				continue;
-			switch (tmp_record->type) {
-			case DNS_RTYPE_A:
-				if (!memcmp(&((struct sockaddr_in *)&dns_answer_record->address)->sin_addr,
-				    &((struct sockaddr_in *)&tmp_record->address)->sin_addr, sizeof(in_addr_t)))
-					found = 1;
-				break;
-			case DNS_RTYPE_AAAA:
-				if (!memcmp(&((struct sockaddr_in6 *)&dns_answer_record->address)->sin6_addr,
-				    &((struct sockaddr_in6 *)&tmp_record->address)->sin6_addr, sizeof(struct in6_addr)))
-					found = 1;
-				break;
+
+			switch(tmp_record->type) {
+				case DNS_RTYPE_A:
+					if (!memcmp(&((struct sockaddr_in *)&dns_answer_record->address)->sin_addr,
+						    &((struct sockaddr_in *)&tmp_record->address)->sin_addr,
+						    sizeof(in_addr_t)))
+						found = 1;
+					break;
+
+				case DNS_RTYPE_AAAA:
+					if (!memcmp(&((struct sockaddr_in6 *)&dns_answer_record->address)->sin6_addr,
+						    &((struct sockaddr_in6 *)&tmp_record->address)->sin6_addr,
+						    sizeof(struct in6_addr)))
+						found = 1;
+					break;
+
 			case DNS_RTYPE_SRV:
                                 if (dns_answer_record->data_len == tmp_record->data_len &&
-				    !memcmp(dns_answer_record->target,
-                                    tmp_record->target, dns_answer_record->data_len) &&
+				    !memcmp(dns_answer_record->target, tmp_record->target, dns_answer_record->data_len) &&
 				    dns_answer_record->port == tmp_record->port) {
 					tmp_record->weight = dns_answer_record->weight;
                                         found = 1;
 				}
                                 break;
+
 			default:
 				break;
 			}
+
 			if (found == 1)
 				break;
 		}
+
 		if (found == 1) {
 			tmp_record->last_seen = now.tv_sec;
-			free_dns_answer_item(dns_answer_record);
-		} else {
+			pool_free2(dns_answer_item_pool, dns_answer_record);
+		}
+		else {
 			dns_answer_record->last_seen = now.tv_sec;
 			LIST_ADDQ(&dns_p->answer_list, &dns_answer_record->list);
 		}
-
 	} /* for i 0 to ancount */
 
-
-	/* save the number of records we really own */
+	/* Save the number of records we really own */
 	dns_p->header.ancount = nb_saved_records;
-
+	dns_check_dns_response(resolution);
 	return DNS_RESP_VALID;
 }
 
-/*
- * search dn_name resolution in resp.
+/* Searches dn_name resolution in resp.
  * If existing IP not found, return the first IP matching family_priority,
  * otherwise, first ip found
  * The following tasks are the responsibility of the caller:
@@ -1444,11 +943,11 @@
 	int j;
 	int score, max_score;
 
-	family_priority = dns_opts->family_prio;
-	*newip = newip4 = newip6 = NULL;
-	currentip_found = 0;
+	family_priority   = dns_opts->family_prio;
+	*newip = newip4   = newip6 = NULL;
+	currentip_found   = 0;
 	*newip_sin_family = AF_UNSPEC;
-	max_score = -1;
+	max_score         = -1;
 
 	/* Select an IP regarding configuration preference.
 	 * Top priority is the prefered network ip version,
@@ -1471,10 +970,12 @@
 		if (record->type == DNS_RTYPE_A) {
 			ip = &(((struct sockaddr_in *)&record->address)->sin_addr);
 			ip_type = AF_INET;
-		} else if (record->type == DNS_RTYPE_AAAA) {
+		}
+		else if (record->type == DNS_RTYPE_AAAA) {
 			ip_type = AF_INET6;
 			ip = &(((struct sockaddr_in6 *)&record->address)->sin6_addr);
-		} else
+		}
+		else
 			continue;
 		score = 0;
 
@@ -1502,32 +1003,28 @@
 			}
 		}
 
-		/* Check if the IP found in the record is already affected to a member of a group.
-		 * If yes, the score should be incremented by 2.
-		 */
-		if (owner) {
-			if (snr_check_ip_callback(owner, ip, &ip_type))
-			{
-				continue;
-			}
-		}
+		/* Check if the IP found in the record is already affected to a
+		 * member of a group.  If yes, the score should be incremented
+		 * by 2. */
+		if (owner && snr_check_ip_callback(owner, ip, &ip_type))
+			continue;
+
 		/* Check for current ip matching. */
 		if (ip_type == currentip_sin_family &&
 		    ((currentip_sin_family == AF_INET &&
-		      memcmp(ip, currentip, 4) == 0) ||
+		      !memcmp(ip, currentip, 4)) ||
 		     (currentip_sin_family == AF_INET6 &&
-		      memcmp(ip, currentip, 16) == 0))) {
-			score += 1;
+		      !memcmp(ip, currentip, 16)))) {
+			score++;
 			currentip_sel = 1;
-		} else
+		}
+		else
 			currentip_sel = 0;
 
-
 		/* Keep the address if the score is better than the previous
-		 * score. The maximum score is 15, if this value is reached,
-		 * we break the parsing. Implicitly, this score is reached
-		 * the ip selected is the current ip.
-		 */
+		 * score. The maximum score is 15, if this value is reached, we
+		 * break the parsing. Implicitly, this score is reached the ip
+		 * selected is the current ip. */
 		if (score > max_score) {
 			if (ip_type == AF_INET)
 				newip4 = ip;
@@ -1538,414 +1035,132 @@
 				return DNS_UPD_NO;
 			max_score = score;
 		}
-
-
 	} /* list for each record entries */
 
-	/* no IP found in the response */
+	/* No IP found in the response */
 	if (!newip4 && !newip6)
 		return DNS_UPD_NO_IP_FOUND;
 
-	/* case when the caller looks first for an IPv4 address */
+	/* Case when the caller looks first for an IPv4 address */
 	if (family_priority == AF_INET) {
 		if (newip4) {
 			*newip = newip4;
 			*newip_sin_family = AF_INET;
-			if (currentip_found == 1)
-				return DNS_UPD_NO;
-			goto return_DNS_UPD_SRVIP_NOT_FOUND;
 		}
 		else if (newip6) {
 			*newip = newip6;
 			*newip_sin_family = AF_INET6;
-			if (currentip_found == 1)
-				return DNS_UPD_NO;
-			goto return_DNS_UPD_SRVIP_NOT_FOUND;
 		}
+		if (!currentip_found)
+			goto not_found;
 	}
-	/* case when the caller looks first for an IPv6 address */
+	/* Case when the caller looks first for an IPv6 address */
 	else if (family_priority == AF_INET6) {
 		if (newip6) {
 			*newip = newip6;
 			*newip_sin_family = AF_INET6;
-			if (currentip_found == 1)
-				return DNS_UPD_NO;
-			goto return_DNS_UPD_SRVIP_NOT_FOUND;
 		}
 		else if (newip4) {
 			*newip = newip4;
 			*newip_sin_family = AF_INET;
-			if (currentip_found == 1)
-				return DNS_UPD_NO;
-			goto return_DNS_UPD_SRVIP_NOT_FOUND;
 		}
+		if (!currentip_found)
+			goto not_found;
 	}
-	/* case when the caller have no preference (we prefer IPv6) */
+	/* Case when the caller have no preference (we prefer IPv6) */
 	else if (family_priority == AF_UNSPEC) {
 		if (newip6) {
 			*newip = newip6;
 			*newip_sin_family = AF_INET6;
-			if (currentip_found == 1)
-				return DNS_UPD_NO;
-			goto return_DNS_UPD_SRVIP_NOT_FOUND;
 		}
 		else if (newip4) {
 			*newip = newip4;
 			*newip_sin_family = AF_INET;
-			if (currentip_found == 1)
-				return DNS_UPD_NO;
-			goto return_DNS_UPD_SRVIP_NOT_FOUND;
 		}
+		if (!currentip_found)
+			goto not_found;
 	}
 
-	/* no reason why we should change the server's IP address */
+	/* No reason why we should change the server's IP address */
 	return DNS_UPD_NO;
 
- return_DNS_UPD_SRVIP_NOT_FOUND:
+ not_found:
 	list_for_each_entry(record, &dns_p->answer_list, list) {
-		/* move the first record to the end of the list, for internal round robin */
-		if (record) {
-			LIST_DEL(&record->list);
-			LIST_ADDQ(&dns_p->answer_list, &record->list);
-			break;
-		}
+		/* Move the first record to the end of the list, for internal
+		 * round robin */
+		LIST_DEL(&record->list);
+		LIST_ADDQ(&dns_p->answer_list, &record->list);
+		break;
 	}
 	return DNS_UPD_SRVIP_NOT_FOUND;
 }
 
-/*
- * returns the query id contained in a DNS response
- */
-unsigned short dns_response_get_query_id(unsigned char *resp)
-{
-	/* read the query id from the response */
-	return resp[0] * 256 + resp[1];
-}
-
-/*
- * used during haproxy's init phase
- * parses resolvers sections and initializes:
- *  - task (time events) for each resolvers section
- *  - the datagram layer (network IO events) for each nameserver
- * It takes one argument:
- *  - close_first takes 2 values: 0 or 1. If 1, the connection is closed first.
- * returns:
- *  0 in case of error
- *  1 when no error
- */
-int dns_init_resolvers(int close_socket)
-{
-	struct dns_resolvers *curr_resolvers;
-	struct dns_nameserver *curnameserver;
-	struct dns_resolution *resolution, *res_back;
-	struct dgram_conn *dgram;
-	struct task *t;
-	int fd;
-
-	/* initialize our DNS resolution cache */
-	dns_lru_tree = lru64_new(dns_cache_size);
-
-	/* give a first random value to our dns query_id seed */
-	dns_query_id_seed = random();
-
-	/* Initialize the answer items pool */
-	dns_answer_item_pool = create_pool("dns_answer_item",
-	    sizeof(struct dns_answer_item), MEM_F_SHARED);
-	if (dns_answer_item_pool == NULL) {
-		Alert("Failed to create the dns answer items pool");
-		return 0;
-	}
-
-	/* run through the resolvers section list */
-	list_for_each_entry(curr_resolvers, &dns_resolvers, list) {
-		/* create the task associated to the resolvers section */
-		if ((t = task_new()) == NULL) {
-			Alert("Starting [%s] resolvers: out of memory.\n", curr_resolvers->id);
-			return 0;
-		}
-
-		/* update task's parameters */
-		t->process = dns_process_resolve;
-		t->context = curr_resolvers;
-
-		/* no need to keep the new task if one is already affected to our resolvers
-		 * section */
-		if (!curr_resolvers->t)
-			curr_resolvers->t = t;
-		else
-			task_free(t);
-
-		list_for_each_entry(curnameserver, &curr_resolvers->nameserver_list, list) {
-			dgram = NULL;
-
-			if (close_socket == 1) {
-				if (curnameserver->dgram) {
-					fd_delete(curnameserver->dgram->t.sock.fd);
-					memset(curnameserver->dgram, '\0', sizeof(*dgram));
-					dgram = curnameserver->dgram;
-				}
-			}
-
-			/* allocate memory only if it has not already been allocated
-			 * by a previous call to this function */
-
-			if (!dgram && (dgram = calloc(1, sizeof(*dgram))) == NULL) {
-				Alert("Starting [%s/%s] nameserver: out of memory.\n", curr_resolvers->id,
-						curnameserver->id);
-				return 0;
-			}
-			/* update datagram's parameters */
-			dgram->owner = (void *)curnameserver;
-			dgram->data = &resolve_dgram_cb;
-
-			/* create network UDP socket for this nameserver */
-			if ((fd = socket(curnameserver->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
-				Alert("Starting [%s/%s] nameserver: can't create socket.\n", curr_resolvers->id,
-						curnameserver->id);
-				free(dgram);
-				dgram = NULL;
-				return 0;
-			}
-
-			/* "connect" the UDP socket to the name server IP */
-			if (connect(fd, (struct sockaddr*)&curnameserver->addr, get_addr_len(&curnameserver->addr)) == -1) {
-				Alert("Starting [%s/%s] nameserver: can't connect socket.\n", curr_resolvers->id,
-						curnameserver->id);
-				close(fd);
-				free(dgram);
-				dgram = NULL;
-				return 0;
-			}
-
-			/* make the socket non blocking */
-			fcntl(fd, F_SETFL, O_NONBLOCK);
-
-			/* add the fd in the fd list and update its parameters */
-			fdtab[fd].owner = dgram;
-			fdtab[fd].iocb = dgram_fd_handler;
-			fd_insert(fd);
-			fd_want_recv(fd);
-			dgram->t.sock.fd = fd;
-
-			/* update nameserver's datagram property */
-			curnameserver->dgram = dgram;
-
-			continue;
-		}
-
-		if (close_socket == 0)
-			continue;
-
-		/* now, we can trigger DNS resolution */
-		list_for_each_entry_safe(resolution, res_back, &curr_resolvers->resolution.wait, list) {
-			/* if there is no requester in the wait queue, no need to trigger the resolution */
-			if (LIST_ISEMPTY(&resolution->requester.wait))
-				continue;
-
-			dns_trigger_resolution(resolution);
-		}
-
-		/* task can be queued */
-		task_queue(t);
-	}
-
-	return 1;
-}
-
-/*
- * Allocate a pool of resolution to a resolvers section.
- * Each resolution is associated with a UUID.
+/* Turns a domain name label into a string.
  *
- * Return code:
- * -  0 if everything went smoothly
- * - -1 if an error occured
- */
-int dns_alloc_resolution_pool(struct dns_resolvers *resolvers)
-{
-	int i;
-	struct dns_resolution *resolution;
-
-	/* return if a pool has already been set for this resolvers */
-	if (!LIST_ISEMPTY(&resolvers->resolution.pool)) {
-		return 0;
-	}
-
-	for (i = 0; i < resolvers->resolution_pool_size; i++) {
-		resolution = dns_alloc_resolution();
-		if (!resolution) {
-			Alert("Starting [%s] resolvers: can't allocate memory for DNS resolution pool.\n", resolvers->id);
-			return -1;
-		}
-		resolution->uuid = i;
-		LIST_ADDQ(&resolvers->resolution.pool, &resolution->list);
-	}
-
-	return 0;
-}
-
-/*
- * Forge a DNS query. It needs the following information from the caller:
- *  - <query_id>: the DNS query id corresponding to this query
- *  - <query_type>: DNS_RTYPE_* request DNS record type (A, AAAA, ANY, etc...)
- *  - <hostname_dn>: hostname in domain name format
- *  - <hostname_dn_len>: length of <hostname_dn>
- * To store the query, the caller must pass a buffer <buf> and its size <bufsize>
+ * <dn> must be a null-terminated string. <dn_len> must include the terminating
+ * null byte. <str> must be allocated and its size must be passed in <str_len>.
  *
- * the DNS query is stored in <buf>
- * returns:
- *  -1 if <buf> is too short
+ *  In case of error, -1 is returned, otherwise, the number of bytes copied in
+ * <str> (including the terminating null byte).
  */
-int dns_build_query(int query_id, int query_type, unsigned int accepted_payload_size, char *hostname_dn, int hostname_dn_len, char *buf, int bufsize)
+int dns_dn_label_to_str(const char *dn, int dn_len, char *str, int str_len)
 {
-	struct dns_header *dns;
-	struct dns_question qinfo;
-	struct dns_additional_record edns;
-	char *ptr, *bufend;
-
-	memset(buf, '\0', bufsize);
-	ptr = buf;
-	bufend = buf + bufsize;
-
-	/* check if there is enough room for DNS headers */
-	if (ptr + sizeof(struct dns_header) >= bufend)
-		return -1;
-
-	/* set dns query headers */
-	dns = (struct dns_header *)ptr;
-	dns->id = (unsigned short) htons(query_id);
-	dns->flags = htons(0x0100); /* qr=0, opcode=0, aa=0, tc=0, rd=1, ra=0, z=0, rcode=0 */
-	dns->qdcount = htons(1);	/* 1 question */
-	dns->ancount = 0;
-	dns->nscount = 0;
-	dns->arcount = htons(1);
-
-	/* move forward ptr */
-	ptr += sizeof(struct dns_header);
-
-	/* check if there is enough room for query hostname */
-	if ((ptr + hostname_dn_len) >= bufend)
-		return -1;
-
-	/* set up query hostname */
-	memcpy(ptr, hostname_dn, hostname_dn_len);
-	ptr[hostname_dn_len + 1] = '\0';
-
-	/* move forward ptr */
-	ptr += (hostname_dn_len + 1);
-
-	/* check if there is enough room for query hostname*/
-	if (ptr + sizeof(struct dns_question) >= bufend)
-		return -1;
-
-	/* set up query info (type and class) */
-	qinfo.qtype = htons(query_type);
-	qinfo.qclass = htons(DNS_RCLASS_IN);
-	memcpy(ptr, &qinfo, sizeof(qinfo));
-
-	ptr += sizeof(struct dns_question);
+	char *ptr;
+	int i, sz;
 
-	/* check if there is enough room for additional records */
-	if (ptr + sizeof(edns) >= bufend)
+	if (str_len < dn_len - 1)
 		return -1;
 
-	/* set the DNS extension */
-	edns.name = 0;
-	edns.type = htons(DNS_RTYPE_OPT);
-	edns.udp_payload_size = htons(accepted_payload_size);
-	edns.extension = 0;
-	edns.data_length = 0;
-	memcpy(ptr, &edns, sizeof(edns));
-	ptr += sizeof(edns);
-
-	return ptr - buf;
-}
-
-/* Turn a domain name label into a string */
-void dns_dn_label_to_str(char *dn, char *str, int dn_len)
-{
-	int remain_size = 0;
-	int i;
-
-	for (i = 0; i < dn_len; i++) {
-		if (remain_size == 0) {
-			remain_size = dn[i];
-			if (i != 0) {
-				str[i - 1] = '.';
-
-			}
-		} else {
-			str[i - 1] = dn[i];
-			remain_size--;
-		}
+	ptr = str;
+	for (i = 0; i < dn_len-1; ++i) {
+		sz = dn[i];
+		if (i)
+			*ptr++ = '.';
+		memcpy(ptr, dn+i+1, sz);
+		ptr += sz;
+		i   += sz;
 	}
-	str[dn_len - 1] = 0;
-
+	*ptr++ = '\0';
+	return (ptr - str);
 }
 
-/*
- * turn a string into domain name label:
- * www.haproxy.org into 3www7haproxy3org
- * if dn memory is pre-allocated, you must provide its size in dn_len
- * if dn memory isn't allocated, dn_len must be set to 0.
- * In the second case, memory will be allocated.
- * in case of error, -1 is returned, otherwise, number of bytes copied in dn
+/* Turns a string into domain name label: www.haproxy.org into 3www7haproxy3org
+ *
+ * <str> must be a null-terminated string. <str_len> must include the
+ * terminating null byte. <dn> buffer must be allocated and its size must be
+ * passed in <dn_len>.
+ *
+ *  In case of error, -1 is returned, otherwise, the number of bytes copied in
+ * <dn> (excluding the terminating null byte).
  */
-char *dns_str_to_dn_label(const char *string, char *dn, int dn_len)
+int dns_str_to_dn_label(const char *str, int str_len, char *dn, int dn_len)
 {
-	char *c, *d;
 	int i, offset;
 
-	/* offset between string size and theorical dn size */
-	offset = 1;
-
-	/*
-	 * first, get the size of the string turned into its domain name version
-	 * This function also validates the string respect the RFC
-	 */
-	if ((i = dns_str_to_dn_label_len(string)) == -1)
-		return NULL;
-
-	/* yes, so let's check there is enough memory */
-	if (dn_len < i + offset)
-		return NULL;
+	if (dn_len < str_len + 1)
+		return -1;
 
-	i = strlen(string);
-	memcpy(dn + offset, string, i);
-	dn[i + offset] = '\0';
-	/* avoid a '\0' at the beginning of dn string which may prevent the for loop
-	 * below from working.
-	 * Actually, this is the reason of the offset. */
-	dn[0] = '0';
+	/* First byte of dn will be used to store the length of the first
+	 * label */
+	offset = 0;
+	for (i = 0; i < str_len; ++i) {
+		if (str[i] == '.') {
+			/* 2 or more consecutive dots is invalid */
+			if (i == offset)
+				return -1;
 
-	for (c = dn; *c ; ++c) {
-		/* c points to the first '0' char or a dot, which we don't want to read */
-		d = c + offset;
-		i = 0;
-		while (*d != '.' && *d) {
-			i++;
-			d++;
+			dn[offset] = (i - offset);
+			offset = i+1;
+			continue;
 		}
-		*c = i;
-
-		c = d - 1; /* because of c++ of the for loop */
+		dn[i+1] = str[i];
 	}
-
-	return dn;
-}
-
-/*
- * compute and return the length of <string> it it were translated into domain name
- * label:
- * www.haproxy.org into 3www7haproxy3org would return 16
- * NOTE: add +1 for '\0' when allocating memory ;)
- */
-int dns_str_to_dn_label_len(const char *string)
-{
-	return strlen(string) + 1;
+	dn[offset] = (i - offset - 1);
+	dn[i] = '\0';
+	return i;
 }
 
-/*
- * validates host name:
+/* Validates host name:
  *  - total size
  *  - each label size individually
  * returns:
@@ -1996,628 +1211,736 @@
 	return 1;
 }
 
-/*
- * 2 bytes random generator to generate DNS query ID
+/* Picks up an available resolution from the different resolution list
+ * associated to a resolvers section, in this order:
+ *   1. check in resolutions.curr for the same hostname and query_type
+ *   2. check in resolutions.wait for the same hostname and query_type
+ *   3. Get a new resolution from resolution pool
+ *
+ * Returns an available resolution, NULL if none found.
  */
-uint16_t dns_rnd16(void)
+static struct dns_resolution *dns_pick_resolution(struct dns_resolvers *resolvers,
+						  char **hostname_dn, int hostname_dn_len,
+						  int query_type)
 {
-	dns_query_id_seed ^= dns_query_id_seed << 13;
-	dns_query_id_seed ^= dns_query_id_seed >> 7;
-	dns_query_id_seed ^= dns_query_id_seed << 17;
-	return dns_query_id_seed;
+	struct dns_resolution *res;
+
+	if (!*hostname_dn)
+		goto from_pool;
+
+	/* Search for same hostname and query type in resolutions.curr */
+	list_for_each_entry(res, &resolvers->resolutions.curr, list) {
+		if (!res->hostname_dn)
+			continue;
+		if ((query_type == res->prefered_query_type) &&
+		    hostname_dn_len == res->hostname_dn_len  &&
+		    !memcmp(*hostname_dn, res->hostname_dn, hostname_dn_len))
+			return res;
+	}
+
+	/* Search for same hostname and query type in resolutions.wait */
+	list_for_each_entry(res, &resolvers->resolutions.wait, list) {
+		if (!res->hostname_dn)
+			continue;
+		if ((query_type == res->prefered_query_type) &&
+		    hostname_dn_len == res->hostname_dn_len  &&
+		    !memcmp(*hostname_dn, res->hostname_dn, hostname_dn_len))
+			return res;
+	}
+
+  from_pool:
+	/* No resolution could be found, so let's allocate a new one */
+	res = pool_alloc2(dns_resolution_pool);
+	if (res) {
+		memset(res, 0, sizeof(*res));
+		res->resolvers  = resolvers;
+		res->uuid       = resolution_uuid;
+		res->status     = RSLV_STATUS_NONE;
+		res->step       = RSLV_STEP_NONE;
+		res->last_valid = now_ms;
+
+		LIST_INIT(&res->requesters);
+		LIST_INIT(&res->response.answer_list);
+
+		res->prefered_query_type = query_type;
+		res->query_type          = query_type;
+		res->hostname_dn         = *hostname_dn;
+		res->hostname_dn_len     = hostname_dn_len;
+
+		++resolution_uuid;
+
+		/* Move the resolution to the resolvers wait queue */
+		LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
+	}
+	return res;
+}
+
+/* Releases a resolution from its requester(s) and move it back to the pool */
+static void dns_free_resolution(struct dns_resolution *resolution)
+{
+	struct dns_requester *req, *reqback;
+
+	/* clean up configuration */
+	dns_reset_resolution(resolution);
+	resolution->hostname_dn = NULL;
+	resolution->hostname_dn_len = 0;
+
+	list_for_each_entry_safe(req, reqback, &resolution->requesters, list) {
+		LIST_DEL(&req->list);
+		req->resolution = NULL;
+	}
+
+	LIST_DEL(&resolution->list);
+	pool_free2(dns_resolution_pool, resolution);
+}
+
+/* Links a requester (a server or a dns_srvrq) with a resolution. It returns 0
+ * on success, -1 otherwise.
+ */
+int dns_link_resolution(void *requester, int requester_type)
+{
+	struct dns_resolution *res = NULL;
+	struct dns_requester  *req;
+	struct dns_resolvers  *resolvers;
+	struct server         *srv   = NULL;
+	struct dns_srvrq      *srvrq = NULL;
+	char **hostname_dn;
+	int   hostname_dn_len, query_type;
+
+	switch (requester_type) {
+		case OBJ_TYPE_SERVER:
+			srv             = (struct server *)requester;
+			hostname_dn     = &srv->hostname_dn;
+			hostname_dn_len = srv->hostname_dn_len;
+			resolvers       = srv->resolvers;
+			query_type      = ((srv->dns_opts.family_prio == AF_INET)
+					   ? DNS_RTYPE_A
+					   : DNS_RTYPE_AAAA);
+			break;
+
+		case OBJ_TYPE_SRVRQ:
+			srvrq           = (struct dns_srvrq *)requester;
+			hostname_dn     = &srvrq->hostname_dn;
+			hostname_dn_len = srvrq->hostname_dn_len;
+			resolvers       = srvrq->resolvers;
+			query_type      = DNS_RTYPE_SRV;
+			break;
+
+		default:
+			goto err;
+	}
+
+	/* Get a resolution from the resolvers' wait queue or pool */
+	if ((res = dns_pick_resolution(resolvers, hostname_dn, hostname_dn_len, query_type)) == NULL)
+		goto err;
+
+	if (srv) {
+		if (srv->dns_requester == NULL) {
+			if ((req = calloc(1, sizeof(*req))) == NULL)
+				goto err;
+			req->owner         = &srv->obj_type;
+			srv->dns_requester = req;
+		}
+		else
+			req = srv->dns_requester;
+	}
+	else if (srvrq) {
+		if (srvrq->dns_requester == NULL) {
+			if ((req = calloc(1, sizeof(*req))) == NULL)
+				goto err;
+			req->owner           = &srvrq->obj_type;
+			srvrq->dns_requester = req;
+		}
+		else
+			req = srvrq->dns_requester;
+	}
+	else
+		goto err;
+
+	req->resolution         = res;
+	req->requester_cb       = snr_resolution_cb;
+	req->requester_error_cb = snr_resolution_error_cb;
+
+	LIST_ADDQ(&res->requesters, &req->list);
+	return 0;
+
+  err:
+	if (res && LIST_ISEMPTY(&res->requesters))
+		dns_free_resolution(res);
+	return -1;
+}
+
+/* Removes a requester from a DNS resoltion. It takes takes care of all the
+ * consequences. It also cleans up some parameters from the requester.
+ */
+void dns_unlink_resolution(struct dns_requester *requester)
+{
+	struct dns_resolution *res;
+	struct dns_requester  *req;
+
+	/* Nothing to do */
+	if (!requester || !requester->resolution)
+		return;
+	res = requester->resolution;
+
+	/* Clean up the requester */
+	LIST_DEL(&requester->list);
+	requester->resolution = NULL;
+
+	/* We need to find another requester linked on this resolution */
+	if (!LIST_ISEMPTY(&res->requesters))
+		req = LIST_NEXT(&res->requesters, struct dns_requester *, list);
+	else {
+		dns_free_resolution(res);
+		return;
+	}
+
+	/* Move hostname_dn related pointers to the next requester */
+	switch (obj_type(req->owner)) {
+		case OBJ_TYPE_SERVER:
+			res->hostname_dn     = objt_server(req->owner)->hostname_dn;
+			res->hostname_dn_len = objt_server(req->owner)->hostname_dn_len;
+			break;
+		case OBJ_TYPE_SRVRQ:
+			res->hostname_dn     = objt_dns_srvrq(req->owner)->hostname_dn;
+			res->hostname_dn_len = objt_dns_srvrq(req->owner)->hostname_dn_len;
+			break;
+		default:
+			res->hostname_dn     = NULL;
+			res->hostname_dn_len = 0;
+			break;
+	}
 }
 
+/* Called when a network IO is generated on a name server socket for an incoming
+ * packet. It performs the following actions:
+ *  - check if the packet requires processing (not outdated resolution)
+ *  - ensure the DNS packet received is valid and call requester's callback
+ *  - call requester's error callback if invalid response
+ *  - check the dn_name in the packet against the one sent
+ */
+static void dns_resolve_recv(struct dgram_conn *dgram)
+{
+	struct dns_nameserver *ns, *tmpns;
+	struct dns_resolvers  *resolvers;
+	struct dns_resolution *res;
+	struct dns_query_item *query;
+	unsigned char  buf[DNS_MAX_UDP_MESSAGE + 1];
+	unsigned char *bufend;
+	int fd, buflen, dns_resp;
+	int max_answer_records;
+	unsigned short query_id;
+	struct eb32_node *eb;
+	struct dns_requester *req;
+
+	fd = dgram->t.sock.fd;
+
+	/* check if ready for reading */
+	if (!fd_recv_ready(fd))
+		return;
+
+	/* no need to go further if we can't retrieve the nameserver */
+	if ((ns = dgram->owner) == NULL)
+		return;
+
+	resolvers = ns->resolvers;
+
+	/* process all pending input messages */
+	while (1) {
+		/* read message received */
+		memset(buf, '\0', resolvers->accepted_payload_size + 1);
+		if ((buflen = recv(fd, (char*)buf , resolvers->accepted_payload_size + 1, 0)) < 0) {
+			/* FIXME : for now we consider EAGAIN only */
+			fd_cant_recv(fd);
+			break;
+		}
+
+		/* message too big */
+		if (buflen > resolvers->accepted_payload_size) {
+			ns->counters.too_big++;
+			continue;
+		}
+
+		/* initializing variables */
+		bufend = buf + buflen;	/* pointer to mark the end of the buffer */
+
+		/* read the query id from the packet (16 bits) */
+		if (buf + 2 > bufend) {
+			ns->counters.invalid++;
+			continue;
+		}
+		query_id = dns_response_get_query_id(buf);
+
+		/* search the query_id in the pending resolution tree */
+		eb = eb32_lookup(&resolvers->query_ids, query_id);
+		if (eb == NULL) {
+			/* unknown query id means an outdated response and can be safely ignored */
+			ns->counters.outdated++;
+			continue;
+		}
+
+		/* known query id means a resolution in prgress */
+		res = eb32_entry(eb, struct dns_resolution, qid);
+		if (!res) {
+			ns->counters.outdated++;
+			continue;
+		}
 
-/*
- * function called when a timeout occurs during name resolution process
- * if max number of tries is reached, then stop, otherwise, retry.
- */
-struct task *dns_process_resolve(struct task *t)
-{
-	struct dns_resolvers *resolvers = t->context;
-	struct dns_resolution *resolution, *res_back;
-	int res_preferred_afinet, res_preferred_afinet6;
-	struct dns_options *dns_opts = NULL;
+		/* number of responses received */
+		res->nb_responses++;
 
-	/* if both there is no resolution in the run queue, we can re-schedule a wake up */
-	if (LIST_ISEMPTY(&resolvers->resolution.curr)) {
-		/* no first entry, so wake up was useless */
-		dns_update_resolvers_timeout(resolvers);
-		return t;
-	}
+		max_answer_records = (resolvers->accepted_payload_size - DNS_HEADER_SIZE) / DNS_MIN_RECORD_SIZE;
+		dns_resp = dns_validate_dns_response(buf, bufend, res, max_answer_records);
 
-	/* look for the first resolution which is not expired */
-	list_for_each_entry_safe(resolution, res_back, &resolvers->resolution.curr, list) {
-		struct dns_requester *requester = NULL;
+		switch (dns_resp) {
+			case DNS_RESP_VALID:
+				break;
 
-		/* when we find the first resolution in the future, then we can stop here */
-		if (tick_is_le(now_ms, resolution->last_sent_packet))
-			goto out;
+			case DNS_RESP_INVALID:
+			case DNS_RESP_QUERY_COUNT_ERROR:
+			case DNS_RESP_WRONG_NAME:
+				res->status = RSLV_STATUS_INVALID;
+				ns->counters.invalid++;
+				break;
 
-		if (LIST_ISEMPTY(&resolution->requester.curr))
-			goto out;
+			case DNS_RESP_NX_DOMAIN:
+				res->status = RSLV_STATUS_NX;
+				ns->counters.nx++;
+				break;
 
-		/*
-		 * if current resolution has been tried too many times and finishes in timeout
-		 * we update its status and remove it from the list
-		 */
-		if (resolution->try <= 0) {
-			struct dns_requester *tmprequester;
-			/* clean up resolution information and remove from the list */
-			dns_reset_resolution(resolution);
+			case DNS_RESP_REFUSED:
+				res->status = RSLV_STATUS_REFUSED;
+				ns->counters.refused++;
+				break;
 
-			LIST_DEL(&resolution->list);
-			LIST_ADDQ(&resolvers->resolution.wait, &resolution->list);
+			case DNS_RESP_ANCOUNT_ZERO:
+				res->status = RSLV_STATUS_OTHER;
+				ns->counters.any_err++;
+				break;
 
-			if (resolution->status != RSLV_STATUS_TIMEOUT) {
-				resolution->status = RSLV_STATUS_TIMEOUT;
-				resolution->last_status_change = now_ms;
-			}
+			case DNS_RESP_CNAME_ERROR:
+				res->status = RSLV_STATUS_OTHER;
+				ns->counters.cname_error++;
+				break;
 
-			/* notify the result to the requesters */
-			list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
-				requester->requester_error_cb(requester, DNS_RESP_TIMEOUT);
-				LIST_DEL(&requester->list);
-				LIST_ADDQ(&resolution->requester.wait, &requester->list);
-			}
+			case DNS_RESP_TRUNCATED:
+				res->status = RSLV_STATUS_OTHER;
+				ns->counters.truncated++;
+				break;
 
-			/* this might be triggered by too big UDP packets dropped
-			 * somewhere on the network, so lowering the accepted_payload_size
-			 * announced */
-			if (resolvers->accepted_payload_size > 1280)
-				resolvers->accepted_payload_size = 1280;
-			goto out;
+			case DNS_RESP_NO_EXPECTED_RECORD:
+			case DNS_RESP_ERROR:
+			case DNS_RESP_INTERNAL:
+				res->status = RSLV_STATUS_OTHER;
+				ns->counters.other++;
+				break;
 		}
 
-		resolution->try -= 1;
-
-		/* running queue is empty, nothing to do but wait */
-		if (LIST_ISEMPTY(&resolution->requester.curr))
-			goto out;
-
-		requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
-
-		switch (obj_type(requester->requester)) {
-			case OBJ_TYPE_SERVER:
-				dns_opts = &(objt_server(requester->requester)->dns_opts);
-				res_preferred_afinet = dns_opts->family_prio == AF_INET && resolution->query_type == DNS_RTYPE_A;
-				res_preferred_afinet6 = dns_opts->family_prio == AF_INET6 && resolution->query_type == DNS_RTYPE_AAAA;
+		/* Wait all nameservers response to handle errors */
+		if (dns_resp != DNS_RESP_VALID && res->nb_responses < resolvers->nb_nameservers)
+			continue;
 
-				/* let's change the query type if needed */
-				if (res_preferred_afinet6) {
-					/* fallback from AAAA to A */
-					resolution->query_type = DNS_RTYPE_A;
+		/* Process error codes */
+		if (dns_resp != DNS_RESP_VALID)  {
+			if (res->prefered_query_type != res->query_type) {
+				/* The fallback on the query type was already performed,
+				 * so check the try counter. If it falls to 0, we can
+				 * report an error. Else, wait the next attempt. */
+				if (!res->try)
+					goto report_res_error;
+			}
+			else {
+				/* Fallback from A to AAAA or the opposite and re-send
+				 * the resolution immediately. try counter is not
+				 * decremented. */
+				if (res->prefered_query_type == DNS_RTYPE_A) {
+					res->query_type = DNS_RTYPE_AAAA;
+					dns_send_query(res);
 				}
-				else if (res_preferred_afinet) {
-					/* fallback from A to AAAA */
-					resolution->query_type = DNS_RTYPE_AAAA;
+				else if (res->prefered_query_type == DNS_RTYPE_AAAA) {
+					res->query_type = DNS_RTYPE_A;
+					dns_send_query(res);
 				}
+			}
+			continue;
+		}
 
-				break;
+		/* Now let's check the query's dname corresponds to the one we
+		 * sent. We can check only the first query of the list. We send
+		 * one query at a time so we get one query in the response */
+		query = LIST_NEXT(&res->response.query_list, struct dns_query_item *, list);
+		if (query && memcmp(query->name, res->hostname_dn, res->hostname_dn_len) != 0) {
+			dns_resp = DNS_RESP_WRONG_NAME;
+			ns->counters.other++;
+			goto report_res_error;
+		}
 
-			case OBJ_TYPE_SRVRQ:
-				break;
-			case OBJ_TYPE_NONE:
-			default:
-				/* clean up resolution information and remove from the list */
-				dns_reset_resolution(resolution);
+		/* So the resolution succeeded */
+		res->status     = RSLV_STATUS_VALID;
+		res->last_valid = now_ms;
+		ns->counters.valid++;
+		goto report_res_success;
 
-				LIST_DEL(&resolution->list);
-				LIST_ADDQ(&resolvers->resolution.wait, &resolution->list);
+	report_res_error:
+		list_for_each_entry(req, &res->requesters, list)
+			req->requester_error_cb(req, dns_resp);
+		dns_reset_resolution(res);
+		LIST_DEL(&res->list);
+		LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
+		continue;
 
-				/* notify the result to the requester */
-				requester->requester_error_cb(requester, DNS_RESP_INTERNAL);
-				goto out;
+	report_res_success:
+		/* Only the 1rst requester s managed by the server, others are
+		 * from the cache */
+		tmpns = ns;
+		list_for_each_entry(req, &res->requesters, list) {
+			req->requester_cb(req, tmpns);
+			tmpns = NULL;
 		}
 
-		/* resend the DNS query */
-		dns_send_query(resolution);
-
-		/* check if we have more than one resolution in the list */
-		if (dns_check_resolution_queue(resolvers) > 1) {
-			/* move the rsolution to the end of the list */
-			LIST_DEL(&resolution->list);
-			LIST_ADDQ(&resolvers->resolution.curr, &resolution->list);
-		}
+		dns_reset_resolution(res);
+		LIST_DEL(&res->list);
+		LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
+		continue;
 	}
-
- out:
 	dns_update_resolvers_timeout(resolvers);
-	return t;
 }
 
-/*
- * build a dns cache key composed as follow:
- *   <query type>#<hostname in domain name format>
- * and store it into <str>.
- * It's up to the caller to allocate <buf> and to reset it.
- * The function returns NULL in case of error (IE <buf> too small) or a pointer
- * to buf if successful
- */
-struct chunk *
-dns_cache_key(int query_type, char *hostname_dn, int hostname_dn_len, struct chunk *buf)
+/* Called when a resolvers network socket is ready to send data */
+static void dns_resolve_send(struct dgram_conn *dgram)
 {
-	int len, size;
-	char *str;
-
-	str = buf->str;
-	len = buf->len;
-	size = buf->size;
-
-	switch (query_type) {
-		case DNS_RTYPE_A:
-			if (len + 1 > size)
-				return NULL;
-			memcpy(&str[len], "A", 1);
-			len += 1;
-			break;
-		case DNS_RTYPE_AAAA:
-			if (len + 4 > size)
-				return NULL;
-			memcpy(&str[len], "AAAA", 4);
-			len += 4;
-			break;
-		default:
-			return NULL;
-	}
+	struct dns_resolvers  *resolvers;
+	struct dns_nameserver *ns;
+	struct dns_resolution *res;
+	int fd;
 
-	if (len + 1 > size)
-		return NULL;
-	memcpy(&str[len], "#", 1);
-	len += 1;
+	fd = dgram->t.sock.fd;
 
-	if (len + hostname_dn_len + 1 > size) // +1 for trailing zero
-		return NULL;
-	memcpy(&str[len], hostname_dn, hostname_dn_len);
-	len += hostname_dn_len;
-	str[len] = '\0';
+	/* check if ready for sending */
+	if (!fd_send_ready(fd))
+		return;
 
-	return buf;
-}
+	/* we don't want/need to be waked up any more for sending */
+	fd_stop_send(fd);
 
-/*
- * returns a pointer to a cache entry which may still be considered as up to date
- * by the caller.
- * returns NULL if no entry can be found or if the data found is outdated.
- */
-struct lru64 *
-dns_cache_lookup(int query_type, char *hostname_dn, int hostname_dn_len, int valid_period, void *cache_domain) {
-	struct lru64 *elem = NULL;
-	struct dns_resolution *resolution = NULL;
-	struct dns_resolvers *resolvers = NULL;
-	struct dns_requester *requester = NULL;
-	int inter = 0;
-	struct chunk *buf = get_trash_chunk();
-	struct chunk *tmp = NULL;
+	/* no need to go further if we can't retrieve the nameserver */
+	if ((ns = dgram->owner) == NULL)
+		return;
 
-	if (!dns_lru_tree)
-		return NULL;
+	resolvers = ns->resolvers;
+	list_for_each_entry(res, &resolvers->resolutions.curr, list) {
+		int ret;
 
-	chunk_reset(buf);
-	tmp = dns_cache_key(query_type, hostname_dn, hostname_dn_len, buf);
-	if (tmp == NULL)
-		return NULL;
+		if (res->nb_queries == resolvers->nb_nameservers)
+			continue;
 
-	elem = lru64_lookup(XXH64(buf->str, buf->len, 1), dns_lru_tree, cache_domain, 1);
+		trash.len = dns_build_query(res->query_id, res->query_type,
+					    resolvers->accepted_payload_size,
+					    res->hostname_dn, res->hostname_dn_len,
+					    trash.str, trash.size);
+		if (trash.len == -1)
+			goto snd_error;
 
-	if (!elem || !elem->data)
-		return NULL;
+		ret = send(fd, trash.str, trash.len, 0);
+		if (ret != trash.len)
+			goto snd_error;
 
-	resolution = elem->data;
+		ns->counters.sent++;
+		res->nb_queries++;
+		continue;
 
-	/* since we can change the fqdn of a server at run time, it may happen that
-	 * we got an innacurate elem.
-	 * This is because resolution->hostname_dn points to (owner)->hostname_dn (which
-	 * may be changed at run time)
-	 */
-	if ((hostname_dn_len == resolution->hostname_dn_len) &&
-	    (memcmp(hostname_dn, resolution->hostname_dn, hostname_dn_len) != 0)) {
-		return NULL;
+	  snd_error:
+		ns->counters.snd_error++;
+		res->nb_queries++;
 	}
+}
 
-	requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
+/* Processes DNS resolution. First, it checks the active list to detect expired
+ * resolutions and retry them if possible. Else a timeout is reported. Then, it
+ * checks the wait list to trigger new resolutions.
+ */
+static struct task *dns_process_resolvers(struct task *t)
+{
+	struct dns_resolvers  *resolvers = t->context;
+	struct dns_resolution *res, *resback;
+	int exp;
 
-	switch (obj_type(requester->requester)) {
-		case OBJ_TYPE_SERVER:
-			resolvers = objt_server(requester->requester)->resolvers;
+	/* Handle all expired resolutions from the active list */
+	list_for_each_entry_safe(res, resback, &resolvers->resolutions.curr, list) {
+		/* When we find the first resolution in the future, then we can
+		 * stop here */
+		exp = tick_add(res->last_query, resolvers->timeout.retry);
+		if (!tick_is_expired(exp, now_ms))
 			break;
-		case OBJ_TYPE_SRVRQ:
-			resolvers = objt_dns_srvrq(requester->requester)->resolvers;
-			break;
-		case OBJ_TYPE_NONE:
-		default:
-			return NULL;
-	}
 
-	if (!resolvers)
-		return NULL;
+		/* If current resolution has been tried too many times and
+		 * finishes in timeout we update its status and remove it from
+		 * the list */
+		if (!res->try) {
+			struct dns_requester *req;
 
-	if (resolvers->hold.valid < valid_period)
-		inter = resolvers->hold.valid;
-	else
-		inter = valid_period;
-
-	if (!tick_is_expired(tick_add(resolution->last_resolution, inter), now_ms))
-		return elem;
-
-	return NULL;
-}
+			/* Notify the result to the requesters */
+			if (!res->nb_responses)
+				res->status = RSLV_STATUS_TIMEOUT;
+			list_for_each_entry(req, &res->requesters, list)
+				req->requester_error_cb(req, res->status);
 
-/* if an arg is found, it sets the resolvers section pointer into cli.p0 */
-static int cli_parse_stat_resolvers(char **args, struct appctx *appctx, void *private)
-{
-	struct dns_resolvers *presolvers;
+			/* Clean up resolution info and remove it from the
+			 * current list */
+			dns_reset_resolution(res);
+			LIST_DEL(&res->list);
+			LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
 
-	if (*args[2]) {
-		list_for_each_entry(presolvers, &dns_resolvers, list) {
-			if (strcmp(presolvers->id, args[2]) == 0) {
-				appctx->ctx.cli.p0 = presolvers;
-				break;
-			}
+			/* This might be triggered by too big UDP packets
+			 * dropped somewhere on the network, so lowering the
+			 * accepted_payload_size announced */
+			if (resolvers->accepted_payload_size > 1280)
+				resolvers->accepted_payload_size = 1280;
 		}
-		if (appctx->ctx.cli.p0 == NULL) {
-			appctx->ctx.cli.severity = LOG_ERR;
-			appctx->ctx.cli.msg = "Can't find that resolvers section\n";
-			appctx->st0 = CLI_ST_PRINT;
-			return 1;
+		else {
+			/* Otherwise resend the DNS query and requeue the resolution */
+			if (!res->nb_responses || res->prefered_query_type != res->query_type) {
+				/* No response received (a real timeout) or fallback already done */
+				res->query_type = res->prefered_query_type;
+				res->try--;
+			}
+			else {
+				/* Fallback from A to AAAA or the opposite and re-send
+				 * the resolution immediately. try counter is not
+				 * decremented. */
+				if (res->prefered_query_type == DNS_RTYPE_A)
+					res->query_type = DNS_RTYPE_AAAA;
+				else if (res->prefered_query_type == DNS_RTYPE_AAAA)
+					res->query_type = DNS_RTYPE_A;
+				else
+					res->try--;
+			}
+			dns_send_query(res);
 		}
 	}
-	return 0;
-}
 
-/*
- * if <resolution> is provided, then the function skips the memory allocation part.
- * It does the linking only.
- *
- * if <resolution> is NULL, the function links a dns resolution to a requester:
- *  - it allocates memory for the struct requester used to link
- *    the resolution to the requester
- *  - it configures the resolution if this is the first requester to be linked to it
- *  - it updates the requester with a pointer to the resolution
- *
- * Return code:
- * -  0 if everything happened smoothly
- * - -1 if an error occured. Of course, no resolution is linked to the requester
- */
-int dns_link_resolution(void *requester, int requester_type, struct dns_resolution *resolution)
-{
-	struct dns_resolution *tmpresolution = NULL;
-	struct dns_requester *tmprequester = NULL;
-	struct dns_resolvers *resolvers = NULL;
-	char *hostname_dn = NULL;
-	int new_resolution;
-
-
-	if (!resolution) {
-		tmprequester = calloc(1, sizeof(*tmprequester));
-		if (!tmprequester)
-			return -1;
-
-		switch (requester_type) {
-			case OBJ_TYPE_SERVER:
-				tmprequester->requester = &((struct server *)requester)->obj_type;
-				hostname_dn = objt_server(tmprequester->requester)->hostname_dn;
-				resolvers = objt_server(tmprequester->requester)->resolvers;
-				switch (objt_server(tmprequester->requester)->dns_opts.family_prio) {
-					case AF_INET:
-						tmprequester->prefered_query_type = DNS_RTYPE_A;
-						break;
-					default:
-						tmprequester->prefered_query_type = DNS_RTYPE_AAAA;
-				}
-
-				break;
-			case OBJ_TYPE_SRVRQ:
-				tmprequester->requester = &((struct dns_srvrq *)requester)->obj_type;
-				hostname_dn = objt_dns_srvrq(requester)->hostname_dn;
-				resolvers = objt_dns_srvrq(requester)->resolvers;
-				break;
-			case OBJ_TYPE_NONE:
-			default:
-				free(tmprequester);
-				return -1;
-		}
+	/* Handle all resolutions in the wait list */
+	list_for_each_entry_safe(res, resback, &resolvers->resolutions.wait, list) {
+		exp = tick_add(res->last_resolution, dns_resolution_timeout(res));
+		if (tick_isset(res->last_resolution) && !tick_is_expired(exp, now_ms))
+			continue;
 
-		/* get a resolution from the resolvers' wait queue or pool */
-		tmpresolution = dns_resolution_list_get(resolvers, hostname_dn, tmprequester->prefered_query_type);
-		if (!tmpresolution) {
-			free(tmprequester);
-			return -1;
+		if (dns_run_resolution(res) != 1) {
+			res->last_resolution = now_ms;
+			LIST_DEL(&res->list);
+			LIST_ADDQ(&resolvers->resolutions.wait, &res->list);
 		}
 	}
-	else {
-		tmpresolution = resolution;
 
-		switch (requester_type) {
-			case OBJ_TYPE_SERVER:
-				tmprequester = ((struct server *)requester)->dns_requester;
-				resolvers = ((struct server *)requester)->resolvers;
-				break;
-			case OBJ_TYPE_SRVRQ:
-				tmprequester = objt_dns_srvrq(requester)->dns_requester;
-				resolvers = objt_dns_srvrq(requester)->resolvers;
-				break;
-			case OBJ_TYPE_NONE:
-			default:
-				return -1;
-		}
-	}
+	dns_update_resolvers_timeout(resolvers);
+	return t;
+}
 
-	/* flag this resolution as NEW if applicable (not already linked to any requester).
-	 * this is required to decide which parameters we have to update on the resolution.
-	 * If new, it means we pulled up the resolution from the resolvers' pool.
-	 */
-	if (LIST_ISEMPTY(&tmpresolution->requester.wait)) {
-		new_resolution = 1;
-	}
-	else
-		new_resolution = 0;
+/* proto_udp callback functions for a DNS resolution */
+struct dgram_data_cb resolve_dgram_cb = {
+	.recv = dns_resolve_recv,
+	.send = dns_resolve_send,
+};
 
-	/* those parameters are related to the requester type */
-	switch (obj_type(tmprequester->requester)) {
-		case OBJ_TYPE_SERVER:
-			/* some parameters should be set only if the resolution is brand new */
-			if (new_resolution) {
-				tmpresolution->query_type = tmprequester->prefered_query_type;
-				tmpresolution->hostname_dn = objt_server(tmprequester->requester)->hostname_dn;
-				tmpresolution->hostname_dn_len = objt_server(tmprequester->requester)->hostname_dn_len;
-			}
+/* Release memory allocated by DNS */
+static void dns_deinit(void)
+{
+	struct dns_resolvers  *resolvers, *resolversback;
+	struct dns_nameserver *ns, *nsback;
+	struct dns_resolution *res, *resback;
+	struct dns_requester  *req, *reqback;
+	struct dns_srvrq      *srvrq, *srvrqback;
 
-			/* update requester as well, only if we just allocated it */
-			objt_server(tmprequester->requester)->resolution = tmpresolution;
-			if (!resolution) {
-				tmprequester->requester_cb = snr_resolution_cb;
-				tmprequester->requester_error_cb = snr_resolution_error_cb;
-				objt_server(tmprequester->requester)->dns_requester = tmprequester;
-			}
-			break;
-		case OBJ_TYPE_SRVRQ:
-			/* some parameters should be set only if the resolution is brand new */
-			if (new_resolution) {
-				tmpresolution->query_type = DNS_RTYPE_SRV;
-				tmpresolution->hostname_dn = objt_dns_srvrq(tmprequester->requester)->hostname_dn;
-				tmpresolution->hostname_dn_len = objt_dns_srvrq(tmprequester->requester)->hostname_dn_len;
+	list_for_each_entry_safe(resolvers, resolversback, &dns_resolvers, list) {
+		list_for_each_entry_safe(ns, nsback, &resolvers->nameservers, list) {
+			free(ns->id);
+			free((char *)ns->conf.file);
+			if (ns->dgram && ns->dgram->t.sock.fd != -1)
+				fd_delete(ns->dgram->t.sock.fd);
+			free(ns->dgram);
+			LIST_DEL(&ns->list);
+			free(ns);
+		}
+
+		list_for_each_entry_safe(res, resback, &resolvers->resolutions.curr, list) {
+			list_for_each_entry_safe(req, reqback, &res->requesters, list) {
+				LIST_DEL(&req->list);
+				free(req);
 			}
+			dns_free_resolution(res);
+		}
 
-			/* update requester as well, only if we just allocated it */
-			objt_dns_srvrq(tmprequester->requester)->resolution = tmpresolution;
-			if (!resolution) {
-				tmprequester->requester_cb = snr_resolution_cb;
-				tmprequester->requester_error_cb = snr_resolution_error_cb;
-				objt_dns_srvrq(tmprequester->requester)->dns_requester = tmprequester;
+		list_for_each_entry_safe(res, resback, &resolvers->resolutions.wait, list) {
+			list_for_each_entry_safe(req, reqback, &res->requesters, list) {
+				LIST_DEL(&req->list);
+				free(req);
 			}
-			break;
+			dns_free_resolution(res);
+		}
 
-		case OBJ_TYPE_NONE:
-		default:
-			free(tmprequester);
-			return -1;
+		free(resolvers->id);
+		free((char *)resolvers->conf.file);
+		task_delete(resolvers->t);
+		task_free(resolvers->t);
+		LIST_DEL(&resolvers->list);
+		free(resolvers);
 	}
 
-	/* update some parameters only if this is a brand new resolution */
-	if (new_resolution) {
-		/* move the resolution to the requesters' wait queue */
-		LIST_DEL(&tmpresolution->list);
-		LIST_ADDQ(&resolvers->resolution.wait, &tmpresolution->list);
-
-		tmpresolution->status = RSLV_STATUS_NONE;
-		tmpresolution->step = RSLV_STEP_NONE;
-		tmpresolution->revision = 1;
-		LIST_INIT(&tmpresolution->response.answer_list);
+	list_for_each_entry_safe(srvrq, srvrqback, &dns_srvrq_list, list) {
+		free(srvrq->name);
+		free(srvrq->hostname_dn);
+		LIST_DEL(&srvrq->list);
+		free(srvrq);
 	}
 
-	/* add the requester to the resolution's wait queue */
-	if (resolution)
-		LIST_DEL(&tmprequester->list);
-	LIST_ADDQ(&tmpresolution->requester.wait, &tmprequester->list);
-
-	return 0;
+	pool_destroy2(dns_answer_item_pool);
+	pool_destroy2(dns_resolution_pool);
 }
 
-/*
- * pick up an available resolution from the different resolution list associated to a resolvers section,
- * in this order:
- * 1. check in resolution.curr for the same hostname and query_type
- * 2. check in resolution.wait for the same hostname and query_type
- * 3. take an available resolution from resolution.pool
- *
- * return an available resolution, NULL if none found.
+/* Finalizes the DNS configuration by allocating required resources and checking
+ * live parameters.
+ * Returns 0 on success, ERR_* flags otherwise.
  */
-struct dns_resolution *dns_resolution_list_get(struct dns_resolvers *resolvers, char *hostname_dn, int query_type)
+static int dns_finalize_config(void)
 {
-	struct dns_resolution *resolution, *tmpresolution;
-	struct dns_requester *requester;
+	struct dns_resolvers *resolvers;
+	struct proxy	     *px;
+	int err_code = 0;
 
-	if (hostname_dn) {
-		/* search for same hostname and query type in resolution.curr */
-		list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.curr, list) {
-			requester = NULL;
+	/* allocate pool of resolution per resolvers */
+	list_for_each_entry(resolvers, &dns_resolvers, list) {
+		struct dns_nameserver *ns;
+		struct task           *t;
 
-			if (!LIST_ISEMPTY(&resolution->requester.wait))
-				requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
-			else if (!LIST_ISEMPTY(&resolution->requester.curr))
-				requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
+		/* Check if we can create the socket with nameservers info */
+		list_for_each_entry(ns, &resolvers->nameservers, list) {
+			struct dgram_conn *dgram = NULL;
+			int fd;
 
-			if (!requester)
+			/* Check nameserver info */
+			if ((fd = socket(ns->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+				Alert("config : resolvers '%s': can't create socket for nameserver '%s'.\n",
+				      resolvers->id, ns->id);
+				err_code |= (ERR_ALERT|ERR_ABORT);
 				continue;
-
-			if ((query_type == requester->prefered_query_type) &&
-			    (resolution->hostname_dn &&
-			     strcmp(hostname_dn, resolution->hostname_dn) == 0)) {
-				return resolution;
 			}
-		}
-
-		/* search for same hostname and query type in resolution.wait */
-		list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.wait, list) {
-			requester = NULL;
-
-			if (!LIST_ISEMPTY(&resolution->requester.wait))
-				requester = LIST_NEXT(&resolution->requester.wait, struct dns_requester *, list);
-			else if (!LIST_ISEMPTY(&resolution->requester.curr))
-				requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, list);
-
-			if (!requester)
+			if (connect(fd, (struct sockaddr*)&ns->addr, get_addr_len(&ns->addr)) == -1) {
+				Alert("config : resolvers '%s': can't connect socket for nameserver '%s'.\n",
+				      resolvers->id, ns->id);
+				close(fd);
+				err_code |= (ERR_ALERT|ERR_ABORT);
 				continue;
-
-			if ((query_type == requester->prefered_query_type) &&
-			    (resolution->hostname_dn &&
-			     strcmp(hostname_dn, resolution->hostname_dn) == 0)) {
-				return resolution;
 			}
-		}
-	}
-	/* take the first one (hopefully) from the pool */
-	list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.pool, list) {
-		if (LIST_ISEMPTY(&resolution->requester.wait)) {
-			return resolution;
-		}
-	}
+			close(fd);
 
-	return NULL;
-}
+			/* Create dgram structure that will hold the UPD socket
+			 * and attach it on the current nameserver */
+			if ((dgram = calloc(1, sizeof(*dgram))) == NULL) {
+				Alert("config: resolvers '%s' : out of memory.\n",
+				      resolvers->id);
+				err_code |= (ERR_ALERT|ERR_ABORT);
+				goto err;
+			}
 
-/* This function allocates memory for a DNS resolution structure.
- * It's up to the caller to set the parameters
- * Returns a pointer to the structure resolution or NULL if memory could
- * not be allocated.
- */
-struct dns_resolution *dns_alloc_resolution(void)
-{
-	struct dns_resolution *resolution = NULL;
-	char *buffer = NULL;
+			/* Leave dgram partially initialized, no FD attached for
+			 * now. */
+			dgram->owner     = ns;
+			dgram->data      = &resolve_dgram_cb;
+			dgram->t.sock.fd = -1;
+			ns->dgram        = dgram;
+		}
 
-	resolution = calloc(1, sizeof(*resolution));
-	buffer = calloc(1, global.tune.bufsize);
+		/* Create the task associated to the resolvers section */
+		if ((t = task_new()) == NULL) {
+			Alert("config : resolvers '%s' : out of memory.\n", resolvers->id);
+			err_code |= (ERR_ALERT|ERR_ABORT);
+			goto err;
+		}
 
-	if (!resolution || !buffer) {
-		free(buffer);
-		free(resolution);
-		return NULL;
+		/* Update task's parameters */
+		t->process   = dns_process_resolvers;
+		t->context   = resolvers;
+		resolvers->t = t;
+		task_wakeup(t, TASK_WOKEN_INIT);
 	}
 
-	LIST_INIT(&resolution->requester.wait);
-	LIST_INIT(&resolution->requester.curr);
-
-	return resolution;
-}
-
-/* This function free the memory allocated to a DNS resolution */
-void dns_free_resolution(struct dns_resolution *resolution)
-{
-	free(resolution);
+	for (px = proxy; px; px = px->next) {
+		struct server *srv;
 
-	return;
-}
+		for (srv = px->srv; srv; srv = srv->next) {
+			struct dns_resolvers *resolvers;
 
-/* this function free a resolution from its requester(s) and move it back to the pool */
-void dns_resolution_free(struct dns_resolvers *resolvers, struct dns_resolution *resolution)
-{
-	struct dns_requester *requester, *tmprequester;
+			if (!srv->resolvers_id)
+				continue;
 
-	/* clean up configuration */
-	dns_reset_resolution(resolution);
-	resolution->hostname_dn = NULL;
-	resolution->hostname_dn_len = 0;
+			if ((resolvers = find_resolvers_by_id(srv->resolvers_id)) == NULL) {
+				Alert("config : %s '%s', server '%s': unable to find required resolvers '%s'\n",
+				      proxy_type_str(px), px->id, srv->id, srv->resolvers_id);
+				err_code |= (ERR_ALERT|ERR_ABORT);
+				continue;
+			}
+			srv->resolvers = resolvers;
 
-	list_for_each_entry_safe(requester, tmprequester, &resolution->requester.wait, list) {
-		LIST_DEL(&requester->list);
-	}
-	list_for_each_entry_safe(requester, tmprequester, &resolution->requester.curr, list) {
-		LIST_DEL(&requester->list);
+			if (srv->srvrq && !srv->srvrq->resolvers) {
+				srv->srvrq->resolvers = srv->resolvers;
+				if (dns_link_resolution(srv->srvrq, OBJ_TYPE_SRVRQ) == -1) {
+					Alert("config : %s '%s' : unable to set DNS resolution for server '%s'.\n",
+					      proxy_type_str(px), px->id, srv->id);
+					err_code |= (ERR_ALERT|ERR_ABORT);
+					continue;
+				}
+			}
+			if (dns_link_resolution(srv, OBJ_TYPE_SERVER) == -1) {
+				Alert("config : %s '%s', unable to set DNS resolution for server '%s'.\n",
+				      proxy_type_str(px), px->id, srv->id);
+				err_code |= (ERR_ALERT|ERR_ABORT);
+				continue;
+			}
+		}
 	}
 
-	LIST_DEL(&resolution->list);
-	LIST_ADDQ(&resolvers->resolution.pool, &resolution->list);
+	if (err_code & (ERR_ALERT|ERR_ABORT))
+		goto err;
 
-	return;
+	return err_code;
+  err:
+	dns_deinit();
+	return err_code;
+
 }
 
-/*
- * this function remove a requester from a resolution
- * and takes care of all the consequences.
- * It also cleans up some parameters from the requester
- */
-void dns_rm_requester_from_resolution(struct dns_requester *requester, struct dns_resolution *resolution)
+/* if an arg is found, it sets the resolvers section pointer into cli.p0 */
+static int cli_parse_stat_resolvers(char **args, struct appctx *appctx, void *private)
 {
-	char *hostname_dn;
-	struct dns_requester *tmprequester;
-
-	/* resolution is still used by other requesters, we need to move
-	 * some pointers to an other requester if needed
-	 */
-	switch (obj_type(requester->requester)) {
-		case OBJ_TYPE_SERVER:
-			hostname_dn = objt_server(requester->requester)->hostname_dn;
-			break;
-		case OBJ_TYPE_SRVRQ:
-			hostname_dn = objt_dns_srvrq(requester->requester)->hostname_dn;
-			break;
-		case OBJ_TYPE_NONE:
-		default:
-			hostname_dn = NULL;
-			break;
-	}
-
-	if (resolution->hostname_dn != hostname_dn)
-		return;
+	struct dns_resolvers *presolvers;
 
-	/* First, we need to find this other requester */
-	tmprequester = NULL;
-	list_for_each_entry(tmprequester, &resolution->requester.wait, list) {
-		if (tmprequester != requester)
-			break;
-	}
-	if (!tmprequester) {
-		/* if we can't find it in wait queue, let's get one in run queue */
-		list_for_each_entry(tmprequester, &resolution->requester.curr, list) {
-			if (tmprequester != requester)
+	if (*args[2]) {
+		list_for_each_entry(presolvers, &dns_resolvers, list) {
+			if (strcmp(presolvers->id, args[2]) == 0) {
+				appctx->ctx.cli.p0 = presolvers;
 				break;
+			}
 		}
-	}
-
-	/* move hostname_dn related pointers to the next requester */
-	switch (obj_type(tmprequester->requester)) {
-		case OBJ_TYPE_SERVER:
-			resolution->hostname_dn = objt_server(tmprequester->requester)->hostname_dn;
-			resolution->hostname_dn_len = objt_server(tmprequester->requester)->hostname_dn_len;
-			break;
-		case OBJ_TYPE_SRVRQ:
-			resolution->hostname_dn = objt_dns_srvrq(tmprequester->requester)->hostname_dn;
-			resolution->hostname_dn_len = objt_dns_srvrq(tmprequester->requester)->hostname_dn_len;
-			break;
-
-		case OBJ_TYPE_NONE:
-		default:
-			;;
-	}
-
-
-	/* clean up the requester */
-	LIST_DEL(&requester->list);
-	switch (obj_type(requester->requester)) {
-		case OBJ_TYPE_SERVER:
-			objt_server(requester->requester)->resolution = NULL;
-			break;
-		case OBJ_TYPE_SRVRQ:
-			objt_dns_srvrq(requester->requester)->resolution = NULL;
-			break;
-		case OBJ_TYPE_NONE:
-		default:
-			;;
+		if (appctx->ctx.cli.p0 == NULL) {
+			appctx->ctx.cli.severity = LOG_ERR;
+			appctx->ctx.cli.msg = "Can't find that resolvers section\n";
+			appctx->st0 = CLI_ST_PRINT;
+			return 1;
+		}
 	}
+	return 0;
 }
 
-/* This function dumps counters from all resolvers section and associated name
- * servers. It returns 0 if the output buffer is full and it needs to be called
- * again, otherwise non-zero. It may limit itself to the resolver pointed to by
+/* Dumps counters from all resolvers section and associated name servers. It
+ * returns 0 if the output buffer is full and it needs to be called again,
+ * otherwise non-zero. It may limit itself to the resolver pointed to by
  * <cli.p0> if it's not null.
  */
 static int cli_io_handler_dump_resolvers_to_buffer(struct appctx *appctx)
 {
 	struct stream_interface *si = appctx->owner;
-	struct dns_resolvers *presolvers;
-	struct dns_nameserver *pnameserver;
+	struct dns_resolvers    *resolvers;
+	struct dns_nameserver   *ns;
 
 	chunk_reset(&trash);
 
@@ -2631,28 +1954,30 @@
 			chunk_appendf(&trash, "No resolvers found\n");
 		}
 		else {
-			list_for_each_entry(presolvers, &dns_resolvers, list) {
-				if (appctx->ctx.cli.p0 != NULL && appctx->ctx.cli.p0 != presolvers)
+			list_for_each_entry(resolvers, &dns_resolvers, list) {
+				if (appctx->ctx.cli.p0 != NULL && appctx->ctx.cli.p0 != resolvers)
 					continue;
 
-				chunk_appendf(&trash, "Resolvers section %s\n", presolvers->id);
-				list_for_each_entry(pnameserver, &presolvers->nameserver_list, list) {
-					chunk_appendf(&trash, " nameserver %s:\n", pnameserver->id);
-					chunk_appendf(&trash, "  sent: %ld\n", pnameserver->counters.sent);
-					chunk_appendf(&trash, "  valid: %ld\n", pnameserver->counters.valid);
-					chunk_appendf(&trash, "  update: %ld\n", pnameserver->counters.update);
-					chunk_appendf(&trash, "  cname: %ld\n", pnameserver->counters.cname);
-					chunk_appendf(&trash, "  cname_error: %ld\n", pnameserver->counters.cname_error);
-					chunk_appendf(&trash, "  any_err: %ld\n", pnameserver->counters.any_err);
-					chunk_appendf(&trash, "  nx: %ld\n", pnameserver->counters.nx);
-					chunk_appendf(&trash, "  timeout: %ld\n", pnameserver->counters.timeout);
-					chunk_appendf(&trash, "  refused: %ld\n", pnameserver->counters.refused);
-					chunk_appendf(&trash, "  other: %ld\n", pnameserver->counters.other);
-					chunk_appendf(&trash, "  invalid: %ld\n", pnameserver->counters.invalid);
-					chunk_appendf(&trash, "  too_big: %ld\n", pnameserver->counters.too_big);
-					chunk_appendf(&trash, "  truncated: %ld\n", pnameserver->counters.truncated);
-					chunk_appendf(&trash, "  outdated: %ld\n", pnameserver->counters.outdated);
+				chunk_appendf(&trash, "Resolvers section %s\n", resolvers->id);
+				list_for_each_entry(ns, &resolvers->nameservers, list) {
+					chunk_appendf(&trash, " nameserver %s:\n", ns->id);
+					chunk_appendf(&trash, "  sent:        %lld\n", ns->counters.sent);
+					chunk_appendf(&trash, "  snd_error:   %lld\n", ns->counters.snd_error);
+					chunk_appendf(&trash, "  valid:       %lld\n", ns->counters.valid);
+					chunk_appendf(&trash, "  update:      %lld\n", ns->counters.update);
+					chunk_appendf(&trash, "  cname:       %lld\n", ns->counters.cname);
+					chunk_appendf(&trash, "  cname_error: %lld\n", ns->counters.cname_error);
+					chunk_appendf(&trash, "  any_err:     %lld\n", ns->counters.any_err);
+					chunk_appendf(&trash, "  nx:          %lld\n", ns->counters.nx);
+					chunk_appendf(&trash, "  timeout:     %lld\n", ns->counters.timeout);
+					chunk_appendf(&trash, "  refused:     %lld\n", ns->counters.refused);
+					chunk_appendf(&trash, "  other:       %lld\n", ns->counters.other);
+					chunk_appendf(&trash, "  invalid:     %lld\n", ns->counters.invalid);
+					chunk_appendf(&trash, "  too_big:     %lld\n", ns->counters.too_big);
+					chunk_appendf(&trash, "  truncated:   %lld\n", ns->counters.truncated);
+					chunk_appendf(&trash, "  outdated:    %lld\n",  ns->counters.outdated);
 				}
+				chunk_appendf(&trash, "\n");
 			}
 		}
 
@@ -2687,6 +2012,14 @@
 __attribute__((constructor))
 static void __dns_init(void)
 {
+	dns_answer_item_pool = create_pool("dns_answer_item", sizeof(struct dns_answer_item), MEM_F_SHARED);
+	dns_resolution_pool  = create_pool("dns_resolution",  sizeof(struct dns_resolution),  MEM_F_SHARED);
+
+	/* give a first random value to our dns query_id seed */
+	dns_query_id_seed = random();
+
+	hap_register_post_check(dns_finalize_config);
+	hap_register_post_deinit(dns_deinit);
+
 	cli_register_kw(&cli_kws);
 }
-
diff --git a/src/haproxy.c b/src/haproxy.c
index a5e4a38..5506ed5 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1766,10 +1766,6 @@
 	if (!hlua_post_init())
 		exit(1);
 
-	/* initialize structures for name resolution */
-	if (!dns_init_resolvers(0))
-		exit(1);
-
 	free(err_msg);
 }
 
@@ -2025,6 +2021,7 @@
 			free(s->agent.bi);
 			free(s->agent.bo);
 			free(s->agent.send_string);
+			free(s->hostname_dn);
 			free((char*)s->conf.file);
 
 			if (s->use_ssl || s->check.use_ssl) {
@@ -2698,10 +2695,6 @@
 		fork_poller();
 	}
 
-	/* initialize structures for name resolution */
-	if (!dns_init_resolvers(1))
-		exit(1);
-
 	if (global.mode & MODE_MWORKER)
 		mworker_pipe_register(mworker_pipe);
 
diff --git a/src/proxy.c b/src/proxy.c
index 52a17c2..bcd5868 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -754,7 +754,6 @@
 	LIST_INIT(&p->conf.args.list);
 	LIST_INIT(&p->tcpcheck_rules);
 	LIST_INIT(&p->filter_configs);
-	LIST_INIT(&p->srvrq_list);
 
 	/* Timeouts are defined as -1 */
 	proxy_reset_timeouts(p);
diff --git a/src/server.c b/src/server.c
index 844d696..01a7d9b 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1375,71 +1375,37 @@
  */
 static int srv_prepare_for_resolution(struct server *srv, const char *hostname)
 {
+	char *hostname_dn;
+	int   hostname_len, hostname_dn_len;
+
 	if (!hostname)
 		return 0;
 
-	free(srv->hostname);
-	srv->hostname = strdup(hostname);
+	hostname_len    = strlen(hostname);
+	hostname_dn     = trash.str;
+	hostname_dn_len = dns_str_to_dn_label(hostname, hostname_len + 1,
+					      hostname_dn, trash.size);
+	if (hostname_dn_len == -1)
+		goto err;
 
-	srv->hostname_dn_len = dns_str_to_dn_label_len(hostname);
-	srv->hostname_dn = calloc(srv->hostname_dn_len + 1, sizeof(char));
 
+	free(srv->hostname);
+	free(srv->hostname_dn);
+	srv->hostname        = strdup(hostname);
+	srv->hostname_dn     = strdup(hostname_dn);
+	srv->hostname_dn_len = hostname_dn_len;
 	if (!srv->hostname || !srv->hostname_dn)
 		goto err;
 
-	if (!dns_str_to_dn_label(srv->hostname,
-				 srv->hostname_dn,
-				 srv->hostname_dn_len + 1))
-		goto err;
-
 	return 0;
 
  err:
-	free(srv->hostname);
-	srv->hostname = NULL;
-	free(srv->hostname_dn);
-	srv->hostname_dn = NULL;
+	free(srv->hostname);    srv->hostname    = NULL;
+	free(srv->hostname_dn); srv->hostname_dn = NULL;
 	return -1;
 }
 
 /*
- * Free the link between a server and its resolution.
- * It also performs the following tasks:
- *   - check if resolution can be moved back in the resolvers' pool
- *     (and do it)
- *   - move resolution's hostname_dn and hostname_dn_len to the next requester
- *     available (when applied)
- */
-void srv_free_from_resolution(struct server *srv)
-{
-	struct dns_requester *requester;
-	int count;
-
-	/* check if we can move the resolution back to the pool.
-	 * if <count> is greater than 1, then we can't */
-	count = 0;
-	list_for_each_entry(requester, &srv->resolution->requester.wait, list) {
-		++count;
-		if (count > 1)
-			break;
-	}
-	list_for_each_entry(requester, &srv->resolution->requester.curr, list) {
-		++count;
-		if (count > 1)
-			break;
-	}
-	if (count <= 1) {
-		/* move the resolution back to the pool */
-		dns_resolution_free(srv->resolvers, srv->resolution);
-		return;
-	}
-
-	dns_rm_requester_from_resolution(srv->dns_requester, srv->resolution);
-
-	return;
-}
-
-/*
  * Copy <src> server settings to <srv> server allocating
  * everything needed.
  * This function is not supposed to be called at any time, but only
@@ -2006,61 +1972,20 @@
 
 			/* save hostname and create associated name resolution */
 			if (fqdn) {
-				if (fqdn[0] == '_') {
-					struct dns_srvrq *srvrq = NULL;
-					int found = 0;
-					/* SRV record */
+				if (fqdn[0] == '_') { /* SRV record */
 					/* Check if a SRV request already exists, and if not, create it */
-					list_for_each_entry(srvrq, &curproxy->srvrq_list, list) {
-						if (!strcmp(srvrq->name, fqdn)) {
-							found = 1;
-							break;
-						}
-					}
-					if (found == 0) {
-						int hostname_dn_len;
-
-						srvrq = calloc(1, sizeof(*srvrq));
-						if (!srvrq) {
-							Alert("Failed to allocate memory");
-							err_code = ERR_ALERT | ERR_FATAL;
-							goto out;
-						}
-						srvrq->obj_type = OBJ_TYPE_SRVRQ;
-						srvrq->proxy = proxy;
-						srvrq->name = strdup(fqdn);
-						srvrq->inter = 2000;
-						hostname_dn_len = dns_str_to_dn_label_len(fqdn);
-						if (hostname_dn_len == -1) {
-							Alert("Failed to parse domaine name '%s'", fqdn);
-							err_code = ERR_ALERT | ERR_FATAL;
-							goto out;
-						}
-						srvrq->hostname_dn = malloc(hostname_dn_len + 1);
-						srvrq->hostname_dn_len = hostname_dn_len;
-						if (!srvrq->hostname_dn) {
-							Alert("Failed to alloc memory");
-							err_code = ERR_ALERT | ERR_FATAL;
-							goto out;
-						}
-						if (!dns_str_to_dn_label(fqdn,
-						    srvrq->hostname_dn,
-						    hostname_dn_len + 1)) {
-							Alert("Failed to parse domain name '%s'", fqdn);
-							err_code = ERR_ALERT | ERR_FATAL;
-							goto out;
-						}
-						LIST_ADDQ(&proxy->srvrq_list, &srvrq->list);
-
-					}
-					newsrv->srvrq = srvrq;
-
-
-				} else if (srv_prepare_for_resolution(newsrv, fqdn) == -1) {
-						Alert("parsing [%s:%d] : Can't create DNS resolution for server '%s'\n",
-						    file, linenum, newsrv->id);
+					if ((newsrv->srvrq = find_srvrq_by_name(fqdn, curproxy)) == NULL)
+						newsrv->srvrq = new_dns_srvrq(newsrv, fqdn);
+					if (newsrv->srvrq == NULL) {
 						err_code |= ERR_ALERT | ERR_FATAL;
 						goto out;
+					}
+				}
+				else if (srv_prepare_for_resolution(newsrv, fqdn) == -1) {
+					Alert("parsing [%s:%d] : Can't create DNS resolution for server '%s'\n",
+					      file, linenum, newsrv->id);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
 				}
 			}
 
@@ -2299,8 +2224,6 @@
 					goto out;
 				}
 				newsrv->check.inter = val;
-				if (newsrv->srvrq)
-					newsrv->srvrq->inter = val;
 				cur_arg += 2;
 			}
 			else if (!strcmp(args[cur_arg], "fastinter")) {
@@ -3531,8 +3454,9 @@
  */
 int snr_update_srv_status(struct server *s, int has_no_ip)
 {
-	struct dns_resolution *resolution = s->resolution;
-	struct dns_resolvers *resolvers = s->resolvers;
+	struct dns_resolvers  *resolvers  = s->resolvers;
+	struct dns_resolution *resolution = s->dns_requester->resolution;
+	int exp;
 
 	switch (resolution->status) {
 		case RSLV_STATUS_NONE:
@@ -3550,8 +3474,9 @@
 					return 1;
 				srv_set_admin_flag(s, SRV_ADMF_RMAINT,
 				    "No IP for server ");
-				return (0);
+				return 0;
 			}
+
 			if (!(s->next_admin & SRV_ADMF_RMAINT))
 				return 1;
 			srv_clr_admin_flag(s, SRV_ADMF_RMAINT);
@@ -3564,43 +3489,47 @@
 
 		case RSLV_STATUS_NX:
 			/* stop server if resolution is NX for a long enough period */
-			if (tick_is_expired(tick_add(resolution->last_status_change, resolvers->hold.nx), now_ms)) {
-				if (s->next_admin & SRV_ADMF_RMAINT)
-					return 1;
-				srv_set_admin_flag(s, SRV_ADMF_RMAINT, "DNS NX status");
-				return 0;
-			}
-			break;
+			exp = tick_add(resolution->last_valid, resolvers->hold.nx);
+			if (!tick_is_expired(exp, now_ms))
+				break;
+
+			if (s->next_admin & SRV_ADMF_RMAINT)
+				return 1;
+			srv_set_admin_flag(s, SRV_ADMF_RMAINT, "DNS NX status");
+			return 0;
 
 		case RSLV_STATUS_TIMEOUT:
 			/* stop server if resolution is TIMEOUT for a long enough period */
-			if (tick_is_expired(tick_add(resolution->last_status_change, resolvers->hold.timeout), now_ms)) {
-				if (s->next_admin & SRV_ADMF_RMAINT)
-					return 1;
-				srv_set_admin_flag(s, SRV_ADMF_RMAINT, "DNS timeout status");
-				return 0;
-			}
-			break;
+			exp = tick_add(resolution->last_valid, resolvers->hold.timeout);
+			if (!tick_is_expired(exp, now_ms))
+				break;
+
+			if (s->next_admin & SRV_ADMF_RMAINT)
+				return 1;
+			srv_set_admin_flag(s, SRV_ADMF_RMAINT, "DNS timeout status");
+			return 0;
 
 		case RSLV_STATUS_REFUSED:
 			/* stop server if resolution is REFUSED for a long enough period */
-			if (tick_is_expired(tick_add(resolution->last_status_change, resolvers->hold.refused), now_ms)) {
-				if (s->next_admin & SRV_ADMF_RMAINT)
-					return 1;
-				srv_set_admin_flag(s, SRV_ADMF_RMAINT, "DNS refused status");
-				return 0;
-			}
-			break;
+			exp = tick_add(resolution->last_valid, resolvers->hold.refused);
+			if (!tick_is_expired(exp, now_ms))
+				break;
+
+			if (s->next_admin & SRV_ADMF_RMAINT)
+				return 1;
+			srv_set_admin_flag(s, SRV_ADMF_RMAINT, "DNS refused status");
+			return 0;
 
 		default:
-			/* stop server if resolution is in unmatched error for a long enough period */
-			if (tick_is_expired(tick_add(resolution->last_status_change, resolvers->hold.other), now_ms)) {
-				if (s->next_admin & SRV_ADMF_RMAINT)
-					return 1;
-				srv_set_admin_flag(s, SRV_ADMF_RMAINT, "unspecified DNS error");
-				return 0;
-			}
-			break;
+			/* stop server if resolution failed for a long enough period */
+			exp = tick_add(resolution->last_valid, resolvers->hold.other);
+			if (!tick_is_expired(exp, now_ms))
+				break;
+
+			if (s->next_admin & SRV_ADMF_RMAINT)
+				return 1;
+			srv_set_admin_flag(s, SRV_ADMF_RMAINT, "unspecified DNS error");
+			return 0;
 	}
 
 	return 1;
@@ -3629,11 +3558,11 @@
 	struct chunk *chk = get_trash_chunk();
 	int has_no_ip = 0;
 
-	s = objt_server(requester->requester);
+	s = objt_server(requester->owner);
 	if (!s)
 		return 1;
 
-	resolution = s->resolution;
+	resolution = s->dns_requester->resolution;
 
 	/* initializing variables */
 	firstip = NULL;		/* pointer to the first valid response found */
@@ -3678,14 +3607,8 @@
 			goto update_status;
 
 		case DNS_UPD_NAME_ERROR:
-			/* if this is not the last expected response, we ignore it */
-			if (nameserver && (resolution->nb_responses < nameserver->resolvers->count_nameservers))
-				return 0;
 			/* update resolution status to OTHER error type */
-			if (resolution->status != RSLV_STATUS_OTHER) {
-				resolution->status = RSLV_STATUS_OTHER;
-				resolution->last_status_change = now_ms;
-			}
+			resolution->status = RSLV_STATUS_OTHER;
 			goto update_status;
 
 		default:
@@ -3694,12 +3617,11 @@
 	}
 
  save_ip:
-	if (nameserver)
-		nameserver->counters.update += 1;
-
-	/* save the first ip we found */
-	if (nameserver)
+	if (nameserver) {
+		nameserver->counters.update++;
+		/* save the first ip we found */
 		chunk_printf(chk, "%s/%s", nameserver->resolvers->id, nameserver->id);
+	}
 	else
 		chunk_printf(chk, "DNS cache");
 	update_server_addr(s, firstip, firstip_sin_family, (char *)chk->str);
@@ -3710,11 +3632,9 @@
 
  invalid:
 	if (nameserver) {
-		nameserver->counters.invalid += 1;
-		if (resolution->nb_responses >= nameserver->resolvers->count_nameservers)
-			goto update_status;
+		nameserver->counters.invalid++;
+		goto update_status;
 	}
-
 	snr_update_srv_status(s, has_no_ip);
 	return 0;
 }
@@ -3727,22 +3647,11 @@
  */
 int snr_resolution_error_cb(struct dns_requester *requester, int error_code)
 {
-	struct server *s = NULL;
-	struct dns_resolution *resolution = NULL;
-	struct dns_resolvers *resolvers = NULL;
+	struct server *s;
 
-	s = objt_server(requester->requester);
+	s = objt_server(requester->owner);
 	if (!s)
 		return 1;
-
-	resolution = s->resolution;
-	resolvers = s->resolvers;
-
-	/* can be ignored if this is not the last response */
-	if ((error_code != DNS_RESP_TIMEOUT) && (resolution->nb_responses < resolvers->count_nameservers)) {
-		return 1;
-	}
-
 	snr_update_srv_status(s, 0);
 	return 1;
 }
@@ -3816,8 +3725,8 @@
 int srv_set_fqdn(struct server *srv, const char *hostname)
 {
 	struct dns_resolution *resolution;
-	int hostname_dn_len;
-	int did_set_reso = 0;
+	char                  *hostname_dn;
+	int                    hostname_len, hostname_dn_len;
 
 	/* run time DNS resolution was not active for this server
 	 * and we can't enable it at run time for now.
@@ -3826,60 +3735,31 @@
 		return -1;
 
 	chunk_reset(&trash);
-
-	/* check if hostname is really a hostname and if we have enough
-	 * room to save it in its domain name format
-	 */
-	hostname_dn_len = dns_str_to_dn_label_len(hostname);
-	if (hostname_dn_len == -1 || hostname_dn_len + 1 > trash.size)
-		return -1;
-
-	if (!dns_str_to_dn_label(hostname,
-				 trash.str,
-				 hostname_dn_len + 1))
+	hostname_len    = strlen(hostname);
+	hostname_dn     = trash.str;
+	hostname_dn_len = dns_str_to_dn_label(hostname, hostname_len + 1,
+					      hostname_dn, trash.size);
+	if (hostname_dn_len == -1)
 		return -1;
 
-
-	if (srv->resolution->hostname_dn) {
-		/* get a resolution from the curr or wait queues, or a brand new one from the pool */
-		resolution = dns_resolution_list_get(srv->resolvers, trash.str, srv->dns_requester->prefered_query_type);
-		if (!resolution)
-			return -1;
-
-		/* in this case, the new hostanme is the same than the old one */
-		if (srv->resolution == resolution && srv->hostname)
-			return 0;
+	resolution = srv->dns_requester->resolution;
+	if (resolution &&
+	    resolution->hostname_dn &&
+	    !strcmp(resolution->hostname_dn, hostname_dn))
+		return 0;
 
-		/* first, we need to unlink our server from its current resolution */
-		srv_free_from_resolution(srv);
-	} else {
-		/* this server's fqdn has been set by a SRV record */
-		resolution = dns_resolution_list_get(srv->resolvers, trash.str, srv->dns_requester->prefered_query_type);
-		srv_free_from_resolution(srv);
-		srv->resolution = resolution;
-		if (resolution->hostname_dn == NULL) {
-			resolution->last_resolution = now_ms;
-			did_set_reso = 1;
-		}
-	}
+	dns_unlink_resolution(srv->dns_requester);
 
-	/* now we update server's parameters */
 	free(srv->hostname);
 	free(srv->hostname_dn);
-	srv->hostname = strdup(hostname);
-	srv->hostname_dn = strdup(trash.str);
+	srv->hostname        = strdup(hostname);
+	srv->hostname_dn     = strdup(hostname_dn);
 	srv->hostname_dn_len = hostname_dn_len;
 	if (!srv->hostname || !srv->hostname_dn)
 		return -1;
-	if (did_set_reso) {
-		resolution->query_type = srv->dns_requester->prefered_query_type;
-		resolution->hostname_dn = srv->hostname_dn;
-		resolution->hostname_dn_len = hostname_dn_len;
-	}
-
-	/* then we can link srv to its new resolution */
-	dns_link_resolution(srv, OBJ_TYPE_SERVER, resolution);
 
+	if (dns_link_resolution(srv, OBJ_TYPE_SERVER) == -1)
+		return -1;
 	return 0;
 }