| /** |
| * \addtogroup uip |
| * @{ |
| */ |
| |
| /** |
| * \defgroup uipdns uIP hostname resolver functions |
| * @{ |
| * |
| * The uIP DNS resolver functions are used to lookup a hostname and |
| * map it to a numerical IP address. It maintains a list of resolved |
| * hostnames that can be queried with the resolv_lookup() |
| * function. New hostnames can be resolved using the resolv_query() |
| * function. |
| * |
| * The event resolv_event_found is posted when a hostname has been |
| * resolved. It is up to the receiving process to determine if the |
| * correct hostname has been found by calling the resolv_lookup() |
| * function with the hostname. |
| */ |
| |
| /** |
| * \file |
| * DNS host name to IP address resolver. |
| * \author Adam Dunkels <adam@dunkels.com> |
| * |
| * This file implements a DNS host name to IP address resolver. |
| */ |
| |
| /* |
| * Copyright (c) 2002-2003, Adam Dunkels. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote |
| * products derived from this software without specific prior |
| * written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
| * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * This file is part of the uIP TCP/IP stack. |
| * |
| * $Id: resolv.c,v 1.15 2005/02/07 07:08:03 adamdunkels Exp $ |
| * |
| */ |
| |
| #include "ek.h" |
| #include "tcpip.h" |
| #include "resolv.h" |
| |
| #include <string.h> |
| |
| #ifndef NULL |
| #define NULL (void *)0 |
| #endif /* NULL */ |
| |
| /** \internal The maximum number of retries when asking for a name. */ |
| #define MAX_RETRIES 8 |
| |
| /** \internal The DNS message header. */ |
| struct dns_hdr { |
| u16_t id; |
| u8_t flags1, flags2; |
| #define DNS_FLAG1_RESPONSE 0x80 |
| #define DNS_FLAG1_OPCODE_STATUS 0x10 |
| #define DNS_FLAG1_OPCODE_INVERSE 0x08 |
| #define DNS_FLAG1_OPCODE_STANDARD 0x00 |
| #define DNS_FLAG1_AUTHORATIVE 0x04 |
| #define DNS_FLAG1_TRUNC 0x02 |
| #define DNS_FLAG1_RD 0x01 |
| #define DNS_FLAG2_RA 0x80 |
| #define DNS_FLAG2_ERR_MASK 0x0f |
| #define DNS_FLAG2_ERR_NONE 0x00 |
| #define DNS_FLAG2_ERR_NAME 0x03 |
| u16_t numquestions; |
| u16_t numanswers; |
| u16_t numauthrr; |
| u16_t numextrarr; |
| }; |
| |
| /** \internal The DNS answer message structure. */ |
| struct dns_answer { |
| /* DNS answer record starts with either a domain name or a pointer |
| to a name already present somewhere in the packet. */ |
| u16_t type; |
| u16_t class; |
| u16_t ttl[2]; |
| u16_t len; |
| u16_t ipaddr[2]; |
| }; |
| |
| struct namemap { |
| #define STATE_UNUSED 0 |
| #define STATE_NEW 1 |
| #define STATE_ASKING 2 |
| #define STATE_DONE 3 |
| #define STATE_ERROR 4 |
| u8_t state; |
| u8_t tmr; |
| u8_t retries; |
| u8_t seqno; |
| u8_t err; |
| char name[32]; |
| u16_t ipaddr[2]; |
| }; |
| |
| #ifndef UIP_CONF_RESOLV_ENTRIES |
| #define RESOLV_ENTRIES 4 |
| #else /* UIP_CONF_RESOLV_ENTRIES */ |
| #define RESOLV_ENTRIES UIP_CONF_RESOLV_ENTRIES |
| #endif /* UIP_CONF_RESOLV_ENTRIES */ |
| |
| |
| static struct namemap names[RESOLV_ENTRIES]; |
| |
| static u8_t seqno; |
| |
| static struct uip_udp_conn *resolv_conn = NULL; |
| |
| ek_event_t resolv_event_found; |
| |
| /*static DISPATCHER_UIPCALL(udp_appcall, arg); |
| static struct dispatcher_proc p = |
| {DISPATCHER_PROC("DNS resolver", NULL, NULL, udp_appcall)};*/ |
| EK_EVENTHANDLER(resolv_eventhandler, ev, data); |
| EK_PROCESS(p, "DNS resolver", EK_PRIO_NORMAL, resolv_eventhandler, NULL, NULL); |
| static ek_id_t id = EK_ID_NONE; |
| |
| enum { |
| EVENT_NEW_SERVER=0 |
| }; |
| |
| /*-----------------------------------------------------------------------------------*/ |
| /** \internal |
| * Walk through a compact encoded DNS name and return the end of it. |
| * |
| * \return The end of the name. |
| */ |
| /*-----------------------------------------------------------------------------------*/ |
| static unsigned char * |
| parse_name(unsigned char *query) |
| { |
| unsigned char n; |
| |
| do { |
| n = *query++; |
| |
| while(n > 0) { |
| /* printf("%c", *query);*/ |
| ++query; |
| --n; |
| }; |
| /* printf(".");*/ |
| } while(*query != 0); |
| /* printf("\n");*/ |
| return query + 1; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| /** \internal |
| * Runs through the list of names to see if there are any that have |
| * not yet been queried and, if so, sends out a query. |
| */ |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| check_entries(void) |
| { |
| register struct dns_hdr *hdr; |
| char *query, *nptr, *nameptr; |
| static u8_t i; |
| static u8_t n; |
| register struct namemap *namemapptr; |
| |
| for(i = 0; i < RESOLV_ENTRIES; ++i) { |
| namemapptr = &names[i]; |
| if(namemapptr->state == STATE_NEW || |
| namemapptr->state == STATE_ASKING) { |
| if(namemapptr->state == STATE_ASKING) { |
| if(--namemapptr->tmr == 0) { |
| if(++namemapptr->retries == MAX_RETRIES) { |
| namemapptr->state = STATE_ERROR; |
| resolv_found(namemapptr->name, NULL); |
| continue; |
| } |
| namemapptr->tmr = namemapptr->retries; |
| } else { |
| /* printf("Timer %d\n", namemapptr->tmr);*/ |
| /* Its timer has not run out, so we move on to next |
| entry. */ |
| continue; |
| } |
| } else { |
| namemapptr->state = STATE_ASKING; |
| namemapptr->tmr = 1; |
| namemapptr->retries = 0; |
| } |
| hdr = (struct dns_hdr *)uip_appdata; |
| memset(hdr, 0, sizeof(struct dns_hdr)); |
| hdr->id = htons(i); |
| hdr->flags1 = DNS_FLAG1_RD; |
| hdr->numquestions = HTONS(1); |
| query = (char *)uip_appdata + 12; |
| nameptr = namemapptr->name; |
| --nameptr; |
| /* Convert hostname into suitable query format. */ |
| do { |
| ++nameptr; |
| nptr = query; |
| ++query; |
| for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) { |
| *query = *nameptr; |
| ++query; |
| ++n; |
| } |
| *nptr = n; |
| } while(*nameptr != 0); |
| { |
| static unsigned char endquery[] = |
| {0,0,1,0,1}; |
| memcpy(query, endquery, 5); |
| } |
| uip_udp_send((unsigned char)(query + 5 - (char *)uip_appdata)); |
| break; |
| } |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| /** \internal |
| * Called when new UDP data arrives. |
| */ |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| newdata(void) |
| { |
| char *nameptr; |
| struct dns_answer *ans; |
| struct dns_hdr *hdr; |
| static u8_t nquestions, nanswers; |
| static u8_t i; |
| register struct namemap *namemapptr; |
| |
| hdr = (struct dns_hdr *)uip_appdata; |
| /* printf("ID %d\n", htons(hdr->id)); |
| printf("Query %d\n", hdr->flags1 & DNS_FLAG1_RESPONSE); |
| printf("Error %d\n", hdr->flags2 & DNS_FLAG2_ERR_MASK); |
| printf("Num questions %d, answers %d, authrr %d, extrarr %d\n", |
| htons(hdr->numquestions), |
| htons(hdr->numanswers), |
| htons(hdr->numauthrr), |
| htons(hdr->numextrarr)); |
| */ |
| |
| /* The ID in the DNS header should be our entry into the name |
| table. */ |
| i = htons(hdr->id); |
| namemapptr = &names[i]; |
| if(i < RESOLV_ENTRIES && |
| namemapptr->state == STATE_ASKING) { |
| |
| /* This entry is now finished. */ |
| namemapptr->state = STATE_DONE; |
| namemapptr->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; |
| |
| /* Check for error. If so, call callback to inform. */ |
| if(namemapptr->err != 0) { |
| namemapptr->state = STATE_ERROR; |
| resolv_found(namemapptr->name, NULL); |
| return; |
| } |
| |
| /* We only care about the question(s) and the answers. The authrr |
| and the extrarr are simply discarded. */ |
| nquestions = htons(hdr->numquestions); |
| nanswers = htons(hdr->numanswers); |
| |
| /* Skip the name in the question. XXX: This should really be |
| checked agains the name in the question, to be sure that they |
| match. */ |
| nameptr = parse_name((char *)uip_appdata + 12) + 4; |
| |
| while(nanswers > 0) { |
| /* The first byte in the answer resource record determines if it |
| is a compressed record or a normal one. */ |
| if(*nameptr & 0xc0) { |
| /* Compressed name. */ |
| nameptr +=2; |
| /* printf("Compressed anwser\n");*/ |
| } else { |
| /* Not compressed name. */ |
| nameptr = parse_name((char *)nameptr); |
| } |
| |
| ans = (struct dns_answer *)nameptr; |
| /* printf("Answer: type %x, class %x, ttl %x, length %x\n", |
| htons(ans->type), htons(ans->class), (htons(ans->ttl[0]) |
| << 16) | htons(ans->ttl[1]), htons(ans->len));*/ |
| |
| /* Check for IP address type and Internet class. Others are |
| discarded. */ |
| if(ans->type == HTONS(1) && |
| ans->class == HTONS(1) && |
| ans->len == HTONS(4)) { |
| /* printf("IP address %d.%d.%d.%d\n", |
| htons(ans->ipaddr[0]) >> 8, |
| htons(ans->ipaddr[0]) & 0xff, |
| htons(ans->ipaddr[1]) >> 8, |
| htons(ans->ipaddr[1]) & 0xff);*/ |
| /* XXX: we should really check that this IP address is the one |
| we want. */ |
| namemapptr->ipaddr[0] = ans->ipaddr[0]; |
| namemapptr->ipaddr[1] = ans->ipaddr[1]; |
| |
| resolv_found(namemapptr->name, namemapptr->ipaddr); |
| return; |
| } else { |
| nameptr = nameptr + 10 + htons(ans->len); |
| } |
| --nanswers; |
| } |
| } |
| |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| /** \internal |
| * The main UDP function. |
| */ |
| /*-----------------------------------------------------------------------------------*/ |
| EK_EVENTHANDLER(resolv_eventhandler, ev, data) |
| { |
| EK_EVENTHANDLER_ARGS(ev, data); |
| if(ev == EVENT_NEW_SERVER) { |
| if(resolv_conn != NULL) { |
| uip_udp_remove(resolv_conn); |
| } |
| |
| resolv_conn = udp_new((u16_t *)data, HTONS(53), NULL); |
| |
| } else if(ev == tcpip_event) { |
| if(uip_udp_conn->rport == HTONS(53)) { |
| if(uip_poll()) { |
| check_entries(); |
| } |
| if(uip_newdata()) { |
| newdata(); |
| } |
| } |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| /** |
| * Queues a name so that a question for the name will be sent out. |
| * |
| * \param name The hostname that is to be queried. |
| */ |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| resolv_query(char *name) |
| { |
| static u8_t i; |
| static u8_t lseq, lseqi; |
| register struct namemap *nameptr; |
| |
| lseq = lseqi = 0; |
| |
| for(i = 0; i < RESOLV_ENTRIES; ++i) { |
| nameptr = &names[i]; |
| if(nameptr->state == STATE_UNUSED) { |
| break; |
| } |
| if(seqno - nameptr->seqno > lseq) { |
| lseq = seqno - nameptr->seqno; |
| lseqi = i; |
| } |
| } |
| |
| if(i == RESOLV_ENTRIES) { |
| i = lseqi; |
| nameptr = &names[i]; |
| } |
| |
| strncpy(nameptr->name, name, sizeof(nameptr->name)); |
| nameptr->state = STATE_NEW; |
| nameptr->seqno = seqno; |
| ++seqno; |
| |
| if(resolv_conn != NULL) { |
| tcpip_poll_udp(resolv_conn); |
| /*ek_post(EK_BROADCAST, uip_event_poll_udp, resolv_conn);*/ |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| /** |
| * Look up a hostname in the array of known hostnames. |
| * |
| * \note This function only looks in the internal array of known |
| * hostnames, it does not send out a query for the hostname if none |
| * was found. The function resolv_query() can be used to send a query |
| * for a hostname. |
| * |
| * \return A pointer to a 4-byte representation of the hostname's IP |
| * address, or NULL if the hostname was not found in the array of |
| * hostnames. |
| */ |
| /*-----------------------------------------------------------------------------------*/ |
| u16_t * |
| resolv_lookup(char *name) |
| { |
| static u8_t i; |
| struct namemap *nameptr; |
| |
| /* Walk through the list to see if the name is in there. If it is |
| not, we return NULL. */ |
| for(i = 0; i < RESOLV_ENTRIES; ++i) { |
| nameptr = &names[i]; |
| if(nameptr->state == STATE_DONE && |
| strcmp(name, nameptr->name) == 0) { |
| return nameptr->ipaddr; |
| } |
| } |
| return NULL; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| /** |
| * Obtain the currently configured DNS server. |
| * |
| * \return A pointer to a 4-byte representation of the IP address of |
| * the currently configured DNS server or NULL if no DNS server has |
| * been configured. |
| */ |
| /*-----------------------------------------------------------------------------------*/ |
| u16_t * |
| resolv_getserver(void) |
| { |
| if(resolv_conn == NULL) { |
| return NULL; |
| } |
| return resolv_conn->ripaddr; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| /** |
| * Configure a DNS server. |
| * |
| * \param dnsserver A pointer to a 4-byte representation of the IP |
| * address of the DNS server to be configured. |
| */ |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| resolv_conf(u16_t *dnsserver) |
| { |
| static u16_t server[2]; |
| uip_ipaddr_copy(server, dnsserver); |
| ek_post(id, EVENT_NEW_SERVER, server); |
| |
| /* if(resolv_conn != NULL) { |
| uip_udp_remove(resolv_conn); |
| } |
| |
| resolv_conn = udp_new(dnsserver, 53, NULL);*/ |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| /** |
| * Initalize the resolver. |
| */ |
| /*-----------------------------------------------------------------------------------*/ |
| /*LOADER_INIT_FUNC(resolv_init, arg)*/ |
| void |
| resolv_init(char *arg) |
| { |
| static u8_t i; |
| arg_free(arg); |
| |
| id = ek_start(&p); |
| |
| for(i = 0; i < RESOLV_ENTRIES; ++i) { |
| names[i].state = STATE_UNUSED; |
| } |
| resolv_conn = NULL; |
| resolv_event_found = ek_alloc_event(); |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| /** \internal |
| * Callback function which is called when a hostname is found. |
| * |
| */ |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| resolv_found(char *name, u16_t *ipaddr) |
| { |
| ek_post(EK_BROADCAST, resolv_event_found, name); |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| |
| /** @} */ |
| /** @} */ |