MEDIUM: thread/dns: Make DNS thread-safe
diff --git a/include/common/hathreads.h b/include/common/hathreads.h
index 39d8220..242bece 100644
--- a/include/common/hathreads.h
+++ b/include/common/hathreads.h
@@ -168,6 +168,7 @@
 	LUA_LOCK,
 	NOTIF_LOCK,
 	SPOE_APPLET_LOCK,
+	DNS_LOCK,
 	LOCK_LABELS
 };
 struct lock_stat {
@@ -256,7 +257,7 @@
 					   "UPDATED_SERVERS", "LBPRM", "SIGNALS", "STK_TABLE", "STK_SESS",
 					   "APPLETS", "PEER", "BUF_WQ", "STREAMS", "SSL", "SSL_GEN_CERTS",
 					   "PATREF", "PATEXP", "PATLRU", "VARS", "COMP_POOL", "LUA",
-					   "NOTIF", "SPOE_APPLET" };
+					   "NOTIF", "SPOE_APPLET", "DNS" };
 	int lbl;
 
 	for (lbl = 0; lbl < LOCK_LABELS; lbl++) {
diff --git a/include/types/dns.h b/include/types/dns.h
index 1c68551..bdc8cfc 100644
--- a/include/types/dns.h
+++ b/include/types/dns.h
@@ -25,6 +25,7 @@
 #include <eb32tree.h>
 
 #include <common/mini-clist.h>
+#include <common/hathreads.h>
 
 #include <types/connection.h>
 #include <types/obj_type.h>
@@ -192,6 +193,9 @@
 	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) */
+#ifdef USE_THREAD
+	HA_SPINLOCK_T lock;
+#endif
 	struct list list;                   /* resolvers list */
 };
 
diff --git a/src/cfgparse.c b/src/cfgparse.c
index ca2d5d7..d693bfb 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2182,6 +2182,7 @@
 		LIST_INIT(&curr_resolvers->nameservers);
 		LIST_INIT(&curr_resolvers->resolutions.curr);
 		LIST_INIT(&curr_resolvers->resolutions.wait);
+		SPIN_INIT(&curr_resolvers->lock);
 	}
 	else if (strcmp(args[0], "nameserver") == 0) { /* nameserver definition */
 		struct sockaddr_storage *sk;
diff --git a/src/dns.c b/src/dns.c
index 3383faa..1fdc461 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -45,7 +45,7 @@
 struct list dns_resolvers  = LIST_HEAD_INIT(dns_resolvers);
 struct list dns_srvrq_list = LIST_HEAD_INIT(dns_srvrq_list);
 
-static int64_t dns_query_id_seed = 0;	/* random seed */
+static THREAD_LOCAL 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;
@@ -125,6 +125,8 @@
 /* 2 bytes random generator to generate DNS query ID */
 static inline uint16_t dns_rnd16(void)
 {
+	if (!dns_query_id_seed)
+		dns_query_id_seed = now_ms;
 	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;
@@ -1444,6 +1446,7 @@
 		return;
 
 	resolvers = ns->resolvers;
+	SPIN_LOCK(DNS_LOCK, &resolvers->lock);
 
 	/* process all pending input messages */
 	while (1) {
@@ -1604,6 +1607,7 @@
 		continue;
 	}
 	dns_update_resolvers_timeout(resolvers);
+	SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
 }
 
 /* Called when a resolvers network socket is ready to send data */
@@ -1628,6 +1632,8 @@
 		return;
 
 	resolvers = ns->resolvers;
+	SPIN_LOCK(DNS_LOCK, &resolvers->lock);
+
 	list_for_each_entry(res, &resolvers->resolutions.curr, list) {
 		int ret;
 
@@ -1653,6 +1659,7 @@
 		ns->counters.snd_error++;
 		res->nb_queries++;
 	}
+	SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
 }
 
 /* Processes DNS resolution. First, it checks the active list to detect expired
@@ -1665,6 +1672,8 @@
 	struct dns_resolution *res, *resback;
 	int exp;
 
+	SPIN_LOCK(DNS_LOCK, &resolvers->lock);
+
 	/* 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
@@ -1733,6 +1742,7 @@
 	}
 
 	dns_update_resolvers_timeout(resolvers);
+	SPIN_UNLOCK(DNS_LOCK, &resolvers->lock);
 	return t;
 }
 
@@ -2015,9 +2025,6 @@
 	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);
 
diff --git a/src/server.c b/src/server.c
index 648f3dd..f0b912f 100644
--- a/src/server.c
+++ b/src/server.c
@@ -3751,11 +3751,12 @@
 	char                  *hostname_dn;
 	int                    hostname_len, hostname_dn_len;
 
+	SPIN_LOCK(DNS_LOCK, &srv->resolvers->lock);
 	/* run time DNS resolution was not active for this server
 	 * and we can't enable it at run time for now.
 	 */
 	if (!srv->dns_requester)
-		return -1;
+		goto err;
 
 	chunk_reset(&trash);
 	hostname_len    = strlen(hostname);
@@ -3763,13 +3764,13 @@
 	hostname_dn_len = dns_str_to_dn_label(hostname, hostname_len + 1,
 					      hostname_dn, trash.size);
 	if (hostname_dn_len == -1)
-		return -1;
+		goto err;
 
 	resolution = srv->dns_requester->resolution;
 	if (resolution &&
 	    resolution->hostname_dn &&
 	    !strcmp(resolution->hostname_dn, hostname_dn))
-		return 0;
+		goto end;
 
 	dns_unlink_resolution(srv->dns_requester);
 
@@ -3779,11 +3780,18 @@
 	srv->hostname_dn     = strdup(hostname_dn);
 	srv->hostname_dn_len = hostname_dn_len;
 	if (!srv->hostname || !srv->hostname_dn)
-		return -1;
+		goto err;
 
 	if (dns_link_resolution(srv, OBJ_TYPE_SERVER) == -1)
-		return -1;
+		goto err;
+
+  end:
+	SPIN_UNLOCK(DNS_LOCK, &srv->resolvers->lock);
 	return 0;
+
+  err:
+	SPIN_UNLOCK(DNS_LOCK, &srv->resolvers->lock);
+	return -1;
 }
 
 /* Sets the server's address (srv->addr) from srv->lastaddr which was filled