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