]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
Remove the same craq
[user/henk/code/inspircd.git] / src / dns.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 /*
18 dns.cpp - Nonblocking DNS functions.
19 Very loosely based on the firedns library,
20 Copyright (C) 2002 Ian Gulliver.
21
22 There have been so many modifications to this file
23 to make it fit into InspIRCd and make it object
24 orientated that you should not take this code as
25 being what firedns really looks like. It used to
26 look very different to this! :-P
27 */
28
29 using namespace std;
30
31 #include <string>
32 #include <stdlib.h>
33 #include <time.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/time.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <stdio.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <poll.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <map>
48 #include <algorithm>
49 #include "dns.h"
50 #include "inspircd.h"
51 #include "helperfuncs.h"
52 #include "inspircd_config.h"
53 #include "socketengine.h"
54 #include "configreader.h"
55
56 #ifdef THREADED_DNS
57 pthread_mutex_t connmap_lock = PTHREAD_MUTEX_INITIALIZER;
58 #endif
59
60 extern InspIRCd* ServerInstance;
61 extern ServerConfig* Config;
62 extern time_t TIME;
63 extern userrec* fd_ref_table[MAX_DESCRIPTORS];
64
65 enum QueryType { DNS_QRY_A = 1, DNS_QRY_PTR = 12 };
66 enum QueryFlags1 { FLAGS1_MASK_RD = 0x01, FLAGS1_MASK_TC = 0x02, FLAGS1_MASK_AA = 0x04, FLAGS1_MASK_OPCODE = 0x78, FLAGS1_MASK_QR = 0x80 };
67 enum QueryFlags2 { FLAGS2_MASK_RCODE = 0x0F, FLAGS2_MASK_Z = 0x70, FLAGS2_MASK_RA = 0x80 };
68
69 class s_connection;
70
71 typedef std::map<int,s_connection*> connlist;
72 typedef connlist::iterator connlist_iter;
73 connlist connections;
74
75 Resolver* dns_classes[MAX_DESCRIPTORS];
76
77 insp_inaddr servers[8];
78 int i4;
79 int initdone = 0;
80 int lastcreate = -1;
81
82 class s_connection
83 {
84  public:
85         unsigned char   id[2];
86         unsigned int    _class;
87         QueryType       type;
88         int             want_list;
89         int             fd;
90 };
91
92 class s_rr_middle
93 {
94  public:
95         QueryType       type;
96         unsigned int    _class;
97         unsigned long   ttl;
98         unsigned int    rdlength;
99 };
100
101 class s_header
102 {
103  public:
104         unsigned char   id[2];
105         unsigned int    flags1;
106         unsigned int    flags2;
107         unsigned int    qdcount;
108         unsigned int    ancount;
109         unsigned int    nscount;
110         unsigned int    arcount;
111         unsigned char   payload[512];
112 };
113
114
115 void *dns_align(void *inp)
116 {
117         char *p = (char*)inp;
118         int offby = ((char *)p - (char *)0) % (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long));
119         if (offby != 0)
120                 return p + ((sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long)) - offby);
121         else
122                 return p;
123 }
124
125 /*
126  * Optimized by brain, these were using integer division and modulus.
127  * We can use logic shifts and logic AND to replace these even divisions
128  * and multiplications, it should be a bit faster (probably not noticably,
129  * but of course, more impressive). Also made these inline.
130  */
131
132 inline void dns_fill_rr(s_rr_middle* rr, const unsigned char *input)
133 {
134         rr->type = (QueryType)((input[0] << 8) + input[1]);
135         rr->_class = (input[2] << 8) + input[3];
136         rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
137         rr->rdlength = (input[8] << 8) + input[9];
138 }
139
140 inline void dns_fill_header(s_header *header, const unsigned char *input, const int l)
141 {
142         header->id[0] = input[0];
143         header->id[1] = input[1];
144         header->flags1 = input[2];
145         header->flags2 = input[3];
146         header->qdcount = (input[4] << 8) + input[5];
147         header->ancount = (input[6] << 8) + input[7];
148         header->nscount = (input[8] << 8) + input[9];
149         header->arcount = (input[10] << 8) + input[11];
150         memcpy(header->payload,&input[12],l);
151 }
152
153 inline void dns_empty_header(unsigned char *output, const s_header *header, const int l)
154 {
155         output[0] = header->id[0];
156         output[1] = header->id[1];
157         output[2] = header->flags1;
158         output[3] = header->flags2;
159         output[4] = header->qdcount >> 8;
160         output[5] = header->qdcount & 0xFF;
161         output[6] = header->ancount >> 8;
162         output[7] = header->ancount & 0xFF;
163         output[8] = header->nscount >> 8;
164         output[9] = header->nscount & 0xFF;
165         output[10] = header->arcount >> 8;
166         output[11] = header->arcount & 0xFF;
167         memcpy(&output[12],header->payload,l);
168 }
169
170 void dns_close(int fd)
171 {
172 #ifndef THREADED_DNS
173         if (ServerInstance && ServerInstance->SE)
174                 ServerInstance->SE->DelFd(fd);
175 #endif
176         log(DEBUG,"DNS: dns_close on fd %d",fd);
177         shutdown(fd,2);
178         close(fd);
179         return;
180 }
181
182 void DNS::dns_init()
183 {
184         FILE *f;
185         int i;
186         insp_inaddr addr;
187         char buf[1024];
188         if (initdone == 1)
189                 return;
190         i4 = 0;
191
192         initdone = 1;
193         srand((unsigned int) TIME);
194         memset(servers,'\0',sizeof(insp_inaddr) * 8);
195         f = fopen("/etc/resolv.conf","r");
196         if (f == NULL)
197                 return;
198         while (fgets(buf,1024,f) != NULL) {
199                 if (strncmp(buf,"nameserver",10) == 0)
200                 {
201                         i = 10;
202                         while (buf[i] == ' ' || buf[i] == '\t')
203                                 i++;
204                         if (i4 < 8)
205                         {
206                                 if (insp_aton(&buf[i],&addr) > 0)
207                                 {
208                                         log(DEBUG,"Add server %d, %s",i4,&buf[i]);
209                                         memcpy(&servers[i4++],&addr,sizeof(insp_inaddr));
210                                 }
211                         }
212                 }
213         }
214         fclose(f);
215 }
216
217 void DNS::dns_init_2(const char* dnsserver)
218 {
219         log(DEBUG,"*************** DNS INIT 2 **************");
220         insp_inaddr addr;
221         i4 = 0;
222         srand((unsigned int) TIME);
223         memset(servers,'\0',sizeof(insp_inaddr) * 8);
224         log(DEBUG,"ADD DNS: %s",dnsserver);
225         if (insp_aton(dnsserver,&addr) > 0)
226         {
227                 unsigned char* n = (unsigned char*)&addr;
228                 log(DEBUG,"***** Add server %d, %s: %d, %d, %d, %d",i4,dnsserver,n[0],n[1],n[2],n[3]);
229                 memcpy(&servers[i4++],&addr,sizeof(insp_inaddr));
230         }
231 }
232
233
234 int dns_send_requests(const s_header *h, const s_connection *s, const int l)
235 {
236         int i;
237         insp_sockaddr addr;
238         unsigned char payload[sizeof(s_header)];
239
240         dns_empty_header(payload,h,l);
241
242
243         i = 0;
244
245         /* otherwise send via standard ipv4 boringness */
246         memset(&addr,0,sizeof(addr));
247 #ifdef IPV6
248         memcpy(&addr.sin6_addr,&servers[i],sizeof(addr.sin6_addr));
249         addr.sin6_family = AF_FAMILY;
250         addr.sin6_port = htons(53);
251 #else
252         memcpy(&addr.sin_addr.s_addr,&servers[i],sizeof(addr.sin_addr));
253         addr.sin_family = AF_FAMILY;
254         addr.sin_port = htons(53);
255 #endif
256         if (sendto(s->fd, payload, l + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
257         {
258                 log(DEBUG,"Error in sendto!");
259                 return -1;
260         }
261
262         return 0;
263 }
264
265 s_connection *dns_add_query(s_header *h)
266 {
267
268         s_connection * s = new s_connection;
269         int id = rand() % 65536;
270
271         /* set header flags */
272         h->id[0] = s->id[0] = id >> 8; /* verified by dns_getresult_s() */
273         h->id[1] = s->id[1] = id & 0xFF;
274         h->flags1 = 0 | FLAGS1_MASK_RD;
275         h->flags2 = 0;
276         h->qdcount = 1;
277         h->ancount = 0;
278         h->nscount = 0;
279         h->arcount = 0;
280         s->want_list = 0;
281         s->fd = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
282         if (s->fd != -1)
283         {
284                 log(DEBUG,"Set query socket nonblock");
285                 if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0)
286                 {
287                         shutdown(s->fd,2);
288                         close(s->fd);
289                         s->fd = -1;
290                 }
291         }
292         if (s->fd != -1)
293         {
294 #ifdef IPV6
295                 insp_sockaddr addr;
296                 memset(&addr,0,sizeof(addr));
297                 addr.sin6_family = AF_FAMILY;
298                 addr.sin6_port = 0;
299                 memset(&addr.sin6_addr,255,sizeof(in6_addr));
300 #else
301                 insp_sockaddr addr;
302                 memset(&addr,0,sizeof(addr));
303                 addr.sin_family = AF_FAMILY;
304                 addr.sin_port = 0;
305                 addr.sin_addr.s_addr = INADDR_ANY;
306                 if (bind(s->fd,(sockaddr *)&addr,sizeof(addr)) != 0)
307                 {
308                         log(DEBUG,"Cant bind with source port = 0");
309                         shutdown(s->fd,2);
310                         close(s->fd);
311                         s->fd = -1;
312                 }
313 #endif
314         }
315         if (s->fd == -1)
316         {
317                 DELETE(s);
318                 return NULL;
319         }
320         /* create new connection object, add to linked list */
321 #ifdef THREADED_DNS
322         pthread_mutex_lock(&connmap_lock);
323 #endif
324         if (connections.find(s->fd) == connections.end())
325                 connections[s->fd] = s;
326 #ifdef THREADED_DNS
327         pthread_mutex_unlock(&connmap_lock);
328 #endif
329
330         return s;
331 }
332
333 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
334 {
335         short payloadpos;
336         const char * tempchr, * tempchr2;
337         unsigned short l;
338
339         payloadpos = 0;
340         tempchr2 = name;
341
342         /* split name up into labels, create query */
343         while ((tempchr = strchr(tempchr2,'.')) != NULL)
344         {
345                 l = tempchr - tempchr2;
346                 if (payloadpos + l + 1 > 507)
347                         return -1;
348                 payload[payloadpos++] = l;
349                 memcpy(&payload[payloadpos],tempchr2,l);
350                 payloadpos += l;
351                 tempchr2 = &tempchr[1];
352         }
353         l = strlen(tempchr2);
354         if (l)
355         {
356                 if (payloadpos + l + 2 > 507)
357                         return -1;
358                 payload[payloadpos++] = l;
359                 memcpy(&payload[payloadpos],tempchr2,l);
360                 payloadpos += l;
361                 payload[payloadpos++] = '\0';
362         }
363         if (payloadpos > 508)
364                 return -1;
365         l = htons(rr);
366         memcpy(&payload[payloadpos],&l,2);
367         l = htons(_class);
368         memcpy(&payload[payloadpos + 2],&l,2);
369         return payloadpos + 4;
370 }
371
372 int DNS::dns_getip4(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
373         s_header h;
374         s_connection *s;
375         int l;
376
377         dns_init();
378         
379
380         l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
381         if (l == -1)
382                 return -1;
383         s = dns_add_query(&h);
384         if (s == NULL)
385                 return -1;
386         s->_class = 1;
387         s->type = DNS_QRY_A;
388         if (dns_send_requests(&h,s,l) == -1)
389                 return -1;
390
391         return s->fd;
392 }
393
394 int DNS::dns_getip4list(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
395         s_header h;
396         s_connection *s;
397         int l;
398
399         dns_init();
400         
401         l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
402         if (l == -1)
403                 return -1;
404         s = dns_add_query(&h);
405         if (s == NULL)
406                 return -1;
407         s->_class = 1;
408         s->type = DNS_QRY_A;
409         s->want_list = 1;
410         if (dns_send_requests(&h,s,l) == -1)
411                 return -1;
412
413         return s->fd;
414 }
415
416 int DNS::dns_getname4(const insp_inaddr *ip)
417 { /* build, add and send PTR query; retrieve result with dns_getresult() */
418 #ifdef IPV6
419         return -1;
420 #else
421         log(DEBUG,"DNS::dns_getname4");
422         char query[512];
423         s_header h;
424         s_connection * s;
425         unsigned char *c;
426         int l;
427
428         c = (unsigned char *)&ip->s_addr;
429
430         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
431
432         l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
433         if (l == -1)
434                 return -1;
435         s = dns_add_query(&h);
436         if (s == NULL)
437                 return -1;
438         s->_class = 1;
439         s->type = DNS_QRY_PTR;
440         if (dns_send_requests(&h,s,l) == -1)
441                 return -1;
442
443         return s->fd;
444 #endif
445 }
446
447 char* DNS::dns_getresult(const int cfd) { /* retrieve result of DNS query */
448         log(DEBUG,"DNS: dns_getresult with cfd=%d",cfd);
449         return dns_getresult_s(cfd,this->localbuf);
450 }
451
452 char* DNS::dns_getresult_s(const int cfd, char *res) { /* retrieve result of DNS query (buffered) */
453         s_header h;
454         s_connection *c;
455         int l, i, q, curanswer, o;
456         s_rr_middle rr;
457         unsigned char buffer[sizeof(s_header)];
458         unsigned short p;
459
460         if (res)
461                 *res = 0;
462
463         /* FireDNS used a linked list for this. How ugly (and slow). */
464
465 #ifdef THREADED_DNS
466         /* XXX: STL really does NOT like being poked and prodded in more than
467          * one orifice by threaded apps. Make sure we remain nice to it, and
468          * lock a mutex around any access to the std::map.
469          */
470         pthread_mutex_lock(&connmap_lock);
471 #endif
472         connlist_iter n_iter = connections.find(cfd);
473         if (n_iter == connections.end())
474         {
475                 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d",cfd);
476 #ifdef THREADED_DNS
477                 pthread_mutex_unlock(&connmap_lock);
478 #endif
479                 return NULL;
480         }
481         else
482         {
483                 /* Remove the query from the list */
484                 c = (s_connection*)n_iter->second;
485                 /* We don't delete c here, because its done later when needed */
486                 connections.erase(n_iter);
487         }
488 #ifdef THREADED_DNS
489         pthread_mutex_unlock(&connmap_lock);
490 #endif
491
492         l = recv(c->fd,buffer,sizeof(s_header),0);
493         dns_close(c->fd);
494         if (l < 12)
495         {
496                 DELETE(c);
497                 return NULL;
498         }
499         dns_fill_header(&h,buffer,l - 12);
500         if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
501         {
502                 log(DEBUG,"DNS: id mismatch on query");
503                 DELETE(c);
504                 return NULL; /* ID mismatch */
505         }
506         if ((h.flags1 & FLAGS1_MASK_QR) == 0)
507         {
508                 log(DEBUG,"DNS: didnt get a query result");
509                 DELETE(c);
510                 return NULL;
511         }
512         if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
513         {
514                 log(DEBUG,"DNS: got an OPCODE and didnt want one");
515                 DELETE(c);
516                 return NULL;
517         }
518         if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
519         {
520                 log(DEBUG,"DNS lookup failed due to SERVFAIL");
521                 DELETE(c);
522                 return NULL;
523         }
524         if (h.ancount < 1)
525         {
526                 log(DEBUG,"DNS: no answers!");
527                 DELETE(c);
528                 return NULL;
529         }
530         i = 0;
531         q = 0;
532         l -= 12;
533         while ((unsigned)q < h.qdcount && i < l)
534         {
535                 if (h.payload[i] > 63)
536                 {
537                         i += 6;
538                         q++;
539                 }
540                 else
541                 {
542                         if (h.payload[i] == 0)
543                         {
544                                 q++;
545                                 i += 5;
546                         }
547                         else i += h.payload[i] + 1;
548                 }
549         }
550         curanswer = 0;
551         while ((unsigned)curanswer < h.ancount)
552         {
553                 q = 0;
554                 while (q == 0 && i < l)
555                 {
556                         if (h.payload[i] > 63)
557                         {
558                                 i += 2;
559                                 q = 1;
560                         }
561                         else
562                         {
563                                 if (h.payload[i] == 0)
564                                 {
565                                         i++;
566                                         q = 1;
567                                 }
568                                 else i += h.payload[i] + 1; /* skip length and label */
569                         }
570                 }
571                 if (l - i < 10)
572                 {
573                         DELETE(c);
574                         return NULL;
575                 }
576                 dns_fill_rr(&rr,&h.payload[i]);
577                 i += 10;
578                 if (rr.type != c->type)
579                 {
580                         curanswer++;
581                         i += rr.rdlength;
582                         continue;
583                 }
584                 if (rr._class != c->_class)
585                 {
586                         curanswer++;
587                         i += rr.rdlength;
588                         continue;
589                 }
590                 break;
591         }
592         if ((unsigned)curanswer == h.ancount)
593                 return NULL;
594         if ((unsigned)i + rr.rdlength > (unsigned)l)
595                 return NULL;
596         if (rr.rdlength > 1023)
597                 return NULL;
598
599         switch (rr.type)
600         {
601                 case DNS_QRY_PTR:
602                         log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
603                         o = 0;
604                         q = 0;
605                         while (q == 0 && i < l && o + 256 < 1023)
606                         {
607                                 if (h.payload[i] > 63)
608                                 {
609                                         log(DEBUG,"DNS: h.payload[i] > 63");
610                                         memcpy(&p,&h.payload[i],2);
611                                         i = ntohs(p) - 0xC000 - 12;
612                                 }
613                                 else
614                                 {
615                                         if (h.payload[i] == 0)
616                                         {
617                                                 q = 1;
618                                         }
619                                         else
620                                         {
621                                                 res[o] = '\0';
622                                                 if (o != 0)
623                                                         res[o++] = '.';
624                                                 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
625                                                 o += h.payload[i];
626                                                 i += h.payload[i] + 1;
627                                         }
628                                 }
629                         }
630                         res[o] = '\0';
631                 break;
632                 case DNS_QRY_A:
633                         log(DEBUG,"DNS: got a result of type DNS_QRY_A");
634                         if (c->want_list)
635                         {
636                                 dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
637                                 while ((char *)alist - (char *)res < 700)
638                                 {
639                                         if (rr.type != DNS_QRY_A)
640                                                 break;
641                                         if (rr._class != 1)
642                                                 break;
643                                         if (rr.rdlength != 4)
644                                         {
645                                                 DELETE(c);
646                                                 return NULL;
647                                         }
648                                         memcpy(&alist->ip,&h.payload[i],4);
649                                         if ((unsigned)++curanswer >= h.ancount)
650                                                 break;
651                                         i += rr.rdlength;
652                                         q = 0;
653                                         while (q == 0 && i < l)
654                                         {
655                                                 if (h.payload[i] > 63)
656                                                 {
657                                                         i += 2;
658                                                         q = 1;
659                                                 }
660                                                 else
661                                                 {
662                                                         if (h.payload[i] == 0)
663                                                         {
664                                                                 i++;
665                                                                 q = 1;
666                                                         }
667                                                         else i += h.payload[i] + 1;
668                                                 }
669                                         }
670                                         if (l - i < 10)
671                                         {
672                                                 DELETE(c);
673                                                 return NULL;
674                                         }
675                                         dns_fill_rr(&rr,&h.payload[i]);
676                                         i += 10;
677                                         alist->next = (dns_ip4list *) dns_align(((char *) alist) + sizeof(dns_ip4list));
678                                         alist = alist->next;
679                                         alist->next = NULL;
680                                 }
681                                 alist->next = NULL;
682                                 break;
683                         }
684                         memcpy(res,&h.payload[i],rr.rdlength);
685                         res[rr.rdlength] = '\0';
686                         break;
687                 default:
688                         memcpy(res,&h.payload[i],rr.rdlength);
689                         res[rr.rdlength] = '\0';
690                         break;
691         }
692         DELETE(c);
693         return res;
694 }
695
696 DNS::DNS()
697 {
698         dns_init();
699         log(DEBUG,"Create blank DNS");
700 }
701
702 DNS::DNS(const std::string &dnsserver)
703 {
704         dns_init_2(dnsserver.c_str());
705         log(DEBUG,"Create DNS with server '%s'",dnsserver.c_str());
706 }
707
708 void DNS::SetNS(const std::string &dnsserver)
709 {
710         dns_init_2(dnsserver.c_str());
711         log(DEBUG,"Set NS");
712 }
713
714 DNS::~DNS()
715 {
716 }
717
718 bool DNS::ReverseLookup(const std::string &ip, bool ins)
719 {
720         if (ServerInstance && ServerInstance->stats)
721                 ServerInstance->stats->statsDns++;
722         insp_inaddr binip;
723         if (insp_aton(ip.c_str(), &binip) < 1)
724         {
725                 return false;
726         }
727
728         this->myfd = dns_getname4(&binip);
729         if (this->myfd == -1)
730         {
731                 return false;
732         }
733         log(DEBUG,"DNS: ReverseLookup, fd=%d",this->myfd);
734 #ifndef THREADED_DNS
735         if (ins)
736         {
737                 if (ServerInstance && ServerInstance->SE)
738                         ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
739         }
740 #endif
741         return true;
742 }
743
744 bool DNS::ForwardLookup(const std::string &host, bool ins)
745 {
746         if (ServerInstance && ServerInstance->stats)
747                 ServerInstance->stats->statsDns++;
748         this->myfd = dns_getip4(host.c_str());
749         if (this->myfd == -1)
750         {
751                 return false;
752         }
753         log(DEBUG,"DNS: ForwardLookup, fd=%d",this->myfd);
754 #ifndef THREADED_DNS
755         if (ins)
756         {
757                 if (ServerInstance && ServerInstance->SE)
758                         ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
759         }
760 #endif
761         return true;
762 }
763
764 bool DNS::ForwardLookupWithFD(const std::string &host, int &fd)
765 {
766         if (ServerInstance && ServerInstance->stats)
767                 ServerInstance->stats->statsDns++;
768         this->myfd = dns_getip4(host.c_str());
769         fd = this->myfd;
770         if (this->myfd == -1)
771         {
772                 return false;
773         }
774         log(DEBUG,"DNS: ForwardLookupWithFD, fd=%d",this->myfd);
775         if (ServerInstance && ServerInstance->SE)
776                 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_MODULE);
777         return true;
778 }
779
780 bool DNS::HasResult(int fd)
781 {
782         return (fd == this->myfd);
783 }
784
785 /* Only the multithreaded dns uses this poll() based
786  * check now. As its in another thread we dont have
787  * to worry about its performance that much.
788  */
789 bool DNS::HasResult()
790 {
791         log(DEBUG,"DNS: HasResult, fd=%d",this->myfd);
792         pollfd polls;
793         polls.fd = this->myfd;
794         polls.events = POLLIN;
795         int ret = poll(&polls,1,1);
796         log(DEBUG,"DNS: Hasresult returning %d",ret);
797         return (ret > 0);
798 }
799
800 int DNS::GetFD()
801 {
802         return this->myfd;
803 }
804
805 std::string DNS::GetResult()
806 {
807         log(DEBUG,"DNS: GetResult()");
808         result = dns_getresult(this->myfd);
809         if (result)
810         {
811                 if (ServerInstance && ServerInstance->stats)
812                         ServerInstance->stats->statsDnsGood++;
813                 dns_close(this->myfd);
814                 this->myfd = -1;
815                 return result;
816         }
817         else
818         {
819                 if (ServerInstance && ServerInstance->stats)
820                         ServerInstance->stats->statsDnsBad++;
821                 if (this->myfd != -1)
822                 {
823                         dns_close(this->myfd);
824                         this->myfd = -1;
825                 }
826                 return "";
827         }
828 }
829
830 std::string DNS::GetResultIP()
831 {
832         char r[1024];
833         log(DEBUG,"DNS: GetResultIP()");
834         result = dns_getresult(this->myfd);
835         if (this->myfd != -1)
836         {
837                 dns_close(this->myfd);
838                 this->myfd = -1;
839         }
840         if (result)
841         {
842                 if (ServerInstance && ServerInstance->stats)
843                         ServerInstance->stats->statsDnsGood++;
844                 unsigned char a = (unsigned)result[0];
845                 unsigned char b = (unsigned)result[1];
846                 unsigned char c = (unsigned)result[2];
847                 unsigned char d = (unsigned)result[3];
848                 snprintf(r,1024,"%u.%u.%u.%u",a,b,c,d);
849                 return r;
850         }
851         else
852         {
853                 if (ServerInstance && ServerInstance->stats)
854                         ServerInstance->stats->statsDnsBad++;
855                 log(DEBUG,"DANGER WILL ROBINSON! NXDOMAIN for forward lookup, but we got a reverse lookup!");
856                 return "";
857         }
858 }
859
860
861
862 #ifdef THREADED_DNS
863
864 /* This function is a thread function which can be thought of as a lightweight process
865  * to all you non-threaded people. In actuality its so much more, and pretty damn cool.
866  * With threaded dns enabled, each user which connects gets a thread attached to their
867  * user record when their DNS lookup starts. This function starts in parallel, and
868  * commences a blocking dns lookup. Because its a seperate thread, this occurs without
869  * actually blocking the main application. Once the dns lookup is completed, the thread
870  * checks if the user is still around by checking their fd against the reference table,
871  * and if they are, writes the hostname into the struct and terminates, after setting
872  * userrec::dns_done to true. Because this is multi-threaded it can make proper use of
873  * SMP setups (like the one i have here *grin*).
874  * This is in comparison to the non-threaded dns, which must monitor the thread sockets
875  * in a nonblocking fashion, consuming more resources to do so.
876  *
877  * NB: Yes this does scale, thank you. Even with large numbers of connecting clients
878  * in any one timeframe, they wont all connect *at the same time* therefore any argument
879  * of "but there will be thousands of threads it'll blow up" is moot, ive tested this and
880  * there will only ever be somewhere around the listen backlog in number of pending
881  * lookups at any one time. This is significant on any modern SMP system.
882  */
883 void* dns_task(void* arg)
884 {
885         userrec* u = (userrec*)arg;
886         int thisfd = u->fd;
887
888         log(DEBUG,"DNS thread for user %s on ip %s",u->nick,insp_ntoa(u->ip4));
889         DNS dns1(Config->DNSServer);
890         DNS dns2(Config->DNSServer);
891         std::string host;
892         std::string ip;
893         int iterations = 0;
894
895         if (dns1.ReverseLookup(insp_ntoa(u->ip4),false))
896         {
897                 /* FIX: Dont make these infinite! */
898                 while ((!dns1.HasResult()) && (++iterations < 20))
899                         usleep(100);
900
901                 if (iterations < 20)
902                 {
903                         if (dns1.GetFD() != -1)
904                         {
905                                 host = dns1.GetResult();
906                                 if (host != "")
907                                 {
908                                         if (dns2.ForwardLookup(host, false))
909                                         {
910                                                 iterations = 0;
911                                                 while ((!dns2.HasResult()) && (++iterations < 20))
912                                                         usleep(100);
913
914                                                 if (iterations < 20)
915                                                 {
916                                                         if (dns2.GetFD() != -1)
917                                                         {
918                                                                 ip = dns2.GetResultIP();
919                                                                 if (ip == std::string(insp_ntoa(u->ip4)))
920                                                                 {
921                                                                         if (host.length() < 65)
922                                                                         {
923                                                                                 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
924                                                                                 {
925                                                                                         if (!u->dns_done)
926                                                                                         {
927                                                                                                 strcpy(u->host,host.c_str());
928                                                                                                 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
929                                                                                                 {
930                                                                                                         strcpy(u->dhost,host.c_str());
931                                                                                                 }
932                                                                                         }
933                                                                                 }
934                                                                         }
935                                                                 }
936                                                         }
937                                                 }
938                                         }
939                                 }
940                         }
941                 }
942         }
943         if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
944                 u->dns_done = true;
945         log(DEBUG,"THREAD EXIT");
946         return NULL;
947 }
948 #endif
949
950 Resolver::Resolver(const std::string &source, bool forward, const std::string &dnsserver = "") : input(source), fwd(forward), server(dnsserver)
951 {
952         if (this->server != "")
953                 Query.SetNS(this->server);
954         else
955                 Query.SetNS(Config->DNSServer);
956
957         if (forward)
958         {
959                 Query.ForwardLookup(input.c_str(), false);
960                 this->fd = Query.GetFD();
961         }
962         else
963         {
964                 Query.ReverseLookup(input.c_str(), false);
965                 this->fd = Query.GetFD();
966         }
967         if (fd < 0)
968         {
969                 log(DEBUG,"Resolver::Resolver: RESOLVER_NSDOWN");
970                 this->OnError(RESOLVER_NSDOWN);
971                 ModuleException e("Resolver: Nameserver is down");
972                 throw e;
973                 /* We shouldnt get here really */
974                 return;
975         }
976
977         if (ServerInstance && ServerInstance->SE)
978         {
979                 log(DEBUG,"Resolver::Resolver: this->fd=%d",this->fd);
980                 ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_CLASSDNS);
981         }
982         else
983         {
984                 log(DEBUG,"Resolver::Resolver: RESOLVER_NOTREADY");
985                 this->OnError(RESOLVER_NOTREADY);
986                 ModuleException e("Resolver: Core not initialized yet");
987                 throw e;
988                 /* We shouldnt get here really */
989                 return;
990         }
991 }
992
993 Resolver::~Resolver()
994 {
995         log(DEBUG,"Resolver::~Resolver");
996         if (ServerInstance && ServerInstance->SE)
997                 ServerInstance->SE->DelFd(this->fd);
998 }
999
1000 int Resolver::GetFd()
1001 {
1002         return this->fd;
1003 }
1004
1005 bool Resolver::ProcessResult()
1006 {
1007         log(DEBUG,"Resolver::ProcessResult");
1008         if (this->fwd)
1009                 result = Query.GetResultIP();
1010         else
1011                 result = Query.GetResult();
1012
1013         if (result != "")
1014         {
1015                 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
1016                 this->OnLookupComplete(result);
1017                 return true;
1018         }
1019         else
1020         {
1021                 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
1022                 this->OnError(RESOLVER_NXDOMAIN);
1023                 return false;
1024         }
1025 }
1026
1027 void Resolver::OnLookupComplete(const std::string &result)
1028 {
1029 }
1030
1031 void Resolver::OnError(ResolverError e)
1032 {
1033 }
1034
1035 void dns_deal_with_classes(int fd)
1036 {
1037         log(DEBUG,"dns_deal_with_classes(%d)",fd);
1038         if ((fd > -1) && (dns_classes[fd]))
1039         {
1040                 log(DEBUG,"Valid fd %d",fd);
1041                 dns_classes[fd]->ProcessResult();
1042                 delete dns_classes[fd];
1043                 dns_classes[fd] = NULL;
1044         }
1045 }
1046
1047 bool dns_add_class(Resolver* r)
1048 {
1049         log(DEBUG,"dns_add_class");
1050         if ((r) && (r->GetFd() > -1))
1051         {
1052                 if (!dns_classes[r->GetFd()])
1053                 {
1054                         log(DEBUG,"dns_add_class: added class");
1055                         dns_classes[r->GetFd()] = r;
1056                         return true;
1057                 }
1058                 else
1059                 {
1060                         log(DEBUG,"Space occupied!");
1061                         return false;
1062                 }
1063         }
1064         else
1065         {
1066                 log(DEBUG,"Bad class");
1067                 delete r;
1068                 return true;
1069         }
1070 }
1071
1072 void init_dns()
1073 {
1074         memset(dns_classes,0,sizeof(dns_classes));
1075 }
1076