ignore dns response parsing errors, only abort resolving for correct responses or error responses from correct server (bug #47459)

This commit is contained in:
sg 2016-03-22 07:22:17 +01:00
parent 5794ac2340
commit a1c78ea7bf
2 changed files with 104 additions and 109 deletions

View File

@ -302,6 +302,10 @@ HISTORY
++ Bugfixes: ++ Bugfixes:
2016-03-22: Simon Goldschmidt
* dns.c: ignore dns response parsing errors, only abort resolving for correct
responses or error responses from correct server (bug #47459)
2016-03-17: Simon Goldschmidt 2016-03-17: Simon Goldschmidt
* api_msg.c: fixed bug #47448 (netconn/socket leak if RST is received during close) * api_msg.c: fixed bug #47448 (netconn/socket leak if RST is received during close)

View File

@ -113,6 +113,8 @@ static u16_t dns_txid;
/** DNS resource record max. TTL (one week as default) */ /** DNS resource record max. TTL (one week as default) */
#ifndef DNS_MAX_TTL #ifndef DNS_MAX_TTL
#define DNS_MAX_TTL 604800 #define DNS_MAX_TTL 604800
#elif DNS_MAX_TTL > 0x7FFFFFFF
#error DNS_MAX_TTL must be a positive 32-bit value
#endif #endif
/* The number of parallel requests (i.e. calls to dns_gethostbyname /* The number of parallel requests (i.e. calls to dns_gethostbyname
@ -649,7 +651,7 @@ dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addr
* @return 0xFFFF: names differ, other: names equal -> offset behind name * @return 0xFFFF: names differ, other: names equal -> offset behind name
*/ */
static u16_t static u16_t
dns_compare_name(char *query, struct pbuf* p, u16_t start_offset) dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
{ {
unsigned char n; unsigned char n;
u16_t response_offset = start_offset; u16_t response_offset = start_offset;
@ -1048,6 +1050,33 @@ dns_check_entries(void)
} }
} }
/**
* Save TTL and call dns_call_found for correct response.
*/
static void
dns_correct_response(u8_t idx, u32_t ttl)
{
struct dns_table_entry *entry = &dns_table[idx];
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name));
ip_addr_debug_print(DNS_DEBUG, (&(entry->ipaddr)));
LWIP_DEBUGF(DNS_DEBUG, ("\n"));
/* read the answer resource record's TTL, and maximize it if needed */
entry->ttl = ttl;
if (entry->ttl > DNS_MAX_TTL) {
entry->ttl = DNS_MAX_TTL;
}
dns_call_found(idx, &entry->ipaddr);
if (entry->ttl == 0) {
/* RFC 883, page 29: "Zero values are
interpreted to mean that the RR can only be used for the
transaction in progress, and should not be cached."
-> flush this entry now */
entry->state = DNS_STATE_UNUSED;
}
}
/** /**
* Receive input function for DNS response packets arriving for the dns UDP pcb. * Receive input function for DNS response packets arriving for the dns UDP pcb.
* *
@ -1056,7 +1085,7 @@ dns_check_entries(void)
static void static void
dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{ {
u8_t i, entry_idx = DNS_TABLE_SIZE; u8_t i;
u16_t txid; u16_t txid;
u16_t res_idx; u16_t res_idx;
struct dns_hdr hdr; struct dns_hdr hdr;
@ -1080,32 +1109,29 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
/* Match the ID in the DNS header with the name table. */ /* Match the ID in the DNS header with the name table. */
txid = htons(hdr.id); txid = htons(hdr.id);
for (i = 0; i < DNS_TABLE_SIZE; i++) { for (i = 0; i < DNS_TABLE_SIZE; i++) {
struct dns_table_entry *entry = &dns_table[i]; const struct dns_table_entry *entry = &dns_table[i];
entry_idx = i;
if ((entry->state == DNS_STATE_ASKING) && if ((entry->state == DNS_STATE_ASKING) &&
(entry->txid == txid)) { (entry->txid == txid)) {
u8_t dns_err;
/* This entry is now completed. */
entry->state = DNS_STATE_DONE;
dns_err = hdr.flags2 & DNS_FLAG2_ERR_MASK;
/* We only care about the question(s) and the answers. The authrr /* We only care about the question(s) and the answers. The authrr
and the extrarr are simply discarded. */ and the extrarr are simply discarded. */
nquestions = htons(hdr.numquestions); nquestions = htons(hdr.numquestions);
nanswers = htons(hdr.numanswers); nanswers = htons(hdr.numanswers);
/* Check for error. If so, call callback to inform. */ /* Check for correct response. */
if (((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) || (dns_err != 0) || (nquestions != 1)) { if ((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name)); LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": not a response\n", entry->name));
/* call callback to indicate error, clean up memory and return */ goto memerr; /* ignore this packet */
goto responseerr; }
if (nquestions != 1) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
goto memerr; /* ignore this packet */
} }
/* Check whether response comes from the same network address to which the /* Check whether response comes from the same network address to which the
question was sent. (RFC 5452) */ question was sent. (RFC 5452) */
if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) { if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
/* call callback to indicate error, clean up memory and return */ goto memerr; /* ignore this packet */
goto responseerr;
} }
/* Check if the name in the "question" part match with the name in the entry and /* Check if the name in the "question" part match with the name in the entry and
@ -1113,8 +1139,7 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR); res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR);
if (res_idx == 0xFFFF) { if (res_idx == 0xFFFF) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name)); LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
/* call callback to indicate error, clean up memory and return */ goto memerr; /* ignore this packet */
goto responseerr;
} }
/* check if "question" part matches the request */ /* check if "question" part matches the request */
@ -1123,18 +1148,22 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
(LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_AAAA))) || (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_AAAA))) ||
(!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_A)))) { (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_A)))) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name)); LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
/* call callback to indicate error, clean up memory and return */ goto memerr; /* ignore this packet */
goto responseerr;
} }
/* skip the rest of the "question" part */ /* skip the rest of the "question" part */
res_idx += SIZEOF_DNS_QUERY; res_idx += SIZEOF_DNS_QUERY;
/* Check for error. If so, call callback to inform. */
if (hdr.flags2 & DNS_FLAG2_ERR_MASK) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name));
} else {
while ((nanswers > 0) && (res_idx < p->tot_len)) { while ((nanswers > 0) && (res_idx < p->tot_len)) {
/* skip answer resource record's host name */ /* skip answer resource record's host name */
res_idx = dns_parse_name(p, res_idx); res_idx = dns_parse_name(p, res_idx);
/* Check for IP address type and Internet class. Others are discarded. */ /* Check for IP address type and Internet class. Others are discarded. */
pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx); pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx);
res_idx += SIZEOF_DNS_ANSWER;
if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) { if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) {
#if LWIP_IPV4 #if LWIP_IPV4
if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) { if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) {
@ -1143,29 +1172,13 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
#endif /* LWIP_IPV4 && LWIP_IPV6 */ #endif /* LWIP_IPV4 && LWIP_IPV6 */
{ {
ip4_addr_t ip4addr; ip4_addr_t ip4addr;
res_idx += SIZEOF_DNS_ANSWER;
/* read the answer resource record's TTL, and maximize it if needed */
entry->ttl = ntohl(ans.ttl);
if (entry->ttl > DNS_MAX_TTL) {
entry->ttl = DNS_MAX_TTL;
}
/* read the IP address after answer resource record's header */ /* read the IP address after answer resource record's header */
pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx); pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx);
ip_addr_copy_from_ip4(entry->ipaddr, ip4addr); ip_addr_copy_from_ip4(dns_table[i].ipaddr, ip4addr);
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name)); pbuf_free(p);
ip_addr_debug_print(DNS_DEBUG, (&(entry->ipaddr))); /* handle correct response */
LWIP_DEBUGF(DNS_DEBUG, ("\n")); dns_correct_response(i, ntohl(ans.ttl));
/* call specified callback function if provided */ return;
dns_call_found(entry_idx, &entry->ipaddr);
if (entry->ttl == 0) {
/* RFC 883, page 29: "Zero values are
interpreted to mean that the RR can only be used for the
transaction in progress, and should not be cached."
-> flush this entry now */
goto flushentry;
}
/* deallocate memory and return */
goto memerr;
} }
} }
#endif /* LWIP_IPV4 */ #endif /* LWIP_IPV4 */
@ -1176,35 +1189,19 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
#endif /* LWIP_IPV4 && LWIP_IPV6 */ #endif /* LWIP_IPV4 && LWIP_IPV6 */
{ {
ip6_addr_t ip6addr; ip6_addr_t ip6addr;
res_idx += SIZEOF_DNS_ANSWER;
/* read the answer resource record's TTL, and maximize it if needed */
entry->ttl = ntohl(ans.ttl);
if (entry->ttl > DNS_MAX_TTL) {
entry->ttl = DNS_MAX_TTL;
}
/* read the IP address after answer resource record's header */ /* read the IP address after answer resource record's header */
pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_t), res_idx); pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_t), res_idx);
ip_addr_copy_from_ip6(entry->ipaddr, ip6addr); ip_addr_copy_from_ip6(dns_table[i].ipaddr, ip6addr);
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name)); pbuf_free(p);
ip_addr_debug_print(DNS_DEBUG, (&(entry->ipaddr))); /* handle correct response */
LWIP_DEBUGF(DNS_DEBUG, (" AAAA\n")); dns_correct_response(i, ntohl(ans.ttl));
/* call specified callback function if provided */ return;
dns_call_found(entry_idx, &entry->ipaddr);
if (entry->ttl == 0) {
/* RFC 883, page 29: "Zero values are
interpreted to mean that the RR can only be used for the
transaction in progress, and should not be cached."
-> flush this entry now */
goto flushentry;
}
/* deallocate memory and return */
goto memerr;
} }
} }
#endif /* LWIP_IPV6 */ #endif /* LWIP_IPV6 */
} }
/* skip this answer */ /* skip this answer */
res_idx += SIZEOF_DNS_ANSWER + htons(ans.len); res_idx += htons(ans.len);
--nanswers; --nanswers;
} }
#if LWIP_IPV4 && LWIP_IPV6 #if LWIP_IPV4 && LWIP_IPV6
@ -1212,36 +1209,30 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
(entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) { (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
if (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) { if (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
/* IPv4 failed, try IPv6 */ /* IPv4 failed, try IPv6 */
entry->reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6; dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
} else { } else {
/* IPv6 failed, try IPv4 */ /* IPv6 failed, try IPv4 */
entry->reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4; dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
} }
pbuf_free(p); pbuf_free(p);
entry->state = DNS_STATE_NEW; dns_table[i].state = DNS_STATE_NEW;
dns_check_entry(entry_idx); dns_check_entry(i);
return; return;
} }
#endif /* LWIP_IPV4 && LWIP_IPV6 */ #endif /* LWIP_IPV4 && LWIP_IPV6 */
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->name)); LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->name));
}
/* call callback to indicate error, clean up memory and return */ /* call callback to indicate error, clean up memory and return */
goto responseerr; pbuf_free(p);
dns_call_found(i, NULL);
dns_table[i].state = DNS_STATE_UNUSED;
return;
} }
} }
} }
/* deallocate memory and return */
goto memerr;
responseerr:
/* ERROR: call specified callback function with NULL as name to indicate an error */
dns_call_found(entry_idx, NULL);
flushentry:
/* flush this entry */
dns_table[entry_idx].state = DNS_STATE_UNUSED;
memerr: memerr:
/* free pbuf */ /* deallocate memory and return */
pbuf_free(p); pbuf_free(p);
return; return;
} }