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