]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: dns: Handle SRV records.
authorOlivier Houchard <ohouchard@haproxy.com>
Fri, 4 Aug 2017 16:35:36 +0000 (18:35 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 9 Aug 2017 14:32:49 +0000 (16:32 +0200)
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
include/proto/server.h
include/types/dns.h
include/types/proxy.h
include/types/server.h
src/cfgparse.c
src/dns.c
src/proxy.c
src/server.c

index 6675d50fe75f149a717ebd3999ab1a56198a16e2..a84f07c4a39b956dd591c40b50015cd1ab8466a8 100644 (file)
@@ -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);
index c4f8e1d5096af61aad38f0ede6633b8c238eb42a..d35a9c1c8a184812c50826b69bf495c93f74e4aa 100644 (file)
@@ -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);
index 12c11552f209f68d45225cf55363a29c78af0d80..c371d5f5112fd7160de7c4955e01252da99120f6 100644 (file)
@@ -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 */
index 5306a3b6c5552f5c97a8a914b82369d85f2b8cfa..a4f3b9e5f94f9bad4a3a0966256c3b411295cab1 100644 (file)
@@ -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 {
index 724d49651713426c1071510b75a9c4adef02869f..77263dbbee5d547dabeb8f1ef4d28b43d64a4541 100644 (file)
@@ -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
index e29ae53daa56ea2094d585c7616866e57598349b..437d9ef280eadcc2ab460d1517563d6079c86678 100644 (file)
@@ -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.
index 0ce63c910728ee8cbc74ecb01a147090e36dd8d1..00f7b10ca5f6d26f7c4d6420d57b85f7f96003bd 100644 (file)
--- 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,11 +217,25 @@ 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)
@@ -2046,6 +2236,11 @@ int dns_link_resolution(void *requester, int requester_type, struct dns_resoluti
                                                tmprequester->prefered_query_type = DNS_RTYPE_AAAA;
                                }
 
+                               break;
+                       case OBJ_TYPE_SRVRQ:
+                               tmprequester->requester = &((struct dns_srvrq *)requester)->obj_type;
+                               hostname_dn = objt_dns_srvrq(requester)->hostname_dn;
+                               resolvers = objt_dns_srvrq(requester)->resolvers;
                                break;
                        case OBJ_TYPE_NONE:
                        default:
@@ -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:
                        ;;
index 641d4fa196d9fd075a628f17679f9ae3a6398fd1..bd2031ee912f9e1d6bb945ae03dfd823ecb4d424 100644 (file)
@@ -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);
index ef62a63fcf911c8c2ec031709627d4a2a9cba265..cfbdb3d715142ca73c57a1e19acdf0055c7b9009 100644 (file)
@@ -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;
        }