1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2010 InspIRCd Development Team
6 * See: http://wiki.inspircd.org/Credits
8 * This program is free but copyrighted software; see
9 * the file COPYING for details.
11 * ---------------------------------------------------
17 dns.cpp - Nonblocking DNS functions.
18 Very very loosely based on the firedns library,
19 Copyright (C) 2002 Ian Gulliver. This file is no
20 longer anything like firedns, there are many major
21 differences between this code and the original.
22 Please do not assume that firedns works like this,
23 looks like this, walks like this or tastes like this.
27 #include <sys/types.h>
28 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 #include "inspircd_win32wrapper.h"
37 #include "socketengine.h"
38 #include "configreader.h"
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(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 Timer
110 CacheTimer(DNS* thisdns)
111 : Timer(3600, ServerInstance->Time(), true), dns(thisdns) { }
113 virtual void Tick(time_t)
119 class RequestTimeout : public Timer
124 RequestTimeout(unsigned long n, DNSRequest* watching, int id) : Timer(n, ServerInstance->Time()), watch(watching), watchid(id)
129 if (ServerInstance->Res)
135 if (ServerInstance->Res->requests[watchid] == watch)
137 /* Still exists, whack it */
138 if (ServerInstance->Res->Classes[watchid])
140 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
141 delete ServerInstance->Res->Classes[watchid];
142 ServerInstance->Res->Classes[watchid] = NULL;
144 ServerInstance->Res->requests[watchid] = NULL;
150 CachedQuery::CachedQuery(const std::string &res, unsigned int ttl) : data(res)
152 expires = ServerInstance->Time() + ttl;
155 int CachedQuery::CalcTTLRemaining()
157 int n = expires - ServerInstance->Time();
158 return (n < 0 ? 0 : n);
161 /* Allocate the processing buffer */
162 DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns)
164 res = new unsigned char[512];
167 RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid);
168 ServerInstance->Timers->AddTimer(RT); /* The timer manager frees this */
171 /* Deallocate the processing buffer */
172 DNSRequest::~DNSRequest()
177 /** Fill a ResourceRecord class based on raw data input */
178 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
180 rr->type = (QueryType)((input[0] << 8) + input[1]);
181 rr->rr_class = (input[2] << 8) + input[3];
182 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
183 rr->rdlength = (input[8] << 8) + input[9];
186 /** Fill a DNSHeader class based on raw data input of a given length */
187 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
189 header->id[0] = input[0];
190 header->id[1] = input[1];
191 header->flags1 = input[2];
192 header->flags2 = input[3];
193 header->qdcount = (input[4] << 8) + input[5];
194 header->ancount = (input[6] << 8) + input[7];
195 header->nscount = (input[8] << 8) + input[9];
196 header->arcount = (input[10] << 8) + input[11];
197 memcpy(header->payload,&input[12],length);
200 /** Empty a DNSHeader class out into raw data, ready for transmission */
201 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
203 output[0] = header->id[0];
204 output[1] = header->id[1];
205 output[2] = header->flags1;
206 output[3] = header->flags2;
207 output[4] = header->qdcount >> 8;
208 output[5] = header->qdcount & 0xFF;
209 output[6] = header->ancount >> 8;
210 output[7] = header->ancount & 0xFF;
211 output[8] = header->nscount >> 8;
212 output[9] = header->nscount & 0xFF;
213 output[10] = header->arcount >> 8;
214 output[11] = header->arcount & 0xFF;
215 memcpy(&output[12],header->payload,length);
218 /** Send requests we have previously built down the UDP socket */
219 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
221 ServerInstance->Logs->Log("RESOLVER", DEBUG,"DNSRequest::SendRequests");
223 unsigned char payload[sizeof(DNSHeader)];
228 DNS::EmptyHeader(payload,header,length);
230 if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, &(dnsobj->myserver.sa), sa_size(dnsobj->myserver)) != length+12)
233 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
237 /** Add a query with a predefined header, and allocate an ID for it. */
238 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
240 /* Is the DNS connection down? */
241 if (this->GetFd() == -1)
246 id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID);
247 } while (requests[id]);
249 DNSRequest* req = new DNSRequest(this, id, original);
251 header->id[0] = req->id[0] = id >> 8;
252 header->id[1] = req->id[1] = id & 0xFF;
253 header->flags1 = FLAGS_MASK_RD;
260 /* At this point we already know the id doesnt exist,
261 * so there needs to be no second check for the ::end()
265 /* According to the C++ spec, new never returns NULL. */
269 int DNS::ClearCache()
271 /* This ensures the buckets are reset to sane levels */
272 int rv = this->cache->size();
274 this->cache = new dnscache();
278 int DNS::PruneCache()
281 dnscache* newcache = new dnscache();
282 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
283 /* Dont include expired items (theres no point) */
284 if (i->second.CalcTTLRemaining())
285 newcache->insert(*i);
290 this->cache = newcache;
298 if (this->GetFd() > -1)
300 if (ServerInstance && ServerInstance->SE)
301 ServerInstance->SE->DelFd(this);
302 ServerInstance->SE->Shutdown(this, 2);
303 ServerInstance->SE->Close(this);
306 /* Rehash the cache */
311 /* Create initial dns cache */
312 this->cache = new dnscache();
315 irc::sockets::aptosa(ServerInstance->Config->DNSServer, DNS::QUERY_PORT, myserver);
317 /* Initialize mastersocket */
318 int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0);
321 /* Have we got a socket and is it nonblocking? */
322 if (this->GetFd() != -1)
324 ServerInstance->SE->SetReuse(s);
325 ServerInstance->SE->NonBlocking(s);
326 /* Bind the port - port 0 INADDR_ANY */
327 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
330 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error binding dns socket");
331 ServerInstance->SE->Shutdown(this, 2);
332 ServerInstance->SE->Close(this);
336 if (this->GetFd() >= 0)
338 /* Hook the descriptor into the socket engine */
339 if (ServerInstance && ServerInstance->SE)
341 if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
343 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
344 ServerInstance->SE->Shutdown(this, 2);
345 ServerInstance->SE->Close(this);
353 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error creating dns socket");
357 /** Initialise the DNS UDP socket so that we can send requests */
360 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
361 /* Clear the Resolver class table */
362 memset(Classes,0,sizeof(Classes));
364 /* Clear the requests class table */
365 memset(requests,0,sizeof(requests));
367 /* Set the id of the next request to 0
371 /* DNS::Rehash() sets this to a valid ptr
375 /* Again, DNS::Rehash() sets this to a
380 /* Actually read the settings
384 this->PruneTimer = new CacheTimer(this);
386 ServerInstance->Timers->AddTimer(this->PruneTimer);
389 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
390 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
392 short payloadpos = 0;
393 const char* tempchr, *tempchr2 = name;
394 unsigned short length;
396 /* split name up into labels, create query */
397 while ((tempchr = strchr(tempchr2,'.')) != NULL)
399 length = tempchr - tempchr2;
400 if (payloadpos + length + 1 > 507)
402 payload[payloadpos++] = length;
403 memcpy(&payload[payloadpos],tempchr2,length);
404 payloadpos += length;
405 tempchr2 = &tempchr[1];
407 length = strlen(tempchr2);
410 if (payloadpos + length + 2 > 507)
412 payload[payloadpos++] = length;
413 memcpy(&payload[payloadpos],tempchr2,length);
414 payloadpos += length;
415 payload[payloadpos++] = 0;
417 if (payloadpos > 508)
420 memcpy(&payload[payloadpos],&length,2);
421 length = htons(rr_class);
422 memcpy(&payload[payloadpos + 2],&length,2);
423 return payloadpos + 4;
426 /** Start lookup of an hostname to an IP address */
427 int DNS::GetIP(const char *name)
433 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
436 DNSRequest* req = this->AddQuery(&h, id, name);
438 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
444 /** Start lookup of an hostname to an IPv6 address */
445 int DNS::GetIP6(const char *name)
451 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
454 DNSRequest* req = this->AddQuery(&h, id, name);
456 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
462 /** Start lookup of a cname to another name */
463 int DNS::GetCName(const char *alias)
469 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
472 DNSRequest* req = this->AddQuery(&h, id, alias);
474 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
480 /** Start lookup of an IP address to a hostname */
481 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
488 if (fp == PROTOCOL_IPV6)
491 if (inet_pton(AF_INET6, ip, &i) > 0)
493 DNS::MakeIP6Int(query, &i);
496 /* Invalid IP address */
502 if (inet_aton(ip, &i))
504 unsigned char* c = (unsigned char*)&i.s_addr;
505 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
508 /* Invalid IP address */
512 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
515 DNSRequest* req = this->AddQuery(&h, id, ip);
517 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
523 /** Build an ipv6 reverse domain from an in6_addr
525 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
527 const char* hex = "0123456789abcdef";
528 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
532 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
535 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
536 *query++ = '.'; /* Seperator */
538 strcpy(query,"ip6.arpa"); /* Suffix the string */
541 /** Return the next id which is ready, and the result attached to it */
542 DNSResult DNS::GetResult()
544 /* Fetch dns query response and decide where it belongs */
547 unsigned char buffer[sizeof(DNSHeader)];
548 irc::sockets::sockaddrs from;
549 memset(&from, 0, sizeof(from));
550 socklen_t x = sizeof(from);
552 int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);
554 /* Did we get the whole header? */
557 /* Nope - something screwed up. */
558 return DNSResult(-1,"",0,"");
561 /* Check wether the reply came from a different DNS
562 * server to the one we sent it to, or the source-port
564 * A user could in theory still spoof dns packets anyway
565 * but this is less trivial than just sending garbage
566 * to the server, which is possible without this check.
568 * -- Thanks jilles for pointing this one out.
570 if (memcmp(&from, &myserver, sizeof(irc::sockets::sockaddrs)))
572 return DNSResult(-1,"",0,"");
575 /* Put the read header info into a header class */
576 DNS::FillHeader(&header,buffer,length - 12);
578 /* Get the id of this request.
579 * Its a 16 bit value stored in two char's,
580 * so we use logic shifts to create the value.
582 unsigned long this_id = header.id[1] + (header.id[0] << 8);
584 /* Do we have a pending request matching this id? */
585 if (!requests[this_id])
587 /* Somehow we got a DNS response for a request we never made... */
588 return DNSResult(-1,"",0,"");
592 /* Remove the query from the list of pending queries */
593 req = requests[this_id];
594 requests[this_id] = NULL;
597 /* Inform the DNSRequest class that it has a result to be read.
598 * When its finished it will return a DNSInfo which is a pair of
599 * unsigned char* resource record data, and an error message.
601 DNSInfo data = req->ResultIsReady(header, length);
602 std::string resultstr;
604 /* Check if we got a result, if we didnt, its an error */
605 if (data.first == NULL)
608 * Mask the ID with the value of ERROR_MASK, so that
609 * the dns_deal_with_classes() function knows that its
610 * an error response and needs to be treated uniquely.
611 * Put the error message in the second field.
613 std::string ro = req->orig;
615 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
619 unsigned long ttl = req->ttl;
622 /* Forward lookups come back as binary data. We must format them into ascii */
626 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
627 resultstr = formatted;
632 inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted));
633 char* c = strstr(formatted,":0:");
636 memmove(c+1,c+2,strlen(c+2) + 1);
638 while (memcmp(c,"0:",2) == 0)
639 memmove(c,c+2,strlen(c+2) + 1);
640 if (memcmp(c,"0",2) == 0)
642 if (memcmp(formatted,"0::",3) == 0)
643 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
645 resultstr = formatted;
647 /* Special case. Sending ::1 around between servers
648 * and to clients is dangerous, because the : on the
649 * start makes the client or server interpret the IP
650 * as the last parameter on the line with a value ":1".
652 if (*formatted == ':')
653 resultstr.insert(0, "0");
657 case DNS_QUERY_CNAME:
658 /* Identical handling to PTR */
661 /* Reverse lookups just come back as char* */
662 resultstr = std::string((const char*)data.first);
669 /* Build the reply with the id and hostname/ip in it */
670 std::string ro = req->orig;
672 return DNSResult(this_id,resultstr,ttl,ro);
676 /** A result is ready, process it */
677 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
685 /* This is just to keep _FORTIFY_SOURCE happy */
686 rr.type = DNS_QUERY_NONE;
688 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
689 rr.rr_class = 0; /* Same for VC++ */
691 if (!(header.flags1 & FLAGS_MASK_QR))
692 return std::make_pair((unsigned char*)NULL,"Not a query result");
694 if (header.flags1 & FLAGS_MASK_OPCODE)
695 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
697 if (header.flags2 & FLAGS_MASK_RCODE)
698 return std::make_pair((unsigned char*)NULL,"Domain name not found");
700 if (header.ancount < 1)
701 return std::make_pair((unsigned char*)NULL,"No resource records returned");
703 /* Subtract the length of the header from the length of the packet */
706 while ((unsigned int)q < header.qdcount && i < length)
708 if (header.payload[i] > 63)
715 if (header.payload[i] == 0)
720 else i += header.payload[i] + 1;
724 while ((unsigned)curanswer < header.ancount)
727 while (q == 0 && i < length)
729 if (header.payload[i] > 63)
736 if (header.payload[i] == 0)
741 else i += header.payload[i] + 1; /* skip length and label */
745 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
747 /* XXX: We actually initialise 'rr' here including its ttl field */
748 DNS::FillResourceRecord(&rr,&header.payload[i]);
751 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);
752 if (rr.type != this->type)
758 if (rr.rr_class != this->rr_class)
766 if ((unsigned int)curanswer == header.ancount)
767 return std::make_pair((unsigned char*)NULL,"No A, AAAA or PTR type answers (" + ConvToStr(header.ancount) + " answers)");
769 if (i + rr.rdlength > (unsigned int)length)
770 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
772 if (rr.rdlength > 1023)
773 return std::make_pair((unsigned char*)NULL,"Resource record too large");
779 case DNS_QUERY_CNAME:
780 /* CNAME and PTR have the same processing code */
784 while (q == 0 && i < length && o + 256 < 1023)
786 if (header.payload[i] > 63)
788 memcpy(&ptr,&header.payload[i],2);
789 i = ntohs(ptr) - 0xC000 - 12;
793 if (header.payload[i] == 0)
802 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
803 o += header.payload[i];
804 i += header.payload[i] + 1;
811 memcpy(res,&header.payload[i],rr.rdlength);
812 res[rr.rdlength] = 0;
815 memcpy(res,&header.payload[i],rr.rdlength);
816 res[rr.rdlength] = 0;
819 memcpy(res,&header.payload[i],rr.rdlength);
820 res[rr.rdlength] = 0;
823 return std::make_pair(res,"No error");
826 /** Close the master socket */
829 ServerInstance->SE->Shutdown(this, 2);
830 ServerInstance->SE->Close(this);
831 ServerInstance->Timers->DelTimer(this->PruneTimer);
836 CachedQuery* DNS::GetCache(const std::string &source)
838 dnscache::iterator x = cache->find(source.c_str());
839 if (x != cache->end())
845 void DNS::DelCache(const std::string &source)
847 cache->erase(source.c_str());
850 void Resolver::TriggerCachedResult()
853 OnLookupComplete(CQ->data, time_left, true);
856 /** High level abstraction of dns used by application at large */
857 Resolver::Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator) : Creator(creator), input(source), querytype(qt)
859 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
862 CQ = ServerInstance->Res->GetCache(source);
865 time_left = CQ->CalcTTLRemaining();
868 ServerInstance->Res->DelCache(source);
880 this->myid = ServerInstance->Res->GetIP(source.c_str());
884 querytype = DNS_QUERY_PTR;
885 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
889 querytype = DNS_QUERY_PTR;
890 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
894 this->myid = ServerInstance->Res->GetIP6(source.c_str());
897 case DNS_QUERY_CNAME:
898 this->myid = ServerInstance->Res->GetCName(source.c_str());
905 if (this->myid == -1)
907 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
908 throw ModuleException("Resolver: Couldnt get an id to make a request");
909 /* We shouldnt get here really */
914 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
918 /** Called when an error occurs */
919 void Resolver::OnError(ResolverError, const std::string&)
921 /* Nothing in here */
924 /** Destroy a resolver */
925 Resolver::~Resolver()
927 /* Nothing here (yet) either */
930 /** Get the request id associated with this class */
931 int Resolver::GetId()
936 Module* Resolver::GetCreator()
938 return this->Creator;
941 /** Process a socket read event */
942 void DNS::HandleEvent(EventType, int)
944 /* Fetch the id and result of the next available packet */
945 DNSResult res(0,"",0,"");
947 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
949 res = this->GetResult();
951 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result id %d", res.id);
953 /* Is there a usable request id? */
956 /* Its an error reply */
957 if (res.id & ERROR_MASK)
959 /* Mask off the error bit */
960 res.id -= ERROR_MASK;
961 /* Marshall the error to the correct class */
964 if (ServerInstance && ServerInstance->stats)
965 ServerInstance->stats->statsDnsBad++;
966 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
967 delete Classes[res.id];
968 Classes[res.id] = NULL;
974 /* It is a non-error result, marshall the result to the correct class */
977 if (ServerInstance && ServerInstance->stats)
978 ServerInstance->stats->statsDnsGood++;
980 if (!this->GetCache(res.original.c_str()))
981 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
983 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
984 delete Classes[res.id];
985 Classes[res.id] = NULL;
989 if (ServerInstance && ServerInstance->stats)
990 ServerInstance->stats->statsDns++;
994 /** Add a derived Resolver to the working set */
995 bool DNS::AddResolverClass(Resolver* r)
997 ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
998 /* Check the pointers validity and the id's validity */
999 if ((r) && (r->GetId() > -1))
1001 /* Check the slot isnt already occupied -
1002 * This should NEVER happen unless we have
1003 * a severely broken DNS server somewhere
1005 if (!Classes[r->GetId()])
1007 /* Set up the pointer to the class */
1008 Classes[r->GetId()] = r;
1017 /* Pointer or id not valid.
1018 * Free the item and return
1027 void DNS::CleanResolvers(Module* module)
1029 for (int i = 0; i < MAX_REQUEST_ID; i++)
1033 if (Classes[i]->GetCreator() == module)
1035 Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading");