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)
139 if (ServerInstance->Res->requests[watchid] == watch)
141 /* Still exists, whack it */
142 if (ServerInstance->Res->Classes[watchid])
144 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
145 delete ServerInstance->Res->Classes[watchid];
146 ServerInstance->Res->Classes[watchid] = NULL;
148 ServerInstance->Res->requests[watchid] = NULL;
154 /* Allocate the processing buffer */
155 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int rid, const std::string &original) : dnsobj(dns), ServerInstance(Instance)
157 res = new unsigned char[512];
160 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, rid);
161 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
164 /* Deallocate the processing buffer */
165 DNSRequest::~DNSRequest()
170 /** Fill a ResourceRecord class based on raw data input */
171 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
173 rr->type = (QueryType)((input[0] << 8) + input[1]);
174 rr->rr_class = (input[2] << 8) + input[3];
175 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
176 rr->rdlength = (input[8] << 8) + input[9];
179 /** Fill a DNSHeader class based on raw data input of a given length */
180 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
182 header->id[0] = input[0];
183 header->id[1] = input[1];
184 header->flags1 = input[2];
185 header->flags2 = input[3];
186 header->qdcount = (input[4] << 8) + input[5];
187 header->ancount = (input[6] << 8) + input[7];
188 header->nscount = (input[8] << 8) + input[9];
189 header->arcount = (input[10] << 8) + input[11];
190 memcpy(header->payload,&input[12],length);
193 /** Empty a DNSHeader class out into raw data, ready for transmission */
194 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
196 output[0] = header->id[0];
197 output[1] = header->id[1];
198 output[2] = header->flags1;
199 output[3] = header->flags2;
200 output[4] = header->qdcount >> 8;
201 output[5] = header->qdcount & 0xFF;
202 output[6] = header->ancount >> 8;
203 output[7] = header->ancount & 0xFF;
204 output[8] = header->nscount >> 8;
205 output[9] = header->nscount & 0xFF;
206 output[10] = header->arcount >> 8;
207 output[11] = header->arcount & 0xFF;
208 memcpy(&output[12],header->payload,length);
211 /** Send requests we have previously built down the UDP socket */
212 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
214 ServerInstance->Logs->Log("RESOLVER", DEBUG,"DNSRequest::SendRequests");
216 unsigned char payload[sizeof(DNSHeader)];
221 DNS::EmptyHeader(payload,header,length);
223 if (this->dnsobj->socketfamily == AF_INET6)
226 memset(&addr,0,sizeof(addr));
227 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
228 addr.sin6_family = AF_INET6;
229 addr.sin6_port = htons(DNS::QUERY_PORT);
230 if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
236 memset(&addr,0,sizeof(addr));
237 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
238 addr.sin_family = AF_INET;
239 addr.sin_port = htons(DNS::QUERY_PORT);
240 if (ServerInstance->SE->SendTo(dnsobj, (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
244 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
248 /** Add a query with a predefined header, and allocate an ID for it. */
249 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
251 /* Is the DNS connection down? */
252 if (this->GetFd() == -1)
256 id = this->PRNG() & DNS::MAX_REQUEST_ID;
258 /* If this id is already 'in flight', pick another. */
260 id = this->PRNG() & DNS::MAX_REQUEST_ID;
262 DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
264 header->id[0] = req->id[0] = id >> 8;
265 header->id[1] = req->id[1] = id & 0xFF;
266 header->flags1 = FLAGS_MASK_RD;
273 /* At this point we already know the id doesnt exist,
274 * so there needs to be no second check for the ::end()
278 /* According to the C++ spec, new never returns NULL. */
282 int DNS::ClearCache()
284 /* This ensures the buckets are reset to sane levels */
285 int rv = this->cache->size();
287 this->cache = new dnscache();
291 int DNS::PruneCache()
294 dnscache* newcache = new dnscache();
295 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
296 /* Dont include expired items (theres no point) */
297 if (i->second.CalcTTLRemaining())
298 newcache->insert(*i);
303 this->cache = newcache;
312 if (this->GetFd() > -1)
314 if (ServerInstance && ServerInstance->SE)
315 ServerInstance->SE->DelFd(this);
316 ServerInstance->SE->Shutdown(this, 2);
317 ServerInstance->SE->Close(this);
320 /* Rehash the cache */
325 /* Create initial dns cache */
326 this->cache = new dnscache();
329 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
331 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
332 ServerInstance->Logs->Log("RESOLVER",DEFAULT," This should not cause a problem, however it is recommended you migrate");
333 ServerInstance->Logs->Log("RESOLVER",DEFAULT," to a true IPv6 environment.");
334 this->ip6munge = true;
337 this->socketfamily = AF_INET;
338 if (strchr(ServerInstance->Config->DNSServer,':'))
340 this->socketfamily = AF_INET6;
341 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
345 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
349 /* Initialize mastersocket */
350 int s = irc::sockets::OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
352 ServerInstance->SE->NonBlocking(this->GetFd());
354 /* Have we got a socket and is it nonblocking? */
355 if (this->GetFd() != -1)
357 /* Bind the port - port 0 INADDR_ANY */
358 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
361 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error binding dns socket");
362 ServerInstance->SE->Shutdown(this, 2);
363 ServerInstance->SE->Close(this);
367 if (this->GetFd() >= 0)
369 /* Hook the descriptor into the socket engine */
370 if (ServerInstance && ServerInstance->SE)
372 if (!ServerInstance->SE->AddFd(this))
374 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
375 ServerInstance->SE->Shutdown(this, 2);
376 ServerInstance->SE->Close(this);
384 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error creating dns socket");
388 /** Initialise the DNS UDP socket so that we can send requests */
389 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
391 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
392 /* Clear the Resolver class table */
393 memset(Classes,0,sizeof(Classes));
395 /* Clear the requests class table */
396 memset(requests,0,sizeof(requests));
398 /* Set the id of the next request to 0
402 /* DNS::Rehash() sets this to a valid ptr
406 /* Again, DNS::Rehash() sets this to a
411 /* Actually read the settings
415 this->PruneTimer = new CacheTimer(ServerInstance, this);
417 ServerInstance->Timers->AddTimer(this->PruneTimer);
420 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
421 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
423 short payloadpos = 0;
424 const char* tempchr, *tempchr2 = name;
425 unsigned short length;
427 /* split name up into labels, create query */
428 while ((tempchr = strchr(tempchr2,'.')) != NULL)
430 length = tempchr - tempchr2;
431 if (payloadpos + length + 1 > 507)
433 payload[payloadpos++] = length;
434 memcpy(&payload[payloadpos],tempchr2,length);
435 payloadpos += length;
436 tempchr2 = &tempchr[1];
438 length = strlen(tempchr2);
441 if (payloadpos + length + 2 > 507)
443 payload[payloadpos++] = length;
444 memcpy(&payload[payloadpos],tempchr2,length);
445 payloadpos += length;
446 payload[payloadpos++] = 0;
448 if (payloadpos > 508)
451 memcpy(&payload[payloadpos],&length,2);
452 length = htons(rr_class);
453 memcpy(&payload[payloadpos + 2],&length,2);
454 return payloadpos + 4;
457 /** Start lookup of an hostname to an IP address */
458 int DNS::GetIP(const char *name)
464 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
467 DNSRequest* req = this->AddQuery(&h, id, name);
469 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
475 /** Start lookup of an hostname to an IPv6 address */
476 int DNS::GetIP6(const char *name)
482 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
485 DNSRequest* req = this->AddQuery(&h, id, name);
487 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
493 /** Start lookup of a cname to another name */
494 int DNS::GetCName(const char *alias)
500 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
503 DNSRequest* req = this->AddQuery(&h, id, alias);
505 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
511 /** Start lookup of an IP address to a hostname */
512 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
519 if (fp == PROTOCOL_IPV6)
522 if (inet_pton(AF_INET6, ip, &i) > 0)
524 DNS::MakeIP6Int(query, &i);
527 /* Invalid IP address */
533 if (inet_aton(ip, &i))
535 unsigned char* c = (unsigned char*)&i.s_addr;
536 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
539 /* Invalid IP address */
543 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
546 DNSRequest* req = this->AddQuery(&h, id, ip);
548 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
554 /** Build an ipv6 reverse domain from an in6_addr
556 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
558 const char* hex = "0123456789abcdef";
559 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
563 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
566 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
567 *query++ = '.'; /* Seperator */
569 strcpy(query,"ip6.arpa"); /* Suffix the string */
572 /** Return the next id which is ready, and the result attached to it */
573 DNSResult DNS::GetResult()
575 /* Fetch dns query response and decide where it belongs */
578 unsigned char buffer[sizeof(DNSHeader)];
579 irc::sockets::sockaddrs from;
580 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
581 const char* ipaddr_from;
582 unsigned short int port_from = 0;
584 int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, &from.sa, &x);
586 /* Did we get the whole header? */
589 /* Nope - something screwed up. */
590 return DNSResult(-1,"",0,"");
593 /* Check wether the reply came from a different DNS
594 * server to the one we sent it to, or the source-port
596 * A user could in theory still spoof dns packets anyway
597 * but this is less trivial than just sending garbage
598 * to the client, which is possible without this check.
600 * -- Thanks jilles for pointing this one out.
603 if (this->socketfamily == AF_INET6)
605 ipaddr_from = inet_ntop(AF_INET6, &from.in6.sin6_addr, nbuf, sizeof(nbuf));
606 port_from = ntohs(from.in6.sin6_port);
610 ipaddr_from = inet_ntoa(from.in4.sin_addr);
611 port_from = ntohs(from.in4.sin_port);
614 /* We cant perform this security check if you're using 4in6.
615 * Tough luck to you, choose one or't other!
619 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
621 return DNSResult(-1,"",0,"");
625 /* Put the read header info into a header class */
626 DNS::FillHeader(&header,buffer,length - 12);
628 /* Get the id of this request.
629 * Its a 16 bit value stored in two char's,
630 * so we use logic shifts to create the value.
632 unsigned long this_id = header.id[1] + (header.id[0] << 8);
634 /* Do we have a pending request matching this id? */
635 if (!requests[this_id])
637 /* Somehow we got a DNS response for a request we never made... */
638 return DNSResult(-1,"",0,"");
642 /* Remove the query from the list of pending queries */
643 req = requests[this_id];
644 requests[this_id] = NULL;
647 /* Inform the DNSRequest class that it has a result to be read.
648 * When its finished it will return a DNSInfo which is a pair of
649 * unsigned char* resource record data, and an error message.
651 DNSInfo data = req->ResultIsReady(header, length);
652 std::string resultstr;
654 /* Check if we got a result, if we didnt, its an error */
655 if (data.first == NULL)
658 * Mask the ID with the value of ERROR_MASK, so that
659 * the dns_deal_with_classes() function knows that its
660 * an error response and needs to be treated uniquely.
661 * Put the error message in the second field.
663 std::string ro = req->orig;
665 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
669 unsigned long ttl = req->ttl;
672 /* Forward lookups come back as binary data. We must format them into ascii */
676 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
677 resultstr = formatted;
682 inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted));
683 char* c = strstr(formatted,":0:");
686 memmove(c+1,c+2,strlen(c+2) + 1);
688 while (memcmp(c,"0:",2) == 0)
689 memmove(c,c+2,strlen(c+2) + 1);
690 if (memcmp(c,"0",2) == 0)
692 if (memcmp(formatted,"0::",3) == 0)
693 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
695 resultstr = formatted;
697 /* Special case. Sending ::1 around between servers
698 * and to clients is dangerous, because the : on the
699 * start makes the client or server interpret the IP
700 * as the last parameter on the line with a value ":1".
702 if (*formatted == ':')
703 resultstr.insert(0, "0");
707 case DNS_QUERY_CNAME:
708 /* Identical handling to PTR */
711 /* Reverse lookups just come back as char* */
712 resultstr = std::string((const char*)data.first);
719 /* Build the reply with the id and hostname/ip in it */
720 std::string ro = req->orig;
722 return DNSResult(this_id,resultstr,ttl,ro);
726 /** A result is ready, process it */
727 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
735 /* This is just to keep _FORTIFY_SOURCE happy */
736 rr.type = DNS_QUERY_NONE;
738 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
739 rr.rr_class = 0; /* Same for VC++ */
741 if (!(header.flags1 & FLAGS_MASK_QR))
742 return std::make_pair((unsigned char*)NULL,"Not a query result");
744 if (header.flags1 & FLAGS_MASK_OPCODE)
745 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
747 if (header.flags2 & FLAGS_MASK_RCODE)
748 return std::make_pair((unsigned char*)NULL,"Domain name not found");
750 if (header.ancount < 1)
751 return std::make_pair((unsigned char*)NULL,"No resource records returned");
753 /* Subtract the length of the header from the length of the packet */
756 while ((unsigned int)q < header.qdcount && i < length)
758 if (header.payload[i] > 63)
765 if (header.payload[i] == 0)
770 else i += header.payload[i] + 1;
774 while ((unsigned)curanswer < header.ancount)
777 while (q == 0 && i < length)
779 if (header.payload[i] > 63)
786 if (header.payload[i] == 0)
791 else i += header.payload[i] + 1; /* skip length and label */
795 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
797 /* XXX: We actually initialise 'rr' here including its ttl field */
798 DNS::FillResourceRecord(&rr,&header.payload[i]);
801 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);
802 if (rr.type != this->type)
808 if (rr.rr_class != this->rr_class)
816 if ((unsigned int)curanswer == header.ancount)
817 return std::make_pair((unsigned char*)NULL,"No A, AAAA or PTR type answers (" + ConvToStr(header.ancount) + " answers)");
819 if (i + rr.rdlength > (unsigned int)length)
820 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
822 if (rr.rdlength > 1023)
823 return std::make_pair((unsigned char*)NULL,"Resource record too large");
829 case DNS_QUERY_CNAME:
830 /* CNAME and PTR have the same processing code */
834 while (q == 0 && i < length && o + 256 < 1023)
836 if (header.payload[i] > 63)
838 memcpy(&ptr,&header.payload[i],2);
839 i = ntohs(ptr) - 0xC000 - 12;
843 if (header.payload[i] == 0)
852 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
853 o += header.payload[i];
854 i += header.payload[i] + 1;
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 memcpy(res,&header.payload[i],rr.rdlength);
870 res[rr.rdlength] = 0;
873 return std::make_pair(res,"No error");
876 /** Close the master socket */
879 ServerInstance->SE->Shutdown(this, 2);
880 ServerInstance->SE->Close(this);
881 ServerInstance->Timers->DelTimer(this->PruneTimer);
886 CachedQuery* DNS::GetCache(const std::string &source)
888 dnscache::iterator x = cache->find(source.c_str());
889 if (x != cache->end())
895 void DNS::DelCache(const std::string &source)
897 cache->erase(source.c_str());
900 void Resolver::TriggerCachedResult()
903 OnLookupComplete(CQ->data, time_left, true);
906 /** High level abstraction of dns used by application at large */
907 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
909 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
912 CQ = ServerInstance->Res->GetCache(source);
915 time_left = CQ->CalcTTLRemaining();
918 ServerInstance->Res->DelCache(source);
930 this->myid = ServerInstance->Res->GetIP(source.c_str());
934 querytype = DNS_QUERY_PTR;
935 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
939 querytype = DNS_QUERY_PTR;
940 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
944 this->myid = ServerInstance->Res->GetIP6(source.c_str());
947 case DNS_QUERY_CNAME:
948 this->myid = ServerInstance->Res->GetCName(source.c_str());
955 if (this->myid == -1)
957 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
958 throw ModuleException("Resolver: Couldnt get an id to make a request");
959 /* We shouldnt get here really */
964 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
968 /** Called when an error occurs */
969 void Resolver::OnError(ResolverError, const std::string&)
971 /* Nothing in here */
974 /** Destroy a resolver */
975 Resolver::~Resolver()
977 /* Nothing here (yet) either */
980 /** Get the request id associated with this class */
981 int Resolver::GetId()
986 Module* Resolver::GetCreator()
988 return this->Creator;
991 /** Process a socket read event */
992 void DNS::HandleEvent(EventType, int)
994 /* Fetch the id and result of the next available packet */
995 DNSResult res(0,"",0,"");
997 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
999 res = this->GetResult();
1001 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result id %d", res.id);
1003 /* Is there a usable request id? */
1006 /* Its an error reply */
1007 if (res.id & ERROR_MASK)
1009 /* Mask off the error bit */
1010 res.id -= ERROR_MASK;
1011 /* Marshall the error to the correct class */
1012 if (Classes[res.id])
1014 if (ServerInstance && ServerInstance->stats)
1015 ServerInstance->stats->statsDnsBad++;
1016 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1017 delete Classes[res.id];
1018 Classes[res.id] = NULL;
1024 /* It is a non-error result, marshall the result to the correct class */
1025 if (Classes[res.id])
1027 if (ServerInstance && ServerInstance->stats)
1028 ServerInstance->stats->statsDnsGood++;
1030 if (!this->GetCache(res.original.c_str()))
1031 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1033 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1034 delete Classes[res.id];
1035 Classes[res.id] = NULL;
1039 if (ServerInstance && ServerInstance->stats)
1040 ServerInstance->stats->statsDns++;
1044 /** Add a derived Resolver to the working set */
1045 bool DNS::AddResolverClass(Resolver* r)
1047 ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
1048 /* Check the pointers validity and the id's validity */
1049 if ((r) && (r->GetId() > -1))
1051 /* Check the slot isnt already occupied -
1052 * This should NEVER happen unless we have
1053 * a severely broken DNS server somewhere
1055 if (!Classes[r->GetId()])
1057 /* Set up the pointer to the class */
1058 Classes[r->GetId()] = r;
1067 /* Pointer or id not valid.
1068 * Free the item and return
1077 void DNS::CleanResolvers(Module* module)
1079 for (int i = 0; i < MAX_REQUEST_ID; i++)
1083 if (Classes[i]->GetCreator() == module)
1085 Classes[i]->OnError(RESOLVER_FORCEUNLOAD, "Parent module is unloading");
1093 /** Generate pseudo-random number */
1094 unsigned long DNS::PRNG()
1096 unsigned long val = 0;
1098 serverstats* s = ServerInstance->stats;
1099 gettimeofday(&n,NULL);
1100 val = (n.tv_usec ^ (getpid() ^ geteuid()) ^ ((this->currid++)) ^ s->statsAccept) + n.tv_sec;
1101 val = val + (s->statsCollisions ^ s->statsDnsGood) - s->statsDnsBad;
1102 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->ports.size();