blob: 5dc58f596a5f2e5421aa5c85e45902b3a0428225 [file] [log] [blame]
adamdunkelsca9ddcb2003-03-19 14:13:31 +00001/*
2 * Copyright (c) 2002, Adam Dunkels.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Adam Dunkels.
16 * 4. The name of the author may not be used to endorse or promote
17 * products derived from this software without specific prior
18 * written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
21 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
26 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * This file is part of the uIP TCP/IP stack.
33 *
adamdunkels66c6af62003-04-16 18:28:16 +000034 * $Id: resolv.c,v 1.3 2003/04/16 18:28:16 adamdunkels Exp $
adamdunkelsca9ddcb2003-03-19 14:13:31 +000035 *
36 */
37
38#include "resolv.h"
39#include "dispatcher.h"
40
41#ifndef NULL
42#define NULL (void *)0
43#endif /* NULL */
44
45#define MAX_RETRIES 8
46
47struct dns_hdr {
48 u16_t id;
49 u8_t flags1, flags2;
50#define DNS_FLAG1_RESPONSE 0x80
51#define DNS_FLAG1_OPCODE_STATUS 0x10
52#define DNS_FLAG1_OPCODE_INVERSE 0x08
53#define DNS_FLAG1_OPCODE_STANDARD 0x00
54#define DNS_FLAG1_AUTHORATIVE 0x04
55#define DNS_FLAG1_TRUNC 0x02
56#define DNS_FLAG1_RD 0x01
57#define DNS_FLAG2_RA 0x80
58#define DNS_FLAG2_ERR_MASK 0x0f
59#define DNS_FLAG2_ERR_NONE 0x00
60#define DNS_FLAG2_ERR_NAME 0x03
61 u16_t numquestions;
62 u16_t numanswers;
63 u16_t numauthrr;
64 u16_t numextrarr;
65};
66
67struct dns_answer {
68 /* DNS answer record starts with either a domain name or a pointer
69 to a name already present somewhere in the packet. */
70 u16_t type;
71 u16_t class;
72 u16_t ttl[2];
73 u16_t len;
74 u16_t ipaddr[2];
75};
76
77
78#define STATE_UNUSED 0
79#define STATE_NEW 1
80#define STATE_ASKING 2
81#define STATE_DONE 3
82#define STATE_ERROR 4
83struct namemap {
84 u8_t state;
85 u8_t tmr;
86 u8_t retries;
87 u8_t seqno;
88 u8_t err;
89 char name[64];
90 u16_t ipaddr[2];
91};
92
93#define RESOLV_ENTRIES 8
94
95static struct namemap names[RESOLV_ENTRIES];
96
97static u8_t seqno;
98
99static struct uip_udp_conn *resolv_conn = NULL;
100
101ek_signal_t resolv_signal_found = EK_SIGNAL_NONE;
102
103/*-----------------------------------------------------------------------------------*/
104/* parse_name(name):
105 *
106 * Returns the end of the name.
107 */
108static unsigned char *
109parse_name(unsigned char *query)
110{
111 unsigned char n;
112
113 do {
114 n = *query++;
115
116 while(n > 0) {
117 /* printf("%c", *query);*/
118 ++query;
119 --n;
120 };
121 /* printf(".");*/
122 } while(*query != 0);
123 /* printf("\n");*/
124 return query + 1;
125}
126/*-----------------------------------------------------------------------------------*/
127/* check_entries(void):
128 *
129 * Runs through the list of names to see if there are any that have
130 * not been queried yet. If so, a query is sent out.
131 */
132static void
133check_entries(void)
134{
135 struct dns_hdr *hdr;
136 char *query, *nptr, *nameptr;
137 u8_t i;
138 u8_t n;
139
140 for(i = 0; i < RESOLV_ENTRIES; ++i) {
141
142 if(names[i].state == STATE_NEW ||
143 names[i].state == STATE_ASKING) {
144 if(names[i].state == STATE_ASKING) {
145 --names[i].tmr;
146 if(names[i].tmr == 0) {
147 ++names[i].retries;
148 if(names[i].retries == MAX_RETRIES) {
149 names[i].state = STATE_ERROR;
150 resolv_found(names[i].name, NULL);
151 continue;
152 }
153 names[i].tmr = names[i].retries;
154 } else {
155 /* printf("Timer %d\n", names[i].tmr);*/
156 /* Its timer has not run out, so we move on to next
157 entry. */
158 continue;
159 }
160 } else {
161 names[i].state = STATE_ASKING;
162 names[i].tmr = 1;
163 names[i].retries = 0;
164 }
165 hdr = (struct dns_hdr *)uip_appdata;
166 hdr->id = htons(i);
167 hdr->flags1 = DNS_FLAG1_RD;
168 hdr->flags2 = 0;
adamdunkels47ec7fa2003-03-28 12:11:17 +0000169 hdr->numquestions = HTONS(1);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000170 hdr->numanswers = hdr->numauthrr = hdr->numextrarr = 0;
171 query = (char *)uip_appdata + 12;
172 nameptr = names[i].name;
173 --nameptr;
174 /* Convert hostname into suitable query format. */
175 do {
176 ++nameptr;
177 nptr = query;
178 ++query;
179 for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) {
180 *query = *nameptr;
181 ++query;
182 ++n;
183 }
184 *nptr = n;
185 } while(*nameptr != 0);
186 nptr = query;
187 *nptr = 0; /* End of query name. */
188 ++nptr;
189 *nptr = 0; /* High byte of query type. */
190 ++nptr;
191 *nptr = 1; /* Low byte of query type. 1 == IP address query. */
192 ++nptr;
193 *nptr = 0; /* High byte of query class. */
194 ++nptr;
195 *nptr = 1; /* Low byte of query class. */
196 ++nptr;
197 uip_udp_send((unsigned char)(nptr - (char *)uip_appdata));
198 break;
199 }
200 }
201}
202/*-----------------------------------------------------------------------------------*/
203static void
204newdata(void)
205{
206 char *nameptr;
207 struct dns_answer *ans;
208 struct dns_hdr *hdr;
209 u8_t nquestions, nanswers;
210 u8_t i;
211
212 hdr = (struct dns_hdr *)uip_appdata;
213 /* printf("ID %d\n", htons(hdr->id));
214 printf("Query %d\n", hdr->flags1 & DNS_FLAG1_RESPONSE);
215 printf("Error %d\n", hdr->flags2 & DNS_FLAG2_ERR_MASK);
216 printf("Num questions %d, answers %d, authrr %d, extrarr %d\n",
217 htons(hdr->numquestions),
218 htons(hdr->numanswers),
219 htons(hdr->numauthrr),
220 htons(hdr->numextrarr));
221 */
222
223 /* The ID in the DNS header should be our entry into the name
224 table. */
225 i = htons(hdr->id);
226 if(i < RESOLV_ENTRIES &&
227 names[i].state == STATE_ASKING) {
228
229 /* This entry is now finished. */
230 names[i].state = STATE_DONE;
231 names[i].err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
232
233 /* Check for error. If so, call callback to inform. */
234 if(names[i].err != 0) {
235 names[i].state = STATE_ERROR;
236 resolv_found(names[i].name, NULL);
237 return;
238 }
239
240 /* We only care about the question(s) and the answers. The authrr
241 and the extrarr are simply discarded. */
242 nquestions = htons(hdr->numquestions);
243 nanswers = htons(hdr->numanswers);
244
245 /* Skip the name in the question. XXX: This should really be
246 checked agains the name in the question, to be sure that they
247 match. */
248 nameptr = parse_name((char *)uip_appdata + 12) + 4;
249
250 while(nanswers > 0) {
251 /* The first byte in the answer resource record determines if it
252 is a compressed record or a normal one. */
253 if(*nameptr & 0xc0) {
254 /* Compressed name. */
255 nameptr +=2;
256 /* printf("Compressed anwser\n");*/
257 } else {
258 /* Not compressed name. */
259 nameptr = parse_name((char *)nameptr);
260 }
261
262 ans = (struct dns_answer *)nameptr;
263 /* printf("Answer: type %x, class %x, ttl %x, length %x\n",
264 htons(ans->type), htons(ans->class),
265 (htons(ans->ttl[0]) << 16) | htons(ans->ttl[1]),
266 htons(ans->len));*/
267
268 /* Check for IP address type and Internet class. Others are
269 discarded. */
adamdunkels47ec7fa2003-03-28 12:11:17 +0000270 if(ans->type == HTONS(1) &&
271 ans->class == HTONS(1) &&
272 ans->len == HTONS(4)) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000273 /* printf("IP address %d.%d.%d.%d\n",
274 htons(ans->ipaddr[0]) >> 8,
275 htons(ans->ipaddr[0]) & 0xff,
276 htons(ans->ipaddr[1]) >> 8,
277 htons(ans->ipaddr[1]) & 0xff);*/
278 /* XXX: we should really check that this IP address is the one
279 we want. */
280 names[i].ipaddr[0] = ans->ipaddr[0];
281 names[i].ipaddr[1] = ans->ipaddr[1];
282 resolv_found(names[i].name, names[i].ipaddr);
283 return;
284 } else {
285 nameptr = nameptr + 10 + htons(ans->len);
286 }
287 --nanswers;
288 }
289 }
290
291}
292/*-----------------------------------------------------------------------------------*/
293/* udp_appcall():
294 *
295 * The main UDP function.
296 */
297void
298udp_appcall(void)
299{
adamdunkels47ec7fa2003-03-28 12:11:17 +0000300 if(uip_udp_conn->rport == HTONS(53)) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000301 if(uip_poll()) {
302 check_entries();
303 }
304 if(uip_newdata()) {
305 newdata();
306 }
307 }
308}
309/*-----------------------------------------------------------------------------------*/
310/* resolv_query(name):
311 *
312 * Queues a name so that a question for the name will be sent out the
313 * next time the udp_appcall is polled.
314 */
315void
316resolv_query(char *name)
317{
318 u8_t i;
319 u8_t lseq, lseqi;
320
321 lseq = lseqi = 0;
322
323 for(i = 0; i < RESOLV_ENTRIES; ++i) {
324 if(names[i].state == STATE_UNUSED) {
325 break;
326 }
327 if(seqno - names[i].seqno > lseq) {
328 lseq = seqno - names[i].seqno;
329 lseqi = i;
330 }
331 }
332
333 if(i == RESOLV_ENTRIES) {
334 i = lseqi;
335 }
336
337 /* printf("Using entry %d\n", i);*/
338
339 strcpy(names[i].name, name);
340 names[i].state = STATE_NEW;
341 names[i].seqno = seqno;
342 ++seqno;
343
344}
345/*-----------------------------------------------------------------------------------*/
346u16_t *
347resolv_lookup(char *name)
348{
349 u8_t i;
350
351 /* Walk through the list to see if the name is in there. If it is
352 not, we return NULL. */
353 for(i = 0; i < RESOLV_ENTRIES; ++i) {
354 if(names[i].state == STATE_DONE &&
355 strcmp(name, names[i].name) == 0) {
356 return names[i].ipaddr;
357 }
358 }
359 return NULL;
360}
361/*-----------------------------------------------------------------------------------*/
adamdunkels66c6af62003-04-16 18:28:16 +0000362u16_t *
363resolv_getserver(void)
364{
365 if(resolv_conn == NULL) {
366 return NULL;
367 }
368 return resolv_conn->ripaddr;
369}
370/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000371void
372resolv_conf(u16_t *dnsserver)
373{
374 if(resolv_conn != NULL) {
375 uip_udp_remove(resolv_conn);
376 }
377
378 resolv_conn = uip_udp_new(dnsserver, 53);
379
380}
381/*-----------------------------------------------------------------------------------*/
382void
383resolv_init(void)
384{
385 u8_t i;
386
387 for(i = 0; i < RESOLV_ENTRIES; ++i) {
388 names[i].state = STATE_DONE;
389 }
390
391 resolv_signal_found = dispatcher_sigalloc();
392}
393/*-----------------------------------------------------------------------------------*/
394void
395resolv_found(char *name, u16_t *ipaddr)
396{
397 dispatcher_emit(resolv_signal_found, name, DISPATCHER_BROADCAST);
398}
adamdunkels66c6af62003-04-16 18:28:16 +0000399/*-----------------------------------------------------------------------------------*/