]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/dns.cpp
Fix server prefix on JOIN.
[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 /** Initialise the DNS UDP socket so that we can send requests */
254 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
255 {
256         ServerInstance->Log(DEBUG,"DNS::DNS: Instance = %08x",Instance);
257
258         insp_inaddr addr;
259
260         /* Clear the Resolver class table */
261         memset(Classes,0,sizeof(Classes));
262
263         /* Clear the requests class table */
264         memset(requests,0,sizeof(requests));
265
266         /* Set the id of the next request to 0
267          */
268         currid = 0;
269
270         /* By default we're not munging ip's
271          */
272         ip6munge = false;
273
274         /* Clear the namesever address */
275         memset(&myserver,0,sizeof(insp_inaddr));
276
277         /* Convert the nameserver address into an insp_inaddr */
278         if (insp_aton(ServerInstance->Config->DNSServer,&addr) > 0)
279         {
280                 memcpy(&myserver,&addr,sizeof(insp_inaddr));
281                 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) ||  (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
282                 {
283                         /* These dont come back looking like they did when they went in.
284                          * We're forced to turn some checks off.
285                          * If anyone knows how to fix this, let me know. --Brain
286                          */
287                         ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
288                         ServerInstance->Log(DEFAULT,"         This should not cause a problem, however it is recommended you migrate");
289                         ServerInstance->Log(DEFAULT,"         to a true IPv6 environment.");
290                         this->ip6munge = true;
291                 }
292                 ServerInstance->Log(DEBUG,"Added nameserver '%s'",ServerInstance->Config->DNSServer);
293         }
294         else
295         {
296                 ServerInstance->Log(DEBUG,"GACK! insp_aton says the nameserver '%s' is invalid!",ServerInstance->Config->DNSServer);
297         }
298
299         /* Initialize mastersocket */
300         this->SetFd(socket(PF_PROTOCOL, SOCK_DGRAM, 0));
301         if (this->GetFd() != -1)
302         {
303                 /* Did it succeed? */
304                 if (fcntl(this->GetFd(), F_SETFL, O_NONBLOCK) != 0)
305                 {
306                         /* Couldn't make the socket nonblocking */
307                         shutdown(this->GetFd(),2);
308                         close(this->GetFd());
309                         this->SetFd(-1);
310                 }
311         }
312         else
313         {
314                 ServerInstance->Log(DEBUG,"I cant socket() this socket! (%s)",strerror(errno));
315         }
316         /* Have we got a socket and is it nonblocking? */
317         if (this->GetFd() != -1)
318         {
319 #ifdef IPV6
320                 insp_sockaddr addr;
321                 memset(&addr,0,sizeof(addr));
322                 addr.sin6_family = AF_FAMILY;
323                 addr.sin6_port = 0;
324                 addr.sin6_addr = in6addr_any;
325 #else
326                 insp_sockaddr addr;
327                 memset(&addr,0,sizeof(addr));
328                 addr.sin_family = AF_FAMILY;
329                 addr.sin_port = 0;
330                 addr.sin_addr.s_addr = INADDR_ANY;
331 #endif
332                 /* Bind the port */
333                 if (bind(this->GetFd(),(sockaddr *)&addr,sizeof(addr)) != 0)
334                 {
335                         /* Failed to bind */
336                         ServerInstance->Log(DEBUG,"Cant bind DNS fd");
337                         shutdown(this->GetFd(),2);
338                         close(this->GetFd());
339                         this->SetFd(-1);
340                 }
341
342                 if (this->GetFd() >= 0)
343                 {
344                         ServerInstance->Log(DEBUG,"Add master socket %d",this->GetFd());
345                         /* Hook the descriptor into the socket engine */
346                         if (ServerInstance && ServerInstance->SE)
347                         {
348                                 if (!ServerInstance->SE->AddFd(this))
349                                 {
350                                         ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
351                                         shutdown(this->GetFd(),2);
352                                         close(this->GetFd());
353                                         this->SetFd(-1);
354                                 }
355                         }
356                 }
357         }
358 }
359
360 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
361 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
362 {
363         short payloadpos = 0;
364         const char* tempchr, *tempchr2 = name;
365         unsigned short length;
366
367         /* split name up into labels, create query */
368         while ((tempchr = strchr(tempchr2,'.')) != NULL)
369         {
370                 length = tempchr - tempchr2;
371                 if (payloadpos + length + 1 > 507)
372                         return -1;
373                 payload[payloadpos++] = length;
374                 memcpy(&payload[payloadpos],tempchr2,length);
375                 payloadpos += length;
376                 tempchr2 = &tempchr[1];
377         }
378         length = strlen(tempchr2);
379         if (length)
380         {
381                 if (payloadpos + length + 2 > 507)
382                         return -1;
383                 payload[payloadpos++] = length;
384                 memcpy(&payload[payloadpos],tempchr2,length);
385                 payloadpos += length;
386                 payload[payloadpos++] = 0;
387         }
388         if (payloadpos > 508)
389                 return -1;
390         length = htons(rr);
391         memcpy(&payload[payloadpos],&length,2);
392         length = htons(rr_class);
393         memcpy(&payload[payloadpos + 2],&length,2);
394         return payloadpos + 4;
395 }
396
397 /** Start lookup of an hostname to an IP address */
398 int DNS::GetIP(const char *name)
399 {
400         DNSHeader h;
401         int id;
402         int length;
403         
404         if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
405                 return -1;
406
407         DNSRequest* req = this->AddQuery(&h, id);
408
409         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
410                 return -1;
411
412         return id;
413 }
414
415 /** Start lookup of an hostname to an IPv6 address */
416 int DNS::GetIP6(const char *name)
417 {
418         DNSHeader h;
419         int id;
420         int length;
421
422         if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
423                 return -1;
424
425         DNSRequest* req = this->AddQuery(&h, id);
426
427         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
428                 return -1;
429
430         return id;
431 }
432
433 /** Start lookup of a cname to another name */
434 int DNS::GetCName(const char *alias)
435 {
436         DNSHeader h;
437         int id;
438         int length;
439
440         if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
441                 return -1;
442
443         DNSRequest* req = this->AddQuery(&h, id);
444
445         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
446                 return -1;
447
448         return id;
449 }
450
451 /** Start lookup of an IP address to a hostname */
452 int DNS::GetName(const insp_inaddr *ip)
453 {
454         char query[128];
455         DNSHeader h;
456         int id;
457         int length;
458
459 #ifdef IPV6
460         unsigned char* c = (unsigned char*)&ip->s6_addr;
461         if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
462             c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
463             c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
464                 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
465         else
466                 DNS::MakeIP6Int(query, (in6_addr*)ip);
467 #else
468         unsigned char* c = (unsigned char*)&ip->s_addr;
469         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
470 #endif
471
472         if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
473                 return -1;
474
475         DNSRequest* req = this->AddQuery(&h, id);
476
477         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
478                 return -1;
479
480         return id;
481 }
482
483 /** Start lookup of an IP address to a hostname */
484 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
485 {
486         char query[128];
487         DNSHeader h;
488         int id;
489         int length;
490 #ifdef SUPPORT_IP6LINKS
491         if (fp == PROTOCOL_IPV6)
492         {
493                 in6_addr i;
494                 if (inet_pton(AF_INET6, ip, &i) > 0)
495                 {
496                         DNS::MakeIP6Int(query, &i);
497                 }
498                 else
499                         /* Invalid IP address */
500                         return -1;
501         }
502         else
503 #endif
504         {
505                 in_addr i;
506                 if (inet_aton(ip, &i))
507                 {
508                         unsigned char* c = (unsigned char*)&i.s_addr;
509                         sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
510                 }
511                 else
512                         /* Invalid IP address */
513                         return -1;
514         }
515
516         ServerInstance->Log(DEBUG,"DNS::GetNameForce: %s %d",query, fp);
517
518         if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
519                 return -1;
520
521         DNSRequest* req = this->AddQuery(&h, id);
522
523         if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
524                 return -1;
525
526         return id;
527 }
528
529 /** Build an ipv6 reverse domain from an in6_addr
530  */
531 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
532 {
533 #ifdef SUPPORT_IP6LINKS
534         const char* hex = "0123456789abcdef";
535         for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
536         {
537                 if (index % 2)
538                         /* low nibble */
539                         *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
540                 else
541                         /* high nibble */
542                         *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
543                 *query++ = '.'; /* Seperator */
544         }
545         strcpy(query,"ip6.arpa"); /* Suffix the string */
546 #else
547         *query = 0;
548 #endif
549 }
550
551 /** Return the next id which is ready, and the result attached to it */
552 DNSResult DNS::GetResult()
553 {
554         /* Fetch dns query response and decide where it belongs */
555         DNSHeader header;
556         DNSRequest *req;
557         unsigned char buffer[sizeof(DNSHeader)];
558         sockaddr from;
559         socklen_t x = sizeof(from);
560         const char* ipaddr_from = "";
561         unsigned short int port_from = 0;
562
563         int length = recvfrom(this->GetFd(),buffer,sizeof(DNSHeader),0,&from,&x);
564
565         if (length < 0)
566                 ServerInstance->Log(DEBUG,"Error in recvfrom()! (%s)",strerror(errno));
567
568         /* Did we get the whole header? */
569         if (length < 12)
570         {
571                 /* Nope - something screwed up. */
572                 ServerInstance->Log(DEBUG,"Whole header not read!");
573                 return std::make_pair(-1,"");
574         }
575
576         /* Check wether the reply came from a different DNS
577          * server to the one we sent it to, or the source-port
578          * is not 53.
579          * A user could in theory still spoof dns packets anyway
580          * but this is less trivial than just sending garbage
581          * to the client, which is possible without this check.
582          *
583          * -- Thanks jilles for pointing this one out.
584          */
585 #ifdef IPV6
586         ipaddr_from = insp_ntoa(((sockaddr_in6*)&from)->sin6_addr);
587         port_from = ntohs(((sockaddr_in6*)&from)->sin6_port);
588 #else
589         ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
590         port_from = ntohs(((sockaddr_in*)&from)->sin_port);
591 #endif
592
593         /* We cant perform this security check if you're using 4in6.
594          * Tough luck to you, choose one or't other!
595          */
596         if (!ip6munge)
597         {
598                 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
599                 {
600                         ServerInstance->Log(DEBUG,"port %d is not 53, or %s is not %s",port_from, ipaddr_from, ServerInstance->Config->DNSServer);
601                         return std::make_pair(-1,"");
602                 }
603         }
604
605         /* Put the read header info into a header class */
606         DNS::FillHeader(&header,buffer,length - 12);
607
608         /* Get the id of this request.
609          * Its a 16 bit value stored in two char's,
610          * so we use logic shifts to create the value.
611          */
612         unsigned long this_id = header.id[1] + (header.id[0] << 8);
613
614         /* Do we have a pending request matching this id? */
615         if (!requests[this_id])
616         {
617                 /* Somehow we got a DNS response for a request we never made... */
618                 ServerInstance->Log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",this->GetFd(),this_id);
619                 return std::make_pair(-1,"");
620         }
621         else
622         {
623                 /* Remove the query from the list of pending queries */
624                 req = requests[this_id];
625                 requests[this_id] = NULL;
626         }
627
628         /* Inform the DNSRequest class that it has a result to be read.
629          * When its finished it will return a DNSInfo which is a pair of
630          * unsigned char* resource record data, and an error message.
631          */
632         DNSInfo data = req->ResultIsReady(header, length);
633         std::string resultstr;
634
635         /* Check if we got a result, if we didnt, its an error */
636         if (data.first == NULL)
637         {
638                 /* An error.
639                  * Mask the ID with the value of ERROR_MASK, so that
640                  * the dns_deal_with_classes() function knows that its
641                  * an error response and needs to be treated uniquely.
642                  * Put the error message in the second field.
643                  */
644                 delete req;
645                 return std::make_pair(this_id | ERROR_MASK, data.second);
646         }
647         else
648         {
649                 char formatted[128];
650
651                 /* Forward lookups come back as binary data. We must format them into ascii */
652                 switch (req->type)
653                 {
654                         case DNS_QUERY_A:
655                                 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
656                                 resultstr = formatted;
657                         break;
658
659                         case DNS_QUERY_AAAA:
660                         {
661                                 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
662                                                 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
663                                                 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
664                                                 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
665                                                 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
666                                                 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
667                                                 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
668                                                 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
669                                                 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
670                                 char* c = strstr(formatted,":0:");
671                                 if (c != NULL)
672                                 {
673                                         memmove(c+1,c+2,strlen(c+2) + 1);
674                                         c += 2;
675                                         while (memcmp(c,"0:",2) == 0)
676                                                 memmove(c,c+2,strlen(c+2) + 1);
677                                         if (memcmp(c,"0",2) == 0)
678                                                 *c = 0;
679                                         if (memcmp(formatted,"0::",3) == 0)
680                                                 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
681                                 }
682                                 resultstr = formatted;
683
684                                 /* Special case. Sending ::1 around between servers
685                                  * and to clients is dangerous, because the : on the
686                                  * start makes the client or server interpret the IP
687                                  * as the last parameter on the line with a value ":1".
688                                  */
689                                 if (*formatted == ':')
690                                         resultstr = "0" + resultstr;
691                         }
692                         break;
693
694                         case DNS_QUERY_CNAME:
695                                 /* Identical handling to PTR */
696
697                         case DNS_QUERY_PTR:
698                                 /* Reverse lookups just come back as char* */
699                                 resultstr = std::string((const char*)data.first);
700                         break;
701
702                         default:
703                                 ServerInstance->Log(DEBUG,"WARNING: Somehow we made a request for a DNS_QUERY_PTR4 or DNS_QUERY_PTR6, but these arent real rr types!");
704                         break;
705                         
706                 }
707
708                 /* Build the reply with the id and hostname/ip in it */
709                 delete req;
710                 return std::make_pair(this_id,resultstr);
711         }
712 }
713
714 /** A result is ready, process it */
715 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
716 {
717         int i = 0;
718         int q = 0;
719         int curanswer, o;
720         ResourceRecord rr;
721         unsigned short ptr;
722
723         /* This is just to keep _FORTIFY_SOURCE happy */
724         rr.type = DNS_QUERY_NONE;
725         rr.rdlength = 0;
726
727         if (!(header.flags1 & FLAGS_MASK_QR))
728                 return std::make_pair((unsigned char*)NULL,"Not a query result");
729
730         if (header.flags1 & FLAGS_MASK_OPCODE)
731                 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
732
733         if (header.flags2 & FLAGS_MASK_RCODE)
734                 return std::make_pair((unsigned char*)NULL,"Domain name not found");
735
736         if (header.ancount < 1)
737                 return std::make_pair((unsigned char*)NULL,"No resource records returned");
738
739         /* Subtract the length of the header from the length of the packet */
740         length -= 12;
741
742         while ((unsigned int)q < header.qdcount && i < length)
743         {
744                 if (header.payload[i] > 63)
745                 {
746                         i += 6;
747                         q++;
748                 }
749                 else
750                 {
751                         if (header.payload[i] == 0)
752                         {
753                                 q++;
754                                 i += 5;
755                         }
756                         else i += header.payload[i] + 1;
757                 }
758         }
759         curanswer = 0;
760         while ((unsigned)curanswer < header.ancount)
761         {
762                 q = 0;
763                 while (q == 0 && i < length)
764                 {
765                         if (header.payload[i] > 63)
766                         {
767                                 i += 2;
768                                 q = 1;
769                         }
770                         else
771                         {
772                                 if (header.payload[i] == 0)
773                                 {
774                                         i++;
775                                         q = 1;
776                                 }
777                                 else i += header.payload[i] + 1; /* skip length and label */
778                         }
779                 }
780                 if (length - i < 10)
781                         return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
782
783                 DNS::FillResourceRecord(&rr,&header.payload[i]);
784                 i += 10;
785                 if (rr.type != this->type)
786                 {
787                         curanswer++;
788                         i += rr.rdlength;
789                         continue;
790                 }
791                 if (rr.rr_class != this->rr_class)
792                 {
793                         curanswer++;
794                         i += rr.rdlength;
795                         continue;
796                 }
797                 break;
798         }
799         if ((unsigned int)curanswer == header.ancount)
800                 return std::make_pair((unsigned char*)NULL,"No valid answers");
801
802         if (i + rr.rdlength > (unsigned int)length)
803                 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
804
805         if (rr.rdlength > 1023)
806                 return std::make_pair((unsigned char*)NULL,"Resource record too large");
807
808         switch (rr.type)
809         {
810                 case DNS_QUERY_CNAME:
811                         /* CNAME and PTR have the same processing code */
812                 case DNS_QUERY_PTR:
813                         o = 0;
814                         q = 0;
815                         while (q == 0 && i < length && o + 256 < 1023)
816                         {
817                                 if (header.payload[i] > 63)
818                                 {
819                                         memcpy(&ptr,&header.payload[i],2);
820                                         i = ntohs(ptr) - 0xC000 - 12;
821                                 }
822                                 else
823                                 {
824                                         if (header.payload[i] == 0)
825                                         {
826                                                 q = 1;
827                                         }
828                                         else
829                                         {
830                                                 res[o] = 0;
831                                                 if (o != 0)
832                                                         res[o++] = '.';
833                                                 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
834                                                 o += header.payload[i];
835                                                 i += header.payload[i] + 1;
836                                         }
837                                 }
838                         }
839                         res[o] = 0;
840                 break;
841                 case DNS_QUERY_AAAA:
842                         memcpy(res,&header.payload[i],rr.rdlength);
843                         res[rr.rdlength] = 0;
844                 break;
845                 case DNS_QUERY_A:
846                         memcpy(res,&header.payload[i],rr.rdlength);
847                         res[rr.rdlength] = 0;
848                 break;
849                 default:
850                         memcpy(res,&header.payload[i],rr.rdlength);
851                         res[rr.rdlength] = 0;
852                 break;
853         }
854         return std::make_pair(res,"No error");;
855 }
856
857 /** Close the master socket */
858 DNS::~DNS()
859 {
860         shutdown(this->GetFd(), 2);
861         close(this->GetFd());
862 }
863
864 /** High level abstraction of dns used by application at large */
865 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
866 {
867         ServerInstance->Log(DEBUG,"Instance: %08x %08x",Instance, ServerInstance);
868
869         insp_inaddr binip;
870
871         switch (querytype)
872         {
873                 case DNS_QUERY_A:
874                         this->myid = ServerInstance->Res->GetIP(source.c_str());
875                 break;
876
877                 case DNS_QUERY_PTR:
878                         if (insp_aton(source.c_str(), &binip) > 0)
879                         {
880                                 /* Valid ip address */
881                                 this->myid = ServerInstance->Res->GetName(&binip);
882                         }
883                         else
884                         {
885                                 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
886                                 throw ModuleException("Resolver: Bad IP address");
887                                 return;
888                         }
889                 break;
890
891                 case DNS_QUERY_PTR4:
892                         querytype = DNS_QUERY_PTR;
893                         this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
894                 break;
895
896                 case DNS_QUERY_PTR6:
897                         querytype = DNS_QUERY_PTR;
898                         this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
899                 break;
900
901                 case DNS_QUERY_AAAA:
902                         this->myid = ServerInstance->Res->GetIP6(source.c_str());
903                 break;
904
905                 case DNS_QUERY_CNAME:
906                         this->myid = ServerInstance->Res->GetCName(source.c_str());
907                 break;
908
909                 default:
910                         this->myid = -1;
911                 break;
912         }
913         if (this->myid == -1)
914         {
915                 ServerInstance->Log(DEBUG,"Resolver::Resolver: Could not get an id!");
916                 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
917                 throw ModuleException("Resolver: Couldnt get an id to make a request");
918                 /* We shouldnt get here really */
919                 return;
920         }
921
922         ServerInstance->Log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
923 }
924
925 /** Called when an error occurs */
926 void Resolver::OnError(ResolverError e, const std::string &errormessage)
927 {
928         /* Nothing in here */
929 }
930
931 /** Destroy a resolver */
932 Resolver::~Resolver()
933 {
934         /* Nothing here (yet) either */
935 }
936
937 /** Get the request id associated with this class */
938 int Resolver::GetId()
939 {
940         return this->myid;
941 }
942
943 Module* Resolver::GetCreator()
944 {
945         return this->Creator;
946 }
947
948 /** Process a socket read event */
949 void DNS::HandleEvent(EventType et, int errornum)
950 {
951         /* Fetch the id and result of the next available packet */
952         DNSResult res = this->GetResult();
953         /* Is there a usable request id? */
954         if (res.first != -1)
955         {
956                 /* Its an error reply */
957                 if (res.first & ERROR_MASK)
958                 {
959                         /* Mask off the error bit */
960                         res.first -= ERROR_MASK;
961                         /* Marshall the error to the correct class */
962                         ServerInstance->Log(DEBUG,"Error available, id=%d",res.first);
963                         if (Classes[res.first])
964                         {
965                                 if (ServerInstance && ServerInstance->stats)
966                                         ServerInstance->stats->statsDnsBad++;
967                                 Classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
968                                 delete Classes[res.first];
969                                 Classes[res.first] = NULL;
970                         }
971                 }
972                 else
973                 {
974                         /* It is a non-error result */
975                         ServerInstance->Log(DEBUG,"Result available, id=%d",res.first);
976                         /* Marshall the result to the correct class */
977                         if (Classes[res.first])
978                         {
979                                 if (ServerInstance && ServerInstance->stats)
980                                         ServerInstance->stats->statsDnsGood++;
981                                 Classes[res.first]->OnLookupComplete(res.second);
982                                 delete Classes[res.first];
983                                 Classes[res.first] = NULL;
984                         }
985                 }
986
987                 if (ServerInstance && ServerInstance->stats)
988                         ServerInstance->stats->statsDns++;
989         }
990 }
991
992 /** Add a derived Resolver to the working set */
993 bool DNS::AddResolverClass(Resolver* r)
994 {
995         /* Check the pointers validity and the id's validity */
996         if ((r) && (r->GetId() > -1))
997         {
998                 /* Check the slot isnt already occupied -
999                  * This should NEVER happen unless we have
1000                  * a severely broken DNS server somewhere
1001                  */
1002                 if (!Classes[r->GetId()])
1003                 {
1004                         /* Set up the pointer to the class */
1005                         Classes[r->GetId()] = r;
1006                         return true;
1007                 }
1008                 else
1009                         /* Duplicate id */
1010                         return false;
1011         }
1012         else
1013         {
1014                 /* Pointer or id not valid.
1015                  * Free the item and return
1016                  */
1017                 if (r)
1018                         delete r;
1019
1020                 return false;
1021         }
1022 }
1023
1024 void DNS::CleanResolvers(Module* module)
1025 {
1026         for (int i = 0; i < MAX_REQUEST_ID; i++)
1027         {
1028                 if (Classes[i])
1029                 {
1030                         if (Classes[i]->GetCreator() == module)
1031                         {
1032                                 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1033                                 delete Classes[i];
1034                                 Classes[i] = NULL;
1035                         }
1036                 }
1037         }
1038 }
1039
1040 /** Generate pseudo-random number */
1041 unsigned long DNS::PRNG()
1042 {
1043         unsigned long val = 0;
1044         timeval n;
1045         serverstats* s = ServerInstance->stats;
1046         gettimeofday(&n,NULL);
1047         val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1048         val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1049         val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - s->BoundPortCount;
1050         return val;
1051 }
1052