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