]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lwip.git/commitdiff
opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name requests...
authorfbernon <fbernon>
Wed, 14 Nov 2007 23:27:13 +0000 (23:27 +0000)
committerfbernon <fbernon>
Wed, 14 Nov 2007 23:27:13 +0000 (23:27 +0000)
CHANGELOG
src/api/tcpip.c
src/core/dhcp.c
src/core/dns.c [new file with mode: 0644]
src/core/init.c
src/include/lwip/dns.h [new file with mode: 0644]
src/include/lwip/opt.h

index 2ac1830a1e328d44d151662d910414520b20ded2..f7cdd1c27fda73f0b5de85bab2f6020a02cd5426 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -19,6 +19,15 @@ HISTORY
 
   ++ New features:
 
+  2007-11-15 Jim Pettinato, Frédéric Bernon
+  * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name
+    requests with RAW api interface. Initialization is done in lwip_init() with
+    build time options. DNS timer is added in tcpip_thread context. DHCP can set
+    DNS server ip addresses when options are received. You need to set LWIP_DNS=1
+    in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get
+    some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo"
+    list with points to improve.
+
   2007-11-06 Simon Goldschmidt
   * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly
     enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status
index 60a96fa1534bbe1b237bb14c2b5c8801a169b0bc..4ba223d27b113fae0ebc1acd8f7fccaa458050df 100644 (file)
@@ -48,6 +48,7 @@
 #include "lwip/autoip.h"
 #include "lwip/dhcp.h"
 #include "lwip/igmp.h"
+#include "lwip/dns.h"
 #include "lwip/tcpip.h"
 #include "lwip/init.h"
 #include "netif/etharp.h"
@@ -202,6 +203,22 @@ igmp_timer(void *arg)
 }
 #endif /* LWIP_IGMP */
 
+#if LWIP_DNS
+/**
+ * Timer callback function that calls dns_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+dns_timer(void *arg)
+{
+  LWIP_UNUSED_ARG(arg);
+  LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: dns_tmr()\n"));
+  dns_tmr();
+  sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
+}
+#endif /* LWIP_DNS */
+
 /**
  * The main lwIP thread. This thread has exclusive access to lwIP core functions
  * (unless access to them is not locked). Other threads communicate with this
@@ -234,6 +251,9 @@ tcpip_thread(void *arg)
 #if LWIP_IGMP
   sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
 #endif /* LWIP_IGMP */
+#if LWIP_DNS
+  sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
+#endif /* LWIP_DNS */
 
   if (tcpip_init_done != NULL) {
     tcpip_init_done(tcpip_init_done_arg);
index 572591fe6c88ffb1113717456df0edafba6460f9..1963bb0f9df3ce9f3a1abf2acbf30eca898b8ea8 100644 (file)
@@ -81,6 +81,7 @@
 #include "lwip/sys.h"
 #include "lwip/dhcp.h"
 #include "lwip/autoip.h"
+#include "lwip/dns.h"
 #include "netif/etharp.h"
 
 #include <string.h>
@@ -517,6 +518,9 @@ static void dhcp_handle_ack(struct netif *netif)
       dhcp->dns_count = DHCP_MAX_DNS;
     for (n = 0; n < dhcp->dns_count; n++) {
       dhcp->offered_dns_addr[n].addr = htonl(dhcp_get_option_long(&option_ptr[2 + n * 4]));
+#if LWIP_DNS
+      dns_setserver( n, (struct ip_addr *)(&(dhcp->offered_dns_addr[n].addr)));
+#endif /* LWIP_DNS */
     }
   }
 }
