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 id = this->PRNG() & DNS::MAX_REQUEST_ID;
207 /* This id is already 'in flight', pick another.
210 while (requests.find(id) != requests.end())
211 id = this->PRNG() & DNS::MAX_REQUEST_ID;
213 DNSRequest* req = new DNSRequest(this->myserver);
215 header->id[0] = req->id[0] = id >> 8;
216 header->id[1] = req->id[1] = id & 0xFF;
217 header->flags1 = FLAGS_MASK_RD;
224 /* At this point we already know the id doesnt exist,
225 * so there needs to be no second check for the ::end()
229 /* According to the C++ spec, new never returns NULL. */
233 int DNS::GetMasterSocket()
238 /* Initialise the DNS UDP socket so that we can send requests */
243 /* Clear the Resolver class table */
244 memset(Classes,0,sizeof(Classes));
246 /* Set the id of the next request to 0
250 /* Clear the namesever address */
251 memset(&myserver,0,sizeof(insp_inaddr));
253 /* Convert the nameserver address into an insp_inaddr */
254 if (insp_aton(Config->DNSServer,&addr) > 0)
255 memcpy(&myserver,&addr,sizeof(insp_inaddr));
257 /* Initialize mastersocket */
258 MasterSocket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
259 if (MasterSocket != -1)
261 /* Did it succeed? */
262 if (fcntl(MasterSocket, F_SETFL, O_NONBLOCK) != 0)
264 /* Couldn't make the socket nonblocking */
265 shutdown(MasterSocket,2);
270 /* Have we got a socket and is it nonblocking? */
271 if (MasterSocket != -1)
275 memset(&addr,0,sizeof(addr));
276 addr.sin6_family = AF_FAMILY;
278 memset(&addr.sin6_addr,255,sizeof(in6_addr));
281 memset(&addr,0,sizeof(addr));
282 addr.sin_family = AF_FAMILY;
284 addr.sin_addr.s_addr = INADDR_ANY;
287 if (bind(MasterSocket,(sockaddr *)&addr,sizeof(addr)) != 0)
290 log(DEBUG,"Cant bind DNS::MasterSocket");
291 shutdown(MasterSocket,2);
296 if (MasterSocket >= 0)
298 /* Hook the descriptor into the socket engine */
299 if (ServerInstance && ServerInstance->SE)
300 ServerInstance->SE->AddFd(MasterSocket,true,X_ESTAB_DNS);
305 /* Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
306 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
308 short payloadpos = 0;
309 const char* tempchr, *tempchr2 = name;
310 unsigned short length;
312 /* split name up into labels, create query */
313 while ((tempchr = strchr(tempchr2,'.')) != NULL)
315 length = tempchr - tempchr2;
316 if (payloadpos + length + 1 > 507)
318 payload[payloadpos++] = length;
319 memcpy(&payload[payloadpos],tempchr2,length);
320 payloadpos += length;
321 tempchr2 = &tempchr[1];
323 length = strlen(tempchr2);
326 if (payloadpos + length + 2 > 507)
328 payload[payloadpos++] = length;
329 memcpy(&payload[payloadpos],tempchr2,length);
330 payloadpos += length;
331 payload[payloadpos++] = 0;
333 if (payloadpos > 508)
336 memcpy(&payload[payloadpos],&length,2);
337 length = htons(rr_class);
338 memcpy(&payload[payloadpos + 2],&length,2);
339 return payloadpos + 4;
342 /* Start lookup of an hostname to an IP address */
343 int DNS::GetIP(const char *name)
349 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
352 DNSRequest* req = this->AddQuery(&h, id);
354 if (req->SendRequests(&h, length, DNS_QUERY_A) == -1)
360 /* Start lookup of an IP address to a hostname */
361 int DNS::GetName(const insp_inaddr *ip)
371 unsigned char* c = (unsigned char*)&ip->s_addr;
373 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
375 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
378 DNSRequest* req = this->AddQuery(&h, id);
380 if (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)
387 /* Return the next id which is ready, and the result attached to it */
388 DNSResult DNS::GetResult()
390 /* Fetch dns query response and decide where it belongs */
393 unsigned char buffer[sizeof(DNSHeader)];
395 socklen_t x = sizeof(from);
396 const char* ipaddr_from = "";
397 unsigned short int port_from = 0;
399 int length = recvfrom(MasterSocket,buffer,sizeof(DNSHeader),0,&from,&x);
401 /* Did we get the whole header? */
403 /* Nope - something screwed up. */
404 return std::make_pair(-1,"");
406 /* Check wether the reply came from a different DNS
407 * server to the one we sent it to, or the source-port
409 * A user could in theory still spoof dns packets anyway
410 * but this is less trivial than just sending garbage
411 * to the client, which is possible without this check.
413 * -- Thanks jilles for pointing this one out.
416 ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin6_addr);
417 port_from = ntohs(((sockaddr_in*)&from)->sin6_port);
419 ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
420 port_from = ntohs(((sockaddr_in*)&from)->sin_port);
423 if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, Config->DNSServer)))
424 return std::make_pair(-1,"");
426 /* Put the read header info into a header class */
427 DNS::FillHeader(&header,buffer,length - 12);
429 /* Get the id of this request.
430 * Its a 16 bit value stored in two char's,
431 * so we use logic shifts to create the value.
433 unsigned long this_id = header.id[1] + (header.id[0] << 8);
435 /* Do we have a pending request matching this id? */
436 requestlist_iter n_iter = requests.find(this_id);
437 if (n_iter == requests.end())
439 /* Somehow we got a DNS response for a request we never made... */
440 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",MasterSocket,this_id);
441 return std::make_pair(-1,"");
445 /* Remove the query from the list of pending queries */
446 req = (DNSRequest*)n_iter->second;
447 requests.erase(n_iter);
450 /* Inform the DNSRequest class that it has a result to be read.
451 * When its finished it will return a DNSInfo which is a pair of
452 * unsigned char* resource record data, and an error message.
454 DNSInfo data = req->ResultIsReady(header, length);
455 std::string resultstr;
457 /* Check if we got a result, if we didnt, its an error */
458 if (data.first == NULL)
461 * Mask the ID with the value of ERROR_MASK, so that
462 * the dns_deal_with_classes() function knows that its
463 * an error response and needs to be treated uniquely.
464 * Put the error message in the second field.
467 return std::make_pair(this_id | ERROR_MASK, data.second);
471 /* Forward lookups come back as binary data. We must format them into ascii */
472 if (req->type == DNS_QUERY_A)
475 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
476 resultstr = formatted;
480 /* Reverse lookups just come back as char* */
481 resultstr = std::string((const char*)data.first);
484 /* Build the reply with the id and hostname/ip in it */
486 return std::make_pair(this_id,resultstr);
490 /* A result is ready, process it */
491 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
499 if (!(header.flags1 & FLAGS_MASK_QR))
500 return std::make_pair((unsigned char*)NULL,"Not a query result");
502 if (header.flags1 & FLAGS_MASK_OPCODE)
503 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
505 if (header.flags2 & FLAGS_MASK_RCODE)
506 return std::make_pair((unsigned char*)NULL,"Domain name not found");
508 if (header.ancount < 1)
509 return std::make_pair((unsigned char*)NULL,"No resource records returned");
511 /* Subtract the length of the header from the length of the packet */
514 while ((unsigned int)q < header.qdcount && i < length)
516 if (header.payload[i] > 63)
523 if (header.payload[i] == 0)
528 else i += header.payload[i] + 1;
532 while ((unsigned)curanswer < header.ancount)
535 while (q == 0 && i < length)
537 if (header.payload[i] > 63)
544 if (header.payload[i] == 0)
549 else i += header.payload[i] + 1; /* skip length and label */
553 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
555 DNS::FillResourceRecord(&rr,&header.payload[i]);
557 if (rr.type != this->type)
563 if (rr.rr_class != this->rr_class)
571 if ((unsigned int)curanswer == header.ancount)
572 return std::make_pair((unsigned char*)NULL,"No valid answers");
574 if (i + rr.rdlength > (unsigned int)length)
575 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
577 if (rr.rdlength > 1023)
578 return std::make_pair((unsigned char*)NULL,"Resource record too large");
585 while (q == 0 && i < length && o + 256 < 1023)
587 if (header.payload[i] > 63)
589 memcpy(&ptr,&header.payload[i],2);
590 i = ntohs(ptr) - 0xC000 - 12;
594 if (header.payload[i] == 0)
603 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
604 o += header.payload[i];
605 i += header.payload[i] + 1;
612 memcpy(res,&header.payload[i],rr.rdlength);
613 res[rr.rdlength] = 0;
616 memcpy(res,&header.payload[i],rr.rdlength);
617 res[rr.rdlength] = 0;
620 return std::make_pair(res,"No error");;
623 /* Close the master socket */
626 shutdown(MasterSocket, 2);
630 /* High level abstraction of dns used by application at large */
631 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
635 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
636 this->myid = ServerInstance->Res->GetIP(source.c_str());
640 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
642 if (insp_aton(source.c_str(), &binip) > 0)
644 /* Valid ip address */
645 this->myid = ServerInstance->Res->GetName(&binip);
649 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
650 throw ModuleException("Resolver: Bad IP address");
654 if (this->myid == -1)
656 log(DEBUG,"Resolver::Resolver: Could not get an id!");
657 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
658 throw ModuleException("Resolver: Couldnt get an id to make a request");
659 /* We shouldnt get here really */
663 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
666 void Resolver::OnError(ResolverError e, const std::string &errormessage)
668 /* Nothing in here */
671 Resolver::~Resolver()
673 /* Nothing here (yet) either */
676 /* Get the request id associated with this class */
677 int Resolver::GetId()
682 /* Process a socket read event */
683 void DNS::MarshallReads(int fd)
685 /* We are only intrested in our single fd */
686 if (fd == GetMasterSocket())
688 /* Fetch the id and result of the next available packet */
689 DNSResult res = this->GetResult();
690 /* Is there a usable request id? */
693 /* Its an error reply */
694 if (res.first & ERROR_MASK)
696 /* Mask off the error bit */
697 res.first -= ERROR_MASK;
699 /* Marshall the error to the correct class */
700 log(DEBUG,"Error available, id=%d",res.first);
701 if (Classes[res.first])
703 if (ServerInstance && ServerInstance->stats)
704 ServerInstance->stats->statsDnsBad++;
705 Classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
706 delete Classes[res.first];
707 Classes[res.first] = NULL;
712 /* It is a non-error result */
713 log(DEBUG,"Result available, id=%d",res.first);
714 /* Marshall the result to the correct class */
715 if (Classes[res.first])
717 if (ServerInstance && ServerInstance->stats)
718 ServerInstance->stats->statsDnsGood++;
719 Classes[res.first]->OnLookupComplete(res.second);
720 delete Classes[res.first];
721 Classes[res.first] = NULL;
725 if (ServerInstance && ServerInstance->stats)
726 ServerInstance->stats->statsDns++;
732 /* Add a derived Resolver to the working set */
733 bool DNS::AddResolverClass(Resolver* r)
735 /* Check the pointers validity and the id's validity */
736 if ((r) && (r->GetId() > -1))
738 /* Check the slot isnt already occupied -
739 * This should NEVER happen unless we have
740 * a severely broken DNS server somewhere
742 if (!Classes[r->GetId()])
744 /* Set up the pointer to the class */
745 Classes[r->GetId()] = r;
754 /* Pointer or id not valid.
755 * Free the item and return
764 unsigned long DNS::PRNG()
766 unsigned long val = 0;
768 serverstats* s = ServerInstance->stats;
769 gettimeofday(&n,NULL);
770 val = (n.tv_usec ^ getpid() ^ geteuid() ^ (this->currid++)) ^ s->statsAccept + n.tv_sec;
771 val = val + s->statsCollisions ^ s->statsDnsGood - s->statsDnsBad;
772 val += (s->statsConnects ^ (unsigned long)s->statsSent ^ (unsigned long)s->statsRecv) - s->BoundPortCount;