xref: /minix3/minix/lib/liblwip/dist/src/core/dns.c (revision 5d5fbe79c1b60734f34c69330aec5496644e8651)
1*5d5fbe79SDavid van Moolenbroek /**
2*5d5fbe79SDavid van Moolenbroek  * @file
3*5d5fbe79SDavid van Moolenbroek  * DNS - host name to IP address resolver.
4*5d5fbe79SDavid van Moolenbroek  *
5*5d5fbe79SDavid van Moolenbroek  * @defgroup dns DNS
6*5d5fbe79SDavid van Moolenbroek  * @ingroup callbackstyle_api
7*5d5fbe79SDavid van Moolenbroek  *
8*5d5fbe79SDavid van Moolenbroek  * Implements a DNS host name to IP address resolver.
9*5d5fbe79SDavid van Moolenbroek  *
10*5d5fbe79SDavid van Moolenbroek  * The lwIP DNS resolver functions are used to lookup a host name and
11*5d5fbe79SDavid van Moolenbroek  * map it to a numerical IP address. It maintains a list of resolved
12*5d5fbe79SDavid van Moolenbroek  * hostnames that can be queried with the dns_lookup() function.
13*5d5fbe79SDavid van Moolenbroek  * New hostnames can be resolved using the dns_query() function.
14*5d5fbe79SDavid van Moolenbroek  *
15*5d5fbe79SDavid van Moolenbroek  * The lwIP version of the resolver also adds a non-blocking version of
16*5d5fbe79SDavid van Moolenbroek  * gethostbyname() that will work with a raw API application. This function
17*5d5fbe79SDavid van Moolenbroek  * checks for an IP address string first and converts it if it is valid.
18*5d5fbe79SDavid van Moolenbroek  * gethostbyname() then does a dns_lookup() to see if the name is
19*5d5fbe79SDavid van Moolenbroek  * already in the table. If so, the IP is returned. If not, a query is
20*5d5fbe79SDavid van Moolenbroek  * issued and the function returns with a ERR_INPROGRESS status. The app
21*5d5fbe79SDavid van Moolenbroek  * using the dns client must then go into a waiting state.
22*5d5fbe79SDavid van Moolenbroek  *
23*5d5fbe79SDavid van Moolenbroek  * Once a hostname has been resolved (or found to be non-existent),
24*5d5fbe79SDavid van Moolenbroek  * the resolver code calls a specified callback function (which
25*5d5fbe79SDavid van Moolenbroek  * must be implemented by the module that uses the resolver).
26*5d5fbe79SDavid van Moolenbroek  *
27*5d5fbe79SDavid van Moolenbroek  * Multicast DNS queries are supported for names ending on ".local".
28*5d5fbe79SDavid van Moolenbroek  * However, only "One-Shot Multicast DNS Queries" are supported (RFC 6762
29*5d5fbe79SDavid van Moolenbroek  * chapter 5.1), this is not a fully compliant implementation of continuous
30*5d5fbe79SDavid van Moolenbroek  * mDNS querying!
31*5d5fbe79SDavid van Moolenbroek  *
32*5d5fbe79SDavid van Moolenbroek  * All functions must be called from TCPIP thread.
33*5d5fbe79SDavid van Moolenbroek  *
34*5d5fbe79SDavid van Moolenbroek  * @see @ref netconn_common for thread-safe access.
35*5d5fbe79SDavid van Moolenbroek  */
36*5d5fbe79SDavid van Moolenbroek 
37*5d5fbe79SDavid van Moolenbroek /*
38*5d5fbe79SDavid van Moolenbroek  * Port to lwIP from uIP
39*5d5fbe79SDavid van Moolenbroek  * by Jim Pettinato April 2007
40*5d5fbe79SDavid van Moolenbroek  *
41*5d5fbe79SDavid van Moolenbroek  * security fixes and more by Simon Goldschmidt
42*5d5fbe79SDavid van Moolenbroek  *
43*5d5fbe79SDavid van Moolenbroek  * uIP version Copyright (c) 2002-2003, Adam Dunkels.
44*5d5fbe79SDavid van Moolenbroek  * All rights reserved.
45*5d5fbe79SDavid van Moolenbroek  *
46*5d5fbe79SDavid van Moolenbroek  * Redistribution and use in source and binary forms, with or without
47*5d5fbe79SDavid van Moolenbroek  * modification, are permitted provided that the following conditions
48*5d5fbe79SDavid van Moolenbroek  * are met:
49*5d5fbe79SDavid van Moolenbroek  * 1. Redistributions of source code must retain the above copyright
50*5d5fbe79SDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer.
51*5d5fbe79SDavid van Moolenbroek  * 2. Redistributions in binary form must reproduce the above copyright
52*5d5fbe79SDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer in the
53*5d5fbe79SDavid van Moolenbroek  *    documentation and/or other materials provided with the distribution.
54*5d5fbe79SDavid van Moolenbroek  * 3. The name of the author may not be used to endorse or promote
55*5d5fbe79SDavid van Moolenbroek  *    products derived from this software without specific prior
56*5d5fbe79SDavid van Moolenbroek  *    written permission.
57*5d5fbe79SDavid van Moolenbroek  *
58*5d5fbe79SDavid van Moolenbroek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
59*5d5fbe79SDavid van Moolenbroek  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
60*5d5fbe79SDavid van Moolenbroek  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
61*5d5fbe79SDavid van Moolenbroek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
62*5d5fbe79SDavid van Moolenbroek  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
63*5d5fbe79SDavid van Moolenbroek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
64*5d5fbe79SDavid van Moolenbroek  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
65*5d5fbe79SDavid van Moolenbroek  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
66*5d5fbe79SDavid van Moolenbroek  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
67*5d5fbe79SDavid van Moolenbroek  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
68*5d5fbe79SDavid van Moolenbroek  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
69*5d5fbe79SDavid van Moolenbroek  */
70*5d5fbe79SDavid van Moolenbroek 
71*5d5fbe79SDavid van Moolenbroek /*-----------------------------------------------------------------------------
72*5d5fbe79SDavid van Moolenbroek  * RFC 1035 - Domain names - implementation and specification
73*5d5fbe79SDavid van Moolenbroek  * RFC 2181 - Clarifications to the DNS Specification
74*5d5fbe79SDavid van Moolenbroek  *----------------------------------------------------------------------------*/
75*5d5fbe79SDavid van Moolenbroek 
76*5d5fbe79SDavid van Moolenbroek /** @todo: define good default values (rfc compliance) */
77*5d5fbe79SDavid van Moolenbroek /** @todo: improve answer parsing, more checkings... */
78*5d5fbe79SDavid van Moolenbroek /** @todo: check RFC1035 - 7.3. Processing responses */
79*5d5fbe79SDavid van Moolenbroek /** @todo: one-shot mDNS: dual-stack fallback to another IP version */
80*5d5fbe79SDavid van Moolenbroek 
81*5d5fbe79SDavid van Moolenbroek /*-----------------------------------------------------------------------------
82*5d5fbe79SDavid van Moolenbroek  * Includes
83*5d5fbe79SDavid van Moolenbroek  *----------------------------------------------------------------------------*/
84*5d5fbe79SDavid van Moolenbroek 
85*5d5fbe79SDavid van Moolenbroek #include "lwip/opt.h"
86*5d5fbe79SDavid van Moolenbroek 
87*5d5fbe79SDavid van Moolenbroek #if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
88*5d5fbe79SDavid van Moolenbroek 
89*5d5fbe79SDavid van Moolenbroek #include "lwip/def.h"
90*5d5fbe79SDavid van Moolenbroek #include "lwip/udp.h"
91*5d5fbe79SDavid van Moolenbroek #include "lwip/mem.h"
92*5d5fbe79SDavid van Moolenbroek #include "lwip/memp.h"
93*5d5fbe79SDavid van Moolenbroek #include "lwip/dns.h"
94*5d5fbe79SDavid van Moolenbroek #include "lwip/prot/dns.h"
95*5d5fbe79SDavid van Moolenbroek 
96*5d5fbe79SDavid van Moolenbroek #include <string.h>
97*5d5fbe79SDavid van Moolenbroek 
98*5d5fbe79SDavid van Moolenbroek /** Random generator function to create random TXIDs and source ports for queries */
99*5d5fbe79SDavid van Moolenbroek #ifndef DNS_RAND_TXID
100*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0)
101*5d5fbe79SDavid van Moolenbroek #define DNS_RAND_TXID LWIP_RAND
102*5d5fbe79SDavid van Moolenbroek #else
103*5d5fbe79SDavid van Moolenbroek static u16_t dns_txid;
104*5d5fbe79SDavid van Moolenbroek #define DNS_RAND_TXID() (++dns_txid)
105*5d5fbe79SDavid van Moolenbroek #endif
106*5d5fbe79SDavid van Moolenbroek #endif
107*5d5fbe79SDavid van Moolenbroek 
108*5d5fbe79SDavid van Moolenbroek /** Limits the source port to be >= 1024 by default */
109*5d5fbe79SDavid van Moolenbroek #ifndef DNS_PORT_ALLOWED
110*5d5fbe79SDavid van Moolenbroek #define DNS_PORT_ALLOWED(port) ((port) >= 1024)
111*5d5fbe79SDavid van Moolenbroek #endif
112*5d5fbe79SDavid van Moolenbroek 
113*5d5fbe79SDavid van Moolenbroek /** DNS maximum number of retries when asking for a name, before "timeout". */
114*5d5fbe79SDavid van Moolenbroek #ifndef DNS_MAX_RETRIES
115*5d5fbe79SDavid van Moolenbroek #define DNS_MAX_RETRIES           4
116*5d5fbe79SDavid van Moolenbroek #endif
117*5d5fbe79SDavid van Moolenbroek 
118*5d5fbe79SDavid van Moolenbroek /** DNS resource record max. TTL (one week as default) */
119*5d5fbe79SDavid van Moolenbroek #ifndef DNS_MAX_TTL
120*5d5fbe79SDavid van Moolenbroek #define DNS_MAX_TTL               604800
121*5d5fbe79SDavid van Moolenbroek #elif DNS_MAX_TTL > 0x7FFFFFFF
122*5d5fbe79SDavid van Moolenbroek #error DNS_MAX_TTL must be a positive 32-bit value
123*5d5fbe79SDavid van Moolenbroek #endif
124*5d5fbe79SDavid van Moolenbroek 
125*5d5fbe79SDavid van Moolenbroek #if DNS_TABLE_SIZE > 255
126*5d5fbe79SDavid van Moolenbroek #error DNS_TABLE_SIZE must fit into an u8_t
127*5d5fbe79SDavid van Moolenbroek #endif
128*5d5fbe79SDavid van Moolenbroek #if DNS_MAX_SERVERS > 255
129*5d5fbe79SDavid van Moolenbroek #error DNS_MAX_SERVERS must fit into an u8_t
130*5d5fbe79SDavid van Moolenbroek #endif
131*5d5fbe79SDavid van Moolenbroek 
132*5d5fbe79SDavid van Moolenbroek /* The number of parallel requests (i.e. calls to dns_gethostbyname
133*5d5fbe79SDavid van Moolenbroek  * that cannot be answered from the DNS table.
134*5d5fbe79SDavid van Moolenbroek  * This is set to the table size by default.
135*5d5fbe79SDavid van Moolenbroek  */
136*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
137*5d5fbe79SDavid van Moolenbroek #ifndef DNS_MAX_REQUESTS
138*5d5fbe79SDavid van Moolenbroek #define DNS_MAX_REQUESTS          DNS_TABLE_SIZE
139*5d5fbe79SDavid van Moolenbroek #else
140*5d5fbe79SDavid van Moolenbroek #if DNS_MAX_REQUESTS > 255
141*5d5fbe79SDavid van Moolenbroek #error DNS_MAX_REQUESTS must fit into an u8_t
142*5d5fbe79SDavid van Moolenbroek #endif
143*5d5fbe79SDavid van Moolenbroek #endif
144*5d5fbe79SDavid van Moolenbroek #else
145*5d5fbe79SDavid van Moolenbroek /* In this configuration, both arrays have to have the same size and are used
146*5d5fbe79SDavid van Moolenbroek  * like one entry (used/free) */
147*5d5fbe79SDavid van Moolenbroek #define DNS_MAX_REQUESTS          DNS_TABLE_SIZE
148*5d5fbe79SDavid van Moolenbroek #endif
149*5d5fbe79SDavid van Moolenbroek 
150*5d5fbe79SDavid van Moolenbroek /* The number of UDP source ports used in parallel */
151*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
152*5d5fbe79SDavid van Moolenbroek #ifndef DNS_MAX_SOURCE_PORTS
153*5d5fbe79SDavid van Moolenbroek #define DNS_MAX_SOURCE_PORTS      DNS_MAX_REQUESTS
154*5d5fbe79SDavid van Moolenbroek #else
155*5d5fbe79SDavid van Moolenbroek #if DNS_MAX_SOURCE_PORTS > 255
156*5d5fbe79SDavid van Moolenbroek #error DNS_MAX_SOURCE_PORTS must fit into an u8_t
157*5d5fbe79SDavid van Moolenbroek #endif
158*5d5fbe79SDavid van Moolenbroek #endif
159*5d5fbe79SDavid van Moolenbroek #else
160*5d5fbe79SDavid van Moolenbroek #ifdef DNS_MAX_SOURCE_PORTS
161*5d5fbe79SDavid van Moolenbroek #undef DNS_MAX_SOURCE_PORTS
162*5d5fbe79SDavid van Moolenbroek #endif
163*5d5fbe79SDavid van Moolenbroek #define DNS_MAX_SOURCE_PORTS      1
164*5d5fbe79SDavid van Moolenbroek #endif
165*5d5fbe79SDavid van Moolenbroek 
166*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
167*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) (((t) == LWIP_DNS_ADDRTYPE_IPV6_IPV4) || ((t) == LWIP_DNS_ADDRTYPE_IPV6))
168*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) (IP_IS_V6_VAL(ip) ? LWIP_DNS_ADDRTYPE_IS_IPV6(t) : (!LWIP_DNS_ADDRTYPE_IS_IPV6(t)))
169*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_ADDRTYPE_ARG(x) , x
170*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) x
171*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_SET_ADDRTYPE(x, y) do { x = y; } while(0)
172*5d5fbe79SDavid van Moolenbroek #else
173*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV6
174*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 1
175*5d5fbe79SDavid van Moolenbroek #else
176*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 0
177*5d5fbe79SDavid van Moolenbroek #endif
178*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) 1
179*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_ADDRTYPE_ARG(x)
180*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) 0
181*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_SET_ADDRTYPE(x, y)
182*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
183*5d5fbe79SDavid van Moolenbroek 
184*5d5fbe79SDavid van Moolenbroek #if LWIP_DNS_SUPPORT_MDNS_QUERIES
185*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_ISMDNS_ARG(x) , x
186*5d5fbe79SDavid van Moolenbroek #else
187*5d5fbe79SDavid van Moolenbroek #define LWIP_DNS_ISMDNS_ARG(x)
188*5d5fbe79SDavid van Moolenbroek #endif
189*5d5fbe79SDavid van Moolenbroek 
190*5d5fbe79SDavid van Moolenbroek /** DNS query message structure.
191*5d5fbe79SDavid van Moolenbroek     No packing needed: only used locally on the stack. */
192*5d5fbe79SDavid van Moolenbroek struct dns_query {
193*5d5fbe79SDavid van Moolenbroek   /* DNS query record starts with either a domain name or a pointer
194*5d5fbe79SDavid van Moolenbroek      to a name already present somewhere in the packet. */
195*5d5fbe79SDavid van Moolenbroek   u16_t type;
196*5d5fbe79SDavid van Moolenbroek   u16_t cls;
197*5d5fbe79SDavid van Moolenbroek };
198*5d5fbe79SDavid van Moolenbroek #define SIZEOF_DNS_QUERY 4
199*5d5fbe79SDavid van Moolenbroek 
200*5d5fbe79SDavid van Moolenbroek /** DNS answer message structure.
201*5d5fbe79SDavid van Moolenbroek     No packing needed: only used locally on the stack. */
202*5d5fbe79SDavid van Moolenbroek struct dns_answer {
203*5d5fbe79SDavid van Moolenbroek   /* DNS answer record starts with either a domain name or a pointer
204*5d5fbe79SDavid van Moolenbroek      to a name already present somewhere in the packet. */
205*5d5fbe79SDavid van Moolenbroek   u16_t type;
206*5d5fbe79SDavid van Moolenbroek   u16_t cls;
207*5d5fbe79SDavid van Moolenbroek   u32_t ttl;
208*5d5fbe79SDavid van Moolenbroek   u16_t len;
209*5d5fbe79SDavid van Moolenbroek };
210*5d5fbe79SDavid van Moolenbroek #define SIZEOF_DNS_ANSWER 10
211*5d5fbe79SDavid van Moolenbroek /* maximum allowed size for the struct due to non-packed */
212*5d5fbe79SDavid van Moolenbroek #define SIZEOF_DNS_ANSWER_ASSERT 12
213*5d5fbe79SDavid van Moolenbroek 
214*5d5fbe79SDavid van Moolenbroek /* DNS table entry states */
215*5d5fbe79SDavid van Moolenbroek typedef enum {
216*5d5fbe79SDavid van Moolenbroek   DNS_STATE_UNUSED           = 0,
217*5d5fbe79SDavid van Moolenbroek   DNS_STATE_NEW              = 1,
218*5d5fbe79SDavid van Moolenbroek   DNS_STATE_ASKING           = 2,
219*5d5fbe79SDavid van Moolenbroek   DNS_STATE_DONE             = 3
220*5d5fbe79SDavid van Moolenbroek } dns_state_enum_t;
221*5d5fbe79SDavid van Moolenbroek 
222*5d5fbe79SDavid van Moolenbroek /** DNS table entry */
223*5d5fbe79SDavid van Moolenbroek struct dns_table_entry {
224*5d5fbe79SDavid van Moolenbroek   u32_t ttl;
225*5d5fbe79SDavid van Moolenbroek   ip_addr_t ipaddr;
226*5d5fbe79SDavid van Moolenbroek   u16_t txid;
227*5d5fbe79SDavid van Moolenbroek   u8_t  state;
228*5d5fbe79SDavid van Moolenbroek   u8_t  server_idx;
229*5d5fbe79SDavid van Moolenbroek   u8_t  tmr;
230*5d5fbe79SDavid van Moolenbroek   u8_t  retries;
231*5d5fbe79SDavid van Moolenbroek   u8_t  seqno;
232*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
233*5d5fbe79SDavid van Moolenbroek   u8_t pcb_idx;
234*5d5fbe79SDavid van Moolenbroek #endif
235*5d5fbe79SDavid van Moolenbroek   char name[DNS_MAX_NAME_LENGTH];
236*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
237*5d5fbe79SDavid van Moolenbroek   u8_t reqaddrtype;
238*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
239*5d5fbe79SDavid van Moolenbroek #if LWIP_DNS_SUPPORT_MDNS_QUERIES
240*5d5fbe79SDavid van Moolenbroek   u8_t is_mdns;
241*5d5fbe79SDavid van Moolenbroek #endif
242*5d5fbe79SDavid van Moolenbroek };
243*5d5fbe79SDavid van Moolenbroek 
244*5d5fbe79SDavid van Moolenbroek /** DNS request table entry: used when dns_gehostbyname cannot answer the
245*5d5fbe79SDavid van Moolenbroek  * request from the DNS table */
246*5d5fbe79SDavid van Moolenbroek struct dns_req_entry {
247*5d5fbe79SDavid van Moolenbroek   /* pointer to callback on DNS query done */
248*5d5fbe79SDavid van Moolenbroek   dns_found_callback found;
249*5d5fbe79SDavid van Moolenbroek   /* argument passed to the callback function */
250*5d5fbe79SDavid van Moolenbroek   void *arg;
251*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
252*5d5fbe79SDavid van Moolenbroek   u8_t dns_table_idx;
253*5d5fbe79SDavid van Moolenbroek #endif
254*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
255*5d5fbe79SDavid van Moolenbroek   u8_t reqaddrtype;
256*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
257*5d5fbe79SDavid van Moolenbroek };
258*5d5fbe79SDavid van Moolenbroek 
259*5d5fbe79SDavid van Moolenbroek #if DNS_LOCAL_HOSTLIST
260*5d5fbe79SDavid van Moolenbroek 
261*5d5fbe79SDavid van Moolenbroek #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
262*5d5fbe79SDavid van Moolenbroek /** Local host-list. For hostnames in this list, no
263*5d5fbe79SDavid van Moolenbroek  *  external name resolution is performed */
264*5d5fbe79SDavid van Moolenbroek static struct local_hostlist_entry *local_hostlist_dynamic;
265*5d5fbe79SDavid van Moolenbroek #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
266*5d5fbe79SDavid van Moolenbroek 
267*5d5fbe79SDavid van Moolenbroek /** Defining this allows the local_hostlist_static to be placed in a different
268*5d5fbe79SDavid van Moolenbroek  * linker section (e.g. FLASH) */
269*5d5fbe79SDavid van Moolenbroek #ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
270*5d5fbe79SDavid van Moolenbroek #define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
271*5d5fbe79SDavid van Moolenbroek #endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
272*5d5fbe79SDavid van Moolenbroek /** Defining this allows the local_hostlist_static to be placed in a different
273*5d5fbe79SDavid van Moolenbroek  * linker section (e.g. FLASH) */
274*5d5fbe79SDavid van Moolenbroek #ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
275*5d5fbe79SDavid van Moolenbroek #define DNS_LOCAL_HOSTLIST_STORAGE_POST
276*5d5fbe79SDavid van Moolenbroek #endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
277*5d5fbe79SDavid van Moolenbroek DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
278*5d5fbe79SDavid van Moolenbroek   DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
279*5d5fbe79SDavid van Moolenbroek 
280*5d5fbe79SDavid van Moolenbroek #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
281*5d5fbe79SDavid van Moolenbroek 
282*5d5fbe79SDavid van Moolenbroek static void dns_init_local(void);
283*5d5fbe79SDavid van Moolenbroek static err_t dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype));
284*5d5fbe79SDavid van Moolenbroek #endif /* DNS_LOCAL_HOSTLIST */
285*5d5fbe79SDavid van Moolenbroek 
286*5d5fbe79SDavid van Moolenbroek 
287*5d5fbe79SDavid van Moolenbroek /* forward declarations */
288*5d5fbe79SDavid van Moolenbroek static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
289*5d5fbe79SDavid van Moolenbroek static void dns_check_entries(void);
290*5d5fbe79SDavid van Moolenbroek static void dns_call_found(u8_t idx, ip_addr_t* addr);
291*5d5fbe79SDavid van Moolenbroek 
292*5d5fbe79SDavid van Moolenbroek /*-----------------------------------------------------------------------------
293*5d5fbe79SDavid van Moolenbroek  * Globals
294*5d5fbe79SDavid van Moolenbroek  *----------------------------------------------------------------------------*/
295*5d5fbe79SDavid van Moolenbroek 
296*5d5fbe79SDavid van Moolenbroek /* DNS variables */
297*5d5fbe79SDavid van Moolenbroek static struct udp_pcb        *dns_pcbs[DNS_MAX_SOURCE_PORTS];
298*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
299*5d5fbe79SDavid van Moolenbroek static u8_t                   dns_last_pcb_idx;
300*5d5fbe79SDavid van Moolenbroek #endif
301*5d5fbe79SDavid van Moolenbroek static u8_t                   dns_seqno;
302*5d5fbe79SDavid van Moolenbroek static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
303*5d5fbe79SDavid van Moolenbroek static struct dns_req_entry   dns_requests[DNS_MAX_REQUESTS];
304*5d5fbe79SDavid van Moolenbroek static ip_addr_t              dns_servers[DNS_MAX_SERVERS];
305*5d5fbe79SDavid van Moolenbroek 
306*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4
307*5d5fbe79SDavid van Moolenbroek const ip_addr_t dns_mquery_v4group = DNS_MQUERY_IPV4_GROUP_INIT;
308*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 */
309*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV6
310*5d5fbe79SDavid van Moolenbroek const ip_addr_t dns_mquery_v6group = DNS_MQUERY_IPV6_GROUP_INIT;
311*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV6 */
312*5d5fbe79SDavid van Moolenbroek 
313*5d5fbe79SDavid van Moolenbroek /**
314*5d5fbe79SDavid van Moolenbroek  * Initialize the resolver: set up the UDP pcb and configure the default server
315*5d5fbe79SDavid van Moolenbroek  * (if DNS_SERVER_ADDRESS is set).
316*5d5fbe79SDavid van Moolenbroek  */
317*5d5fbe79SDavid van Moolenbroek void
dns_init(void)318*5d5fbe79SDavid van Moolenbroek dns_init(void)
319*5d5fbe79SDavid van Moolenbroek {
320*5d5fbe79SDavid van Moolenbroek #ifdef DNS_SERVER_ADDRESS
321*5d5fbe79SDavid van Moolenbroek   /* initialize default DNS server address */
322*5d5fbe79SDavid van Moolenbroek   ip_addr_t dnsserver;
323*5d5fbe79SDavid van Moolenbroek   DNS_SERVER_ADDRESS(&dnsserver);
324*5d5fbe79SDavid van Moolenbroek   dns_setserver(0, &dnsserver);
325*5d5fbe79SDavid van Moolenbroek #endif /* DNS_SERVER_ADDRESS */
326*5d5fbe79SDavid van Moolenbroek 
327*5d5fbe79SDavid van Moolenbroek   LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
328*5d5fbe79SDavid van Moolenbroek     sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
329*5d5fbe79SDavid van Moolenbroek   LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER",
330*5d5fbe79SDavid van Moolenbroek     sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT);
331*5d5fbe79SDavid van Moolenbroek 
332*5d5fbe79SDavid van Moolenbroek   LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
333*5d5fbe79SDavid van Moolenbroek 
334*5d5fbe79SDavid van Moolenbroek   /* if dns client not yet initialized... */
335*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
336*5d5fbe79SDavid van Moolenbroek   if (dns_pcbs[0] == NULL) {
337*5d5fbe79SDavid van Moolenbroek     dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY);
338*5d5fbe79SDavid van Moolenbroek     LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != NULL);
339*5d5fbe79SDavid van Moolenbroek 
340*5d5fbe79SDavid van Moolenbroek     /* initialize DNS table not needed (initialized to zero since it is a
341*5d5fbe79SDavid van Moolenbroek      * global variable) */
342*5d5fbe79SDavid van Moolenbroek     LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
343*5d5fbe79SDavid van Moolenbroek       DNS_STATE_UNUSED == 0);
344*5d5fbe79SDavid van Moolenbroek 
345*5d5fbe79SDavid van Moolenbroek     /* initialize DNS client */
346*5d5fbe79SDavid van Moolenbroek     udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0);
347*5d5fbe79SDavid van Moolenbroek     udp_recv(dns_pcbs[0], dns_recv, NULL);
348*5d5fbe79SDavid van Moolenbroek   }
349*5d5fbe79SDavid van Moolenbroek #endif
350*5d5fbe79SDavid van Moolenbroek 
351*5d5fbe79SDavid van Moolenbroek #if DNS_LOCAL_HOSTLIST
352*5d5fbe79SDavid van Moolenbroek   dns_init_local();
353*5d5fbe79SDavid van Moolenbroek #endif
354*5d5fbe79SDavid van Moolenbroek }
355*5d5fbe79SDavid van Moolenbroek 
356*5d5fbe79SDavid van Moolenbroek /**
357*5d5fbe79SDavid van Moolenbroek  * @ingroup dns
358*5d5fbe79SDavid van Moolenbroek  * Initialize one of the DNS servers.
359*5d5fbe79SDavid van Moolenbroek  *
360*5d5fbe79SDavid van Moolenbroek  * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
361*5d5fbe79SDavid van Moolenbroek  * @param dnsserver IP address of the DNS server to set
362*5d5fbe79SDavid van Moolenbroek  */
363*5d5fbe79SDavid van Moolenbroek void
dns_setserver(u8_t numdns,const ip_addr_t * dnsserver)364*5d5fbe79SDavid van Moolenbroek dns_setserver(u8_t numdns, const ip_addr_t *dnsserver)
365*5d5fbe79SDavid van Moolenbroek {
366*5d5fbe79SDavid van Moolenbroek   if (numdns < DNS_MAX_SERVERS) {
367*5d5fbe79SDavid van Moolenbroek     if (dnsserver != NULL) {
368*5d5fbe79SDavid van Moolenbroek       dns_servers[numdns] = (*dnsserver);
369*5d5fbe79SDavid van Moolenbroek     } else {
370*5d5fbe79SDavid van Moolenbroek       dns_servers[numdns] = *IP_ADDR_ANY;
371*5d5fbe79SDavid van Moolenbroek     }
372*5d5fbe79SDavid van Moolenbroek   }
373*5d5fbe79SDavid van Moolenbroek }
374*5d5fbe79SDavid van Moolenbroek 
375*5d5fbe79SDavid van Moolenbroek /**
376*5d5fbe79SDavid van Moolenbroek  * @ingroup dns
377*5d5fbe79SDavid van Moolenbroek  * Obtain one of the currently configured DNS server.
378*5d5fbe79SDavid van Moolenbroek  *
379*5d5fbe79SDavid van Moolenbroek  * @param numdns the index of the DNS server
380*5d5fbe79SDavid van Moolenbroek  * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
381*5d5fbe79SDavid van Moolenbroek  *         server has not been configured.
382*5d5fbe79SDavid van Moolenbroek  */
383*5d5fbe79SDavid van Moolenbroek const ip_addr_t*
dns_getserver(u8_t numdns)384*5d5fbe79SDavid van Moolenbroek dns_getserver(u8_t numdns)
385*5d5fbe79SDavid van Moolenbroek {
386*5d5fbe79SDavid van Moolenbroek   if (numdns < DNS_MAX_SERVERS) {
387*5d5fbe79SDavid van Moolenbroek     return &dns_servers[numdns];
388*5d5fbe79SDavid van Moolenbroek   } else {
389*5d5fbe79SDavid van Moolenbroek     return IP_ADDR_ANY;
390*5d5fbe79SDavid van Moolenbroek   }
391*5d5fbe79SDavid van Moolenbroek }
392*5d5fbe79SDavid van Moolenbroek 
393*5d5fbe79SDavid van Moolenbroek /**
394*5d5fbe79SDavid van Moolenbroek  * The DNS resolver client timer - handle retries and timeouts and should
395*5d5fbe79SDavid van Moolenbroek  * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
396*5d5fbe79SDavid van Moolenbroek  */
397*5d5fbe79SDavid van Moolenbroek void
dns_tmr(void)398*5d5fbe79SDavid van Moolenbroek dns_tmr(void)
399*5d5fbe79SDavid van Moolenbroek {
400*5d5fbe79SDavid van Moolenbroek   LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
401*5d5fbe79SDavid van Moolenbroek   dns_check_entries();
402*5d5fbe79SDavid van Moolenbroek }
403*5d5fbe79SDavid van Moolenbroek 
404*5d5fbe79SDavid van Moolenbroek #if DNS_LOCAL_HOSTLIST
405*5d5fbe79SDavid van Moolenbroek static void
dns_init_local(void)406*5d5fbe79SDavid van Moolenbroek dns_init_local(void)
407*5d5fbe79SDavid van Moolenbroek {
408*5d5fbe79SDavid van Moolenbroek #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
409*5d5fbe79SDavid van Moolenbroek   size_t i;
410*5d5fbe79SDavid van Moolenbroek   struct local_hostlist_entry *entry;
411*5d5fbe79SDavid van Moolenbroek   /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
412*5d5fbe79SDavid van Moolenbroek   struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
413*5d5fbe79SDavid van Moolenbroek   size_t namelen;
414*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_init); i++) {
415*5d5fbe79SDavid van Moolenbroek     struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
416*5d5fbe79SDavid van Moolenbroek     LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
417*5d5fbe79SDavid van Moolenbroek     namelen = strlen(init_entry->name);
418*5d5fbe79SDavid van Moolenbroek     LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
419*5d5fbe79SDavid van Moolenbroek     entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
420*5d5fbe79SDavid van Moolenbroek     LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
421*5d5fbe79SDavid van Moolenbroek     if (entry != NULL) {
422*5d5fbe79SDavid van Moolenbroek       char* entry_name = (char*)entry + sizeof(struct local_hostlist_entry);
423*5d5fbe79SDavid van Moolenbroek       MEMCPY(entry_name, init_entry->name, namelen);
424*5d5fbe79SDavid van Moolenbroek       entry_name[namelen] = 0;
425*5d5fbe79SDavid van Moolenbroek       entry->name = entry_name;
426*5d5fbe79SDavid van Moolenbroek       entry->addr = init_entry->addr;
427*5d5fbe79SDavid van Moolenbroek       entry->next = local_hostlist_dynamic;
428*5d5fbe79SDavid van Moolenbroek       local_hostlist_dynamic = entry;
429*5d5fbe79SDavid van Moolenbroek     }
430*5d5fbe79SDavid van Moolenbroek   }
431*5d5fbe79SDavid van Moolenbroek #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
432*5d5fbe79SDavid van Moolenbroek }
433*5d5fbe79SDavid van Moolenbroek 
434*5d5fbe79SDavid van Moolenbroek /**
435*5d5fbe79SDavid van Moolenbroek  * @ingroup dns
436*5d5fbe79SDavid van Moolenbroek  * Iterate the local host-list for a hostname.
437*5d5fbe79SDavid van Moolenbroek  *
438*5d5fbe79SDavid van Moolenbroek  * @param iterator_fn a function that is called for every entry in the local host-list
439*5d5fbe79SDavid van Moolenbroek  * @param iterator_arg 3rd argument passed to iterator_fn
440*5d5fbe79SDavid van Moolenbroek  * @return the number of entries in the local host-list
441*5d5fbe79SDavid van Moolenbroek  */
442*5d5fbe79SDavid van Moolenbroek size_t
dns_local_iterate(dns_found_callback iterator_fn,void * iterator_arg)443*5d5fbe79SDavid van Moolenbroek dns_local_iterate(dns_found_callback iterator_fn, void *iterator_arg)
444*5d5fbe79SDavid van Moolenbroek {
445*5d5fbe79SDavid van Moolenbroek   size_t i;
446*5d5fbe79SDavid van Moolenbroek #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
447*5d5fbe79SDavid van Moolenbroek   struct local_hostlist_entry *entry = local_hostlist_dynamic;
448*5d5fbe79SDavid van Moolenbroek   i = 0;
449*5d5fbe79SDavid van Moolenbroek   while (entry != NULL) {
450*5d5fbe79SDavid van Moolenbroek     if (iterator_fn != NULL) {
451*5d5fbe79SDavid van Moolenbroek       iterator_fn(entry->name, &entry->addr, iterator_arg);
452*5d5fbe79SDavid van Moolenbroek     }
453*5d5fbe79SDavid van Moolenbroek     i++;
454*5d5fbe79SDavid van Moolenbroek     entry = entry->next;
455*5d5fbe79SDavid van Moolenbroek   }
456*5d5fbe79SDavid van Moolenbroek #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
457*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
458*5d5fbe79SDavid van Moolenbroek     if (iterator_fn != NULL) {
459*5d5fbe79SDavid van Moolenbroek       iterator_fn(local_hostlist_static[i].name, &local_hostlist_static[i].addr, iterator_arg);
460*5d5fbe79SDavid van Moolenbroek     }
461*5d5fbe79SDavid van Moolenbroek   }
462*5d5fbe79SDavid van Moolenbroek #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
463*5d5fbe79SDavid van Moolenbroek   return i;
464*5d5fbe79SDavid van Moolenbroek }
465*5d5fbe79SDavid van Moolenbroek 
466*5d5fbe79SDavid van Moolenbroek /**
467*5d5fbe79SDavid van Moolenbroek  * @ingroup dns
468*5d5fbe79SDavid van Moolenbroek  * Scans the local host-list for a hostname.
469*5d5fbe79SDavid van Moolenbroek  *
470*5d5fbe79SDavid van Moolenbroek  * @param hostname Hostname to look for in the local host-list
471*5d5fbe79SDavid van Moolenbroek  * @param addr the first IP address for the hostname in the local host-list or
472*5d5fbe79SDavid van Moolenbroek  *         IPADDR_NONE if not found.
473*5d5fbe79SDavid van Moolenbroek  * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 (ATTENTION: no fallback here!)
474*5d5fbe79SDavid van Moolenbroek  *                     - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 (ATTENTION: no fallback here!)
475*5d5fbe79SDavid van Moolenbroek  *                     - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
476*5d5fbe79SDavid van Moolenbroek  *                     - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
477*5d5fbe79SDavid van Moolenbroek  * @return ERR_OK if found, ERR_ARG if not found
478*5d5fbe79SDavid van Moolenbroek  */
479*5d5fbe79SDavid van Moolenbroek err_t
dns_local_lookup(const char * hostname,ip_addr_t * addr,u8_t dns_addrtype)480*5d5fbe79SDavid van Moolenbroek dns_local_lookup(const char *hostname, ip_addr_t *addr, u8_t dns_addrtype)
481*5d5fbe79SDavid van Moolenbroek {
482*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(dns_addrtype);
483*5d5fbe79SDavid van Moolenbroek   return dns_lookup_local(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype));
484*5d5fbe79SDavid van Moolenbroek }
485*5d5fbe79SDavid van Moolenbroek 
486*5d5fbe79SDavid van Moolenbroek /* Internal implementation for dns_local_lookup and dns_lookup */
487*5d5fbe79SDavid van Moolenbroek static err_t
dns_lookup_local(const char * hostname,ip_addr_t * addr LWIP_DNS_ADDRTYPE_ARG (u8_t dns_addrtype))488*5d5fbe79SDavid van Moolenbroek dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
489*5d5fbe79SDavid van Moolenbroek {
490*5d5fbe79SDavid van Moolenbroek #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
491*5d5fbe79SDavid van Moolenbroek   struct local_hostlist_entry *entry = local_hostlist_dynamic;
492*5d5fbe79SDavid van Moolenbroek   while (entry != NULL) {
493*5d5fbe79SDavid van Moolenbroek     if ((lwip_stricmp(entry->name, hostname) == 0) &&
494*5d5fbe79SDavid van Moolenbroek         LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, entry->addr)) {
495*5d5fbe79SDavid van Moolenbroek       if (addr) {
496*5d5fbe79SDavid van Moolenbroek         ip_addr_copy(*addr, entry->addr);
497*5d5fbe79SDavid van Moolenbroek       }
498*5d5fbe79SDavid van Moolenbroek       return ERR_OK;
499*5d5fbe79SDavid van Moolenbroek     }
500*5d5fbe79SDavid van Moolenbroek     entry = entry->next;
501*5d5fbe79SDavid van Moolenbroek   }
502*5d5fbe79SDavid van Moolenbroek #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
503*5d5fbe79SDavid van Moolenbroek   size_t i;
504*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
505*5d5fbe79SDavid van Moolenbroek     if ((lwip_stricmp(local_hostlist_static[i].name, hostname) == 0) &&
506*5d5fbe79SDavid van Moolenbroek         LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, local_hostlist_static[i].addr)) {
507*5d5fbe79SDavid van Moolenbroek       if (addr) {
508*5d5fbe79SDavid van Moolenbroek         ip_addr_copy(*addr, local_hostlist_static[i].addr);
509*5d5fbe79SDavid van Moolenbroek       }
510*5d5fbe79SDavid van Moolenbroek       return ERR_OK;
511*5d5fbe79SDavid van Moolenbroek     }
512*5d5fbe79SDavid van Moolenbroek   }
513*5d5fbe79SDavid van Moolenbroek #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
514*5d5fbe79SDavid van Moolenbroek   return ERR_ARG;
515*5d5fbe79SDavid van Moolenbroek }
516*5d5fbe79SDavid van Moolenbroek 
517*5d5fbe79SDavid van Moolenbroek #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
518*5d5fbe79SDavid van Moolenbroek /**
519*5d5fbe79SDavid van Moolenbroek  * @ingroup dns
520*5d5fbe79SDavid van Moolenbroek  * Remove all entries from the local host-list for a specific hostname
521*5d5fbe79SDavid van Moolenbroek  * and/or IP address
522*5d5fbe79SDavid van Moolenbroek  *
523*5d5fbe79SDavid van Moolenbroek  * @param hostname hostname for which entries shall be removed from the local
524*5d5fbe79SDavid van Moolenbroek  *                 host-list
525*5d5fbe79SDavid van Moolenbroek  * @param addr address for which entries shall be removed from the local host-list
526*5d5fbe79SDavid van Moolenbroek  * @return the number of removed entries
527*5d5fbe79SDavid van Moolenbroek  */
528*5d5fbe79SDavid van Moolenbroek int
dns_local_removehost(const char * hostname,const ip_addr_t * addr)529*5d5fbe79SDavid van Moolenbroek dns_local_removehost(const char *hostname, const ip_addr_t *addr)
530*5d5fbe79SDavid van Moolenbroek {
531*5d5fbe79SDavid van Moolenbroek   int removed = 0;
532*5d5fbe79SDavid van Moolenbroek   struct local_hostlist_entry *entry = local_hostlist_dynamic;
533*5d5fbe79SDavid van Moolenbroek   struct local_hostlist_entry *last_entry = NULL;
534*5d5fbe79SDavid van Moolenbroek   while (entry != NULL) {
535*5d5fbe79SDavid van Moolenbroek     if (((hostname == NULL) || !lwip_stricmp(entry->name, hostname)) &&
536*5d5fbe79SDavid van Moolenbroek         ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
537*5d5fbe79SDavid van Moolenbroek       struct local_hostlist_entry *free_entry;
538*5d5fbe79SDavid van Moolenbroek       if (last_entry != NULL) {
539*5d5fbe79SDavid van Moolenbroek         last_entry->next = entry->next;
540*5d5fbe79SDavid van Moolenbroek       } else {
541*5d5fbe79SDavid van Moolenbroek         local_hostlist_dynamic = entry->next;
542*5d5fbe79SDavid van Moolenbroek       }
543*5d5fbe79SDavid van Moolenbroek       free_entry = entry;
544*5d5fbe79SDavid van Moolenbroek       entry = entry->next;
545*5d5fbe79SDavid van Moolenbroek       memp_free(MEMP_LOCALHOSTLIST, free_entry);
546*5d5fbe79SDavid van Moolenbroek       removed++;
547*5d5fbe79SDavid van Moolenbroek     } else {
548*5d5fbe79SDavid van Moolenbroek       last_entry = entry;
549*5d5fbe79SDavid van Moolenbroek       entry = entry->next;
550*5d5fbe79SDavid van Moolenbroek     }
551*5d5fbe79SDavid van Moolenbroek   }
552*5d5fbe79SDavid van Moolenbroek   return removed;
553*5d5fbe79SDavid van Moolenbroek }
554*5d5fbe79SDavid van Moolenbroek 
555*5d5fbe79SDavid van Moolenbroek /**
556*5d5fbe79SDavid van Moolenbroek  * @ingroup dns
557*5d5fbe79SDavid van Moolenbroek  * Add a hostname/IP address pair to the local host-list.
558*5d5fbe79SDavid van Moolenbroek  * Duplicates are not checked.
559*5d5fbe79SDavid van Moolenbroek  *
560*5d5fbe79SDavid van Moolenbroek  * @param hostname hostname of the new entry
561*5d5fbe79SDavid van Moolenbroek  * @param addr IP address of the new entry
562*5d5fbe79SDavid van Moolenbroek  * @return ERR_OK if succeeded or ERR_MEM on memory error
563*5d5fbe79SDavid van Moolenbroek  */
564*5d5fbe79SDavid van Moolenbroek err_t
dns_local_addhost(const char * hostname,const ip_addr_t * addr)565*5d5fbe79SDavid van Moolenbroek dns_local_addhost(const char *hostname, const ip_addr_t *addr)
566*5d5fbe79SDavid van Moolenbroek {
567*5d5fbe79SDavid van Moolenbroek   struct local_hostlist_entry *entry;
568*5d5fbe79SDavid van Moolenbroek   size_t namelen;
569*5d5fbe79SDavid van Moolenbroek   char* entry_name;
570*5d5fbe79SDavid van Moolenbroek   LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
571*5d5fbe79SDavid van Moolenbroek   namelen = strlen(hostname);
572*5d5fbe79SDavid van Moolenbroek   LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
573*5d5fbe79SDavid van Moolenbroek   entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
574*5d5fbe79SDavid van Moolenbroek   if (entry == NULL) {
575*5d5fbe79SDavid van Moolenbroek     return ERR_MEM;
576*5d5fbe79SDavid van Moolenbroek   }
577*5d5fbe79SDavid van Moolenbroek   entry_name = (char*)entry + sizeof(struct local_hostlist_entry);
578*5d5fbe79SDavid van Moolenbroek   MEMCPY(entry_name, hostname, namelen);
579*5d5fbe79SDavid van Moolenbroek   entry_name[namelen] = 0;
580*5d5fbe79SDavid van Moolenbroek   entry->name = entry_name;
581*5d5fbe79SDavid van Moolenbroek   ip_addr_copy(entry->addr, *addr);
582*5d5fbe79SDavid van Moolenbroek   entry->next = local_hostlist_dynamic;
583*5d5fbe79SDavid van Moolenbroek   local_hostlist_dynamic = entry;
584*5d5fbe79SDavid van Moolenbroek   return ERR_OK;
585*5d5fbe79SDavid van Moolenbroek }
586*5d5fbe79SDavid van Moolenbroek #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
587*5d5fbe79SDavid van Moolenbroek #endif /* DNS_LOCAL_HOSTLIST */
588*5d5fbe79SDavid van Moolenbroek 
589*5d5fbe79SDavid van Moolenbroek /**
590*5d5fbe79SDavid van Moolenbroek  * @ingroup dns
591*5d5fbe79SDavid van Moolenbroek  * Look up a hostname in the array of known hostnames.
592*5d5fbe79SDavid van Moolenbroek  *
593*5d5fbe79SDavid van Moolenbroek  * @note This function only looks in the internal array of known
594*5d5fbe79SDavid van Moolenbroek  * hostnames, it does not send out a query for the hostname if none
595*5d5fbe79SDavid van Moolenbroek  * was found. The function dns_enqueue() can be used to send a query
596*5d5fbe79SDavid van Moolenbroek  * for a hostname.
597*5d5fbe79SDavid van Moolenbroek  *
598*5d5fbe79SDavid van Moolenbroek  * @param name the hostname to look up
599*5d5fbe79SDavid van Moolenbroek  * @param addr the hostname's IP address, as u32_t (instead of ip_addr_t to
600*5d5fbe79SDavid van Moolenbroek  *         better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
601*5d5fbe79SDavid van Moolenbroek  *         was not found in the cached dns_table.
602*5d5fbe79SDavid van Moolenbroek  * @return ERR_OK if found, ERR_ARG if not found
603*5d5fbe79SDavid van Moolenbroek  */
604*5d5fbe79SDavid van Moolenbroek static err_t
dns_lookup(const char * name,ip_addr_t * addr LWIP_DNS_ADDRTYPE_ARG (u8_t dns_addrtype))605*5d5fbe79SDavid van Moolenbroek dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
606*5d5fbe79SDavid van Moolenbroek {
607*5d5fbe79SDavid van Moolenbroek   u8_t i;
608*5d5fbe79SDavid van Moolenbroek #if DNS_LOCAL_HOSTLIST
609*5d5fbe79SDavid van Moolenbroek   if (dns_lookup_local(name, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
610*5d5fbe79SDavid van Moolenbroek     return ERR_OK;
611*5d5fbe79SDavid van Moolenbroek   }
612*5d5fbe79SDavid van Moolenbroek #endif /* DNS_LOCAL_HOSTLIST */
613*5d5fbe79SDavid van Moolenbroek #ifdef DNS_LOOKUP_LOCAL_EXTERN
614*5d5fbe79SDavid van Moolenbroek   if (DNS_LOOKUP_LOCAL_EXTERN(name, addr, LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(dns_addrtype)) == ERR_OK) {
615*5d5fbe79SDavid van Moolenbroek     return ERR_OK;
616*5d5fbe79SDavid van Moolenbroek   }
617*5d5fbe79SDavid van Moolenbroek #endif /* DNS_LOOKUP_LOCAL_EXTERN */
618*5d5fbe79SDavid van Moolenbroek 
619*5d5fbe79SDavid van Moolenbroek   /* Walk through name list, return entry if found. If not, return NULL. */
620*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
621*5d5fbe79SDavid van Moolenbroek     if ((dns_table[i].state == DNS_STATE_DONE) &&
622*5d5fbe79SDavid van Moolenbroek         (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0) &&
623*5d5fbe79SDavid van Moolenbroek         LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr)) {
624*5d5fbe79SDavid van Moolenbroek       LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
625*5d5fbe79SDavid van Moolenbroek       ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
626*5d5fbe79SDavid van Moolenbroek       LWIP_DEBUGF(DNS_DEBUG, ("\n"));
627*5d5fbe79SDavid van Moolenbroek       if (addr) {
628*5d5fbe79SDavid van Moolenbroek         ip_addr_copy(*addr, dns_table[i].ipaddr);
629*5d5fbe79SDavid van Moolenbroek       }
630*5d5fbe79SDavid van Moolenbroek       return ERR_OK;
631*5d5fbe79SDavid van Moolenbroek     }
632*5d5fbe79SDavid van Moolenbroek   }
633*5d5fbe79SDavid van Moolenbroek 
634*5d5fbe79SDavid van Moolenbroek   return ERR_ARG;
635*5d5fbe79SDavid van Moolenbroek }
636*5d5fbe79SDavid van Moolenbroek 
637*5d5fbe79SDavid van Moolenbroek /**
638*5d5fbe79SDavid van Moolenbroek  * Compare the "dotted" name "query" with the encoded name "response"
639*5d5fbe79SDavid van Moolenbroek  * to make sure an answer from the DNS server matches the current dns_table
640*5d5fbe79SDavid van Moolenbroek  * entry (otherwise, answers might arrive late for hostname not on the list
641*5d5fbe79SDavid van Moolenbroek  * any more).
642*5d5fbe79SDavid van Moolenbroek  *
643*5d5fbe79SDavid van Moolenbroek  * @param query hostname (not encoded) from the dns_table
644*5d5fbe79SDavid van Moolenbroek  * @param p pbuf containing the encoded hostname in the DNS response
645*5d5fbe79SDavid van Moolenbroek  * @param start_offset offset into p where the name starts
646*5d5fbe79SDavid van Moolenbroek  * @return 0xFFFF: names differ, other: names equal -> offset behind name
647*5d5fbe79SDavid van Moolenbroek  */
648*5d5fbe79SDavid van Moolenbroek static u16_t
dns_compare_name(const char * query,struct pbuf * p,u16_t start_offset)649*5d5fbe79SDavid van Moolenbroek dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
650*5d5fbe79SDavid van Moolenbroek {
651*5d5fbe79SDavid van Moolenbroek   int n;
652*5d5fbe79SDavid van Moolenbroek   u16_t response_offset = start_offset;
653*5d5fbe79SDavid van Moolenbroek 
654*5d5fbe79SDavid van Moolenbroek   do {
655*5d5fbe79SDavid van Moolenbroek     n = pbuf_try_get_at(p, response_offset++);
656*5d5fbe79SDavid van Moolenbroek     if (n < 0) {
657*5d5fbe79SDavid van Moolenbroek       return 0xFFFF;
658*5d5fbe79SDavid van Moolenbroek     }
659*5d5fbe79SDavid van Moolenbroek     /** @see RFC 1035 - 4.1.4. Message compression */
660*5d5fbe79SDavid van Moolenbroek     if ((n & 0xc0) == 0xc0) {
661*5d5fbe79SDavid van Moolenbroek       /* Compressed name: cannot be equal since we don't send them */
662*5d5fbe79SDavid van Moolenbroek       return 0xFFFF;
663*5d5fbe79SDavid van Moolenbroek     } else {
664*5d5fbe79SDavid van Moolenbroek       /* Not compressed name */
665*5d5fbe79SDavid van Moolenbroek       while (n > 0) {
666*5d5fbe79SDavid van Moolenbroek         int c = pbuf_try_get_at(p, response_offset);
667*5d5fbe79SDavid van Moolenbroek         if (c < 0) {
668*5d5fbe79SDavid van Moolenbroek           return 0xFFFF;
669*5d5fbe79SDavid van Moolenbroek         }
670*5d5fbe79SDavid van Moolenbroek         if ((*query) != (u8_t)c) {
671*5d5fbe79SDavid van Moolenbroek           return 0xFFFF;
672*5d5fbe79SDavid van Moolenbroek         }
673*5d5fbe79SDavid van Moolenbroek         ++response_offset;
674*5d5fbe79SDavid van Moolenbroek         ++query;
675*5d5fbe79SDavid van Moolenbroek         --n;
676*5d5fbe79SDavid van Moolenbroek       }
677*5d5fbe79SDavid van Moolenbroek       ++query;
678*5d5fbe79SDavid van Moolenbroek     }
679*5d5fbe79SDavid van Moolenbroek     n = pbuf_try_get_at(p, response_offset);
680*5d5fbe79SDavid van Moolenbroek     if (n < 0) {
681*5d5fbe79SDavid van Moolenbroek       return 0xFFFF;
682*5d5fbe79SDavid van Moolenbroek     }
683*5d5fbe79SDavid van Moolenbroek   } while (n != 0);
684*5d5fbe79SDavid van Moolenbroek 
685*5d5fbe79SDavid van Moolenbroek   return response_offset + 1;
686*5d5fbe79SDavid van Moolenbroek }
687*5d5fbe79SDavid van Moolenbroek 
688*5d5fbe79SDavid van Moolenbroek /**
689*5d5fbe79SDavid van Moolenbroek  * Walk through a compact encoded DNS name and return the end of the name.
690*5d5fbe79SDavid van Moolenbroek  *
691*5d5fbe79SDavid van Moolenbroek  * @param p pbuf containing the name
692*5d5fbe79SDavid van Moolenbroek  * @param query_idx start index into p pointing to encoded DNS name in the DNS server response
693*5d5fbe79SDavid van Moolenbroek  * @return index to end of the name
694*5d5fbe79SDavid van Moolenbroek  */
695*5d5fbe79SDavid van Moolenbroek static u16_t
dns_skip_name(struct pbuf * p,u16_t query_idx)696*5d5fbe79SDavid van Moolenbroek dns_skip_name(struct pbuf* p, u16_t query_idx)
697*5d5fbe79SDavid van Moolenbroek {
698*5d5fbe79SDavid van Moolenbroek   int n;
699*5d5fbe79SDavid van Moolenbroek   u16_t offset = query_idx;
700*5d5fbe79SDavid van Moolenbroek 
701*5d5fbe79SDavid van Moolenbroek   do {
702*5d5fbe79SDavid van Moolenbroek     n = pbuf_try_get_at(p, offset++);
703*5d5fbe79SDavid van Moolenbroek     if (n < 0) {
704*5d5fbe79SDavid van Moolenbroek       return 0xFFFF;
705*5d5fbe79SDavid van Moolenbroek     }
706*5d5fbe79SDavid van Moolenbroek     /** @see RFC 1035 - 4.1.4. Message compression */
707*5d5fbe79SDavid van Moolenbroek     if ((n & 0xc0) == 0xc0) {
708*5d5fbe79SDavid van Moolenbroek       /* Compressed name: since we only want to skip it (not check it), stop here */
709*5d5fbe79SDavid van Moolenbroek       break;
710*5d5fbe79SDavid van Moolenbroek     } else {
711*5d5fbe79SDavid van Moolenbroek       /* Not compressed name */
712*5d5fbe79SDavid van Moolenbroek       if (offset + n >= p->tot_len) {
713*5d5fbe79SDavid van Moolenbroek         return 0xFFFF;
714*5d5fbe79SDavid van Moolenbroek       }
715*5d5fbe79SDavid van Moolenbroek       offset = (u16_t)(offset + n);
716*5d5fbe79SDavid van Moolenbroek     }
717*5d5fbe79SDavid van Moolenbroek     n = pbuf_try_get_at(p, offset);
718*5d5fbe79SDavid van Moolenbroek     if (n < 0) {
719*5d5fbe79SDavid van Moolenbroek       return 0xFFFF;
720*5d5fbe79SDavid van Moolenbroek     }
721*5d5fbe79SDavid van Moolenbroek   } while (n != 0);
722*5d5fbe79SDavid van Moolenbroek 
723*5d5fbe79SDavid van Moolenbroek   return offset + 1;
724*5d5fbe79SDavid van Moolenbroek }
725*5d5fbe79SDavid van Moolenbroek 
726*5d5fbe79SDavid van Moolenbroek /**
727*5d5fbe79SDavid van Moolenbroek  * Send a DNS query packet.
728*5d5fbe79SDavid van Moolenbroek  *
729*5d5fbe79SDavid van Moolenbroek  * @param idx the DNS table entry index for which to send a request
730*5d5fbe79SDavid van Moolenbroek  * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
731*5d5fbe79SDavid van Moolenbroek  */
732*5d5fbe79SDavid van Moolenbroek static err_t
dns_send(u8_t idx)733*5d5fbe79SDavid van Moolenbroek dns_send(u8_t idx)
734*5d5fbe79SDavid van Moolenbroek {
735*5d5fbe79SDavid van Moolenbroek   err_t err;
736*5d5fbe79SDavid van Moolenbroek   struct dns_hdr hdr;
737*5d5fbe79SDavid van Moolenbroek   struct dns_query qry;
738*5d5fbe79SDavid van Moolenbroek   struct pbuf *p;
739*5d5fbe79SDavid van Moolenbroek   u16_t query_idx, copy_len;
740*5d5fbe79SDavid van Moolenbroek   const char *hostname, *hostname_part;
741*5d5fbe79SDavid van Moolenbroek   u8_t n;
742*5d5fbe79SDavid van Moolenbroek   u8_t pcb_idx;
743*5d5fbe79SDavid van Moolenbroek   struct dns_table_entry* entry = &dns_table[idx];
744*5d5fbe79SDavid van Moolenbroek 
745*5d5fbe79SDavid van Moolenbroek   LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
746*5d5fbe79SDavid van Moolenbroek               (u16_t)(entry->server_idx), entry->name));
747*5d5fbe79SDavid van Moolenbroek   LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS);
748*5d5fbe79SDavid van Moolenbroek   if (ip_addr_isany_val(dns_servers[entry->server_idx])
749*5d5fbe79SDavid van Moolenbroek #if LWIP_DNS_SUPPORT_MDNS_QUERIES
750*5d5fbe79SDavid van Moolenbroek       && !entry->is_mdns
751*5d5fbe79SDavid van Moolenbroek #endif
752*5d5fbe79SDavid van Moolenbroek     ) {
753*5d5fbe79SDavid van Moolenbroek     /* DNS server not valid anymore, e.g. PPP netif has been shut down */
754*5d5fbe79SDavid van Moolenbroek     /* call specified callback function if provided */
755*5d5fbe79SDavid van Moolenbroek     dns_call_found(idx, NULL);
756*5d5fbe79SDavid van Moolenbroek     /* flush this entry */
757*5d5fbe79SDavid van Moolenbroek     entry->state = DNS_STATE_UNUSED;
758*5d5fbe79SDavid van Moolenbroek     return ERR_OK;
759*5d5fbe79SDavid van Moolenbroek   }
760*5d5fbe79SDavid van Moolenbroek 
761*5d5fbe79SDavid van Moolenbroek   /* if here, we have either a new query or a retry on a previous query to process */
762*5d5fbe79SDavid van Moolenbroek   p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 +
763*5d5fbe79SDavid van Moolenbroek                  SIZEOF_DNS_QUERY), PBUF_RAM);
764*5d5fbe79SDavid van Moolenbroek   if (p != NULL) {
765*5d5fbe79SDavid van Moolenbroek     const ip_addr_t* dst;
766*5d5fbe79SDavid van Moolenbroek     u16_t dst_port;
767*5d5fbe79SDavid van Moolenbroek     /* fill dns header */
768*5d5fbe79SDavid van Moolenbroek     memset(&hdr, 0, SIZEOF_DNS_HDR);
769*5d5fbe79SDavid van Moolenbroek     hdr.id = lwip_htons(entry->txid);
770*5d5fbe79SDavid van Moolenbroek     hdr.flags1 = DNS_FLAG1_RD;
771*5d5fbe79SDavid van Moolenbroek     hdr.numquestions = PP_HTONS(1);
772*5d5fbe79SDavid van Moolenbroek     pbuf_take(p, &hdr, SIZEOF_DNS_HDR);
773*5d5fbe79SDavid van Moolenbroek     hostname = entry->name;
774*5d5fbe79SDavid van Moolenbroek     --hostname;
775*5d5fbe79SDavid van Moolenbroek 
776*5d5fbe79SDavid van Moolenbroek     /* convert hostname into suitable query format. */
777*5d5fbe79SDavid van Moolenbroek     query_idx = SIZEOF_DNS_HDR;
778*5d5fbe79SDavid van Moolenbroek     do {
779*5d5fbe79SDavid van Moolenbroek       ++hostname;
780*5d5fbe79SDavid van Moolenbroek       hostname_part = hostname;
781*5d5fbe79SDavid van Moolenbroek       for (n = 0; *hostname != '.' && *hostname != 0; ++hostname) {
782*5d5fbe79SDavid van Moolenbroek         ++n;
783*5d5fbe79SDavid van Moolenbroek       }
784*5d5fbe79SDavid van Moolenbroek       copy_len = (u16_t)(hostname - hostname_part);
785*5d5fbe79SDavid van Moolenbroek       pbuf_put_at(p, query_idx, n);
786*5d5fbe79SDavid van Moolenbroek       pbuf_take_at(p, hostname_part, copy_len, query_idx + 1);
787*5d5fbe79SDavid van Moolenbroek       query_idx += n + 1;
788*5d5fbe79SDavid van Moolenbroek     } while (*hostname != 0);
789*5d5fbe79SDavid van Moolenbroek     pbuf_put_at(p, query_idx, 0);
790*5d5fbe79SDavid van Moolenbroek     query_idx++;
791*5d5fbe79SDavid van Moolenbroek 
792*5d5fbe79SDavid van Moolenbroek     /* fill dns query */
793*5d5fbe79SDavid van Moolenbroek     if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) {
794*5d5fbe79SDavid van Moolenbroek       qry.type = PP_HTONS(DNS_RRTYPE_AAAA);
795*5d5fbe79SDavid van Moolenbroek     } else {
796*5d5fbe79SDavid van Moolenbroek       qry.type = PP_HTONS(DNS_RRTYPE_A);
797*5d5fbe79SDavid van Moolenbroek     }
798*5d5fbe79SDavid van Moolenbroek     qry.cls = PP_HTONS(DNS_RRCLASS_IN);
799*5d5fbe79SDavid van Moolenbroek     pbuf_take_at(p, &qry, SIZEOF_DNS_QUERY, query_idx);
800*5d5fbe79SDavid van Moolenbroek 
801*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
802*5d5fbe79SDavid van Moolenbroek     pcb_idx = entry->pcb_idx;
803*5d5fbe79SDavid van Moolenbroek #else
804*5d5fbe79SDavid van Moolenbroek     pcb_idx = 0;
805*5d5fbe79SDavid van Moolenbroek #endif
806*5d5fbe79SDavid van Moolenbroek     /* send dns packet */
807*5d5fbe79SDavid van Moolenbroek     LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n",
808*5d5fbe79SDavid van Moolenbroek       entry->txid, entry->name, entry->server_idx));
809*5d5fbe79SDavid van Moolenbroek #if LWIP_DNS_SUPPORT_MDNS_QUERIES
810*5d5fbe79SDavid van Moolenbroek     if (entry->is_mdns) {
811*5d5fbe79SDavid van Moolenbroek       dst_port = DNS_MQUERY_PORT;
812*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV6
813*5d5fbe79SDavid van Moolenbroek       if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
814*5d5fbe79SDavid van Moolenbroek       {
815*5d5fbe79SDavid van Moolenbroek         dst = &dns_mquery_v6group;
816*5d5fbe79SDavid van Moolenbroek       }
817*5d5fbe79SDavid van Moolenbroek #endif
818*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
819*5d5fbe79SDavid van Moolenbroek       else
820*5d5fbe79SDavid van Moolenbroek #endif
821*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4
822*5d5fbe79SDavid van Moolenbroek       {
823*5d5fbe79SDavid van Moolenbroek         dst = &dns_mquery_v4group;
824*5d5fbe79SDavid van Moolenbroek       }
825*5d5fbe79SDavid van Moolenbroek #endif
826*5d5fbe79SDavid van Moolenbroek     } else
827*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
828*5d5fbe79SDavid van Moolenbroek     {
829*5d5fbe79SDavid van Moolenbroek       dst_port = DNS_SERVER_PORT;
830*5d5fbe79SDavid van Moolenbroek       dst = &dns_servers[entry->server_idx];
831*5d5fbe79SDavid van Moolenbroek     }
832*5d5fbe79SDavid van Moolenbroek     err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port);
833*5d5fbe79SDavid van Moolenbroek 
834*5d5fbe79SDavid van Moolenbroek     /* free pbuf */
835*5d5fbe79SDavid van Moolenbroek     pbuf_free(p);
836*5d5fbe79SDavid van Moolenbroek   } else {
837*5d5fbe79SDavid van Moolenbroek     err = ERR_MEM;
838*5d5fbe79SDavid van Moolenbroek   }
839*5d5fbe79SDavid van Moolenbroek 
840*5d5fbe79SDavid van Moolenbroek   return err;
841*5d5fbe79SDavid van Moolenbroek }
842*5d5fbe79SDavid van Moolenbroek 
843*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
844*5d5fbe79SDavid van Moolenbroek static struct udp_pcb*
dns_alloc_random_port(void)845*5d5fbe79SDavid van Moolenbroek dns_alloc_random_port(void)
846*5d5fbe79SDavid van Moolenbroek {
847*5d5fbe79SDavid van Moolenbroek   err_t err;
848*5d5fbe79SDavid van Moolenbroek   struct udp_pcb* pcb;
849*5d5fbe79SDavid van Moolenbroek 
850*5d5fbe79SDavid van Moolenbroek   pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
851*5d5fbe79SDavid van Moolenbroek   if (pcb == NULL) {
852*5d5fbe79SDavid van Moolenbroek     /* out of memory, have to reuse an existing pcb */
853*5d5fbe79SDavid van Moolenbroek     return NULL;
854*5d5fbe79SDavid van Moolenbroek   }
855*5d5fbe79SDavid van Moolenbroek   do {
856*5d5fbe79SDavid van Moolenbroek     u16_t port = (u16_t)DNS_RAND_TXID();
857*5d5fbe79SDavid van Moolenbroek     if (DNS_PORT_ALLOWED(port)) {
858*5d5fbe79SDavid van Moolenbroek       err = udp_bind(pcb, IP_ANY_TYPE, port);
859*5d5fbe79SDavid van Moolenbroek     } else {
860*5d5fbe79SDavid van Moolenbroek       /* this port is not allowed, try again */
861*5d5fbe79SDavid van Moolenbroek       err = ERR_USE;
862*5d5fbe79SDavid van Moolenbroek     }
863*5d5fbe79SDavid van Moolenbroek   } while (err == ERR_USE);
864*5d5fbe79SDavid van Moolenbroek   if (err != ERR_OK) {
865*5d5fbe79SDavid van Moolenbroek     udp_remove(pcb);
866*5d5fbe79SDavid van Moolenbroek     return NULL;
867*5d5fbe79SDavid van Moolenbroek   }
868*5d5fbe79SDavid van Moolenbroek   udp_recv(pcb, dns_recv, NULL);
869*5d5fbe79SDavid van Moolenbroek   return pcb;
870*5d5fbe79SDavid van Moolenbroek }
871*5d5fbe79SDavid van Moolenbroek 
872*5d5fbe79SDavid van Moolenbroek /**
873*5d5fbe79SDavid van Moolenbroek  * dns_alloc_pcb() - allocates a new pcb (or reuses an existing one) to be used
874*5d5fbe79SDavid van Moolenbroek  * for sending a request
875*5d5fbe79SDavid van Moolenbroek  *
876*5d5fbe79SDavid van Moolenbroek  * @return an index into dns_pcbs
877*5d5fbe79SDavid van Moolenbroek  */
878*5d5fbe79SDavid van Moolenbroek static u8_t
dns_alloc_pcb(void)879*5d5fbe79SDavid van Moolenbroek dns_alloc_pcb(void)
880*5d5fbe79SDavid van Moolenbroek {
881*5d5fbe79SDavid van Moolenbroek   u8_t i;
882*5d5fbe79SDavid van Moolenbroek   u8_t idx;
883*5d5fbe79SDavid van Moolenbroek 
884*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < DNS_MAX_SOURCE_PORTS; i++) {
885*5d5fbe79SDavid van Moolenbroek     if (dns_pcbs[i] == NULL) {
886*5d5fbe79SDavid van Moolenbroek       break;
887*5d5fbe79SDavid van Moolenbroek     }
888*5d5fbe79SDavid van Moolenbroek   }
889*5d5fbe79SDavid van Moolenbroek   if (i < DNS_MAX_SOURCE_PORTS) {
890*5d5fbe79SDavid van Moolenbroek     dns_pcbs[i] = dns_alloc_random_port();
891*5d5fbe79SDavid van Moolenbroek     if (dns_pcbs[i] != NULL) {
892*5d5fbe79SDavid van Moolenbroek       /* succeeded */
893*5d5fbe79SDavid van Moolenbroek       dns_last_pcb_idx = i;
894*5d5fbe79SDavid van Moolenbroek       return i;
895*5d5fbe79SDavid van Moolenbroek     }
896*5d5fbe79SDavid van Moolenbroek   }
897*5d5fbe79SDavid van Moolenbroek   /* if we come here, creating a new UDP pcb failed, so we have to use
898*5d5fbe79SDavid van Moolenbroek      an already existing one */
899*5d5fbe79SDavid van Moolenbroek   for (i = 0, idx = dns_last_pcb_idx + 1; i < DNS_MAX_SOURCE_PORTS; i++, idx++) {
900*5d5fbe79SDavid van Moolenbroek     if (idx >= DNS_MAX_SOURCE_PORTS) {
901*5d5fbe79SDavid van Moolenbroek       idx = 0;
902*5d5fbe79SDavid van Moolenbroek     }
903*5d5fbe79SDavid van Moolenbroek     if (dns_pcbs[idx] != NULL) {
904*5d5fbe79SDavid van Moolenbroek       dns_last_pcb_idx = idx;
905*5d5fbe79SDavid van Moolenbroek       return idx;
906*5d5fbe79SDavid van Moolenbroek     }
907*5d5fbe79SDavid van Moolenbroek   }
908*5d5fbe79SDavid van Moolenbroek   return DNS_MAX_SOURCE_PORTS;
909*5d5fbe79SDavid van Moolenbroek }
910*5d5fbe79SDavid van Moolenbroek #endif /* ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) */
911*5d5fbe79SDavid van Moolenbroek 
912*5d5fbe79SDavid van Moolenbroek /**
913*5d5fbe79SDavid van Moolenbroek  * dns_call_found() - call the found callback and check if there are duplicate
914*5d5fbe79SDavid van Moolenbroek  * entries for the given hostname. If there are any, their found callback will
915*5d5fbe79SDavid van Moolenbroek  * be called and they will be removed.
916*5d5fbe79SDavid van Moolenbroek  *
917*5d5fbe79SDavid van Moolenbroek  * @param idx dns table index of the entry that is resolved or removed
918*5d5fbe79SDavid van Moolenbroek  * @param addr IP address for the hostname (or NULL on error or memory shortage)
919*5d5fbe79SDavid van Moolenbroek  */
920*5d5fbe79SDavid van Moolenbroek static void
dns_call_found(u8_t idx,ip_addr_t * addr)921*5d5fbe79SDavid van Moolenbroek dns_call_found(u8_t idx, ip_addr_t* addr)
922*5d5fbe79SDavid van Moolenbroek {
923*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & (LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT)) != 0)
924*5d5fbe79SDavid van Moolenbroek   u8_t i;
925*5d5fbe79SDavid van Moolenbroek #endif
926*5d5fbe79SDavid van Moolenbroek 
927*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
928*5d5fbe79SDavid van Moolenbroek   if (addr != NULL) {
929*5d5fbe79SDavid van Moolenbroek     /* check that address type matches the request and adapt the table entry */
930*5d5fbe79SDavid van Moolenbroek     if (IP_IS_V6_VAL(*addr)) {
931*5d5fbe79SDavid van Moolenbroek       LWIP_ASSERT("invalid response", LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
932*5d5fbe79SDavid van Moolenbroek       dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
933*5d5fbe79SDavid van Moolenbroek     } else {
934*5d5fbe79SDavid van Moolenbroek       LWIP_ASSERT("invalid response", !LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
935*5d5fbe79SDavid van Moolenbroek       dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
936*5d5fbe79SDavid van Moolenbroek     }
937*5d5fbe79SDavid van Moolenbroek   }
938*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
939*5d5fbe79SDavid van Moolenbroek 
940*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
941*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < DNS_MAX_REQUESTS; i++) {
942*5d5fbe79SDavid van Moolenbroek     if (dns_requests[i].found && (dns_requests[i].dns_table_idx == idx)) {
943*5d5fbe79SDavid van Moolenbroek       (*dns_requests[i].found)(dns_table[idx].name, addr, dns_requests[i].arg);
944*5d5fbe79SDavid van Moolenbroek       /* flush this entry */
945*5d5fbe79SDavid van Moolenbroek       dns_requests[i].found = NULL;
946*5d5fbe79SDavid van Moolenbroek     }
947*5d5fbe79SDavid van Moolenbroek   }
948*5d5fbe79SDavid van Moolenbroek #else
949*5d5fbe79SDavid van Moolenbroek   if (dns_requests[idx].found) {
950*5d5fbe79SDavid van Moolenbroek     (*dns_requests[idx].found)(dns_table[idx].name, addr, dns_requests[idx].arg);
951*5d5fbe79SDavid van Moolenbroek   }
952*5d5fbe79SDavid van Moolenbroek   dns_requests[idx].found = NULL;
953*5d5fbe79SDavid van Moolenbroek #endif
954*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
955*5d5fbe79SDavid van Moolenbroek   /* close the pcb used unless other request are using it */
956*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < DNS_MAX_REQUESTS; i++) {
957*5d5fbe79SDavid van Moolenbroek     if (i == idx) {
958*5d5fbe79SDavid van Moolenbroek       continue; /* only check other requests */
959*5d5fbe79SDavid van Moolenbroek     }
960*5d5fbe79SDavid van Moolenbroek     if (dns_table[i].state == DNS_STATE_ASKING) {
961*5d5fbe79SDavid van Moolenbroek       if (dns_table[i].pcb_idx == dns_table[idx].pcb_idx) {
962*5d5fbe79SDavid van Moolenbroek         /* another request is still using the same pcb */
963*5d5fbe79SDavid van Moolenbroek         dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
964*5d5fbe79SDavid van Moolenbroek         break;
965*5d5fbe79SDavid van Moolenbroek       }
966*5d5fbe79SDavid van Moolenbroek     }
967*5d5fbe79SDavid van Moolenbroek   }
968*5d5fbe79SDavid van Moolenbroek   if (dns_table[idx].pcb_idx < DNS_MAX_SOURCE_PORTS) {
969*5d5fbe79SDavid van Moolenbroek     /* if we come here, the pcb is not used any more and can be removed */
970*5d5fbe79SDavid van Moolenbroek     udp_remove(dns_pcbs[dns_table[idx].pcb_idx]);
971*5d5fbe79SDavid van Moolenbroek     dns_pcbs[dns_table[idx].pcb_idx] = NULL;
972*5d5fbe79SDavid van Moolenbroek     dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
973*5d5fbe79SDavid van Moolenbroek   }
974*5d5fbe79SDavid van Moolenbroek #endif
975*5d5fbe79SDavid van Moolenbroek }
976*5d5fbe79SDavid van Moolenbroek 
977*5d5fbe79SDavid van Moolenbroek /* Create a query transmission ID that is unique for all outstanding queries */
978*5d5fbe79SDavid van Moolenbroek static u16_t
dns_create_txid(void)979*5d5fbe79SDavid van Moolenbroek dns_create_txid(void)
980*5d5fbe79SDavid van Moolenbroek {
981*5d5fbe79SDavid van Moolenbroek   u16_t txid;
982*5d5fbe79SDavid van Moolenbroek   u8_t i;
983*5d5fbe79SDavid van Moolenbroek 
984*5d5fbe79SDavid van Moolenbroek again:
985*5d5fbe79SDavid van Moolenbroek   txid = (u16_t)DNS_RAND_TXID();
986*5d5fbe79SDavid van Moolenbroek 
987*5d5fbe79SDavid van Moolenbroek   /* check whether the ID is unique */
988*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < DNS_TABLE_SIZE; i++) {
989*5d5fbe79SDavid van Moolenbroek     if ((dns_table[i].state == DNS_STATE_ASKING) &&
990*5d5fbe79SDavid van Moolenbroek         (dns_table[i].txid == txid)) {
991*5d5fbe79SDavid van Moolenbroek       /* ID already used by another pending query */
992*5d5fbe79SDavid van Moolenbroek       goto again;
993*5d5fbe79SDavid van Moolenbroek     }
994*5d5fbe79SDavid van Moolenbroek   }
995*5d5fbe79SDavid van Moolenbroek 
996*5d5fbe79SDavid van Moolenbroek   return txid;
997*5d5fbe79SDavid van Moolenbroek }
998*5d5fbe79SDavid van Moolenbroek 
999*5d5fbe79SDavid van Moolenbroek /**
1000*5d5fbe79SDavid van Moolenbroek  * dns_check_entry() - see if entry has not yet been queried and, if so, sends out a query.
1001*5d5fbe79SDavid van Moolenbroek  * Check an entry in the dns_table:
1002*5d5fbe79SDavid van Moolenbroek  * - send out query for new entries
1003*5d5fbe79SDavid van Moolenbroek  * - retry old pending entries on timeout (also with different servers)
1004*5d5fbe79SDavid van Moolenbroek  * - remove completed entries from the table if their TTL has expired
1005*5d5fbe79SDavid van Moolenbroek  *
1006*5d5fbe79SDavid van Moolenbroek  * @param i index of the dns_table entry to check
1007*5d5fbe79SDavid van Moolenbroek  */
1008*5d5fbe79SDavid van Moolenbroek static void
dns_check_entry(u8_t i)1009*5d5fbe79SDavid van Moolenbroek dns_check_entry(u8_t i)
1010*5d5fbe79SDavid van Moolenbroek {
1011*5d5fbe79SDavid van Moolenbroek   err_t err;
1012*5d5fbe79SDavid van Moolenbroek   struct dns_table_entry *entry = &dns_table[i];
1013*5d5fbe79SDavid van Moolenbroek 
1014*5d5fbe79SDavid van Moolenbroek   LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
1015*5d5fbe79SDavid van Moolenbroek 
1016*5d5fbe79SDavid van Moolenbroek   switch (entry->state) {
1017*5d5fbe79SDavid van Moolenbroek     case DNS_STATE_NEW:
1018*5d5fbe79SDavid van Moolenbroek       /* initialize new entry */
1019*5d5fbe79SDavid van Moolenbroek       entry->txid = dns_create_txid();
1020*5d5fbe79SDavid van Moolenbroek       entry->state = DNS_STATE_ASKING;
1021*5d5fbe79SDavid van Moolenbroek       entry->server_idx = 0;
1022*5d5fbe79SDavid van Moolenbroek       entry->tmr = 1;
1023*5d5fbe79SDavid van Moolenbroek       entry->retries = 0;
1024*5d5fbe79SDavid van Moolenbroek 
1025*5d5fbe79SDavid van Moolenbroek       /* send DNS packet for this entry */
1026*5d5fbe79SDavid van Moolenbroek       err = dns_send(i);
1027*5d5fbe79SDavid van Moolenbroek       if (err != ERR_OK) {
1028*5d5fbe79SDavid van Moolenbroek         LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
1029*5d5fbe79SDavid van Moolenbroek                     ("dns_send returned error: %s\n", lwip_strerr(err)));
1030*5d5fbe79SDavid van Moolenbroek       }
1031*5d5fbe79SDavid van Moolenbroek       break;
1032*5d5fbe79SDavid van Moolenbroek     case DNS_STATE_ASKING:
1033*5d5fbe79SDavid van Moolenbroek       if (--entry->tmr == 0) {
1034*5d5fbe79SDavid van Moolenbroek         if (++entry->retries == DNS_MAX_RETRIES) {
1035*5d5fbe79SDavid van Moolenbroek           if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1])
1036*5d5fbe79SDavid van Moolenbroek #if LWIP_DNS_SUPPORT_MDNS_QUERIES
1037*5d5fbe79SDavid van Moolenbroek             && !entry->is_mdns
1038*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
1039*5d5fbe79SDavid van Moolenbroek             ) {
1040*5d5fbe79SDavid van Moolenbroek             /* change of server */
1041*5d5fbe79SDavid van Moolenbroek             entry->server_idx++;
1042*5d5fbe79SDavid van Moolenbroek             entry->tmr = 1;
1043*5d5fbe79SDavid van Moolenbroek             entry->retries = 0;
1044*5d5fbe79SDavid van Moolenbroek           } else {
1045*5d5fbe79SDavid van Moolenbroek             LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name));
1046*5d5fbe79SDavid van Moolenbroek             /* call specified callback function if provided */
1047*5d5fbe79SDavid van Moolenbroek             dns_call_found(i, NULL);
1048*5d5fbe79SDavid van Moolenbroek             /* flush this entry */
1049*5d5fbe79SDavid van Moolenbroek             entry->state = DNS_STATE_UNUSED;
1050*5d5fbe79SDavid van Moolenbroek             break;
1051*5d5fbe79SDavid van Moolenbroek           }
1052*5d5fbe79SDavid van Moolenbroek         } else {
1053*5d5fbe79SDavid van Moolenbroek           /* wait longer for the next retry */
1054*5d5fbe79SDavid van Moolenbroek           entry->tmr = entry->retries;
1055*5d5fbe79SDavid van Moolenbroek         }
1056*5d5fbe79SDavid van Moolenbroek 
1057*5d5fbe79SDavid van Moolenbroek         /* send DNS packet for this entry */
1058*5d5fbe79SDavid van Moolenbroek         err = dns_send(i);
1059*5d5fbe79SDavid van Moolenbroek         if (err != ERR_OK) {
1060*5d5fbe79SDavid van Moolenbroek           LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
1061*5d5fbe79SDavid van Moolenbroek                       ("dns_send returned error: %s\n", lwip_strerr(err)));
1062*5d5fbe79SDavid van Moolenbroek         }
1063*5d5fbe79SDavid van Moolenbroek       }
1064*5d5fbe79SDavid van Moolenbroek       break;
1065*5d5fbe79SDavid van Moolenbroek     case DNS_STATE_DONE:
1066*5d5fbe79SDavid van Moolenbroek       /* if the time to live is nul */
1067*5d5fbe79SDavid van Moolenbroek       if ((entry->ttl == 0) || (--entry->ttl == 0)) {
1068*5d5fbe79SDavid van Moolenbroek         LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", entry->name));
1069*5d5fbe79SDavid van Moolenbroek         /* flush this entry, there cannot be any related pending entries in this state */
1070*5d5fbe79SDavid van Moolenbroek         entry->state = DNS_STATE_UNUSED;
1071*5d5fbe79SDavid van Moolenbroek       }
1072*5d5fbe79SDavid van Moolenbroek       break;
1073*5d5fbe79SDavid van Moolenbroek     case DNS_STATE_UNUSED:
1074*5d5fbe79SDavid van Moolenbroek       /* nothing to do */
1075*5d5fbe79SDavid van Moolenbroek       break;
1076*5d5fbe79SDavid van Moolenbroek     default:
1077*5d5fbe79SDavid van Moolenbroek       LWIP_ASSERT("unknown dns_table entry state:", 0);
1078*5d5fbe79SDavid van Moolenbroek       break;
1079*5d5fbe79SDavid van Moolenbroek   }
1080*5d5fbe79SDavid van Moolenbroek }
1081*5d5fbe79SDavid van Moolenbroek 
1082*5d5fbe79SDavid van Moolenbroek /**
1083*5d5fbe79SDavid van Moolenbroek  * Call dns_check_entry for each entry in dns_table - check all entries.
1084*5d5fbe79SDavid van Moolenbroek  */
1085*5d5fbe79SDavid van Moolenbroek static void
dns_check_entries(void)1086*5d5fbe79SDavid van Moolenbroek dns_check_entries(void)
1087*5d5fbe79SDavid van Moolenbroek {
1088*5d5fbe79SDavid van Moolenbroek   u8_t i;
1089*5d5fbe79SDavid van Moolenbroek 
1090*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
1091*5d5fbe79SDavid van Moolenbroek     dns_check_entry(i);
1092*5d5fbe79SDavid van Moolenbroek   }
1093*5d5fbe79SDavid van Moolenbroek }
1094*5d5fbe79SDavid van Moolenbroek 
1095*5d5fbe79SDavid van Moolenbroek /**
1096*5d5fbe79SDavid van Moolenbroek  * Save TTL and call dns_call_found for correct response.
1097*5d5fbe79SDavid van Moolenbroek  */
1098*5d5fbe79SDavid van Moolenbroek static void
dns_correct_response(u8_t idx,u32_t ttl)1099*5d5fbe79SDavid van Moolenbroek dns_correct_response(u8_t idx, u32_t ttl)
1100*5d5fbe79SDavid van Moolenbroek {
1101*5d5fbe79SDavid van Moolenbroek   struct dns_table_entry *entry = &dns_table[idx];
1102*5d5fbe79SDavid van Moolenbroek 
1103*5d5fbe79SDavid van Moolenbroek   entry->state = DNS_STATE_DONE;
1104*5d5fbe79SDavid van Moolenbroek 
1105*5d5fbe79SDavid van Moolenbroek   LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name));
1106*5d5fbe79SDavid van Moolenbroek   ip_addr_debug_print(DNS_DEBUG, (&(entry->ipaddr)));
1107*5d5fbe79SDavid van Moolenbroek   LWIP_DEBUGF(DNS_DEBUG, ("\n"));
1108*5d5fbe79SDavid van Moolenbroek 
1109*5d5fbe79SDavid van Moolenbroek   /* read the answer resource record's TTL, and maximize it if needed */
1110*5d5fbe79SDavid van Moolenbroek   entry->ttl = ttl;
1111*5d5fbe79SDavid van Moolenbroek   if (entry->ttl > DNS_MAX_TTL) {
1112*5d5fbe79SDavid van Moolenbroek     entry->ttl = DNS_MAX_TTL;
1113*5d5fbe79SDavid van Moolenbroek   }
1114*5d5fbe79SDavid van Moolenbroek   dns_call_found(idx, &entry->ipaddr);
1115*5d5fbe79SDavid van Moolenbroek 
1116*5d5fbe79SDavid van Moolenbroek   if (entry->ttl == 0) {
1117*5d5fbe79SDavid van Moolenbroek     /* RFC 883, page 29: "Zero values are
1118*5d5fbe79SDavid van Moolenbroek        interpreted to mean that the RR can only be used for the
1119*5d5fbe79SDavid van Moolenbroek        transaction in progress, and should not be cached."
1120*5d5fbe79SDavid van Moolenbroek        -> flush this entry now */
1121*5d5fbe79SDavid van Moolenbroek     /* entry reused during callback? */
1122*5d5fbe79SDavid van Moolenbroek     if (entry->state == DNS_STATE_DONE) {
1123*5d5fbe79SDavid van Moolenbroek       entry->state = DNS_STATE_UNUSED;
1124*5d5fbe79SDavid van Moolenbroek     }
1125*5d5fbe79SDavid van Moolenbroek   }
1126*5d5fbe79SDavid van Moolenbroek }
1127*5d5fbe79SDavid van Moolenbroek /**
1128*5d5fbe79SDavid van Moolenbroek  * Receive input function for DNS response packets arriving for the dns UDP pcb.
1129*5d5fbe79SDavid van Moolenbroek  */
1130*5d5fbe79SDavid van Moolenbroek static void
dns_recv(void * arg,struct udp_pcb * pcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)1131*5d5fbe79SDavid van Moolenbroek dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
1132*5d5fbe79SDavid van Moolenbroek {
1133*5d5fbe79SDavid van Moolenbroek   u8_t i;
1134*5d5fbe79SDavid van Moolenbroek   u16_t txid;
1135*5d5fbe79SDavid van Moolenbroek   u16_t res_idx;
1136*5d5fbe79SDavid van Moolenbroek   struct dns_hdr hdr;
1137*5d5fbe79SDavid van Moolenbroek   struct dns_answer ans;
1138*5d5fbe79SDavid van Moolenbroek   struct dns_query qry;
1139*5d5fbe79SDavid van Moolenbroek   u16_t nquestions, nanswers;
1140*5d5fbe79SDavid van Moolenbroek 
1141*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(arg);
1142*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(pcb);
1143*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(port);
1144*5d5fbe79SDavid van Moolenbroek 
1145*5d5fbe79SDavid van Moolenbroek   /* is the dns message big enough ? */
1146*5d5fbe79SDavid van Moolenbroek   if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY)) {
1147*5d5fbe79SDavid van Moolenbroek     LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
1148*5d5fbe79SDavid van Moolenbroek     /* free pbuf and return */
1149*5d5fbe79SDavid van Moolenbroek     goto memerr;
1150*5d5fbe79SDavid van Moolenbroek   }
1151*5d5fbe79SDavid van Moolenbroek 
1152*5d5fbe79SDavid van Moolenbroek   /* copy dns payload inside static buffer for processing */
1153*5d5fbe79SDavid van Moolenbroek   if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, 0) == SIZEOF_DNS_HDR) {
1154*5d5fbe79SDavid van Moolenbroek     /* Match the ID in the DNS header with the name table. */
1155*5d5fbe79SDavid van Moolenbroek     txid = lwip_htons(hdr.id);
1156*5d5fbe79SDavid van Moolenbroek     for (i = 0; i < DNS_TABLE_SIZE; i++) {
1157*5d5fbe79SDavid van Moolenbroek       const struct dns_table_entry *entry = &dns_table[i];
1158*5d5fbe79SDavid van Moolenbroek       if ((entry->state == DNS_STATE_ASKING) &&
1159*5d5fbe79SDavid van Moolenbroek           (entry->txid == txid)) {
1160*5d5fbe79SDavid van Moolenbroek 
1161*5d5fbe79SDavid van Moolenbroek         /* We only care about the question(s) and the answers. The authrr
1162*5d5fbe79SDavid van Moolenbroek            and the extrarr are simply discarded. */
1163*5d5fbe79SDavid van Moolenbroek         nquestions = lwip_htons(hdr.numquestions);
1164*5d5fbe79SDavid van Moolenbroek         nanswers   = lwip_htons(hdr.numanswers);
1165*5d5fbe79SDavid van Moolenbroek 
1166*5d5fbe79SDavid van Moolenbroek         /* Check for correct response. */
1167*5d5fbe79SDavid van Moolenbroek         if ((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) {
1168*5d5fbe79SDavid van Moolenbroek           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": not a response\n", entry->name));
1169*5d5fbe79SDavid van Moolenbroek           goto memerr; /* ignore this packet */
1170*5d5fbe79SDavid van Moolenbroek         }
1171*5d5fbe79SDavid van Moolenbroek         if (nquestions != 1) {
1172*5d5fbe79SDavid van Moolenbroek           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
1173*5d5fbe79SDavid van Moolenbroek           goto memerr; /* ignore this packet */
1174*5d5fbe79SDavid van Moolenbroek         }
1175*5d5fbe79SDavid van Moolenbroek 
1176*5d5fbe79SDavid van Moolenbroek #if LWIP_DNS_SUPPORT_MDNS_QUERIES
1177*5d5fbe79SDavid van Moolenbroek         if (!entry->is_mdns)
1178*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
1179*5d5fbe79SDavid van Moolenbroek         {
1180*5d5fbe79SDavid van Moolenbroek           /* Check whether response comes from the same network address to which the
1181*5d5fbe79SDavid van Moolenbroek              question was sent. (RFC 5452) */
1182*5d5fbe79SDavid van Moolenbroek           if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
1183*5d5fbe79SDavid van Moolenbroek             goto memerr; /* ignore this packet */
1184*5d5fbe79SDavid van Moolenbroek           }
1185*5d5fbe79SDavid van Moolenbroek         }
1186*5d5fbe79SDavid van Moolenbroek 
1187*5d5fbe79SDavid van Moolenbroek         /* Check if the name in the "question" part match with the name in the entry and
1188*5d5fbe79SDavid van Moolenbroek            skip it if equal. */
1189*5d5fbe79SDavid van Moolenbroek         res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR);
1190*5d5fbe79SDavid van Moolenbroek         if (res_idx == 0xFFFF) {
1191*5d5fbe79SDavid van Moolenbroek           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
1192*5d5fbe79SDavid van Moolenbroek           goto memerr; /* ignore this packet */
1193*5d5fbe79SDavid van Moolenbroek         }
1194*5d5fbe79SDavid van Moolenbroek 
1195*5d5fbe79SDavid van Moolenbroek         /* check if "question" part matches the request */
1196*5d5fbe79SDavid van Moolenbroek         if (pbuf_copy_partial(p, &qry, SIZEOF_DNS_QUERY, res_idx) != SIZEOF_DNS_QUERY) {
1197*5d5fbe79SDavid van Moolenbroek           goto memerr; /* ignore this packet */
1198*5d5fbe79SDavid van Moolenbroek         }
1199*5d5fbe79SDavid van Moolenbroek         if ((qry.cls != PP_HTONS(DNS_RRCLASS_IN)) ||
1200*5d5fbe79SDavid van Moolenbroek           (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_AAAA))) ||
1201*5d5fbe79SDavid van Moolenbroek           (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_A)))) {
1202*5d5fbe79SDavid van Moolenbroek           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
1203*5d5fbe79SDavid van Moolenbroek           goto memerr; /* ignore this packet */
1204*5d5fbe79SDavid van Moolenbroek         }
1205*5d5fbe79SDavid van Moolenbroek         /* skip the rest of the "question" part */
1206*5d5fbe79SDavid van Moolenbroek         res_idx += SIZEOF_DNS_QUERY;
1207*5d5fbe79SDavid van Moolenbroek 
1208*5d5fbe79SDavid van Moolenbroek         /* Check for error. If so, call callback to inform. */
1209*5d5fbe79SDavid van Moolenbroek         if (hdr.flags2 & DNS_FLAG2_ERR_MASK) {
1210*5d5fbe79SDavid van Moolenbroek           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name));
1211*5d5fbe79SDavid van Moolenbroek         } else {
1212*5d5fbe79SDavid van Moolenbroek           while ((nanswers > 0) && (res_idx < p->tot_len)) {
1213*5d5fbe79SDavid van Moolenbroek             /* skip answer resource record's host name */
1214*5d5fbe79SDavid van Moolenbroek             res_idx = dns_skip_name(p, res_idx);
1215*5d5fbe79SDavid van Moolenbroek             if (res_idx == 0xFFFF) {
1216*5d5fbe79SDavid van Moolenbroek               goto memerr; /* ignore this packet */
1217*5d5fbe79SDavid van Moolenbroek             }
1218*5d5fbe79SDavid van Moolenbroek 
1219*5d5fbe79SDavid van Moolenbroek             /* Check for IP address type and Internet class. Others are discarded. */
1220*5d5fbe79SDavid van Moolenbroek             if (pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx) != SIZEOF_DNS_ANSWER) {
1221*5d5fbe79SDavid van Moolenbroek               goto memerr; /* ignore this packet */
1222*5d5fbe79SDavid van Moolenbroek             }
1223*5d5fbe79SDavid van Moolenbroek             res_idx += SIZEOF_DNS_ANSWER;
1224*5d5fbe79SDavid van Moolenbroek 
1225*5d5fbe79SDavid van Moolenbroek             if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) {
1226*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4
1227*5d5fbe79SDavid van Moolenbroek               if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) {
1228*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
1229*5d5fbe79SDavid van Moolenbroek                 if (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
1230*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
1231*5d5fbe79SDavid van Moolenbroek                 {
1232*5d5fbe79SDavid van Moolenbroek                   ip4_addr_t ip4addr;
1233*5d5fbe79SDavid van Moolenbroek                   /* read the IP address after answer resource record's header */
1234*5d5fbe79SDavid van Moolenbroek                   if (pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx) != sizeof(ip4_addr_t)) {
1235*5d5fbe79SDavid van Moolenbroek                     goto memerr; /* ignore this packet */
1236*5d5fbe79SDavid van Moolenbroek                   }
1237*5d5fbe79SDavid van Moolenbroek                   ip_addr_copy_from_ip4(dns_table[i].ipaddr, ip4addr);
1238*5d5fbe79SDavid van Moolenbroek                   pbuf_free(p);
1239*5d5fbe79SDavid van Moolenbroek                   /* handle correct response */
1240*5d5fbe79SDavid van Moolenbroek                   dns_correct_response(i, lwip_ntohl(ans.ttl));
1241*5d5fbe79SDavid van Moolenbroek                   return;
1242*5d5fbe79SDavid van Moolenbroek                 }
1243*5d5fbe79SDavid van Moolenbroek               }
1244*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 */
1245*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV6
1246*5d5fbe79SDavid van Moolenbroek               if ((ans.type == PP_HTONS(DNS_RRTYPE_AAAA)) && (ans.len == PP_HTONS(sizeof(ip6_addr_t)))) {
1247*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
1248*5d5fbe79SDavid van Moolenbroek                 if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
1249*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
1250*5d5fbe79SDavid van Moolenbroek                 {
1251*5d5fbe79SDavid van Moolenbroek                   ip6_addr_t ip6addr;
1252*5d5fbe79SDavid van Moolenbroek                   /* read the IP address after answer resource record's header */
1253*5d5fbe79SDavid van Moolenbroek                   if (pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_t), res_idx) != sizeof(ip6_addr_t)) {
1254*5d5fbe79SDavid van Moolenbroek                     goto memerr; /* ignore this packet */
1255*5d5fbe79SDavid van Moolenbroek                   }
1256*5d5fbe79SDavid van Moolenbroek                   ip_addr_copy_from_ip6(dns_table[i].ipaddr, ip6addr);
1257*5d5fbe79SDavid van Moolenbroek                   pbuf_free(p);
1258*5d5fbe79SDavid van Moolenbroek                   /* handle correct response */
1259*5d5fbe79SDavid van Moolenbroek                   dns_correct_response(i, lwip_ntohl(ans.ttl));
1260*5d5fbe79SDavid van Moolenbroek                   return;
1261*5d5fbe79SDavid van Moolenbroek                 }
1262*5d5fbe79SDavid van Moolenbroek               }
1263*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV6 */
1264*5d5fbe79SDavid van Moolenbroek             }
1265*5d5fbe79SDavid van Moolenbroek             /* skip this answer */
1266*5d5fbe79SDavid van Moolenbroek             if ((int)(res_idx + lwip_htons(ans.len)) > 0xFFFF) {
1267*5d5fbe79SDavid van Moolenbroek               goto memerr; /* ignore this packet */
1268*5d5fbe79SDavid van Moolenbroek             }
1269*5d5fbe79SDavid van Moolenbroek             res_idx += lwip_htons(ans.len);
1270*5d5fbe79SDavid van Moolenbroek             --nanswers;
1271*5d5fbe79SDavid van Moolenbroek           }
1272*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
1273*5d5fbe79SDavid van Moolenbroek           if ((entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) ||
1274*5d5fbe79SDavid van Moolenbroek               (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
1275*5d5fbe79SDavid van Moolenbroek             if (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
1276*5d5fbe79SDavid van Moolenbroek               /* IPv4 failed, try IPv6 */
1277*5d5fbe79SDavid van Moolenbroek               dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
1278*5d5fbe79SDavid van Moolenbroek             } else {
1279*5d5fbe79SDavid van Moolenbroek               /* IPv6 failed, try IPv4 */
1280*5d5fbe79SDavid van Moolenbroek               dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
1281*5d5fbe79SDavid van Moolenbroek             }
1282*5d5fbe79SDavid van Moolenbroek             pbuf_free(p);
1283*5d5fbe79SDavid van Moolenbroek             dns_table[i].state = DNS_STATE_NEW;
1284*5d5fbe79SDavid van Moolenbroek             dns_check_entry(i);
1285*5d5fbe79SDavid van Moolenbroek             return;
1286*5d5fbe79SDavid van Moolenbroek           }
1287*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
1288*5d5fbe79SDavid van Moolenbroek           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->name));
1289*5d5fbe79SDavid van Moolenbroek         }
1290*5d5fbe79SDavid van Moolenbroek         /* call callback to indicate error, clean up memory and return */
1291*5d5fbe79SDavid van Moolenbroek         pbuf_free(p);
1292*5d5fbe79SDavid van Moolenbroek         dns_call_found(i, NULL);
1293*5d5fbe79SDavid van Moolenbroek         dns_table[i].state = DNS_STATE_UNUSED;
1294*5d5fbe79SDavid van Moolenbroek         return;
1295*5d5fbe79SDavid van Moolenbroek       }
1296*5d5fbe79SDavid van Moolenbroek     }
1297*5d5fbe79SDavid van Moolenbroek   }
1298*5d5fbe79SDavid van Moolenbroek 
1299*5d5fbe79SDavid van Moolenbroek memerr:
1300*5d5fbe79SDavid van Moolenbroek   /* deallocate memory and return */
1301*5d5fbe79SDavid van Moolenbroek   pbuf_free(p);
1302*5d5fbe79SDavid van Moolenbroek   return;
1303*5d5fbe79SDavid van Moolenbroek }
1304*5d5fbe79SDavid van Moolenbroek 
1305*5d5fbe79SDavid van Moolenbroek /**
1306*5d5fbe79SDavid van Moolenbroek  * Queues a new hostname to resolve and sends out a DNS query for that hostname
1307*5d5fbe79SDavid van Moolenbroek  *
1308*5d5fbe79SDavid van Moolenbroek  * @param name the hostname that is to be queried
1309*5d5fbe79SDavid van Moolenbroek  * @param hostnamelen length of the hostname
1310*5d5fbe79SDavid van Moolenbroek  * @param found a callback function to be called on success, failure or timeout
1311*5d5fbe79SDavid van Moolenbroek  * @param callback_arg argument to pass to the callback function
1312*5d5fbe79SDavid van Moolenbroek  * @return err_t return code.
1313*5d5fbe79SDavid van Moolenbroek  */
1314*5d5fbe79SDavid van Moolenbroek static err_t
dns_enqueue(const char * name,size_t hostnamelen,dns_found_callback found,void * callback_arg LWIP_DNS_ADDRTYPE_ARG (u8_t dns_addrtype)LWIP_DNS_ISMDNS_ARG (u8_t is_mdns))1315*5d5fbe79SDavid van Moolenbroek dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
1316*5d5fbe79SDavid van Moolenbroek             void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype) LWIP_DNS_ISMDNS_ARG(u8_t is_mdns))
1317*5d5fbe79SDavid van Moolenbroek {
1318*5d5fbe79SDavid van Moolenbroek   u8_t i;
1319*5d5fbe79SDavid van Moolenbroek   u8_t lseq, lseqi;
1320*5d5fbe79SDavid van Moolenbroek   struct dns_table_entry *entry = NULL;
1321*5d5fbe79SDavid van Moolenbroek   size_t namelen;
1322*5d5fbe79SDavid van Moolenbroek   struct dns_req_entry* req;
1323*5d5fbe79SDavid van Moolenbroek 
1324*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
1325*5d5fbe79SDavid van Moolenbroek   u8_t r;
1326*5d5fbe79SDavid van Moolenbroek   /* check for duplicate entries */
1327*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < DNS_TABLE_SIZE; i++) {
1328*5d5fbe79SDavid van Moolenbroek     if ((dns_table[i].state == DNS_STATE_ASKING) &&
1329*5d5fbe79SDavid van Moolenbroek         (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0)) {
1330*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
1331*5d5fbe79SDavid van Moolenbroek       if (dns_table[i].reqaddrtype != dns_addrtype) {
1332*5d5fbe79SDavid van Moolenbroek         /* requested address types don't match
1333*5d5fbe79SDavid van Moolenbroek            this can lead to 2 concurrent requests, but mixing the address types
1334*5d5fbe79SDavid van Moolenbroek            for the same host should not be that common */
1335*5d5fbe79SDavid van Moolenbroek         continue;
1336*5d5fbe79SDavid van Moolenbroek       }
1337*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
1338*5d5fbe79SDavid van Moolenbroek       /* this is a duplicate entry, find a free request entry */
1339*5d5fbe79SDavid van Moolenbroek       for (r = 0; r < DNS_MAX_REQUESTS; r++) {
1340*5d5fbe79SDavid van Moolenbroek         if (dns_requests[r].found == 0) {
1341*5d5fbe79SDavid van Moolenbroek           dns_requests[r].found = found;
1342*5d5fbe79SDavid van Moolenbroek           dns_requests[r].arg = callback_arg;
1343*5d5fbe79SDavid van Moolenbroek           dns_requests[r].dns_table_idx = i;
1344*5d5fbe79SDavid van Moolenbroek           LWIP_DNS_SET_ADDRTYPE(dns_requests[r].reqaddrtype, dns_addrtype);
1345*5d5fbe79SDavid van Moolenbroek           LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": duplicate request\n", name));
1346*5d5fbe79SDavid van Moolenbroek           return ERR_INPROGRESS;
1347*5d5fbe79SDavid van Moolenbroek         }
1348*5d5fbe79SDavid van Moolenbroek       }
1349*5d5fbe79SDavid van Moolenbroek     }
1350*5d5fbe79SDavid van Moolenbroek   }
1351*5d5fbe79SDavid van Moolenbroek   /* no duplicate entries found */
1352*5d5fbe79SDavid van Moolenbroek #endif
1353*5d5fbe79SDavid van Moolenbroek 
1354*5d5fbe79SDavid van Moolenbroek   /* search an unused entry, or the oldest one */
1355*5d5fbe79SDavid van Moolenbroek   lseq = 0;
1356*5d5fbe79SDavid van Moolenbroek   lseqi = DNS_TABLE_SIZE;
1357*5d5fbe79SDavid van Moolenbroek   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
1358*5d5fbe79SDavid van Moolenbroek     entry = &dns_table[i];
1359*5d5fbe79SDavid van Moolenbroek     /* is it an unused entry ? */
1360*5d5fbe79SDavid van Moolenbroek     if (entry->state == DNS_STATE_UNUSED) {
1361*5d5fbe79SDavid van Moolenbroek       break;
1362*5d5fbe79SDavid van Moolenbroek     }
1363*5d5fbe79SDavid van Moolenbroek     /* check if this is the oldest completed entry */
1364*5d5fbe79SDavid van Moolenbroek     if (entry->state == DNS_STATE_DONE) {
1365*5d5fbe79SDavid van Moolenbroek       u8_t age = dns_seqno - entry->seqno;
1366*5d5fbe79SDavid van Moolenbroek       if (age > lseq) {
1367*5d5fbe79SDavid van Moolenbroek         lseq = age;
1368*5d5fbe79SDavid van Moolenbroek         lseqi = i;
1369*5d5fbe79SDavid van Moolenbroek       }
1370*5d5fbe79SDavid van Moolenbroek     }
1371*5d5fbe79SDavid van Moolenbroek   }
1372*5d5fbe79SDavid van Moolenbroek 
1373*5d5fbe79SDavid van Moolenbroek   /* if we don't have found an unused entry, use the oldest completed one */
1374*5d5fbe79SDavid van Moolenbroek   if (i == DNS_TABLE_SIZE) {
1375*5d5fbe79SDavid van Moolenbroek     if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
1376*5d5fbe79SDavid van Moolenbroek       /* no entry can be used now, table is full */
1377*5d5fbe79SDavid van Moolenbroek       LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
1378*5d5fbe79SDavid van Moolenbroek       return ERR_MEM;
1379*5d5fbe79SDavid van Moolenbroek     } else {
1380*5d5fbe79SDavid van Moolenbroek       /* use the oldest completed one */
1381*5d5fbe79SDavid van Moolenbroek       i = lseqi;
1382*5d5fbe79SDavid van Moolenbroek       entry = &dns_table[i];
1383*5d5fbe79SDavid van Moolenbroek     }
1384*5d5fbe79SDavid van Moolenbroek   }
1385*5d5fbe79SDavid van Moolenbroek 
1386*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
1387*5d5fbe79SDavid van Moolenbroek   /* find a free request entry */
1388*5d5fbe79SDavid van Moolenbroek   req = NULL;
1389*5d5fbe79SDavid van Moolenbroek   for (r = 0; r < DNS_MAX_REQUESTS; r++) {
1390*5d5fbe79SDavid van Moolenbroek     if (dns_requests[r].found == NULL) {
1391*5d5fbe79SDavid van Moolenbroek       req = &dns_requests[r];
1392*5d5fbe79SDavid van Moolenbroek       break;
1393*5d5fbe79SDavid van Moolenbroek     }
1394*5d5fbe79SDavid van Moolenbroek   }
1395*5d5fbe79SDavid van Moolenbroek   if (req == NULL) {
1396*5d5fbe79SDavid van Moolenbroek     /* no request entry can be used now, table is full */
1397*5d5fbe79SDavid van Moolenbroek     LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS request entries table is full\n", name));
1398*5d5fbe79SDavid van Moolenbroek     return ERR_MEM;
1399*5d5fbe79SDavid van Moolenbroek   }
1400*5d5fbe79SDavid van Moolenbroek   req->dns_table_idx = i;
1401*5d5fbe79SDavid van Moolenbroek #else
1402*5d5fbe79SDavid van Moolenbroek   /* in this configuration, the entry index is the same as the request index */
1403*5d5fbe79SDavid van Moolenbroek   req = &dns_requests[i];
1404*5d5fbe79SDavid van Moolenbroek #endif
1405*5d5fbe79SDavid van Moolenbroek 
1406*5d5fbe79SDavid van Moolenbroek   /* use this entry */
1407*5d5fbe79SDavid van Moolenbroek   LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
1408*5d5fbe79SDavid van Moolenbroek 
1409*5d5fbe79SDavid van Moolenbroek   /* fill the entry */
1410*5d5fbe79SDavid van Moolenbroek   entry->state = DNS_STATE_NEW;
1411*5d5fbe79SDavid van Moolenbroek   entry->seqno = dns_seqno;
1412*5d5fbe79SDavid van Moolenbroek   LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype);
1413*5d5fbe79SDavid van Moolenbroek   LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype);
1414*5d5fbe79SDavid van Moolenbroek   req->found = found;
1415*5d5fbe79SDavid van Moolenbroek   req->arg   = callback_arg;
1416*5d5fbe79SDavid van Moolenbroek   namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1);
1417*5d5fbe79SDavid van Moolenbroek   MEMCPY(entry->name, name, namelen);
1418*5d5fbe79SDavid van Moolenbroek   entry->name[namelen] = 0;
1419*5d5fbe79SDavid van Moolenbroek 
1420*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
1421*5d5fbe79SDavid van Moolenbroek   entry->pcb_idx = dns_alloc_pcb();
1422*5d5fbe79SDavid van Moolenbroek   if (entry->pcb_idx >= DNS_MAX_SOURCE_PORTS) {
1423*5d5fbe79SDavid van Moolenbroek     /* failed to get a UDP pcb */
1424*5d5fbe79SDavid van Moolenbroek     LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": failed to allocate a pcb\n", name));
1425*5d5fbe79SDavid van Moolenbroek     entry->state = DNS_STATE_UNUSED;
1426*5d5fbe79SDavid van Moolenbroek     req->found = NULL;
1427*5d5fbe79SDavid van Moolenbroek     return ERR_MEM;
1428*5d5fbe79SDavid van Moolenbroek   }
1429*5d5fbe79SDavid van Moolenbroek   LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx)));
1430*5d5fbe79SDavid van Moolenbroek #endif
1431*5d5fbe79SDavid van Moolenbroek 
1432*5d5fbe79SDavid van Moolenbroek #if LWIP_DNS_SUPPORT_MDNS_QUERIES
1433*5d5fbe79SDavid van Moolenbroek   entry->is_mdns = is_mdns;
1434*5d5fbe79SDavid van Moolenbroek #endif
1435*5d5fbe79SDavid van Moolenbroek 
1436*5d5fbe79SDavid van Moolenbroek   dns_seqno++;
1437*5d5fbe79SDavid van Moolenbroek 
1438*5d5fbe79SDavid van Moolenbroek   /* force to send query without waiting timer */
1439*5d5fbe79SDavid van Moolenbroek   dns_check_entry(i);
1440*5d5fbe79SDavid van Moolenbroek 
1441*5d5fbe79SDavid van Moolenbroek   /* dns query is enqueued */
1442*5d5fbe79SDavid van Moolenbroek   return ERR_INPROGRESS;
1443*5d5fbe79SDavid van Moolenbroek }
1444*5d5fbe79SDavid van Moolenbroek 
1445*5d5fbe79SDavid van Moolenbroek /**
1446*5d5fbe79SDavid van Moolenbroek  * @ingroup dns
1447*5d5fbe79SDavid van Moolenbroek  * Resolve a hostname (string) into an IP address.
1448*5d5fbe79SDavid van Moolenbroek  * NON-BLOCKING callback version for use with raw API!!!
1449*5d5fbe79SDavid van Moolenbroek  *
1450*5d5fbe79SDavid van Moolenbroek  * Returns immediately with one of err_t return codes:
1451*5d5fbe79SDavid van Moolenbroek  * - ERR_OK if hostname is a valid IP address string or the host
1452*5d5fbe79SDavid van Moolenbroek  *   name is already in the local names table.
1453*5d5fbe79SDavid van Moolenbroek  * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
1454*5d5fbe79SDavid van Moolenbroek  *   for resolution if no errors are present.
1455*5d5fbe79SDavid van Moolenbroek  * - ERR_ARG: dns client not initialized or invalid hostname
1456*5d5fbe79SDavid van Moolenbroek  *
1457*5d5fbe79SDavid van Moolenbroek  * @param hostname the hostname that is to be queried
1458*5d5fbe79SDavid van Moolenbroek  * @param addr pointer to a ip_addr_t where to store the address if it is already
1459*5d5fbe79SDavid van Moolenbroek  *             cached in the dns_table (only valid if ERR_OK is returned!)
1460*5d5fbe79SDavid van Moolenbroek  * @param found a callback function to be called on success, failure or timeout (only if
1461*5d5fbe79SDavid van Moolenbroek  *              ERR_INPROGRESS is returned!)
1462*5d5fbe79SDavid van Moolenbroek  * @param callback_arg argument to pass to the callback function
1463*5d5fbe79SDavid van Moolenbroek  * @return a err_t return code.
1464*5d5fbe79SDavid van Moolenbroek  */
1465*5d5fbe79SDavid van Moolenbroek err_t
dns_gethostbyname(const char * hostname,ip_addr_t * addr,dns_found_callback found,void * callback_arg)1466*5d5fbe79SDavid van Moolenbroek dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
1467*5d5fbe79SDavid van Moolenbroek                   void *callback_arg)
1468*5d5fbe79SDavid van Moolenbroek {
1469*5d5fbe79SDavid van Moolenbroek   return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT);
1470*5d5fbe79SDavid van Moolenbroek }
1471*5d5fbe79SDavid van Moolenbroek 
1472*5d5fbe79SDavid van Moolenbroek /**
1473*5d5fbe79SDavid van Moolenbroek  * @ingroup dns
1474*5d5fbe79SDavid van Moolenbroek  * Like dns_gethostbyname, but returned address type can be controlled:
1475*5d5fbe79SDavid van Moolenbroek  * @param hostname the hostname that is to be queried
1476*5d5fbe79SDavid van Moolenbroek  * @param addr pointer to a ip_addr_t where to store the address if it is already
1477*5d5fbe79SDavid van Moolenbroek  *             cached in the dns_table (only valid if ERR_OK is returned!)
1478*5d5fbe79SDavid van Moolenbroek  * @param found a callback function to be called on success, failure or timeout (only if
1479*5d5fbe79SDavid van Moolenbroek  *              ERR_INPROGRESS is returned!)
1480*5d5fbe79SDavid van Moolenbroek  * @param callback_arg argument to pass to the callback function
1481*5d5fbe79SDavid van Moolenbroek  * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 first, try IPv6 if IPv4 fails only
1482*5d5fbe79SDavid van Moolenbroek  *                     - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 first, try IPv4 if IPv6 fails only
1483*5d5fbe79SDavid van Moolenbroek  *                     - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
1484*5d5fbe79SDavid van Moolenbroek  *                     - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
1485*5d5fbe79SDavid van Moolenbroek  */
1486*5d5fbe79SDavid van Moolenbroek err_t
dns_gethostbyname_addrtype(const char * hostname,ip_addr_t * addr,dns_found_callback found,void * callback_arg,u8_t dns_addrtype)1487*5d5fbe79SDavid van Moolenbroek dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found,
1488*5d5fbe79SDavid van Moolenbroek                            void *callback_arg, u8_t dns_addrtype)
1489*5d5fbe79SDavid van Moolenbroek {
1490*5d5fbe79SDavid van Moolenbroek   size_t hostnamelen;
1491*5d5fbe79SDavid van Moolenbroek #if LWIP_DNS_SUPPORT_MDNS_QUERIES
1492*5d5fbe79SDavid van Moolenbroek   u8_t is_mdns;
1493*5d5fbe79SDavid van Moolenbroek #endif
1494*5d5fbe79SDavid van Moolenbroek   /* not initialized or no valid server yet, or invalid addr pointer
1495*5d5fbe79SDavid van Moolenbroek    * or invalid hostname or invalid hostname length */
1496*5d5fbe79SDavid van Moolenbroek   if ((addr == NULL) ||
1497*5d5fbe79SDavid van Moolenbroek       (!hostname) || (!hostname[0])) {
1498*5d5fbe79SDavid van Moolenbroek     return ERR_ARG;
1499*5d5fbe79SDavid van Moolenbroek   }
1500*5d5fbe79SDavid van Moolenbroek #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
1501*5d5fbe79SDavid van Moolenbroek   if (dns_pcbs[0] == NULL) {
1502*5d5fbe79SDavid van Moolenbroek     return ERR_ARG;
1503*5d5fbe79SDavid van Moolenbroek   }
1504*5d5fbe79SDavid van Moolenbroek #endif
1505*5d5fbe79SDavid van Moolenbroek   hostnamelen = strlen(hostname);
1506*5d5fbe79SDavid van Moolenbroek   if (hostnamelen >= DNS_MAX_NAME_LENGTH) {
1507*5d5fbe79SDavid van Moolenbroek     LWIP_DEBUGF(DNS_DEBUG, ("dns_gethostbyname: name too long to resolve"));
1508*5d5fbe79SDavid van Moolenbroek     return ERR_ARG;
1509*5d5fbe79SDavid van Moolenbroek   }
1510*5d5fbe79SDavid van Moolenbroek 
1511*5d5fbe79SDavid van Moolenbroek 
1512*5d5fbe79SDavid van Moolenbroek #if LWIP_HAVE_LOOPIF
1513*5d5fbe79SDavid van Moolenbroek   if (strcmp(hostname, "localhost") == 0) {
1514*5d5fbe79SDavid van Moolenbroek     ip_addr_set_loopback(LWIP_DNS_ADDRTYPE_IS_IPV6(dns_addrtype), addr);
1515*5d5fbe79SDavid van Moolenbroek     return ERR_OK;
1516*5d5fbe79SDavid van Moolenbroek   }
1517*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_HAVE_LOOPIF */
1518*5d5fbe79SDavid van Moolenbroek 
1519*5d5fbe79SDavid van Moolenbroek   /* host name already in octet notation? set ip addr and return ERR_OK */
1520*5d5fbe79SDavid van Moolenbroek   if (ipaddr_aton(hostname, addr)) {
1521*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
1522*5d5fbe79SDavid van Moolenbroek     if ((IP_IS_V6(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV4)) ||
1523*5d5fbe79SDavid van Moolenbroek         (IP_IS_V4(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV6)))
1524*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
1525*5d5fbe79SDavid van Moolenbroek     {
1526*5d5fbe79SDavid van Moolenbroek       return ERR_OK;
1527*5d5fbe79SDavid van Moolenbroek     }
1528*5d5fbe79SDavid van Moolenbroek   }
1529*5d5fbe79SDavid van Moolenbroek   /* already have this address cached? */
1530*5d5fbe79SDavid van Moolenbroek   if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
1531*5d5fbe79SDavid van Moolenbroek     return ERR_OK;
1532*5d5fbe79SDavid van Moolenbroek   }
1533*5d5fbe79SDavid van Moolenbroek #if LWIP_IPV4 && LWIP_IPV6
1534*5d5fbe79SDavid van Moolenbroek   if ((dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
1535*5d5fbe79SDavid van Moolenbroek     /* fallback to 2nd IP type and try again to lookup */
1536*5d5fbe79SDavid van Moolenbroek     u8_t fallback;
1537*5d5fbe79SDavid van Moolenbroek     if (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
1538*5d5fbe79SDavid van Moolenbroek       fallback = LWIP_DNS_ADDRTYPE_IPV6;
1539*5d5fbe79SDavid van Moolenbroek     } else {
1540*5d5fbe79SDavid van Moolenbroek       fallback = LWIP_DNS_ADDRTYPE_IPV4;
1541*5d5fbe79SDavid van Moolenbroek     }
1542*5d5fbe79SDavid van Moolenbroek     if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(fallback)) == ERR_OK) {
1543*5d5fbe79SDavid van Moolenbroek       return ERR_OK;
1544*5d5fbe79SDavid van Moolenbroek     }
1545*5d5fbe79SDavid van Moolenbroek   }
1546*5d5fbe79SDavid van Moolenbroek #else /* LWIP_IPV4 && LWIP_IPV6 */
1547*5d5fbe79SDavid van Moolenbroek   LWIP_UNUSED_ARG(dns_addrtype);
1548*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_IPV4 && LWIP_IPV6 */
1549*5d5fbe79SDavid van Moolenbroek 
1550*5d5fbe79SDavid van Moolenbroek #if LWIP_DNS_SUPPORT_MDNS_QUERIES
1551*5d5fbe79SDavid van Moolenbroek   if (strstr(hostname, ".local") == &hostname[hostnamelen] - 6) {
1552*5d5fbe79SDavid van Moolenbroek     is_mdns = 1;
1553*5d5fbe79SDavid van Moolenbroek   } else {
1554*5d5fbe79SDavid van Moolenbroek     is_mdns = 0;
1555*5d5fbe79SDavid van Moolenbroek   }
1556*5d5fbe79SDavid van Moolenbroek 
1557*5d5fbe79SDavid van Moolenbroek   if (!is_mdns)
1558*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
1559*5d5fbe79SDavid van Moolenbroek   {
1560*5d5fbe79SDavid van Moolenbroek     /* prevent calling found callback if no server is set, return error instead */
1561*5d5fbe79SDavid van Moolenbroek     if (ip_addr_isany_val(dns_servers[0])) {
1562*5d5fbe79SDavid van Moolenbroek       return ERR_VAL;
1563*5d5fbe79SDavid van Moolenbroek     }
1564*5d5fbe79SDavid van Moolenbroek   }
1565*5d5fbe79SDavid van Moolenbroek 
1566*5d5fbe79SDavid van Moolenbroek   /* queue query with specified callback */
1567*5d5fbe79SDavid van Moolenbroek   return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)
1568*5d5fbe79SDavid van Moolenbroek      LWIP_DNS_ISMDNS_ARG(is_mdns));
1569*5d5fbe79SDavid van Moolenbroek }
1570*5d5fbe79SDavid van Moolenbroek 
1571*5d5fbe79SDavid van Moolenbroek #endif /* LWIP_DNS */
1572