Author: Ghostwolf <foxxe@wtfs.net>
[ircu2.10.12-pk.git] / ircd / res.c
index 8f60bc4cde9e70b0ce5b9892f0066c06f0830e9e..d4d85fd848c1c021646ebeb33a00bc09864ad88d 100644 (file)
  *     added callbacks and reference counting of returned hostents.
  *     --Bleep (Thomas Helvey <tomh@inxpress.net>)
  */
+#include "config.h"
+
 #include "res.h"
 #include "client.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
+#include "ircd_events.h"
 #include "ircd_log.h"
 #include "ircd_osdep.h"
+#include "ircd_reply.h"
+#include "ircd_snprintf.h"
 #include "ircd_string.h"
 #include "msg.h"
 #include "numeric.h"
@@ -23,7 +28,6 @@
 #include "s_debug.h"
 #include "s_misc.h"
 #include "send.h"
-#include "sprintf_irc.h"
 #include "struct.h"
 #include "support.h"
 #include "sys.h"
@@ -36,6 +40,7 @@
 #include <sys/socket.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <regex.h>
 
 #include <arpa/nameser.h>
 #include <resolv.h>
  *  Nov. 17, 1997 --Bleep
  */
 
-typedef struct Hostent {
+struct Hostent {
   struct hostent h;      /* the hostent struct we are passing around */
   char*          buf;    /* buffer for data pointed to from hostent */
-} aHostent;
+};
 
 struct ResRequest {
   struct ResRequest* next;
@@ -175,7 +180,7 @@ struct ResRequest {
   struct in_addr     addr;
   char*              name;
   struct DNSQuery    query;         /* query callback for this request */
-  aHostent           he;
+  struct Hostent     he;
 };
 
 struct CacheEntry {
@@ -196,6 +201,10 @@ struct CacheTable {
 
 int ResolverFileDescriptor    = -1;   /* GLOBAL - used in s_bsd.c */
 
+static struct Socket resSock;          /* Socket describing resolver */
+static struct Timer  resExpireDNS;     /* Timer for DNS expiration */
+static struct Timer  resExpireCache;   /* Timer for cache expiration */
+
 static time_t nextDNSCheck    = 0;
 static time_t nextCacheExpire = 1;
 
@@ -218,6 +227,8 @@ static struct ResRequest* requestListTail;   /* tail of resolver request list */
 static void     add_request(struct ResRequest* request);
 static void     rem_request(struct ResRequest* request);
 static struct ResRequest*   make_request(const struct DNSQuery* query);
+static time_t   timeout_query_list(time_t now);
+static time_t   expire_cache(time_t now);
 static void     rem_cache(struct CacheEntry*);
 static void     do_query_name(const struct DNSQuery* query, 
                               const char* name, 
@@ -236,7 +247,7 @@ static struct CacheEntry*  find_cache_number(struct ResRequest* request,
                                              const char* addr);
 static struct ResRequest*   find_id(int);
 
-static  struct cacheinfo {
+static struct cacheinfo {
   int  ca_adds;
   int  ca_dels;
   int  ca_expires;
@@ -276,7 +287,7 @@ extern u_int    _getlong(const u_char *);
  *      paul vixie, 29may94
  */
 static int
-res_ourserver(const struct __res_state* statp, const struct sockaddr_in *inp) 
+res_ourserver(const struct __res_state* statp, const struct sockaddr_ininp) 
 {
   struct sockaddr_in ina;
   int ns;
@@ -294,6 +305,14 @@ res_ourserver(const struct __res_state* statp, const struct sockaddr_in *inp)
   return (0);
 }
 
+/* Socket callback for resolver */
+static void res_callback(struct Event* ev)
+{
+  assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
+
+  resolver_read();
+}
+
 /*
  * start_resolver - do everything we need to read the resolv.conf file
  * and initialize the resolver file descriptor if needed
@@ -316,7 +335,8 @@ static void start_resolver(void)
   spare_fd = open("/dev/null",O_RDONLY,0);
   if ((spare_fd < 0) || (spare_fd > 255)) {
     char sparemsg[80];
-    sprintf_irc(sparemsg, "invalid spare_fd %d", spare_fd);
+    ircd_snprintf(0, sparemsg, sizeof(sparemsg), "invalid spare_fd %d",
+                 spare_fd);
     server_restart(sparemsg);
   }
 
@@ -330,15 +350,39 @@ static void start_resolver(void)
     ResolverFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
     if (-1 == ResolverFileDescriptor) {
       report_error("Resolver: error creating socket for %s: %s", 
-                   me.name, errno);
+                   cli_name(&me), errno);
       return;
     }
     if (!os_set_nonblocking(ResolverFileDescriptor))
       report_error("Resolver: error setting non-blocking for %s: %s", 
-                   me.name, errno);
+                   cli_name(&me), errno);
+    if (!socket_add(&resSock, res_callback, 0, SS_DATAGRAM,
+                   SOCK_EVENT_READABLE, ResolverFileDescriptor))
+      report_error("Resolver: unable to queue resolver file descriptor for %s",
+                  cli_name(&me), ENFILE);
   }
 }
 
+/* Call the query timeout function */
+static void expire_DNS_callback(struct Event* ev)
+{
+  time_t next;
+
+  next = timeout_query_list(CurrentTime);
+
+  timer_add(&resExpireDNS, expire_DNS_callback, 0, TT_ABSOLUTE, next);
+}
+
+/* Call the cache expire function */
+static void expire_cache_callback(struct Event* ev)
+{
+  time_t next;
+
+  next = expire_cache(CurrentTime);
+
+  timer_add(&resExpireCache, expire_cache_callback, 0, TT_ABSOLUTE, next);
+}
+
 /*
  * init_resolver - initialize resolver and resolver library
  */
@@ -352,9 +396,16 @@ int init_resolver(void)
   memset(hashtable, 0, sizeof(hashtable));
   memset(&reinfo,   0, sizeof(reinfo));
 
-  requestListHead = requestListTail = NULL;
+  requestListHead = requestListTail = 0;
+
+  /* initiate the resolver timers */
+  timer_add(timer_init(&resExpireDNS), expire_DNS_callback, 0,
+           TT_RELATIVE, 1);
+  timer_add(timer_init(&resExpireCache), expire_cache_callback, 0,
+           TT_RELATIVE, 1);
 
   errno = h_errno = 0;
+
   start_resolver();
   Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
          ResolverFileDescriptor, errno, h_errno, 
@@ -371,6 +422,18 @@ void restart_resolver(void)
   start_resolver();
 }
 
+static int validate_hostent(const struct hostent* hp)
+{
+  const char* name;
+  int  i = 0;
+  assert(0 != hp);
+  for (name = hp->h_name; name; name = hp->h_aliases[i++]) {
+    if (!string_is_hostname(name))
+      return 0;
+  }
+  return 1;
+}
+
 /*
  * add_request - place a new request in the request list
  */
@@ -563,8 +626,7 @@ static int send_res_msg(const u_char* msg, int len, int rcount)
       ++sent;
     }
     else
-      ircd_log(L_ERROR, "Resolver: send failed %s", 
-               (strerror(errno)) ? strerror(errno) : "Unknown");
+      log_write(LS_RESOLVER, L_ERROR, 0, "Resolver: send failed %m");
   }
   return sent;
 }
@@ -656,9 +718,9 @@ static void do_query_number(const struct DNSQuery* query,
 
   assert(0 != addr);
   cp = (const unsigned char*) &addr->s_addr;
-  sprintf_irc(ipbuf, "%u.%u.%u.%u.in-addr.arpa.",
-              (unsigned int)(cp[3]), (unsigned int)(cp[2]),
-              (unsigned int)(cp[1]), (unsigned int)(cp[0]));
+  ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%u.%u.%u.%u.in-addr.arpa.",
+               (unsigned int)(cp[3]), (unsigned int)(cp[2]),
+               (unsigned int)(cp[1]), (unsigned int)(cp[0]));
 
   if (!request) {
     request              = make_request(query);
@@ -744,8 +806,10 @@ static int proc_answer(struct ResRequest* request, HEADER* header,
   u_char* current;             /* current position in buf */
   char** alias;                /* alias list */
   char** addr;                 /* address list */
+  char** base_addr;            /* original pointer to address list */
   char*  name;                 /* pointer to name string */
   char*  address;              /* pointer to address */
+  char*  base_address;         /* original pointer to address */
   char*  endp;                 /* end of our buffer */
   int    query_class;          /* answer class */
   int    type;                 /* answer type */
@@ -754,6 +818,7 @@ static int proc_answer(struct ResRequest* request, HEADER* header,
   int    n;                    /* temp count */
   int    addr_count  = 0;      /* number of addresses in hostent */
   int    alias_count = 0;      /* number of aliases in hostent */
+  int    t_ptr_seen = 0;       /* Seen a T_PTR in proc_answer? */
   struct hostent* hp;          /* hostent getting filled */
 
   assert(0 != request);
@@ -794,11 +859,14 @@ static int proc_answer(struct ResRequest* request, HEADER* header,
     ++addr;
     ++addr_count;
   }
+  base_addr = addr;
   /*
    * make address point to first available address slot
    */
   address = request->he.buf + ADDRS_OFFSET +
                     (sizeof(struct in_addr) * addr_count);
+  base_address = address;
+
   /*
    * find the end of the alias list
    */
@@ -868,7 +936,10 @@ static int proc_answer(struct ResRequest* request, HEADER* header,
     case T_A:
       /*
        * check for invalid rd_length or too many addresses
+       * ignore T_A relies if looking for a T_PTR
        */
+      if (t_ptr_seen)
+       return answer_count;
       if (rd_length != sizeof(struct in_addr))
         return answer_count;
       if (++addr_count < RES_MAXADDRS) {
@@ -892,6 +963,11 @@ static int proc_answer(struct ResRequest* request, HEADER* header,
       ++answer_count;
       break;
     case T_PTR:
+      t_ptr_seen = 1;
+      addr_count = 0;
+      addr = base_addr;
+      *addr = 0;
+      address = base_address;
       n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
       if (n < 0) {
         /*
@@ -994,8 +1070,7 @@ int resolver_read(void)
    * check against possibly fake replies
    */
   if (!res_ourserver(&_res, &sin)) {
-    Debug((DEBUG_DNS, "Resolver: fake reply from: %s",
-           (const char*) &sin.sin_addr));
+    Debug((DEBUG_DNS, "Resolver: fake reply from: %s", (const char*) &sin.sin_addr));
     ++reinfo.re_unkrep;
     return 1;
   }
@@ -1048,7 +1123,7 @@ int resolver_read(void)
   answer_count = proc_answer(request, header, buf, buf + rc);
   if (answer_count) {
     if (T_PTR == request->type) {
-      struct DNSReply* reply = NULL;
+      struct DNSReply* reply = 0;
       if (0 == request->he.h.h_name) {
         /*
          * got a PTR response with no name, something bogus is happening
@@ -1067,7 +1142,10 @@ int resolver_read(void)
        * extra kludges.
        */
       reply = gethost_byname(request->he.h.h_name, &request->query);
-      if (0 == reply) {
+      if (reply) {
+        (*request->query.callback)(request->query.vptr, reply);
+      }
+      else {
         /*
          * If name wasn't found, a request has been queued and it will
          * be the last one queued.  This is rather nasty way to keep
@@ -1078,8 +1156,6 @@ int resolver_read(void)
         request->he.buf = 0;
         memcpy(&requestListTail->he.h, &request->he.h, sizeof(struct hostent));
       }
-      else
-        (*request->query.callback)(request->query.vptr, reply);
       rem_request(request);
     }
     else {
@@ -1090,9 +1166,12 @@ int resolver_read(void)
        * PTR returned a CNAME, cp was not checked before so the
        * callback was being called with a value of 0x2C != NULL.
        */
-      cp = make_cache(request);
-      (*request->query.callback)(request->query.vptr,
-                                 (cp) ? &cp->reply : 0);
+      struct DNSReply* reply = 0;
+      if (validate_hostent(&request->he.h)) {
+        if ((cp = make_cache(request)))
+          reply = &cp->reply;
+      }
+      (*request->query.callback)(request->query.vptr, reply);
       rem_request(request);
     }
   }
@@ -1151,7 +1230,7 @@ static size_t calc_hostent_buffer_size(const struct hostent* hp)
  * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
  * the data we're putting in it.
  */
-static void dup_hostent(aHostent* new_hp, struct hostent* hp)
+static void dup_hostent(struct Hostent* new_hp, struct hostent* hp)
 {
   char*  p;
   char** ap;
@@ -1216,7 +1295,7 @@ static void dup_hostent(aHostent* new_hp, struct hostent* hp)
 /*
  * update_hostent - Add records to a Hostent struct in place.
  */
-static void update_hostent(aHostent* hp, char** addr, char** alias)
+static void update_hostent(struct Hostent* hp, char** addr, char** alias)
 {
   char*  p;
   char** ap;
@@ -1387,9 +1466,6 @@ static struct CacheEntry* add_to_cache(struct CacheEntry* ocp)
 */
 static void update_list(struct ResRequest* request, struct CacheEntry* cachep)
 {
-#if 0
-  struct CacheEntry** cpp;
-#endif
   struct CacheEntry*  cp = cachep;
   char*    s;
   char*    t;
@@ -1404,17 +1480,6 @@ static void update_list(struct ResRequest* request, struct CacheEntry* cachep)
    * If found, move the entry to the top of the list and return.
    */
   ++cainfo.ca_updates;
-#if 0
-  for (cpp = &cacheTop; *cpp; cpp = &((*cpp)->list_next)) {
-    if (cp == *cpp)
-      break;
-  }
-  if (!*cpp)
-    return;
-  *cpp = cp->list_next;
-  cp->list_next = cacheTop;
-  cacheTop = cp;
-#endif
 
   if (!request)
     return;
@@ -1548,7 +1613,7 @@ static struct CacheEntry* make_cache(struct ResRequest* request)
    * shouldn't happen but it just might...
    */
   assert(0 != hp->h_name);
-  assert(0 != hp->h_addr_list[0]);
+/*    assert(0 != hp->h_addr_list[0]); */
   if (!hp->h_name || !hp->h_addr_list[0])
     return NULL;
   /*
@@ -1723,16 +1788,16 @@ size_t cres_mem(struct Client* sptr)
       request_mem += MAXGETHOSTLEN + 1;
     ++request_count;
   }
-  /* XXX sendto_one used to send STATSDEBUG */
+
   if (cachedCount != cache_count) {
-    sendto_one(sptr, 
-               ":%s %d %s :Resolver: cache count mismatch: %d != %d",
-               me.name, RPL_STATSDEBUG, sptr->name, cachedCount, cache_count);
+    send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
+              ":Resolver: cache count mismatch: %d != %d", cachedCount,
+              cache_count);
     assert(cachedCount == cache_count);
   }
-  sendto_one(sptr, ":%s %d %s :Resolver: cache %d(%d) requests %d(%d)",
-             me.name, RPL_STATSDEBUG, sptr->name, cache_count, cache_mem,
-             request_count, request_mem);
+  send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
+            ":Resolver: cache %d(%d) requests %d(%d)", cache_count,
+            cache_mem, request_count, request_mem);
   return cache_mem + request_mem;
 }