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