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 */
103 InspIRCd* ServerInstance;
105 DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
107 DNSInfo ResultIsReady(DNSHeader &h, int length, int result_we_want);
108 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
111 class CacheTimer : public InspTimer
114 InspIRCd* ServerInstance;
117 CacheTimer(InspIRCd* Instance, DNS* thisdns)
118 : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
120 virtual void Tick(time_t TIME)
126 class RequestTimeout : public InspTimer
128 InspIRCd* ServerInstance;
132 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
136 void Tick(time_t TIME)
138 if (ServerInstance->Res->requests[watchid] == watch)
140 /* Still exists, whack it */
141 if (ServerInstance->Res->Classes[watchid])
143 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
144 delete ServerInstance->Res->Classes[watchid];
145 ServerInstance->Res->Classes[watchid] = NULL;
147 ServerInstance->Res->requests[watchid] = NULL;
154 /* Allocate the processing buffer */
155 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns), ServerInstance(Instance)
157 res = new unsigned char[512];
160 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
161 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
164 /* Deallocate the processing buffer */
165 DNSRequest::~DNSRequest()
170 /** Fill a ResourceRecord class based on raw data input */
171 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
173 rr->type = (QueryType)((input[0] << 8) + input[1]);
174 rr->rr_class = (input[2] << 8) + input[3];
175 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
176 rr->rdlength = (input[8] << 8) + input[9];
179 /** Fill a DNSHeader class based on raw data input of a given length */
180 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
182 header->id[0] = input[0];
183 header->id[1] = input[1];
184 header->flags1 = input[2];
185 header->flags2 = input[3];
186 header->qdcount = (input[4] << 8) + input[5];
187 header->ancount = (input[6] << 8) + input[7];
188 header->nscount = (input[8] << 8) + input[9];
189 header->arcount = (input[10] << 8) + input[11];
190 memcpy(header->payload,&input[12],length);
193 /** Empty a DNSHeader class out into raw data, ready for transmission */
194 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
196 output[0] = header->id[0];
197 output[1] = header->id[1];
198 output[2] = header->flags1;
199 output[3] = header->flags2;
200 output[4] = header->qdcount >> 8;
201 output[5] = header->qdcount & 0xFF;
202 output[6] = header->ancount >> 8;
203 output[7] = header->ancount & 0xFF;
204 output[8] = header->nscount >> 8;
205 output[9] = header->nscount & 0xFF;
206 output[10] = header->arcount >> 8;
207 output[11] = header->arcount & 0xFF;
208 memcpy(&output[12],header->payload,length);
211 /** Send requests we have previously built down the UDP socket */
212 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
214 unsigned char payload[sizeof(DNSHeader)];
219 DNS::EmptyHeader(payload,header,length);
222 if (this->dnsobj->socketfamily == AF_INET6)
225 memset(&addr,0,sizeof(addr));
226 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
227 addr.sin6_family = AF_INET6;
228 addr.sin6_port = htons(DNS::QUERY_PORT);
229 if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
236 memset(&addr,0,sizeof(addr));
237 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
238 addr.sin_family = AF_INET;
239 addr.sin_port = htons(DNS::QUERY_PORT);
240 if (ServerInstance->SE->SendTo(dnsobj, (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
246 /** Add a query with a predefined header, and allocate an ID for it. */
247 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
249 /* Is the DNS connection down? */
250 if (this->GetFd() == -1)
254 id = this->PRNG() & DNS::MAX_REQUEST_ID;
256 /* If this id is already 'in flight', pick another. */
258 id = this->PRNG() & DNS::MAX_REQUEST_ID;
260 DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
262 header->id[0] = req->id[0] = id >> 8;
263 header->id[1] = req->id[1] = id & 0xFF;
264 header->flags1 = FLAGS_MASK_RD;
271 /* At this point we already know the id doesnt exist,
272 * so there needs to be no second check for the ::end()
276 /* According to the C++ spec, new never returns NULL. */
280 int DNS::ClearCache()
282 /* This ensures the buckets are reset to sane levels */
283 int rv = this->cache->size();
285 this->cache = new dnscache();
289 int DNS::PruneCache()
292 dnscache* newcache = new dnscache();
293 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
294 /* Dont include expired items (theres no point) */
295 if (i->second.CalcTTLRemaining())
296 newcache->insert(*i);
301 this->cache = newcache;
310 if (this->GetFd() > -1)
312 if (ServerInstance && ServerInstance->SE)
313 ServerInstance->SE->DelFd(this);
314 ServerInstance->SE->Shutdown(this, 2);
315 ServerInstance->SE->Close(this);
318 /* Rehash the cache */
323 /* Create initial dns cache */
324 this->cache = new dnscache();
327 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
329 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
330 ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
331 ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
332 this->ip6munge = true;
335 this->socketfamily = AF_INET;
337 if (strchr(ServerInstance->Config->DNSServer,':'))
339 this->socketfamily = AF_INET6;
340 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
344 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
348 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
351 /* Initialize mastersocket */
352 int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
354 ServerInstance->SE->NonBlocking(this->GetFd());
356 /* Have we got a socket and is it nonblocking? */
357 if (this->GetFd() != -1)
359 /* Bind the port - port 0 INADDR_ANY */
360 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
363 ServerInstance->SE->Shutdown(this, 2);
364 ServerInstance->SE->Close(this);
368 if (this->GetFd() >= 0)
370 /* Hook the descriptor into the socket engine */
371 if (ServerInstance && ServerInstance->SE)
373 if (!ServerInstance->SE->AddFd(this))
375 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
376 ServerInstance->SE->Shutdown(this, 2);
377 ServerInstance->SE->Close(this);
385 /** Initialise the DNS UDP socket so that we can send requests */
386 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
388 /* Clear the Resolver class table */
389 memset(Classes,0,sizeof(Classes));
391 /* Clear the requests class table */
392 memset(requests,0,sizeof(requests));
394 /* Set the id of the next request to 0
398 /* DNS::Rehash() sets this to a valid ptr
402 /* Again, DNS::Rehash() sets this to a
407 /* Actually read the settings
411 this->PruneTimer = new CacheTimer(ServerInstance, this);
413 ServerInstance->Timers->AddTimer(this->PruneTimer);
416 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
417 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
419 short payloadpos = 0;
420 const char* tempchr, *tempchr2 = name;
421 unsigned short length;
423 /* split name up into labels, create query */
424 while ((tempchr = strchr(tempchr2,'.')) != NULL)
426 length = tempchr - tempchr2;
427 if (payloadpos + length + 1 > 507)
429 payload[payloadpos++] = length;
430 memcpy(&payload[payloadpos],tempchr2,length);
431 payloadpos += length;
432 tempchr2 = &tempchr[1];
434 length = strlen(tempchr2);
437 if (payloadpos + length + 2 > 507)
439 payload[payloadpos++] = length;
440 memcpy(&payload[payloadpos],tempchr2,length);
441 payloadpos += length;
442 payload[payloadpos++] = 0;
444 if (payloadpos > 508)
447 memcpy(&payload[payloadpos],&length,2);
448 length = htons(rr_class);
449 memcpy(&payload[payloadpos + 2],&length,2);
450 return payloadpos + 4;
453 /** Start lookup of an hostname to an IP address */
454 int DNS::GetIP(const char *name)
460 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
463 DNSRequest* req = this->AddQuery(&h, id, name);
465 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
471 /** Start lookup of an hostname to an IPv6 address */
472 int DNS::GetIP6(const char *name)
478 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
481 DNSRequest* req = this->AddQuery(&h, id, name);
483 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
489 /** Start lookup of a cname to another name */
490 int DNS::GetCName(const char *alias)
496 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
499 DNSRequest* req = this->AddQuery(&h, id, alias);
501 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
507 /** Start lookup of an IP address to a hostname */
508 int DNS::GetName(const insp_inaddr *ip)
516 unsigned char* c = (unsigned char*)&ip->s6_addr;
517 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
518 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
519 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
520 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
522 DNS::MakeIP6Int(query, (in6_addr*)ip);
524 unsigned char* c = (unsigned char*)&ip->s_addr;
525 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
528 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
531 DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
533 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
539 /** Start lookup of an IP address to a hostname */
540 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
546 #ifdef SUPPORT_IP6LINKS
547 if (fp == PROTOCOL_IPV6)
550 if (inet_pton(AF_INET6, ip, &i) > 0)
552 DNS::MakeIP6Int(query, &i);
555 /* Invalid IP address */
562 if (inet_aton(ip, &i))
564 unsigned char* c = (unsigned char*)&i.s_addr;
565 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
568 /* Invalid IP address */
572 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
575 DNSRequest* req = this->AddQuery(&h, id, ip);
577 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
583 /** Build an ipv6 reverse domain from an in6_addr
585 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
587 #ifdef SUPPORT_IP6LINKS
588 const char* hex = "0123456789abcdef";
589 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
593 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
596 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
597 *query++ = '.'; /* Seperator */
599 strcpy(query,"ip6.arpa"); /* Suffix the string */
605 /** Return the next id which is ready, and the result attached to it */
606 DNSResult DNS::GetResult(int resultnum)
608 /* Fetch dns query response and decide where it belongs */
611 unsigned char buffer[sizeof(DNSHeader)];
612 sockaddr* from = new sockaddr[2];
614 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
616 socklen_t x = sizeof(sockaddr_in);
618 const char* ipaddr_from;
619 unsigned short int port_from = 0;
621 int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, from, &x);
623 /* Did we get the whole header? */
626 /* Nope - something screwed up. */
628 return DNSResult(-1,"",0,"");
631 /* Check wether the reply came from a different DNS
632 * server to the one we sent it to, or the source-port
634 * A user could in theory still spoof dns packets anyway
635 * but this is less trivial than just sending garbage
636 * to the client, which is possible without this check.
638 * -- Thanks jilles for pointing this one out.
642 if (this->socketfamily == AF_INET6)
644 ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
645 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);
656 /* We cant perform this security check if you're using 4in6.
657 * Tough luck to you, choose one or't other!
661 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
663 return DNSResult(-1,"",0,"");
667 /* Put the read header info into a header class */
668 DNS::FillHeader(&header,buffer,length - 12);
670 /* Get the id of this request.
671 * Its a 16 bit value stored in two char's,
672 * so we use logic shifts to create the value.
674 unsigned long this_id = header.id[1] + (header.id[0] << 8);
676 /* Do we have a pending request matching this id? */
677 if (!requests[this_id])
679 /* Somehow we got a DNS response for a request we never made... */
680 return DNSResult(-1,"",0,"");
684 /* Remove the query from the list of pending queries */
685 req = requests[this_id];
686 requests[this_id] = NULL;
689 /* Inform the DNSRequest class that it has a result to be read.
690 * When its finished it will return a DNSInfo which is a pair of
691 * unsigned char* resource record data, and an error message.
693 DNSInfo data = req->ResultIsReady(header, length, resultnum);
694 std::string resultstr;
696 /* Check if we got a result, if we didnt, its an error */
697 if (data.first == NULL)
700 * Mask the ID with the value of ERROR_MASK, so that
701 * the dns_deal_with_classes() function knows that its
702 * an error response and needs to be treated uniquely.
703 * Put the error message in the second field.
705 std::string ro = req->orig;
707 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
711 unsigned long ttl = req->ttl;
714 /* Forward lookups come back as binary data. We must format them into ascii */
718 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
719 resultstr = formatted;
724 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
725 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
726 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
727 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
728 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
729 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
730 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
731 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
732 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
733 char* c = strstr(formatted,":0:");
736 memmove(c+1,c+2,strlen(c+2) + 1);
738 while (memcmp(c,"0:",2) == 0)
739 memmove(c,c+2,strlen(c+2) + 1);
740 if (memcmp(c,"0",2) == 0)
742 if (memcmp(formatted,"0::",3) == 0)
743 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
745 resultstr = formatted;
747 /* Special case. Sending ::1 around between servers
748 * and to clients is dangerous, because the : on the
749 * start makes the client or server interpret the IP
750 * as the last parameter on the line with a value ":1".
752 if (*formatted == ':')
753 resultstr.insert(0, "0");
757 case DNS_QUERY_CNAME:
758 /* Identical handling to PTR */
761 /* Reverse lookups just come back as char* */
762 resultstr = std::string((const char*)data.first);
770 /* Build the reply with the id and hostname/ip in it */
771 std::string ro = req->orig;
773 return DNSResult(this_id,resultstr,ttl,ro);
777 /** A result is ready, process it */
778 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length, int result_we_want)
786 /* This is just to keep _FORTIFY_SOURCE happy */
787 rr.type = DNS_QUERY_NONE;
789 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
791 if (!(header.flags1 & FLAGS_MASK_QR))
792 return std::make_pair((unsigned char*)NULL,"Not a query result");
794 if (header.flags1 & FLAGS_MASK_OPCODE)
795 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
797 if (header.flags2 & FLAGS_MASK_RCODE)
798 return std::make_pair((unsigned char*)NULL,"Domain name not found");
800 if (header.ancount < 1)
801 return std::make_pair((unsigned char*)NULL,"No resource records returned");
803 /* Subtract the length of the header from the length of the packet */
806 while ((unsigned int)q < header.qdcount && i < length)
808 if (header.payload[i] > 63)
815 if (header.payload[i] == 0)
820 else i += header.payload[i] + 1;
824 while ((unsigned)curanswer < header.ancount)
827 while (q == 0 && i < length)
829 if (header.payload[i] > 63)
836 if (header.payload[i] == 0)
841 else i += header.payload[i] + 1; /* skip length and label */
845 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
847 /* XXX: We actually initialise 'rr' here including its ttl field */
848 if (curanswer == result_we_want)
849 DNS::FillResourceRecord(&rr,&header.payload[i]);
852 if (rr.type != this->type)
858 if (rr.rr_class != this->rr_class)
866 if ((unsigned int)curanswer == header.ancount)
867 return std::make_pair((unsigned char*)NULL,"No more records");
869 if (i + rr.rdlength > (unsigned int)length)
870 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
872 if (rr.rdlength > 1023)
873 return std::make_pair((unsigned char*)NULL,"Resource record too large");
879 case DNS_QUERY_CNAME:
880 /* CNAME and PTR have the same processing code */
884 while (q == 0 && i < length && o + 256 < 1023)
886 if (header.payload[i] > 63)
888 memcpy(&ptr,&header.payload[i],2);
889 i = ntohs(ptr) - 0xC000 - 12;
893 if (header.payload[i] == 0)
902 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
903 o += header.payload[i];
904 i += header.payload[i] + 1;
911 memcpy(res,&header.payload[i],rr.rdlength);
912 res[rr.rdlength] = 0;
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 return std::make_pair(res,"No error");
926 /** Close the master socket */
929 ServerInstance->SE->Shutdown(this, 2);
930 ServerInstance->SE->Close(this);
931 ServerInstance->Timers->DelTimer(this->PruneTimer);
932 delete this->PruneTimer;
935 CachedQuery* DNS::GetCache(const std::string &source)
937 dnscache::iterator x = cache->find(source.c_str());
938 if (x != cache->end())
944 void DNS::DelCache(const std::string &source)
946 cache->erase(source.c_str());
949 void Resolver::TriggerCachedResult()
952 OnLookupComplete(CQ->data, time_left, true, 0);
955 /** High level abstraction of dns used by application at large */
956 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
960 CQ = ServerInstance->Res->GetCache(source);
963 time_left = CQ->CalcTTLRemaining();
966 ServerInstance->Res->DelCache(source);
980 this->myid = ServerInstance->Res->GetIP(source.c_str());
984 if (insp_aton(source.c_str(), &binip) > 0)
986 /* Valid ip address */
987 this->myid = ServerInstance->Res->GetName(&binip);
991 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
992 throw ModuleException("Resolver: Bad IP address");
998 querytype = DNS_QUERY_PTR;
999 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
1002 case DNS_QUERY_PTR6:
1003 querytype = DNS_QUERY_PTR;
1004 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
1007 case DNS_QUERY_AAAA:
1008 this->myid = ServerInstance->Res->GetIP6(source.c_str());
1011 case DNS_QUERY_CNAME:
1012 this->myid = ServerInstance->Res->GetCName(source.c_str());
1019 if (this->myid == -1)
1021 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1022 throw ModuleException("Resolver: Couldnt get an id to make a request");
1023 /* We shouldnt get here really */
1028 /** Called when an error occurs */
1029 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1031 /* Nothing in here */
1034 /** Destroy a resolver */
1035 Resolver::~Resolver()
1037 /* Nothing here (yet) either */
1040 /** Get the request id associated with this class */
1041 int Resolver::GetId()
1046 Module* Resolver::GetCreator()
1048 return this->Creator;
1051 /** Process a socket read event */
1052 void DNS::HandleEvent(EventType et, int errornum)
1054 /* Fetch the id and result of the next available packet */
1056 DNSResult res(0,"",0,"");
1058 ServerInstance->Log(DEBUG,"Handle DNS event");
1059 while ((res.id & ERROR_MASK) == 0)
1061 res = this->GetResult(resultnum);
1063 ServerInstance->Log(DEBUG,"Result %d id %d", resultnum, res.id);
1065 /* Is there a usable request id? */
1068 /* Its an error reply */
1069 if (res.id & ERROR_MASK)
1071 /* Mask off the error bit */
1072 res.id -= ERROR_MASK;
1073 /* Marshall the error to the correct class */
1074 if (Classes[res.id])
1076 if (ServerInstance && ServerInstance->stats)
1077 ServerInstance->stats->statsDnsBad++;
1078 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1079 delete Classes[res.id];
1080 Classes[res.id] = NULL;
1086 /* It is a non-error result, marshall the result to the correct class */
1087 if (Classes[res.id])
1089 if (ServerInstance && ServerInstance->stats)
1090 ServerInstance->stats->statsDnsGood++;
1092 if (!this->GetCache(res.original.c_str()))
1093 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1095 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false, resultnum);
1096 delete Classes[res.id];
1097 Classes[res.id] = NULL;
1101 if (ServerInstance && ServerInstance->stats)
1102 ServerInstance->stats->statsDns++;
1109 /** Add a derived Resolver to the working set */
1110 bool DNS::AddResolverClass(Resolver* r)
1112 /* Check the pointers validity and the id's validity */
1113 if ((r) && (r->GetId() > -1))
1115 /* Check the slot isnt already occupied -
1116 * This should NEVER happen unless we have
1117 * a severely broken DNS server somewhere
1119 if (!Classes[r->GetId()])
1121 /* Set up the pointer to the class */
1122 Classes[r->GetId()] = r;
1131 /* Pointer or id not valid.
1132 * Free the item and return
1141 void DNS::CleanResolvers(Module* module)
1143 for (int i = 0; i < MAX_REQUEST_ID; i++)
1147 if (Classes[i]->GetCreator() == module)
1149 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1157 /** Generate pseudo-random number */
1158 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();