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