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;
39 using irc::sockets::OpenTCPSocket;
41 /** Masks to mask off the responses we get from the DNSRequest methods
45 ERROR_MASK = 0x10000 /* Result is an error */
48 /** Flags which can be ORed into a request or reply for different meanings
52 FLAGS_MASK_RD = 0x01, /* Recursive */
54 FLAGS_MASK_AA = 0x04, /* Authoritative */
55 FLAGS_MASK_OPCODE = 0x78,
57 FLAGS_MASK_RCODE = 0x0F, /* Request */
63 /** 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 DNS* dnsobj; /* DNS caller (where we get our FD from) */
96 unsigned long ttl; /* Time to live */
97 std::string orig; /* Original requested name/ip */
99 DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original);
101 DNSInfo ResultIsReady(DNSHeader &h, int length);
102 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
105 class CacheTimer : public InspTimer
108 InspIRCd* ServerInstance;
111 CacheTimer(InspIRCd* Instance, DNS* thisdns)
112 : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
114 virtual void Tick(time_t TIME)
120 class RequestTimeout : public InspTimer
122 InspIRCd* ServerInstance;
126 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
130 void Tick(time_t TIME)
132 if (ServerInstance->Res->requests[watchid] == watch)
134 /* Still exists, whack it */
135 if (ServerInstance->Res->Classes[watchid])
137 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
138 delete ServerInstance->Res->Classes[watchid];
139 ServerInstance->Res->Classes[watchid] = NULL;
141 ServerInstance->Res->requests[watchid] = NULL;
148 /* Allocate the processing buffer */
149 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, int id, const std::string &original) : dnsobj(dns)
151 res = new unsigned char[512];
154 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
155 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
158 /* Deallocate the processing buffer */
159 DNSRequest::~DNSRequest()
164 /** Fill a ResourceRecord class based on raw data input */
165 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
167 rr->type = (QueryType)((input[0] << 8) + input[1]);
168 rr->rr_class = (input[2] << 8) + input[3];
169 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
170 rr->rdlength = (input[8] << 8) + input[9];
173 /** Fill a DNSHeader class based on raw data input of a given length */
174 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
176 header->id[0] = input[0];
177 header->id[1] = input[1];
178 header->flags1 = input[2];
179 header->flags2 = input[3];
180 header->qdcount = (input[4] << 8) + input[5];
181 header->ancount = (input[6] << 8) + input[7];
182 header->nscount = (input[8] << 8) + input[9];
183 header->arcount = (input[10] << 8) + input[11];
184 memcpy(header->payload,&input[12],length);
187 /** Empty a DNSHeader class out into raw data, ready for transmission */
188 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
190 output[0] = header->id[0];
191 output[1] = header->id[1];
192 output[2] = header->flags1;
193 output[3] = header->flags2;
194 output[4] = header->qdcount >> 8;
195 output[5] = header->qdcount & 0xFF;
196 output[6] = header->ancount >> 8;
197 output[7] = header->ancount & 0xFF;
198 output[8] = header->nscount >> 8;
199 output[9] = header->nscount & 0xFF;
200 output[10] = header->arcount >> 8;
201 output[11] = header->arcount & 0xFF;
202 memcpy(&output[12],header->payload,length);
205 /** Send requests we have previously built down the UDP socket */
206 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
208 unsigned char payload[sizeof(DNSHeader)];
213 DNS::EmptyHeader(payload,header,length);
216 if (this->dnsobj->socketfamily == AF_INET6)
219 memset(&addr,0,sizeof(addr));
220 memcpy(&addr.sin6_addr,&dnsobj->myserver6,sizeof(addr.sin6_addr));
221 addr.sin6_family = AF_INET6;
222 addr.sin6_port = htons(DNS::QUERY_PORT);
223 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
229 memset(&addr,0,sizeof(addr));
230 memcpy(&addr.sin_addr.s_addr,&dnsobj->myserver4,sizeof(addr.sin_addr));
231 addr.sin_family = AF_INET;
232 addr.sin_port = htons(DNS::QUERY_PORT);
233 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
238 memset(&addr,0,sizeof(addr));
239 memcpy(&addr.sin_addr.s_addr, &dnsobj->myserver4, sizeof(addr.sin_addr));
240 addr.sin_family = AF_INET;
241 addr.sin_port = htons(DNS::QUERY_PORT);
242 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) != length+12)
249 /** Add a query with a predefined header, and allocate an ID for it. */
250 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
252 /* Is the DNS connection down? */
253 if (this->GetFd() == -1)
257 id = this->PRNG() & DNS::MAX_REQUEST_ID;
259 /* If this id is already 'in flight', pick another. */
261 id = this->PRNG() & DNS::MAX_REQUEST_ID;
263 DNSRequest* req = new DNSRequest(ServerInstance, this, id, original);
265 header->id[0] = req->id[0] = id >> 8;
266 header->id[1] = req->id[1] = id & 0xFF;
267 header->flags1 = FLAGS_MASK_RD;
274 /* At this point we already know the id doesnt exist,
275 * so there needs to be no second check for the ::end()
279 /* According to the C++ spec, new never returns NULL. */
283 int DNS::ClearCache()
285 /* This ensures the buckets are reset to sane levels */
286 int rv = this->cache->size();
288 this->cache = new dnscache();
292 int DNS::PruneCache()
295 dnscache* newcache = new dnscache();
296 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
297 /* Dont include expired items (theres no point) */
298 if (i->second.CalcTTLRemaining())
299 newcache->insert(*i);
304 this->cache = newcache;
313 if (this->GetFd() > -1)
315 if (ServerInstance && ServerInstance->SE)
316 ServerInstance->SE->DelFd(this);
317 shutdown(this->GetFd(), 2);
318 close(this->GetFd());
321 /* Rehash the cache */
326 /* Create initial dns cache */
327 this->cache = new dnscache();
330 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
332 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
333 ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
334 ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
335 this->ip6munge = true;
338 this->socketfamily = AF_INET;
340 if (strchr(ServerInstance->Config->DNSServer,':'))
342 this->socketfamily = AF_INET6;
343 inet_pton(AF_INET6, ServerInstance->Config->DNSServer, &this->myserver6);
347 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
351 inet_aton(ServerInstance->Config->DNSServer, &this->myserver4);
354 /* Initialize mastersocket */
355 int s = OpenTCPSocket(ServerInstance->Config->DNSServer, SOCK_DGRAM);
358 /* Have we got a socket and is it nonblocking? */
359 if (this->GetFd() != -1)
361 /* Bind the port - port 0 INADDR_ANY */
362 if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
365 shutdown(this->GetFd(),2);
366 close(this->GetFd());
370 if (this->GetFd() >= 0)
372 /* Hook the descriptor into the socket engine */
373 if (ServerInstance && ServerInstance->SE)
375 if (!ServerInstance->SE->AddFd(this))
377 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
378 shutdown(this->GetFd(),2);
379 close(this->GetFd());
387 /** Initialise the DNS UDP socket so that we can send requests */
388 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
390 /* Clear the Resolver class table */
391 memset(Classes,0,sizeof(Classes));
393 /* Clear the requests class table */
394 memset(requests,0,sizeof(requests));
396 /* Set the id of the next request to 0
400 /* DNS::Rehash() sets this to a valid ptr
404 /* Again, DNS::Rehash() sets this to a
409 /* Actually read the settings
413 this->PruneTimer = new CacheTimer(ServerInstance, this);
415 ServerInstance->Timers->AddTimer(this->PruneTimer);
418 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
419 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
421 short payloadpos = 0;
422 const char* tempchr, *tempchr2 = name;
423 unsigned short length;
425 /* split name up into labels, create query */
426 while ((tempchr = strchr(tempchr2,'.')) != NULL)
428 length = tempchr - tempchr2;
429 if (payloadpos + length + 1 > 507)
431 payload[payloadpos++] = length;
432 memcpy(&payload[payloadpos],tempchr2,length);
433 payloadpos += length;
434 tempchr2 = &tempchr[1];
436 length = strlen(tempchr2);
439 if (payloadpos + length + 2 > 507)
441 payload[payloadpos++] = length;
442 memcpy(&payload[payloadpos],tempchr2,length);
443 payloadpos += length;
444 payload[payloadpos++] = 0;
446 if (payloadpos > 508)
449 memcpy(&payload[payloadpos],&length,2);
450 length = htons(rr_class);
451 memcpy(&payload[payloadpos + 2],&length,2);
452 return payloadpos + 4;
455 /** Start lookup of an hostname to an IP address */
456 int DNS::GetIP(const char *name)
462 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
465 DNSRequest* req = this->AddQuery(&h, id, name);
467 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
473 /** Start lookup of an hostname to an IPv6 address */
474 int DNS::GetIP6(const char *name)
480 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
483 DNSRequest* req = this->AddQuery(&h, id, name);
485 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
491 /** Start lookup of a cname to another name */
492 int DNS::GetCName(const char *alias)
498 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
501 DNSRequest* req = this->AddQuery(&h, id, alias);
503 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
509 /** Start lookup of an IP address to a hostname */
510 int DNS::GetName(const insp_inaddr *ip)
518 unsigned char* c = (unsigned char*)&ip->s6_addr;
519 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
520 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
521 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
522 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
524 DNS::MakeIP6Int(query, (in6_addr*)ip);
526 unsigned char* c = (unsigned char*)&ip->s_addr;
527 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
530 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
533 DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
535 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
541 /** Start lookup of an IP address to a hostname */
542 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
544 ServerInstance->Log(DEBUG,"GetNameForce: %s", ip);
549 #ifdef SUPPORT_IP6LINKS
550 if (fp == PROTOCOL_IPV6)
553 if (inet_pton(AF_INET6, ip, &i) > 0)
555 ServerInstance->Log(DEBUG,"Resolve to ipv6");
556 DNS::MakeIP6Int(query, &i);
559 /* Invalid IP address */
566 if (inet_aton(ip, &i))
568 ServerInstance->Log(DEBUG,"Resolve to ipv4");
569 unsigned char* c = (unsigned char*)&i.s_addr;
570 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
573 /* Invalid IP address */
577 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
580 DNSRequest* req = this->AddQuery(&h, id, ip);
582 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
588 /** Build an ipv6 reverse domain from an in6_addr
590 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
592 #ifdef SUPPORT_IP6LINKS
593 const char* hex = "0123456789abcdef";
594 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
598 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
601 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
602 *query++ = '.'; /* Seperator */
604 strcpy(query,"ip6.arpa"); /* Suffix the string */
610 /** Return the next id which is ready, and the result attached to it */
611 DNSResult DNS::GetResult()
613 /* Fetch dns query response and decide where it belongs */
616 unsigned char buffer[sizeof(DNSHeader)];
617 sockaddr* from = new sockaddr[2];
619 socklen_t x = this->socketfamily == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
621 socklen_t x = sizeof(sockaddr_in);
623 const char* ipaddr_from;
624 unsigned short int port_from = 0;
626 int length = recvfrom(this->GetFd(),buffer,sizeof(DNSHeader),0,from,&x);
628 ServerInstance->Log(DEBUG,"Recv %d.", length);
630 /* Did we get the whole header? */
633 /* Nope - something screwed up. */
635 return DNSResult(-1,"",0,"");
638 /* Check wether the reply came from a different DNS
639 * server to the one we sent it to, or the source-port
641 * A user could in theory still spoof dns packets anyway
642 * but this is less trivial than just sending garbage
643 * to the client, which is possible without this check.
645 * -- Thanks jilles for pointing this one out.
649 if (this->socketfamily == AF_INET6)
651 ipaddr_from = inet_ntop(AF_INET6, &((sockaddr_in6*)from)->sin6_addr, nbuf, sizeof(nbuf));
652 port_from = ntohs(((sockaddr_in6*)from)->sin6_port);
656 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
657 port_from = ntohs(((sockaddr_in*)from)->sin_port);
660 ipaddr_from = inet_ntoa(((sockaddr_in*)from)->sin_addr);
661 port_from = ntohs(((sockaddr_in*)from)->sin_port);
666 /* We cant perform this security check if you're using 4in6.
667 * Tough luck to you, choose one or't other!
671 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
673 ServerInstance->Log(DEBUG,"Doesnt match security: port_from=%d ipaddr_from=%s",port_from,ipaddr_from);
674 return DNSResult(-1,"",0,"");
678 /* Put the read header info into a header class */
679 DNS::FillHeader(&header,buffer,length - 12);
681 /* Get the id of this request.
682 * Its a 16 bit value stored in two char's,
683 * so we use logic shifts to create the value.
685 unsigned long this_id = header.id[1] + (header.id[0] << 8);
687 /* Do we have a pending request matching this id? */
688 if (!requests[this_id])
690 /* Somehow we got a DNS response for a request we never made... */
691 return DNSResult(-1,"",0,"");
695 /* Remove the query from the list of pending queries */
696 req = requests[this_id];
697 requests[this_id] = NULL;
700 /* Inform the DNSRequest class that it has a result to be read.
701 * When its finished it will return a DNSInfo which is a pair of
702 * unsigned char* resource record data, and an error message.
704 DNSInfo data = req->ResultIsReady(header, length);
705 std::string resultstr;
707 /* Check if we got a result, if we didnt, its an error */
708 if (data.first == NULL)
711 * Mask the ID with the value of ERROR_MASK, so that
712 * the dns_deal_with_classes() function knows that its
713 * an error response and needs to be treated uniquely.
714 * Put the error message in the second field.
716 std::string ro = req->orig;
718 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
722 unsigned long ttl = req->ttl;
725 /* Forward lookups come back as binary data. We must format them into ascii */
729 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
730 resultstr = formatted;
735 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
736 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
737 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
738 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
739 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
740 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
741 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
742 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
743 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
744 char* c = strstr(formatted,":0:");
747 memmove(c+1,c+2,strlen(c+2) + 1);
749 while (memcmp(c,"0:",2) == 0)
750 memmove(c,c+2,strlen(c+2) + 1);
751 if (memcmp(c,"0",2) == 0)
753 if (memcmp(formatted,"0::",3) == 0)
754 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
756 resultstr = formatted;
758 /* Special case. Sending ::1 around between servers
759 * and to clients is dangerous, because the : on the
760 * start makes the client or server interpret the IP
761 * as the last parameter on the line with a value ":1".
763 if (*formatted == ':')
764 resultstr = "0" + resultstr;
768 case DNS_QUERY_CNAME:
769 /* Identical handling to PTR */
772 /* Reverse lookups just come back as char* */
773 resultstr = std::string((const char*)data.first);
781 /* Build the reply with the id and hostname/ip in it */
782 std::string ro = req->orig;
784 return DNSResult(this_id,resultstr,ttl,ro);
788 /** A result is ready, process it */
789 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
797 /* This is just to keep _FORTIFY_SOURCE happy */
798 rr.type = DNS_QUERY_NONE;
800 rr.ttl = 1; /* GCC is a whiney bastard -- see the XXX below. */
802 if (!(header.flags1 & FLAGS_MASK_QR))
803 return std::make_pair((unsigned char*)NULL,"Not a query result");
805 if (header.flags1 & FLAGS_MASK_OPCODE)
806 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
808 if (header.flags2 & FLAGS_MASK_RCODE)
809 return std::make_pair((unsigned char*)NULL,"Domain name not found");
811 if (header.ancount < 1)
812 return std::make_pair((unsigned char*)NULL,"No resource records returned");
814 /* Subtract the length of the header from the length of the packet */
817 while ((unsigned int)q < header.qdcount && i < length)
819 if (header.payload[i] > 63)
826 if (header.payload[i] == 0)
831 else i += header.payload[i] + 1;
835 while ((unsigned)curanswer < header.ancount)
838 while (q == 0 && i < length)
840 if (header.payload[i] > 63)
847 if (header.payload[i] == 0)
852 else i += header.payload[i] + 1; /* skip length and label */
856 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
858 /* XXX: We actually initialise 'rr' here including its ttl field */
859 DNS::FillResourceRecord(&rr,&header.payload[i]);
861 if (rr.type != this->type)
867 if (rr.rr_class != this->rr_class)
875 if ((unsigned int)curanswer == header.ancount)
876 return std::make_pair((unsigned char*)NULL,"No valid answers");
878 if (i + rr.rdlength > (unsigned int)length)
879 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
881 if (rr.rdlength > 1023)
882 return std::make_pair((unsigned char*)NULL,"Resource record too large");
888 case DNS_QUERY_CNAME:
889 /* CNAME and PTR have the same processing code */
893 while (q == 0 && i < length && o + 256 < 1023)
895 if (header.payload[i] > 63)
897 memcpy(&ptr,&header.payload[i],2);
898 i = ntohs(ptr) - 0xC000 - 12;
902 if (header.payload[i] == 0)
911 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
912 o += header.payload[i];
913 i += header.payload[i] + 1;
920 memcpy(res,&header.payload[i],rr.rdlength);
921 res[rr.rdlength] = 0;
924 memcpy(res,&header.payload[i],rr.rdlength);
925 res[rr.rdlength] = 0;
928 memcpy(res,&header.payload[i],rr.rdlength);
929 res[rr.rdlength] = 0;
932 return std::make_pair(res,"No error");;
935 /** Close the master socket */
938 shutdown(this->GetFd(), 2);
939 close(this->GetFd());
940 ServerInstance->Timers->DelTimer(this->PruneTimer);
941 delete this->PruneTimer;
944 CachedQuery* DNS::GetCache(const std::string &source)
946 dnscache::iterator x = cache->find(source.c_str());
947 if (x != cache->end())
953 void DNS::DelCache(const std::string &source)
955 cache->erase(source.c_str());
958 void Resolver::TriggerCachedResult()
961 OnLookupComplete(CQ->data, time_left, true);
964 /** High level abstraction of dns used by application at large */
965 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
969 CQ = ServerInstance->Res->GetCache(source);
972 time_left = CQ->CalcTTLRemaining();
975 ServerInstance->Res->DelCache(source);
989 this->myid = ServerInstance->Res->GetIP(source.c_str());
993 if (insp_aton(source.c_str(), &binip) > 0)
995 /* Valid ip address */
996 this->myid = ServerInstance->Res->GetName(&binip);
1000 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
1001 throw ModuleException("Resolver: Bad IP address");
1006 case DNS_QUERY_PTR4:
1007 querytype = DNS_QUERY_PTR;
1008 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
1011 case DNS_QUERY_PTR6:
1012 querytype = DNS_QUERY_PTR;
1013 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
1016 case DNS_QUERY_AAAA:
1017 this->myid = ServerInstance->Res->GetIP6(source.c_str());
1020 case DNS_QUERY_CNAME:
1021 this->myid = ServerInstance->Res->GetCName(source.c_str());
1028 if (this->myid == -1)
1030 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1031 throw ModuleException("Resolver: Couldnt get an id to make a request");
1032 /* We shouldnt get here really */
1037 /** Called when an error occurs */
1038 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1040 /* Nothing in here */
1043 /** Destroy a resolver */
1044 Resolver::~Resolver()
1046 /* Nothing here (yet) either */
1049 /** Get the request id associated with this class */
1050 int Resolver::GetId()
1055 Module* Resolver::GetCreator()
1057 return this->Creator;
1060 /** Process a socket read event */
1061 void DNS::HandleEvent(EventType et, int errornum)
1063 ServerInstance->Log(DEBUG,"Marshall dns reads");
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) - s->BoundPortCount;