]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
Remove unused temp buffers
[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);
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)
175 {
176         insp_sockaddr addr;
177         unsigned char payload[sizeof(s_header)];
178
179         dns_empty_header(payload,h,l);
180
181         memset(&addr,0,sizeof(addr));
182 #ifdef IPV6
183         memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
184         addr.sin6_family = AF_FAMILY;
185         addr.sin6_port = htons(53);
186 #else
187         memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
188         addr.sin_family = AF_FAMILY;
189         addr.sin_port = htons(53);
190 #endif
191         if (sendto(master_socket, payload, l + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
192         {
193                 log(DEBUG,"Error in sendto!");
194                 return -1;
195         }
196
197         return 0;
198 }
199
200 s_connection* dns_add_query(s_header *h, int &id)
201 {
202
203         id = rand() % 65536;
204         s_connection * s = new s_connection();
205
206         h->id[0] = s->id[0] = id >> 8;
207         h->id[1] = s->id[1] = id & 0xFF;
208         h->flags1 = 0 | FLAGS1_MASK_RD;
209         h->flags2 = 0;
210         h->qdcount = 1;
211         h->ancount = 0;
212         h->nscount = 0;
213         h->arcount = 0;
214
215         if (connections.find(id) == connections.end())
216                 connections[id] = s;
217         return s;
218 }
219
220 void create_socket()
221 {
222         log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
223         insp_inaddr addr;
224         srand((unsigned int) TIME);
225         memset(&myserver,0,sizeof(insp_inaddr));
226         if (insp_aton(Config->DNSServer,&addr) > 0)
227                 memcpy(&myserver,&addr,sizeof(insp_inaddr));
228
229         master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
230         if (master_socket != -1)
231         {
232                 log(DEBUG,"Set query socket nonblock");
233                 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
234                 {
235                         shutdown(master_socket,2);
236                         close(master_socket);
237                         master_socket = -1;
238                 }
239         }
240         if (master_socket != -1)
241         {
242 #ifdef IPV6
243                 insp_sockaddr addr;
244                 memset(&addr,0,sizeof(addr));
245                 addr.sin6_family = AF_FAMILY;
246                 addr.sin6_port = 0;
247                 memset(&addr.sin6_addr,255,sizeof(in6_addr));
248 #else
249                 insp_sockaddr addr;
250                 memset(&addr,0,sizeof(addr));
251                 addr.sin_family = AF_FAMILY;
252                 addr.sin_port = 0;
253                 addr.sin_addr.s_addr = INADDR_ANY;
254 #endif
255                 log(DEBUG,"Binding query port");
256                 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
257                 {
258                         log(DEBUG,"Cant bind with source port = 0");
259                         shutdown(master_socket,2);
260                         close(master_socket);
261                         master_socket = -1;
262                 }
263
264                 if (master_socket >= 0)
265                 {
266                         log(DEBUG,"Attach query port to socket engine");
267                         if (ServerInstance && ServerInstance->SE)
268                                 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
269                 }
270         }
271 }
272
273 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
274 {
275         short payloadpos;
276         const char * tempchr, * tempchr2;
277         unsigned short l;
278
279         payloadpos = 0;
280         tempchr2 = name;
281
282         /* split name up into labels, create query */
283         while ((tempchr = strchr(tempchr2,'.')) != NULL)
284         {
285                 l = tempchr - tempchr2;
286                 if (payloadpos + l + 1 > 507)
287                         return -1;
288                 payload[payloadpos++] = l;
289                 memcpy(&payload[payloadpos],tempchr2,l);
290                 payloadpos += l;
291                 tempchr2 = &tempchr[1];
292         }
293         l = strlen(tempchr2);
294         if (l)
295         {
296                 if (payloadpos + l + 2 > 507)
297                         return -1;
298                 payload[payloadpos++] = l;
299                 memcpy(&payload[payloadpos],tempchr2,l);
300                 payloadpos += l;
301                 payload[payloadpos++] = '\0';
302         }
303         if (payloadpos > 508)
304                 return -1;
305         l = htons(rr);
306         memcpy(&payload[payloadpos],&l,2);
307         l = htons(_class);
308         memcpy(&payload[payloadpos + 2],&l,2);
309         return payloadpos + 4;
310 }
311
312 int DNS::dns_getip4(const char *name)
313 {
314         /* build, add and send A query; retrieve result with dns_getresult() */
315         s_header h;
316         s_connection *s;
317         int l;
318         int id;
319         
320         l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
321         if (l == -1)
322                 return -1;
323         s = dns_add_query(&h, id);
324         if (s == NULL)
325                 return -1;
326         s->_class = 1;
327         s->type = DNS_QRY_A;
328         if (s->send_requests(&h,l) == -1)
329                 return -1;
330
331         return id;
332 }
333
334 int DNS::dns_getname4(const insp_inaddr *ip)
335 { /* build, add and send PTR query; retrieve result with dns_getresult() */
336 #ifdef IPV6
337         return -1;
338 #else
339         log(DEBUG,"DNS::dns_getname4");
340         char query[512];
341         s_header h;
342         s_connection * s;
343         unsigned char *c;
344         int l;
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         l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
352         if (l == -1)
353                 return -1;
354         s = dns_add_query(&h, id);
355         if (s == NULL)
356                 return -1;
357         s->_class = 1;
358         s->type = DNS_QRY_PTR;
359         if (s->send_requests(&h,l) == -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                 this->myid = Res->dns_getip4(source.c_str());
582         }
583         else
584         {
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