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