MINOR: dns: Cache previous DNS answers.

As DNS servers may not return all IPs in one answer, we want to cache the
previous entries. Those entries are removed when considered obsolete, which
happens when the IP hasn't been returned by the DNS server for a time
defined in the "hold obsolete" parameter of the resolver section. The default
is 30s.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index bfeb3ce..f467438 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -11693,6 +11693,10 @@
  - first response is truncated and second one is a NX Domain, then HAProxy
    stops resolution.
 
+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.
+
 
 resolvers <resolvers id>
   Creates a new name server list labelled <resolvers id>
@@ -11709,7 +11713,7 @@
   Defines <period> during which the last name resolution should be kept based
   on last resolution <status>
     <status> : last name resolution status. Acceptable values are "nx",
-               "other", "refused", "timeout", "valid".
+               "other", "refused", "timeout", "valid", "obsolete".
     <period> : interval between two successive name resolution when the last
                answer was in <status>. It follows the HAProxy time format.
                <period> is in milliseconds by default.
@@ -11756,6 +11760,7 @@
      hold nx              30s
      hold timeout         30s
      hold valid           10s
+     hold obsolete        30s
 
 
 6. HTTP header manipulation
diff --git a/include/proto/server.h b/include/proto/server.h
index 43e4e42..c4f8e1d 100644
--- a/include/proto/server.h
+++ b/include/proto/server.h
@@ -52,7 +52,7 @@
 struct server *cli_find_server(struct appctx *appctx, char *arg);
 
 /* functions related to server name resolution */
-int snr_update_srv_status(struct server *s);
+int snr_update_srv_status(struct server *s, int has_no_ip);
 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);
diff --git a/include/types/dns.h b/include/types/dns.h
index 7a19aa3..12c1155 100644
--- a/include/types/dns.h
+++ b/include/types/dns.h
@@ -113,7 +113,7 @@
 /* NOTE: big endian structure */
 struct dns_answer_item {
 	struct list list;
-	char *name;				/* answer name
+	char name[DNS_MAX_NAME_SIZE];		/* answer name
 						 * For SRV type, name also includes service
 						 * and protocol value */
 	int16_t type;				/* question type */
@@ -124,7 +124,8 @@
 	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;				/* Response data: SRV or CNAME type target */
+	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 dns_response_packet {
@@ -158,6 +159,7 @@
 		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 */
@@ -252,8 +254,6 @@
 	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_answer_item response_answer_records[DNS_MAX_ANSWER_RECORDS];	/* <response> answer records */
-	struct chunk response_buffer;	/* buffer used as a data store for <response> above TODO: optimize the size (might be smaller) */
 };
 
 /*
@@ -315,6 +315,7 @@
 	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 */
 };
 
 #endif /* _TYPES_DNS_H */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 8b6aec6..e29ae53 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2167,6 +2167,7 @@
 		curr_resolvers->hold.other = 30000;
 		curr_resolvers->hold.refused = 30000;
 		curr_resolvers->hold.timeout = 30000;
+		curr_resolvers->hold.obsolete = 30000;
 		/* default hold period for valid is 10s */
 		curr_resolvers->hold.valid = 10000;
 		curr_resolvers->timeout.retry = 1000;
@@ -2280,8 +2281,10 @@
 			curr_resolvers->hold.timeout = time;
 		else if (strcmp(args[1], "valid") == 0)
 			curr_resolvers->hold.valid = time;
+		else if (strcmp(args[1], "obsolete") == 0)
+			curr_resolvers->hold.obsolete = time;
 		else {
-			Alert("parsing [%s:%d] : '%s' unknown <event>: '%s', expects either 'nx', 'timeout', 'valid', or 'other'.\n",
+			Alert("parsing [%s:%d] : '%s' unknown <event>: '%s', expects either 'nx', 'timeout', 'valid', 'obsolete' or 'other'.\n",
 				file, linenum, args[0], args[1]);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
diff --git a/src/dns.c b/src/dns.c
index 221f870..0ce63c9 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -51,6 +51,8 @@
 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,
@@ -169,6 +171,7 @@
 			}
 
 			requester->requester_cb(requester, NULL);
+			resolvers = NULL;
 		}
 		else {
 			LIST_DEL(&requester->list);
@@ -306,6 +309,12 @@
 	resolution->qid.key = 0;
 }
 
