blob: fc892b01968462e5c6241e1019da4178cf7f3ef4 [file] [log] [blame]
adamdunkels1e45c6d2003-09-02 21:47:27 +00001/**
2 * \file
3 * DNS host name to IP address resolver.
4 * \author Adam Dunkels <adam@dunkels.com>
5 *
6 * This file implements a DNS host name to IP address resolver. It
7 * maintains a list of resolved hostnames that can be queried with the
8 * resolv_lookup() function. New hostnames can be resolved using the
9 * resolv_query() function.
10 *
11 * The signal resolv_signal_found is emitted when a hostname has been
12 * resolved. The signal is emitted to all processes listening for the
13 * signal, and it is up to the receiving process to determine if the
14 * correct hostname has been found by calling the resolv_lookup()
15 * function with the hostname.
16 */
17
adamdunkelsca9ddcb2003-03-19 14:13:31 +000018/*
adamdunkels1e45c6d2003-09-02 21:47:27 +000019 * Copyright (c) 2002-2003, Adam Dunkels.
adamdunkelsca9ddcb2003-03-19 14:13:31 +000020 * All rights reserved.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 * 1. Redistributions of source code must retain the above copyright
26 * notice, this list of conditions and the following disclaimer.
27 * 2. Redistributions in binary form must reproduce the above copyright
28 * notice, this list of conditions and the following disclaimer in the
29 * documentation and/or other materials provided with the distribution.
adamdunkels1e45c6d2003-09-02 21:47:27 +000030 * 3. The name of the author may not be used to endorse or promote
adamdunkelsca9ddcb2003-03-19 14:13:31 +000031 * products derived from this software without specific prior
32 * written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
35 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
36 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
38 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
40 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
41 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
42 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
43 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
44 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 *
46 * This file is part of the uIP TCP/IP stack.
47 *
adamdunkels1e45c6d2003-09-02 21:47:27 +000048 * $Id: resolv.c,v 1.7 2003/09/02 21:47:28 adamdunkels Exp $
adamdunkelsca9ddcb2003-03-19 14:13:31 +000049 *
50 */
51
52#include "resolv.h"
53#include "dispatcher.h"
54
55#ifndef NULL
56#define NULL (void *)0
57#endif /* NULL */
58
adamdunkels1e45c6d2003-09-02 21:47:27 +000059/** \internal The maximum number of retries when asking for a name. */
adamdunkelsca9ddcb2003-03-19 14:13:31 +000060#define MAX_RETRIES 8
61
adamdunkels1e45c6d2003-09-02 21:47:27 +000062/** \internal The DNS message header. */
adamdunkelsca9ddcb2003-03-19 14:13:31 +000063struct dns_hdr {
64 u16_t id;
65 u8_t flags1, flags2;
66#define DNS_FLAG1_RESPONSE 0x80
67#define DNS_FLAG1_OPCODE_STATUS 0x10
68#define DNS_FLAG1_OPCODE_INVERSE 0x08
69#define DNS_FLAG1_OPCODE_STANDARD 0x00
70#define DNS_FLAG1_AUTHORATIVE 0x04
71#define DNS_FLAG1_TRUNC 0x02
72#define DNS_FLAG1_RD 0x01
73#define DNS_FLAG2_RA 0x80
74#define DNS_FLAG2_ERR_MASK 0x0f
75#define DNS_FLAG2_ERR_NONE 0x00
76#define DNS_FLAG2_ERR_NAME 0x03
77 u16_t numquestions;
78 u16_t numanswers;
79 u16_t numauthrr;
80 u16_t numextrarr;
81};
82
adamdunkels1e45c6d2003-09-02 21:47:27 +000083/** \internal The DNS answer message structure. */
adamdunkelsca9ddcb2003-03-19 14:13:31 +000084struct dns_answer {
85 /* DNS answer record starts with either a domain name or a pointer
86 to a name already present somewhere in the packet. */
87 u16_t type;
88 u16_t class;
89 u16_t ttl[2];
90 u16_t len;
91 u16_t ipaddr[2];
92};
93
adamdunkels1e45c6d2003-09-02 21:47:27 +000094struct namemap {
adamdunkelsca9ddcb2003-03-19 14:13:31 +000095#define STATE_UNUSED 0
96#define STATE_NEW 1
97#define STATE_ASKING 2
98#define STATE_DONE 3
99#define STATE_ERROR 4
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000100 u8_t state;
101 u8_t tmr;
102 u8_t retries;
103 u8_t seqno;
104 u8_t err;
adamdunkels1d31c322003-08-24 22:40:32 +0000105 char name[32];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000106 u16_t ipaddr[2];
107};
108
adamdunkels99dcf452003-08-15 18:50:36 +0000109#define RESOLV_ENTRIES 4
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000110
111static struct namemap names[RESOLV_ENTRIES];
112
113static u8_t seqno;
114
115static struct uip_udp_conn *resolv_conn = NULL;
116
adamdunkels1e45c6d2003-09-02 21:47:27 +0000117/**
118 * Signal that is sent when a name has been resolved.
119 */
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000120ek_signal_t resolv_signal_found = EK_SIGNAL_NONE;
121
122/*-----------------------------------------------------------------------------------*/
adamdunkels1e45c6d2003-09-02 21:47:27 +0000123/** \internal
124 * Walk through a compact encoded DNS name and return the end of it.
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000125 *
adamdunkels1e45c6d2003-09-02 21:47:27 +0000126 * \return The end of the name.
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000127 */
adamdunkels1e45c6d2003-09-02 21:47:27 +0000128/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000129static unsigned char *
130parse_name(unsigned char *query)
131{
132 unsigned char n;
133
134 do {
135 n = *query++;
136
137 while(n > 0) {
138 /* printf("%c", *query);*/
139 ++query;
140 --n;
141 };
142 /* printf(".");*/
143 } while(*query != 0);
144 /* printf("\n");*/
145 return query + 1;
146}
147/*-----------------------------------------------------------------------------------*/
adamdunkels1e45c6d2003-09-02 21:47:27 +0000148/** \internal
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000149 * Runs through the list of names to see if there are any that have
adamdunkels1e45c6d2003-09-02 21:47:27 +0000150 * not yet been queried and, if so, sends out a query.
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000151 */
adamdunkels1e45c6d2003-09-02 21:47:27 +0000152/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000153static void
154check_entries(void)
155{
adamdunkels23664022003-08-05 13:51:50 +0000156 register struct dns_hdr *hdr;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000157 char *query, *nptr, *nameptr;
adamdunkels99dcf452003-08-15 18:50:36 +0000158 static u8_t i;
159 static u8_t n;
adamdunkels23664022003-08-05 13:51:50 +0000160 register struct namemap *namemapptr;
161
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000162 for(i = 0; i < RESOLV_ENTRIES; ++i) {
adamdunkels23664022003-08-05 13:51:50 +0000163 namemapptr = &names[i];
164 if(namemapptr->state == STATE_NEW ||
165 namemapptr->state == STATE_ASKING) {
166 if(namemapptr->state == STATE_ASKING) {
167 if(--namemapptr->tmr == 0) {
168 if(++namemapptr->retries == MAX_RETRIES) {
169 namemapptr->state = STATE_ERROR;
170 resolv_found(namemapptr->name, NULL);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000171 continue;
172 }
adamdunkels23664022003-08-05 13:51:50 +0000173 namemapptr->tmr = namemapptr->retries;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000174 } else {
adamdunkels23664022003-08-05 13:51:50 +0000175 /* printf("Timer %d\n", namemapptr->tmr);*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000176 /* Its timer has not run out, so we move on to next
177 entry. */
178 continue;
179 }
180 } else {
adamdunkels23664022003-08-05 13:51:50 +0000181 namemapptr->state = STATE_ASKING;
182 namemapptr->tmr = 1;
183 namemapptr->retries = 0;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000184 }
185 hdr = (struct dns_hdr *)uip_appdata;
adamdunkels99dcf452003-08-15 18:50:36 +0000186 memset(hdr, 0, sizeof(struct dns_hdr));
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000187 hdr->id = htons(i);
188 hdr->flags1 = DNS_FLAG1_RD;
adamdunkels47ec7fa2003-03-28 12:11:17 +0000189 hdr->numquestions = HTONS(1);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000190 query = (char *)uip_appdata + 12;
adamdunkels23664022003-08-05 13:51:50 +0000191 nameptr = namemapptr->name;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000192 --nameptr;
193 /* Convert hostname into suitable query format. */
194 do {
195 ++nameptr;
196 nptr = query;
197 ++query;
198 for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) {
199 *query = *nameptr;
200 ++query;
201 ++n;
202 }
203 *nptr = n;
204 } while(*nameptr != 0);
adamdunkels23664022003-08-05 13:51:50 +0000205 {
206 static unsigned char endquery[] =
207 {0,0,1,0,1};
208 memcpy(query, endquery, 5);
209 }
adamdunkels23664022003-08-05 13:51:50 +0000210 uip_udp_send((unsigned char)(query + 5 - (char *)uip_appdata));
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000211 break;
212 }
213 }
214}
215/*-----------------------------------------------------------------------------------*/
adamdunkels1e45c6d2003-09-02 21:47:27 +0000216/** \internal
217 * Called when new UDP data arrives.
218 */
219/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000220static void
221newdata(void)
222{
223 char *nameptr;
224 struct dns_answer *ans;
225 struct dns_hdr *hdr;
adamdunkels99dcf452003-08-15 18:50:36 +0000226 static u8_t nquestions, nanswers;
227 static u8_t i;
adamdunkels23664022003-08-05 13:51:50 +0000228 register struct namemap *namemapptr;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000229
230 hdr = (struct dns_hdr *)uip_appdata;
231 /* printf("ID %d\n", htons(hdr->id));
232 printf("Query %d\n", hdr->flags1 & DNS_FLAG1_RESPONSE);
233 printf("Error %d\n", hdr->flags2 & DNS_FLAG2_ERR_MASK);
234 printf("Num questions %d, answers %d, authrr %d, extrarr %d\n",
235 htons(hdr->numquestions),
236 htons(hdr->numanswers),
237 htons(hdr->numauthrr),
238 htons(hdr->numextrarr));
239 */
240
241 /* The ID in the DNS header should be our entry into the name
242 table. */
adamdunkels23664022003-08-05 13:51:50 +0000243 i = htons(hdr->id);
244 namemapptr = &names[i];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000245 if(i < RESOLV_ENTRIES &&
adamdunkels23664022003-08-05 13:51:50 +0000246 namemapptr->state == STATE_ASKING) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000247
248 /* This entry is now finished. */
adamdunkels23664022003-08-05 13:51:50 +0000249 namemapptr->state = STATE_DONE;
250 namemapptr->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000251
252 /* Check for error. If so, call callback to inform. */
adamdunkels23664022003-08-05 13:51:50 +0000253 if(namemapptr->err != 0) {
254 namemapptr->state = STATE_ERROR;
255 resolv_found(namemapptr->name, NULL);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000256 return;
257 }
258
259 /* We only care about the question(s) and the answers. The authrr
260 and the extrarr are simply discarded. */
261 nquestions = htons(hdr->numquestions);
262 nanswers = htons(hdr->numanswers);
263
264 /* Skip the name in the question. XXX: This should really be
265 checked agains the name in the question, to be sure that they
266 match. */
267 nameptr = parse_name((char *)uip_appdata + 12) + 4;
268
269 while(nanswers > 0) {
270 /* The first byte in the answer resource record determines if it
271 is a compressed record or a normal one. */
272 if(*nameptr & 0xc0) {
273 /* Compressed name. */
274 nameptr +=2;
275 /* printf("Compressed anwser\n");*/
276 } else {
277 /* Not compressed name. */
278 nameptr = parse_name((char *)nameptr);
279 }
280
281 ans = (struct dns_answer *)nameptr;
282 /* printf("Answer: type %x, class %x, ttl %x, length %x\n",
adamdunkels99dcf452003-08-15 18:50:36 +0000283 htons(ans->type), htons(ans->class), (htons(ans->ttl[0])
284 << 16) | htons(ans->ttl[1]), htons(ans->len));*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000285
286 /* Check for IP address type and Internet class. Others are
287 discarded. */
adamdunkels47ec7fa2003-03-28 12:11:17 +0000288 if(ans->type == HTONS(1) &&
289 ans->class == HTONS(1) &&
290 ans->len == HTONS(4)) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000291 /* printf("IP address %d.%d.%d.%d\n",
292 htons(ans->ipaddr[0]) >> 8,
293 htons(ans->ipaddr[0]) & 0xff,
294 htons(ans->ipaddr[1]) >> 8,
295 htons(ans->ipaddr[1]) & 0xff);*/
296 /* XXX: we should really check that this IP address is the one
297 we want. */
adamdunkels23664022003-08-05 13:51:50 +0000298 namemapptr->ipaddr[0] = ans->ipaddr[0];
299 namemapptr->ipaddr[1] = ans->ipaddr[1];
adamdunkels99dcf452003-08-15 18:50:36 +0000300
adamdunkels23664022003-08-05 13:51:50 +0000301 resolv_found(namemapptr->name, namemapptr->ipaddr);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000302 return;
303 } else {
304 nameptr = nameptr + 10 + htons(ans->len);
305 }
306 --nanswers;
307 }
308 }
309
310}
311/*-----------------------------------------------------------------------------------*/
adamdunkels1e45c6d2003-09-02 21:47:27 +0000312/** \internal
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000313 * The main UDP function.
314 */
adamdunkels1e45c6d2003-09-02 21:47:27 +0000315/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000316void
317udp_appcall(void)
318{
adamdunkels47ec7fa2003-03-28 12:11:17 +0000319 if(uip_udp_conn->rport == HTONS(53)) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000320 if(uip_poll()) {
321 check_entries();
322 }
323 if(uip_newdata()) {
324 newdata();
325 }
326 }
327}
328/*-----------------------------------------------------------------------------------*/
adamdunkels1e45c6d2003-09-02 21:47:27 +0000329/**
330 * Queues a name so that a question for the name will be sent out.
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000331 *
adamdunkels1e45c6d2003-09-02 21:47:27 +0000332 * \param name The hostname that is to be queried.
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000333 */
adamdunkels1e45c6d2003-09-02 21:47:27 +0000334/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000335void
336resolv_query(char *name)
337{
adamdunkels99dcf452003-08-15 18:50:36 +0000338 static u8_t i;
339 static u8_t lseq, lseqi;
adamdunkels23664022003-08-05 13:51:50 +0000340 register struct namemap *nameptr;
341
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000342 lseq = lseqi = 0;
343
344 for(i = 0; i < RESOLV_ENTRIES; ++i) {
adamdunkels23664022003-08-05 13:51:50 +0000345 nameptr = &names[i];
346 if(nameptr->state == STATE_UNUSED) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000347 break;
348 }
adamdunkels23664022003-08-05 13:51:50 +0000349 if(seqno - nameptr->seqno > lseq) {
350 lseq = seqno - nameptr->seqno;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000351 lseqi = i;
352 }
353 }
354
355 if(i == RESOLV_ENTRIES) {
356 i = lseqi;
adamdunkels23664022003-08-05 13:51:50 +0000357 nameptr = &names[i];
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000358 }
359
360 /* printf("Using entry %d\n", i);*/
361
adamdunkels23664022003-08-05 13:51:50 +0000362 strcpy(nameptr->name, name);
363 nameptr->state = STATE_NEW;
364 nameptr->seqno = seqno;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000365 ++seqno;
366
adamdunkels1e45c6d2003-09-02 21:47:27 +0000367 if(resolv_conn != NULL) {
368 dispatcher_emit(uip_signal_poll_udp, resolv_conn);
369 }
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000370}
371/*-----------------------------------------------------------------------------------*/
adamdunkels1e45c6d2003-09-02 21:47:27 +0000372/**
373 * Look up a hostname in the array of known hostnames.
374 *
375 * \note This function only looks in the internal array of known
376 * hostnames, it does not send out a query for the hostname if none
377 * was found. The function resolv_query() can be used to send a query
378 * for a hostname.
379 *
380 * \return A pointer to a 4-byte representation of the hostname's IP
381 * address, or NULL if the hostname was not found in the array of
382 * hostnames.
383 */
384/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000385u16_t *
386resolv_lookup(char *name)
387{
adamdunkels99dcf452003-08-15 18:50:36 +0000388 static u8_t i;
adamdunkels23664022003-08-05 13:51:50 +0000389 struct namemap *nameptr;
390
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000391 /* Walk through the list to see if the name is in there. If it is
392 not, we return NULL. */
393 for(i = 0; i < RESOLV_ENTRIES; ++i) {
adamdunkels23664022003-08-05 13:51:50 +0000394 nameptr = &names[i];
395 if(nameptr->state == STATE_DONE &&
adamdunkels99dcf452003-08-15 18:50:36 +0000396 strcmp(name, nameptr->name) == 0) {
adamdunkels23664022003-08-05 13:51:50 +0000397 return nameptr->ipaddr;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000398 }
399 }
400 return NULL;
401}
402/*-----------------------------------------------------------------------------------*/
adamdunkels1e45c6d2003-09-02 21:47:27 +0000403/**
404 * Obtain the currently configured DNS server.
405 *
406 * \return A pointer to a 4-byte representation of the IP address of
407 * the currently configured DNS server or NULL if no DNS server has
408 * been configured.
409 */
410/*-----------------------------------------------------------------------------------*/
adamdunkels66c6af62003-04-16 18:28:16 +0000411u16_t *
412resolv_getserver(void)
413{
414 if(resolv_conn == NULL) {
415 return NULL;
416 }
417 return resolv_conn->ripaddr;
418}
419/*-----------------------------------------------------------------------------------*/
adamdunkels1e45c6d2003-09-02 21:47:27 +0000420/**
421 * Configure a DNS server.
422 *
423 * \param dnsserver A pointer to a 4-byte representation of the IP
424 * address of the DNS server to be configured.
425 */
426/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000427void
428resolv_conf(u16_t *dnsserver)
429{
430 if(resolv_conn != NULL) {
431 uip_udp_remove(resolv_conn);
432 }
433
434 resolv_conn = uip_udp_new(dnsserver, 53);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000435}
436/*-----------------------------------------------------------------------------------*/
adamdunkels1e45c6d2003-09-02 21:47:27 +0000437/**
438 * Initalize the resolver.
439 */
440/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000441void
442resolv_init(void)
443{
adamdunkels99dcf452003-08-15 18:50:36 +0000444 static u8_t i;
445
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000446 for(i = 0; i < RESOLV_ENTRIES; ++i) {
447 names[i].state = STATE_DONE;
448 }
449
450 resolv_signal_found = dispatcher_sigalloc();
451}
452/*-----------------------------------------------------------------------------------*/
adamdunkels1e45c6d2003-09-02 21:47:27 +0000453/** \internal
454 * Callback function which is called when a hostname is found.
455 *
456 */
457/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000458void
459resolv_found(char *name, u16_t *ipaddr)
460{
461 dispatcher_emit(resolv_signal_found, name, DISPATCHER_BROADCAST);
462}
adamdunkels66c6af62003-04-16 18:28:16 +0000463/*-----------------------------------------------------------------------------------*/