📄 dns.c
字号:
}
return 0;
}
/**
* dns_parse_name() - walk through a compact encoded DNS name and return the end
* of the name.
*/
static unsigned char *
dns_parse_name(unsigned char *query)
{
unsigned char n;
do {
n = *query++;
while(n > 0) {
++query;
--n;
};
} while(*query != 0);
return query + 1;
}
/**
* dns_send
*/
static err_t
dns_send( char* name, u8_t id)
{
struct dns_hdr *hdr;
struct pbuf *p;
char *query, *nptr, *pHostname;
u8_t n;
LWIP_DEBUGF(DNS_DEBUG, ("dns_send: \"%s\": request\n", name));
/* 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(dns_endquery), PBUF_RAM);
if (p) {
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);
memcpy( query, dns_endquery, sizeof(dns_endquery));
// resize pbuf to the exact dns query
pbuf_realloc(p, (query+sizeof(dns_endquery))-((char*)(p->payload)));
// send dns packet
udp_send(dns_pcb, p);
// free pbuf
pbuf_free(p);
return ERR_OK;
}
return ERR_BUF;
}
/**
* dns_check_entries() - Runs through the list of names to see if there are any
* that have not yet been queried and, if so, sends out a query.
*/
static void
dns_check_entries(void)
{
u8_t i;
struct dns_table_entry *pEntry;
for (i = 0; i < DNS_TABLE_SIZE; ++i) {
pEntry = &dns_table[i];
switch(pEntry->state) {
case DNS_STATE_NEW:
case DNS_STATE_ASKING: {
if (pEntry->state == DNS_STATE_ASKING) {
if (--pEntry->tmr == 0) {
if (++pEntry->retries == DNS_MAX_RETRIES) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entries: \"%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;
continue;
}
/* wait longer for the next retry */
pEntry->tmr = pEntry->retries;
} else {
/* Its timer has not run out, so we move on to next entry. */
continue;
}
} else {
pEntry->state = DNS_STATE_ASKING;
pEntry->tmr = 1;
pEntry->retries = 0;
}
/* send DNS packet for this entry */
dns_send(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_entries: \"%s\": flush\n", pEntry->name));
/* flush this entry */
pEntry->state = DNS_STATE_UNUSED;
pEntry->found = NULL;
}
break;
}
}
}
}
/**
* Callback for DNS responses
*/
static void
dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
{
u8_t i;
char *pHostname;
struct dns_answer *ans;
struct dns_hdr *hdr;
struct dns_table_entry *pEntry;
u8_t nquestions, nanswers;
hdr = (struct dns_hdr *)p->payload;
/* The ID in the DNS header should be our entry into the name table. */
i = htons(hdr->id);
pEntry = &dns_table[i];
if( (i < DNS_TABLE_SIZE) && (pEntry->state == DNS_STATE_ASKING) ) {
/* This entry is now completed. */
pEntry->state = DNS_STATE_DONE;
pEntry->ttl = DNS_TTL_ENTRY;
pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
/* Check for error. If so, call callback to inform. */
if(pEntry->err != 0) {
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\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;
return;
}
/* 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);
/* Skip the name in the question. XXX: This should really be checked
agains the name in the question, to be sure that they match. */
pHostname = (char *) dns_parse_name((unsigned char *)p->payload + sizeof(struct dns_hdr)) + 4/*type(2)+class(2)*/;
while(nanswers > 0) {
/* The first byte in the answer resource record determines if it
is a compressed record or a normal one. */
if(*pHostname & 0xc0) {
/* Compressed name. */
pHostname +=2;
/* printf("Compressed anwser\n");*/
} else {
/* Not compressed name. */
pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
}
ans = (struct dns_answer *)pHostname;
/* printf("Answer: type %x, class %x, ttl %x, length %x\n",
htons(ans->type), htons(ans->class), (htons(ans->ttl[0])
<< 16) | htons(ans->ttl[1]), htons(ans->len));*/
/* Check for IP address type and Internet class. Others are discarded. */
if((ntohs(ans->type) == DNS_RRTYPE_A) && (ntohs(ans->class) == DNS_RRCLASS_IN) && (ntohs(ans->len) == 4/*IPv4 address*/) ) {
/* TODO: we should really check that this IP address is the one we want. */
pEntry->ipaddr = ans->ipaddr;
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);
return;
} else {
pHostname = pHostname + 10 + htons(ans->len);
}
--nanswers;
}
LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
}
}
/**
* Queues a name so that a question for the name will be sent out.
* param name - The hostname that is to be queried.
*/
static void
dns_query(char *name, void (*found)(char *name, struct ip_addr *addr, void *arg), void *arg)
{
u8_t i;
u8_t lseq, lseqi;
struct dns_table_entry *pEntry;
/* 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 entry used */
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 one */
if (i == DNS_TABLE_SIZE) {
i = lseqi;
pEntry = &dns_table[i];
/* since we replace the previous entry, we "unblock" the caller */
LWIP_DEBUGF(DNS_DEBUG, ("dns_query: \"%s\": replaced by new entry\n", pEntry->name));
/* call specified callback function if provided */
if (pEntry->found)
(*pEntry->found)(pEntry->name, NULL, pEntry->arg);
}
/* fill the entry */
strcpy(pEntry->name, name);
pEntry->found = found;
pEntry->arg = arg;
pEntry->state = DNS_STATE_NEW;
pEntry->seqno = dns_seqno++;
}
/**
* NON-BLOCKING callback version for use with raw API
*/
DNS_RESULT dns_gethostbyname(char *hostname, struct ip_addr *addr,
void (*found)(char *name, struct ip_addr *ipaddr, void *arg),
void *arg
)
{
/* not initialized or no valid server yet */
if (dns_pcb == NULL)
return DNS_QUERY_INVALID;
/* invalid hostname */
if ((!hostname) || (!hostname[0]))
return DNS_QUERY_INVALID;
/* invalid hostname length */
if (strlen(hostname) >= DNS_MAX_NAME_LENGTH)
return DNS_QUERY_INVALID;
/* host name already in octet notation? set ip addr and return COMPLETE */
if ((addr->addr = inet_addr(hostname)) != INADDR_NONE)
return DNS_COMPLETE;
/* already have this address cached? */
if ((addr->addr = dns_lookup(hostname)) != 0)
return DNS_COMPLETE;
/* queue query with specified callback */
dns_query(hostname, found, arg);
/* force to send request */
dns_check_entries();
return DNS_QUERY_QUEUED;
}
#endif /* LWIP_DNS */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -