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));
260 log(DEBUG,"Added nameserver '%s'",Config->DNSServer);
264 log(DEBUG,"GACK! insp_aton says the nameserver '%s' is invalid!",Config->DNSServer);
267 /* Initialize mastersocket */
268 MasterSocket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
269 if (MasterSocket != -1)
271 /* Did it succeed? */
272 if (fcntl(MasterSocket, F_SETFL, O_NONBLOCK) != 0)
274 /* Couldn't make the socket nonblocking */
275 shutdown(MasterSocket,2);
282 log(DEBUG,"I cant socket() this socket! (%s)",strerror(errno));
284 /* Have we got a socket and is it nonblocking? */
285 if (MasterSocket != -1)
289 memset(&addr,0,sizeof(addr));
290 addr.sin6_family = AF_FAMILY;
292 addr.sin6_addr = in6addr_any;
295 memset(&addr,0,sizeof(addr));
296 addr.sin_family = AF_FAMILY;
298 addr.sin_addr.s_addr = INADDR_ANY;
301 if (bind(MasterSocket,(sockaddr *)&addr,sizeof(addr)) != 0)
304 log(DEBUG,"Cant bind DNS::MasterSocket");
305 shutdown(MasterSocket,2);
310 if (MasterSocket >= 0)
312 log(DEBUG,"Add master socket %d",MasterSocket);
313 /* Hook the descriptor into the socket engine */
314 if (ServerInstance && ServerInstance->SE)
315 ServerInstance->SE->AddFd(MasterSocket,true,X_ESTAB_DNS);
320 /* Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
321 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
323 short payloadpos = 0;
324 const char* tempchr, *tempchr2 = name;
325 unsigned short length;
327 /* split name up into labels, create query */
328 while ((tempchr = strchr(tempchr2,'.')) != NULL)
330 length = tempchr - tempchr2;
331 if (payloadpos + length + 1 > 507)
333 payload[payloadpos++] = length;
334 memcpy(&payload[payloadpos],tempchr2,length);
335 payloadpos += length;
336 tempchr2 = &tempchr[1];
338 length = strlen(tempchr2);
341 if (payloadpos + length + 2 > 507)
343 payload[payloadpos++] = length;
344 memcpy(&payload[payloadpos],tempchr2,length);
345 payloadpos += length;
346 payload[payloadpos++] = 0;
348 if (payloadpos > 508)
351 memcpy(&payload[payloadpos],&length,2);
352 length = htons(rr_class);
353 memcpy(&payload[payloadpos + 2],&length,2);
354 return payloadpos + 4;
357 /* Start lookup of an hostname to an IP address */
358 int DNS::GetIP(const char *name)
364 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
367 DNSRequest* req = this->AddQuery(&h, id);
369 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
375 /* Start lookup of an hostname to an IPv6 address */
376 int DNS::GetIP6(const char *name)
382 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
385 DNSRequest* req = this->AddQuery(&h, id);
387 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
393 /* Start lookup of a cname to another name */
394 int DNS::GetCName(const char *alias)
400 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
403 DNSRequest* req = this->AddQuery(&h, id);
405 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
411 /* Start lookup of an IP address to a hostname */
412 int DNS::GetName(const insp_inaddr *ip)
420 DNS::MakeIP6Int(query, (in6_addr*)ip);
422 unsigned char* c = (unsigned char*)&ip->s_addr;
424 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
427 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
430 DNSRequest* req = this->AddQuery(&h, id);
432 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
438 /* Start lookup of an IP address to a hostname */
439 int DNS::GetNameForce(const char *ip, ForceProtocol fp)
446 if (fp == PROTOCOL_IPV6)
449 if (inet_pton(AF_INET6, ip, &i) > 0)
451 DNS::MakeIP6Int(query, &i);
454 /* Invalid IP address */
460 if (inet_aton(ip, &i))
462 unsigned char* c = (unsigned char*)&i.s_addr;
463 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
466 /* Invalid IP address */
470 log(DEBUG,"DNS::GetNameForce: %s %d",query, fp);
472 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
475 DNSRequest* req = this->AddQuery(&h, id);
477 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
483 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
485 const char* hex = "0123456789abcdef";
486 for (int index = 31; index >= 0; index--) /* for() loop steps twice per byte */
490 *query++ = hex[ip->s6_addr[index / 2] & 0x0F];
493 *query++ = hex[(ip->s6_addr[index / 2] & 0xF0) >> 4];
494 *query++ = '.'; /* Seperator */
496 strcpy(query,"ip6.arpa"); /* Suffix the string */
499 /* Return the next id which is ready, and the result attached to it */
500 DNSResult DNS::GetResult()
502 /* Fetch dns query response and decide where it belongs */
505 unsigned char buffer[sizeof(DNSHeader)];
507 socklen_t x = sizeof(from);
508 const char* ipaddr_from = "";
509 unsigned short int port_from = 0;
511 int length = recvfrom(MasterSocket,buffer,sizeof(DNSHeader),0,&from,&x);
514 log(DEBUG,"Error in recvfrom()! (%s)",strerror(errno));
516 /* Did we get the whole header? */
519 /* Nope - something screwed up. */
520 log(DEBUG,"Whole header not read!");
521 return std::make_pair(-1,"");
524 /* Check wether the reply came from a different DNS
525 * server to the one we sent it to, or the source-port
527 * A user could in theory still spoof dns packets anyway
528 * but this is less trivial than just sending garbage
529 * to the client, which is possible without this check.
531 * -- Thanks jilles for pointing this one out.
534 ipaddr_from = insp_ntoa(((sockaddr_in6*)&from)->sin6_addr);
535 port_from = ntohs(((sockaddr_in6*)&from)->sin6_port);
537 ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
538 port_from = ntohs(((sockaddr_in*)&from)->sin_port);
541 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, Config->DNSServer)))
543 log(DEBUG,"port %d is not 53, or %s is not %s",port_from, ipaddr_from, Config->DNSServer);
544 return std::make_pair(-1,"");
547 /* Put the read header info into a header class */
548 DNS::FillHeader(&header,buffer,length - 12);
550 /* Get the id of this request.
551 * Its a 16 bit value stored in two char's,
552 * so we use logic shifts to create the value.
554 unsigned long this_id = header.id[1] + (header.id[0] << 8);
556 /* Do we have a pending request matching this id? */
557 requestlist_iter n_iter = requests.find(this_id);
558 if (n_iter == requests.end())
560 /* Somehow we got a DNS response for a request we never made... */
561 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",MasterSocket,this_id);
562 return std::make_pair(-1,"");
566 /* Remove the query from the list of pending queries */
567 req = (DNSRequest*)n_iter->second;
568 requests.erase(n_iter);
571 /* Inform the DNSRequest class that it has a result to be read.
572 * When its finished it will return a DNSInfo which is a pair of
573 * unsigned char* resource record data, and an error message.
575 DNSInfo data = req->ResultIsReady(header, length);
576 std::string resultstr;
578 /* Check if we got a result, if we didnt, its an error */
579 if (data.first == NULL)
582 * Mask the ID with the value of ERROR_MASK, so that
583 * the dns_deal_with_classes() function knows that its
584 * an error response and needs to be treated uniquely.
585 * Put the error message in the second field.
588 return std::make_pair(this_id | ERROR_MASK, data.second);
594 /* Forward lookups come back as binary data. We must format them into ascii */
598 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
599 resultstr = formatted;
604 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
605 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
606 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
607 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
608 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
609 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
610 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
611 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
612 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
613 char* c = strstr(formatted,":0:");
616 memmove(c+1,c+2,strlen(c+2) + 1);
618 while (memcmp(c,"0:",2) == 0)
619 memmove(c,c+2,strlen(c+2) + 1);
620 if (memcmp(c,"0",2) == 0)
622 if (memcmp(formatted,"0::",3) == 0)
623 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
625 resultstr = formatted;
629 case DNS_QUERY_CNAME:
630 /* Identical handling to PTR */
633 /* Reverse lookups just come back as char* */
634 resultstr = std::string((const char*)data.first);
638 log(DEBUG,"WARNING: Somehow we made a request for a DNS_QUERY_PTR4 or DNS_QUERY_PTR6, but these arent real rr types!");
643 /* Build the reply with the id and hostname/ip in it */
645 return std::make_pair(this_id,resultstr);
649 /* A result is ready, process it */
650 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
658 if (!(header.flags1 & FLAGS_MASK_QR))
659 return std::make_pair((unsigned char*)NULL,"Not a query result");
661 if (header.flags1 & FLAGS_MASK_OPCODE)
662 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
664 if (header.flags2 & FLAGS_MASK_RCODE)
665 return std::make_pair((unsigned char*)NULL,"Domain name not found");
667 if (header.ancount < 1)
668 return std::make_pair((unsigned char*)NULL,"No resource records returned");
670 /* Subtract the length of the header from the length of the packet */
673 while ((unsigned int)q < header.qdcount && i < length)
675 if (header.payload[i] > 63)
682 if (header.payload[i] == 0)
687 else i += header.payload[i] + 1;
691 while ((unsigned)curanswer < header.ancount)
694 while (q == 0 && i < length)
696 if (header.payload[i] > 63)
703 if (header.payload[i] == 0)
708 else i += header.payload[i] + 1; /* skip length and label */
712 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
714 DNS::FillResourceRecord(&rr,&header.payload[i]);
716 if (rr.type != this->type)
722 if (rr.rr_class != this->rr_class)
730 if ((unsigned int)curanswer == header.ancount)
731 return std::make_pair((unsigned char*)NULL,"No valid answers");
733 if (i + rr.rdlength > (unsigned int)length)
734 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
736 if (rr.rdlength > 1023)
737 return std::make_pair((unsigned char*)NULL,"Resource record too large");
741 case DNS_QUERY_CNAME:
742 /* CNAME and PTR have the same processing code */
746 while (q == 0 && i < length && o + 256 < 1023)
748 if (header.payload[i] > 63)
750 memcpy(&ptr,&header.payload[i],2);
751 i = ntohs(ptr) - 0xC000 - 12;
755 if (header.payload[i] == 0)
764 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
765 o += header.payload[i];
766 i += header.payload[i] + 1;
773 memcpy(res,&header.payload[i],rr.rdlength);
774 res[rr.rdlength] = 0;
777 memcpy(res,&header.payload[i],rr.rdlength);
778 res[rr.rdlength] = 0;
781 memcpy(res,&header.payload[i],rr.rdlength);
782 res[rr.rdlength] = 0;
785 return std::make_pair(res,"No error");;
788 /* Close the master socket */
791 shutdown(MasterSocket, 2);
795 /* High level abstraction of dns used by application at large */
796 Resolver::Resolver(const std::string &source, QueryType qt) : input(source), querytype(qt)
803 this->myid = ServerInstance->Res->GetIP(source.c_str());
807 if (insp_aton(source.c_str(), &binip) > 0)
809 /* Valid ip address */
810 this->myid = ServerInstance->Res->GetName(&binip);
814 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
815 throw ModuleException("Resolver: Bad IP address");
821 querytype = DNS_QUERY_PTR;
822 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV4);
826 querytype = DNS_QUERY_PTR;
827 this->myid = ServerInstance->Res->GetNameForce(source.c_str(), PROTOCOL_IPV6);
831 this->myid = ServerInstance->Res->GetIP6(source.c_str());
834 case DNS_QUERY_CNAME:
835 this->myid = ServerInstance->Res->GetCName(source.c_str());
842 if (this->myid == -1)
844 log(DEBUG,"Resolver::Resolver: Could not get an id!");
845 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
846 throw ModuleException("Resolver: Couldnt get an id to make a request");
847 /* We shouldnt get here really */
851 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
854 void Resolver::OnError(ResolverError e, const std::string &errormessage)
856 /* Nothing in here */
859 Resolver::~Resolver()
861 /* Nothing here (yet) either */
864 /* Get the request id associated with this class */
865 int Resolver::GetId()
870 /* Process a socket read event */
871 void DNS::MarshallReads(int fd)
873 log(DEBUG,"Marshall reads: %d %d",fd,GetMasterSocket());
874 /* We are only intrested in our single fd */
875 if (fd == GetMasterSocket())
877 /* Fetch the id and result of the next available packet */
878 DNSResult res = this->GetResult();
879 /* Is there a usable request id? */
882 /* Its an error reply */
883 if (res.first & ERROR_MASK)
885 /* Mask off the error bit */
886 res.first -= ERROR_MASK;
888 /* Marshall the error to the correct class */
889 log(DEBUG,"Error available, id=%d",res.first);
890 if (Classes[res.first])
892 if (ServerInstance && ServerInstance->stats)
893 ServerInstance->stats->statsDnsBad++;
894 Classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
895 delete Classes[res.first];
896 Classes[res.first] = NULL;
901 /* It is a non-error result */
902 log(DEBUG,"Result available, id=%d",res.first);
903 /* Marshall the result to the correct class */
904 if (Classes[res.first])
906 if (ServerInstance && ServerInstance->stats)
907 ServerInstance->stats->statsDnsGood++;
908 Classes[res.first]->OnLookupComplete(res.second);
909 delete Classes[res.first];
910 Classes[res.first] = NULL;
914 if (ServerInstance && ServerInstance->stats)
915 ServerInstance->stats->statsDns++;
921 /* Add a derived Resolver to the working set */
922 bool DNS::AddResolverClass(Resolver* r)
924 /* Check the pointers validity and the id's validity */
925 if ((r) && (r->GetId() > -1))
927 /* Check the slot isnt already occupied -
928 * This should NEVER happen unless we have
929 * a severely broken DNS server somewhere
931 if (!Classes[r->GetId()])
933 /* Set up the pointer to the class */
934 Classes[r->GetId()] = r;
943 /* Pointer or id not valid.
944 * Free the item and return
953 unsigned long DNS::PRNG()
955 unsigned long val = 0;
957 serverstats* s = ServerInstance->stats;
958 gettimeofday(&n,NULL);
959 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
960 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
961 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - s->BoundPortCount;