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