From 9d8dbbc56b73a4473b4e312317692a3d13d1da61 Mon Sep 17 00:00:00 2001 From: Baptiste Assmann Date: Fri, 18 Aug 2017 23:35:08 +0200 Subject: [PATCH] MINOR: dns: Maximum DNS udp payload set to 8192 Following up DNS extension introduction, this patch aims at making the computation of the maximum number of records in DNS response dynamic. This computation is based on the announced payload size accepted by HAProxy. --- doc/configuration.txt | 4 +++- include/proto/dns.h | 2 +- include/types/dns.h | 2 +- src/cfgparse.c | 13 ++++++++++++- src/dns.c | 15 +++++++++------ 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 66c932597a..9f7f9ff3dd 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -11716,9 +11716,11 @@ accepted_payload_size 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 + Note: to get bigger responses but still be sure that responses won't be dropped on the wire, one can choose a value between 1280 and 1410. + Note: the maximum allowed value is 8192. + nameserver : DNS server description: : label of the server, should be unique diff --git a/include/proto/dns.h b/include/proto/dns.h index 5aed46e78c..aa063c7b26 100644 --- a/include/proto/dns.h +++ b/include/proto/dns.h @@ -33,7 +33,7 @@ int dns_build_query(int query_id, int query_type, unsigned int accepted_payload_ struct task *dns_process_resolve(struct task *t); int dns_init_resolvers(int close_socket); uint16_t dns_rnd16(void); -int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct dns_resolution *resolution); +int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct dns_resolution *resolution, int max_answer_records); int dns_get_ip_from_response(struct dns_response_packet *dns_p, struct dns_options *dns_opts, void *currentip, short currentip_sin_family, diff --git a/include/types/dns.h b/include/types/dns.h index 0f9c1b9707..c7338c7924 100644 --- a/include/types/dns.h +++ b/include/types/dns.h @@ -30,7 +30,7 @@ */ #define DNS_MAX_LABEL_SIZE 63 #define DNS_MAX_NAME_SIZE 255 -#define DNS_MAX_UDP_MESSAGE 512 +#define DNS_MAX_UDP_MESSAGE 8192 /* DNS minimun record size: 1 char + 1 NULL + type + class */ #define DNS_MIN_RECORD_SIZE ( 1 + 1 + 2 + 2 ) diff --git a/src/cfgparse.c b/src/cfgparse.c index ee249794f9..e69a4ab78b 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -2294,13 +2294,24 @@ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm) } else if (strcmp(args[0], "accepted_payload_size") == 0) { + int i = 0; + if (!*args[1]) { Alert("parsing [%s:%d] : '%s' expects as argument.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } - curr_resolvers->accepted_payload_size = atoi(args[1]); + + i = atoi(args[1]); + if (i > DNS_MAX_UDP_MESSAGE) { + Alert("parsing [%s:%d] : '%s' size %d exceeds maximum allowed size %d.\n", + file, linenum, args[0], i, DNS_MAX_UDP_MESSAGE); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + curr_resolvers->accepted_payload_size = i; } else if (strcmp(args[0], "resolution_pool_size") == 0) { if (!*args[1]) { diff --git a/src/dns.c b/src/dns.c index c91d4237be..62291fe1fc 100644 --- a/src/dns.c +++ b/src/dns.c @@ -391,6 +391,7 @@ void dns_resolve_recv(struct dgram_conn *dgram) unsigned char buf[DNS_MAX_UDP_MESSAGE + 1]; unsigned char *bufend; int fd, buflen, dns_resp, need_resend = 0; + int max_answer_records = 0; unsigned short query_id; struct eb32_node *eb; struct lru64 *lru = NULL; @@ -413,15 +414,15 @@ void dns_resolve_recv(struct dgram_conn *dgram) 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) { + memset(buf, '\0', resolvers->accepted_payload_size + 1); + if ((buflen = recv(fd, (char*)buf , resolvers->accepted_payload_size + 1, 0)) < 0) { /* FIXME : for now we consider EAGAIN only */ fd_cant_recv(fd); break; } /* message too big */ - if (buflen > DNS_MAX_UDP_MESSAGE) { + if (buflen > resolvers->accepted_payload_size) { nameserver->counters.too_big += 1; continue; } @@ -455,7 +456,9 @@ void dns_resolve_recv(struct dgram_conn *dgram) /* number of responses received */ resolution->nb_responses += 1; - dns_resp = dns_validate_dns_response(buf, bufend, resolution); + + max_answer_records = (resolvers->accepted_payload_size - DNS_HEADER_SIZE) / DNS_MIN_RECORD_SIZE; + dns_resp = dns_validate_dns_response(buf, bufend, resolution, max_answer_records); switch (dns_resp) { case DNS_RESP_VALID: @@ -1086,7 +1089,7 @@ int dns_read_name(unsigned char *buffer, unsigned char *bufend, unsigned char *n * This function returns one of the DNS_RESP_* code to indicate the type of * error found. */ -int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct dns_resolution *resolution) +int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct dns_resolution *resolution, int max_answer_records) { unsigned char *reader; char *previous_dname, tmpname[DNS_MAX_NAME_SIZE]; @@ -1157,7 +1160,7 @@ int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct if (dns_p->header.ancount == 0) return DNS_RESP_ANCOUNT_ZERO; /* check if too many records are announced */ - if (dns_p->header.ancount > DNS_MAX_ANSWER_RECORDS) + if (dns_p->header.ancount > max_answer_records) return DNS_RESP_INVALID; reader += 2; -- 2.39.5