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.
Fork of lwip by
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 memset(&qry, 0, sizeof(qry)); 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: %s\n", lwip_strerr(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: %s\n", lwip_strerr(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 * - ERR_ARG: dns client not initialized or invalid hostname 00927 * 00928 * @param hostname the hostname that is to be queried 00929 * @param addr pointer to a ip_addr_t where to store the address if it is already 00930 * cached in the dns_table (only valid if ERR_OK is returned!) 00931 * @param found a callback function to be called on success, failure or timeout (only if 00932 * ERR_INPROGRESS is returned!) 00933 * @param callback_arg argument to pass to the callback function 00934 * @return a err_t return code. 00935 */ 00936 err_t 00937 dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, 00938 void *callback_arg) 00939 { 00940 u32_t ipaddr; 00941 /* not initialized or no valid server yet, or invalid addr pointer 00942 * or invalid hostname or invalid hostname length */ 00943 if ((dns_pcb == NULL) || (addr == NULL) || 00944 (!hostname) || (!hostname[0]) || 00945 (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) { 00946 return ERR_ARG; 00947 } 00948 00949 #if LWIP_HAVE_LOOPIF 00950 if (strcmp(hostname, "localhost")==0) { 00951 ip_addr_set_loopback(addr); 00952 return ERR_OK; 00953 } 00954 #endif /* LWIP_HAVE_LOOPIF */ 00955 00956 /* host name already in octet notation? set ip addr and return ERR_OK */ 00957 ipaddr = ipaddr_addr(hostname); 00958 if (ipaddr == IPADDR_NONE) { 00959 /* already have this address cached? */ 00960 ipaddr = dns_lookup(hostname); 00961 } 00962 if (ipaddr != IPADDR_NONE) { 00963 ip4_addr_set_u32(addr, ipaddr); 00964 return ERR_OK; 00965 } 00966 00967 /* queue query with specified callback */ 00968 return dns_enqueue(hostname, found, callback_arg); 00969 } 00970 00971 #endif /* LWIP_DNS */
Generated on Tue Jul 12 2022 13:44:40 by
1.7.2
