]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/dns.cpp
Removed threaded dns (it might make a comeback some day, but as it stands its incompa...
[user/henk/code/inspircd.git] / src / dns.cpp
index a1287e49937ab8078b7b702e360e4e9f6108564e..a8268aafc4a233d6bd19b24ad93d024f6e72b202 100644 (file)
@@ -1,21 +1,32 @@
-/*
-dns.cpp - based on the dns 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>
 #include <stdlib.h>
@@ -29,258 +40,258 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #include <errno.h>
 #include <fcntl.h>
 #include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#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"
+
+extern InspIRCd* ServerInstance;
+extern ServerConfig* Config;
+extern time_t TIME;
+extern userrec* fd_ref_table[MAX_DESCRIPTORS];
+
+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 };
+
+class s_connection;
+
+typedef std::map<int,s_connection*> connlist;
+typedef connlist::iterator connlist_iter;
+
+DNS* Res = NULL;
+
+/* As lookups are completed, they are pushed into this result_list */
+std::map<int, std::string> result_list;
+connlist connections;
 
-static const char tagstring[] = "$Id$";
-
-#define max(a,b) (a > b ? a : b)
-#define DNS_MAX              8                    /* max number of nameservers used */
-#define DNS_CONFIG_FBCK     "/etc/resolv.conf"    /* fallback config file */
-#define DNS_PORT            53                    /* DNS well known port */
-#define DNS_QRY_A            1                    /* name to IP address */
-#define DNS_QRY_AAAA        28                    /* name to IP6 address */
-#define DNS_QRY_PTR         12                    /* IP address to name */
-#define DNS_QRY_MX          15                    /* name to MX */
-#define DNS_QRY_TXT         16                    /* name to TXT */
-#define DNS_QRY_CNAME       5
-
-#define DNS_ALIGN (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long))
-#define DNS_TRIES 3
-#define RESULTSIZE 1024
-#define min(a,b) (a < b ? a : b)
-
-static struct in_addr servers4[DNS_MAX]; /* up to DNS_MAX nameservers; populated by dns_init() */
-static int i4; /* actual count of nameservers; set by dns_init() */
-
-static int initdone = 0; /* to ensure dns_init() only runs once (on the first call) */
-static int wantclose = 0;
-static int lastcreate = -1;
-
-struct s_connection { /* open DNS query */
-       struct s_connection *next; /* next in list */
-       unsigned char id[2];
-       unsigned int _class;
-       unsigned int type;
-       int want_list;
-       int fd; /* file descriptor returned from sockets */
+int master_socket = -1;
+
+Resolver* dns_classes[65536];
+
+insp_inaddr servers[8];
+int i4;
+
+class s_rr_middle
+{
+ public:
+       QueryType       type;
+       unsigned int    _class;
+       unsigned long   ttl;
+       unsigned int    rdlength;
 };
 
-struct s_rr_middle {
-       unsigned int type;
-       unsigned int _class;
-       unsigned long ttl;
-       unsigned int rdlength;
+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];
 };
 
-#define DNS_POINTER_VALUE 0xc000
-
-static s_connection *connection_head = NULL; /* linked list of open DNS queries; populated by dns_add_query(), decimated by dns_getresult_s() */
-
-struct s_header { /* DNS query header */
-       unsigned char id[2];
-       unsigned int flags1;
-#define FLAGS1_MASK_QR 0x80
-#define FLAGS1_MASK_OPCODE 0x78 /* bitshift right 3 */
-#define FLAGS1_MASK_AA 0x04
-#define FLAGS1_MASK_TC 0x02
-#define FLAGS1_MASK_RD 0x01
-       unsigned int flags2;
-#define FLAGS2_MASK_RA 0x80
-#define FLAGS2_MASK_Z  0x70
-#define FLAGS2_MASK_RCODE 0x0f
-       unsigned int qdcount;
-       unsigned int ancount;
-       unsigned int nscount;
-       unsigned int arcount;
-       unsigned char payload[512]; /* DNS question, populated by dns_build_query_payload() */
+class s_connection
+{
+ public:
+       unsigned char   id[2];
+       unsigned char   res[512];
+       unsigned int    _class;
+       QueryType       type;
+       int             want_list;
+
+       s_connection()
+       {
+               *res = 0;
+       }
+
+       unsigned char*  result_ready(s_header &h, int length);
+       int             send_requests(const s_header *h, const int l);
 };
 
-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;
 }
 
 /*
- * These little hacks are here to avoid alignment and type sizing issues completely by doing manual copies
+ * Optimized by brain, these were using integer division and modulus.
+ * We can use logic shifts and logic AND to replace these even divisions
+ * and multiplications, it should be a bit faster (probably not noticably,
+ * but of course, more impressive). Also made these inline.
  */
-void dns_fill_rr(s_rr_middle* rr, const unsigned char *input) {
-       rr->type = input[0] * 256 + input[1];
-       rr->_class = input[2] * 256 + input[3];
-       rr->ttl = input[4] * 16777216 + input[5] * 65536 + input[6] * 256 + input[7];
-       rr->rdlength = input[8] * 256 + input[9];
+
+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];
 }
 
