Hi,

After some review and tests by Baptiste, here comes an updated patchset,
with a few bugfixes.
This one is probably mergeable.

Regards,

Olivier
>From 1b408464590fea38d8a45b2b7fed5c615465a858 Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouch...@haproxy.com>
Date: Thu, 6 Jul 2017 18:46:47 +0200
Subject: [PATCH 1/6] 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.
---
 doc/configuration.txt  |   7 +-
 include/proto/server.h |   2 +-
 include/types/dns.h    |   9 +-
 src/cfgparse.c         |   5 +-
 src/dns.c              | 247 ++++++++++++++++++++++++++++---------------------
 src/server.c           |  28 ++++--
 6 files changed, 175 insertions(+), 123 deletions(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index bfeb3ce0..f4674387 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -11693,6 +11693,10 @@ For example, with 2 name servers configured in a 
resolvers section:
  - 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 @@ hold <status> <period>
   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 @@ timeout <event> <time>
      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 43e4e425..c4f8e1d5 100644
--- a/include/proto/server.h
+++ b/include/proto/server.h
@@ -52,7 +52,7 @@ int srv_init_addr(void);
 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 7a19aa37..12c11552 100644
--- a/include/types/dns.h
+++ b/include/types/dns.h
@@ -113,7 +113,7 @@ struct dns_query_item {
 /* 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 @@ struct dns_answer_item {
        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 @@ struct dns_resolvers {
                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 @@ struct dns_resolution {
        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 @@ enum {
        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 8b6aec6a..e29ae53d 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2167,6 +2167,7 @@ int cfg_parse_resolvers(const char *file, int linenum, 
char **args, int kwm)
                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 @@ int cfg_parse_resolvers(const char *file, int linenum, 
char **args, int kwm)
                        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 221f8709..0ce63c91 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -51,6 +51,8 @@ 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,
@@ -169,6 +171,7 @@ int dns_trigger_resolution(struct dns_resolution 
*resolution)
                        }
 
                        requester->requester_cb(requester, NULL);
+                       resolvers = NULL;
                }
                else {
                        LIST_DEL(&requester->list);
@@ -306,6 +309,12 @@ void dns_reset_resolution(struct dns_resolution 
*resolution)
        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 @@ void dns_resolve_recv(struct dgram_conn *dgram)
        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 @@ void dns_resolve_recv(struct dgram_conn *dgram)
                                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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
 {
        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 @@ int dns_validate_dns_response(unsigned char *resp, unsigned 
char *bufend, struct
 
        /* 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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
        }
 
        /* 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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
 
                }
 
-               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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
                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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
                                 * + 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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
 
                        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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
 
                /* 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 @@ int dns_get_ip_from_response(struct dns_response_packet 
*dns_p,
 {
        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 @@ int dns_get_ip_from_response(struct dns_response_packet 
*dns_p,
         *  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 @@ int dns_get_ip_from_response(struct dns_response_packet 
*dns_p,
                 * 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 @@ int dns_get_ip_from_response(struct dns_response_packet 
*dns_p,
                 * 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 @@ int dns_init_resolvers(int close_socket)
        /* 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 @@ int dns_init_resolvers(int close_socket)
 
                        /* 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 @@ int dns_link_resolution(void *requester, int 
requester_type, struct dns_resoluti
                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 @@ struct dns_resolution *dns_alloc_resolution(void)
                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 @@ struct dns_resolution *dns_alloc_resolution(void)
 /* 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 f457d555..ef62a63f 100644
--- a/src/server.c
+++ b/src/server.c
@@ -3756,7 +3756,7 @@ out:
  *  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 @@ int snr_update_srv_status(struct server *s)
                         * 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 @@ int snr_resolution_cb(struct dns_requester *requester, 
struct dns_nameserver *na
        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 @@ int snr_resolution_cb(struct dns_requester *requester, 
struct dns_nameserver *na
                        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 @@ int snr_resolution_cb(struct dns_requester *requester, 
struct dns_nameserver *na
        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 @@ int snr_resolution_cb(struct dns_requester *requester, 
struct dns_nameserver *na
        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 @@ int snr_resolution_error_cb(struct dns_requester 
*requester, int error_code)
                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 @@ struct server *snr_check_ip_callback(struct server *srv, 
void *ip, unsigned char
                    (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.
                 */
-- 
2.13.3

>From 477e84d747896aaeeacd40525af5458630bb52dc Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouch...@haproxy.com>
Date: Fri, 4 Aug 2017 18:31:56 +0200
Subject: [PATCH 2/6] MINOR: obj: Add a new type of object, OBJ_TYPE_SRVRQ.

dns_srvrq will be objects used for dealing with SRV records.
---
 include/proto/obj_type.h | 12 ++++++++++++
 include/types/obj_type.h |  1 +
 2 files changed, 13 insertions(+)

diff --git a/include/proto/obj_type.h b/include/proto/obj_type.h
index b64244fc..60265b5e 100644
--- a/include/proto/obj_type.h
+++ b/include/proto/obj_type.h
@@ -131,6 +131,18 @@ static inline struct connection *objt_conn(enum obj_type 
*t)
        return __objt_conn(t);
 }
 
+static inline struct dns_srvrq *__objt_dns_srvrq(enum obj_type *t)
+{
+       return container_of(t, struct dns_srvrq, obj_type);
+}
+
+static inline struct dns_srvrq *objt_dns_srvrq(enum obj_type *t)
+{
+       if (!t || *t != OBJ_TYPE_SRVRQ)
+               return NULL;
+       return __objt_dns_srvrq(t);
+}
+
 static inline void *obj_base_ptr(enum obj_type *t)
 {
        switch (obj_type(t)) {
diff --git a/include/types/obj_type.h b/include/types/obj_type.h
index 83a3e782..a6310cfc 100644
--- a/include/types/obj_type.h
+++ b/include/types/obj_type.h
@@ -39,6 +39,7 @@ enum obj_type {
        OBJ_TYPE_APPLET,       /* object is a struct applet */
        OBJ_TYPE_APPCTX,       /* object is a struct appctx */
        OBJ_TYPE_CONN,         /* object is a struct connection */
+       OBJ_TYPE_SRVRQ,        /* object is a struct dns_srvrq */
        OBJ_TYPE_ENTRIES       /* last one : number of entries */
 } __attribute__((packed)) ;
 
-- 
2.13.3

>From bb29aa5f46162a797d85a438d1d5e43f6eabc2cf Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouch...@haproxy.com>
Date: Mon, 7 Aug 2017 19:20:04 +0200
Subject: [PATCH 3/6] [MINOR]: Add a few functions to do unaligned access.

Add a few functions to read 16bits and 32bits integers that may be
unaligned, both in host and network order.
---
 include/common/net_helper.h | 75 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)
 create mode 100644 include/common/net_helper.h

diff --git a/include/common/net_helper.h b/include/common/net_helper.h
new file mode 100644
index 00000000..86809a68
--- /dev/null
+++ b/include/common/net_helper.h
@@ -0,0 +1,75 @@
+/*
+ * include/common/net_helper.h
+ * This file contains miscellaneous network helper functions.
+ *
+ * Copyright (C) 2017 Olivier Houchard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
+ * SOFTWARE.
+ */
+
+#ifndef _COMMON_NET_HELPER_H
+#define _COMMON_NET_HELPER_H
+
+#include <arpa/inet.h>
+
+/* Functions to read various integer that may be unaligned */
+
+/* Read a uint16_t */
+uint16_t readu16(const void *p)
+{
+        const union {  uint16_t u16; } __attribute__((packed))*u = p;
+        return u->u16;
+}
+
+/* Read a int16_t */
+int16_t readi16(const void *p)
+{
+        const union {  int16_t i16; } __attribute__((packed))*u = p;
+        return u->i16;
+}
+
+/* Read a uint16_t, and convert from network order to host order */
+uint16_t readn16(const void *p)
+{
+        const union {  uint16_t u16; } __attribute__((packed))*u = p;
+        return ntohs(u->u16);
+}
+
+/* Read a uint32_t */
+uint32_t readu32(const void *p)
+{
+        const union {  uint32_t u32; } __attribute__((packed))*u = p;
+        return u->u32;
+}
+
+/* Read a int32_t */
+int16_t readi32(const void *p)
+{
+        const union {  int32_t i32; } __attribute__((packed))*u = p;
+        return u->i32;
+}
+
+/* Read a uint32_t, and convert from network order to host order */
+uint32_t readn32(const void *p)
+{
+        const union {  uint32_t u32; } __attribute__((packed))*u = p;
+        return ntohl(u->u32);
+}
+
+#endif /* COMMON_NET_HELPER_H */
-- 
2.13.3

>From a3e3b12137ae8219e2e90ce3b614d36bd098cece Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouch...@haproxy.com>
Date: Fri, 4 Aug 2017 18:35:36 +0200
Subject: [PATCH 4/6] 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.
---
 include/proto/dns.h    |   1 +
 include/proto/server.h |   1 +
 include/types/dns.h    |  16 +++
 include/types/proxy.h  |   1 +
 include/types/server.h |   1 +
 src/cfgparse.c         |  22 +++-
 src/dns.c              | 312 ++++++++++++++++++++++++++++++++++++++++++-------
 src/proxy.c            |   1 +
 src/server.c           |  98 +++++++++++++---
 9 files changed, 395 insertions(+), 58 deletions(-)

diff --git a/include/proto/dns.h b/include/proto/dns.h
index 6675d50f..a84f07c4 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 c4f8e1d5..d35a9c1c 100644
--- a/include/proto/server.h
+++ b/include/proto/server.h
@@ -53,6 +53,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 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 12c11552..c371d5f5 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 @@ enum {
        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 5306a3b6..a4f3b9e5 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -438,6 +438,7 @@ struct proxy {
                                                 * 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 724d4965..77263dbb 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -298,6 +298,7 @@ struct server {
                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 e29ae53d..437d9ef2 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -8549,7 +8549,20 @@ out_uri_auth_compat:
                                        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 @@ out_uri_auth_compat:
                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 0ce63c91..00f7b10c 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 @@ int dns_trigger_resolution(struct dns_resolution 
*resolution)
                                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 @@ dns_run_resolution(struct dns_requester *requester)
                        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 @@ void dns_resolve_recv(struct dgram_conn *dgram)
 
        /* 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 @@ void dns_resolve_recv(struct dgram_conn *dgram)
                                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 @@ void dns_resolve_recv(struct dgram_conn *dgram)
                 * 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 @@ int dns_send_query(struct dns_resolution *resolution)
                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 @@ void dns_update_resolvers_timeout(struct dns_resolvers 
*resolvers)
                                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 @@ void dns_update_resolvers_timeout(struct dns_resolvers 
*resolvers)
                                        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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
        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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
 
        /* 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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
 
                                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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
 
                /* 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 @@ int dns_validate_dns_response(unsigned char *resp, 
unsigned char *bufend, struct
                                    &((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 @@ int dns_build_query(int query_id, int query_type, char 
*hostname_dn, int hostnam
        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 @@ struct task *dns_process_resolve(struct task *t)
                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 @@ struct task *dns_process_resolve(struct task *t)
                                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 @@ dns_cache_lookup(int query_type, char *hostname_dn, int 
hostname_dn_len, int val
                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 @@ int dns_link_resolution(void *requester, int 
requester_type, struct dns_resoluti
        char *hostname_dn = NULL;
        int new_resolution;
 
+
        if (!resolution) {
                tmprequester = calloc(1, sizeof(*tmprequester));
                if (!tmprequester)
@@ -2047,6 +2237,11 @@ int dns_link_resolution(void *requester, int 
requester_type, struct dns_resoluti
                                }
 
                                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 @@ int dns_link_resolution(void *requester, int 
requester_type, struct dns_resoluti
                                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 @@ int dns_link_resolution(void *requester, int 
requester_type, struct dns_resoluti
                                
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 *dns_resolution_list_get(struct 
dns_resolvers *resolvers,
        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 @@ void dns_rm_requester_from_resolution(struct 
dns_requester *requester, struct dn
                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 @@ void dns_rm_requester_from_resolution(struct 
dns_requester *requester, struct dn
                        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 @@ void dns_rm_requester_from_resolution(struct 
dns_requester *requester, struct dn
                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 641d4fa1..bd2031ee 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -754,6 +754,7 @@ void init_new_proxy(struct proxy *p)
        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 ef62a63f..cfbdb3d7 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1820,6 +1820,8 @@ static void srv_settings_cpy(struct server *srv, struct 
server *src, int srv_tmp
 #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 @@ int parse_server(const char *file, int linenum, char 
**args, struct proxy *curpr
 
                        /* 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 @@ int parse_server(const char *file, int linenum, char 
**args, struct proxy *curpr
                                        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 @@ int srv_set_fqdn(struct server *srv, const char *hostname)
 {
        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 @@ int srv_set_fqdn(struct server *srv, const char 
*hostname)
                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 @@ int srv_set_fqdn(struct server *srv, const char 
*hostname)
        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 @@ const char *update_server_fqdn(struct server *server, 
const char *fqdn, const ch
        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;
        }
-- 
2.13.3

>From f4dcf59ac931a610f08042ca3d71119efe2c4c3f Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouch...@haproxy.com>
Date: Fri, 4 Aug 2017 18:39:01 +0200
Subject: [PATCH 5/6] MINOR: check: Fix checks when using SRV records.

When started, a server may not yet have an associated protocol, so don't
bother trying to run the checks until it is there.
---
 src/checks.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/checks.c b/src/checks.c
index 7938b873..fc92a243 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -1557,7 +1557,7 @@ static int connect_conn_chk(struct task *t)
        }
 
        ret = SF_ERR_INTERNAL;
-       if (proto->connect)
+       if (proto && proto->connect)
                ret = proto->connect(conn, check->type, quickack ? 2 : 0);
        if (s->check.send_proxy && !(check->state & CHK_ST_AGENT)) {
                conn->send_proxy_ofs = 1;
-- 
2.13.3

>From 4a8db4f2d0b35ea633ded8decb7c6f147189d325 Mon Sep 17 00:00:00 2001
From: Olivier Houchard <ohouch...@haproxy.com>
Date: Mon, 7 Aug 2017 17:30:03 +0200
Subject: [PATCH 6/6] MINOR: doc: Document SRV label usage.

---
 doc/configuration.txt | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index f4674387..731b2857 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -11637,6 +11637,13 @@ A few other events can trigger a name resolution at 
run time:
     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.
+
 A few things important to notice:
   - all the name servers are queried in the mean time. HAProxy will process the
     first valid response.
-- 
2.13.3

Reply via email to