1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2007 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 * ---------------------------------------------------
15 dns.cpp - Nonblocking DNS functions.
16 Very very loosely based on the firedns library,
17 Copyright (C) 2002 Ian Gulliver. This file is no
18 longer anything like firedns, there are many major
19 differences between this code and the original.
20 Please do not assume that firedns works like this,
21 looks like this, walks like this or tastes like this.
25 #include <sys/types.h>
26 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
31 #include "inspircd_win32wrapper.h"
36 #include "socketengine.h"
37 #include "configreader.h"
40 using irc::sockets::insp_inaddr;
41 using irc::sockets::insp_ntoa;
42 using irc::sockets::insp_aton;
43 using irc::sockets::OpenTCPSocket;
45 /** Masks to mask off the responses we get from the DNSRequest methods
49 ERROR_MASK = 0x10000 /* Result is an error */
52 /** Flags which can be ORed into a request or reply for different meanings
56 FLAGS_MASK_RD = 0x01, /* Recursive */
58 FLAGS_MASK_AA = 0x04, /* Authoritative */
59 FLAGS_MASK_OPCODE = 0x78,
61 FLAGS_MASK_RCODE = 0x0F, /* Request */
67 /** Represents a dns resource record (rr)
71 QueryType type; /* Record type */
72 unsigned int rr_class; /* Record class */
73 unsigned long ttl; /* Time to live */
74 unsigned int rdlength; /* Record length */
77 /** Represents a dns request/reply header, and its payload as opaque data.
82 unsigned char id[2]; /* Request id */
83 unsigned int flags1; /* Flags */
84 unsigned int flags2; /* Flags */
86 unsigned int ancount; /* Answer count */
87 unsigned int nscount; /* Nameserver count */
89 unsigned char payload[512]; /* Packet payload */
95 unsigned char id[2]; /* Request id */
96 unsigned char* res; /* Result processing buffer */
97 unsigned int rr_class; /* Request class */
98 QueryType type; /* Request type */
99 DNS* dnsobj; /* DNS caller (where we get our FD from) */
100 unsigned long ttl; /* Time to live */
101 std::string orig; /* Original requested name/ip */
103 DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
105 DNSInfo ResultIsReady(DNSHeader &h, int length);
106 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
109 class CacheTimer : public InspTimer
112 InspIRCd* ServerInstance;
115 CacheTimer(InspIRCd* Instance, DNS* thisdns)
116 : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
118 virtual void Tick(time_t TIME)
124 class RequestTimeout : public InspTimer
126 InspIRCd* ServerInstance;
130 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
134 void Tick(time_t TIME)
136 if (ServerInstance->Res->requests[watchid] == watch)
138 /* Still exists, whack it */
139 if (ServerInstance->Res->Classes[watchid])
141 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
142 delete ServerInstance->Res->Classes[watchid];
143 ServerInstance->Res->Classes[watchid] = NULL;
145 ServerInstance->Res->requests[watchid] = NULL;
152 /* Allocate the processing buffer */
153 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns)
155 res = new unsigned char[512];
158 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
159 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
162 /* Deallocate the processing buffer */
163 DNSRequest::~DNSRequest()
168 /** Fill a ResourceRecord class based on raw data input */
169 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
171 rr->type = (QueryType)((input[0] << 8) + input[1]);
172 rr->rr_class = (input[2] << 8) + input[3];
173 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
174 rr->rdlength = (input[8] << 8) + input[9];
177 /** Fill a DNSHeader class based on raw data input of a given length */
178 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
180 header->id[0] = input[0];
181 header->id[1] = input[1];
182 header->flags1 = input[2];
183 header->flags2 = input[3];
184 header->qdcount = (input[4] << 8) + input[5];
185 header->ancount = (input[6] << 8) + input[7];
186 header->nscount = (input[8] << 8) + input[9];
187 header->arcount = (input[10] << 8) + input[11];
188 memcpy(header->payload,&input[12],length);
191 /** Empty a DNSHeader class out into raw data, ready for transmission */
192 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
194 output[0] = header->id[0];
195 output[1] = header->id[1];
196 output[2] = header->flags1;
197 output[3] = header->flags2;
198 output[4] = header->qdcount >> 8;
199 output[5] = header->qdcount & 0xFF;
200 output[6] = header->ancount >> 8;
201 output[7] = header->ancount & 0xFF;
202 output[8] = header->nscount >> 8;
203 output[9] = header->nscount & 0xFF;
204 output[10] = header->arcount >> 8;
205 output[11] = header->arcount & 0xFF;
206 memcpy(&output[12],header->payload,length);
209 /** Send requests we have previously built down the UDP socket */
210 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
212 unsigned char payload[sizeof(DNSHeader)];
217 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 (sendto(dnsobj->GetFd(), 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 (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
242 memset(&addr,0,sizeof(addr));
243 memcpy(&addr.sin_addr.s_addr, &dnsobj->myserver4, sizeof(addr.sin_addr));
244 addr.sin_family = AF_INET;
245 addr.sin_port = htons(DNS::QUERY_PORT);
246 if (sendto(dnsobj->GetFd(), (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
253 /** Add a query with a predefined header, and allocate an ID for it. */
254 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
256 /* Is the DNS connection down? */
257 if (this->GetFd() == -1)
261 id = this->PRNG() & DNS::MAX_REQUEST_ID;
263 /* If this id is already 'in flight', pick another. */
265 id = this->PRNG() & DNS::MAX_REQUEST_ID;
267 DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
269 header->id[0] = req->id[0] = id >> 8;
270 header->id[1] = req->id[1] = id & 0xFF;
271 header->flags1 = FLAGS_MASK_RD;
278 /* At this point we already know the id doesnt exist,
279 * so there needs to be no second check for the ::end()
283 /* According to the C++ spec, new never returns NULL. */
287 int DNS::ClearCache()
289 /* This ensures the buckets are reset to sane levels */
290 int rv = this->cache->size();
292 this->cache = new dnscache();
296 int DNS::PruneCache()
299 dnscache* newcache = new dnscache();
300 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
301 /* Dont include expired items (theres no point) */
302 if (i->second.CalcTTLRemaining())
303 newcache->insert(*i);
308 this->cache = newcache;
317 if (this->GetFd() > -1)
319 if (ServerInstance && ServerInstance->SE)
320 ServerInstance->SE->DelFd(this);
321 shutdown(this->GetFd(), 2);
322 close(this->GetFd());
325 /* Rehash the cache */
330 /* Create initial dns cache */
331 this->cache = new dnscache();
334 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
336 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
337 ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
338 ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
339 this->ip6munge = true;
342 this->socketfamily = AF_INET;
344 if (strchr(ServerInstance->Config->DNSServer,':'))
346 this->socketfamily = AF_INET6;
347 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
351 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
355 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
358 /* Initialize mastersocket */
359 int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
362 /* Have we got a socket and is it nonblocking? */
363 if (this->GetFd() != -1)
365 /* Bind the port - port 0 INADDR_ANY */
366 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
369 shutdown(this->GetFd(),2);
370 close(this->GetFd());
374 if (this->GetFd() >= 0)
376 /* Hook the descriptor into the socket engine */
377 if (ServerInstance && ServerInstance->SE)
379 if (!ServerInstance->SE->AddFd(this))
381 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
382 shutdown(this->GetFd(),2);
383 close(this->GetFd());
391 /** Initialise the DNS UDP socket so that we can send requests */
392 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
394 /* Clear the Resolver class table */
395 memset(Classes,0,sizeof(Classes));
397 /* Clear the requests class table */
398 memset(requests,0,sizeof(requests));
400 /* Set the id of the next request to 0
404 /* DNS::Rehash() sets this to a valid ptr
408 /* Again, DNS::Rehash() sets this to a
413 /* Actually read the settings
417 this->PruneTimer = new CacheTimer(ServerInstance, this);
419 ServerInstance->Timers->AddTimer(this->PruneTimer);
422 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
423 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
425 short payloadpos = 0;
426 const char* tempchr, *tempchr2 = name;
427 unsigned short length;
429 /* split name up into labels, create query */
430 while ((tempchr = strchr(tempchr2,'.')) != NULL)
432 length = tempchr - tempchr2;
433 if (payloadpos + length + 1 > 507)
435 payload[payloadpos++] = length;
436 memcpy(&payload[payloadpos],tempchr2,length);
437 payloadpos += length;
438 tempchr2 = &tempchr[1];
440 length = strlen(tempchr2);
443 if (payloadpos + length + 2 > 507)
445 payload[payloadpos++] = length;
446 memcpy(&payload[payloadpos],tempchr2,length);
447 payloadpos += length;
448 payload[payloadpos++] = 0;
450 if (payloadpos > 508)
453 memcpy(&payload[payloadpos],&length,2);
454 length = htons(rr_class);
455 memcpy(&payload[payloadpos + 2],&length,2);
456 return payloadpos + 4;
459 /** Start lookup of an hostname to an IP address */
460 int DNS::GetIP(const char *name)
466 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
469 DNSRequest* req = this->AddQuery(&h, id, name);
471 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
477 /** Start lookup of an hostname to an IPv6 address */
478 int DNS::GetIP6(const char *name)
484 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
487 DNSRequest* req = this->AddQuery(&h, id, name);
489 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
495 /** Start lookup of a cname to another name */
496 int DNS::GetCName(const char *alias)
502 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
505 DNSRequest* req = this->AddQuery(&h, id, alias);
507 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
513 /** Start lookup of an IP address to a hostname */
514 int DNS::GetName(const insp_inaddr *ip)
522 unsigned char* c = (unsigned char*)&ip->s6_addr;
523 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
524 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
525 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
526 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
528 DNS::MakeIP6Int(query, (in6_addr*)ip);
530 unsigned char* c = (unsigned char*)&ip->s_addr;
531 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
534 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
537 DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
539 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
545 /** Start lookup of an IP address to a hostname */
546 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
552 #ifdef SUPPORT_IP6LINKS
553 if (fp == PROTOCOL_IPV6)
556 if (inet_pton(AF_INET6, ip, &i) > 0)
558 DNS::MakeIP6Int(query, &i);
561 /* Invalid IP address */
568 if (inet_aton(ip, &i))
570 unsigned char* c = (unsigned char*)&i.s_addr;
571 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
574 /* Invalid IP address */
578 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
581 DNSRequest* req = this->AddQuery(&h, id, ip);
583 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
589 /** Build an ipv6 reverse domain from an in6_addr
591 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
593 #ifdef SUPPORT_IP6LINKS
594 const char* hex = "0123456789abcdef";
595 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
599 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
602 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
603 *query++ = '.'; /* Seperator */
605 strcpy(query,"ip6.arpa"); /* Suffix the string */
611 /** Return the next id which is ready, and the result attached to it */
612 DNSResult DNS::GetResult()
614 /* Fetch dns query response and decide where it belongs */
617 unsigned char buffer[sizeof(DNSHeader)];
618 sockaddr* from = new sockaddr[2];
620 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
622 socklen_t x = sizeof(sockaddr_in);
624 const char* ipaddr_from;
625 unsigned short int port_from = 0;
627 int length = recvfrom(this->GetFd(),(char*)buffer,sizeof(DNSHeader),0,from,&x);
629 /* Did we get the whole header? */
632 /* Nope - something screwed up. */
634 return DNSResult(-1,"",0,"");
637 /* Check wether the reply came from a different DNS
638 * server to the one we sent it to, or the source-port
640 * A user could in theory still spoof dns packets anyway
641 * but this is less trivial than just sending garbage
642 * to the client, which is possible without this check.
644 * -- Thanks jilles for pointing this one out.
648 if (this->socketfamily == AF_INET6)
650 ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
651 port_from = ntohs(((sockaddr_in6*)from)->sin6_port);
655 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
656 port_from = ntohs(((sockaddr_in*)from)->sin_port);
659 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
660 port_from = ntohs(((sockaddr_in*)from)->sin_port);
665 /* We cant perform this security check if you're using 4in6.
666 * Tough luck to you, choose one or't other!
670 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
672 return DNSResult(-1,"",0,"");
676 /* Put the read header info into a header class */
677 DNS::FillHeader(&header,buffer,length - 12);
679 /* Get the id of this request.
680 * Its a 16 bit value stored in two char's,
681 * so we use logic shifts to create the value.
683 unsigned long this_id = header.id[1] + (header.id[0] << 8);
685 /* Do we have a pending request matching this id? */
686 if (!requests[this_id])
688 /* Somehow we got a DNS response for a request we never made... */
689 return DNSResult(-1,"",0,"");
693 /* Remove the query from the list of pending queries */
694 req = requests[this_id];
695 requests[this_id] = NULL;
698 /* Inform the DNSRequest class that it has a result to be read.
699 * When its finished it will return a DNSInfo which is a pair of
700 * unsigned char* resource record data, and an error message.
702 DNSInfo data = req->ResultIsReady(header, length);
703 std::string resultstr;
705 /* Check if we got a result, if we didnt, its an error */
706 if (data.first == NULL)
709 * Mask the ID with the value of ERROR_MASK, so that
710 * the dns_deal_with_classes() function knows that its
711 * an error response and needs to be treated uniquely.
712 * Put the error message in the second field.
714 std::string ro = req->orig;
716 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
720 unsigned long ttl = req->ttl;
723 /* Forward lookups come back as binary data. We must format them into ascii */
727 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
728 resultstr = formatted;
733 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
734 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
735 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
736 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
737 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
738 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
739 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
740 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
741 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
742 char* c = strstr(formatted,":0:");
745 memmove(c+1,c+2,strlen(c+2) + 1);
747 while (memcmp(c,"0:",2) == 0)
748 memmove(c,c+2,strlen(c+2) + 1);
749 if (memcmp(c,"0",2) == 0)
751 if (memcmp(formatted,"0::",3) == 0)
752 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
754 resultstr = formatted;
756 /* Special case. Sending ::1 around between servers
757 * and to clients is dangerous, because the : on the
758 * start makes the client or server interpret the IP
759 * as the last parameter on the line with a value ":1".
761 if (*formatted == ':')
762 resultstr = "0" + resultstr;
766 case DNS_QUERY_CNAME:
767 /* Identical handling to PTR */
770 /* Reverse lookups just come back as char* */
771 resultstr = std::string((const char*)data.first);
779 /* Build the reply with the id and hostname/ip in it */
780 std::string ro = req->orig;
782 return DNSResult(this_id,resultstr,ttl,ro);
786 /** A result is ready, process it */
787 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
795 /* This is just to keep _FORTIFY_SOURCE happy */
796 rr.type = DNS_QUERY_NONE;
798 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
800 if (!(header.flags1 & FLAGS_MASK_QR))
801 return std::make_pair((unsigned char*)NULL,"Not a query result");
803 if (header.flags1 & FLAGS_MASK_OPCODE)
804 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
806 if (header.flags2 & FLAGS_MASK_RCODE)
807 return std::make_pair((unsigned char*)NULL,"Domain name not found");
809 if (header.ancount < 1)
810 return std::make_pair((unsigned char*)NULL,"No resource records returned");
812 /* Subtract the length of the header from the length of the packet */
815 while ((unsigned int)q < header.qdcount && i < length)
817 if (header.payload[i] > 63)
824 if (header.payload[i] == 0)
829 else i += header.payload[i] + 1;
833 while ((unsigned)curanswer < header.ancount)
836 while (q == 0 && i < length)
838 if (header.payload[i] > 63)
845 if (header.payload[i] == 0)
850 else i += header.payload[i] + 1; /* skip length and label */
854 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
856 /* XXX: We actually initialise 'rr' here including its ttl field */
857 DNS::FillResourceRecord(&rr,&header.payload[i]);
859 if (rr.type != this->type)
865 if (rr.rr_class != this->rr_class)
873 if ((unsigned int)curanswer == header.ancount)
874 return std::make_pair((unsigned char*)NULL,"No valid answers");
876 if (i + rr.rdlength > (unsigned int)length)
877 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
879 if (rr.rdlength > 1023)
880 return std::make_pair((unsigned char*)NULL,"Resource record too large");
886 case DNS_QUERY_CNAME:
887 /* CNAME and PTR have the same processing code */
891 while (q == 0 && i < length && o + 256 < 1023)
893 if (header.payload[i] > 63)
895 memcpy(&ptr,&header.payload[i],2);
896 i = ntohs(ptr) - 0xC000 - 12;
900 if (header.payload[i] == 0)
909 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
910 o += header.payload[i];
911 i += header.payload[i] + 1;
918 memcpy(res,&header.payload[i],rr.rdlength);
919 res[rr.rdlength] = 0;
922 memcpy(res,&header.payload[i],rr.rdlength);
923 res[rr.rdlength] = 0;
926 memcpy(res,&header.payload[i],rr.rdlength);
927 res[rr.rdlength] = 0;
930 return std::make_pair(res,"No error");;
933 /** Close the master socket */
936 shutdown(this->GetFd(), 2);
937 close(this->GetFd());
938 ServerInstance->Timers->DelTimer(this->PruneTimer);
939 delete this->PruneTimer;
942 CachedQuery* DNS::GetCache(const std::string &source)
944 dnscache::iterator x = cache->find(source.c_str());
945 if (x != cache->end())
951 void DNS::DelCache(const std::string &source)
953 cache->erase(source.c_str());
956 void Resolver::TriggerCachedResult()
959 OnLookupComplete(CQ->data, time_left, true);
962 /** High level abstraction of dns used by application at large */
963 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
967 CQ = ServerInstance->Res->GetCache(source);
970 time_left = CQ->CalcTTLRemaining();
973 ServerInstance->Res->DelCache(source);
987 this->myid = ServerInstance->Res->GetIP(source.c_str());
991 if (insp_aton(source.c_str(), &binip) > 0)
993 /* Valid ip address */
994 this->myid = ServerInstance->Res->GetName(&binip);
998 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
999 throw ModuleException("Resolver: Bad IP address");
1004 case DNS_QUERY_PTR4:
1005 querytype = DNS_QUERY_PTR;
1006 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
1009 case DNS_QUERY_PTR6:
1010 querytype = DNS_QUERY_PTR;
1011 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
1014 case DNS_QUERY_AAAA:
1015 this->myid = ServerInstance->Res->GetIP6(source.c_str());
1018 case DNS_QUERY_CNAME:
1019 this->myid = ServerInstance->Res->GetCName(source.c_str());
1026 if (this->myid == -1)
1028 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1029 throw ModuleException("Resolver: Couldnt get an id to make a request");
1030 /* We shouldnt get here really */
1035 /** Called when an error occurs */
1036 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1038 /* Nothing in here */
1041 /** Destroy a resolver */
1042 Resolver::~Resolver()
1044 /* Nothing here (yet) either */
1047 /** Get the request id associated with this class */
1048 int Resolver::GetId()
1053 Module* Resolver::GetCreator()
1055 return this->Creator;
1058 /** Process a socket read event */
1059 void DNS::HandleEvent(EventType et, int errornum)
1061 /* Fetch the id and result of the next available packet */
1062 DNSResult res = this->GetResult();
1063 /* Is there a usable request id? */
1066 /* Its an error reply */
1067 if (res.id & ERROR_MASK)
1069 /* Mask off the error bit */
1070 res.id -= ERROR_MASK;
1071 /* Marshall the error to the correct class */
1072 if (Classes[res.id])
1074 if (ServerInstance && ServerInstance->stats)
1075 ServerInstance->stats->statsDnsBad++;
1076 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1077 delete Classes[res.id];
1078 Classes[res.id] = NULL;
1083 /* It is a non-error result, marshall the result to the correct class */
1084 if (Classes[res.id])
1086 if (ServerInstance && ServerInstance->stats)
1087 ServerInstance->stats->statsDnsGood++;
1089 if (!this->GetCache(res.original.c_str()))
1090 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1092 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1093 delete Classes[res.id];
1094 Classes[res.id] = NULL;
1098 if (ServerInstance && ServerInstance->stats)
1099 ServerInstance->stats->statsDns++;
1103 /** Add a derived Resolver to the working set */
1104 bool DNS::AddResolverClass(Resolver* r)
1106 /* Check the pointers validity and the id's validity */
1107 if ((r) && (r->GetId() > -1))
1109 /* Check the slot isnt already occupied -
1110 * This should NEVER happen unless we have
1111 * a severely broken DNS server somewhere
1113 if (!Classes[r->GetId()])
1115 /* Set up the pointer to the class */
1116 Classes[r->GetId()] = r;
1125 /* Pointer or id not valid.
1126 * Free the item and return
1135 void DNS::CleanResolvers(Module* module)
1137 for (int i = 0; i < MAX_REQUEST_ID; i++)
1141 if (Classes[i]->GetCreator() == module)
1143 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1151 /** Generate pseudo-random number */
1152 unsigned long DNS::PRNG()
1155 unsigned long val = 0;
1157 serverstats* s = ServerInstance->stats;
1158 gettimeofday(&n,NULL);
1159 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1160 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1161 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size();
1164 unsigned long val = 0;
1165 serverstats* s = ServerInstance->stats;
1166 val = (time(NULL) ^ GetCurrentProcessId() ^ GetCurrentThreadId() ^ (this->currid++)) ^ s->statsAccept + time(NULL);
1167 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1168 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv);