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