]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
This won't work yet.
[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 /* $Core: libIRCDasyncdns */
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 using irc::sockets::insp_inaddr;
44 using irc::sockets::insp_ntoa;
45 using irc::sockets::insp_aton;
46 using irc::sockets::OpenTCPSocket;
47
48 /** Masks to mask off the responses we get from the DNSRequest methods
49  */
50 enum QueryInfo
51 {
52         ERROR_MASK      = 0x10000       /* Result is an error */
53 };
54
55 /** Flags which can be ORed into a request or reply for different meanings
56  */
57 enum QueryFlags
58 {
59         FLAGS_MASK_RD           = 0x01, /* Recursive */
60         FLAGS_MASK_TC           = 0x02,
61         FLAGS_MASK_AA           = 0x04, /* Authoritative */
62         FLAGS_MASK_OPCODE       = 0x78,
63         FLAGS_MASK_QR           = 0x80,
64         FLAGS_MASK_RCODE        = 0x0F, /* Request */
65         FLAGS_MASK_Z            = 0x70,
66         FLAGS_MASK_RA           = 0x80
67 };
68
69
70 /** Represents a dns resource record (rr)
71  */
72 struct ResourceRecord
73 {
74         QueryType       type;           /* Record type */
75         unsigned int    rr_class;       /* Record class */
76         unsigned long   ttl;            /* Time to live */
77         unsigned int    rdlength;       /* Record length */
78 };
79
80 /** Represents a dns request/reply header, and its payload as opaque data.
81  */
82 class DNSHeader
83 {
84  public:
85         unsigned char   id[2];          /* Request id */
86         unsigned int    flags1;         /* Flags */
87         unsigned int    flags2;         /* Flags */
88         unsigned int    qdcount;
89         unsigned int    ancount;        /* Answer count */
90         unsigned int    nscount;        /* Nameserver count */
91         unsigned int    arcount;
92         unsigned char   payload[512];   /* Packet payload */
93 };
94
95 class DNSRequest
96 {
97  public:
98         unsigned char   id[2];          /* Request id */
99         unsigned char*  res;            /* Result processing buffer */
100         unsigned int    rr_class;       /* Request class */
101         QueryType       type;           /* Request type */
102         DNS*            dnsobj;         /* DNS caller (where we get our FD from) */
103         unsigned long   ttl;            /* Time to live */
104         std::string     orig;           /* Original requested name/ip */
105         InspIRCd*       ServerInstance;
106
107         DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
108         ~DNSRequest();
109         DNSInfo ResultIsReady(DNSHeader &h, int length, int result_we_want);
110         int SendRequests(const DNSHeader *header, const int length, QueryType qt);
111 };
112
113 class CacheTimer : public Timer
114 {
115  private:
116         InspIRCd* ServerInstance;
117         DNS* dns;
118  public:
119         CacheTimer(InspIRCd* Instance, DNS* thisdns)
120                 : Timer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
121
122         virtual void Tick(time_t)
123         {
124                 dns->PruneCache();
125         }
126 };
127
128 class RequestTimeout : public Timer
129 {
130         InspIRCd* ServerInstance;
131         DNSRequest* watch;
132         int watchid;
133  public:
134         RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : Timer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
135         {
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                         return;
152                 }
153         }
154 };
155
156 /* Allocate the processing buffer */
157 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns), ServerInstance(Instance)
158 {
159         res = new unsigned char[512];
160         *res = 0;
161         orig = original;
162         RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
163         Instance->Timers->AddTimer(RT); /* The timer manager frees this */
164 }
165
166 /* Deallocate the processing buffer */
167 DNSRequest::~DNSRequest()
168 {
169         delete[] res;
170 }
171
172 /** Fill a ResourceRecord class based on raw data input */
173 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
174 {
175         rr->type = (QueryType)((input[0] << 8) + input[1]);
176         rr->rr_class = (input[2] << 8) + input[3];
177         rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
178         rr->rdlength = (input[8] << 8) + input[9];
179 }
180
181 /** Fill a DNSHeader class based on raw data input of a given length */
182 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
183 {
184         header->id[0] = input[0];
185         header->id[1] = input[1];
186         header->flags1 = input[2];
187         header->flags2 = input[3];
188         header->qdcount = (input[4] << 8) + input[5];
189         header->ancount = (input[6] << 8) + input[7];
190         header->nscount = (input[8] << 8) + input[9];
191         header->arcount = (input[10] << 8) + input[11];
192         memcpy(header->payload,&input[12],length);
193 }
194
195 /** Empty a DNSHeader class out into raw data, ready for transmission */
196 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
197 {
198         output[0] = header->id[0];
199         output[1] = header->id[1];
200         output[2] = header->flags1;
201         output[3] = header->flags2;
202         output[4] = header->qdcount >> 8;
203         output[5] = header->qdcount & 0xFF;
204         output[6] = header->ancount >> 8;
205         output[7] = header->ancount & 0xFF;
206         output[8] = header->nscount >> 8;
207         output[9] = header->nscount & 0xFF;
208         output[10] = header->arcount >> 8;
209         output[11] = header->arcount & 0xFF;
210         memcpy(&output[12],header->payload,length);
211 }
212
213 /** Send requests we have previously built down the UDP socket */
214 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
215 {
216         unsigned char payload[sizeof(DNSHeader)];
217
218         this->rr_class = 1;
219         this->type = qt;
220                 
221         DNS::EmptyHeader(payload,header,length);
222
223 #ifdef IPV6
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 #endif
236         {
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 (ServerInstance->SE->SendTo(dnsobj, (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
243                         return -1;
244         }
245         return 0;
246 }
247
248 /** Add a query with a predefined header, and allocate an ID for it. */
249 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
250 {
251         /* Is the DNS connection down? */
252         if (this->GetFd() == -1)
253                 return NULL;
254         
255         /* Create an id */
256         id = this->PRNG() & DNS::MAX_REQUEST_ID;
257
258         /* If this id is already 'in flight', pick another. */
259         while (requests[id])
260                 id = this->PRNG() & DNS::MAX_REQUEST_ID;
261
262         DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
263
264         header->id[0] = req->id[0] = id >> 8;
265         header->id[1] = req->id[1] = id & 0xFF;
266         header->flags1 = FLAGS_MASK_RD;
267         header->flags2 = 0;
268         header->qdcount = 1;
269         header->ancount = 0;
270         header->nscount = 0;
271         header->arcount = 0;
272
273         /* At this point we already know the id doesnt exist,
274          * so there needs to be no second check for the ::end()
275          */
276         requests[id] = req;
277
278         /* According to the C++ spec, new never returns NULL. */
279         return req;
280 }
281
282 int DNS::ClearCache()
283 {
284         /* This ensures the buckets are reset to sane levels */
285         int rv = this->cache->size();
286         delete this->cache;
287         this->cache = new dnscache();
288         return rv;
289 }
290
291 int DNS::PruneCache()
292 {
293         int n = 0;
294         dnscache* newcache = new dnscache();
295         for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
296                 /* Dont include expired items (theres no point) */
297                 if (i->second.CalcTTLRemaining())
298                         newcache->insert(*i);
299                 else
300                         n++;
301
302         delete this->cache;
303         this->cache = newcache;
304         return n;
305 }
306
307 void DNS::Rehash()
308 {
309         ip6munge = false;
310         int portpass = 0;
311
312         if (this->GetFd() > -1)
313         {
314                 if (ServerInstance && ServerInstance->SE)
315                         ServerInstance->SE->DelFd(this);
316                 ServerInstance->SE->Shutdown(this, 2);
317                 ServerInstance->SE->Close(this);
318                 this->SetFd(-1);
319
320                 /* Rehash the cache */
321                 this->PruneCache();
322         }
323         else
324         {
325                 /* Create initial dns cache */
326                 this->cache = new dnscache();
327         }
328
329         if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) ||  (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
330         {
331                 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
332                 ServerInstance->Log(DEFAULT,"         This should not cause a problem, however it is recommended you migrate");
333                 ServerInstance->Log(DEFAULT,"         to a true IPv6 environment.");
334                 this->ip6munge = true;
335         }
336
337         this->socketfamily = AF_INET;
338 #ifdef IPV6
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 #else
350         inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
351 #endif
352
353         /* Initialize mastersocket */
354         int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
355         this->SetFd(s);
356         ServerInstance->SE->NonBlocking(this->GetFd());
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                         ServerInstance->SE->Shutdown(this, 2);
366                         ServerInstance->SE->Close(this);
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                                         ServerInstance->SE->Shutdown(this, 2);
379                                         ServerInstance->SE->Close(this);
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(int resultnum)
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 = ServerInstance->SE->RecvFrom(this, (char*)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 #endif
651         {
652                 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
653                 port_from = ntohs(((sockaddr_in*)from)->sin_port);
654         }
655
656         delete[] from;
657
658         /* We cant perform this security check if you're using 4in6.
659          * Tough luck to you, choose one or't other!
660          */
661         if (!ip6munge)
662         {
663                 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
664                 {
665                         return DNSResult(-1,"",0,"");
666                 }
667         }
668
669         /* Put the read header info into a header class */
670         DNS::FillHeader(&header,buffer,length - 12);
671
672         /* Get the id of this request.
673          * Its a 16 bit value stored in two char's,
674          * so we use logic shifts to create the value.
675          */
676         unsigned long this_id = header.id[1] + (header.id[0] << 8);
677
678         /* Do we have a pending request matching this id? */
679         if (!requests[this_id])
680         {
681                 /* Somehow we got a DNS response for a request we never made... */
682                 return DNSResult(-1,"",0,"");
683         }
684         else
685         {
686                 /* Remove the query from the list of pending queries */
687                 req = requests[this_id];
688                 requests[this_id] = NULL;
689         }
690
691         /* Inform the DNSRequest class that it has a result to be read.
692          * When its finished it will return a DNSInfo which is a pair of
693          * unsigned char* resource record data, and an error message.
694          */
695         DNSInfo data = req->ResultIsReady(header, length, resultnum);
696         std::string resultstr;
697
698         /* Check if we got a result, if we didnt, its an error */
699         if (data.first == NULL)
700         {
701                 /* An error.
702                  * Mask the ID with the value of ERROR_MASK, so that
703                  * the dns_deal_with_classes() function knows that its
704                  * an error response and needs to be treated uniquely.
705                  * Put the error message in the second field.
706                  */
707                 std::string ro = req->orig;
708                 delete req;
709                 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
710         }
711         else
712         {
713                 unsigned long ttl = req->ttl;
714                 char formatted[128];
715
716                 /* Forward lookups come back as binary data. We must format them into ascii */
717                 switch (req->type)
718                 {
719                         case DNS_QUERY_A:
720                                 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
721                                 resultstr = formatted;
722                         break;
723
724                         case DNS_QUERY_AAAA:
725                         {
726                                 inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted));
727                                 char* c = strstr(formatted,":0:");
728                                 if (c != NULL)
729                                 {
730                                         memmove(c+1,c+2,strlen(c+2) + 1);
731                                         c += 2;
732                                         while (memcmp(c,"0:",2) == 0)
733                                                 memmove(c,c+2,strlen(c+2) + 1);
734                                         if (memcmp(c,"0",2) == 0)
735                                                 *c = 0;
736                                         if (memcmp(formatted,"0::",3) == 0)
737                                                 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
738                                 }
739                                 resultstr = formatted;
740
741                                 /* Special case. Sending ::1 around between servers
742                                  * and to clients is dangerous, because the : on the
743                                  * start makes the client or server interpret the IP
744                                  * as the last parameter on the line with a value ":1".
745                                  */
746                                 if (*formatted == ':')
747                                         resultstr.insert(0, "0");
748                         }
749                         break;
750
751                         case DNS_QUERY_CNAME:
752                                 /* Identical handling to PTR */
753
754                         case DNS_QUERY_PTR:
755                                 /* Reverse lookups just come back as char* */
756                                 resultstr = std::string((const char*)data.first);
757                         break;
758
759                         default:
760                         break;
761                         
762                 }
763
764                 /* Build the reply with the id and hostname/ip in it */
765                 std::string ro = req->orig;
766                 delete req;
767                 return DNSResult(this_id,resultstr,ttl,ro);
768         }
769 }
770
771 /** A result is ready, process it */
772 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length, int result_we_want)
773 {
774         int i = 0;
775         int q = 0;
776         int curanswer, o;
777         ResourceRecord rr;
778         unsigned short ptr;
779
780         /* This is just to keep _FORTIFY_SOURCE happy */
781         rr.type = DNS_QUERY_NONE;
782         rr.rdlength = 0;
783         rr.ttl = 1;     /* GCC is a whiney bastard -- see the XXX below. */
784
785         if (!(header.flags1 & FLAGS_MASK_QR))
786                 return std::make_pair((unsigned char*)NULL,"Not a query result");
787
788         if (header.flags1 & FLAGS_MASK_OPCODE)
789                 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
790
791         if (header.flags2 & FLAGS_MASK_RCODE)
792                 return std::make_pair((unsigned char*)NULL,"Domain name not found");
793
794         if (header.ancount < 1)
795                 return std::make_pair((unsigned char*)NULL,"No resource records returned");
796
797         /* Subtract the length of the header from the length of the packet */
798         length -= 12;
799
800         while ((unsigned int)q < header.qdcount && i < length)
801         {
802                 if (header.payload[i] > 63)
803                 {
804                         i += 6;
805                         q++;
806                 }
807                 else
808                 {
809                         if (header.payload[i] == 0)
810                         {
811                                 q++;
812                                 i += 5;
813                         }
814                         else i += header.payload[i] + 1;
815                 }
816         }
817         curanswer = 0;
818         while ((unsigned)curanswer < header.ancount)
819         {
820                 q = 0;
821                 while (q == 0 && i < length)
822                 {
823                         if (header.payload[i] > 63)
824                         {
825                                 i += 2;
826                                 q = 1;
827                         }
828                         else
829                         {
830                                 if (header.payload[i] == 0)
831                                 {
832                                         i++;
833                                         q = 1;
834                                 }
835                                 else i += header.payload[i] + 1; /* skip length and label */
836                         }
837                 }
838                 if (length - i < 10)
839                         return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
840
841                 /* XXX: We actually initialise 'rr' here including its ttl field */
842                 if (curanswer == result_we_want)
843                         DNS::FillResourceRecord(&rr,&header.payload[i]);
844         
845                 i += 10;
846                 if (rr.type != this->type)
847                 {
848                         curanswer++;
849                         i += rr.rdlength;
850                         continue;
851                 }
852                 if (rr.rr_class != this->rr_class)
853                 {
854                         curanswer++;
855                         i += rr.rdlength;
856                         continue;
857                 }
858                 break;
859         }
860         if ((unsigned int)curanswer == header.ancount)
861                 return std::make_pair((unsigned char*)NULL,"No more records");
862
863         if (i + rr.rdlength > (unsigned int)length)
864                 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
865
866         if (rr.rdlength > 1023)
867                 return std::make_pair((unsigned char*)NULL,"Resource record too large");
868
869         this->ttl = rr.ttl;
870
871         switch (rr.type)
872         {
873                 case DNS_QUERY_CNAME:
874                         /* CNAME and PTR have the same processing code */
875                 case DNS_QUERY_PTR:
876                         o = 0;
877                         q = 0;
878                         while (q == 0 && i < length && o + 256 < 1023)
879                         {
880                                 if (header.payload[i] > 63)
881                                 {
882                                         memcpy(&ptr,&header.payload[i],2);
883                                         i = ntohs(ptr) - 0xC000 - 12;
884                                 }
885                                 else
886                                 {
887                                         if (header.payload[i] == 0)
888                                         {
889                                                 q = 1;
890                                         }
891                                         else
892                                         {
893                                                 res[o] = 0;
894                                                 if (o != 0)
895                                                         res[o++] = '.';
896                                                 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
897                                                 o += header.payload[i];
898                                                 i += header.payload[i] + 1;
899                                         }
900                                 }
901                         }
902                         res[o] = 0;
903                 break;
904                 case DNS_QUERY_AAAA:
905                         memcpy(res,&header.payload[i],rr.rdlength);
906                         res[rr.rdlength] = 0;
907                 break;
908                 case DNS_QUERY_A:
909                         memcpy(res,&header.payload[i],rr.rdlength);
910                         res[rr.rdlength] = 0;
911                 break;
912                 default:
913                         memcpy(res,&header.payload[i],rr.rdlength);
914                         res[rr.rdlength] = 0;
915                 break;
916         }
917         return std::make_pair(res,"No error");
918 }
919
920 /** Close the master socket */
921 DNS::~DNS()
922 {
923         ServerInstance->SE->Shutdown(this, 2);
924         ServerInstance->SE->Close(this);
925         ServerInstance->Timers->DelTimer(this->PruneTimer);
926         delete this->PruneTimer;
927 }
928
929 CachedQuery* DNS::GetCache(const std::string &source)
930 {
931         dnscache::iterator x = cache->find(source.c_str());
932         if (x != cache->end())
933                 return &(x->second);
934         else
935                 return NULL;
936 }
937                 
938 void DNS::DelCache(const std::string &source)
939 {
940         cache->erase(source.c_str());
941 }
942
943 void Resolver::TriggerCachedResult()
944 {
945         if (CQ)
946                 OnLookupComplete(CQ->data, time_left, true, 0);
947 }
948
949 /** High level abstraction of dns used by application at large */
950 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
951 {
952         cached = false;
953
954         CQ = ServerInstance->Res->GetCache(source);
955         if (CQ)
956         {
957                 time_left = CQ->CalcTTLRemaining();
958                 if (!time_left)
959                 {
960                         ServerInstance->Res->DelCache(source);
961                 }
962                 else
963                 {
964                         cached = true;
965                         return;
966                 }
967         }
968
969         insp_inaddr binip;
970
971         switch (querytype)
972         {
973                 case DNS_QUERY_A:
974                         this->myid = ServerInstance->Res->GetIP(source.c_str());
975                 break;
976
977                 case DNS_QUERY_PTR:
978                         if (insp_aton(source.c_str(), &binip) > 0)
979                         {
980                                 /* Valid ip address */
981                                 this->myid = ServerInstance->Res->GetName(&binip);
982                         }
983                         else
984                         {
985                                 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
986                                 throw ModuleException("Resolver: Bad IP address");
987                                 return;
988                         }
989                 break;
990
991                 case DNS_QUERY_PTR4:
992                         querytype = DNS_QUERY_PTR;
993                         this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
994                 break;
995
996                 case DNS_QUERY_PTR6:
997                         querytype = DNS_QUERY_PTR;
998                         this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
999                 break;
1000
1001                 case DNS_QUERY_AAAA:
1002                         this->myid = ServerInstance->Res->GetIP6(source.c_str());
1003                 break;
1004
1005                 case DNS_QUERY_CNAME:
1006                         this->myid = ServerInstance->Res->GetCName(source.c_str());
1007                 break;
1008
1009                 default:
1010                         this->myid = -1;
1011                 break;
1012         }
1013         if (this->myid == -1)
1014         {
1015                 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1016                 throw ModuleException("Resolver: Couldnt get an id to make a request");
1017                 /* We shouldnt get here really */
1018                 return;
1019         }
1020 }
1021
1022 /** Called when an error occurs */
1023 void Resolver::OnError(ResolverError, const std::string&)
1024 {
1025         /* Nothing in here */
1026 }
1027
1028 /** Destroy a resolver */
1029 Resolver::~Resolver()
1030 {
1031         /* Nothing here (yet) either */
1032 }
1033
1034 /** Get the request id associated with this class */
1035 int Resolver::GetId()
1036 {
1037         return this->myid;
1038 }
1039
1040 Module* Resolver::GetCreator()
1041 {
1042         return this->Creator;
1043 }
1044
1045 /** Process a socket read event */
1046 void DNS::HandleEvent(EventType, int)
1047 {
1048         /* Fetch the id and result of the next available packet */
1049         int resultnum = 0;
1050         DNSResult res(0,"",0,"");
1051         res.id = 0;
1052         ServerInstance->Log(DEBUG,"Handle DNS event");
1053         while ((res.id & ERROR_MASK) == 0)
1054         {
1055                 res = this->GetResult(resultnum);
1056
1057                 ServerInstance->Log(DEBUG,"Result %d id %d", resultnum, res.id);
1058         
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                                 break;
1077                         }
1078                         else
1079                         {
1080                                 /* It is a non-error result, marshall the result to the correct class */
1081                                 if (Classes[res.id])
1082                                 {
1083                                         if (ServerInstance && ServerInstance->stats)
1084                                                 ServerInstance->stats->statsDnsGood++;
1085         
1086                                         if (!this->GetCache(res.original.c_str()))
1087                                                 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1088         
1089                                         Classes[res.id]->OnLookupComplete(res.result, res.ttl, false, resultnum);
1090                                         delete Classes[res.id];
1091                                         Classes[res.id] = NULL;
1092                                 }
1093                         }
1094         
1095                         if (ServerInstance && ServerInstance->stats)
1096                                 ServerInstance->stats->statsDns++;
1097                 }
1098
1099                 resultnum++;
1100         }
1101 }
1102         
1103 /** Add a derived Resolver to the working set */
1104 bool DNS::AddResolverClass(Resolver* r)
1105 {
1106         /* Check the pointers validity and the id's validity */
1107         if ((r) && (r->GetId() > -1))
1108         {
1109                 /* Check the slot isnt already occupied -
1110                  * This should NEVER happen unless we have
1111                  * a severely broken DNS server somewhere
1112                  */
1113                 if (!Classes[r->GetId()])
1114                 {
1115                         /* Set up the pointer to the class */
1116                         Classes[r->GetId()] = r;
1117                         return true;
1118                 }
1119                 else
1120                         /* Duplicate id */
1121                         return false;
1122         }
1123         else
1124         {
1125                 /* Pointer or id not valid.
1126                  * Free the item and return
1127                  */
1128                 if (r)
1129                         delete r;
1130
1131                 return false;
1132         }
1133 }
1134
1135 void DNS::CleanResolvers(Module* module)
1136 {
1137         for (int i = 0; i < MAX_REQUEST_ID; i++)
1138         {
1139                 if (Classes[i])
1140                 {
1141                         if (Classes[i]->GetCreator() == module)
1142                         {
1143                                 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1144                                 delete Classes[i];
1145                                 Classes[i] = NULL;
1146                         }
1147                 }
1148         }
1149 }
1150
1151 /** Generate pseudo-random number */
1152 unsigned long DNS::PRNG()
1153 {
1154         unsigned long val = 0;
1155         timeval n;
1156         serverstats* s = ServerInstance->stats;
1157         gettimeofday(&n,NULL);
1158         val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1159         val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1160         val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size();
1161         return val;
1162 }
1163