1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2007 InspIRCd Development Team
6 * See: http://www.inspircd.org/wiki/index.php/Credits
8 * This program is free but copyrighted software; see
9 * the file COPYING for details.
11 * ---------------------------------------------------
15 dns.cpp - Nonblocking DNS functions.
16 Very very loosely based on the firedns library,
17 Copyright (C) 2002 Ian Gulliver. This file is no
18 longer anything like firedns, there are many major
19 differences between this code and the original.
20 Please do not assume that firedns works like this,
21 looks like this, walks like this or tastes like this.
25 #include <sys/types.h>
26 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
31 #include "inspircd_win32wrapper.h"
32 #include "inspircd_se_config.h"
37 #include "socketengine.h"
38 #include "configreader.h"
41 using irc::sockets::insp_inaddr;
42 using irc::sockets::insp_ntoa;
43 using irc::sockets::insp_aton;
44 using irc::sockets::OpenTCPSocket;
46 /** Masks to mask off the responses we get from the DNSRequest methods
50 ERROR_MASK = 0x10000 /* Result is an error */
53 /** Flags which can be ORed into a request or reply for different meanings
57 FLAGS_MASK_RD = 0x01, /* Recursive */
59 FLAGS_MASK_AA = 0x04, /* Authoritative */
60 FLAGS_MASK_OPCODE = 0x78,
62 FLAGS_MASK_RCODE = 0x0F, /* Request */
68 /** Represents a dns resource record (rr)
72 QueryType type; /* Record type */
73 unsigned int rr_class; /* Record class */
74 unsigned long ttl; /* Time to live */
75 unsigned int rdlength; /* Record length */
78 /** Represents a dns request/reply header, and its payload as opaque data.
83 unsigned char id[2]; /* Request id */
84 unsigned int flags1; /* Flags */
85 unsigned int flags2; /* Flags */
87 unsigned int ancount; /* Answer count */
88 unsigned int nscount; /* Nameserver count */
90 unsigned char payload[512]; /* Packet payload */
96 unsigned char id[2]; /* Request id */
97 unsigned char* res; /* Result processing buffer */
98 unsigned int rr_class; /* Request class */
99 QueryType type; /* Request type */
100 DNS* dnsobj; /* DNS caller (where we get our FD from) */
101 unsigned long ttl; /* Time to live */
102 std::string orig; /* Original requested name/ip */
104 DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
106 DNSInfo ResultIsReady(DNSHeader &h, int length);
107 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
110 class CacheTimer : public InspTimer
113 InspIRCd* ServerInstance;
116 CacheTimer(InspIRCd* Instance, DNS* thisdns)
117 : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
119 virtual void Tick(time_t TIME)
125 class RequestTimeout : public InspTimer
127 InspIRCd* ServerInstance;
131 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
135 void Tick(time_t TIME)
137 if (ServerInstance->Res->requests[watchid] == watch)
139 /* Still exists, whack it */
140 if (ServerInstance->Res->Classes[watchid])
142 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
143 delete ServerInstance->Res->Classes[watchid];
144 ServerInstance->Res->Classes[watchid] = NULL;
146 ServerInstance->Res->requests[watchid] = NULL;
153 /* Allocate the processing buffer */
154 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns)
156 res = new unsigned char[512];
159 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
160 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
163 /* Deallocate the processing buffer */
164 DNSRequest::~DNSRequest()
169 /** Fill a ResourceRecord class based on raw data input */
170 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
172 rr->type = (QueryType)((input[0] << 8) + input[1]);
173 rr->rr_class = (input[2] << 8) + input[3];
174 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
175 rr->rdlength = (input[8] << 8) + input[9];
178 /** Fill a DNSHeader class based on raw data input of a given length */
179 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
181 header->id[0] = input[0];
182 header->id[1] = input[1];
183 header->flags1 = input[2];
184 header->flags2 = input[3];
185 header->qdcount = (input[4] << 8) + input[5];
186 header->ancount = (input[6] << 8) + input[7];
187 header->nscount = (input[8] << 8) + input[9];
188 header->arcount = (input[10] << 8) + input[11];
189 memcpy(header->payload,&input[12],length);
192 /** Empty a DNSHeader class out into raw data, ready for transmission */
193 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
195 output[0] = header->id[0];
196 output[1] = header->id[1];
197 output[2] = header->flags1;
198 output[3] = header->flags2;
199 output[4] = header->qdcount >> 8;
200 output[5] = header->qdcount & 0xFF;
201 output[6] = header->ancount >> 8;
202 output[7] = header->ancount & 0xFF;
203 output[8] = header->nscount >> 8;
204 output[9] = header->nscount & 0xFF;
205 output[10] = header->arcount >> 8;
206 output[11] = header->arcount & 0xFF;
207 memcpy(&output[12],header->payload,length);
210 /** Send requests we have previously built down the UDP socket */
211 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
213 unsigned char payload[sizeof(DNSHeader)];
218 DNS::EmptyHeader(payload,header,length);
221 if (this->dnsobj->socketfamily == AF_INET6)
224 memset(&addr,0,sizeof(addr));
225 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
226 addr.sin6_family = AF_INET6;
227 addr.sin6_port = htons(DNS::QUERY_PORT);
228 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
234 memset(&addr,0,sizeof(addr));
235 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
236 addr.sin_family = AF_INET;
237 addr.sin_port = htons(DNS::QUERY_PORT);
238 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
243 memset(&addr,0,sizeof(addr));
244 memcpy(&addr.sin_addr.s_addr, &dnsobj->myserver4, sizeof(addr.sin_addr));
245 addr.sin_family = AF_INET;
246 addr.sin_port = htons(DNS::QUERY_PORT);
247 if (sendto(dnsobj->GetFd(), (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
254 /** Add a query with a predefined header, and allocate an ID for it. */
255 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
257 /* Is the DNS connection down? */
258 if (this->GetFd() == -1)
262 id = this->PRNG() & DNS::MAX_REQUEST_ID;
264 /* If this id is already 'in flight', pick another. */
266 id = this->PRNG() & DNS::MAX_REQUEST_ID;
268 DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
270 header->id[0] = req->id[0] = id >> 8;
271 header->id[1] = req->id[1] = id & 0xFF;
272 header->flags1 = FLAGS_MASK_RD;
279 /* At this point we already know the id doesnt exist,
280 * so there needs to be no second check for the ::end()
284 /* According to the C++ spec, new never returns NULL. */
288 int DNS::ClearCache()
290 /* This ensures the buckets are reset to sane levels */
291 int rv = this->cache->size();
293 this->cache = new dnscache();
297 int DNS::PruneCache()
300 dnscache* newcache = new dnscache();
301 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
302 /* Dont include expired items (theres no point) */
303 if (i->second.CalcTTLRemaining())
304 newcache->insert(*i);
309 this->cache = newcache;
318 if (this->GetFd() > -1)
320 if (ServerInstance && ServerInstance->SE)
321 ServerInstance->SE->DelFd(this);
322 shutdown(this->GetFd(), 2);
323 close(this->GetFd());
326 /* Rehash the cache */
331 /* Create initial dns cache */
332 this->cache = new dnscache();
335 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
337 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
338 ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
339 ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
340 this->ip6munge = true;
343 this->socketfamily = AF_INET;
345 if (strchr(ServerInstance->Config->DNSServer,':'))
347 this->socketfamily = AF_INET6;
348 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
352 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
356 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
359 /* Initialize mastersocket */
360 int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
363 /* Have we got a socket and is it nonblocking? */
364 if (this->GetFd() != -1)
366 /* Bind the port - port 0 INADDR_ANY */
367 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
370 shutdown(this->GetFd(),2);
371 close(this->GetFd());
375 if (this->GetFd() >= 0)
377 /* Hook the descriptor into the socket engine */
378 if (ServerInstance && ServerInstance->SE)
380 if (!ServerInstance->SE->AddFd(this))
382 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
383 shutdown(this->GetFd(),2);
384 close(this->GetFd());
392 /** Initialise the DNS UDP socket so that we can send requests */
393 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
395 /* Clear the Resolver class table */
396 memset(Classes,0,sizeof(Classes));
398 /* Clear the requests class table */
399 memset(requests,0,sizeof(requests));
401 /* Set the id of the next request to 0
405 /* DNS::Rehash() sets this to a valid ptr
409 /* Again, DNS::Rehash() sets this to a
414 /* Actually read the settings
418 this->PruneTimer = new CacheTimer(ServerInstance, this);
420 ServerInstance->Timers->AddTimer(this->PruneTimer);
423 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
424 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
426 short payloadpos = 0;
427 const char* tempchr, *tempchr2 = name;
428 unsigned short length;
430 /* split name up into labels, create query */
431 while ((tempchr = strchr(tempchr2,'.')) != NULL)
433 length = tempchr - tempchr2;
434 if (payloadpos + length + 1 > 507)
436 payload[payloadpos++] = length;
437 memcpy(&payload[payloadpos],tempchr2,length);
438 payloadpos += length;
439 tempchr2 = &tempchr[1];
441 length = strlen(tempchr2);
444 if (payloadpos + length + 2 > 507)
446 payload[payloadpos++] = length;
447 memcpy(&payload[payloadpos],tempchr2,length);
448 payloadpos += length;
449 payload[payloadpos++] = 0;
451 if (payloadpos > 508)
454 memcpy(&payload[payloadpos],&length,2);
455 length = htons(rr_class);
456 memcpy(&payload[payloadpos + 2],&length,2);
457 return payloadpos + 4;
460 /** Start lookup of an hostname to an IP address */
461 int DNS::GetIP(const char *name)
467 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
470 DNSRequest* req = this->AddQuery(&h, id, name);
472 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
478 /** Start lookup of an hostname to an IPv6 address */
479 int DNS::GetIP6(const char *name)
485 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
488 DNSRequest* req = this->AddQuery(&h, id, name);
490 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
496 /** Start lookup of a cname to another name */
497 int DNS::GetCName(const char *alias)
503 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
506 DNSRequest* req = this->AddQuery(&h, id, alias);
508 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
514 /** Start lookup of an IP address to a hostname */
515 int DNS::GetName(const insp_inaddr *ip)
523 unsigned char* c = (unsigned char*)&ip->s6_addr;
524 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
525 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
526 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
527 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
529 DNS::MakeIP6Int(query, (in6_addr*)ip);
531 unsigned char* c = (unsigned char*)&ip->s_addr;
532 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
535 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
538 DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
540 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
546 /** Start lookup of an IP address to a hostname */
547 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
553 #ifdef SUPPORT_IP6LINKS
554 if (fp == PROTOCOL_IPV6)
557 if (inet_pton(AF_INET6, ip, &i) > 0)
559 DNS::MakeIP6Int(query, &i);
562 /* Invalid IP address */
569 if (inet_aton(ip, &i))
571 unsigned char* c = (unsigned char*)&i.s_addr;
572 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
575 /* Invalid IP address */
579 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
582 DNSRequest* req = this->AddQuery(&h, id, ip);
584 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
590 /** Build an ipv6 reverse domain from an in6_addr
592 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
594 #ifdef SUPPORT_IP6LINKS
595 const char* hex = "0123456789abcdef";
596 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
600 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
603 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
604 *query++ = '.'; /* Seperator */
606 strcpy(query,"ip6.arpa"); /* Suffix the string */
612 /** Return the next id which is ready, and the result attached to it */
613 DNSResult DNS::GetResult()
615 /* Fetch dns query response and decide where it belongs */
618 unsigned char buffer[sizeof(DNSHeader)];
619 sockaddr* from = new sockaddr[2];
621 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
623 socklen_t x = sizeof(sockaddr_in);
625 const char* ipaddr_from;
626 unsigned short int port_from = 0;
628 /** Dirty hack for IOCP UDP sockets **/
629 udp_overlap * ov = ((IOCPEngine*)ServerInstance->SE)->udp_ov;
630 memcpy(buffer, ov->udp_buffer, ov->udp_len);
631 memcpy(from, ov->udp_sockaddr, ov->udp_sockaddr_len);
632 int length = ov->udp_len;
634 int length = recvfrom(this->GetFd(),(char*)buffer,sizeof(DNSHeader),0,from,&x);
637 /* Did we get the whole header? */
640 /* Nope - something screwed up. */
642 return DNSResult(-1,"",0,"");
645 /* Check wether the reply came from a different DNS
646 * server to the one we sent it to, or the source-port
648 * A user could in theory still spoof dns packets anyway
649 * but this is less trivial than just sending garbage
650 * to the client, which is possible without this check.
652 * -- Thanks jilles for pointing this one out.
656 if (this->socketfamily == AF_INET6)
658 ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
659 port_from = ntohs(((sockaddr_in6*)from)->sin6_port);
664 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
665 port_from = ntohs(((sockaddr_in*)from)->sin_port);
670 /* We cant perform this security check if you're using 4in6.
671 * Tough luck to you, choose one or't other!
675 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
677 return DNSResult(-1,"",0,"");
681 /* Put the read header info into a header class */
682 DNS::FillHeader(&header,buffer,length - 12);
684 /* Get the id of this request.
685 * Its a 16 bit value stored in two char's,
686 * so we use logic shifts to create the value.
688 unsigned long this_id = header.id[1] + (header.id[0] << 8);
690 /* Do we have a pending request matching this id? */
691 if (!requests[this_id])
693 /* Somehow we got a DNS response for a request we never made... */
694 return DNSResult(-1,"",0,"");
698 /* Remove the query from the list of pending queries */
699 req = requests[this_id];
700 requests[this_id] = NULL;
703 /* Inform the DNSRequest class that it has a result to be read.
704 * When its finished it will return a DNSInfo which is a pair of
705 * unsigned char* resource record data, and an error message.
707 DNSInfo data = req->ResultIsReady(header, length);
708 std::string resultstr;
710 /* Check if we got a result, if we didnt, its an error */
711 if (data.first == NULL)
714 * Mask the ID with the value of ERROR_MASK, so that
715 * the dns_deal_with_classes() function knows that its
716 * an error response and needs to be treated uniquely.
717 * Put the error message in the second field.
719 std::string ro = req->orig;
721 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
725 unsigned long ttl = req->ttl;
728 /* Forward lookups come back as binary data. We must format them into ascii */
732 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
733 resultstr = formatted;
738 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
739 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
740 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
741 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
742 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
743 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
744 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
745 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
746 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
747 char* c = strstr(formatted,":0:");
750 memmove(c+1,c+2,strlen(c+2) + 1);
752 while (memcmp(c,"0:",2) == 0)
753 memmove(c,c+2,strlen(c+2) + 1);
754 if (memcmp(c,"0",2) == 0)
756 if (memcmp(formatted,"0::",3) == 0)
757 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
759 resultstr = formatted;
761 /* Special case. Sending ::1 around between servers
762 * and to clients is dangerous, because the : on the
763 * start makes the client or server interpret the IP
764 * as the last parameter on the line with a value ":1".
766 if (*formatted == ':')
767 resultstr = "0" + resultstr;
771 case DNS_QUERY_CNAME:
772 /* Identical handling to PTR */
775 /* Reverse lookups just come back as char* */
776 resultstr = std::string((const char*)data.first);
784 /* Build the reply with the id and hostname/ip in it */
785 std::string ro = req->orig;
787 return DNSResult(this_id,resultstr,ttl,ro);
791 /** A result is ready, process it */
792 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
800 /* This is just to keep _FORTIFY_SOURCE happy */
801 rr.type = DNS_QUERY_NONE;
803 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
805 if (!(header.flags1 & FLAGS_MASK_QR))
806 return std::make_pair((unsigned char*)NULL,"Not a query result");
808 if (header.flags1 & FLAGS_MASK_OPCODE)
809 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
811 if (header.flags2 & FLAGS_MASK_RCODE)
812 return std::make_pair((unsigned char*)NULL,"Domain name not found");
814 if (header.ancount < 1)
815 return std::make_pair((unsigned char*)NULL,"No resource records returned");
817 /* Subtract the length of the header from the length of the packet */
820 while ((unsigned int)q < header.qdcount && i < length)
822 if (header.payload[i] > 63)
829 if (header.payload[i] == 0)
834 else i += header.payload[i] + 1;
838 while ((unsigned)curanswer < header.ancount)
841 while (q == 0 && i < length)
843 if (header.payload[i] > 63)
850 if (header.payload[i] == 0)
855 else i += header.payload[i] + 1; /* skip length and label */
859 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
861 /* XXX: We actually initialise 'rr' here including its ttl field */
862 DNS::FillResourceRecord(&rr,&header.payload[i]);
864 if (rr.type != this->type)
870 if (rr.rr_class != this->rr_class)
878 if ((unsigned int)curanswer == header.ancount)
879 return std::make_pair((unsigned char*)NULL,"No valid answers");
881 if (i + rr.rdlength > (unsigned int)length)
882 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
884 if (rr.rdlength > 1023)
885 return std::make_pair((unsigned char*)NULL,"Resource record too large");
891 case DNS_QUERY_CNAME:
892 /* CNAME and PTR have the same processing code */
896 while (q == 0 && i < length && o + 256 < 1023)
898 if (header.payload[i] > 63)
900 memcpy(&ptr,&header.payload[i],2);
901 i = ntohs(ptr) - 0xC000 - 12;
905 if (header.payload[i] == 0)
914 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
915 o += header.payload[i];
916 i += header.payload[i] + 1;
923 memcpy(res,&header.payload[i],rr.rdlength);
924 res[rr.rdlength] = 0;
927 memcpy(res,&header.payload[i],rr.rdlength);
928 res[rr.rdlength] = 0;
931 memcpy(res,&header.payload[i],rr.rdlength);
932 res[rr.rdlength] = 0;
935 return std::make_pair(res,"No error");;
938 /** Close the master socket */
941 shutdown(this->GetFd(), 2);
942 close(this->GetFd());
943 ServerInstance->Timers->DelTimer(this->PruneTimer);
944 delete this->PruneTimer;
947 CachedQuery* DNS::GetCache(const std::string &source)
949 dnscache::iterator x = cache->find(source.c_str());
950 if (x != cache->end())
956 void DNS::DelCache(const std::string &source)
958 cache->erase(source.c_str());
961 void Resolver::TriggerCachedResult()
964 OnLookupComplete(CQ->data, time_left, true);
967 /** High level abstraction of dns used by application at large */
968 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
972 CQ = ServerInstance->Res->GetCache(source);
975 time_left = CQ->CalcTTLRemaining();
978 ServerInstance->Res->DelCache(source);
992 this->myid = ServerInstance->Res->GetIP(source.c_str());
996 if (insp_aton(source.c_str(), &binip) > 0)
998 /* Valid ip address */
999 this->myid = ServerInstance->Res->GetName(&binip);
1003 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
1004 throw ModuleException("Resolver: Bad IP address");
1009 case DNS_QUERY_PTR4:
1010 querytype = DNS_QUERY_PTR;
1011 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
1014 case DNS_QUERY_PTR6:
1015 querytype = DNS_QUERY_PTR;
1016 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
1019 case DNS_QUERY_AAAA:
1020 this->myid = ServerInstance->Res->GetIP6(source.c_str());
1023 case DNS_QUERY_CNAME:
1024 this->myid = ServerInstance->Res->GetCName(source.c_str());
1031 if (this->myid == -1)
1033 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1034 throw ModuleException("Resolver: Couldnt get an id to make a request");
1035 /* We shouldnt get here really */
1040 /** Called when an error occurs */
1041 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1043 /* Nothing in here */
1046 /** Destroy a resolver */
1047 Resolver::~Resolver()
1049 /* Nothing here (yet) either */
1052 /** Get the request id associated with this class */
1053 int Resolver::GetId()
1058 Module* Resolver::GetCreator()
1060 return this->Creator;
1063 /** Process a socket read event */
1064 void DNS::HandleEvent(EventType et, int errornum)
1066 /* Fetch the id and result of the next available packet */
1067 DNSResult res = this->GetResult();
1068 /* Is there a usable request id? */
1071 /* Its an error reply */
1072 if (res.id & ERROR_MASK)
1074 /* Mask off the error bit */
1075 res.id -= ERROR_MASK;
1076 /* Marshall the error to the correct class */
1077 if (Classes[res.id])
1079 if (ServerInstance && ServerInstance->stats)
1080 ServerInstance->stats->statsDnsBad++;
1081 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1082 delete Classes[res.id];
1083 Classes[res.id] = NULL;
1088 /* It is a non-error result, marshall the result to the correct class */
1089 if (Classes[res.id])
1091 if (ServerInstance && ServerInstance->stats)
1092 ServerInstance->stats->statsDnsGood++;
1094 if (!this->GetCache(res.original.c_str()))
1095 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1097 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1098 delete Classes[res.id];
1099 Classes[res.id] = NULL;
1103 if (ServerInstance && ServerInstance->stats)
1104 ServerInstance->stats->statsDns++;
1108 /** Add a derived Resolver to the working set */
1109 bool DNS::AddResolverClass(Resolver* r)
1111 /* Check the pointers validity and the id's validity */
1112 if ((r) && (r->GetId() > -1))
1114 /* Check the slot isnt already occupied -
1115 * This should NEVER happen unless we have
1116 * a severely broken DNS server somewhere
1118 if (!Classes[r->GetId()])
1120 /* Set up the pointer to the class */
1121 Classes[r->GetId()] = r;
1130 /* Pointer or id not valid.
1131 * Free the item and return
1140 void DNS::CleanResolvers(Module* module)
1142 for (int i = 0; i < MAX_REQUEST_ID; i++)
1146 if (Classes[i]->GetCreator() == module)
1148 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1156 /** Generate pseudo-random number */
1157 unsigned long DNS::PRNG()
1160 unsigned long val = 0;
1162 serverstats* s = ServerInstance->stats;
1163 gettimeofday(&n,NULL);
1164 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1165 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1166 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size();
1169 unsigned long val = 0;
1170 serverstats* s = ServerInstance->stats;
1171 val = (time(NULL) ^ GetCurrentProcessId() ^ GetCurrentThreadId() ^ (this->currid++)) ^ s->statsAccept + time(NULL);
1172 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1173 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv);