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