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