]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
Removed threaded dns (it might make a comeback some day, but as it stands its incompa...
[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 extern InspIRCd* ServerInstance;
57 extern ServerConfig* Config;
58 extern time_t TIME;
59 extern userrec* fd_ref_table[MAX_DESCRIPTORS];
60
61 enum QueryType { DNS_QRY_A = 1, DNS_QRY_PTR = 12 };
62 enum QueryFlags1 { FLAGS1_MASK_RD = 0x01, FLAGS1_MASK_TC = 0x02, FLAGS1_MASK_AA = 0x04, FLAGS1_MASK_OPCODE = 0x78, FLAGS1_MASK_QR = 0x80 };
63 enum QueryFlags2 { FLAGS2_MASK_RCODE = 0x0F, FLAGS2_MASK_Z = 0x70, FLAGS2_MASK_RA = 0x80 };
64
65 class s_connection;
66
67 typedef std::map<int,s_connection*> connlist;
68 typedef connlist::iterator connlist_iter;
69
70 DNS* Res = NULL;
71
72 /* As lookups are completed, they are pushed into this result_list */
73 std::map<int, std::string> result_list;
74 connlist connections;
75
76 int master_socket = -1;
77
78 Resolver* dns_classes[65536];
79
80 insp_inaddr servers[8];
81 int i4;
82
83 class s_rr_middle
84 {
85  public:
86         QueryType       type;
87         unsigned int    _class;
88         unsigned long   ttl;
89         unsigned int    rdlength;
90 };
91
92 class s_header
93 {
94  public:
95         unsigned char   id[2];
96         unsigned int    flags1;
97         unsigned int    flags2;
98         unsigned int    qdcount;
99         unsigned int    ancount;
100         unsigned int    nscount;
101         unsigned int    arcount;
102         unsigned char   payload[512];
103 };
104
105 class s_connection
106 {
107  public:
108         unsigned char   id[2];
109         unsigned char   res[512];
110         unsigned int    _class;
111         QueryType       type;
112         int             want_list;
113
114         s_connection()
115         {
116                 *res = 0;
117         }
118
119         unsigned char*  result_ready(s_header &h, int length);
120         int             send_requests(const s_header *h, const int l);
121 };
122
123
124
125 void *dns_align(void *inp)
126 {
127         char *p = (char*)inp;
128         int offby = ((char *)p - (char *)0) % (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long));
129         if (offby != 0)
130                 return p + ((sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long)) - offby);
131         else
132                 return p;
133 }
134
135 /*
136  * Optimized by brain, these were using integer division and modulus.
137  * We can use logic shifts and logic AND to replace these even divisions
138  * and multiplications, it should be a bit faster (probably not noticably,
139  * but of course, more impressive). Also made these inline.
140  */
141
142 inline void dns_fill_rr(s_rr_middle* rr, const unsigned char *input)
143 {
144         rr->type = (QueryType)((input[0] << 8) + input[1]);
145         rr->_class = (input[2] << 8) + input[3];
146         rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
147         rr->rdlength = (input[8] << 8) + input[9];
148 }
149
150 inline void dns_fill_header(s_header *header, const unsigned char *input, const int l)
151 {
152         header->id[0] = input[0];
153         header->id[1] = input[1];
154         header->flags1 = input[2];
155         header->flags2 = input[3];
156         header->qdcount = (input[4] << 8) + input[5];
157         header->ancount = (input[6] << 8) + input[7];
158         header->nscount = (input[8] << 8) + input[9];
159         header->arcount = (input[10] << 8) + input[11];
160         memcpy(header->payload,&input[12],l);
161 }
162
163 inline void dns_empty_header(unsigned char *output, const s_header *header, const int l)
164 {
165         output[0] = header->id[0];
166         output[1] = header->id[1];
167         output[2] = header->flags1;
168         output[3] = header->flags2;
169         output[4] = header->qdcount >> 8;
170         output[5] = header->qdcount & 0xFF;
171         output[6] = header->ancount >> 8;
172         output[7] = header->ancount & 0xFF;
173         output[8] = header->nscount >> 8;
174         output[9] = header->nscount & 0xFF;
175         output[10] = header->arcount >> 8;
176         output[11] = header->arcount & 0xFF;
177         memcpy(&output[12],header->payload,l);
178 }
179
180
181 int s_connection::send_requests(const s_header *h, const int l)
182 {
183         insp_sockaddr addr;
184         unsigned char payload[sizeof(s_header)];
185
186         dns_empty_header(payload,h,l);
187
188         /* otherwise send via standard ipv4 boringness */
189         memset(&addr,0,sizeof(addr));
190 #ifdef IPV6
191         memcpy(&addr.sin6_addr,&servers[0],sizeof(addr.sin6_addr));
192         addr.sin6_family = AF_FAMILY;
193         addr.sin6_port = htons(53);
194 #else
195         memcpy(&addr.sin_addr.s_addr,&servers[0],sizeof(addr.sin_addr));
196         addr.sin_family = AF_FAMILY;
197         addr.sin_port = htons(53);
198 #endif
199         if (sendto(master_socket, payload, l + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
200         {
201                 log(DEBUG,"Error in sendto!");
202                 return -1;
203         }
204
205         return 0;
206 }
207
208 s_connection* dns_add_query(s_header *h, int &id)
209 {
210
211         id = rand() % 65536;
212         s_connection * s = new s_connection();
213
214         /* set header flags */
215         h->id[0] = s->id[0] = id >> 8;
216         h->id[1] = s->id[1] = id & 0xFF;
217         h->flags1 = 0 | FLAGS1_MASK_RD;
218         h->flags2 = 0;
219         h->qdcount = 1;
220         h->ancount = 0;
221         h->nscount = 0;
222         h->arcount = 0;
223         s->want_list = 0;
224         if (connections.find(id) == connections.end())
225                 connections[id] = s;
226         return s;
227 }
228
229 void create_socket()
230 {
231         log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
232         insp_inaddr addr;
233         i4 = 0;
234         srand((unsigned int) TIME);
235         memset(servers,'\0',sizeof(insp_inaddr) * 8);
236         if (insp_aton(Config->DNSServer,&addr) > 0)
237                 memcpy(&servers[i4++],&addr,sizeof(insp_inaddr));
238
239         master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
240         if (master_socket != -1)
241         {
242                 log(DEBUG,"Set query socket nonblock");
243                 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
244                 {
245                         shutdown(master_socket,2);
246                         close(master_socket);
247                         master_socket = -1;
248                 }
249         }
250         if (master_socket != -1)
251         {
252 #ifdef IPV6
253                 insp_sockaddr addr;
254                 memset(&addr,0,sizeof(addr));
255                 addr.sin6_family = AF_FAMILY;
256                 addr.sin6_port = 0;
257                 memset(&addr.sin6_addr,255,sizeof(in6_addr));
258 #else
259                 insp_sockaddr addr;
260                 memset(&addr,0,sizeof(addr));
261                 addr.sin_family = AF_FAMILY;
262                 addr.sin_port = 0;
263                 addr.sin_addr.s_addr = INADDR_ANY;
264 #endif
265                 log(DEBUG,"Binding query port");
266                 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
267                 {
268                         log(DEBUG,"Cant bind with source port = 0");
269                         shutdown(master_socket,2);
270                         close(master_socket);
271                         master_socket = -1;
272                 }
273
274                 if (master_socket >= 0)
275                 {
276                         log(DEBUG,"Attach query port to socket engine");
277                         if (ServerInstance && ServerInstance->SE)
278                                 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
279                 }
280         }
281 }
282
283 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
284 {
285         short payloadpos;
286         const char * tempchr, * tempchr2;
287         unsigned short l;
288
289         payloadpos = 0;
290         tempchr2 = name;
291
292         /* split name up into labels, create query */
293         while ((tempchr = strchr(tempchr2,'.')) != NULL)
294         {
295                 l = tempchr - tempchr2;
296                 if (payloadpos + l + 1 > 507)
297                         return -1;
298                 payload[payloadpos++] = l;
299                 memcpy(&payload[payloadpos],tempchr2,l);
300                 payloadpos += l;
301                 tempchr2 = &tempchr[1];
302         }
303         l = strlen(tempchr2);
304         if (l)
305         {
306                 if (payloadpos + l + 2 > 507)
307                         return -1;
308                 payload[payloadpos++] = l;
309                 memcpy(&payload[payloadpos],tempchr2,l);
310                 payloadpos += l;
311                 payload[payloadpos++] = '\0';
312         }
313         if (payloadpos > 508)
314                 return -1;
315         l = htons(rr);
316         memcpy(&payload[payloadpos],&l,2);
317         l = htons(_class);
318         memcpy(&payload[payloadpos + 2],&l,2);
319         return payloadpos + 4;
320 }
321
322 int DNS::dns_getip4(const char *name)
323 {
324         /* build, add and send A query; retrieve result with dns_getresult() */
325         s_header h;
326         s_connection *s;
327         int l;
328         int id;
329         
330         l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
331         if (l == -1)
332                 return -1;
333         s = dns_add_query(&h, id);
334         if (s == NULL)
335                 return -1;
336         s->_class = 1;
337         s->type = DNS_QRY_A;
338         if (s->send_requests(&h,l) == -1)
339                 return -1;
340
341         return id;
342 }
343
344 int DNS::dns_getname4(const insp_inaddr *ip)
345 { /* build, add and send PTR query; retrieve result with dns_getresult() */
346 #ifdef IPV6
347         return -1;
348 #else
349         log(DEBUG,"DNS::dns_getname4");
350         char query[512];
351         s_header h;
352         s_connection * s;
353         unsigned char *c;
354         int l;
355         int id;
356
357         c = (unsigned char *)&ip->s_addr;
358
359         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
360
361         l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
362         if (l == -1)
363                 return -1;
364         s = dns_add_query(&h, id);
365         if (s == NULL)
366                 return -1;
367         s->_class = 1;
368         s->type = DNS_QRY_PTR;
369         if (s->send_requests(&h,l) == -1)
370                 return -1;
371
372         return id;
373 #endif
374 }
375
376 /* Return the next id which is ready.
377  * result_list[id] will have been populated with the result string.
378  */
379 int DNS::dns_getresult()
380 {
381         /* retrieve result of DNS query (buffered) */
382         s_header h;
383         s_connection *c;
384         int length;
385         unsigned char buffer[sizeof(s_header)];
386
387         length = recv(master_socket,buffer,sizeof(s_header),0);
388
389         if (length < 12)
390                 return -1;
391
392         dns_fill_header(&h,buffer,length - 12);
393
394         // Get the id of this request
395         unsigned long this_id = h.id[1] + (h.id[0] << 8);
396
397         // Do we have a pending request for it?
398
399         connlist_iter n_iter = connections.find(this_id);
400         if (n_iter == connections.end())
401         {
402                 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
403                 return -1;
404         }
405         else
406         {
407                 /* Remove the query from the list */
408                 c = (s_connection*)n_iter->second;
409                 /* We don't delete c here, because its done later when needed */
410                 connections.erase(n_iter);
411         }
412         unsigned char* a = c->result_ready(h, length);
413
414         if (a == NULL)
415         {
416                 result_list[this_id] = "";
417         }
418         else
419         {
420                 if (c->type == DNS_QRY_A)
421                 {
422                         char formatted[1024];
423                         snprintf(formatted,1024,"%u.%u.%u.%u",a[0],a[1],a[2],a[3]);
424                         result_list[this_id] = std::string(formatted);
425                 }
426                 else
427                 {
428                         result_list[this_id] = std::string((const char*)a);
429                 }
430         }
431
432         delete c;
433         return this_id;
434 }
435
436 /** A result is ready, process it
437  */
438 unsigned char* s_connection::result_ready(s_header &h, int length)
439 {
440         int i, q, curanswer, o;
441         s_rr_middle rr;
442         unsigned short p;
443                                         
444         if ((h.flags1 & FLAGS1_MASK_QR) == 0)
445         {
446                 log(DEBUG,"DNS: didnt get a query result");
447                 return NULL;
448         }
449         if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
450         {
451                 log(DEBUG,"DNS: got an OPCODE and didnt want one");
452                 return NULL;
453         }
454         if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
455         {
456                 log(DEBUG,"DNS lookup failed due to SERVFAIL");
457                 return NULL;
458         }
459         if (h.ancount < 1)
460         {
461                 log(DEBUG,"DNS: no answers!");
462                 return NULL;
463         }
464         i = 0;
465         q = 0;
466         length -= 12;
467         while ((unsigned)q < h.qdcount && i < length)
468         {
469                 if (h.payload[i] > 63)
470                 {
471                         i += 6;
472                         q++;
473                 }
474                 else
475                 {
476                         if (h.payload[i] == 0)
477                         {
478                                 q++;
479                                 i += 5;
480                         }
481                         else i += h.payload[i] + 1;
482                 }
483         }
484         curanswer = 0;
485         while ((unsigned)curanswer < h.ancount)
486         {
487                 q = 0;
488                 while (q == 0 && i < length)
489                 {
490                         if (h.payload[i] > 63)
491                         {
492                                 i += 2;
493                                 q = 1;
494                         }
495                         else
496                         {
497                                 if (h.payload[i] == 0)
498                                 {
499                                         i++;
500                                         q = 1;
501                                 }
502                                 else i += h.payload[i] + 1; /* skip length and label */
503                         }
504                 }
505                 if (length - i < 10)
506                 {
507                         return NULL;
508                 }
509                 dns_fill_rr(&rr,&h.payload[i]);
510                 i += 10;
511                 if (rr.type != this->type)
512                 {
513                         curanswer++;
514                         i += rr.rdlength;
515                         continue;
516                 }
517                 if (rr._class != this->_class)
518                 {
519                         curanswer++;
520                         i += rr.rdlength;
521                         continue;
522                 }
523                 break;
524         }
525         if ((unsigned int)curanswer == h.ancount)
526                 return NULL;
527         if (i + rr.rdlength > (unsigned int)length)
528                 return NULL;
529         if (rr.rdlength > 1023)
530                 return NULL;
531
532         switch (rr.type)
533         {
534                 case DNS_QRY_PTR:
535                         log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
536                         o = 0;
537                         q = 0;
538                         while (q == 0 && i < length && o + 256 < 1023)
539                         {
540                                 if (h.payload[i] > 63)
541                                 {
542                                         log(DEBUG,"DNS: h.payload[i] > 63");
543                                         memcpy(&p,&h.payload[i],2);
544                                         i = ntohs(p) - 0xC000 - 12;
545                                 }
546                                 else
547                                 {
548                                         if (h.payload[i] == 0)
549                                         {
550                                                 q = 1;
551                                         }
552                                         else
553                                         {
554                                                 res[o] = '\0';
555                                                 if (o != 0)
556                                                         res[o++] = '.';
557                                                 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
558                                                 o += h.payload[i];
559                                                 i += h.payload[i] + 1;
560                                         }
561                                 }
562                         }
563                         res[o] = '\0';
564                 break;
565                 case DNS_QRY_A:
566                         log(DEBUG,"DNS: got a result of type DNS_QRY_A");
567                         if (this->want_list)
568                         {
569                                 dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
570                                 while ((char *)alist - (char *)res < 700)
571                                 {
572                                         if (rr.type != DNS_QRY_A)
573                                                 break;
574                                         if (rr._class != 1)
575                                                 break;
576                                         if (rr.rdlength != 4)
577                                         {
578                                                 return NULL;
579                                         }
580                                         memcpy(&alist->ip,&h.payload[i],4);
581                                         if ((unsigned)++curanswer >= h.ancount)
582                                                 break;
583                                         i += rr.rdlength;
584                                         q = 0;
585                                         while (q == 0 && i < length)
586                                         {
587                                                 if (h.payload[i] > 63)
588                                                 {
589                                                         i += 2;
590                                                         q = 1;
591                                                 }
592                                                 else
593                                                 {
594                                                         if (h.payload[i] == 0)
595                                                         {
596                                                                 i++;
597                                                                 q = 1;
598                                                         }
599                                                         else i += h.payload[i] + 1;
600                                                 }
601                                         }
602                                         if (length - i < 10)
603                                         {
604                                                 return NULL;
605                                         }
606                                         dns_fill_rr(&rr,&h.payload[i]);
607                                         i += 10;
608                                         alist->next = (dns_ip4list *) dns_align(((char *) alist) + sizeof(dns_ip4list));
609                                         alist = alist->next;
610                                         alist->next = NULL;
611                                 }
612                                 alist->next = NULL;
613                                 break;
614                         }
615                         memcpy(res,&h.payload[i],rr.rdlength);
616                         res[rr.rdlength] = '\0';
617                         break;
618                 default:
619                         memcpy(res,&h.payload[i],rr.rdlength);
620                         res[rr.rdlength] = '\0';
621                         break;
622         }
623         return res;
624 }
625
626 DNS::DNS()
627 {
628         log(DEBUG,"Create blank DNS");
629 }
630
631 DNS::~DNS()
632 {
633 }
634
635 Resolver::Resolver(const std::string &source, bool forward, const std::string &dnsserver = "") : input(source), fwd(forward), server(dnsserver)
636 {
637         if (forward)
638         {
639                 this->myid = Res->dns_getip4(source.c_str());
640         }
641         else
642         {
643                 insp_inaddr binip;
644                 if (insp_aton(source.c_str(), &binip) > 0)
645                 {
646                         /* Valid ip address */
647                         this->myid = Res->dns_getname4(&binip);
648                 }
649         }
650         if (this->myid == -1)
651         {
652                 log(DEBUG,"Resolver::Resolver: Could not get an id!");
653                 this->OnError(RESOLVER_NSDOWN);
654                 ModuleException e("Resolver: Couldnt get an id to make a request");
655                 throw e;
656                 /* We shouldnt get here really */
657                 return;
658         }
659
660         log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
661 }
662
663 Resolver::~Resolver()
664 {
665         log(DEBUG,"Resolver::~Resolver");
666 }
667
668 int Resolver::GetId()
669 {
670         return this->myid;
671 }
672
673 bool Resolver::ProcessResult()
674 {
675         log(DEBUG,"Resolver::ProcessResult");
676
677         std::map<int, std::string>::iterator x = result_list.find(this->myid);
678
679         if (x == result_list.end())
680         {
681                 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
682                 this->OnError(RESOLVER_NXDOMAIN);
683                 return false;
684         }
685         else
686         {
687
688                 log(DEBUG,"Resolver::OnLookupComplete(%s)",x->second.c_str());
689                 this->OnLookupComplete(x->second);
690                 result_list.erase(x);
691                 return true;
692         }
693 }
694
695 void Resolver::OnLookupComplete(const std::string &result)
696 {
697 }
698
699 void Resolver::OnError(ResolverError e)
700 {
701 }
702
703 void dns_deal_with_classes(int fd)
704 {
705         log(DEBUG,"dns_deal_with_classes(%d)",fd);
706         if (fd == master_socket)
707         {
708                 int id = Res->dns_getresult();
709                 if (id != -1)
710                 {
711                         log(DEBUG,"Result available, id=%d",id);
712                         dns_classes[id]->ProcessResult();
713                         delete dns_classes[id];
714                         dns_classes[id] = NULL;
715                 }
716         }
717 }
718
719 bool dns_add_class(Resolver* r)
720 {
721         log(DEBUG,"dns_add_class");
722         if ((r) && (r->GetId() > -1))
723         {
724                 if (!dns_classes[r->GetId()])
725                 {
726                         log(DEBUG,"dns_add_class: added class");
727                         dns_classes[r->GetId()] = r;
728                         return true;
729                 }
730                 else
731                 {
732                         log(DEBUG,"Space occupied!");
733                         return false;
734                 }
735         }
736         else
737         {
738                 log(DEBUG,"Bad class");
739                 delete r;
740                 return true;
741         }
742 }
743
744 void init_dns()
745 {
746         Res = new DNS();
747         memset(dns_classes,0,sizeof(dns_classes));
748         create_socket();
749 }
750