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_sockaddr;
36 using irc::sockets::insp_inaddr;
37 using irc::sockets::insp_ntoa;
38 using irc::sockets::insp_aton;
39 using irc::sockets::OpenTCPSocket;
41 /** Masks to mask off the responses we get from the DNSRequest methods
45 ERROR_MASK = 0x10000 /* Result is an error */
48 /** Flags which can be ORed into a request or reply for different meanings
52 FLAGS_MASK_RD = 0x01, /* Recursive */
54 FLAGS_MASK_AA = 0x04, /* Authoritative */
55 FLAGS_MASK_OPCODE = 0x78,
57 FLAGS_MASK_RCODE = 0x0F, /* Request */
63 /** Represents a dns resource record (rr)
67 QueryType type; /* Record type */
68 unsigned int rr_class; /* Record class */
69 unsigned long ttl; /* Time to live */
70 unsigned int rdlength; /* Record length */
73 /** Represents a dns request/reply header, and its payload as opaque data.
78 unsigned char id[2]; /* Request id */
79 unsigned int flags1; /* Flags */
80 unsigned int flags2; /* Flags */
82 unsigned int ancount; /* Answer count */
83 unsigned int nscount; /* Nameserver count */
85 unsigned char payload[512]; /* Packet payload */
91 unsigned char id[2]; /* Request id */
92 unsigned char* res; /* Result processing buffer */
93 unsigned int rr_class; /* Request class */
94 QueryType type; /* Request type */
95 DNS* dnsobj; /* DNS caller (where we get our FD from) */
96 unsigned long ttl; /* Time to live */
97 std::string orig; /* Original requested name/ip */
99 DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
101 DNSInfo ResultIsReady(DNSHeader &h, int length);
102 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
105 class CacheTimer : public InspTimer
108 InspIRCd* ServerInstance;
111 CacheTimer(InspIRCd* Instance, DNS* thisdns)
112 : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
114 virtual void Tick(time_t TIME)
120 class RequestTimeout : public InspTimer
122 InspIRCd* ServerInstance;
126 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
130 void Tick(time_t TIME)
132 if (ServerInstance->Res->requests[watchid] == watch)
134 /* Still exists, whack it */
135 if (ServerInstance->Res->Classes[watchid])
137 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
138 delete ServerInstance->Res->Classes[watchid];
139 ServerInstance->Res->Classes[watchid] = NULL;
141 ServerInstance->Res->requests[watchid] = NULL;
148 /* Allocate the processing buffer */
149 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns)
151 res = new unsigned char[512];
154 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
155 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
158 /* Deallocate the processing buffer */
159 DNSRequest::~DNSRequest()
164 /** Fill a ResourceRecord class based on raw data input */
165 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
167 rr->type = (QueryType)((input[0] << 8) + input[1]);
168 rr->rr_class = (input[2] << 8) + input[3];
169 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
170 rr->rdlength = (input[8] << 8) + input[9];
173 /** Fill a DNSHeader class based on raw data input of a given length */
174 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
176 header->id[0] = input[0];
177 header->id[1] = input[1];
178 header->flags1 = input[2];
179 header->flags2 = input[3];
180 header->qdcount = (input[4] << 8) + input[5];
181 header->ancount = (input[6] << 8) + input[7];
182 header->nscount = (input[8] << 8) + input[9];
183 header->arcount = (input[10] << 8) + input[11];
184 memcpy(header->payload,&input[12],length);
187 /** Empty a DNSHeader class out into raw data, ready for transmission */
188 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
190 output[0] = header->id[0];
191 output[1] = header->id[1];
192 output[2] = header->flags1;
193 output[3] = header->flags2;
194 output[4] = header->qdcount >> 8;
195 output[5] = header->qdcount & 0xFF;
196 output[6] = header->ancount >> 8;
197 output[7] = header->ancount & 0xFF;
198 output[8] = header->nscount >> 8;
199 output[9] = header->nscount & 0xFF;
200 output[10] = header->arcount >> 8;
201 output[11] = header->arcount & 0xFF;
202 memcpy(&output[12],header->payload,length);
205 /** Send requests we have previously built down the UDP socket */
206 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
208 unsigned char payload[sizeof(DNSHeader)];
213 DNS::EmptyHeader(payload,header,length);
216 if (this->dnsobj->socketfamily == AF_INET6)
219 memset(&addr,0,sizeof(addr));
220 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
221 addr.sin6_family = AF_FAMILY;
222 addr.sin6_port = htons(DNS::QUERY_PORT);
223 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
229 memset(&addr,0,sizeof(addr));
230 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
231 addr.sin_family = AF_FAMILY;
232 addr.sin_port = htons(DNS::QUERY_PORT);
233 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
238 memset(&addr,0,sizeof(addr));
239 memcpy(&addr.sin_addr.s_addr, &dnsobj->myserver4, sizeof(addr.sin_addr));
240 addr.sin_family = AF_FAMILY;
241 addr.sin_port = htons(DNS::QUERY_PORT);
242 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
249 /** Add a query with a predefined header, and allocate an ID for it. */
250 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
252 /* Is the DNS connection down? */
253 if (this->GetFd() == -1)
257 id = this->PRNG() & DNS::MAX_REQUEST_ID;
259 /* If this id is already 'in flight', pick another. */
261 id = this->PRNG() & DNS::MAX_REQUEST_ID;
263 DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
265 header->id[0] = req->id[0] = id >> 8;
266 header->id[1] = req->id[1] = id & 0xFF;
267 header->flags1 = FLAGS_MASK_RD;
274 /* At this point we already know the id doesnt exist,
275 * so there needs to be no second check for the ::end()
279 /* According to the C++ spec, new never returns NULL. */
283 int DNS::ClearCache()
285 /* This ensures the buckets are reset to sane levels */
286 int rv = this->cache->size();
288 this->cache = new dnscache();
292 int DNS::PruneCache()
295 dnscache* newcache = new dnscache();
296 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
297 /* Dont include expired items (theres no point) */
298 if (i->second.CalcTTLRemaining())
299 newcache->insert(*i);
304 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);
345 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
347 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
350 /* Initialize mastersocket */
351 int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
354 /* Have we got a socket and is it nonblocking? */
355 if (this->GetFd() != -1)
357 /* Bind the port - port 0 INADDR_ANY */
358 if (!ServerInstance->BindSocket(this->GetFd(), 0, "", false))
361 shutdown(this->GetFd(),2);
362 close(this->GetFd());
366 if (this->GetFd() >= 0)
368 /* Hook the descriptor into the socket engine */
369 if (ServerInstance && ServerInstance->SE)
371 if (!ServerInstance->SE->AddFd(this))
373 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
374 shutdown(this->GetFd(),2);
375 close(this->GetFd());
383 /** Initialise the DNS UDP socket so that we can send requests */
384 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
386 /* Clear the Resolver class table */
387 memset(Classes,0,sizeof(Classes));
389 /* Clear the requests class table */
390 memset(requests,0,sizeof(requests));
392 /* Set the id of the next request to 0
396 /* DNS::Rehash() sets this to a valid ptr
400 /* Again, DNS::Rehash() sets this to a
405 /* Actually read the settings
409 this->PruneTimer = new CacheTimer(ServerInstance, this);
411 ServerInstance->Timers->AddTimer(this->PruneTimer);
414 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
415 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
417 short payloadpos = 0;
418 const char* tempchr, *tempchr2 = name;
419 unsigned short length;
421 /* split name up into labels, create query */
422 while ((tempchr = strchr(tempchr2,'.')) != NULL)
424 length = tempchr - tempchr2;
425 if (payloadpos + length + 1 > 507)
427 payload[payloadpos++] = length;
428 memcpy(&payload[payloadpos],tempchr2,length);
429 payloadpos += length;
430 tempchr2 = &tempchr[1];
432 length = strlen(tempchr2);
435 if (payloadpos + length + 2 > 507)
437 payload[payloadpos++] = length;
438 memcpy(&payload[payloadpos],tempchr2,length);
439 payloadpos += length;
440 payload[payloadpos++] = 0;
442 if (payloadpos > 508)
445 memcpy(&payload[payloadpos],&length,2);
446 length = htons(rr_class);
447 memcpy(&payload[payloadpos + 2],&length,2);
448 return payloadpos + 4;
451 /** Start lookup of an hostname to an IP address */
452 int DNS::GetIP(const char *name)
458 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
461 DNSRequest* req = this->AddQuery(&h, id, name);
463 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
469 /** Start lookup of an hostname to an IPv6 address */
470 int DNS::GetIP6(const char *name)
476 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
479 DNSRequest* req = this->AddQuery(&h, id, name);
481 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
487 /** Start lookup of a cname to another name */
488 int DNS::GetCName(const char *alias)
494 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
497 DNSRequest* req = this->AddQuery(&h, id, alias);
499 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
505 /** Start lookup of an IP address to a hostname */
506 int DNS::GetName(const insp_inaddr *ip)
514 unsigned char* c = (unsigned char*)&ip->s6_addr;
515 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
516 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
517 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
518 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
520 DNS::MakeIP6Int(query, (in6_addr*)ip);
522 unsigned char* c = (unsigned char*)&ip->s_addr;
523 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
526 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
529 DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
531 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
537 /** Start lookup of an IP address to a hostname */
538 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
540 ServerInstance->Log(DEBUG,"GetNameForce: %s", ip);
545 #ifdef SUPPORT_IP6LINKS
546 if (fp == PROTOCOL_IPV6)
549 if (inet_pton(AF_INET6, ip, &i) > 0)
551 ServerInstance->Log(DEBUG,"Resolve to ipv6");
552 DNS::MakeIP6Int(query, &i);
555 /* Invalid IP address */
562 if (inet_aton(ip, &i))
564 ServerInstance->Log(DEBUG,"Resolve to ipv4");
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)];
614 socklen_t x = sizeof(from);
615 const char* ipaddr_from = "";
616 unsigned short int port_from = 0;
618 int length = recvfrom(this->GetFd(),buffer,sizeof(DNSHeader),0,&from,&x);
620 /* Did we get the whole header? */
623 /* Nope - something screwed up. */
624 return DNSResult(-1,"",0,"");
627 /* Check wether the reply came from a different DNS
628 * server to the one we sent it to, or the source-port
630 * A user could in theory still spoof dns packets anyway
631 * but this is less trivial than just sending garbage
632 * to the client, which is possible without this check.
634 * -- Thanks jilles for pointing this one out.
637 ipaddr_from = insp_ntoa(((sockaddr_in6*)&from)->sin6_addr);
638 port_from = ntohs(((sockaddr_in6*)&from)->sin6_port);
640 ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
641 port_from = ntohs(((sockaddr_in*)&from)->sin_port);
644 /* We cant perform this security check if you're using 4in6.
645 * Tough luck to you, choose one or't other!
649 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
651 return DNSResult(-1,"",0,"");
655 /* Put the read header info into a header class */
656 DNS::FillHeader(&header,buffer,length - 12);
658 /* Get the id of this request.
659 * Its a 16 bit value stored in two char's,
660 * so we use logic shifts to create the value.
662 unsigned long this_id = header.id[1] + (header.id[0] << 8);
664 /* Do we have a pending request matching this id? */
665 if (!requests[this_id])
667 /* Somehow we got a DNS response for a request we never made... */
668 return DNSResult(-1,"",0,"");
672 /* Remove the query from the list of pending queries */
673 req = requests[this_id];
674 requests[this_id] = NULL;
677 /* Inform the DNSRequest class that it has a result to be read.
678 * When its finished it will return a DNSInfo which is a pair of
679 * unsigned char* resource record data, and an error message.
681 DNSInfo data = req->ResultIsReady(header, length);
682 std::string resultstr;
684 /* Check if we got a result, if we didnt, its an error */
685 if (data.first == NULL)
688 * Mask the ID with the value of ERROR_MASK, so that
689 * the dns_deal_with_classes() function knows that its
690 * an error response and needs to be treated uniquely.
691 * Put the error message in the second field.
693 std::string ro = req->orig;
695 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
699 unsigned long ttl = req->ttl;
702 /* Forward lookups come back as binary data. We must format them into ascii */
706 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
707 resultstr = formatted;
712 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
713 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
714 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
715 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
716 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
717 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
718 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
719 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
720 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
721 char* c = strstr(formatted,":0:");
724 memmove(c+1,c+2,strlen(c+2) + 1);
726 while (memcmp(c,"0:",2) == 0)
727 memmove(c,c+2,strlen(c+2) + 1);
728 if (memcmp(c,"0",2) == 0)
730 if (memcmp(formatted,"0::",3) == 0)
731 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
733 resultstr = formatted;
735 /* Special case. Sending ::1 around between servers
736 * and to clients is dangerous, because the : on the
737 * start makes the client or server interpret the IP
738 * as the last parameter on the line with a value ":1".
740 if (*formatted == ':')
741 resultstr = "0" + resultstr;
745 case DNS_QUERY_CNAME:
746 /* Identical handling to PTR */
749 /* Reverse lookups just come back as char* */
750 resultstr = std::string((const char*)data.first);
758 /* Build the reply with the id and hostname/ip in it */
759 std::string ro = req->orig;
761 return DNSResult(this_id,resultstr,ttl,ro);
765 /** A result is ready, process it */
766 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
774 /* This is just to keep _FORTIFY_SOURCE happy */
775 rr.type = DNS_QUERY_NONE;
777 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
779 if (!(header.flags1 & FLAGS_MASK_QR))
780 return std::make_pair((unsigned char*)NULL,"Not a query result");
782 if (header.flags1 & FLAGS_MASK_OPCODE)
783 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
785 if (header.flags2 & FLAGS_MASK_RCODE)
786 return std::make_pair((unsigned char*)NULL,"Domain name not found");
788 if (header.ancount < 1)
789 return std::make_pair((unsigned char*)NULL,"No resource records returned");
791 /* Subtract the length of the header from the length of the packet */
794 while ((unsigned int)q < header.qdcount && i < length)
796 if (header.payload[i] > 63)
803 if (header.payload[i] == 0)
808 else i += header.payload[i] + 1;
812 while ((unsigned)curanswer < header.ancount)
815 while (q == 0 && i < length)
817 if (header.payload[i] > 63)
824 if (header.payload[i] == 0)
829 else i += header.payload[i] + 1; /* skip length and label */
833 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
835 /* XXX: We actually initialise 'rr' here including its ttl field */
836 DNS::FillResourceRecord(&rr,&header.payload[i]);
838 if (rr.type != this->type)
844 if (rr.rr_class != this->rr_class)
852 if ((unsigned int)curanswer == header.ancount)
853 return std::make_pair((unsigned char*)NULL,"No valid answers");
855 if (i + rr.rdlength > (unsigned int)length)
856 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
858 if (rr.rdlength > 1023)
859 return std::make_pair((unsigned char*)NULL,"Resource record too large");
865 case DNS_QUERY_CNAME:
866 /* CNAME and PTR have the same processing code */
870 while (q == 0 && i < length && o + 256 < 1023)
872 if (header.payload[i] > 63)
874 memcpy(&ptr,&header.payload[i],2);
875 i = ntohs(ptr) - 0xC000 - 12;
879 if (header.payload[i] == 0)
888 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
889 o += header.payload[i];
890 i += header.payload[i] + 1;
897 memcpy(res,&header.payload[i],rr.rdlength);
898 res[rr.rdlength] = 0;
901 memcpy(res,&header.payload[i],rr.rdlength);
902 res[rr.rdlength] = 0;
905 memcpy(res,&header.payload[i],rr.rdlength);
906 res[rr.rdlength] = 0;
909 return std::make_pair(res,"No error");;
912 /** Close the master socket */
915 shutdown(this->GetFd(), 2);
916 close(this->GetFd());
917 ServerInstance->Timers->DelTimer(this->PruneTimer);
918 delete this->PruneTimer;
921 CachedQuery* DNS::GetCache(const std::string &source)
923 dnscache::iterator x = cache->find(source.c_str());
924 if (x != cache->end())
930 void DNS::DelCache(const std::string &source)
932 cache->erase(source.c_str());
935 void Resolver::TriggerCachedResult()
938 OnLookupComplete(CQ->data, time_left, true);
941 /** High level abstraction of dns used by application at large */
942 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
946 CQ = ServerInstance->Res->GetCache(source);
949 time_left = CQ->CalcTTLRemaining();
952 ServerInstance->Res->DelCache(source);
966 this->myid = ServerInstance->Res->GetIP(source.c_str());
970 if (insp_aton(source.c_str(), &binip) > 0)
972 /* Valid ip address */
973 this->myid = ServerInstance->Res->GetName(&binip);
977 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
978 throw ModuleException("Resolver: Bad IP address");
984 querytype = DNS_QUERY_PTR;
985 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
989 querytype = DNS_QUERY_PTR;
990 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
994 this->myid = ServerInstance->Res->GetIP6(source.c_str());
997 case DNS_QUERY_CNAME:
998 this->myid = ServerInstance->Res->GetCName(source.c_str());
1005 if (this->myid == -1)
1007 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1008 throw ModuleException("Resolver: Couldnt get an id to make a request");
1009 /* We shouldnt get here really */
1014 /** Called when an error occurs */
1015 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1017 /* Nothing in here */
1020 /** Destroy a resolver */
1021 Resolver::~Resolver()
1023 /* Nothing here (yet) either */
1026 /** Get the request id associated with this class */
1027 int Resolver::GetId()
1032 Module* Resolver::GetCreator()
1034 return this->Creator;
1037 /** Process a socket read event */
1038 void DNS::HandleEvent(EventType et, int errornum)
1040 /* Fetch the id and result of the next available packet */
1041 DNSResult res = this->GetResult();
1042 /* Is there a usable request id? */
1045 /* Its an error reply */
1046 if (res.id & ERROR_MASK)
1048 /* Mask off the error bit */
1049 res.id -= ERROR_MASK;
1050 /* Marshall the error to the correct class */
1051 if (Classes[res.id])
1053 if (ServerInstance && ServerInstance->stats)
1054 ServerInstance->stats->statsDnsBad++;
1055 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1056 delete Classes[res.id];
1057 Classes[res.id] = NULL;
1062 /* It is a non-error result, marshall the result to the correct class */
1063 if (Classes[res.id])
1065 if (ServerInstance && ServerInstance->stats)
1066 ServerInstance->stats->statsDnsGood++;
1068 if (!this->GetCache(res.original.c_str()))
1069 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1071 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1072 delete Classes[res.id];
1073 Classes[res.id] = NULL;
1077 if (ServerInstance && ServerInstance->stats)
1078 ServerInstance->stats->statsDns++;
1082 /** Add a derived Resolver to the working set */
1083 bool DNS::AddResolverClass(Resolver* r)
1085 /* Check the pointers validity and the id's validity */
1086 if ((r) && (r->GetId() > -1))
1088 /* Check the slot isnt already occupied -
1089 * This should NEVER happen unless we have
1090 * a severely broken DNS server somewhere
1092 if (!Classes[r->GetId()])
1094 /* Set up the pointer to the class */
1095 Classes[r->GetId()] = r;
1104 /* Pointer or id not valid.
1105 * Free the item and return
1114 void DNS::CleanResolvers(Module* module)
1116 for (int i = 0; i < MAX_REQUEST_ID; i++)
1120 if (Classes[i]->GetCreator() == module)
1122 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1130 /** Generate pseudo-random number */
1131 unsigned long DNS::PRNG()
1133 unsigned long val = 0;
1135 serverstats* s = ServerInstance->stats;
1136 gettimeofday(&n,NULL);
1137 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1138 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1139 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - s->BoundPortCount;