]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
Make threaded dns stable by placing some mutexes around some stl stuff (this really...
[user/henk/code/inspircd.git] / src / dns.cpp
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                 return NULL;
479         }
480         else
481         {
482                 /* Remove the query from the list */
483                 c = (s_connection*)n_iter->second;
484                 /* We don't delete c here, because its done later when needed */
485                 connections.erase(n_iter);
486         }
487 #ifdef THREADED_DNS
488         pthread_mutex_unlock(&connmap_lock);
489 #endif
490
491         l = recv(c->fd,buffer,sizeof(s_header),0);
492         dns_close(c->fd);
493         if (l < 12)
494         {
495                 DELETE(c);
496                 return NULL;
497         }
498         dns_fill_header(&h,buffer,l - 12);
499         if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
500         {
501                 log(DEBUG,"DNS: id mismatch on query");
502                 DELETE(c);
503                 return NULL; /* ID mismatch */
504         }
505         if ((h.flags1 & FLAGS1_MASK_QR) == 0)
506         {
507                 log(DEBUG,"DNS: didnt get a query result");
508                 DELETE(c);
509                 return NULL;
510         }
511         if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
512         {
513                 log(DEBUG,"DNS: got an OPCODE and didnt want one");
514                 DELETE(c);
515                 return NULL;
516         }
517         if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
518         {
519                 log(DEBUG,"DNS lookup failed due to SERVFAIL");
520                 DELETE(c);
521                 return NULL;
522         }
523         if (h.ancount < 1)
524         {
525                 log(DEBUG,"DNS: no answers!");
526                 DELETE(c);
527                 return NULL;
528         }
529         i = 0;
530         q = 0;
531         l -= 12;
532         while ((unsigned)q < h.qdcount && i < l)
533         {
534                 if (h.payload[i] > 63)
535                 {
536                         i += 6;
537                         q++;
538                 }
539                 else
540                 {
541                         if (h.payload[i] == 0)
542                         {
543                                 q++;
544                                 i += 5;
545                         }
546                         else i += h.payload[i] + 1;
547                 }
548         }
549         curanswer = 0;
550         while ((unsigned)curanswer < h.ancount)
551         {
552                 q = 0;
553                 while (q == 0 && i < l)
554                 {
555                         if (h.payload[i] > 63)
556                         {
557                                 i += 2;
558                                 q = 1;
559                         }
560                         else
561                         {
562                                 if (h.payload[i] == 0)
563                                 {
564                                         i++;
565                                         q = 1;
566                                 }
567                                 else i += h.payload[i] + 1; /* skip length and label */
568                         }
569                 }
570                 if (l - i < 10)
571                 {
572                         DELETE(c);
573                         return NULL;
574                 }
575                 dns_fill_rr(&rr,&h.payload[i]);
576                 i += 10;
577                 if (rr.type != c->type)
578                 {
579                         curanswer++;
580                         i += rr.rdlength;
581                         continue;
582                 }
583                 if (rr._class != c->_class)
584                 {
585                         curanswer++;
586                         i += rr.rdlength;
587                         continue;
588                 }
589                 break;
590         }
591         if ((unsigned)curanswer == h.ancount)
592                 return NULL;
593         if ((unsigned)i + rr.rdlength > (unsigned)l)
594                 return NULL;
595         if (rr.rdlength > 1023)
596                 return NULL;
597
598         switch (rr.type)
599         {
600                 case DNS_QRY_PTR:
601                         log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
602                         o = 0;
603                         q = 0;
604                         while (q == 0 && i < l && o + 256 < 1023)
605                         {
606                                 if (h.payload[i] > 63)
607                                 {
608                                         log(DEBUG,"DNS: h.payload[i] > 63");
609                                         memcpy(&p,&h.payload[i],2);
610                                         i = ntohs(p) - 0xC000 - 12;
611                                 }
612                                 else
613                                 {
614                                         if (h.payload[i] == 0)
615                                         {
616                                                 q = 1;
617                                         }
618                                         else
619                                         {
620                                                 res[o] = '\0';
621                                                 if (o != 0)
622                                                         res[o++] = '.';
623                                                 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
624                                                 o += h.payload[i];
625                                                 i += h.payload[i] + 1;
626                                         }
627                                 }
628                         }
629                         res[o] = '\0';
630                 break;
631                 case DNS_QRY_A:
632                         log(DEBUG,"DNS: got a result of type DNS_QRY_A");
633                         if (c->want_list)
634                         {
635                                 dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
636                                 while ((char *)alist - (char *)res < 700)
637                                 {
638                                         if (rr.type != DNS_QRY_A)
639                                                 break;
640                                         if (rr._class != 1)
641                                                 break;
642                                         if (rr.rdlength != 4)
643                                         {
644                                                 DELETE(c);
645                                                 return NULL;
646                                         }
647                                         memcpy(&alist->ip,&h.payload[i],4);
648                                         if ((unsigned)++curanswer >= h.ancount)
649                                                 break;
650                                         i += rr.rdlength;
651                                         q = 0;
652                                         while (q == 0 && i < l)
653                                         {
654                                                 if (h.payload[i] > 63)
655                                                 {
656                                                         i += 2;
657                                                         q = 1;
658                                                 }
659                                                 else
660                                                 {
661                                                         if (h.payload[i] == 0)
662                                                         {
663                                                                 i++;
664                                                                 q = 1;
665                                                         }
666                                                         else i += h.payload[i] + 1;
667                                                 }
668                                         }
669                                         if (l - i < 10)
670                                         {
671                                                 DELETE(c);
672                                                 return NULL;
673                                         }
674                                         dns_fill_rr(&rr,&h.payload[i]);
675                                         i += 10;
676                                         alist->next = (dns_ip4list *) dns_align(((char *) alist) + sizeof(dns_ip4list));
677                                         alist = alist->next;
678                                         alist->next = NULL;
679                                 }
680                                 alist->next = NULL;
681                                 break;
682                         }
683                         memcpy(res,&h.payload[i],rr.rdlength);
684                         res[rr.rdlength] = '\0';
685                         break;
686                 default:
687                         memcpy(res,&h.payload[i],rr.rdlength);
688                         res[rr.rdlength] = '\0';
689                         break;
690         }
691         DELETE(c);
692         return res;
693 }
694
695 DNS::DNS()
696 {
697         dns_init();
698         log(DEBUG,"Create blank DNS");
699 }
700
701 DNS::DNS(const std::string &dnsserver)
702 {
703         dns_init_2(dnsserver.c_str());
704         log(DEBUG,"Create DNS with server '%s'",dnsserver.c_str());
705 }
706
707 void DNS::SetNS(const std::string &dnsserver)
708 {
709         dns_init_2(dnsserver.c_str());
710         log(DEBUG,"Set NS");
711 }
712
713 DNS::~DNS()
714 {
715 }
716
717 bool DNS::ReverseLookup(const std::string &ip, bool ins)
718 {
719         if (ServerInstance && ServerInstance->stats)
720                 ServerInstance->stats->statsDns++;
721         binip = dns_aton4(ip.c_str());
722         if (binip == NULL)
723         {
724                 return false;
725         }
726
727         this->myfd = dns_getname4(binip);
728         if (this->myfd == -1)
729         {
730                 return false;
731         }
732         log(DEBUG,"DNS: ReverseLookup, fd=%d",this->myfd);
733 #ifndef THREADED_DNS
734         if (ins)
735         {
736                 if (ServerInstance && ServerInstance->SE)
737                         ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
738         }
739 #endif
740         return true;
741 }
742
743 bool DNS::ForwardLookup(const std::string &host, bool ins)
744 {
745         if (ServerInstance && ServerInstance->stats)
746                 ServerInstance->stats->statsDns++;
747         this->myfd = dns_getip4(host.c_str());
748         if (this->myfd == -1)
749         {
750                 return false;
751         }
752         log(DEBUG,"DNS: ForwardLookup, fd=%d",this->myfd);
753 #ifndef THREADED_DNS
754         if (ins)
755         {
756                 if (ServerInstance && ServerInstance->SE)
757                         ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
758         }
759 #endif
760         return true;
761 }
762
763 bool DNS::ForwardLookupWithFD(const std::string &host, int &fd)
764 {
765         if (ServerInstance && ServerInstance->stats)
766                 ServerInstance->stats->statsDns++;
767         this->myfd = dns_getip4(host.c_str());
768         fd = this->myfd;
769         if (this->myfd == -1)
770         {
771                 return false;
772         }
773         log(DEBUG,"DNS: ForwardLookupWithFD, fd=%d",this->myfd);
774         if (ServerInstance && ServerInstance->SE)
775                 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_MODULE);
776         return true;
777 }
778
779 bool DNS::HasResult(int fd)
780 {
781         return (fd == this->myfd);
782 }
783
784 /* Only the multithreaded dns uses this poll() based
785  * check now. As its in another thread we dont have
786  * to worry about its performance that much.
787  */
788 bool DNS::HasResult()
789 {
790         log(DEBUG,"DNS: HasResult, fd=%d",this->myfd);
791         pollfd polls;
792         polls.fd = this->myfd;
793         polls.events = POLLIN;
794         int ret = poll(&polls,1,1);
795         log(DEBUG,"DNS: Hasresult returning %d",ret);
796         return (ret > 0);
797 }
798
799 int DNS::GetFD()
800 {
801         return this->myfd;
802 }
803
804 std::string DNS::GetResult()
805 {
806         log(DEBUG,"DNS: GetResult()");
807         result = dns_getresult(this->myfd);
808         if (result)
809         {
810                 if (ServerInstance && ServerInstance->stats)
811                         ServerInstance->stats->statsDnsGood++;
812                 dns_close(this->myfd);
813                 this->myfd = -1;
814                 return result;
815         }
816         else
817         {
818                 if (ServerInstance && ServerInstance->stats)
819                         ServerInstance->stats->statsDnsBad++;
820                 if (this->myfd != -1)
821                 {
822                         dns_close(this->myfd);
823                         this->myfd = -1;
824                 }
825                 return "";
826         }
827 }
828
829 std::string DNS::GetResultIP()
830 {
831         char r[1024];
832         log(DEBUG,"DNS: GetResultIP()");
833         result = dns_getresult(this->myfd);
834         if (this->myfd != -1)
835         {
836                 dns_close(this->myfd);
837                 this->myfd = -1;
838         }
839         if (result)
840         {
841                 if (ServerInstance && ServerInstance->stats)
842                         ServerInstance->stats->statsDnsGood++;
843                 unsigned char a = (unsigned)result[0];
844                 unsigned char b = (unsigned)result[1];
845                 unsigned char c = (unsigned)result[2];
846                 unsigned char d = (unsigned)result[3];
847                 snprintf(r,1024,"%u.%u.%u.%u",a,b,c,d);
848                 return r;
849         }
850         else
851         {
852                 if (ServerInstance && ServerInstance->stats)
853                         ServerInstance->stats->statsDnsBad++;
854                 log(DEBUG,"DANGER WILL ROBINSON! NXDOMAIN for forward lookup, but we got a reverse lookup!");
855                 return "";
856         }
857 }
858
859
860
861 #ifdef THREADED_DNS
862
863 /* This function is a thread function which can be thought of as a lightweight process
864  * to all you non-threaded people. In actuality its so much more, and pretty damn cool.
865  * With threaded dns enabled, each user which connects gets a thread attached to their
866  * user record when their DNS lookup starts. This function starts in parallel, and
867  * commences a blocking dns lookup. Because its a seperate thread, this occurs without
868  * actually blocking the main application. Once the dns lookup is completed, the thread
869  * checks if the user is still around by checking their fd against the reference table,
870  * and if they are, writes the hostname into the struct and terminates, after setting
871  * userrec::dns_done to true. Because this is multi-threaded it can make proper use of
872  * SMP setups (like the one i have here *grin*).
873  * This is in comparison to the non-threaded dns, which must monitor the thread sockets
874  * in a nonblocking fashion, consuming more resources to do so.
875  */
876 void* dns_task(void* arg)
877 {
878         userrec* u = (userrec*)arg;
879         int thisfd = u->fd;
880
881         log(DEBUG,"DNS thread for user %s",u->nick);
882         DNS dns1;
883         DNS dns2;
884         std::string host;
885         std::string ip;
886         if (dns1.ReverseLookup(inet_ntoa(u->ip4),false))
887         {
888                 while (!dns1.HasResult())
889                         usleep(100);
890                 host = dns1.GetResult();
891                 if (host != "")
892                 {
893                         if (dns2.ForwardLookup(host, false))
894                         {
895                                 while (!dns2.HasResult())
896                                         usleep(100);
897                                 ip = dns2.GetResultIP();
898                                 if (ip == std::string(inet_ntoa(u->ip4)))
899                                 {
900                                         if (host.length() < 65)
901                                         {
902                                                 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
903                                                 {
904                                                         if (!u->dns_done)
905                                                         {
906                                                                 strcpy(u->host,host.c_str());
907                                                                 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
908                                                                 {
909                                                                         strcpy(u->dhost,host.c_str());
910                                                                 }
911                                                         }
912                                                 }
913                                         }
914                                 }
915                         }
916                 }
917         }
918         if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
919                 u->dns_done = true;
920         return NULL;
921 }
922 #endif
923
924 Resolver::Resolver(const std::string &source, bool forward, const std::string &dnsserver = "") : input(source), fwd(forward), server(dnsserver)
925 {
926         if (this->server != "")
927                 Query.SetNS(this->server);
928         else
929                 Query.SetNS(Config->DNSServer);
930
931         if (forward)
932         {
933                 Query.ForwardLookup(input.c_str(), false);
934                 this->fd = Query.GetFD();
935         }
936         else
937         {
938                 Query.ReverseLookup(input.c_str(), false);
939                 this->fd = Query.GetFD();
940         }
941         if (fd < 0)
942         {
943                 log(DEBUG,"Resolver::Resolver: RESOLVER_NSDOWN");
944                 this->OnError(RESOLVER_NSDOWN);
945                 ModuleException e("Resolver: Nameserver is down");
946                 throw e;
947                 /* We shouldnt get here really */
948                 return;
949         }
950
951         if (ServerInstance && ServerInstance->SE)
952         {
953                 log(DEBUG,"Resolver::Resolver: this->fd=%d",this->fd);
954                 ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_CLASSDNS);
955         }
956         else
957         {
958                 log(DEBUG,"Resolver::Resolver: RESOLVER_NOTREADY");
959                 this->OnError(RESOLVER_NOTREADY);
960                 ModuleException e("Resolver: Core not initialized yet");
961                 throw e;
962                 /* We shouldnt get here really */
963                 return;
964         }
965 }
966
967 Resolver::~Resolver()
968 {
969         log(DEBUG,"Resolver::~Resolver");
970         if (ServerInstance && ServerInstance->SE)
971                 ServerInstance->SE->DelFd(this->fd);
972 }
973
974 int Resolver::GetFd()
975 {
976         return this->fd;
977 }
978
979 bool Resolver::ProcessResult()
980 {
981         log(DEBUG,"Resolver::ProcessResult");
982         if (this->fwd)
983                 result = Query.GetResultIP();
984         else
985                 result = Query.GetResult();
986
987         if (result != "")
988         {
989                 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
990                 this->OnLookupComplete(result);
991                 return true;
992         }
993         else
994         {
995                 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
996                 this->OnError(RESOLVER_NXDOMAIN);
997                 return false;
998         }
999 }
1000
1001 void Resolver::OnLookupComplete(const std::string &result)
1002 {
1003 }
1004
1005 void Resolver::OnError(ResolverError e)
1006 {
1007 }
1008
1009 void dns_deal_with_classes(int fd)
1010 {
1011         log(DEBUG,"dns_deal_with_classes(%d)",fd);
1012         if ((fd > -1) && (dns_classes[fd]))
1013         {
1014                 log(DEBUG,"Valid fd %d",fd);
1015                 dns_classes[fd]->ProcessResult();
1016                 delete dns_classes[fd];
1017                 dns_classes[fd] = NULL;
1018         }
1019 }
1020
1021 bool dns_add_class(Resolver* r)
1022 {
1023         log(DEBUG,"dns_add_class");
1024         if ((r) && (r->GetFd() > -1))
1025         {
1026                 if (!dns_classes[r->GetFd()])
1027                 {
1028                         log(DEBUG,"dns_add_class: added class");
1029                         dns_classes[r->GetFd()] = r;
1030                         return true;
1031                 }
1032                 else
1033                 {
1034                         log(DEBUG,"Space occupied!");
1035                         return false;
1036                 }
1037         }
1038         else
1039         {
1040                 log(DEBUG,"Bad class");
1041                 delete r;
1042                 return true;
1043         }
1044 }
1045
1046 void init_dns()
1047 {
1048         memset(dns_classes,0,sizeof(dns_classes));
1049 }
1050