Example program with HTTPServer and sensor data streaming over TCPSockets, using Donatien Garnier's Net APIs and services code on top of LWIP. Files StreamServer.h and .cpp encapsulate streaming over TCPSockets. Broadcast is done by sendToAll(), and all incoming data is echoed back to the client. Echo code can be replaced with some remote control of the streaming interface. See main() that shows how to periodically send some data to all subscribed clients. To subscribe, a client should open a socket at <mbed_ip> port 123. I used few lines in TCL code to set up a quick sink for the data. HTTP files are served on port 80 concurrently to the streaming.

Dependencies:   mbed

Committer:
iva2k
Date:
Mon Jun 14 03:24:33 2010 +0000
Revision:
1:3ee499525aa5
Parent:
0:e614f7875b60

        

Who changed what in which revision?

UserRevisionLine numberNew contents of line
iva2k 0:e614f7875b60 1 /**
iva2k 0:e614f7875b60 2 * @file
iva2k 0:e614f7875b60 3 * DNS - host name to IP address resolver.
iva2k 0:e614f7875b60 4 *
iva2k 0:e614f7875b60 5 */
iva2k 0:e614f7875b60 6
iva2k 0:e614f7875b60 7 /**
iva2k 0:e614f7875b60 8
iva2k 0:e614f7875b60 9 * This file implements a DNS host name to IP address resolver.
iva2k 0:e614f7875b60 10
iva2k 0:e614f7875b60 11 * Port to lwIP from uIP
iva2k 0:e614f7875b60 12 * by Jim Pettinato April 2007
iva2k 0:e614f7875b60 13
iva2k 0:e614f7875b60 14 * uIP version Copyright (c) 2002-2003, Adam Dunkels.
iva2k 0:e614f7875b60 15 * All rights reserved.
iva2k 0:e614f7875b60 16 *
iva2k 0:e614f7875b60 17 * Redistribution and use in source and binary forms, with or without
iva2k 0:e614f7875b60 18 * modification, are permitted provided that the following conditions
iva2k 0:e614f7875b60 19 * are met:
iva2k 0:e614f7875b60 20 * 1. Redistributions of source code must retain the above copyright
iva2k 0:e614f7875b60 21 * notice, this list of conditions and the following disclaimer.
iva2k 0:e614f7875b60 22 * 2. Redistributions in binary form must reproduce the above copyright
iva2k 0:e614f7875b60 23 * notice, this list of conditions and the following disclaimer in the
iva2k 0:e614f7875b60 24 * documentation and/or other materials provided with the distribution.
iva2k 0:e614f7875b60 25 * 3. The name of the author may not be used to endorse or promote
iva2k 0:e614f7875b60 26 * products derived from this software without specific prior
iva2k 0:e614f7875b60 27 * written permission.
iva2k 0:e614f7875b60 28 *
iva2k 0:e614f7875b60 29 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
iva2k 0:e614f7875b60 30 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
iva2k 0:e614f7875b60 31 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
iva2k 0:e614f7875b60 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
iva2k 0:e614f7875b60 33 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
iva2k 0:e614f7875b60 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
iva2k 0:e614f7875b60 35 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
iva2k 0:e614f7875b60 36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
iva2k 0:e614f7875b60 37 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
iva2k 0:e614f7875b60 38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
iva2k 0:e614f7875b60 39 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
iva2k 0:e614f7875b60 40 *
iva2k 0:e614f7875b60 41 *
iva2k 0:e614f7875b60 42 * DNS.C
iva2k 0:e614f7875b60 43 *
iva2k 0:e614f7875b60 44 * The lwIP DNS resolver functions are used to lookup a host name and
iva2k 0:e614f7875b60 45 * map it to a numerical IP address. It maintains a list of resolved
iva2k 0:e614f7875b60 46 * hostnames that can be queried with the dns_lookup() function.
iva2k 0:e614f7875b60 47 * New hostnames can be resolved using the dns_query() function.
iva2k 0:e614f7875b60 48 *
iva2k 0:e614f7875b60 49 * The lwIP version of the resolver also adds a non-blocking version of
iva2k 0:e614f7875b60 50 * gethostbyname() that will work with a raw API application. This function
iva2k 0:e614f7875b60 51 * checks for an IP address string first and converts it if it is valid.
iva2k 0:e614f7875b60 52 * gethostbyname() then does a dns_lookup() to see if the name is
iva2k 0:e614f7875b60 53 * already in the table. If so, the IP is returned. If not, a query is
iva2k 0:e614f7875b60 54 * issued and the function returns with a ERR_INPROGRESS status. The app
iva2k 0:e614f7875b60 55 * using the dns client must then go into a waiting state.
iva2k 0:e614f7875b60 56 *
iva2k 0:e614f7875b60 57 * Once a hostname has been resolved (or found to be non-existent),
iva2k 0:e614f7875b60 58 * the resolver code calls a specified callback function (which
iva2k 0:e614f7875b60 59 * must be implemented by the module that uses the resolver).
iva2k 0:e614f7875b60 60 */
iva2k 0:e614f7875b60 61
iva2k 0:e614f7875b60 62 /*-----------------------------------------------------------------------------
iva2k 0:e614f7875b60 63 * RFC 1035 - Domain names - implementation and specification
iva2k 0:e614f7875b60 64 * RFC 2181 - Clarifications to the DNS Specification
iva2k 0:e614f7875b60 65 *----------------------------------------------------------------------------*/
iva2k 0:e614f7875b60 66
iva2k 0:e614f7875b60 67 /** @todo: define good default values (rfc compliance) */
iva2k 0:e614f7875b60 68 /** @todo: improve answer parsing, more checkings... */
iva2k 0:e614f7875b60 69 /** @todo: check RFC1035 - 7.3. Processing responses */
iva2k 0:e614f7875b60 70
iva2k 0:e614f7875b60 71 /*-----------------------------------------------------------------------------
iva2k 0:e614f7875b60 72 * Includes
iva2k 0:e614f7875b60 73 *----------------------------------------------------------------------------*/
iva2k 0:e614f7875b60 74
iva2k 0:e614f7875b60 75 #include "lwip/opt.h"
iva2k 0:e614f7875b60 76
iva2k 0:e614f7875b60 77 #if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
iva2k 0:e614f7875b60 78
iva2k 0:e614f7875b60 79 #include "lwip/udp.h"
iva2k 0:e614f7875b60 80 #include "lwip/mem.h"
iva2k 0:e614f7875b60 81 #include "lwip/dns.h"
iva2k 0:e614f7875b60 82
iva2k 0:e614f7875b60 83 #include <string.h>
iva2k 0:e614f7875b60 84
iva2k 0:e614f7875b60 85 /** DNS server IP address */
iva2k 0:e614f7875b60 86 #ifndef DNS_SERVER_ADDRESS
iva2k 0:e614f7875b60 87 #define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */
iva2k 0:e614f7875b60 88 #endif
iva2k 0:e614f7875b60 89
iva2k 0:e614f7875b60 90 /** DNS server port address */
iva2k 0:e614f7875b60 91 #ifndef DNS_SERVER_PORT
iva2k 0:e614f7875b60 92 #define DNS_SERVER_PORT 53
iva2k 0:e614f7875b60 93 #endif
iva2k 0:e614f7875b60 94
iva2k 0:e614f7875b60 95 /** DNS maximum number of retries when asking for a name, before "timeout". */
iva2k 0:e614f7875b60 96 #ifndef DNS_MAX_RETRIES
iva2k 0:e614f7875b60 97 #define DNS_MAX_RETRIES 4
iva2k 0:e614f7875b60 98 #endif
iva2k 0:e614f7875b60 99
iva2k 0:e614f7875b60 100 /** DNS resource record max. TTL (one week as default) */
iva2k 0:e614f7875b60 101 #ifndef DNS_MAX_TTL
iva2k 0:e614f7875b60 102 #define DNS_MAX_TTL 604800
iva2k 0:e614f7875b60 103 #endif
iva2k 0:e614f7875b60 104
iva2k 0:e614f7875b60 105 /* DNS protocol flags */
iva2k 0:e614f7875b60 106 #define DNS_FLAG1_RESPONSE 0x80
iva2k 0:e614f7875b60 107 #define DNS_FLAG1_OPCODE_STATUS 0x10
iva2k 0:e614f7875b60 108 #define DNS_FLAG1_OPCODE_INVERSE 0x08
iva2k 0:e614f7875b60 109 #define DNS_FLAG1_OPCODE_STANDARD 0x00
iva2k 0:e614f7875b60 110 #define DNS_FLAG1_AUTHORATIVE 0x04
iva2k 0:e614f7875b60 111 #define DNS_FLAG1_TRUNC 0x02
iva2k 0:e614f7875b60 112 #define DNS_FLAG1_RD 0x01
iva2k 0:e614f7875b60 113 #define DNS_FLAG2_RA 0x80
iva2k 0:e614f7875b60 114 #define DNS_FLAG2_ERR_MASK 0x0f
iva2k 0:e614f7875b60 115 #define DNS_FLAG2_ERR_NONE 0x00
iva2k 0:e614f7875b60 116 #define DNS_FLAG2_ERR_NAME 0x03
iva2k 0:e614f7875b60 117
iva2k 0:e614f7875b60 118 /* DNS protocol states */
iva2k 0:e614f7875b60 119 #define DNS_STATE_UNUSED 0
iva2k 0:e614f7875b60 120 #define DNS_STATE_NEW 1
iva2k 0:e614f7875b60 121 #define DNS_STATE_ASKING 2
iva2k 0:e614f7875b60 122 #define DNS_STATE_DONE 3
iva2k 0:e614f7875b60 123
iva2k 0:e614f7875b60 124 #ifdef PACK_STRUCT_USE_INCLUDES
iva2k 0:e614f7875b60 125 # include "arch/bpstruct.h"
iva2k 0:e614f7875b60 126 #endif
iva2k 0:e614f7875b60 127 PACK_STRUCT_BEGIN
iva2k 0:e614f7875b60 128 /** DNS message header */
iva2k 0:e614f7875b60 129 struct dns_hdr {
iva2k 0:e614f7875b60 130 PACK_STRUCT_FIELD(u16_t id);
iva2k 0:e614f7875b60 131 PACK_STRUCT_FIELD(u8_t flags1);
iva2k 0:e614f7875b60 132 PACK_STRUCT_FIELD(u8_t flags2);
iva2k 0:e614f7875b60 133 PACK_STRUCT_FIELD(u16_t numquestions);
iva2k 0:e614f7875b60 134 PACK_STRUCT_FIELD(u16_t numanswers);
iva2k 0:e614f7875b60 135 PACK_STRUCT_FIELD(u16_t numauthrr);
iva2k 0:e614f7875b60 136 PACK_STRUCT_FIELD(u16_t numextrarr);
iva2k 0:e614f7875b60 137 } PACK_STRUCT_STRUCT;
iva2k 0:e614f7875b60 138 PACK_STRUCT_END
iva2k 0:e614f7875b60 139 #ifdef PACK_STRUCT_USE_INCLUDES
iva2k 0:e614f7875b60 140 # include "arch/epstruct.h"
iva2k 0:e614f7875b60 141 #endif
iva2k 0:e614f7875b60 142 #define SIZEOF_DNS_HDR 12
iva2k 0:e614f7875b60 143
iva2k 0:e614f7875b60 144 /** DNS query message structure.
iva2k 0:e614f7875b60 145 No packing needed: only used locally on the stack. */
iva2k 0:e614f7875b60 146 PACK_STRUCT_BEGIN
iva2k 0:e614f7875b60 147 struct dns_query {
iva2k 0:e614f7875b60 148 /* DNS query record starts with either a domain name or a pointer
iva2k 0:e614f7875b60 149 to a name already present somewhere in the packet. */
iva2k 0:e614f7875b60 150 u16_t type;
iva2k 0:e614f7875b60 151 u16_t cls;
iva2k 0:e614f7875b60 152 } PACK_STRUCT_STRUCT;
iva2k 0:e614f7875b60 153 PACK_STRUCT_END
iva2k 0:e614f7875b60 154 #define SIZEOF_DNS_QUERY 4
iva2k 0:e614f7875b60 155
iva2k 0:e614f7875b60 156 /** DNS answer message structure.
iva2k 0:e614f7875b60 157 No packing needed: only used locally on the stack. */
iva2k 0:e614f7875b60 158 PACK_STRUCT_BEGIN
iva2k 0:e614f7875b60 159 struct dns_answer {
iva2k 0:e614f7875b60 160 /* DNS answer record starts with either a domain name or a pointer
iva2k 0:e614f7875b60 161 to a name already present somewhere in the packet. */
iva2k 0:e614f7875b60 162 u16_t type;
iva2k 0:e614f7875b60 163 u16_t cls;
iva2k 0:e614f7875b60 164 u32_t ttl;
iva2k 0:e614f7875b60 165 u16_t len;
iva2k 0:e614f7875b60 166 } PACK_STRUCT_STRUCT;
iva2k 0:e614f7875b60 167 PACK_STRUCT_END
iva2k 0:e614f7875b60 168 #define SIZEOF_DNS_ANSWER 10
iva2k 0:e614f7875b60 169
iva2k 0:e614f7875b60 170 /** DNS table entry */
iva2k 0:e614f7875b60 171 struct dns_table_entry {
iva2k 0:e614f7875b60 172 u8_t state;
iva2k 0:e614f7875b60 173 u8_t numdns;
iva2k 0:e614f7875b60 174 u8_t tmr;
iva2k 0:e614f7875b60 175 u8_t retries;
iva2k 0:e614f7875b60 176 u8_t seqno;
iva2k 0:e614f7875b60 177 u8_t err;
iva2k 0:e614f7875b60 178 u32_t ttl;
iva2k 0:e614f7875b60 179 char name[DNS_MAX_NAME_LENGTH];
iva2k 0:e614f7875b60 180 ip_addr_t ipaddr;
iva2k 0:e614f7875b60 181 /* pointer to callback on DNS query done */
iva2k 0:e614f7875b60 182 dns_found_callback found;
iva2k 0:e614f7875b60 183 void *arg;
iva2k 0:e614f7875b60 184 };
iva2k 0:e614f7875b60 185
iva2k 0:e614f7875b60 186 #if DNS_LOCAL_HOSTLIST
iva2k 0:e614f7875b60 187 /** struct used for local host-list */
iva2k 0:e614f7875b60 188 struct local_hostlist_entry {
iva2k 0:e614f7875b60 189 /** static hostname */
iva2k 0:e614f7875b60 190 const char *name;
iva2k 0:e614f7875b60 191 /** static host address in network byteorder */
iva2k 0:e614f7875b60 192 ip_addr_t addr;
iva2k 0:e614f7875b60 193 struct local_hostlist_entry *next;
iva2k 0:e614f7875b60 194 };
iva2k 0:e614f7875b60 195
iva2k 0:e614f7875b60 196 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
iva2k 0:e614f7875b60 197 /** Local host-list. For hostnames in this list, no
iva2k 0:e614f7875b60 198 * external name resolution is performed */
iva2k 0:e614f7875b60 199 static struct local_hostlist_entry *local_hostlist_dynamic;
iva2k 0:e614f7875b60 200 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
iva2k 0:e614f7875b60 201
iva2k 0:e614f7875b60 202 /** Defining this allows the local_hostlist_static to be placed in a different
iva2k 0:e614f7875b60 203 * linker section (e.g. FLASH) */
iva2k 0:e614f7875b60 204 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
iva2k 0:e614f7875b60 205 #define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
iva2k 0:e614f7875b60 206 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
iva2k 0:e614f7875b60 207 /** Defining this allows the local_hostlist_static to be placed in a different
iva2k 0:e614f7875b60 208 * linker section (e.g. FLASH) */
iva2k 0:e614f7875b60 209 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
iva2k 0:e614f7875b60 210 #define DNS_LOCAL_HOSTLIST_STORAGE_POST
iva2k 0:e614f7875b60 211 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
iva2k 0:e614f7875b60 212 DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
iva2k 0:e614f7875b60 213 DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
iva2k 0:e614f7875b60 214
iva2k 0:e614f7875b60 215 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
iva2k 0:e614f7875b60 216
iva2k 0:e614f7875b60 217 static void dns_init_local();
iva2k 0:e614f7875b60 218 #endif /* DNS_LOCAL_HOSTLIST */
iva2k 0:e614f7875b60 219
iva2k 0:e614f7875b60 220
iva2k 0:e614f7875b60 221 /* forward declarations */
iva2k 0:e614f7875b60 222 static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
iva2k 0:e614f7875b60 223 static void dns_check_entries(void);
iva2k 0:e614f7875b60 224
iva2k 0:e614f7875b60 225 /*-----------------------------------------------------------------------------
iva2k 0:e614f7875b60 226 * Globales
iva2k 0:e614f7875b60 227 *----------------------------------------------------------------------------*/
iva2k 0:e614f7875b60 228
iva2k 0:e614f7875b60 229 /* DNS variables */
iva2k 0:e614f7875b60 230 static struct udp_pcb *dns_pcb;
iva2k 0:e614f7875b60 231 static u8_t dns_seqno;
iva2k 0:e614f7875b60 232 static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
iva2k 0:e614f7875b60 233 static ip_addr_t dns_servers[DNS_MAX_SERVERS];
iva2k 0:e614f7875b60 234 /** Contiguous buffer for processing responses */
iva2k 0:e614f7875b60 235 static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)];
iva2k 0:e614f7875b60 236 static u8_t* dns_payload;
iva2k 0:e614f7875b60 237
iva2k 0:e614f7875b60 238 /**
iva2k 0:e614f7875b60 239 * Initialize the resolver: set up the UDP pcb and configure the default server
iva2k 0:e614f7875b60 240 * (DNS_SERVER_ADDRESS).
iva2k 0:e614f7875b60 241 */
iva2k 0:e614f7875b60 242 void
iva2k 0:e614f7875b60 243 dns_init()
iva2k 0:e614f7875b60 244 {
iva2k 0:e614f7875b60 245 ip_addr_t dnsserver;
iva2k 0:e614f7875b60 246
iva2k 0:e614f7875b60 247 dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer);
iva2k 0:e614f7875b60 248
iva2k 0:e614f7875b60 249 /* initialize default DNS server address */
iva2k 0:e614f7875b60 250 DNS_SERVER_ADDRESS(&dnsserver);
iva2k 0:e614f7875b60 251
iva2k 0:e614f7875b60 252 LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
iva2k 0:e614f7875b60 253
iva2k 0:e614f7875b60 254 /* if dns client not yet initialized... */
iva2k 0:e614f7875b60 255 if (dns_pcb == NULL) {
iva2k 0:e614f7875b60 256 dns_pcb = udp_new();
iva2k 0:e614f7875b60 257
iva2k 0:e614f7875b60 258 if (dns_pcb != NULL) {
iva2k 0:e614f7875b60 259 /* initialize DNS table not needed (initialized to zero since it is a
iva2k 0:e614f7875b60 260 * global variable) */
iva2k 0:e614f7875b60 261 LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
iva2k 0:e614f7875b60 262 DNS_STATE_UNUSED == 0);
iva2k 0:e614f7875b60 263
iva2k 0:e614f7875b60 264 /* initialize DNS client */
iva2k 0:e614f7875b60 265 udp_bind(dns_pcb, IP_ADDR_ANY, 0);
iva2k 0:e614f7875b60 266 udp_recv(dns_pcb, dns_recv, NULL);
iva2k 0:e614f7875b60 267
iva2k 0:e614f7875b60 268 /* initialize default DNS primary server */
iva2k 0:e614f7875b60 269 dns_setserver(0, &dnsserver);
iva2k 0:e614f7875b60 270 }
iva2k 0:e614f7875b60 271 }
iva2k 0:e614f7875b60 272 #if DNS_LOCAL_HOSTLIST
iva2k 0:e614f7875b60 273 dns_init_local();
iva2k 0:e614f7875b60 274 #endif
iva2k 0:e614f7875b60 275 }
iva2k 0:e614f7875b60 276
iva2k 0:e614f7875b60 277 /**
iva2k 0:e614f7875b60 278 * Initialize one of the DNS servers.
iva2k 0:e614f7875b60 279 *
iva2k 0:e614f7875b60 280 * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
iva2k 0:e614f7875b60 281 * @param dnsserver IP address of the DNS server to set
iva2k 0:e614f7875b60 282 */
iva2k 0:e614f7875b60 283 void
iva2k 0:e614f7875b60 284 dns_setserver(u8_t numdns, ip_addr_t *dnsserver)
iva2k 0:e614f7875b60 285 {
iva2k 0:e614f7875b60 286 if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) &&
iva2k 0:e614f7875b60 287 (dnsserver != NULL) && !ip_addr_isany(dnsserver)) {
iva2k 0:e614f7875b60 288 dns_servers[numdns] = (*dnsserver);
iva2k 0:e614f7875b60 289 }
iva2k 0:e614f7875b60 290 }
iva2k 0:e614f7875b60 291
iva2k 0:e614f7875b60 292 /**
iva2k 0:e614f7875b60 293 * Obtain one of the currently configured DNS server.
iva2k 0:e614f7875b60 294 *
iva2k 0:e614f7875b60 295 * @param numdns the index of the DNS server
iva2k 0:e614f7875b60 296 * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
iva2k 0:e614f7875b60 297 * server has not been configured.
iva2k 0:e614f7875b60 298 */
iva2k 0:e614f7875b60 299 ip_addr_t
iva2k 0:e614f7875b60 300 dns_getserver(u8_t numdns)
iva2k 0:e614f7875b60 301 {
iva2k 0:e614f7875b60 302 if (numdns < DNS_MAX_SERVERS) {
iva2k 0:e614f7875b60 303 return dns_servers[numdns];
iva2k 0:e614f7875b60 304 } else {
iva2k 0:e614f7875b60 305 return *IP_ADDR_ANY;
iva2k 0:e614f7875b60 306 }
iva2k 0:e614f7875b60 307 }
iva2k 0:e614f7875b60 308
iva2k 0:e614f7875b60 309 /**
iva2k 0:e614f7875b60 310 * The DNS resolver client timer - handle retries and timeouts and should
iva2k 0:e614f7875b60 311 * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
iva2k 0:e614f7875b60 312 */
iva2k 0:e614f7875b60 313 void
iva2k 0:e614f7875b60 314 dns_tmr(void)
iva2k 0:e614f7875b60 315 {
iva2k 0:e614f7875b60 316 if (dns_pcb != NULL) {
iva2k 0:e614f7875b60 317 LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
iva2k 0:e614f7875b60 318 dns_check_entries();
iva2k 0:e614f7875b60 319 }
iva2k 0:e614f7875b60 320 }
iva2k 0:e614f7875b60 321
iva2k 0:e614f7875b60 322 #if DNS_LOCAL_HOSTLIST
iva2k 0:e614f7875b60 323 static void
iva2k 0:e614f7875b60 324 dns_init_local()
iva2k 0:e614f7875b60 325 {
iva2k 0:e614f7875b60 326 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
iva2k 0:e614f7875b60 327 int i;
iva2k 0:e614f7875b60 328 struct local_hostlist_entry *entry;
iva2k 0:e614f7875b60 329 /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
iva2k 0:e614f7875b60 330 struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
iva2k 0:e614f7875b60 331 size_t namelen;
iva2k 0:e614f7875b60 332 for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) {
iva2k 0:e614f7875b60 333 struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
iva2k 0:e614f7875b60 334 LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
iva2k 0:e614f7875b60 335 namelen = strlen(init_entry->name);
iva2k 0:e614f7875b60 336 entry = mem_malloc((mem_size_t)(sizeof(struct local_hostlist_entry) + namelen + 1));
iva2k 0:e614f7875b60 337 LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
iva2k 0:e614f7875b60 338 if (entry != NULL) {
iva2k 0:e614f7875b60 339 entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
iva2k 0:e614f7875b60 340 MEMCPY((char*)entry->name, init_entry->name, namelen);
iva2k 0:e614f7875b60 341 ((char*)entry->name)[namelen] = 0;
iva2k 0:e614f7875b60 342 entry->addr = init_entry->addr;
iva2k 0:e614f7875b60 343 entry->next = local_hostlist_dynamic;
iva2k 0:e614f7875b60 344 local_hostlist_dynamic = entry;
iva2k 0:e614f7875b60 345 }
iva2k 0:e614f7875b60 346 }
iva2k 0:e614f7875b60 347 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
iva2k 0:e614f7875b60 348 }
iva2k 0:e614f7875b60 349
iva2k 0:e614f7875b60 350 /**
iva2k 0:e614f7875b60 351 * Scans the local host-list for a hostname.
iva2k 0:e614f7875b60 352 *
iva2k 0:e614f7875b60 353 * @param hostname Hostname to look for in the local host-list
iva2k 0:e614f7875b60 354 * @return The first IP address for the hostname in the local host-list or
iva2k 0:e614f7875b60 355 * IPADDR_NONE if not found.
iva2k 0:e614f7875b60 356 */
iva2k 0:e614f7875b60 357 static u32_t
iva2k 0:e614f7875b60 358 dns_lookup_local(const char *hostname)
iva2k 0:e614f7875b60 359 {
iva2k 0:e614f7875b60 360 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
iva2k 0:e614f7875b60 361 struct local_hostlist_entry *entry = local_hostlist_dynamic;
iva2k 0:e614f7875b60 362 while(entry != NULL) {
iva2k 0:e614f7875b60 363 if(strcmp(entry->name, hostname) == 0) {
iva2k 0:e614f7875b60 364 return ip4_addr_get_u32(&entry->addr);
iva2k 0:e614f7875b60 365 }
iva2k 0:e614f7875b60 366 entry = entry->next;
iva2k 0:e614f7875b60 367 }
iva2k 0:e614f7875b60 368 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
iva2k 0:e614f7875b60 369 int i;
iva2k 0:e614f7875b60 370 for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) {
iva2k 0:e614f7875b60 371 if(strcmp(local_hostlist_static[i].name, hostname) == 0) {
iva2k 0:e614f7875b60 372 return ip4_addr_get_u32(&local_hostlist_static[i].addr);
iva2k 0:e614f7875b60 373 }
iva2k 0:e614f7875b60 374 }
iva2k 0:e614f7875b60 375 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
iva2k 0:e614f7875b60 376 return IPADDR_NONE;
iva2k 0:e614f7875b60 377 }
iva2k 0:e614f7875b60 378
iva2k 0:e614f7875b60 379 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
iva2k 0:e614f7875b60 380 /** Remove all entries from the local host-list for a specific hostname
iva2k 0:e614f7875b60 381 * and/or IP addess
iva2k 0:e614f7875b60 382 *
iva2k 0:e614f7875b60 383 * @param hostname hostname for which entries shall be removed from the local
iva2k 0:e614f7875b60 384 * host-list
iva2k 0:e614f7875b60 385 * @param addr address for which entries shall be removed from the local host-list
iva2k 0:e614f7875b60 386 * @return the number of removed entries
iva2k 0:e614f7875b60 387 */
iva2k 0:e614f7875b60 388 int
iva2k 0:e614f7875b60 389 dns_local_removehost(const char *hostname, const ip_addr_t *addr)
iva2k 0:e614f7875b60 390 {
iva2k 0:e614f7875b60 391 int removed = 0;
iva2k 0:e614f7875b60 392 struct local_hostlist_entry *entry = local_hostlist_dynamic;
iva2k 0:e614f7875b60 393 struct local_hostlist_entry *last_entry = NULL;
iva2k 0:e614f7875b60 394 while (entry != NULL) {
iva2k 0:e614f7875b60 395 if (((hostname == NULL) || !strcmp(entry->name, hostname)) &&
iva2k 0:e614f7875b60 396 ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
iva2k 0:e614f7875b60 397 struct local_hostlist_entry *free_entry;
iva2k 0:e614f7875b60 398 if (last_entry != NULL) {
iva2k 0:e614f7875b60 399 last_entry->next = entry->next;
iva2k 0:e614f7875b60 400 } else {
iva2k 0:e614f7875b60 401 local_hostlist_dynamic = entry->next;
iva2k 0:e614f7875b60 402 }
iva2k 0:e614f7875b60 403 free_entry = entry;
iva2k 0:e614f7875b60 404 entry = entry->next;
iva2k 0:e614f7875b60 405 mem_free(free_entry);
iva2k 0:e614f7875b60 406 removed++;
iva2k 0:e614f7875b60 407 } else {
iva2k 0:e614f7875b60 408 last_entry = entry;
iva2k 0:e614f7875b60 409 entry = entry->next;
iva2k 0:e614f7875b60 410 }
iva2k 0:e614f7875b60 411 }
iva2k 0:e614f7875b60 412 return removed;
iva2k 0:e614f7875b60 413 }
iva2k 0:e614f7875b60 414
iva2k 0:e614f7875b60 415 /**
iva2k 0:e614f7875b60 416 * Add a hostname/IP address pair to the local host-list.
iva2k 0:e614f7875b60 417 * Duplicates are not checked.
iva2k 0:e614f7875b60 418 *
iva2k 0:e614f7875b60 419 * @param hostname hostname of the new entry
iva2k 0:e614f7875b60 420 * @param addr IP address of the new entry
iva2k 0:e614f7875b60 421 * @return ERR_OK if succeeded or ERR_MEM on memory error
iva2k 0:e614f7875b60 422 */
iva2k 0:e614f7875b60 423 err_t
iva2k 0:e614f7875b60 424 dns_local_addhost(const char *hostname, const ip_addr_t *addr)
iva2k 0:e614f7875b60 425 {
iva2k 0:e614f7875b60 426 struct local_hostlist_entry *entry;
iva2k 0:e614f7875b60 427 size_t namelen;
iva2k 0:e614f7875b60 428 LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
iva2k 0:e614f7875b60 429 namelen = strlen(hostname);
iva2k 0:e614f7875b60 430 entry = mem_malloc((mem_size_t)(sizeof(struct local_hostlist_entry) + namelen + 1));
iva2k 0:e614f7875b60 431 if (entry == NULL) {
iva2k 0:e614f7875b60 432 return ERR_MEM;
iva2k 0:e614f7875b60 433 }
iva2k 0:e614f7875b60 434 entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
iva2k 0:e614f7875b60 435 MEMCPY((char*)entry->name, hostname, namelen);
iva2k 0:e614f7875b60 436 ((char*)entry->name)[namelen] = 0;
iva2k 0:e614f7875b60 437 ip_addr_copy(entry->addr, *addr);
iva2k 0:e614f7875b60 438 entry->next = local_hostlist_dynamic;
iva2k 0:e614f7875b60 439 local_hostlist_dynamic = entry;
iva2k 0:e614f7875b60 440 return ERR_OK;
iva2k 0:e614f7875b60 441 }
iva2k 0:e614f7875b60 442 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
iva2k 0:e614f7875b60 443 #endif /* DNS_LOCAL_HOSTLIST */
iva2k 0:e614f7875b60 444
iva2k 0:e614f7875b60 445 /**
iva2k 0:e614f7875b60 446 * Look up a hostname in the array of known hostnames.
iva2k 0:e614f7875b60 447 *
iva2k 0:e614f7875b60 448 * @note This function only looks in the internal array of known
iva2k 0:e614f7875b60 449 * hostnames, it does not send out a query for the hostname if none
iva2k 0:e614f7875b60 450 * was found. The function dns_enqueue() can be used to send a query
iva2k 0:e614f7875b60 451 * for a hostname.
iva2k 0:e614f7875b60 452 *
iva2k 0:e614f7875b60 453 * @param name the hostname to look up
iva2k 0:e614f7875b60 454 * @return the hostname's IP address, as u32_t (instead of ip_addr_t to
iva2k 0:e614f7875b60 455 * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
iva2k 0:e614f7875b60 456 * was not found in the cached dns_table.
iva2k 0:e614f7875b60 457 */
iva2k 0:e614f7875b60 458 static u32_t
iva2k 0:e614f7875b60 459 dns_lookup(const char *name)
iva2k 0:e614f7875b60 460 {
iva2k 0:e614f7875b60 461 u8_t i;
iva2k 0:e614f7875b60 462 #if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
iva2k 0:e614f7875b60 463 u32_t addr;
iva2k 0:e614f7875b60 464 #endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
iva2k 0:e614f7875b60 465 #if DNS_LOCAL_HOSTLIST
iva2k 0:e614f7875b60 466 if ((addr = dns_lookup_local(name)) != IPADDR_NONE) {
iva2k 0:e614f7875b60 467 return addr;
iva2k 0:e614f7875b60 468 }
iva2k 0:e614f7875b60 469 #endif /* DNS_LOCAL_HOSTLIST */
iva2k 0:e614f7875b60 470 #ifdef DNS_LOOKUP_LOCAL_EXTERN
iva2k 0:e614f7875b60 471 if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) {
iva2k 0:e614f7875b60 472 return addr;
iva2k 0:e614f7875b60 473 }
iva2k 0:e614f7875b60 474 #endif /* DNS_LOOKUP_LOCAL_EXTERN */
iva2k 0:e614f7875b60 475
iva2k 0:e614f7875b60 476 /* Walk through name list, return entry if found. If not, return NULL. */
iva2k 0:e614f7875b60 477 for (i = 0; i < DNS_TABLE_SIZE; ++i) {
iva2k 0:e614f7875b60 478 if ((dns_table[i].state == DNS_STATE_DONE) &&
iva2k 0:e614f7875b60 479 (strcmp(name, dns_table[i].name) == 0)) {
iva2k 0:e614f7875b60 480 LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
iva2k 0:e614f7875b60 481 ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
iva2k 0:e614f7875b60 482 LWIP_DEBUGF(DNS_DEBUG, ("\n"));
iva2k 0:e614f7875b60 483 return ip4_addr_get_u32(&dns_table[i].ipaddr);
iva2k 0:e614f7875b60 484 }
iva2k 0:e614f7875b60 485 }
iva2k 0:e614f7875b60 486
iva2k 0:e614f7875b60 487 return IPADDR_NONE;
iva2k 0:e614f7875b60 488 }
iva2k 0:e614f7875b60 489
iva2k 0:e614f7875b60 490 #if DNS_DOES_NAME_CHECK
iva2k 0:e614f7875b60 491 /**
iva2k 0:e614f7875b60 492 * Compare the "dotted" name "query" with the encoded name "response"
iva2k 0:e614f7875b60 493 * to make sure an answer from the DNS server matches the current dns_table
iva2k 0:e614f7875b60 494 * entry (otherwise, answers might arrive late for hostname not on the list
iva2k 0:e614f7875b60 495 * any more).
iva2k 0:e614f7875b60 496 *
iva2k 0:e614f7875b60 497 * @param query hostname (not encoded) from the dns_table
iva2k 0:e614f7875b60 498 * @param response encoded hostname in the DNS response
iva2k 0:e614f7875b60 499 * @return 0: names equal; 1: names differ
iva2k 0:e614f7875b60 500 */
iva2k 0:e614f7875b60 501 static u8_t
iva2k 0:e614f7875b60 502 dns_compare_name(unsigned char *query, unsigned char *response)
iva2k 0:e614f7875b60 503 {
iva2k 0:e614f7875b60 504 unsigned char n;
iva2k 0:e614f7875b60 505
iva2k 0:e614f7875b60 506 do {
iva2k 0:e614f7875b60 507 n = *response++;
iva2k 0:e614f7875b60 508 /** @see RFC 1035 - 4.1.4. Message compression */
iva2k 0:e614f7875b60 509 if ((n & 0xc0) == 0xc0) {
iva2k 0:e614f7875b60 510 /* Compressed name */
iva2k 0:e614f7875b60 511 break;
iva2k 0:e614f7875b60 512 } else {
iva2k 0:e614f7875b60 513 /* Not compressed name */
iva2k 0:e614f7875b60 514 while (n > 0) {
iva2k 0:e614f7875b60 515 if ((*query) != (*response)) {
iva2k 0:e614f7875b60 516 return 1;
iva2k 0:e614f7875b60 517 }
iva2k 0:e614f7875b60 518 ++response;
iva2k 0:e614f7875b60 519 ++query;
iva2k 0:e614f7875b60 520 --n;
iva2k 0:e614f7875b60 521 };
iva2k 0:e614f7875b60 522 ++query;
iva2k 0:e614f7875b60 523 }
iva2k 0:e614f7875b60 524 } while (*response != 0);
iva2k 0:e614f7875b60 525
iva2k 0:e614f7875b60 526 return 0;
iva2k 0:e614f7875b60 527 }
iva2k 0:e614f7875b60 528 #endif /* DNS_DOES_NAME_CHECK */
iva2k 0:e614f7875b60 529
iva2k 0:e614f7875b60 530 /**
iva2k 0:e614f7875b60 531 * Walk through a compact encoded DNS name and return the end of the name.
iva2k 0:e614f7875b60 532 *
iva2k 0:e614f7875b60 533 * @param query encoded DNS name in the DNS server response
iva2k 0:e614f7875b60 534 * @return end of the name
iva2k 0:e614f7875b60 535 */
iva2k 0:e614f7875b60 536 static unsigned char *
iva2k 0:e614f7875b60 537 dns_parse_name(unsigned char *query)
iva2k 0:e614f7875b60 538 {
iva2k 0:e614f7875b60 539 unsigned char n;
iva2k 0:e614f7875b60 540
iva2k 0:e614f7875b60 541 do {
iva2k 0:e614f7875b60 542 n = *query++;
iva2k 0:e614f7875b60 543 /** @see RFC 1035 - 4.1.4. Message compression */
iva2k 0:e614f7875b60 544 if ((n & 0xc0) == 0xc0) {
iva2k 0:e614f7875b60 545 /* Compressed name */
iva2k 0:e614f7875b60 546 break;
iva2k 0:e614f7875b60 547 } else {
iva2k 0:e614f7875b60 548 /* Not compressed name */
iva2k 0:e614f7875b60 549 while (n > 0) {
iva2k 0:e614f7875b60 550 ++query;
iva2k 0:e614f7875b60 551 --n;
iva2k 0:e614f7875b60 552 };
iva2k 0:e614f7875b60 553 }
iva2k 0:e614f7875b60 554 } while (*query != 0);
iva2k 0:e614f7875b60 555
iva2k 0:e614f7875b60 556 return query + 1;
iva2k 0:e614f7875b60 557 }
iva2k 0:e614f7875b60 558
iva2k 0:e614f7875b60 559 /**
iva2k 0:e614f7875b60 560 * Send a DNS query packet.
iva2k 0:e614f7875b60 561 *
iva2k 0:e614f7875b60 562 * @param numdns index of the DNS server in the dns_servers table
iva2k 0:e614f7875b60 563 * @param name hostname to query
iva2k 0:e614f7875b60 564 * @param id index of the hostname in dns_table, used as transaction ID in the
iva2k 0:e614f7875b60 565 * DNS query packet
iva2k 0:e614f7875b60 566 * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
iva2k 0:e614f7875b60 567 */
iva2k 0:e614f7875b60 568 static err_t
iva2k 0:e614f7875b60 569 dns_send(u8_t numdns, const char* name, u8_t id)
iva2k 0:e614f7875b60 570 {
iva2k 0:e614f7875b60 571 err_t err;
iva2k 0:e614f7875b60 572 struct dns_hdr *hdr;
iva2k 0:e614f7875b60 573 struct dns_query qry;
iva2k 0:e614f7875b60 574 struct pbuf *p;
iva2k 0:e614f7875b60 575 char *query, *nptr;
iva2k 0:e614f7875b60 576 const char *pHostname;
iva2k 0:e614f7875b60 577 u8_t n;
iva2k 0:e614f7875b60 578
iva2k 0:e614f7875b60 579 LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
iva2k 0:e614f7875b60 580 (u16_t)(numdns), name));
iva2k 0:e614f7875b60 581 LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS);
iva2k 0:e614f7875b60 582 LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns]));
iva2k 0:e614f7875b60 583
iva2k 0:e614f7875b60 584 /* if here, we have either a new query or a retry on a previous query to process */
iva2k 0:e614f7875b60 585 p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH +
iva2k 0:e614f7875b60 586 SIZEOF_DNS_QUERY, PBUF_RAM);
iva2k 0:e614f7875b60 587 if (p != NULL) {
iva2k 0:e614f7875b60 588 LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
iva2k 0:e614f7875b60 589 /* fill dns header */
iva2k 0:e614f7875b60 590 hdr = (struct dns_hdr*)p->payload;
iva2k 0:e614f7875b60 591 memset(hdr, 0, SIZEOF_DNS_HDR);
iva2k 0:e614f7875b60 592 hdr->id = htons(id);
iva2k 0:e614f7875b60 593 hdr->flags1 = DNS_FLAG1_RD;
iva2k 0:e614f7875b60 594 hdr->numquestions = htons(1);
iva2k 0:e614f7875b60 595 query = (char*)hdr + SIZEOF_DNS_HDR;
iva2k 0:e614f7875b60 596 pHostname = name;
iva2k 0:e614f7875b60 597 --pHostname;
iva2k 0:e614f7875b60 598
iva2k 0:e614f7875b60 599 /* convert hostname into suitable query format. */
iva2k 0:e614f7875b60 600 do {
iva2k 0:e614f7875b60 601 ++pHostname;
iva2k 0:e614f7875b60 602 nptr = query;
iva2k 0:e614f7875b60 603 ++query;
iva2k 0:e614f7875b60 604 for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
iva2k 0:e614f7875b60 605 *query = *pHostname;
iva2k 0:e614f7875b60 606 ++query;
iva2k 0:e614f7875b60 607 ++n;
iva2k 0:e614f7875b60 608 }
iva2k 0:e614f7875b60 609 *nptr = n;
iva2k 0:e614f7875b60 610 } while(*pHostname != 0);
iva2k 0:e614f7875b60 611 *query++='\0';
iva2k 0:e614f7875b60 612
iva2k 0:e614f7875b60 613 /* fill dns query */
iva2k 0:e614f7875b60 614 qry.type = htons(DNS_RRTYPE_A);
iva2k 0:e614f7875b60 615 qry.cls = htons(DNS_RRCLASS_IN);
iva2k 0:e614f7875b60 616 SMEMCPY(query, &qry, SIZEOF_DNS_QUERY);
iva2k 0:e614f7875b60 617
iva2k 0:e614f7875b60 618 /* resize pbuf to the exact dns query */
iva2k 0:e614f7875b60 619 pbuf_realloc(p, (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))));
iva2k 0:e614f7875b60 620
iva2k 0:e614f7875b60 621 /* connect to the server for faster receiving */
iva2k 0:e614f7875b60 622 udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT);
iva2k 0:e614f7875b60 623 /* send dns packet */
iva2k 0:e614f7875b60 624 err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT);
iva2k 0:e614f7875b60 625
iva2k 0:e614f7875b60 626 /* free pbuf */
iva2k 0:e614f7875b60 627 pbuf_free(p);
iva2k 0:e614f7875b60 628 } else {
iva2k 0:e614f7875b60 629 err = ERR_MEM;
iva2k 0:e614f7875b60 630 }
iva2k 0:e614f7875b60 631
iva2k 0:e614f7875b60 632 return err;
iva2k 0:e614f7875b60 633 }
iva2k 0:e614f7875b60 634
iva2k 0:e614f7875b60 635 /**
iva2k 0:e614f7875b60 636 * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query.
iva2k 0:e614f7875b60 637 * Check an entry in the dns_table:
iva2k 0:e614f7875b60 638 * - send out query for new entries
iva2k 0:e614f7875b60 639 * - retry old pending entries on timeout (also with different servers)
iva2k 0:e614f7875b60 640 * - remove completed entries from the table if their TTL has expired
iva2k 0:e614f7875b60 641 *
iva2k 0:e614f7875b60 642 * @param i index of the dns_table entry to check
iva2k 0:e614f7875b60 643 */
iva2k 0:e614f7875b60 644 static void
iva2k 0:e614f7875b60 645 dns_check_entry(u8_t i)
iva2k 0:e614f7875b60 646 {
iva2k 0:e614f7875b60 647 struct dns_table_entry *pEntry = &dns_table[i];
iva2k 0:e614f7875b60 648
iva2k 0:e614f7875b60 649 LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
iva2k 0:e614f7875b60 650
iva2k 0:e614f7875b60 651 switch(pEntry->state) {
iva2k 0:e614f7875b60 652
iva2k 0:e614f7875b60 653 case DNS_STATE_NEW: {
iva2k 0:e614f7875b60 654 /* initialize new entry */
iva2k 0:e614f7875b60 655 pEntry->state = DNS_STATE_ASKING;
iva2k 0:e614f7875b60 656 pEntry->numdns = 0;
iva2k 0:e614f7875b60 657 pEntry->tmr = 1;
iva2k 0:e614f7875b60 658 pEntry->retries = 0;
iva2k 0:e614f7875b60 659
iva2k 0:e614f7875b60 660 /* send DNS packet for this entry */
iva2k 0:e614f7875b60 661 dns_send(pEntry->numdns, pEntry->name, i);
iva2k 0:e614f7875b60 662 break;
iva2k 0:e614f7875b60 663 }
iva2k 0:e614f7875b60 664
iva2k 0:e614f7875b60 665 case DNS_STATE_ASKING: {
iva2k 0:e614f7875b60 666 if (--pEntry->tmr == 0) {
iva2k 0:e614f7875b60 667 if (++pEntry->retries == DNS_MAX_RETRIES) {
iva2k 0:e614f7875b60 668 if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) {
iva2k 0:e614f7875b60 669 /* change of server */
iva2k 0:e614f7875b60 670 pEntry->numdns++;
iva2k 0:e614f7875b60 671 pEntry->tmr = 1;
iva2k 0:e614f7875b60 672 pEntry->retries = 0;
iva2k 0:e614f7875b60 673 break;
iva2k 0:e614f7875b60 674 } else {
iva2k 0:e614f7875b60 675 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));
iva2k 0:e614f7875b60 676 /* call specified callback function if provided */
iva2k 0:e614f7875b60 677 if (pEntry->found)
iva2k 0:e614f7875b60 678 (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
iva2k 0:e614f7875b60 679 /* flush this entry */
iva2k 0:e614f7875b60 680 pEntry->state = DNS_STATE_UNUSED;
iva2k 0:e614f7875b60 681 pEntry->found = NULL;
iva2k 0:e614f7875b60 682 break;
iva2k 0:e614f7875b60 683 }
iva2k 0:e614f7875b60 684 }
iva2k 0:e614f7875b60 685
iva2k 0:e614f7875b60 686 /* wait longer for the next retry */
iva2k 0:e614f7875b60 687 pEntry->tmr = pEntry->retries;
iva2k 0:e614f7875b60 688
iva2k 0:e614f7875b60 689 /* send DNS packet for this entry */
iva2k 0:e614f7875b60 690 dns_send(pEntry->numdns, pEntry->name, i);
iva2k 0:e614f7875b60 691 }
iva2k 0:e614f7875b60 692 break;
iva2k 0:e614f7875b60 693 }
iva2k 0:e614f7875b60 694
iva2k 0:e614f7875b60 695 case DNS_STATE_DONE: {
iva2k 0:e614f7875b60 696 /* if the time to live is nul */
iva2k 0:e614f7875b60 697 if (--pEntry->ttl == 0) {
iva2k 0:e614f7875b60 698 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));
iva2k 0:e614f7875b60 699 /* flush this entry */
iva2k 0:e614f7875b60 700 pEntry->state = DNS_STATE_UNUSED;
iva2k 0:e614f7875b60 701 pEntry->found = NULL;
iva2k 0:e614f7875b60 702 }
iva2k 0:e614f7875b60 703 break;
iva2k 0:e614f7875b60 704 }
iva2k 0:e614f7875b60 705 case DNS_STATE_UNUSED:
iva2k 0:e614f7875b60 706 /* nothing to do */
iva2k 0:e614f7875b60 707 break;
iva2k 0:e614f7875b60 708 default:
iva2k 0:e614f7875b60 709 LWIP_ASSERT("unknown dns_table entry state:", 0);
iva2k 0:e614f7875b60 710 break;
iva2k 0:e614f7875b60 711 }
iva2k 0:e614f7875b60 712 }
iva2k 0:e614f7875b60 713
iva2k 0:e614f7875b60 714 /**
iva2k 0:e614f7875b60 715 * Call dns_check_entry for each entry in dns_table - check all entries.
iva2k 0:e614f7875b60 716 */
iva2k 0:e614f7875b60 717 static void
iva2k 0:e614f7875b60 718 dns_check_entries(void)
iva2k 0:e614f7875b60 719 {
iva2k 0:e614f7875b60 720 u8_t i;
iva2k 0:e614f7875b60 721
iva2k 0:e614f7875b60 722 for (i = 0; i < DNS_TABLE_SIZE; ++i) {
iva2k 0:e614f7875b60 723 dns_check_entry(i);
iva2k 0:e614f7875b60 724 }
iva2k 0:e614f7875b60 725 }
iva2k 0:e614f7875b60 726
iva2k 0:e614f7875b60 727 /**
iva2k 0:e614f7875b60 728 * Receive input function for DNS response packets arriving for the dns UDP pcb.
iva2k 0:e614f7875b60 729 *
iva2k 0:e614f7875b60 730 * @params see udp.h
iva2k 0:e614f7875b60 731 */
iva2k 0:e614f7875b60 732 static void
iva2k 0:e614f7875b60 733 dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
iva2k 0:e614f7875b60 734 {
iva2k 0:e614f7875b60 735 u16_t i;
iva2k 0:e614f7875b60 736 char *pHostname;
iva2k 0:e614f7875b60 737 struct dns_hdr *hdr;
iva2k 0:e614f7875b60 738 struct dns_answer ans;
iva2k 0:e614f7875b60 739 struct dns_table_entry *pEntry;
iva2k 0:e614f7875b60 740 u16_t nquestions, nanswers;
iva2k 0:e614f7875b60 741
iva2k 0:e614f7875b60 742 LWIP_UNUSED_ARG(arg);
iva2k 0:e614f7875b60 743 LWIP_UNUSED_ARG(pcb);
iva2k 0:e614f7875b60 744 LWIP_UNUSED_ARG(addr);
iva2k 0:e614f7875b60 745 LWIP_UNUSED_ARG(port);
iva2k 0:e614f7875b60 746
iva2k 0:e614f7875b60 747 /* is the dns message too big ? */
iva2k 0:e614f7875b60 748 if (p->tot_len > DNS_MSG_SIZE) {
iva2k 0:e614f7875b60 749 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
iva2k 0:e614f7875b60 750 /* free pbuf and return */
iva2k 0:e614f7875b60 751 goto memerr;
iva2k 0:e614f7875b60 752 }
iva2k 0:e614f7875b60 753
iva2k 0:e614f7875b60 754 /* is the dns message big enough ? */
iva2k 0:e614f7875b60 755 if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) {
iva2k 0:e614f7875b60 756 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
iva2k 0:e614f7875b60 757 /* free pbuf and return */
iva2k 0:e614f7875b60 758 goto memerr;
iva2k 0:e614f7875b60 759 }
iva2k 0:e614f7875b60 760
iva2k 0:e614f7875b60 761 /* copy dns payload inside static buffer for processing */
iva2k 0:e614f7875b60 762 if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) {
iva2k 0:e614f7875b60 763 /* The ID in the DNS header should be our entry into the name table. */
iva2k 0:e614f7875b60 764 hdr = (struct dns_hdr*)dns_payload;
iva2k 0:e614f7875b60 765 i = htons(hdr->id);
iva2k 0:e614f7875b60 766 if (i < DNS_TABLE_SIZE) {
iva2k 0:e614f7875b60 767 pEntry = &dns_table[i];
iva2k 0:e614f7875b60 768 if(pEntry->state == DNS_STATE_ASKING) {
iva2k 0:e614f7875b60 769 /* This entry is now completed. */
iva2k 0:e614f7875b60 770 pEntry->state = DNS_STATE_DONE;
iva2k 0:e614f7875b60 771 pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
iva2k 0:e614f7875b60 772
iva2k 0:e614f7875b60 773 /* We only care about the question(s) and the answers. The authrr
iva2k 0:e614f7875b60 774 and the extrarr are simply discarded. */
iva2k 0:e614f7875b60 775 nquestions = htons(hdr->numquestions);
iva2k 0:e614f7875b60 776 nanswers = htons(hdr->numanswers);
iva2k 0:e614f7875b60 777
iva2k 0:e614f7875b60 778 /* Check for error. If so, call callback to inform. */
iva2k 0:e614f7875b60 779 if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) {
iva2k 0:e614f7875b60 780 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));
iva2k 0:e614f7875b60 781 /* call callback to indicate error, clean up memory and return */
iva2k 0:e614f7875b60 782 goto responseerr;
iva2k 0:e614f7875b60 783 }
iva2k 0:e614f7875b60 784
iva2k 0:e614f7875b60 785 #if DNS_DOES_NAME_CHECK
iva2k 0:e614f7875b60 786 /* Check if the name in the "question" part match with the name in the entry. */
iva2k 0:e614f7875b60 787 if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) {
iva2k 0:e614f7875b60 788 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name));
iva2k 0:e614f7875b60 789 /* call callback to indicate error, clean up memory and return */
iva2k 0:e614f7875b60 790 goto responseerr;
iva2k 0:e614f7875b60 791 }
iva2k 0:e614f7875b60 792 #endif /* DNS_DOES_NAME_CHECK */
iva2k 0:e614f7875b60 793
iva2k 0:e614f7875b60 794 /* Skip the name in the "question" part */
iva2k 0:e614f7875b60 795 pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY;
iva2k 0:e614f7875b60 796
iva2k 0:e614f7875b60 797 while (nanswers > 0) {
iva2k 0:e614f7875b60 798 /* skip answer resource record's host name */
iva2k 0:e614f7875b60 799 pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
iva2k 0:e614f7875b60 800
iva2k 0:e614f7875b60 801 /* Check for IP address type and Internet class. Others are discarded. */
iva2k 0:e614f7875b60 802 SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER);
iva2k 0:e614f7875b60 803 if((ans.type == htons(DNS_RRTYPE_A)) && (ans.cls == htons(DNS_RRCLASS_IN)) &&
iva2k 0:e614f7875b60 804 (ans.len == htons(sizeof(ip_addr_t))) ) {
iva2k 0:e614f7875b60 805 /* read the answer resource record's TTL, and maximize it if needed */
iva2k 0:e614f7875b60 806 pEntry->ttl = ntohl(ans.ttl);
iva2k 0:e614f7875b60 807 if (pEntry->ttl > DNS_MAX_TTL) {
iva2k 0:e614f7875b60 808 pEntry->ttl = DNS_MAX_TTL;
iva2k 0:e614f7875b60 809 }
iva2k 0:e614f7875b60 810 /* read the IP address after answer resource record's header */
iva2k 0:e614f7875b60 811 SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t));
iva2k 0:e614f7875b60 812 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));
iva2k 0:e614f7875b60 813 ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
iva2k 0:e614f7875b60 814 LWIP_DEBUGF(DNS_DEBUG, ("\n"));
iva2k 0:e614f7875b60 815 /* call specified callback function if provided */
iva2k 0:e614f7875b60 816 if (pEntry->found) {
iva2k 0:e614f7875b60 817 (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);
iva2k 0:e614f7875b60 818 }
iva2k 0:e614f7875b60 819 /* deallocate memory and return */
iva2k 0:e614f7875b60 820 goto memerr;
iva2k 0:e614f7875b60 821 } else {
iva2k 0:e614f7875b60 822 pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len);
iva2k 0:e614f7875b60 823 }
iva2k 0:e614f7875b60 824 --nanswers;
iva2k 0:e614f7875b60 825 }
iva2k 0:e614f7875b60 826 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
iva2k 0:e614f7875b60 827 /* call callback to indicate error, clean up memory and return */
iva2k 0:e614f7875b60 828 goto responseerr;
iva2k 0:e614f7875b60 829 }
iva2k 0:e614f7875b60 830 }
iva2k 0:e614f7875b60 831 }
iva2k 0:e614f7875b60 832
iva2k 0:e614f7875b60 833 /* deallocate memory and return */
iva2k 0:e614f7875b60 834 goto memerr;
iva2k 0:e614f7875b60 835
iva2k 0:e614f7875b60 836 responseerr:
iva2k 0:e614f7875b60 837 /* ERROR: call specified callback function with NULL as name to indicate an error */
iva2k 0:e614f7875b60 838 if (pEntry->found) {
iva2k 0:e614f7875b60 839 (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
iva2k 0:e614f7875b60 840 }
iva2k 0:e614f7875b60 841 /* flush this entry */
iva2k 0:e614f7875b60 842 pEntry->state = DNS_STATE_UNUSED;
iva2k 0:e614f7875b60 843 pEntry->found = NULL;
iva2k 0:e614f7875b60 844
iva2k 0:e614f7875b60 845 memerr:
iva2k 0:e614f7875b60 846 /* free pbuf */
iva2k 0:e614f7875b60 847 pbuf_free(p);
iva2k 0:e614f7875b60 848 return;
iva2k 0:e614f7875b60 849 }
iva2k 0:e614f7875b60 850
iva2k 0:e614f7875b60 851 /**
iva2k 0:e614f7875b60 852 * Queues a new hostname to resolve and sends out a DNS query for that hostname
iva2k 0:e614f7875b60 853 *
iva2k 0:e614f7875b60 854 * @param name the hostname that is to be queried
iva2k 0:e614f7875b60 855 * @param found a callback founction to be called on success, failure or timeout
iva2k 0:e614f7875b60 856 * @param callback_arg argument to pass to the callback function
iva2k 0:e614f7875b60 857 * @return @return a err_t return code.
iva2k 0:e614f7875b60 858 */
iva2k 0:e614f7875b60 859 static err_t
iva2k 0:e614f7875b60 860 dns_enqueue(const char *name, dns_found_callback found, void *callback_arg)
iva2k 0:e614f7875b60 861 {
iva2k 0:e614f7875b60 862 u8_t i;
iva2k 0:e614f7875b60 863 u8_t lseq, lseqi;
iva2k 0:e614f7875b60 864 struct dns_table_entry *pEntry = NULL;
iva2k 0:e614f7875b60 865 size_t namelen;
iva2k 0:e614f7875b60 866
iva2k 0:e614f7875b60 867 /* search an unused entry, or the oldest one */
iva2k 0:e614f7875b60 868 lseq = lseqi = 0;
iva2k 0:e614f7875b60 869 for (i = 0; i < DNS_TABLE_SIZE; ++i) {
iva2k 0:e614f7875b60 870 pEntry = &dns_table[i];
iva2k 0:e614f7875b60 871 /* is it an unused entry ? */
iva2k 0:e614f7875b60 872 if (pEntry->state == DNS_STATE_UNUSED)
iva2k 0:e614f7875b60 873 break;
iva2k 0:e614f7875b60 874
iva2k 0:e614f7875b60 875 /* check if this is the oldest completed entry */
iva2k 0:e614f7875b60 876 if (pEntry->state == DNS_STATE_DONE) {
iva2k 0:e614f7875b60 877 if ((dns_seqno - pEntry->seqno) > lseq) {
iva2k 0:e614f7875b60 878 lseq = dns_seqno - pEntry->seqno;
iva2k 0:e614f7875b60 879 lseqi = i;
iva2k 0:e614f7875b60 880 }
iva2k 0:e614f7875b60 881 }
iva2k 0:e614f7875b60 882 }
iva2k 0:e614f7875b60 883
iva2k 0:e614f7875b60 884 /* if we don't have found an unused entry, use the oldest completed one */
iva2k 0:e614f7875b60 885 if (i == DNS_TABLE_SIZE) {
iva2k 0:e614f7875b60 886 if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
iva2k 0:e614f7875b60 887 /* no entry can't be used now, table is full */
iva2k 0:e614f7875b60 888 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
iva2k 0:e614f7875b60 889 return ERR_MEM;
iva2k 0:e614f7875b60 890 } else {
iva2k 0:e614f7875b60 891 /* use the oldest completed one */
iva2k 0:e614f7875b60 892 i = lseqi;
iva2k 0:e614f7875b60 893 pEntry = &dns_table[i];
iva2k 0:e614f7875b60 894 }
iva2k 0:e614f7875b60 895 }
iva2k 0:e614f7875b60 896
iva2k 0:e614f7875b60 897 /* use this entry */
iva2k 0:e614f7875b60 898 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
iva2k 0:e614f7875b60 899
iva2k 0:e614f7875b60 900 /* fill the entry */
iva2k 0:e614f7875b60 901 pEntry->state = DNS_STATE_NEW;
iva2k 0:e614f7875b60 902 pEntry->seqno = dns_seqno++;
iva2k 0:e614f7875b60 903 pEntry->found = found;
iva2k 0:e614f7875b60 904 pEntry->arg = callback_arg;
iva2k 0:e614f7875b60 905 namelen = LWIP_MIN(strlen(name), DNS_MAX_NAME_LENGTH-1);
iva2k 0:e614f7875b60 906 MEMCPY(pEntry->name, name, namelen);
iva2k 0:e614f7875b60 907 pEntry->name[namelen] = 0;
iva2k 0:e614f7875b60 908
iva2k 0:e614f7875b60 909 /* force to send query without waiting timer */
iva2k 0:e614f7875b60 910 dns_check_entry(i);
iva2k 0:e614f7875b60 911
iva2k 0:e614f7875b60 912 /* dns query is enqueued */
iva2k 0:e614f7875b60 913 return ERR_INPROGRESS;
iva2k 0:e614f7875b60 914 }
iva2k 0:e614f7875b60 915
iva2k 0:e614f7875b60 916 /**
iva2k 0:e614f7875b60 917 * Resolve a hostname (string) into an IP address.
iva2k 0:e614f7875b60 918 * NON-BLOCKING callback version for use with raw API!!!
iva2k 0:e614f7875b60 919 *
iva2k 0:e614f7875b60 920 * Returns immediately with one of err_t return codes:
iva2k 0:e614f7875b60 921 * - ERR_OK if hostname is a valid IP address string or the host
iva2k 0:e614f7875b60 922 * name is already in the local names table.
iva2k 0:e614f7875b60 923 * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
iva2k 0:e614f7875b60 924 * for resolution if no errors are present.
iva2k 0:e614f7875b60 925 *
iva2k 0:e614f7875b60 926 * @param hostname the hostname that is to be queried
iva2k 0:e614f7875b60 927 * @param addr pointer to a ip_addr_t where to store the address if it is already
iva2k 0:e614f7875b60 928 * cached in the dns_table (only valid if ERR_OK is returned!)
iva2k 0:e614f7875b60 929 * @param found a callback function to be called on success, failure or timeout (only if
iva2k 0:e614f7875b60 930 * ERR_INPROGRESS is returned!)
iva2k 0:e614f7875b60 931 * @param callback_arg argument to pass to the callback function
iva2k 0:e614f7875b60 932 * @return a err_t return code.
iva2k 0:e614f7875b60 933 */
iva2k 0:e614f7875b60 934 err_t
iva2k 0:e614f7875b60 935 dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
iva2k 0:e614f7875b60 936 void *callback_arg)
iva2k 0:e614f7875b60 937 {
iva2k 0:e614f7875b60 938 u32_t ipaddr;
iva2k 0:e614f7875b60 939 /* not initialized or no valid server yet, or invalid addr pointer
iva2k 0:e614f7875b60 940 * or invalid hostname or invalid hostname length */
iva2k 0:e614f7875b60 941 if ((dns_pcb == NULL) || (addr == NULL) ||
iva2k 0:e614f7875b60 942 (!hostname) || (!hostname[0]) ||
iva2k 0:e614f7875b60 943 (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) {
iva2k 0:e614f7875b60 944 return ERR_VAL;
iva2k 0:e614f7875b60 945 }
iva2k 0:e614f7875b60 946
iva2k 0:e614f7875b60 947 #if LWIP_HAVE_LOOPIF
iva2k 0:e614f7875b60 948 if (strcmp(hostname, "localhost")==0) {
iva2k 0:e614f7875b60 949 ip_addr_set_loopback(addr);
iva2k 0:e614f7875b60 950 return ERR_OK;
iva2k 0:e614f7875b60 951 }
iva2k 0:e614f7875b60 952 #endif /* LWIP_HAVE_LOOPIF */
iva2k 0:e614f7875b60 953
iva2k 0:e614f7875b60 954 /* host name already in octet notation? set ip addr and return ERR_OK */
iva2k 0:e614f7875b60 955 ipaddr = ipaddr_addr(hostname);
iva2k 0:e614f7875b60 956 if (ipaddr == IPADDR_NONE) {
iva2k 0:e614f7875b60 957 /* already have this address cached? */
iva2k 0:e614f7875b60 958 ipaddr = dns_lookup(hostname);
iva2k 0:e614f7875b60 959 }
iva2k 0:e614f7875b60 960 if (ipaddr != IPADDR_NONE) {
iva2k 0:e614f7875b60 961 ip4_addr_set_u32(addr, ipaddr);
iva2k 0:e614f7875b60 962 return ERR_OK;
iva2k 0:e614f7875b60 963 }
iva2k 0:e614f7875b60 964
iva2k 0:e614f7875b60 965 /* queue query with specified callback */
iva2k 0:e614f7875b60 966 return dns_enqueue(hostname, found, callback_arg);
iva2k 0:e614f7875b60 967 }
iva2k 0:e614f7875b60 968 #endif /* LWIP_DNS */