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