Darran Shepherd
/
AutoIpNetStack
Net stack with AutoIP enabled
Embed:
(wiki syntax)
Show/hide line numbers
dns.c
Go to the documentation of this file.
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 15:37:03 by 1.7.2