7 * \defgroup resolv DNS resolver
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()
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.
23 * DNS host name to IP address resolver.
24 * \author Adam Dunkels <adam@dunkels.com>
26 * This file implements a DNS host name to IP address resolver.
30 * Copyright (c) 2002-2003, Adam Dunkels.
31 * All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
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
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.
57 * This file is part of the uIP TCP/IP stack.
59 * $Id: resolv.c,v 1.1 2008/08/06 23:21:19 andrewlxia Exp $
63 #include <uip/resolv.h>
69 #define NULL (void *)0
72 /** \internal The maximum number of retries when asking for a name. */
75 /** \internal The DNS message header. */
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
96 /** \internal The DNS answer message structure. */
98 /* DNS answer record starts with either a domain name or a pointer
99 to a name already present somewhere in the packet. */
108 #define STATE_UNUSED 0
110 #define STATE_ASKING 2
112 #define STATE_ERROR 4
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 */
129 static struct namemap names[RESOLV_ENTRIES];
133 static struct uip_udp_conn *resolv_conn = NULL;
136 /*---------------------------------------------------------------------------*/
138 * Walk through a compact encoded DNS name and return the end of it.
140 * \return The end of the name.
142 /*---------------------------------------------------------------------------*/
143 static unsigned char *
144 parse_name(unsigned char *query)
152 /* printf("%c", *query);*/
157 } while(*query != 0);
161 /*---------------------------------------------------------------------------*/
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.
166 /*---------------------------------------------------------------------------*/
170 register struct dns_hdr *hdr;
171 char *query, *nptr, *nameptr;
174 register struct namemap *namemapptr;
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);
187 namemapptr->tmr = namemapptr->retries;
189 /* printf("Timer %d\n", namemapptr->tmr);*/
190 /* Its timer has not run out, so we move on to next
195 namemapptr->state = STATE_ASKING;
197 namemapptr->retries = 0;
199 hdr = (struct dns_hdr *)uip_appdata;
200 memset(hdr, 0, sizeof(struct dns_hdr));
202 hdr->flags1 = DNS_FLAG1_RD;
203 hdr->numquestions = HTONS(1);
204 query = (char *)uip_appdata + 12;
205 nameptr = namemapptr->name;
207 /* Convert hostname into suitable query format. */
212 for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) {
218 } while(*nameptr != 0);
220 static unsigned char endquery[] =
222 memcpy(query, endquery, 5);
224 uip_udp_send((unsigned char)(query + 5 - (char *)uip_appdata));
229 /*---------------------------------------------------------------------------*/
231 * Called when new UDP data arrives.
233 /*---------------------------------------------------------------------------*/
238 struct dns_answer *ans;
240 static u8_t nquestions, nanswers;
242 register struct namemap *namemapptr;
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));
255 /* The ID in the DNS header should be our entry into the name
258 namemapptr = &names[i];
259 if(i < RESOLV_ENTRIES &&
260 namemapptr->state == STATE_ASKING) {
262 /* This entry is now finished. */
263 namemapptr->state = STATE_DONE;
264 namemapptr->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
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);
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);
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
281 nameptr = parse_name((char *)uip_appdata + 12) + 4;
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. */
289 /* printf("Compressed anwser\n");*/
291 /* Not compressed name. */
292 nameptr = parse_name((char *)nameptr);
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));*/
300 /* Check for IP address type and Internet class. Others are
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
312 namemapptr->ipaddr[0] = ans->ipaddr[0];
313 namemapptr->ipaddr[1] = ans->ipaddr[1];
315 resolv_found(namemapptr->name, namemapptr->ipaddr);
318 nameptr = nameptr + 10 + htons(ans->len);
325 /*---------------------------------------------------------------------------*/
327 * The main UDP function.
329 /*---------------------------------------------------------------------------*/
333 if(uip_udp_conn->rport == HTONS(53)) {
342 /*---------------------------------------------------------------------------*/
344 * Queues a name so that a question for the name will be sent out.
346 * \param name The hostname that is to be queried.
348 /*---------------------------------------------------------------------------*/
350 resolv_query(char *name)
353 static u8_t lseq, lseqi;
354 register struct namemap *nameptr;
358 for(i = 0; i < RESOLV_ENTRIES; ++i) {
360 if(nameptr->state == STATE_UNUSED) {
363 if(seqno - nameptr->seqno > lseq) {
364 lseq = seqno - nameptr->seqno;
369 if(i == RESOLV_ENTRIES) {
374 /* printf("Using entry %d\n", i);*/
376 strcpy(nameptr->name, name);
377 nameptr->state = STATE_NEW;
378 nameptr->seqno = seqno;
381 /*---------------------------------------------------------------------------*/
383 * Look up a hostname in the array of known hostnames.
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
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
394 /*---------------------------------------------------------------------------*/
396 resolv_lookup(char *name)
399 struct namemap *nameptr;
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) {
405 if(nameptr->state == STATE_DONE &&
406 strcmp(name, nameptr->name) == 0) {
407 return nameptr->ipaddr;
412 /*---------------------------------------------------------------------------*/
414 * Obtain the currently configured DNS server.
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
420 /*---------------------------------------------------------------------------*/
422 resolv_getserver(void)
424 if(resolv_conn == NULL) {
427 return resolv_conn->ripaddr;
429 /*---------------------------------------------------------------------------*/
431 * Configure which DNS server to use for queries.
433 * \param dnsserver A pointer to a 4-byte representation of the IP
434 * address of the DNS server to be configured.
436 /*---------------------------------------------------------------------------*/
438 resolv_conf(u16_t *dnsserver)
440 if(resolv_conn != NULL) {
441 uip_udp_remove(resolv_conn);
444 resolv_conn = uip_udp_new((uip_ipaddr_t *)dnsserver, HTONS(53));
446 /*---------------------------------------------------------------------------*/
448 * Initalize the resolver.
450 /*---------------------------------------------------------------------------*/
456 for(i = 0; i < RESOLV_ENTRIES; ++i) {
457 names[i].state = STATE_DONE;
461 /*---------------------------------------------------------------------------*/