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)
167 expires = ServerInstance->Time() + ttl;
170 int CachedQuery::CalcTTLRemaining()
172 int n = expires - ServerInstance->Time();
173 return (n < 0 ? 0 : n);
176 /* Allocate the processing buffer */
177 DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns)
179 /* hardening against overflow here: make our work buffer twice the theoretical
180 * maximum size so that hostile input doesn't screw us over.
182 res = new unsigned char[sizeof(DNSHeader) * 2];
185 RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid);
186 ServerInstance->Timers->AddTimer(RT); /* The timer manager frees this */
189 /* Deallocate the processing buffer */
190 DNSRequest::~DNSRequest()
195 /** Fill a ResourceRecord class based on raw data input */
196 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
198 rr->type = (QueryType)((input[0] << 8) + input[1]);
199 rr->rr_class = (input[2] << 8) + input[3];
200 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
201 rr->rdlength = (input[8] << 8) + input[9];
204 /** Fill a DNSHeader class based on raw data input of a given length */
205 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
207 header->id[0] = input[0];
208 header->id[1] = input[1];
209 header->flags1 = input[2];
210 header->flags2 = input[3];
211 header->qdcount = (input[4] << 8) + input[5];
212 header->ancount = (input[6] << 8) + input[7];
213 header->nscount = (input[8] << 8) + input[9];
214 header->arcount = (input[10] << 8) + input[11];
215 memcpy(header->payload,&input[12],length);
218 /** Empty a DNSHeader class out into raw data, ready for transmission */
219 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
221 output[0] = header->id[0];
222 output[1] = header->id[1];
223 output[2] = header->flags1;
224 output[3] = header->flags2;
225 output[4] = header->qdcount >> 8;
226 output[5] = header->qdcount & 0xFF;
227 output[6] = header->ancount >> 8;
228 output[7] = header->ancount & 0xFF;
229 output[8] = header->nscount >> 8;
230 output[9] = header->nscount & 0xFF;
231 output[10] = header->arcount >> 8;
232 output[11] = header->arcount & 0xFF;
233 memcpy(&output[12],header->payload,length);
236 /** Send requests we have previously built down the UDP socket */
237 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
239 ServerInstance->Logs->Log("RESOLVER", DEBUG,"DNSRequest::SendRequests");
241 unsigned char payload[sizeof(DNSHeader)];
246 DNS::EmptyHeader(payload,header,length);
248 if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, &(dnsobj->myserver.sa), sa_size(dnsobj->myserver)) != length+12)
251 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
255 /** Add a query with a predefined header, and allocate an ID for it. */
256 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
258 /* Is the DNS connection down? */
259 if (this->GetFd() == -1)
263 unsigned int tries = 0;
265 id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID);
266 if (++tries == DNS::MAX_REQUEST_ID*5)
268 // If we couldn't find an empty slot this many times, do a sequential scan as a last
269 // resort. If an empty slot is found that way, go on, otherwise throw an exception
271 for (int i = 0; i < DNS::MAX_REQUEST_ID; i++)
281 throw ModuleException("DNS: All ids are in use");
285 } while (requests[id]);
287 DNSRequest* req = new DNSRequest(this, id, original);
289 header->id[0] = req->id[0] = id >> 8;
290 header->id[1] = req->id[1] = id & 0xFF;
291 header->flags1 = FLAGS_MASK_RD;
298 /* At this point we already know the id doesnt exist,
299 * so there needs to be no second check for the ::end()
303 /* According to the C++ spec, new never returns NULL. */
307 int DNS::ClearCache()
309 /* This ensures the buckets are reset to sane levels */
310 int rv = this->cache->size();
312 this->cache = new dnscache();
316 int DNS::PruneCache()
319 dnscache* newcache = new dnscache();
320 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
321 /* Dont include expired items (theres no point) */
322 if (i->second.CalcTTLRemaining())
323 newcache->insert(*i);
328 this->cache = newcache;
334 if (this->GetFd() > -1)
336 ServerInstance->SE->DelFd(this);
337 ServerInstance->SE->Shutdown(this, 2);
338 ServerInstance->SE->Close(this);
341 /* Rehash the cache */
346 /* Create initial dns cache */
347 this->cache = new dnscache();
350 irc::sockets::aptosa(ServerInstance->Config->DNSServer, DNS::QUERY_PORT, myserver);
352 /* Initialize mastersocket */
353 int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0);
356 /* Have we got a socket and is it nonblocking? */
357 if (this->GetFd() != -1)
359 ServerInstance->SE->SetReuse(s);
360 ServerInstance->SE->NonBlocking(s);
361 irc::sockets::sockaddrs bindto;
362 memset(&bindto, 0, sizeof(bindto));
363 bindto.sa.sa_family = myserver.sa.sa_family;
364 if (ServerInstance->SE->Bind(this->GetFd(), bindto) < 0)
367 ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error binding dns socket - hostnames will NOT resolve");
368 ServerInstance->SE->Shutdown(this, 2);
369 ServerInstance->SE->Close(this);
372 else if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
374 ServerInstance->Logs->Log("RESOLVER",SPARSE,"Internal error starting DNS - hostnames will NOT resolve.");
375 ServerInstance->SE->Shutdown(this, 2);
376 ServerInstance->SE->Close(this);
382 ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error creating DNS socket - hostnames will NOT resolve");
386 /** Initialise the DNS UDP socket so that we can send requests */
389 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
390 /* Clear the Resolver class table */
391 memset(Classes,0,sizeof(Classes));
393 /* Clear the requests class table */
394 memset(requests,0,sizeof(requests));
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(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::GetNameForce(const char *ip, ForceProtocol fp)
513 if (fp == PROTOCOL_IPV6)
516 if (inet_pton(AF_INET6, ip, &i) > 0)
518 DNS::MakeIP6Int(query, &i);
522 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv6 bad format for '%s'", ip);
523 /* Invalid IP address */
530 if (inet_aton(ip, &i))
532 unsigned char* c = (unsigned char*)&i.s_addr;
533 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
537 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv4 bad format for '%s'", ip);
538 /* Invalid IP address */
543 length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload);
546 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't query '%s' using '%s' because it's too long", ip, query);
550 DNSRequest* req = this->AddQuery(&h, id, ip);
554 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't add query (resolver down?)");
558 if (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)
560 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't send (firewall?)");
567 /** Build an ipv6 reverse domain from an in6_addr
569 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
571 const char* hex = "0123456789abcdef";
572 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
576 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
579 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
580 *query++ = '.'; /* Seperator */
582 strcpy(query,"ip6.arpa"); /* Suffix the string */
585 /** Return the next id which is ready, and the result attached to it */
586 DNSResult DNS::GetResult()
588 /* Fetch dns query response and decide where it belongs */
591 unsigned char buffer[sizeof(DNSHeader)];
592 irc::sockets::sockaddrs from;
593 memset(&from, 0, sizeof(from));
594 socklen_t x = sizeof(from);
596 int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);
598 /* Did we get the whole header? */
601 ServerInstance->Logs->Log("RESOLVER",DEBUG,"GetResult didn't get a full packet (len=%d)", length);
602 /* Nope - something screwed up. */
603 return DNSResult(-1,"",0,"");
606 /* Check wether the reply came from a different DNS
607 * server to the one we sent it to, or the source-port
609 * A user could in theory still spoof dns packets anyway
610 * but this is less trivial than just sending garbage
611 * to the server, which is possible without this check.
613 * -- Thanks jilles for pointing this one out.
615 if (from != myserver)
617 std::string server1 = from.str();
618 std::string server2 = myserver.str();
619 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
620 server1.c_str(), server2.c_str());
621 return DNSResult(-1,"",0,"");
624 /* Put the read header info into a header class */
625 DNS::FillHeader(&header,buffer,length - 12);
627 /* Get the id of this request.
628 * Its a 16 bit value stored in two char's,
629 * so we use logic shifts to create the value.
631 unsigned long this_id = header.id[1] + (header.id[0] << 8);
633 /* Do we have a pending request matching this id? */
634 if (!requests[this_id])
636 /* Somehow we got a DNS response for a request we never made... */
637 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Hmm, got a result that we didn't ask for (id=%lx). Ignoring.", this_id);
638 return DNSResult(-1,"",0,"");
642 /* Remove the query from the list of pending queries */
643 req = requests[this_id];
644 requests[this_id] = NULL;
647 /* Inform the DNSRequest class that it has a result to be read.
648 * When its finished it will return a DNSInfo which is a pair of
649 * unsigned char* resource record data, and an error message.
651 DNSInfo data = req->ResultIsReady(header, length);
652 std::string resultstr;
654 /* Check if we got a result, if we didnt, its an error */
655 if (data.first == NULL)
658 * Mask the ID with the value of ERROR_MASK, so that
659 * the dns_deal_with_classes() function knows that its
660 * an error response and needs to be treated uniquely.
661 * Put the error message in the second field.
663 std::string ro = req->orig;
665 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
669 unsigned long ttl = req->ttl;
672 /* Forward lookups come back as binary data. We must format them into ascii */
676 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
677 resultstr = formatted;
682 if (!inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted)))
684 std::string ro = req->orig;
686 return DNSResult(this_id | ERROR_MASK, "inet_ntop() failed", 0, ro);
689 resultstr = formatted;
691 /* Special case. Sending ::1 around between servers
692 * and to clients is dangerous, because the : on the
693 * start makes the client or server interpret the IP
694 * as the last parameter on the line with a value ":1".
696 if (*formatted == ':')
697 resultstr.insert(0, "0");
701 case DNS_QUERY_CNAME:
702 /* Identical handling to PTR */
706 /* Reverse lookups just come back as char* */
707 resultstr = std::string((const char*)data.first);
708 if (resultstr.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-") != std::string::npos)
710 std::string ro = req->orig;
712 return DNSResult(this_id | ERROR_MASK, "Invalid char(s) in reply", 0, ro);
721 /* Build the reply with the id and hostname/ip in it */
722 std::string ro = req->orig;
723 DNSResult result = DNSResult(this_id,resultstr,ttl,ro,req->type);
729 /** A result is ready, process it */
730 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, unsigned length)
738 /* This is just to keep _FORTIFY_SOURCE happy */
739 rr.type = DNS_QUERY_NONE;
741 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
742 rr.rr_class = 0; /* Same for VC++ */
744 if (!(header.flags1 & FLAGS_MASK_QR))
745 return std::make_pair((unsigned char*)NULL,"Not a query result");
747 if (header.flags1 & FLAGS_MASK_OPCODE)
748 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
750 if (header.flags2 & FLAGS_MASK_RCODE)
751 return std::make_pair((unsigned char*)NULL,"Domain name not found");
753 if (header.ancount < 1)
754 return std::make_pair((unsigned char*)NULL,"No resource records returned");
756 /* Subtract the length of the header from the length of the packet */
759 while ((unsigned int)q < header.qdcount && i < length)
761 if (header.payload[i] > 63)
768 if (header.payload[i] == 0)
773 else i += header.payload[i] + 1;
777 while ((unsigned)curanswer < header.ancount)
780 while (q == 0 && i < length)
782 if (header.payload[i] > 63)
789 if (header.payload[i] == 0)
794 else i += header.payload[i] + 1; /* skip length and label */
797 if (static_cast<int>(length - i) < 10)
798 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
800 /* XXX: We actually initialise 'rr' here including its ttl field */
801 DNS::FillResourceRecord(&rr,&header.payload[i]);
804 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);
805 if (rr.type != this->type)
811 if (rr.rr_class != this->rr_class)
819 if ((unsigned int)curanswer == header.ancount)
820 return std::make_pair((unsigned char*)NULL,"No A, AAAA or PTR type answers (" + ConvToStr(header.ancount) + " answers)");
822 if (i + rr.rdlength > (unsigned int)length)
823 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
825 if (rr.rdlength > 1023)
826 return std::make_pair((unsigned char*)NULL,"Resource record too large");
833 * CNAME and PTR are compressed. We need to decompress them.
835 case DNS_QUERY_CNAME:
838 unsigned short lowest_pos = length;
841 while (q == 0 && i < length && o + 256 < 1023)
843 /* DN label found (byte over 63) */
844 if (header.payload[i] > 63)
846 memcpy(&ptr,&header.payload[i],2);
850 /* check that highest two bits are set. if not, we've been had */
851 if ((i & DN_COMP_BITMASK) != DN_COMP_BITMASK)
852 return std::make_pair((unsigned char *) NULL, "DN label decompression header is bogus");
854 /* mask away the two highest bits. */
855 i &= ~DN_COMP_BITMASK;
857 /* and decrease length by 12 bytes. */
861 return std::make_pair((unsigned char *) NULL, "Invalid decompression pointer");
866 if (header.payload[i] == 0)
876 if (o + header.payload[i] > sizeof(DNSHeader))
877 return std::make_pair((unsigned char *) NULL, "DN label decompression is impossible -- malformed/hostile packet?");
879 memcpy(&res[o], &header.payload[i + 1], header.payload[i]);
880 o += header.payload[i];
881 i += header.payload[i] + 1;
889 if (rr.rdlength != sizeof(struct in6_addr))
890 return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 16 bytes for an ipv6 entry -- malformed/hostile packet?");
892 memcpy(res,&header.payload[i],rr.rdlength);
893 res[rr.rdlength] = 0;
896 if (rr.rdlength != sizeof(struct in_addr))
897 return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 4 bytes for an ipv4 entry -- malformed/hostile packet?");
899 memcpy(res,&header.payload[i],rr.rdlength);
900 res[rr.rdlength] = 0;
903 return std::make_pair((unsigned char *) NULL, "don't know how to handle undefined type (" + ConvToStr(rr.type) + ") -- rejecting");
906 return std::make_pair(res,"No error");
909 /** Close the master socket */
912 ServerInstance->SE->Shutdown(this, 2);
913 ServerInstance->SE->Close(this);
914 ServerInstance->Timers->DelTimer(this->PruneTimer);
919 CachedQuery* DNS::GetCache(const std::string &source)
921 dnscache::iterator x = cache->find(source.c_str());
922 if (x != cache->end())
928 void DNS::DelCache(const std::string &source)
930 cache->erase(source.c_str());
933 void Resolver::TriggerCachedResult()
936 OnLookupComplete(CQ->data, time_left, true);
939 /** High level abstraction of dns used by application at large */
940 Resolver::Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator) : Creator(creator), input(source), querytype(qt)
942 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
945 CQ = ServerInstance->Res->GetCache(source);
948 time_left = CQ->CalcTTLRemaining();
951 ServerInstance->Res->DelCache(source);
953 else if (CQ->type == qt)
964 this->myid = ServerInstance->Res->GetIP(source.c_str());
968 querytype = DNS_QUERY_PTR;
969 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
973 querytype = DNS_QUERY_PTR;
974 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
978 this->myid = ServerInstance->Res->GetIP6(source.c_str());
981 case DNS_QUERY_CNAME:
982 this->myid = ServerInstance->Res->GetCName(source.c_str());
986 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request with unknown query type %d", querytype);
990 if (this->myid == -1)
992 throw ModuleException("Resolver: Couldn't get an id to make a request");
996 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
1000 /** Called when an error occurs */
1001 void Resolver::OnError(ResolverError, const std::string&)
1003 /* Nothing in here */
1006 /** Destroy a resolver */
1007 Resolver::~Resolver()
1009 /* Nothing here (yet) either */
1012 /** Get the request id associated with this class */
1013 int Resolver::GetId()
1018 Module* Resolver::GetCreator()
1020 return this->Creator;
1023 /** Process a socket read event */
1024 void DNS::HandleEvent(EventType, int)
1026 /* Fetch the id and result of the next available packet */
1027 DNSResult res(0,"",0,"");
1029 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
1031 res = this->GetResult();
1033 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result id %d", res.id);
1035 /* Is there a usable request id? */
1038 /* Its an error reply */
1039 if (res.id & ERROR_MASK)
1041 /* Mask off the error bit */
1042 res.id -= ERROR_MASK;
1043 /* Marshall the error to the correct class */
1044 if (Classes[res.id])
1046 if (ServerInstance && ServerInstance->stats)
1047 ServerInstance->stats->statsDnsBad++;
1048 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1049 delete Classes[res.id];
1050 Classes[res.id] = NULL;
1056 /* It is a non-error result, marshall the result to the correct class */
1057 if (Classes[res.id])
1059 if (ServerInstance && ServerInstance->stats)
1060 ServerInstance->stats->statsDnsGood++;
1062 if (!this->GetCache(res.original.c_str()))
1064 if (cache->size() >= MAX_CACHE_SIZE)
1066 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.type, res.ttl)));
1069 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1070 delete Classes[res.id];
1071 Classes[res.id] = NULL;
1075 if (ServerInstance && ServerInstance->stats)
1076 ServerInstance->stats->statsDns++;
1080 /** Add a derived Resolver to the working set */
1081 bool DNS::AddResolverClass(Resolver* r)
1083 ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
1084 /* Check the pointers validity and the id's validity */
1085 if ((r) && (r->GetId() > -1))
1087 /* Check the slot isnt already occupied -
1088 * This should NEVER happen unless we have
1089 * a severely broken DNS server somewhere
1091 if (!Classes[r->GetId()])
1093 /* Set up the pointer to the class */
1094 Classes[r->GetId()] = r;
1099 /* Pointer or id not valid, or duplicate id.
1100 * Free the item and return
1106 void DNS::CleanResolvers(Module* module)
1108 for (int i = 0; i < MAX_REQUEST_ID; i++)
1112 if (Classes[i]->GetCreator() == module)
1114 Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading");