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 */
99 DNSRequest(InspIRCd* Instance, DNS* dns, insp_inaddr server, int id);
101 DNSInfo ResultIsReady(DNSHeader &h, int length);
102 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
105 class RequestTimeout : public InspTimer
107 InspIRCd* ServerInstance;
111 RequestTimeout(unsigned long n, InspIRCd* SI, DNSRequest* watching, int id) : InspTimer(n, time(NULL)), ServerInstance(SI), watch(watching), watchid(id)
113 ServerInstance->Log(DEBUG, "New DNS timeout set on %08x", watching);
116 void Tick(time_t TIME)
118 if (ServerInstance->Res->requests[watchid] == watch)
120 /* Still exists, whack it */
121 if (ServerInstance->Res->Classes[watchid])
123 ServerInstance->Res->Classes[watchid]->OnError(RESOLVER_TIMEOUT, "Request timed out");
124 delete ServerInstance->Res->Classes[watchid];
125 ServerInstance->Res->Classes[watchid] = NULL;
127 ServerInstance->Res->requests[watchid] = NULL;
129 ServerInstance->Log(DEBUG, "DNS timeout on %08x squished pointer", watch);
132 ServerInstance->Log(DEBUG, "DNS timeout on %08x: result already received!", watch);
136 /* Allocate the processing buffer */
137 DNSRequest::DNSRequest(InspIRCd* Instance, DNS* dns, insp_inaddr server, int id) : dnsobj(dns)
139 res = new unsigned char[512];
141 memcpy(&myserver, &server, sizeof(insp_inaddr));
142 RequestTimeout* RT = new RequestTimeout(Instance->Config->dns_timeout ? Instance->Config->dns_timeout : 5, Instance, this, id);
143 Instance->Timers->AddTimer(RT); /* The timer manager frees this */
146 /* Deallocate the processing buffer */
147 DNSRequest::~DNSRequest()
152 /** Fill a ResourceRecord class based on raw data input */
153 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
155 rr->type = (QueryType)((input[0] << 8) + input[1]);
156 rr->rr_class = (input[2] << 8) + input[3];
157 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
158 rr->rdlength = (input[8] << 8) + input[9];
161 /** Fill a DNSHeader class based on raw data input of a given length */
162 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
164 header->id[0] = input[0];
165 header->id[1] = input[1];
166 header->flags1 = input[2];
167 header->flags2 = input[3];
168 header->qdcount = (input[4] << 8) + input[5];
169 header->ancount = (input[6] << 8) + input[7];
170 header->nscount = (input[8] << 8) + input[9];
171 header->arcount = (input[10] << 8) + input[11];
172 memcpy(header->payload,&input[12],length);
175 /** Empty a DNSHeader class out into raw data, ready for transmission */
176 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
178 output[0] = header->id[0];
179 output[1] = header->id[1];
180 output[2] = header->flags1;
181 output[3] = header->flags2;
182 output[4] = header->qdcount >> 8;
183 output[5] = header->qdcount & 0xFF;
184 output[6] = header->ancount >> 8;
185 output[7] = header->ancount & 0xFF;
186 output[8] = header->nscount >> 8;
187 output[9] = header->nscount & 0xFF;
188 output[10] = header->arcount >> 8;
189 output[11] = header->arcount & 0xFF;
190 memcpy(&output[12],header->payload,length);
193 /** Send requests we have previously built down the UDP socket */
194 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
197 unsigned char payload[sizeof(DNSHeader)];
202 DNS::EmptyHeader(payload,header,length);
204 memset(&addr,0,sizeof(addr));
206 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
207 addr.sin6_family = AF_FAMILY;
208 addr.sin6_port = htons(DNS::QUERY_PORT);
210 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
211 addr.sin_family = AF_FAMILY;
212 addr.sin_port = htons(DNS::QUERY_PORT);
214 if (sendto(dnsobj->GetFd(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
220 /** Add a query with a predefined header, and allocate an ID for it. */
221 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id)
223 /* Is the DNS connection down? */
224 if (this->GetFd() == -1)
228 id = this->PRNG() & DNS::MAX_REQUEST_ID;
230 /* If this id is already 'in flight', pick another. */
232 id = this->PRNG() & DNS::MAX_REQUEST_ID;
234 DNSRequest* req = new DNSRequest(ServerInstance, this, this->myserver, id);
236 header->id[0] = req->id[0] = id >> 8;
237 header->id[1] = req->id[1] = id & 0xFF;
238 header->flags1 = FLAGS_MASK_RD;
245 /* At this point we already know the id doesnt exist,
246 * so there needs to be no second check for the ::end()
250 /* According to the C++ spec, new never returns NULL. */
259 if (this->GetFd() > -1)
261 if (ServerInstance && ServerInstance->SE)
262 ServerInstance->SE->DelFd(this);
263 shutdown(this->GetFd(), 2);
264 close(this->GetFd());
268 if (insp_aton(ServerInstance->Config->DNSServer,&addr) > 0)
270 memcpy(&myserver,&addr,sizeof(insp_inaddr));
271 if ((strstr(ServerInstance->Config->DNSServer,"::ffff:") == (char*)&ServerInstance->Config->DNSServer) || (strstr(ServerInstance->Config->DNSServer,"::FFFF:") == (char*)&ServerInstance->Config->DNSServer))
273 ServerInstance->Log(DEFAULT,"WARNING: Using IPv4 addresses over IPv6 forces some DNS checks to be disabled.");
274 ServerInstance->Log(DEFAULT," This should not cause a problem, however it is recommended you migrate");
275 ServerInstance->Log(DEFAULT," to a true IPv6 environment.");
276 this->ip6munge = true;
278 ServerInstance->Log(DEBUG,"Added nameserver '%s'",ServerInstance->Config->DNSServer);
282 ServerInstance->Log(DEBUG,"GACK! insp_aton says the nameserver '%s' is invalid!",ServerInstance->Config->DNSServer);
285 /* Initialize mastersocket */
286 this->SetFd(socket(PF_PROTOCOL, SOCK_DGRAM, 0));
287 if (this->GetFd() != -1)
289 /* Did it succeed? */
290 if (fcntl(this->GetFd(), F_SETFL, O_NONBLOCK) != 0)
292 /* Couldn't make the socket nonblocking */
293 shutdown(this->GetFd(),2);
294 close(this->GetFd());
300 ServerInstance->Log(DEBUG,"I cant socket() this socket! (%s)",strerror(errno));
303 /* Have we got a socket and is it nonblocking? */
304 if (this->GetFd() != -1)
308 memset(&addr,0,sizeof(addr));
309 addr.sin6_family = AF_FAMILY;
311 addr.sin6_addr = in6addr_any;
314 memset(&addr,0,sizeof(addr));
315 addr.sin_family = AF_FAMILY;
317 addr.sin_addr.s_addr = INADDR_ANY;
320 if (bind(this->GetFd(),(sockaddr *)&addr,sizeof(addr)) != 0)
323 ServerInstance->Log(DEBUG,"Cant bind DNS fd");
324 shutdown(this->GetFd(),2);
325 close(this->GetFd());
329 if (this->GetFd() >= 0)
331 ServerInstance->Log(DEBUG,"Add master socket %d",this->GetFd());
332 /* Hook the descriptor into the socket engine */
333 if (ServerInstance && ServerInstance->SE)
335 if (!ServerInstance->SE->AddFd(this))
337 ServerInstance->Log(DEFAULT,"Internal error starting DNS - hostnames will NOT resolve.");
338 shutdown(this->GetFd(),2);
339 close(this->GetFd());
347 /** Initialise the DNS UDP socket so that we can send requests */
348 DNS::DNS(InspIRCd* Instance) : ServerInstance(Instance)
350 ServerInstance->Log(DEBUG,"DNS::DNS: Instance = %08x",Instance);
352 /* Clear the Resolver class table */
353 memset(Classes,0,sizeof(Classes));
355 /* Clear the requests class table */
356 memset(requests,0,sizeof(requests));
358 /* Set the id of the next request to 0
367 /** Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
368 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
370 short payloadpos = 0;
371 const char* tempchr, *tempchr2 = name;
372 unsigned short length;
374 /* split name up into labels, create query */
375 while ((tempchr = strchr(tempchr2,'.')) != NULL)
377 length = tempchr - tempchr2;
378 if (payloadpos + length + 1 > 507)
380 payload[payloadpos++] = length;
381 memcpy(&payload[payloadpos],tempchr2,length);
382 payloadpos += length;
383 tempchr2 = &tempchr[1];
385 length = strlen(tempchr2);
388 if (payloadpos + length + 2 > 507)
390 payload[payloadpos++] = length;
391 memcpy(&payload[payloadpos],tempchr2,length);
392 payloadpos += length;
393 payload[payloadpos++] = 0;
395 if (payloadpos > 508)
398 memcpy(&payload[payloadpos],&length,2);
399 length = htons(rr_class);
400 memcpy(&payload[payloadpos + 2],&length,2);
401 return payloadpos + 4;
404 /** Start lookup of an hostname to an IP address */
405 int DNS::GetIP(const char *name)
411 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
414 DNSRequest* req = this->AddQuery(&h, id);
416 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
422 /** Start lookup of an hostname to an IPv6 address */
423 int DNS::GetIP6(const char *name)
429 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
432 DNSRequest* req = this->AddQuery(&h, id);
434 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
440 /** Start lookup of a cname to another name */
441 int DNS::GetCName(const char *alias)
447 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
450 DNSRequest* req = this->AddQuery(&h, id);
452 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
458 /** Start lookup of an IP address to a hostname */
459 int DNS::GetName(const insp_inaddr *ip)
467 unsigned char* c = (unsigned char*)&ip->s6_addr;
468 if (c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0 &&
469 c[4] == 0 && c[5] == 0 && c[6] == 0 && c[7] == 0 &&
470 c[8] == 0 && c[9] == 0 && c[10] == 0xFF && c[11] == 0xFF)
471 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[15],c[14],c[13],c[12]);
473 DNS::MakeIP6Int(query, (in6_addr*)ip);
475 unsigned char* c = (unsigned char*)&ip->s_addr;
476 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
479 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
482 DNSRequest* req = this->AddQuery(&h, id);
484 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
490 /** Start lookup of an IP address to a hostname */
491 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
497 #ifdef SUPPORT_IP6LINKS
498 if (fp == PROTOCOL_IPV6)
501 if (inet_pton(AF_INET6, ip, &i) > 0)
503 DNS::MakeIP6Int(query, &i);
506 /* Invalid IP address */
513 if (inet_aton(ip, &i))
515 unsigned char* c = (unsigned char*)&i.s_addr;
516 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
519 /* Invalid IP address */
523 ServerInstance->Log(DEBUG,"DNS::GetNameForce: %s %d",query, fp);
525 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
528 DNSRequest* req = this->AddQuery(&h, id);
530 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
536 /** Build an ipv6 reverse domain from an in6_addr
538 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
540 #ifdef SUPPORT_IP6LINKS
541 const char* hex = "0123456789abcdef";
542 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
546 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
549 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
550 *query++ = '.'; /* Seperator */
552 strcpy(query,"ip6.arpa"); /* Suffix the string */
558 /** Return the next id which is ready, and the result attached to it */
559 DNSResult DNS::GetResult()
561 /* Fetch dns query response and decide where it belongs */
564 unsigned char buffer[sizeof(DNSHeader)];
566 socklen_t x = sizeof(from);
567 const char* ipaddr_from = "";
568 unsigned short int port_from = 0;
570 int length = recvfrom(this->GetFd(),buffer,sizeof(DNSHeader),0,&from,&x);
573 ServerInstance->Log(DEBUG,"Error in recvfrom()! (%s)",strerror(errno));
575 /* Did we get the whole header? */
578 /* Nope - something screwed up. */
579 ServerInstance->Log(DEBUG,"Whole header not read!");
580 return DNSResult(-1,"",0);
583 /* Check wether the reply came from a different DNS
584 * server to the one we sent it to, or the source-port
586 * A user could in theory still spoof dns packets anyway
587 * but this is less trivial than just sending garbage
588 * to the client, which is possible without this check.
590 * -- Thanks jilles for pointing this one out.
593 ipaddr_from = insp_ntoa(((sockaddr_in6*)&from)->sin6_addr);
594 port_from = ntohs(((sockaddr_in6*)&from)->sin6_port);
596 ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
597 port_from = ntohs(((sockaddr_in*)&from)->sin_port);
600 /* We cant perform this security check if you're using 4in6.
601 * Tough luck to you, choose one or't other!
605 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, ServerInstance->Config->DNSServer)))
607 ServerInstance->Log(DEBUG,"port %d is not 53, or %s is not %s",port_from, ipaddr_from, ServerInstance->Config->DNSServer);
608 return DNSResult(-1,"",0);
612 /* Put the read header info into a header class */
613 DNS::FillHeader(&header,buffer,length - 12);
615 /* Get the id of this request.
616 * Its a 16 bit value stored in two char's,
617 * so we use logic shifts to create the value.
619 unsigned long this_id = header.id[1] + (header.id[0] << 8);
621 /* Do we have a pending request matching this id? */
622 if (!requests[this_id])
624 /* Somehow we got a DNS response for a request we never made... */
625 ServerInstance->Log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",this->GetFd(),this_id);
626 return DNSResult(-1,"",0);
630 /* Remove the query from the list of pending queries */
631 req = requests[this_id];
632 requests[this_id] = NULL;
635 /* Inform the DNSRequest class that it has a result to be read.
636 * When its finished it will return a DNSInfo which is a pair of
637 * unsigned char* resource record data, and an error message.
639 DNSInfo data = req->ResultIsReady(header, length);
640 std::string resultstr;
642 /* Check if we got a result, if we didnt, its an error */
643 if (data.first == NULL)
646 * Mask the ID with the value of ERROR_MASK, so that
647 * the dns_deal_with_classes() function knows that its
648 * an error response and needs to be treated uniquely.
649 * Put the error message in the second field.
652 return DNSResult(this_id | ERROR_MASK, data.second, 0);
656 unsigned long ttl = req->ttl;
659 /* Forward lookups come back as binary data. We must format them into ascii */
663 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
664 resultstr = formatted;
669 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
670 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
671 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
672 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
673 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
674 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
675 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
676 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
677 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
678 char* c = strstr(formatted,":0:");
681 memmove(c+1,c+2,strlen(c+2) + 1);
683 while (memcmp(c,"0:",2) == 0)
684 memmove(c,c+2,strlen(c+2) + 1);
685 if (memcmp(c,"0",2) == 0)
687 if (memcmp(formatted,"0::",3) == 0)
688 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
690 resultstr = formatted;
692 /* Special case. Sending ::1 around between servers
693 * and to clients is dangerous, because the : on the
694 * start makes the client or server interpret the IP
695 * as the last parameter on the line with a value ":1".
697 if (*formatted == ':')
698 resultstr = "0" + resultstr;
702 case DNS_QUERY_CNAME:
703 /* Identical handling to PTR */
706 /* Reverse lookups just come back as char* */
707 resultstr = std::string((const char*)data.first);
711 ServerInstance->Log(DEBUG,"WARNING: Somehow we made a request for a DNS_QUERY_PTR4 or DNS_QUERY_PTR6, but these arent real rr types!");
716 /* Build the reply with the id and hostname/ip in it */
718 return DNSResult(this_id,resultstr,ttl);
722 /** A result is ready, process it */
723 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
731 /* This is just to keep _FORTIFY_SOURCE happy */
732 rr.type = DNS_QUERY_NONE;
735 if (!(header.flags1 & FLAGS_MASK_QR))
736 return std::make_pair((unsigned char*)NULL,"Not a query result");
738 if (header.flags1 & FLAGS_MASK_OPCODE)
739 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
741 if (header.flags2 & FLAGS_MASK_RCODE)
742 return std::make_pair((unsigned char*)NULL,"Domain name not found");
744 if (header.ancount < 1)
745 return std::make_pair((unsigned char*)NULL,"No resource records returned");
747 /* Subtract the length of the header from the length of the packet */
750 while ((unsigned int)q < header.qdcount && i < length)
752 if (header.payload[i] > 63)
759 if (header.payload[i] == 0)
764 else i += header.payload[i] + 1;
768 while ((unsigned)curanswer < header.ancount)
771 while (q == 0 && i < length)
773 if (header.payload[i] > 63)
780 if (header.payload[i] == 0)
785 else i += header.payload[i] + 1; /* skip length and label */
789 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
791 DNS::FillResourceRecord(&rr,&header.payload[i]);
793 if (rr.type != this->type)
799 if (rr.rr_class != this->rr_class)
807 if ((unsigned int)curanswer == header.ancount)
808 return std::make_pair((unsigned char*)NULL,"No valid answers");
810 if (i + rr.rdlength > (unsigned int)length)
811 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
813 if (rr.rdlength > 1023)
814 return std::make_pair((unsigned char*)NULL,"Resource record too large");
820 case DNS_QUERY_CNAME:
821 /* CNAME and PTR have the same processing code */
825 while (q == 0 && i < length && o + 256 < 1023)
827 if (header.payload[i] > 63)
829 memcpy(&ptr,&header.payload[i],2);
830 i = ntohs(ptr) - 0xC000 - 12;
834 if (header.payload[i] == 0)
843 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
844 o += header.payload[i];
845 i += header.payload[i] + 1;
852 memcpy(res,&header.payload[i],rr.rdlength);
853 res[rr.rdlength] = 0;
856 memcpy(res,&header.payload[i],rr.rdlength);
857 res[rr.rdlength] = 0;
860 memcpy(res,&header.payload[i],rr.rdlength);
861 res[rr.rdlength] = 0;
864 return std::make_pair(res,"No error");;
867 /** Close the master socket */
870 shutdown(this->GetFd(), 2);
871 close(this->GetFd());
874 /** High level abstraction of dns used by application at large */
875 Resolver::Resolver(InspIRCd* Instance, const std::string &source, QueryType qt, Module* creator) : ServerInstance(Instance), Creator(creator), input(source), querytype(qt)
877 ServerInstance->Log(DEBUG,"Instance: %08x %08x",Instance, ServerInstance);
884 this->myid = ServerInstance->Res->GetIP(source.c_str());
888 if (insp_aton(source.c_str(), &binip) > 0)
890 /* Valid ip address */
891 this->myid = ServerInstance->Res->GetName(&binip);
895 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
896 throw ModuleException("Resolver: Bad IP address");
902 querytype = DNS_QUERY_PTR;
903 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
907 querytype = DNS_QUERY_PTR;
908 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
912 this->myid = ServerInstance->Res->GetIP6(source.c_str());
915 case DNS_QUERY_CNAME:
916 this->myid = ServerInstance->Res->GetCName(source.c_str());
923 if (this->myid == -1)
925 ServerInstance->Log(DEBUG,"Resolver::Resolver: Could not get an id!");
926 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
927 throw ModuleException("Resolver: Couldnt get an id to make a request");
928 /* We shouldnt get here really */
932 ServerInstance->Log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
935 /** Called when an error occurs */
936 void Resolver::OnError(ResolverError e, const std::string &errormessage)
938 /* Nothing in here */
941 /** Destroy a resolver */
942 Resolver::~Resolver()
944 /* Nothing here (yet) either */
947 /** Get the request id associated with this class */
948 int Resolver::GetId()
953 Module* Resolver::GetCreator()
955 return this->Creator;
958 /** Process a socket read event */
959 void DNS::HandleEvent(EventType et, int errornum)
961 /* Fetch the id and result of the next available packet */
962 DNSResult res = this->GetResult();
963 /* Is there a usable request id? */
966 /* Its an error reply */
967 if (res.id & ERROR_MASK)
969 /* Mask off the error bit */
970 res.id -= ERROR_MASK;
971 /* Marshall the error to the correct class */
972 ServerInstance->Log(DEBUG,"Error available, id=%d",res.id);
975 if (ServerInstance && ServerInstance->stats)
976 ServerInstance->stats->statsDnsBad++;
977 Classes[res.id]->OnError(RESOLVER_NXDOMAIN, res.result);
978 delete Classes[res.id];
979 Classes[res.id] = NULL;
984 /* It is a non-error result */
985 ServerInstance->Log(DEBUG,"Result available, id=%d",res.id);
986 /* Marshall the result to the correct class */
989 if (ServerInstance && ServerInstance->stats)
990 ServerInstance->stats->statsDnsGood++;
991 Classes[res.id]->OnLookupComplete(res.result, res.ttl);
992 delete Classes[res.id];
993 Classes[res.id] = NULL;
997 if (ServerInstance && ServerInstance->stats)
998 ServerInstance->stats->statsDns++;
1002 /** Add a derived Resolver to the working set */
1003 bool DNS::AddResolverClass(Resolver* r)
1005 /* Check the pointers validity and the id's validity */
1006 if ((r) && (r->GetId() > -1))
1008 /* Check the slot isnt already occupied -
1009 * This should NEVER happen unless we have
1010 * a severely broken DNS server somewhere
1012 if (!Classes[r->GetId()])
1014 /* Set up the pointer to the class */
1015 Classes[r->GetId()] = r;
1024 /* Pointer or id not valid.
1025 * Free the item and return
1034 void DNS::CleanResolvers(Module* module)
1036 for (int i = 0; i < MAX_REQUEST_ID; i++)
1040 if (Classes[i]->GetCreator() == module)
1042 Classes[i]->OnError(RESLOVER_FORCEUNLOAD, "Parent module is unloading");
1050 /** Generate pseudo-random number */
1051 unsigned long DNS::PRNG()
1053 unsigned long val = 0;
1055 serverstats* s = ServerInstance->stats;
1056 gettimeofday(&n,NULL);
1057 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
1058 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
1059 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - s->BoundPortCount;