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