?? dns.c
字號(hào):
* to make sure an answer from the DNS server matches the current dns_table * entry (otherwise, answers might arrive late for hostname not on the list * any more). * * @param query hostname (not encoded) from the dns_table * @param response encoded hostname in the DNS response * @return 0: names equal; 1: names differ */static u8_tdns_compare_name(unsigned char *query, unsigned char *response){ unsigned char n; do { n = *response++; /** @see RFC 1035 - 4.1.4. Message compression */ if ((n & 0xc0) == 0xc0) { /* Compressed name */ break; } else { /* Not compressed name */ while (n > 0) { if ((*query) != (*response)) { return 1; } ++response; ++query; --n; }; ++query; } } while (*response != 0); return 0;}#endif /* DNS_DOES_NAME_CHECK *//** * Walk through a compact encoded DNS name and return the end of the name. * * @param query encoded DNS name in the DNS server response * @return end of the name */static unsigned char *dns_parse_name(unsigned char *query){ unsigned char n; do { n = *query++; if ((n & 0xc0) == 0xc0) { /* Compressed name */ break; } else { /* Not compressed name */ while (n > 0) { ++query; --n; }; } } while (*query != 0); return query + 1;}/** * Send a DNS query packet. * * @param numdns index of the DNS server in the dns_servers table * @param name hostname to query * @param id index of the hostname in dns_table, used as transaction ID in the * DNS query packet * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise */static err_tdns_send(u8_t numdns, const char* name, u8_t id){ err_t err; struct dns_hdr *hdr; struct dns_query qry; struct pbuf *p; char *query, *nptr; const char *pHostname; u8_t n; LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\r\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_DNS_HDR + DNS_MAX_NAME_LENGTH + SIZEOF_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_DNS_HDR); hdr->id = htons(id); hdr->flags1 = DNS_FLAG1_RD; hdr->numquestions = htons(1); query = (char*)hdr + SIZEOF_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_DNS_QUERY); /* resize pbuf to the exact dns query */ pbuf_realloc(p, (query + SIZEOF_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 voiddns_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\r\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\r\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 voiddns_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 voiddns_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\r\n")); /* free pbuf and return */ goto memerr1; } /* is the dns message big enough ? */ if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) { LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\r\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\r\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\r\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_DNS_HDR) != 0) { LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\r\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_DNS_HDR) + SIZEOF_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_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_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, ("\r\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_DNS_ANSWER + htons(ans.len); } --nanswers; } LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\r\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_tdns_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\r\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"\r\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_tdns_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 = htonl(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)) != INADDR_NONE)) { return ERR_OK; } /* queue query with specified callback */ return dns_enqueue(hostname, found, callback_arg);}#endif /* LWIP_DNS */
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -