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