1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
7 * <brain@chatspike.net>
8 * <Craig@chatspike.net>
10 * Written by Craig Edwards, Craig McLure, and others.
11 * This program is free but copyrighted software; see
12 * the file COPYING for details.
14 * ---------------------------------------------------
18 dns.cpp - Nonblocking DNS functions.
19 Very very loosely based on the firedns library,
20 Copyright (C) 2002 Ian Gulliver. This file is no
21 longer anything like firedns, there are many major
22 differences between this code and the original.
23 Please do not assume that firedns works like this,
24 looks like this, walks like this or tastes like this.
31 #include <sys/types.h>
32 #include <sys/socket.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
41 #include "helperfuncs.h"
42 #include "inspircd_config.h"
43 #include "socketengine.h"
44 #include "configreader.h"
47 extern InspIRCd* ServerInstance;
48 extern ServerConfig* Config;
50 /* Master file descriptor */
51 int DNS::MasterSocket;
53 /* Masks to mask off the responses we get from the DNSRequest methods */
56 ERROR_MASK = 0x10000 /* Result is an error */
59 /* Flags which can be ORed into a request or reply for different meanings */
62 FLAGS_MASK_RD = 0x01, /* Recursive */
64 FLAGS_MASK_AA = 0x04, /* Authoritative */
65 FLAGS_MASK_OPCODE = 0x78,
67 FLAGS_MASK_RCODE = 0x0F, /* Request */
73 /* Represents a dns resource record (rr) */
77 QueryType type; /* Record type */
78 unsigned int rr_class; /* Record class */
79 unsigned long ttl; /* Time to live */
80 unsigned int rdlength; /* Record length */
83 /* Represents a dns request/reply header, and its payload as opaque data.
88 unsigned char id[2]; /* Request id */
89 unsigned int flags1; /* Flags */
90 unsigned int flags2; /* Flags */
92 unsigned int ancount; /* Answer count */
93 unsigned int nscount; /* Nameserver count */
95 unsigned char payload[512]; /* Packet payload */
98 /* Represents a request 'on the wire' with routing information relating to
99 * where to call when we get a result
104 unsigned char id[2]; /* Request id */
105 unsigned char* res; /* Result processing buffer */
106 unsigned int rr_class; /* Request class */
107 QueryType type; /* Request type */
108 insp_inaddr myserver; /* DNS server address*/
110 /* Allocate the processing buffer */
111 DNSRequest(insp_inaddr server)
113 res = new unsigned char[512];
115 memcpy(&myserver, &server, sizeof(insp_inaddr));
118 /* Deallocate the processing buffer */
124 /* Called when a result is ready to be processed which matches this id */
125 DNSInfo ResultIsReady(DNSHeader &h, int length);
127 /* Called when there are requests to be sent out */
128 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
131 /* Fill a ResourceRecord class based on raw data input */
132 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
134 rr->type = (QueryType)((input[0] << 8) + input[1]);
135 rr->rr_class = (input[2] << 8) + input[3];
136 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
137 rr->rdlength = (input[8] << 8) + input[9];
140 /* Fill a DNSHeader class based on raw data input of a given length */
141 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
143 header->id[0] = input[0];
144 header->id[1] = input[1];
145 header->flags1 = input[2];
146 header->flags2 = input[3];
147 header->qdcount = (input[4] << 8) + input[5];
148 header->ancount = (input[6] << 8) + input[7];
149 header->nscount = (input[8] << 8) + input[9];
150 header->arcount = (input[10] << 8) + input[11];
151 memcpy(header->payload,&input[12],length);
154 /* Empty a DNSHeader class out into raw data, ready for transmission */
155 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
157 output[0] = header->id[0];
158 output[1] = header->id[1];
159 output[2] = header->flags1;
160 output[3] = header->flags2;
161 output[4] = header->qdcount >> 8;
162 output[5] = header->qdcount & 0xFF;
163 output[6] = header->ancount >> 8;
164 output[7] = header->ancount & 0xFF;
165 output[8] = header->nscount >> 8;
166 output[9] = header->nscount & 0xFF;
167 output[10] = header->arcount >> 8;
168 output[11] = header->arcount & 0xFF;
169 memcpy(&output[12],header->payload,length);
172 /* Send requests we have previously built down the UDP socket */
173 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
176 unsigned char payload[sizeof(DNSHeader)];
181 DNS::EmptyHeader(payload,header,length);
183 memset(&addr,0,sizeof(addr));
185 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
186 addr.sin6_family = AF_FAMILY;
187 addr.sin6_port = htons(DNS::QUERY_PORT);
189 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
190 addr.sin_family = AF_FAMILY;
191 addr.sin_port = htons(DNS::QUERY_PORT);
193 if (sendto(DNS::GetMasterSocket(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
195 log(DEBUG,"Error in sendto! (%s)",strerror(errno));
202 /* Add a query with a predefined header, and allocate an ID for it. */
203 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id)
205 /* Are there already the max number of requests on the go? */
206 if (requests.size() == DNS::MAX_REQUEST_ID + 1)
210 id = this->PRNG() & DNS::MAX_REQUEST_ID;
212 /* If this id is already 'in flight', pick another. */
213 while (requests.find(id) != requests.end())
214 id = this->PRNG() & DNS::MAX_REQUEST_ID;
216 DNSRequest* req = new DNSRequest(this->myserver);
218 header->id[0] = req->id[0] = id >> 8;
219 header->id[1] = req->id[1] = id & 0xFF;
220 header->flags1 = FLAGS_MASK_RD;
227 /* At this point we already know the id doesnt exist,
228 * so there needs to be no second check for the ::end()
232 /* According to the C++ spec, new never returns NULL. */
236 int DNS::GetMasterSocket()
241 /* Initialise the DNS UDP socket so that we can send requests */
246 /* Clear the Resolver class table */
247 memset(Classes,0,sizeof(Classes));
249 /* Set the id of the next request to 0
253 /* Clear the namesever address */
254 memset(&myserver,0,sizeof(insp_inaddr));
256 /* Convert the nameserver address into an insp_inaddr */
257 if (insp_aton(Config->DNSServer,&addr) > 0)
259 memcpy(&myserver,&addr,sizeof(insp_inaddr));
263 log(DEBUG,"GACK! insp_aton says the nameserver '%s' is invalid!",Config->DNSServer);
266 /* Initialize mastersocket */
267 MasterSocket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
268 if (MasterSocket != -1)
270 /* Did it succeed? */
271 if (fcntl(MasterSocket, F_SETFL, O_NONBLOCK) != 0)
273 /* Couldn't make the socket nonblocking */
274 shutdown(MasterSocket,2);
279 /* Have we got a socket and is it nonblocking? */
280 if (MasterSocket != -1)
284 memset(&addr,0,sizeof(addr));
285 addr.sin6_family = AF_FAMILY;
287 memset(&addr.sin6_addr,255,sizeof(in6_addr));
290 memset(&addr,0,sizeof(addr));
291 addr.sin_family = AF_FAMILY;
293 addr.sin_addr.s_addr = INADDR_ANY;
296 if (bind(MasterSocket,(sockaddr *)&addr,sizeof(addr)) != 0)
299 log(DEBUG,"Cant bind DNS::MasterSocket");
300 shutdown(MasterSocket,2);
305 if (MasterSocket >= 0)
307 /* Hook the descriptor into the socket engine */
308 if (ServerInstance && ServerInstance->SE)
309 ServerInstance->SE->AddFd(MasterSocket,true,X_ESTAB_DNS);
314 /* Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
315 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
317 short payloadpos = 0;
318 const char* tempchr, *tempchr2 = name;
319 unsigned short length;
321 /* split name up into labels, create query */
322 while ((tempchr = strchr(tempchr2,'.')) != NULL)
324 length = tempchr - tempchr2;
325 if (payloadpos + length + 1 > 507)
327 payload[payloadpos++] = length;
328 memcpy(&payload[payloadpos],tempchr2,length);
329 payloadpos += length;
330 tempchr2 = &tempchr[1];
332 length = strlen(tempchr2);
335 if (payloadpos + length + 2 > 507)
337 payload[payloadpos++] = length;
338 memcpy(&payload[payloadpos],tempchr2,length);
339 payloadpos += length;
340 payload[payloadpos++] = 0;
342 if (payloadpos > 508)
345 memcpy(&payload[payloadpos],&length,2);
346 length = htons(rr_class);
347 memcpy(&payload[payloadpos + 2],&length,2);
348 return payloadpos + 4;
351 /* Start lookup of an hostname to an IP address */
352 int DNS::GetIP(const char *name)
358 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
361 DNSRequest* req = this->AddQuery(&h, id);
363 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
369 /* Start lookup of an hostname to an IPv6 address */
370 int DNS::GetIP6(const char *name)
376 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
379 DNSRequest* req = this->AddQuery(&h, id);
381 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
387 /* Start lookup of a cname to another name */
388 int DNS::GetCName(const char *alias)
394 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
397 DNSRequest* req = this->AddQuery(&h, id);
399 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
405 /* Start lookup of an IP address to a hostname */
406 int DNS::GetName(const insp_inaddr *ip)
414 DNS::MakeIP6Int(query, (in6_addr*)ip);
416 unsigned char* c = (unsigned char*)&ip->s_addr;
418 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
421 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
424 DNSRequest* req = this->AddQuery(&h, id);
426 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
432 /* Start lookup of an IP address to a hostname */
433 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
440 if (fp == PROTOCOL_IPV6)
443 if (inet_pton(AF_INET6, ip, &i) > 0)
445 DNS::MakeIP6Int(query, &i);
448 /* Invalid IP address */
454 if (inet_aton(ip, &i))
456 unsigned char* c = (unsigned char*)&i.s_addr;
457 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
460 /* Invalid IP address */
464 log(DEBUG,"DNS::GetNameForce: %s %d",query, fp);
466 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
469 DNSRequest* req = this->AddQuery(&h, id);
471 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
477 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
479 const char* hex = "0123456789abcdef";
480 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
484 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
487 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
488 *query++ = '.'; /* Seperator */
490 strcpy(query,"ip6.arpa"); /* Suffix the string */
493 /* Return the next id which is ready, and the result attached to it */
494 DNSResult DNS::GetResult()
496 /* Fetch dns query response and decide where it belongs */
499 unsigned char buffer[sizeof(DNSHeader)];
501 socklen_t x = sizeof(from);
502 const char* ipaddr_from = "";
503 unsigned short int port_from = 0;
505 int length = recvfrom(MasterSocket,buffer,sizeof(DNSHeader),0,&from,&x);
508 log(DEBUG,"Error in recvfrom()! (%s)",strerror(errno));
510 /* Did we get the whole header? */
512 /* Nope - something screwed up. */
513 return std::make_pair(-1,"");
515 /* Check wether the reply came from a different DNS
516 * server to the one we sent it to, or the source-port
518 * A user could in theory still spoof dns packets anyway
519 * but this is less trivial than just sending garbage
520 * to the client, which is possible without this check.
522 * -- Thanks jilles for pointing this one out.
525 ipaddr_from = insp_ntoa(((sockaddr_in6*)&from)->sin6_addr);
526 port_from = ntohs(((sockaddr_in6*)&from)->sin6_port);
528 ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
529 port_from = ntohs(((sockaddr_in*)&from)->sin_port);
532 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, Config->DNSServer)))
533 return std::make_pair(-1,"");
535 /* Put the read header info into a header class */
536 DNS::FillHeader(&header,buffer,length - 12);
538 /* Get the id of this request.
539 * Its a 16 bit value stored in two char's,
540 * so we use logic shifts to create the value.
542 unsigned long this_id = header.id[1] + (header.id[0] << 8);
544 /* Do we have a pending request matching this id? */
545 requestlist_iter n_iter = requests.find(this_id);
546 if (n_iter == requests.end())
548 /* Somehow we got a DNS response for a request we never made... */
549 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",MasterSocket,this_id);
550 return std::make_pair(-1,"");
554 /* Remove the query from the list of pending queries */
555 req = (DNSRequest*)n_iter->second;
556 requests.erase(n_iter);
559 /* Inform the DNSRequest class that it has a result to be read.
560 * When its finished it will return a DNSInfo which is a pair of
561 * unsigned char* resource record data, and an error message.
563 DNSInfo data = req->ResultIsReady(header, length);
564 std::string resultstr;
566 /* Check if we got a result, if we didnt, its an error */
567 if (data.first == NULL)
570 * Mask the ID with the value of ERROR_MASK, so that
571 * the dns_deal_with_classes() function knows that its
572 * an error response and needs to be treated uniquely.
573 * Put the error message in the second field.
576 return std::make_pair(this_id | ERROR_MASK, data.second);
582 /* Forward lookups come back as binary data. We must format them into ascii */
586 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
587 resultstr = formatted;
592 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
593 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
594 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
595 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
596 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
597 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
598 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
599 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
600 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
601 char* c = strstr(formatted,":0:");
604 memmove(c+1,c+2,strlen(c+2) + 1);
606 while (memcmp(c,"0:",2) == 0)
607 memmove(c,c+2,strlen(c+2) + 1);
608 if (memcmp(c,"0",2) == 0)
610 if (memcmp(formatted,"0::",3) == 0)
611 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
613 resultstr = formatted;
617 case DNS_QUERY_CNAME:
618 /* Identical handling to PTR */
621 /* Reverse lookups just come back as char* */
622 resultstr = std::string((const char*)data.first);
626 log(DEBUG,"WARNING: Somehow we made a request for a DNS_QUERY_PTR4 or DNS_QUERY_PTR6, but these arent real rr types!");
631 /* Build the reply with the id and hostname/ip in it */
633 return std::make_pair(this_id,resultstr);
637 /* A result is ready, process it */
638 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
646 if (!(header.flags1 & FLAGS_MASK_QR))
647 return std::make_pair((unsigned char*)NULL,"Not a query result");
649 if (header.flags1 & FLAGS_MASK_OPCODE)
650 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
652 if (header.flags2 & FLAGS_MASK_RCODE)
653 return std::make_pair((unsigned char*)NULL,"Domain name not found");
655 if (header.ancount < 1)
656 return std::make_pair((unsigned char*)NULL,"No resource records returned");
658 /* Subtract the length of the header from the length of the packet */
661 while ((unsigned int)q < header.qdcount && i < length)
663 if (header.payload[i] > 63)
670 if (header.payload[i] == 0)
675 else i += header.payload[i] + 1;
679 while ((unsigned)curanswer < header.ancount)
682 while (q == 0 && i < length)
684 if (header.payload[i] > 63)
691 if (header.payload[i] == 0)
696 else i += header.payload[i] + 1; /* skip length and label */
700 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
702 DNS::FillResourceRecord(&rr,&header.payload[i]);
704 if (rr.type != this->type)
710 if (rr.rr_class != this->rr_class)
718 if ((unsigned int)curanswer == header.ancount)
719 return std::make_pair((unsigned char*)NULL,"No valid answers");
721 if (i + rr.rdlength > (unsigned int)length)
722 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
724 if (rr.rdlength > 1023)
725 return std::make_pair((unsigned char*)NULL,"Resource record too large");
729 case DNS_QUERY_CNAME:
730 /* CNAME and PTR have the same processing code */
734 while (q == 0 && i < length && o + 256 < 1023)
736 if (header.payload[i] > 63)
738 memcpy(&ptr,&header.payload[i],2);
739 i = ntohs(ptr) - 0xC000 - 12;
743 if (header.payload[i] == 0)
752 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
753 o += header.payload[i];
754 i += header.payload[i] + 1;
761 memcpy(res,&header.payload[i],rr.rdlength);
762 res[rr.rdlength] = 0;
765 memcpy(res,&header.payload[i],rr.rdlength);
766 res[rr.rdlength] = 0;
769 memcpy(res,&header.payload[i],rr.rdlength);
770 res[rr.rdlength] = 0;
773 return std::make_pair(res,"No error");;
776 /* Close the master socket */
779 shutdown(MasterSocket, 2);
783 /* High level abstraction of dns used by application at large */
784 Resolver::Resolver(const std::string &source, QueryType qt) : input(source), querytype(qt)
791 this->myid = ServerInstance->Res->GetIP(source.c_str());
795 if (insp_aton(source.c_str(), &binip) > 0)
797 /* Valid ip address */
798 this->myid = ServerInstance->Res->GetName(&binip);
802 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
803 throw ModuleException("Resolver: Bad IP address");
809 querytype = DNS_QUERY_PTR;
810 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
814 querytype = DNS_QUERY_PTR;
815 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
819 this->myid = ServerInstance->Res->GetIP6(source.c_str());
822 case DNS_QUERY_CNAME:
823 this->myid = ServerInstance->Res->GetCName(source.c_str());
830 if (this->myid == -1)
832 log(DEBUG,"Resolver::Resolver: Could not get an id!");
833 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
834 throw ModuleException("Resolver: Couldnt get an id to make a request");
835 /* We shouldnt get here really */
839 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
842 void Resolver::OnError(ResolverError e, const std::string &errormessage)
844 /* Nothing in here */
847 Resolver::~Resolver()
849 /* Nothing here (yet) either */
852 /* Get the request id associated with this class */
853 int Resolver::GetId()
858 /* Process a socket read event */
859 void DNS::MarshallReads(int fd)
861 /* We are only intrested in our single fd */
862 if (fd == GetMasterSocket())
864 /* Fetch the id and result of the next available packet */
865 DNSResult res = this->GetResult();
866 /* Is there a usable request id? */
869 /* Its an error reply */
870 if (res.first & ERROR_MASK)
872 /* Mask off the error bit */
873 res.first -= ERROR_MASK;
875 /* Marshall the error to the correct class */
876 log(DEBUG,"Error available, id=%d",res.first);
877 if (Classes[res.first])
879 if (ServerInstance && ServerInstance->stats)
880 ServerInstance->stats->statsDnsBad++;
881 Classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
882 delete Classes[res.first];
883 Classes[res.first] = NULL;
888 /* It is a non-error result */
889 log(DEBUG,"Result available, id=%d",res.first);
890 /* Marshall the result to the correct class */
891 if (Classes[res.first])
893 if (ServerInstance && ServerInstance->stats)
894 ServerInstance->stats->statsDnsGood++;
895 Classes[res.first]->OnLookupComplete(res.second);
896 delete Classes[res.first];
897 Classes[res.first] = NULL;
901 if (ServerInstance && ServerInstance->stats)
902 ServerInstance->stats->statsDns++;
908 /* Add a derived Resolver to the working set */
909 bool DNS::AddResolverClass(Resolver* r)
911 /* Check the pointers validity and the id's validity */
912 if ((r) && (r->GetId() > -1))
914 /* Check the slot isnt already occupied -
915 * This should NEVER happen unless we have
916 * a severely broken DNS server somewhere
918 if (!Classes[r->GetId()])
920 /* Set up the pointer to the class */
921 Classes[r->GetId()] = r;
930 /* Pointer or id not valid.
931 * Free the item and return
940 unsigned long DNS::PRNG()
942 unsigned long val = 0;
944 serverstats* s = ServerInstance->stats;
945 gettimeofday(&n,NULL);
946 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
947 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
948 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - s->BoundPortCount;