A version of LWIP, provided for backwards compatibility.
Dependents: AA_DemoBoard DemoBoard HelloServerDemo DemoBoard_RangeIndicator ... more
dns.c
00001 /** 00002 * @file 00003 * DNS - host name to IP address resolver. 00004 * 00005 */ 00006 00007 /** 00008 00009 * This file implements a DNS host name to IP address resolver. 00010 00011 * Port to lwIP from uIP 00012 * by Jim Pettinato April 2007 00013 00014 * uIP version Copyright (c) 2002-2003, Adam Dunkels. 00015 * All rights reserved. 00016 * 00017 * Redistribution and use in source and binary forms, with or without 00018 * modification, are permitted provided that the following conditions 00019 * are met: 00020 * 1. Redistributions of source code must retain the above copyright 00021 * notice, this list of conditions and the following disclaimer. 00022 * 2. Redistributions in binary form must reproduce the above copyright 00023 * notice, this list of conditions and the following disclaimer in the 00024 * documentation and/or other materials provided with the distribution. 00025 * 3. The name of the author may not be used to endorse or promote 00026 * products derived from this software without specific prior 00027 * written permission. 00028 * 00029 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 00030 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00031 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00032 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 00033 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00034 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 00035 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00036 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 00037 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00038 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00039 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00040 * 00041 * 00042 * DNS.C 00043 * 00044 * The lwIP DNS resolver functions are used to lookup a host name and 00045 * map it to a numerical IP address. It maintains a list of resolved 00046 * hostnames that can be queried with the dns_lookup() function. 00047 * New hostnames can be resolved using the dns_query() function. 00048 * 00049 * The lwIP version of the resolver also adds a non-blocking version of 00050 * gethostbyname() that will work with a raw API application. This function 00051 * checks for an IP address string first and converts it if it is valid. 00052 * gethostbyname() then does a dns_lookup() to see if the name is 00053 * already in the table. If so, the IP is returned. If not, a query is 00054 * issued and the function returns with a ERR_INPROGRESS status. The app 00055 * using the dns client must then go into a waiting state. 00056 * 00057 * Once a hostname has been resolved (or found to be non-existent), 00058 * the resolver code calls a specified callback function (which 00059 * must be implemented by the module that uses the resolver). 00060 */ 00061 00062 /*----------------------------------------------------------------------------- 00063 * RFC 1035 - Domain names - implementation and specification 00064 * RFC 2181 - Clarifications to the DNS Specification 00065 *----------------------------------------------------------------------------*/ 00066 00067 /** @todo: define good default values (rfc compliance) */ 00068 /** @todo: improve answer parsing, more checkings... */ 00069 /** @todo: check RFC1035 - 7.3. Processing responses */ 00070 00071 /*----------------------------------------------------------------------------- 00072 * Includes 00073 *----------------------------------------------------------------------------*/ 00074 00075 #include "lwip/opt.h" 00076 00077 #if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ 00078 00079 #include "lwip/udp.h" 00080 #include "lwip/mem.h" 00081 #include "lwip/dns.h" 00082 00083 #include <string.h> 00084 00085 /** DNS server IP address */ 00086 #ifndef DNS_SERVER_ADDRESS 00087 #define DNS_SERVER_ADDRESS inet_addr("208.67.222.222") /* resolver1.opendns.com */ 00088 #endif 00089 00090 /** DNS server port address */ 00091 #ifndef DNS_SERVER_PORT 00092 #define DNS_SERVER_PORT 53 00093 #endif 00094 00095 /** DNS maximum number of retries when asking for a name, before "timeout". */ 00096 #ifndef DNS_MAX_RETRIES 00097 #define DNS_MAX_RETRIES 4 00098 #endif 00099 00100 /** DNS resource record max. TTL (one week as default) */ 00101 #ifndef DNS_MAX_TTL 00102 #define DNS_MAX_TTL 604800 00103 #endif 00104 00105 /* DNS protocol flags */ 00106 #define DNS_FLAG1_RESPONSE 0x80 00107 #define DNS_FLAG1_OPCODE_STATUS 0x10 00108 #define DNS_FLAG1_OPCODE_INVERSE 0x08 00109 #define DNS_FLAG1_OPCODE_STANDARD 0x00 00110 #define DNS_FLAG1_AUTHORATIVE 0x04 00111 #define DNS_FLAG1_TRUNC 0x02 00112 #define DNS_FLAG1_RD 0x01 00113 #define DNS_FLAG2_RA 0x80 00114 #define DNS_FLAG2_ERR_MASK 0x0f 00115 #define DNS_FLAG2_ERR_NONE 0x00 00116 #define DNS_FLAG2_ERR_NAME 0x03 00117 00118 /* DNS protocol states */ 00119 #define DNS_STATE_UNUSED 0 00120 #define DNS_STATE_NEW 1 00121 #define DNS_STATE_ASKING 2 00122 #define DNS_STATE_DONE 3 00123 00124 00125 // XXX: For C++ compatibility 00126 #define class mclass 00127 00128 #ifdef PACK_STRUCT_USE_INCLUDES 00129 # include "arch/bpstruct.h" 00130 #endif 00131 PACK_STRUCT_BEGIN 00132 /** DNS message header */ 00133 struct dns_hdr { 00134 u16_t id; 00135 u8_t flags1; 00136 u8_t flags2; 00137 u16_t numquestions; 00138 u16_t numanswers; 00139 u16_t numauthrr; 00140 u16_t numextrarr; 00141 } PACK_STRUCT_STRUCT; 00142 PACK_STRUCT_END 00143 #ifdef PACK_STRUCT_USE_INCLUDES 00144 # include "arch/epstruct.h" 00145 #endif 00146 00147 #ifdef PACK_STRUCT_USE_INCLUDES 00148 # include "arch/bpstruct.h" 00149 #endif 00150 PACK_STRUCT_BEGIN 00151 /** DNS query message structure */ 00152 struct dns_query { 00153 /* DNS query record starts with either a domain name or a pointer 00154 to a name already present somewhere in the packet. */ 00155 u16_t type; 00156 u16_t class; 00157 } PACK_STRUCT_STRUCT; 00158 PACK_STRUCT_END 00159 #ifdef PACK_STRUCT_USE_INCLUDES 00160 # include "arch/epstruct.h" 00161 #endif 00162 00163 #ifdef PACK_STRUCT_USE_INCLUDES 00164 # include "arch/bpstruct.h" 00165 #endif 00166 PACK_STRUCT_BEGIN 00167 /** DNS answer message structure */ 00168 struct dns_answer { 00169 /* DNS answer record starts with either a domain name or a pointer 00170 to a name already present somewhere in the packet. */ 00171 u16_t type; 00172 u16_t class; 00173 u32_t ttl; 00174 u16_t len; 00175 } PACK_STRUCT_STRUCT; 00176 PACK_STRUCT_END 00177 #ifdef PACK_STRUCT_USE_INCLUDES 00178 # include "arch/epstruct.h" 00179 #endif 00180 00181 /** DNS table entry */ 00182 struct dns_table_entry { 00183 u8_t state; 00184 u8_t numdns; 00185 u8_t tmr; 00186 u8_t retries; 00187 u8_t seqno; 00188 u8_t err; 00189 u32_t ttl; 00190 char name[DNS_MAX_NAME_LENGTH]; 00191 struct ip_addr ipaddr; 00192 /* pointer to callback on DNS query done */ 00193 dns_found_callback found; 00194 void *arg; 00195 }; 00196 00197 00198 /* forward declarations */ 00199 static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); 00200 static void dns_check_entries(void); 00201 00202 /*----------------------------------------------------------------------------- 00203 * Globales 00204 *----------------------------------------------------------------------------*/ 00205 00206 /* DNS variables */ 00207 static struct udp_pcb *dns_pcb; 00208 static u8_t dns_seqno; 00209 static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; 00210 static struct ip_addr dns_servers[DNS_MAX_SERVERS]; 00211 00212 #if (DNS_USES_STATIC_BUF == 1) 00213 static u8_t dns_payload[DNS_MSG_SIZE]; 00214 #endif /* (DNS_USES_STATIC_BUF == 1) */ 00215 00216 /** 00217 * Initialize the resolver: set up the UDP pcb and configure the default server 00218 * (DNS_SERVER_ADDRESS). 00219 */ 00220 void 00221 dns_init() 00222 { 00223 struct ip_addr dnsserver; 00224 00225 /* initialize default DNS server address */ 00226 dnsserver.addr = DNS_SERVER_ADDRESS; 00227 00228 LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); 00229 00230 /* if dns client not yet initialized... */ 00231 if (dns_pcb == NULL) { 00232 dns_pcb = udp_new(); 00233 00234 if (dns_pcb != NULL) { 00235 /* initialize DNS table not needed (initialized to zero since it is a 00236 * global variable) */ 00237 LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", 00238 DNS_STATE_UNUSED == 0); 00239 00240 /* initialize DNS client */ 00241 udp_bind(dns_pcb, IP_ADDR_ANY, 0); 00242 udp_recv(dns_pcb, dns_recv, NULL); 00243 00244 /* initialize default DNS primary server */ 00245 dns_setserver(0, &dnsserver); 00246 } 00247 } 00248 } 00249 00250 /** 00251 * Initialize one of the DNS servers. 00252 * 00253 * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS 00254 * @param dnsserver IP address of the DNS server to set 00255 */ 00256 void 00257 dns_setserver(u8_t numdns, struct ip_addr *dnsserver) 00258 { 00259 if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) && 00260 (dnsserver != NULL) && (dnsserver->addr !=0 )) { 00261 dns_servers[numdns] = (*dnsserver); 00262 } 00263 } 00264 00265 /** 00266 * Obtain one of the currently configured DNS server. 00267 * 00268 * @param numdns the index of the DNS server 00269 * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS 00270 * server has not been configured. 00271 */ 00272 struct ip_addr 00273 dns_getserver(u8_t numdns) 00274 { 00275 if (numdns < DNS_MAX_SERVERS) { 00276 return dns_servers[numdns]; 00277 } else { 00278 return *IP_ADDR_ANY; 00279 } 00280 } 00281 00282 /** 00283 * The DNS resolver client timer - handle retries and timeouts and should 00284 * be called every DNS_TMR_INTERVAL milliseconds (every second by default). 00285 */ 00286 void 00287 dns_tmr(void) 00288 { 00289 if (dns_pcb != NULL) { 00290 LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); 00291 dns_check_entries(); 00292 } 00293 } 00294 00295 /** 00296 * Look up a hostname in the array of known hostnames. 00297 * 00298 * @note This function only looks in the internal array of known 00299 * hostnames, it does not send out a query for the hostname if none 00300 * was found. The function dns_enqueue() can be used to send a query 00301 * for a hostname. 00302 * 00303 * @param name the hostname to look up 00304 * @return the hostname's IP address, as u32_t (instead of struct ip_addr to 00305 * better check for failure: != 0) or 0 if the hostname was not found 00306 * in the cached dns_table. 00307 */ 00308 static u32_t 00309 dns_lookup(const char *name) 00310 { 00311 u8_t i; 00312 00313 /* Walk through name list, return entry if found. If not, return NULL. */ 00314 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 00315 if ((dns_table[i].state == DNS_STATE_DONE) && 00316 (strcmp(name, dns_table[i].name) == 0)) { 00317 LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); 00318 ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); 00319 LWIP_DEBUGF(DNS_DEBUG, ("\n")); 00320 return dns_table[i].ipaddr.addr; 00321 } 00322 } 00323 00324 return 0; 00325 } 00326 00327 #if DNS_DOES_NAME_CHECK 00328 /** 00329 * Compare the "dotted" name "query" with the encoded name "response" 00330 * to make sure an answer from the DNS server matches the current dns_table 00331 * entry (otherwise, answers might arrive late for hostname not on the list 00332 * any more). 00333 * 00334 * @param query hostname (not encoded) from the dns_table 00335 * @param response encoded hostname in the DNS response 00336 * @return 0: names equal; 1: names differ 00337 */ 00338 static u8_t 00339 dns_compare_name(unsigned char *query, unsigned char *response) 00340 { 00341 unsigned char n; 00342 00343 do { 00344 n = *response++; 00345 /** @see RFC 1035 - 4.1.4. Message compression */ 00346 if ((n & 0xc0) == 0xc0) { 00347 /* Compressed name */ 00348 break; 00349 } else { 00350 /* Not compressed name */ 00351 while (n > 0) { 00352 if ((*query) != (*response)) { 00353 return 1; 00354 } 00355 ++response; 00356 ++query; 00357 --n; 00358 }; 00359 ++query; 00360 } 00361 } while (*response != 0); 00362 00363 return 0; 00364 } 00365 #endif /* DNS_DOES_NAME_CHECK */ 00366 00367 /** 00368 * Walk through a compact encoded DNS name and return the end of the name. 00369 * 00370 * @param query encoded DNS name in the DNS server response 00371 * @return end of the name 00372 */ 00373 static unsigned char * 00374 dns_parse_name(unsigned char *query) 00375 { 00376 unsigned char n; 00377 00378 do { 00379 n = *query++; 00380 /** @see RFC 1035 - 4.1.4. Message compression */ 00381 if ((n & 0xc0) == 0xc0) { 00382 /* Compressed name */ 00383 break; 00384 } else { 00385 /* Not compressed name */ 00386 while (n > 0) { 00387 ++query; 00388 --n; 00389 }; 00390 } 00391 } while (*query != 0); 00392 00393 return query + 1; 00394 } 00395 00396 /** 00397 * Send a DNS query packet. 00398 * 00399 * @param numdns index of the DNS server in the dns_servers table 00400 * @param name hostname to query 00401 * @param id index of the hostname in dns_table, used as transaction ID in the 00402 * DNS query packet 00403 * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise 00404 */ 00405 static err_t 00406 dns_send(u8_t numdns, const char* name, u8_t id) 00407 { 00408 err_t err; 00409 struct dns_hdr *hdr; 00410 struct dns_query qry; 00411 struct pbuf *p; 00412 char *query, *nptr; 00413 const char *pHostname; 00414 u8_t n; 00415 00416 LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", 00417 (u16_t)(numdns), name)); 00418 LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS); 00419 LWIP_ASSERT("dns server has no IP address set", dns_servers[numdns].addr != 0); 00420 00421 /* if here, we have either a new query or a retry on a previous query to process */ 00422 p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dns_hdr) + DNS_MAX_NAME_LENGTH + 00423 sizeof(struct dns_query), PBUF_RAM); 00424 if (p != NULL) { 00425 LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); 00426 /* fill dns header */ 00427 hdr = (struct dns_hdr*)p->payload; 00428 memset(hdr, 0, sizeof(struct dns_hdr)); 00429 hdr->id = htons(id); 00430 hdr->flags1 = DNS_FLAG1_RD; 00431 hdr->numquestions = htons(1); 00432 query = (char*)hdr + sizeof(struct dns_hdr); 00433 pHostname = name; 00434 --pHostname; 00435 00436 /* convert hostname into suitable query format. */ 00437 do { 00438 ++pHostname; 00439 nptr = query; 00440 ++query; 00441 for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { 00442 *query = *pHostname; 00443 ++query; 00444 ++n; 00445 } 00446 *nptr = n; 00447 } while(*pHostname != 0); 00448 *query++='\0'; 00449 00450 /* fill dns query */ 00451 qry.type = htons(DNS_RRTYPE_A); 00452 qry.class = htons(DNS_RRCLASS_IN); 00453 MEMCPY( query, &qry, sizeof(struct dns_query)); 00454 00455 /* resize pbuf to the exact dns query */ 00456 pbuf_realloc(p, (query + sizeof(struct dns_query)) - ((char*)(p->payload))); 00457 00458 /* connect to the server for faster receiving */ 00459 udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT); 00460 /* send dns packet */ 00461 err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT); 00462 00463 /* free pbuf */ 00464 pbuf_free(p); 00465 } else { 00466 err = ERR_MEM; 00467 } 00468 00469 return err; 00470 } 00471 00472 /** 00473 * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query. 00474 * Check an entry in the dns_table: 00475 * - send out query for new entries 00476 * - retry old pending entries on timeout (also with different servers) 00477 * - remove completed entries from the table if their TTL has expired 00478 * 00479 * @param i index of the dns_table entry to check 00480 */ 00481 static void 00482 dns_check_entry(u8_t i) 00483 { 00484 struct dns_table_entry *pEntry = &dns_table[i]; 00485 00486 LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); 00487 00488 switch(pEntry->state) { 00489 00490 case DNS_STATE_NEW: { 00491 /* initialize new entry */ 00492 pEntry->state = DNS_STATE_ASKING; 00493 pEntry->numdns = 0; 00494 pEntry->tmr = 1; 00495 pEntry->retries = 0; 00496 00497 /* send DNS packet for this entry */ 00498 dns_send(pEntry->numdns, pEntry->name, i); 00499 break; 00500 } 00501 00502 case DNS_STATE_ASKING: { 00503 if (--pEntry->tmr == 0) { 00504 if (++pEntry->retries == DNS_MAX_RETRIES) { 00505 if ((pEntry->numdns+1<DNS_MAX_SERVERS) && (dns_servers[pEntry->numdns+1].addr!=0)) { 00506 /* change of server */ 00507 pEntry->numdns++; 00508 pEntry->tmr = 1; 00509 pEntry->retries = 0; 00510 break; 00511 } else { 00512 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name)); 00513 /* call specified callback function if provided */ 00514 if (pEntry->found) 00515 (*pEntry->found)(pEntry->name, NULL, pEntry->arg); 00516 /* flush this entry */ 00517 pEntry->state = DNS_STATE_UNUSED; 00518 pEntry->found = NULL; 00519 break; 00520 } 00521 } 00522 00523 /* wait longer for the next retry */ 00524 pEntry->tmr = pEntry->retries; 00525 00526 /* send DNS packet for this entry */ 00527 dns_send(pEntry->numdns, pEntry->name, i); 00528 } 00529 break; 00530 } 00531 00532 case DNS_STATE_DONE: { 00533 /* if the time to live is nul */ 00534 if (--pEntry->ttl == 0) { 00535 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name)); 00536 /* flush this entry */ 00537 pEntry->state = DNS_STATE_UNUSED; 00538 pEntry->found = NULL; 00539 } 00540 break; 00541 } 00542 case DNS_STATE_UNUSED: 00543 /* nothing to do */ 00544 break; 00545 default: 00546 LWIP_ASSERT("unknown dns_table entry state:", 0); 00547 break; 00548 } 00549 } 00550 00551 /** 00552 * Call dns_check_entry for each entry in dns_table - check all entries. 00553 */ 00554 static void 00555 dns_check_entries(void) 00556 { 00557 u8_t i; 00558 00559 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 00560 dns_check_entry(i); 00561 } 00562 } 00563 00564 /** 00565 * Receive input function for DNS response packets arriving for the dns UDP pcb. 00566 * 00567 * @params see udp.h 00568 */ 00569 static void 00570 dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) 00571 { 00572 u8_t i; 00573 char *pHostname; 00574 struct dns_hdr *hdr; 00575 struct dns_answer ans; 00576 struct dns_table_entry *pEntry; 00577 u8_t nquestions, nanswers; 00578 #if (DNS_USES_STATIC_BUF == 0) 00579 u8_t dns_payload[DNS_MSG_SIZE]; 00580 #endif /* (DNS_USES_STATIC_BUF == 0) */ 00581 #if (DNS_USES_STATIC_BUF == 2) 00582 u8_t* dns_payload; 00583 #endif /* (DNS_USES_STATIC_BUF == 2) */ 00584 00585 LWIP_UNUSED_ARG(arg); 00586 LWIP_UNUSED_ARG(pcb); 00587 LWIP_UNUSED_ARG(addr); 00588 LWIP_UNUSED_ARG(port); 00589 00590 /* is the dns message too big ? */ 00591 if (p->tot_len > DNS_MSG_SIZE) { 00592 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n")); 00593 /* free pbuf and return */ 00594 goto memerr1; 00595 } 00596 00597 /* is the dns message big enough ? */ 00598 if (p->tot_len < (sizeof(struct dns_hdr) + sizeof(struct dns_query) + sizeof(struct dns_answer))) { 00599 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n")); 00600 /* free pbuf and return */ 00601 goto memerr1; 00602 } 00603 00604 #if (DNS_USES_STATIC_BUF == 2) 00605 dns_payload = mem_malloc(p->tot_len); 00606 if (dns_payload == NULL) { 00607 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: mem_malloc error\n")); 00608 /* free pbuf and return */ 00609 goto memerr1; 00610 } 00611 #endif /* (DNS_USES_STATIC_BUF == 2) */ 00612 00613 /* copy dns payload inside static buffer for processing */ 00614 if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) { 00615 /* The ID in the DNS header should be our entry into the name table. */ 00616 hdr = (struct dns_hdr*)dns_payload; 00617 i = htons(hdr->id); 00618 if (i < DNS_TABLE_SIZE) { 00619 pEntry = &dns_table[i]; 00620 if(pEntry->state == DNS_STATE_ASKING) { 00621 /* This entry is now completed. */ 00622 pEntry->state = DNS_STATE_DONE; 00623 pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; 00624 00625 /* We only care about the question(s) and the answers. The authrr 00626 and the extrarr are simply discarded. */ 00627 nquestions = htons(hdr->numquestions); 00628 nanswers = htons(hdr->numanswers); 00629 00630 /* Check for error. If so, call callback to inform. */ 00631 if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) { 00632 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name)); 00633 /* call callback to indicate error, clean up memory and return */ 00634 goto responseerr; 00635 } 00636 00637 #if DNS_DOES_NAME_CHECK 00638 /* Check if the name in the "question" part match with the name in the entry. */ 00639 if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + sizeof(struct dns_hdr)) != 0) { 00640 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name)); 00641 /* call callback to indicate error, clean up memory and return */ 00642 goto responseerr; 00643 } 00644 #endif /* DNS_DOES_NAME_CHECK */ 00645 00646 /* Skip the name in the "question" part */ 00647 pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + sizeof(struct dns_hdr)) + sizeof(struct dns_query); 00648 00649 while(nanswers > 0) { 00650 /* skip answer resource record's host name */ 00651 pHostname = (char *) dns_parse_name((unsigned char *)pHostname); 00652 00653 /* Check for IP address type and Internet class. Others are discarded. */ 00654 MEMCPY(&ans, pHostname, sizeof(struct dns_answer)); 00655 if((ntohs(ans.type) == DNS_RRTYPE_A) && (ntohs(ans.class) == DNS_RRCLASS_IN) && (ntohs(ans.len) == sizeof(struct ip_addr)) ) { 00656 /* read the answer resource record's TTL, and maximize it if needed */ 00657 pEntry->ttl = ntohl(ans.ttl); 00658 if (pEntry->ttl > DNS_MAX_TTL) { 00659 pEntry->ttl = DNS_MAX_TTL; 00660 } 00661 /* read the IP address after answer resource record's header */ 00662 MEMCPY( &(pEntry->ipaddr), (pHostname+sizeof(struct dns_answer)), sizeof(struct ip_addr)); 00663 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name)); 00664 ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr))); 00665 LWIP_DEBUGF(DNS_DEBUG, ("\n")); 00666 /* call specified callback function if provided */ 00667 if (pEntry->found) { 00668 (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg); 00669 } 00670 /* deallocate memory and return */ 00671 goto memerr2; 00672 } else { 00673 pHostname = pHostname + sizeof(struct dns_answer) + htons(ans.len); 00674 } 00675 --nanswers; 00676 } 00677 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name)); 00678 /* call callback to indicate error, clean up memory and return */ 00679 goto responseerr; 00680 } 00681 } 00682 } 00683 00684 /* deallocate memory and return */ 00685 goto memerr2; 00686 00687 responseerr: 00688 /* ERROR: call specified callback function with NULL as name to indicate an error */ 00689 if (pEntry->found) { 00690 (*pEntry->found)(pEntry->name, NULL, pEntry->arg); 00691 } 00692 /* flush this entry */ 00693 pEntry->state = DNS_STATE_UNUSED; 00694 pEntry->found = NULL; 00695 00696 memerr2: 00697 #if (DNS_USES_STATIC_BUF == 2) 00698 /* free dns buffer */ 00699 mem_free(dns_payload); 00700 #endif /* (DNS_USES_STATIC_BUF == 2) */ 00701 00702 memerr1: 00703 /* free pbuf */ 00704 pbuf_free(p); 00705 return; 00706 } 00707 00708 /** 00709 * Queues a new hostname to resolve and sends out a DNS query for that hostname 00710 * 00711 * @param name the hostname that is to be queried 00712 * @param found a callback founction to be called on success, failure or timeout 00713 * @param callback_arg argument to pass to the callback function 00714 * @return @return a err_t return code. 00715 */ 00716 static err_t 00717 dns_enqueue(const char *name, dns_found_callback found, void *callback_arg) 00718 { 00719 u8_t i; 00720 u8_t lseq, lseqi; 00721 struct dns_table_entry *pEntry = NULL; 00722 00723 /* search an unused entry, or the oldest one */ 00724 lseq = lseqi = 0; 00725 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 00726 pEntry = &dns_table[i]; 00727 /* is it an unused entry ? */ 00728 if (pEntry->state == DNS_STATE_UNUSED) 00729 break; 00730 00731 /* check if this is the oldest completed entry */ 00732 if (pEntry->state == DNS_STATE_DONE) { 00733 if ((dns_seqno - pEntry->seqno) > lseq) { 00734 lseq = dns_seqno - pEntry->seqno; 00735 lseqi = i; 00736 } 00737 } 00738 } 00739 00740 /* if we don't have found an unused entry, use the oldest completed one */ 00741 if (i == DNS_TABLE_SIZE) { 00742 if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { 00743 /* no entry can't be used now, table is full */ 00744 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); 00745 return ERR_MEM; 00746 } else { 00747 /* use the oldest completed one */ 00748 i = lseqi; 00749 pEntry = &dns_table[i]; 00750 } 00751 } 00752 00753 /* use this entry */ 00754 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); 00755 00756 /* fill the entry */ 00757 pEntry->state = DNS_STATE_NEW; 00758 pEntry->seqno = dns_seqno++; 00759 pEntry->found = found; 00760 pEntry->arg = callback_arg; 00761 strcpy(pEntry->name, name); 00762 00763 /* force to send query without waiting timer */ 00764 dns_check_entry(i); 00765 00766 /* dns query is enqueued */ 00767 return ERR_INPROGRESS; 00768 } 00769 00770 /** 00771 * Resolve a hostname (string) into an IP address. 00772 * NON-BLOCKING callback version for use with raw API!!! 00773 * 00774 * Returns immediately with one of err_t return codes: 00775 * - ERR_OK if hostname is a valid IP address string or the host 00776 * name is already in the local names table. 00777 * - ERR_INPROGRESS enqueue a request to be sent to the DNS server 00778 * for resolution if no errors are present. 00779 * 00780 * @param hostname the hostname that is to be queried 00781 * @param addr pointer to a struct ip_addr where to store the address if it is already 00782 * cached in the dns_table (only valid if ERR_OK is returned!) 00783 * @param found a callback function to be called on success, failure or timeout (only if 00784 * ERR_INPROGRESS is returned!) 00785 * @param callback_arg argument to pass to the callback function 00786 * @return a err_t return code. 00787 */ 00788 err_t 00789 dns_gethostbyname(const char *hostname, struct ip_addr *addr, dns_found_callback found, 00790 void *callback_arg) 00791 { 00792 /* not initialized or no valid server yet, or invalid addr pointer 00793 * or invalid hostname or invalid hostname length */ 00794 if ((dns_pcb == NULL) || (addr == NULL) || 00795 (!hostname) || (!hostname[0]) || 00796 (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) { 00797 return ERR_VAL; 00798 } 00799 00800 #if LWIP_HAVE_LOOPIF 00801 if (strcmp(hostname,"localhost")==0) { 00802 addr->addr = INADDR_LOOPBACK; 00803 return ERR_OK; 00804 } 00805 #endif /* LWIP_HAVE_LOOPIF */ 00806 00807 /* host name already in octet notation? set ip addr and return ERR_OK 00808 * already have this address cached? */ 00809 if (((addr->addr = inet_addr(hostname)) != INADDR_NONE) || 00810 ((addr->addr = dns_lookup(hostname)) != 0)) { 00811 return ERR_OK; 00812 } 00813 00814 /* queue query with specified callback */ 00815 return dns_enqueue(hostname, found, callback_arg); 00816 } 00817 00818 #endif /* LWIP_DNS */
Generated on Tue Jul 12 2022 16:06:05 by 1.7.2