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