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