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