2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2012 William Pitcock <nenolod@dereferenced.org>
5 * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
6 * Copyright (C) 2006, 2009 Robin Burchell <robin+git@viroteck.net>
7 * Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
8 * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
9 * Copyright (C) 2005-2007 Craig Edwards <craigedwards@brainbox.cc>
11 * This file is part of InspIRCd. InspIRCd is free software: you can
12 * redistribute it and/or modify it under the terms of the GNU General Public
13 * License as published by the Free Software Foundation, version 2.
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 dns.cpp - Nonblocking DNS functions.
29 Very very loosely based on the firedns library,
30 Copyright (C) 2002 Ian Gulliver. This file is no
31 longer anything like firedns, there are many major
32 differences between this code and the original.
33 Please do not assume that firedns works like this,
34 looks like this, walks like this or tastes like this.
38 #include <sys/types.h>
39 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
44 #include "inspircd_win32wrapper.h"
48 #include "socketengine.h"
49 #include "configreader.h"
52 #define DN_COMP_BITMASK 0xC000 /* highest 6 bits in a DN label header */
54 /** Masks to mask off the responses we get from the DNSRequest methods
58 ERROR_MASK = 0x10000 /* Result is an error */
61 /** Flags which can be ORed into a request or reply for different meanings
65 FLAGS_MASK_RD = 0x01, /* Recursive */
67 FLAGS_MASK_AA = 0x04, /* Authoritative */
68 FLAGS_MASK_OPCODE = 0x78,
70 FLAGS_MASK_RCODE = 0x0F, /* Request */
76 /** Represents a dns resource record (rr)
80 QueryType type; /* Record type */
81 unsigned int rr_class; /* Record class */
82 unsigned long ttl; /* Time to live */
83 unsigned int rdlength; /* Record length */
86 /** Represents a dns request/reply header, and its payload as opaque data.
91 unsigned char id[2]; /* Request id */
92 unsigned int flags1; /* Flags */
93 unsigned int flags2; /* Flags */
95 unsigned int ancount; /* Answer count */
96 unsigned int nscount; /* Nameserver count */
98 unsigned char payload[512]; /* Packet payload */
104 unsigned char id[2]; /* Request id */
105 unsigned char* res; /* Result processing buffer */
106 unsigned int rr_class; /* Request class */
107 QueryType type; /* Request type */
108 DNS* dnsobj; /* DNS caller (where we get our FD from) */
109 unsigned long ttl; /* Time to live */
110 std::string orig; /* Original requested name/ip */
112 DNSRequest(DNS* dns, int id, const std::string &original);
114 DNSInfo ResultIsReady(DNSHeader &h, unsigned length);
115 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
118 class CacheTimer : public Timer
123 CacheTimer(DNS* thisdns)
124 : Timer(5*60, ServerInstance->Time(), true), dns(thisdns) { }
126 virtual void Tick(time_t)
132 class RequestTimeout : public Timer
137 RequestTimeout(unsigned long n, DNSRequest* watching, int id) : Timer(n, ServerInstance->Time()), watch(watching), watchid(id)
142 if (ServerInstance->Res)
148 if (ServerInstance->Res->requests[watchid] == watch)
150 /* Still exists, whack it */
151 if (ServerInstance->Res->Classes[watchid])
153 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
154 delete ServerInstance->Res->Classes[watchid];
155 ServerInstance->Res->Classes[watchid] = NULL;
157 ServerInstance->Res->requests[watchid] = NULL;
163 CachedQuery::CachedQuery(const std::string &res, QueryType qt, unsigned int ttl) : data(res), type(qt)
165 expires = ServerInstance->Time() + ttl;
168 int CachedQuery::CalcTTLRemaining()
170 int n = expires - ServerInstance->Time();
171 return (n < 0 ? 0 : n);
174 /* Allocate the processing buffer */
175 DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns)
177 /* hardening against overflow here: make our work buffer twice the theoretical
178 * maximum size so that hostile input doesn't screw us over.
180 res = new unsigned char[sizeof(DNSHeader) * 2];
183 RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid);
184 ServerInstance->Timers->AddTimer(RT); /* The timer manager frees this */
187 /* Deallocate the processing buffer */
188 DNSRequest::~DNSRequest()
193 /** Fill a ResourceRecord class based on raw data input */
194 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
196 rr->type = (QueryType)((input[0] << 8) + input[1]);
197 rr->rr_class = (input[2] << 8) + input[3];
198 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
199 rr->rdlength = (input[8] << 8) + input[9];
202 /** Fill a DNSHeader class based on raw data input of a given length */
203 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
205 header->id[0] = input[0];
206 header->id[1] = input[1];
207 header->flags1 = input[2];
208 header->flags2 = input[3];
209 header->qdcount = (input[4] << 8) + input[5];
210 header->ancount = (input[6] << 8) + input[7];
211 header->nscount = (input[8] << 8) + input[9];
212 header->arcount = (input[10] << 8) + input[11];
213 memcpy(header->payload,&input[12],length);
216 /** Empty a DNSHeader class out into raw data, ready for transmission */
217 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
219 output[0] = header->id[0];
220 output[1] = header->id[1];
221 output[2] = header->flags1;
222 output[3] = header->flags2;
223 output[4] = header->qdcount >> 8;
224 output[5] = header->qdcount & 0xFF;
225 output[6] = header->ancount >> 8;
226 output[7] = header->ancount & 0xFF;
227 output[8] = header->nscount >> 8;
228 output[9] = header->nscount & 0xFF;
229 output[10] = header->arcount >> 8;
230 output[11] = header->arcount & 0xFF;
231 memcpy(&output[12],header->payload,length);
234 /** Send requests we have previously built down the UDP socket */
235 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
237 ServerInstance->Logs->Log("RESOLVER", DEBUG,"DNSRequest::SendRequests");
239 unsigned char payload[sizeof(DNSHeader)];
244 DNS::EmptyHeader(payload,header,length);
246 if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, &(dnsobj->myserver.sa), sa_size(dnsobj->myserver)) != length+12)
249 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
253 /** Add a query with a predefined header, and allocate an ID for it. */
254 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
256 /* Is the DNS connection down? */
257 if (this->GetFd() == -1)
261 unsigned int tries = 0;
263 id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID);
264 if (++tries == DNS::MAX_REQUEST_ID*5)
266 // If we couldn't find an empty slot this many times, do a sequential scan as a last
267 // resort. If an empty slot is found that way, go on, otherwise throw an exception
269 for (int i = 0; i < DNS::MAX_REQUEST_ID; i++)
279 throw ModuleException("DNS: All ids are in use");
283 } while (requests[id]);
285 DNSRequest* req = new DNSRequest(this, id, original);
287 header->id[0] = req->id[0] = id >> 8;
288 header->id[1] = req->id[1] = id & 0xFF;
289 header->flags1 = FLAGS_MASK_RD;
296 /* At this point we already know the id doesnt exist,
297 * so there needs to be no second check for the ::end()
301 /* According to the C++ spec, new never returns NULL. */
305 int DNS::ClearCache()
307 /* This ensures the buckets are reset to sane levels */
308 int rv = this->cache->size();
310 this->cache = new dnscache();
314 int DNS::PruneCache()
317 dnscache* newcache = new dnscache();
318 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
319 /* Dont include expired items (theres no point) */
320 if (i->second.CalcTTLRemaining())
321 newcache->insert(*i);
326 this->cache = newcache;
332 if (this->GetFd() > -1)
334 ServerInstance->SE->DelFd(this);
335 ServerInstance->SE->Shutdown(this, 2);
336 ServerInstance->SE->Close(this);
339 /* Rehash the cache */
344 /* Create initial dns cache */
345 this->cache = new dnscache();
348 irc::sockets::aptosa(ServerInstance->Config->DNSServer, DNS::QUERY_PORT, myserver);
350 /* Initialize mastersocket */
351 int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0);
354 /* Have we got a socket and is it nonblocking? */
355 if (this->GetFd() != -1)
357 ServerInstance->SE->SetReuse(s);
358 ServerInstance->SE->NonBlocking(s);
359 irc::sockets::sockaddrs bindto;
360 memset(&bindto, 0, sizeof(bindto));
361 bindto.sa.sa_family = myserver.sa.sa_family;
362 if (ServerInstance->SE->Bind(this->GetFd(), bindto) < 0)
365 ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error binding dns socket - hostnames will NOT resolve");
366 ServerInstance->SE->Shutdown(this, 2);
367 ServerInstance->SE->Close(this);
370 else if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
372 ServerInstance->Logs->Log("RESOLVER",SPARSE,"Internal error starting DNS - hostnames will NOT resolve.");
373 ServerInstance->SE->Shutdown(this, 2);
374 ServerInstance->SE->Close(this);
380 ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error creating DNS socket - hostnames will NOT resolve");
384 /** Initialise the DNS UDP socket so that we can send requests */
387 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
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 /* DNS::Rehash() sets this to a valid ptr
398 /* Again, DNS::Rehash() sets this to a
403 /* Actually read the settings
407 this->PruneTimer = new CacheTimer(this);
409 ServerInstance->Timers->AddTimer(this->PruneTimer);
412 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
413 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
415 short payloadpos = 0;
416 const char* tempchr, *tempchr2 = name;
417 unsigned short length;
419 /* split name up into labels, create query */
420 while ((tempchr = strchr(tempchr2,'.')) != NULL)
422 length = tempchr - tempchr2;
423 if (payloadpos + length + 1 > 507)
425 payload[payloadpos++] = length;
426 memcpy(&payload[payloadpos],tempchr2,length);
427 payloadpos += length;
428 tempchr2 = &tempchr[1];
430 length = strlen(tempchr2);
433 if (payloadpos + length + 2 > 507)
435 payload[payloadpos++] = length;
436 memcpy(&payload[payloadpos],tempchr2,length);
437 payloadpos += length;
438 payload[payloadpos++] = 0;
440 if (payloadpos > 508)
443 memcpy(&payload[payloadpos],&length,2);
444 length = htons(rr_class);
445 memcpy(&payload[payloadpos + 2],&length,2);
446 return payloadpos + 4;
449 /** Start lookup of an hostname to an IP address */
450 int DNS::GetIP(const char *name)
456 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
459 DNSRequest* req = this->AddQuery(&h, id, name);
461 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
467 /** Start lookup of an hostname to an IPv6 address */
468 int DNS::GetIP6(const char *name)
474 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
477 DNSRequest* req = this->AddQuery(&h, id, name);
479 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
485 /** Start lookup of a cname to another name */
486 int DNS::GetCName(const char *alias)
492 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
495 DNSRequest* req = this->AddQuery(&h, id, alias);
497 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
503 /** Start lookup of an IP address to a hostname */
504 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
511 if (fp == PROTOCOL_IPV6)
514 if (inet_pton(AF_INET6, ip, &i) > 0)
516 DNS::MakeIP6Int(query, &i);
520 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv6 bad format for '%s'", ip);
521 /* Invalid IP address */
528 if (inet_aton(ip, &i))
530 unsigned char* c = (unsigned char*)&i.s_addr;
531 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
535 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv4 bad format for '%s'", ip);
536 /* Invalid IP address */
541 length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload);
544 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't query '%s' using '%s' because it's too long", ip, query);
548 DNSRequest* req = this->AddQuery(&h, id, ip);
552 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't add query (resolver down?)");
556 if (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)
558 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't send (firewall?)");
565 /** Build an ipv6 reverse domain from an in6_addr
567 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
569 const char* hex = "0123456789abcdef";
570 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
574 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
577 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
578 *query++ = '.'; /* Seperator */
580 strcpy(query,"ip6.arpa"); /* Suffix the string */
583 /** Return the next id which is ready, and the result attached to it */
584 DNSResult DNS::GetResult()
586 /* Fetch dns query response and decide where it belongs */
589 unsigned char buffer[sizeof(DNSHeader)];
590 irc::sockets::sockaddrs from;
591 memset(&from, 0, sizeof(from));
592 socklen_t x = sizeof(from);
594 int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);
596 /* Did we get the whole header? */
599 ServerInstance->Logs->Log("RESOLVER",DEBUG,"GetResult didn't get a full packet (len=%d)", length);
600 /* Nope - something screwed up. */
601 return DNSResult(-1,"",0,"");
604 /* Check wether the reply came from a different DNS
605 * server to the one we sent it to, or the source-port
607 * A user could in theory still spoof dns packets anyway
608 * but this is less trivial than just sending garbage
609 * to the server, which is possible without this check.
611 * -- Thanks jilles for pointing this one out.
613 if (from != myserver)
615 std::string server1 = from.str();
616 std::string server2 = myserver.str();
617 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
618 server1.c_str(), server2.c_str());
619 return DNSResult(-1,"",0,"");
622 /* Put the read header info into a header class */
623 DNS::FillHeader(&header,buffer,length - 12);
625 /* Get the id of this request.
626 * Its a 16 bit value stored in two char's,
627 * so we use logic shifts to create the value.
629 unsigned long this_id = header.id[1] + (header.id[0] << 8);
631 /* Do we have a pending request matching this id? */
632 if (!requests[this_id])
634 /* Somehow we got a DNS response for a request we never made... */
635 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Hmm, got a result that we didn't ask for (id=%lx). Ignoring.", this_id);
636 return DNSResult(-1,"",0,"");
640 /* Remove the query from the list of pending queries */
641 req = requests[this_id];
642 requests[this_id] = NULL;
645 /* Inform the DNSRequest class that it has a result to be read.
646 * When its finished it will return a DNSInfo which is a pair of
647 * unsigned char* resource record data, and an error message.
649 DNSInfo data = req->ResultIsReady(header, length);
650 std::string resultstr;
652 /* Check if we got a result, if we didnt, its an error */
653 if (data.first == NULL)
656 * Mask the ID with the value of ERROR_MASK, so that
657 * the dns_deal_with_classes() function knows that its
658 * an error response and needs to be treated uniquely.
659 * Put the error message in the second field.
661 std::string ro = req->orig;
663 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
667 unsigned long ttl = req->ttl;
670 /* Forward lookups come back as binary data. We must format them into ascii */
674 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
675 resultstr = formatted;
680 if (!inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted)))
682 std::string ro = req->orig;
684 return DNSResult(this_id | ERROR_MASK, "inet_ntop() failed", 0, ro);
687 resultstr = formatted;
689 /* Special case. Sending ::1 around between servers
690 * and to clients is dangerous, because the : on the
691 * start makes the client or server interpret the IP
692 * as the last parameter on the line with a value ":1".
694 if (*formatted == ':')
695 resultstr.insert(0, "0");
699 case DNS_QUERY_CNAME:
700 /* Identical handling to PTR */
704 /* Reverse lookups just come back as char* */
705 resultstr = std::string((const char*)data.first);
706 if (resultstr.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-") != std::string::npos)
708 std::string ro = req->orig;
710 return DNSResult(this_id | ERROR_MASK, "Invalid char(s) in reply", 0, ro);
719 /* Build the reply with the id and hostname/ip in it */
720 std::string ro = req->orig;
721 DNSResult result = DNSResult(this_id,resultstr,ttl,ro,req->type);
727 /** A result is ready, process it */
728 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, unsigned length)
736 /* This is just to keep _FORTIFY_SOURCE happy */
737 rr.type = DNS_QUERY_NONE;
739 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
740 rr.rr_class = 0; /* Same for VC++ */
742 if (!(header.flags1 & FLAGS_MASK_QR))
743 return std::make_pair((unsigned char*)NULL,"Not a query result");
745 if (header.flags1 & FLAGS_MASK_OPCODE)
746 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
748 if (header.flags2 & FLAGS_MASK_RCODE)
749 return std::make_pair((unsigned char*)NULL,"Domain name not found");
751 if (header.ancount < 1)
752 return std::make_pair((unsigned char*)NULL,"No resource records returned");
754 /* Subtract the length of the header from the length of the packet */
757 while ((unsigned int)q < header.qdcount && i < length)
759 if (header.payload[i] > 63)
766 if (header.payload[i] == 0)
771 else i += header.payload[i] + 1;
775 while ((unsigned)curanswer < header.ancount)
778 while (q == 0 && i < length)
780 if (header.payload[i] > 63)
787 if (header.payload[i] == 0)
792 else i += header.payload[i] + 1; /* skip length and label */
795 if (static_cast<int>(length - i) < 10)
796 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
798 /* XXX: We actually initialise 'rr' here including its ttl field */
799 DNS::FillResourceRecord(&rr,&header.payload[i]);
802 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver: rr.type is %d and this.type is %d rr.class %d this.class %d", rr.type, this->type, rr.rr_class, this->rr_class);
803 if (rr.type != this->type)
809 if (rr.rr_class != this->rr_class)
817 if ((unsigned int)curanswer == header.ancount)
818 return std::make_pair((unsigned char*)NULL,"No A, AAAA or PTR type answers (" + ConvToStr(header.ancount) + " answers)");
820 if (i + rr.rdlength > (unsigned int)length)
821 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
823 if (rr.rdlength > 1023)
824 return std::make_pair((unsigned char*)NULL,"Resource record too large");
831 * CNAME and PTR are compressed. We need to decompress them.
833 case DNS_QUERY_CNAME:
836 unsigned short lowest_pos = length;
839 while (q == 0 && i < length && o + 256 < 1023)
841 /* DN label found (byte over 63) */
842 if (header.payload[i] > 63)
844 memcpy(&ptr,&header.payload[i],2);
848 /* check that highest two bits are set. if not, we've been had */
849 if ((i & DN_COMP_BITMASK) != DN_COMP_BITMASK)
850 return std::make_pair((unsigned char *) NULL, "DN label decompression header is bogus");
852 /* mask away the two highest bits. */
853 i &= ~DN_COMP_BITMASK;
855 /* and decrease length by 12 bytes. */
859 return std::make_pair((unsigned char *) NULL, "Invalid decompression pointer");
864 if (header.payload[i] == 0)
874 if (o + header.payload[i] > sizeof(DNSHeader))
875 return std::make_pair((unsigned char *) NULL, "DN label decompression is impossible -- malformed/hostile packet?");
877 memcpy(&res[o], &header.payload[i + 1], header.payload[i]);
878 o += header.payload[i];
879 i += header.payload[i] + 1;
887 if (rr.rdlength != sizeof(struct in6_addr))
888 return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 16 bytes for an ipv6 entry -- malformed/hostile packet?");
890 memcpy(res,&header.payload[i],rr.rdlength);
891 res[rr.rdlength] = 0;
894 if (rr.rdlength != sizeof(struct in_addr))
895 return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 4 bytes for an ipv4 entry -- malformed/hostile packet?");
897 memcpy(res,&header.payload[i],rr.rdlength);
898 res[rr.rdlength] = 0;
901 return std::make_pair((unsigned char *) NULL, "don't know how to handle undefined type (" + ConvToStr(rr.type) + ") -- rejecting");
904 return std::make_pair(res,"No error");
907 /** Close the master socket */
910 ServerInstance->SE->Shutdown(this, 2);
911 ServerInstance->SE->Close(this);
912 ServerInstance->Timers->DelTimer(this->PruneTimer);
917 CachedQuery* DNS::GetCache(const std::string &source)
919 dnscache::iterator x = cache->find(source.c_str());
920 if (x != cache->end())
926 void DNS::DelCache(const std::string &source)
928 cache->erase(source.c_str());
931 void Resolver::TriggerCachedResult()
934 OnLookupComplete(CQ->data, time_left, true);
937 /** High level abstraction of dns used by application at large */
938 Resolver::Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator) : Creator(creator), input(source), querytype(qt)
940 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
943 CQ = ServerInstance->Res->GetCache(source);
946 time_left = CQ->CalcTTLRemaining();
949 ServerInstance->Res->DelCache(source);
951 else if (CQ->type == qt)
962 this->myid = ServerInstance->Res->GetIP(source.c_str());
966 querytype = DNS_QUERY_PTR;
967 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
971 querytype = DNS_QUERY_PTR;
972 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
976 this->myid = ServerInstance->Res->GetIP6(source.c_str());
979 case DNS_QUERY_CNAME:
980 this->myid = ServerInstance->Res->GetCName(source.c_str());
984 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request with unknown query type %d", querytype);
988 if (this->myid == -1)
990 throw ModuleException("Resolver: Couldn't get an id to make a request");
994 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
998 /** Called when an error occurs */
999 void Resolver::OnError(ResolverError, const std::string&)
1001 /* Nothing in here */
1004 /** Destroy a resolver */
1005 Resolver::~Resolver()
1007 /* Nothing here (yet) either */
1010 /** Get the request id associated with this class */
1011 int Resolver::GetId()
1016 Module* Resolver::GetCreator()
1018 return this->Creator;
1021 /** Process a socket read event */
1022 void DNS::HandleEvent(EventType, int)
1024 /* Fetch the id and result of the next available packet */
1025 DNSResult res(0,"",0,"");
1027 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
1029 res = this->GetResult();
1031 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result id %d", res.id);
1033 /* Is there a usable request id? */
1036 /* Its an error reply */
1037 if (res.id & ERROR_MASK)
1039 /* Mask off the error bit */
1040 res.id -= ERROR_MASK;
1041 /* Marshall the error to the correct class */
1042 if (Classes[res.id])
1044 if (ServerInstance && ServerInstance->stats)
1045 ServerInstance->stats->statsDnsBad++;
1046 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1047 delete Classes[res.id];
1048 Classes[res.id] = NULL;
1054 /* It is a non-error result, marshall the result to the correct class */
1055 if (Classes[res.id])
1057 if (ServerInstance && ServerInstance->stats)
1058 ServerInstance->stats->statsDnsGood++;
1060 if (!this->GetCache(res.original.c_str()))
1061 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.type, res.ttl)));
1063 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1064 delete Classes[res.id];
1065 Classes[res.id] = NULL;
1069 if (ServerInstance && ServerInstance->stats)
1070 ServerInstance->stats->statsDns++;
1074 /** Add a derived Resolver to the working set */
1075 bool DNS::AddResolverClass(Resolver* r)
1077 ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
1078 /* Check the pointers validity and the id's validity */
1079 if ((r) && (r->GetId() > -1))
1081 /* Check the slot isnt already occupied -
1082 * This should NEVER happen unless we have
1083 * a severely broken DNS server somewhere
1085 if (!Classes[r->GetId()])
1087 /* Set up the pointer to the class */
1088 Classes[r->GetId()] = r;
1093 /* Pointer or id not valid, or duplicate id.
1094 * Free the item and return
1100 void DNS::CleanResolvers(Module* module)
1102 for (int i = 0; i < MAX_REQUEST_ID; i++)
1106 if (Classes[i]->GetCreator() == module)
1108 Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading");