]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/dns.cpp
Make threaded dns stable by placing some mutexes around some stl stuff (this really...
[user/henk/code/inspircd.git] / src / dns.cpp
index 025f4979c5ebba985abe56cda34797d1436117d7..a6e41307170d513938cba0f29795373935c5a97c 100644 (file)
@@ -1,22 +1,31 @@
-/*
-dns.cpp - based on the firedns library Copyright (C) 2002 Ian Gulliver
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of version 2 of the GNU General Public License as
-published by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+/*       +------------------------------------+
+ *       | Inspire Internet Relay Chat Daemon |
+ *       +------------------------------------+
+ *
+ *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
+ *                       E-mail:
+ *                <brain@chatspike.net>
+ *                <Craig@chatspike.net>
+ *     
+ * Written by Craig Edwards, Craig McLure, and others.
+ * This program is free but copyrighted software; see
+ *            the file COPYING for details.
+ *
+ * ---------------------------------------------------
+ */
 
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+/*
+dns.cpp - Nonblocking DNS functions.
+Very loosely based on the firedns library,
+Copyright (C) 2002 Ian Gulliver.
+
+There have been so many modifications to this file
+to make it fit into InspIRCd and make it object
+orientated that you should not take this code as
+being what firedns really looks like. It used to
+look very different to this! :-P
 */
 
-#define _DNS_C
-
 using namespace std;
 
 #include <string>
@@ -36,74 +45,79 @@ using namespace std;
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <map>
+#include <algorithm>
 #include "dns.h"
 #include "inspircd.h"
 #include "helperfuncs.h"
+#include "inspircd_config.h"
 #include "socketengine.h"
+#include "configreader.h"
+
+#ifdef THREADED_DNS
+pthread_mutex_t connmap_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
 
 extern InspIRCd* ServerInstance;
 extern ServerConfig* Config;
+extern time_t TIME;
+extern userrec* fd_ref_table[MAX_DESCRIPTORS];
 
-#define max(a,b) (a > b ? a : b)
-#define min(a,b) (a < b ? a : b)
-
-enum QueryTypes { DNS_QRY_A = 1, DNS_QRY_PTR = 12};
+enum QueryType { DNS_QRY_A = 1, DNS_QRY_PTR = 12 };
 enum QueryFlags1 { FLAGS1_MASK_RD = 0x01, FLAGS1_MASK_TC = 0x02, FLAGS1_MASK_AA = 0x04, FLAGS1_MASK_OPCODE = 0x78, FLAGS1_MASK_QR = 0x80 };
 enum QueryFlags2 { FLAGS2_MASK_RCODE = 0x0F, FLAGS2_MASK_Z = 0x70, FLAGS2_MASK_RA = 0x80 };
 
-#define DNS_ALIGN (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long))
-#define RESULTSIZE 1024
+class s_connection;
 
-static struct in_addr servers4[8]; /* up to 8 nameservers; populated by dns_init() */
-static int i4; /* actual count of nameservers; set by dns_init() */
+typedef std::map<int,s_connection*> connlist;
+typedef connlist::iterator connlist_iter;
+connlist connections;
+
+Resolver* dns_classes[MAX_DESCRIPTORS];
 
-static int initdone = 0; /* to ensure dns_init() only runs once (on the first call) */
-static int wantclose = 0;
-static int lastcreate = -1;
+struct in_addr servers4[8];
+int i4;
+int initdone = 0;
+int lastcreate = -1;
 
 class s_connection
 {
  public:
-       unsigned char id[2];
-       unsigned int _class;
-       unsigned int type;
-       int want_list;
-       int fd; /* file descriptor returned from sockets */
+       unsigned char   id[2];
+       unsigned int    _class;
+       QueryType       type;
+       int             want_list;
+       int             fd;
 };
 
 class s_rr_middle
 {
  public:
-       unsigned int type;
-       unsigned int _class;
-       unsigned long ttl;
-       unsigned int rdlength;
+       QueryType       type;
+       unsigned int    _class;
+       unsigned long   ttl;
+       unsigned int    rdlength;
 };
 
-typedef std::map<int,s_connection*> connlist;
-typedef connlist::iterator connlist_iter;
-connlist connections;
-
 class s_header
 {
  public:
-       unsigned char id[2];
-       unsigned int flags1;
-       unsigned int flags2;
-       unsigned int qdcount;
-       unsigned int ancount;
-       unsigned int nscount;
-       unsigned int arcount;
-       unsigned char payload[512]; /* DNS question, populated by dns_build_query_payload() */
+       unsigned char   id[2];
+       unsigned int    flags1;
+       unsigned int    flags2;
+       unsigned int    qdcount;
+       unsigned int    ancount;
+       unsigned int    nscount;
+       unsigned int    arcount;
+       unsigned char   payload[512];
 };
 
-extern time_t TIME;
 
-void *dns_align(void *inp) {
+void *dns_align(void *inp)
+{
        char *p = (char*)inp;
-       int offby = ((char *)p - (char *)0) % DNS_ALIGN;
+       int offby = ((char *)p - (char *)0) % (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long));
        if (offby != 0)
-               return p + (DNS_ALIGN - offby);
+               return p + ((sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long)) - offby);
        else
                return p;
 }
