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