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