MINOR: dns: Handle SRV records.

Make it so for each server, instead of specifying a hostname, one can use
a SRV label.
When doing so, haproxy will first resolve the SRV label, then use the
resulting hostnames, as well as port and weight (priority is ignored right
now), to each server using the SRV label.
It is resolved periodically, and any server disappearing from the SRV records
will be removed, and any server appearing will be added, assuming there're
free servers in haproxy.
diff --git a/include/proto/dns.h b/include/proto/dns.h
index 6675d50..a84f07c 100644
--- a/include/proto/dns.h
+++ b/include/proto/dns.h
@@ -27,6 +27,7 @@
 
 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);
 int dns_hostname_validation(const char *string, char **err);
 int dns_build_query(int query_id, int query_type, char *hostname_dn, int hostname_dn_len, char *buf, int bufsize);
 struct task *dns_process_resolve(struct task *t);
diff --git a/include/proto/server.h b/include/proto/server.h
index c4f8e1d..d35a9c1 100644
--- a/include/proto/server.h
+++ b/include/proto/server.h
@@ -53,6 +53,7 @@
 
 /* functions related to server name resolution */
 int snr_update_srv_status(struct server *s, int has_no_ip);
+const char *update_server_fqdn(struct server *server, const char *fqdn, const char *updater);
 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 12c1155..c371d5f 100644
--- a/include/types/dns.h
+++ b/include/types/dns.h
@@ -63,6 +63,7 @@
 #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_ANY		255	/* all records */
 
 /* dns rcode values */
@@ -318,4 +319,19 @@
 	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 */
+};
+
 #endif /* _TYPES_DNS_H */
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 5306a3b..a4f3b9e 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -438,6 +438,7 @@
 						 * 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 724d496..77263db 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -298,6 +298,7 @@
 		int nb_low;
 		int nb_high;
 	} tmpl_info;
+	struct dns_srvrq *srvrq;		/* Pointer representing the DNS SRV requeest, if any */
 };
 
 /* Descriptor for a "server" keyword. The ->parse() function returns 0 in case of
diff --git a/src/cfgparse.c b/src/cfgparse.c
index e29ae53..437d9ef 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -8549,7 +8549,20 @@
 					newsrv->id, newsrv->resolvers_id);
 					cfgerr++;
 				} else {
-					if (newsrv->hostname_dn) {
+					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",
@@ -8575,6 +8588,13 @@
 		next_srv:
 			newsrv = newsrv->next;
 		}
+		{
+			struct dns_srvrq *srvrq;
+
+			list_for_each_entry(srvrq, &curproxy->srvrq_list, list) {
+					dns_link_resolution(srvrq, OBJ_TYPE_SRVRQ, NULL);
+			}
+		}
 
 		/*
 		 * Try to generate dynamic cookies for servers now.
diff --git a/src/dns.c b/src/dns.c
index 0ce63c9..00f7b10 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -21,6 +21,7 @@
 
 #include <common/time.h>
 #include <common/ticks.h>
+#include <common/net_helper.h>
 
 #include <import/lru.h>
 #include <import/xxhash.h>
@@ -153,6 +154,10 @@
 				inter = objt_server(requester->requester)->check.inter;
 				resolvers = objt_server(requester->requester)->resolvers;
 				break;
+			case OBJ_TYPE_SRVRQ:
+				inter = objt_dns_srvrq(requester->requester)->inter;
+				resolvers = objt_dns_srvrq(requester->requester)->resolvers;
+				break;
 			case OBJ_TYPE_NONE:
 			default:
 				return -1;
@@ -212,12 +217,26 @@
 			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;
 	}
 
 	/*
+	 * 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.
 	 */
