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"
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 /* Allocate the processing buffer */
151 DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns)
153 res = new unsigned char[512];
156 RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid);
157 ServerInstance->Timers->AddTimer(RT); /* The timer manager frees this */
160 /* Deallocate the processing buffer */
161 DNSRequest::~DNSRequest()
166 /** Fill a ResourceRecord class based on raw data input */
167 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
169 rr->type = (QueryType)((input[0] << 8) + input[1]);
170 rr->rr_class = (input[2] << 8) + input[3];
171 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
172 rr->rdlength = (input[8] << 8) + input[9];
175 /** Fill a DNSHeader class based on raw data input of a given length */
176 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
178 header->id[0] = input[0];
179 header->id[1] = input[1];
180 header->flags1 = input[2];
181 header->flags2 = input[3];
182 header->qdcount = (input[4] << 8) + input[5];
183 header->ancount = (input[6] << 8) + input[7];
184 header->nscount = (input[8] << 8) + input[9];
185 header->arcount = (input[10] << 8) + input[11];
186 memcpy(header->payload,&input[12],length);
189 /** Empty a DNSHeader class out into raw data, ready for transmission */
190 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
192 output[0] = header->id[0];
193 output[1] = header->id[1];
194 output[2] = header->flags1;
195 output[3] = header->flags2;
196 output[4] = header->qdcount >> 8;
197 output[5] = header->qdcount & 0xFF;
198 output[6] = header->ancount >> 8;
199 output[7] = header->ancount & 0xFF;
200 output[8] = header->nscount >> 8;
201 output[9] = header->nscount & 0xFF;
202 output[10] = header->arcount >> 8;
203 output[11] = header->arcount & 0xFF;
204 memcpy(&output[12],header->payload,length);
207 /** Send requests we have previously built down the UDP socket */
208 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
210 ServerInstance->Logs->Log("RESOLVER", DEBUG,"DNSRequest::SendRequests");
212 unsigned char payload[sizeof(DNSHeader)];
217 DNS::EmptyHeader(payload,header,length);
219 if (this->dnsobj->socketfamily == AF_INET6)
222 memset(&addr,0,sizeof(addr));
223 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
224 addr.sin6_family = AF_INET6;
225 addr.sin6_port = htons(DNS::QUERY_PORT);
226 if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
232 memset(&addr,0,sizeof(addr));
233 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
234 addr.sin_family = AF_INET;
235 addr.sin_port = htons(DNS::QUERY_PORT);
236 if (ServerInstance->SE->SendTo(dnsobj, (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
240 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
244 /** Add a query with a predefined header, and allocate an ID for it. */
245 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
247 /* Is the DNS connection down? */
248 if (this->GetFd() == -1)
252 id = this->PRNG() & DNS::MAX_REQUEST_ID;
254 /* If this id is already 'in flight', pick another. */
256 id = this->PRNG() & DNS::MAX_REQUEST_ID;
258 DNSRequest* req = new DNSRequest(this, id, original);
260 header->id[0] = req->id[0] = id >> 8;
261 header->id[1] = req->id[1] = id & 0xFF;
262 header->flags1 = FLAGS_MASK_RD;
269 /* At this point we already know the id doesnt exist,
270 * so there needs to be no second check for the ::end()
274 /* According to the C++ spec, new never returns NULL. */
278 int DNS::ClearCache()
280 /* This ensures the buckets are reset to sane levels */
281 int rv = this->cache->size();
283 this->cache = new dnscache();
287 int DNS::PruneCache()
290 dnscache* newcache = new dnscache();
291 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
292 /* Dont include expired items (theres no point) */
293 if (i->second.CalcTTLRemaining())
294 newcache->insert(*i);
299 this->cache = newcache;
308 if (this->GetFd() > -1)
310 if (ServerInstance && ServerInstance->SE)
311 ServerInstance->SE->DelFd(this);
312 ServerInstance->SE->Shutdown(this, 2);
313 ServerInstance->SE->Close(this);
316 /* Rehash the cache */
321 /* Create initial dns cache */
322 this->cache = new dnscache();
325 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
327 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
328 ServerInstance->Logs->Log("RESOLVER",DEFAULT," This should not cause a problem, however it is recommended you migrate");
329 ServerInstance->Logs->Log("RESOLVER",DEFAULT," to a true IPv6 environment.");
330 this->ip6munge = true;
333 this->socketfamily = AF_INET;
334 if (strchr(ServerInstance->Config->DNSServer,':'))
336 this->socketfamily = AF_INET6;
337 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
341 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
345 /* Initialize mastersocket */
346 int s = irc::sockets::OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
348 ServerInstance->SE->NonBlocking(this->GetFd());
350 /* Have we got a socket and is it nonblocking? */
351 if (this->GetFd() != -1)
353 /* Bind the port - port 0 INADDR_ANY */
354 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
357 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error binding dns socket");
358 ServerInstance->SE->Shutdown(this, 2);
359 ServerInstance->SE->Close(this);
363 if (this->GetFd() >= 0)
365 /* Hook the descriptor into the socket engine */
366 if (ServerInstance && ServerInstance->SE)
368 if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
370 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
371 ServerInstance->SE->Shutdown(this, 2);
372 ServerInstance->SE->Close(this);
380 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error creating dns socket");
384 /** Initialise the DNS UDP socket so that we can send requests */
387 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
388 /* Clear the Resolver class table */
389 memset(Classes,0,sizeof(Classes));
391 /* Clear the requests class table */
392 memset(requests,0,sizeof(requests));
394 /* Set the id of the next request to 0
398 /* DNS::Rehash() sets this to a valid ptr
402 /* Again, DNS::Rehash() sets this to a
407 /* Actually read the settings
411 this->PruneTimer = new CacheTimer(this);
413 ServerInstance->Timers->AddTimer(this->PruneTimer);
416 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
417 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
419 short payloadpos = 0;
420 const char* tempchr, *tempchr2 = name;
421 unsigned short length;
423 /* split name up into labels, create query */
424 while ((tempchr = strchr(tempchr2,'.')) != NULL)
426 length = tempchr - tempchr2;
427 if (payloadpos + length + 1 > 507)
429 payload[payloadpos++] = length;
430 memcpy(&payload[payloadpos],tempchr2,length);
431 payloadpos += length;
432 tempchr2 = &tempchr[1];
434 length = strlen(tempchr2);
437 if (payloadpos + length + 2 > 507)
439 payload[payloadpos++] = length;
440 memcpy(&payload[payloadpos],tempchr2,length);
441 payloadpos += length;
442 payload[payloadpos++] = 0;
444 if (payloadpos > 508)
447 memcpy(&payload[payloadpos],&length,2);
448 length = htons(rr_class);
449 memcpy(&payload[payloadpos + 2],&length,2);
450 return payloadpos + 4;
453 /** Start lookup of an hostname to an IP address */
454 int DNS::GetIP(const char *name)
460 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
463 DNSRequest* req = this->AddQuery(&h, id, name);
465 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
471 /** Start lookup of an hostname to an IPv6 address */
472 int DNS::GetIP6(const char *name)
478 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
481 DNSRequest* req = this->AddQuery(&h, id, name);
483 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
489 /** Start lookup of a cname to another name */
490 int DNS::GetCName(const char *alias)
496 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
499 DNSRequest* req = this->AddQuery(&h, id, alias);
501 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
507 /** Start lookup of an IP address to a hostname */
508 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
515 if (fp == PROTOCOL_IPV6)
518 if (inet_pton(AF_INET6, ip, &i) > 0)
520 DNS::MakeIP6Int(query, &i);
523 /* Invalid IP address */
529 if (inet_aton(ip, &i))
531 unsigned char* c = (unsigned char*)&i.s_addr;
532 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
535 /* Invalid IP address */
539 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
542 DNSRequest* req = this->AddQuery(&h, id, ip);
544 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
550 /** Build an ipv6 reverse domain from an in6_addr
552 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
554 const char* hex = "0123456789abcdef";
555 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
559 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
562 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
563 *query++ = '.'; /* Seperator */
565 strcpy(query,"ip6.arpa"); /* Suffix the string */
568 /** Return the next id which is ready, and the result attached to it */
569 DNSResult DNS::GetResult()
571 /* Fetch dns query response and decide where it belongs */
574 unsigned char buffer[sizeof(DNSHeader)];
575 irc::sockets::sockaddrs from;
576 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
577 const char* ipaddr_from;
578 unsigned short int port_from = 0;
580 int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);
582 /* Did we get the whole header? */
585 /* Nope - something screwed up. */
586 return DNSResult(-1,"",0,"");
589 /* Check wether the reply came from a different DNS
590 * server to the one we sent it to, or the source-port
592 * A user could in theory still spoof dns packets anyway
593 * but this is less trivial than just sending garbage
594 * to the client, which is possible without this check.
596 * -- Thanks jilles for pointing this one out.
599 if (this->socketfamily == AF_INET6)
601 ipaddr_from = inet_ntop(AF_INET6, &from.in6.sin6_addr, nbuf, sizeof(nbuf));
602 port_from = ntohs(from.in6.sin6_port);
606 ipaddr_from = inet_ntoa(from.in4.sin_addr);
607 port_from = ntohs(from.in4.sin_port);
610 /* We cant perform this security check if you're using 4in6.
611 * Tough luck to you, choose one or't other!
615 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
617 return DNSResult(-1,"",0,"");
621 /* Put the read header info into a header class */
622 DNS::FillHeader(&header,buffer,length - 12);
624 /* Get the id of this request.
625 * Its a 16 bit value stored in two char's,
626 * so we use logic shifts to create the value.
628 unsigned long this_id = header.id[1] + (header.id[0] << 8);
630 /* Do we have a pending request matching this id? */
631 if (!requests[this_id])
633 /* Somehow we got a DNS response for a request we never made... */
634 return DNSResult(-1,"",0,"");
638 /* Remove the query from the list of pending queries */
639 req = requests[this_id];
640 requests[this_id] = NULL;
643 /* Inform the DNSRequest class that it has a result to be read.
644 * When its finished it will return a DNSInfo which is a pair of
645 * unsigned char* resource record data, and an error message.
647 DNSInfo data = req->ResultIsReady(header, length);
648 std::string resultstr;
650 /* Check if we got a result, if we didnt, its an error */
651 if (data.first == NULL)
654 * Mask the ID with the value of ERROR_MASK, so that
655 * the dns_deal_with_classes() function knows that its
656 * an error response and needs to be treated uniquely.
657 * Put the error message in the second field.
659 std::string ro = req->orig;
661 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
665 unsigned long ttl = req->ttl;
668 /* Forward lookups come back as binary data. We must format them into ascii */
672 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
673 resultstr = formatted;
678 inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted));
679 char* c = strstr(formatted,":0:");
682 memmove(c+1,c+2,strlen(c+2) + 1);
684 while (memcmp(c,"0:",2) == 0)
685 memmove(c,c+2,strlen(c+2) + 1);
686 if (memcmp(c,"0",2) == 0)
688 if (memcmp(formatted,"0::",3) == 0)
689 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
691 resultstr = formatted;
693 /* Special case. Sending ::1 around between servers
694 * and to clients is dangerous, because the : on the
695 * start makes the client or server interpret the IP
696 * as the last parameter on the line with a value ":1".
698 if (*formatted == ':')
699 resultstr.insert(0, "0");
703 case DNS_QUERY_CNAME:
704 /* Identical handling to PTR */
707 /* Reverse lookups just come back as char* */
708 resultstr = std::string((const char*)data.first);
715 /* Build the reply with the id and hostname/ip in it */
716 std::string ro = req->orig;
718 return DNSResult(this_id,resultstr,ttl,ro);
722 /** A result is ready, process it */
723 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
731 /* This is just to keep _FORTIFY_SOURCE happy */
732 rr.type = DNS_QUERY_NONE;
734 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
735 rr.rr_class = 0; /* Same for VC++ */
737 if (!(header.flags1 & FLAGS_MASK_QR))
738 return std::make_pair((unsigned char*)NULL,"Not a query result");
740 if (header.flags1 & FLAGS_MASK_OPCODE)
741 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
743 if (header.flags2 & FLAGS_MASK_RCODE)
744 return std::make_pair((unsigned char*)NULL,"Domain name not found");
746 if (header.ancount < 1)
747 return std::make_pair((unsigned char*)NULL,"No resource records returned");
749 /* Subtract the length of the header from the length of the packet */
752 while ((unsigned int)q < header.qdcount && i < length)
754 if (header.payload[i] > 63)
761 if (header.payload[i] == 0)
766 else i += header.payload[i] + 1;
770 while ((unsigned)curanswer < header.ancount)
773 while (q == 0 && i < length)
775 if (header.payload[i] > 63)
782 if (header.payload[i] == 0)
787 else i += header.payload[i] + 1; /* skip length and label */
791 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
793 /* XXX: We actually initialise 'rr' here including its ttl field */
794 DNS::FillResourceRecord(&rr,&header.payload[i]);
797 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);
798 if (rr.type != this->type)
804 if (rr.rr_class != this->rr_class)
812 if ((unsigned int)curanswer == header.ancount)
813 return std::make_pair((unsigned char*)NULL,"No A, AAAA or PTR type answers (" + ConvToStr(header.ancount) + " answers)");
815 if (i + rr.rdlength > (unsigned int)length)
816 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
818 if (rr.rdlength > 1023)
819 return std::make_pair((unsigned char*)NULL,"Resource record too large");
825 case DNS_QUERY_CNAME:
826 /* CNAME and PTR have the same processing code */
830 while (q == 0 && i < length && o + 256 < 1023)
832 if (header.payload[i] > 63)
834 memcpy(&ptr,&header.payload[i],2);
835 i = ntohs(ptr) - 0xC000 - 12;
839 if (header.payload[i] == 0)
848 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
849 o += header.payload[i];
850 i += header.payload[i] + 1;
857 memcpy(res,&header.payload[i],rr.rdlength);
858 res[rr.rdlength] = 0;
861 memcpy(res,&header.payload[i],rr.rdlength);
862 res[rr.rdlength] = 0;
865 memcpy(res,&header.payload[i],rr.rdlength);
866 res[rr.rdlength] = 0;
869 return std::make_pair(res,"No error");
872 /** Close the master socket */
875 ServerInstance->SE->Shutdown(this, 2);
876 ServerInstance->SE->Close(this);
877 ServerInstance->Timers->DelTimer(this->PruneTimer);
882 CachedQuery* DNS::GetCache(const std::string &source)
884 dnscache::iterator x = cache->find(source.c_str());
885 if (x != cache->end())
891 void DNS::DelCache(const std::string &source)
893 cache->erase(source.c_str());
896 void Resolver::TriggerCachedResult()
899 OnLookupComplete(CQ->data, time_left, true);
902 /** High level abstraction of dns used by application at large */
903 Resolver::Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator) : Creator(creator), input(source), querytype(qt)
905 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
908 CQ = ServerInstance->Res->GetCache(source);
911 time_left = CQ->CalcTTLRemaining();
914 ServerInstance->Res->DelCache(source);
926 this->myid = ServerInstance->Res->GetIP(source.c_str());
930 querytype = DNS_QUERY_PTR;
931 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
935 querytype = DNS_QUERY_PTR;
936 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
940 this->myid = ServerInstance->Res->GetIP6(source.c_str());
943 case DNS_QUERY_CNAME:
944 this->myid = ServerInstance->Res->GetCName(source.c_str());
951 if (this->myid == -1)
953 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
954 throw ModuleException("Resolver: Couldnt get an id to make a request");
955 /* We shouldnt get here really */
960 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
964 /** Called when an error occurs */
965 void Resolver::OnError(ResolverError, const std::string&)
967 /* Nothing in here */
970 /** Destroy a resolver */
971 Resolver::~Resolver()
973 /* Nothing here (yet) either */
976 /** Get the request id associated with this class */
977 int Resolver::GetId()
982 Module* Resolver::GetCreator()
984 return this->Creator;
987 /** Process a socket read event */
988 void DNS::HandleEvent(EventType, int)
990 /* Fetch the id and result of the next available packet */
991 DNSResult res(0,"",0,"");
993 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
995 res = this->GetResult();
997 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result id %d", res.id);
999 /* Is there a usable request id? */
1002 /* Its an error reply */
1003 if (res.id & ERROR_MASK)
1005 /* Mask off the error bit */
1006 res.id -= ERROR_MASK;
1007 /* Marshall the error to the correct class */
1008 if (Classes[res.id])
1010 if (ServerInstance && ServerInstance->stats)
1011 ServerInstance->stats->statsDnsBad++;
1012 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1013 delete Classes[res.id];
1014 Classes[res.id] = NULL;
1020 /* It is a non-error result, marshall the result to the correct class */
1021 if (Classes[res.id])
1023 if (ServerInstance && ServerInstance->stats)
1024 ServerInstance->stats->statsDnsGood++;
1026 if (!this->GetCache(res.original.c_str()))
1027 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1029 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1030 delete Classes[res.id];
1031 Classes[res.id] = NULL;
1035 if (ServerInstance && ServerInstance->stats)
1036 ServerInstance->stats->statsDns++;
1040 /** Add a derived Resolver to the working set */
1041 bool DNS::AddResolverClass(Resolver* r)
1043 ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
1044 /* Check the pointers validity and the id's validity */
1045 if ((r) && (r->GetId() > -1))
1047 /* Check the slot isnt already occupied -
1048 * This should NEVER happen unless we have
1049 * a severely broken DNS server somewhere
1051 if (!Classes[r->GetId()])
1053 /* Set up the pointer to the class */
1054 Classes[r->GetId()] = r;
1063 /* Pointer or id not valid.
1064 * Free the item and return
1073 void DNS::CleanResolvers(Module* module)
1075 for (int i = 0; i < MAX_REQUEST_ID; i++)
1079 if (Classes[i]->GetCreator() == module)
1081 Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading");
1089 /** Generate pseudo-random number */
1090 unsigned long DNS::PRNG()
1092 unsigned long val = 0;
1094 serverstats* s = ServerInstance->stats;
1095 gettimeofday(&n,NULL);
1096 val = (n.tv_usec ^ (getpid() ^ geteuid()) ^ ((this->currid++)) ^ s->statsAccept) + n.tv_sec;
1097 val = val + (s->statsCollisions ^ s->statsDnsGood) - s->statsDnsBad;
1098 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->ports.size();