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