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(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_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 #ifdef PACK_STRUCT_USE_INCLUDES 00125 # include "arch/bpstruct.h" 00126 #endif 00127 PACK_STRUCT_BEGIN 00128 /** DNS message header */ 00129 struct dns_hdr { 00130 PACK_STRUCT_FIELD(u16_t id); 00131 PACK_STRUCT_FIELD(u8_t flags1); 00132 PACK_STRUCT_FIELD(u8_t flags2); 00133 PACK_STRUCT_FIELD(u16_t numquestions); 00134 PACK_STRUCT_FIELD(u16_t numanswers); 00135 PACK_STRUCT_FIELD(u16_t numauthrr); 00136 PACK_STRUCT_FIELD(u16_t numextrarr); 00137 } PACK_STRUCT_STRUCT; 00138 PACK_STRUCT_END 00139 #ifdef PACK_STRUCT_USE_INCLUDES 00140 # include "arch/epstruct.h" 00141 #endif 00142 #define SIZEOF_DNS_HDR 12 00143 00144 /** DNS query message structure. 00145 No packing needed: only used locally on the stack. */ 00146 PACK_STRUCT_BEGIN 00147 struct dns_query { 00148 /* DNS query record starts with either a domain name or a pointer 00149 to a name already present somewhere in the packet. */ 00150 u16_t type; 00151 u16_t cls; 00152 } PACK_STRUCT_STRUCT; 00153 PACK_STRUCT_END 00154 #define SIZEOF_DNS_QUERY 4 00155 00156 /** DNS answer message structure. 00157 No packing needed: only used locally on the stack. */ 00158 PACK_STRUCT_BEGIN 00159 struct dns_answer { 00160 /* DNS answer record starts with either a domain name or a pointer 00161 to a name already present somewhere in the packet. */ 00162 u16_t type; 00163 u16_t cls; 00164 u32_t ttl; 00165 u16_t len; 00166 } PACK_STRUCT_STRUCT; 00167 PACK_STRUCT_END 00168 #define SIZEOF_DNS_ANSWER 10 00169 00170 /** DNS table entry */ 00171 struct dns_table_entry { 00172 u8_t state; 00173 u8_t numdns; 00174 u8_t tmr; 00175 u8_t retries; 00176 u8_t seqno; 00177 u8_t err; 00178 u32_t ttl; 00179 char name[DNS_MAX_NAME_LENGTH]; 00180 ip_addr_t ipaddr; 00181 /* pointer to callback on DNS query done */ 00182 dns_found_callback found; 00183 void *arg; 00184 }; 00185 00186 #if DNS_LOCAL_HOSTLIST 00187 /** struct used for local host-list */ 00188 struct local_hostlist_entry { 00189 /** static hostname */ 00190 const char *name; 00191 /** static host address in network byteorder */ 00192 ip_addr_t addr; 00193 struct local_hostlist_entry *next; 00194 }; 00195 00196 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00197 /** Local host-list. For hostnames in this list, no 00198 * external name resolution is performed */ 00199 static struct local_hostlist_entry *local_hostlist_dynamic; 00200 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00201 00202 /** Defining this allows the local_hostlist_static to be placed in a different 00203 * linker section (e.g. FLASH) */ 00204 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE 00205 #define DNS_LOCAL_HOSTLIST_STORAGE_PRE static 00206 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */ 00207 /** Defining this allows the local_hostlist_static to be placed in a different 00208 * linker section (e.g. FLASH) */ 00209 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST 00210 #define DNS_LOCAL_HOSTLIST_STORAGE_POST 00211 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */ 00212 DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[] 00213 DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT; 00214 00215 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00216 00217 static void dns_init_local(); 00218 #endif /* DNS_LOCAL_HOSTLIST */ 00219 00220 00221 /* forward declarations */ 00222 static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); 00223 static void dns_check_entries(void); 00224 00225 /*----------------------------------------------------------------------------- 00226 * Globales 00227 *----------------------------------------------------------------------------*/ 00228 00229 /* DNS variables */ 00230 static struct udp_pcb *dns_pcb; 00231 static u8_t dns_seqno; 00232 static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; 00233 static ip_addr_t dns_servers[DNS_MAX_SERVERS]; 00234 /** Contiguous buffer for processing responses */ 00235 static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)]; 00236 static u8_t* dns_payload; 00237 00238 /** 00239 * Initialize the resolver: set up the UDP pcb and configure the default server 00240 * (DNS_SERVER_ADDRESS). 00241 */ 00242 void 00243 dns_init() 00244 { 00245 ip_addr_t dnsserver; 00246 00247 dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer); 00248 00249 /* initialize default DNS server address */ 00250 DNS_SERVER_ADDRESS(&dnsserver); 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, ip_addr_t *dnsserver) 00285 { 00286 if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) && 00287 (dnsserver != NULL) && !ip_addr_isany(dnsserver)) { 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 ip_addr_t 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 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) 00327 int i; 00328 struct local_hostlist_entry *entry; 00329 /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */ 00330 struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT; 00331 size_t namelen; 00332 for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) { 00333 struct local_hostlist_entry *init_entry = &local_hostlist_init[i]; 00334 LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL); 00335 namelen = strlen(init_entry->name); 00336 entry = mem_malloc((mem_size_t)(sizeof(struct local_hostlist_entry) + namelen + 1)); 00337 LWIP_ASSERT("mem-error in dns_init_local", entry != NULL); 00338 if (entry != NULL) { 00339 entry->name = (char*)entry + sizeof(struct local_hostlist_entry); 00340 MEMCPY((char*)entry->name, init_entry->name, namelen); 00341 ((char*)entry->name)[namelen] = 0; 00342 entry->addr = init_entry->addr; 00343 entry->next = local_hostlist_dynamic; 00344 local_hostlist_dynamic = entry; 00345 } 00346 } 00347 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */ 00348 } 00349 00350 /** 00351 * Scans the local host-list for a hostname. 00352 * 00353 * @param hostname Hostname to look for in the local host-list 00354 * @return The first IP address for the hostname in the local host-list or 00355 * IPADDR_NONE if not found. 00356 */ 00357 static u32_t 00358 dns_lookup_local(const char *hostname) 00359 { 00360 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00361 struct local_hostlist_entry *entry = local_hostlist_dynamic; 00362 while(entry != NULL) { 00363 if(strcmp(entry->name, hostname) == 0) { 00364 return ip4_addr_get_u32(&entry->addr); 00365 } 00366 entry = entry->next; 00367 } 00368 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00369 int i; 00370 for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) { 00371 if(strcmp(local_hostlist_static[i].name, hostname) == 0) { 00372 return ip4_addr_get_u32(&local_hostlist_static[i].addr); 00373 } 00374 } 00375 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00376 return IPADDR_NONE; 00377 } 00378 00379 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00380 /** Remove all entries from the local host-list for a specific hostname 00381 * and/or IP addess 00382 * 00383 * @param hostname hostname for which entries shall be removed from the local 00384 * host-list 00385 * @param addr address for which entries shall be removed from the local host-list 00386 * @return the number of removed entries 00387 */ 00388 int 00389 dns_local_removehost(const char *hostname, const ip_addr_t *addr) 00390 { 00391 int removed = 0; 00392 struct local_hostlist_entry *entry = local_hostlist_dynamic; 00393 struct local_hostlist_entry *last_entry = NULL; 00394 while (entry != NULL) { 00395 if (((hostname == NULL) || !strcmp(entry->name, hostname)) && 00396 ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) { 00397 struct local_hostlist_entry *free_entry; 00398 if (last_entry != NULL) { 00399 last_entry->next = entry->next; 00400 } else { 00401 local_hostlist_dynamic = entry->next; 00402 } 00403 free_entry = entry; 00404 entry = entry->next; 00405 mem_free(free_entry); 00406 removed++; 00407 } else { 00408 last_entry = entry; 00409 entry = entry->next; 00410 } 00411 } 00412 return removed; 00413 } 00414 00415 /** 00416 * Add a hostname/IP address pair to the local host-list. 00417 * Duplicates are not checked. 00418 * 00419 * @param hostname hostname of the new entry 00420 * @param addr IP address of the new entry 00421 * @return ERR_OK if succeeded or ERR_MEM on memory error 00422 */ 00423 err_t 00424 dns_local_addhost(const char *hostname, const ip_addr_t *addr) 00425 { 00426 struct local_hostlist_entry *entry; 00427 size_t namelen; 00428 LWIP_ASSERT("invalid host name (NULL)", hostname != NULL); 00429 namelen = strlen(hostname); 00430 entry = mem_malloc((mem_size_t)(sizeof(struct local_hostlist_entry) + namelen + 1)); 00431 if (entry == NULL) { 00432 return ERR_MEM; 00433 } 00434 entry->name = (char*)entry + sizeof(struct local_hostlist_entry); 00435 MEMCPY((char*)entry->name, hostname, namelen); 00436 ((char*)entry->name)[namelen] = 0; 00437 ip_addr_copy(entry->addr, *addr); 00438 entry->next = local_hostlist_dynamic; 00439 local_hostlist_dynamic = 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 ip_addr_t to 00455 * better check for failure: != IPADDR_NONE) or IPADDR_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 || defined(DNS_LOOKUP_LOCAL_EXTERN) 00463 u32_t addr; 00464 #endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */ 00465 #if DNS_LOCAL_HOSTLIST 00466 if ((addr = dns_lookup_local(name)) != IPADDR_NONE) { 00467 return addr; 00468 } 00469 #endif /* DNS_LOCAL_HOSTLIST */ 00470 #ifdef DNS_LOOKUP_LOCAL_EXTERN 00471 if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) { 00472 return addr; 00473 } 00474 #endif /* DNS_LOOKUP_LOCAL_EXTERN */ 00475 00476 /* Walk through name list, return entry if found. If not, return NULL. */ 00477 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 00478 if ((dns_table[i].state == DNS_STATE_DONE) && 00479 (strcmp(name, dns_table[i].name) == 0)) { 00480 LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); 00481 ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); 00482 LWIP_DEBUGF(DNS_DEBUG, ("\n")); 00483 return ip4_addr_get_u32(&dns_table[i].ipaddr); 00484 } 00485 } 00486 00487 return IPADDR_NONE; 00488 } 00489 00490 #if DNS_DOES_NAME_CHECK 00491 /** 00492 * Compare the "dotted" name "query" with the encoded name "response" 00493 * to make sure an answer from the DNS server matches the current dns_table 00494 * entry (otherwise, answers might arrive late for hostname not on the list 00495 * any more). 00496 * 00497 * @param query hostname (not encoded) from the dns_table 00498 * @param response encoded hostname in the DNS response 00499 * @return 0: names equal; 1: names differ 00500 */ 00501 static u8_t 00502 dns_compare_name(unsigned char *query, unsigned char *response) 00503 { 00504 unsigned char n; 00505 00506 do { 00507 n = *response++; 00508 /** @see RFC 1035 - 4.1.4. Message compression */ 00509 if ((n & 0xc0) == 0xc0) { 00510 /* Compressed name */ 00511 break; 00512 } else { 00513 /* Not compressed name */ 00514 while (n > 0) { 00515 if ((*query) != (*response)) { 00516 return 1; 00517 } 00518 ++response; 00519 ++query; 00520 --n; 00521 }; 00522 ++query; 00523 } 00524 } while (*response != 0); 00525 00526 return 0; 00527 } 00528 #endif /* DNS_DOES_NAME_CHECK */ 00529 00530 /** 00531 * Walk through a compact encoded DNS name and return the end of the name. 00532 * 00533 * @param query encoded DNS name in the DNS server response 00534 * @return end of the name 00535 */ 00536 static unsigned char * 00537 dns_parse_name(unsigned char *query) 00538 { 00539 unsigned char n; 00540 00541 do { 00542 n = *query++; 00543 /** @see RFC 1035 - 4.1.4. Message compression */ 00544 if ((n & 0xc0) == 0xc0) { 00545 /* Compressed name */ 00546 break; 00547 } else { 00548 /* Not compressed name */ 00549 while (n > 0) { 00550 ++query; 00551 --n; 00552 }; 00553 } 00554 } while (*query != 0); 00555 00556 return query + 1; 00557 } 00558 00559 /** 00560 * Send a DNS query packet. 00561 * 00562 * @param numdns index of the DNS server in the dns_servers table 00563 * @param name hostname to query 00564 * @param id index of the hostname in dns_table, used as transaction ID in the 00565 * DNS query packet 00566 * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise 00567 */ 00568 static err_t 00569 dns_send(u8_t numdns, const char* name, u8_t id) 00570 { 00571 err_t err; 00572 struct dns_hdr *hdr; 00573 struct dns_query qry; 00574 struct pbuf *p; 00575 char *query, *nptr; 00576 const char *pHostname; 00577 u8_t n; 00578 00579 LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", 00580 (u16_t)(numdns), name)); 00581 LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS); 00582 LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns])); 00583 00584 /* if here, we have either a new query or a retry on a previous query to process */ 00585 p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH + 00586 SIZEOF_DNS_QUERY, PBUF_RAM); 00587 if (p != NULL) { 00588 LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); 00589 /* fill dns header */ 00590 hdr = (struct dns_hdr*)p->payload; 00591 memset(hdr, 0, SIZEOF_DNS_HDR); 00592 hdr->id = htons(id); 00593 hdr->flags1 = DNS_FLAG1_RD; 00594 hdr->numquestions = htons(1); 00595 query = (char*)hdr + SIZEOF_DNS_HDR; 00596 pHostname = name; 00597 --pHostname; 00598 00599 /* convert hostname into suitable query format. */ 00600 do { 00601 ++pHostname; 00602 nptr = query; 00603 ++query; 00604 for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { 00605 *query = *pHostname; 00606 ++query; 00607 ++n; 00608 } 00609 *nptr = n; 00610 } while(*pHostname != 0); 00611 *query++='\0'; 00612 00613 /* fill dns query */ 00614 qry.type = htons(DNS_RRTYPE_A); 00615 qry.cls = htons(DNS_RRCLASS_IN); 00616 SMEMCPY(query, &qry, SIZEOF_DNS_QUERY); 00617 00618 /* resize pbuf to the exact dns query */ 00619 pbuf_realloc(p, (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload)))); 00620 00621 /* connect to the server for faster receiving */ 00622 udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT); 00623 /* send dns packet */ 00624 err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT); 00625 00626 /* free pbuf */ 00627 pbuf_free(p); 00628 } else { 00629 err = ERR_MEM; 00630 } 00631 00632 return err; 00633 } 00634 00635 /** 00636 * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query. 00637 * Check an entry in the dns_table: 00638 * - send out query for new entries 00639 * - retry old pending entries on timeout (also with different servers) 00640 * - remove completed entries from the table if their TTL has expired 00641 * 00642 * @param i index of the dns_table entry to check 00643 */ 00644 static void 00645 dns_check_entry(u8_t i) 00646 { 00647 struct dns_table_entry *pEntry = &dns_table[i]; 00648 00649 LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); 00650 00651 switch(pEntry->state) { 00652 00653 case DNS_STATE_NEW: { 00654 /* initialize new entry */ 00655 pEntry->state = DNS_STATE_ASKING; 00656 pEntry->numdns = 0; 00657 pEntry->tmr = 1; 00658 pEntry->retries = 0; 00659 00660 /* send DNS packet for this entry */ 00661 dns_send(pEntry->numdns, pEntry->name, i); 00662 break; 00663 } 00664 00665 case DNS_STATE_ASKING: { 00666 if (--pEntry->tmr == 0) { 00667 if (++pEntry->retries == DNS_MAX_RETRIES) { 00668 if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) { 00669 /* change of server */ 00670 pEntry->numdns++; 00671 pEntry->tmr = 1; 00672 pEntry->retries = 0; 00673 break; 00674 } else { 00675 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name)); 00676 /* call specified callback function if provided */ 00677 if (pEntry->found) 00678 (*pEntry->found)(pEntry->name, NULL, pEntry->arg); 00679 /* flush this entry */ 00680 pEntry->state = DNS_STATE_UNUSED; 00681 pEntry->found = NULL; 00682 break; 00683 } 00684 } 00685 00686 /* wait longer for the next retry */ 00687 pEntry->tmr = pEntry->retries; 00688 00689 /* send DNS packet for this entry */ 00690 dns_send(pEntry->numdns, pEntry->name, i); 00691 } 00692 break; 00693 } 00694 00695 case DNS_STATE_DONE: { 00696 /* if the time to live is nul */ 00697 if (--pEntry->ttl == 0) { 00698 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name)); 00699 /* flush this entry */ 00700 pEntry->state = DNS_STATE_UNUSED; 00701 pEntry->found = NULL; 00702 } 00703 break; 00704 } 00705 case DNS_STATE_UNUSED: 00706 /* nothing to do */ 00707 break; 00708 default: 00709 LWIP_ASSERT("unknown dns_table entry state:", 0); 00710 break; 00711 } 00712 } 00713 00714 /** 00715 * Call dns_check_entry for each entry in dns_table - check all entries. 00716 */ 00717 static void 00718 dns_check_entries(void) 00719 { 00720 u8_t i; 00721 00722 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 00723 dns_check_entry(i); 00724 } 00725 } 00726 00727 /** 00728 * Receive input function for DNS response packets arriving for the dns UDP pcb. 00729 * 00730 * @params see udp.h 00731 */ 00732 static void 00733 dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) 00734 { 00735 u16_t i; 00736 char *pHostname; 00737 struct dns_hdr *hdr; 00738 struct dns_answer ans; 00739 struct dns_table_entry *pEntry; 00740 u16_t nquestions, nanswers; 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 memerr; 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 memerr; 00759 } 00760 00761 /* copy dns payload inside static buffer for processing */ 00762 if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) { 00763 /* The ID in the DNS header should be our entry into the name table. */ 00764 hdr = (struct dns_hdr*)dns_payload; 00765 i = htons(hdr->id); 00766 if (i < DNS_TABLE_SIZE) { 00767 pEntry = &dns_table[i]; 00768 if(pEntry->state == DNS_STATE_ASKING) { 00769 /* This entry is now completed. */ 00770 pEntry->state = DNS_STATE_DONE; 00771 pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; 00772 00773 /* We only care about the question(s) and the answers. The authrr 00774 and the extrarr are simply discarded. */ 00775 nquestions = htons(hdr->numquestions); 00776 nanswers = htons(hdr->numanswers); 00777 00778 /* Check for error. If so, call callback to inform. */ 00779 if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) { 00780 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name)); 00781 /* call callback to indicate error, clean up memory and return */ 00782 goto responseerr; 00783 } 00784 00785 #if DNS_DOES_NAME_CHECK 00786 /* Check if the name in the "question" part match with the name in the entry. */ 00787 if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) { 00788 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name)); 00789 /* call callback to indicate error, clean up memory and return */ 00790 goto responseerr; 00791 } 00792 #endif /* DNS_DOES_NAME_CHECK */ 00793 00794 /* Skip the name in the "question" part */ 00795 pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY; 00796 00797 while (nanswers > 0) { 00798 /* skip answer resource record's host name */ 00799 pHostname = (char *) dns_parse_name((unsigned char *)pHostname); 00800 00801 /* Check for IP address type and Internet class. Others are discarded. */ 00802 SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER); 00803 if((ans.type == htons(DNS_RRTYPE_A)) && (ans.cls == htons(DNS_RRCLASS_IN)) && 00804 (ans.len == htons(sizeof(ip_addr_t))) ) { 00805 /* read the answer resource record's TTL, and maximize it if needed */ 00806 pEntry->ttl = ntohl(ans.ttl); 00807 if (pEntry->ttl > DNS_MAX_TTL) { 00808 pEntry->ttl = DNS_MAX_TTL; 00809 } 00810 /* read the IP address after answer resource record's header */ 00811 SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t)); 00812 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name)); 00813 ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr))); 00814 LWIP_DEBUGF(DNS_DEBUG, ("\n")); 00815 /* call specified callback function if provided */ 00816 if (pEntry->found) { 00817 (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg); 00818 } 00819 /* deallocate memory and return */ 00820 goto memerr; 00821 } else { 00822 pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len); 00823 } 00824 --nanswers; 00825 } 00826 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name)); 00827 /* call callback to indicate error, clean up memory and return */ 00828 goto responseerr; 00829 } 00830 } 00831 } 00832 00833 /* deallocate memory and return */ 00834 goto memerr; 00835 00836 responseerr: 00837 /* ERROR: call specified callback function with NULL as name to indicate an error */ 00838 if (pEntry->found) { 00839 (*pEntry->found)(pEntry->name, NULL, pEntry->arg); 00840 } 00841 /* flush this entry */ 00842 pEntry->state = DNS_STATE_UNUSED; 00843 pEntry->found = NULL; 00844 00845 memerr: 00846 /* free pbuf */ 00847 pbuf_free(p); 00848 return; 00849 } 00850 00851 /** 00852 * Queues a new hostname to resolve and sends out a DNS query for that hostname 00853 * 00854 * @param name the hostname that is to be queried 00855 * @param found a callback founction to be called on success, failure or timeout 00856 * @param callback_arg argument to pass to the callback function 00857 * @return @return a err_t return code. 00858 */ 00859 static err_t 00860 dns_enqueue(const char *name, dns_found_callback found, void *callback_arg) 00861 { 00862 u8_t i; 00863 u8_t lseq, lseqi; 00864 struct dns_table_entry *pEntry = NULL; 00865 size_t namelen; 00866 00867 /* search an unused entry, or the oldest one */ 00868 lseq = lseqi = 0; 00869 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 00870 pEntry = &dns_table[i]; 00871 /* is it an unused entry ? */ 00872 if (pEntry->state == DNS_STATE_UNUSED) 00873 break; 00874 00875 /* check if this is the oldest completed entry */ 00876 if (pEntry->state == DNS_STATE_DONE) { 00877 if ((dns_seqno - pEntry->seqno) > lseq) { 00878 lseq = dns_seqno - pEntry->seqno; 00879 lseqi = i; 00880 } 00881 } 00882 } 00883 00884 /* if we don't have found an unused entry, use the oldest completed one */ 00885 if (i == DNS_TABLE_SIZE) { 00886 if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { 00887 /* no entry can't be used now, table is full */ 00888 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); 00889 return ERR_MEM; 00890 } else { 00891 /* use the oldest completed one */ 00892 i = lseqi; 00893 pEntry = &dns_table[i]; 00894 } 00895 } 00896 00897 /* use this entry */ 00898 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); 00899 00900 /* fill the entry */ 00901 pEntry->state = DNS_STATE_NEW; 00902 pEntry->seqno = dns_seqno++; 00903 pEntry->found = found; 00904 pEntry->arg = callback_arg; 00905 namelen = LWIP_MIN(strlen(name), DNS_MAX_NAME_LENGTH-1); 00906 MEMCPY(pEntry->name, name, namelen); 00907 pEntry->name[namelen] = 0; 00908 00909 /* force to send query without waiting timer */ 00910 dns_check_entry(i); 00911 00912 /* dns query is enqueued */ 00913 return ERR_INPROGRESS; 00914 } 00915 00916 /** 00917 * Resolve a hostname (string) into an IP address. 00918 * NON-BLOCKING callback version for use with raw API!!! 00919 * 00920 * Returns immediately with one of err_t return codes: 00921 * - ERR_OK if hostname is a valid IP address string or the host 00922 * name is already in the local names table. 00923 * - ERR_INPROGRESS enqueue a request to be sent to the DNS server 00924 * for resolution if no errors are present. 00925 * 00926 * @param hostname the hostname that is to be queried 00927 * @param addr pointer to a ip_addr_t where to store the address if it is already 00928 * cached in the dns_table (only valid if ERR_OK is returned!) 00929 * @param found a callback function to be called on success, failure or timeout (only if 00930 * ERR_INPROGRESS is returned!) 00931 * @param callback_arg argument to pass to the callback function 00932 * @return a err_t return code. 00933 */ 00934 err_t 00935 dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, 00936 void *callback_arg) 00937 { 00938 u32_t ipaddr; 00939 /* not initialized or no valid server yet, or invalid addr pointer 00940 * or invalid hostname or invalid hostname length */ 00941 if ((dns_pcb == NULL) || (addr == NULL) || 00942 (!hostname) || (!hostname[0]) || 00943 (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) { 00944 return ERR_VAL; 00945 } 00946 00947 #if LWIP_HAVE_LOOPIF 00948 if (strcmp(hostname, "localhost")==0) { 00949 ip_addr_set_loopback(addr); 00950 return ERR_OK; 00951 } 00952 #endif /* LWIP_HAVE_LOOPIF */ 00953 00954 /* host name already in octet notation? set ip addr and return ERR_OK */ 00955 ipaddr = ipaddr_addr(hostname); 00956 if (ipaddr == IPADDR_NONE) { 00957 /* already have this address cached? */ 00958 ipaddr = dns_lookup(hostname); 00959 } 00960 if (ipaddr != IPADDR_NONE) { 00961 ip4_addr_set_u32(addr, ipaddr); 00962 return ERR_OK; 00963 } 00964 00965 /* queue query with specified callback */ 00966 return dns_enqueue(hostname, found, callback_arg); 00967 } 00968 #endif /* LWIP_DNS */
Generated on Tue Jul 12 2022 21:10:25 by
1.7.2