]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
Remove InspIRCd* parameters and fields
[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
100         DNSRequest(DNS* dns, int id, const std::string &original);
101         ~DNSRequest();
102         DNSInfo ResultIsReady(DNSHeader &h, int length);
103         int SendRequests(const DNSHeader *header, const int length, QueryType qt);
104 };
105
106 class CacheTimer : public Timer
107 {
108  private:
109         DNS* dns;
110  public:
111         CacheTimer(DNS* thisdns)
112                 : Timer(3600, ServerInstance->Time(), true), dns(thisdns) { }
113
114         virtual void Tick(time_t)
115         {
116                 dns->PruneCache();
117         }
118 };
119
120 class RequestTimeout : public Timer
121 {
122         DNSRequest* watch;
123         int watchid;
124  public:
125         RequestTimeout(unsigned long n, DNSRequest* watching, int id) : Timer(n, ServerInstance->Time()), watch(watching), watchid(id)
126         {
127         }
128         ~RequestTimeout()
129         {
130                 if (ServerInstance->Res)
131                         Tick(0);
132         }
133
134         void Tick(time_t)
135         {
136                 if (ServerInstance->Res->requests[watchid] == watch)
137                 {
138                         /* Still exists, whack it */
139                         if (ServerInstance->Res->Classes[watchid])
140                         {
141                                 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
142                                 delete ServerInstance->Res->Classes[watchid];
143                                 ServerInstance->Res->Classes[watchid] = NULL;
144                         }
145                         ServerInstance->Res->requests[watchid] = NULL;
146                         delete watch;
147                 }
148         }
149 };
150
151 /* Allocate the processing buffer */
152 DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns)
153 {
154         res = new unsigned char[512];
155         *res = 0;
156         orig = original;
157         RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid);
158         ServerInstance->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(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, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
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()
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(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         if (cache)
880                 delete cache;
881 }
882
883 CachedQuery* DNS::GetCache(const std::string &source)
884 {
885         dnscache::iterator x = cache->find(source.c_str());
886         if (x != cache->end())
887                 return &(x->second);
888         else
889                 return NULL;
890 }
891
892 void DNS::DelCache(const std::string &source)
893 {
894         cache->erase(source.c_str());
895 }
896
897 void Resolver::TriggerCachedResult()
898 {
899         if (CQ)
900                 OnLookupComplete(CQ->data, time_left, true);
901 }
902
903 /** High level abstraction of dns used by application at large */
904 Resolver::Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator) : Creator(creator), input(source), querytype(qt)
905 {
906         ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
907         cached = false;
908
909         CQ = ServerInstance->Res->GetCache(source);
910         if (CQ)
911         {
912                 time_left = CQ->CalcTTLRemaining();
913                 if (!time_left)
914                 {
915                         ServerInstance->Res->DelCache(source);
916                 }
917                 else
918                 {
919                         cached = true;
920                         return;
921                 }
922         }
923
924         switch (querytype)
925         {
926                 case DNS_QUERY_A:
927                         this->myid = ServerInstance->Res->GetIP(source.c_str());
928                 break;
929
930                 case DNS_QUERY_PTR4:
931                         querytype = DNS_QUERY_PTR;
932                         this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
933                 break;
934
935                 case DNS_QUERY_PTR6:
936                         querytype = DNS_QUERY_PTR;
937                         this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
938                 break;
939
940                 case DNS_QUERY_AAAA:
941                         this->myid = ServerInstance->Res->GetIP6(source.c_str());
942                 break;
943
944                 case DNS_QUERY_CNAME:
945                         this->myid = ServerInstance->Res->GetCName(source.c_str());
946                 break;
947
948                 default:
949                         this->myid = -1;
950                 break;
951         }
952         if (this->myid == -1)
953         {
954                 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
955                 throw ModuleException("Resolver: Couldnt get an id to make a request");
956                 /* We shouldnt get here really */
957                 return;
958         }
959         else
960         {
961                 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
962         }
963 }
964
965 /** Called when an error occurs */
966 void Resolver::OnError(ResolverError, const std::string&)
967 {
968         /* Nothing in here */
969 }
970
971 /** Destroy a resolver */
972 Resolver::~Resolver()
973 {
974         /* Nothing here (yet) either */
975 }
976
977 /** Get the request id associated with this class */
978 int Resolver::GetId()
979 {
980         return this->myid;
981 }
982
983 Module* Resolver::GetCreator()
984 {
985         return this->Creator;
986 }
987
988 /** Process a socket read event */
989 void DNS::HandleEvent(EventType, int)
990 {
991         /* Fetch the id and result of the next available packet */
992         DNSResult res(0,"",0,"");
993         res.id = 0;
994         ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
995
996         res = this->GetResult();
997
998         ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result id %d", res.id);
999
1000         /* Is there a usable request id? */
1001         if (res.id != -1)
1002         {
1003                 /* Its an error reply */
1004                 if (res.id & ERROR_MASK)
1005                 {
1006                         /* Mask off the error bit */
1007                         res.id -= ERROR_MASK;
1008                         /* Marshall the error to the correct class */
1009                         if (Classes[res.id])
1010                         {
1011                                 if (ServerInstance && ServerInstance->stats)
1012                                         ServerInstance->stats->statsDnsBad++;
1013                                 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1014                                 delete Classes[res.id];
1015                                 Classes[res.id] = NULL;
1016                         }
1017                         return;
1018                 }
1019                 else
1020                 {
1021                         /* It is a non-error result, marshall the result to the correct class */
1022                         if (Classes[res.id])
1023                         {
1024                                 if (ServerInstance && ServerInstance->stats)
1025                                         ServerInstance->stats->statsDnsGood++;
1026
1027                                 if (!this->GetCache(res.original.c_str()))
1028                                         this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1029
1030                                 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1031                                 delete Classes[res.id];
1032                                 Classes[res.id] = NULL;
1033                         }
1034                 }
1035
1036                 if (ServerInstance && ServerInstance->stats)
1037                         ServerInstance->stats->statsDns++;
1038         }
1039 }
1040
1041 /** Add a derived Resolver to the working set */
1042 bool DNS::AddResolverClass(Resolver* r)
1043 {
1044         ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
1045         /* Check the pointers validity and the id's validity */
1046         if ((r) && (r->GetId() > -1))
1047         {
1048                 /* Check the slot isnt already occupied -
1049                  * This should NEVER happen unless we have
1050                  * a severely broken DNS server somewhere
1051                  */
1052                 if (!Classes[r->GetId()])
1053                 {
1054                         /* Set up the pointer to the class */
1055                         Classes[r->GetId()] = r;
1056                         return true;
1057                 }
1058                 else
1059                         /* Duplicate id */
1060                         return false;
1061         }
1062         else
1063         {
1064                 /* Pointer or id not valid.
1065                  * Free the item and return
1066                  */
1067                 if (r)
1068                         delete r;
1069
1070                 return false;
1071         }
1072 }
1073
1074 void DNS::CleanResolvers(Module* module)
1075 {
1076         for (int i = 0; i < MAX_REQUEST_ID; i++)
1077         {
1078                 if (Classes[i])
1079                 {
1080                         if (Classes[i]->GetCreator() == module)
1081                         {
1082                                 Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading");
1083                                 delete Classes[i];
1084                                 Classes[i] = NULL;
1085                         }
1086                 }
1087         }
1088 }
1089
1090 /** Generate pseudo-random number */
1091 unsigned long DNS::PRNG()
1092 {
1093         unsigned long val = 0;
1094         timeval n;
1095         serverstats* s = ServerInstance->stats;
1096         gettimeofday(&n,NULL);
1097         val = (n.tv_usec ^ (getpid() ^ geteuid()) ^ ((this->currid++)) ^ s->statsAccept) + n.tv_sec;
1098         val = val + (s->statsCollisions ^ s->statsDnsGood) - s->statsDnsBad;
1099         val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->ports.size();
1100         return val;
1101 }