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