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