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