diff --git a/src/core/dns.c b/src/core/dns.c
new file mode 100644 (file)
index 0000000..f82e43f
--- /dev/null
@@ -0,0 +1,640 @@
+/**
+ * @file
+ * DNS - host name to IP address resolver.
+ *
+ */
+
+/**
+
+ * This file implements a DNS host name to IP address resolver.
+
+ * Port to lwIP from uIP
+ * by Jim Pettinato April 2007
+
+ * uIP version 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.
+ *
+ *
+ * DNS.C
+ *
+ * The lwIP DNS resolver functions are used to lookup a host name and
+ * map it to a numerical IP address. It maintains a list of resolved
+ * hostnames that can be queried with the dns_lookup() function.
+ * New hostnames can be resolved using the dns_query() function.
+ *
+ * The lwIP version of the resolver also adds a non-blocking version of
+ * gethostbyname() that will work with a raw API application. This function
+ * checks for an IP address string first and converts it if it is valid.
+ * gethostbyname() then does a dns_lookup() to see if the name is 
+ * already in the table. If so, the IP is returned. If not, a query is 
+ * issued and the function returns with a DNS_QUERY_QUEUED status. The app
+ * using the dns client must then go into a waiting state.
+ *
+ * Once a hostname has been resolved (or found to be non-existent),
+ * the resolver code calls a specified callback function (which 
+ * must be implemented by the module that uses the resolver).
+ */
+
+/** @todo: define good default values (rfc compliance) */
+/** @todo: secondary server support */
+/** @todo: compressed answer support */
+/** @todo: improve answer parsing, more checkings... */
+/** @todo: possible alignment problems to access to dns_answer fields? */
+
+/*-----------------------------------------------------------------------------
+ * RFC 1035 - Domain names - implementation and specification
+ * RFC 2181 - Clarifications to the DNS Specification
+ *----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+
+/** DNS server IP address */
+#ifndef DNS_SERVER_ADDRESS
+#define DNS_SERVER_ADDRESS        inet_addr("208.67.222.222") /* resolver1.opendns.com */
+#endif
+
+/** DNS server port address */
+#ifndef DNS_SERVER_PORT
+#define DNS_SERVER_PORT           53
+#endif
+
+/* The maximum number of table entries to maintain locally */
+#ifndef DNS_TABLE_SIZE
+#define DNS_TABLE_SIZE            4
+#endif
+
+/* The maximum length of a host name supported in the name table. */
+#ifndef DNS_MAX_NAME_LENGTH
+#define DNS_MAX_NAME_LENGTH       256
+#endif
+
+/* The maximum number of retries when asking for a name, before "timeout". */
+#ifndef DNS_MAX_RETRIES
+#define DNS_MAX_RETRIES           8
+#endif 
+
+/* DNS entry time to live (in DNS_TMR_INTERVAL ticks) */
+#ifndef DNS_TTL_ENTRY
+#define DNS_TTL_ENTRY             60
+#endif
+
+/* DNS protocol flags */
+#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
+
+/* DNS protocol states */
+#define DNS_STATE_UNUSED          0
+#define DNS_STATE_NEW             1
+#define DNS_STATE_ASKING          2
+#define DNS_STATE_DONE            3
+
+/* DNS field TYPE used for "Resource Records" */
+#define DNS_RRTYPE_A              1     /* a host address */
+#define DNS_RRTYPE_NS             2     /* an authoritative name server */
+#define DNS_RRTYPE_MD             3     /* a mail destination (Obsolete - use MX) */
+#define DNS_RRTYPE_MF             4     /* a mail forwarder (Obsolete - use MX) */
+#define DNS_RRTYPE_CNAME          5     /* the canonical name for an alias */
+#define DNS_RRTYPE_SOA            6     /* marks the start of a zone of authority */
+#define DNS_RRTYPE_MB             7     /* a mailbox domain name (EXPERIMENTAL) */
+#define DNS_RRTYPE_MG             8     /* a mail group member (EXPERIMENTAL) */
+#define DNS_RRTYPE_MR             9     /* a mail rename domain name (EXPERIMENTAL) */
+#define DNS_RRTYPE_NULL           10    /* a null RR (EXPERIMENTAL) */
+#define DNS_RRTYPE_WKS            11    /* a well known service description */
+#define DNS_RRTYPE_PTR            12    /* a domain name pointer */
+#define DNS_RRTYPE_HINFO          13    /* host information */
+#define DNS_RRTYPE_MINFO          14    /* mailbox or mail list information */
+#define DNS_RRTYPE_MX             15    /* mail exchange */
+#define DNS_RRTYPE_TXT            16    /* text strings */
+
+/* DNS field CLASS used for "Resource Records" */
+#define DNS_RRCLASS_IN            1     /* the Internet */
+#define DNS_RRCLASS_CS            2     /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
+#define DNS_RRCLASS_CH            3     /* the CHAOS class */
+#define DNS_RRCLASS_HS            4     /* Hesiod [Dyer 87] */
+#define DNS_RRCLASS_FLUSH         0x800 /* Flush bit */
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** DNS message header */
+struct dns_hdr {
+  u16_t id;
+  u8_t flags1;
+  u8_t flags2;
+  u16_t numquestions;
+  u16_t numanswers;
+  u16_t numauthrr;
+  u16_t numextrarr;
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** 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;
+  u32_t ttl;
+  u16_t len;
+  struct ip_addr ipaddr;
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** DNS table entry */
+struct dns_table_entry {
+  u8_t state;
+  u8_t tmr;
+  u8_t retries;
+  u8_t ttl;
+  u8_t seqno;
+  u8_t err;
+  char name[DNS_MAX_NAME_LENGTH];
+  struct ip_addr ipaddr;
+  void (* found)(char *name, struct ip_addr *ipaddr, void *arg); /* pointer to callback on DNS query done */
+  void *arg;
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+#  include "arch/epstruct.h"
+#endif
+
+/* forward declarations */
+static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port);
+static void dns_check_entries(void);
+
+/*-----------------------------------------------------------------------------
+ * Globales
+ *----------------------------------------------------------------------------*/
+
+/* DNS variables */
+static struct udp_pcb        *dns_pcb;
+static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
+static u8_t                   dns_seqno;
+
+/* DNS request termination sequence: zero(1)+type(2)+class(2) */
+static unsigned char dns_endquery[] = {0,0,1,0,1};
+
+/**
+ * Initialize the resolver and configure which DNS server to use for queries.
+ *
+ * param dnsserver A pointer to a 4-byte representation of the IP
+ * address of the DNS server to be configured.
+ */
+err_t
+dns_init()
+{
+  u8_t i;
+  struct ip_addr dnsserver = {DNS_SERVER_ADDRESS};
+
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
+
+  /* if dns client not yet initialized... */
+  if (dns_pcb == NULL) {
+    dns_pcb = udp_new();
+
+    if (dns_pcb != NULL) {
+      /* initialize DNS table */
+      for (i=0; i<DNS_TABLE_SIZE; ++i) {
+        dns_table[i].state = DNS_STATE_UNUSED;
+        dns_table[i].found = NULL;
+      }
+
+      /* initialize DNS client */
+      udp_bind(dns_pcb, IP_ADDR_ANY, 0);
+      udp_recv(dns_pcb, dns_recv, NULL);
+      
+      /* initialize default DNS primary server */
+      dns_setserver(0, &dnsserver);
+    }
+  }
+  return ERR_OK;
+}
+
+/**
+ * Obtain the currently configured DNS server.
+ * return unsigned long encoding of the IP address of
+ * the currently configured DNS server or NULL if no DNS server has
+ * been configured.
+ */
+void
+dns_setserver(u8_t numdns, struct ip_addr *dnsserver)
+{
+  LWIP_UNUSED_ARG(numdns);
+  if ((dns_pcb != NULL) && (dnsserver != NULL) && (dnsserver->addr !=0 )) {
+    udp_connect( dns_pcb, dnsserver, DNS_SERVER_PORT);
+  }
+}
+
+/**
+ * Obtain the currently configured DNS server.
+ * return unsigned long encoding of the IP address of
+ * the currently configured DNS server or NULL if no DNS server has
+ * been configured.
+ */
+u32_t
+dns_getserver(u8_t numdns)
+{
+  LWIP_UNUSED_ARG(numdns);
+  return ((dns_pcb != NULL)?dns_pcb->remote_ip.addr:0);
+}
+
+/**
+ * The DNS resolver client timer - handle retries and timeouts
+ */
+void
+dns_tmr(void)
+{
+  if (dns_pcb != NULL) {
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
+    dns_check_entries();
+  }
+}
+
+/**
+ * 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 dns_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.
+ */
+u32_t
+dns_lookup(char *name)
+{
+  u8_t i;
+
+  /* Walk through name list, return entry if found. If not, return NULL. */
+  for (i=0; i<DNS_TABLE_SIZE; ++i) {
+    if ( (dns_table[i].state==DNS_STATE_DONE) && (strcmp(name, dns_table[i].name)==0) ) {
+      LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
+      ip_addr_debug_print(DNS_DEBUG, (&(dns_table[i].ipaddr)));
+      LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+      return dns_table[i].ipaddr.addr;
+    }
+  }
+
+  return 0;
+}
+
+/**
+ * dns_parse_name() - walk through a compact encoded DNS name and return the end 
+ * of the name.
+ */
+static unsigned char *
+dns_parse_name(unsigned char *query)
+{
+  unsigned char n;
+
+  do {
+    n = *query++;
+    
+    while(n > 0) {
+      ++query;
+      --n;
+    };
+  } while(*query != 0);
+
+  return query + 1;
+}
+
+/**
+ * dns_send
+ */
+static err_t
+dns_send( char* name, u8_t id)
+{ 
+  struct dns_hdr *hdr;
+  struct pbuf *p;
+  char *query, *nptr, *pHostname;
+  u8_t n;
+
+  LWIP_DEBUGF(DNS_DEBUG, ("dns_send: \"%s\": request\n", name));
+
+  /* if here, we have either a new query or a retry on a previous query to process */
+  p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dns_hdr)+DNS_MAX_NAME_LENGTH+sizeof(dns_endquery), PBUF_RAM);
+  if (p) {
+    hdr = (struct dns_hdr *)p->payload;
+    memset(hdr, 0, sizeof(struct dns_hdr));
+    hdr->id = htons(id);
+    hdr->flags1 = DNS_FLAG1_RD;
+    hdr->numquestions = htons(1);
+    query = (char *)hdr + sizeof(struct dns_hdr);
+    pHostname = name;
+    --pHostname;
+    /* convert hostname into suitable query format. */
+    do {
+      ++pHostname;
+      nptr = query;
+      ++query;
+      for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
+        *query = *pHostname;
+        ++query;
+        ++n;
+      }
+      *nptr = n;
+    } while(*pHostname != 0);
+
+    memcpy( query, dns_endquery, sizeof(dns_endquery));
+
+    // resize pbuf to the exact dns query
+    pbuf_realloc(p, (query+sizeof(dns_endquery))-((char*)(p->payload)));
+
+    // send dns packet
+    udp_send(dns_pcb, p);
+
+    // free pbuf
+    pbuf_free(p);
+
+    return ERR_OK;
+  }
+
+  return ERR_BUF;
+}
+
+/**
+ * dns_check_entries() - 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
+dns_check_entries(void)
+{
+  u8_t i;
+  struct dns_table_entry *pEntry;
+
+  for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+    pEntry = &dns_table[i];
+    switch(pEntry->state) {
+
+      case DNS_STATE_NEW:
+      case DNS_STATE_ASKING: {
+        if (pEntry->state == DNS_STATE_ASKING) {
+          if (--pEntry->tmr == 0) {
+            if (++pEntry->retries == DNS_MAX_RETRIES) {
+              LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entries: \"%s\": timeout\n", pEntry->name));
+              /* call specified callback function if provided */
+              if (pEntry->found)
+                (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+              /* flush this entry */
+              pEntry->state   = DNS_STATE_UNUSED;
+              pEntry->found   = NULL;
+              continue;
+            }
+            /* wait longer for the next retry */
+            pEntry->tmr = pEntry->retries;
+          } else {
+            /* Its timer has not run out, so we move on to next entry. */
+            continue;
+          }
+        } else {
+          pEntry->state   = DNS_STATE_ASKING;
+          pEntry->tmr     = 1;
+          pEntry->retries = 0;
+        }
+        /* send DNS packet for this entry */
+        dns_send(pEntry->name, i);
+        break;
+      }
+
+      case DNS_STATE_DONE: {
+        /* if the time to live is nul */
+        if (--pEntry->ttl == 0) {
+          LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entries: \"%s\": flush\n", pEntry->name));
+          /* flush this entry */
+          pEntry->state   = DNS_STATE_UNUSED;
+          pEntry->found   = NULL;
+        }
+        break;
+      }
+    }
+  }
+}
+
+/**
+ * Callback for DNS responses
+ */
+static void
+dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
+{
+  u8_t i;
+  char *pHostname;
+  struct dns_answer *ans;
+  struct dns_hdr *hdr;
+  struct dns_table_entry *pEntry;
+  u8_t nquestions, nanswers;
+  
+  hdr = (struct dns_hdr *)p->payload;
+
+  /* The ID in the DNS header should be our entry into the name table. */
+  i = htons(hdr->id);
+  pEntry = &dns_table[i];
+  if( (i < DNS_TABLE_SIZE) && (pEntry->state == DNS_STATE_ASKING) ) {
+    /* This entry is now completed. */
+    pEntry->state = DNS_STATE_DONE;
+    pEntry->ttl   = DNS_TTL_ENTRY;
+    pEntry->err   = hdr->flags2 & DNS_FLAG2_ERR_MASK;
+
+    /* Check for error. If so, call callback to inform. */
+    if(pEntry->err != 0) {
+      LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));
+      /* call specified callback function if provided */
+      if (pEntry->found)
+        (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+      /* flush this entry */
+      pEntry->state   = DNS_STATE_UNUSED;
+      pEntry->found   = 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. */
+    pHostname = (char *) dns_parse_name((unsigned char *)p->payload + sizeof(struct dns_hdr)) + 4/*type(2)+class(2)*/;
+
+    while(nanswers > 0) {
+      /* The first byte in the answer resource record determines if it
+         is a compressed record or a normal one. */
+      if(*pHostname & 0xc0) {
+        /* Compressed name. */
+        pHostname +=2;
+        /* printf("Compressed anwser\n");*/
+      } else {
+        /* Not compressed name. */
+        pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
+      }
+
+      /* TODO: isn't it any problem to access to dns_answer fields since pHostname's length can be unaligned? */
+      ans = (struct dns_answer *)pHostname;
+      /* 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((ntohs(ans->type) == DNS_RRTYPE_A) && (ntohs(ans->class) == DNS_RRCLASS_IN) && (ntohs(ans->len) == 4/*IPv4 address*/) ) {
+        /* TODO: we should really check that this IP address is the one we want. */
+        pEntry->ipaddr =  ans->ipaddr;
+        LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));
+        ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
+        LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+        /* call specified callback function if provided */
+        if (pEntry->found)
+          (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);
+        return;
+      } else {
+        pHostname = pHostname + 10 + htons(ans->len);
+      }
+      --nanswers;
+    }
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
+  }
+}
+
+/**
+ * Queues a name so that a question for the name will be sent out.
+ * param name - The hostname that is to be queried.
+ */
+static void
+dns_query(char *name, void (*found)(char *name, struct ip_addr *addr, void *arg), void *arg)
+{
+  u8_t i;
+  u8_t lseq, lseqi;
+  struct dns_table_entry *pEntry;
+
+  /* search an unused entry, or the oldest one */
+  lseq = lseqi = 0;
+  for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+    pEntry = &dns_table[i];
+    /* is it an unused entry ? */
+    if (pEntry->state == DNS_STATE_UNUSED)
+      break;
+
+    /* check if this is the oldest entry used */
+    if (dns_seqno - pEntry->seqno > lseq) {
+      lseq = dns_seqno - pEntry->seqno;
+      lseqi = i;
+    }
+  }
+
+  /* if we don't have found an unused entry, use the oldest one */
+  if (i == DNS_TABLE_SIZE) {
+    i = lseqi;
+    pEntry = &dns_table[i];
+    /* since we replace the previous entry, we "unblock" the caller */
+    LWIP_DEBUGF(DNS_DEBUG, ("dns_query: \"%s\": replaced by new entry\n", pEntry->name));
+    /* call specified callback function if provided */
+    if (pEntry->found) 
+      (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+  }
+
+  /* fill the entry */
+  strcpy(pEntry->name, name);
+  pEntry->found = found;
+  pEntry->arg   = arg;
+  pEntry->state = DNS_STATE_NEW;
+  pEntry->seqno = dns_seqno++;
+}
+
+/**
+ * NON-BLOCKING callback version for use with raw API
+ */
+DNS_RESULT dns_gethostbyname(char *hostname, struct ip_addr *addr, 
+                             void (*found)(char *name, struct ip_addr *ipaddr, void *arg),
+                             void *arg
+                             )
+{
+  /* not initialized or no valid server yet, or invalid addr pointer */
+  if ((dns_pcb == NULL) || (addr == NULL))
+    return DNS_QUERY_INVALID;
+
+  /* invalid hostname */
+  if ((!hostname) || (!hostname[0]))
+    return DNS_QUERY_INVALID;
+
+  /* invalid hostname length */
+  if (strlen(hostname) >= DNS_MAX_NAME_LENGTH)
+    return DNS_QUERY_INVALID;
+
+  /* host name already in octet notation? set ip addr and return COMPLETE */
+  if ((addr->addr = inet_addr(hostname)) != INADDR_NONE)
+    return DNS_COMPLETE;
+
+  /* already have this address cached? */
+  if ((addr->addr = dns_lookup(hostname)) != 0) 
+    return DNS_COMPLETE;
+
+  /* queue query with specified callback */
+  dns_query(hostname, found, arg);
+
+  /* force to send request */
+  dns_check_entries();
+  
+  return DNS_QUERY_QUEUED;
+}
+
+#endif /* LWIP_DNS */
index 9d1e97e6688e9341efe91c55b89966403d6f3b01..31ad2c75eec20ad7e8d5782156945a90059959ce 100644 (file)
@@ -52,6 +52,7 @@
 #include "lwip/tcp.h"
 #include "lwip/autoip.h"
 #include "lwip/igmp.h"