-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];
        header->flags2 = input[3];
-       header->qdcount = input[4] * 256 + input[5];
-       header->ancount = input[6] * 256 + input[7];
-       header->nscount = input[8] * 256 + input[9];
-       header->arcount = input[10] * 256 + input[11];
+       header->qdcount = (input[4] << 8) + input[5];
+       header->ancount = (input[6] << 8) + input[7];
+       header->nscount = (input[8] << 8) + input[9];
+       header->arcount = (input[10] << 8) + input[11];
        memcpy(header->payload,&input[12],l);
 }
 
-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;
        output[3] = header->flags2;
-       output[4] = header->qdcount / 256;
-       output[5] = header->qdcount % 256;
-       output[6] = header->ancount / 256;
-       output[7] = header->ancount % 256;
-       output[8] = header->nscount / 256;
-       output[9] = header->nscount % 256;
-       output[10] = header->arcount / 256;
-       output[11] = header->arcount % 256;
+       output[4] = header->qdcount >> 8;
+       output[5] = header->qdcount & 0xFF;
+       output[6] = header->ancount >> 8;
+       output[7] = header->ancount & 0xFF;
+       output[8] = header->nscount >> 8;
+       output[9] = header->nscount & 0xFF;
+       output[10] = header->arcount >> 8;
+       output[11] = header->arcount & 0xFF;
        memcpy(&output[12],header->payload,l);
 }
 