@@ -115,14 +129,16 @@ void *dns_align(void *inp) {
  * but of course, more impressive). Also made these inline.
  */
 
-inline void dns_fill_rr(s_rr_middle* rr, const unsigned char *input) {
-       rr->type = (input[0] << 8) + input[1];
+inline void dns_fill_rr(s_rr_middle* rr, const unsigned char *input)
+{
+       rr->type = (QueryType)((input[0] << 8) + input[1]);
        rr->_class = (input[2] << 8) + input[3];
        rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
        rr->rdlength = (input[8] << 8) + input[9];
 }
 
-inline void dns_fill_header(s_header *header, const unsigned char *input, const int l) {
+inline void dns_fill_header(s_header *header, const unsigned char *input, const int l)
+{
        header->id[0] = input[0];
        header->id[1] = input[1];
        header->flags1 = input[2];
@@ -134,7 +150,8 @@ inline void dns_fill_header(s_header *header, const unsigned char *input, const
        memcpy(header->payload,&input[12],l);
 }
 
-inline void dns_empty_header(unsigned char *output, const s_header *header, const int l) {
+inline void dns_empty_header(unsigned char *output, const s_header *header, const int l)
+{
        output[0] = header->id[0];
        output[1] = header->id[1];
        output[2] = header->flags1;
@@ -150,21 +167,20 @@ inline void dns_empty_header(unsigned char *output, const s_header *header, cons
        memcpy(&output[12],header->payload,l);
 }
 
-void dns_close(int fd) { /* close query */
+void dns_close(int fd)
+{
 #ifndef THREADED_DNS
-        ServerInstance->SE->DelFd(fd);
+       if (ServerInstance && ServerInstance->SE)
+               ServerInstance->SE->DelFd(fd);
 #endif
        log(DEBUG,"DNS: dns_close on fd %d",fd);
-       if (fd == lastcreate) {
-               wantclose = 1;
-               return;
-       }
        shutdown(fd,2);
        close(fd);
        return;
 }
 
-void DNS::dns_init() {
+void DNS::dns_init()
+{
        FILE *f;
        int i;
        in_addr addr4;
@@ -180,11 +196,13 @@ void DNS::dns_init() {
        if (f == NULL)
                return;
        while (fgets(buf,1024,f) != NULL) {
-               if (strncmp(buf,"nameserver",10) == 0) {
+               if (strncmp(buf,"nameserver",10) == 0)
+               {
                        i = 10;
                        while (buf[i] == ' ' || buf[i] == '\t')
                                i++;
-                       if (i4 < 8) {
+                       if (i4 < 8)
+                       {
                                if (dns_aton4_s(&buf[i],&addr4) != NULL)
                                        memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
                        }
@@ -195,16 +213,16 @@ void DNS::dns_init() {
 
 void DNS::dns_init_2(const char* dnsserver)
 {
-        in_addr addr4;
-        i4 = 0;
-        srand((unsigned int) TIME);
-        memset(servers4,'\0',sizeof(in_addr) * 8);
-        if (dns_aton4_s(dnsserver,&addr4) != NULL)
-            memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
+       in_addr addr4;
+       i4 = 0;
+       srand((unsigned int) TIME);
+       memset(servers4,'\0',sizeof(in_addr) * 8);
+       if (dns_aton4_s(dnsserver,&addr4) != NULL)
+           memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
 }
 
 
-static int dns_send_requests(const s_header *h, const s_connection *s, const int l)
+int dns_send_requests(const s_header *h, const s_connection *s, const int l)
 {
        int i;
        sockaddr_in addr4;
@@ -228,7 +246,8 @@ static int dns_send_requests(const s_header *h, const s_connection *s, const int
        return 0;
 }
 
-static s_connection *dns_add_query(s_header *h) { /* build DNS query, add to list */
+s_connection *dns_add_query(s_header *h)
+{
 
        s_connection * s = new s_connection;
        int id = rand() % 65536;
@@ -242,49 +261,51 @@ static s_connection *dns_add_query(s_header *h) { /* build DNS query, add to lis
        h->ancount = 0;
        h->nscount = 0;
        h->arcount = 0;
-
-       /* turn off want_list by default */
        s->want_list = 0;
-
-       /* try to create ipv6 or ipv4 socket */
-               s->fd = socket(PF_INET, SOCK_DGRAM, 0);
-               if (s->fd != -1) {
-                       if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0) {
-                               shutdown(s->fd,2);
-                               close(s->fd);
-                               s->fd = -1;
-                       }
-               }
-               if (s->fd != -1) {
-                       sockaddr_in addr;
-                       memset(&addr,0,sizeof(addr));
-                       addr.sin_family = AF_INET;
-                       addr.sin_port = 0;
-                       addr.sin_addr.s_addr = INADDR_ANY;
-                       if (bind(s->fd,(sockaddr *)&addr,sizeof(addr)) != 0) {
-                               shutdown(s->fd,2);
-                               close(s->fd);
-                               s->fd = -1;
-                       }
+       s->fd = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s->fd != -1)
+       {
+               if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0)
+               {
+                       shutdown(s->fd,2);
+                       close(s->fd);
+                       s->fd = -1;
                }
-               if (s->fd == -1) {
-                       delete s;
-                       return NULL;
+       }
+       if (s->fd != -1)
+       {
+               sockaddr_in addr;
+               memset(&addr,0,sizeof(addr));
+               addr.sin_family = AF_INET;
+               addr.sin_port = 0;
+               addr.sin_addr.s_addr = INADDR_ANY;
+               if (bind(s->fd,(sockaddr *)&addr,sizeof(addr)) != 0)
+               {
+                       shutdown(s->fd,2);
+                       close(s->fd);
+                       s->fd = -1;
                }
+       }
+       if (s->fd == -1)
+       {
+               DELETE(s);
+               return NULL;
+       }
        /* create new connection object, add to linked list */
+#ifdef THREADED_DNS
+       pthread_mutex_lock(&connmap_lock);
+#endif
        if (connections.find(s->fd) == connections.end())
                connections[s->fd] = s;
+#ifdef THREADED_DNS
+       pthread_mutex_unlock(&connmap_lock);
+#endif
 
-       if (wantclose == 1) {
-               shutdown(lastcreate,2);
-               close(lastcreate);
-               wantclose = 0;
-       }
-       lastcreate = s->fd;
        return s;
 }
 
-static int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload) { 
+int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
+{
        short payloadpos;
        const char * tempchr, * tempchr2;
        unsigned short l;
@@ -293,7 +314,8 @@ static int dns_build_query_payload(const char * const name, const unsigned short
        tempchr2 = name;
 
        /* split name up into labels, create query */
-       while ((tempchr = strchr(tempchr2,'.')) != NULL) {
+       while ((tempchr = strchr(tempchr2,'.')) != NULL)
+       {
                l = tempchr - tempchr2;
                if (payloadpos + l + 1 > 507)
                        return -1;
@@ -303,7 +325,8 @@ static int dns_build_query_payload(const char * const name, const unsigned short
                tempchr2 = &tempchr[1];
        }
        l = strlen(tempchr2);
-       if (l) {
+       if (l)
+       {
                if (payloadpos + l + 2 > 507)
                        return -1;
                payload[payloadpos++] = l;
@@ -320,7 +343,8 @@ static int dns_build_query_payload(const char * const name, const unsigned short
        return payloadpos + 4;
 }
 
-in_addr* DNS::dns_aton4(const char * const ipstring) { /* ascii to numeric: convert string to static 4part IP addr struct */
+in_addr* DNS::dns_aton4(const char * const ipstring)
+{
        static in_addr ip;
        return dns_aton4_s(ipstring,&ip);
 }
@@ -328,8 +352,9 @@ in_addr* DNS::dns_aton4(const char * const ipstring) { /* ascii to numeric: conv
 in_addr* DNS::dns_aton4_r(const char *ipstring) { /* ascii to numeric (reentrant): convert string to new 4part IP addr struct */
        in_addr* ip;
        ip = new in_addr;
-       if(dns_aton4_s(ipstring,ip) == NULL) {
-               delete ip;
+       if(dns_aton4_s(ipstring,ip) == NULL)
+       {
+               DELETE(ip);
                return NULL;
        }
        return ip;
@@ -369,7 +394,6 @@ int DNS::dns_getip4list(const char *name) { /* build, add and send A query; retr
 
        dns_init();
        
-
        l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
        if (l == -1)
                return -1;
@@ -439,6 +463,14 @@ char* DNS::dns_getresult_s(const int cfd, char *res) { /* retrieve result of DNS
                *res = 0;
 
        /* FireDNS used a linked list for this. How ugly (and slow). */
+
+#ifdef THREADED_DNS
+       /* XXX: STL really does NOT like being poked and prodded in more than
+        * one orifice by threaded apps. Make sure we remain nice to it, and
+        * lock a mutex around any access to the std::map.
+        */
+       pthread_mutex_lock(&connmap_lock);
+#endif
        connlist_iter n_iter = connections.find(cfd);
        if (n_iter == connections.end())
        {
@@ -452,83 +484,104 @@ char* DNS::dns_getresult_s(const int cfd, char *res) { /* retrieve result of DNS
                /* We don't delete c here, because its done later when needed */
                connections.erase(n_iter);
        }
+#ifdef THREADED_DNS
+       pthread_mutex_unlock(&connmap_lock);
+#endif
 
        l = recv(c->fd,buffer,sizeof(s_header),0);
        dns_close(c->fd);
-       if (l < 12) {
-               delete c;
+       if (l < 12)
+       {
+               DELETE(c);
                return NULL;
        }
        dns_fill_header(&h,buffer,l - 12);
-       if (c->id[0] != h.id[0] || c->id[1] != h.id[1]) {
+       if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
+       {
                log(DEBUG,"DNS: id mismatch on query");
-               delete c;
+               DELETE(c);
                return NULL; /* ID mismatch */
        }
-       if ((h.flags1 & FLAGS1_MASK_QR) == 0) {
+       if ((h.flags1 & FLAGS1_MASK_QR) == 0)
+       {
                log(DEBUG,"DNS: didnt get a query result");
-               delete c;
+               DELETE(c);
                return NULL;
        }
-       if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0) {
+       if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
+       {
                log(DEBUG,"DNS: got an OPCODE and didnt want one");
-               delete c;
+               DELETE(c);
                return NULL;
        }
-       if ((h.flags2 & FLAGS2_MASK_RCODE) != 0) {
+       if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
+       {
                log(DEBUG,"DNS lookup failed due to SERVFAIL");
-               delete c;
+               DELETE(c);
                return NULL;
        }
-       if (h.ancount < 1)  { /* no sense going on if we don't have any answers */
+       if (h.ancount < 1)
+       {
                log(DEBUG,"DNS: no answers!");
-               delete c;
+               DELETE(c);
                return NULL;
        }
-       /* skip queries */
        i = 0;
        q = 0;
        l -= 12;
-       while ((unsigned)q < h.qdcount && i < l) {
-               if (h.payload[i] > 63) { /* pointer */
-                       i += 6; /* skip pointer, _class and type */
+       while ((unsigned)q < h.qdcount && i < l)
+       {
+               if (h.payload[i] > 63)
+               {
+                       i += 6;
                        q++;
-               } else { /* label */
-                       if (h.payload[i] == 0) {
+               }
+               else
+               {
+                       if (h.payload[i] == 0)
+                       {
                                q++;
-                               i += 5; /* skip nil, _class and type */
-                       } else
-                               i += h.payload[i] + 1; /* skip length and label */
+                               i += 5;
+                       }
+                       else i += h.payload[i] + 1;
                }
        }
-       /* &h.payload[i] should now be the start of the first response */
        curanswer = 0;
-       while ((unsigned)curanswer < h.ancount) {
+       while ((unsigned)curanswer < h.ancount)
+       {
                q = 0;
-               while (q == 0 && i < l) {
-                       if (h.payload[i] > 63) { /* pointer */
-                               i += 2; /* skip pointer */
+               while (q == 0 && i < l)
+               {
+                       if (h.payload[i] > 63)
+                       {
+                               i += 2;
                                q = 1;
-                       } else { /* label */
-                               if (h.payload[i] == 0) {
+                       }
+                       else
+                       {
+                               if (h.payload[i] == 0)
+                               {
                                        i++;
                                        q = 1;
-                               } else
-                                       i += h.payload[i] + 1; /* skip length and label */
+                               }
+                               else i += h.payload[i] + 1; /* skip length and label */
                        }
                }
-               if (l - i < 10) {
-                       delete c;
+               if (l - i < 10)
+               {
+                       DELETE(c);
                        return NULL;
                }
                dns_fill_rr(&rr,&h.payload[i]);
                i += 10;
-               if (rr.type != c->type) {
+               if (rr.type != c->type)
+               {
                        curanswer++;
                        i += rr.rdlength;
                        continue;
                }
-               if (rr._class != c->_class) {
+               if (rr._class != c->_class)
+               {
                        curanswer++;
                        i += rr.rdlength;
                        continue;
@@ -542,19 +595,28 @@ char* DNS::dns_getresult_s(const int cfd, char *res) { /* retrieve result of DNS
        if (rr.rdlength > 1023)
                return NULL;
 
-       switch (rr.type) {
+       switch (rr.type)
+       {
                case DNS_QRY_PTR:
                        log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
                        o = 0;
                        q = 0;
-                       while (q == 0 && i < l && o + 256 < 1023) {
-                               if (h.payload[i] > 63) { /* pointer */
+                       while (q == 0 && i < l && o + 256 < 1023)
+                       {
+                               if (h.payload[i] > 63)
+                               {
+                                       log(DEBUG,"DNS: h.payload[i] > 63");
                                        memcpy(&p,&h.payload[i],2);
                                        i = ntohs(p) - 0xC000 - 12;
-                               } else { /* label */
+                               }
+                               else
+                               {
                                        if (h.payload[i] == 0)
+                                       {
                                                q = 1;
-                                       else {
+                                       }
+                                       else
+                                       {
                                                res[o] = '\0';
                                                if (o != 0)
                                                        res[o++] = '.';
@@ -565,42 +627,48 @@ char* DNS::dns_getresult_s(const int cfd, char *res) { /* retrieve result of DNS
                                }
                        }
                        res[o] = '\0';
-                       break;
+               break;
                case DNS_QRY_A:
                        log(DEBUG,"DNS: got a result of type DNS_QRY_A");
-                       if (c->want_list) {
+                       if (c->want_list)
+                       {
                                dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
-                               while ((char *)alist - (char *)res < 700) {
+                               while ((char *)alist - (char *)res < 700)
+                               {
                                        if (rr.type != DNS_QRY_A)
                                                break;
                                        if (rr._class != 1)
                                                break;
-                                       if (rr.rdlength != 4) {
-                                               delete c;
+                                       if (rr.rdlength != 4)
+                                       {
+                                               DELETE(c);
                                                return NULL;
                                        }
                                        memcpy(&alist->ip,&h.payload[i],4);
                                        if ((unsigned)++curanswer >= h.ancount)
                                                break;
                                        i += rr.rdlength;
+                                       q = 0;
+                                       while (q == 0 && i < l)
                                        {
-                                               /* skip next name */
-                                               q = 0;
-                                               while (q == 0 && i < l) {
-                                                       if (h.payload[i] > 63) { /* pointer */
-                                                               i += 2; /* skip pointer */
+                                               if (h.payload[i] > 63)
+                                               {
+                                                       i += 2;
+                                                       q = 1;
+                                               }
+                                               else
+                                               {
+                                                       if (h.payload[i] == 0)
+                                                       {
+                                                               i++;
                                                                q = 1;
-                                                       } else { /* label */
-                                                               if (h.payload[i] == 0) {
-                                                                       i++;
-                                                                       q = 1;
-                                                               } else
-                                                                       i += h.payload[i] + 1; /* skip length and label */
                                                        }
+                                                       else i += h.payload[i] + 1;
                                                }
                                        }
-                                       if (l - i < 10) {
-                                               delete c;
+                                       if (l - i < 10)
+                                       {
+                                               DELETE(c);
                                                return NULL;
                                        }
                                        dns_fill_rr(&rr,&h.payload[i]);
@@ -612,16 +680,15 @@ char* DNS::dns_getresult_s(const int cfd, char *res) { /* retrieve result of DNS
                                alist->next = NULL;
                                break;
                        }
-                       goto defaultcase;
+                       memcpy(res,&h.payload[i],rr.rdlength);
+                       res[rr.rdlength] = '\0';
                        break;
                default:
-               defaultcase:
-                       log(DEBUG,"DNS: doing something with result 'default'");
                        memcpy(res,&h.payload[i],rr.rdlength);
                        res[rr.rdlength] = '\0';
                        break;
        }
-       delete c;
+       DELETE(c);
        return res;
 }
 
@@ -631,13 +698,13 @@ DNS::DNS()
        log(DEBUG,"Create blank DNS");
 }
 
-DNS::DNS(std::string dnsserver)
+DNS::DNS(const std::string &dnsserver)
 {
        dns_init_2(dnsserver.c_str());
-       log(DEBUG,"Create DNS");
+       log(DEBUG,"Create DNS with server '%s'",dnsserver.c_str());
 }
 
-void DNS::SetNS(std::string dnsserver)
+void DNS::SetNS(const std::string &dnsserver)
 {
        dns_init_2(dnsserver.c_str());
        log(DEBUG,"Set NS");
@@ -647,29 +714,36 @@ DNS::~DNS()
 {
 }
 
-bool DNS::ReverseLookup(std::string ip)
+bool DNS::ReverseLookup(const std::string &ip, bool ins)
 {
-       ServerInstance->stats->statsDns++;
-        binip = dns_aton4(ip.c_str());
-        if (binip == NULL) {
-                return false;
-        }
+       if (ServerInstance && ServerInstance->stats)
+               ServerInstance->stats->statsDns++;
+       binip = dns_aton4(ip.c_str());
+       if (binip == NULL)
+       {
+               return false;
+       }
 
-        this->myfd = dns_getname4(binip);
+       this->myfd = dns_getname4(binip);
        if (this->myfd == -1)
        {
                return false;
        }
        log(DEBUG,"DNS: ReverseLookup, fd=%d",this->myfd);
 #ifndef THREADED_DNS
-       ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
+       if (ins)
+       {
+               if (ServerInstance && ServerInstance->SE)
+                       ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
+       }
 #endif
        return true;
 }
 
-bool DNS::ForwardLookup(std::string host)
+bool DNS::ForwardLookup(const std::string &host, bool ins)
 {
-       ServerInstance->stats->statsDns++;
+       if (ServerInstance && ServerInstance->stats)
+               ServerInstance->stats->statsDns++;
        this->myfd = dns_getip4(host.c_str());
        if (this->myfd == -1)
        {
@@ -677,11 +751,31 @@ bool DNS::ForwardLookup(std::string host)
        }
        log(DEBUG,"DNS: ForwardLookup, fd=%d",this->myfd);
 #ifndef THREADED_DNS
-       ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
+       if (ins)
+       {
+               if (ServerInstance && ServerInstance->SE)
+                       ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
+       }
 #endif
        return true;
 }
 
+bool DNS::ForwardLookupWithFD(const std::string &host, int &fd)
+{
+       if (ServerInstance && ServerInstance->stats)
+               ServerInstance->stats->statsDns++;
+       this->myfd = dns_getip4(host.c_str());
+       fd = this->myfd;
+       if (this->myfd == -1)
+       {
+               return false;
+       }
+       log(DEBUG,"DNS: ForwardLookupWithFD, fd=%d",this->myfd);
+       if (ServerInstance && ServerInstance->SE)
+               ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_MODULE);
+       return true;
+}
+
 bool DNS::HasResult(int fd)
 {
        return (fd == this->myfd);
@@ -710,16 +804,23 @@ int DNS::GetFD()
 std::string DNS::GetResult()
 {
        log(DEBUG,"DNS: GetResult()");
-        result = dns_getresult(this->myfd);
-        if (result) {
-               ServerInstance->stats->statsDnsGood++;
+       result = dns_getresult(this->myfd);
+       if (result)
+       {
+               if (ServerInstance && ServerInstance->stats)
+                       ServerInstance->stats->statsDnsGood++;
                dns_close(this->myfd);
+               this->myfd = -1;
                return result;
-        } else {
-               ServerInstance->stats->statsDnsBad++;
+       }
+       else
+       {
+               if (ServerInstance && ServerInstance->stats)
+                       ServerInstance->stats->statsDnsBad++;
                if (this->myfd != -1)
                {
                        dns_close(this->myfd);
+                       this->myfd = -1;
                }
                return "";
        }
@@ -733,10 +834,12 @@ std::string DNS::GetResultIP()
        if (this->myfd != -1)
        {
                dns_close(this->myfd);
+               this->myfd = -1;
        }
        if (result)
        {
-               ServerInstance->stats->statsDnsGood++;
+               if (ServerInstance && ServerInstance->stats)
+                       ServerInstance->stats->statsDnsGood++;
                unsigned char a = (unsigned)result[0];
                unsigned char b = (unsigned)result[1];
                unsigned char c = (unsigned)result[2];
@@ -746,7 +849,8 @@ std::string DNS::GetResultIP()
        }
        else
        {
-               ServerInstance->stats->statsDnsBad++;
+               if (ServerInstance && ServerInstance->stats)
+                       ServerInstance->stats->statsDnsBad++;
                log(DEBUG,"DANGER WILL ROBINSON! NXDOMAIN for forward lookup, but we got a reverse lookup!");
                return "";
        }
@@ -755,42 +859,192 @@ std::string DNS::GetResultIP()
 
 
 #ifdef THREADED_DNS
+
+/* This function is a thread function which can be thought of as a lightweight process
+ * to all you non-threaded people. In actuality its so much more, and pretty damn cool.
+ * With threaded dns enabled, each user which connects gets a thread attached to their
+ * user record when their DNS lookup starts. This function starts in parallel, and
+ * commences a blocking dns lookup. Because its a seperate thread, this occurs without
+ * actually blocking the main application. Once the dns lookup is completed, the thread
+ * checks if the user is still around by checking their fd against the reference table,
+ * and if they are, writes the hostname into the struct and terminates, after setting
+ * userrec::dns_done to true. Because this is multi-threaded it can make proper use of
+ * SMP setups (like the one i have here *grin*).
+ * This is in comparison to the non-threaded dns, which must monitor the thread sockets
+ * in a nonblocking fashion, consuming more resources to do so.
+ */
 void* dns_task(void* arg)
 {
-        userrec* u = (userrec*)arg;
-        log(DEBUG,"DNS thread for user %s",u->nick);
-        DNS dns1;
-        DNS dns2;
-        std::string host;
-        std::string ip;
-        if (dns1.ReverseLookup(u->ip))
-        {
-                while (!dns1.HasResult())
-                {
-                        usleep(100);
-                }
-                host = dns1.GetResult();
-                if (host != "")
-                {
-                        if (dns2.ForwardLookup(host))
-                        {
-                                while (!dns2.HasResult())
-                                {
-                                        usleep(100);
-                                }
-                                ip = dns2.GetResultIP();
-                                if (ip == std::string(u->ip))
-                                {
-                                        if (host.length() < 160)
-                                        {
-                                                strcpy(u->host,host.c_str());
-                                                strcpy(u->dhost,host.c_str());
-                                        }
-                                }
-                        }
-                }
-        }
-        u->dns_done = true;
-        return NULL;
+       userrec* u = (userrec*)arg;
+       int thisfd = u->fd;
+
+       log(DEBUG,"DNS thread for user %s",u->nick);
+       DNS dns1;
+       DNS dns2;
+       std::string host;
+       std::string ip;
+       if (dns1.ReverseLookup(inet_ntoa(u->ip4),false))
+       {
+               while (!dns1.HasResult())
+                       usleep(100);
+               host = dns1.GetResult();
+               if (host != "")
+               {
+                       if (dns2.ForwardLookup(host, false))
+                       {
+                               while (!dns2.HasResult())
+                                       usleep(100);
+                               ip = dns2.GetResultIP();
+                               if (ip == std::string(inet_ntoa(u->ip4)))
+                               {
+                                       if (host.length() < 65)
+                                       {
+                                               if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
+                                               {
+                                                       if (!u->dns_done)
+                                                       {
+                                                               strcpy(u->host,host.c_str());
+                                                               if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
+                                                               {
+                                                                       strcpy(u->dhost,host.c_str());
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
+               u->dns_done = true;
+       return NULL;
 }
 #endif
+
+Resolver::Resolver(const std::string &source, bool forward, const std::string &dnsserver = "") : input(source), fwd(forward), server(dnsserver)
+{
+       if (this->server != "")
+               Query.SetNS(this->server);
+       else
+               Query.SetNS(Config->DNSServer);
+
+       if (forward)
+       {
+               Query.ForwardLookup(input.c_str(), false);
+               this->fd = Query.GetFD();
+       }
+       else
+       {
+               Query.ReverseLookup(input.c_str(), false);
+               this->fd = Query.GetFD();
+       }
+       if (fd < 0)
+       {
+               log(DEBUG,"Resolver::Resolver: RESOLVER_NSDOWN");
+               this->OnError(RESOLVER_NSDOWN);
+               ModuleException e("Resolver: Nameserver is down");
+               throw e;
+               /* We shouldnt get here really */
+               return;
+       }
+
+       if (ServerInstance && ServerInstance->SE)
+       {
+               log(DEBUG,"Resolver::Resolver: this->fd=%d",this->fd);
+               ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_CLASSDNS);
+       }
+       else
+       {
+               log(DEBUG,"Resolver::Resolver: RESOLVER_NOTREADY");
+               this->OnError(RESOLVER_NOTREADY);
+               ModuleException e("Resolver: Core not initialized yet");
+               throw e;
+               /* We shouldnt get here really */
+               return;
+       }
+}
+
+Resolver::~Resolver()
+{
+       log(DEBUG,"Resolver::~Resolver");
+       if (ServerInstance && ServerInstance->SE)
+               ServerInstance->SE->DelFd(this->fd);
+}
+
+int Resolver::GetFd()
+{
+       return this->fd;
+}
+
+bool Resolver::ProcessResult()
+{
+       log(DEBUG,"Resolver::ProcessResult");
+       if (this->fwd)
+               result = Query.GetResultIP();
+       else
+               result = Query.GetResult();
+
+       if (result != "")
+       {
+               log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
+               this->OnLookupComplete(result);
+               return true;
+       }
+       else
+       {
+               log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
+               this->OnError(RESOLVER_NXDOMAIN);
+               return false;
+       }
+}
+
+void Resolver::OnLookupComplete(const std::string &result)
+{
+}
+
+void Resolver::OnError(ResolverError e)
+{
+}
+
+void dns_deal_with_classes(int fd)
+{
+       log(DEBUG,"dns_deal_with_classes(%d)",fd);
+       if ((fd > -1) && (dns_classes[fd]))
+       {
+               log(DEBUG,"Valid fd %d",fd);
+               dns_classes[fd]->ProcessResult();
+               delete dns_classes[fd];
+               dns_classes[fd] = NULL;
+       }
+}
+
+bool dns_add_class(Resolver* r)
+{
+       log(DEBUG,"dns_add_class");
+       if ((r) && (r->GetFd() > -1))
+       {
+               if (!dns_classes[r->GetFd()])
+               {
+                       log(DEBUG,"dns_add_class: added class");
+                       dns_classes[r->GetFd()] = r;
+                       return true;
+               }
+               else
+               {
+                       log(DEBUG,"Space occupied!");
+                       return false;
+               }
+       }
+       else
+       {
+               log(DEBUG,"Bad class");
+               delete r;
+               return true;
+       }
+}
+
+void init_dns()
+{
+       memset(dns_classes,0,sizeof(dns_classes));
+}
+