+#include "lwip/dns.h"
 #include "netif/etharp.h"
 
 /* Compile-time sanity checks for configuration errors.
@@ -75,6 +76,9 @@
 #if (!LWIP_UDP && LWIP_IGMP)
   #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h"
 #endif
+#if (!LWIP_UDP && LWIP_DNS)
+  #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
 #if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f))
   #error "If you want to use ARP, ARP_TABLE_SIZE must fit in an s8_t, so, you have to reduce it in your lwipopts.h"
 #endif
   #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in lwipopts.h"
 #endif
 /* There must be sufficient timeouts, taking into account requirements of the subsystems. */
-#if ((NO_SYS==0) && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_PPP)))
+#if ((NO_SYS==0) && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + LWIP_PPP)))
   #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
 #endif
 #if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
@@ -233,4 +237,7 @@ lwip_init(void)
 #if LWIP_IGMP
   igmp_init();
 #endif /* LWIP_IGMP */
+#if LWIP_DNS
+  dns_init();
+#endif /* LWIP_DNS */
 }
diff --git a/src/include/lwip/dns.h b/src/include/lwip/dns.h
new file mode 100644 (file)
index 0000000..f9064bb
--- /dev/null
@@ -0,0 +1,90 @@
+/**
+ * lwip DNS resolver header file.
+
+ * Author: Jim Pettinato 
+ *   April 2007
+
+ * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels.
+ *
+ * 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.
+ */
+
+#ifndef __LWIP_DNS_H__
+#define __LWIP_DNS_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+/** DNS timer period */
+#ifndef DNS_TMR_INTERVAL
+#define DNS_TMR_INTERVAL 1000
+#endif
+
+/* enumerated list of possible result values returned by dns_gethostname() */
+typedef enum dns_result {
+  DNS_QUERY_INVALID,
+  DNS_QUERY_QUEUED,
+  DNS_COMPLETE
+}DNS_RESULT;
+
+/* initializes the resolver */
+err_t dns_init(void);
+
+/* handles requests, retries and timeouts - call every DNS_TMR_INTERVAL tick */
+void  dns_tmr(void);
+
+/* initializes DNS server IP address */
+void dns_setserver(u8_t numdns, struct ip_addr *dnsserver);
+
+/* returns configured DNS server IP address */
+u32_t dns_getserver(u8_t numdns);
+
+/* returns IP for host 'name' only if already in table */
+u32_t dns_lookup(char *name);
+
+/* resolove a host 'name' in ip address */
+DNS_RESULT dns_gethostbyname(char *hostName, struct ip_addr *addr, 
+                             void (*found)(char *name, struct ip_addr *ipaddr, void *arg),
+                             void *arg);
+
+/* dns_gethostbyname() - Returns immediately with one of DNS_RESULT return codes
+ *                       Return value will be DNS_COMPLETE if hostName is a valid
+ *                       IP address string or the host name is already in the local
+ *                       names table. Returns DNS_REQUEST_QUEUED and queues a
+ *                       request to be sent to the DNS server for resolution if no
+ *                       errors are present.
+ */
+
+/* dns_found_func() - Callback which is invoked when a hostname is found.
+ * This function should be implemented by the application using the DNS resolver.
+ *  param 'name'   - pointer to the name that was looked up.
+ *  param 'ipaddr' - pointer to a struct ip_addr containing the IP address of the
+ *                   hostname, or NULL if the name could not be found.
+*/
+
+#endif /* LWIP_DNS */
+
+#endif /* __LWIP_DNS_H__ */
index 147a0dd58c440b0fe3bb5ec64b08cad58c4c4eb8..2c9579ad0cf87a02a6e39de2eefdbbdafa93405b 100644 (file)
 #define LWIP_IGMP                       0
 #endif
 
+/*
+   ----------------------------------
+   ---------- DNS options -----------
+   ----------------------------------
+*/
+/**
+ * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS
+ * transport.
+ */
+#ifndef LWIP_DNS
+#define LWIP_DNS                        0
+#endif
+
 /*
    ---------------------------------
    ---------- UDP options ----------
 #define SNMP_MIB_DEBUG                  LWIP_DBG_OFF
 #endif
 
+/**
+ * DNS_DEBUG: Enable debugging for DNS.
+ */
+#ifndef DNS_DEBUG
+#define DNS_DEBUG                       LWIP_DBG_OFF
+#endif
+
 #endif /* __LWIP_OPT_H__ */