1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2008 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 * ---------------------------------------------------
14 /* $Core: libIRCDasyncdns */
17 dns.cpp - Nonblocking DNS functions.
18 Very very loosely based on the firedns library,
19 Copyright (C) 2002 Ian Gulliver. This file is no
20 longer anything like firedns, there are many major
21 differences between this code and the original.
22 Please do not assume that firedns works like this,
23 looks like this, walks like this or tastes like this.
27 #include <sys/types.h>
28 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 #include "inspircd_win32wrapper.h"
34 #include "inspircd_se_config.h"
39 #include "socketengine.h"
40 #include "configreader.h"
43 using irc::sockets::insp_inaddr;
44 using irc::sockets::insp_ntoa;
45 using irc::sockets::insp_aton;
46 using irc::sockets::OpenTCPSocket;
48 /** Masks to mask off the responses we get from the DNSRequest methods
52 ERROR_MASK = 0x10000 /* Result is an error */
55 /** Flags which can be ORed into a request or reply for different meanings
59 FLAGS_MASK_RD = 0x01, /* Recursive */
61 FLAGS_MASK_AA = 0x04, /* Authoritative */
62 FLAGS_MASK_OPCODE = 0x78,
64 FLAGS_MASK_RCODE = 0x0F, /* Request */
70 /** Represents a dns resource record (rr)
74 QueryType type; /* Record type */
75 unsigned int rr_class; /* Record class */
76 unsigned long ttl; /* Time to live */
77 unsigned int rdlength; /* Record length */
80 /** Represents a dns request/reply header, and its payload as opaque data.
85 unsigned char id[2]; /* Request id */
86 unsigned int flags1; /* Flags */
87 unsigned int flags2; /* Flags */
89 unsigned int ancount; /* Answer count */
90 unsigned int nscount; /* Nameserver count */
92 unsigned char payload[512]; /* Packet payload */
98 unsigned char id[2]; /* Request id */
99 unsigned char* res; /* Result processing buffer */
100 unsigned int rr_class; /* Request class */
101 QueryType type; /* Request type */
102 DNS* dnsobj; /* DNS caller (where we get our FD from) */
103 unsigned long ttl; /* Time to live */
104 std::string orig; /* Original requested name/ip */
105 InspIRCd* ServerInstance;
107 DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
109 DNSInfo ResultIsReady(DNSHeader &h, int length, int result_we_want);
110 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
113 class CacheTimer : public Timer
116 InspIRCd* ServerInstance;
119 CacheTimer(InspIRCd* Instance, DNS* thisdns)
120 : Timer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
122 virtual void Tick(time_t)
128 class RequestTimeout : public Timer
130 InspIRCd* ServerInstance;
134 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : Timer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
140 if (ServerInstance->Res->requests[watchid] == watch)
142 /* Still exists, whack it */
143 if (ServerInstance->Res->Classes[watchid])
145 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
146 delete ServerInstance->Res->Classes[watchid];
147 ServerInstance->Res->Classes[watchid] = NULL;
149 ServerInstance->Res->requests[watchid] = NULL;
156 /* Allocate the processing buffer */
157 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int rid, const std::string &original) : dnsobj(dns), ServerInstance(Instance)
159 res = new unsigned char[512];
162 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, rid);
163 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
166 /* Deallocate the processing buffer */
167 DNSRequest::~DNSRequest()
172 /** Fill a ResourceRecord class based on raw data input */
173 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
175 rr->type = (QueryType)((input[0] << 8) + input[1]);
176 rr->rr_class = (input[2] << 8) + input[3];
177 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
178 rr->rdlength = (input[8] << 8) + input[9];
181 /** Fill a DNSHeader class based on raw data input of a given length */
182 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
184 header->id[0] = input[0];
185 header->id[1] = input[1];
186 header->flags1 = input[2];
187 header->flags2 = input[3];
188 header->qdcount = (input[4] << 8) + input[5];
189 header->ancount = (input[6] << 8) + input[7];
190 header->nscount = (input[8] << 8) + input[9];
191 header->arcount = (input[10] << 8) + input[11];
192 memcpy(header->payload,&input[12],length);
195 /** Empty a DNSHeader class out into raw data, ready for transmission */
196 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
198 output[0] = header->id[0];
199 output[1] = header->id[1];
200 output[2] = header->flags1;
201 output[3] = header->flags2;
202 output[4] = header->qdcount >> 8;
203 output[5] = header->qdcount & 0xFF;
204 output[6] = header->ancount >> 8;
205 output[7] = header->ancount & 0xFF;
206 output[8] = header->nscount >> 8;
207 output[9] = header->nscount & 0xFF;
208 output[10] = header->arcount >> 8;
209 output[11] = header->arcount & 0xFF;
210 memcpy(&output[12],header->payload,length);
213 /** Send requests we have previously built down the UDP socket */
214 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
216 ServerInstance->Logs->Log("RESOLVER", DEBUG,"DNSRequest::SendRequests");
218 unsigned char payload[sizeof(DNSHeader)];
223 DNS::EmptyHeader(payload,header,length);
226 if (this->dnsobj->socketfamily == AF_INET6)
229 memset(&addr,0,sizeof(addr));
230 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
231 addr.sin6_family = AF_INET6;
232 addr.sin6_port = htons(DNS::QUERY_PORT);
233 if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
240 memset(&addr,0,sizeof(addr));
241 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
242 addr.sin_family = AF_INET;
243 addr.sin_port = htons(DNS::QUERY_PORT);
244 if (ServerInstance->SE->SendTo(dnsobj, (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
248 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
252 /** Add a query with a predefined header, and allocate an ID for it. */
253 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
255 /* Is the DNS connection down? */
256 if (this->GetFd() == -1)
260 id = this->PRNG() & DNS::MAX_REQUEST_ID;
262 /* If this id is already 'in flight', pick another. */
264 id = this->PRNG() & DNS::MAX_REQUEST_ID;
266 DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
268 header->id[0] = req->id[0] = id >> 8;
269 header->id[1] = req->id[1] = id & 0xFF;
270 header->flags1 = FLAGS_MASK_RD;
277 /* At this point we already know the id doesnt exist,
278 * so there needs to be no second check for the ::end()
282 /* According to the C++ spec, new never returns NULL. */
286 int DNS::ClearCache()
288 /* This ensures the buckets are reset to sane levels */
289 int rv = this->cache->size();
291 this->cache = new dnscache();
295 int DNS::PruneCache()
298 dnscache* newcache = new dnscache();
299 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
300 /* Dont include expired items (theres no point) */
301 if (i->second.CalcTTLRemaining())
302 newcache->insert(*i);
307 this->cache = newcache;
316 if (this->GetFd() > -1)
318 if (ServerInstance && ServerInstance->SE)
319 ServerInstance->SE->DelFd(this);
320 ServerInstance->SE->Shutdown(this, 2);
321 ServerInstance->SE->Close(this);
324 /* Rehash the cache */
329 /* Create initial dns cache */
330 this->cache = new dnscache();
333 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
335 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
336 ServerInstance->Logs->Log("RESOLVER",DEFAULT," This should not cause a problem, however it is recommended you migrate");
337 ServerInstance->Logs->Log("RESOLVER",DEFAULT," to a true IPv6 environment.");
338 this->ip6munge = true;
341 this->socketfamily = AF_INET;
343 if (strchr(ServerInstance->Config->DNSServer,':'))
345 this->socketfamily = AF_INET6;
346 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
350 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
354 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
357 /* Initialize mastersocket */
358 int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
360 ServerInstance->SE->NonBlocking(this->GetFd());
362 /* Have we got a socket and is it nonblocking? */
363 if (this->GetFd() != -1)
365 /* Bind the port - port 0 INADDR_ANY */
366 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
369 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error binding dns socket");
370 ServerInstance->SE->Shutdown(this, 2);
371 ServerInstance->SE->Close(this);
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->Logs->Log("RESOLVER",DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
383 ServerInstance->SE->Shutdown(this, 2);
384 ServerInstance->SE->Close(this);
392 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error creating dns socket");
396 /** Initialise the DNS UDP socket so that we can send requests */
397 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
399 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
400 /* Clear the Resolver class table */
401 memset(Classes,0,sizeof(Classes));
403 /* Clear the requests class table */
404 memset(requests,0,sizeof(requests));
406 /* Set the id of the next request to 0
410 /* DNS::Rehash() sets this to a valid ptr
414 /* Again, DNS::Rehash() sets this to a
419 /* Actually read the settings
423 this->PruneTimer = new CacheTimer(ServerInstance, this);
425 ServerInstance->Timers->AddTimer(this->PruneTimer);
428 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
429 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
431 short payloadpos = 0;
432 const char* tempchr, *tempchr2 = name;
433 unsigned short length;
435 /* split name up into labels, create query */
436 while ((tempchr = strchr(tempchr2,'.')) != NULL)
438 length = tempchr - tempchr2;
439 if (payloadpos + length + 1 > 507)
441 payload[payloadpos++] = length;
442 memcpy(&payload[payloadpos],tempchr2,length);
443 payloadpos += length;
444 tempchr2 = &tempchr[1];
446 length = strlen(tempchr2);
449 if (payloadpos + length + 2 > 507)
451 payload[payloadpos++] = length;
452 memcpy(&payload[payloadpos],tempchr2,length);
453 payloadpos += length;
454 payload[payloadpos++] = 0;
456 if (payloadpos > 508)
459 memcpy(&payload[payloadpos],&length,2);
460 length = htons(rr_class);
461 memcpy(&payload[payloadpos + 2],&length,2);
462 return payloadpos + 4;
465 /** Start lookup of an hostname to an IP address */
466 int DNS::GetIP(const char *name)
472 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
475 DNSRequest* req = this->AddQuery(&h, id, name);
477 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
483 /** Start lookup of an hostname to an IPv6 address */
484 int DNS::GetIP6(const char *name)
490 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
493 DNSRequest* req = this->AddQuery(&h, id, name);
495 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
501 /** Start lookup of a cname to another name */
502 int DNS::GetCName(const char *alias)
508 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
511 DNSRequest* req = this->AddQuery(&h, id, alias);
513 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
519 /** Start lookup of an IP address to a hostname */
520 int DNS::GetName(const insp_inaddr *ip)
528 unsigned char* c = (unsigned char*)&ip->s6_addr;
529 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
530 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
531 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
532 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
534 DNS::MakeIP6Int(query, (in6_addr*)ip);
536 unsigned char* c = (unsigned char*)&ip->s_addr;
537 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
540 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
543 DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
545 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
551 /** Start lookup of an IP address to a hostname */
552 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
558 #ifdef SUPPORT_IP6LINKS
559 if (fp == PROTOCOL_IPV6)
562 if (inet_pton(AF_INET6, ip, &i) > 0)
564 DNS::MakeIP6Int(query, &i);
567 /* Invalid IP address */
574 if (inet_aton(ip, &i))
576 unsigned char* c = (unsigned char*)&i.s_addr;
577 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
580 /* Invalid IP address */
584 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
587 DNSRequest* req = this->AddQuery(&h, id, ip);
589 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
595 /** Build an ipv6 reverse domain from an in6_addr
597 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
599 #ifdef SUPPORT_IP6LINKS
600 const char* hex = "0123456789abcdef";
601 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
605 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
608 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
609 *query++ = '.'; /* Seperator */
611 strcpy(query,"ip6.arpa"); /* Suffix the string */
617 /** Return the next id which is ready, and the result attached to it */
618 DNSResult DNS::GetResult(int resultnum)
620 /* Fetch dns query response and decide where it belongs */
623 unsigned char buffer[sizeof(DNSHeader)];
624 sockaddr* from = new sockaddr[2];
626 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
628 socklen_t x = sizeof(sockaddr_in);
630 const char* ipaddr_from;
631 unsigned short int port_from = 0;
633 int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, from, &x);
635 /* Did we get the whole header? */
638 /* Nope - something screwed up. */
640 return DNSResult(-1,"",0,"");
643 /* Check wether the reply came from a different DNS
644 * server to the one we sent it to, or the source-port
646 * A user could in theory still spoof dns packets anyway
647 * but this is less trivial than just sending garbage
648 * to the client, which is possible without this check.
650 * -- Thanks jilles for pointing this one out.
654 if (this->socketfamily == AF_INET6)
656 ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
657 port_from = ntohs(((sockaddr_in6*)from)->sin6_port);
662 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
663 port_from = ntohs(((sockaddr_in*)from)->sin_port);
668 /* We cant perform this security check if you're using 4in6.
669 * Tough luck to you, choose one or't other!
673 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
675 return DNSResult(-1,"",0,"");
679 /* Put the read header info into a header class */
680 DNS::FillHeader(&header,buffer,length - 12);
682 /* Get the id of this request.
683 * Its a 16 bit value stored in two char's,
684 * so we use logic shifts to create the value.
686 unsigned long this_id = header.id[1] + (header.id[0] << 8);
688 /* Do we have a pending request matching this id? */
689 if (!requests[this_id])
691 /* Somehow we got a DNS response for a request we never made... */
692 return DNSResult(-1,"",0,"");
696 /* Remove the query from the list of pending queries */
697 req = requests[this_id];
698 requests[this_id] = NULL;
701 /* Inform the DNSRequest class that it has a result to be read.
702 * When its finished it will return a DNSInfo which is a pair of
703 * unsigned char* resource record data, and an error message.
705 DNSInfo data = req->ResultIsReady(header, length, resultnum);
706 std::string resultstr;
708 /* Check if we got a result, if we didnt, its an error */
709 if (data.first == NULL)
712 * Mask the ID with the value of ERROR_MASK, so that
713 * the dns_deal_with_classes() function knows that its
714 * an error response and needs to be treated uniquely.
715 * Put the error message in the second field.
717 std::string ro = req->orig;
719 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
723 unsigned long ttl = req->ttl;
726 /* Forward lookups come back as binary data. We must format them into ascii */
730 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
731 resultstr = formatted;
736 inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted));
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.insert(0, "0");
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, int result_we_want)
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 if (curanswer == result_we_want)
853 DNS::FillResourceRecord(&rr,&header.payload[i]);
856 if (rr.type != this->type)
862 if (rr.rr_class != this->rr_class)
870 if ((unsigned int)curanswer == header.ancount)
871 return std::make_pair((unsigned char*)NULL,"No more answers (" + ConvToStr(header.ancount) + " answers, wanted #" + ConvToStr(result_we_want) + ")");
873 if (i + rr.rdlength > (unsigned int)length)
874 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
876 if (rr.rdlength > 1023)
877 return std::make_pair((unsigned char*)NULL,"Resource record too large");
883 case DNS_QUERY_CNAME:
884 /* CNAME and PTR have the same processing code */
888 while (q == 0 && i < length && o + 256 < 1023)
890 if (header.payload[i] > 63)
892 memcpy(&ptr,&header.payload[i],2);
893 i = ntohs(ptr) - 0xC000 - 12;
897 if (header.payload[i] == 0)
906 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
907 o += header.payload[i];
908 i += header.payload[i] + 1;
915 memcpy(res,&header.payload[i],rr.rdlength);
916 res[rr.rdlength] = 0;
919 memcpy(res,&header.payload[i],rr.rdlength);
920 res[rr.rdlength] = 0;
923 memcpy(res,&header.payload[i],rr.rdlength);
924 res[rr.rdlength] = 0;
927 return std::make_pair(res,"No error");
930 /** Close the master socket */
933 ServerInstance->SE->Shutdown(this, 2);
934 ServerInstance->SE->Close(this);
935 ServerInstance->Timers->DelTimer(this->PruneTimer);
936 delete this->PruneTimer;
939 CachedQuery* DNS::GetCache(const std::string &source)
941 dnscache::iterator x = cache->find(source.c_str());
942 if (x != cache->end())
948 void DNS::DelCache(const std::string &source)
950 cache->erase(source.c_str());
953 void Resolver::TriggerCachedResult()
956 OnLookupComplete(CQ->data, time_left, true, 0);
959 /** High level abstraction of dns used by application at large */
960 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
962 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
965 CQ = ServerInstance->Res->GetCache(source);
968 time_left = CQ->CalcTTLRemaining();
971 ServerInstance->Res->DelCache(source);
985 this->myid = ServerInstance->Res->GetIP(source.c_str());
989 if (insp_aton(source.c_str(), &binip) > 0)
991 /* Valid ip address */
992 this->myid = ServerInstance->Res->GetName(&binip);
996 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
997 throw ModuleException("Resolver: Bad IP address");
1002 case DNS_QUERY_PTR4:
1003 querytype = DNS_QUERY_PTR;
1004 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
1007 case DNS_QUERY_PTR6:
1008 querytype = DNS_QUERY_PTR;
1009 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
1012 case DNS_QUERY_AAAA:
1013 this->myid = ServerInstance->Res->GetIP6(source.c_str());
1016 case DNS_QUERY_CNAME:
1017 this->myid = ServerInstance->Res->GetCName(source.c_str());
1024 if (this->myid == -1)
1026 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1027 throw ModuleException("Resolver: Couldnt get an id to make a request");
1028 /* We shouldnt get here really */
1033 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
1037 /** Called when an error occurs */
1038 void Resolver::OnError(ResolverError, const std::string&)
1040 /* Nothing in here */
1043 /** Destroy a resolver */
1044 Resolver::~Resolver()
1046 /* Nothing here (yet) either */
1049 /** Get the request id associated with this class */
1050 int Resolver::GetId()
1055 Module* Resolver::GetCreator()
1057 return this->Creator;
1060 /** Process a socket read event */
1061 void DNS::HandleEvent(EventType, int)
1063 /* Fetch the id and result of the next available packet */
1065 DNSResult res(0,"",0,"");
1067 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
1069 res = this->GetResult(resultnum);
1071 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result %d id %d", resultnum, res.id);
1073 /* Is there a usable request id? */
1076 /* Its an error reply */
1077 if (res.id & ERROR_MASK)
1079 /* Mask off the error bit */
1080 res.id -= ERROR_MASK;
1081 /* Marshall the error to the correct class */
1082 if (Classes[res.id])
1084 if (ServerInstance && ServerInstance->stats)
1085 ServerInstance->stats->statsDnsBad++;
1086 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1087 delete Classes[res.id];
1088 Classes[res.id] = NULL;
1094 /* It is a non-error result, marshall the result to the correct class */
1095 if (Classes[res.id])
1097 if (ServerInstance && ServerInstance->stats)
1098 ServerInstance->stats->statsDnsGood++;
1100 if (!this->GetCache(res.original.c_str()))
1101 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1103 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false, resultnum);
1104 delete Classes[res.id];
1105 Classes[res.id] = NULL;
1109 if (ServerInstance && ServerInstance->stats)
1110 ServerInstance->stats->statsDns++;
1116 /** Add a derived Resolver to the working set */
1117 bool DNS::AddResolverClass(Resolver* r)
1119 ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass %08lx", r);
1120 /* Check the pointers validity and the id's validity */
1121 if ((r) && (r->GetId() > -1))
1123 /* Check the slot isnt already occupied -
1124 * This should NEVER happen unless we have
1125 * a severely broken DNS server somewhere
1127 if (!Classes[r->GetId()])
1129 /* Set up the pointer to the class */
1130 Classes[r->GetId()] = r;
1139 /* Pointer or id not valid.
1140 * Free the item and return
1149 void DNS::CleanResolvers(Module* module)
1151 for (int i = 0; i < MAX_REQUEST_ID; i++)
1155 if (Classes[i]->GetCreator() == module)
1157 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1165 /** Generate pseudo-random number */
1166 unsigned long DNS::PRNG()
1168 unsigned long val = 0;
1170 serverstats* s = ServerInstance->stats;
1171 gettimeofday(&n,NULL);
1172 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1173 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1174 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size();