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