]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: dns: enabled edns0 extension and make accpeted payload size tunable
authorBaptiste Assmann <bedis9@gmail.com>
Sun, 13 Aug 2017 22:13:01 +0000 (00:13 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 18 Aug 2017 09:25:56 +0000 (11:25 +0200)
Edns extensions may be used to negotiate some settings between a DNS
client and a server.
For now we only use it to announce the maximum response payload size accpeted
by HAProxy.
This size can be set through a configuration parameter in the resolvers
section. If not set, it defaults to 512 bytes.

doc/configuration.txt
include/proto/dns.h
include/types/dns.h
src/cfgparse.c
src/dns.c

index 731b2857e30a18e920e080f83f46655ddf17ce4a..8aafbe3ce635045e64a5ac4e86e98b02095efd50 100644 (file)
@@ -11710,6 +11710,15 @@ resolvers <resolvers id>
 
 A resolvers section accept the following parameters:
 
+accepted_payload_size <nb>
+  Defines the maxium payload size accepted by HAProxy and announced to all the
+  naeservers configured in this resolvers section.
+  <nb> is in bytes. If not set, HAProxy announces 512. (minimal value defined
+       by RFC 6891)
+
+  Note: to get biggers response but still be sure that responses won't be
+        dropped on the wire, one can choose a value between 1280 and 1410.
+
 nameserver <id> <ip>:<port>
   DNS server description:
     <id>   : label of the server, should be unique
index a84f07c4a39b956dd591c40b50015cd1ab8466a8..5aed46e78c726ef31e9228aabc6a7976b42c9552 100644 (file)
@@ -29,7 +29,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);
+int dns_build_query(int query_id, int query_type, unsigned int accepted_payload_size, char *hostname_dn, int hostname_dn_len, char *buf, int bufsize);
 struct task *dns_process_resolve(struct task *t);
 int dns_init_resolvers(int close_socket);
 uint16_t dns_rnd16(void);
index 9bf3c7e917b9f5fd87d6559466b719088cf4e5f0..0f9c1b970766954c41a8c95f2e837cf93a2f2399 100644 (file)
@@ -112,6 +112,20 @@ struct dns_query_item {
        unsigned short class;                   /* query class */
 };
 
+/* NOTE: big endian structure */
+struct dns_additional_record {
+       uint8_t name;                           /* domain name, must be 0 (RFC 6891) */
+       uint16_t type;                  /* record type DNS_RTYPE_OPT (41) */
+       uint16_t udp_payload_size;      /* maximum size accepted for the response */
+       uint32_t extension;                             /* extended rcode and flags, not used for now */
+       uint16_t data_length;           /* data length */
+/* as of today, we don't support yet edns options, that said I already put a placeholder here
+ * for this purpose. We may need to define a dns_option_record structure which itself should
+ * point to different type of data, based on the extension set (client subnet, tcp keepalive,
+ * etc...)*/
+//     struct list options;                    /* list of option records */
+} __attribute__ ((packed));
+
 /* NOTE: big endian structure */
 struct dns_answer_item {
        struct list list;
@@ -150,6 +164,7 @@ struct dns_resolvers {
                int line;               /* line where the section appears */
        } conf;                         /* config information */
        struct list nameserver_list;    /* dns server list */
+       unsigned int accepted_payload_size;     /* maximum payload size we accept for responses */
        int count_nameservers;                  /* total number of nameservers in a resolvers section */
        int resolve_retries;            /* number of retries before giving up */
        struct {                        /* time to: */
index a8e54aa78c24f4b96d688d0b09155ae7b92eeb4c..eebd72c114c92fb1a2060b6c3d8034a092965f56 100644 (file)
@@ -2162,6 +2162,8 @@ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
                curr_resolvers->conf.line = linenum;
                curr_resolvers->id = strdup(args[1]);
                curr_resolvers->query_ids = EB_ROOT;
+               /* default maximum response size */
+               curr_resolvers->accepted_payload_size = 512;
                /* default hold period for nx, other, refuse and timeout is 30s */
                curr_resolvers->hold.nx = 30000;
                curr_resolvers->hold.other = 30000;
@@ -2291,6 +2293,15 @@ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
                }
 
        }
+       else if (strcmp(args[0], "accepted_payload_size") == 0) {
+               if (!*args[1]) {
+                       Alert("parsing [%s:%d] : '%s' expects <nb> as argument.\n",
+                               file, linenum, args[0]);
+                       err_code |= ERR_ALERT | ERR_FATAL;
+                       goto out;
+               }
+               curr_resolvers->accepted_payload_size = atoi(args[1]);
+       }
        else if (strcmp(args[0], "resolution_pool_size") == 0) {
                if (!*args[1]) {
                        Alert("parsing [%s:%d] : '%s' expects <nb> as argument.\n",
index 09ba8f687e0856736f00815f6b697cb864dcc19d..d46160fcf722eeffc092402162b92445530987e7 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -897,8 +897,8 @@ int dns_send_query(struct dns_resolution *resolution)
        if (!resolvers)
                return 0;
 
-       bufsize = dns_build_query(resolution->query_id, resolution->query_type, resolution->hostname_dn,
-                       resolution->hostname_dn_len, trash.str, trash.size);
+       bufsize = dns_build_query(resolution->query_id, resolution->query_type, resolvers->accepted_payload_size,
+                       resolution->hostname_dn, resolution->hostname_dn_len, trash.str, trash.size);
 
        if (bufsize == -1)
                return 0;
@@ -1820,10 +1820,11 @@ int dns_alloc_resolution_pool(struct dns_resolvers *resolvers)
  * returns:
  *  -1 if <buf> is too short
  */
-int dns_build_query(int query_id, int query_type, char *hostname_dn, int hostname_dn_len, char *buf, int bufsize)
+int dns_build_query(int query_id, int query_type, unsigned int accepted_payload_size, char *hostname_dn, int hostname_dn_len, char *buf, int bufsize)
 {
        struct dns_header *dns;
        struct dns_question qinfo;
+       struct dns_additional_record edns;
        char *ptr, *bufend;
 
        memset(buf, '\0', bufsize);
@@ -1841,7 +1842,7 @@ int dns_build_query(int query_id, int query_type, char *hostname_dn, int hostnam
        dns->qdcount = htons(1);        /* 1 question */
        dns->ancount = 0;
        dns->nscount = 0;
-       dns->arcount = 0;
+       dns->arcount = htons(1);
 
        /* move forward ptr */
        ptr += sizeof(struct dns_header);
@@ -1868,6 +1869,19 @@ int dns_build_query(int query_id, int query_type, char *hostname_dn, int hostnam
 
        ptr += sizeof(struct dns_question);
 
+       /* check if there is enough room for additional records */
+       if (ptr + sizeof(edns) >= bufend)
+               return -1;
+
+       /* set the DNS extension */
+       edns.name = 0;
+       edns.type = htons(DNS_RTYPE_OPT);
+       edns.udp_payload_size = htons(accepted_payload_size);
+       edns.extension = 0;
+       edns.data_length = 0;
+       memcpy(ptr, &edns, sizeof(edns));
+       ptr += sizeof(edns);
+
        return ptr - buf;
 }