< Previous by Date Date Index Next by Date >
< Previous in Thread Thread Index Next in Thread >

Re: [reSIProcate] /etc/hosts is ignored


On Wed, 14 Jun 2006 at 14:59 +0400, Dmitry Semyonov wrote:

> On Tue, 13 Jun 2006 at 20:02 +0400, Dmitry Semyonov wrote:
> 
> > Shouldn't ares_query() be updated with the code similar to 
> > ares_gethostby*() functions to use /etc/hosts?
> 
> Well, it should not. By design.
> 
> But then another question -- shouldn't DNS A queries be performed via 
> ares_gethostbyname() rather than via ares_query() inside 
> AresDns::lookup() (and LocalDns::lookup()?)?

Finally, I decided to use ares_local_query() for my needs. See the 
patch below for reference. Hope it will save some time to others.

/etc/hosts is read into memory during ares initialization, and is not 
reloaded afterward. Real network DNS A queries are performed only if 
they could not be resolved locally. Lookup algorithm is quite poor, so 
don't use it with huge /etc/hosts, and put most used entries to the 
top ;)

If somebody knows what 0xc00c mean in the beginning of RR, please 
reply.


Index: ares_destroy.c
===================================================================
--- ares_destroy.c      (revision 6386)
+++ ares_destroy.c      (working copy)
@@ -56,4 +56,6 @@
       free(query);
    }
    free(channel);
+
+   ares_local_destroy();
 }
Index: ares_dns.h
===================================================================
--- ares_dns.h  (revision 6386)
+++ ares_dns.h  (working copy)
@@ -21,10 +21,10 @@
                                         ((p)[2] << 8) | (p)[3])
 #define DNS__SET16BIT(p, v)            (((p)[0] = ((v) >> 8) & 0xff), \
                                         ((p)[1] = (v) & 0xff))
-#define DNS__SET32BIT(p, v)            (((p)[0] = ((v) >> 24) & 0xff), \
-                                        ((p)[1] = ((v) >> 16) & 0xff), \
-                                        ((p)[2] = ((v) >> 8) & 0xff), \
-                                        ((p)[3] = (v) & 0xff))
+#define DNS__SET32BIT(p, v)            (((p)[0] = (unsigned char)((v) >> 24) & 
0xff), \
+                                        ((p)[1] = (unsigned char)((v) >> 16) & 
0xff), \
+                                        ((p)[2] = (unsigned char)((v) >> 8) & 
0xff), \
+                                        ((p)[3] = (unsigned char)(v) & 0xff))
 
 /* Macros for parsing a DNS header */
 #define DNS_HEADER_QID(h)              DNS__16BIT(h)
Index: ares_init.c
===================================================================
--- ares_init.c (revision 6386)
+++ ares_init.c (working copy)
@@ -42,6 +42,7 @@
 #endif
 
 #include "ares.h"
+#include "ares_local.h"
 #include "ares_private.h"
 
 static int init_by_options(ares_channel channel, struct ares_options *options,
@@ -127,8 +128,10 @@
          RegCloseKey(hKey);
                }
        }
+  ares_local_init(w32hostspath);
+#else
+  ares_local_init(PATH_HOSTS);
 #endif
- //  struct timeval tv;
 
   channel = malloc(sizeof(struct ares_channeldata));
   if (!channel)
Index: ares_local.c
===================================================================
--- ares_local.c        (revision 6386)
+++ ares_local.c        (working copy)
@@ -1,8 +1,89 @@
 #include "ares.h"
+#include "ares_dns.h"
 #include "ares_local.h"
+#include "ares_private.h"
 
 
+/* Use simplified structure instead of hostent for efficiency and simplicity */
+typedef struct _hosts_ent {
+  char    *name;
+  struct in_addr  addr;
+} hosts_ent;
 
