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