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