From: Baptiste Assmann Date: Sat, 14 May 2016 09:26:22 +0000 (+0200) Subject: MEDIUM: dns: new DNS response parser X-Git-Tag: v1.7-dev5~43 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c1ce5f358e5d3c435a875a6f5520d3bf146337c0;p=thirdparty%2Fhaproxy.git MEDIUM: dns: new DNS response parser New DNS response parser function which turn the DNS response from a network buffer into a DNS structure, much easier for later analysis by upper layer. Memory is pre-allocated at start-up in a chunk dedicated to DNS response store. New error code to report a wrong number of queries in a DNS response. --- diff --git a/include/proto/dns.h b/include/proto/dns.h index 170eefa522..c62834f94e 100644 --- a/include/proto/dns.h +++ b/include/proto/dns.h @@ -32,8 +32,8 @@ int dns_build_query(int query_id, int query_type, char *hostname_dn, int hostnam struct task *dns_process_resolve(struct task *t); int dns_init_resolvers(void); uint16_t dns_rnd16(void); -int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, char *dn_name, int dn_name_len); -int dns_get_ip_from_response(unsigned char *resp, unsigned char *resp_end, +int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct dns_response_packet *dns_p); +int dns_get_ip_from_response(struct dns_response_packet *dns_p, struct dns_resolution *resol, void *currentip, short currentip_sin_family, void **newip, short *newip_sin_family); diff --git a/include/proto/server.h b/include/proto/server.h index 47630fe24c..0ed68b8c1a 100644 --- a/include/proto/server.h +++ b/include/proto/server.h @@ -48,7 +48,7 @@ void apply_server_state(void); /* functions related to server name resolution */ int snr_update_srv_status(struct server *s); -int snr_resolution_cb(struct dns_resolution *resolution, struct dns_nameserver *nameserver, unsigned char *response, int response_len); +int snr_resolution_cb(struct dns_resolution *resolution, struct dns_nameserver *nameserver, struct dns_response_packet *dns_p); int snr_resolution_error_cb(struct dns_resolution *resolution, int error_code); /* increase the number of cumulated connections on the designated server */ diff --git a/include/types/dns.h b/include/types/dns.h index 03ffdc1483..5d6b5a1fbf 100644 --- a/include/types/dns.h +++ b/include/types/dns.h @@ -42,6 +42,12 @@ /* maximum number of answer record in a DNS response */ #define DNS_MAX_ANSWER_RECORDS ((DNS_MAX_UDP_MESSAGE - DNS_HEADER_SIZE) / DNS_MIN_RECORD_SIZE) +/* size of dns_buffer used to store responses from the buffer + * dns_buffer is used to store data collected from records found in a response. + * Before using it, caller will always check that there is at least DNS_MAX_NAME_SIZE bytes + * available */ +#define DNS_ANALYZE_BUFFER_SIZE DNS_MAX_UDP_MESSAGE + DNS_MAX_NAME_SIZE + /* DNS error messages */ #define DNS_TOO_LONG_FQDN "hostname too long" #define DNS_LABEL_TOO_LONG "one label too long" @@ -204,7 +210,7 @@ struct dns_resolution { struct list list; /* resolution list */ struct dns_resolvers *resolvers; /* resolvers section associated to this resolution */ void *requester; /* owner of this name resolution */ - int (*requester_cb)(struct dns_resolution *, struct dns_nameserver *, unsigned char *, int); + int (*requester_cb)(struct dns_resolution *, struct dns_nameserver *, struct dns_response_packet *); /* requester callback for valid response */ int (*requester_error_cb)(struct dns_resolution *, int); /* requester callback, for error management */ @@ -256,6 +262,7 @@ enum { DNS_RESP_TIMEOUT, /* DNS server has not answered in time */ DNS_RESP_TRUNCATED, /* DNS response is truncated */ DNS_RESP_NO_EXPECTED_RECORD, /* No expected records were found in the response */ + DNS_RESP_QUERY_COUNT_ERROR, /* we did not get the expected number of queries in the response */ }; /* return codes after searching an IP in a DNS response buffer, using a family preference */ diff --git a/src/dns.c b/src/dns.c index b9dce6b1e3..7d5ab002f1 100644 --- a/src/dns.c +++ b/src/dns.c @@ -37,6 +37,17 @@ struct list dns_resolvers = LIST_HEAD_INIT(dns_resolvers); struct dns_resolution *resolution = NULL; +/* + * pre-allocated memory for maximum record names in a DNS response + * Each name is DNS_MAX_NAME_SIZE, we add 1 for the NULL character + * + * WARNING: this is not thread safe... + */ +struct dns_response_packet dns_response; +struct chunk dns_trash = { }; +struct dns_query_item dns_query_records[DNS_MAX_QUERY_RECORDS]; +struct dns_answer_item dns_answer_records[DNS_MAX_ANSWER_RECORDS]; + static int64_t dns_query_id_seed; /* random seed */ /* proto_udp callback functions for a DNS resolution */ @@ -124,11 +135,13 @@ void dns_resolve_recv(struct dgram_conn *dgram) struct dns_nameserver *nameserver; struct dns_resolvers *resolvers; struct dns_resolution *resolution; + struct dns_query_item *query; unsigned char buf[DNS_MAX_UDP_MESSAGE + 1]; unsigned char *bufend; int fd, buflen, ret; unsigned short query_id; struct eb32_node *eb; + struct dns_response_packet *dns_p = &dns_response; fd = dgram->t.sock.fd; @@ -187,12 +200,12 @@ void dns_resolve_recv(struct dgram_conn *dgram) /* number of responses received */ resolution->nb_responses += 1; - ret = dns_validate_dns_response(buf, bufend, resolution->hostname_dn, resolution->hostname_dn_len); + ret = dns_validate_dns_response(buf, bufend, dns_p); /* treat only errors */ switch (ret) { + case DNS_RESP_QUERY_COUNT_ERROR: case DNS_RESP_INVALID: - case DNS_RESP_WRONG_NAME: nameserver->counters.invalid += 1; resolution->requester_error_cb(resolution, DNS_RESP_INVALID); continue; @@ -233,8 +246,18 @@ void dns_resolve_recv(struct dgram_conn *dgram) continue; } + /* Now let's check the query's dname corresponds to the one we sent. + * 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(&dns_p->query_list, struct dns_query_item *, list); + if (query && memcmp(query->name, resolution->hostname_dn, resolution->hostname_dn_len) != 0) { + nameserver->counters.other += 1; + resolution->requester_error_cb(resolution, DNS_RESP_WRONG_NAME); + continue; + } + nameserver->counters.valid += 1; - resolution->requester_cb(resolution, nameserver, buf, buflen); + resolution->requester_cb(resolution, nameserver, dns_p); } } @@ -331,36 +354,118 @@ void dns_update_resolvers_timeout(struct dns_resolvers *resolvers) } } +/* + * Analyse, re-build and copy the name from the DNS response packet . + * must point to the 'data_len' information or pointer 'c0' for compressed data. + * The result is copied into , ensuring we don't overflow using + * Returns the number of bytes the caller can move forward. If 0 it means an error occured + * while parsing the name. + * is the number of bytes the caller could move forward. + */ +int dns_read_name(unsigned char *buffer, unsigned char *bufend, unsigned char *name, char *destination, int dest_len, int *offset) +{ + int nb_bytes = 0, n = 0; + int label_len; + unsigned char *reader = name; + char *dest = destination; + + while (1) { + /* name compression is in use */ + if ((*reader & 0xc0) == 0xc0) { + /* a pointer must point BEFORE current position */ + if ((buffer + reader[1]) > reader) { + goto out_error; + } + + n = dns_read_name(buffer, bufend, buffer + reader[1], dest, dest_len - nb_bytes, offset); + if (n == 0) + goto out_error; + + dest += n; + nb_bytes += n; + goto out; + } + + label_len = *reader; + if (label_len == 0) + goto out; + /* Check if: + * - we won't read outside the buffer + * - there is enough place in the destination + */ + if ((reader + label_len >= bufend) || (nb_bytes + label_len >= dest_len)) + goto out_error; + + /* +1 to take label len + label string */ + label_len += 1; + + memcpy(dest, reader, label_len); + + dest += label_len; + nb_bytes += label_len; + reader += label_len; + } + + out: + /* offset computation: + * parse from until finding either NULL or a pointer "c0xx" + */ + reader = name; + *offset = 0; + while (reader < bufend) { + if ((reader[0] & 0xc0) == 0xc0) { + *offset += 2; + break; + } + else if (*reader == 0) { + *offset += 1; + break; + } + *offset += 1; + ++reader; + } + + return nb_bytes; + + out_error: + return 0; +} + /* * Function to validate that the buffer DNS response provided in and * finishing before is valid from a DNS protocol point of view. - * The caller can also ask the function to check if the response contains data - * for a domain name whose length is returns one of the - * DNS_RESP_* code. + * + * The result is stored in the structured pointed by . + * It's up to the caller to allocate memory for . + * + * 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, char *dn_name, int dn_name_len) +int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, struct dns_response_packet *dns_p) { - unsigned char *reader, *cname, *ptr; - int i, len, flags, type, ancount, cnamelen, expected_record; + unsigned char *reader; + char *previous_dname, tmpname[DNS_MAX_NAME_SIZE]; + int len, flags, offset, ret; + int dns_query_record_id, dns_answer_record_id; + struct dns_query_item *dns_query; + struct dns_answer_item *dns_answer_record; reader = resp; - cname = NULL; - cnamelen = 0; len = 0; - expected_record = 0; /* flag to report if at least one expected record type is found in the response. - * For now, only records containing an IP address (A and AAAA) are - * considered as expected. - * Later, this function may be updated to let the caller decide what type - * of record is expected to consider the response as valid. (SRV or TXT types) - */ - - /* move forward 2 bytes for the query id */ - reader += 2; - if (reader >= bufend) + previous_dname = NULL; + + /* initialization of local buffer */ + memset(dns_p, '\0', sizeof(struct dns_response_packet)); + chunk_reset(&dns_trash); + + /* query id */ + if (reader + 2 >= bufend) return DNS_RESP_INVALID; + dns_p->header.id = reader[0] * 256 + reader[1]; + reader += 2; /* - * flags are stored over 2 bytes + * flags and rcode are stored over 2 bytes * First byte contains: * - response flag (1 bit) * - opcode (4 bits) @@ -387,196 +492,215 @@ int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, char * /* move forward 2 bytes for flags */ reader += 2; - if (reader >= bufend) - return DNS_RESP_INVALID; - /* move forward 2 bytes for question count */ - reader += 2; - if (reader >= bufend) + /* 2 bytes for question count */ + if (reader + 2 >= bufend) return DNS_RESP_INVALID; - - /* analyzing answer count */ - if (reader + 2 > bufend) + dns_p->header.qdcount = reader[0] * 256 + reader[1]; + /* (for now) we send one query only, so we expect only one in the response too */ + if (dns_p->header.qdcount != 1) + return DNS_RESP_QUERY_COUNT_ERROR; + if (dns_p->header.qdcount > DNS_MAX_QUERY_RECORDS) return DNS_RESP_INVALID; - ancount = reader[0] * 256 + reader[1]; + reader += 2; - if (ancount == 0) + /* 2 bytes for answer count */ + if (reader + 2 >= bufend) + return DNS_RESP_INVALID; + dns_p->header.ancount = reader[0] * 256 + reader[1]; + if (dns_p->header.ancount == 0) return DNS_RESP_ANCOUNT_ZERO; - - /* move forward 2 bytes for answer count */ - reader += 2; - if (reader >= bufend) + /* check if too many records are announced */ + if (dns_p->header.ancount > DNS_MAX_ANSWER_RECORDS) return DNS_RESP_INVALID; + reader += 2; - /* move forward 4 bytes authority and additional count */ - reader += 4; - if (reader >= bufend) + /* 2 bytes authority count */ + if (reader + 2 >= bufend) return DNS_RESP_INVALID; + dns_p->header.nscount = reader[0] * 256 + reader[1]; + reader += 2; - /* check if the name can stand in response */ - if (dn_name && ((reader + dn_name_len + 1) > bufend)) + /* 2 bytes additional count */ + if (reader + 2 >= bufend) return DNS_RESP_INVALID; + dns_p->header.arcount = reader[0] * 256 + reader[1]; + reader += 2; - /* check hostname */ - if (dn_name && (memcmp(reader, dn_name, dn_name_len) != 0)) - return DNS_RESP_WRONG_NAME; + /* parsing dns queries */ + LIST_INIT(&dns_p->query_list); + for (dns_query_record_id = 0; dns_query_record_id < dns_p->header.qdcount; dns_query_record_id++) { + /* use next pre-allocated dns_query_item after ensuring there is + * still one available. + * It's then added to our packet query list. + */ + if (dns_query_record_id > DNS_MAX_QUERY_RECORDS) + return DNS_RESP_INVALID; + dns_query = &dns_query_records[dns_query_record_id]; + LIST_ADDQ(&dns_p->query_list, &dns_query->list); - /* move forward hostname len bytes + 1 for NULL byte */ - if (dn_name) { - reader = reader + dn_name_len + 1; - } - else { - ptr = reader; - while (*ptr) { - ptr++; - if (ptr >= bufend) - return DNS_RESP_INVALID; - } - reader = ptr + 1; - } + /* name is a NULL terminated string in our case, since we have + * one query per response and the first one can't be compressed + * (using the 0x0c format) + */ + offset = 0; + len = dns_read_name(resp, bufend, reader, dns_query->name, DNS_MAX_NAME_SIZE, &offset); - /* move forward 4 bytes for question type and question class */ - reader += 4; - if (reader >= bufend) - return DNS_RESP_INVALID; + if (len == 0) + return DNS_RESP_INVALID; + + reader += offset; + previous_dname = dns_query->name; + + /* move forward 2 bytes for question type */ + if (reader + 2 >= bufend) + return DNS_RESP_INVALID; + dns_query->type = reader[0] * 256 + reader[1]; + reader += 2; + + /* move forward 2 bytes for question class */ + if (reader + 2 >= bufend) + return DNS_RESP_INVALID; + dns_query->class = reader[0] * 256 + reader[1]; + reader += 2; + } /* now parsing response records */ - for (i = 1; i <= ancount; i++) { + LIST_INIT(&dns_p->answer_list); + for (dns_answer_record_id = 0; dns_answer_record_id < dns_p->header.ancount; dns_answer_record_id++) { if (reader >= bufend) return DNS_RESP_INVALID; - /* - * name can be a pointer, so move forward reader cursor accordingly - * if 1st byte is '11XXXXXX', it means name is a pointer - * and 2nd byte gives the offset from resp where the hostname can - * be found - */ - if ((*reader & 0xc0) == 0xc0) { - /* - * pointer, hostname can be found at resp + *(reader + 1) - */ - if (reader + 1 > bufend) - return DNS_RESP_INVALID; + /* pull next response record from the list, if still one available, then add it + * to the record list */ + if (dns_answer_record_id > DNS_MAX_ANSWER_RECORDS) + return DNS_RESP_INVALID; + dns_answer_record = &dns_answer_records[dns_answer_record_id]; + LIST_ADDQ(&dns_p->answer_list, &dns_answer_record->list); - ptr = resp + *(reader + 1); + offset = 0; + len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset); - /* check if the pointer points inside the buffer */ - if (ptr >= bufend) + if (len == 0) + return DNS_RESP_INVALID; + + /* check if the current record dname is valid. + * previous_dname points either to queried dname or last CNAME target + */ + if (memcmp(previous_dname, tmpname, len) != 0) { + if (dns_answer_record_id == 0) { + /* first record, means a mismatch issue between queried dname + * and dname found in the first record */ return DNS_RESP_INVALID; - } - else { - /* - * name is a string which starts at first byte - * checking against last cname when recursing through the response - */ - /* look for the end of the string and ensure it's in the buffer */ - ptr = reader; - len = 0; - while (*ptr) { - ++len; - ++ptr; - if (ptr >= bufend) - return DNS_RESP_INVALID; + } else { + /* if not the first record, this means we have a CNAME resolution + * error */ + return DNS_RESP_CNAME_ERROR; } - /* if cname is set, it means a CNAME recursion is in progress */ - ptr = reader; } - /* ptr now points to the name */ - if ((*reader & 0xc0) != 0xc0) { - /* if cname is set, it means a CNAME recursion is in progress */ - if (cname) { - /* check if the name can stand in response */ - if ((reader + cnamelen) > bufend) - return DNS_RESP_INVALID; - /* compare cname and current name */ - if (memcmp(ptr, cname, cnamelen) != 0) - return DNS_RESP_CNAME_ERROR; - - cname = reader; - cnamelen = dns_str_to_dn_label_len((const char *)cname); - - /* move forward cnamelen bytes + NULL byte */ - reader += (cnamelen + 1); - } - /* compare server hostname to current name */ - else if (dn_name) { - /* check if the name can stand in response */ - if ((reader + dn_name_len) > bufend) - return DNS_RESP_INVALID; - if (memcmp(ptr, dn_name, dn_name_len) != 0) - return DNS_RESP_WRONG_NAME; + dns_answer_record->name = chunk_newstr(&dns_trash); + if (dns_answer_record->name == NULL) + return DNS_RESP_INVALID; - reader += (dn_name_len + 1); - } - else { - reader += (len + 1); - } - } - else { - /* shortname in progress */ - /* move forward 2 bytes for information pointer and address pointer */ - reader += 2; - } + ret = chunk_strncat(&dns_trash, tmpname, len); + if (ret == 0) + return DNS_RESP_INVALID; + reader += offset; if (reader >= bufend) return DNS_RESP_INVALID; - /* - * we know the record is either for our server hostname - * or a valid CNAME in a crecursion - */ + if (reader >= bufend) + return DNS_RESP_INVALID; - /* now reading record type (A, AAAA, CNAME, etc...) */ + /* 2 bytes for record type (A, AAAA, CNAME, etc...) */ if (reader + 2 > bufend) return DNS_RESP_INVALID; - type = reader[0] * 256 + reader[1]; + dns_answer_record->type = reader[0] * 256 + reader[1]; + reader += 2; - /* move forward 2 bytes for type (2) */ + /* 2 bytes for class (2) */ + if (reader + 2 > bufend) + return DNS_RESP_INVALID; + dns_answer_record->class = reader[0] * 256 + reader[1]; reader += 2; - /* move forward 6 bytes for class (2) and ttl (4) */ - reader += 6; - if (reader >= bufend) + /* 4 bytes for ttl (4) */ + if (reader + 4 > bufend) return DNS_RESP_INVALID; + dns_answer_record->ttl = reader[0] * 16777216 + reader[1] * 65536 + + reader[2] * 256 + reader[3]; + reader += 4; /* now reading data len */ if (reader + 2 > bufend) return DNS_RESP_INVALID; - len = reader[0] * 256 + reader[1]; + dns_answer_record->data_len = reader[0] * 256 + reader[1]; /* move forward 2 bytes for data len */ reader += 2; /* analyzing record content */ - switch (type) { + switch (dns_answer_record->type) { case DNS_RTYPE_A: /* ipv4 is stored on 4 bytes */ - if (len != 4) + if (dns_answer_record->data_len != 4) return DNS_RESP_INVALID; - expected_record = 1; + dns_answer_record->address.sa_family = AF_INET; + memcpy(&(((struct sockaddr_in *)&dns_answer_record->address)->sin_addr), + reader, dns_answer_record->data_len); break; case DNS_RTYPE_CNAME: - cname = reader; - cnamelen = len; + /* check if this is the last record and update the caller about the status: + * no IP could be found and last record was a CNAME. Could be triggered + * by a wrong query type + * + * + 1 because dns_answer_record_id starts at 0 while number of answers + * is an integer and starts at 1. + */ + if (dns_answer_record_id + 1 == dns_p->header.ancount) + return DNS_RESP_CNAME_ERROR; + + offset = 0; + len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset); + + if (len == 0) + return DNS_RESP_INVALID; + + dns_answer_record->target = chunk_newstr(&dns_trash); + if (dns_answer_record->target == NULL) + return DNS_RESP_INVALID; + + ret = chunk_strncat(&dns_trash, tmpname, len); + if (ret == 0) + return DNS_RESP_INVALID; + + previous_dname = dns_answer_record->target; + break; case DNS_RTYPE_AAAA: /* ipv6 is stored on 16 bytes */ - if (len != 16) + if (dns_answer_record->data_len != 16) return DNS_RESP_INVALID; - expected_record = 1; + dns_answer_record->address.sa_family = AF_INET6; + memcpy(&(((struct sockaddr_in6 *)&dns_answer_record->address)->sin6_addr), + reader, dns_answer_record->data_len); break; + } /* switch (record type) */ - /* move forward len for analyzing next record in the response */ - reader += len; + /* move forward dns_answer_record->data_len for analyzing next record in the response */ + reader += dns_answer_record->data_len; } /* for i 0 to ancount */ - if (expected_record == 0) - return DNS_RESP_NO_EXPECTED_RECORD; + /* let's add a last \0 to close our last string */ + ret = chunk_strncat(&dns_trash, "\0", 1); + if (ret == 0) + return DNS_RESP_INVALID; return DNS_RESP_VALID; } @@ -592,18 +716,19 @@ int dns_validate_dns_response(unsigned char *resp, unsigned char *bufend, char * * returns one of the DNS_UPD_* code */ #define DNS_MAX_IP_REC 20 -int dns_get_ip_from_response(unsigned char *resp, unsigned char *resp_end, +int dns_get_ip_from_response(struct dns_response_packet *dns_p, struct dns_resolution *resol, void *currentip, short currentip_sin_family, void **newip, short *newip_sin_family) { + struct dns_answer_item *record; int family_priority; char *dn_name; int dn_name_len; - int i, ancount, cnamelen, type, data_len, currentip_found; - unsigned char *reader, *cname, *ptr, *newip4, *newip6; + int i, cnamelen, currentip_found; + unsigned char *cname, *newip4, *newip6; struct { - unsigned char *ip; + void *ip; unsigned char type; } rec[DNS_MAX_IP_REC]; int currentip_sel; @@ -614,59 +739,19 @@ int dns_get_ip_from_response(unsigned char *resp, unsigned char *resp_end, family_priority = resol->opts->family_prio; dn_name = resol->hostname_dn; dn_name_len = resol->hostname_dn_len; - cname = *newip = newip4 = newip6 = NULL; cnamelen = currentip_found = 0; *newip_sin_family = AF_UNSPEC; - ancount = *(resp + 7); /* Assume no more than 256 answers */ - - /* bypass DNS response header */ - reader = resp + sizeof(struct dns_header); - - /* bypass DNS query section */ - /* move forward hostname len bytes + 1 for NULL byte */ - reader = reader + dn_name_len + 1; - - /* move forward 4 bytes for question type and question class */ - reader += 4; /* now parsing response records */ - for (i = 1; i <= ancount; i++) { - /* - * name can be a pointer, so move forward reader cursor accordingly - * if 1st byte is '11XXXXXX', it means name is a pointer - * and 2nd byte gives the offset from buf where the hostname can - * be found - */ - if ((*reader & 0xc0) == 0xc0) - ptr = resp + *(reader + 1); - else - ptr = reader; - + list_for_each_entry(record, &dns_response.answer_list, list) { if (cname) { - if (memcmp(ptr, cname, cnamelen)) { + if (memcmp(record->name, cname, cnamelen) != 0) { return DNS_UPD_NAME_ERROR; } } - else if (memcmp(ptr, dn_name, dn_name_len)) + else if (memcmp(record->name, dn_name, dn_name_len) != 0) { return DNS_UPD_NAME_ERROR; - - if ((*reader & 0xc0) == 0xc0) { - /* move forward 2 bytes for information pointer and address pointer */ - reader += 2; - } - else { - if (cname) { - cname = reader; - cnamelen = dns_str_to_dn_label_len((char *)cname); - - /* move forward cnamelen bytes + NULL byte */ - reader += (cnamelen + 1); - } - else { - /* move forward dn_name_len bytes + NULL byte */ - reader += (dn_name_len + 1); - } } /* @@ -674,56 +759,32 @@ int dns_get_ip_from_response(unsigned char *resp, unsigned char *resp_end, * or a valid CNAME in a crecursion */ - /* now reading record type (A, AAAA, CNAME, etc...) */ - type = reader[0] * 256 + reader[1]; - - /* move forward 2 bytes for type (2) */ - reader += 2; - - /* move forward 6 bytes for class (2) and ttl (4) */ - reader += 6; - - /* now reading data len */ - data_len = reader[0] * 256 + reader[1]; - - /* move forward 2 bytes for data len */ - reader += 2; - /* analyzing record content */ - switch (type) { + switch (record->type) { case DNS_RTYPE_A: /* Store IPv4, only if some room is avalaible. */ if (rec_nb < DNS_MAX_IP_REC) { - rec[rec_nb].ip = reader; + rec[rec_nb].ip = &(((struct sockaddr_in *)&record->address)->sin_addr); rec[rec_nb].type = AF_INET; rec_nb++; } - /* move forward data_len for analyzing next record in the response */ - reader += data_len; break; case DNS_RTYPE_CNAME: - cname = reader; - cnamelen = data_len; + cname = record->target; + cnamelen = record->data_len; - reader += data_len; break; case DNS_RTYPE_AAAA: /* Store IPv6, only if some room is avalaible. */ if (rec_nb < DNS_MAX_IP_REC) { - rec[rec_nb].ip = reader; + rec[rec_nb].ip = &(((struct sockaddr_in6 *)&record->address)->sin6_addr); rec[rec_nb].type = AF_INET6; rec_nb++; } - /* move forward data_len for analyzing next record in the response */ - reader += data_len; break; - default: - /* not supported record type */ - /* move forward data_len for analyzing next record in the response */ - reader += data_len; } /* switch (record type) */ } /* list for each record entries */ @@ -886,8 +947,19 @@ int dns_init_resolvers(void) struct dns_nameserver *curnameserver; struct dgram_conn *dgram; struct task *t; + char *dns_trash_str; int fd; + dns_trash_str = malloc(global.tune.bufsize); + if (dns_trash_str == NULL) { + Alert("Starting [%s] resolvers: out of memory.\n", curr_resolvers->id); + return 0; + } + + /* allocate memory for the dns_trash buffer used to temporarily store + * the records of the received response */ + chunk_init(&dns_trash, dns_trash_str, global.tune.bufsize); + /* give a first random value to our dns query_id seed */ dns_query_id_seed = random(); diff --git a/src/server.c b/src/server.c index b105e28eb3..b6eef5db9c 100644 --- a/src/server.c +++ b/src/server.c @@ -2820,17 +2820,15 @@ int snr_update_srv_status(struct server *s) * 0 on error * 1 when no error or safe ignore */ -int snr_resolution_cb(struct dns_resolution *resolution, struct dns_nameserver *nameserver, unsigned char *response, int response_len) +int snr_resolution_cb(struct dns_resolution *resolution, struct dns_nameserver *nameserver, struct dns_response_packet *dns_p) { struct server *s; void *serverip, *firstip; short server_sin_family, firstip_sin_family; - unsigned char *response_end; int ret; struct chunk *chk = get_trash_chunk(); /* initializing variables */ - response_end = response + response_len; /* pointer to mark the end of the response */ firstip = NULL; /* pointer to the first valid response found */ /* it will be used as the new IP if a change is required */ firstip_sin_family = AF_UNSPEC; @@ -2854,7 +2852,7 @@ int snr_resolution_cb(struct dns_resolution *resolution, struct dns_nameserver * goto invalid; } - ret = dns_get_ip_from_response(response, response_end, resolution, + ret = dns_get_ip_from_response(dns_p, resolution, serverip, server_sin_family, &firstip, &firstip_sin_family);