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.
24 #include <sys/types.h>
25 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
31 #include "socketengine.h"
32 #include "configreader.h"
35 using irc::sockets::insp_sockaddr;
36 using irc::sockets::insp_inaddr;
37 using irc::sockets::insp_ntoa;
38 using irc::sockets::insp_aton;
40 /** Masks to mask off the responses we get from the DNSRequest methods
44 ERROR_MASK = 0x10000 /* Result is an error */
47 /** Flags which can be ORed into a request or reply for different meanings
51 FLAGS_MASK_RD = 0x01, /* Recursive */
53 FLAGS_MASK_AA = 0x04, /* Authoritative */
54 FLAGS_MASK_OPCODE = 0x78,
56 FLAGS_MASK_RCODE = 0x0F, /* Request */
62 /** Represents a dns resource record (rr)
67 QueryType type; /* Record type */
68 unsigned int rr_class; /* Record class */
69 unsigned long ttl; /* Time to live */
70 unsigned int rdlength; /* Record length */
73 /** Represents a dns request/reply header, and its payload as opaque data.
78 unsigned char id[2]; /* Request id */
79 unsigned int flags1; /* Flags */
80 unsigned int flags2; /* Flags */
82 unsigned int ancount; /* Answer count */
83 unsigned int nscount; /* Nameserver count */
85 unsigned char payload[512]; /* Packet payload */
91 unsigned char id[2]; /* Request id */
92 unsigned char* res; /* Result processing buffer */
93 unsigned int rr_class; /* Request class */
94 QueryType type; /* Request type */
95 insp_inaddr myserver; /* DNS server address*/
96 DNS* dnsobj; /* DNS caller (where we get our FD from) */
97 unsigned long ttl; /* Time to live */
98 std::string orig; /* Original requested name/ip */
100 DNSRequest(InspIRCd* Instance, DNS* dns, insp_inaddr server, int id, const std::string &original);
102 DNSInfo ResultIsReady(DNSHeader &h, int length);
103 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
106 class RequestTimeout : public InspTimer
108 InspIRCd* ServerInstance;
112 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
114 ServerInstance->Log(DEBUG, "New DNS timeout set on %08x", watching);
117 void Tick(time_t TIME)
119 if (ServerInstance->Res->requests[watchid] == watch)
121 /* Still exists, whack it */
122 if (ServerInstance->Res->Classes[watchid])
124 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
125 delete ServerInstance->Res->Classes[watchid];
126 ServerInstance->Res->Classes[watchid] = NULL;
128 ServerInstance->Res->requests[watchid] = NULL;
130 ServerInstance->Log(DEBUG, "DNS timeout on %08x squished pointer", watch);
133 ServerInstance->Log(DEBUG, "DNS timeout on %08x: result already received!", watch);
137 /* Allocate the processing buffer */
138 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, insp_inaddr server, int id, const std::string &original) : dnsobj(dns)
140 res = new unsigned char[512];
142 memcpy(&myserver, &server, sizeof(insp_inaddr));
144 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
145 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
148 /* Deallocate the processing buffer */
149 DNSRequest::~DNSRequest()
154 /** Fill a ResourceRecord class based on raw data input */
155 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
157 rr->type = (QueryType)((input[0] << 8) + input[1]);
158 rr->rr_class = (input[2] << 8) + input[3];
159 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
160 rr->rdlength = (input[8] << 8) + input[9];
163 /** Fill a DNSHeader class based on raw data input of a given length */
164 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
166 header->id[0] = input[0];
167 header->id[1] = input[1];
168 header->flags1 = input[2];
169 header->flags2 = input[3];
170 header->qdcount = (input[4] << 8) + input[5];
171 header->ancount = (input[6] << 8) + input[7];
172 header->nscount = (input[8] << 8) + input[9];
173 header->arcount = (input[10] << 8) + input[11];
174 memcpy(header->payload,&input[12],length);
177 /** Empty a DNSHeader class out into raw data, ready for transmission */
178 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
180 output[0] = header->id[0];
181 output[1] = header->id[1];
182 output[2] = header->flags1;
183 output[3] = header->flags2;
184 output[4] = header->qdcount >> 8;
185 output[5] = header->qdcount & 0xFF;
186 output[6] = header->ancount >> 8;
187 output[7] = header->ancount & 0xFF;
188 output[8] = header->nscount >> 8;
189 output[9] = header->nscount & 0xFF;
190 output[10] = header->arcount >> 8;
191 output[11] = header->arcount & 0xFF;
192 memcpy(&output[12],header->payload,length);
195 /** Send requests we have previously built down the UDP socket */
196 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
199 unsigned char payload[sizeof(DNSHeader)];
204 DNS::EmptyHeader(payload,header,length);
206 memset(&addr,0,sizeof(addr));
208 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
209 addr.sin6_family = AF_FAMILY;
210 addr.sin6_port = htons(DNS::QUERY_PORT);
212 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
213 addr.sin_family = AF_FAMILY;
214 addr.sin_port = htons(DNS::QUERY_PORT);
216 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
222 /** Add a query with a predefined header, and allocate an ID for it. */
223 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
225 /* Is the DNS connection down? */
226 if (this->GetFd() == -1)
230 id = this->PRNG() & DNS::MAX_REQUEST_ID;
232 /* If this id is already 'in flight', pick another. */
234 id = this->PRNG() & DNS::MAX_REQUEST_ID;
236 DNSRequest* req = new DNSRequest(ServerInstance, this, this->myserver, id, original);
238 header->id[0] = req->id[0] = id >> 8;
239 header->id[1] = req->id[1] = id & 0xFF;
240 header->flags1 = FLAGS_MASK_RD;
247 /* At this point we already know the id doesnt exist,
248 * so there needs to be no second check for the ::end()
252 /* According to the C++ spec, new never returns NULL. */
261 if (this->GetFd() > -1)
263 if (ServerInstance && ServerInstance->SE)
264 ServerInstance->SE->DelFd(this);
265 shutdown(this->GetFd(), 2);
266 close(this->GetFd());
269 /* Rehash the cache */
270 dnscache* newcache = new dnscache();
271 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
272 newcache->insert(*i);
275 this->cache = newcache;
279 /* Create initial dns cache */
280 this->cache = new dnscache();
283 if (insp_aton(ServerInstance->Config->DNSServer,&addr) > 0)
285 memcpy(&myserver,&addr,sizeof(insp_inaddr));
286 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
288 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
289 ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
290 ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
291 this->ip6munge = true;
293 ServerInstance->Log(DEBUG,"Added nameserver '%s'",ServerInstance->Config->DNSServer);
297 ServerInstance->Log(DEBUG,"GACK! insp_aton says the nameserver '%s' is invalid!",ServerInstance->Config->DNSServer);
300 /* Initialize mastersocket */
301 this->SetFd(socket(PF_PROTOCOL, SOCK_DGRAM, 0));
302 if (this->GetFd() != -1)
304 /* Did it succeed? */
305 if (fcntl(this->GetFd(), F_SETFL, O_NONBLOCK) != 0)
307 /* Couldn't make the socket nonblocking */
308 shutdown(this->GetFd(),2);
309 close(this->GetFd());
315 ServerInstance->Log(DEBUG,"I cant socket() this socket! (%s)",strerror(errno));
318 /* Have we got a socket and is it nonblocking? */
319 if (this->GetFd() != -1)
323 memset(&addr,0,sizeof(addr));
324 addr.sin6_family = AF_FAMILY;
326 addr.sin6_addr = in6addr_any;
329 memset(&addr,0,sizeof(addr));
330 addr.sin_family = AF_FAMILY;
332 addr.sin_addr.s_addr = INADDR_ANY;
335 if (bind(this->GetFd(),(sockaddr *)&addr,sizeof(addr)) != 0)
338 ServerInstance->Log(DEBUG,"Cant bind DNS fd");
339 shutdown(this->GetFd(),2);
340 close(this->GetFd());
344 if (this->GetFd() >= 0)
346 ServerInstance->Log(DEBUG,"Add master socket %d",this->GetFd());
347 /* Hook the descriptor into the socket engine */
348 if (ServerInstance && ServerInstance->SE)
350 if (!ServerInstance->SE->AddFd(this))
352 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
353 shutdown(this->GetFd(),2);
354 close(this->GetFd());
362 /** Initialise the DNS UDP socket so that we can send requests */
363 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
365 ServerInstance->Log(DEBUG,"DNS::DNS: Instance = %08x",Instance);
367 /* Clear the Resolver class table */
368 memset(Classes,0,sizeof(Classes));
370 /* Clear the requests class table */
371 memset(requests,0,sizeof(requests));
373 /* Set the id of the next request to 0
377 /* DNS::Rehash() sets this to a valid ptr
381 /* Again, DNS::Rehash() sets this to a
386 /* Actually read the settings
391 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
392 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
394 short payloadpos = 0;
395 const char* tempchr, *tempchr2 = name;
396 unsigned short length;
398 /* split name up into labels, create query */
399 while ((tempchr = strchr(tempchr2,'.')) != NULL)
401 length = tempchr - tempchr2;
402 if (payloadpos + length + 1 > 507)
404 payload[payloadpos++] = length;
405 memcpy(&payload[payloadpos],tempchr2,length);
406 payloadpos += length;
407 tempchr2 = &tempchr[1];
409 length = strlen(tempchr2);
412 if (payloadpos + length + 2 > 507)
414 payload[payloadpos++] = length;
415 memcpy(&payload[payloadpos],tempchr2,length);
416 payloadpos += length;
417 payload[payloadpos++] = 0;
419 if (payloadpos > 508)
422 memcpy(&payload[payloadpos],&length,2);
423 length = htons(rr_class);
424 memcpy(&payload[payloadpos + 2],&length,2);
425 return payloadpos + 4;
428 /** Start lookup of an hostname to an IP address */
429 int DNS::GetIP(const char *name)
435 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
438 DNSRequest* req = this->AddQuery(&h, id, name);
440 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
446 /** Start lookup of an hostname to an IPv6 address */
447 int DNS::GetIP6(const char *name)
453 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
456 DNSRequest* req = this->AddQuery(&h, id, name);
458 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
464 /** Start lookup of a cname to another name */
465 int DNS::GetCName(const char *alias)
471 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
474 DNSRequest* req = this->AddQuery(&h, id, alias);
476 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
482 /** Start lookup of an IP address to a hostname */
483 int DNS::GetName(const insp_inaddr *ip)
491 unsigned char* c = (unsigned char*)&ip->s6_addr;
492 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
493 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
494 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
495 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
497 DNS::MakeIP6Int(query, (in6_addr*)ip);
499 unsigned char* c = (unsigned char*)&ip->s_addr;
500 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
503 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
506 DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
508 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
514 /** Start lookup of an IP address to a hostname */
515 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
521 #ifdef SUPPORT_IP6LINKS
522 if (fp == PROTOCOL_IPV6)
525 if (inet_pton(AF_INET6, ip, &i) > 0)
527 DNS::MakeIP6Int(query, &i);
530 /* Invalid IP address */
537 if (inet_aton(ip, &i))
539 unsigned char* c = (unsigned char*)&i.s_addr;
540 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
543 /* Invalid IP address */
547 ServerInstance->Log(DEBUG,"DNS::GetNameForce: %s %d",query, fp);
549 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
552 DNSRequest* req = this->AddQuery(&h, id, ip);
554 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
560 /** Build an ipv6 reverse domain from an in6_addr
562 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
564 #ifdef SUPPORT_IP6LINKS
565 const char* hex = "0123456789abcdef";
566 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
570 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
573 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
574 *query++ = '.'; /* Seperator */
576 strcpy(query,"ip6.arpa"); /* Suffix the string */
582 /** Return the next id which is ready, and the result attached to it */
583 DNSResult DNS::GetResult()
585 /* Fetch dns query response and decide where it belongs */
588 unsigned char buffer[sizeof(DNSHeader)];
590 socklen_t x = sizeof(from);
591 const char* ipaddr_from = "";
592 unsigned short int port_from = 0;
594 int length = recvfrom(this->GetFd(),buffer,sizeof(DNSHeader),0,&from,&x);
597 ServerInstance->Log(DEBUG,"Error in recvfrom()! (%s)",strerror(errno));
599 /* Did we get the whole header? */
602 /* Nope - something screwed up. */
603 ServerInstance->Log(DEBUG,"Whole header not read!");
604 return DNSResult(-1,"",0,"");
607 /* Check wether the reply came from a different DNS
608 * server to the one we sent it to, or the source-port
610 * A user could in theory still spoof dns packets anyway
611 * but this is less trivial than just sending garbage
612 * to the client, which is possible without this check.
614 * -- Thanks jilles for pointing this one out.
617 ipaddr_from = insp_ntoa(((sockaddr_in6*)&from)->sin6_addr);
618 port_from = ntohs(((sockaddr_in6*)&from)->sin6_port);
620 ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
621 port_from = ntohs(((sockaddr_in*)&from)->sin_port);
624 /* We cant perform this security check if you're using 4in6.
625 * Tough luck to you, choose one or't other!
629 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
631 ServerInstance->Log(DEBUG,"port %d is not 53, or %s is not %s",port_from, ipaddr_from, ServerInstance->Config->DNSServer);
632 return DNSResult(-1,"",0,"");
636 /* Put the read header info into a header class */
637 DNS::FillHeader(&header,buffer,length - 12);
639 /* Get the id of this request.
640 * Its a 16 bit value stored in two char's,
641 * so we use logic shifts to create the value.
643 unsigned long this_id = header.id[1] + (header.id[0] << 8);
645 /* Do we have a pending request matching this id? */
646 if (!requests[this_id])
648 /* Somehow we got a DNS response for a request we never made... */
649 ServerInstance->Log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",this->GetFd(),this_id);
650 return DNSResult(-1,"",0,"");
654 /* Remove the query from the list of pending queries */
655 req = requests[this_id];
656 requests[this_id] = NULL;
659 /* Inform the DNSRequest class that it has a result to be read.
660 * When its finished it will return a DNSInfo which is a pair of
661 * unsigned char* resource record data, and an error message.
663 DNSInfo data = req->ResultIsReady(header, length);
664 std::string resultstr;
666 /* Check if we got a result, if we didnt, its an error */
667 if (data.first == NULL)
670 * Mask the ID with the value of ERROR_MASK, so that
671 * the dns_deal_with_classes() function knows that its
672 * an error response and needs to be treated uniquely.
673 * Put the error message in the second field.
675 std::string ro = req->orig;
677 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
681 unsigned long ttl = req->ttl;
684 /* Forward lookups come back as binary data. We must format them into ascii */
688 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
689 resultstr = formatted;
694 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
695 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
696 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
697 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
698 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
699 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
700 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
701 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
702 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
703 char* c = strstr(formatted,":0:");
706 memmove(c+1,c+2,strlen(c+2) + 1);
708 while (memcmp(c,"0:",2) == 0)
709 memmove(c,c+2,strlen(c+2) + 1);
710 if (memcmp(c,"0",2) == 0)
712 if (memcmp(formatted,"0::",3) == 0)
713 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
715 resultstr = formatted;
717 /* Special case. Sending ::1 around between servers
718 * and to clients is dangerous, because the : on the
719 * start makes the client or server interpret the IP
720 * as the last parameter on the line with a value ":1".
722 if (*formatted == ':')
723 resultstr = "0" + resultstr;
727 case DNS_QUERY_CNAME:
728 /* Identical handling to PTR */
731 /* Reverse lookups just come back as char* */
732 resultstr = std::string((const char*)data.first);
736 ServerInstance->Log(DEBUG,"WARNING: Somehow we made a request for a DNS_QUERY_PTR4 or DNS_QUERY_PTR6, but these arent real rr types!");
741 /* Build the reply with the id and hostname/ip in it */
742 std::string ro = req->orig;
744 return DNSResult(this_id,resultstr,ttl,ro);
748 /** A result is ready, process it */
749 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
757 /* This is just to keep _FORTIFY_SOURCE happy */
758 rr.type = DNS_QUERY_NONE;
761 if (!(header.flags1 & FLAGS_MASK_QR))
762 return std::make_pair((unsigned char*)NULL,"Not a query result");
764 if (header.flags1 & FLAGS_MASK_OPCODE)
765 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
767 if (header.flags2 & FLAGS_MASK_RCODE)
768 return std::make_pair((unsigned char*)NULL,"Domain name not found");
770 if (header.ancount < 1)
771 return std::make_pair((unsigned char*)NULL,"No resource records returned");
773 /* Subtract the length of the header from the length of the packet */
776 while ((unsigned int)q < header.qdcount && i < length)
778 if (header.payload[i] > 63)
785 if (header.payload[i] == 0)
790 else i += header.payload[i] + 1;
794 while ((unsigned)curanswer < header.ancount)
797 while (q == 0 && i < length)
799 if (header.payload[i] > 63)
806 if (header.payload[i] == 0)
811 else i += header.payload[i] + 1; /* skip length and label */
815 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
817 DNS::FillResourceRecord(&rr,&header.payload[i]);
819 if (rr.type != this->type)
825 if (rr.rr_class != this->rr_class)
833 if ((unsigned int)curanswer == header.ancount)
834 return std::make_pair((unsigned char*)NULL,"No valid answers");
836 if (i + rr.rdlength > (unsigned int)length)
837 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
839 if (rr.rdlength > 1023)
840 return std::make_pair((unsigned char*)NULL,"Resource record too large");
846 case DNS_QUERY_CNAME:
847 /* CNAME and PTR have the same processing code */
851 while (q == 0 && i < length && o + 256 < 1023)
853 if (header.payload[i] > 63)
855 memcpy(&ptr,&header.payload[i],2);
856 i = ntohs(ptr) - 0xC000 - 12;
860 if (header.payload[i] == 0)
869 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
870 o += header.payload[i];
871 i += header.payload[i] + 1;
878 memcpy(res,&header.payload[i],rr.rdlength);
879 res[rr.rdlength] = 0;
882 memcpy(res,&header.payload[i],rr.rdlength);
883 res[rr.rdlength] = 0;
886 memcpy(res,&header.payload[i],rr.rdlength);
887 res[rr.rdlength] = 0;
890 return std::make_pair(res,"No error");;
893 /** Close the master socket */
896 shutdown(this->GetFd(), 2);
897 close(this->GetFd());
900 CachedQuery* DNS::GetCache(const std::string &source)
902 dnscache::iterator x = cache->find(source.c_str());
903 if (x != cache->end())
909 void DNS::DelCache(const std::string &source)
911 cache->erase(source.c_str());
914 void Resolver::TriggerCachedResult()
917 OnLookupComplete(CQ->data, time_left, true);
920 /** High level abstraction of dns used by application at large */
921 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
923 ServerInstance->Log(DEBUG,"Instance: %08x %08x",Instance, ServerInstance);
927 CQ = ServerInstance->Res->GetCache(source);
930 time_left = CQ->CalcTTLRemaining();
933 ServerInstance->Log(DEBUG,"Cached but EXPIRED result: %s", CQ->data.c_str());
934 ServerInstance->Res->DelCache(source);
939 ServerInstance->Log(DEBUG,"Cached result: %s", CQ->data.c_str());
949 this->myid = ServerInstance->Res->GetIP(source.c_str());
953 if (insp_aton(source.c_str(), &binip) > 0)
955 /* Valid ip address */
956 this->myid = ServerInstance->Res->GetName(&binip);
960 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
961 throw ModuleException("Resolver: Bad IP address");
967 querytype = DNS_QUERY_PTR;
968 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
972 querytype = DNS_QUERY_PTR;
973 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
977 this->myid = ServerInstance->Res->GetIP6(source.c_str());
980 case DNS_QUERY_CNAME:
981 this->myid = ServerInstance->Res->GetCName(source.c_str());
988 if (this->myid == -1)
990 ServerInstance->Log(DEBUG,"Resolver::Resolver: Could not get an id!");
991 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
992 throw ModuleException("Resolver: Couldnt get an id to make a request");
993 /* We shouldnt get here really */
997 ServerInstance->Log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
1000 /** Called when an error occurs */
1001 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1003 /* Nothing in here */
1006 /** Destroy a resolver */
1007 Resolver::~Resolver()
1009 /* Nothing here (yet) either */
1012 /** Get the request id associated with this class */
1013 int Resolver::GetId()
1018 Module* Resolver::GetCreator()
1020 return this->Creator;
1023 /** Process a socket read event */
1024 void DNS::HandleEvent(EventType et, int errornum)
1026 /* Fetch the id and result of the next available packet */
1027 DNSResult res = this->GetResult();
1028 /* Is there a usable request id? */
1031 /* Its an error reply */
1032 if (res.id & ERROR_MASK)
1034 /* Mask off the error bit */
1035 res.id -= ERROR_MASK;
1036 /* Marshall the error to the correct class */
1037 ServerInstance->Log(DEBUG,"Error available, id=%d",res.id);
1038 if (Classes[res.id])
1040 if (ServerInstance && ServerInstance->stats)
1041 ServerInstance->stats->statsDnsBad++;
1042 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1043 delete Classes[res.id];
1044 Classes[res.id] = NULL;
1049 /* It is a non-error result */
1050 ServerInstance->Log(DEBUG,"Result available, id=%d",res.id);
1051 /* Marshall the result to the correct class */
1052 if (Classes[res.id])
1054 if (ServerInstance && ServerInstance->stats)
1055 ServerInstance->stats->statsDnsGood++;
1057 if (!this->GetCache(res.original.c_str()))
1059 ServerInstance->Log(DEBUG,"Caching result: %s->%s for %lu secs", res.original.c_str(), res.result.c_str(), res.ttl);
1060 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1063 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1064 delete Classes[res.id];
1065 Classes[res.id] = NULL;
1069 if (ServerInstance && ServerInstance->stats)
1070 ServerInstance->stats->statsDns++;
1074 /** Add a derived Resolver to the working set */
1075 bool DNS::AddResolverClass(Resolver* r)
1077 /* Check the pointers validity and the id's validity */
1078 if ((r) && (r->GetId() > -1))
1080 /* Check the slot isnt already occupied -
1081 * This should NEVER happen unless we have
1082 * a severely broken DNS server somewhere
1084 if (!Classes[r->GetId()])
1086 /* Set up the pointer to the class */
1087 Classes[r->GetId()] = r;
1096 /* Pointer or id not valid.
1097 * Free the item and return
1106 void DNS::CleanResolvers(Module* module)
1108 for (int i = 0; i < MAX_REQUEST_ID; i++)
1112 if (Classes[i]->GetCreator() == module)
1114 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1122 /** Generate pseudo-random number */
1123 unsigned long DNS::PRNG()
1125 unsigned long val = 0;
1127 serverstats* s = ServerInstance->stats;
1128 gettimeofday(&n,NULL);
1129 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1130 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1131 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - s->BoundPortCount;