MINOR: dns: enabled edns0 extension and make accpeted payload size tunable
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.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 731b285..8aafbe3 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -11710,6 +11710,15 @@
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
diff --git a/include/proto/dns.h b/include/proto/dns.h
index a84f07c..5aed46e 100644
--- a/include/proto/dns.h
+++ b/include/proto/dns.h
@@ -29,7 +29,7 @@
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);
diff --git a/include/types/dns.h b/include/types/dns.h
index 9bf3c7e..0f9c1b9 100644
--- a/include/types/dns.h
+++ b/include/types/dns.h
@@ -113,6 +113,20 @@
};
/* 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;
char name[DNS_MAX_NAME_SIZE]; /* answer name
@@ -150,6 +164,7 @@
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: */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index a8e54aa..eebd72c 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2162,6 +2162,8 @@
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 @@
}
}
+ 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",
diff --git a/src/dns.c b/src/dns.c
index 09ba8f6..d46160f 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -897,8 +897,8 @@
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 @@
* 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 @@
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 @@
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;
}