-void dns_close(int fd) { /* close query */
-       if (fd == lastcreate) {
-               wantclose = 1;
-               return;
-       }
-       close(fd);
-       return;
-}
-
-void DNS::dns_init() { /* on first call only: populates servers4 struct with up to DNS_MAX nameserver IP addresses from /etc/resolv.conf */
-       FILE *f;
-       int i;
-       in_addr addr4;
-       char buf[1024];
-       if (initdone == 1)
-               return;
-       i4 = 0;
-
-       initdone = 1;
-       srand((unsigned int) time(NULL));
-       memset(servers4,'\0',sizeof(in_addr) * DNS_MAX);
-       f = fopen(DNS_CONFIG_FBCK,"r");
-       if (f == NULL)
-               return;
-       while (fgets(buf,1024,f) != NULL) {
-               if (strncmp(buf,"nameserver",10) == 0) {
-                       i = 10;
-                       while (buf[i] == ' ' || buf[i] == '\t')
-                               i++;
-                       if (i4 < DNS_MAX) {
-                               if (dns_aton4_s(&buf[i],&addr4) != NULL)
-                                       memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
-                       }
-               }
-       }
-       fclose(f);
-
-}
-
-void DNS::dns_init_2(const char* dnsserver) { /* populates servers4 struct with address from the given parameter */
-        FILE *f;
-        int i;
-        in_addr addr4;
-        char buf[1024];
-        if (initdone == 1)
-                return;
-        i4 = 0;
-
-        initdone = 1;
-        srand((unsigned int) time(NULL));
-        memset(servers4,'\0',sizeof(in_addr) * DNS_MAX);
-        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) { /* send DNS query */
-       int i;
-       sockaddr_in addr4;
+int s_connection::send_requests(const s_header *h, const int l)
+{
+       insp_sockaddr addr;
        unsigned char payload[sizeof(s_header)];
 
        dns_empty_header(payload,h,l);
 
-
-       for (i = 0; i < i4; i++) {
-               /* otherwise send via standard ipv4 boringness */
-               memset(&addr4,0,sizeof(addr4));
-               memcpy(&addr4.sin_addr,&servers4[i],sizeof(addr4.sin_addr));
-               addr4.sin_family = AF_INET;
-               addr4.sin_port = htons(DNS_PORT);
-               sendto(s->fd, payload, l + 12, 0, (sockaddr *) &addr4, sizeof(addr4));
+       /* otherwise send via standard ipv4 boringness */
+       memset(&addr,0,sizeof(addr));
+#ifdef IPV6
+       memcpy(&addr.sin6_addr,&servers[0],sizeof(addr.sin6_addr));
+       addr.sin6_family = AF_FAMILY;
+       addr.sin6_port = htons(53);
+#else
+       memcpy(&addr.sin_addr.s_addr,&servers[0],sizeof(addr.sin_addr));
+       addr.sin_family = AF_FAMILY;
+       addr.sin_port = htons(53);
+#endif
+       if (sendto(master_socket, payload, l + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
+       {
+               log(DEBUG,"Error in sendto!");
+               return -1;
        }
 
        return 0;
 }
 
-static s_connection *dns_add_query(s_header *h) { /* build DNS query, add to list */
-       s_connection * s;
+s_connection* dns_add_query(s_header *h, int &id)
+{
 
-       s = new s_connection;
+       id = rand() % 65536;
+       s_connection * s = new s_connection();
 
        /* set header flags */
-       h->id[0] = s->id[0] = rand() % 255; /* verified by dns_getresult_s() */
-       h->id[1] = s->id[1] = rand() % 255;
+       h->id[0] = s->id[0] = id >> 8;
+       h->id[1] = s->id[1] = id & 0xFF;
        h->flags1 = 0 | FLAGS1_MASK_RD;
        h->flags2 = 0;
        h->qdcount = 1;
        h->ancount = 0;
        h->nscount = 0;
        h->arcount = 0;
-
-       /* turn off want_list by default */
        s->want_list = 0;
+       if (connections.find(id) == connections.end())
+               connections[id] = s;
+       return s;
+}
 
-       /* 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) {
-                               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) {
-                               close(s->fd);
-                               s->fd = -1;
-                       }
+void create_socket()
+{
+       log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
+       insp_inaddr addr;
+       i4 = 0;
+       srand((unsigned int) TIME);
+       memset(servers,'\0',sizeof(insp_inaddr) * 8);
+       if (insp_aton(Config->DNSServer,&addr) > 0)
+               memcpy(&servers[i4++],&addr,sizeof(insp_inaddr));
+
+       master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
+       if (master_socket != -1)
+       {
+               log(DEBUG,"Set query socket nonblock");
+               if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
+               {
+                       shutdown(master_socket,2);
+                       close(master_socket);
+                       master_socket = -1;
                }
-               if (s->fd == -1) {
-                       delete s;
-                       return NULL;
+       }
+       if (master_socket != -1)
+       {
+#ifdef IPV6
+               insp_sockaddr addr;
+               memset(&addr,0,sizeof(addr));
+               addr.sin6_family = AF_FAMILY;
+               addr.sin6_port = 0;
+               memset(&addr.sin6_addr,255,sizeof(in6_addr));
+#else
+               insp_sockaddr addr;
+               memset(&addr,0,sizeof(addr));
+               addr.sin_family = AF_FAMILY;
+               addr.sin_port = 0;
+               addr.sin_addr.s_addr = INADDR_ANY;
+#endif
+               log(DEBUG,"Binding query port");
+               if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
+               {
+                       log(DEBUG,"Cant bind with source port = 0");
+                       shutdown(master_socket,2);
+                       close(master_socket);
+                       master_socket = -1;
                }
-       /* create new connection object, add to linked list */
-       s->next = connection_head;
-       connection_head = s;
 
-       if (wantclose == 1) {
-               close(lastcreate);
-               wantclose = 0;
+               if (master_socket >= 0)
+               {
+                       log(DEBUG,"Attach query port to socket engine");
+                       if (ServerInstance && ServerInstance->SE)
+                               ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
+               }
        }
-       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;
-       
+
        payloadpos = 0;
        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;
@@ -290,7 +301,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;
@@ -307,124 +319,40 @@ 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 ip;
-       return dns_aton4_s(ipstring,&ip);
-}
-
-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;
-               return NULL;
-       }
-       return ip;
-}
-
-in_addr* DNS::dns_aton4_s(const char *ipstring, in_addr *ip) { /* ascii to numeric (buffered): convert string to given 4part IP addr struct */
-       unsigned char *myip;
-       int i,part = 0;
-       myip = (unsigned char *)ip;
-
-       memset(myip,'\0',4);
-       for (i = 0; i < 16; i++) {
-               switch (ipstring[i]) {
-                       case '\0':
-                               if (part != 3)
-                                       return NULL;
-                               return ip;
-                               break;
-                       case '0':
-                       case '1':
-                       case '2':
-                       case '3':
-                       case '4':
-                       case '5':
-                       case '6':
-                       case '7':
-                       case '8':
-                       case '9':
-                               if (myip[part] > 25)
-                                       return NULL;
-                               myip[part] *= 10;
-                               if (myip[part] == 250 && ipstring[i] - '0' > 6)
-                                       return NULL;
-                               myip[part] += ipstring[i] - '0';
-                               break;
-                       case '.':
-                               if (part == 3)
-                                       return ip;
-                               else
-                                       part++;
-                               break;
-                       default:
-                               if (part == 3)
-                                       return ip;
-                               else
-                                       return NULL;
-                               break;
-               }
-       }
-       if (part == 3)
-               return ip;
-       else
-               return NULL;
-}
-
-int DNS::dns_getip4(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
-       s_header h;
-       s_connection *s;
-       int l;
-
-       dns_init();
-       
-
-       l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
-       if (l == -1)
-               return -1;
-       s = dns_add_query(&h);
-       if (s == NULL)
-               return -1;
-       s->_class = 1;
-       s->type = DNS_QRY_A;
-       if (dns_send_requests(&h,s,l) == -1)
-               return -1;
-
-       return s->fd;
-}
-
-int DNS::dns_getip4list(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
+int DNS::dns_getip4(const char *name)
+{
+       /* build, add and send A query; retrieve result with dns_getresult() */
        s_header h;
        s_connection *s;
        int l;
-
-       dns_init();
+       int id;
        
-
        l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
        if (l == -1)
                return -1;
-       s = dns_add_query(&h);
+       s = dns_add_query(&h, id);
        if (s == NULL)
                return -1;
        s->_class = 1;
        s->type = DNS_QRY_A;
-       s->want_list = 1;
-       if (dns_send_requests(&h,s,l) == -1)
+       if (s->send_requests(&h,l) == -1)
                return -1;
 
-       return s->fd;
+       return id;
 }
 
-int DNS::dns_getname4(const in_addr *ip) { /* build, add and send PTR query; retrieve result with dns_getresult() */
+int DNS::dns_getname4(const insp_inaddr *ip)
+{ /* build, add and send PTR query; retrieve result with dns_getresult() */
+#ifdef IPV6
+       return -1;
+#else
+       log(DEBUG,"DNS::dns_getname4");
        char query[512];
        s_header h;
        s_connection * s;
        unsigned char *c;
        int l;
-
-       dns_init();
+       int id;
 
        c = (unsigned char *)&ip->s_addr;
 
@@ -433,221 +361,246 @@ int DNS::dns_getname4(const in_addr *ip) { /* build, add and send PTR query; ret
        l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
        if (l == -1)
                return -1;
-       s = dns_add_query(&h);
+       s = dns_add_query(&h, id);
        if (s == NULL)
                return -1;
        s->_class = 1;
        s->type = DNS_QRY_PTR;
-       if (dns_send_requests(&h,s,l) == -1)
+       if (s->send_requests(&h,l) == -1)
                return -1;
 
-       return s->fd;
+       return id;
+#endif
 }
 
-char* DNS::dns_ntoa4(const in_addr * const ip) { /* numeric to ascii: convert 4part IP addr struct to static string */
-       static char result[256];
-       return dns_ntoa4_s(ip,result);
-}
+/* Return the next id which is ready.
+ * result_list[id] will have been populated with the result string.
+ */
+int DNS::dns_getresult()
+{
+       /* retrieve result of DNS query (buffered) */
+       s_header h;
+       s_connection *c;
+       int length;
+       unsigned char buffer[sizeof(s_header)];
 
-char* DNS::dns_ntoa4_r(const in_addr *ip) { /* numeric to ascii (reentrant): convert 4part IP addr struct to new string */
-       char *result;
-       result = new char[256];
-       return dns_ntoa4_s(ip,result);
-}
+       length = recv(master_socket,buffer,sizeof(s_header),0);
 
-char* DNS::dns_ntoa4_s(const in_addr *ip, char *result) { /* numeric to ascii (buffered): convert 4part IP addr struct to given string */
-       unsigned char *m;
-       m = (unsigned char *)&ip->s_addr;
-       sprintf(result,"%d.%d.%d.%d",m[0],m[1],m[2],m[3]);
-       return result;
-}
+       if (length < 12)
+               return -1;
 
-char* DNS::dns_getresult(const int fd) { /* retrieve result of DNS query */
-       static char result[RESULTSIZE];
-       return dns_getresult_s(fd,result);
-}
+       dns_fill_header(&h,buffer,length - 12);
 
-char* DNS::dns_getresult_r(const int fd) { /* retrieve result of DNS query (reentrant) */
-       char *result;
-       result = new char[RESULTSIZE];
-       if(dns_getresult_s(fd,result) == NULL) {
-               delete result;
-               return NULL;
-       }
-       return result;
-}
+       // Get the id of this request
+       unsigned long this_id = h.id[1] + (h.id[0] << 8);
 
-char* DNS::dns_getresult_s(const int fd, char *result) { /* retrieve result of DNS query (buffered) */
-       s_header h;
-       s_connection *c, *prev;
-       int l,i,q,curanswer,o;
-       s_rr_middle rr;
-       unsigned char buffer[sizeof(s_header)];
-       unsigned short p;
+       // Do we have a pending request for it?
 
-       if (result)
-       {
-               result[0] = 0;
-       }
+        connlist_iter n_iter = connections.find(this_id);
+        if (n_iter == connections.end())
+        {
+                log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
+                return -1;
+        }
+        else
+        {
+                /* Remove the query from the list */
+                c = (s_connection*)n_iter->second;
+                /* We don't delete c here, because its done later when needed */
+                connections.erase(n_iter);
+        }
+       unsigned char* a = c->result_ready(h, length);
 
-       prev = NULL;
-       c = connection_head;
-       while (c != NULL) { /* find query in list of open queries */
-               if (c->fd == fd)
-                       break;
-               prev = c;
-               c = c->next;
-       }
-       if (c == NULL) {
-               return NULL; /* query not found */
+       if (a == NULL)
+       {
+               result_list[this_id] = "";
        }
-       /* query found-- pull from list: */
-       if (prev != NULL)
-               prev->next = c->next;
        else
-               connection_head = c->next;
-
-       l = recv(c->fd,buffer,sizeof(s_header),0);
-       dns_close(c->fd);
-       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]) {
-               delete c;
-               return NULL; /* ID mismatch */
+       {
+               if (c->type == DNS_QRY_A)
+               {
+                       char formatted[1024];
+                       snprintf(formatted,1024,"%u.%u.%u.%u",a[0],a[1],a[2],a[3]);
+                       result_list[this_id] = std::string(formatted);
+               }
+               else
+               {
+                       result_list[this_id] = std::string((const char*)a);
+               }
        }
-       if ((h.flags1 & FLAGS1_MASK_QR) == 0) {
-               delete c;
+
+       delete c;
+       return this_id;
+}
+
+/** A result is ready, process it
+ */
+unsigned char* s_connection::result_ready(s_header &h, int length)
+{
+       int i, q, curanswer, o;
+       s_rr_middle rr;
+       unsigned short p;
+                                       
+       if ((h.flags1 & FLAGS1_MASK_QR) == 0)
+       {
+               log(DEBUG,"DNS: didnt get a query result");
                return NULL;
        }
-       if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0) {
-               delete c;
+       if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
+       {
+               log(DEBUG,"DNS: got an OPCODE and didnt want one");
                return NULL;
        }
-       if ((h.flags2 & FLAGS2_MASK_RCODE) != 0) {
-               delete c;
+       if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
+       {
+               log(DEBUG,"DNS lookup failed due to SERVFAIL");
                return NULL;
        }
-       if (h.ancount < 1)  { /* no sense going on if we don't have any answers */
-               delete c;
+       if (h.ancount < 1)
+       {
+               log(DEBUG,"DNS: no answers!");
                return NULL;
        }
-       /* skip queries */
        i = 0;
        q = 0;
-       l -= 12;
-       while (q < h.qdcount && i < l) {
-               if (h.payload[i] > 63) { /* pointer */
-                       i += 6; /* skip pointer, _class and type */
+       length -= 12;
+       while ((unsigned)q < h.qdcount && i < length)
+       {
+               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 (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 < length)
+               {
+                       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 (length - i < 10)
+               {
                        return NULL;
                }
                dns_fill_rr(&rr,&h.payload[i]);
                i += 10;
-               if (rr.type != c->type) {
+               if (rr.type != this->type)
+               {
                        curanswer++;
                        i += rr.rdlength;
                        continue;
                }
-               if (rr._class != c->_class) {
+               if (rr._class != this->_class)
+               {
                        curanswer++;
                        i += rr.rdlength;
                        continue;
                }
                break;
        }
-       if (curanswer == h.ancount)
+       if ((unsigned int)curanswer == h.ancount)
                return NULL;
-       if (i + rr.rdlength > l)
+       if (i + rr.rdlength > (unsigned int)length)
                return NULL;
        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 < length && o + 256 < 1023)
+                       {
+                               if (h.payload[i] > 63)
+                               {
+                                       log(DEBUG,"DNS: h.payload[i] > 63");
                                        memcpy(&p,&h.payload[i],2);
-                                       i = ntohs(p) - DNS_POINTER_VALUE - 12;
-                               } else { /* label */
+                                       i = ntohs(p) - 0xC000 - 12;
+                               }
+                               else
+                               {
                                        if (h.payload[i] == 0)
+                                       {
                                                q = 1;
-                                       else {
-                                               result[o] = '\0';
+                                       }
+                                       else
+                                       {
+                                               res[o] = '\0';
                                                if (o != 0)
-                                                       result[o++] = '.';
-                                               memcpy(&result[o],&h.payload[i + 1],h.payload[i]);
+                                                       res[o++] = '.';
+                                               memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
                                                o += h.payload[i];
                                                i += h.payload[i] + 1;
                                        }
                                }
                        }
-                       result[o] = '\0';
-                       break;
+                       res[o] = '\0';
+               break;
                case DNS_QRY_A:
-                       if (c->want_list) {
-                               dns_ip4list *alist = (dns_ip4list *) result; /* we have to trust that this is aligned */
-                               while ((char *)alist - (char *)result < 700) {
+                       log(DEBUG,"DNS: got a result of type DNS_QRY_A");
+                       if (this->want_list)
+                       {
+                               dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
+                               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)
+                                       {
                                                return NULL;
                                        }
                                        memcpy(&alist->ip,&h.payload[i],4);
-                                       if (++curanswer >= h.ancount)
+                                       if ((unsigned)++curanswer >= h.ancount)
                                                break;
                                        i += rr.rdlength;
+                                       q = 0;
+                                       while (q == 0 && i < length)
                                        {
-                                               /* 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 (length - i < 10)
+                                       {
                                                return NULL;
                                        }
                                        dns_fill_rr(&rr,&h.payload[i]);
@@ -659,66 +612,139 @@ char* DNS::dns_getresult_s(const int fd, char *result) { /* retrieve result of D
                                alist->next = NULL;
                                break;
                        }
-                       goto defaultcase;
+                       memcpy(res,&h.payload[i],rr.rdlength);
+                       res[rr.rdlength] = '\0';
                        break;
                default:
-               defaultcase:
-                       memcpy(result,&h.payload[i],rr.rdlength);
-                       result[rr.rdlength] = '\0';
+                       memcpy(res,&h.payload[i],rr.rdlength);
+                       res[rr.rdlength] = '\0';
                        break;
        }
-       delete c;
-       return result;
+       return res;
 }
 
 DNS::DNS()
 {
-       dns_init();
+       log(DEBUG,"Create blank DNS");
 }
 
-DNS::DNS(std::string dnsserver)
+DNS::~DNS()
 {
-       dns_init_2(dnsserver.c_str());
 }
 
-DNS::~DNS()
+Resolver::Resolver(const std::string &source, bool forward, const std::string &dnsserver = "") : input(source), fwd(forward), server(dnsserver)
 {
+       if (forward)
+       {
+               this->myid = Res->dns_getip4(source.c_str());
+       }
+       else
+       {
+               insp_inaddr binip;
+               if (insp_aton(source.c_str(), &binip) > 0)
+               {
+                       /* Valid ip address */
+                       this->myid = Res->dns_getname4(&binip);
+               }
+       }
+       if (this->myid == -1)
+       {
+               log(DEBUG,"Resolver::Resolver: Could not get an id!");
+               this->OnError(RESOLVER_NSDOWN);
+               ModuleException e("Resolver: Couldnt get an id to make a request");
+               throw e;
+               /* We shouldnt get here really */
+               return;
+       }
+
+       log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
 }
 
-bool DNS::ReverseLookup(std::string ip)
+Resolver::~Resolver()
 {
-        binip = dns_aton4(ip.c_str());
-        if (binip == NULL) {
-                fprintf(stderr,"invalid IP address.\n");
-                return 2;
-        }
-        this->fd = dns_getname4(binip);
+       log(DEBUG,"Resolver::~Resolver");
 }
 
-bool DNS::ForwardLookup(std::string host)
+int Resolver::GetId()
 {
+       return this->myid;
 }
 
-bool DNS::HasResult()
+bool Resolver::ProcessResult()
 {
-       pollfd polls;
-       polls.fd = this->fd;
-       polls.events = POLLIN;
-       int ret = poll(&polls,1,1);
-       return (ret > 0);
+       log(DEBUG,"Resolver::ProcessResult");
+
+       std::map<int, std::string>::iterator x = result_list.find(this->myid);
+
+       if (x == result_list.end())
+       {
+               log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
+               this->OnError(RESOLVER_NXDOMAIN);
+               return false;
+       }
+       else
+       {
+
+               log(DEBUG,"Resolver::OnLookupComplete(%s)",x->second.c_str());
+               this->OnLookupComplete(x->second);
+               result_list.erase(x);
+               return true;
+       }
 }
 
-int DNS::GetFD()
+void Resolver::OnLookupComplete(const std::string &result)
 {
-       return this->fd;
 }
 
-std::string DNS::GetResult()
+void Resolver::OnError(ResolverError e)
 {
-        result = dns_getresult(this->fd);
-        if (result) {
-               return result;
-        } else {
-               return "";
+}
+
+void dns_deal_with_classes(int fd)
+{
+       log(DEBUG,"dns_deal_with_classes(%d)",fd);
+       if (fd == master_socket)
+       {
+               int id = Res->dns_getresult();
+               if (id != -1)
+               {
+                       log(DEBUG,"Result available, id=%d",id);
+                       dns_classes[id]->ProcessResult();
+                       delete dns_classes[id];
+                       dns_classes[id] = NULL;
+               }
        }
 }
+
+bool dns_add_class(Resolver* r)
+{
+       log(DEBUG,"dns_add_class");
+       if ((r) && (r->GetId() > -1))
+       {
+               if (!dns_classes[r->GetId()])
+               {
+                       log(DEBUG,"dns_add_class: added class");
+                       dns_classes[r->GetId()] = r;
+                       return true;
+               }
+               else
+               {
+                       log(DEBUG,"Space occupied!");
+                       return false;
+               }
+       }
+       else
+       {
+               log(DEBUG,"Bad class");
+               delete r;
+               return true;
+       }
+}
+
+void init_dns()
+{
+       Res = new DNS();
+       memset(dns_classes,0,sizeof(dns_classes));
+       create_socket();
+}
+