+static inline void free_dns_answer_item(struct dns_answer_item *item)
+{
+	pool_free2(dns_answer_item_pool, item);
+}
+
+
 /*
  * function called when a network IO is generated on a name server socket for an incoming packet
  * It performs the following actions:
@@ -327,6 +336,7 @@
 	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;
 
@@ -468,6 +478,15 @@
 				break;
 		}
 
+		/* Check for any obsolete record */
+		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);
+				free_dns_answer_item(item1);
+			}
+		}
+
 		/* some error codes trigger a re-send of the query, but switching the
 		 * query type.
 		 * This is the case for the following error codes:
@@ -885,13 +904,13 @@
 {
 	unsigned char *reader;
 	char *previous_dname, tmpname[DNS_MAX_NAME_SIZE];
-	int len, flags, offset, ret;
-	int dns_query_record_id, dns_answer_record_id;
+	int len, flags, offset;
+	int dns_query_record_id;
 	int nb_saved_records;
 	struct dns_query_item *dns_query;
-	struct dns_answer_item *dns_answer_record;
+	struct dns_answer_item *dns_answer_record, *tmp_record;
 	struct dns_response_packet *dns_p;
-	struct chunk *dns_response_buffer;
+	int found = 0;
 
 	reader = resp;
 	len = 0;
@@ -899,9 +918,6 @@
 
 	/* initialization of response buffer and structure */
 	dns_p = &resolution->response;
-	dns_response_buffer = &resolution->response_buffer;
-	memset(dns_p, '\0', sizeof(struct dns_response_packet));
-	chunk_reset(dns_response_buffer);
 
 	/* query id */
 	if (reader + 2 >= bufend)
@@ -1011,30 +1027,29 @@
 	}
 
 	/* now parsing response records */
-	LIST_INIT(&dns_p->answer_list);
 	nb_saved_records = 0;
