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