1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2009 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"
34 #include "inspircd_se_config.h"
39 #include "socketengine.h"
40 #include "configreader.h"
43 /** Masks to mask off the responses we get from the DNSRequest methods
47 ERROR_MASK = 0x10000 /* Result is an error */
50 /** Flags which can be ORed into a request or reply for different meanings
54 FLAGS_MASK_RD = 0x01, /* Recursive */
56 FLAGS_MASK_AA = 0x04, /* Authoritative */
57 FLAGS_MASK_OPCODE = 0x78,
59 FLAGS_MASK_RCODE = 0x0F, /* Request */
65 /** Represents a dns resource record (rr)
69 QueryType type; /* Record type */
70 unsigned int rr_class; /* Record class */
71 unsigned long ttl; /* Time to live */
72 unsigned int rdlength; /* Record length */
75 /** Represents a dns request/reply header, and its payload as opaque data.
80 unsigned char id[2]; /* Request id */
81 unsigned int flags1; /* Flags */
82 unsigned int flags2; /* Flags */
84 unsigned int ancount; /* Answer count */
85 unsigned int nscount; /* Nameserver count */
87 unsigned char payload[512]; /* Packet payload */
93 unsigned char id[2]; /* Request id */
94 unsigned char* res; /* Result processing buffer */
95 unsigned int rr_class; /* Request class */
96 QueryType type; /* Request type */
97 DNS* dnsobj; /* DNS caller (where we get our FD from) */
98 unsigned long ttl; /* Time to live */
99 std::string orig; /* Original requested name/ip */
100 InspIRCd* ServerInstance;
102 DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
104 DNSInfo ResultIsReady(DNSHeader &h, int length);
105 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
108 class CacheTimer : public Timer
111 InspIRCd* ServerInstance;
114 CacheTimer(InspIRCd* Instance, DNS* thisdns)
115 : Timer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
117 virtual void Tick(time_t)
123 class RequestTimeout : public Timer
125 InspIRCd* ServerInstance;
129 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : Timer(n, SI->Time()), ServerInstance(SI), watch(watching), watchid(id)
134 if (ServerInstance->Res)
140 if (ServerInstance->Res->requests[watchid] == watch)
142 /* Still exists, whack it */
143 if (ServerInstance->Res->Classes[watchid])
145 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
146 delete ServerInstance->Res->Classes[watchid];
147 ServerInstance->Res->Classes[watchid] = NULL;
149 ServerInstance->Res->requests[watchid] = NULL;
155 /* Allocate the processing buffer */
156 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int rid, const std::string &original) : dnsobj(dns), ServerInstance(Instance)
158 res = new unsigned char[512];
161 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, rid);
162 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
165 /* Deallocate the processing buffer */
166 DNSRequest::~DNSRequest()
171 /** Fill a ResourceRecord class based on raw data input */
172 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
174 rr->type = (QueryType)((input[0] << 8) + input[1]);
175 rr->rr_class = (input[2] << 8) + input[3];
176 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
177 rr->rdlength = (input[8] << 8) + input[9];
180 /** Fill a DNSHeader class based on raw data input of a given length */
181 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
183 header->id[0] = input[0];
184 header->id[1] = input[1];
185 header->flags1 = input[2];
186 header->flags2 = input[3];
187 header->qdcount = (input[4] << 8) + input[5];
188 header->ancount = (input[6] << 8) + input[7];
189 header->nscount = (input[8] << 8) + input[9];
190 header->arcount = (input[10] << 8) + input[11];
191 memcpy(header->payload,&input[12],length);
194 /** Empty a DNSHeader class out into raw data, ready for transmission */
195 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
197 output[0] = header->id[0];
198 output[1] = header->id[1];
199 output[2] = header->flags1;
200 output[3] = header->flags2;
201 output[4] = header->qdcount >> 8;
202 output[5] = header->qdcount & 0xFF;
203 output[6] = header->ancount >> 8;
204 output[7] = header->ancount & 0xFF;
205 output[8] = header->nscount >> 8;
206 output[9] = header->nscount & 0xFF;
207 output[10] = header->arcount >> 8;
208 output[11] = header->arcount & 0xFF;
209 memcpy(&output[12],header->payload,length);
212 /** Send requests we have previously built down the UDP socket */
213 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
215 ServerInstance->Logs->Log("RESOLVER", DEBUG,"DNSRequest::SendRequests");
217 unsigned char payload[sizeof(DNSHeader)];
222 DNS::EmptyHeader(payload,header,length);
224 if (this->dnsobj->socketfamily == AF_INET6)
227 memset(&addr,0,sizeof(addr));
228 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
229 addr.sin6_family = AF_INET6;
230 addr.sin6_port = htons(DNS::QUERY_PORT);
231 if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
237 memset(&addr,0,sizeof(addr));
238 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
239 addr.sin_family = AF_INET;
240 addr.sin_port = htons(DNS::QUERY_PORT);
241 if (ServerInstance->SE->SendTo(dnsobj, (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
245 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
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;
313 if (this->GetFd() > -1)
315 if (ServerInstance && ServerInstance->SE)
316 ServerInstance->SE->DelFd(this);
317 ServerInstance->SE->Shutdown(this, 2);
318 ServerInstance->SE->Close(this);
321 /* Rehash the cache */
326 /* Create initial dns cache */
327 this->cache = new dnscache();
330 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
332 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
333 ServerInstance->Logs->Log("RESOLVER",DEFAULT," This should not cause a problem, however it is recommended you migrate");
334 ServerInstance->Logs->Log("RESOLVER",DEFAULT," to a true IPv6 environment.");
335 this->ip6munge = true;
338 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);
346 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
350 /* Initialize mastersocket */
351 int s = irc::sockets::OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
353 ServerInstance->SE->NonBlocking(this->GetFd());
355 /* Have we got a socket and is it nonblocking? */
356 if (this->GetFd() != -1)
358 /* Bind the port - port 0 INADDR_ANY */
359 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
362 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error binding dns socket");
363 ServerInstance->SE->Shutdown(this, 2);
364 ServerInstance->SE->Close(this);
368 if (this->GetFd() >= 0)
370 /* Hook the descriptor into the socket engine */
371 if (ServerInstance && ServerInstance->SE)
373 if (!ServerInstance->SE->AddFd(this))
375 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
376 ServerInstance->SE->Shutdown(this, 2);
377 ServerInstance->SE->Close(this);
385 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error creating dns socket");
389 /** Initialise the DNS UDP socket so that we can send requests */
390 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
392 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
393 /* Clear the Resolver class table */
394 memset(Classes,0,sizeof(Classes));
396 /* Clear the requests class table */
397 memset(requests,0,sizeof(requests));
399 /* Set the id of the next request to 0
403 /* DNS::Rehash() sets this to a valid ptr
407 /* Again, DNS::Rehash() sets this to a
412 /* Actually read the settings
416 this->PruneTimer = new CacheTimer(ServerInstance, this);
418 ServerInstance->Timers->AddTimer(this->PruneTimer);
421 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
422 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
424 short payloadpos = 0;
425 const char* tempchr, *tempchr2 = name;
426 unsigned short length;
428 /* split name up into labels, create query */
429 while ((tempchr = strchr(tempchr2,'.')) != NULL)
431 length = tempchr - tempchr2;
432 if (payloadpos + length + 1 > 507)
434 payload[payloadpos++] = length;
435 memcpy(&payload[payloadpos],tempchr2,length);
436 payloadpos += length;
437 tempchr2 = &tempchr[1];
439 length = strlen(tempchr2);
442 if (payloadpos + length + 2 > 507)
444 payload[payloadpos++] = length;
445 memcpy(&payload[payloadpos],tempchr2,length);
446 payloadpos += length;
447 payload[payloadpos++] = 0;
449 if (payloadpos > 508)
452 memcpy(&payload[payloadpos],&length,2);
453 length = htons(rr_class);
454 memcpy(&payload[payloadpos + 2],&length,2);
455 return payloadpos + 4;
458 /** Start lookup of an hostname to an IP address */
459 int DNS::GetIP(const char *name)
465 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
468 DNSRequest* req = this->AddQuery(&h, id, name);
470 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
476 /** Start lookup of an hostname to an IPv6 address */
477 int DNS::GetIP6(const char *name)
483 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
486 DNSRequest* req = this->AddQuery(&h, id, name);
488 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
494 /** Start lookup of a cname to another name */
495 int DNS::GetCName(const char *alias)
501 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
504 DNSRequest* req = this->AddQuery(&h, id, alias);
506 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
512 /** Start lookup of an IP address to a hostname */
513 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
520 if (fp == PROTOCOL_IPV6)
523 if (inet_pton(AF_INET6, ip, &i) > 0)
525 DNS::MakeIP6Int(query, &i);
528 /* Invalid IP address */
534 if (inet_aton(ip, &i))
536 unsigned char* c = (unsigned char*)&i.s_addr;
537 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
540 /* Invalid IP address */
544 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
547 DNSRequest* req = this->AddQuery(&h, id, ip);
549 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
555 /** Build an ipv6 reverse domain from an in6_addr
557 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
559 const char* hex = "0123456789abcdef";
560 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
564 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
567 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
568 *query++ = '.'; /* Seperator */
570 strcpy(query,"ip6.arpa"); /* Suffix the string */
573 /** Return the next id which is ready, and the result attached to it */
574 DNSResult DNS::GetResult()
576 /* Fetch dns query response and decide where it belongs */
579 unsigned char buffer[sizeof(DNSHeader)];
580 irc::sockets::sockaddrs from;
581 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
582 const char* ipaddr_from;
583 unsigned short int port_from = 0;
585 int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);
587 /* Did we get the whole header? */
590 /* Nope - something screwed up. */
591 return DNSResult(-1,"",0,"");
594 /* Check wether the reply came from a different DNS
595 * server to the one we sent it to, or the source-port
597 * A user could in theory still spoof dns packets anyway
598 * but this is less trivial than just sending garbage
599 * to the client, which is possible without this check.
601 * -- Thanks jilles for pointing this one out.
604 if (this->socketfamily == AF_INET6)
606 ipaddr_from = inet_ntop(AF_INET6, &from.in6.sin6_addr, nbuf, sizeof(nbuf));
607 port_from = ntohs(from.in6.sin6_port);
611 ipaddr_from = inet_ntoa(from.in4.sin_addr);
612 port_from = ntohs(from.in4.sin_port);
615 /* We cant perform this security check if you're using 4in6.
616 * Tough luck to you, choose one or't other!
620 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
622 return DNSResult(-1,"",0,"");
626 /* Put the read header info into a header class */
627 DNS::FillHeader(&header,buffer,length - 12);
629 /* Get the id of this request.
630 * Its a 16 bit value stored in two char's,
631 * so we use logic shifts to create the value.
633 unsigned long this_id = header.id[1] + (header.id[0] << 8);
635 /* Do we have a pending request matching this id? */
636 if (!requests[this_id])
638 /* Somehow we got a DNS response for a request we never made... */
639 return DNSResult(-1,"",0,"");
643 /* Remove the query from the list of pending queries */
644 req = requests[this_id];
645 requests[this_id] = NULL;
648 /* Inform the DNSRequest class that it has a result to be read.
649 * When its finished it will return a DNSInfo which is a pair of
650 * unsigned char* resource record data, and an error message.
652 DNSInfo data = req->ResultIsReady(header, length);
653 std::string resultstr;
655 /* Check if we got a result, if we didnt, its an error */
656 if (data.first == NULL)
659 * Mask the ID with the value of ERROR_MASK, so that
660 * the dns_deal_with_classes() function knows that its
661 * an error response and needs to be treated uniquely.
662 * Put the error message in the second field.
664 std::string ro = req->orig;
666 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
670 unsigned long ttl = req->ttl;
673 /* Forward lookups come back as binary data. We must format them into ascii */
677 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
678 resultstr = formatted;
683 inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted));
684 char* c = strstr(formatted,":0:");
687 memmove(c+1,c+2,strlen(c+2) + 1);
689 while (memcmp(c,"0:",2) == 0)
690 memmove(c,c+2,strlen(c+2) + 1);
691 if (memcmp(c,"0",2) == 0)
693 if (memcmp(formatted,"0::",3) == 0)
694 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
696 resultstr = formatted;
698 /* Special case. Sending ::1 around between servers
699 * and to clients is dangerous, because the : on the
700 * start makes the client or server interpret the IP
701 * as the last parameter on the line with a value ":1".
703 if (*formatted == ':')
704 resultstr.insert(0, "0");
708 case DNS_QUERY_CNAME:
709 /* Identical handling to PTR */
712 /* Reverse lookups just come back as char* */
713 resultstr = std::string((const char*)data.first);
720 /* Build the reply with the id and hostname/ip in it */
721 std::string ro = req->orig;
723 return DNSResult(this_id,resultstr,ttl,ro);
727 /** A result is ready, process it */
728 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int 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 */
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");
830 case DNS_QUERY_CNAME:
831 /* CNAME and PTR have the same processing code */
835 while (q == 0 && i < length && o + 256 < 1023)
837 if (header.payload[i] > 63)
839 memcpy(&ptr,&header.payload[i],2);
840 i = ntohs(ptr) - 0xC000 - 12;
844 if (header.payload[i] == 0)
853 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
854 o += header.payload[i];
855 i += header.payload[i] + 1;
862 memcpy(res,&header.payload[i],rr.rdlength);
863 res[rr.rdlength] = 0;
866 memcpy(res,&header.payload[i],rr.rdlength);
867 res[rr.rdlength] = 0;
870 memcpy(res,&header.payload[i],rr.rdlength);
871 res[rr.rdlength] = 0;
874 return std::make_pair(res,"No error");
877 /** Close the master socket */
880 ServerInstance->SE->Shutdown(this, 2);
881 ServerInstance->SE->Close(this);
882 ServerInstance->Timers->DelTimer(this->PruneTimer);
887 CachedQuery* DNS::GetCache(const std::string &source)
889 dnscache::iterator x = cache->find(source.c_str());
890 if (x != cache->end())
896 void DNS::DelCache(const std::string &source)
898 cache->erase(source.c_str());
901 void Resolver::TriggerCachedResult()
904 OnLookupComplete(CQ->data, time_left, true);
907 /** High level abstraction of dns used by application at large */
908 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
910 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
913 CQ = ServerInstance->Res->GetCache(source);
916 time_left = CQ->CalcTTLRemaining();
919 ServerInstance->Res->DelCache(source);
931 this->myid = ServerInstance->Res->GetIP(source.c_str());
935 querytype = DNS_QUERY_PTR;
936 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
940 querytype = DNS_QUERY_PTR;
941 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
945 this->myid = ServerInstance->Res->GetIP6(source.c_str());
948 case DNS_QUERY_CNAME:
949 this->myid = ServerInstance->Res->GetCName(source.c_str());
956 if (this->myid == -1)
958 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
959 throw ModuleException("Resolver: Couldnt get an id to make a request");
960 /* We shouldnt get here really */
965 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
969 /** Called when an error occurs */
970 void Resolver::OnError(ResolverError, const std::string&)
972 /* Nothing in here */
975 /** Destroy a resolver */
976 Resolver::~Resolver()
978 /* Nothing here (yet) either */
981 /** Get the request id associated with this class */
982 int Resolver::GetId()
987 Module* Resolver::GetCreator()
989 return this->Creator;
992 /** Process a socket read event */
993 void DNS::HandleEvent(EventType, int)
995 /* Fetch the id and result of the next available packet */
996 DNSResult res(0,"",0,"");
998 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
1000 res = this->GetResult();
1002 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result id %d", res.id);
1004 /* Is there a usable request id? */
1007 /* Its an error reply */
1008 if (res.id & ERROR_MASK)
1010 /* Mask off the error bit */
1011 res.id -= ERROR_MASK;
1012 /* Marshall the error to the correct class */
1013 if (Classes[res.id])
1015 if (ServerInstance && ServerInstance->stats)
1016 ServerInstance->stats->statsDnsBad++;
1017 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1018 delete Classes[res.id];
1019 Classes[res.id] = NULL;
1025 /* It is a non-error result, marshall the result to the correct class */
1026 if (Classes[res.id])
1028 if (ServerInstance && ServerInstance->stats)
1029 ServerInstance->stats->statsDnsGood++;
1031 if (!this->GetCache(res.original.c_str()))
1032 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1034 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1035 delete Classes[res.id];
1036 Classes[res.id] = NULL;
1040 if (ServerInstance && ServerInstance->stats)
1041 ServerInstance->stats->statsDns++;
1045 /** Add a derived Resolver to the working set */
1046 bool DNS::AddResolverClass(Resolver* r)
1048 ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
1049 /* Check the pointers validity and the id's validity */
1050 if ((r) && (r->GetId() > -1))
1052 /* Check the slot isnt already occupied -
1053 * This should NEVER happen unless we have
1054 * a severely broken DNS server somewhere
1056 if (!Classes[r->GetId()])
1058 /* Set up the pointer to the class */
1059 Classes[r->GetId()] = r;
1068 /* Pointer or id not valid.
1069 * Free the item and return
1078 void DNS::CleanResolvers(Module* module)
1080 for (int i = 0; i < MAX_REQUEST_ID; i++)
1084 if (Classes[i]->GetCreator() == module)
1086 Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading");
1094 /** Generate pseudo-random number */
1095 unsigned long DNS::PRNG()
1097 unsigned long val = 0;
1099 serverstats* s = ServerInstance->stats;
1100 gettimeofday(&n,NULL);
1101 val = (n.tv_usec ^ (getpid() ^ geteuid()) ^ ((this->currid++)) ^ s->statsAccept) + n.tv_sec;
1102 val = val + (s->statsCollisions ^ s->statsDnsGood) - s->statsDnsBad;
1103 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->ports.size();