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