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. */
256 int DNS::ClearCache()
258 /* This ensures the buckets are reset to sane levels */
259 int rv = this->cache->size();
261 this->cache = new dnscache();
270 if (this->GetFd() > -1)
272 if (ServerInstance && ServerInstance->SE)
273 ServerInstance->SE->DelFd(this);
274 shutdown(this->GetFd(), 2);
275 close(this->GetFd());
278 /* Rehash the cache */
279 dnscache* newcache = new dnscache();
280 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
281 /* Dont include expired items (theres no point) */
282 if (i->second.CalcTTLRemaining())
283 newcache->insert(*i);
286 this->cache = newcache;
290 /* Create initial dns cache */
291 this->cache = new dnscache();
294 if (insp_aton(ServerInstance->Config->DNSServer,&addr) > 0)
296 memcpy(&myserver,&addr,sizeof(insp_inaddr));
297 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
299 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
300 ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
301 ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
302 this->ip6munge = true;
304 ServerInstance->Log(DEBUG,"Added nameserver '%s'",ServerInstance->Config->DNSServer);
308 ServerInstance->Log(DEBUG,"GACK! insp_aton says the nameserver '%s' is invalid!",ServerInstance->Config->DNSServer);
311 /* Initialize mastersocket */
312 this->SetFd(socket(PF_PROTOCOL, SOCK_DGRAM, 0));
313 if (this->GetFd() != -1)
315 /* Did it succeed? */
316 if (fcntl(this->GetFd(), F_SETFL, O_NONBLOCK) != 0)
318 /* Couldn't make the socket nonblocking */
319 shutdown(this->GetFd(),2);
320 close(this->GetFd());
326 ServerInstance->Log(DEBUG,"I cant socket() this socket! (%s)",strerror(errno));
329 /* Have we got a socket and is it nonblocking? */
330 if (this->GetFd() != -1)
334 memset(&addr,0,sizeof(addr));
335 addr.sin6_family = AF_FAMILY;
337 addr.sin6_addr = in6addr_any;
340 memset(&addr,0,sizeof(addr));
341 addr.sin_family = AF_FAMILY;
343 addr.sin_addr.s_addr = INADDR_ANY;
346 if (bind(this->GetFd(),(sockaddr *)&addr,sizeof(addr)) != 0)
349 ServerInstance->Log(DEBUG,"Cant bind DNS fd");
350 shutdown(this->GetFd(),2);
351 close(this->GetFd());
355 if (this->GetFd() >= 0)
357 ServerInstance->Log(DEBUG,"Add master socket %d",this->GetFd());
358 /* Hook the descriptor into the socket engine */
359 if (ServerInstance && ServerInstance->SE)
361 if (!ServerInstance->SE->AddFd(this))
363 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
364 shutdown(this->GetFd(),2);
365 close(this->GetFd());
373 /** Initialise the DNS UDP socket so that we can send requests */
374 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
376 ServerInstance->Log(DEBUG,"DNS::DNS: Instance = %08x",Instance);
378 /* Clear the Resolver class table */
379 memset(Classes,0,sizeof(Classes));
381 /* Clear the requests class table */
382 memset(requests,0,sizeof(requests));
384 /* Set the id of the next request to 0
388 /* DNS::Rehash() sets this to a valid ptr
392 /* Again, DNS::Rehash() sets this to a
397 /* Actually read the settings
402 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
403 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
405 short payloadpos = 0;
406 const char* tempchr, *tempchr2 = name;
407 unsigned short length;
409 /* split name up into labels, create query */
410 while ((tempchr = strchr(tempchr2,'.')) != NULL)
412 length = tempchr - tempchr2;
413 if (payloadpos + length + 1 > 507)
415 payload[payloadpos++] = length;
416 memcpy(&payload[payloadpos],tempchr2,length);
417 payloadpos += length;
418 tempchr2 = &tempchr[1];
420 length = strlen(tempchr2);
423 if (payloadpos + length + 2 > 507)
425 payload[payloadpos++] = length;
426 memcpy(&payload[payloadpos],tempchr2,length);
427 payloadpos += length;
428 payload[payloadpos++] = 0;
430 if (payloadpos > 508)
433 memcpy(&payload[payloadpos],&length,2);
434 length = htons(rr_class);
435 memcpy(&payload[payloadpos + 2],&length,2);
436 return payloadpos + 4;
439 /** Start lookup of an hostname to an IP address */
440 int DNS::GetIP(const char *name)
446 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
449 DNSRequest* req = this->AddQuery(&h, id, name);
451 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
457 /** Start lookup of an hostname to an IPv6 address */
458 int DNS::GetIP6(const char *name)
464 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
467 DNSRequest* req = this->AddQuery(&h, id, name);
469 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
475 /** Start lookup of a cname to another name */
476 int DNS::GetCName(const char *alias)
482 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
485 DNSRequest* req = this->AddQuery(&h, id, alias);
487 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
493 /** Start lookup of an IP address to a hostname */
494 int DNS::GetName(const insp_inaddr *ip)
502 unsigned char* c = (unsigned char*)&ip->s6_addr;
503 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
504 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
505 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
506 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
508 DNS::MakeIP6Int(query, (in6_addr*)ip);
510 unsigned char* c = (unsigned char*)&ip->s_addr;
511 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
514 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
517 DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
519 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
525 /** Start lookup of an IP address to a hostname */
526 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
532 #ifdef SUPPORT_IP6LINKS
533 if (fp == PROTOCOL_IPV6)
536 if (inet_pton(AF_INET6, ip, &i) > 0)
538 DNS::MakeIP6Int(query, &i);
541 /* Invalid IP address */
548 if (inet_aton(ip, &i))
550 unsigned char* c = (unsigned char*)&i.s_addr;
551 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
554 /* Invalid IP address */
558 ServerInstance->Log(DEBUG,"DNS::GetNameForce: %s %d",query, fp);
560 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
563 DNSRequest* req = this->AddQuery(&h, id, ip);
565 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
571 /** Build an ipv6 reverse domain from an in6_addr
573 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
575 #ifdef SUPPORT_IP6LINKS
576 const char* hex = "0123456789abcdef";
577 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
581 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
584 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
585 *query++ = '.'; /* Seperator */
587 strcpy(query,"ip6.arpa"); /* Suffix the string */
593 /** Return the next id which is ready, and the result attached to it */
594 DNSResult DNS::GetResult()
596 /* Fetch dns query response and decide where it belongs */
599 unsigned char buffer[sizeof(DNSHeader)];
601 socklen_t x = sizeof(from);
602 const char* ipaddr_from = "";
603 unsigned short int port_from = 0;
605 int length = recvfrom(this->GetFd(),buffer,sizeof(DNSHeader),0,&from,&x);
608 ServerInstance->Log(DEBUG,"Error in recvfrom()! (%s)",strerror(errno));
610 /* Did we get the whole header? */
613 /* Nope - something screwed up. */
614 ServerInstance->Log(DEBUG,"Whole header not read!");
615 return DNSResult(-1,"",0,"");
618 /* Check wether the reply came from a different DNS
619 * server to the one we sent it to, or the source-port
621 * A user could in theory still spoof dns packets anyway
622 * but this is less trivial than just sending garbage
623 * to the client, which is possible without this check.
625 * -- Thanks jilles for pointing this one out.
628 ipaddr_from = insp_ntoa(((sockaddr_in6*)&from)->sin6_addr);
629 port_from = ntohs(((sockaddr_in6*)&from)->sin6_port);
631 ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
632 port_from = ntohs(((sockaddr_in*)&from)->sin_port);
635 /* We cant perform this security check if you're using 4in6.
636 * Tough luck to you, choose one or't other!
640 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
642 ServerInstance->Log(DEBUG,"port %d is not 53, or %s is not %s",port_from, ipaddr_from, ServerInstance->Config->DNSServer);
643 return DNSResult(-1,"",0,"");
647 /* Put the read header info into a header class */
648 DNS::FillHeader(&header,buffer,length - 12);
650 /* Get the id of this request.
651 * Its a 16 bit value stored in two char's,
652 * so we use logic shifts to create the value.
654 unsigned long this_id = header.id[1] + (header.id[0] << 8);
656 /* Do we have a pending request matching this id? */
657 if (!requests[this_id])
659 /* Somehow we got a DNS response for a request we never made... */
660 ServerInstance->Log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",this->GetFd(),this_id);
661 return DNSResult(-1,"",0,"");
665 /* Remove the query from the list of pending queries */
666 req = requests[this_id];
667 requests[this_id] = NULL;
670 /* Inform the DNSRequest class that it has a result to be read.
671 * When its finished it will return a DNSInfo which is a pair of
672 * unsigned char* resource record data, and an error message.
674 DNSInfo data = req->ResultIsReady(header, length);
675 std::string resultstr;
677 /* Check if we got a result, if we didnt, its an error */
678 if (data.first == NULL)
681 * Mask the ID with the value of ERROR_MASK, so that
682 * the dns_deal_with_classes() function knows that its
683 * an error response and needs to be treated uniquely.
684 * Put the error message in the second field.
686 std::string ro = req->orig;
688 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
692 unsigned long ttl = req->ttl;
695 /* Forward lookups come back as binary data. We must format them into ascii */
699 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
700 resultstr = formatted;
705 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
706 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
707 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
708 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
709 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
710 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
711 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
712 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
713 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
714 char* c = strstr(formatted,":0:");
717 memmove(c+1,c+2,strlen(c+2) + 1);
719 while (memcmp(c,"0:",2) == 0)
720 memmove(c,c+2,strlen(c+2) + 1);
721 if (memcmp(c,"0",2) == 0)
723 if (memcmp(formatted,"0::",3) == 0)
724 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
726 resultstr = formatted;
728 /* Special case. Sending ::1 around between servers
729 * and to clients is dangerous, because the : on the
730 * start makes the client or server interpret the IP
731 * as the last parameter on the line with a value ":1".
733 if (*formatted == ':')
734 resultstr = "0" + resultstr;
738 case DNS_QUERY_CNAME:
739 /* Identical handling to PTR */
742 /* Reverse lookups just come back as char* */
743 resultstr = std::string((const char*)data.first);
747 ServerInstance->Log(DEBUG,"WARNING: Somehow we made a request for a DNS_QUERY_PTR4 or DNS_QUERY_PTR6, but these arent real rr types!");
752 /* Build the reply with the id and hostname/ip in it */
753 std::string ro = req->orig;
755 return DNSResult(this_id,resultstr,ttl,ro);
759 /** A result is ready, process it */
760 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
768 /* This is just to keep _FORTIFY_SOURCE happy */
769 rr.type = DNS_QUERY_NONE;
772 if (!(header.flags1 & FLAGS_MASK_QR))
773 return std::make_pair((unsigned char*)NULL,"Not a query result");
775 if (header.flags1 & FLAGS_MASK_OPCODE)
776 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
778 if (header.flags2 & FLAGS_MASK_RCODE)
779 return std::make_pair((unsigned char*)NULL,"Domain name not found");
781 if (header.ancount < 1)
782 return std::make_pair((unsigned char*)NULL,"No resource records returned");
784 /* Subtract the length of the header from the length of the packet */
787 while ((unsigned int)q < header.qdcount && i < length)
789 if (header.payload[i] > 63)
796 if (header.payload[i] == 0)
801 else i += header.payload[i] + 1;
805 while ((unsigned)curanswer < header.ancount)
808 while (q == 0 && i < length)
810 if (header.payload[i] > 63)
817 if (header.payload[i] == 0)
822 else i += header.payload[i] + 1; /* skip length and label */
826 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
828 DNS::FillResourceRecord(&rr,&header.payload[i]);
830 if (rr.type != this->type)
836 if (rr.rr_class != this->rr_class)
844 if ((unsigned int)curanswer == header.ancount)
845 return std::make_pair((unsigned char*)NULL,"No valid answers");
847 if (i + rr.rdlength > (unsigned int)length)
848 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
850 if (rr.rdlength > 1023)
851 return std::make_pair((unsigned char*)NULL,"Resource record too large");
857 case DNS_QUERY_CNAME:
858 /* CNAME and PTR have the same processing code */
862 while (q == 0 && i < length && o + 256 < 1023)
864 if (header.payload[i] > 63)
866 memcpy(&ptr,&header.payload[i],2);
867 i = ntohs(ptr) - 0xC000 - 12;
871 if (header.payload[i] == 0)
880 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
881 o += header.payload[i];
882 i += header.payload[i] + 1;
889 memcpy(res,&header.payload[i],rr.rdlength);
890 res[rr.rdlength] = 0;
893 memcpy(res,&header.payload[i],rr.rdlength);
894 res[rr.rdlength] = 0;
897 memcpy(res,&header.payload[i],rr.rdlength);
898 res[rr.rdlength] = 0;
901 return std::make_pair(res,"No error");;
904 /** Close the master socket */
907 shutdown(this->GetFd(), 2);
908 close(this->GetFd());
911 CachedQuery* DNS::GetCache(const std::string &source)
913 dnscache::iterator x = cache->find(source.c_str());
914 if (x != cache->end())
920 void DNS::DelCache(const std::string &source)
922 cache->erase(source.c_str());
925 void Resolver::TriggerCachedResult()
928 OnLookupComplete(CQ->data, time_left, true);
931 /** High level abstraction of dns used by application at large */
932 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
934 ServerInstance->Log(DEBUG,"Instance: %08x %08x",Instance, ServerInstance);
938 CQ = ServerInstance->Res->GetCache(source);
941 time_left = CQ->CalcTTLRemaining();
944 ServerInstance->Log(DEBUG,"Cached but EXPIRED result: %s", CQ->data.c_str());
945 ServerInstance->Res->DelCache(source);
950 ServerInstance->Log(DEBUG,"Cached result: %s", CQ->data.c_str());
960 this->myid = ServerInstance->Res->GetIP(source.c_str());
964 if (insp_aton(source.c_str(), &binip) > 0)
966 /* Valid ip address */
967 this->myid = ServerInstance->Res->GetName(&binip);
971 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
972 throw ModuleException("Resolver: Bad IP address");
978 querytype = DNS_QUERY_PTR;
979 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
983 querytype = DNS_QUERY_PTR;
984 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
988 this->myid = ServerInstance->Res->GetIP6(source.c_str());
991 case DNS_QUERY_CNAME:
992 this->myid = ServerInstance->Res->GetCName(source.c_str());
999 if (this->myid == -1)
1001 ServerInstance->Log(DEBUG,"Resolver::Resolver: Could not get an id!");
1002 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1003 throw ModuleException("Resolver: Couldnt get an id to make a request");
1004 /* We shouldnt get here really */
1008 ServerInstance->Log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
1011 /** Called when an error occurs */
1012 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1014 /* Nothing in here */
1017 /** Destroy a resolver */
1018 Resolver::~Resolver()
1020 /* Nothing here (yet) either */
1023 /** Get the request id associated with this class */
1024 int Resolver::GetId()
1029 Module* Resolver::GetCreator()
1031 return this->Creator;
1034 /** Process a socket read event */
1035 void DNS::HandleEvent(EventType et, int errornum)
1037 /* Fetch the id and result of the next available packet */
1038 DNSResult res = this->GetResult();
1039 /* Is there a usable request id? */
1042 /* Its an error reply */
1043 if (res.id & ERROR_MASK)
1045 /* Mask off the error bit */
1046 res.id -= ERROR_MASK;
1047 /* Marshall the error to the correct class */
1048 ServerInstance->Log(DEBUG,"Error available, id=%d",res.id);
1049 if (Classes[res.id])
1051 if (ServerInstance && ServerInstance->stats)
1052 ServerInstance->stats->statsDnsBad++;
1053 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1054 delete Classes[res.id];
1055 Classes[res.id] = NULL;
1060 /* It is a non-error result */
1061 ServerInstance->Log(DEBUG,"Result available, id=%d",res.id);
1062 /* Marshall the result to the correct class */
1063 if (Classes[res.id])
1065 if (ServerInstance && ServerInstance->stats)
1066 ServerInstance->stats->statsDnsGood++;
1068 if (!this->GetCache(res.original.c_str()))
1070 ServerInstance->Log(DEBUG,"Caching result: %s->%s for %lu secs", res.original.c_str(), res.result.c_str(), res.ttl);
1071 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1074 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1075 delete Classes[res.id];
1076 Classes[res.id] = NULL;
1080 if (ServerInstance && ServerInstance->stats)
1081 ServerInstance->stats->statsDns++;
1085 /** Add a derived Resolver to the working set */
1086 bool DNS::AddResolverClass(Resolver* r)
1088 /* Check the pointers validity and the id's validity */
1089 if ((r) && (r->GetId() > -1))
1091 /* Check the slot isnt already occupied -
1092 * This should NEVER happen unless we have
1093 * a severely broken DNS server somewhere
1095 if (!Classes[r->GetId()])
1097 /* Set up the pointer to the class */
1098 Classes[r->GetId()] = r;
1107 /* Pointer or id not valid.
1108 * Free the item and return
1117 void DNS::CleanResolvers(Module* module)
1119 for (int i = 0; i < MAX_REQUEST_ID; i++)
1123 if (Classes[i]->GetCreator() == module)
1125 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1133 /** Generate pseudo-random number */
1134 unsigned long DNS::PRNG()
1136 unsigned long val = 0;
1138 serverstats* s = ServerInstance->stats;
1139 gettimeofday(&n,NULL);
1140 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1141 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1142 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - s->BoundPortCount;