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