-	for (dns_answer_record_id = 0; dns_answer_record_id < dns_p->header.ancount; dns_answer_record_id++) {
+	for (int i = 0; i < dns_p->header.ancount; i++) {
 		if (reader >= bufend)
 			return DNS_RESP_INVALID;
 
-		/* pull next response record from the list, if still one available, then add it
-		 * to the record list */
-		if (dns_answer_record_id > DNS_MAX_ANSWER_RECORDS)
-			return DNS_RESP_INVALID;
-		dns_answer_record = &resolution->response_answer_records[dns_answer_record_id];
-		LIST_ADDQ(&dns_p->answer_list, &dns_answer_record->list);
+		dns_answer_record = pool_alloc2(dns_answer_item_pool);
+		if (dns_answer_record == NULL)
+			return (DNS_RESP_INVALID);
 
 		offset = 0;
 		len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
 
-		if (len == 0)
+		if (len == 0) {
+			free_dns_answer_item(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
 		 */
 		if (memcmp(previous_dname, tmpname, len) != 0) {
-			if (dns_answer_record_id == 0) {
+			free_dns_answer_item(dns_answer_record);
+			if (i == 0) {
 				/* first record, means a mismatch issue between queried dname
 				 * and dname found in the first record */
 				return DNS_RESP_INVALID;
@@ -1046,43 +1061,50 @@
 
 		}
 
-		dns_answer_record->name = chunk_newstr(dns_response_buffer);
-		if (dns_answer_record->name == NULL)
-			return DNS_RESP_INVALID;
-
-		ret = chunk_strncat(dns_response_buffer, tmpname, len);
-		if (ret == 0)
-			return DNS_RESP_INVALID;
+		memcpy(dns_answer_record->name, tmpname, len);
+		dns_answer_record->name[len] = 0;
 
 		reader += offset;
-		if (reader >= bufend)
+		if (reader >= bufend) {
+			free_dns_answer_item(dns_answer_record);
 			return DNS_RESP_INVALID;
+		}
 
-		if (reader >= bufend)
+		if (reader >= bufend) {
+			free_dns_answer_item(dns_answer_record);
 			return DNS_RESP_INVALID;
+		}
 
 		/* 2 bytes for record type (A, AAAA, CNAME, etc...) */
-		if (reader + 2 > bufend)
+		if (reader + 2 > bufend) {
+			free_dns_answer_item(dns_answer_record);
 			return DNS_RESP_INVALID;
+		}
 		dns_answer_record->type = reader[0] * 256 + reader[1];
 		reader += 2;
 
 		/* 2 bytes for class (2) */
-		if (reader + 2 > bufend)
+		if (reader + 2 > bufend) {
+			free_dns_answer_item(dns_answer_record);
 			return DNS_RESP_INVALID;
+		}
 		dns_answer_record->class = reader[0] * 256 + reader[1];
 		reader += 2;
 
 		/* 4 bytes for ttl (4) */
-		if (reader + 4 > bufend)
+		if (reader + 4 > bufend) {
+			free_dns_answer_item(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 */
-		if (reader + 2 > bufend)
+		if (reader + 2 > bufend) {
+			free_dns_answer_item(dns_answer_record);
 			return DNS_RESP_INVALID;
+		}
 		dns_answer_record->data_len = reader[0] * 256 + reader[1];
 
 		/* move forward 2 bytes for data len */
@@ -1092,8 +1114,10 @@
 		switch (dns_answer_record->type) {
 			case DNS_RTYPE_A:
 				/* ipv4 is stored on 4 bytes */
-				if (dns_answer_record->data_len != 4)
+				if (dns_answer_record->data_len != 4) {
+					free_dns_answer_item(dns_answer_record);
 					return DNS_RESP_INVALID;
+				}
 				dns_answer_record->address.sa_family = AF_INET;
 				memcpy(&(((struct sockaddr_in *)&dns_answer_record->address)->sin_addr),
 						reader, dns_answer_record->data_len);
@@ -1107,22 +1131,21 @@
 				 * + 1 because dns_answer_record_id starts at 0 while number of answers
 				 * is an integer and starts at 1.
 				 */
-				if (dns_answer_record_id + 1 == dns_p->header.ancount)
+				if (i + 1 == dns_p->header.ancount) {
+					free_dns_answer_item(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)
-					return DNS_RESP_INVALID;
-
-				dns_answer_record->target = chunk_newstr(dns_response_buffer);
-				if (dns_answer_record->target == NULL)
+				if (len == 0) {
+					free_dns_answer_item(dns_answer_record);
 					return DNS_RESP_INVALID;
+				}
 
-				ret = chunk_strncat(dns_response_buffer, tmpname, len);
-				if (ret == 0)
-					return DNS_RESP_INVALID;
+				memcpy(dns_answer_record->target, tmpname, len);
+				dns_answer_record->target[len] = 0;
 
 				previous_dname = dns_answer_record->target;
 
@@ -1130,8 +1153,10 @@
 
 			case DNS_RTYPE_AAAA:
 				/* ipv6 is stored on 16 bytes */
-				if (dns_answer_record->data_len != 16)
+				if (dns_answer_record->data_len != 16) {
+					free_dns_answer_item(dns_answer_record);
 					return DNS_RESP_INVALID;
+				}
 				dns_answer_record->address.sa_family = AF_INET6;
 				memcpy(&(((struct sockaddr_in6 *)&dns_answer_record->address)->sin6_addr),
 						reader, dns_answer_record->data_len);
@@ -1144,12 +1169,39 @@
 
 		/* move forward dns_answer_record->data_len for analyzing next record in the response */
 		reader += dns_answer_record->data_len;
+
+		/* Lookup to see if we already had this entry */
+
+		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;
+			default:
+				break;
+			}
+			if (found == 1)
+				break;
+		}
+		if (found == 1) {
+			tmp_record->last_seen = now.tv_sec;
+			free_dns_answer_item(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 */
 
-	/* let's add a last \0 to close our last string */
-	ret = chunk_strncat(dns_response_buffer, "\0", 1);
-	if (ret == 0)
-		return DNS_RESP_INVALID;
 
 	/* save the number of records we really own */
 	dns_p->header.ancount = nb_saved_records;
@@ -1175,51 +1227,17 @@
 {
 	struct dns_answer_item *record;
 	int family_priority;
-	int i, currentip_found;
+	int currentip_found;
 	unsigned char *newip4, *newip6;
-	struct {
-		void *ip;
-		unsigned char type;
-	} rec[DNS_MAX_IP_REC];
 	int currentip_sel;
 	int j;
-	int rec_nb = 0;
 	int score, max_score;
 
 	family_priority = dns_opts->family_prio;
 	*newip = newip4 = newip6 = NULL;
 	currentip_found = 0;
 	*newip_sin_family = AF_UNSPEC;
-
-	/* now parsing response records */
-	list_for_each_entry(record, &dns_p->answer_list, list) {
-		/* analyzing record content */
-		switch (record->type) {
-			case DNS_RTYPE_A:
-				/* Store IPv4, only if some room is avalaible. */
-				if (rec_nb < DNS_MAX_IP_REC) {
-					rec[rec_nb].ip = &(((struct sockaddr_in *)&record->address)->sin_addr);
-					rec[rec_nb].type = AF_INET;
-					rec_nb++;
-				}
-				break;
-
-			/* we're looking for IPs only. CNAME validation is done when
-			 * parsing the response buffer for the first time */
-			case DNS_RTYPE_CNAME:
-				break;
-
-			case DNS_RTYPE_AAAA:
-				/* Store IPv6, only if some room is avalaible. */
-				if (rec_nb < DNS_MAX_IP_REC) {
-					rec[rec_nb].ip = &(((struct sockaddr_in6 *)&record->address)->sin6_addr);
-					rec[rec_nb].type = AF_INET6;
-					rec_nb++;
-				}
-				break;
-
-		} /* switch (record type) */
-	} /* list for each record entries */
+	max_score = -1;
 
 	/* Select an IP regarding configuration preference.
 	 * Top priority is the prefered network ip version,
@@ -1234,29 +1252,38 @@
 	 *  1 - current ip.
 	 * The result with the biggest score is returned.
 	 */
-	max_score = -1;
-	for (i = 0; i < rec_nb; i++) {
-		int record_ip_already_affected = 0;
+
+	list_for_each_entry(record, &dns_p->answer_list, list) {
+		void *ip;
+		unsigned char ip_type;
 
+		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) {
+			ip_type = AF_INET6;
+			ip = &(((struct sockaddr_in6 *)&record->address)->sin6_addr);
+		} else
+			continue;
 		score = 0;
 
 		/* Check for prefered ip protocol. */
-		if (rec[i].type == family_priority)
+		if (ip_type == family_priority)
 			score += 8;
 
 		/* Check for prefered network. */
 		for (j = 0; j < dns_opts->pref_net_nb; j++) {
 
 			/* Compare only the same adresses class. */
-			if (dns_opts->pref_net[j].family != rec[i].type)
+			if (dns_opts->pref_net[j].family != ip_type)
 				continue;
 
-			if ((rec[i].type == AF_INET &&
-			     in_net_ipv4(rec[i].ip,
+			if ((ip_type == AF_INET &&
+			     in_net_ipv4(ip,
 			                 &dns_opts->pref_net[j].mask.in4,
 			                 &dns_opts->pref_net[j].addr.in4)) ||
-			    (rec[i].type == AF_INET6 &&
-			     in_net_ipv6(rec[i].ip,
+			    (ip_type == AF_INET6 &&
+			     in_net_ipv6(ip,
 			                 &dns_opts->pref_net[j].mask.in6,
 			                 &dns_opts->pref_net[j].addr.in6))) {
 				score += 4;
@@ -1268,18 +1295,17 @@
 		 * If yes, the score should be incremented by 2.
 		 */
 		if (owner) {
-			if (snr_check_ip_callback(owner, rec[i].ip, &rec[i].type))
-				record_ip_already_affected = 1;
+			if (snr_check_ip_callback(owner, ip, &ip_type))
+			{
+				continue;
+			}
 		}
-		if (record_ip_already_affected == 0)
-			score += 2;
-
 		/* Check for current ip matching. */
-		if (rec[i].type == currentip_sin_family &&
+		if (ip_type == currentip_sin_family &&
 		    ((currentip_sin_family == AF_INET &&
-		      memcmp(rec[i].ip, currentip, 4) == 0) ||
+		      memcmp(ip, currentip, 4) == 0) ||
 		     (currentip_sin_family == AF_INET6 &&
-		      memcmp(rec[i].ip, currentip, 16) == 0))) {
+		      memcmp(ip, currentip, 16) == 0))) {
 			score += 1;
 			currentip_sel = 1;
 		} else
@@ -1292,21 +1318,22 @@
 		 * the ip selected is the current ip.
 		 */
 		if (score > max_score) {
-			if (rec[i].type == AF_INET)
-				newip4 = rec[i].ip;
+			if (ip_type == AF_INET)
+				newip4 = ip;
 			else
-				newip6 = rec[i].ip;
+				newip6 = ip;
 			currentip_found = currentip_sel;
 			if (score == 15)
 				return DNS_UPD_NO;
 			max_score = score;
 		}
-	}
+
+
+	} /* list for each record entries */
 
 	/* no IP found in the response */
-	if (!newip4 && !newip6) {
+	if (!newip4 && !newip6)
 		return DNS_UPD_NO_IP_FOUND;
-	}
 
 	/* case when the caller looks first for an IPv4 address */
 	if (family_priority == AF_INET) {
@@ -1410,6 +1437,14 @@
 	/* 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 */
@@ -1442,6 +1477,7 @@
 
 			/* 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);
@@ -2081,6 +2117,7 @@
 		tmpresolution->status = RSLV_STATUS_NONE;
 		tmpresolution->step = RSLV_STEP_NONE;
 		tmpresolution->revision = 1;
+		LIST_INIT(&tmpresolution->response.answer_list);
 	}
 
 	/* add the requester to the resolution's wait queue */
@@ -2170,7 +2207,6 @@
 		return NULL;
 	}
 
-	chunk_init(&resolution->response_buffer, buffer, global.tune.bufsize);
 	LIST_INIT(&resolution->requester.wait);
 	LIST_INIT(&resolution->requester.curr);
 
@@ -2180,7 +2216,6 @@
 /* This function free the memory allocated to a DNS resolution */
 void dns_free_resolution(struct dns_resolution *resolution)
 {
-	chunk_destroy(&resolution->response_buffer);
 	free(resolution);
 
 	return;
diff --git a/src/server.c b/src/server.c
index f457d55..ef62a63 100644
--- a/src/server.c
+++ b/src/server.c
@@ -3756,7 +3756,7 @@
  *  0 if server status is updated
  *  1 if server status has not changed
  */
-int snr_update_srv_status(struct server *s)
+int snr_update_srv_status(struct server *s, int has_no_ip)
 {
 	struct dns_resolution *resolution = s->resolution;
 	struct dns_resolvers *resolvers = s->resolvers;
@@ -3772,6 +3772,13 @@
 			 * resume health checks
 			 * server will be turned back on if health check is safe
 			 */
+			if (has_no_ip) {
+				if (s->admin & SRV_ADMF_RMAINT)
+					return 1;
+				srv_set_admin_flag(s, SRV_ADMF_RMAINT,
+				    "No IP for server ");
+				return (0);
+			}
 			if (!(s->admin & SRV_ADMF_RMAINT))
 				return 1;
 			srv_clr_admin_flag(s, SRV_ADMF_RMAINT);
@@ -3847,6 +3854,7 @@
 	short server_sin_family, firstip_sin_family;
 	int ret;
 	struct chunk *chk = get_trash_chunk();
+	int has_no_ip = 0;
 
 	s = objt_server(requester->requester);
 	if (!s)
@@ -3893,10 +3901,7 @@
 			goto invalid;
 
 		case DNS_UPD_NO_IP_FOUND:
-			if (resolution->status != RSLV_STATUS_OTHER) {
-				resolution->status = RSLV_STATUS_OTHER;
-				resolution->last_status_change = now_ms;
-			}
+			has_no_ip = 1;
 			goto update_status;
 
 		case DNS_UPD_NAME_ERROR:
@@ -3927,7 +3932,7 @@
 	update_server_addr(s, firstip, firstip_sin_family, (char *)chk->str);
 
  update_status:
-	snr_update_srv_status(s);
+	snr_update_srv_status(s, has_no_ip);
 	return 1;
 
  invalid:
@@ -3936,7 +3941,7 @@
 	if (resolution->nb_responses >= nameserver->resolvers->count_nameservers)
 		goto update_status;
 
-	snr_update_srv_status(s);
+	snr_update_srv_status(s, has_no_ip);
 	return 0;
 }
 
@@ -3964,16 +3969,15 @@
 		return 1;
 	}
 
-	snr_update_srv_status(s);
+	snr_update_srv_status(s, 0);
 	return 1;
 }
 
 /*
  * Function to check if <ip> is already affected to a server in the backend
- * which owns <srv>.
+ * which owns <srv> and is up.
  * It returns a pointer to the first server found or NULL if <ip> is not yet
  * assigned.
- * NOTE: <ip> and <ip_family> are provided by a 'struct rec' available in dns.c.
  */
 struct server *snr_check_ip_callback(struct server *srv, void *ip, unsigned char *ip_family)
 {
@@ -3998,6 +4002,10 @@
 		    (srv->puid == tmpsrv->puid))
 			continue;
 
+		/* If the server has been taken down, don't consider it */
+		if (tmpsrv->admin & SRV_ADMF_RMAINT)
+			continue;
+
 		/* At this point, we have 2 different servers using the same DNS hostname
 		 * for their respective resolution.
 		 */