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