1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2008 InspIRCd Development Team
6 * See: http://www.inspircd.org/wiki/index.php/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, int result_we_want);
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, time(NULL)), 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);
221 if (this->dnsobj->socketfamily == AF_INET6)
224 memset(&addr,0,sizeof(addr));
225 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
226 addr.sin6_family = AF_INET6;
227 addr.sin6_port = htons(DNS::QUERY_PORT);
228 if (ServerInstance->SE->SendTo(dnsobj, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
235 memset(&addr,0,sizeof(addr));
236 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
237 addr.sin_family = AF_INET;
238 addr.sin_port = htons(DNS::QUERY_PORT);
239 if (ServerInstance->SE->SendTo(dnsobj, (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
243 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Sent OK");
247 /** Add a query with a predefined header, and allocate an ID for it. */
248 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
250 /* Is the DNS connection down? */
251 if (this->GetFd() == -1)
255 id = this->PRNG() & DNS::MAX_REQUEST_ID;
257 /* If this id is already 'in flight', pick another. */
259 id = this->PRNG() & DNS::MAX_REQUEST_ID;
261 DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
263 header->id[0] = req->id[0] = id >> 8;
264 header->id[1] = req->id[1] = id & 0xFF;
265 header->flags1 = FLAGS_MASK_RD;
272 /* At this point we already know the id doesnt exist,
273 * so there needs to be no second check for the ::end()
277 /* According to the C++ spec, new never returns NULL. */
281 int DNS::ClearCache()
283 /* This ensures the buckets are reset to sane levels */
284 int rv = this->cache->size();
286 this->cache = new dnscache();
290 int DNS::PruneCache()
293 dnscache* newcache = new dnscache();
294 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
295 /* Dont include expired items (theres no point) */
296 if (i->second.CalcTTLRemaining())
297 newcache->insert(*i);
302 this->cache = newcache;
311 if (this->GetFd() > -1)
313 if (ServerInstance && ServerInstance->SE)
314 ServerInstance->SE->DelFd(this);
315 ServerInstance->SE->Shutdown(this, 2);
316 ServerInstance->SE->Close(this);
319 /* Rehash the cache */
324 /* Create initial dns cache */
325 this->cache = new dnscache();
328 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
330 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
331 ServerInstance->Logs->Log("RESOLVER",DEFAULT," This should not cause a problem, however it is recommended you migrate");
332 ServerInstance->Logs->Log("RESOLVER",DEFAULT," to a true IPv6 environment.");
333 this->ip6munge = true;
336 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 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
352 /* Initialize mastersocket */
353 int s = irc::sockets::OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
355 ServerInstance->SE->NonBlocking(this->GetFd());
357 /* Have we got a socket and is it nonblocking? */
358 if (this->GetFd() != -1)
360 /* Bind the port - port 0 INADDR_ANY */
361 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
364 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error binding dns socket");
365 ServerInstance->SE->Shutdown(this, 2);
366 ServerInstance->SE->Close(this);
370 if (this->GetFd() >= 0)
372 /* Hook the descriptor into the socket engine */
373 if (ServerInstance && ServerInstance->SE)
375 if (!ServerInstance->SE->AddFd(this))
377 ServerInstance->Logs->Log("RESOLVER",DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
378 ServerInstance->SE->Shutdown(this, 2);
379 ServerInstance->SE->Close(this);
387 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error creating dns socket");
391 /** Initialise the DNS UDP socket so that we can send requests */
392 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
394 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::DNS");
395 /* Clear the Resolver class table */
396 memset(Classes,0,sizeof(Classes));
398 /* Clear the requests class table */
399 memset(requests,0,sizeof(requests));
401 /* Set the id of the next request to 0
405 /* DNS::Rehash() sets this to a valid ptr
409 /* Again, DNS::Rehash() sets this to a
414 /* Actually read the settings
418 this->PruneTimer = new CacheTimer(ServerInstance, this);
420 ServerInstance->Timers->AddTimer(this->PruneTimer);
423 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
424 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
426 short payloadpos = 0;
427 const char* tempchr, *tempchr2 = name;
428 unsigned short length;
430 /* split name up into labels, create query */
431 while ((tempchr = strchr(tempchr2,'.')) != NULL)
433 length = tempchr - tempchr2;
434 if (payloadpos + length + 1 > 507)
436 payload[payloadpos++] = length;
437 memcpy(&payload[payloadpos],tempchr2,length);
438 payloadpos += length;
439 tempchr2 = &tempchr[1];
441 length = strlen(tempchr2);
444 if (payloadpos + length + 2 > 507)
446 payload[payloadpos++] = length;
447 memcpy(&payload[payloadpos],tempchr2,length);
448 payloadpos += length;
449 payload[payloadpos++] = 0;
451 if (payloadpos > 508)
454 memcpy(&payload[payloadpos],&length,2);
455 length = htons(rr_class);
456 memcpy(&payload[payloadpos + 2],&length,2);
457 return payloadpos + 4;
460 /** Start lookup of an hostname to an IP address */
461 int DNS::GetIP(const char *name)
467 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
470 DNSRequest* req = this->AddQuery(&h, id, name);
472 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
478 /** Start lookup of an hostname to an IPv6 address */
479 int DNS::GetIP6(const char *name)
485 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
488 DNSRequest* req = this->AddQuery(&h, id, name);
490 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
496 /** Start lookup of a cname to another name */
497 int DNS::GetCName(const char *alias)
503 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
506 DNSRequest* req = this->AddQuery(&h, id, alias);
508 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
514 /** Start lookup of an IP address to a hostname */
515 int DNS::GetName(const irc::sockets::insp_inaddr *ip)
523 unsigned char* c = (unsigned char*)&ip->s6_addr;
524 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
525 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
526 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
527 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
529 DNS::MakeIP6Int(query, (in6_addr*)ip);
531 unsigned char* c = (unsigned char*)&ip->s_addr;
532 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
535 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
538 DNSRequest* req = this->AddQuery(&h, id, irc::sockets::insp_ntoa(*ip));
540 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
546 /** Start lookup of an IP address to a hostname */
547 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
553 #ifdef SUPPORT_IP6LINKS
554 if (fp == PROTOCOL_IPV6)
557 if (inet_pton(AF_INET6, ip, &i) > 0)
559 DNS::MakeIP6Int(query, &i);
562 /* Invalid IP address */
569 if (inet_aton(ip, &i))
571 unsigned char* c = (unsigned char*)&i.s_addr;
572 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
575 /* Invalid IP address */
579 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
582 DNSRequest* req = this->AddQuery(&h, id, ip);
584 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
590 /** Build an ipv6 reverse domain from an in6_addr
592 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
594 #ifdef SUPPORT_IP6LINKS
595 const char* hex = "0123456789abcdef";
596 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
600 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
603 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
604 *query++ = '.'; /* Seperator */
606 strcpy(query,"ip6.arpa"); /* Suffix the string */
612 /** Return the next id which is ready, and the result attached to it */
613 DNSResult DNS::GetResult(int resultnum)
615 /* Fetch dns query response and decide where it belongs */
618 unsigned char buffer[sizeof(DNSHeader)];
619 sockaddr* from = new sockaddr[2];
621 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
623 socklen_t x = sizeof(sockaddr_in);
625 const char* ipaddr_from;
626 unsigned short int port_from = 0;
628 int length = ServerInstance->SE->RecvFrom(this, (char*)buffer, sizeof(DNSHeader), 0, from, &x);
630 /* Did we get the whole header? */
633 /* Nope - something screwed up. */
635 return DNSResult(-1,"",0,"");
638 /* Check wether the reply came from a different DNS
639 * server to the one we sent it to, or the source-port
641 * A user could in theory still spoof dns packets anyway
642 * but this is less trivial than just sending garbage
643 * to the client, which is possible without this check.
645 * -- Thanks jilles for pointing this one out.
649 if (this->socketfamily == AF_INET6)
651 ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
652 port_from = ntohs(((sockaddr_in6*)from)->sin6_port);
657 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
658 port_from = ntohs(((sockaddr_in*)from)->sin_port);
663 /* We cant perform this security check if you're using 4in6.
664 * Tough luck to you, choose one or't other!
668 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
670 return DNSResult(-1,"",0,"");
674 /* Put the read header info into a header class */
675 DNS::FillHeader(&header,buffer,length - 12);
677 /* Get the id of this request.
678 * Its a 16 bit value stored in two char's,
679 * so we use logic shifts to create the value.
681 unsigned long this_id = header.id[1] + (header.id[0] << 8);
683 /* Do we have a pending request matching this id? */
684 if (!requests[this_id])
686 /* Somehow we got a DNS response for a request we never made... */
687 return DNSResult(-1,"",0,"");
691 /* Remove the query from the list of pending queries */
692 req = requests[this_id];
693 requests[this_id] = NULL;
696 /* Inform the DNSRequest class that it has a result to be read.
697 * When its finished it will return a DNSInfo which is a pair of
698 * unsigned char* resource record data, and an error message.
700 DNSInfo data = req->ResultIsReady(header, length, resultnum);
701 std::string resultstr;
703 /* Check if we got a result, if we didnt, its an error */
704 if (data.first == NULL)
707 * Mask the ID with the value of ERROR_MASK, so that
708 * the dns_deal_with_classes() function knows that its
709 * an error response and needs to be treated uniquely.
710 * Put the error message in the second field.
712 std::string ro = req->orig;
714 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
718 unsigned long ttl = req->ttl;
721 /* Forward lookups come back as binary data. We must format them into ascii */
725 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
726 resultstr = formatted;
731 inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted));
732 char* c = strstr(formatted,":0:");
735 memmove(c+1,c+2,strlen(c+2) + 1);
737 while (memcmp(c,"0:",2) == 0)
738 memmove(c,c+2,strlen(c+2) + 1);
739 if (memcmp(c,"0",2) == 0)
741 if (memcmp(formatted,"0::",3) == 0)
742 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
744 resultstr = formatted;
746 /* Special case. Sending ::1 around between servers
747 * and to clients is dangerous, because the : on the
748 * start makes the client or server interpret the IP
749 * as the last parameter on the line with a value ":1".
751 if (*formatted == ':')
752 resultstr.insert(0, "0");
756 case DNS_QUERY_CNAME:
757 /* Identical handling to PTR */
760 /* Reverse lookups just come back as char* */
761 resultstr = std::string((const char*)data.first);
769 /* Build the reply with the id and hostname/ip in it */
770 std::string ro = req->orig;
772 return DNSResult(this_id,resultstr,ttl,ro);
776 /** A result is ready, process it */
777 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length, int result_we_want)
785 /* This is just to keep _FORTIFY_SOURCE happy */
786 rr.type = DNS_QUERY_NONE;
788 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
789 rr.rr_class = 0; /* Same for VC++ */
791 if (!(header.flags1 & FLAGS_MASK_QR))
792 return std::make_pair((unsigned char*)NULL,"Not a query result");
794 if (header.flags1 & FLAGS_MASK_OPCODE)
795 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
797 if (header.flags2 & FLAGS_MASK_RCODE)
798 return std::make_pair((unsigned char*)NULL,"Domain name not found");
800 if (header.ancount < 1)
801 return std::make_pair((unsigned char*)NULL,"No resource records returned");
803 /* Subtract the length of the header from the length of the packet */
806 while ((unsigned int)q < header.qdcount && i < length)
808 if (header.payload[i] > 63)
815 if (header.payload[i] == 0)
820 else i += header.payload[i] + 1;
824 while ((unsigned)curanswer < header.ancount)
827 while (q == 0 && i < length)
829 if (header.payload[i] > 63)
836 if (header.payload[i] == 0)
841 else i += header.payload[i] + 1; /* skip length and label */
845 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
847 /* XXX: We actually initialise 'rr' here including its ttl field */
848 if (curanswer == result_we_want)
849 DNS::FillResourceRecord(&rr,&header.payload[i]);
852 if (rr.type != this->type)
858 if (rr.rr_class != this->rr_class)
866 if ((unsigned int)curanswer == header.ancount)
867 return std::make_pair((unsigned char*)NULL,"No more answers (" + ConvToStr(header.ancount) + " answers, wanted #" + ConvToStr(result_we_want) + ")");
869 if (i + rr.rdlength > (unsigned int)length)
870 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
872 if (rr.rdlength > 1023)
873 return std::make_pair((unsigned char*)NULL,"Resource record too large");
879 case DNS_QUERY_CNAME:
880 /* CNAME and PTR have the same processing code */
884 while (q == 0 && i < length && o + 256 < 1023)
886 if (header.payload[i] > 63)
888 memcpy(&ptr,&header.payload[i],2);
889 i = ntohs(ptr) - 0xC000 - 12;
893 if (header.payload[i] == 0)
902 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
903 o += header.payload[i];
904 i += header.payload[i] + 1;
911 memcpy(res,&header.payload[i],rr.rdlength);
912 res[rr.rdlength] = 0;
915 memcpy(res,&header.payload[i],rr.rdlength);
916 res[rr.rdlength] = 0;
919 memcpy(res,&header.payload[i],rr.rdlength);
920 res[rr.rdlength] = 0;
923 return std::make_pair(res,"No error");
926 /** Close the master socket */
929 ServerInstance->SE->Shutdown(this, 2);
930 ServerInstance->SE->Close(this);
931 ServerInstance->Timers->DelTimer(this->PruneTimer);
934 CachedQuery* DNS::GetCache(const std::string &source)
936 dnscache::iterator x = cache->find(source.c_str());
937 if (x != cache->end())
943 void DNS::DelCache(const std::string &source)
945 cache->erase(source.c_str());
948 void Resolver::TriggerCachedResult()
951 OnLookupComplete(CQ->data, time_left, true, 0);
954 /** High level abstraction of dns used by application at large */
955 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
957 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Resolver::Resolver");
960 CQ = ServerInstance->Res->GetCache(source);
963 time_left = CQ->CalcTTLRemaining();
966 ServerInstance->Res->DelCache(source);
975 irc::sockets::insp_inaddr binip;
980 this->myid = ServerInstance->Res->GetIP(source.c_str());
984 if (irc::sockets::insp_aton(source.c_str(), &binip) > 0)
986 /* Valid ip address */
987 this->myid = ServerInstance->Res->GetName(&binip);
991 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
992 throw ModuleException("Resolver: Bad IP address");
998 querytype = DNS_QUERY_PTR;
999 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
1002 case DNS_QUERY_PTR6:
1003 querytype = DNS_QUERY_PTR;
1004 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
1007 case DNS_QUERY_AAAA:
1008 this->myid = ServerInstance->Res->GetIP6(source.c_str());
1011 case DNS_QUERY_CNAME:
1012 this->myid = ServerInstance->Res->GetCName(source.c_str());
1019 if (this->myid == -1)
1021 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1022 throw ModuleException("Resolver: Couldnt get an id to make a request");
1023 /* We shouldnt get here really */
1028 ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request id %d", this->myid);
1032 /** Called when an error occurs */
1033 void Resolver::OnError(ResolverError, const std::string&)
1035 /* Nothing in here */
1038 /** Destroy a resolver */
1039 Resolver::~Resolver()
1041 /* Nothing here (yet) either */
1044 /** Get the request id associated with this class */
1045 int Resolver::GetId()
1050 Module* Resolver::GetCreator()
1052 return this->Creator;
1055 /** Process a socket read event */
1056 void DNS::HandleEvent(EventType, int)
1058 /* Fetch the id and result of the next available packet */
1060 DNSResult res(0,"",0,"");
1062 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Handle DNS event");
1064 res = this->GetResult(resultnum);
1066 ServerInstance->Logs->Log("RESOLVER",DEBUG,"Result %d id %d", resultnum, res.id);
1068 /* Is there a usable request id? */
1071 /* Its an error reply */
1072 if (res.id & ERROR_MASK)
1074 /* Mask off the error bit */
1075 res.id -= ERROR_MASK;
1076 /* Marshall the error to the correct class */
1077 if (Classes[res.id])
1079 if (ServerInstance && ServerInstance->stats)
1080 ServerInstance->stats->statsDnsBad++;
1081 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1082 delete Classes[res.id];
1083 Classes[res.id] = NULL;
1089 /* It is a non-error result, marshall the result to the correct class */
1090 if (Classes[res.id])
1092 if (ServerInstance && ServerInstance->stats)
1093 ServerInstance->stats->statsDnsGood++;
1095 if (!this->GetCache(res.original.c_str()))
1096 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1098 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false, resultnum);
1099 delete Classes[res.id];
1100 Classes[res.id] = NULL;
1104 if (ServerInstance && ServerInstance->stats)
1105 ServerInstance->stats->statsDns++;
1111 /** Add a derived Resolver to the working set */
1112 bool DNS::AddResolverClass(Resolver* r)
1114 ServerInstance->Logs->Log("RESOLVER",DEBUG,"AddResolverClass 0x%08lx", (unsigned long)r);
1115 /* Check the pointers validity and the id's validity */
1116 if ((r) && (r->GetId() > -1))
1118 /* Check the slot isnt already occupied -
1119 * This should NEVER happen unless we have
1120 * a severely broken DNS server somewhere
1122 if (!Classes[r->GetId()])
1124 /* Set up the pointer to the class */
1125 Classes[r->GetId()] = r;
1134 /* Pointer or id not valid.
1135 * Free the item and return
1144 void DNS::CleanResolvers(Module* module)
1146 for (int i = 0; i < MAX_REQUEST_ID; i++)
1150 if (Classes[i]->GetCreator() == module)
1152 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1160 /** Generate pseudo-random number */
1161 unsigned long DNS::PRNG()
1163 unsigned long val = 0;
1165 serverstats* s = ServerInstance->stats;
1166 gettimeofday(&n,NULL);
1167 val = (n.tv_usec ^ (getpid() ^ geteuid()) ^ ((this->currid++)) ^ s->statsAccept) + n.tv_sec;
1168 val = val + (s->statsCollisions ^ s->statsDnsGood) - s->statsDnsBad;
1169 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size();