]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
Tidyup var names. If i ever meet this person who called all the firedns vars 'l'...
[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* req;
320         int id;
321         
322         int length = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char*)&h.payload);
323         if (length == -1)
324                 return -1;
325         req = dns_add_query(&h, id);
326         if (req == NULL)
327                 return -1;
328
329         if (req->send_requests(&h,length,DNS_QRY_A) == -1)
330                 return -1;
331
332         return id;
333 }
334
335 int DNS::dns_getname4(const insp_inaddr *ip)
336 { /* build, add and send PTR query; retrieve result with dns_getresult() */
337 #ifdef IPV6
338         return -1;
339 #else
340         log(DEBUG,"DNS::dns_getname4");
341         char query[512];
342         s_header h;
343         s_connection* req;
344         unsigned char *c;
345         int id;
346
347         c = (unsigned char *)&ip->s_addr;
348
349         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
350
351         int length = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload);
352         if (length == -1)
353                 return -1;
354         req = dns_add_query(&h, id);
355         if (req == NULL)
356                 return -1;
357         if (req->send_requests(&h,length,DNS_QRY_PTR) == -1)
358                 return -1;
359
360         return id;
361 #endif
362 }
363
364 /* Return the next id which is ready, and the result attached to it
365  */
366 DNSResult DNS::dns_getresult()
367 {
368         /* retrieve result of DNS query (buffered) */
369         s_header h;
370         s_connection *c;
371         int length;
372         unsigned char buffer[sizeof(s_header)];
373
374         length = recv(master_socket,buffer,sizeof(s_header),0);
375
376         if (length < 12)
377                 return std::make_pair(-1,"");
378
379         dns_fill_header(&h,buffer,length - 12);
380
381         // Get the id of this request
382         unsigned long this_id = h.id[1] + (h.id[0] << 8);
383
384         // Do we have a pending request for it?
385
386         connlist_iter n_iter = connections.find(this_id);
387         if (n_iter == connections.end())
388         {
389                 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
390                 return std::make_pair(-1,"");
391         }
392         else
393         {
394                 /* Remove the query from the list */
395                 c = (s_connection*)n_iter->second;
396                 /* We don't delete c here, because its done later when needed */
397                 connections.erase(n_iter);
398         }
399         unsigned char* a = c->result_ready(h, length);
400         std::string resultstr;
401
402         if (a == NULL)
403         {
404                 resultstr = "";
405         }
406         else
407         {
408                 if (c->type == DNS_QRY_A)
409                 {
410                         char formatted[1024];
411                         snprintf(formatted,1024,"%u.%u.%u.%u",a[0],a[1],a[2],a[3]);
412                         resultstr = std::string(formatted);
413                 }
414                 else
415                 {
416                         resultstr = std::string((const char*)a);
417                 }
418         }
419
420         delete c;
421         return std::make_pair(this_id,resultstr);
422 }
423
424 /** A result is ready, process it
425  */
426 unsigned char* s_connection::result_ready(s_header &h, int length)
427 {
428         int i, q, curanswer, o;
429         s_rr_middle rr;
430         unsigned short p;
431                                         
432         if ((h.flags1 & FLAGS1_MASK_QR) == 0)
433         {
434                 log(DEBUG,"DNS: didnt get a query result");
435                 return NULL;
436         }
437         if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
438         {
439                 log(DEBUG,"DNS: got an OPCODE and didnt want one");
440                 return NULL;
441         }
442         if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
443         {
444                 log(DEBUG,"DNS lookup failed due to SERVFAIL");
445                 return NULL;
446         }
447         if (h.ancount < 1)
448         {
449                 log(DEBUG,"DNS: no answers!");
450                 return NULL;
451         }
452         i = 0;
453         q = 0;
454         length -= 12;
455         while ((unsigned)q < h.qdcount && i < length)
456         {
457                 if (h.payload[i] > 63)
458                 {
459                         i += 6;
460                         q++;
461                 }
462                 else
463                 {
464                         if (h.payload[i] == 0)
465                         {
466                                 q++;
467                                 i += 5;
468                         }
469                         else i += h.payload[i] + 1;
470                 }
471         }
472         curanswer = 0;
473         while ((unsigned)curanswer < h.ancount)
474         {
475                 q = 0;
476                 while (q == 0 && i < length)
477                 {
478                         if (h.payload[i] > 63)
479                         {
480                                 i += 2;
481                                 q = 1;
482                         }
483                         else
484                         {
485                                 if (h.payload[i] == 0)
486                                 {
487                                         i++;
488                                         q = 1;
489                                 }
490                                 else i += h.payload[i] + 1; /* skip length and label */
491                         }
492                 }
493                 if (length - i < 10)
494                 {
495                         return NULL;
496                 }
497                 dns_fill_rr(&rr,&h.payload[i]);
498                 i += 10;
499                 if (rr.type != this->type)
500                 {
501                         curanswer++;
502                         i += rr.rdlength;
503                         continue;
504                 }
505                 if (rr._class != this->_class)
506                 {
507                         curanswer++;
508                         i += rr.rdlength;
509                         continue;
510                 }
511                 break;
512         }
513         if ((unsigned int)curanswer == h.ancount)
514                 return NULL;
515         if (i + rr.rdlength > (unsigned int)length)
516                 return NULL;
517         if (rr.rdlength > 1023)
518                 return NULL;
519
520         switch (rr.type)
521         {
522                 case DNS_QRY_PTR:
523                         log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
524                         o = 0;
525                         q = 0;
526                         while (q == 0 && i < length && o + 256 < 1023)
527                         {
528                                 if (h.payload[i] > 63)
529                                 {
530                                         log(DEBUG,"DNS: h.payload[i] > 63");
531                                         memcpy(&p,&h.payload[i],2);
532                                         i = ntohs(p) - 0xC000 - 12;
533                                 }
534                                 else
535                                 {
536                                         if (h.payload[i] == 0)
537                                         {
538                                                 q = 1;
539                                         }
540                                         else
541                                         {
542                                                 res[o] = '\0';
543                                                 if (o != 0)
544                                                         res[o++] = '.';
545                                                 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
546                                                 o += h.payload[i];
547                                                 i += h.payload[i] + 1;
548                                         }
549                                 }
550                         }
551                         res[o] = '\0';
552                 break;
553                 case DNS_QRY_A:
554                         log(DEBUG,"DNS: got a result of type DNS_QRY_A");
555                         memcpy(res,&h.payload[i],rr.rdlength);
556                         res[rr.rdlength] = '\0';
557                         break;
558                 default:
559                         memcpy(res,&h.payload[i],rr.rdlength);
560                         res[rr.rdlength] = '\0';
561                         break;
562         }
563         return res;
564 }
565
566 DNS::DNS()
567 {
568         log(DEBUG,"Create blank DNS");
569 }
570
571 DNS::~DNS()
572 {
573 }
574
575 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
576 {
577         if (forward)
578         {
579                 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
580                 this->myid = Res->dns_getip4(source.c_str());
581         }
582         else
583         {
584                 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
585                 insp_inaddr binip;
586                 if (insp_aton(source.c_str(), &binip) > 0)
587                 {
588                         /* Valid ip address */
589                         this->myid = Res->dns_getname4(&binip);
590                 }
591         }
592         if (this->myid == -1)
593         {
594                 log(DEBUG,"Resolver::Resolver: Could not get an id!");
595                 this->OnError(RESOLVER_NSDOWN);
596                 throw ModuleException("Resolver: Couldnt get an id to make a request");
597                 /* We shouldnt get here really */
598                 return;
599         }
600
601         log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
602 }
603
604 void Resolver::OnLookupComplete(const std::string &result)
605 {
606 }
607
608 void Resolver::OnError(ResolverError e)
609 {
610 }
611
612 Resolver::~Resolver()
613 {
614         log(DEBUG,"Resolver::~Resolver");
615 }
616
617 int Resolver::GetId()
618 {
619         return this->myid;
620 }
621
622 bool Resolver::ProcessResult(const std::string &result)
623 {
624         log(DEBUG,"Resolver::ProcessResult");
625
626         if (!result.length())
627         {
628                 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
629                 this->OnError(RESOLVER_NXDOMAIN);
630                 return false;
631         }
632         else
633         {
634
635                 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
636                 this->OnLookupComplete(result);
637                 return true;
638         }
639 }
640
641 void dns_deal_with_classes(int fd)
642 {
643         log(DEBUG,"dns_deal_with_classes(%d)",fd);
644         if (fd == master_socket)
645         {
646                 DNSResult res = Res->dns_getresult();
647                 if (res.first != -1)
648                 {
649                         log(DEBUG,"Result available, id=%d",res.first);
650                         if (dns_classes[res.first])
651                         {
652                                 dns_classes[res.first]->ProcessResult(res.second);
653                                 delete dns_classes[res.first];
654                                 dns_classes[res.first] = NULL;
655                         }
656                 }
657         }
658 }
659
660 bool dns_add_class(Resolver* r)
661 {
662         log(DEBUG,"dns_add_class");
663         if ((r) && (r->GetId() > -1))
664         {
665                 if (!dns_classes[r->GetId()])
666                 {
667                         log(DEBUG,"dns_add_class: added class");
668                         dns_classes[r->GetId()] = r;
669                         return true;
670                 }
671                 else
672                 {
673                         log(DEBUG,"Space occupied!");
674                         return false;
675                 }
676         }
677         else
678         {
679                 log(DEBUG,"Bad class");
680                 delete r;
681                 return true;
682         }
683 }
684
685 void init_dns()
686 {
687         Res = new DNS();
688         memset(dns_classes,0,sizeof(dns_classes));
689         create_socket();
690 }
691