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