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