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!");
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)
258 memcpy(&myserver,&addr,sizeof(insp_inaddr));
260 /* Initialize mastersocket */
261 MasterSocket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
262 if (MasterSocket != -1)
264 /* Did it succeed? */
265 if (fcntl(MasterSocket, F_SETFL, O_NONBLOCK) != 0)
267 /* Couldn't make the socket nonblocking */
268 shutdown(MasterSocket,2);
273 /* Have we got a socket and is it nonblocking? */
274 if (MasterSocket != -1)
278 memset(&addr,0,sizeof(addr));
279 addr.sin6_family = AF_FAMILY;
281 memset(&addr.sin6_addr,255,sizeof(in6_addr));
284 memset(&addr,0,sizeof(addr));
285 addr.sin_family = AF_FAMILY;
287 addr.sin_addr.s_addr = INADDR_ANY;
290 if (bind(MasterSocket,(sockaddr *)&addr,sizeof(addr)) != 0)
293 log(DEBUG,"Cant bind DNS::MasterSocket");
294 shutdown(MasterSocket,2);
299 if (MasterSocket >= 0)
301 /* Hook the descriptor into the socket engine */
302 if (ServerInstance && ServerInstance->SE)
303 ServerInstance->SE->AddFd(MasterSocket,true,X_ESTAB_DNS);
308 /* Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
309 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
311 short payloadpos = 0;
312 const char* tempchr, *tempchr2 = name;
313 unsigned short length;
315 /* split name up into labels, create query */
316 while ((tempchr = strchr(tempchr2,'.')) != NULL)
318 length = tempchr - tempchr2;
319 if (payloadpos + length + 1 > 507)
321 payload[payloadpos++] = length;
322 memcpy(&payload[payloadpos],tempchr2,length);
323 payloadpos += length;
324 tempchr2 = &tempchr[1];
326 length = strlen(tempchr2);
329 if (payloadpos + length + 2 > 507)
331 payload[payloadpos++] = length;
332 memcpy(&payload[payloadpos],tempchr2,length);
333 payloadpos += length;
334 payload[payloadpos++] = 0;
336 if (payloadpos > 508)
339 memcpy(&payload[payloadpos],&length,2);
340 length = htons(rr_class);
341 memcpy(&payload[payloadpos + 2],&length,2);
342 return payloadpos + 4;
345 /* Start lookup of an hostname to an IP address */
346 int DNS::GetIP(const char *name)
352 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
355 DNSRequest* req = this->AddQuery(&h, id);
357 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_A) == -1))
363 /* Start lookup of an hostname to an IPv6 address */
364 int DNS::GetIP6(const char *name)
370 if ((length = this->MakePayload(name, DNS_QUERY_AAAA, 1, (unsigned char*)&h.payload)) == -1)
373 DNSRequest* req = this->AddQuery(&h, id);
375 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_AAAA) == -1))
381 /* Start lookup of a cname to another name */
382 int DNS::GetCName(const char *alias)
388 if ((length = this->MakePayload(alias, DNS_QUERY_CNAME, 1, (unsigned char*)&h.payload)) == -1)
391 DNSRequest* req = this->AddQuery(&h, id);
393 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_CNAME) == -1))
399 /* Start lookup of an IP address to a hostname */
400 int DNS::GetName(const insp_inaddr *ip)
408 DNS::MakeIP6Int(query, (in6_addr*)ip);
410 unsigned char* c = (unsigned char*)&ip->s_addr;
412 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
415 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
418 DNSRequest* req = this->AddQuery(&h, id);
420 if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
426 void DNS::MakeIP6Int(char* query, const in6_addr *ip)
428 sprintf(query,"%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.%0x.ip6.int",
429 ip->s6_addr[15] & 0x0f,
430 (ip->s6_addr[15] & 0xf0) >> 4,
431 ip->s6_addr[14] & 0x0f,
432 (ip->s6_addr[14] & 0xf0) >> 4,
433 ip->s6_addr[13] & 0x0f,
434 (ip->s6_addr[13] & 0xf0) >> 4,
435 ip->s6_addr[12] & 0x0f,
436 (ip->s6_addr[12] & 0xf0) >> 4,
437 ip->s6_addr[11] & 0x0f,
438 (ip->s6_addr[11] & 0xf0) >> 4,
439 ip->s6_addr[10] & 0x0f,
440 (ip->s6_addr[10] & 0xf0) >> 4,
441 ip->s6_addr[9] & 0x0f,
442 (ip->s6_addr[9] & 0xf0) >> 4,
443 ip->s6_addr[8] & 0x0f,
444 (ip->s6_addr[8] & 0xf0) >> 4,
445 ip->s6_addr[7] & 0x0f,
446 (ip->s6_addr[7] & 0xf0) >> 4,
447 ip->s6_addr[6] & 0x0f,
448 (ip->s6_addr[6] & 0xf0) >> 4,
449 ip->s6_addr[5] & 0x0f,
450 (ip->s6_addr[5] & 0xf0) >> 4,
451 ip->s6_addr[4] & 0x0f,
452 (ip->s6_addr[4] & 0xf0) >> 4,
453 ip->s6_addr[3] & 0x0f,
454 (ip->s6_addr[3] & 0xf0) >> 4,
455 ip->s6_addr[2] & 0x0f,
456 (ip->s6_addr[2] & 0xf0) >> 4,
457 ip->s6_addr[1] & 0x0f,
458 (ip->s6_addr[1] & 0xf0) >> 4,
459 ip->s6_addr[0] & 0x0f,
460 (ip->s6_addr[0] & 0xf0) >> 4
464 /* Return the next id which is ready, and the result attached to it */
465 DNSResult DNS::GetResult()
467 /* Fetch dns query response and decide where it belongs */
470 unsigned char buffer[sizeof(DNSHeader)];
472 socklen_t x = sizeof(from);
473 const char* ipaddr_from = "";
474 unsigned short int port_from = 0;
476 int length = recvfrom(MasterSocket,buffer,sizeof(DNSHeader),0,&from,&x);
478 /* Did we get the whole header? */
480 /* Nope - something screwed up. */
481 return std::make_pair(-1,"");
483 /* Check wether the reply came from a different DNS
484 * server to the one we sent it to, or the source-port
486 * A user could in theory still spoof dns packets anyway
487 * but this is less trivial than just sending garbage
488 * to the client, which is possible without this check.
490 * -- Thanks jilles for pointing this one out.
493 ipaddr_from = insp_ntoa(((sockaddr_in6*)&from)->sin6_addr);
494 port_from = ntohs(((sockaddr_in6*)&from)->sin6_port);
496 ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
497 port_from = ntohs(((sockaddr_in*)&from)->sin_port);
500 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, Config->DNSServer)))
501 return std::make_pair(-1,"");
503 /* Put the read header info into a header class */
504 DNS::FillHeader(&header,buffer,length - 12);
506 /* Get the id of this request.
507 * Its a 16 bit value stored in two char's,
508 * so we use logic shifts to create the value.
510 unsigned long this_id = header.id[1] + (header.id[0] << 8);
512 /* Do we have a pending request matching this id? */
513 requestlist_iter n_iter = requests.find(this_id);
514 if (n_iter == requests.end())
516 /* Somehow we got a DNS response for a request we never made... */
517 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",MasterSocket,this_id);
518 return std::make_pair(-1,"");
522 /* Remove the query from the list of pending queries */
523 req = (DNSRequest*)n_iter->second;
524 requests.erase(n_iter);
527 /* Inform the DNSRequest class that it has a result to be read.
528 * When its finished it will return a DNSInfo which is a pair of
529 * unsigned char* resource record data, and an error message.
531 DNSInfo data = req->ResultIsReady(header, length);
532 std::string resultstr;
534 /* Check if we got a result, if we didnt, its an error */
535 if (data.first == NULL)
538 * Mask the ID with the value of ERROR_MASK, so that
539 * the dns_deal_with_classes() function knows that its
540 * an error response and needs to be treated uniquely.
541 * Put the error message in the second field.
544 return std::make_pair(this_id | ERROR_MASK, data.second);
550 /* Forward lookups come back as binary data. We must format them into ascii */
554 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
555 resultstr = formatted;
560 snprintf(formatted,40,"%x:%x:%x:%x:%x:%x:%x:%x",
561 (ntohs(data.first[0]) + ntohs(data.first[1] << 8)),
562 (ntohs(data.first[2]) + ntohs(data.first[3] << 8)),
563 (ntohs(data.first[4]) + ntohs(data.first[5] << 8)),
564 (ntohs(data.first[6]) + ntohs(data.first[7] << 8)),
565 (ntohs(data.first[8]) + ntohs(data.first[9] << 8)),
566 (ntohs(data.first[10]) + ntohs(data.first[11] << 8)),
567 (ntohs(data.first[12]) + ntohs(data.first[13] << 8)),
568 (ntohs(data.first[14]) + ntohs(data.first[15] << 8)));
569 char* c = strstr(formatted,":0:");
572 memmove(c+1,c+2,strlen(c+2) + 1);
574 while (memcmp(c,"0:",2) == 0)
575 memmove(c,c+2,strlen(c+2) + 1);
576 if (memcmp(c,"0",2) == 0)
578 if (memcmp(formatted,"0::",3) == 0)
579 memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
581 resultstr = formatted;
585 case DNS_QUERY_CNAME:
586 /* Identical handling to PTR */
589 /* Reverse lookups just come back as char* */
590 resultstr = std::string((const char*)data.first);
595 /* Build the reply with the id and hostname/ip in it */
597 return std::make_pair(this_id,resultstr);
601 /* A result is ready, process it */
602 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
610 if (!(header.flags1 & FLAGS_MASK_QR))
611 return std::make_pair((unsigned char*)NULL,"Not a query result");
613 if (header.flags1 & FLAGS_MASK_OPCODE)
614 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
616 if (header.flags2 & FLAGS_MASK_RCODE)
617 return std::make_pair((unsigned char*)NULL,"Domain name not found");
619 if (header.ancount < 1)
620 return std::make_pair((unsigned char*)NULL,"No resource records returned");
622 /* Subtract the length of the header from the length of the packet */
625 while ((unsigned int)q < header.qdcount && i < length)
627 if (header.payload[i] > 63)
634 if (header.payload[i] == 0)
639 else i += header.payload[i] + 1;
643 while ((unsigned)curanswer < header.ancount)
646 while (q == 0 && i < length)
648 if (header.payload[i] > 63)
655 if (header.payload[i] == 0)
660 else i += header.payload[i] + 1; /* skip length and label */
664 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
666 DNS::FillResourceRecord(&rr,&header.payload[i]);
668 if (rr.type != this->type)
674 if (rr.rr_class != this->rr_class)
682 if ((unsigned int)curanswer == header.ancount)
683 return std::make_pair((unsigned char*)NULL,"No valid answers");
685 if (i + rr.rdlength > (unsigned int)length)
686 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
688 if (rr.rdlength > 1023)
689 return std::make_pair((unsigned char*)NULL,"Resource record too large");
693 case DNS_QUERY_CNAME:
694 /* CNAME and PTR have the same processing code */
698 while (q == 0 && i < length && o + 256 < 1023)
700 if (header.payload[i] > 63)
702 memcpy(&ptr,&header.payload[i],2);
703 i = ntohs(ptr) - 0xC000 - 12;
707 if (header.payload[i] == 0)
716 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
717 o += header.payload[i];
718 i += header.payload[i] + 1;
725 memcpy(res,&header.payload[i],rr.rdlength);
726 res[rr.rdlength] = 0;
729 memcpy(res,&header.payload[i],rr.rdlength);
730 res[rr.rdlength] = 0;
733 memcpy(res,&header.payload[i],rr.rdlength);
734 res[rr.rdlength] = 0;
737 return std::make_pair(res,"No error");;
740 /* Close the master socket */
743 shutdown(MasterSocket, 2);
747 /* High level abstraction of dns used by application at large */
748 Resolver::Resolver(const std::string &source, QueryType qt) : input(source), querytype(qt)
755 this->myid = ServerInstance->Res->GetIP(source.c_str());
759 if (insp_aton(source.c_str(), &binip) > 0)
761 /* Valid ip address */
762 this->myid = ServerInstance->Res->GetName(&binip);
766 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
767 throw ModuleException("Resolver: Bad IP address");
773 this->myid = ServerInstance->Res->GetIP6(source.c_str());
776 case DNS_QUERY_CNAME:
777 this->myid = ServerInstance->Res->GetCName(source.c_str());
784 if (this->myid == -1)
786 log(DEBUG,"Resolver::Resolver: Could not get an id!");
787 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
788 throw ModuleException("Resolver: Couldnt get an id to make a request");
789 /* We shouldnt get here really */
793 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
796 void Resolver::OnError(ResolverError e, const std::string &errormessage)
798 /* Nothing in here */
801 Resolver::~Resolver()
803 /* Nothing here (yet) either */
806 /* Get the request id associated with this class */
807 int Resolver::GetId()
812 /* Process a socket read event */
813 void DNS::MarshallReads(int fd)
815 /* We are only intrested in our single fd */
816 if (fd == GetMasterSocket())
818 /* Fetch the id and result of the next available packet */
819 DNSResult res = this->GetResult();
820 /* Is there a usable request id? */
823 /* Its an error reply */
824 if (res.first & ERROR_MASK)
826 /* Mask off the error bit */
827 res.first -= ERROR_MASK;
829 /* Marshall the error to the correct class */
830 log(DEBUG,"Error available, id=%d",res.first);
831 if (Classes[res.first])
833 if (ServerInstance && ServerInstance->stats)
834 ServerInstance->stats->statsDnsBad++;
835 Classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
836 delete Classes[res.first];
837 Classes[res.first] = NULL;
842 /* It is a non-error result */
843 log(DEBUG,"Result available, id=%d",res.first);
844 /* Marshall the result to the correct class */
845 if (Classes[res.first])
847 if (ServerInstance && ServerInstance->stats)
848 ServerInstance->stats->statsDnsGood++;
849 Classes[res.first]->OnLookupComplete(res.second);
850 delete Classes[res.first];
851 Classes[res.first] = NULL;
855 if (ServerInstance && ServerInstance->stats)
856 ServerInstance->stats->statsDns++;
862 /* Add a derived Resolver to the working set */
863 bool DNS::AddResolverClass(Resolver* r)
865 /* Check the pointers validity and the id's validity */
866 if ((r) && (r->GetId() > -1))
868 /* Check the slot isnt already occupied -
869 * This should NEVER happen unless we have
870 * a severely broken DNS server somewhere
872 if (!Classes[r->GetId()])
874 /* Set up the pointer to the class */
875 Classes[r->GetId()] = r;
884 /* Pointer or id not valid.
885 * Free the item and return
894 unsigned long DNS::PRNG()
896 unsigned long val = 0;
898 serverstats* s = ServerInstance->stats;
899 gettimeofday(&n,NULL);
900 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
901 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
902 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - s->BoundPortCount;