]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
d7f7b96975b50d0878c2e979df81bf1caeaafda3
[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 <time.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <map>
41 #include "dns.h"
42 #include "inspircd.h"
43 #include "helperfuncs.h"
44 #include "inspircd_config.h"
45 #include "socketengine.h"
46 #include "configreader.h"
47
48 extern InspIRCd* ServerInstance;
49 extern ServerConfig* Config;
50 extern time_t TIME;
51 extern userrec* fd_ref_table[MAX_DESCRIPTORS];
52
53 enum QueryType
54 {
55         DNS_QRY_A       = 1,
56         DNS_QRY_PTR     = 12
57 };
58
59 enum QueryInfo
60 {
61         ERROR_MASK      = 0x10000
62 };
63
64 enum QueryFlags
65 {
66         FLAGS_MASK_RD           = 0x01,
67         FLAGS_MASK_TC           = 0x02,
68         FLAGS_MASK_AA           = 0x04,
69         FLAGS_MASK_OPCODE       = 0x78,
70         FLAGS_MASK_QR           = 0x80,
71         FLAGS_MASK_RCODE        = 0x0F,
72         FLAGS_MASK_Z            = 0x70,
73         FLAGS_MASK_RA           = 0x80
74 };
75
76 class dns_connection;
77 typedef std::map<int,dns_connection*> connlist;
78 typedef connlist::iterator connlist_iter;
79
80 DNS* Res = NULL;
81
82 connlist connections;
83 int master_socket = -1;
84 Resolver* dns_classes[65536];
85 insp_inaddr myserver;
86
87 class dns_rr_middle
88 {
89  public:
90         QueryType       type;
91         unsigned int    rr_class;
92         unsigned long   ttl;
93         unsigned int    rdlength;
94 };
95
96 class dns_header
97 {
98  public:
99         unsigned char   id[2];
100         unsigned int    flags1;
101         unsigned int    flags2;
102         unsigned int    qdcount;
103         unsigned int    ancount;
104         unsigned int    nscount;
105         unsigned int    arcount;
106         unsigned char   payload[512];
107 };
108
109 class dns_connection
110 {
111  public:
112         unsigned char   id[2];
113         unsigned char*  res;
114         unsigned int    rr_class;
115         QueryType       type;
116
117         dns_connection()
118         {
119                 res = new unsigned char[512];
120                 *res = 0;
121         }
122
123         ~dns_connection()
124         {
125                 delete[] res;
126         }
127
128         DNSInfo result_ready(dns_header &h, int length);
129         int     send_requests(const dns_header *header, const int length, QueryType qt);
130 };
131
132 /*
133  * Optimized by brain, these were using integer division and modulus.
134  * We can use logic shifts and logic AND to replace these even divisions
135  * and multiplications, it should be a bit faster (probably not noticably,
136  * but of course, more impressive). Also made these inline.
137  */
138
139 inline void dns_fill_rr(dns_rr_middle* rr, const unsigned char *input)
140 {
141         rr->type = (QueryType)((input[0] << 8) + input[1]);
142         rr->rr_class = (input[2] << 8) + input[3];
143         rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
144         rr->rdlength = (input[8] << 8) + input[9];
145 }
146
147 inline void dns_fill_header(dns_header *header, const unsigned char *input, const int length)
148 {
149         header->id[0] = input[0];
150         header->id[1] = input[1];
151         header->flags1 = input[2];
152         header->flags2 = input[3];
153         header->qdcount = (input[4] << 8) + input[5];
154         header->ancount = (input[6] << 8) + input[7];
155         header->nscount = (input[8] << 8) + input[9];
156         header->arcount = (input[10] << 8) + input[11];
157         memcpy(header->payload,&input[12],length);
158 }
159
160 inline void dns_empty_header(unsigned char *output, const dns_header *header, const int length)
161 {
162         output[0] = header->id[0];
163         output[1] = header->id[1];
164         output[2] = header->flags1;
165         output[3] = header->flags2;
166         output[4] = header->qdcount >> 8;
167         output[5] = header->qdcount & 0xFF;
168         output[6] = header->ancount >> 8;
169         output[7] = header->ancount & 0xFF;
170         output[8] = header->nscount >> 8;
171         output[9] = header->nscount & 0xFF;
172         output[10] = header->arcount >> 8;
173         output[11] = header->arcount & 0xFF;
174         memcpy(&output[12],header->payload,length);
175 }
176
177
178 int dns_connection::send_requests(const dns_header *header, const int length, QueryType qt)
179 {
180         insp_sockaddr addr;
181         unsigned char payload[sizeof(dns_header)];
182
183         this->rr_class = 1;
184         this->type = qt;
185                 
186         dns_empty_header(payload,header,length);
187
188         memset(&addr,0,sizeof(addr));
189 #ifdef IPV6
190         memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
191         addr.sin6_family = AF_FAMILY;
192         addr.sin6_port = htons(53);
193 #else
194         memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
195         addr.sin_family = AF_FAMILY;
196         addr.sin_port = htons(53);
197 #endif
198         if (sendto(master_socket, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
199         {
200                 log(DEBUG,"Error in sendto!");
201                 return -1;
202         }
203
204         return 0;
205 }
206
207 dns_connection* dns_add_query(dns_header *header, int &id)
208 {
209
210         id = rand() % 65536;
211         dns_connection* req = new dns_connection();
212
213         header->id[0] = req->id[0] = id >> 8;
214         header->id[1] = req->id[1] = id & 0xFF;
215         header->flags1 = FLAGS_MASK_RD;
216         header->flags2 = 0;
217         header->qdcount = 1;
218         header->ancount = 0;
219         header->nscount = 0;
220         header->arcount = 0;
221
222         if (connections.find(id) == connections.end())
223                 connections[id] = req;
224
225         /* According to the C++ spec, new never returns NULL. */
226         return req;
227 }
228
229 void create_socket()
230 {
231         log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
232         insp_inaddr addr;
233         srand((unsigned int) TIME);
234         memset(&myserver,0,sizeof(insp_inaddr));
235         if (insp_aton(Config->DNSServer,&addr) > 0)
236                 memcpy(&myserver,&addr,sizeof(insp_inaddr));
237
238         master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
239         if (master_socket != -1)
240         {
241                 log(DEBUG,"Set query socket nonblock");
242                 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
243                 {
244                         shutdown(master_socket,2);
245                         close(master_socket);
246                         master_socket = -1;
247                 }
248         }
249         if (master_socket != -1)
250         {
251 #ifdef IPV6
252                 insp_sockaddr addr;
253                 memset(&addr,0,sizeof(addr));
254                 addr.sin6_family = AF_FAMILY;
255                 addr.sin6_port = 0;
256                 memset(&addr.sin6_addr,255,sizeof(in6_addr));
257 #else
258                 insp_sockaddr addr;
259                 memset(&addr,0,sizeof(addr));
260                 addr.sin_family = AF_FAMILY;
261                 addr.sin_port = 0;
262                 addr.sin_addr.s_addr = INADDR_ANY;
263 #endif
264                 log(DEBUG,"Binding query port");
265                 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
266                 {
267                         log(DEBUG,"Cant bind with source port = 0");
268                         shutdown(master_socket,2);
269                         close(master_socket);
270                         master_socket = -1;
271                 }
272
273                 if (master_socket >= 0)
274                 {
275                         log(DEBUG,"Attach query port to socket engine");
276                         if (ServerInstance && ServerInstance->SE)
277                                 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
278                 }
279         }
280 }
281
282 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short rr_class, unsigned char * const payload)
283 {
284         short payloadpos;
285         const char * tempchr, * tempchr2;
286         unsigned short l;
287
288         payloadpos = 0;
289         tempchr2 = name;
290
291         /* split name up into labels, create query */
292         while ((tempchr = strchr(tempchr2,'.')) != NULL)
293         {
294                 l = tempchr - tempchr2;
295                 if (payloadpos + l + 1 > 507)
296                         return -1;
297                 payload[payloadpos++] = l;
298                 memcpy(&payload[payloadpos],tempchr2,l);
299                 payloadpos += l;
300                 tempchr2 = &tempchr[1];
301         }
302         l = strlen(tempchr2);
303         if (l)
304         {
305                 if (payloadpos + l + 2 > 507)
306                         return -1;
307                 payload[payloadpos++] = l;
308                 memcpy(&payload[payloadpos],tempchr2,l);
309                 payloadpos += l;
310                 payload[payloadpos++] = '\0';
311         }
312         if (payloadpos > 508)
313                 return -1;
314         l = htons(rr);
315         memcpy(&payload[payloadpos],&l,2);
316         l = htons(rr_class);
317         memcpy(&payload[payloadpos + 2],&l,2);
318         return payloadpos + 4;
319 }
320
321 int DNS::dns_getip(const char *name)
322 {
323         dns_header h;
324         int id;
325         int length;
326         dns_connection* req;
327         
328         if ((length = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
329                 return -1;
330
331         req = dns_add_query(&h, id);
332
333         if (req->send_requests(&h,length,DNS_QRY_A) == -1)
334                 return -1;
335
336         return id;
337 }
338
339 int DNS::dns_getname(const insp_inaddr *ip)
340 {
341 #ifdef IPV6
342         return -1;
343 #else
344         char query[29];
345         dns_header h;
346         int id;
347         int length;
348         dns_connection* req;
349
350         unsigned char* c = (unsigned char*)&ip->s_addr;
351
352         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
353
354         if ((length = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
355                 return -1;
356
357         req = dns_add_query(&h, id);
358
359         if (req->send_requests(&h,length,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         /* Fetch dns query response and decide where it belongs */
371         dns_header header;
372         dns_connection *req;
373         int length;
374         unsigned char buffer[sizeof(dns_header)];
375
376         /* Attempt to read a header */
377         length = recv(master_socket,buffer,sizeof(dns_header),0);
378
379         /* Did we get the whole header? */
380         if (length < 12)
381                 /* Nope - something screwed up. */
382                 return std::make_pair(-1,"");
383
384         /* Put the read header info into a header class */
385         dns_fill_header(&header,buffer,length - 12);
386
387         /* Get the id of this request.
388          * Its a 16 bit value stored in two char's,
389          * so we use logic shifts to create the value.
390          */
391         unsigned long this_id = header.id[1] + (header.id[0] << 8);
392
393         /* Do we have a pending request matching this id? */
394         connlist_iter n_iter = connections.find(this_id);
395         if (n_iter == connections.end())
396         {
397                 /* Somehow we got a DNS response for a request we never made... */
398                 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
399                 return std::make_pair(-1,"");
400         }
401         else
402         {
403                 /* Remove the query from the list of pending queries */
404                 req = (dns_connection*)n_iter->second;
405                 connections.erase(n_iter);
406         }
407
408         /* Inform the dns_connection class that it has a result to be read.
409          * When its finished it will return a DNSInfo which is a pair of
410          * unsigned char* resource record data, and an error message.
411          */
412         DNSInfo data = req->result_ready(header, length);
413         std::string resultstr;
414
415         /* Check if we got a result, if we didnt, its an error */
416         if (data.first == NULL)
417         {
418                 /* An error.
419                  * Mask the ID with the value of ERROR_MASK, so that
420                  * the dns_deal_with_classes() function knows that its
421                  * an error response and needs to be treated uniquely.
422                  * Put the error message in the second field.
423                  */
424                 delete req;
425                 return std::make_pair(this_id | ERROR_MASK, data.second);
426         }
427         else
428         {
429                 /* Forward lookups come back as binary data. We must format them into ascii */
430                 if (req->type == DNS_QRY_A)
431                 {
432                         char formatted[16];
433                         snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
434                         resultstr = formatted;
435                 }
436                 else
437                 {
438                         /* Reverse lookups just come back as char* */
439                         resultstr = std::string((const char*)data.first);
440                 }
441
442                 /* Build the reply with the id and hostname/ip in it */
443                 delete req;
444                 return std::make_pair(this_id,resultstr);
445         }
446 }
447
448 /* A result is ready, process it */
449 DNSInfo dns_connection::result_ready(dns_header &header, int length)
450 {
451         int i = 0;
452         int q = 0;
453         int curanswer, o;
454         dns_rr_middle rr;
455         unsigned short p;
456                                         
457         if (!(header.flags1 & FLAGS_MASK_QR))
458                 return std::make_pair((unsigned char*)NULL,"Not a query result");
459
460         if (header.flags1 & FLAGS_MASK_OPCODE)
461                 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
462
463         if (header.flags2 & FLAGS_MASK_RCODE)
464                 return std::make_pair((unsigned char*)NULL,"Internal server error (SERVFAIL)");
465
466         if (header.ancount < 1)
467                 return std::make_pair((unsigned char*)NULL,"No resource records returned");
468
469         /* Subtract the length of the header from the length of the packet */
470         length -= 12;
471
472         while ((unsigned int)q < header.qdcount && i < length)
473         {
474                 if (header.payload[i] > 63)
475                 {
476                         i += 6;
477                         q++;
478                 }
479                 else
480                 {
481                         if (header.payload[i] == 0)
482                         {
483                                 q++;
484                                 i += 5;
485                         }
486                         else i += header.payload[i] + 1;
487                 }
488         }
489         curanswer = 0;
490         while ((unsigned)curanswer < header.ancount)
491         {
492                 q = 0;
493                 while (q == 0 && i < length)
494                 {
495                         if (header.payload[i] > 63)
496                         {
497                                 i += 2;
498                                 q = 1;
499                         }
500                         else
501                         {
502                                 if (header.payload[i] == 0)
503                                 {
504                                         i++;
505                                         q = 1;
506                                 }
507                                 else i += header.payload[i] + 1; /* skip length and label */
508                         }
509                 }
510                 if (length - i < 10)
511                         return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
512
513                 dns_fill_rr(&rr,&header.payload[i]);
514                 i += 10;
515                 if (rr.type != this->type)
516                 {
517                         curanswer++;
518                         i += rr.rdlength;
519                         continue;
520                 }
521                 if (rr.rr_class != this->rr_class)
522                 {
523                         curanswer++;
524                         i += rr.rdlength;
525                         continue;
526                 }
527                 break;
528         }
529         if ((unsigned int)curanswer == header.ancount)
530                 return std::make_pair((unsigned char*)NULL,"No valid answers");
531
532         if (i + rr.rdlength > (unsigned int)length)
533                 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
534
535         if (rr.rdlength > 1023)
536                 return std::make_pair((unsigned char*)NULL,"Resource record too large");
537
538         switch (rr.type)
539         {
540                 case DNS_QRY_PTR:
541                         o = 0;
542                         q = 0;
543                         while (q == 0 && i < length && o + 256 < 1023)
544                         {
545                                 if (header.payload[i] > 63)
546                                 {
547                                         memcpy(&p,&header.payload[i],2);
548                                         i = ntohs(p) - 0xC000 - 12;
549                                 }
550                                 else
551                                 {
552                                         if (header.payload[i] == 0)
553                                         {
554                                                 q = 1;
555                                         }
556                                         else
557                                         {
558                                                 res[o] = '\0';
559                                                 if (o != 0)
560                                                         res[o++] = '.';
561                                                 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
562                                                 o += header.payload[i];
563                                                 i += header.payload[i] + 1;
564                                         }
565                                 }
566                         }
567                         res[o] = '\0';
568                 break;
569                 case DNS_QRY_A:
570                         memcpy(res,&header.payload[i],rr.rdlength);
571                         res[rr.rdlength] = '\0';
572                         break;
573                 default:
574                         memcpy(res,&header.payload[i],rr.rdlength);
575                         res[rr.rdlength] = '\0';
576                         break;
577         }
578         return std::make_pair(res,"No error");;
579 }
580
581 DNS::DNS()
582 {
583 }
584
585 DNS::~DNS()
586 {
587 }
588
589 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
590 {
591         if (forward)
592         {
593                 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
594                 this->myid = Res->dns_getip(source.c_str());
595         }
596         else
597         {
598                 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
599                 insp_inaddr binip;
600                 if (insp_aton(source.c_str(), &binip) > 0)
601                 {
602                         /* Valid ip address */
603                         this->myid = Res->dns_getname(&binip);
604                 }
605                 else
606                 {
607                         this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
608                         throw ModuleException("Resolver: Bad IP address");
609                         return;
610                 }
611         }
612         if (this->myid == -1)
613         {
614                 log(DEBUG,"Resolver::Resolver: Could not get an id!");
615                 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
616                 throw ModuleException("Resolver: Couldnt get an id to make a request");
617                 /* We shouldnt get here really */
618                 return;
619         }
620
621         log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
622 }
623
624 //void Resolver::OnLookupComplete(const std::string &result)
625 //{
626 //}
627
628 void Resolver::OnError(ResolverError e, const std::string &errormessage)
629 {
630 }
631
632 Resolver::~Resolver()
633 {
634         log(DEBUG,"Resolver::~Resolver");
635 }
636
637 int Resolver::GetId()
638 {
639         return this->myid;
640 }
641
642 void dns_deal_with_classes(int fd)
643 {
644         log(DEBUG,"dns_deal_with_classes(%d)",fd);
645         if (fd == master_socket)
646         {
647                 DNSResult res = Res->dns_getresult();
648                 if (res.first != -1)
649                 {
650                         if (res.first & ERROR_MASK)
651                         {
652                                 res.first -= ERROR_MASK;
653
654                                 log(DEBUG,"Error available, id=%d",res.first);
655                                 if (dns_classes[res.first])
656                                 {
657                                         dns_classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
658                                         delete dns_classes[res.first];
659                                         dns_classes[res.first] = NULL;
660                                 }
661                         }
662                         else
663                         {
664                                 log(DEBUG,"Result available, id=%d",res.first);
665                                 if (dns_classes[res.first])
666                                 {
667                                         dns_classes[res.first]->OnLookupComplete(res.second);
668                                         delete dns_classes[res.first];
669                                         dns_classes[res.first] = NULL;
670                                 }
671                         }
672                 }
673         }
674 }
675
676 bool dns_add_class(Resolver* r)
677 {
678         log(DEBUG,"dns_add_class");
679         if ((r) && (r->GetId() > -1))
680         {
681                 if (!dns_classes[r->GetId()])
682                 {
683                         log(DEBUG,"dns_add_class: added class");
684                         dns_classes[r->GetId()] = r;
685                         return true;
686                 }
687                 else
688                 {
689                         log(DEBUG,"Space occupied!");
690                         return false;
691                 }
692         }
693         else
694         {
695                 log(DEBUG,"Bad class");
696                 delete r;
697                 return true;
698         }
699 }
700
701 void init_dns()
702 {
703         Res = new DNS();
704         memset(dns_classes,0,sizeof(dns_classes));
705         create_socket();
706 }
707