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