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