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 mbed-os by
lwip_dns.c
00001 /** 00002 * @file 00003 * DNS - host name to IP address resolver. 00004 */ 00005 00006 /* 00007 * Port to lwIP from uIP 00008 * by Jim Pettinato April 2007 00009 * 00010 * security fixes and more by Simon Goldschmidt 00011 * 00012 * uIP version Copyright (c) 2002-2003, Adam Dunkels. 00013 * All rights reserved. 00014 * 00015 * Redistribution and use in source and binary forms, with or without 00016 * modification, are permitted provided that the following conditions 00017 * are met: 00018 * 1. Redistributions of source code must retain the above copyright 00019 * notice, this list of conditions and the following disclaimer. 00020 * 2. Redistributions in binary form must reproduce the above copyright 00021 * notice, this list of conditions and the following disclaimer in the 00022 * documentation and/or other materials provided with the distribution. 00023 * 3. The name of the author may not be used to endorse or promote 00024 * products derived from this software without specific prior 00025 * written permission. 00026 * 00027 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 00028 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 00029 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00030 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 00031 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00032 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 00033 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00034 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 00035 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00036 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00037 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00038 */ 00039 00040 /** 00041 * @defgroup dns DNS 00042 * @ingroup callbackstyle_api 00043 * 00044 * Implements a DNS host name to IP address resolver. 00045 * 00046 * The lwIP DNS resolver functions are used to lookup a host name and 00047 * map it to a numerical IP address. It maintains a list of resolved 00048 * hostnames that can be queried with the dns_lookup() function. 00049 * New hostnames can be resolved using the dns_query() function. 00050 * 00051 * The lwIP version of the resolver also adds a non-blocking version of 00052 * gethostbyname() that will work with a raw API application. This function 00053 * checks for an IP address string first and converts it if it is valid. 00054 * gethostbyname() then does a dns_lookup() to see if the name is 00055 * already in the table. If so, the IP is returned. If not, a query is 00056 * issued and the function returns with a ERR_INPROGRESS status. The app 00057 * using the dns client must then go into a waiting state. 00058 * 00059 * Once a hostname has been resolved (or found to be non-existent), 00060 * the resolver code calls a specified callback function (which 00061 * must be implemented by the module that uses the resolver). 00062 * 00063 * All functions must be called from TCPIP thread. 00064 * 00065 * @see @ref netconn_common for thread-safe access. 00066 */ 00067 00068 /*----------------------------------------------------------------------------- 00069 * RFC 1035 - Domain names - implementation and specification 00070 * RFC 2181 - Clarifications to the DNS Specification 00071 *----------------------------------------------------------------------------*/ 00072 00073 /** @todo: define good default values (rfc compliance) */ 00074 /** @todo: improve answer parsing, more checkings... */ 00075 /** @todo: check RFC1035 - 7.3. Processing responses */ 00076 00077 /*----------------------------------------------------------------------------- 00078 * Includes 00079 *----------------------------------------------------------------------------*/ 00080 00081 #include "lwip/opt.h" 00082 00083 #if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ 00084 00085 #include "lwip/udp.h" 00086 #include "lwip/mem.h" 00087 #include "lwip/memp.h" 00088 #include "lwip/dns.h" 00089 00090 #include <string.h> 00091 00092 /** Random generator function to create random TXIDs and source ports for queries */ 00093 #ifndef DNS_RAND_TXID 00094 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0) 00095 #define DNS_RAND_TXID LWIP_RAND 00096 #else 00097 static u16_t dns_txid; 00098 #define DNS_RAND_TXID() (++dns_txid) 00099 #endif 00100 #endif 00101 00102 /** Limits the source port to be >= 1024 by default */ 00103 #ifndef DNS_PORT_ALLOWED 00104 #define DNS_PORT_ALLOWED(port) ((port) >= 1024) 00105 #endif 00106 00107 /** DNS server port address */ 00108 #ifndef DNS_SERVER_PORT 00109 #define DNS_SERVER_PORT 53 00110 #endif 00111 00112 /** DNS maximum number of retries when asking for a name, before "timeout". */ 00113 #ifndef DNS_MAX_RETRIES 00114 #define DNS_MAX_RETRIES 4 00115 #endif 00116 00117 /** DNS resource record max. TTL (one week as default) */ 00118 #ifndef DNS_MAX_TTL 00119 #define DNS_MAX_TTL 604800 00120 #elif DNS_MAX_TTL > 0x7FFFFFFF 00121 #error DNS_MAX_TTL must be a positive 32-bit value 00122 #endif 00123 00124 /* The number of parallel requests (i.e. calls to dns_gethostbyname 00125 * that cannot be answered from the DNS table. 00126 * This is set to the table size by default. 00127 */ 00128 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) 00129 #ifndef DNS_MAX_REQUESTS 00130 #define DNS_MAX_REQUESTS DNS_TABLE_SIZE 00131 #endif 00132 #else 00133 /* In this configuration, both arrays have to have the same size and are used 00134 * like one entry (used/free) */ 00135 #define DNS_MAX_REQUESTS DNS_TABLE_SIZE 00136 #endif 00137 00138 /* The number of UDP source ports used in parallel */ 00139 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) 00140 #ifndef DNS_MAX_SOURCE_PORTS 00141 #define DNS_MAX_SOURCE_PORTS DNS_MAX_REQUESTS 00142 #endif 00143 #else 00144 #ifdef DNS_MAX_SOURCE_PORTS 00145 #undef DNS_MAX_SOURCE_PORTS 00146 #endif 00147 #define DNS_MAX_SOURCE_PORTS 1 00148 #endif 00149 00150 #if LWIP_IPV4 && LWIP_IPV6 00151 #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) (((t) == LWIP_DNS_ADDRTYPE_IPV6_IPV4) || ((t) == LWIP_DNS_ADDRTYPE_IPV6)) 00152 #define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) (IP_IS_V6_VAL(ip) ? LWIP_DNS_ADDRTYPE_IS_IPV6(t) : (!LWIP_DNS_ADDRTYPE_IS_IPV6(t))) 00153 #define LWIP_DNS_ADDRTYPE_ARG(x) , x 00154 #define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) x 00155 #define LWIP_DNS_SET_ADDRTYPE(x, y) do { x = y; } while(0) 00156 #else 00157 #if LWIP_IPV6 00158 #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 1 00159 #else 00160 #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 0 00161 #endif 00162 #define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) 1 00163 #define LWIP_DNS_ADDRTYPE_ARG(x) 00164 #define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) 0 00165 #define LWIP_DNS_SET_ADDRTYPE(x, y) 00166 #endif /* LWIP_IPV4 && LWIP_IPV6 */ 00167 00168 /** DNS field TYPE used for "Resource Records" */ 00169 #define DNS_RRTYPE_A 1 /* a host address */ 00170 #define DNS_RRTYPE_NS 2 /* an authoritative name server */ 00171 #define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */ 00172 #define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */ 00173 #define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */ 00174 #define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */ 00175 #define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */ 00176 #define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */ 00177 #define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */ 00178 #define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */ 00179 #define DNS_RRTYPE_WKS 11 /* a well known service description */ 00180 #define DNS_RRTYPE_PTR 12 /* a domain name pointer */ 00181 #define DNS_RRTYPE_HINFO 13 /* host information */ 00182 #define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */ 00183 #define DNS_RRTYPE_MX 15 /* mail exchange */ 00184 #define DNS_RRTYPE_TXT 16 /* text strings */ 00185 #define DNS_RRTYPE_AAAA 28 /* IPv6 address */ 00186 00187 /** DNS field CLASS used for "Resource Records" */ 00188 #define DNS_RRCLASS_IN 1 /* the Internet */ 00189 #define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */ 00190 #define DNS_RRCLASS_CH 3 /* the CHAOS class */ 00191 #define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */ 00192 #define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */ 00193 00194 /* DNS protocol flags */ 00195 #define DNS_FLAG1_RESPONSE 0x80 00196 #define DNS_FLAG1_OPCODE_STATUS 0x10 00197 #define DNS_FLAG1_OPCODE_INVERSE 0x08 00198 #define DNS_FLAG1_OPCODE_STANDARD 0x00 00199 #define DNS_FLAG1_AUTHORATIVE 0x04 00200 #define DNS_FLAG1_TRUNC 0x02 00201 #define DNS_FLAG1_RD 0x01 00202 #define DNS_FLAG2_RA 0x80 00203 #define DNS_FLAG2_ERR_MASK 0x0f 00204 #define DNS_FLAG2_ERR_NONE 0x00 00205 #define DNS_FLAG2_ERR_NAME 0x03 00206 00207 /* DNS protocol states */ 00208 #define DNS_STATE_UNUSED 0 00209 #define DNS_STATE_NEW 1 00210 #define DNS_STATE_ASKING 2 00211 #define DNS_STATE_DONE 3 00212 00213 #ifdef PACK_STRUCT_USE_INCLUDES 00214 # include "arch/bpstruct.h" 00215 #endif 00216 PACK_STRUCT_BEGIN 00217 /** DNS message header */ 00218 struct dns_hdr { 00219 PACK_STRUCT_FIELD(u16_t id); 00220 PACK_STRUCT_FLD_8(u8_t flags1); 00221 PACK_STRUCT_FLD_8(u8_t flags2); 00222 PACK_STRUCT_FIELD(u16_t numquestions); 00223 PACK_STRUCT_FIELD(u16_t numanswers); 00224 PACK_STRUCT_FIELD(u16_t numauthrr); 00225 PACK_STRUCT_FIELD(u16_t numextrarr); 00226 } PACK_STRUCT_STRUCT; 00227 PACK_STRUCT_END 00228 #ifdef PACK_STRUCT_USE_INCLUDES 00229 # include "arch/epstruct.h" 00230 #endif 00231 #define SIZEOF_DNS_HDR 12 00232 00233 /** DNS query message structure. 00234 No packing needed: only used locally on the stack. */ 00235 struct dns_query { 00236 /* DNS query record starts with either a domain name or a pointer 00237 to a name already present somewhere in the packet. */ 00238 u16_t type; 00239 u16_t cls; 00240 }; 00241 #define SIZEOF_DNS_QUERY 4 00242 00243 /** DNS answer message structure. 00244 No packing needed: only used locally on the stack. */ 00245 struct dns_answer { 00246 /* DNS answer record starts with either a domain name or a pointer 00247 to a name already present somewhere in the packet. */ 00248 u16_t type; 00249 u16_t cls; 00250 u32_t ttl; 00251 u16_t len; 00252 }; 00253 #define SIZEOF_DNS_ANSWER 10 00254 /* maximum allowed size for the struct due to non-packed */ 00255 #define SIZEOF_DNS_ANSWER_ASSERT 12 00256 00257 /** DNS table entry */ 00258 struct dns_table_entry { 00259 u32_t ttl; 00260 ip_addr_t ipaddr; 00261 u16_t txid; 00262 u8_t state; 00263 u8_t server_idx; 00264 u8_t tmr; 00265 u8_t retries; 00266 u8_t seqno; 00267 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) 00268 u8_t pcb_idx; 00269 #endif 00270 char name[DNS_MAX_NAME_LENGTH]; 00271 #if LWIP_IPV4 && LWIP_IPV6 00272 u8_t reqaddrtype; 00273 #endif /* LWIP_IPV4 && LWIP_IPV6 */ 00274 }; 00275 00276 /** DNS request table entry: used when dns_gehostbyname cannot answer the 00277 * request from the DNS table */ 00278 struct dns_req_entry { 00279 /* pointer to callback on DNS query done */ 00280 dns_found_callback found; 00281 /* argument passed to the callback function */ 00282 void *arg; 00283 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) 00284 u8_t dns_table_idx; 00285 #endif 00286 #if LWIP_IPV4 && LWIP_IPV6 00287 u8_t reqaddrtype; 00288 #endif /* LWIP_IPV4 && LWIP_IPV6 */ 00289 }; 00290 00291 #if DNS_LOCAL_HOSTLIST 00292 00293 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00294 /** Local host-list. For hostnames in this list, no 00295 * external name resolution is performed */ 00296 static struct local_hostlist_entry *local_hostlist_dynamic; 00297 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00298 00299 /** Defining this allows the local_hostlist_static to be placed in a different 00300 * linker section (e.g. FLASH) */ 00301 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE 00302 #define DNS_LOCAL_HOSTLIST_STORAGE_PRE static 00303 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */ 00304 /** Defining this allows the local_hostlist_static to be placed in a different 00305 * linker section (e.g. FLASH) */ 00306 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST 00307 #define DNS_LOCAL_HOSTLIST_STORAGE_POST 00308 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */ 00309 DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[] 00310 DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT; 00311 00312 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00313 00314 static void dns_init_local(void); 00315 #endif /* DNS_LOCAL_HOSTLIST */ 00316 00317 00318 /* forward declarations */ 00319 static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port); 00320 static void dns_check_entries(void); 00321 static void dns_call_found(u8_t idx, ip_addr_t* addr); 00322 00323 /*----------------------------------------------------------------------------- 00324 * Globals 00325 *----------------------------------------------------------------------------*/ 00326 00327 /* DNS variables */ 00328 static struct udp_pcb *dns_pcbs[DNS_MAX_SOURCE_PORTS]; 00329 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) 00330 static u8_t dns_last_pcb_idx; 00331 #endif 00332 static u8_t dns_seqno; 00333 static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; 00334 static struct dns_req_entry dns_requests[DNS_MAX_REQUESTS]; 00335 static ip_addr_t dns_servers[DNS_MAX_SERVERS]; 00336 00337 #ifndef LWIP_DNS_STRICMP 00338 #define LWIP_DNS_STRICMP(str1, str2) dns_stricmp(str1, str2) 00339 /** 00340 * A small but sufficient implementation for case insensitive strcmp. 00341 * This can be defined to e.g. stricmp for windows or strcasecmp for linux. */ 00342 static int 00343 dns_stricmp(const char* str1, const char* str2) 00344 { 00345 char c1, c2; 00346 00347 do { 00348 c1 = *str1++; 00349 c2 = *str2++; 00350 if (c1 != c2) { 00351 char c1_upc = c1 | 0x20; 00352 if ((c1_upc >= 'a') && (c1_upc <= 'z')) { 00353 /* characters are not equal an one is in the alphabet range: 00354 downcase both chars and check again */ 00355 char c2_upc = c2 | 0x20; 00356 if (c1_upc != c2_upc) { 00357 /* still not equal */ 00358 /* don't care for < or > */ 00359 return 1; 00360 } 00361 } else { 00362 /* characters are not equal but none is in the alphabet range */ 00363 return 1; 00364 } 00365 } 00366 } while (c1 != 0); 00367 return 0; 00368 } 00369 #endif /* LWIP_DNS_STRICMP */ 00370 00371 /** 00372 * Initialize the resolver: set up the UDP pcb and configure the default server 00373 * (if DNS_SERVER_ADDRESS is set). 00374 */ 00375 void 00376 dns_init(void) 00377 { 00378 #ifdef DNS_SERVER_ADDRESS 00379 /* initialize default DNS server address */ 00380 ip_addr_t dnsserver; 00381 DNS_SERVER_ADDRESS(&dnsserver); 00382 dns_setserver(0, &dnsserver); 00383 #endif /* DNS_SERVER_ADDRESS */ 00384 00385 LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY", 00386 sizeof(struct dns_query) == SIZEOF_DNS_QUERY); 00387 LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER", 00388 sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT); 00389 00390 LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); 00391 00392 /* if dns client not yet initialized... */ 00393 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0) 00394 if (dns_pcbs[0] == NULL) { 00395 dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY); 00396 LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != NULL); 00397 00398 /* initialize DNS table not needed (initialized to zero since it is a 00399 * global variable) */ 00400 LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", 00401 DNS_STATE_UNUSED == 0); 00402 00403 /* initialize DNS client */ 00404 udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0); 00405 udp_recv(dns_pcbs[0], dns_recv, NULL); 00406 } 00407 #endif 00408 00409 #if DNS_LOCAL_HOSTLIST 00410 dns_init_local(); 00411 #endif 00412 } 00413 00414 /** 00415 * @ingroup dns 00416 * Initialize one of the DNS servers. 00417 * 00418 * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS 00419 * @param dnsserver IP address of the DNS server to set 00420 */ 00421 void 00422 dns_setserver(u8_t numdns, const ip_addr_t *dnsserver) 00423 { 00424 if (numdns < DNS_MAX_SERVERS) { 00425 if (dnsserver != NULL) { 00426 dns_servers[numdns] = (*dnsserver); 00427 } else { 00428 dns_servers[numdns] = *IP_ADDR_ANY; 00429 } 00430 } 00431 } 00432 00433 /** 00434 * @ingroup dns 00435 * Obtain one of the currently configured DNS server. 00436 * 00437 * @param numdns the index of the DNS server 00438 * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS 00439 * server has not been configured. 00440 */ 00441 const ip_addr_t* 00442 dns_getserver(u8_t numdns) 00443 { 00444 if (numdns < DNS_MAX_SERVERS) { 00445 return &dns_servers[numdns]; 00446 } else { 00447 return IP_ADDR_ANY; 00448 } 00449 } 00450 00451 /** 00452 * The DNS resolver client timer - handle retries and timeouts and should 00453 * be called every DNS_TMR_INTERVAL milliseconds (every second by default). 00454 */ 00455 void 00456 dns_tmr(void) 00457 { 00458 LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); 00459 dns_check_entries(); 00460 } 00461 00462 #if DNS_LOCAL_HOSTLIST 00463 static void 00464 dns_init_local(void) 00465 { 00466 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) 00467 size_t i; 00468 struct local_hostlist_entry *entry; 00469 /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */ 00470 struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT; 00471 size_t namelen; 00472 for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_init); i++) { 00473 struct local_hostlist_entry *init_entry = &local_hostlist_init[i]; 00474 LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL); 00475 namelen = strlen(init_entry->name); 00476 LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); 00477 entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); 00478 LWIP_ASSERT("mem-error in dns_init_local", entry != NULL); 00479 if (entry != NULL) { 00480 char* entry_name = (char*)entry + sizeof(struct local_hostlist_entry); 00481 MEMCPY(entry_name, init_entry->name, namelen); 00482 entry_name[namelen] = 0; 00483 entry->name = entry_name; 00484 entry->addr = init_entry->addr; 00485 entry->next = local_hostlist_dynamic; 00486 local_hostlist_dynamic = entry; 00487 } 00488 } 00489 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */ 00490 } 00491 00492 /** 00493 * @ingroup dns 00494 * Scans the local host-list for a hostname. 00495 * 00496 * @param hostname Hostname to look for in the local host-list 00497 * @param addr the first IP address for the hostname in the local host-list or 00498 * IPADDR_NONE if not found. 00499 * @return ERR_OK if found, ERR_ARG if not found 00500 */ 00501 static err_t 00502 dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype)) 00503 { 00504 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00505 struct local_hostlist_entry *entry = local_hostlist_dynamic; 00506 while (entry != NULL) { 00507 if ((LWIP_DNS_STRICMP(entry->name, hostname) == 0) && 00508 LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, entry->addr)) { 00509 if (addr) { 00510 ip_addr_copy(*addr, entry->addr); 00511 } 00512 return ERR_OK; 00513 } 00514 entry = entry->next; 00515 } 00516 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00517 size_t i; 00518 for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) { 00519 if ((LWIP_DNS_STRICMP(local_hostlist_static[i].name, hostname) == 0) && 00520 LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, local_hostlist_static[i].addr)) { 00521 if (addr) { 00522 ip_addr_copy(*addr, local_hostlist_static[i].addr); 00523 } 00524 return ERR_OK; 00525 } 00526 } 00527 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ 00528 return ERR_ARG; 00529 } 00530 00531 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC 00532 /** 00533 * @ingroup dns 00534 * Remove all entries from the local host-list for a specific hostname 00535 * and/or IP address 00536 * 00537 * @param hostname hostname for which entries shall be removed from the local 00538 * host-list 00539 * @param addr address for which entries shall be removed from the local host-list 00540 * @return the number of removed entries 00541 */ 00542 int 00543 dns_local_removehost(const char *hostname, const ip_addr_t *addr) 00544 { 00545 int removed = 0; 00546 struct local_hostlist_entry *entry = local_hostlist_dynamic; 00547 struct local_hostlist_entry *last_entry = NULL; 00548 while (entry != NULL) { 00549 if (((hostname == NULL) || !LWIP_DNS_STRICMP(entry->name, hostname)) && 00550 ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) { 00551 struct local_hostlist_entry *free_entry; 00552 if (last_entry != NULL) { 00553 last_entry->next = entry->next; 00554 } else { 00555 local_hostlist_dynamic = entry->next; 00556 } 00557 free_entry = entry; 00558 entry = entry->next; 00559 memp_free(MEMP_LOCALHOSTLIST, free_entry); 00560 removed++; 00561 } else { 00562 last_entry = entry; 00563 entry = entry->next; 00564 } 00565 } 00566 return removed; 00567 } 00568 00569 /** 00570 * @ingroup dns 00571 * Add a hostname/IP address pair to the local host-list. 00572 * Duplicates are not checked. 00573 * 00574 * @param hostname hostname of the new entry 00575 * @param addr IP address of the new entry 00576 * @return ERR_OK if succeeded or ERR_MEM on memory error 00577 */ 00578 err_t 00579 dns_local_addhost(const char *hostname, const ip_addr_t *addr) 00580 { 00581 struct local_hostlist_entry *entry; 00582 size_t namelen; 00583 char* entry_name; 00584 LWIP_ASSERT("invalid host name (NULL)", hostname != NULL); 00585 namelen = strlen(hostname); 00586 LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN); 00587 entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST); 00588 if (entry == NULL) { 00589 return ERR_MEM; 00590 } 00591 entry_name = (char*)entry + sizeof(struct local_hostlist_entry); 00592 MEMCPY(entry_name, hostname, namelen); 00593 entry_name[namelen] = 0; 00594 entry->name = entry_name; 00595 ip_addr_copy(entry->addr, *addr); 00596 entry->next = local_hostlist_dynamic; 00597 local_hostlist_dynamic = entry; 00598 return ERR_OK; 00599 } 00600 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/ 00601 #endif /* DNS_LOCAL_HOSTLIST */ 00602 00603 /** 00604 * @ingroup dns 00605 * Look up a hostname in the array of known hostnames. 00606 * 00607 * @note This function only looks in the internal array of known 00608 * hostnames, it does not send out a query for the hostname if none 00609 * was found. The function dns_enqueue() can be used to send a query 00610 * for a hostname. 00611 * 00612 * @param name the hostname to look up 00613 * @param addr the hostname's IP address, as u32_t (instead of ip_addr_t to 00614 * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname 00615 * was not found in the cached dns_table. 00616 * @return ERR_OK if found, ERR_ARG if not found 00617 */ 00618 static err_t 00619 dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype)) 00620 { 00621 u8_t i; 00622 #if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) 00623 #endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */ 00624 #if DNS_LOCAL_HOSTLIST 00625 if (dns_lookup_local(name, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) { 00626 return ERR_OK; 00627 } 00628 #endif /* DNS_LOCAL_HOSTLIST */ 00629 #ifdef DNS_LOOKUP_LOCAL_EXTERN 00630 if (DNS_LOOKUP_LOCAL_EXTERN(name, addr, LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(dns_addrtype)) == ERR_OK) { 00631 return ERR_OK; 00632 } 00633 #endif /* DNS_LOOKUP_LOCAL_EXTERN */ 00634 00635 /* Walk through name list, return entry if found. If not, return NULL. */ 00636 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 00637 if ((dns_table[i].state == DNS_STATE_DONE) && 00638 (LWIP_DNS_STRICMP(name, dns_table[i].name) == 0) && 00639 LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr)) { 00640 LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name)); 00641 ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr)); 00642 LWIP_DEBUGF(DNS_DEBUG, ("\n")); 00643 if (addr) { 00644 ip_addr_copy(*addr, dns_table[i].ipaddr); 00645 } 00646 return ERR_OK; 00647 } 00648 } 00649 00650 return ERR_ARG; 00651 } 00652 00653 /** 00654 * Compare the "dotted" name "query" with the encoded name "response" 00655 * to make sure an answer from the DNS server matches the current dns_table 00656 * entry (otherwise, answers might arrive late for hostname not on the list 00657 * any more). 00658 * 00659 * @param query hostname (not encoded) from the dns_table 00660 * @param p pbuf containing the encoded hostname in the DNS response 00661 * @param start_offset offset into p where the name starts 00662 * @return 0xFFFF: names differ, other: names equal -> offset behind name 00663 */ 00664 static u16_t 00665 dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset) 00666 { 00667 unsigned char n; 00668 u16_t response_offset = start_offset; 00669 00670 do { 00671 n = pbuf_get_at(p, response_offset++); 00672 /** @see RFC 1035 - 4.1.4. Message compression */ 00673 if ((n & 0xc0) == 0xc0) { 00674 /* Compressed name: cannot be equal since we don't send them */ 00675 return 0xFFFF; 00676 } else { 00677 /* Not compressed name */ 00678 while (n > 0) { 00679 if ((*query) != pbuf_get_at(p, response_offset)) { 00680 return 0xFFFF; 00681 } 00682 ++response_offset; 00683 ++query; 00684 --n; 00685 } 00686 ++query; 00687 } 00688 } while (pbuf_get_at(p, response_offset) != 0); 00689 00690 return response_offset + 1; 00691 } 00692 00693 /** 00694 * Walk through a compact encoded DNS name and return the end of the name. 00695 * 00696 * @param p pbuf containing the name 00697 * @param query_idx start index into p pointing to encoded DNS name in the DNS server response 00698 * @return index to end of the name 00699 */ 00700 static u16_t 00701 dns_parse_name(struct pbuf* p, u16_t query_idx) 00702 { 00703 unsigned char n; 00704 00705 do { 00706 n = pbuf_get_at(p, query_idx++); 00707 /** @see RFC 1035 - 4.1.4. Message compression */ 00708 if ((n & 0xc0) == 0xc0) { 00709 /* Compressed name */ 00710 break; 00711 } else { 00712 /* Not compressed name */ 00713 while (n > 0) { 00714 ++query_idx; 00715 --n; 00716 } 00717 } 00718 } while (pbuf_get_at(p, query_idx) != 0); 00719 00720 return query_idx + 1; 00721 } 00722 00723 /** 00724 * Send a DNS query packet. 00725 * 00726 * @param idx the DNS table entry index for which to send a request 00727 * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise 00728 */ 00729 static err_t 00730 dns_send(u8_t idx) 00731 { 00732 err_t err; 00733 struct dns_hdr hdr; 00734 struct dns_query qry; 00735 struct pbuf *p; 00736 u16_t query_idx, copy_len; 00737 const char *hostname, *hostname_part; 00738 u8_t n; 00739 u8_t pcb_idx; 00740 struct dns_table_entry* entry = &dns_table[idx]; 00741 00742 LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", 00743 (u16_t)(entry->server_idx), entry->name)); 00744 LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS); 00745 if (ip_addr_isany_val(dns_servers[entry->server_idx])) { 00746 /* DNS server not valid anymore, e.g. PPP netif has been shut down */ 00747 /* call specified callback function if provided */ 00748 dns_call_found(idx, NULL); 00749 /* flush this entry */ 00750 entry->state = DNS_STATE_UNUSED; 00751 return ERR_OK; 00752 } 00753 00754 /* if here, we have either a new query or a retry on a previous query to process */ 00755 p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 + 00756 SIZEOF_DNS_QUERY), PBUF_RAM); 00757 if (p != NULL) { 00758 /* fill dns header */ 00759 memset(&hdr, 0, SIZEOF_DNS_HDR); 00760 hdr.id = htons(entry->txid); 00761 hdr.flags1 = DNS_FLAG1_RD; 00762 hdr.numquestions = PP_HTONS(1); 00763 pbuf_take(p, &hdr, SIZEOF_DNS_HDR); 00764 hostname = entry->name; 00765 --hostname; 00766 00767 /* convert hostname into suitable query format. */ 00768 query_idx = SIZEOF_DNS_HDR; 00769 do { 00770 ++hostname; 00771 hostname_part = hostname; 00772 for (n = 0; *hostname != '.' && *hostname != 0; ++hostname) { 00773 ++n; 00774 } 00775 copy_len = (u16_t)(hostname - hostname_part); 00776 pbuf_put_at(p, query_idx, n); 00777 pbuf_take_at(p, hostname_part, copy_len, query_idx + 1); 00778 query_idx += n + 1; 00779 } while (*hostname != 0); 00780 pbuf_put_at(p, query_idx, 0); 00781 query_idx++; 00782 00783 /* fill dns query */ 00784 if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) { 00785 qry.type = PP_HTONS(DNS_RRTYPE_AAAA); 00786 } else { 00787 qry.type = PP_HTONS(DNS_RRTYPE_A); 00788 } 00789 qry.cls = PP_HTONS(DNS_RRCLASS_IN); 00790 pbuf_take_at(p, &qry, SIZEOF_DNS_QUERY, query_idx); 00791 00792 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) 00793 pcb_idx = entry->pcb_idx; 00794 #else 00795 pcb_idx = 0; 00796 #endif 00797 /* send dns packet */ 00798 LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n", 00799 entry->txid, entry->name, entry->server_idx)); 00800 err = udp_sendto(dns_pcbs[pcb_idx], p, &dns_servers[entry->server_idx], DNS_SERVER_PORT); 00801 00802 /* free pbuf */ 00803 pbuf_free(p); 00804 } else { 00805 err = ERR_MEM; 00806 } 00807 00808 return err; 00809 } 00810 00811 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) 00812 static struct udp_pcb* 00813 dns_alloc_random_port(void) 00814 { 00815 err_t err; 00816 struct udp_pcb* ret; 00817 00818 ret = udp_new_ip_type(IPADDR_TYPE_ANY); 00819 if (ret == NULL) { 00820 /* out of memory, have to reuse an existing pcb */ 00821 return NULL; 00822 } 00823 do { 00824 u16_t port = (u16_t)DNS_RAND_TXID(); 00825 if (!DNS_PORT_ALLOWED(port)) { 00826 /* this port is not allowed, try again */ 00827 err = ERR_USE; 00828 continue; 00829 } 00830 err = udp_bind(ret, IP_ANY_TYPE, port); 00831 } while (err == ERR_USE); 00832 if (err != ERR_OK) { 00833 udp_remove(ret); 00834 return NULL; 00835 } 00836 udp_recv(ret, dns_recv, NULL); 00837 return ret; 00838 } 00839 00840 /** 00841 * dns_alloc_pcb() - allocates a new pcb (or reuses an existing one) to be used 00842 * for sending a request 00843 * 00844 * @return an index into dns_pcbs 00845 */ 00846 static u8_t 00847 dns_alloc_pcb(void) 00848 { 00849 u8_t i; 00850 u8_t idx; 00851 00852 for (i = 0; i < DNS_MAX_SOURCE_PORTS; i++) { 00853 if (dns_pcbs[i] == NULL) { 00854 break; 00855 } 00856 } 00857 if (i < DNS_MAX_SOURCE_PORTS) { 00858 dns_pcbs[i] = dns_alloc_random_port(); 00859 if (dns_pcbs[i] != NULL) { 00860 /* succeeded */ 00861 dns_last_pcb_idx = i; 00862 return i; 00863 } 00864 } 00865 /* if we come here, creating a new UDP pcb failed, so we have to use 00866 an already existing one */ 00867 for (i = 0, idx = dns_last_pcb_idx + 1; i < DNS_MAX_SOURCE_PORTS; i++, idx++) { 00868 if (idx >= DNS_MAX_SOURCE_PORTS) { 00869 idx = 0; 00870 } 00871 if (dns_pcbs[idx] != NULL) { 00872 dns_last_pcb_idx = idx; 00873 return idx; 00874 } 00875 } 00876 return DNS_MAX_SOURCE_PORTS; 00877 } 00878 #endif /* ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) */ 00879 00880 /** 00881 * dns_call_found() - call the found callback and check if there are duplicate 00882 * entries for the given hostname. If there are any, their found callback will 00883 * be called and they will be removed. 00884 * 00885 * @param idx dns table index of the entry that is resolved or removed 00886 * @param addr IP address for the hostname (or NULL on error or memory shortage) 00887 */ 00888 static void 00889 dns_call_found(u8_t idx, ip_addr_t* addr) 00890 { 00891 #if ((LWIP_DNS_SECURE & (LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT)) != 0) 00892 u8_t i; 00893 #endif 00894 00895 #if LWIP_IPV4 && LWIP_IPV6 00896 if (addr != NULL) { 00897 /* check that address type matches the request and adapt the table entry */ 00898 if (IP_IS_V6_VAL(*addr)) { 00899 LWIP_ASSERT("invalid response", LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype)); 00900 dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6; 00901 } else { 00902 LWIP_ASSERT("invalid response", !LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype)); 00903 dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4; 00904 } 00905 } 00906 #endif /* LWIP_IPV4 && LWIP_IPV6 */ 00907 00908 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) 00909 for (i = 0; i < DNS_MAX_REQUESTS; i++) { 00910 if (dns_requests[i].found && (dns_requests[i].dns_table_idx == idx)) { 00911 (*dns_requests[i].found)(dns_table[idx].name, addr, dns_requests[i].arg); 00912 /* flush this entry */ 00913 dns_requests[i].found = NULL; 00914 } 00915 } 00916 #else 00917 if (dns_requests[idx].found) { 00918 (*dns_requests[idx].found)(dns_table[idx].name, addr, dns_requests[idx].arg); 00919 } 00920 dns_requests[idx].found = NULL; 00921 #endif 00922 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) 00923 /* close the pcb used unless other request are using it */ 00924 for (i = 0; i < DNS_MAX_REQUESTS; i++) { 00925 if (i == idx) { 00926 continue; /* only check other requests */ 00927 } 00928 if (dns_table[i].state == DNS_STATE_ASKING) { 00929 if (dns_table[i].pcb_idx == dns_table[idx].pcb_idx) { 00930 /* another request is still using the same pcb */ 00931 dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS; 00932 break; 00933 } 00934 } 00935 } 00936 if (dns_table[idx].pcb_idx < DNS_MAX_SOURCE_PORTS) { 00937 /* if we come here, the pcb is not used any more and can be removed */ 00938 udp_remove(dns_pcbs[dns_table[idx].pcb_idx]); 00939 dns_pcbs[dns_table[idx].pcb_idx] = NULL; 00940 dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS; 00941 } 00942 #endif 00943 } 00944 00945 /* Create a query transmission ID that is unique for all outstanding queries */ 00946 static u16_t 00947 dns_create_txid(void) 00948 { 00949 u16_t txid; 00950 u8_t i; 00951 00952 again: 00953 txid = (u16_t)DNS_RAND_TXID(); 00954 00955 /* check whether the ID is unique */ 00956 for (i = 0; i < DNS_TABLE_SIZE; i++) { 00957 if ((dns_table[i].state == DNS_STATE_ASKING) && 00958 (dns_table[i].txid == txid)) { 00959 /* ID already used by another pending query */ 00960 goto again; 00961 } 00962 } 00963 00964 return txid; 00965 } 00966 00967 /** 00968 * dns_check_entry() - see if entry has not yet been queried and, if so, sends out a query. 00969 * Check an entry in the dns_table: 00970 * - send out query for new entries 00971 * - retry old pending entries on timeout (also with different servers) 00972 * - remove completed entries from the table if their TTL has expired 00973 * 00974 * @param i index of the dns_table entry to check 00975 */ 00976 static void 00977 dns_check_entry(u8_t i) 00978 { 00979 err_t err; 00980 struct dns_table_entry *entry = &dns_table[i]; 00981 00982 LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); 00983 00984 switch (entry->state) { 00985 00986 case DNS_STATE_NEW: { 00987 u16_t txid; 00988 /* initialize new entry */ 00989 txid = dns_create_txid(); 00990 entry->txid = txid; 00991 entry->state = DNS_STATE_ASKING; 00992 entry->server_idx = 0; 00993 entry->tmr = 1; 00994 entry->retries = 0; 00995 00996 /* send DNS packet for this entry */ 00997 err = dns_send(i); 00998 if (err != ERR_OK) { 00999 LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, 01000 ("dns_send returned error: %s\n", lwip_strerr(err))); 01001 } 01002 break; 01003 } 01004 01005 case DNS_STATE_ASKING: 01006 if (--entry->tmr == 0) { 01007 if (++entry->retries == DNS_MAX_RETRIES) { 01008 if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1])) { 01009 /* change of server */ 01010 entry->server_idx++; 01011 entry->tmr = 1; 01012 entry->retries = 0; 01013 } else { 01014 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name)); 01015 /* call specified callback function if provided */ 01016 dns_call_found(i, NULL); 01017 /* flush this entry */ 01018 entry->state = DNS_STATE_UNUSED; 01019 break; 01020 } 01021 } else { 01022 /* wait longer for the next retry */ 01023 entry->tmr = entry->retries; 01024 } 01025 01026 /* send DNS packet for this entry */ 01027 err = dns_send(i); 01028 if (err != ERR_OK) { 01029 LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING, 01030 ("dns_send returned error: %s\n", lwip_strerr(err))); 01031 } 01032 } 01033 break; 01034 case DNS_STATE_DONE: 01035 /* if the time to live is nul */ 01036 if ((entry->ttl == 0) || (--entry->ttl == 0)) { 01037 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", entry->name)); 01038 /* flush this entry, there cannot be any related pending entries in this state */ 01039 entry->state = DNS_STATE_UNUSED; 01040 } 01041 break; 01042 case DNS_STATE_UNUSED: 01043 /* nothing to do */ 01044 break; 01045 default: 01046 LWIP_ASSERT("unknown dns_table entry state:", 0); 01047 break; 01048 } 01049 } 01050 01051 /** 01052 * Call dns_check_entry for each entry in dns_table - check all entries. 01053 */ 01054 static void 01055 dns_check_entries(void) 01056 { 01057 u8_t i; 01058 01059 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 01060 dns_check_entry(i); 01061 } 01062 } 01063 01064 /** 01065 * Save TTL and call dns_call_found for correct response. 01066 */ 01067 static void 01068 dns_correct_response(u8_t idx, u32_t ttl) 01069 { 01070 struct dns_table_entry *entry = &dns_table[idx]; 01071 01072 entry->state = DNS_STATE_DONE; 01073 01074 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name)); 01075 ip_addr_debug_print(DNS_DEBUG, (&(entry->ipaddr))); 01076 LWIP_DEBUGF(DNS_DEBUG, ("\n")); 01077 01078 /* read the answer resource record's TTL, and maximize it if needed */ 01079 entry->ttl = ttl; 01080 if (entry->ttl > DNS_MAX_TTL) { 01081 entry->ttl = DNS_MAX_TTL; 01082 } 01083 dns_call_found(idx, &entry->ipaddr); 01084 01085 if (entry->ttl == 0) { 01086 /* RFC 883, page 29: "Zero values are 01087 interpreted to mean that the RR can only be used for the 01088 transaction in progress, and should not be cached." 01089 -> flush this entry now */ 01090 /* entry reused during callback? */ 01091 if (entry->state == DNS_STATE_DONE) { 01092 entry->state = DNS_STATE_UNUSED; 01093 } 01094 } 01095 } 01096 /** 01097 * Receive input function for DNS response packets arriving for the dns UDP pcb. 01098 * 01099 * @params see udp.h 01100 */ 01101 static void 01102 dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) 01103 { 01104 u8_t i; 01105 u16_t txid; 01106 u16_t res_idx; 01107 struct dns_hdr hdr; 01108 struct dns_answer ans; 01109 struct dns_query qry; 01110 u16_t nquestions, nanswers; 01111 01112 LWIP_UNUSED_ARG(arg); 01113 LWIP_UNUSED_ARG(pcb); 01114 LWIP_UNUSED_ARG(port); 01115 01116 /* is the dns message big enough ? */ 01117 if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY)) { 01118 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n")); 01119 /* free pbuf and return */ 01120 goto memerr; 01121 } 01122 01123 /* copy dns payload inside static buffer for processing */ 01124 if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, 0) == SIZEOF_DNS_HDR) { 01125 /* Match the ID in the DNS header with the name table. */ 01126 txid = htons(hdr.id); 01127 for (i = 0; i < DNS_TABLE_SIZE; i++) { 01128 const struct dns_table_entry *entry = &dns_table[i]; 01129 if ((entry->state == DNS_STATE_ASKING) && 01130 (entry->txid == txid)) { 01131 01132 /* We only care about the question(s) and the answers. The authrr 01133 and the extrarr are simply discarded. */ 01134 nquestions = htons(hdr.numquestions); 01135 nanswers = htons(hdr.numanswers); 01136 01137 /* Check for correct response. */ 01138 if ((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) { 01139 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": not a response\n", entry->name)); 01140 goto memerr; /* ignore this packet */ 01141 } 01142 if (nquestions != 1) { 01143 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name)); 01144 goto memerr; /* ignore this packet */ 01145 } 01146 01147 /* Check whether response comes from the same network address to which the 01148 question was sent. (RFC 5452) */ 01149 if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) { 01150 goto memerr; /* ignore this packet */ 01151 } 01152 01153 /* Check if the name in the "question" part match with the name in the entry and 01154 skip it if equal. */ 01155 res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR); 01156 if (res_idx == 0xFFFF) { 01157 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name)); 01158 goto memerr; /* ignore this packet */ 01159 } 01160 01161 /* check if "question" part matches the request */ 01162 pbuf_copy_partial(p, &qry, SIZEOF_DNS_QUERY, res_idx); 01163 if ((qry.cls != PP_HTONS(DNS_RRCLASS_IN)) || 01164 (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_AAAA))) || 01165 (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_A)))) { 01166 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name)); 01167 goto memerr; /* ignore this packet */ 01168 } 01169 /* skip the rest of the "question" part */ 01170 res_idx += SIZEOF_DNS_QUERY; 01171 01172 /* Check for error. If so, call callback to inform. */ 01173 if (hdr.flags2 & DNS_FLAG2_ERR_MASK) { 01174 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name)); 01175 } else { 01176 while ((nanswers > 0) && (res_idx < p->tot_len)) { 01177 /* skip answer resource record's host name */ 01178 res_idx = dns_parse_name(p, res_idx); 01179 01180 /* Check for IP address type and Internet class. Others are discarded. */ 01181 pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx); 01182 res_idx += SIZEOF_DNS_ANSWER; 01183 if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) { 01184 #if LWIP_IPV4 01185 if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) { 01186 #if LWIP_IPV4 && LWIP_IPV6 01187 if (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) 01188 #endif /* LWIP_IPV4 && LWIP_IPV6 */ 01189 { 01190 ip4_addr_t ip4addr; 01191 /* read the IP address after answer resource record's header */ 01192 pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx); 01193 ip_addr_copy_from_ip4(dns_table[i].ipaddr, ip4addr); 01194 pbuf_free(p); 01195 /* handle correct response */ 01196 dns_correct_response(i, ntohl(ans.ttl)); 01197 return; 01198 } 01199 } 01200 #endif /* LWIP_IPV4 */ 01201 #if LWIP_IPV6 01202 if ((ans.type == PP_HTONS(DNS_RRTYPE_AAAA)) && (ans.len == PP_HTONS(sizeof(ip6_addr_t)))) { 01203 #if LWIP_IPV4 && LWIP_IPV6 01204 if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) 01205 #endif /* LWIP_IPV4 && LWIP_IPV6 */ 01206 { 01207 ip6_addr_t ip6addr; 01208 /* read the IP address after answer resource record's header */ 01209 pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_t), res_idx); 01210 ip_addr_copy_from_ip6(dns_table[i].ipaddr, ip6addr); 01211 pbuf_free(p); 01212 /* handle correct response */ 01213 dns_correct_response(i, ntohl(ans.ttl)); 01214 return; 01215 } 01216 } 01217 #endif /* LWIP_IPV6 */ 01218 } 01219 /* skip this answer */ 01220 res_idx += htons(ans.len); 01221 --nanswers; 01222 } 01223 #if LWIP_IPV4 && LWIP_IPV6 01224 if ((entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || 01225 (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) { 01226 if (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) { 01227 /* IPv4 failed, try IPv6 */ 01228 dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6; 01229 } else { 01230 /* IPv6 failed, try IPv4 */ 01231 dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4; 01232 } 01233 pbuf_free(p); 01234 dns_table[i].state = DNS_STATE_NEW; 01235 dns_check_entry(i); 01236 return; 01237 } 01238 #endif /* LWIP_IPV4 && LWIP_IPV6 */ 01239 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->name)); 01240 } 01241 /* call callback to indicate error, clean up memory and return */ 01242 pbuf_free(p); 01243 dns_call_found(i, NULL); 01244 dns_table[i].state = DNS_STATE_UNUSED; 01245 return; 01246 } 01247 } 01248 } 01249 01250 memerr: 01251 /* deallocate memory and return */ 01252 pbuf_free(p); 01253 return; 01254 } 01255 01256 /** 01257 * Queues a new hostname to resolve and sends out a DNS query for that hostname 01258 * 01259 * @param name the hostname that is to be queried 01260 * @param hostnamelen length of the hostname 01261 * @param found a callback function to be called on success, failure or timeout 01262 * @param callback_arg argument to pass to the callback function 01263 * @return @return a err_t return code. 01264 */ 01265 static err_t 01266 dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, 01267 void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype)) 01268 { 01269 u8_t i; 01270 u8_t lseq, lseqi; 01271 struct dns_table_entry *entry = NULL; 01272 size_t namelen; 01273 struct dns_req_entry* req; 01274 01275 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) 01276 u8_t r; 01277 /* check for duplicate entries */ 01278 for (i = 0; i < DNS_TABLE_SIZE; i++) { 01279 if ((dns_table[i].state == DNS_STATE_ASKING) && 01280 (LWIP_DNS_STRICMP(name, dns_table[i].name) == 0)) { 01281 #if LWIP_IPV4 && LWIP_IPV6 01282 if (dns_table[i].reqaddrtype != dns_addrtype) { 01283 /* requested address types don't match 01284 this can lead to 2 concurrent requests, but mixing the address types 01285 for the same host should not be that common */ 01286 continue; 01287 } 01288 #endif /* LWIP_IPV4 && LWIP_IPV6 */ 01289 /* this is a duplicate entry, find a free request entry */ 01290 for (r = 0; r < DNS_MAX_REQUESTS; r++) { 01291 if (dns_requests[r].found == 0) { 01292 dns_requests[r].found = found; 01293 dns_requests[r].arg = callback_arg; 01294 dns_requests[r].dns_table_idx = i; 01295 LWIP_DNS_SET_ADDRTYPE(dns_requests[r].reqaddrtype, dns_addrtype); 01296 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": duplicate request\n", name)); 01297 return ERR_INPROGRESS; 01298 } 01299 } 01300 } 01301 } 01302 /* no duplicate entries found */ 01303 #endif 01304 01305 /* search an unused entry, or the oldest one */ 01306 lseq = 0; 01307 lseqi = DNS_TABLE_SIZE; 01308 for (i = 0; i < DNS_TABLE_SIZE; ++i) { 01309 entry = &dns_table[i]; 01310 /* is it an unused entry ? */ 01311 if (entry->state == DNS_STATE_UNUSED) { 01312 break; 01313 } 01314 /* check if this is the oldest completed entry */ 01315 if (entry->state == DNS_STATE_DONE) { 01316 if ((u8_t)(dns_seqno - entry->seqno) > lseq) { 01317 lseq = dns_seqno - entry->seqno; 01318 lseqi = i; 01319 } 01320 } 01321 } 01322 01323 /* if we don't have found an unused entry, use the oldest completed one */ 01324 if (i == DNS_TABLE_SIZE) { 01325 if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) { 01326 /* no entry can be used now, table is full */ 01327 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name)); 01328 return ERR_MEM; 01329 } else { 01330 /* use the oldest completed one */ 01331 i = lseqi; 01332 entry = &dns_table[i]; 01333 } 01334 } 01335 01336 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) 01337 /* find a free request entry */ 01338 req = NULL; 01339 for (r = 0; r < DNS_MAX_REQUESTS; r++) { 01340 if (dns_requests[r].found == NULL) { 01341 req = &dns_requests[r]; 01342 break; 01343 } 01344 } 01345 if (req == NULL) { 01346 /* no request entry can be used now, table is full */ 01347 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS request entries table is full\n", name)); 01348 return ERR_MEM; 01349 } 01350 req->dns_table_idx = i; 01351 #else 01352 /* in this configuration, the entry index is the same as the request index */ 01353 req = &dns_requests[i]; 01354 #endif 01355 01356 /* use this entry */ 01357 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); 01358 01359 /* fill the entry */ 01360 entry->state = DNS_STATE_NEW; 01361 entry->seqno = dns_seqno; 01362 LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype); 01363 LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype); 01364 req->found = found; 01365 req->arg = callback_arg; 01366 namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1); 01367 MEMCPY(entry->name, name, namelen); 01368 entry->name[namelen] = 0; 01369 01370 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) 01371 entry->pcb_idx = dns_alloc_pcb(); 01372 if (entry->pcb_idx >= DNS_MAX_SOURCE_PORTS) { 01373 /* failed to get a UDP pcb */ 01374 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": failed to allocate a pcb\n", name)); 01375 entry->state = DNS_STATE_UNUSED; 01376 req->found = NULL; 01377 return ERR_MEM; 01378 } 01379 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx))); 01380 #endif 01381 01382 dns_seqno++; 01383 01384 /* force to send query without waiting timer */ 01385 dns_check_entry(i); 01386 01387 /* dns query is enqueued */ 01388 return ERR_INPROGRESS; 01389 } 01390 01391 /** 01392 * @ingroup dns 01393 * Resolve a hostname (string) into an IP address. 01394 * NON-BLOCKING callback version for use with raw API!!! 01395 * 01396 * Returns immediately with one of err_t return codes: 01397 * - ERR_OK if hostname is a valid IP address string or the host 01398 * name is already in the local names table. 01399 * - ERR_INPROGRESS enqueue a request to be sent to the DNS server 01400 * for resolution if no errors are present. 01401 * - ERR_ARG: dns client not initialized or invalid hostname 01402 * 01403 * @param hostname the hostname that is to be queried 01404 * @param addr pointer to a ip_addr_t where to store the address if it is already 01405 * cached in the dns_table (only valid if ERR_OK is returned!) 01406 * @param found a callback function to be called on success, failure or timeout (only if 01407 * ERR_INPROGRESS is returned!) 01408 * @param callback_arg argument to pass to the callback function 01409 * @return a err_t return code. 01410 */ 01411 err_t 01412 dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, 01413 void *callback_arg) 01414 { 01415 return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT); 01416 } 01417 01418 /** 01419 * @ingroup dns 01420 * Like dns_gethostbyname, but returned address type can be controlled: 01421 * @param hostname the hostname that is to be queried 01422 * @param addr pointer to a ip_addr_t where to store the address if it is already 01423 * cached in the dns_table (only valid if ERR_OK is returned!) 01424 * @param found a callback function to be called on success, failure or timeout (only if 01425 * ERR_INPROGRESS is returned!) 01426 * @param callback_arg argument to pass to the callback function 01427 * @param dns_addrtype: - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 first, try IPv6 if IPv4 fails only 01428 * - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 first, try IPv4 if IPv6 fails only 01429 * - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only 01430 * - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only 01431 */ 01432 err_t 01433 dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found, 01434 void *callback_arg, u8_t dns_addrtype) 01435 { 01436 size_t hostnamelen; 01437 /* not initialized or no valid server yet, or invalid addr pointer 01438 * or invalid hostname or invalid hostname length */ 01439 if ((addr == NULL) || 01440 (!hostname) || (!hostname[0])) { 01441 return ERR_ARG; 01442 } 01443 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0) 01444 if (dns_pcbs[0] == NULL) { 01445 return ERR_ARG; 01446 } 01447 #endif 01448 hostnamelen = strlen(hostname); 01449 if (hostnamelen >= DNS_MAX_NAME_LENGTH) { 01450 LWIP_DEBUGF(DNS_DEBUG, ("dns_gethostbyname: name too long to resolve")); 01451 return ERR_ARG; 01452 } 01453 01454 01455 #if LWIP_HAVE_LOOPIF 01456 if (strcmp(hostname, "localhost") == 0) { 01457 ip_addr_set_loopback(LWIP_DNS_ADDRTYPE_IS_IPV6(dns_addrtype), addr); 01458 return ERR_OK; 01459 } 01460 #endif /* LWIP_HAVE_LOOPIF */ 01461 01462 /* host name already in octet notation? set ip addr and return ERR_OK */ 01463 if (ipaddr_aton(hostname, addr)) { 01464 #if LWIP_IPV4 && LWIP_IPV6 01465 if ((IP_IS_V6(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV4)) || 01466 (IP_IS_V4(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV6))) 01467 #endif /* LWIP_IPV4 && LWIP_IPV6 */ 01468 { 01469 return ERR_OK; 01470 } 01471 } 01472 /* already have this address cached? */ 01473 if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) { 01474 return ERR_OK; 01475 } 01476 #if LWIP_IPV4 && LWIP_IPV6 01477 if ((dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) { 01478 /* fallback to 2nd IP type and try again to lookup */ 01479 u8_t fallback; 01480 if (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) { 01481 fallback = LWIP_DNS_ADDRTYPE_IPV6; 01482 } else { 01483 fallback = LWIP_DNS_ADDRTYPE_IPV4; 01484 } 01485 if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(fallback)) == ERR_OK) { 01486 return ERR_OK; 01487 } 01488 } 01489 #else /* LWIP_IPV4 && LWIP_IPV6 */ 01490 LWIP_UNUSED_ARG(dns_addrtype); 01491 #endif /* LWIP_IPV4 && LWIP_IPV6 */ 01492 01493 /* prevent calling found callback if no server is set, return error instead */ 01494 if (ip_addr_isany_val(dns_servers[0])) { 01495 return ERR_VAL; 01496 } 01497 01498 /* queue query with specified callback */ 01499 return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)); 01500 } 01501 01502 #endif /* LWIP_DNS */
Generated on Tue Jul 12 2022 13:15:52 by
