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