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