1 /**
2 * \addtogroup apps
3 * @{
4 */
5
6 /**
7 * \defgroup resolv DNS resolver
8 * @{
9 *
10 * The uIP DNS resolver functions are used to lookup a hostname and
11 * map it to a numerical IP address. It maintains a list of resolved
12 * hostnames that can be queried with the resolv_lookup()
13 * function. New hostnames can be resolved using the resolv_query()
14 * function.
15 *
16 * When a hostname has been resolved (or found to be non-existant),
17 * the resolver code calls a callback function called resolv_found()
18 * that must be implemented by the module that uses the resolver.
19 */
20
21 /**
22 * \file
23 * DNS host name to IP address resolver.
24 * \author Adam Dunkels <[email protected]>
25 *
26 * This file implements a DNS host name to IP address resolver.
27 */
28
29 /*
30 * Copyright (c) 2002-2003, Adam Dunkels.
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. The name of the author may not be used to endorse or promote
42 * products derived from this software without specific prior
43 * written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
46 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
47 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
49 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
51 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
52 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
53 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
54 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
55 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 *
57 * This file is part of the uIP TCP/IP stack.
58 *
59 * $Id: resolv.c,v 1.5 2006/06/11 21:46:37 adam Exp $
60 *
61 */
62
63 #include "resolv.h"
64 #include "uip.h"
65
66 #include <string.h>
67
68 #ifndef NULL
69 #define NULL (void *)0
70 #endif /* NULL */
71
72 /** \internal The maximum number of retries when asking for a name. */
73 #define MAX_RETRIES 8
74
75 /** \internal The DNS message header. */
76 struct dns_hdr {
77 u16_t id;
78 u8_t flags1, flags2;
79 #define DNS_FLAG1_RESPONSE 0x80
80 #define DNS_FLAG1_OPCODE_STATUS 0x10
81 #define DNS_FLAG1_OPCODE_INVERSE 0x08
82 #define DNS_FLAG1_OPCODE_STANDARD 0x00
83 #define DNS_FLAG1_AUTHORATIVE 0x04
84 #define DNS_FLAG1_TRUNC 0x02
85 #define DNS_FLAG1_RD 0x01
86 #define DNS_FLAG2_RA 0x80
87 #define DNS_FLAG2_ERR_MASK 0x0f
88 #define DNS_FLAG2_ERR_NONE 0x00
89 #define DNS_FLAG2_ERR_NAME 0x03
90 u16_t numquestions;
91 u16_t numanswers;
92 u16_t numauthrr;
93 u16_t numextrarr;
94 };
95
96 /** \internal The DNS answer message structure. */
97 struct dns_answer {
98 /* DNS answer record starts with either a domain name or a pointer
99 to a name already present somewhere in the packet. */
100 u16_t type;
101 u16_t class;
102 u16_t ttl[2];
103 u16_t len;
104 uip_ipaddr_t ipaddr;
105 };
106
107 struct namemap {
108 #define STATE_UNUSED 0
109 #define STATE_NEW 1
110 #define STATE_ASKING 2
111 #define STATE_DONE 3
112 #define STATE_ERROR 4
113 u8_t state;
114 u8_t tmr;
115 u8_t retries;
116 u8_t seqno;
117 u8_t err;
118 char name[32];
119 uip_ipaddr_t ipaddr;
120 };
121
122 #ifndef UIP_CONF_RESOLV_ENTRIES
123 #define RESOLV_ENTRIES 4
124 #else /* UIP_CONF_RESOLV_ENTRIES */
125 #define RESOLV_ENTRIES UIP_CONF_RESOLV_ENTRIES
126 #endif /* UIP_CONF_RESOLV_ENTRIES */
127
128
129 static struct namemap names[RESOLV_ENTRIES];
130
131 static u8_t seqno;
132
133 static struct uip_udp_conn *resolv_conn = NULL;
134
135
136 /*---------------------------------------------------------------------------*/
137 /** \internal
138 * Walk through a compact encoded DNS name and return the end of it.
139 *
140 * \return The end of the name.
141 */
142 /*---------------------------------------------------------------------------*/
143 static unsigned char *
parse_name(unsigned char * query)144 parse_name(unsigned char *query)
145 {
146 unsigned char n;
147
148 do {
149 n = *query++;
150
151 while(n > 0) {
152 /* printf("%c", *query);*/
153 ++query;
154 --n;
155 };
156 /* printf(".");*/
157 } while(*query != 0);
158 /* printf("\n");*/
159 return query + 1;
160 }
161 /*---------------------------------------------------------------------------*/
162 /** \internal
163 * Runs through the list of names to see if there are any that have
164 * not yet been queried and, if so, sends out a query.
165 */
166 /*---------------------------------------------------------------------------*/
167 static void
check_entries(void)168 check_entries(void)
169 {
170 register struct dns_hdr *hdr;
171 char *query, *nptr, *nameptr;
172 static u8_t i;
173 static u8_t n;
174 register struct namemap *namemapptr;
175
176 for(i = 0; i < RESOLV_ENTRIES; ++i) {
177 namemapptr = &names[i];
178 if(namemapptr->state == STATE_NEW ||
179 namemapptr->state == STATE_ASKING) {
180 if(namemapptr->state == STATE_ASKING) {
181 if(--namemapptr->tmr == 0) {
182 if(++namemapptr->retries == MAX_RETRIES) {
183 namemapptr->state = STATE_ERROR;
184 resolv_found(namemapptr->name, NULL);
185 continue;
186 }
187 namemapptr->tmr = namemapptr->retries;
188 } else {
189 /* printf("Timer %d\n", namemapptr->tmr);*/
190 /* Its timer has not run out, so we move on to next
191 entry. */
192 continue;
193 }
194 } else {
195 namemapptr->state = STATE_ASKING;
196 namemapptr->tmr = 1;
197 namemapptr->retries = 0;
198 }
199 hdr = (struct dns_hdr *)uip_appdata;
200 memset(hdr, 0, sizeof(struct dns_hdr));
201 hdr->id = htons(i);
202 hdr->flags1 = DNS_FLAG1_RD;
203 hdr->numquestions = HTONS(1);
204 query = (char *)uip_appdata + 12;
205 nameptr = namemapptr->name;
206 --nameptr;
207 /* Convert hostname into suitable query format. */
208 do {
209 ++nameptr;
210 nptr = query;
211 ++query;
212 for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) {
213 *query = *nameptr;
214 ++query;
215 ++n;
216 }
217 *nptr = n;
218 } while(*nameptr != 0);
219 {
220 static unsigned char endquery[] =
221 {0,0,1,0,1};
222 memcpy(query, endquery, 5);
223 }
224 uip_udp_send((unsigned char)(query + 5 - (char *)uip_appdata));
225 break;
226 }
227 }
228 }
229 /*---------------------------------------------------------------------------*/
230 /** \internal
231 * Called when new UDP data arrives.
232 */
233 /*---------------------------------------------------------------------------*/
234 static void
newdata(void)235 newdata(void)
236 {
237 char *nameptr;
238 struct dns_answer *ans;
239 struct dns_hdr *hdr;
240 static u8_t nquestions, nanswers;
241 static u8_t i;
242 register struct namemap *namemapptr;
243
244 hdr = (struct dns_hdr *)uip_appdata;
245 /* printf("ID %d\n", htons(hdr->id));
246 printf("Query %d\n", hdr->flags1 & DNS_FLAG1_RESPONSE);
247 printf("Error %d\n", hdr->flags2 & DNS_FLAG2_ERR_MASK);
248 printf("Num questions %d, answers %d, authrr %d, extrarr %d\n",
249 htons(hdr->numquestions),
250 htons(hdr->numanswers),
251 htons(hdr->numauthrr),
252 htons(hdr->numextrarr));
253 */
254
255 /* The ID in the DNS header should be our entry into the name
256 table. */
257 i = htons(hdr->id);
258 namemapptr = &names[i];
259 if(i < RESOLV_ENTRIES &&
260 namemapptr->state == STATE_ASKING) {
261
262 /* This entry is now finished. */
263 namemapptr->state = STATE_DONE;
264 namemapptr->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
265
266 /* Check for error. If so, call callback to inform. */
267 if(namemapptr->err != 0) {
268 namemapptr->state = STATE_ERROR;
269 resolv_found(namemapptr->name, NULL);
270 return;
271 }
272
273 /* We only care about the question(s) and the answers. The authrr
274 and the extrarr are simply discarded. */
275 nquestions = htons(hdr->numquestions);
276 nanswers = htons(hdr->numanswers);
277
278 /* Skip the name in the question. XXX: This should really be
279 checked agains the name in the question, to be sure that they
280 match. */
281 nameptr = parse_name((char *)uip_appdata + 12) + 4;
282
283 while(nanswers > 0) {
284 /* The first byte in the answer resource record determines if it
285 is a compressed record or a normal one. */
286 if(*nameptr & 0xc0) {
287 /* Compressed name. */
288 nameptr +=2;
289 /* printf("Compressed anwser\n");*/
290 } else {
291 /* Not compressed name. */
292 nameptr = parse_name((char *)nameptr);
293 }
294
295 ans = (struct dns_answer *)nameptr;
296 /* printf("Answer: type %x, class %x, ttl %x, length %x\n",
297 htons(ans->type), htons(ans->class), (htons(ans->ttl[0])
298 << 16) | htons(ans->ttl[1]), htons(ans->len));*/
299
300 /* Check for IP address type and Internet class. Others are
301 discarded. */
302 if(ans->type == HTONS(1) &&
303 ans->class == HTONS(1) &&
304 ans->len == HTONS(4)) {
305 /* printf("IP address %d.%d.%d.%d\n",
306 htons(ans->ipaddr[0]) >> 8,
307 htons(ans->ipaddr[0]) & 0xff,
308 htons(ans->ipaddr[1]) >> 8,
309 htons(ans->ipaddr[1]) & 0xff);*/
310 /* XXX: we should really check that this IP address is the one
311 we want. */
312 namemapptr->ipaddr[0] = ans->ipaddr[0];
313 namemapptr->ipaddr[1] = ans->ipaddr[1];
314
315 resolv_found(namemapptr->name, namemapptr->ipaddr);
316 return;
317 } else {
318 nameptr = nameptr + 10 + htons(ans->len);
319 }
320 --nanswers;
321 }
322 }
323
324 }
325 /*---------------------------------------------------------------------------*/
326 /** \internal
327 * The main UDP function.
328 */
329 /*---------------------------------------------------------------------------*/
330 void
resolv_appcall(void)331 resolv_appcall(void)
332 {
333 if(uip_udp_conn->rport == HTONS(53)) {
334 if(uip_poll()) {
335 check_entries();
336 }
337 if(uip_newdata()) {
338 newdata();
339 }
340 }
341 }
342 /*---------------------------------------------------------------------------*/
343 /**
344 * Queues a name so that a question for the name will be sent out.
345 *
346 * \param name The hostname that is to be queried.
347 */
348 /*---------------------------------------------------------------------------*/
349 void
resolv_query(char * name)350 resolv_query(char *name)
351 {
352 static u8_t i;
353 static u8_t lseq, lseqi;
354 register struct namemap *nameptr;
355
356 lseq = lseqi = 0;
357
358 for(i = 0; i < RESOLV_ENTRIES; ++i) {
359 nameptr = &names[i];
360 if(nameptr->state == STATE_UNUSED) {
361 break;
362 }
363 if(seqno - nameptr->seqno > lseq) {
364 lseq = seqno - nameptr->seqno;
365 lseqi = i;
366 }
367 }
368
369 if(i == RESOLV_ENTRIES) {
370 i = lseqi;
371 nameptr = &names[i];
372 }
373
374 /* printf("Using entry %d\n", i);*/
375
376 strcpy(nameptr->name, name);
377 nameptr->state = STATE_NEW;
378 nameptr->seqno = seqno;
379 ++seqno;
380 }
381 /*---------------------------------------------------------------------------*/
382 /**
383 * Look up a hostname in the array of known hostnames.
384 *
385 * \note This function only looks in the internal array of known
386 * hostnames, it does not send out a query for the hostname if none
387 * was found. The function resolv_query() can be used to send a query
388 * for a hostname.
389 *
390 * \return A pointer to a 4-byte representation of the hostname's IP
391 * address, or NULL if the hostname was not found in the array of
392 * hostnames.
393 */
394 /*---------------------------------------------------------------------------*/
395 u16_t *
resolv_lookup(char * name)396 resolv_lookup(char *name)
397 {
398 static u8_t i;
399 struct namemap *nameptr;
400
401 /* Walk through the list to see if the name is in there. If it is
402 not, we return NULL. */
403 for(i = 0; i < RESOLV_ENTRIES; ++i) {
404 nameptr = &names[i];
405 if(nameptr->state == STATE_DONE &&
406 strcmp(name, nameptr->name) == 0) {
407 return nameptr->ipaddr;
408 }
409 }
410 return NULL;
411 }
412 /*---------------------------------------------------------------------------*/
413 /**
414 * Obtain the currently configured DNS server.
415 *
416 * \return A pointer to a 4-byte representation of the IP address of
417 * the currently configured DNS server or NULL if no DNS server has
418 * been configured.
419 */
420 /*---------------------------------------------------------------------------*/
421 u16_t *
resolv_getserver(void)422 resolv_getserver(void)
423 {
424 if(resolv_conn == NULL) {
425 return NULL;
426 }
427 return resolv_conn->ripaddr;
428 }
429 /*---------------------------------------------------------------------------*/
430 /**
431 * Configure which DNS server to use for queries.
432 *
433 * \param dnsserver A pointer to a 4-byte representation of the IP
434 * address of the DNS server to be configured.
435 */
436 /*---------------------------------------------------------------------------*/
437 void
resolv_conf(u16_t * dnsserver)438 resolv_conf(u16_t *dnsserver)
439 {
440 if(resolv_conn != NULL) {
441 uip_udp_remove(resolv_conn);
442 }
443
444 resolv_conn = uip_udp_new(dnsserver, HTONS(53));
445 }
446 /*---------------------------------------------------------------------------*/
447 /**
448 * Initalize the resolver.
449 */
450 /*---------------------------------------------------------------------------*/
451 void
resolv_init(void)452 resolv_init(void)
453 {
454 static u8_t i;
455
456 for(i = 0; i < RESOLV_ENTRIES; ++i) {
457 names[i].state = STATE_DONE;
458 }
459
460 }
461 /*---------------------------------------------------------------------------*/
462
463 /** @} */
464 /** @} */
465