]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
1adf5f8a2496f4dcb1f3d5f3ab42ae2d275bb21c
[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 *header, const int length, 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 *header, 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,header,length);
182
183         memset(&addr,0,sizeof(addr));
184 #ifdef IPV6
185         memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
186         addr.sin6_family = AF_FAMILY;
187         addr.sin6_port = htons(53);
188 #else
189         memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
190         addr.sin_family = AF_FAMILY;
191         addr.sin_port = htons(53);
192 #endif
193         if (sendto(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 *header, int &id)
203 {
204
205         id = rand() % 65536;
206         dns_connection* req = new dns_connection();
207
208         header->id[0] = req->id[0] = id >> 8;
209         header->id[1] = req->id[1] = id & 0xFF;
210         header->flags1 = FLAGS_MASK_RD;
211         header->flags2 = 0;
212         header->qdcount = 1;
213         header->ancount = 0;
214         header->nscount = 0;
215         header->arcount = 0;
216
217         if (connections.find(id) == connections.end())
218                 connections[id] = req;
219
220         /* According to the C++ spec, new never returns NULL. */
221         return req;
222 }
223
224 void create_socket()
225 {
226         log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
227         insp_inaddr addr;
228         srand((unsigned int) TIME);
229         memset(&myserver,0,sizeof(insp_inaddr));
230         if (insp_aton(Config->DNSServer,&addr) > 0)
231                 memcpy(&myserver,&addr,sizeof(insp_inaddr));
232
233         master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
234         if (master_socket != -1)
235         {
236                 log(DEBUG,"Set query socket nonblock");
237                 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
238                 {
239                         shutdown(master_socket,2);
240                         close(master_socket);
241                         master_socket = -1;
242                 }
243         }
244         if (master_socket != -1)
245         {
246 #ifdef IPV6
247                 insp_sockaddr addr;
248                 memset(&addr,0,sizeof(addr));
249                 addr.sin6_family = AF_FAMILY;
250                 addr.sin6_port = 0;
251                 memset(&addr.sin6_addr,255,sizeof(in6_addr));
252 #else
253                 insp_sockaddr addr;
254                 memset(&addr,0,sizeof(addr));
255                 addr.sin_family = AF_FAMILY;
256                 addr.sin_port = 0;
257                 addr.sin_addr.s_addr = INADDR_ANY;
258 #endif
259                 log(DEBUG,"Binding query port");
260                 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
261                 {
262                         log(DEBUG,"Cant bind with source port = 0");
263                         shutdown(master_socket,2);
264                         close(master_socket);
265                         master_socket = -1;
266                 }
267
268                 if (master_socket >= 0)
269                 {
270                         log(DEBUG,"Attach query port to socket engine");
271                         if (ServerInstance && ServerInstance->SE)
272                                 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
273                 }
274         }
275 }
276
277 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short rr_class, unsigned char * const payload)
278 {
279         short payloadpos;
280         const char * tempchr, * tempchr2;
281         unsigned short l;
282
283         payloadpos = 0;
284         tempchr2 = name;
285
286         /* split name up into labels, create query */
287         while ((tempchr = strchr(tempchr2,'.')) != NULL)
288         {
289                 l = tempchr - tempchr2;
290                 if (payloadpos + l + 1 > 507)
291                         return -1;
292                 payload[payloadpos++] = l;
293                 memcpy(&payload[payloadpos],tempchr2,l);
294                 payloadpos += l;
295                 tempchr2 = &tempchr[1];
296         }
297         l = strlen(tempchr2);
298         if (l)
299         {
300                 if (payloadpos + l + 2 > 507)
301                         return -1;
302                 payload[payloadpos++] = l;
303                 memcpy(&payload[payloadpos],tempchr2,l);
304                 payloadpos += l;
305                 payload[payloadpos++] = '\0';
306         }
307         if (payloadpos > 508)
308                 return -1;
309         l = htons(rr);
310         memcpy(&payload[payloadpos],&l,2);
311         l = htons(rr_class);
312         memcpy(&payload[payloadpos + 2],&l,2);
313         return payloadpos + 4;
314 }
315
316 int DNS::dns_getip4(const char *name)
317 {
318         dns_header h;
319         int id;
320         int length;
321         dns_connection* req;
322         
323         if ((length = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
324                 return -1;
325
326         req = dns_add_query(&h, id);
327
328         if (req->send_requests(&h,length,DNS_QRY_A) == -1)
329                 return -1;
330
331         return id;
332 }
333
334 int DNS::dns_getname4(const insp_inaddr *ip)
335 {
336 #ifdef IPV6
337         return -1;
338 #else
339         char query[29];
340         dns_header h;
341         int id;
342         int length;
343         dns_connection* req;
344
345         unsigned char* c = (unsigned char*)&ip->s_addr;
346
347         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
348
349         if ((length = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
350                 return -1;
351
352         req = dns_add_query(&h, id);
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 header;
367         dns_connection *req;
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(&header,buffer,length - 12);
377
378         // Get the id of this request
379         unsigned long this_id = header.id[1] + (header.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                 req = (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* data = req->result_ready(header, length);
397         std::string resultstr;
398
399         if (data == NULL)
400         {
401                 resultstr = "";
402         }
403         else
404         {
405                 if (req->type == DNS_QRY_A)
406                 {
407                         char formatted[16];
408                         snprintf(formatted,16,"%u.%u.%u.%u",data[0],data[1],data[2],data[3]);
409                         resultstr = formatted;
410                 }
411                 else
412                 {
413                         resultstr = std::string((const char*)data);
414                 }
415         }
416
417         delete req;
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 &header, int length)
424 {
425         int i = 0;
426         int q = 0;
427         int curanswer, o;
428         dns_rr_middle rr;
429         unsigned short p;
430                                         
431         if (!(header.flags1 & FLAGS_MASK_QR))
432         {
433                 log(DEBUG,"DNS: didnt get a query result");
434                 return NULL;
435         }
436         if (header.flags1 & FLAGS_MASK_OPCODE)
437         {
438                 log(DEBUG,"DNS: got an OPCODE and didnt want one");
439                 return NULL;
440         }
441         if (header.flags2 & FLAGS_MASK_RCODE)
442         {
443                 log(DEBUG,"DNS lookup failed due to SERVFAIL");
444                 return NULL;
445         }
446         if (header.ancount < 1)
447         {
448                 log(DEBUG,"DNS: no answers!");
449                 return NULL;
450         }
451         length -= 12;
452         while ((unsigned int)q < header.qdcount && i < length)
453         {
454                 if (header.payload[i] > 63)
455                 {
456                         i += 6;
457                         q++;
458                 }
459                 else
460                 {
461                         if (header.payload[i] == 0)
462                         {
463                                 q++;
464                                 i += 5;
465                         }
466                         else i += header.payload[i] + 1;
467                 }
468         }
469         curanswer = 0;
470         while ((unsigned)curanswer < header.ancount)
471         {
472                 q = 0;
473                 while (q == 0 && i < length)
474                 {
475                         if (header.payload[i] > 63)
476                         {
477                                 i += 2;
478                                 q = 1;
479                         }
480                         else
481                         {
482                                 if (header.payload[i] == 0)
483                                 {
484                                         i++;
485                                         q = 1;
486                                 }
487                                 else i += header.payload[i] + 1; /* skip length and label */
488                         }
489                 }
490                 if (length - i < 10)
491                 {
492                         return NULL;
493                 }
494                 dns_fill_rr(&rr,&header.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 == header.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 (header.payload[i] > 63)
526                                 {
527                                         log(DEBUG,"DNS: h.payload[i] > 63");
528                                         memcpy(&p,&header.payload[i],2);
529                                         i = ntohs(p) - 0xC000 - 12;
530                                 }
531                                 else
532                                 {
533                                         if (header.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],&header.payload[i + 1],header.payload[i]);
543                                                 o += header.payload[i];
544                                                 i += header.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,&header.payload[i],rr.rdlength);
553                         res[rr.rdlength] = '\0';
554                         break;
555                 default:
556                         memcpy(res,&header.payload[i],rr.rdlength);
557                         res[rr.rdlength] = '\0';
558                         break;
559         }
560         return res;
561 }
562
563 DNS::DNS()
564 {
565 }
566
567 DNS::~DNS()
568 {
569 }
570
571 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
572 {
573         if (forward)
574         {
575                 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
576                 this->myid = Res->dns_getip4(source.c_str());
577         }
578         else
579         {
580                 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
581                 insp_inaddr binip;
582                 if (insp_aton(source.c_str(), &binip) > 0)
583                 {
584                         /* Valid ip address */
585                         this->myid = Res->dns_getname4(&binip);
586                 }
587         }
588         if (this->myid == -1)
589         {
590                 log(DEBUG,"Resolver::Resolver: Could not get an id!");
591                 this->OnError(RESOLVER_NSDOWN);
592                 throw ModuleException("Resolver: Couldnt get an id to make a request");
593                 /* We shouldnt get here really */
594                 return;
595         }
596
597         log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
598 }
599
600 void Resolver::OnLookupComplete(const std::string &result)
601 {
602 }
603
604 void Resolver::OnError(ResolverError e)
605 {
606 }
607
608 Resolver::~Resolver()
609 {
610         log(DEBUG,"Resolver::~Resolver");
611 }
612
613 int Resolver::GetId()
614 {
615         return this->myid;
616 }
617
618 bool Resolver::ProcessResult(const std::string &result)
619 {
620         log(DEBUG,"Resolver::ProcessResult");
621
622         if (!result.length())
623         {
624                 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
625                 this->OnError(RESOLVER_NXDOMAIN);
626                 return false;
627         }
628         else
629         {
630
631                 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
632                 this->OnLookupComplete(result);
633                 return true;
634         }
635 }
636
637 void dns_deal_with_classes(int fd)
638 {
639         log(DEBUG,"dns_deal_with_classes(%d)",fd);
640         if (fd == master_socket)
641         {
642                 DNSResult res = Res->dns_getresult();
643                 if (res.first != -1)
644                 {
645                         log(DEBUG,"Result available, id=%d",res.first);
646                         if (dns_classes[res.first])
647                         {
648                                 dns_classes[res.first]->ProcessResult(res.second);
649                                 delete dns_classes[res.first];
650                                 dns_classes[res.first] = NULL;
651                         }
652                 }
653         }
654 }
655
656 bool dns_add_class(Resolver* r)
657 {
658         log(DEBUG,"dns_add_class");
659         if ((r) && (r->GetId() > -1))
660         {
661                 if (!dns_classes[r->GetId()])
662                 {
663                         log(DEBUG,"dns_add_class: added class");
664                         dns_classes[r->GetId()] = r;
665                         return true;
666                 }
667                 else
668                 {
669                         log(DEBUG,"Space occupied!");
670                         return false;
671                 }
672         }
673         else
674         {
675                 log(DEBUG,"Bad class");
676                 delete r;
677                 return true;
678         }
679 }
680
681 void init_dns()
682 {
683         Res = new DNS();
684         memset(dns_classes,0,sizeof(dns_classes));
685         create_socket();
686 }
687