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