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