DNS: split request callback information from actual DNS table to be able to optimize memory usage for multiple parallel requests (and clean up the code a bit)

This commit is contained in:
Simon Goldschmidt 2014-09-05 21:11:57 +02:00
parent 3c40d93f36
commit a491aa0f6a

View File

@ -11,6 +11,8 @@
* Port to lwIP from uIP * Port to lwIP from uIP
* by Jim Pettinato April 2007 * by Jim Pettinato April 2007
* security fixes and more by Simon Goldschmidt
* uIP version Copyright (c) 2002-2003, Adam Dunkels. * uIP version Copyright (c) 2002-2003, Adam Dunkels.
* All rights reserved. * All rights reserved.
* *
@ -90,7 +92,7 @@
* This is overridable but should only be needed by very small targets * This is overridable but should only be needed by very small targets
* or when using against non standard DNS servers. */ * or when using against non standard DNS servers. */
#ifndef LWIP_DNS_SECURE #ifndef LWIP_DNS_SECURE
#define LWIP_DNS_SECURE (LWIP_DNS_SECURE_RAND_XID | LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) #define LWIP_DNS_SECURE 0//(LWIP_DNS_SECURE_RAND_XID | LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING)
#endif #endif
/** Random generator function to create random TXIDs for queries */ /** Random generator function to create random TXIDs for queries */
@ -123,6 +125,20 @@ static u16_t dns_txid;
#define DNS_MAX_TTL 604800 #define DNS_MAX_TTL 604800
#endif #endif
/* The number of parallel requests (i.e. calls to dns_gethostbyname
* that cannot be answered from the DNS table.
* This is set to the table size by default.
*/
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
#ifndef DNS_MAX_REQUESTS
#define DNS_MAX_REQUESTS DNS_TABLE_SIZE
#endif
#else
/* In this configuration, both arrays have to have the same size and are used
* like one entry (used/free) */
#define DNS_MAX_REQUESTS DNS_TABLE_SIZE
#endif
/* DNS protocol flags */ /* DNS protocol flags */
#define DNS_FLAG1_RESPONSE 0x80 #define DNS_FLAG1_RESPONSE 0x80
#define DNS_FLAG1_OPCODE_STATUS 0x10 #define DNS_FLAG1_OPCODE_STATUS 0x10
@ -140,10 +156,7 @@ static u16_t dns_txid;
#define DNS_STATE_UNUSED 0 #define DNS_STATE_UNUSED 0
#define DNS_STATE_NEW 1 #define DNS_STATE_NEW 1
#define DNS_STATE_ASKING 2 #define DNS_STATE_ASKING 2
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) #define DNS_STATE_DONE 3
#define DNS_STATE_DUPLICATE_PENDING 3
#endif
#define DNS_STATE_DONE 4
#ifdef PACK_STRUCT_USE_INCLUDES #ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h" # include "arch/bpstruct.h"
@ -186,6 +199,8 @@ struct dns_answer {
u16_t len; u16_t len;
}; };
#define SIZEOF_DNS_ANSWER 10 #define SIZEOF_DNS_ANSWER 10
/* maximum allowed size for the struct due to non-packed */
#define SIZEOF_DNS_ANSWER_ASSERT 12
/** DNS table entry */ /** DNS table entry */
struct dns_table_entry { struct dns_table_entry {
@ -199,9 +214,18 @@ struct dns_table_entry {
u32_t ttl; u32_t ttl;
char name[DNS_MAX_NAME_LENGTH]; char name[DNS_MAX_NAME_LENGTH];
ip_addr_t ipaddr; ip_addr_t ipaddr;
};
/** DNS request table entry: used when dns_gehostbyname cannot answer the
* request from the DNS table */
struct dns_req_entry {
/* pointer to callback on DNS query done */ /* pointer to callback on DNS query done */
dns_found_callback found; dns_found_callback found;
/* argument passed to the callback function */
void *arg; void *arg;
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
u8_t dns_table_idx;
#endif
}; };
#if DNS_LOCAL_HOSTLIST #if DNS_LOCAL_HOSTLIST
@ -243,6 +267,7 @@ static void dns_check_entries(void);
static struct udp_pcb *dns_pcb; static struct udp_pcb *dns_pcb;
static u8_t dns_seqno; static u8_t dns_seqno;
static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
static struct dns_req_entry dns_requests[DNS_MAX_REQUESTS];
static ip_addr_t dns_servers[DNS_MAX_SERVERS]; static ip_addr_t dns_servers[DNS_MAX_SERVERS];
/** Contiguous buffer for processing responses */ /** Contiguous buffer for processing responses */
static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)]; static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)];
@ -291,6 +316,11 @@ dns_init()
{ {
ip_addr_t dnsserver; ip_addr_t dnsserver;
LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER",
sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT);
dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer); dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer);
/* initialize default DNS server address */ /* initialize default DNS server address */
@ -687,31 +717,26 @@ dns_send(u8_t numdns, const char* name, u16_t txid)
* entries for the given hostname. If there are any, their found callback will * entries for the given hostname. If there are any, their found callback will
* be called and they will be removed. * be called and they will be removed.
* *
* @param pEntry entry that is resolved or removed * @param idx dns table index of the entry that is resolved or removed
* @param addr IP address for the hostname (or NULL on error or memory shortage) * @param addr IP address for the hostname (or NULL on error or memory shortage)
*/ */
static void static void
dns_call_found(struct dns_table_entry* pEntry, ip_addr_t* addr) dns_call_found(u8_t idx, ip_addr_t* addr)
{ {
if (pEntry->found) {
(*pEntry->found)(pEntry->name, addr, pEntry->arg);
}
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
{
u8_t i; u8_t i;
for (i = 0; i < DNS_TABLE_SIZE; i++) { for (i = 0; i < DNS_MAX_REQUESTS; i++) {
if ((dns_table[i].state == DNS_STATE_DUPLICATE_PENDING) && if (dns_requests[i].found && (dns_requests[i].dns_table_idx == idx)) {
(LWIP_DNS_STRICMP(dns_table[i].name, pEntry->name) == 0)) { (*dns_requests[i].found)(dns_table[idx].name, addr, dns_requests[i].arg);
if (dns_table[i].found) {
(*dns_table[i].found)(dns_table[i].name, addr, dns_table[i].arg);
}
/* flush this entry */ /* flush this entry */
dns_table[i].state = DNS_STATE_UNUSED; dns_requests[i].found = NULL;
dns_table[i].found = NULL;
} }
} }
#else
if (dns_requests[idx].found) {
(*dns_requests[idx].found)(dns_table[idx].name, addr, dns_requests[idx].arg);
} }
dns_requests[idx].found = NULL;
#endif #endif
} }
@ -754,7 +779,7 @@ dns_check_entry(u8_t i)
LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
switch(pEntry->state) { switch (pEntry->state) {
case DNS_STATE_NEW: { case DNS_STATE_NEW: {
u16_t txid; u16_t txid;
@ -775,7 +800,7 @@ dns_check_entry(u8_t i)
break; break;
} }
case DNS_STATE_ASKING: { case DNS_STATE_ASKING:
if (--pEntry->tmr == 0) { if (--pEntry->tmr == 0) {
if (++pEntry->retries == DNS_MAX_RETRIES) { if (++pEntry->retries == DNS_MAX_RETRIES) {
if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) { if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) {
@ -787,10 +812,9 @@ dns_check_entry(u8_t i)
} else { } else {
LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name)); LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));
/* call specified callback function if provided */ /* call specified callback function if provided */
dns_call_found(pEntry, NULL); dns_call_found(i, NULL);
/* flush this entry */ /* flush this entry */
pEntry->state = DNS_STATE_UNUSED; pEntry->state = DNS_STATE_UNUSED;
pEntry->found = NULL;
break; break;
} }
} }
@ -806,22 +830,14 @@ dns_check_entry(u8_t i)
} }
} }
break; break;
} case DNS_STATE_DONE:
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
case DNS_STATE_DUPLICATE_PENDING:
/* nothing to do */
break;
#endif
case DNS_STATE_DONE: {
/* if the time to live is nul */ /* if the time to live is nul */
if ((pEntry->ttl == 0) || (--pEntry->ttl == 0)) { if ((pEntry->ttl == 0) || (--pEntry->ttl == 0)) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name)); LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));
/* flush this entry, there cannot be any related pending entries in this state */ /* flush this entry, there cannot be any related pending entries in this state */
pEntry->state = DNS_STATE_UNUSED; pEntry->state = DNS_STATE_UNUSED;
pEntry->found = NULL;
} }
break; break;
}
case DNS_STATE_UNUSED: case DNS_STATE_UNUSED:
/* nothing to do */ /* nothing to do */
break; break;
@ -852,12 +868,11 @@ dns_check_entries(void)
static void static void
dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
{ {
u8_t i; u8_t i, entry_idx = DNS_TABLE_SIZE;
u16_t txid; u16_t txid;
char *ptr; char *ptr;
struct dns_hdr *hdr; struct dns_hdr *hdr;
struct dns_answer ans; struct dns_answer ans;
struct dns_table_entry *pEntry;
struct dns_query qry; struct dns_query qry;
u16_t nquestions, nanswers; u16_t nquestions, nanswers;
@ -885,7 +900,8 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t
hdr = (struct dns_hdr*)dns_payload; hdr = (struct dns_hdr*)dns_payload;
txid = htons(hdr->id); txid = htons(hdr->id);
for (i = 0; i < DNS_TABLE_SIZE; i++) { for (i = 0; i < DNS_TABLE_SIZE; i++) {
pEntry = &dns_table[i]; struct dns_table_entry *pEntry = &dns_table[i];
entry_idx = i;
if ((pEntry->state == DNS_STATE_ASKING) && if ((pEntry->state == DNS_STATE_ASKING) &&
(pEntry->txid == txid)) { (pEntry->txid == txid)) {
/* This entry is now completed. */ /* This entry is now completed. */
@ -949,7 +965,7 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t
ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr))); ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
LWIP_DEBUGF(DNS_DEBUG, ("\n")); LWIP_DEBUGF(DNS_DEBUG, ("\n"));
/* call specified callback function if provided */ /* call specified callback function if provided */
dns_call_found(pEntry, &pEntry->ipaddr); dns_call_found(entry_idx, &pEntry->ipaddr);
if (pEntry->ttl == 0) { if (pEntry->ttl == 0) {
/* RFC 883, page 29: "Zero values are /* RFC 883, page 29: "Zero values are
interpreted to mean that the RR can only be used for the interpreted to mean that the RR can only be used for the
@ -976,11 +992,10 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t
responseerr: responseerr:
/* ERROR: call specified callback function with NULL as name to indicate an error */ /* ERROR: call specified callback function with NULL as name to indicate an error */
dns_call_found(pEntry, NULL); dns_call_found(entry_idx, NULL);
flushentry: flushentry:
/* flush this entry */ /* flush this entry */
pEntry->state = DNS_STATE_UNUSED; dns_table[entry_idx].state = DNS_STATE_UNUSED;
pEntry->found = NULL;
memerr: memerr:
/* free pbuf */ /* free pbuf */
@ -1005,6 +1020,28 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
u8_t lseq, lseqi; u8_t lseq, lseqi;
struct dns_table_entry *pEntry = NULL; struct dns_table_entry *pEntry = NULL;
size_t namelen; size_t namelen;
struct dns_req_entry* req;
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
u8_t r;
/* check for duplicate entries */
for (i = 0; i < DNS_TABLE_SIZE; i++) {
if ((dns_table[i].state == DNS_STATE_ASKING) &&
(LWIP_DNS_STRICMP(name, dns_table[i].name) == 0)) {
/* this is a duplicate entry, find a free request entry */
for (r = 0; r < DNS_MAX_REQUESTS; r++) {
if (dns_requests[r].found == 0) {
dns_requests[r].found = found;
dns_requests[r].arg = callback_arg;
dns_requests[r].dns_table_idx = i;
LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": duplicate request\n", name));
return ERR_INPROGRESS;
}
}
}
}
/* no duplicate entries found */
#endif
/* search an unused entry, or the oldest one */ /* search an unused entry, or the oldest one */
lseq = 0; lseq = 0;
@ -1027,7 +1064,7 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
/* if we don't have found an unused entry, use the oldest completed one */ /* if we don't have found an unused entry, use the oldest completed one */
if (i == DNS_TABLE_SIZE) { if (i == DNS_TABLE_SIZE) {
if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
/* no entry can't be used now, table is full */ /* no entry can be used now, table is full */
LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
return ERR_MEM; return ERR_MEM;
} else { } else {
@ -1037,36 +1074,38 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
} }
} }
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
/* find a free request entry */
req = NULL;
for (r = 0; r < DNS_MAX_REQUESTS; r++) {
if (dns_requests[r].found == 0) {
req = &dns_requests[r];
break;
}
}
if (req == NULL) {
/* no request entry can be used now, table is full */
LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS request entries table is full\n", name));
return ERR_MEM;
}
#else
/* in this configuration, the entry index is the same as the request index */
req = &dns_requests[i];
#endif
/* use this entry */ /* use this entry */
LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
/* fill the entry */ /* fill the entry */
pEntry->state = DNS_STATE_NEW; pEntry->state = DNS_STATE_NEW;
pEntry->seqno = dns_seqno; pEntry->seqno = dns_seqno;
pEntry->found = found; req->found = found;
pEntry->arg = callback_arg; req->arg = callback_arg;
namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1); namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1);
MEMCPY(pEntry->name, name, namelen); MEMCPY(pEntry->name, name, namelen);
pEntry->name[namelen] = 0; pEntry->name[namelen] = 0;
#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
/* check for duplicate entries */
{
u8_t n;
for (n = 0; n < DNS_TABLE_SIZE; n++) {
if ((dns_table[n].state == DNS_STATE_ASKING) &&
(LWIP_DNS_STRICMP(name, dns_table[n].name) == 0)) {
/* this is a duplicate entry */
struct dns_table_entry *orig = &dns_table[n];
pEntry->state = DNS_STATE_DUPLICATE_PENDING;
pEntry->seqno = orig->seqno;
/* don't send a query for this entry, only for the original */
return ERR_INPROGRESS;
}
}
}
/* no duplicate entries found */
#endif
dns_seqno++; dns_seqno++;
/* force to send query without waiting timer */ /* force to send query without waiting timer */