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 CacheTimer : public InspTimer
109 InspIRCd* ServerInstance;
112 CacheTimer(InspIRCd* Instance, DNS* thisdns)
113 : InspTimer(3600, Instance->Time(), true), ServerInstance(Instance), dns(thisdns) { }
115 virtual void Tick(time_t TIME)
121 class RequestTimeout : public InspTimer
123 InspIRCd* ServerInstance;
127 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
129 ServerInstance->Log(DEBUG, "New DNS timeout set on %08x", watching);
132 void Tick(time_t TIME)
134 if (ServerInstance->Res->requests[watchid] == watch)
136 /* Still exists, whack it */
137 if (ServerInstance->Res->Classes[watchid])
139 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
140 delete ServerInstance->Res->Classes[watchid];
141 ServerInstance->Res->Classes[watchid] = NULL;
143 ServerInstance->Res->requests[watchid] = NULL;
145 ServerInstance->Log(DEBUG, "DNS timeout on %08x squished pointer", watch);
148 ServerInstance->Log(DEBUG, "DNS timeout on %08x: result already received!", watch);
152 /* Allocate the processing buffer */
153 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, insp_inaddr server, int id, const std::string &original) : dnsobj(dns)
155 res = new unsigned char[512];
157 memcpy(&myserver, &server, sizeof(insp_inaddr));
159 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
160 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
163 /* Deallocate the processing buffer */
164 DNSRequest::~DNSRequest()
169 /** Fill a ResourceRecord class based on raw data input */
170 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
172 rr->type = (QueryType)((input[0] << 8) + input[1]);
173 rr->rr_class = (input[2] << 8) + input[3];
174 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
175 rr->rdlength = (input[8] << 8) + input[9];
178 /** Fill a DNSHeader class based on raw data input of a given length */
179 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
181 header->id[0] = input[0];
182 header->id[1] = input[1];
183 header->flags1 = input[2];
184 header->flags2 = input[3];
185 header->qdcount = (input[4] << 8) + input[5];
186 header->ancount = (input[6] << 8) + input[7];
187 header->nscount = (input[8] << 8) + input[9];
188 header->arcount = (input[10] << 8) + input[11];
189 memcpy(header->payload,&input[12],length);
192 /** Empty a DNSHeader class out into raw data, ready for transmission */
193 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
195 output[0] = header->id[0];
196 output[1] = header->id[1];
197 output[2] = header->flags1;
198 output[3] = header->flags2;
199 output[4] = header->qdcount >> 8;
200 output[5] = header->qdcount & 0xFF;
201 output[6] = header->ancount >> 8;
202 output[7] = header->ancount & 0xFF;
203 output[8] = header->nscount >> 8;
204 output[9] = header->nscount & 0xFF;
205 output[10] = header->arcount >> 8;
206 output[11] = header->arcount & 0xFF;
207 memcpy(&output[12],header->payload,length);
210 /** Send requests we have previously built down the UDP socket */
211 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
214 unsigned char payload[sizeof(DNSHeader)];
219 DNS::EmptyHeader(payload,header,length);
221 memset(&addr,0,sizeof(addr));
223 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
224 addr.sin6_family = AF_FAMILY;
225 addr.sin6_port = htons(DNS::QUERY_PORT);
227 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
228 addr.sin_family = AF_FAMILY;
229 addr.sin_port = htons(DNS::QUERY_PORT);
231 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
237 /** Add a query with a predefined header, and allocate an ID for it. */
238 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id, const char* original)
240 /* Is the DNS connection down? */
241 if (this->GetFd() == -1)
245 id = this->PRNG() & DNS::MAX_REQUEST_ID;
247 /* If this id is already 'in flight', pick another. */
249 id = this->PRNG() & DNS::MAX_REQUEST_ID;
251 DNSRequest* req = new DNSRequest(ServerInstance, this, this->myserver, id, original);
253 header->id[0] = req->id[0] = id >> 8;
254 header->id[1] = req->id[1] = id & 0xFF;
255 header->flags1 = FLAGS_MASK_RD;
262 /* At this point we already know the id doesnt exist,
263 * so there needs to be no second check for the ::end()
267 /* According to the C++ spec, new never returns NULL. */
271 int DNS::ClearCache()
273 /* This ensures the buckets are reset to sane levels */
274 int rv = this->cache->size();
276 this->cache = new dnscache();
280 int DNS::PruneCache()
283 dnscache* newcache = new dnscache();
284 for (dnscache::iterator i = this->cache->begin(); i != this->cache->end(); i++)
285 /* Dont include expired items (theres no point) */
286 if (i->second.CalcTTLRemaining())
287 newcache->insert(*i);
292 this->cache = newcache;
293 ServerInstance->Log(DEBUG,"Prune %d expired cache items", n);
302 if (this->GetFd() > -1)
304 if (ServerInstance && ServerInstance->SE)
305 ServerInstance->SE->DelFd(this);
306 shutdown(this->GetFd(), 2);
307 close(this->GetFd());
310 /* Rehash the cache */
315 /* Create initial dns cache */
316 this->cache = new dnscache();
319 if (insp_aton(ServerInstance->Config->DNSServer,&addr) > 0)
321 memcpy(&myserver,&addr,sizeof(insp_inaddr));
322 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
324 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
325 ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
326 ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
327 this->ip6munge = true;
329 ServerInstance->Log(DEBUG,"Added nameserver '%s'",ServerInstance->Config->DNSServer);
333 ServerInstance->Log(DEBUG,"GACK! insp_aton says the nameserver '%s' is invalid!",ServerInstance->Config->DNSServer);
336 /* Initialize mastersocket */
337 this->SetFd(socket(PF_PROTOCOL, SOCK_DGRAM, 0));
338 if (this->GetFd() != -1)
340 /* Did it succeed? */
341 if (fcntl(this->GetFd(), F_SETFL, O_NONBLOCK) != 0)
343 /* Couldn't make the socket nonblocking */
344 shutdown(this->GetFd(),2);
345 close(this->GetFd());
351 ServerInstance->Log(DEBUG,"I cant socket() this socket! (%s)",strerror(errno));
354 /* Have we got a socket and is it nonblocking? */
355 if (this->GetFd() != -1)
359 memset(&addr,0,sizeof(addr));
360 addr.sin6_family = AF_FAMILY;
362 addr.sin6_addr = in6addr_any;
365 memset(&addr,0,sizeof(addr));
366 addr.sin_family = AF_FAMILY;
368 addr.sin_addr.s_addr = INADDR_ANY;
371 if (bind(this->GetFd(),(sockaddr *)&addr,sizeof(addr)) != 0)
374 ServerInstance->Log(DEBUG,"Cant bind DNS fd");
375 shutdown(this->GetFd(),2);
376 close(this->GetFd());
380 if (this->GetFd() >= 0)
382 ServerInstance->Log(DEBUG,"Add master socket %d",this->GetFd());
383 /* Hook the descriptor into the socket engine */
384 if (ServerInstance && ServerInstance->SE)
386 if (!ServerInstance->SE->AddFd(this))
388 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
389 shutdown(this->GetFd(),2);
390 close(this->GetFd());
398 /** Initialise the DNS UDP socket so that we can send requests */
399 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
401 ServerInstance->Log(DEBUG,"DNS::DNS: Instance = %08x",Instance);
403 /* Clear the Resolver class table */
404 memset(Classes,0,sizeof(Classes));
406 /* Clear the requests class table */
407 memset(requests,0,sizeof(requests));
409 /* Set the id of the next request to 0
413 /* DNS::Rehash() sets this to a valid ptr
417 /* Again, DNS::Rehash() sets this to a
422 /* Actually read the settings
426 this->PruneTimer = new CacheTimer(ServerInstance, this);
428 ServerInstance->Timers->AddTimer(this->PruneTimer);
431 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
432 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
434 short payloadpos = 0;
435 const char* tempchr, *tempchr2 = name;
436 unsigned short length;
438 /* split name up into labels, create query */
439 while ((tempchr = strchr(tempchr2,'.')) != NULL)
441 length = tempchr - tempchr2;
442 if (payloadpos + length + 1 > 507)
444 payload[payloadpos++] = length;
445 memcpy(&payload[payloadpos],tempchr2,length);
446 payloadpos += length;
447 tempchr2 = &tempchr[1];
449 length = strlen(tempchr2);
452 if (payloadpos + length + 2 > 507)
454 payload[payloadpos++] = length;
455 memcpy(&payload[payloadpos],tempchr2,length);
456 payloadpos += length;
457 payload[payloadpos++] = 0;
459 if (payloadpos > 508)
462 memcpy(&payload[payloadpos],&length,2);
463 length = htons(rr_class);
464 memcpy(&payload[payloadpos + 2],&length,2);
465 return payloadpos + 4;
468 /** Start lookup of an hostname to an IP address */
469 int DNS::GetIP(const char *name)
475 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
478 DNSRequest* req = this->AddQuery(&h, id, name);
480 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
486 /** Start lookup of an hostname to an IPv6 address */
487 int DNS::GetIP6(const char *name)
493 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
496 DNSRequest* req = this->AddQuery(&h, id, name);
498 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
504 /** Start lookup of a cname to another name */
505 int DNS::GetCName(const char *alias)
511 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
514 DNSRequest* req = this->AddQuery(&h, id, alias);
516 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
522 /** Start lookup of an IP address to a hostname */
523 int DNS::GetName(const insp_inaddr *ip)
531 unsigned char* c = (unsigned char*)&ip->s6_addr;
532 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
533 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
534 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
535 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
537 DNS::MakeIP6Int(query, (in6_addr*)ip);
539 unsigned char* c = (unsigned char*)&ip->s_addr;
540 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
543 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
546 DNSRequest* req = this->AddQuery(&h, id, insp_ntoa(*ip));
548 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
554 /** Start lookup of an IP address to a hostname */
555 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
561 #ifdef SUPPORT_IP6LINKS
562 if (fp == PROTOCOL_IPV6)
565 if (inet_pton(AF_INET6, ip, &i) > 0)
567 DNS::MakeIP6Int(query, &i);
570 /* Invalid IP address */
577 if (inet_aton(ip, &i))
579 unsigned char* c = (unsigned char*)&i.s_addr;
580 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
583 /* Invalid IP address */
587 ServerInstance->Log(DEBUG,"DNS::GetNameForce: %s %d",query, fp);
589 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
592 DNSRequest* req = this->AddQuery(&h, id, ip);
594 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
600 /** Build an ipv6 reverse domain from an in6_addr
602 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
604 #ifdef SUPPORT_IP6LINKS
605 const char* hex = "0123456789abcdef";
606 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
610 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
613 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
614 *query++ = '.'; /* Seperator */
616 strcpy(query,"ip6.arpa"); /* Suffix the string */
622 /** Return the next id which is ready, and the result attached to it */
623 DNSResult DNS::GetResult()
625 /* Fetch dns query response and decide where it belongs */
628 unsigned char buffer[sizeof(DNSHeader)];
630 socklen_t x = sizeof(from);
631 const char* ipaddr_from = "";
632 unsigned short int port_from = 0;
634 int length = recvfrom(this->GetFd(),buffer,sizeof(DNSHeader),0,&from,&x);
637 ServerInstance->Log(DEBUG,"Error in recvfrom()! (%s)",strerror(errno));
639 /* Did we get the whole header? */
642 /* Nope - something screwed up. */
643 ServerInstance->Log(DEBUG,"Whole header not read!");
644 return DNSResult(-1,"",0,"");
647 /* Check wether the reply came from a different DNS
648 * server to the one we sent it to, or the source-port
650 * A user could in theory still spoof dns packets anyway
651 * but this is less trivial than just sending garbage
652 * to the client, which is possible without this check.
654 * -- Thanks jilles for pointing this one out.
657 ipaddr_from = insp_ntoa(((sockaddr_in6*)&from)->sin6_addr);
658 port_from = ntohs(((sockaddr_in6*)&from)->sin6_port);
660 ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
661 port_from = ntohs(((sockaddr_in*)&from)->sin_port);
664 /* We cant perform this security check if you're using 4in6.
665 * Tough luck to you, choose one or't other!
669 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
671 ServerInstance->Log(DEBUG,"port %d is not 53, or %s is not %s",port_from, ipaddr_from, ServerInstance->Config->DNSServer);
672 return DNSResult(-1,"",0,"");
676 /* Put the read header info into a header class */
677 DNS::FillHeader(&header,buffer,length - 12);
679 /* Get the id of this request.
680 * Its a 16 bit value stored in two char's,
681 * so we use logic shifts to create the value.
683 unsigned long this_id = header.id[1] + (header.id[0] << 8);
685 /* Do we have a pending request matching this id? */
686 if (!requests[this_id])
688 /* Somehow we got a DNS response for a request we never made... */
689 ServerInstance->Log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",this->GetFd(),this_id);
690 return DNSResult(-1,"",0,"");
694 /* Remove the query from the list of pending queries */
695 req = requests[this_id];
696 requests[this_id] = NULL;
699 /* Inform the DNSRequest class that it has a result to be read.
700 * When its finished it will return a DNSInfo which is a pair of
701 * unsigned char* resource record data, and an error message.
703 DNSInfo data = req->ResultIsReady(header, length);
704 std::string resultstr;
706 /* Check if we got a result, if we didnt, its an error */
707 if (data.first == NULL)
710 * Mask the ID with the value of ERROR_MASK, so that
711 * the dns_deal_with_classes() function knows that its
712 * an error response and needs to be treated uniquely.
713 * Put the error message in the second field.
715 std::string ro = req->orig;
717 return DNSResult(this_id | ERROR_MASK, data.second, 0, ro);
721 unsigned long ttl = req->ttl;
724 /* Forward lookups come back as binary data. We must format them into ascii */
728 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
729 resultstr = formatted;
734 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
735 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
736 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
737 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
738 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
739 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
740 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
741 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
742 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
743 char* c = strstr(formatted,":0:");
746 memmove(c+1,c+2,strlen(c+2) + 1);
748 while (memcmp(c,"0:",2) == 0)
749 memmove(c,c+2,strlen(c+2) + 1);
750 if (memcmp(c,"0",2) == 0)
752 if (memcmp(formatted,"0::",3) == 0)
753 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
755 resultstr = formatted;
757 /* Special case. Sending ::1 around between servers
758 * and to clients is dangerous, because the : on the
759 * start makes the client or server interpret the IP
760 * as the last parameter on the line with a value ":1".
762 if (*formatted == ':')
763 resultstr = "0" + resultstr;
767 case DNS_QUERY_CNAME:
768 /* Identical handling to PTR */
771 /* Reverse lookups just come back as char* */
772 resultstr = std::string((const char*)data.first);
776 ServerInstance->Log(DEBUG,"WARNING: Somehow we made a request for a DNS_QUERY_PTR4 or DNS_QUERY_PTR6, but these arent real rr types!");
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;
801 if (!(header.flags1 & FLAGS_MASK_QR))
802 return std::make_pair((unsigned char*)NULL,"Not a query result");
804 if (header.flags1 & FLAGS_MASK_OPCODE)
805 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
807 if (header.flags2 & FLAGS_MASK_RCODE)
808 return std::make_pair((unsigned char*)NULL,"Domain name not found");
810 if (header.ancount < 1)
811 return std::make_pair((unsigned char*)NULL,"No resource records returned");
813 /* Subtract the length of the header from the length of the packet */
816 while ((unsigned int)q < header.qdcount && i < length)
818 if (header.payload[i] > 63)
825 if (header.payload[i] == 0)
830 else i += header.payload[i] + 1;
834 while ((unsigned)curanswer < header.ancount)
837 while (q == 0 && i < length)
839 if (header.payload[i] > 63)
846 if (header.payload[i] == 0)
851 else i += header.payload[i] + 1; /* skip length and label */
855 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
857 DNS::FillResourceRecord(&rr,&header.payload[i]);
859 if (rr.type != this->type)
865 if (rr.rr_class != this->rr_class)
873 if ((unsigned int)curanswer == header.ancount)
874 return std::make_pair((unsigned char*)NULL,"No valid answers");
876 if (i + rr.rdlength > (unsigned int)length)
877 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
879 if (rr.rdlength > 1023)
880 return std::make_pair((unsigned char*)NULL,"Resource record too large");
886 case DNS_QUERY_CNAME:
887 /* CNAME and PTR have the same processing code */
891 while (q == 0 && i < length && o + 256 < 1023)
893 if (header.payload[i] > 63)
895 memcpy(&ptr,&header.payload[i],2);
896 i = ntohs(ptr) - 0xC000 - 12;
900 if (header.payload[i] == 0)
909 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
910 o += header.payload[i];
911 i += header.payload[i] + 1;
918 memcpy(res,&header.payload[i],rr.rdlength);
919 res[rr.rdlength] = 0;
922 memcpy(res,&header.payload[i],rr.rdlength);
923 res[rr.rdlength] = 0;
926 memcpy(res,&header.payload[i],rr.rdlength);
927 res[rr.rdlength] = 0;
930 return std::make_pair(res,"No error");;
933 /** Close the master socket */
936 shutdown(this->GetFd(), 2);
937 close(this->GetFd());
938 ServerInstance->Timers->DelTimer(this->PruneTimer);
939 delete this->PruneTimer;
942 CachedQuery* DNS::GetCache(const std::string &source)
944 dnscache::iterator x = cache->find(source.c_str());
945 if (x != cache->end())
951 void DNS::DelCache(const std::string &source)
953 cache->erase(source.c_str());
956 void Resolver::TriggerCachedResult()
959 OnLookupComplete(CQ->data, time_left, true);
962 /** High level abstraction of dns used by application at large */
963 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, bool &cached, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
965 ServerInstance->Log(DEBUG,"Instance: %08x %08x",Instance, ServerInstance);
969 CQ = ServerInstance->Res->GetCache(source);
972 time_left = CQ->CalcTTLRemaining();
975 ServerInstance->Log(DEBUG,"Cached but EXPIRED result: %s", CQ->data.c_str());
976 ServerInstance->Res->DelCache(source);
981 ServerInstance->Log(DEBUG,"Cached result: %s", CQ->data.c_str());
991 this->myid = ServerInstance->Res->GetIP(source.c_str());
995 if (insp_aton(source.c_str(), &binip) > 0)
997 /* Valid ip address */
998 this->myid = ServerInstance->Res->GetName(&binip);
1002 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
1003 throw ModuleException("Resolver: Bad IP address");
1008 case DNS_QUERY_PTR4:
1009 querytype = DNS_QUERY_PTR;
1010 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
1013 case DNS_QUERY_PTR6:
1014 querytype = DNS_QUERY_PTR;
1015 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
1018 case DNS_QUERY_AAAA:
1019 this->myid = ServerInstance->Res->GetIP6(source.c_str());
1022 case DNS_QUERY_CNAME:
1023 this->myid = ServerInstance->Res->GetCName(source.c_str());
1030 if (this->myid == -1)
1032 ServerInstance->Log(DEBUG,"Resolver::Resolver: Could not get an id!");
1033 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
1034 throw ModuleException("Resolver: Couldnt get an id to make a request");
1035 /* We shouldnt get here really */
1039 ServerInstance->Log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
1042 /** Called when an error occurs */
1043 void Resolver::OnError(ResolverError e, const std::string &errormessage)
1045 /* Nothing in here */
1048 /** Destroy a resolver */
1049 Resolver::~Resolver()
1051 /* Nothing here (yet) either */
1054 /** Get the request id associated with this class */
1055 int Resolver::GetId()
1060 Module* Resolver::GetCreator()
1062 return this->Creator;
1065 /** Process a socket read event */
1066 void DNS::HandleEvent(EventType et, int errornum)
1068 /* Fetch the id and result of the next available packet */
1069 DNSResult res = this->GetResult();
1070 /* Is there a usable request id? */
1073 /* Its an error reply */
1074 if (res.id & ERROR_MASK)
1076 /* Mask off the error bit */
1077 res.id -= ERROR_MASK;
1078 /* Marshall the error to the correct class */
1079 ServerInstance->Log(DEBUG,"Error available, id=%d",res.id);
1080 if (Classes[res.id])
1082 if (ServerInstance && ServerInstance->stats)
1083 ServerInstance->stats->statsDnsBad++;
1084 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
1085 delete Classes[res.id];
1086 Classes[res.id] = NULL;
1091 /* It is a non-error result */
1092 ServerInstance->Log(DEBUG,"Result available, id=%d",res.id);
1093 /* Marshall the result to the correct class */
1094 if (Classes[res.id])
1096 if (ServerInstance && ServerInstance->stats)
1097 ServerInstance->stats->statsDnsGood++;
1099 if (!this->GetCache(res.original.c_str()))
1101 ServerInstance->Log(DEBUG,"Caching result: %s->%s for %lu secs", res.original.c_str(), res.result.c_str(), res.ttl);
1102 this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
1105 Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
1106 delete Classes[res.id];
1107 Classes[res.id] = NULL;
1111 if (ServerInstance && ServerInstance->stats)
1112 ServerInstance->stats->statsDns++;
1116 /** Add a derived Resolver to the working set */
1117 bool DNS::AddResolverClass(Resolver* r)
1119 /* Check the pointers validity and the id's validity */
1120 if ((r) && (r->GetId() > -1))
1122 /* Check the slot isnt already occupied -
1123 * This should NEVER happen unless we have
1124 * a severely broken DNS server somewhere
1126 if (!Classes[r->GetId()])
1128 /* Set up the pointer to the class */
1129 Classes[r->GetId()] = r;
1138 /* Pointer or id not valid.
1139 * Free the item and return
1148 void DNS::CleanResolvers(Module* module)
1150 for (int i = 0; i < MAX_REQUEST_ID; i++)
1154 if (Classes[i]->GetCreator() == module)
1156 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1164 /** Generate pseudo-random number */
1165 unsigned long DNS::PRNG()
1167 unsigned long val = 0;
1169 serverstats* s = ServerInstance->stats;
1170 gettimeofday(&n,NULL);
1171 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1172 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1173 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - s->BoundPortCount;