@@ -352,6 +371,7 @@
 
 	/* process all pending input messages */
 	while (1) {
+		int removed_reso = 0;
 		/* read message received */
 		memset(buf, '\0', DNS_MAX_UDP_MESSAGE + 1);
 		if ((buflen = recv(fd, (char*)buf , DNS_MAX_UDP_MESSAGE, 0)) < 0) {
@@ -478,14 +498,104 @@
 				break;
 		}
 
-		/* Check for any obsolete record */
+		/* 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;
+
+					requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, 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;
+								dns_resolution_free(srv->resolvers, srv->resolution);
+								srv->resolution = dns_resolution_list_get(srv->resolvers, NULL, srv->dns_requester->prefered_query_type);
+								if (resolution == srv->resolution)
+									removed_reso = 1;
+							}
+						}
+					}
+				}
 				free_dns_answer_item(item1);
+				continue;
+			}
+			if (item1->type == DNS_RTYPE_SRV) {
+				struct server *srv;
+				struct dns_srvrq *srvrq;
+
+				if (LIST_ISEMPTY(&resolution->requester.curr))
+					continue;
+
+				requester = LIST_NEXT(&resolution->requester.curr, struct dns_requester *, 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];
+
+						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);
+						update_server_fqdn(srv, hostname, "SRV record");
+						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.
@@ -576,6 +686,8 @@
 		 * 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 */
@@ -706,6 +818,9 @@
 		case OBJ_TYPE_SERVER:
 			resolvers = objt_server(requester->requester)->resolvers;
 			break;
+		case OBJ_TYPE_SRVRQ:
+			resolvers = objt_dns_srvrq(requester->requester)->resolvers;
+			break;
 		case OBJ_TYPE_NONE:
 		default:
 			return 0;
@@ -775,6 +890,9 @@
 				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;
@@ -790,6 +908,9 @@
 					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:
 						;;
@@ -911,6 +1032,7 @@
 	struct dns_answer_item *dns_answer_record, *tmp_record;
 	struct dns_response_packet *dns_p;
 	int found = 0;
+	int i;
 
 	reader = resp;
 	len = 0;
@@ -1028,7 +1150,7 @@
 
 	/* now parsing response records */
 	nb_saved_records = 0;
-	for (int i = 0; i < dns_p->header.ancount; i++) {
+	for (i = 0; i < dns_p->header.ancount; i++) {
 		if (reader >= bufend)
 			return DNS_RESP_INVALID;
 
@@ -1151,6 +1273,36 @@
 
 				break;
 
+
+			case DNS_RTYPE_SRV:
+				/*
+				 * 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);
+					return DNS_RESP_INVALID;
+				}
+				dns_answer_record->priority = readn16(reader);
+				reader += sizeof(uint16_t);
+				dns_answer_record->weight = readn16(reader);
+				reader += sizeof(uint16_t);
+				dns_answer_record->port = readn16(reader);
+				reader += sizeof(uint16_t);
+				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);
+					return DNS_RESP_INVALID;
+				}
+				reader++;
+				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) {
@@ -1172,6 +1324,7 @@
 
 		/* 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;
@@ -1186,6 +1339,15 @@
 				    &((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) &&
+				    dns_answer_record->port == tmp_record->port) {
+					tmp_record->weight = dns_answer_record->weight;
+                                        found = 1;
+				}
+                                break;
 			default:
 				break;
 			}
@@ -1635,6 +1797,28 @@
 	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--;
+		}
+	}
+	str[dn_len - 1] = 0;
+
+}
+
 /*
  * turn a string into domain name label:
  * www.haproxy.org into 3www7haproxy3org
@@ -1827,8 +2011,23 @@
 		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;
+
+				/* let's change the query type if needed */
+				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;
+				}
+
 				break;
 
+			case OBJ_TYPE_SRVRQ:
+				break;
 			case OBJ_TYPE_NONE:
 			default:
 				/* clean up resolution information and remove from the list */
@@ -1842,19 +2041,6 @@
 				goto out;
 		}
 
-		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;
-
-		/* let's change the query type if needed */
-		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;
-		}
-
 		/* resend the DNS query */
 		dns_send_query(resolution);
 
@@ -1966,6 +2152,9 @@
 		case OBJ_TYPE_SERVER:
 			resolvers = objt_server(requester->requester)->resolvers;
 			break;
+		case OBJ_TYPE_SRVRQ:
+			resolvers = objt_dns_srvrq(requester->requester)->resolvers;
+			break;
 		case OBJ_TYPE_NONE:
 		default:
 			return NULL;
@@ -2028,6 +2217,7 @@
 	char *hostname_dn = NULL;
 	int new_resolution;
 
+
 	if (!resolution) {
 		tmprequester = calloc(1, sizeof(*tmprequester));
 		if (!tmprequester)
@@ -2047,6 +2237,11 @@
 				}
 
 				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);
