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.
Dependents: mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510
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 11:02:41 by
