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