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