📄 dns.c
字号:
const char *pHostname;
u8_t n;
LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
(u16_t)(numdns), name));
LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS);
LWIP_ASSERT("dns server has no IP address set", dns_servers[numdns].addr != 0);
/* if here, we have either a new query or a retry on a previous query to process */
p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dns_hdr) + DNS_MAX_NAME_LENGTH +
sizeof(struct dns_query), PBUF_RAM);
if (p != NULL) {
LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
/* fill dns header */
hdr = (struct dns_hdr*)p->payload;
memset(hdr, 0, sizeof(struct dns_hdr));
hdr->id = htons(id);
hdr->flags1 = DNS_FLAG1_RD;
hdr->numquestions = htons(1);
query = (char*)hdr + sizeof(struct dns_hdr);
pHostname = name;
--pHostname;
/* convert hostname into suitable query format. */
do {
++pHostname;
nptr = query;
++query;
for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
*query = *pHostname;
++query;
++n;
}
*nptr = n;
} while(*pHostname != 0);
*query++='\0';
/* fill dns query */
qry.type = htons(DNS_RRTYPE_A);
qry.class = htons(DNS_RRCLASS_IN);
MEMCPY( query, &qry, sizeof(struct dns_query));
/* resize pbuf to the exact dns query */
pbuf_realloc(p, (query + sizeof(struct dns_query)) - ((char*)(p->payload)));
/* connect to the server for faster receiving */
udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT);
/* send dns packet */
err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT);
/* free pbuf */
pbuf_free(p);
} else {
err = ERR_MEM;
}
return err;
}
/**
* dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query.
* Check an entry in the dns_table:
* - send out query for new entries
* - retry old pending entries on timeout (also with different servers)
* - remove completed entries from the table if their TTL has expired
*
* @param i index of the dns_table entry to check
*/
static void
dns_check_entry(u8_t i)
{
struct dns_table_entry *pEntry = &dns_table[i];
LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
switch(pEntry->state) {
case DNS_STATE_NEW: {
/* initialize new entry */
pEntry->state = DNS_STATE_ASKING;
pEntry->numdns = 0;
pEntry->tmr = 1;
pEntry->retries = 0;
/* send DNS packet for this entry */
dns_send(pEntry->numdns, pEntry->name, i);
break;
}
case DNS_STATE_ASKING: {
if (--pEntry->tmr == 0) {
if (++pEntry->retries == DNS_MAX_RETRIES) {
if ((pEntry->numdns+1<DNS_MAX_SERVERS) && (dns_servers[pEntry->numdns+1].addr!=0)) {
/* change of server */
pEntry->numdns++;
pEntry->tmr = 1;
pEntry->retries = 0;
break;
} else {
LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));
/* call specified callback function if provided */
if (pEntry->found)
(*pEntry->found)(pEntry->name, NULL, pEntry->arg);
/* flush this entry */
pEntry->state = DNS_STATE_UNUSED;
pEntry->found = NULL;
break;
}
}
/* wait longer for the next retry */
pEntry->tmr = pEntry->retries;
/* send DNS packet for this entry */
dns_send(pEntry->numdns, pEntry->name, i);
}
break;
}
case DNS_STATE_DONE: {
/* if the time to live is nul */
if (--pEntry->ttl == 0) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));
/* flush this entry */
pEntry->state = DNS_STATE_UNUSED;
pEntry->found = NULL;
}
break;
}
case DNS_STATE_UNUSED:
/* nothing to do */
break;
default:
LWIP_ASSERT("unknown dns_table entry state:", 0);
break;
}
}
/**
* Call dns_check_entry for each entry in dns_table - check all entries.
*/
static void
dns_check_entries(void)
{
u8_t i;
for (i = 0; i < DNS_TABLE_SIZE; ++i) {
dns_check_entry(i);
}
}
/**
* Receive input function for DNS response packets arriving for the dns UDP pcb.
*
* @params see udp.h
*/
static void
dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
{
u8_t i;
char *pHostname;
struct dns_hdr *hdr;
struct dns_answer ans;
struct dns_table_entry *pEntry;
u8_t nquestions, nanswers;
#if (DNS_USES_STATIC_BUF == 0)
u8_t dns_payload[DNS_MSG_SIZE];
#endif /* (DNS_USES_STATIC_BUF == 0) */
#if (DNS_USES_STATIC_BUF == 2)
u8_t* dns_payload;
#endif /* (DNS_USES_STATIC_BUF == 2) */
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(pcb);
LWIP_UNUSED_ARG(addr);
LWIP_UNUSED_ARG(port);
/* is the dns message too big ? */
if (p->tot_len > DNS_MSG_SIZE) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
/* free pbuf and return */
goto memerr1;
}
/* is the dns message big enough ? */
if (p->tot_len < (sizeof(struct dns_hdr) + sizeof(struct dns_query) + sizeof(struct dns_answer))) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
/* free pbuf and return */
goto memerr1;
}
#if (DNS_USES_STATIC_BUF == 2)
dns_payload = mem_malloc(p->tot_len);
if (dns_payload == NULL) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: mem_malloc error\n"));
/* free pbuf and return */
goto memerr1;
}
#endif /* (DNS_USES_STATIC_BUF == 2) */
/* copy dns payload inside static buffer for processing */
if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) {
/* The ID in the DNS header should be our entry into the name table. */
hdr = (struct dns_hdr*)dns_payload;
i = htons(hdr->id);
if (i < DNS_TABLE_SIZE) {
pEntry = &dns_table[i];
if(pEntry->state == DNS_STATE_ASKING) {
/* This entry is now completed. */
pEntry->state = DNS_STATE_DONE;
pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
/* We only care about the question(s) and the answers. The authrr
and the extrarr are simply discarded. */
nquestions = htons(hdr->numquestions);
nanswers = htons(hdr->numanswers);
/* Check for error. If so, call callback to inform. */
if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));
/* call callback to indicate error, clean up memory and return */
goto responseerr;
}
#if DNS_DOES_NAME_CHECK
/* Check if the name in the "question" part match with the name in the entry. */
if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + sizeof(struct dns_hdr)) != 0) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name));
/* call callback to indicate error, clean up memory and return */
goto responseerr;
}
#endif /* DNS_DOES_NAME_CHECK */
/* Skip the name in the "question" part */
pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + sizeof(struct dns_hdr)) + sizeof(struct dns_query);
while(nanswers > 0) {
/* skip answer resource record's host name */
pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
/* Check for IP address type and Internet class. Others are discarded. */
MEMCPY(&ans, pHostname, sizeof(struct dns_answer));
if((ntohs(ans.type) == DNS_RRTYPE_A) && (ntohs(ans.class) == DNS_RRCLASS_IN) && (ntohs(ans.len) == sizeof(struct ip_addr)) ) {
/* read the answer resource record's TTL, and maximize it if needed */
pEntry->ttl = ntohl(ans.ttl);
if (pEntry->ttl > DNS_MAX_TTL) {
pEntry->ttl = DNS_MAX_TTL;
}
/* read the IP address after answer resource record's header */
MEMCPY( &(pEntry->ipaddr), (pHostname+sizeof(struct dns_answer)), sizeof(struct ip_addr));
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));
ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
LWIP_DEBUGF(DNS_DEBUG, ("\n"));
/* call specified callback function if provided */
if (pEntry->found) {
(*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);
}
/* deallocate memory and return */
goto memerr2;
} else {
pHostname = pHostname + sizeof(struct dns_answer) + htons(ans.len);
}
--nanswers;
}
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
/* call callback to indicate error, clean up memory and return */
goto responseerr;
}
}
}
/* deallocate memory and return */
goto memerr2;
responseerr:
/* ERROR: call specified callback function with NULL as name to indicate an error */
if (pEntry->found) {
(*pEntry->found)(pEntry->name, NULL, pEntry->arg);
}
/* flush this entry */
pEntry->state = DNS_STATE_UNUSED;
pEntry->found = NULL;
memerr2:
#if (DNS_USES_STATIC_BUF == 2)
/* free dns buffer */
mem_free(dns_payload);
#endif /* (DNS_USES_STATIC_BUF == 2) */
memerr1:
/* free pbuf */
pbuf_free(p);
return;
}
/**
* Queues a new hostname to resolve and sends out a DNS query for that hostname
*
* @param name the hostname that is to be queried
* @param found a callback founction to be called on success, failure or timeout
* @param callback_arg argument to pass to the callback function
* @return @return a err_t return code.
*/
static err_t
dns_enqueue(const char *name, dns_found_callback found, void *callback_arg)
{
u8_t i;
u8_t lseq, lseqi;
struct dns_table_entry *pEntry = NULL;
/* search an unused entry, or the oldest one */
lseq = lseqi = 0;
for (i = 0; i < DNS_TABLE_SIZE; ++i) {
pEntry = &dns_table[i];
/* is it an unused entry ? */
if (pEntry->state == DNS_STATE_UNUSED)
break;
/* check if this is the oldest completed entry */
if (pEntry->state == DNS_STATE_DONE) {
if ((dns_seqno - pEntry->seqno) > lseq) {
lseq = dns_seqno - pEntry->seqno;
lseqi = i;
}
}
}
/* if we don't have found an unused entry, use the oldest completed one */
if (i == DNS_TABLE_SIZE) {
if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
/* no entry can't be used now, table is full */
LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
return ERR_MEM;
} else {
/* use the oldest completed one */
i = lseqi;
pEntry = &dns_table[i];
}
}
/* use this entry */
LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
/* fill the entry */
pEntry->state = DNS_STATE_NEW;
pEntry->seqno = dns_seqno++;
pEntry->found = found;
pEntry->arg = callback_arg;
strcpy(pEntry->name, name);
/* force to send query without waiting timer */
dns_check_entry(i);
/* dns query is enqueued */
return ERR_INPROGRESS;
}
/**
* Resolve a hostname (string) into an IP address.
* NON-BLOCKING callback version for use with raw API!!!
*
* Returns immediately with one of err_t return codes:
* - ERR_OK if hostname is a valid IP address string or the host
* name is already in the local names table.
* - ERR_INPROGRESS enqueue a request to be sent to the DNS server
* for resolution if no errors are present.
*
* @param hostname the hostname that is to be queried
* @param addr pointer to a struct ip_addr where to store the address if it is already
* cached in the dns_table (only valid if ERR_OK is returned!)
* @param found a callback function to be called on success, failure or timeout (only if
* ERR_INPROGRESS is returned!)
* @param callback_arg argument to pass to the callback function
* @return a err_t return code.
*/
err_t
dns_gethostbyname(const char *hostname, struct ip_addr *addr, dns_found_callback found,
void *callback_arg)
{
/* not initialized or no valid server yet, or invalid addr pointer
* or invalid hostname or invalid hostname length */
if ((dns_pcb == NULL) || (addr == NULL) ||
(!hostname) || (!hostname[0]) ||
(strlen(hostname) >= DNS_MAX_NAME_LENGTH)) {
return ERR_VAL;
}
#if LWIP_HAVE_LOOPIF
if (strcmp(hostname,"localhost")==0) {
addr->addr = INADDR_LOOPBACK;
return ERR_OK;
}
#endif /* LWIP_HAVE_LOOPIF */
/* host name already in octet notation? set ip addr and return ERR_OK
* already have this address cached? */
if (((addr->addr = inet_addr(hostname)) != INADDR_NONE) ||
((addr->addr = dns_lookup(hostname)) != 0)) {
return ERR_OK;
}
/* queue query with specified callback */
return dns_enqueue(hostname, found, callback_arg);
}
#endif /* LWIP_DNS */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -