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) */
98 DNSRequest(InspIRCd* Instance, DNS* dns, insp_inaddr server, int id);
100 DNSInfo ResultIsReady(DNSHeader &h, int length);
101 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
104 class RequestTimeout : public InspTimer
106 InspIRCd* ServerInstance;
110 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
112 ServerInstance->Log(DEBUG, "New DNS timeout set on %08x", watching);
115 void Tick(time_t TIME)
117 if (ServerInstance->Res->requests[watchid] == watch)
119 /* Still exists, whack it */
120 if (ServerInstance->Res->Classes[watchid])
122 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
123 delete ServerInstance->Res->Classes[watchid];
124 ServerInstance->Res->Classes[watchid] = NULL;
126 ServerInstance->Res->requests[watchid] = NULL;
128 ServerInstance->Log(DEBUG, "DNS timeout on %08x squished pointer", watch);
131 ServerInstance->Log(DEBUG, "DNS timeout on %08x: result already received!", watch);
135 /* Allocate the processing buffer */
136 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, insp_inaddr server, int id) : dnsobj(dns)
138 res = new unsigned char[512];
140 memcpy(&myserver, &server, sizeof(insp_inaddr));
141 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
142 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
145 /* Deallocate the processing buffer */
146 DNSRequest::~DNSRequest()
151 /** Fill a ResourceRecord class based on raw data input */
152 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
154 rr->type = (QueryType)((input[0] << 8) + input[1]);
155 rr->rr_class = (input[2] << 8) + input[3];
156 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
157 rr->rdlength = (input[8] << 8) + input[9];
160 /** Fill a DNSHeader class based on raw data input of a given length */
161 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
163 header->id[0] = input[0];
164 header->id[1] = input[1];
165 header->flags1 = input[2];
166 header->flags2 = input[3];
167 header->qdcount = (input[4] << 8) + input[5];
168 header->ancount = (input[6] << 8) + input[7];
169 header->nscount = (input[8] << 8) + input[9];
170 header->arcount = (input[10] << 8) + input[11];
171 memcpy(header->payload,&input[12],length);
174 /** Empty a DNSHeader class out into raw data, ready for transmission */
175 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
177 output[0] = header->id[0];
178 output[1] = header->id[1];
179 output[2] = header->flags1;
180 output[3] = header->flags2;
181 output[4] = header->qdcount >> 8;
182 output[5] = header->qdcount & 0xFF;
183 output[6] = header->ancount >> 8;
184 output[7] = header->ancount & 0xFF;
185 output[8] = header->nscount >> 8;
186 output[9] = header->nscount & 0xFF;
187 output[10] = header->arcount >> 8;
188 output[11] = header->arcount & 0xFF;
189 memcpy(&output[12],header->payload,length);
192 /** Send requests we have previously built down the UDP socket */
193 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
196 unsigned char payload[sizeof(DNSHeader)];
201 DNS::EmptyHeader(payload,header,length);
203 memset(&addr,0,sizeof(addr));
205 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
206 addr.sin6_family = AF_FAMILY;
207 addr.sin6_port = htons(DNS::QUERY_PORT);
209 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
210 addr.sin_family = AF_FAMILY;
211 addr.sin_port = htons(DNS::QUERY_PORT);
213 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
219 /** Add a query with a predefined header, and allocate an ID for it. */
220 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id)
222 /* Is the DNS connection down? */
223 if (this->GetFd() == -1)
227 id = this->PRNG() & DNS::MAX_REQUEST_ID;
229 /* If this id is already 'in flight', pick another. */
231 id = this->PRNG() & DNS::MAX_REQUEST_ID;
233 DNSRequest* req = new DNSRequest(ServerInstance, this, this->myserver, id);
235 header->id[0] = req->id[0] = id >> 8;
236 header->id[1] = req->id[1] = id & 0xFF;
237 header->flags1 = FLAGS_MASK_RD;
244 /* At this point we already know the id doesnt exist,
245 * so there needs to be no second check for the ::end()
249 /* According to the C++ spec, new never returns NULL. */
258 if (this->GetFd() > -1)
260 shutdown(this->GetFd(), 2);
261 close(this->GetFd());
265 if (insp_aton(ServerInstance->Config->DNSServer,&addr) > 0)
267 memcpy(&myserver,&addr,sizeof(insp_inaddr));
268 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
270 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
271 ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
272 ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
273 this->ip6munge = true;
275 ServerInstance->Log(DEBUG,"Added nameserver '%s'",ServerInstance->Config->DNSServer);
279 ServerInstance->Log(DEBUG,"GACK! insp_aton says the nameserver '%s' is invalid!",ServerInstance->Config->DNSServer);
282 /* Initialize mastersocket */
283 this->SetFd(socket(PF_PROTOCOL, SOCK_DGRAM, 0));
284 if (this->GetFd() != -1)
286 /* Did it succeed? */
287 if (fcntl(this->GetFd(), F_SETFL, O_NONBLOCK) != 0)
289 /* Couldn't make the socket nonblocking */
290 shutdown(this->GetFd(),2);
291 close(this->GetFd());
297 ServerInstance->Log(DEBUG,"I cant socket() this socket! (%s)",strerror(errno));
300 /* Have we got a socket and is it nonblocking? */
301 if (this->GetFd() != -1)
305 memset(&addr,0,sizeof(addr));
306 addr.sin6_family = AF_FAMILY;
308 addr.sin6_addr = in6addr_any;
311 memset(&addr,0,sizeof(addr));
312 addr.sin_family = AF_FAMILY;
314 addr.sin_addr.s_addr = INADDR_ANY;
317 if (bind(this->GetFd(),(sockaddr *)&addr,sizeof(addr)) != 0)
320 ServerInstance->Log(DEBUG,"Cant bind DNS fd");
321 shutdown(this->GetFd(),2);
322 close(this->GetFd());
326 if (this->GetFd() >= 0)
328 ServerInstance->Log(DEBUG,"Add master socket %d",this->GetFd());
329 /* Hook the descriptor into the socket engine */
330 if (ServerInstance && ServerInstance->SE)
332 if (!ServerInstance->SE->AddFd(this))
334 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
335 shutdown(this->GetFd(),2);
336 close(this->GetFd());
344 /** Initialise the DNS UDP socket so that we can send requests */
345 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
347 ServerInstance->Log(DEBUG,"DNS::DNS: Instance = %08x",Instance);
349 /* Clear the Resolver class table */
350 memset(Classes,0,sizeof(Classes));
352 /* Clear the requests class table */
353 memset(requests,0,sizeof(requests));
355 /* Set the id of the next request to 0
364 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
365 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
367 short payloadpos = 0;
368 const char* tempchr, *tempchr2 = name;
369 unsigned short length;
371 /* split name up into labels, create query */
372 while ((tempchr = strchr(tempchr2,'.')) != NULL)
374 length = tempchr - tempchr2;
375 if (payloadpos + length + 1 > 507)
377 payload[payloadpos++] = length;
378 memcpy(&payload[payloadpos],tempchr2,length);
379 payloadpos += length;
380 tempchr2 = &tempchr[1];
382 length = strlen(tempchr2);
385 if (payloadpos + length + 2 > 507)
387 payload[payloadpos++] = length;
388 memcpy(&payload[payloadpos],tempchr2,length);
389 payloadpos += length;
390 payload[payloadpos++] = 0;
392 if (payloadpos > 508)
395 memcpy(&payload[payloadpos],&length,2);
396 length = htons(rr_class);
397 memcpy(&payload[payloadpos + 2],&length,2);
398 return payloadpos + 4;
401 /** Start lookup of an hostname to an IP address */
402 int DNS::GetIP(const char *name)
408 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
411 DNSRequest* req = this->AddQuery(&h, id);
413 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
419 /** Start lookup of an hostname to an IPv6 address */
420 int DNS::GetIP6(const char *name)
426 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
429 DNSRequest* req = this->AddQuery(&h, id);
431 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
437 /** Start lookup of a cname to another name */
438 int DNS::GetCName(const char *alias)
444 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
447 DNSRequest* req = this->AddQuery(&h, id);
449 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
455 /** Start lookup of an IP address to a hostname */
456 int DNS::GetName(const insp_inaddr *ip)
464 unsigned char* c = (unsigned char*)&ip->s6_addr;
465 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
466 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
467 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
468 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
470 DNS::MakeIP6Int(query, (in6_addr*)ip);
472 unsigned char* c = (unsigned char*)&ip->s_addr;
473 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
476 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
479 DNSRequest* req = this->AddQuery(&h, id);
481 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
487 /** Start lookup of an IP address to a hostname */
488 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
494 #ifdef SUPPORT_IP6LINKS
495 if (fp == PROTOCOL_IPV6)
498 if (inet_pton(AF_INET6, ip, &i) > 0)
500 DNS::MakeIP6Int(query, &i);
503 /* Invalid IP address */
510 if (inet_aton(ip, &i))
512 unsigned char* c = (unsigned char*)&i.s_addr;
513 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
516 /* Invalid IP address */
520 ServerInstance->Log(DEBUG,"DNS::GetNameForce: %s %d",query, fp);
522 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
525 DNSRequest* req = this->AddQuery(&h, id);
527 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
533 /** Build an ipv6 reverse domain from an in6_addr
535 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
537 #ifdef SUPPORT_IP6LINKS
538 const char* hex = "0123456789abcdef";
539 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
543 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
546 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
547 *query++ = '.'; /* Seperator */
549 strcpy(query,"ip6.arpa"); /* Suffix the string */
555 /** Return the next id which is ready, and the result attached to it */
556 DNSResult DNS::GetResult()
558 /* Fetch dns query response and decide where it belongs */
561 unsigned char buffer[sizeof(DNSHeader)];
563 socklen_t x = sizeof(from);
564 const char* ipaddr_from = "";
565 unsigned short int port_from = 0;
567 int length = recvfrom(this->GetFd(),buffer,sizeof(DNSHeader),0,&from,&x);
570 ServerInstance->Log(DEBUG,"Error in recvfrom()! (%s)",strerror(errno));
572 /* Did we get the whole header? */
575 /* Nope - something screwed up. */
576 ServerInstance->Log(DEBUG,"Whole header not read!");
577 return std::make_pair(-1,"");
580 /* Check wether the reply came from a different DNS
581 * server to the one we sent it to, or the source-port
583 * A user could in theory still spoof dns packets anyway
584 * but this is less trivial than just sending garbage
585 * to the client, which is possible without this check.
587 * -- Thanks jilles for pointing this one out.
590 ipaddr_from = insp_ntoa(((sockaddr_in6*)&from)->sin6_addr);
591 port_from = ntohs(((sockaddr_in6*)&from)->sin6_port);
593 ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
594 port_from = ntohs(((sockaddr_in*)&from)->sin_port);
597 /* We cant perform this security check if you're using 4in6.
598 * Tough luck to you, choose one or't other!
602 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
604 ServerInstance->Log(DEBUG,"port %d is not 53, or %s is not %s",port_from, ipaddr_from, ServerInstance->Config->DNSServer);
605 return std::make_pair(-1,"");
609 /* Put the read header info into a header class */
610 DNS::FillHeader(&header,buffer,length - 12);
612 /* Get the id of this request.
613 * Its a 16 bit value stored in two char's,
614 * so we use logic shifts to create the value.
616 unsigned long this_id = header.id[1] + (header.id[0] << 8);
618 /* Do we have a pending request matching this id? */
619 if (!requests[this_id])
621 /* Somehow we got a DNS response for a request we never made... */
622 ServerInstance->Log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",this->GetFd(),this_id);
623 return std::make_pair(-1,"");
627 /* Remove the query from the list of pending queries */
628 req = requests[this_id];
629 requests[this_id] = NULL;
632 /* Inform the DNSRequest class that it has a result to be read.
633 * When its finished it will return a DNSInfo which is a pair of
634 * unsigned char* resource record data, and an error message.
636 DNSInfo data = req->ResultIsReady(header, length);
637 std::string resultstr;
639 /* Check if we got a result, if we didnt, its an error */
640 if (data.first == NULL)
643 * Mask the ID with the value of ERROR_MASK, so that
644 * the dns_deal_with_classes() function knows that its
645 * an error response and needs to be treated uniquely.
646 * Put the error message in the second field.
649 return std::make_pair(this_id | ERROR_MASK, data.second);
655 /* Forward lookups come back as binary data. We must format them into ascii */
659 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
660 resultstr = formatted;
665 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
666 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
667 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
668 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
669 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
670 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
671 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
672 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
673 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
674 char* c = strstr(formatted,":0:");
677 memmove(c+1,c+2,strlen(c+2) + 1);
679 while (memcmp(c,"0:",2) == 0)
680 memmove(c,c+2,strlen(c+2) + 1);
681 if (memcmp(c,"0",2) == 0)
683 if (memcmp(formatted,"0::",3) == 0)
684 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
686 resultstr = formatted;
688 /* Special case. Sending ::1 around between servers
689 * and to clients is dangerous, because the : on the
690 * start makes the client or server interpret the IP
691 * as the last parameter on the line with a value ":1".
693 if (*formatted == ':')
694 resultstr = "0" + resultstr;
698 case DNS_QUERY_CNAME:
699 /* Identical handling to PTR */
702 /* Reverse lookups just come back as char* */
703 resultstr = std::string((const char*)data.first);
707 ServerInstance->Log(DEBUG,"WARNING: Somehow we made a request for a DNS_QUERY_PTR4 or DNS_QUERY_PTR6, but these arent real rr types!");
712 /* Build the reply with the id and hostname/ip in it */
714 return std::make_pair(this_id,resultstr);
718 /** A result is ready, process it */
719 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
727 /* This is just to keep _FORTIFY_SOURCE happy */
728 rr.type = DNS_QUERY_NONE;
731 if (!(header.flags1 & FLAGS_MASK_QR))
732 return std::make_pair((unsigned char*)NULL,"Not a query result");
734 if (header.flags1 & FLAGS_MASK_OPCODE)
735 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
737 if (header.flags2 & FLAGS_MASK_RCODE)
738 return std::make_pair((unsigned char*)NULL,"Domain name not found");
740 if (header.ancount < 1)
741 return std::make_pair((unsigned char*)NULL,"No resource records returned");
743 /* Subtract the length of the header from the length of the packet */
746 while ((unsigned int)q < header.qdcount && i < length)
748 if (header.payload[i] > 63)
755 if (header.payload[i] == 0)
760 else i += header.payload[i] + 1;
764 while ((unsigned)curanswer < header.ancount)
767 while (q == 0 && i < length)
769 if (header.payload[i] > 63)
776 if (header.payload[i] == 0)
781 else i += header.payload[i] + 1; /* skip length and label */
785 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
787 DNS::FillResourceRecord(&rr,&header.payload[i]);
789 if (rr.type != this->type)
795 if (rr.rr_class != this->rr_class)
803 if ((unsigned int)curanswer == header.ancount)
804 return std::make_pair((unsigned char*)NULL,"No valid answers");
806 if (i + rr.rdlength > (unsigned int)length)
807 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
809 if (rr.rdlength > 1023)
810 return std::make_pair((unsigned char*)NULL,"Resource record too large");
814 case DNS_QUERY_CNAME:
815 /* CNAME and PTR have the same processing code */
819 while (q == 0 && i < length && o + 256 < 1023)
821 if (header.payload[i] > 63)
823 memcpy(&ptr,&header.payload[i],2);
824 i = ntohs(ptr) - 0xC000 - 12;
828 if (header.payload[i] == 0)
837 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
838 o += header.payload[i];
839 i += header.payload[i] + 1;
846 memcpy(res,&header.payload[i],rr.rdlength);
847 res[rr.rdlength] = 0;
850 memcpy(res,&header.payload[i],rr.rdlength);
851 res[rr.rdlength] = 0;
854 memcpy(res,&header.payload[i],rr.rdlength);
855 res[rr.rdlength] = 0;
858 return std::make_pair(res,"No error");;
861 /** Close the master socket */
864 shutdown(this->GetFd(), 2);
865 close(this->GetFd());
868 /** High level abstraction of dns used by application at large */
869 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
871 ServerInstance->Log(DEBUG,"Instance: %08x %08x",Instance, ServerInstance);
878 this->myid = ServerInstance->Res->GetIP(source.c_str());
882 if (insp_aton(source.c_str(), &binip) > 0)
884 /* Valid ip address */
885 this->myid = ServerInstance->Res->GetName(&binip);
889 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
890 throw ModuleException("Resolver: Bad IP address");
896 querytype = DNS_QUERY_PTR;
897 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
901 querytype = DNS_QUERY_PTR;
902 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
906 this->myid = ServerInstance->Res->GetIP6(source.c_str());
909 case DNS_QUERY_CNAME:
910 this->myid = ServerInstance->Res->GetCName(source.c_str());
917 if (this->myid == -1)
919 ServerInstance->Log(DEBUG,"Resolver::Resolver: Could not get an id!");
920 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
921 throw ModuleException("Resolver: Couldnt get an id to make a request");
922 /* We shouldnt get here really */
926 ServerInstance->Log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
929 /** Called when an error occurs */
930 void Resolver::OnError(ResolverError e, const std::string &errormessage)
932 /* Nothing in here */
935 /** Destroy a resolver */
936 Resolver::~Resolver()
938 /* Nothing here (yet) either */
941 /** Get the request id associated with this class */
942 int Resolver::GetId()
947 Module* Resolver::GetCreator()
949 return this->Creator;
952 /** Process a socket read event */
953 void DNS::HandleEvent(EventType et, int errornum)
955 /* Fetch the id and result of the next available packet */
956 DNSResult res = this->GetResult();
957 /* Is there a usable request id? */
960 /* Its an error reply */
961 if (res.first & ERROR_MASK)
963 /* Mask off the error bit */
964 res.first -= ERROR_MASK;
965 /* Marshall the error to the correct class */
966 ServerInstance->Log(DEBUG,"Error available, id=%d",res.first);
967 if (Classes[res.first])
969 if (ServerInstance && ServerInstance->stats)
970 ServerInstance->stats->statsDnsBad++;
971 Classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
972 delete Classes[res.first];
973 Classes[res.first] = NULL;
978 /* It is a non-error result */
979 ServerInstance->Log(DEBUG,"Result available, id=%d",res.first);
980 /* Marshall the result to the correct class */
981 if (Classes[res.first])
983 if (ServerInstance && ServerInstance->stats)
984 ServerInstance->stats->statsDnsGood++;
985 Classes[res.first]->OnLookupComplete(res.second);
986 delete Classes[res.first];
987 Classes[res.first] = NULL;
991 if (ServerInstance && ServerInstance->stats)
992 ServerInstance->stats->statsDns++;
996 /** Add a derived Resolver to the working set */
997 bool DNS::AddResolverClass(Resolver* r)
999 /* Check the pointers validity and the id's validity */
1000 if ((r) && (r->GetId() > -1))
1002 /* Check the slot isnt already occupied -
1003 * This should NEVER happen unless we have
1004 * a severely broken DNS server somewhere
1006 if (!Classes[r->GetId()])
1008 /* Set up the pointer to the class */
1009 Classes[r->GetId()] = r;
1018 /* Pointer or id not valid.
1019 * Free the item and return
1028 void DNS::CleanResolvers(Module* module)
1030 for (int i = 0; i < MAX_REQUEST_ID; i++)
1034 if (Classes[i]->GetCreator() == module)
1036 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1044 /** Generate pseudo-random number */
1045 unsigned long DNS::PRNG()
1047 unsigned long val = 0;
1049 serverstats* s = ServerInstance->stats;
1050 gettimeofday(&n,NULL);
1051 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1052 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1053 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - s->BoundPortCount;