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