]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
Craquity craq De-craq!
[user/henk/code/inspircd.git] / src / dns.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /*
15 dns.cpp - Nonblocking DNS functions.
16 Very very loosely based on the firedns library,
17 Copyright (C) 2002 Ian Gulliver. This file is no
18 longer anything like firedns, there are many major
19 differences between this code and the original.
20 Please do not assume that firedns works like this,
21 looks like this, walks like this or tastes like this.
22 */
23
24 #ifndef WIN32
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <errno.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #else
31 #include "inspircd_win32wrapper.h"
32 #include "inspircd_se_config.h"
33 #endif
34
35 #include "dns.h"
36 #include "inspircd.h"
37 #include "socketengine.h"
38 #include "configreader.h"
39 #include "socket.h"
40
41 using irc::sockets::insp_inaddr;
42 using irc::sockets::insp_ntoa;
43 using irc::sockets::insp_aton;
44 using irc::sockets::OpenTCPSocket;
45
46 /** Masks to mask off the responses we get from the DNSRequest methods
47  */
48 enum QueryInfo
49 {
50         ERROR_MASK      = 0x10000       /* Result is an error */
51 };
52
53 /** Flags which can be ORed into a request or reply for different meanings
54  */
55 enum QueryFlags
56 {
57         FLAGS_MASK_RD           = 0x01, /* Recursive */
58         FLAGS_MASK_TC           = 0x02,
59         FLAGS_MASK_AA           = 0x04, /* Authoritative */
60         FLAGS_MASK_OPCODE       = 0x78,
61         FLAGS_MASK_QR           = 0x80,
62         FLAGS_MASK_RCODE        = 0x0F, /* Request */
63         FLAGS_MASK_Z            = 0x70,
64         FLAGS_MASK_RA           = 0x80
65 };
66
67
68 /** Represents a dns resource record (rr)
69  */
70 struct ResourceRecord
71 {
72         QueryType       type;           /* Record type */
73         unsigned int    rr_class;       /* Record class */
74         unsigned long   ttl;            /* Time to live */
75         unsigned int    rdlength;       /* Record length */
76 };
77
78 /** Represents a dns request/reply header, and its payload as opaque data.
79  */
80 class DNSHeader
81 {
82  public:
83         unsigned char   id[2];          /* Request id */
84         unsigned int    flags1;         /* Flags */
85         unsigned int    flags2;         /* Flags */
86         unsigned int    qdcount;
87         unsigned int    ancount;        /* Answer count */
88         unsigned int    nscount;        /* Nameserver count */
89         unsigned int    arcount;
90         unsigned char   payload[512];   /* Packet payload */
91 };
92
93 class DNSRequest
94 {
95  public:
96         unsigned char   id[2];          /* Request id */
97         unsigned char*  res;            /* Result processing buffer */
98         unsigned int    rr_class;       /* Request class */
99         QueryType       type;           /* Request type */
100         DNS*            dnsobj;         /* DNS caller (where we get our FD from) */
101         unsigned long   ttl;            /* Time to live */
102         std::string     orig;           /* Original requested name/ip */
103         InspIRCd*       ServerInstance;
104
105         DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
106         ~DNSRequest();
107         DNSInfo ResultIsReady(DNSHeader &h, int length, int result_we_want);
108         int SendRequests(const DNSHeader *header, const int length, QueryType qt);
109 };
110
111 class CacheTimer : public InspTimer
112 {
113  private:
114         InspIRCd* ServerInstance;
115         DNS* dns;
116  public:
117         CacheTimer(InspIRCd* Instance, DNS* thisdns)
118                 : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
119
120         virtual void Tick(time_t TIME)
121         {
122                 dns->PruneCache();
123         }
124 };
125
126 class RequestTimeout : public InspTimer
127 {
128         InspIRCd* ServerInstance;
129         DNSRequest* watch;
130         int watchid;
131  public:
132         RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
133         {
134         }
135
136         void Tick(time_t TIME)
137         {
138                 if (ServerInstance->Res->requests[watchid] == watch)
139                 {
140                         /* Still exists, whack it */
141                         if (ServerInstance->Res->Classes[watchid])
142                         {
143                                 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
144                                 delete ServerInstance->Res->Classes[watchid];
145                                 ServerInstance->Res->Classes[watchid] = NULL;
146                         }
147                         ServerInstance->Res->requests[watchid] = NULL;
148                         DELETE(watch);
149                         return;
150                 }
151         }
152 };
153
154 /* Allocate the processing buffer */
155 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns), ServerInstance(Instance)
156 {
157         res = new unsigned char[512];
158         *res = 0;
159         orig = original;
160         RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
161         Instance->Timers->AddTimer(RT); /* The timer manager frees this */
162 }
163
164 /* Deallocate the processing buffer */
165 DNSRequest::~DNSRequest()
166 {
167         delete[] res;
168 }
169
170 /** Fill a ResourceRecord class based on raw data input */
171 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
172 {
173         rr->type = (QueryType)((input[0] << 8) + input[1]);
174         rr->rr_class = (input[2] << 8) + input[3];
175         rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
176         rr->rdlength = (input[8] << 8) + input[9];
177 }
178
179 /** Fill a DNSHeader class based on raw data input of a given length */
180 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
181 {
182         header->id[0] = input[0];
183         header->id[1] = input[1];
184         header->flags1 = input[2];
185         header->flags2 = input[3];
186         header->qdcount = (input[4] << 8) + input[5];
187         header->ancount = (input[6] << 8) + input[7];
188         header->nscount = (input[8] << 8) + input[9];
189         header->arcount = (input[10] << 8) + input[11];
190         memcpy(header->payload,&input[12],length);
191 }
192
193 /** Empty a DNSHeader class out into raw data, ready for transmission */
194 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
195 {
196         output[0] = header->id[0];
197         output[1] = header->id[1];
198         output[2] = header->flags1;
199         output[3] = header->flags2;
200         output[4] = header->qdcount >> 8;
201         output[5] = header->qdcount & 0xFF;
202         output[6] = header->ancount >> 8;
203         output[7] = header->ancount & 0xFF;
204         output[8] = header->nscount >> 8;
205         output[9] = header->nscount & 0xFF;
206         output[10] = header->arcount >> 8;
207         output[11] = header->arcount & 0xFF;
208         memcpy(&output[12],header->payload,length);
209 }
210
211 /** Send requests we have previously built down the UDP socket */
212 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
213 {
214         unsigned char payload[sizeof(DNSHeader)];
215
216         this->rr_class = 1;
217         this->type = qt;
218                 
219         DNS::EmptyHeader(payload,header,length);
220
221 #ifdef IPV6
222         if (this->dnsobj->socketfamily == AF_INET6)
223         {
224                 sockaddr_in6 addr;
225                 memset(&addr,0,sizeof(addr));
226                 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
227                 addr.sin6_family = AF_INET6;
228                 addr.sin6_port = htons(DNS::QUERY_PORT);
229                 if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
230                         return -1;
231         }
232         else
233 #endif
234         {
235                 sockaddr_in addr;
236                 memset(&addr,0,sizeof(addr));
237                 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
238                 addr.sin_family = AF_INET;
239                 addr.sin_port = htons(DNS::QUERY_PORT);
240                 if (ServerInstance->SE->SendTo(dnsobj, (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
241                         return -1;
242         }
243         return 0;
244 }
245
246 /** Add a query with a predefined header, and allocate an ID for it. */
247 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
248 {
249         /* Is the DNS connection down? */
250         if (this->GetFd() == -1)
251                 return NULL;
252         
253         /* Create an id */
254         id = this->PRNG() & DNS::MAX_REQUEST_ID;
255
256         /* If this id is already 'in flight', pick another. */
257         while (requests[id])
258                 id = this->PRNG() & DNS::MAX_REQUEST_ID;
259
260         DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
261
262         header->id[0] = req->id[0] = id >> 8;
263         header->id[1] = req->id[1] = id & 0xFF;
264         header->flags1 = FLAGS_MASK_RD;
265         header->flags2 = 0;
266         header->qdcount = 1;
267         header->ancount = 0;
268         header->nscount = 0;
269         header->arcount = 0;
270
271         /* At this point we already know the id doesnt exist,
272          * so there needs to be no second check for the ::end()
273          */
274         requests[id] = req;
275
276         /* According to the C++ spec, new never returns NULL. */
277         return req;
278 }
279
280 int DNS::ClearCache()
281 {
282         /* This ensures the buckets are reset to sane levels */
283         int rv = this->cache->size();
284         delete this->cache;
285         this->cache = new dnscache();
286         return rv;
287 }
288
289 int DNS::PruneCache()
290 {
291         int n = 0;
292         dnscache* newcache = new dnscache();
293         for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
294                 /* Dont include expired items (theres no point) */
295                 if (i->second.CalcTTLRemaining())
296                         newcache->insert(*i);
297                 else
298                         n++;
299
300         delete this->cache;
301         this->cache = newcache;
302         return n;
303 }
304
305 void DNS::Rehash()
306 {
307         ip6munge = false;
308         int portpass = 0;
309
310         if (this->GetFd() > -1)
311         {
312                 if (ServerInstance && ServerInstance->SE)
313                         ServerInstance->SE->DelFd(this);
314                 ServerInstance->SE->Shutdown(this, 2);
315                 ServerInstance->SE->Close(this);
316                 this->SetFd(-1);
317
318                 /* Rehash the cache */
319                 this->PruneCache();
320         }
321         else
322         {
323                 /* Create initial dns cache */
324                 this->cache = new dnscache();
325         }
326
327         if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) ||  (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
328         {
329                 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
330                 ServerInstance->Log(DEFAULT,"         This should not cause a problem, however it is recommended you migrate");
331                 ServerInstance->Log(DEFAULT,"         to a true IPv6 environment.");
332                 this->ip6munge = true;
333         }
334
335         this->socketfamily = AF_INET;
336 #ifdef IPV6
337         if (strchr(ServerInstance->Config->DNSServer,':'))
338         {
339                 this->socketfamily = AF_INET6;
340                 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
341         }
342         else
343         {
344                 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
345                 portpass = -1;
346         }
347 #else
348         inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
349 #endif
350
351         /* Initialize mastersocket */
352         int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
353         this->SetFd(s);
354         ServerInstance->SE->NonBlocking(this->GetFd());
355
356         /* Have we got a socket and is it nonblocking? */
357         if (this->GetFd() != -1)
358         {
359                 /* Bind the port - port 0 INADDR_ANY */
360                 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
361                 {
362                         /* Failed to bind */
363                         ServerInstance->SE->Shutdown(this, 2);
364                         ServerInstance->SE->Close(this);
365                         this->SetFd(-1);
366                 }
367
368                 if (this->GetFd() >= 0)
369                 {
370                         /* Hook the descriptor into the socket engine */
371                         if (ServerInstance && ServerInstance->SE)
372                         {
373                                 if (!ServerInstance->SE->AddFd(this))
374                                 {
375                                         ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
376                                         ServerInstance->SE->Shutdown(this, 2);
377                                         ServerInstance->SE->Close(this);
378                                         this->SetFd(-1);
379                                 }
380                         }
381                 }
382         }
383 }
384
385 /** Initialise the DNS UDP socket so that we can send requests */
386 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
387 {
388         /* Clear the Resolver class table */
389         memset(Classes,0,sizeof(Classes));
390
391         /* Clear the requests class table */
392         memset(requests,0,sizeof(requests));
393
394         /* Set the id of the next request to 0
395          */
396         currid = 0;
397
398         /* DNS::Rehash() sets this to a valid ptr
399          */
400         this->cache = NULL;
401         
402         /* Again, DNS::Rehash() sets this to a
403          * valid value
404          */
405         this->SetFd(-1);
406
407         /* Actually read the settings
408          */
409         this->Rehash();
410
411         this->PruneTimer = new CacheTimer(ServerInstance, this);
412
413         ServerInstance->Timers->AddTimer(this->PruneTimer);
414 }
415
416 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
417 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
418 {
419         short payloadpos = 0;
420         const char* tempchr, *tempchr2 = name;
421         unsigned short length;
422
423         /* split name up into labels, create query */
424         while ((tempchr = strchr(tempchr2,'.')) != NULL)
425         {
426                 length = tempchr - tempchr2;
427                 if (payloadpos + length + 1 > 507)
428                         return -1;
429                 payload[payloadpos++] = length;
430                 memcpy(&payload[payloadpos],tempchr2,length);
431                 payloadpos += length;
432                 tempchr2 = &tempchr[1];
433         }
434         length = strlen(tempchr2);
435         if (length)
436         {
437                 if (payloadpos + length + 2 > 507)
438                         return -1;
439                 payload[payloadpos++] = length;
440                 memcpy(&payload[payloadpos],tempchr2,length);
441                 payloadpos += length;
442                 payload[payloadpos++] = 0;
443         }
444         if (payloadpos > 508)
445                 return -1;
446         length = htons(rr);
447         memcpy(&payload[payloadpos],&length,2);
448         length = htons(rr_class);
449         memcpy(&payload[payloadpos + 2],&length,2);
450         return payloadpos + 4;
451 }
452
453 /** Start lookup of an hostname to an IP address */
454 int DNS::GetIP(const char *name)
455 {
456         DNSHeader h;
457         int id;
458         int length;
459         
460         if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
461                 return -1;
462
463         DNSRequest* req = this->AddQuery(&h, id, name);
464
465         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
466                 return -1;
467
468         return id;
469 }
470
471 /** Start lookup of an hostname to an IPv6 address */
472 int DNS::GetIP6(const char *name)
473 {
474         DNSHeader h;
475         int id;
476         int length;
477
478         if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
479                 return -1;
480
481         DNSRequest* req = this->AddQuery(&h, id, name);
482
483         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
484                 return -1;
485
486         return id;
487 }
488
489 /** Start lookup of a cname to another name */
490 int DNS::GetCName(const char *alias)
491 {
492         DNSHeader h;
493         int id;
494         int length;
495
496         if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
497                 return -1;
498
499         DNSRequest* req = this->AddQuery(&h, id, alias);
500
501         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
502                 return -1;
503
504         return id;
505 }
506
507 /** Start lookup of an IP address to a hostname */
508 int DNS::GetName(const insp_inaddr *ip)
509 {
510         char query[128];
511         DNSHeader h;
512         int id;
513         int length;
514
515 #ifdef IPV6
516         unsigned char* c = (unsigned char*)&ip->s6_addr;
517         if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
518             c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
519             c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
520                 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
521         else
522                 DNS::MakeIP6Int(query, (in6_addr*)ip);
523 #else
524         unsigned char* c = (unsigned char*)&ip->s_addr;
525         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
526 #endif
527
528         if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
529                 return -1;
530
531         DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
532
533         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
534                 return -1;
535
536         return id;
537 }
538
539 /** Start lookup of an IP address to a hostname */
540 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
541 {
542         char query[128];
543         DNSHeader h;
544         int id;
545         int length;
546 #ifdef SUPPORT_IP6LINKS
547         if (fp == PROTOCOL_IPV6)
548         {
549                 in6_addr i;
550                 if (inet_pton(AF_INET6, ip, &i) > 0)
551                 {
552                         DNS::MakeIP6Int(query, &i);
553                 }
554                 else
555                         /* Invalid IP address */
556                         return -1;
557         }
558         else
559 #endif
560         {
561                 in_addr i;
562                 if (inet_aton(ip, &i))
563                 {
564                         unsigned char* c = (unsigned char*)&i.s_addr;
565                         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
566                 }
567                 else
568                         /* Invalid IP address */
569                         return -1;
570         }
571
572         if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
573                 return -1;
574
575         DNSRequest* req = this->AddQuery(&h, id, ip);
576
577         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
578                 return -1;
579
580         return id;
581 }
582
583 /** Build an ipv6 reverse domain from an in6_addr
584  */
585 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
586 {
587 #ifdef SUPPORT_IP6LINKS
588         const char* hex = "0123456789abcdef";
589         for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
590         {
591                 if (index % 2)
592                         /* low nibble */
593                         *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
594                 else
595                         /* high nibble */
596                         *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
597                 *query++ = '.'; /* Seperator */
598         }
599         strcpy(query,"ip6.arpa"); /* Suffix the string */
600 #else
601         *query = 0;
602 #endif
603 }
604
605 /** Return the next id which is ready, and the result attached to it */
606 DNSResult DNS::GetResult(int resultnum)
607 {
608         /* Fetch dns query response and decide where it belongs */
609         DNSHeader header;
610         DNSRequest *req;
611         unsigned char buffer[sizeof(DNSHeader)];
612         sockaddr* from = new sockaddr[2];
613 #ifdef IPV6
614         socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
615 #else
616         socklen_t x = sizeof(sockaddr_in);
617 #endif
618         const char* ipaddr_from;
619         unsigned short int port_from = 0;
620
621         int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, from, &x);
622
623         /* Did we get the whole header? */
624         if (length < 12)
625         {
626                 /* Nope - something screwed up. */
627                 delete[] from;
628                 return DNSResult(-1,"",0,"");
629         }
630
631         /* Check wether the reply came from a different DNS
632          * server to the one we sent it to, or the source-port
633          * is not 53.
634          * A user could in theory still spoof dns packets anyway
635          * but this is less trivial than just sending garbage
636          * to the client, which is possible without this check.
637          *
638          * -- Thanks jilles for pointing this one out.
639          */
640 #ifdef IPV6
641         char nbuf[MAXBUF];
642         if (this->socketfamily == AF_INET6)
643         {
644                 ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
645                 port_from = ntohs(((sockaddr_in6*)from)->sin6_port);
646         }
647         else
648 #endif
649         {
650                 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
651                 port_from = ntohs(((sockaddr_in*)from)->sin_port);
652         }
653
654         delete[] from;
655
656         /* We cant perform this security check if you're using 4in6.
657          * Tough luck to you, choose one or't other!
658          */
659         if (!ip6munge)
660         {
661                 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
662                 {
663                         return DNSResult(-1,"",0,"");
664                 }
665         }
666
667         /* Put the read header info into a header class */
668         DNS::FillHeader(&header,buffer,length - 12);
669
670         /* Get the id of this request.
671          * Its a 16 bit value stored in two char's,
672          * so we use logic shifts to create the value.
673          */
674         unsigned long this_id = header.id[1] + (header.id[0] << 8);
675
676         /* Do we have a pending request matching this id? */
677         if (!requests[this_id])
678         {
679                 /* Somehow we got a DNS response for a request we never made... */
680                 return DNSResult(-1,"",0,"");
681         }
682         else
683         {
684                 /* Remove the query from the list of pending queries */
685                 req = requests[this_id];
686                 requests[this_id] = NULL;
687         }
688
689         /* Inform the DNSRequest class that it has a result to be read.
690          * When its finished it will return a DNSInfo which is a pair of
691          * unsigned char* resource record data, and an error message.
692          */
693         DNSInfo data = req->ResultIsReady(header, length, resultnum);
694         std::string resultstr;
695
696         /* Check if we got a result, if we didnt, its an error */
697         if (data.first == NULL)
698         {
699                 /* An error.
700                  * Mask the ID with the value of ERROR_MASK, so that
701                  * the dns_deal_with_classes() function knows that its
702                  * an error response and needs to be treated uniquely.
703                  * Put the error message in the second field.
704                  */
705                 std::string ro = req->orig;
706                 delete req;
707                 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
708         }
709         else
710         {
711                 unsigned long ttl = req->ttl;
712                 char formatted[128];
713
714                 /* Forward lookups come back as binary data. We must format them into ascii */
715                 switch (req->type)
716                 {
717                         case DNS_QUERY_A:
718                                 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
719                                 resultstr = formatted;
720                         break;
721
722                         case DNS_QUERY_AAAA:
723                         {
724                                 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
725                                                 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
726                                                 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
727                                                 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
728                                                 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
729                                                 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
730                                                 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
731                                                 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
732                                                 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
733                                 char* c = strstr(formatted,":0:");
734                                 if (c != NULL)
735                                 {
736                                         memmove(c+1,c+2,strlen(c+2) + 1);
737                                         c += 2;
738                                         while (memcmp(c,"0:",2) == 0)
739                                                 memmove(c,c+2,strlen(c+2) + 1);
740                                         if (memcmp(c,"0",2) == 0)
741                                                 *c = 0;
742                                         if (memcmp(formatted,"0::",3) == 0)
743                                                 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
744                                 }
745                                 resultstr = formatted;
746
747                                 /* Special case. Sending ::1 around between servers
748                                  * and to clients is dangerous, because the : on the
749                                  * start makes the client or server interpret the IP
750                                  * as the last parameter on the line with a value ":1".
751                                  */
752                                 if (*formatted == ':')
753                                         resultstr.insert(0, "0");
754                         }
755                         break;
756
757                         case DNS_QUERY_CNAME:
758                                 /* Identical handling to PTR */
759
760                         case DNS_QUERY_PTR:
761                                 /* Reverse lookups just come back as char* */
762                                 resultstr = std::string((const char*)data.first);
763                         break;
764
765                         default:
766                         break;
767                         
768                 }
769
770                 /* Build the reply with the id and hostname/ip in it */
771                 std::string ro = req->orig;
772                 delete req;
773                 return DNSResult(this_id,resultstr,ttl,ro);
774         }
775 }
776
777 /** A result is ready, process it */
778 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length, int result_we_want)
779 {
780         int i = 0;
781         int q = 0;
782         int curanswer, o;
783         ResourceRecord rr;
784         unsigned short ptr;
785
786         /* This is just to keep _FORTIFY_SOURCE happy */
787         rr.type = DNS_QUERY_NONE;
788         rr.rdlength = 0;
789         rr.ttl = 1;     /* GCC is a whiney bastard -- see the XXX below. */
790
791         if (!(header.flags1 & FLAGS_MASK_QR))
792                 return std::make_pair((unsigned char*)NULL,"Not a query result");
793
794         if (header.flags1 & FLAGS_MASK_OPCODE)
795                 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
796
797         if (header.flags2 & FLAGS_MASK_RCODE)
798                 return std::make_pair((unsigned char*)NULL,"Domain name not found");
799
800         if (header.ancount < 1)
801                 return std::make_pair((unsigned char*)NULL,"No resource records returned");
802
803         /* Subtract the length of the header from the length of the packet */
804         length -= 12;
805
806         while ((unsigned int)q < header.qdcount && i < length)
807         {
808                 if (header.payload[i] > 63)
809                 {
810                         i += 6;
811                         q++;
812                 }
813                 else
814                 {
815                         if (header.payload[i] == 0)
816                         {
817                                 q++;
818                                 i += 5;
819                         }
820                         else i += header.payload[i] + 1;
821                 }
822         }
823         curanswer = 0;
824         while ((unsigned)curanswer < header.ancount)
825         {
826                 q = 0;
827                 while (q == 0 && i < length)
828                 {
829                         if (header.payload[i] > 63)
830                         {
831                                 i += 2;
832                                 q = 1;
833                         }
834                         else
835                         {
836                                 if (header.payload[i] == 0)
837                                 {
838                                         i++;
839                                         q = 1;
840                                 }
841                                 else i += header.payload[i] + 1; /* skip length and label */
842                         }
843                 }
844                 if (length - i < 10)
845                         return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
846
847                 /* XXX: We actually initialise 'rr' here including its ttl field */
848                 if (curanswer == result_we_want)
849                         DNS::FillResourceRecord(&rr,&header.payload[i]);
850         
851                 i += 10;
852                 if (rr.type != this->type)
853                 {
854                         curanswer++;
855                         i += rr.rdlength;
856                         continue;
857                 }
858                 if (rr.rr_class != this->rr_class)
859                 {
860                         curanswer++;
861                         i += rr.rdlength;
862                         continue;
863                 }
864                 break;
865         }
866         if ((unsigned int)curanswer == header.ancount)
867                 return std::make_pair((unsigned char*)NULL,"No more records");
868
869         if (i + rr.rdlength > (unsigned int)length)
870                 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
871
872         if (rr.rdlength > 1023)
873                 return std::make_pair((unsigned char*)NULL,"Resource record too large");
874
875         this->ttl = rr.ttl;
876
877         switch (rr.type)
878         {
879                 case DNS_QUERY_CNAME:
880                         /* CNAME and PTR have the same processing code */
881                 case DNS_QUERY_PTR:
882                         o = 0;
883                         q = 0;
884                         while (q == 0 && i < length && o + 256 < 1023)
885                         {
886                                 if (header.payload[i] > 63)
887                                 {
888                                         memcpy(&ptr,&header.payload[i],2);
889                                         i = ntohs(ptr) - 0xC000 - 12;
890                                 }
891                                 else
892                                 {
893                                         if (header.payload[i] == 0)
894                                         {
895                                                 q = 1;
896                                         }
897                                         else
898                                         {
899                                                 res[o] = 0;
900                                                 if (o != 0)
901                                                         res[o++] = '.';
902                                                 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
903                                                 o += header.payload[i];
904                                                 i += header.payload[i] + 1;
905                                         }
906                                 }
907                         }
908                         res[o] = 0;
909                 break;
910                 case DNS_QUERY_AAAA:
911                         memcpy(res,&header.payload[i],rr.rdlength);
912                         res[rr.rdlength] = 0;
913                 break;
914                 case DNS_QUERY_A:
915                         memcpy(res,&header.payload[i],rr.rdlength);
916                         res[rr.rdlength] = 0;
917                 break;
918                 default:
919                         memcpy(res,&header.payload[i],rr.rdlength);
920                         res[rr.rdlength] = 0;
921                 break;
922         }
923         return std::make_pair(res,"No error");
924 }
925
926 /** Close the master socket */
927 DNS::~DNS()
928 {
929         ServerInstance->SE->Shutdown(this, 2);
930         ServerInstance->SE->Close(this);
931         ServerInstance->Timers->DelTimer(this->PruneTimer);
932         delete this->PruneTimer;
933 }
934
935 CachedQuery* DNS::GetCache(const std::string &source)
936 {
937         dnscache::iterator x = cache->find(source.c_str());
938         if (x != cache->end())
939                 return &(x->second);
940         else
941                 return NULL;
942 }
943                 
944 void DNS::DelCache(const std::string &source)
945 {
946         cache->erase(source.c_str());
947 }
948
949 void Resolver::TriggerCachedResult()
950 {
951         if (CQ)
952                 OnLookupComplete(CQ->data, time_left, true, 0);
953 }
954
955 /** High level abstraction of dns used by application at large */
956 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
957 {
958         cached = false;
959
960         CQ = ServerInstance->Res->GetCache(source);
961         if (CQ)
962         {
963                 time_left = CQ->CalcTTLRemaining();
964                 if (!time_left)
965                 {
966                         ServerInstance->Res->DelCache(source);
967                 }
968                 else
969                 {
970                         cached = true;
971                         return;
972                 }
973         }
974
975         insp_inaddr binip;
976
977         switch (querytype)
978         {
979                 case DNS_QUERY_A:
980                         this->myid = ServerInstance->Res->GetIP(source.c_str());
981                 break;
982
983                 case DNS_QUERY_PTR:
984                         if (insp_aton(source.c_str(), &binip) > 0)
985                         {
986                                 /* Valid ip address */
987                                 this->myid = ServerInstance->Res->GetName(&binip);
988                         }
989                         else
990                         {
991                                 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
992                                 throw ModuleException("Resolver: Bad IP address");
993                                 return;
994                         }
995                 break;
996
997                 case DNS_QUERY_PTR4:
998                         querytype = DNS_QUERY_PTR;
999                         this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
1000                 break;
1001
1002                 case DNS_QUERY_PTR6:
1003                         querytype = DNS_QUERY_PTR;
1004                         this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
1005                 break;
1006
1007                 case DNS_QUERY_AAAA:
1008                         this->myid = ServerInstance->Res->GetIP6(source.c_str());
1009                 break;
1010
1011                 case DNS_QUERY_CNAME:
1012                         this->myid = ServerInstance->Res->GetCName(source.c_str());
1013                 break;
1014
1015                 default:
1016                         this->myid = -1;
1017                 break;
1018         }
1019         if (this->myid == -1)
1020         {
1021                 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1022                 throw ModuleException("Resolver: Couldnt get an id to make a request");
1023                 /* We shouldnt get here really */
1024                 return;
1025         }
1026 }
1027
1028 /** Called when an error occurs */
1029 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1030 {
1031         /* Nothing in here */
1032 }
1033
1034 /** Destroy a resolver */
1035 Resolver::~Resolver()
1036 {
1037         /* Nothing here (yet) either */
1038 }
1039
1040 /** Get the request id associated with this class */
1041 int Resolver::GetId()
1042 {
1043         return this->myid;
1044 }
1045
1046 Module* Resolver::GetCreator()
1047 {
1048         return this->Creator;
1049 }
1050
1051 /** Process a socket read event */
1052 void DNS::HandleEvent(EventType et, int errornum)
1053 {
1054         /* Fetch the id and result of the next available packet */
1055         int resultnum = 0;
1056         DNSResult res(0,"",0,"");
1057         res.id = 0;
1058         ServerInstance->Log(DEBUG,"Handle DNS event");
1059         while ((res.id & ERROR_MASK) == 0)
1060         {
1061                 res = this->GetResult(resultnum);
1062
1063                 ServerInstance->Log(DEBUG,"Result %d id %d", resultnum, res.id);
1064         
1065                 /* Is there a usable request id? */
1066                 if (res.id != -1)
1067                 {
1068                         /* Its an error reply */
1069                         if (res.id & ERROR_MASK)
1070                         {
1071                                 /* Mask off the error bit */
1072                                 res.id -= ERROR_MASK;
1073                                 /* Marshall the error to the correct class */
1074                                 if (Classes[res.id])
1075                                 {
1076                                         if (ServerInstance && ServerInstance->stats)
1077                                                 ServerInstance->stats->statsDnsBad++;
1078                                         Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1079                                         delete Classes[res.id];
1080                                         Classes[res.id] = NULL;
1081                                 }
1082                                 break;
1083                         }
1084                         else
1085                         {
1086                                 /* It is a non-error result, marshall the result to the correct class */
1087                                 if (Classes[res.id])
1088                                 {
1089                                         if (ServerInstance && ServerInstance->stats)
1090                                                 ServerInstance->stats->statsDnsGood++;
1091         
1092                                         if (!this->GetCache(res.original.c_str()))
1093                                                 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1094         
1095                                         Classes[res.id]->OnLookupComplete(res.result, res.ttl, false, resultnum);
1096                                         delete Classes[res.id];
1097                                         Classes[res.id] = NULL;
1098                                 }
1099                         }
1100         
1101                         if (ServerInstance && ServerInstance->stats)
1102                                 ServerInstance->stats->statsDns++;
1103                 }
1104
1105                 resultnum++;
1106         }
1107 }
1108         
1109 /** Add a derived Resolver to the working set */
1110 bool DNS::AddResolverClass(Resolver* r)
1111 {
1112         /* Check the pointers validity and the id's validity */
1113         if ((r) && (r->GetId() > -1))
1114         {
1115                 /* Check the slot isnt already occupied -
1116                  * This should NEVER happen unless we have
1117                  * a severely broken DNS server somewhere
1118                  */
1119                 if (!Classes[r->GetId()])
1120                 {
1121                         /* Set up the pointer to the class */
1122                         Classes[r->GetId()] = r;
1123                         return true;
1124                 }
1125                 else
1126                         /* Duplicate id */
1127                         return false;
1128         }
1129         else
1130         {
1131                 /* Pointer or id not valid.
1132                  * Free the item and return
1133                  */
1134                 if (r)
1135                         delete r;
1136
1137                 return false;
1138         }
1139 }
1140
1141 void DNS::CleanResolvers(Module* module)
1142 {
1143         for (int i = 0; i < MAX_REQUEST_ID; i++)
1144         {
1145                 if (Classes[i])
1146                 {
1147                         if (Classes[i]->GetCreator() == module)
1148                         {
1149                                 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1150                                 delete Classes[i];
1151                                 Classes[i] = NULL;
1152                         }
1153                 }
1154         }
1155 }
1156
1157 /** Generate pseudo-random number */
1158 unsigned long DNS::PRNG()
1159 {
1160         unsigned long val = 0;
1161         timeval n;
1162         serverstats* s = ServerInstance->stats;
1163         gettimeofday(&n,NULL);
1164         val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1165         val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1166         val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size();
1167         return val;
1168 }
1169