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