diff --git a/doc/configuration.txt b/doc/configuration.txt index 66c932597..9f7f9ff3d 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 5aed46e78..aa063c7b2 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 0f9c1b970..c7338c792 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 ee249794f..e69a4ab78 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 c91d4237b..62291fe1f 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;