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