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