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