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)
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;
151 /* Allocate the processing buffer */
152 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int rid, const std::string &original) : dnsobj(dns), ServerInstance(Instance)
154 res = new unsigned char[512];
157 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, rid);
158 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
161 /* Deallocate the processing buffer */
162 DNSRequest::~DNSRequest()
167 /** Fill a ResourceRecord class based on raw data input */
168 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
170 rr->type = (QueryType)((input[0] << 8) + input[1]);
171 rr->rr_class = (input[2] << 8) + input[3];
172 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
173 rr->rdlength = (input[8] << 8) + input[9];
176 /** Fill a DNSHeader class based on raw data input of a given length */
177 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
179 header->id[0] = input[0];
180 header->id[1] = input[1];
181 header->flags1 = input[2];
182 header->flags2 = input[3];
183 header->qdcount = (input[4] << 8) + input[5];
184 header->ancount = (input[6] << 8) + input[7];
185 header->nscount = (input[8] << 8) + input[9];
186 header->arcount = (input[10] << 8) + input[11];
187 memcpy(header->payload,&input[12],length);
190 /** Empty a DNSHeader class out into raw data, ready for transmission */
191 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
193 output[0] = header->id[0];
194 output[1] = header->id[1];
195 output[2] = header->flags1;
196 output[3] = header->flags2;
197 output[4] = header->qdcount >> 8;
198 output[5] = header->qdcount & 0xFF;
199 output[6] = header->ancount >> 8;
200 output[7] = header->ancount & 0xFF;
201 output[8] = header->nscount >> 8;
202 output[9] = header->nscount & 0xFF;
203 output[10] = header->arcount >> 8;
204 output[11] = header->arcount & 0xFF;
205 memcpy(&output[12],header->payload,length);
208 /** Send requests we have previously built down the UDP socket */
209 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
211 ServerInstance->Logs->Log("RESOLVER", DEBUG,"DNSRequest::SendRequests");
213 unsigned char payload[sizeof(DNSHeader)];
218 DNS::EmptyHeader(payload,header,length);
220 if (this->dnsobj->socketfamily == AF_INET6)
223 memset(&addr,0,sizeof(addr));
224 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
225 addr.sin6_family = AF_INET6;
226 addr.sin6_port = htons(DNS::QUERY_PORT);
227 if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
233 memset(&addr,0,sizeof(addr));
234 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
235 addr.sin_family = AF_INET;
236 addr.sin_port = htons(DNS::QUERY_PORT);
237 if (ServerInstance->SE->SendTo(dnsobj, (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
241 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
245 /** Add a query with a predefined header, and allocate an ID for it. */
246 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
248 /* Is the DNS connection down? */
249 if (this->GetFd() == -1)
253 id = this->PRNG() & DNS::MAX_REQUEST_ID;
255 /* If this id is already 'in flight', pick another. */
257 id = this->PRNG() & DNS::MAX_REQUEST_ID;
259 DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
261 header->id[0] = req->id[0] = id >> 8;
262 header->id[1] = req->id[1] = id & 0xFF;
263 header->flags1 = FLAGS_MASK_RD;
270 /* At this point we already know the id doesnt exist,
271 * so there needs to be no second check for the ::end()
275 /* According to the C++ spec, new never returns NULL. */
279 int DNS::ClearCache()
281 /* This ensures the buckets are reset to sane levels */
282 int rv = this->cache->size();
284 this->cache = new dnscache();
288 int DNS::PruneCache()
291 dnscache* newcache = new dnscache();
292 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
293 /* Dont include expired items (theres no point) */
294 if (i->second.CalcTTLRemaining())
295 newcache->insert(*i);
300 this->cache = newcache;
309 if (this->GetFd() > -1)
311 if (ServerInstance && ServerInstance->SE)
312 ServerInstance->SE->DelFd(this);
313 ServerInstance->SE->Shutdown(this, 2);
314 ServerInstance->SE->Close(this);
317 /* Rehash the cache */
322 /* Create initial dns cache */
323 this->cache = new dnscache();
326 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
328 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
329 ServerInstance->Logs->Log("RESOLVER",DEFAULT," This should not cause a problem, however it is recommended you migrate");
330 ServerInstance->Logs->Log("RESOLVER",DEFAULT," to a true IPv6 environment.");
331 this->ip6munge = true;
334 this->socketfamily = AF_INET;
335 if (strchr(ServerInstance->Config->DNSServer,':'))
337 this->socketfamily = AF_INET6;
338 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
342 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
346 /* Initialize mastersocket */
347 int s = irc::sockets::OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
349 ServerInstance->SE->NonBlocking(this->GetFd());
351 /* Have we got a socket and is it nonblocking? */
352 if (this->GetFd() != -1)
354 /* Bind the port - port 0 INADDR_ANY */
355 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
358 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error binding dns socket");
359 ServerInstance->SE->Shutdown(this, 2);
360 ServerInstance->SE->Close(this);
364 if (this->GetFd() >= 0)
366 /* Hook the descriptor into the socket engine */
367 if (ServerInstance && ServerInstance->SE)
369 if (!ServerInstance->SE->AddFd(this))
371 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
372 ServerInstance->SE->Shutdown(this, 2);
373 ServerInstance->SE->Close(this);
381 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error creating dns socket");
385 /** Initialise the DNS UDP socket so that we can send requests */
386 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
388 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
389 /* Clear the Resolver class table */
390 memset(Classes,0,sizeof(Classes));
392 /* Clear the requests class table */
393 memset(requests,0,sizeof(requests));
395 /* Set the id of the next request to 0
399 /* DNS::Rehash() sets this to a valid ptr
403 /* Again, DNS::Rehash() sets this to a
408 /* Actually read the settings
412 this->PruneTimer = new CacheTimer(ServerInstance, this);
414 ServerInstance->Timers->AddTimer(this->PruneTimer);
417 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
418 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
420 short payloadpos = 0;
421 const char* tempchr, *tempchr2 = name;
422 unsigned short length;
424 /* split name up into labels, create query */
425 while ((tempchr = strchr(tempchr2,'.')) != NULL)
427 length = tempchr - tempchr2;
428 if (payloadpos + length + 1 > 507)
430 payload[payloadpos++] = length;
431 memcpy(&payload[payloadpos],tempchr2,length);
432 payloadpos += length;
433 tempchr2 = &tempchr[1];
435 length = strlen(tempchr2);
438 if (payloadpos + length + 2 > 507)
440 payload[payloadpos++] = length;
441 memcpy(&payload[payloadpos],tempchr2,length);
442 payloadpos += length;
443 payload[payloadpos++] = 0;
445 if (payloadpos > 508)
448 memcpy(&payload[payloadpos],&length,2);
449 length = htons(rr_class);
450 memcpy(&payload[payloadpos + 2],&length,2);
451 return payloadpos + 4;
454 /** Start lookup of an hostname to an IP address */
455 int DNS::GetIP(const char *name)
461 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
464 DNSRequest* req = this->AddQuery(&h, id, name);
466 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
472 /** Start lookup of an hostname to an IPv6 address */
473 int DNS::GetIP6(const char *name)
479 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
482 DNSRequest* req = this->AddQuery(&h, id, name);
484 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
490 /** Start lookup of a cname to another name */
491 int DNS::GetCName(const char *alias)
497 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
500 DNSRequest* req = this->AddQuery(&h, id, alias);
502 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
508 /** Start lookup of an IP address to a hostname */
509 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
516 if (fp == PROTOCOL_IPV6)
519 if (inet_pton(AF_INET6, ip, &i) > 0)
521 DNS::MakeIP6Int(query, &i);
524 /* 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]);
536 /* Invalid IP address */
540 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
543 DNSRequest* req = this->AddQuery(&h, id, ip);
545 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
551 /** Build an ipv6 reverse domain from an in6_addr
553 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
555 const char* hex = "0123456789abcdef";
556 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
560 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
563 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
564 *query++ = '.'; /* Seperator */
566 strcpy(query,"ip6.arpa"); /* Suffix the string */
569 /** Return the next id which is ready, and the result attached to it */
570 DNSResult DNS::GetResult()
572 /* Fetch dns query response and decide where it belongs */
575 unsigned char buffer[sizeof(DNSHeader)];
576 irc::sockets::sockaddrs from;
577 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
578 const char* ipaddr_from;
579 unsigned short int port_from = 0;
581 int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);
583 /* Did we get the whole header? */
586 /* Nope - something screwed up. */
587 return DNSResult(-1,"",0,"");
590 /* Check wether the reply came from a different DNS
591 * server to the one we sent it to, or the source-port
593 * A user could in theory still spoof dns packets anyway
594 * but this is less trivial than just sending garbage
595 * to the client, which is possible without this check.
597 * -- Thanks jilles for pointing this one out.
600 if (this->socketfamily == AF_INET6)
602 ipaddr_from = inet_ntop(AF_INET6, &from.in6.sin6_addr, nbuf, sizeof(nbuf));
603 port_from = ntohs(from.in6.sin6_port);
607 ipaddr_from = inet_ntoa(from.in4.sin_addr);
608 port_from = ntohs(from.in4.sin_port);
611 /* We cant perform this security check if you're using 4in6.
612 * Tough luck to you, choose one or't other!
616 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
618 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 return DNSResult(-1,"",0,"");
639 /* Remove the query from the list of pending queries */
640 req = requests[this_id];
641 requests[this_id] = NULL;
644 /* Inform the DNSRequest class that it has a result to be read.
645 * When its finished it will return a DNSInfo which is a pair of
646 * unsigned char* resource record data, and an error message.
648 DNSInfo data = req->ResultIsReady(header, length);
649 std::string resultstr;
651 /* Check if we got a result, if we didnt, its an error */
652 if (data.first == NULL)
655 * Mask the ID with the value of ERROR_MASK, so that
656 * the dns_deal_with_classes() function knows that its
657 * an error response and needs to be treated uniquely.
658 * Put the error message in the second field.
660 std::string ro = req->orig;
662 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
666 unsigned long ttl = req->ttl;
669 /* Forward lookups come back as binary data. We must format them into ascii */
673 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
674 resultstr = formatted;
679 inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted));
680 char* c = strstr(formatted,":0:");
683 memmove(c+1,c+2,strlen(c+2) + 1);
685 while (memcmp(c,"0:",2) == 0)
686 memmove(c,c+2,strlen(c+2) + 1);
687 if (memcmp(c,"0",2) == 0)
689 if (memcmp(formatted,"0::",3) == 0)
690 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
692 resultstr = formatted;
694 /* Special case. Sending ::1 around between servers
695 * and to clients is dangerous, because the : on the
696 * start makes the client or server interpret the IP
697 * as the last parameter on the line with a value ":1".
699 if (*formatted == ':')
700 resultstr.insert(0, "0");
704 case DNS_QUERY_CNAME:
705 /* Identical handling to PTR */
708 /* Reverse lookups just come back as char* */
709 resultstr = std::string((const char*)data.first);
716 /* Build the reply with the id and hostname/ip in it */
717 std::string ro = req->orig;
719 return DNSResult(this_id,resultstr,ttl,ro);
723 /** A result is ready, process it */
724 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
732 /* This is just to keep _FORTIFY_SOURCE happy */
733 rr.type = DNS_QUERY_NONE;
735 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
736 rr.rr_class = 0; /* Same for VC++ */
738 if (!(header.flags1 & FLAGS_MASK_QR))
739 return std::make_pair((unsigned char*)NULL,"Not a query result");
741 if (header.flags1 & FLAGS_MASK_OPCODE)
742 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
744 if (header.flags2 & FLAGS_MASK_RCODE)
745 return std::make_pair((unsigned char*)NULL,"Domain name not found");
747 if (header.ancount < 1)
748 return std::make_pair((unsigned char*)NULL,"No resource records returned");
750 /* Subtract the length of the header from the length of the packet */
753 while ((unsigned int)q < header.qdcount && i < length)
755 if (header.payload[i] > 63)
762 if (header.payload[i] == 0)
767 else i += header.payload[i] + 1;
771 while ((unsigned)curanswer < header.ancount)
774 while (q == 0 && i < length)
776 if (header.payload[i] > 63)
783 if (header.payload[i] == 0)
788 else i += header.payload[i] + 1; /* skip length and label */
792 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
794 /* XXX: We actually initialise 'rr' here including its ttl field */
795 DNS::FillResourceRecord(&rr,&header.payload[i]);
798 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);
799 if (rr.type != this->type)
805 if (rr.rr_class != this->rr_class)
813 if ((unsigned int)curanswer == header.ancount)
814 return std::make_pair((unsigned char*)NULL,"No A, AAAA or PTR type answers (" + ConvToStr(header.ancount) + " answers)");
816 if (i + rr.rdlength > (unsigned int)length)
817 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
819 if (rr.rdlength > 1023)
820 return std::make_pair((unsigned char*)NULL,"Resource record too large");
826 case DNS_QUERY_CNAME:
827 /* CNAME and PTR have the same processing code */
831 while (q == 0 && i < length && o + 256 < 1023)
833 if (header.payload[i] > 63)
835 memcpy(&ptr,&header.payload[i],2);
836 i = ntohs(ptr) - 0xC000 - 12;
840 if (header.payload[i] == 0)
849 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
850 o += header.payload[i];
851 i += header.payload[i] + 1;
858 memcpy(res,&header.payload[i],rr.rdlength);
859 res[rr.rdlength] = 0;
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 return std::make_pair(res,"No error");
873 /** Close the master socket */
876 ServerInstance->SE->Shutdown(this, 2);
877 ServerInstance->SE->Close(this);
878 ServerInstance->Timers->DelTimer(this->PruneTimer);
881 CachedQuery* DNS::GetCache(const std::string &source)
883 dnscache::iterator x = cache->find(source.c_str());
884 if (x != cache->end())
890 void DNS::DelCache(const std::string &source)
892 cache->erase(source.c_str());
895 void Resolver::TriggerCachedResult()
898 OnLookupComplete(CQ->data, time_left, true);
901 /** High level abstraction of dns used by application at large */
902 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
904 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
907 CQ = ServerInstance->Res->GetCache(source);
910 time_left = CQ->CalcTTLRemaining();
913 ServerInstance->Res->DelCache(source);
925 this->myid = ServerInstance->Res->GetIP(source.c_str());
929 querytype = DNS_QUERY_PTR;
930 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
934 querytype = DNS_QUERY_PTR;
935 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
939 this->myid = ServerInstance->Res->GetIP6(source.c_str());
942 case DNS_QUERY_CNAME:
943 this->myid = ServerInstance->Res->GetCName(source.c_str());
950 if (this->myid == -1)
952 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
953 throw ModuleException("Resolver: Couldnt get an id to make a request");
954 /* We shouldnt get here really */
959 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
963 /** Called when an error occurs */
964 void Resolver::OnError(ResolverError, const std::string&)
966 /* Nothing in here */
969 /** Destroy a resolver */
970 Resolver::~Resolver()
972 /* Nothing here (yet) either */
975 /** Get the request id associated with this class */
976 int Resolver::GetId()
981 Module* Resolver::GetCreator()
983 return this->Creator;
986 /** Process a socket read event */
987 void DNS::HandleEvent(EventType, int)
989 /* Fetch the id and result of the next available packet */
990 DNSResult res(0,"",0,"");
992 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
994 res = this->GetResult();
996 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result id %d", res.id);
998 /* Is there a usable request id? */
1001 /* Its an error reply */
1002 if (res.id & ERROR_MASK)
1004 /* Mask off the error bit */
1005 res.id -= ERROR_MASK;
1006 /* Marshall the error to the correct class */
1007 if (Classes[res.id])
1009 if (ServerInstance && ServerInstance->stats)
1010 ServerInstance->stats->statsDnsBad++;
1011 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1012 delete Classes[res.id];
1013 Classes[res.id] = NULL;
1019 /* It is a non-error result, marshall the result to the correct class */
1020 if (Classes[res.id])
1022 if (ServerInstance && ServerInstance->stats)
1023 ServerInstance->stats->statsDnsGood++;
1025 if (!this->GetCache(res.original.c_str()))
1026 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1028 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1029 delete Classes[res.id];
1030 Classes[res.id] = NULL;
1034 if (ServerInstance && ServerInstance->stats)
1035 ServerInstance->stats->statsDns++;
1039 /** Add a derived Resolver to the working set */
1040 bool DNS::AddResolverClass(Resolver* r)
1042 ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
1043 /* Check the pointers validity and the id's validity */
1044 if ((r) && (r->GetId() > -1))
1046 /* Check the slot isnt already occupied -
1047 * This should NEVER happen unless we have
1048 * a severely broken DNS server somewhere
1050 if (!Classes[r->GetId()])
1052 /* Set up the pointer to the class */
1053 Classes[r->GetId()] = r;
1062 /* Pointer or id not valid.
1063 * Free the item and return
1072 void DNS::CleanResolvers(Module* module)
1074 for (int i = 0; i < MAX_REQUEST_ID; i++)
1078 if (Classes[i]->GetCreator() == module)
1080 Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading");
1088 /** Generate pseudo-random number */
1089 unsigned long DNS::PRNG()
1091 unsigned long val = 0;
1093 serverstats* s = ServerInstance->stats;
1094 gettimeofday(&n,NULL);
1095 val = (n.tv_usec ^ (getpid() ^ geteuid()) ^ ((this->currid++)) ^ s->statsAccept) + n.tv_sec;
1096 val = val + (s->statsCollisions ^ s->statsDnsGood) - s->statsDnsBad;
1097 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->ports.size();