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