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.
24 #include <sys/types.h>
25 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
31 #include "socketengine.h"
32 #include "configreader.h"
35 using irc::sockets::insp_inaddr;
36 using irc::sockets::insp_ntoa;
37 using irc::sockets::insp_aton;
38 using irc::sockets::OpenTCPSocket;
40 /** Masks to mask off the responses we get from the DNSRequest methods
44 ERROR_MASK = 0x10000 /* Result is an error */
47 /** Flags which can be ORed into a request or reply for different meanings
51 FLAGS_MASK_RD = 0x01, /* Recursive */
53 FLAGS_MASK_AA = 0x04, /* Authoritative */
54 FLAGS_MASK_OPCODE = 0x78,
56 FLAGS_MASK_RCODE = 0x0F, /* Request */
62 /** Represents a dns resource record (rr)
66 QueryType type; /* Record type */
67 unsigned int rr_class; /* Record class */
68 unsigned long ttl; /* Time to live */
69 unsigned int rdlength; /* Record length */
72 /** Represents a dns request/reply header, and its payload as opaque data.
77 unsigned char id[2]; /* Request id */
78 unsigned int flags1; /* Flags */
79 unsigned int flags2; /* Flags */
81 unsigned int ancount; /* Answer count */
82 unsigned int nscount; /* Nameserver count */
84 unsigned char payload[512]; /* Packet payload */
90 unsigned char id[2]; /* Request id */
91 unsigned char* res; /* Result processing buffer */
92 unsigned int rr_class; /* Request class */
93 QueryType type; /* Request type */
94 DNS* dnsobj; /* DNS caller (where we get our FD from) */
95 unsigned long ttl; /* Time to live */
96 std::string orig; /* Original requested name/ip */
98 DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
100 DNSInfo ResultIsReady(DNSHeader &h, int length);
101 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
104 class CacheTimer : public InspTimer
107 InspIRCd* ServerInstance;
110 CacheTimer(InspIRCd* Instance, DNS* thisdns)
111 : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
113 virtual void Tick(time_t TIME)
119 class RequestTimeout : public InspTimer
121 InspIRCd* ServerInstance;
125 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
129 void Tick(time_t TIME)
131 if (ServerInstance->Res->requests[watchid] == watch)
133 /* Still exists, whack it */
134 if (ServerInstance->Res->Classes[watchid])
136 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
137 delete ServerInstance->Res->Classes[watchid];
138 ServerInstance->Res->Classes[watchid] = NULL;
140 ServerInstance->Res->requests[watchid] = NULL;
147 /* Allocate the processing buffer */
148 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns)
150 res = new unsigned char[512];
153 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
154 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
157 /* Deallocate the processing buffer */
158 DNSRequest::~DNSRequest()
163 /** Fill a ResourceRecord class based on raw data input */
164 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
166 rr->type = (QueryType)((input[0] << 8) + input[1]);
167 rr->rr_class = (input[2] << 8) + input[3];
168 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
169 rr->rdlength = (input[8] << 8) + input[9];
172 /** Fill a DNSHeader class based on raw data input of a given length */
173 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
175 header->id[0] = input[0];
176 header->id[1] = input[1];
177 header->flags1 = input[2];
178 header->flags2 = input[3];
179 header->qdcount = (input[4] << 8) + input[5];
180 header->ancount = (input[6] << 8) + input[7];
181 header->nscount = (input[8] << 8) + input[9];
182 header->arcount = (input[10] << 8) + input[11];
183 memcpy(header->payload,&input[12],length);
186 /** Empty a DNSHeader class out into raw data, ready for transmission */
187 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
189 output[0] = header->id[0];
190 output[1] = header->id[1];
191 output[2] = header->flags1;
192 output[3] = header->flags2;
193 output[4] = header->qdcount >> 8;
194 output[5] = header->qdcount & 0xFF;
195 output[6] = header->ancount >> 8;
196 output[7] = header->ancount & 0xFF;
197 output[8] = header->nscount >> 8;
198 output[9] = header->nscount & 0xFF;
199 output[10] = header->arcount >> 8;
200 output[11] = header->arcount & 0xFF;
201 memcpy(&output[12],header->payload,length);
204 /** Send requests we have previously built down the UDP socket */
205 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
207 unsigned char payload[sizeof(DNSHeader)];
212 DNS::EmptyHeader(payload,header,length);
215 if (this->dnsobj->socketfamily == AF_INET6)
218 memset(&addr,0,sizeof(addr));
219 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
220 addr.sin6_family = AF_INET6;
221 addr.sin6_port = htons(DNS::QUERY_PORT);
222 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
228 memset(&addr,0,sizeof(addr));
229 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
230 addr.sin_family = AF_INET;
231 addr.sin_port = htons(DNS::QUERY_PORT);
232 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
237 memset(&addr,0,sizeof(addr));
238 memcpy(&addr.sin_addr.s_addr, &dnsobj->myserver4, sizeof(addr.sin_addr));
239 addr.sin_family = AF_INET;
240 addr.sin_port = htons(DNS::QUERY_PORT);
241 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
248 /** Add a query with a predefined header, and allocate an ID for it. */
249 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
251 /* Is the DNS connection down? */
252 if (this->GetFd() == -1)
256 id = this->PRNG() & DNS::MAX_REQUEST_ID;
258 /* If this id is already 'in flight', pick another. */
260 id = this->PRNG() & DNS::MAX_REQUEST_ID;
262 DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
264 header->id[0] = req->id[0] = id >> 8;
265 header->id[1] = req->id[1] = id & 0xFF;
266 header->flags1 = FLAGS_MASK_RD;
273 /* At this point we already know the id doesnt exist,
274 * so there needs to be no second check for the ::end()
278 /* According to the C++ spec, new never returns NULL. */
282 int DNS::ClearCache()
284 /* This ensures the buckets are reset to sane levels */
285 int rv = this->cache->size();
287 this->cache = new dnscache();
291 int DNS::PruneCache()
294 dnscache* newcache = new dnscache();
295 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
296 /* Dont include expired items (theres no point) */
297 if (i->second.CalcTTLRemaining())
298 newcache->insert(*i);
303 this->cache = newcache;
312 if (this->GetFd() > -1)
314 if (ServerInstance && ServerInstance->SE)
315 ServerInstance->SE->DelFd(this);
316 shutdown(this->GetFd(), 2);
317 close(this->GetFd());
320 /* Rehash the cache */
325 /* Create initial dns cache */
326 this->cache = new dnscache();
329 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
331 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
332 ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
333 ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
334 this->ip6munge = true;
337 this->socketfamily = AF_INET;
339 if (strchr(ServerInstance->Config->DNSServer,':'))
341 this->socketfamily = AF_INET6;
342 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
346 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
350 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
353 /* Initialize mastersocket */
354 int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
357 /* Have we got a socket and is it nonblocking? */
358 if (this->GetFd() != -1)
360 /* Bind the port - port 0 INADDR_ANY */
361 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
364 shutdown(this->GetFd(),2);
365 close(this->GetFd());
369 if (this->GetFd() >= 0)
371 /* Hook the descriptor into the socket engine */
372 if (ServerInstance && ServerInstance->SE)
374 if (!ServerInstance->SE->AddFd(this))
376 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
377 shutdown(this->GetFd(),2);
378 close(this->GetFd());
386 /** Initialise the DNS UDP socket so that we can send requests */
387 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
389 /* Clear the Resolver class table */
390 memset(Classes,0,sizeof(Classes));
392 /* Clear the requests class table */
393 memset(requests,0,sizeof(requests));
395 /* Set the id of the next request to 0
399 /* DNS::Rehash() sets this to a valid ptr
403 /* Again, DNS::Rehash() sets this to a
408 /* Actually read the settings
412 this->PruneTimer = new CacheTimer(ServerInstance, this);
414 ServerInstance->Timers->AddTimer(this->PruneTimer);
417 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
418 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
420 short payloadpos = 0;
421 const char* tempchr, *tempchr2 = name;
422 unsigned short length;
424 /* split name up into labels, create query */
425 while ((tempchr = strchr(tempchr2,'.')) != NULL)
427 length = tempchr - tempchr2;
428 if (payloadpos + length + 1 > 507)
430 payload[payloadpos++] = length;
431 memcpy(&payload[payloadpos],tempchr2,length);
432 payloadpos += length;
433 tempchr2 = &tempchr[1];
435 length = strlen(tempchr2);
438 if (payloadpos + length + 2 > 507)
440 payload[payloadpos++] = length;
441 memcpy(&payload[payloadpos],tempchr2,length);
442 payloadpos += length;
443 payload[payloadpos++] = 0;
445 if (payloadpos > 508)
448 memcpy(&payload[payloadpos],&length,2);
449 length = htons(rr_class);
450 memcpy(&payload[payloadpos + 2],&length,2);
451 return payloadpos + 4;
454 /** Start lookup of an hostname to an IP address */
455 int DNS::GetIP(const char *name)
461 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
464 DNSRequest* req = this->AddQuery(&h, id, name);
466 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
472 /** Start lookup of an hostname to an IPv6 address */
473 int DNS::GetIP6(const char *name)
479 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
482 DNSRequest* req = this->AddQuery(&h, id, name);
484 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
490 /** Start lookup of a cname to another name */
491 int DNS::GetCName(const char *alias)
497 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
500 DNSRequest* req = this->AddQuery(&h, id, alias);
502 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
508 /** Start lookup of an IP address to a hostname */
509 int DNS::GetName(const insp_inaddr *ip)
517 unsigned char* c = (unsigned char*)&ip->s6_addr;
518 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
519 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
520 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
521 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
523 DNS::MakeIP6Int(query, (in6_addr*)ip);
525 unsigned char* c = (unsigned char*)&ip->s_addr;
526 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
529 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
532 DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
534 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
540 /** Start lookup of an IP address to a hostname */
541 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
547 #ifdef SUPPORT_IP6LINKS
548 if (fp == PROTOCOL_IPV6)
551 if (inet_pton(AF_INET6, ip, &i) > 0)
553 DNS::MakeIP6Int(query, &i);
556 /* Invalid IP address */
563 if (inet_aton(ip, &i))
565 unsigned char* c = (unsigned char*)&i.s_addr;
566 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
569 /* Invalid IP address */
573 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
576 DNSRequest* req = this->AddQuery(&h, id, ip);
578 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
584 /** Build an ipv6 reverse domain from an in6_addr
586 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
588 #ifdef SUPPORT_IP6LINKS
589 const char* hex = "0123456789abcdef";
590 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
594 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
597 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
598 *query++ = '.'; /* Seperator */
600 strcpy(query,"ip6.arpa"); /* Suffix the string */
606 /** Return the next id which is ready, and the result attached to it */
607 DNSResult DNS::GetResult()
609 /* Fetch dns query response and decide where it belongs */
612 unsigned char buffer[sizeof(DNSHeader)];
613 sockaddr* from = new sockaddr[2];
615 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
617 socklen_t x = sizeof(sockaddr_in);
619 const char* ipaddr_from;
620 unsigned short int port_from = 0;
622 int length = recvfrom(this->GetFd(),buffer,sizeof(DNSHeader),0,from,&x);
624 /* Did we get the whole header? */
627 /* Nope - something screwed up. */
629 return DNSResult(-1,"",0,"");
632 /* Check wether the reply came from a different DNS
633 * server to the one we sent it to, or the source-port
635 * A user could in theory still spoof dns packets anyway
636 * but this is less trivial than just sending garbage
637 * to the client, which is possible without this check.
639 * -- Thanks jilles for pointing this one out.
643 if (this->socketfamily == AF_INET6)
645 ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
646 port_from = ntohs(((sockaddr_in6*)from)->sin6_port);
650 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
651 port_from = ntohs(((sockaddr_in*)from)->sin_port);
654 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
655 port_from = ntohs(((sockaddr_in*)from)->sin_port);
660 /* We cant perform this security check if you're using 4in6.
661 * Tough luck to you, choose one or't other!
665 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
667 return DNSResult(-1,"",0,"");
671 /* Put the read header info into a header class */
672 DNS::FillHeader(&header,buffer,length - 12);
674 /* Get the id of this request.
675 * Its a 16 bit value stored in two char's,
676 * so we use logic shifts to create the value.
678 unsigned long this_id = header.id[1] + (header.id[0] << 8);
680 /* Do we have a pending request matching this id? */
681 if (!requests[this_id])
683 /* Somehow we got a DNS response for a request we never made... */
684 return DNSResult(-1,"",0,"");
688 /* Remove the query from the list of pending queries */
689 req = requests[this_id];
690 requests[this_id] = NULL;
693 /* Inform the DNSRequest class that it has a result to be read.
694 * When its finished it will return a DNSInfo which is a pair of
695 * unsigned char* resource record data, and an error message.
697 DNSInfo data = req->ResultIsReady(header, length);
698 std::string resultstr;
700 /* Check if we got a result, if we didnt, its an error */
701 if (data.first == NULL)
704 * Mask the ID with the value of ERROR_MASK, so that
705 * the dns_deal_with_classes() function knows that its
706 * an error response and needs to be treated uniquely.
707 * Put the error message in the second field.
709 std::string ro = req->orig;
711 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
715 unsigned long ttl = req->ttl;
718 /* Forward lookups come back as binary data. We must format them into ascii */
722 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
723 resultstr = formatted;
728 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
729 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
730 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
731 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
732 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
733 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
734 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
735 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
736 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
737 char* c = strstr(formatted,":0:");
740 memmove(c+1,c+2,strlen(c+2) + 1);
742 while (memcmp(c,"0:",2) == 0)
743 memmove(c,c+2,strlen(c+2) + 1);
744 if (memcmp(c,"0",2) == 0)
746 if (memcmp(formatted,"0::",3) == 0)
747 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
749 resultstr = formatted;
751 /* Special case. Sending ::1 around between servers
752 * and to clients is dangerous, because the : on the
753 * start makes the client or server interpret the IP
754 * as the last parameter on the line with a value ":1".
756 if (*formatted == ':')
757 resultstr = "0" + resultstr;
761 case DNS_QUERY_CNAME:
762 /* Identical handling to PTR */
765 /* Reverse lookups just come back as char* */
766 resultstr = std::string((const char*)data.first);
774 /* Build the reply with the id and hostname/ip in it */
775 std::string ro = req->orig;
777 return DNSResult(this_id,resultstr,ttl,ro);
781 /** A result is ready, process it */
782 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
790 /* This is just to keep _FORTIFY_SOURCE happy */
791 rr.type = DNS_QUERY_NONE;
793 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
795 if (!(header.flags1 & FLAGS_MASK_QR))
796 return std::make_pair((unsigned char*)NULL,"Not a query result");
798 if (header.flags1 & FLAGS_MASK_OPCODE)
799 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
801 if (header.flags2 & FLAGS_MASK_RCODE)
802 return std::make_pair((unsigned char*)NULL,"Domain name not found");
804 if (header.ancount < 1)
805 return std::make_pair((unsigned char*)NULL,"No resource records returned");
807 /* Subtract the length of the header from the length of the packet */
810 while ((unsigned int)q < header.qdcount && i < length)
812 if (header.payload[i] > 63)
819 if (header.payload[i] == 0)
824 else i += header.payload[i] + 1;
828 while ((unsigned)curanswer < header.ancount)
831 while (q == 0 && i < length)
833 if (header.payload[i] > 63)
840 if (header.payload[i] == 0)
845 else i += header.payload[i] + 1; /* skip length and label */
849 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
851 /* XXX: We actually initialise 'rr' here including its ttl field */
852 DNS::FillResourceRecord(&rr,&header.payload[i]);
854 if (rr.type != this->type)
860 if (rr.rr_class != this->rr_class)
868 if ((unsigned int)curanswer == header.ancount)
869 return std::make_pair((unsigned char*)NULL,"No valid answers");
871 if (i + rr.rdlength > (unsigned int)length)
872 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
874 if (rr.rdlength > 1023)
875 return std::make_pair((unsigned char*)NULL,"Resource record too large");
881 case DNS_QUERY_CNAME:
882 /* CNAME and PTR have the same processing code */
886 while (q == 0 && i < length && o + 256 < 1023)
888 if (header.payload[i] > 63)
890 memcpy(&ptr,&header.payload[i],2);
891 i = ntohs(ptr) - 0xC000 - 12;
895 if (header.payload[i] == 0)
904 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
905 o += header.payload[i];
906 i += header.payload[i] + 1;
913 memcpy(res,&header.payload[i],rr.rdlength);
914 res[rr.rdlength] = 0;
917 memcpy(res,&header.payload[i],rr.rdlength);
918 res[rr.rdlength] = 0;
921 memcpy(res,&header.payload[i],rr.rdlength);
922 res[rr.rdlength] = 0;
925 return std::make_pair(res,"No error");;
928 /** Close the master socket */
931 shutdown(this->GetFd(), 2);
932 close(this->GetFd());
933 ServerInstance->Timers->DelTimer(this->PruneTimer);
934 delete this->PruneTimer;
937 CachedQuery* DNS::GetCache(const std::string &source)
939 dnscache::iterator x = cache->find(source.c_str());
940 if (x != cache->end())
946 void DNS::DelCache(const std::string &source)
948 cache->erase(source.c_str());
951 void Resolver::TriggerCachedResult()
954 OnLookupComplete(CQ->data, time_left, true);
957 /** High level abstraction of dns used by application at large */
958 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
962 CQ = ServerInstance->Res->GetCache(source);
965 time_left = CQ->CalcTTLRemaining();
968 ServerInstance->Res->DelCache(source);
982 this->myid = ServerInstance->Res->GetIP(source.c_str());
986 if (insp_aton(source.c_str(), &binip) > 0)
988 /* Valid ip address */
989 this->myid = ServerInstance->Res->GetName(&binip);
993 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
994 throw ModuleException("Resolver: Bad IP address");
1000 querytype = DNS_QUERY_PTR;
1001 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
1004 case DNS_QUERY_PTR6:
1005 querytype = DNS_QUERY_PTR;
1006 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
1009 case DNS_QUERY_AAAA:
1010 this->myid = ServerInstance->Res->GetIP6(source.c_str());
1013 case DNS_QUERY_CNAME:
1014 this->myid = ServerInstance->Res->GetCName(source.c_str());
1021 if (this->myid == -1)
1023 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1024 throw ModuleException("Resolver: Couldnt get an id to make a request");
1025 /* We shouldnt get here really */
1030 /** Called when an error occurs */
1031 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1033 /* Nothing in here */
1036 /** Destroy a resolver */
1037 Resolver::~Resolver()
1039 /* Nothing here (yet) either */
1042 /** Get the request id associated with this class */
1043 int Resolver::GetId()
1048 Module* Resolver::GetCreator()
1050 return this->Creator;
1053 /** Process a socket read event */
1054 void DNS::HandleEvent(EventType et, int errornum)
1056 /* Fetch the id and result of the next available packet */
1057 DNSResult res = this->GetResult();
1058 /* Is there a usable request id? */
1061 /* Its an error reply */
1062 if (res.id & ERROR_MASK)
1064 /* Mask off the error bit */
1065 res.id -= ERROR_MASK;
1066 /* Marshall the error to the correct class */
1067 if (Classes[res.id])
1069 if (ServerInstance && ServerInstance->stats)
1070 ServerInstance->stats->statsDnsBad++;
1071 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1072 delete Classes[res.id];
1073 Classes[res.id] = NULL;
1078 /* It is a non-error result, marshall the result to the correct class */
1079 if (Classes[res.id])
1081 if (ServerInstance && ServerInstance->stats)
1082 ServerInstance->stats->statsDnsGood++;
1084 if (!this->GetCache(res.original.c_str()))
1085 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1087 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1088 delete Classes[res.id];
1089 Classes[res.id] = NULL;
1093 if (ServerInstance && ServerInstance->stats)
1094 ServerInstance->stats->statsDns++;
1098 /** Add a derived Resolver to the working set */
1099 bool DNS::AddResolverClass(Resolver* r)
1101 /* Check the pointers validity and the id's validity */
1102 if ((r) && (r->GetId() > -1))
1104 /* Check the slot isnt already occupied -
1105 * This should NEVER happen unless we have
1106 * a severely broken DNS server somewhere
1108 if (!Classes[r->GetId()])
1110 /* Set up the pointer to the class */
1111 Classes[r->GetId()] = r;
1120 /* Pointer or id not valid.
1121 * Free the item and return
1130 void DNS::CleanResolvers(Module* module)
1132 for (int i = 0; i < MAX_REQUEST_ID; i++)
1136 if (Classes[i]->GetCreator() == module)
1138 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1146 /** Generate pseudo-random number */
1147 unsigned long DNS::PRNG()
1149 unsigned long val = 0;
1151 serverstats* s = ServerInstance->stats;
1152 gettimeofday(&n,NULL);
1153 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1154 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1155 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size();