Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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 #define SIZEOF_DNS_HDR 12 00147 00148 #ifdef PACK_STRUCT_USE_INCLUDES 00149 # include "arch/bpstruct.h" 00150 #endif 00151 PACK_STRUCT_BEGIN 00152 /** DNS query message structure */ 00153 struct dns_query { 00154 /* DNS query record starts with either a domain name or a pointer 00155 to a name already present somewhere in the packet. */ 00156 u16_t type; 00157 u16_t class; 00158 } PACK_STRUCT_STRUCT; 00159 PACK_STRUCT_END 00160 #ifdef PACK_STRUCT_USE_INCLUDES 00161 # include "arch/epstruct.h" 00162 #endif 00163 #define SIZEOF_DNS_QUERY 4 00164 00165 #ifdef PACK_STRUCT_USE_INCLUDES 00166 # include "arch/bpstruct.h" 00167 #endif 00168 PACK_STRUCT_BEGIN 00169 /** DNS answer message structure */ 00170 struct dns_answer { 00171 /* DNS answer record starts with either a domain name or a pointer 00172 to a name already present somewhere in the packet. */ 00173 u16_t type; 00174 u16_t class; 00175 u32_t ttl; 00176 u16_t len; 00177 } PACK_STRUCT_STRUCT; 00178 PACK_STRUCT_END 00179 #ifdef PACK_STRUCT_USE_INCLUDES 00180 # include "arch/epstruct.h" 00181 #endif 00182 #define SIZEOF_DNS_ANSWER 10 00183 00184 /** DNS table entry */ 00185 struct dns_table_entry { 00186 u8_t state; 00187 u8_t numdns; 00188 u8_t tmr; 00189 u8_t retries; 00190 u8_t seqno; 00191 u8_t err; 00192 u32_t ttl; 00193 char name[DNS_MAX_NAME_LENGTH]; 00194 struct ip_addr ipaddr; 00195 /* pointer to callback on DNS query done */ 00196 dns_found_callback found; 00197 void *arg; 00198 }; 00199 00200 #if DNS_LOCAL_HOSTLIST 00201 /** struct used for local host-list */ 00202 struct local_hostlist_entry { 00203 /** static hostname */ 00204 const char *name; 00205 /** static host address in network byteorder */ 00206 u32_t addr; 00207 struct local_hostlist_entry *next; 00208 }; 00209 00210 #if !DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00211 static struct local_hostlist_entry local_hostlist_static[] = DNS_LOCAL_HOSTLIST_INIT MEM_POSITION; 00212 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00213 00214 /** Local host-list. For hostnames in this list, no 00215 * external name resolution is performed */ 00216 static struct local_hostlist_entry *local_hostlist; 00217 00218 static void dns_init_local(); 00219 #endif /* DNS_LOCAL_HOSTLIST */ 00220 00221 00222 /* forward declarations */ 00223 static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); 00224 static void dns_check_entries(void); 00225 00226 /*----------------------------------------------------------------------------- 00227 * Globales 00228 *----------------------------------------------------------------------------*/ 00229 00230 /* DNS variables */ 00231 static struct udp_pcb *dns_pcb; 00232 static u8_t dns_seqno; 00233 static struct dns_table_entry dns_table[DNS_TABLE_SIZE] MEM_POSITION; 00234 static struct ip_addr dns_servers[DNS_MAX_SERVERS] MEM_POSITION; 00235 00236 #if (DNS_USES_STATIC_BUF == 1) 00237 static u8_t dns_payload[DNS_MSG_SIZE] MEM_POSITION; 00238 #endif /* (DNS_USES_STATIC_BUF == 1) */ 00239 00240 /** 00241 * Initialize the resolver: set up the UDP pcb and configure the default server 00242 * (DNS_SERVER_ADDRESS). 00243 */ 00244 void 00245 dns_init() 00246 { 00247 struct ip_addr dnsserver; 00248 00249 /* initialize default DNS server address */ 00250 dnsserver.addr = DNS_SERVER_ADDRESS; 00251 00252 LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); 00253 00254 /* if dns client not yet initialized... */ 00255 if (dns_pcb == NULL) { 00256 dns_pcb = udp_new(); 00257 00258 if (dns_pcb != NULL) { 00259 /* initialize DNS table not needed (initialized to zero since it is a 00260 * global variable) */ 00261 LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", 00262 DNS_STATE_UNUSED == 0); 00263 00264 /* initialize DNS client */ 00265 udp_bind(dns_pcb, IP_ADDR_ANY, 0); 00266 udp_recv(dns_pcb, dns_recv, NULL); 00267 00268 /* initialize default DNS primary server */ 00269 dns_setserver(0, &dnsserver); 00270 } 00271 } 00272 #if DNS_LOCAL_HOSTLIST 00273 dns_init_local(); 00274 #endif 00275 } 00276 00277 /** 00278 * Initialize one of the DNS servers. 00279 * 00280 * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS 00281 * @param dnsserver IP address of the DNS server to set 00282 */ 00283 void 00284 dns_setserver(u8_t numdns, struct ip_addr *dnsserver) 00285 { 00286 if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) && 00287 (dnsserver != NULL) && (dnsserver->addr !=0 )) { 00288 dns_servers[numdns] = (*dnsserver); 00289 } 00290 } 00291 00292 /** 00293 * Obtain one of the currently configured DNS server. 00294 * 00295 * @param numdns the index of the DNS server 00296 * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS 00297 * server has not been configured. 00298 */ 00299 struct ip_addr 00300 dns_getserver(u8_t numdns) 00301 { 00302 if (numdns < DNS_MAX_SERVERS) { 00303 return dns_servers[numdns]; 00304 } else { 00305 return *IP_ADDR_ANY; 00306 } 00307 } 00308 00309 /** 00310 * The DNS resolver client timer - handle retries and timeouts and should 00311 * be called every DNS_TMR_INTERVAL milliseconds (every second by default). 00312 */ 00313 void 00314 dns_tmr(void) 00315 { 00316 if (dns_pcb != NULL) { 00317 LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); 00318 dns_check_entries(); 00319 } 00320 } 00321 00322 #if DNS_LOCAL_HOSTLIST 00323 static void 00324 dns_init_local() 00325 { 00326 #ifdef DNS_LOCAL_HOSTLIST_INIT 00327 int i; 00328 struct local_hostlist_entry *entry; 00329 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00330 /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */ 00331 struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT; 00332 for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) { 00333 entry = mem_malloc(sizeof(struct local_hostlist_entry)); 00334 LWIP_ASSERT("mem-error in dns_init_local", entry != NULL); 00335 if (entry != NULL) { 00336 struct local_hostlist_entry *init_entry = &local_hostlist_init[i]; 00337 entry->name = init_entry->name; 00338 entry->addr = init_entry->addr; 00339 entry->next = local_hostlist; 00340 local_hostlist = entry; 00341 } 00342 } 00343 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00344 /* Static: only adjust the 'next' pointers */ 00345 entry = NULL; 00346 local_hostlist = local_hostlist_static; 00347 for (i = sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry) - 1; i >= 0; i--) { 00348 local_hostlist_static[i].next = entry; 00349 entry = &local_hostlist_static[i]; 00350 } 00351 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00352 #endif /* DNS_LOCAL_HOSTLIST_INIT */ 00353 } 00354 00355 static u32_t 00356 dns_lookup_static(const char *name) 00357 { 00358 struct local_hostlist_entry *entry = local_hostlist; 00359 while(entry != NULL) { 00360 if(strcmp(entry->name, name) == 0) { 00361 return htons(entry->addr); 00362 } 00363 entry = entry->next; 00364 } 00365 return INADDR_NONE; 00366 } 00367 00368 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00369 /** Remove all entries from the local host-list for a specific hostname 00370 * 00371 * @param hostname hostname for which entries shall be removed from the local 00372 * host-list 00373 * @return the number of removed entries 00374 */ 00375 int 00376 dns_local_removehostname(const char *hostname) 00377 { 00378 int removed = 0; 00379 struct local_hostlist_entry *entry = local_hostlist; 00380 struct local_hostlist_entry *last_entry = NULL; 00381 while (entry != NULL) { 00382 if (!strcmp(entry->name, hostname)) { 00383 if (last_entry != NULL) { 00384 last_entry->next = entry->next; 00385 } else { 00386 local_hostlist = entry->next; 00387 } 00388 mem_free(entry); 00389 removed++; 00390 } 00391 last_entry = entry; 00392 entry = entry->next; 00393 } 00394 return removed; 00395 } 00396 00397 /** Remove all entries from the local host-list for a specific address 00398 * 00399 * @param addr address for which entries shall be removed from the local 00400 * host-list 00401 * @return the number of removed entries 00402 */ 00403 int 00404 dns_local_removehostaddr(const struct ip_addr *addr) 00405 { 00406 int removed = 0; 00407 struct local_hostlist_entry *entry = local_hostlist; 00408 struct local_hostlist_entry *last_entry = NULL; 00409 while (entry != NULL) { 00410 if (entry->addr == addr->addr) { 00411 struct local_hostlist_entry *removed_entry = entry; 00412 if (last_entry != NULL) { 00413 last_entry->next = entry->next; 00414 } else { 00415 local_hostlist = entry->next; 00416 } 00417 entry = entry->next; 00418 mem_free(removed_entry); 00419 removed++; 00420 } else { 00421 last_entry = entry; 00422 entry = entry->next; 00423 } 00424 } 00425 return removed; 00426 } 00427 00428 err_t 00429 dns_local_addhost(const char *hostname, const struct ip_addr *addr) 00430 { 00431 struct local_hostlist_entry *entry; 00432 entry = mem_malloc(sizeof(struct local_hostlist_entry)); 00433 if (entry == NULL) { 00434 return ERR_MEM; 00435 } 00436 entry->name = hostname; 00437 entry->addr = addr->addr; 00438 entry->next = local_hostlist; 00439 local_hostlist = entry; 00440 return ERR_OK; 00441 } 00442 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/ 00443 #endif /* DNS_LOCAL_HOSTLIST */ 00444 00445 /** 00446 * Look up a hostname in the array of known hostnames. 00447 * 00448 * @note This function only looks in the internal array of known 00449 * hostnames, it does not send out a query for the hostname if none 00450 * was found. The function dns_enqueue() can be used to send a query 00451 * for a hostname. 00452 * 00453 * @param name the hostname to look up 00454 * @return the hostname's IP address, as u32_t (instead of struct ip_addr to 00455 * better check for failure: != INADDR_NONE) or INADDR_NONE if the hostname 00456 * was not found in the cached dns_table. 00457 */ 00458 static u32_t 00459 dns_lookup(const char *name) 00460 { 00461 u8_t i; 00462 #if DNS_LOCAL_HOSTLIST 00463 u32_t addr; 00464 00465 if ((addr = dns_lookup_static(name)) != INADDR_NONE) { 00466 return addr; 00467 } 00468 #endif /* DNS_LOCAL_HOSTLIST */ 00469 00470 /* Walk through name list, return entry if found. If not, return NULL. */ 00471 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 00472 if ((dns_table[i].state == DNS_STATE_DONE) && 00473 (strcmp(name, dns_table[i].name) == 0)) { 00474 LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); 00475 ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); 00476 LWIP_DEBUGF(DNS_DEBUG, ("\n")); 00477 return dns_table[i].ipaddr.addr; 00478 } 00479 } 00480 00481 return INADDR_NONE; 00482 } 00483 00484 #if DNS_DOES_NAME_CHECK 00485 /** 00486 * Compare the "dotted" name "query" with the encoded name "response" 00487 * to make sure an answer from the DNS server matches the current dns_table 00488 * entry (otherwise, answers might arrive late for hostname not on the list 00489 * any more). 00490 * 00491 * @param query hostname (not encoded) from the dns_table 00492 * @param response encoded hostname in the DNS response 00493 * @return 0: names equal; 1: names differ 00494 */ 00495 static u8_t 00496 dns_compare_name(unsigned char *query, unsigned char *response) 00497 { 00498 unsigned char n; 00499 00500 do { 00501 n = *response++; 00502 /** @see RFC 1035 - 4.1.4. Message compression */ 00503 if ((n & 0xc0) == 0xc0) { 00504 /* Compressed name */ 00505 break; 00506 } else { 00507 /* Not compressed name */ 00508 while (n > 0) { 00509 if ((*query) != (*response)) { 00510 return 1; 00511 } 00512 ++response; 00513 ++query; 00514 --n; 00515 }; 00516 ++query; 00517 } 00518 } while (*response != 0); 00519 00520 return 0; 00521 } 00522 #endif /* DNS_DOES_NAME_CHECK */ 00523 00524 /** 00525 * Walk through a compact encoded DNS name and return the end of the name. 00526 * 00527 * @param query encoded DNS name in the DNS server response 00528 * @return end of the name 00529 */ 00530 static unsigned char * 00531 dns_parse_name(unsigned char *query) 00532 { 00533 unsigned char n; 00534 00535 do { 00536 n = *query++; 00537 /** @see RFC 1035 - 4.1.4. Message compression */ 00538 if ((n & 0xc0) == 0xc0) { 00539 /* Compressed name */ 00540 break; 00541 } else { 00542 /* Not compressed name */ 00543 while (n > 0) { 00544 ++query; 00545 --n; 00546 }; 00547 } 00548 } while (*query != 0); 00549 00550 return query + 1; 00551 } 00552 00553 /** 00554 * Send a DNS query packet. 00555 * 00556 * @param numdns index of the DNS server in the dns_servers table 00557 * @param name hostname to query 00558 * @param id index of the hostname in dns_table, used as transaction ID in the 00559 * DNS query packet 00560 * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise 00561 */ 00562 static err_t 00563 dns_send(u8_t numdns, const char* name, u8_t id) 00564 { 00565 err_t err; 00566 struct dns_hdr *hdr; 00567 struct dns_query qry; 00568 struct pbuf *p; 00569 char *query, *nptr; 00570 const char *pHostname; 00571 u8_t n; 00572 00573 LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", 00574 (u16_t)(numdns), name)); 00575 LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS); 00576 LWIP_ASSERT("dns server has no IP address set", dns_servers[numdns].addr != 0); 00577 00578 /* if here, we have either a new query or a retry on a previous query to process */ 00579 p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH + 00580 SIZEOF_DNS_QUERY, PBUF_RAM); 00581 if (p != NULL) { 00582 LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); 00583 /* fill dns header */ 00584 hdr = (struct dns_hdr*)p->payload; 00585 memset(hdr, 0, SIZEOF_DNS_HDR); 00586 hdr->id = htons(id); 00587 hdr->flags1 = DNS_FLAG1_RD; 00588 hdr->numquestions = htons(1); 00589 query = (char*)hdr + SIZEOF_DNS_HDR; 00590 pHostname = name; 00591 --pHostname; 00592 00593 /* convert hostname into suitable query format. */ 00594 do { 00595 ++pHostname; 00596 nptr = query; 00597 ++query; 00598 for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { 00599 *query = *pHostname; 00600 ++query; 00601 ++n; 00602 } 00603 *nptr = n; 00604 } while(*pHostname != 0); 00605 *query++='\0'; 00606 00607 /* fill dns query */ 00608 qry.type = htons(DNS_RRTYPE_A); 00609 qry.class = htons(DNS_RRCLASS_IN); 00610 MEMCPY( query, &qry, SIZEOF_DNS_QUERY); 00611 00612 /* resize pbuf to the exact dns query */ 00613 pbuf_realloc(p, (query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))); 00614 00615 /* connect to the server for faster receiving */ 00616 udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT); 00617 /* send dns packet */ 00618 err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT); 00619 00620 /* free pbuf */ 00621 pbuf_free(p); 00622 } else { 00623 err = ERR_MEM; 00624 } 00625 00626 return err; 00627 } 00628 00629 /** 00630 * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query. 00631 * Check an entry in the dns_table: 00632 * - send out query for new entries 00633 * - retry old pending entries on timeout (also with different servers) 00634 * - remove completed entries from the table if their TTL has expired 00635 * 00636 * @param i index of the dns_table entry to check 00637 */ 00638 static void 00639 dns_check_entry(u8_t i) 00640 { 00641 struct dns_table_entry *pEntry = &dns_table[i]; 00642 00643 LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); 00644 00645 switch(pEntry->state) { 00646 00647 case DNS_STATE_NEW: { 00648 /* initialize new entry */ 00649 pEntry->state = DNS_STATE_ASKING; 00650 pEntry->numdns = 0; 00651 pEntry->tmr = 1; 00652 pEntry->retries = 0; 00653 00654 /* send DNS packet for this entry */ 00655 dns_send(pEntry->numdns, pEntry->name, i); 00656 break; 00657 } 00658 00659 case DNS_STATE_ASKING: { 00660 if (--pEntry->tmr == 0) { 00661 if (++pEntry->retries == DNS_MAX_RETRIES) { 00662 if ((pEntry->numdns+1<DNS_MAX_SERVERS) && (dns_servers[pEntry->numdns+1].addr!=0)) { 00663 /* change of server */ 00664 pEntry->numdns++; 00665 pEntry->tmr = 1; 00666 pEntry->retries = 0; 00667 break; 00668 } else { 00669 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name)); 00670 /* call specified callback function if provided */ 00671 if (pEntry->found) 00672 (*pEntry->found)(pEntry->name, NULL, pEntry->arg); 00673 /* flush this entry */ 00674 pEntry->state = DNS_STATE_UNUSED; 00675 pEntry->found = NULL; 00676 break; 00677 } 00678 } 00679 00680 /* wait longer for the next retry */ 00681 pEntry->tmr = pEntry->retries; 00682 00683 /* send DNS packet for this entry */ 00684 dns_send(pEntry->numdns, pEntry->name, i); 00685 } 00686 break; 00687 } 00688 00689 case DNS_STATE_DONE: { 00690 /* if the time to live is nul */ 00691 if (--pEntry->ttl == 0) { 00692 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name)); 00693 /* flush this entry */ 00694 pEntry->state = DNS_STATE_UNUSED; 00695 pEntry->found = NULL; 00696 } 00697 break; 00698 } 00699 case DNS_STATE_UNUSED: 00700 /* nothing to do */ 00701 break; 00702 default: 00703 LWIP_ASSERT("unknown dns_table entry state:", 0); 00704 break; 00705 } 00706 } 00707 00708 /** 00709 * Call dns_check_entry for each entry in dns_table - check all entries. 00710 */ 00711 static void 00712 dns_check_entries(void) 00713 { 00714 u8_t i; 00715 00716 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 00717 dns_check_entry(i); 00718 } 00719 } 00720 00721 /** 00722 * Receive input function for DNS response packets arriving for the dns UDP pcb. 00723 * 00724 * @params see udp.h 00725 */ 00726 static void 00727 dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) 00728 { 00729 u8_t i; 00730 char *pHostname; 00731 struct dns_hdr *hdr; 00732 struct dns_answer ans; 00733 struct dns_table_entry *pEntry; 00734 u8_t nquestions, nanswers; 00735 #if (DNS_USES_STATIC_BUF == 0) 00736 u8_t dns_payload[DNS_MSG_SIZE]; 00737 #endif /* (DNS_USES_STATIC_BUF == 0) */ 00738 #if (DNS_USES_STATIC_BUF == 2) 00739 u8_t* dns_payload; 00740 #endif /* (DNS_USES_STATIC_BUF == 2) */ 00741 00742 LWIP_UNUSED_ARG(arg); 00743 LWIP_UNUSED_ARG(pcb); 00744 LWIP_UNUSED_ARG(addr); 00745 LWIP_UNUSED_ARG(port); 00746 00747 /* is the dns message too big ? */ 00748 if (p->tot_len > DNS_MSG_SIZE) { 00749 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n")); 00750 /* free pbuf and return */ 00751 goto memerr1; 00752 } 00753 00754 /* is the dns message big enough ? */ 00755 if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) { 00756 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n")); 00757 /* free pbuf and return */ 00758 goto memerr1; 00759 } 00760 00761 #if (DNS_USES_STATIC_BUF == 2) 00762 dns_payload = mem_malloc(p->tot_len); 00763 if (dns_payload == NULL) { 00764 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: mem_malloc error\n")); 00765 /* free pbuf and return */ 00766 goto memerr1; 00767 } 00768 #endif /* (DNS_USES_STATIC_BUF == 2) */ 00769 00770 /* copy dns payload inside static buffer for processing */ 00771 if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) { 00772 /* The ID in the DNS header should be our entry into the name table. */ 00773 hdr = (struct dns_hdr*)dns_payload; 00774 i = htons(hdr->id); 00775 if (i < DNS_TABLE_SIZE) { 00776 pEntry = &dns_table[i]; 00777 if(pEntry->state == DNS_STATE_ASKING) { 00778 /* This entry is now completed. */ 00779 pEntry->state = DNS_STATE_DONE; 00780 pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; 00781 00782 /* We only care about the question(s) and the answers. The authrr 00783 and the extrarr are simply discarded. */ 00784 nquestions = htons(hdr->numquestions); 00785 nanswers = htons(hdr->numanswers); 00786 00787 /* Check for error. If so, call callback to inform. */ 00788 if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) { 00789 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name)); 00790 /* call callback to indicate error, clean up memory and return */ 00791 goto responseerr; 00792 } 00793 00794 #if DNS_DOES_NAME_CHECK 00795 /* Check if the name in the "question" part match with the name in the entry. */ 00796 if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) { 00797 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name)); 00798 /* call callback to indicate error, clean up memory and return */ 00799 goto responseerr; 00800 } 00801 #endif /* DNS_DOES_NAME_CHECK */ 00802 00803 /* Skip the name in the "question" part */ 00804 pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY; 00805 00806 while(nanswers > 0) { 00807 /* skip answer resource record's host name */ 00808 pHostname = (char *) dns_parse_name((unsigned char *)pHostname); 00809 00810 /* Check for IP address type and Internet class. Others are discarded. */ 00811 MEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER); 00812 if((ntohs(ans.type) == DNS_RRTYPE_A) && (ntohs(ans.class) == DNS_RRCLASS_IN) && (ntohs(ans.len) == sizeof(struct ip_addr)) ) { 00813 /* read the answer resource record's TTL, and maximize it if needed */ 00814 pEntry->ttl = ntohl(ans.ttl); 00815 if (pEntry->ttl > DNS_MAX_TTL) { 00816 pEntry->ttl = DNS_MAX_TTL; 00817 } 00818 /* read the IP address after answer resource record's header */ 00819 MEMCPY( &(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(struct ip_addr)); 00820 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name)); 00821 ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr))); 00822 LWIP_DEBUGF(DNS_DEBUG, ("\n")); 00823 /* call specified callback function if provided */ 00824 if (pEntry->found) { 00825 (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg); 00826 } 00827 /* deallocate memory and return */ 00828 goto memerr2; 00829 } else { 00830 pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len); 00831 } 00832 --nanswers; 00833 } 00834 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name)); 00835 /* call callback to indicate error, clean up memory and return */ 00836 goto responseerr; 00837 } 00838 } 00839 } 00840 00841 /* deallocate memory and return */ 00842 goto memerr2; 00843 00844 responseerr: 00845 /* ERROR: call specified callback function with NULL as name to indicate an error */ 00846 if (pEntry->found) { 00847 (*pEntry->found)(pEntry->name, NULL, pEntry->arg); 00848 } 00849 /* flush this entry */ 00850 pEntry->state = DNS_STATE_UNUSED; 00851 pEntry->found = NULL; 00852 00853 memerr2: 00854 #if (DNS_USES_STATIC_BUF == 2) 00855 /* free dns buffer */ 00856 mem_free(dns_payload); 00857 #endif /* (DNS_USES_STATIC_BUF == 2) */ 00858 00859 memerr1: 00860 /* free pbuf */ 00861 pbuf_free(p); 00862 return; 00863 } 00864 00865 /** 00866 * Queues a new hostname to resolve and sends out a DNS query for that hostname 00867 * 00868 * @param name the hostname that is to be queried 00869 * @param found a callback founction to be called on success, failure or timeout 00870 * @param callback_arg argument to pass to the callback function 00871 * @return @return a err_t return code. 00872 */ 00873 static err_t 00874 dns_enqueue(const char *name, dns_found_callback found, void *callback_arg) 00875 { 00876 u8_t i; 00877 u8_t lseq, lseqi; 00878 struct dns_table_entry *pEntry = NULL; 00879 00880 /* search an unused entry, or the oldest one */ 00881 lseq = lseqi = 0; 00882 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 00883 pEntry = &dns_table[i]; 00884 /* is it an unused entry ? */ 00885 if (pEntry->state == DNS_STATE_UNUSED) 00886 break; 00887 00888 /* check if this is the oldest completed entry */ 00889 if (pEntry->state == DNS_STATE_DONE) { 00890 if ((dns_seqno - pEntry->seqno) > lseq) { 00891 lseq = dns_seqno - pEntry->seqno; 00892 lseqi = i; 00893 } 00894 } 00895 } 00896 00897 /* if we don't have found an unused entry, use the oldest completed one */ 00898 if (i == DNS_TABLE_SIZE) { 00899 if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { 00900 /* no entry can't be used now, table is full */ 00901 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); 00902 return ERR_MEM; 00903 } else { 00904 /* use the oldest completed one */ 00905 i = lseqi; 00906 pEntry = &dns_table[i]; 00907 } 00908 } 00909 00910 /* use this entry */ 00911 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); 00912 00913 /* fill the entry */ 00914 pEntry->state = DNS_STATE_NEW; 00915 pEntry->seqno = dns_seqno++; 00916 pEntry->found = found; 00917 pEntry->arg = callback_arg; 00918 strcpy(pEntry->name, name); 00919 00920 /* force to send query without waiting timer */ 00921 dns_check_entry(i); 00922 00923 /* dns query is enqueued */ 00924 return ERR_INPROGRESS; 00925 } 00926 00927 /** 00928 * Resolve a hostname (string) into an IP address. 00929 * NON-BLOCKING callback version for use with raw API!!! 00930 * 00931 * Returns immediately with one of err_t return codes: 00932 * - ERR_OK if hostname is a valid IP address string or the host 00933 * name is already in the local names table. 00934 * - ERR_INPROGRESS enqueue a request to be sent to the DNS server 00935 * for resolution if no errors are present. 00936 * 00937 * @param hostname the hostname that is to be queried 00938 * @param addr pointer to a struct ip_addr where to store the address if it is already 00939 * cached in the dns_table (only valid if ERR_OK is returned!) 00940 * @param found a callback function to be called on success, failure or timeout (only if 00941 * ERR_INPROGRESS is returned!) 00942 * @param callback_arg argument to pass to the callback function 00943 * @return a err_t return code. 00944 */ 00945 err_t 00946 dns_gethostbyname(const char *hostname, struct ip_addr *addr, dns_found_callback found, 00947 void *callback_arg) 00948 { 00949 /* not initialized or no valid server yet, or invalid addr pointer 00950 * or invalid hostname or invalid hostname length */ 00951 if ((dns_pcb == NULL) || (addr == NULL) || 00952 (!hostname) || (!hostname[0]) || 00953 (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) { 00954 return ERR_VAL; 00955 } 00956 00957 #if LWIP_HAVE_LOOPIF 00958 if (strcmp(hostname,"localhost")==0) { 00959 addr->addr = INADDR_LOOPBACK; 00960 return ERR_OK; 00961 } 00962 #endif /* LWIP_HAVE_LOOPIF */ 00963 00964 /* host name already in octet notation? set ip addr and return ERR_OK 00965 * already have this address cached? */ 00966 if (((addr->addr = inet_addr(hostname)) != INADDR_NONE) || 00967 ((addr->addr = dns_lookup(hostname)) != INADDR_NONE)) { 00968 return ERR_OK; 00969 } 00970 00971 /* queue query with specified callback */ 00972 return dns_enqueue(hostname, found, callback_arg); 00973 } 00974 00975 #endif /* LWIP_DNS */
Generated on Tue Jul 12 2022 19:24:05 by
1.7.2