]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
b2c32c0fb2f557be6c51341cffd9ebea2b9d0cc8
[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 loosely based on the firedns library,
20 Copyright (C) 2002 Ian Gulliver.
21
22 There have been so many modifications to this file
23 to make it fit into InspIRCd and make it object
24 orientated that you should not take this code as
25 being what firedns really looks like. It used to
26 look very different to this! :-P
27 */
28
29 using namespace std;
30
31 #include <string>
32 #include <time.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <map>
41 #include "dns.h"
42 #include "inspircd.h"
43 #include "helperfuncs.h"
44 #include "inspircd_config.h"
45 #include "socketengine.h"
46 #include "configreader.h"
47
48 extern InspIRCd* ServerInstance;
49 extern ServerConfig* Config;
50 extern time_t TIME;
51 extern userrec* fd_ref_table[MAX_DESCRIPTORS];
52
53 enum QueryType
54 {
55         DNS_QRY_A       = 1,
56         DNS_QRY_PTR     = 12
57 };
58
59 enum QueryFlags
60 {
61         FLAGS_MASK_RD           = 0x01,
62         FLAGS_MASK_TC           = 0x02,
63         FLAGS_MASK_AA           = 0x04,
64         FLAGS_MASK_OPCODE       = 0x78,
65         FLAGS_MASK_QR           = 0x80,
66         FLAGS_MASK_RCODE        = 0x0F,
67         FLAGS_MASK_Z            = 0x70,
68         FLAGS_MASK_RA           = 0x80
69 };
70
71 class dns_connection;
72 typedef std::map<int,dns_connection*> connlist;
73 typedef connlist::iterator connlist_iter;
74
75 DNS* Res = NULL;
76
77 connlist connections;
78 int master_socket = -1;
79 Resolver* dns_classes[65536];
80 insp_inaddr myserver;
81
82 class dns_rr_middle
83 {
84  public:
85         QueryType       type;
86         unsigned int    rr_class;
87         unsigned long   ttl;
88         unsigned int    rdlength;
89 };
90
91 class dns_header
92 {
93  public:
94         unsigned char   id[2];
95         unsigned int    flags1;
96         unsigned int    flags2;
97         unsigned int    qdcount;
98         unsigned int    ancount;
99         unsigned int    nscount;
100         unsigned int    arcount;
101         unsigned char   payload[512];
102 };
103
104 class dns_connection
105 {
106  public:
107         unsigned char   id[2];
108         unsigned char*  res;
109         unsigned int    rr_class;
110         QueryType       type;
111
112         dns_connection()
113         {
114                 res = new unsigned char[512];
115                 *res = 0;
116         }
117
118         ~dns_connection()
119         {
120                 delete[] res;
121         }
122
123         unsigned char*  result_ready(dns_header &h, int length);
124         int             send_requests(const dns_header *h, const int l, QueryType qt);
125 };
126
127 /*
128  * Optimized by brain, these were using integer division and modulus.
129  * We can use logic shifts and logic AND to replace these even divisions
130  * and multiplications, it should be a bit faster (probably not noticably,
131  * but of course, more impressive). Also made these inline.
132  */
133
134 inline void dns_fill_rr(dns_rr_middle* rr, const unsigned char *input)
135 {
136         rr->type = (QueryType)((input[0] << 8) + input[1]);
137         rr->rr_class = (input[2] << 8) + input[3];
138         rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
139         rr->rdlength = (input[8] << 8) + input[9];
140 }
141
142 inline void dns_fill_header(dns_header *header, const unsigned char *input, const int length)
143 {
144         header->id[0] = input[0];
145         header->id[1] = input[1];
146         header->flags1 = input[2];
147         header->flags2 = input[3];
148         header->qdcount = (input[4] << 8) + input[5];
149         header->ancount = (input[6] << 8) + input[7];
150         header->nscount = (input[8] << 8) + input[9];
151         header->arcount = (input[10] << 8) + input[11];
152         memcpy(header->payload,&input[12],length);
153 }
154
155 inline void dns_empty_header(unsigned char *output, const dns_header *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
173 int dns_connection::send_requests(const dns_header *h, const int length, QueryType qt)
174 {
175         insp_sockaddr addr;
176         unsigned char payload[sizeof(dns_header)];
177
178         this->rr_class = 1;
179         this->type = qt;
180                 
181         dns_empty_header(payload,h,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(master_socket, 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 dns_connection* dns_add_query(dns_header *h, int &id)
203 {
204
205         id = rand() % 65536;
206         dns_connection * s = new dns_connection();
207
208         h->id[0] = s->id[0] = id >> 8;
209         h->id[1] = s->id[1] = id & 0xFF;
210         h->flags1 = FLAGS_MASK_RD;
211         h->flags2 = 0;
212         h->qdcount = 1;
213         h->ancount = 0;
214         h->nscount = 0;
215         h->arcount = 0;
216
217         if (connections.find(id) == connections.end())
218                 connections[id] = s;
219         return s;
220 }
221
222 void create_socket()
223 {
224         log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
225         insp_inaddr addr;
226         srand((unsigned int) TIME);
227         memset(&myserver,0,sizeof(insp_inaddr));
228         if (insp_aton(Config->DNSServer,&addr) > 0)
229                 memcpy(&myserver,&addr,sizeof(insp_inaddr));
230
231         master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
232         if (master_socket != -1)
233         {
234                 log(DEBUG,"Set query socket nonblock");
235                 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
236                 {
237                         shutdown(master_socket,2);
238                         close(master_socket);
239                         master_socket = -1;
240                 }
241         }
242         if (master_socket != -1)
243         {
244 #ifdef IPV6
245                 insp_sockaddr addr;
246                 memset(&addr,0,sizeof(addr));
247                 addr.sin6_family = AF_FAMILY;
248                 addr.sin6_port = 0;
249                 memset(&addr.sin6_addr,255,sizeof(in6_addr));
250 #else
251                 insp_sockaddr addr;
252                 memset(&addr,0,sizeof(addr));
253                 addr.sin_family = AF_FAMILY;
254                 addr.sin_port = 0;
255                 addr.sin_addr.s_addr = INADDR_ANY;
256 #endif
257                 log(DEBUG,"Binding query port");
258                 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
259                 {
260                         log(DEBUG,"Cant bind with source port = 0");
261                         shutdown(master_socket,2);
262                         close(master_socket);
263                         master_socket = -1;
264                 }
265
266                 if (master_socket >= 0)
267                 {
268                         log(DEBUG,"Attach query port to socket engine");
269                         if (ServerInstance && ServerInstance->SE)
270                                 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
271                 }
272         }
273 }
274
275 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short rr_class, unsigned char * const payload)
276 {
277         short payloadpos;
278         const char * tempchr, * tempchr2;
279         unsigned short l;
280
281         payloadpos = 0;
282         tempchr2 = name;
283
284         /* split name up into labels, create query */
285         while ((tempchr = strchr(tempchr2,'.')) != NULL)
286         {
287                 l = tempchr - tempchr2;
288                 if (payloadpos + l + 1 > 507)
289                         return -1;
290                 payload[payloadpos++] = l;
291                 memcpy(&payload[payloadpos],tempchr2,l);
292                 payloadpos += l;
293                 tempchr2 = &tempchr[1];
294         }
295         l = strlen(tempchr2);
296         if (l)
297         {
298                 if (payloadpos + l + 2 > 507)
299                         return -1;
300                 payload[payloadpos++] = l;
301                 memcpy(&payload[payloadpos],tempchr2,l);
302                 payloadpos += l;
303                 payload[payloadpos++] = '\0';
304         }
305         if (payloadpos > 508)
306                 return -1;
307         l = htons(rr);
308         memcpy(&payload[payloadpos],&l,2);
309         l = htons(rr_class);
310         memcpy(&payload[payloadpos + 2],&l,2);
311         return payloadpos + 4;
312 }
313
314 int DNS::dns_getip4(const char *name)
315 {
316         dns_header h;
317         int id;
318         int length;
319         dns_connection* req;
320         
321         if ((length = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
322                 return -1;
323
324         if ((req = dns_add_query(&h, id)) == NULL)
325                 return -1;
326
327         if (req->send_requests(&h,length,DNS_QRY_A) == -1)
328                 return -1;
329
330         return id;
331 }
332
333 int DNS::dns_getname4(const insp_inaddr *ip)
334 {
335 #ifdef IPV6
336         return -1;
337 #else
338         char query[29];
339         dns_header h;
340         int id;
341         int length;
342         dns_connection* req;
343
344         unsigned char* c = (unsigned char*)&ip->s_addr;
345
346         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
347
348         if ((length = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
349                 return -1;
350
351         if ((req = dns_add_query(&h, id)) == NULL)
352                 return -1;
353
354         if (req->send_requests(&h,length,DNS_QRY_PTR) == -1)
355                 return -1;
356
357         return id;
358 #endif
359 }
360
361 /* Return the next id which is ready, and the result attached to it
362  */
363 DNSResult DNS::dns_getresult()
364 {
365         /* retrieve result of DNS query (buffered) */
366         dns_header h;
367         dns_connection *c;
368         int length;
369         unsigned char buffer[sizeof(dns_header)];
370
371         length = recv(master_socket,buffer,sizeof(dns_header),0);
372
373         if (length < 12)
374                 return std::make_pair(-1,"");
375
376         dns_fill_header(&h,buffer,length - 12);
377
378         // Get the id of this request
379         unsigned long this_id = h.id[1] + (h.id[0] << 8);
380
381         // Do we have a pending request for it?
382
383         connlist_iter n_iter = connections.find(this_id);
384         if (n_iter == connections.end())
385         {
386                 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
387                 return std::make_pair(-1,"");
388         }
389         else
390         {
391                 /* Remove the query from the list */
392                 c = (dns_connection*)n_iter->second;
393                 /* We don't delete c here, because its done later when needed */
394                 connections.erase(n_iter);
395         }
396         unsigned char* a = c->result_ready(h, length);
397         std::string resultstr;
398
399         if (a == NULL)
400         {
401                 resultstr = "";
402         }
403         else
404         {
405                 if (c->type == DNS_QRY_A)
406                 {
407                         char formatted[1024];
408                         snprintf(formatted,1024,"%u.%u.%u.%u",a[0],a[1],a[2],a[3]);
409                         resultstr = std::string(formatted);
410                 }
411                 else
412                 {
413                         resultstr = std::string((const char*)a);
414                 }
415         }
416
417         delete c;
418         return std::make_pair(this_id,resultstr);
419 }
420
421 /** A result is ready, process it
422  */
423 unsigned char* dns_connection::result_ready(dns_header &h, int length)
424 {
425         int i, q, curanswer, o;
426         dns_rr_middle rr;
427         unsigned short p;
428                                         
429         if ((h.flags1 & FLAGS_MASK_QR) == 0)
430         {
431                 log(DEBUG,"DNS: didnt get a query result");
432                 return NULL;
433         }
434         if ((h.flags1 & FLAGS_MASK_OPCODE) != 0)
435         {
436                 log(DEBUG,"DNS: got an OPCODE and didnt want one");
437                 return NULL;
438         }
439         if ((h.flags2 & FLAGS_MASK_RCODE) != 0)
440         {
441                 log(DEBUG,"DNS lookup failed due to SERVFAIL");
442                 return NULL;
443         }
444         if (h.ancount < 1)
445         {
446                 log(DEBUG,"DNS: no answers!");
447                 return NULL;
448         }
449         i = 0;
450         q = 0;
451         length -= 12;
452         while ((unsigned)q < h.qdcount && i < length)
453         {
454                 if (h.payload[i] > 63)
455                 {
456                         i += 6;
457                         q++;
458                 }
459                 else
460                 {
461                         if (h.payload[i] == 0)
462                         {
463                                 q++;
464                                 i += 5;
465                         }
466                         else i += h.payload[i] + 1;
467                 }
468         }
469         curanswer = 0;
470         while ((unsigned)curanswer < h.ancount)
471         {
472                 q = 0;
473                 while (q == 0 && i < length)
474                 {
475                         if (h.payload[i] > 63)
476                         {
477                                 i += 2;
478                                 q = 1;
479                         }
480                         else
481                         {
482                                 if (h.payload[i] == 0)
483                                 {
484                                         i++;
485                                         q = 1;
486                                 }
487                                 else i += h.payload[i] + 1; /* skip length and label */
488                         }
489                 }
490                 if (length - i < 10)
491                 {
492                         return NULL;
493                 }
494                 dns_fill_rr(&rr,&h.payload[i]);
495                 i += 10;
496                 if (rr.type != this->type)
497                 {
498                         curanswer++;
499                         i += rr.rdlength;
500                         continue;
501                 }
502                 if (rr.rr_class != this->rr_class)
503                 {
504                         curanswer++;
505                         i += rr.rdlength;
506                         continue;
507                 }
508                 break;
509         }
510         if ((unsigned int)curanswer == h.ancount)
511                 return NULL;
512         if (i + rr.rdlength > (unsigned int)length)
513                 return NULL;
514         if (rr.rdlength > 1023)
515                 return NULL;
516
517         switch (rr.type)
518         {
519                 case DNS_QRY_PTR:
520                         log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
521                         o = 0;
522                         q = 0;
523                         while (q == 0 && i < length && o + 256 < 1023)
524                         {
525                                 if (h.payload[i] > 63)
526                                 {
527                                         log(DEBUG,"DNS: h.payload[i] > 63");
528                                         memcpy(&p,&h.payload[i],2);
529                                         i = ntohs(p) - 0xC000 - 12;
530                                 }
531                                 else
532                                 {
533                                         if (h.payload[i] == 0)
534                                         {
535                                                 q = 1;
536                                         }
537                                         else
538                                         {
539                                                 res[o] = '\0';
540                                                 if (o != 0)
541                                                         res[o++] = '.';
542                                                 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
543                                                 o += h.payload[i];
544                                                 i += h.payload[i] + 1;
545                                         }
546                                 }
547                         }
548                         res[o] = '\0';
549                 break;
550                 case DNS_QRY_A:
551                         log(DEBUG,"DNS: got a result of type DNS_QRY_A");
552                         memcpy(res,&h.payload[i],rr.rdlength);
553                         res[rr.rdlength] = '\0';
554                         break;
555                 default:
556                         memcpy(res,&h.payload[i],rr.rdlength);
557                         res[rr.rdlength] = '\0';
558                         break;
559         }
560         return res;
561 }
562
563 DNS::DNS()
564 {
565         log(DEBUG,"Create blank DNS");
566 }
567
568 DNS::~DNS()
569 {
570 }
571
572 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
573 {
574         if (forward)
575         {
576                 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
577                 this->myid = Res->dns_getip4(source.c_str());
578         }
579         else
580         {
581                 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
582                 insp_inaddr binip;
583                 if (insp_aton(source.c_str(), &binip) > 0)
584                 {
585                         /* Valid ip address */
586                         this->myid = Res->dns_getname4(&binip);
587                 }
588         }
589         if (this->myid == -1)
590         {
591                 log(DEBUG,"Resolver::Resolver: Could not get an id!");
592                 this->OnError(RESOLVER_NSDOWN);
593                 throw ModuleException("Resolver: Couldnt get an id to make a request");
594                 /* We shouldnt get here really */
595                 return;
596         }
597
598         log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
599 }
600
601 void Resolver::OnLookupComplete(const std::string &result)
602 {
603 }
604
605 void Resolver::OnError(ResolverError e)
606 {
607 }
608
609 Resolver::~Resolver()
610 {
611         log(DEBUG,"Resolver::~Resolver");
612 }
613
614 int Resolver::GetId()
615 {
616         return this->myid;
617 }
618
619 bool Resolver::ProcessResult(const std::string &result)
620 {
621         log(DEBUG,"Resolver::ProcessResult");
622
623         if (!result.length())
624         {
625                 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
626                 this->OnError(RESOLVER_NXDOMAIN);
627                 return false;
628         }
629         else
630         {
631
632                 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
633                 this->OnLookupComplete(result);
634                 return true;
635         }
636 }
637
638 void dns_deal_with_classes(int fd)
639 {
640         log(DEBUG,"dns_deal_with_classes(%d)",fd);
641         if (fd == master_socket)
642         {
643                 DNSResult res = Res->dns_getresult();
644                 if (res.first != -1)
645                 {
646                         log(DEBUG,"Result available, id=%d",res.first);
647                         if (dns_classes[res.first])
648                         {
649                                 dns_classes[res.first]->ProcessResult(res.second);
650                                 delete dns_classes[res.first];
651                                 dns_classes[res.first] = NULL;
652                         }
653                 }
654         }
655 }
656
657 bool dns_add_class(Resolver* r)
658 {
659         log(DEBUG,"dns_add_class");
660         if ((r) && (r->GetId() > -1))
661         {
662                 if (!dns_classes[r->GetId()])
663                 {
664                         log(DEBUG,"dns_add_class: added class");
665                         dns_classes[r->GetId()] = r;
666                         return true;
667                 }
668                 else
669                 {
670                         log(DEBUG,"Space occupied!");
671                         return false;
672                 }
673         }
674         else
675         {
676                 log(DEBUG,"Bad class");
677                 delete r;
678                 return true;
679         }
680 }
681
682 void init_dns()
683 {
684         Res = new DNS();
685         memset(dns_classes,0,sizeof(dns_classes));
686         create_socket();
687 }
688