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/memp.h" 00082 #include "lwip/dns.h" 00083 00084 #include <string.h> 00085 00086 /** DNS server IP address */ 00087 #ifndef DNS_SERVER_ADDRESS 00088 #define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */ 00089 #endif 00090 00091 /** DNS server port address */ 00092 #ifndef DNS_SERVER_PORT 00093 #define DNS_SERVER_PORT 53 00094 #endif 00095 00096 /** DNS maximum number of retries when asking for a name, before "timeout". */ 00097 #ifndef DNS_MAX_RETRIES 00098 #define DNS_MAX_RETRIES 4 00099 #endif 00100 00101 /** DNS resource record max. TTL (one week as default) */ 00102 #ifndef DNS_MAX_TTL 00103 #define DNS_MAX_TTL 604800 00104 #endif 00105 00106 /* DNS protocol flags */ 00107 #define DNS_FLAG1_RESPONSE 0x80 00108 #define DNS_FLAG1_OPCODE_STATUS 0x10 00109 #define DNS_FLAG1_OPCODE_INVERSE 0x08 00110 #define DNS_FLAG1_OPCODE_STANDARD 0x00 00111 #define DNS_FLAG1_AUTHORATIVE 0x04 00112 #define DNS_FLAG1_TRUNC 0x02 00113 #define DNS_FLAG1_RD 0x01 00114 #define DNS_FLAG2_RA 0x80 00115 #define DNS_FLAG2_ERR_MASK 0x0f 00116 #define DNS_FLAG2_ERR_NONE 0x00 00117 #define DNS_FLAG2_ERR_NAME 0x03 00118 00119 /* DNS protocol states */ 00120 #define DNS_STATE_UNUSED 0 00121 #define DNS_STATE_NEW 1 00122 #define DNS_STATE_ASKING 2 00123 #define DNS_STATE_DONE 3 00124 00125 #ifdef PACK_STRUCT_USE_INCLUDES 00126 # include "arch/bpstruct.h" 00127 #endif 00128 PACK_STRUCT_BEGIN 00129 /** DNS message header */ 00130 struct dns_hdr { 00131 PACK_STRUCT_FIELD(u16_t id); 00132 PACK_STRUCT_FIELD(u8_t flags1); 00133 PACK_STRUCT_FIELD(u8_t flags2); 00134 PACK_STRUCT_FIELD(u16_t numquestions); 00135 PACK_STRUCT_FIELD(u16_t numanswers); 00136 PACK_STRUCT_FIELD(u16_t numauthrr); 00137 PACK_STRUCT_FIELD(u16_t numextrarr); 00138 } PACK_STRUCT_STRUCT; 00139 PACK_STRUCT_END 00140 #ifdef PACK_STRUCT_USE_INCLUDES 00141 # include "arch/epstruct.h" 00142 #endif 00143 #define SIZEOF_DNS_HDR 12 00144 00145 /** DNS query message structure. 00146 No packing needed: only used locally on the stack. */ 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 }; 00153 #define SIZEOF_DNS_QUERY 4 00154 00155 /** DNS answer message structure. 00156 No packing needed: only used locally on the stack. */ 00157 struct dns_answer { 00158 /* DNS answer record starts with either a domain name or a pointer 00159 to a name already present somewhere in the packet. */ 00160 u16_t type; 00161 u16_t cls; 00162 u32_t ttl; 00163 u16_t len; 00164 }; 00165 #define SIZEOF_DNS_ANSWER 10 00166 00167 /** DNS table entry */ 00168 struct dns_table_entry { 00169 u8_t state; 00170 u8_t numdns; 00171 u8_t tmr; 00172 u8_t retries; 00173 u8_t seqno; 00174 u8_t err; 00175 u32_t ttl; 00176 char name[DNS_MAX_NAME_LENGTH]; 00177 ip_addr_t ipaddr; 00178 /* pointer to callback on DNS query done */ 00179 dns_found_callback found; 00180 void *arg; 00181 }; 00182 00183 #if DNS_LOCAL_HOSTLIST 00184 00185 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00186 /** Local host-list. For hostnames in this list, no 00187 * external name resolution is performed */ 00188 static struct local_hostlist_entry *local_hostlist_dynamic; 00189 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00190 00191 /** Defining this allows the local_hostlist_static to be placed in a different 00192 * linker section (e.g. FLASH) */ 00193 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE 00194 #define DNS_LOCAL_HOSTLIST_STORAGE_PRE static 00195 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */ 00196 /** Defining this allows the local_hostlist_static to be placed in a different 00197 * linker section (e.g. FLASH) */ 00198 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST 00199 #define DNS_LOCAL_HOSTLIST_STORAGE_POST 00200 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */ 00201 DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[] 00202 DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT; 00203 00204 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00205 00206 static void dns_init_local(); 00207 #endif /* DNS_LOCAL_HOSTLIST */ 00208 00209 00210 /* forward declarations */ 00211 static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); 00212 static void dns_check_entries(void); 00213 00214 /*----------------------------------------------------------------------------- 00215 * Globales 00216 *----------------------------------------------------------------------------*/ 00217 00218 /* DNS variables */ 00219 static struct udp_pcb *dns_pcb; 00220 static u8_t dns_seqno; 00221 static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; 00222 static ip_addr_t dns_servers[DNS_MAX_SERVERS]; 00223 /** Contiguous buffer for processing responses */ 00224 static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)]; 00225 static u8_t* dns_payload; 00226 00227 /** 00228 * Initialize the resolver: set up the UDP pcb and configure the default server 00229 * (DNS_SERVER_ADDRESS). 00230 */ 00231 void 00232 dns_init() 00233 { 00234 ip_addr_t dnsserver; 00235 00236 dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer); 00237 00238 /* initialize default DNS server address */ 00239 DNS_SERVER_ADDRESS(&dnsserver); 00240 00241 LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); 00242 00243 /* if dns client not yet initialized... */ 00244 if (dns_pcb == NULL) { 00245 dns_pcb = udp_new(); 00246 00247 if (dns_pcb != NULL) { 00248 /* initialize DNS table not needed (initialized to zero since it is a 00249 * global variable) */ 00250 LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", 00251 DNS_STATE_UNUSED == 0); 00252 00253 /* initialize DNS client */ 00254 udp_bind(dns_pcb, IP_ADDR_ANY, 0); 00255 udp_recv(dns_pcb, dns_recv, NULL); 00256 00257 /* initialize default DNS primary server */ 00258 dns_setserver(0, &dnsserver); 00259 } 00260 } 00261 #if DNS_LOCAL_HOSTLIST 00262 dns_init_local(); 00263 #endif 00264 } 00265 00266 /** 00267 * Initialize one of the DNS servers. 00268 * 00269 * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS 00270 * @param dnsserver IP address of the DNS server to set 00271 */ 00272 void 00273 dns_setserver(u8_t numdns, ip_addr_t *dnsserver) 00274 { 00275 if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) && 00276 (dnsserver != NULL) && !ip_addr_isany(dnsserver)) { 00277 dns_servers[numdns] = (*dnsserver); 00278 } 00279 } 00280 00281 /** 00282 * Obtain one of the currently configured DNS server. 00283 * 00284 * @param numdns the index of the DNS server 00285 * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS 00286 * server has not been configured. 00287 */ 00288 ip_addr_t 00289 dns_getserver(u8_t numdns) 00290 { 00291 if (numdns < DNS_MAX_SERVERS) { 00292 return dns_servers[numdns]; 00293 } else { 00294 return *IP_ADDR_ANY; 00295 } 00296 } 00297 00298 /** 00299 * The DNS resolver client timer - handle retries and timeouts and should 00300 * be called every DNS_TMR_INTERVAL milliseconds (every second by default). 00301 */ 00302 void 00303 dns_tmr(void) 00304 { 00305 if (dns_pcb != NULL) { 00306 LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); 00307 dns_check_entries(); 00308 } 00309 } 00310 00311 #if DNS_LOCAL_HOSTLIST 00312 static void 00313 dns_init_local() 00314 { 00315 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) 00316 int i; 00317 struct local_hostlist_entry *entry; 00318 /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */ 00319 struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT; 00320 size_t namelen; 00321 for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) { 00322 struct local_hostlist_entry *init_entry = &local_hostlist_init[i]; 00323 LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL); 00324 namelen = strlen(init_entry->name); 00325 LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); 00326 entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); 00327 LWIP_ASSERT("mem-error in dns_init_local", entry != NULL); 00328 if (entry != NULL) { 00329 entry->name = (char*)entry + sizeof(struct local_hostlist_entry); 00330 MEMCPY((char*)entry->name, init_entry->name, namelen); 00331 ((char*)entry->name)[namelen] = 0; 00332 entry->addr = init_entry->addr; 00333 entry->next = local_hostlist_dynamic; 00334 local_hostlist_dynamic = entry; 00335 } 00336 } 00337 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */ 00338 } 00339 00340 /** 00341 * Scans the local host-list for a hostname. 00342 * 00343 * @param hostname Hostname to look for in the local host-list 00344 * @return The first IP address for the hostname in the local host-list or 00345 * IPADDR_NONE if not found. 00346 */ 00347 static u32_t 00348 dns_lookup_local(const char *hostname) 00349 { 00350 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00351 struct local_hostlist_entry *entry = local_hostlist_dynamic; 00352 while(entry != NULL) { 00353 if(strcmp(entry->name, hostname) == 0) { 00354 return ip4_addr_get_u32(&entry->addr); 00355 } 00356 entry = entry->next; 00357 } 00358 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00359 int i; 00360 for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) { 00361 if(strcmp(local_hostlist_static[i].name, hostname) == 0) { 00362 return ip4_addr_get_u32(&local_hostlist_static[i].addr); 00363 } 00364 } 00365 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00366 return IPADDR_NONE; 00367 } 00368 00369 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00370 /** Remove all entries from the local host-list for a specific hostname 00371 * and/or IP addess 00372 * 00373 * @param hostname hostname for which entries shall be removed from the local 00374 * host-list 00375 * @param addr address for which entries shall be removed from the local host-list 00376 * @return the number of removed entries 00377 */ 00378 int 00379 dns_local_removehost(const char *hostname, const ip_addr_t *addr) 00380 { 00381 int removed = 0; 00382 struct local_hostlist_entry *entry = local_hostlist_dynamic; 00383 struct local_hostlist_entry *last_entry = NULL; 00384 while (entry != NULL) { 00385 if (((hostname == NULL) || !strcmp(entry->name, hostname)) && 00386 ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) { 00387 struct local_hostlist_entry *free_entry; 00388 if (last_entry != NULL) { 00389 last_entry->next = entry->next; 00390 } else { 00391 local_hostlist_dynamic = entry->next; 00392 } 00393 free_entry = entry; 00394 entry = entry->next; 00395 memp_free(MEMP_LOCALHOSTLIST, free_entry); 00396 removed++; 00397 } else { 00398 last_entry = entry; 00399 entry = entry->next; 00400 } 00401 } 00402 return removed; 00403 } 00404 00405 /** 00406 * Add a hostname/IP address pair to the local host-list. 00407 * Duplicates are not checked. 00408 * 00409 * @param hostname hostname of the new entry 00410 * @param addr IP address of the new entry 00411 * @return ERR_OK if succeeded or ERR_MEM on memory error 00412 */ 00413 err_t 00414 dns_local_addhost(const char *hostname, const ip_addr_t *addr) 00415 { 00416 struct local_hostlist_entry *entry; 00417 size_t namelen; 00418 LWIP_ASSERT("invalid host name (NULL)", hostname != NULL); 00419 namelen = strlen(hostname); 00420 LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); 00421 entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); 00422 if (entry == NULL) { 00423 return ERR_MEM; 00424 } 00425 entry->name = (char*)entry + sizeof(struct local_hostlist_entry); 00426 MEMCPY((char*)entry->name, hostname, namelen); 00427 ((char*)entry->name)[namelen] = 0; 00428 ip_addr_copy(entry->addr, *addr); 00429 entry->next = local_hostlist_dynamic; 00430 local_hostlist_dynamic = entry; 00431 return ERR_OK; 00432 } 00433 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/ 00434 #endif /* DNS_LOCAL_HOSTLIST */ 00435 00436 /** 00437 * Look up a hostname in the array of known hostnames. 00438 * 00439 * @note This function only looks in the internal array of known 00440 * hostnames, it does not send out a query for the hostname if none 00441 * was found. The function dns_enqueue() can be used to send a query 00442 * for a hostname. 00443 * 00444 * @param name the hostname to look up 00445 * @return the hostname's IP address, as u32_t (instead of ip_addr_t to 00446 * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname 00447 * was not found in the cached dns_table. 00448 */ 00449 static u32_t 00450 dns_lookup(const char *name) 00451 { 00452 u8_t i; 00453 #if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) 00454 u32_t addr; 00455 #endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */ 00456 #if DNS_LOCAL_HOSTLIST 00457 if ((addr = dns_lookup_local(name)) != IPADDR_NONE) { 00458 return addr; 00459 } 00460 #endif /* DNS_LOCAL_HOSTLIST */ 00461 #ifdef DNS_LOOKUP_LOCAL_EXTERN 00462 if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) { 00463 return addr; 00464 } 00465 #endif /* DNS_LOOKUP_LOCAL_EXTERN */ 00466 00467 /* Walk through name list, return entry if found. If not, return NULL. */ 00468 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 00469 if ((dns_table[i].state == DNS_STATE_DONE) && 00470 (strcmp(name, dns_table[i].name) == 0)) { 00471 LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); 00472 ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); 00473 LWIP_DEBUGF(DNS_DEBUG, ("\n")); 00474 return ip4_addr_get_u32(&dns_table[i].ipaddr); 00475 } 00476 } 00477 00478 return IPADDR_NONE; 00479 } 00480 00481 #if DNS_DOES_NAME_CHECK 00482 /** 00483 * Compare the "dotted" name "query" with the encoded name "response" 00484 * to make sure an answer from the DNS server matches the current dns_table 00485 * entry (otherwise, answers might arrive late for hostname not on the list 00486 * any more). 00487 * 00488 * @param query hostname (not encoded) from the dns_table 00489 * @param response encoded hostname in the DNS response 00490 * @return 0: names equal; 1: names differ 00491 */ 00492 static u8_t 00493 dns_compare_name(unsigned char *query, unsigned char *response) 00494 { 00495 unsigned char n; 00496 00497 do { 00498 n = *response++; 00499 /** @see RFC 1035 - 4.1.4. Message compression */ 00500 if ((n & 0xc0) == 0xc0) { 00501 /* Compressed name */ 00502 break; 00503 } else { 00504 /* Not compressed name */ 00505 while (n > 0) { 00506 if ((*query) != (*response)) { 00507 return 1; 00508 } 00509 ++response; 00510 ++query; 00511 --n; 00512 }; 00513 ++query; 00514 } 00515 } while (*response != 0); 00516 00517 return 0; 00518 } 00519 #endif /* DNS_DOES_NAME_CHECK */ 00520 00521 /** 00522 * Walk through a compact encoded DNS name and return the end of the name. 00523 * 00524 * @param query encoded DNS name in the DNS server response 00525 * @return end of the name 00526 */ 00527 static unsigned char * 00528 dns_parse_name(unsigned char *query) 00529 { 00530 unsigned char n; 00531 00532 do { 00533 n = *query++; 00534 /** @see RFC 1035 - 4.1.4. Message compression */ 00535 if ((n & 0xc0) == 0xc0) { 00536 /* Compressed name */ 00537 break; 00538 } else { 00539 /* Not compressed name */ 00540 while (n > 0) { 00541 ++query; 00542 --n; 00543 }; 00544 } 00545 } while (*query != 0); 00546 00547 return query + 1; 00548 } 00549 00550 /** 00551 * Send a DNS query packet. 00552 * 00553 * @param numdns index of the DNS server in the dns_servers table 00554 * @param name hostname to query 00555 * @param id index of the hostname in dns_table, used as transaction ID in the 00556 * DNS query packet 00557 * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise 00558 */ 00559 static err_t 00560 dns_send(u8_t numdns, const char* name, u8_t id) 00561 { 00562 err_t err; 00563 struct dns_hdr *hdr; 00564 struct dns_query qry; 00565 struct pbuf *p; 00566 char *query, *nptr; 00567 const char *pHostname; 00568 u8_t n; 00569 00570 LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", 00571 (u16_t)(numdns), name)); 00572 LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS); 00573 LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns])); 00574 00575 /* if here, we have either a new query or a retry on a previous query to process */ 00576 p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH + 00577 SIZEOF_DNS_QUERY, PBUF_RAM); 00578 if (p != NULL) { 00579 LWIP_ASSERT("pbuf must be in one piece", p->next == NULL); 00580 /* fill dns header */ 00581 hdr = (struct dns_hdr*)p->payload; 00582 memset(hdr, 0, SIZEOF_DNS_HDR); 00583 hdr->id = htons(id); 00584 hdr->flags1 = DNS_FLAG1_RD; 00585 hdr->numquestions = PP_HTONS(1); 00586 query = (char*)hdr + SIZEOF_DNS_HDR; 00587 pHostname = name; 00588 --pHostname; 00589 00590 /* convert hostname into suitable query format. */ 00591 do { 00592 ++pHostname; 00593 nptr = query; 00594 ++query; 00595 for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) { 00596 *query = *pHostname; 00597 ++query; 00598 ++n; 00599 } 00600 *nptr = n; 00601 } while(*pHostname != 0); 00602 *query++='\0'; 00603 00604 /* fill dns query */ 00605 qry.type = PP_HTONS(DNS_RRTYPE_A); 00606 qry.cls = PP_HTONS(DNS_RRCLASS_IN); 00607 SMEMCPY(query, &qry, SIZEOF_DNS_QUERY); 00608 00609 /* resize pbuf to the exact dns query */ 00610 pbuf_realloc(p, (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload)))); 00611 00612 /* connect to the server for faster receiving */ 00613 udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT); 00614 /* send dns packet */ 00615 err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT); 00616 00617 /* free pbuf */ 00618 pbuf_free(p); 00619 } else { 00620 err = ERR_MEM; 00621 } 00622 00623 return err; 00624 } 00625 00626 /** 00627 * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query. 00628 * Check an entry in the dns_table: 00629 * - send out query for new entries 00630 * - retry old pending entries on timeout (also with different servers) 00631 * - remove completed entries from the table if their TTL has expired 00632 * 00633 * @param i index of the dns_table entry to check 00634 */ 00635 static void 00636 dns_check_entry(u8_t i) 00637 { 00638 err_t err; 00639 struct dns_table_entry *pEntry = &dns_table[i]; 00640 00641 LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); 00642 00643 switch(pEntry->state) { 00644 00645 case DNS_STATE_NEW: { 00646 /* initialize new entry */ 00647 pEntry->state = DNS_STATE_ASKING; 00648 pEntry->numdns = 0; 00649 pEntry->tmr = 1; 00650 pEntry->retries = 0; 00651 00652 /* send DNS packet for this entry */ 00653 err = dns_send(pEntry->numdns, pEntry->name, i); 00654 if (err != ERR_OK) { 00655 LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, 00656 ("dns_send returned error: %s\n", lwip_strerr(err))); 00657 } 00658 break; 00659 } 00660 00661 case DNS_STATE_ASKING: { 00662 if (--pEntry->tmr == 0) { 00663 if (++pEntry->retries == DNS_MAX_RETRIES) { 00664 if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) { 00665 /* change of server */ 00666 pEntry->numdns++; 00667 pEntry->tmr = 1; 00668 pEntry->retries = 0; 00669 break; 00670 } else { 00671 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name)); 00672 /* call specified callback function if provided */ 00673 if (pEntry->found) 00674 (*pEntry->found)(pEntry->name, NULL, pEntry->arg); 00675 /* flush this entry */ 00676 pEntry->state = DNS_STATE_UNUSED; 00677 pEntry->found = NULL; 00678 break; 00679 } 00680 } 00681 00682 /* wait longer for the next retry */ 00683 pEntry->tmr = pEntry->retries; 00684 00685 /* send DNS packet for this entry */ 00686 err = dns_send(pEntry->numdns, pEntry->name, i); 00687 if (err != ERR_OK) { 00688 LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, 00689 ("dns_send returned error: %s\n", lwip_strerr(err))); 00690 } 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 == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) && 00804 (ans.len == PP_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 * - ERR_ARG: dns client not initialized or invalid hostname 00926 * 00927 * @param hostname the hostname that is to be queried 00928 * @param addr pointer to a ip_addr_t where to store the address if it is already 00929 * cached in the dns_table (only valid if ERR_OK is returned!) 00930 * @param found a callback function to be called on success, failure or timeout (only if 00931 * ERR_INPROGRESS is returned!) 00932 * @param callback_arg argument to pass to the callback function 00933 * @return a err_t return code. 00934 */ 00935 err_t 00936 dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, 00937 void *callback_arg) 00938 { 00939 u32_t ipaddr; 00940 /* not initialized or no valid server yet, or invalid addr pointer 00941 * or invalid hostname or invalid hostname length */ 00942 if ((dns_pcb == NULL) || (addr == NULL) || 00943 (!hostname) || (!hostname[0]) || 00944 (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) { 00945 return ERR_ARG; 00946 } 00947 00948 #if LWIP_HAVE_LOOPIF 00949 if (strcmp(hostname, "localhost")==0) { 00950 ip_addr_set_loopback(addr); 00951 return ERR_OK; 00952 } 00953 #endif /* LWIP_HAVE_LOOPIF */ 00954 00955 /* host name already in octet notation? set ip addr and return ERR_OK */ 00956 ipaddr = ipaddr_addr(hostname); 00957 if (ipaddr == IPADDR_NONE) { 00958 /* already have this address cached? */ 00959 ipaddr = dns_lookup(hostname); 00960 } 00961 if (ipaddr != IPADDR_NONE) { 00962 ip4_addr_set_u32(addr, ipaddr); 00963 return ERR_OK; 00964 } 00965 00966 /* queue query with specified callback */ 00967 return dns_enqueue(hostname, found, callback_arg); 00968 } 00969 00970 #endif /* LWIP_DNS */
Generated on Tue Jul 12 2022 17:34:41 by
