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