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"
32 #include "inspircd_se_config.h"
37 #include "socketengine.h"
38 #include "configreader.h"
41 using irc::sockets::insp_inaddr;
42 using irc::sockets::insp_ntoa;
43 using irc::sockets::insp_aton;
44 using irc::sockets::OpenTCPSocket;
45 using irc::sockets::NonBlocking;
47 /** Masks to mask off the responses we get from the DNSRequest methods
51 ERROR_MASK = 0x10000 /* Result is an error */
54 /** Flags which can be ORed into a request or reply for different meanings
58 FLAGS_MASK_RD = 0x01, /* Recursive */
60 FLAGS_MASK_AA = 0x04, /* Authoritative */
61 FLAGS_MASK_OPCODE = 0x78,
63 FLAGS_MASK_RCODE = 0x0F, /* Request */
69 /** Represents a dns resource record (rr)
73 QueryType type; /* Record type */
74 unsigned int rr_class; /* Record class */
75 unsigned long ttl; /* Time to live */
76 unsigned int rdlength; /* Record length */
79 /** Represents a dns request/reply header, and its payload as opaque data.
84 unsigned char id[2]; /* Request id */
85 unsigned int flags1; /* Flags */
86 unsigned int flags2; /* Flags */
88 unsigned int ancount; /* Answer count */
89 unsigned int nscount; /* Nameserver count */
91 unsigned char payload[512]; /* Packet payload */
97 unsigned char id[2]; /* Request id */
98 unsigned char* res; /* Result processing buffer */
99 unsigned int rr_class; /* Request class */
100 QueryType type; /* Request type */
101 DNS* dnsobj; /* DNS caller (where we get our FD from) */
102 unsigned long ttl; /* Time to live */
103 std::string orig; /* Original requested name/ip */
105 DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
107 DNSInfo ResultIsReady(DNSHeader &h, int length);
108 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
111 class CacheTimer : public InspTimer
114 InspIRCd* ServerInstance;
117 CacheTimer(InspIRCd* Instance, DNS* thisdns)
118 : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
120 virtual void Tick(time_t TIME)
126 class RequestTimeout : public InspTimer
128 InspIRCd* ServerInstance;
132 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
136 void Tick(time_t TIME)
138 if (ServerInstance->Res->requests[watchid] == watch)
140 /* Still exists, whack it */
141 if (ServerInstance->Res->Classes[watchid])
143 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
144 delete ServerInstance->Res->Classes[watchid];
145 ServerInstance->Res->Classes[watchid] = NULL;
147 ServerInstance->Res->requests[watchid] = NULL;
154 /* Allocate the processing buffer */
155 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns)
157 res = new unsigned char[512];
160 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
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 unsigned char payload[sizeof(DNSHeader)];
219 DNS::EmptyHeader(payload,header,length);
222 if (this->dnsobj->socketfamily == AF_INET6)
225 memset(&addr,0,sizeof(addr));
226 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
227 addr.sin6_family = AF_INET6;
228 addr.sin6_port = htons(DNS::QUERY_PORT);
229 if (sendto(dnsobj->GetFd(), 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 (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
244 memset(&addr,0,sizeof(addr));
245 memcpy(&addr.sin_addr.s_addr, &dnsobj->myserver4, sizeof(addr.sin_addr));
246 addr.sin_family = AF_INET;
247 addr.sin_port = htons(DNS::QUERY_PORT);
248 if (sendto(dnsobj->GetFd(), (const char*)payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
255 /** Add a query with a predefined header, and allocate an ID for it. */
256 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
258 /* Is the DNS connection down? */
259 if (this->GetFd() == -1)
263 id = this->PRNG() & DNS::MAX_REQUEST_ID;
265 /* If this id is already 'in flight', pick another. */
267 id = this->PRNG() & DNS::MAX_REQUEST_ID;
269 DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
271 header->id[0] = req->id[0] = id >> 8;
272 header->id[1] = req->id[1] = id & 0xFF;
273 header->flags1 = FLAGS_MASK_RD;
280 /* At this point we already know the id doesnt exist,
281 * so there needs to be no second check for the ::end()
285 /* According to the C++ spec, new never returns NULL. */
289 int DNS::ClearCache()
291 /* This ensures the buckets are reset to sane levels */
292 int rv = this->cache->size();
294 this->cache = new dnscache();
298 int DNS::PruneCache()
301 dnscache* newcache = new dnscache();
302 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
303 /* Dont include expired items (theres no point) */
304 if (i->second.CalcTTLRemaining())
305 newcache->insert(*i);
310 this->cache = newcache;
319 if (this->GetFd() > -1)
321 if (ServerInstance && ServerInstance->SE)
322 ServerInstance->SE->DelFd(this);
323 shutdown(this->GetFd(), 2);
324 close(this->GetFd());
327 /* Rehash the cache */
332 /* Create initial dns cache */
333 this->cache = new dnscache();
336 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
338 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
339 ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
340 ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
341 this->ip6munge = true;
344 this->socketfamily = AF_INET;
346 if (strchr(ServerInstance->Config->DNSServer,':'))
348 this->socketfamily = AF_INET6;
349 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
353 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
357 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
360 /* Initialize mastersocket */
361 int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
365 /* Have we got a socket and is it nonblocking? */
366 if (this->GetFd() != -1)
368 /* Bind the port - port 0 INADDR_ANY */
369 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
372 shutdown(this->GetFd(),2);
373 close(this->GetFd());
377 if (this->GetFd() >= 0)
379 /* Hook the descriptor into the socket engine */
380 if (ServerInstance && ServerInstance->SE)
382 if (!ServerInstance->SE->AddFd(this))
384 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
385 shutdown(this->GetFd(),2);
386 close(this->GetFd());
394 /** Initialise the DNS UDP socket so that we can send requests */
395 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
397 /* Clear the Resolver class table */
398 memset(Classes,0,sizeof(Classes));
400 /* Clear the requests class table */
401 memset(requests,0,sizeof(requests));
403 /* Set the id of the next request to 0
407 /* DNS::Rehash() sets this to a valid ptr
411 /* Again, DNS::Rehash() sets this to a
416 /* Actually read the settings
420 this->PruneTimer = new CacheTimer(ServerInstance, this);
422 ServerInstance->Timers->AddTimer(this->PruneTimer);
425 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
426 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
428 short payloadpos = 0;
429 const char* tempchr, *tempchr2 = name;
430 unsigned short length;
432 /* split name up into labels, create query */
433 while ((tempchr = strchr(tempchr2,'.')) != NULL)
435 length = tempchr - tempchr2;
436 if (payloadpos + length + 1 > 507)
438 payload[payloadpos++] = length;
439 memcpy(&payload[payloadpos],tempchr2,length);
440 payloadpos += length;
441 tempchr2 = &tempchr[1];
443 length = strlen(tempchr2);
446 if (payloadpos + length + 2 > 507)
448 payload[payloadpos++] = length;
449 memcpy(&payload[payloadpos],tempchr2,length);
450 payloadpos += length;
451 payload[payloadpos++] = 0;
453 if (payloadpos > 508)
456 memcpy(&payload[payloadpos],&length,2);
457 length = htons(rr_class);
458 memcpy(&payload[payloadpos + 2],&length,2);
459 return payloadpos + 4;
462 /** Start lookup of an hostname to an IP address */
463 int DNS::GetIP(const char *name)
469 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
472 DNSRequest* req = this->AddQuery(&h, id, name);
474 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
480 /** Start lookup of an hostname to an IPv6 address */
481 int DNS::GetIP6(const char *name)
487 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
490 DNSRequest* req = this->AddQuery(&h, id, name);
492 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
498 /** Start lookup of a cname to another name */
499 int DNS::GetCName(const char *alias)
505 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
508 DNSRequest* req = this->AddQuery(&h, id, alias);
510 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
516 /** Start lookup of an IP address to a hostname */
517 int DNS::GetName(const insp_inaddr *ip)
525 unsigned char* c = (unsigned char*)&ip->s6_addr;
526 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
527 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
528 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
529 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
531 DNS::MakeIP6Int(query, (in6_addr*)ip);
533 unsigned char* c = (unsigned char*)&ip->s_addr;
534 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
537 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
540 DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
542 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
548 /** Start lookup of an IP address to a hostname */
549 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
555 #ifdef SUPPORT_IP6LINKS
556 if (fp == PROTOCOL_IPV6)
559 if (inet_pton(AF_INET6, ip, &i) > 0)
561 DNS::MakeIP6Int(query, &i);
564 /* Invalid IP address */
571 if (inet_aton(ip, &i))
573 unsigned char* c = (unsigned char*)&i.s_addr;
574 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
577 /* Invalid IP address */
581 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
584 DNSRequest* req = this->AddQuery(&h, id, ip);
586 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
592 /** Build an ipv6 reverse domain from an in6_addr
594 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
596 #ifdef SUPPORT_IP6LINKS
597 const char* hex = "0123456789abcdef";
598 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
602 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
605 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
606 *query++ = '.'; /* Seperator */
608 strcpy(query,"ip6.arpa"); /* Suffix the string */
614 /** Return the next id which is ready, and the result attached to it */
615 DNSResult DNS::GetResult()
617 /* Fetch dns query response and decide where it belongs */
620 unsigned char buffer[sizeof(DNSHeader)];
621 sockaddr* from = new sockaddr[2];
623 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
625 socklen_t x = sizeof(sockaddr_in);
627 const char* ipaddr_from;
628 unsigned short int port_from = 0;
630 void* m_readEvent = NULL;
631 GetExt("windows_readevent", m_readEvent);
633 int length = _recvfrom(this->GetFd(),(char*)buffer,sizeof(DNSHeader),0,from,&x);
635 /* Did we get the whole header? */
638 /* Nope - something screwed up. */
640 return DNSResult(-1,"",0,"");
643 /* Check wether the reply came from a different DNS
644 * server to the one we sent it to, or the source-port
646 * A user could in theory still spoof dns packets anyway
647 * but this is less trivial than just sending garbage
648 * to the client, which is possible without this check.
650 * -- Thanks jilles for pointing this one out.
654 if (this->socketfamily == AF_INET6)
656 ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
657 port_from = ntohs(((sockaddr_in6*)from)->sin6_port);
662 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
663 port_from = ntohs(((sockaddr_in*)from)->sin_port);
668 /* We cant perform this security check if you're using 4in6.
669 * Tough luck to you, choose one or't other!
673 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
675 return DNSResult(-1,"",0,"");
679 /* Put the read header info into a header class */
680 DNS::FillHeader(&header,buffer,length - 12);
682 /* Get the id of this request.
683 * Its a 16 bit value stored in two char's,
684 * so we use logic shifts to create the value.
686 unsigned long this_id = header.id[1] + (header.id[0] << 8);
688 /* Do we have a pending request matching this id? */
689 if (!requests[this_id])
691 /* Somehow we got a DNS response for a request we never made... */
692 return DNSResult(-1,"",0,"");
696 /* Remove the query from the list of pending queries */
697 req = requests[this_id];
698 requests[this_id] = NULL;
701 /* Inform the DNSRequest class that it has a result to be read.
702 * When its finished it will return a DNSInfo which is a pair of
703 * unsigned char* resource record data, and an error message.
705 DNSInfo data = req->ResultIsReady(header, length);
706 std::string resultstr;
708 /* Check if we got a result, if we didnt, its an error */
709 if (data.first == NULL)
712 * Mask the ID with the value of ERROR_MASK, so that
713 * the dns_deal_with_classes() function knows that its
714 * an error response and needs to be treated uniquely.
715 * Put the error message in the second field.
717 std::string ro = req->orig;
719 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
723 unsigned long ttl = req->ttl;
726 /* Forward lookups come back as binary data. We must format them into ascii */
730 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
731 resultstr = formatted;
736 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
737 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
738 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
739 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
740 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
741 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
742 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
743 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
744 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
745 char* c = strstr(formatted,":0:");
748 memmove(c+1,c+2,strlen(c+2) + 1);
750 while (memcmp(c,"0:",2) == 0)
751 memmove(c,c+2,strlen(c+2) + 1);
752 if (memcmp(c,"0",2) == 0)
754 if (memcmp(formatted,"0::",3) == 0)
755 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
757 resultstr = formatted;
759 /* Special case. Sending ::1 around between servers
760 * and to clients is dangerous, because the : on the
761 * start makes the client or server interpret the IP
762 * as the last parameter on the line with a value ":1".
764 if (*formatted == ':')
765 resultstr.insert(0, "0");
769 case DNS_QUERY_CNAME:
770 /* Identical handling to PTR */
773 /* Reverse lookups just come back as char* */
774 resultstr = std::string((const char*)data.first);
782 /* Build the reply with the id and hostname/ip in it */
783 std::string ro = req->orig;
785 return DNSResult(this_id,resultstr,ttl,ro);
789 /** A result is ready, process it */
790 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
798 /* This is just to keep _FORTIFY_SOURCE happy */
799 rr.type = DNS_QUERY_NONE;
801 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
803 if (!(header.flags1 & FLAGS_MASK_QR))
804 return std::make_pair((unsigned char*)NULL,"Not a query result");
806 if (header.flags1 & FLAGS_MASK_OPCODE)
807 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
809 if (header.flags2 & FLAGS_MASK_RCODE)
810 return std::make_pair((unsigned char*)NULL,"Domain name not found");
812 if (header.ancount < 1)
813 return std::make_pair((unsigned char*)NULL,"No resource records returned");
815 /* Subtract the length of the header from the length of the packet */
818 while ((unsigned int)q < header.qdcount && i < length)
820 if (header.payload[i] > 63)
827 if (header.payload[i] == 0)
832 else i += header.payload[i] + 1;
836 while ((unsigned)curanswer < header.ancount)
839 while (q == 0 && i < length)
841 if (header.payload[i] > 63)
848 if (header.payload[i] == 0)
853 else i += header.payload[i] + 1; /* skip length and label */
857 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
859 /* XXX: We actually initialise 'rr' here including its ttl field */
860 DNS::FillResourceRecord(&rr,&header.payload[i]);
862 if (rr.type != this->type)
868 if (rr.rr_class != this->rr_class)
876 if ((unsigned int)curanswer == header.ancount)
877 return std::make_pair((unsigned char*)NULL,"No valid answers");
879 if (i + rr.rdlength > (unsigned int)length)
880 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
882 if (rr.rdlength > 1023)
883 return std::make_pair((unsigned char*)NULL,"Resource record too large");
889 case DNS_QUERY_CNAME:
890 /* CNAME and PTR have the same processing code */
894 while (q == 0 && i < length && o + 256 < 1023)
896 if (header.payload[i] > 63)
898 memcpy(&ptr,&header.payload[i],2);
899 i = ntohs(ptr) - 0xC000 - 12;
903 if (header.payload[i] == 0)
912 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
913 o += header.payload[i];
914 i += header.payload[i] + 1;
921 memcpy(res,&header.payload[i],rr.rdlength);
922 res[rr.rdlength] = 0;
925 memcpy(res,&header.payload[i],rr.rdlength);
926 res[rr.rdlength] = 0;
929 memcpy(res,&header.payload[i],rr.rdlength);
930 res[rr.rdlength] = 0;
933 return std::make_pair(res,"No error");;
936 /** Close the master socket */
939 shutdown(this->GetFd(), 2);
940 close(this->GetFd());
941 ServerInstance->Timers->DelTimer(this->PruneTimer);
942 delete this->PruneTimer;
945 CachedQuery* DNS::GetCache(const std::string &source)
947 dnscache::iterator x = cache->find(source.c_str());
948 if (x != cache->end())
954 void DNS::DelCache(const std::string &source)
956 cache->erase(source.c_str());
959 void Resolver::TriggerCachedResult()
962 OnLookupComplete(CQ->data, time_left, true);
965 /** High level abstraction of dns used by application at large */
966 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
970 CQ = ServerInstance->Res->GetCache(source);
973 time_left = CQ->CalcTTLRemaining();
976 ServerInstance->Res->DelCache(source);
990 this->myid = ServerInstance->Res->GetIP(source.c_str());
994 if (insp_aton(source.c_str(), &binip) > 0)
996 /* Valid ip address */
997 this->myid = ServerInstance->Res->GetName(&binip);
1001 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
1002 throw ModuleException("Resolver: Bad IP address");
1007 case DNS_QUERY_PTR4:
1008 querytype = DNS_QUERY_PTR;
1009 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
1012 case DNS_QUERY_PTR6:
1013 querytype = DNS_QUERY_PTR;
1014 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
1017 case DNS_QUERY_AAAA:
1018 this->myid = ServerInstance->Res->GetIP6(source.c_str());
1021 case DNS_QUERY_CNAME:
1022 this->myid = ServerInstance->Res->GetCName(source.c_str());
1029 if (this->myid == -1)
1031 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1032 throw ModuleException("Resolver: Couldnt get an id to make a request");
1033 /* We shouldnt get here really */
1038 /** Called when an error occurs */
1039 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1041 /* Nothing in here */
1044 /** Destroy a resolver */
1045 Resolver::~Resolver()
1047 /* Nothing here (yet) either */
1050 /** Get the request id associated with this class */
1051 int Resolver::GetId()
1056 Module* Resolver::GetCreator()
1058 return this->Creator;
1061 /** Process a socket read event */
1062 void DNS::HandleEvent(EventType et, int errornum)
1064 /* Fetch the id and result of the next available packet */
1065 DNSResult res = this->GetResult();
1066 /* Is there a usable request id? */
1069 /* Its an error reply */
1070 if (res.id & ERROR_MASK)
1072 /* Mask off the error bit */
1073 res.id -= ERROR_MASK;
1074 /* Marshall the error to the correct class */
1075 if (Classes[res.id])
1077 if (ServerInstance && ServerInstance->stats)
1078 ServerInstance->stats->statsDnsBad++;
1079 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1080 delete Classes[res.id];
1081 Classes[res.id] = NULL;
1086 /* It is a non-error result, marshall the result to the correct class */
1087 if (Classes[res.id])
1089 if (ServerInstance && ServerInstance->stats)
1090 ServerInstance->stats->statsDnsGood++;
1092 if (!this->GetCache(res.original.c_str()))
1093 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1095 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1096 delete Classes[res.id];
1097 Classes[res.id] = NULL;
1101 if (ServerInstance && ServerInstance->stats)
1102 ServerInstance->stats->statsDns++;
1106 /** Add a derived Resolver to the working set */
1107 bool DNS::AddResolverClass(Resolver* r)
1109 /* Check the pointers validity and the id's validity */
1110 if ((r) && (r->GetId() > -1))
1112 /* Check the slot isnt already occupied -
1113 * This should NEVER happen unless we have
1114 * a severely broken DNS server somewhere
1116 if (!Classes[r->GetId()])
1118 /* Set up the pointer to the class */
1119 Classes[r->GetId()] = r;
1128 /* Pointer or id not valid.
1129 * Free the item and return
1138 void DNS::CleanResolvers(Module* module)
1140 for (int i = 0; i < MAX_REQUEST_ID; i++)
1144 if (Classes[i]->GetCreator() == module)
1146 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1154 /** Generate pseudo-random number */
1155 unsigned long DNS::PRNG()
1157 unsigned long val = 0;
1159 serverstats* s = ServerInstance->stats;
1160 gettimeofday(&n,NULL);
1161 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1162 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1163 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - ServerInstance->Config->ports.size();