@@ -2068,6 +2263,10 @@
 				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;
@@ -2102,6 +2301,23 @@
 				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;
+			}
+
+			/* 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;
+			}
+			break;
+
 		case OBJ_TYPE_NONE:
 		default:
 			free(tmprequester);
@@ -2142,42 +2358,45 @@
 	struct dns_resolution *resolution, *tmpresolution;
 	struct dns_requester *requester;
 
-	/* search for same hostname and query type in resolution.curr */
-	list_for_each_entry_safe(resolution, tmpresolution, &resolvers->resolution.curr, list) {
-		requester = NULL;
+	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;
 
-		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 (!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)
-			continue;
+			if (!requester)
+				continue;
 
-		if ((query_type == requester->prefered_query_type) &&
-		    (strcmp(hostname_dn, resolution->hostname_dn) == 0)) {
-			return resolution;
+			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;
+		/* 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 (!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)
-			continue;
+			if (!requester)
+				continue;
 
-		if ((query_type == requester->prefered_query_type) &&
-		    (strcmp(hostname_dn, resolution->hostname_dn) == 0)) {
-			return resolution;
+			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)) {
@@ -2261,6 +2480,9 @@
 		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;
@@ -2290,6 +2512,11 @@
 			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:
 			;;
@@ -2302,6 +2529,9 @@
 		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:
 			;;
diff --git a/src/proxy.c b/src/proxy.c
index 641d4fa..bd2031e 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -754,6 +754,7 @@
 	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 ef62a63..cfbdb3d 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1820,6 +1820,8 @@
 #ifdef TCP_USER_TIMEOUT
 	srv->tcp_ut = src->tcp_ut;
 #endif
+	if (srv_tmpl)
+		srv->srvrq = src->srvrq;
 }
 
 static struct server *new_server(struct proxy *proxy)
@@ -2285,18 +2287,68 @@
 
 			/* save hostname and create associated name resolution */
 			if (fqdn) {
-				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;
+				if (fqdn[0] == '_') {
+					struct dns_srvrq *srvrq = NULL;
+					int found = 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);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
 				}
 			}
 
 			newsrv->addr = *sk;
 			newsrv->svc_port = port;
 
-			if (!newsrv->hostname && !protocol_by_family(newsrv->addr.ss_family)) {
+			if (!newsrv->srvrq && !newsrv->hostname && !protocol_by_family(newsrv->addr.ss_family)) {
 				Alert("parsing [%s:%d] : Unknown protocol family %d '%s'\n",
 				      file, linenum, newsrv->addr.ss_family, args[cur_arg]);
 				err_code |= ERR_ALERT | ERR_FATAL;
@@ -2528,6 +2580,8 @@
 					goto out;
 				}
 				newsrv->check.inter = val;
+				if (newsrv->srvrq)
+					newsrv->srvrq->inter = val;
 				cur_arg += 2;
 			}
 			else if (!strcmp(args[cur_arg], "fastinter")) {
@@ -4043,6 +4097,7 @@
 {
 	struct dns_resolution *resolution;
 	int hostname_dn_len;
+	int did_set_reso = 0;
 
 	/* run time DNS resolution was not active for this server
 	 * and we can't enable it at run time for now.
@@ -4065,17 +4120,23 @@
 		return -1;
 
 
-	/* 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;
+	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)
-		return 0;
+		/* in this case, the new hostanme is the same than the old one */
+		if (srv->resolution == resolution && srv->hostname)
+			return 0;
 
-	/* first, we need to unlink our server from its current resolution */
-	srv_free_from_resolution(srv);
+		/* first, we need to unlink our server from its current resolution */
+		srv_free_from_resolution(srv);
+	} else {
+		resolution = srv->resolution;
+		resolution->last_resolution = now_ms;
+		did_set_reso = 1;
+	}
 
 	/* now we update server's parameters */
 	free(srv->hostname);
@@ -4085,6 +4146,11 @@
 	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);
@@ -4223,7 +4289,7 @@
 	msg = get_trash_chunk();
 	chunk_reset(msg);
 
-	if (!strcmp(fqdn, server->hostname)) {
+	if (server->hostname && !strcmp(fqdn, server->hostname)) {
 		chunk_appendf(msg, "no need to change the FDQN");
 		goto out;
 	}