+/* Maximum number of locally defined hosts entries + 1. Remaining entries are 
ignored. */
+#define LOCAL_HOSTS_TABLE_SZ  64
+
+static hosts_ent local_hosts[LOCAL_HOSTS_TABLE_SZ];
+
+
+int ares_local_init(const char *etc_hosts)
+{
+  FILE *fp;
+  int status = ARES_ENOMEM;
+  struct hostent *host;
+  hosts_ent *cur_host = local_hosts;
+
+  if (!(fp = fopen(etc_hosts, "r")))
+    return ARES_ENOTFOUND;
+
+  while (cur_host <= local_hosts + LOCAL_HOSTS_TABLE_SZ - 2 /* leave space for 
NULL terminator */ &&
+         (status = ares__get_hostent(fp, &host)) == ARES_SUCCESS)
+  {
+    if (host == NULL)
+      continue; /* to be on the safe side */
+
+    cur_host->name = strdup(host->h_name);
+    cur_host->addr.s_addr = *((ULONG *)(host->h_addr));
+
+    ares_free_hostent(host);
+
+    ++cur_host;
+  }
+
+  fclose(fp);
+
+  cur_host->name = NULL;
+
+  if (status != ARES_EOF)
+    status = ARES_ENOMEM;
+  else
+    status = ARES_SUCCESS;
+
+  return status;
+}
+
+void ares_local_destroy()
+{
+  hosts_ent *cur_host = local_hosts;
+
+  while (cur_host->name != NULL)
+  {
+    free(cur_host->name);
+    ++cur_host;
+  }
+}
+
+/* Note: Put most used entries to the top of /etc/hosts file for better 
performance. */
+static int ares_local_lookup(const char *name, struct in_addr *addr)
+{
+  hosts_ent *cur_host = local_hosts;
+
+  while (cur_host->name != NULL)
+  {
+    if (strcasecmp(cur_host->name, name) == 0)
+    {
+      *addr = cur_host->addr;
+      break;
+    }
+    ++cur_host;
+  }
+
+  if (cur_host->name == NULL)
+    return ARES_ENOTFOUND;
+
+  return ARES_SUCCESS;
+}
+
 int ares_local_gethostbyname(ares_channel channel, const char *name, int 
family,
                               ares_host_callback callback, void *arg)
 {
@@ -18,10 +99,56 @@
 }
 
 int ares_local_query(ares_channel channel, const char *name, int dnsclass,
-                      int type, ares_callback callback, void *arg)
+                     int type, ares_callback callback, void *arg)
 {
-   (void)(channel,name,dnsclass,type,callback,arg);
-   return 0;
+  unsigned char *qbuf, *abuf, *rr;
+  int qlen, alen, status;
+  struct in_addr addr;
+
+  if (type != T_A || ares_local_lookup(name, &addr) != ARES_SUCCESS)
+  {
+    return 0;
+  }
+
+
+  /* Compose the query. */
+
+  if ((status = ares_mkquery(name, dnsclass, type, channel->next_id++,
+                             !(channel->flags & ARES_FLAG_NORECURSE), &qbuf, 
&qlen)) != ARES_SUCCESS)
+  {
+    callback(arg, status, NULL, 0);
+    return 1;
+  }
+
+
+  /* Create the answer */
+
+  alen = qlen + 16;
+  if ((abuf = realloc(qbuf, alen)) == NULL)
+  {
+    free(qbuf);
+    callback(arg, ARES_ENOMEM, NULL, 0);
+    return 1;
+  }
+
+  DNS_HEADER_SET_QR(abuf, 1);
+  DNS_HEADER_SET_RCODE(abuf, NOERROR);
+  DNS_HEADER_SET_ANCOUNT(abuf, 1);
+
+  rr = abuf + qlen;
+  DNS__SET16BIT(rr, 0xc00c);  /* XXX Copied from A response without 
understanding :( */
+  rr += 2;
+  DNS_RR_SET_TYPE(rr, T_A);
+  DNS_RR_SET_CLASS(rr, C_IN);
+  DNS_RR_SET_TTL(rr, 0xFFFFFFFF); /* We don't reload hosts entries 
dynamically. So, use maximum TTL. */
+  DNS_RR_SET_LEN(rr, 4);
+
+  DNS__SET32BIT(rr + RRFIXEDSZ, htonl(addr.s_addr));
+
+  callback(arg, ARES_SUCCESS, abuf, alen);
+
+  free(abuf);
+  return 1;
 }
 
 void ares_local_process_requests()
Index: ares_local.h
===================================================================
--- ares_local.h        (revision 6386)
+++ ares_local.h        (working copy)
@@ -1,5 +1,9 @@
 #ifndef ARES_LOCAL__H
 #define ARES_LOCAL__H
+
+int ares_local_init(const char *etc_hosts);
+void ares_local_destroy();
+
 /*
 ** Define a suite of callbacks that can return locally configured DNS results.
 ** Very useful for testing or simulation.

-- 
...Bye..Dmitry.