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(53);
189 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
190 addr.sin_family = AF_FAMILY;
191 addr.sin_port = htons(53);
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)
206 gettimeofday(&n,NULL);
207 id = (n.tv_usec ^ getpid() ^ geteuid() ^ (currid++)) & 0xFFFF;
209 DNSRequest* req = new DNSRequest(this->myserver);
211 header->id[0] = req->id[0] = id >> 8;
212 header->id[1] = req->id[1] = id & 0xFF;
213 header->flags1 = FLAGS_MASK_RD;
220 if (requests.find(id) == requests.end())
223 /* According to the C++ spec, new never returns NULL. */
227 int DNS::GetMasterSocket()
232 /* Initialise the DNS UDP socket so that we can send requests */
237 /* Clear the Resolver class table */
238 memset(Classes,0,sizeof(Classes));
240 /* Set the id of the next request to 0
244 /* Clear the namesever address */
245 memset(&myserver,0,sizeof(insp_inaddr));
247 /* Convert the nameserver address into an insp_inaddr */
248 if (insp_aton(Config->DNSServer,&addr) > 0)
249 memcpy(&myserver,&addr,sizeof(insp_inaddr));
251 /* Initialize mastersocket */
252 MasterSocket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
253 if (MasterSocket != -1)
255 /* Did it succeed? */
256 if (fcntl(MasterSocket, F_SETFL, O_NONBLOCK) != 0)
258 /* Couldn't make the socket nonblocking */
259 shutdown(MasterSocket,2);
264 /* Have we got a socket and is it nonblocking? */
265 if (MasterSocket != -1)
269 memset(&addr,0,sizeof(addr));
270 addr.sin6_family = AF_FAMILY;
272 memset(&addr.sin6_addr,255,sizeof(in6_addr));
275 memset(&addr,0,sizeof(addr));
276 addr.sin_family = AF_FAMILY;
278 addr.sin_addr.s_addr = INADDR_ANY;
281 if (bind(MasterSocket,(sockaddr *)&addr,sizeof(addr)) != 0)
284 log(DEBUG,"Cant bind DNS::MasterSocket");
285 shutdown(MasterSocket,2);
290 if (MasterSocket >= 0)
292 /* Hook the descriptor into the socket engine */
293 if (ServerInstance && ServerInstance->SE)
294 ServerInstance->SE->AddFd(MasterSocket,true,X_ESTAB_DNS);
299 /* Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
300 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
302 short payloadpos = 0;
303 const char* tempchr, *tempchr2 = name;
304 unsigned short length;
306 /* split name up into labels, create query */
307 while ((tempchr = strchr(tempchr2,'.')) != NULL)
309 length = tempchr - tempchr2;
310 if (payloadpos + length + 1 > 507)
312 payload[payloadpos++] = length;
313 memcpy(&payload[payloadpos],tempchr2,length);
314 payloadpos += length;
315 tempchr2 = &tempchr[1];
317 length = strlen(tempchr2);
320 if (payloadpos + length + 2 > 507)
322 payload[payloadpos++] = length;
323 memcpy(&payload[payloadpos],tempchr2,length);
324 payloadpos += length;
325 payload[payloadpos++] = 0;
327 if (payloadpos > 508)
330 memcpy(&payload[payloadpos],&length,2);
331 length = htons(rr_class);
332 memcpy(&payload[payloadpos + 2],&length,2);
333 return payloadpos + 4;
336 /* Start lookup of an hostname to an IP address */
337 int DNS::GetIP(const char *name)
343 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
346 DNSRequest* req = this->AddQuery(&h, id);
348 if (req->SendRequests(&h, length, DNS_QUERY_A) == -1)
354 /* Start lookup of an IP address to a hostname */
355 int DNS::GetName(const insp_inaddr *ip)
365 unsigned char* c = (unsigned char*)&ip->s_addr;
367 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
369 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
372 DNSRequest* req = this->AddQuery(&h, id);
374 if (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)
381 /* Return the next id which is ready, and the result attached to it */
382 DNSResult DNS::GetResult()
384 /* Fetch dns query response and decide where it belongs */
387 unsigned char buffer[sizeof(DNSHeader)];
389 /* Attempt to read a header */
390 int length = recv(MasterSocket,buffer,sizeof(DNSHeader),0);
392 /* Did we get the whole header? */
394 /* Nope - something screwed up. */
395 return std::make_pair(-1,"");
397 /* Put the read header info into a header class */
398 DNS::FillHeader(&header,buffer,length - 12);
400 /* Get the id of this request.
401 * Its a 16 bit value stored in two char's,
402 * so we use logic shifts to create the value.
404 unsigned long this_id = header.id[1] + (header.id[0] << 8);
406 /* Do we have a pending request matching this id? */
407 requestlist_iter n_iter = requests.find(this_id);
408 if (n_iter == requests.end())
410 /* Somehow we got a DNS response for a request we never made... */
411 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",MasterSocket,this_id);
412 return std::make_pair(-1,"");
416 /* Remove the query from the list of pending queries */
417 req = (DNSRequest*)n_iter->second;
418 requests.erase(n_iter);
421 /* Inform the DNSRequest class that it has a result to be read.
422 * When its finished it will return a DNSInfo which is a pair of
423 * unsigned char* resource record data, and an error message.
425 DNSInfo data = req->ResultIsReady(header, length);
426 std::string resultstr;
428 /* Check if we got a result, if we didnt, its an error */
429 if (data.first == NULL)
432 * Mask the ID with the value of ERROR_MASK, so that
433 * the dns_deal_with_classes() function knows that its
434 * an error response and needs to be treated uniquely.
435 * Put the error message in the second field.
438 return std::make_pair(this_id | ERROR_MASK, data.second);
442 /* Forward lookups come back as binary data. We must format them into ascii */
443 if (req->type == DNS_QUERY_A)
446 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
447 resultstr = formatted;
451 /* Reverse lookups just come back as char* */
452 resultstr = std::string((const char*)data.first);
455 /* Build the reply with the id and hostname/ip in it */
457 return std::make_pair(this_id,resultstr);
461 /* A result is ready, process it */
462 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
470 if (!(header.flags1 & FLAGS_MASK_QR))
471 return std::make_pair((unsigned char*)NULL,"Not a query result");
473 if (header.flags1 & FLAGS_MASK_OPCODE)
474 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
476 if (header.flags2 & FLAGS_MASK_RCODE)
477 return std::make_pair((unsigned char*)NULL,"Domain name not found");
479 if (header.ancount < 1)
480 return std::make_pair((unsigned char*)NULL,"No resource records returned");
482 /* Subtract the length of the header from the length of the packet */
485 while ((unsigned int)q < header.qdcount && i < length)
487 if (header.payload[i] > 63)
494 if (header.payload[i] == 0)
499 else i += header.payload[i] + 1;
503 while ((unsigned)curanswer < header.ancount)
506 while (q == 0 && i < length)
508 if (header.payload[i] > 63)
515 if (header.payload[i] == 0)
520 else i += header.payload[i] + 1; /* skip length and label */
524 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
526 DNS::FillResourceRecord(&rr,&header.payload[i]);
528 if (rr.type != this->type)
534 if (rr.rr_class != this->rr_class)
542 if ((unsigned int)curanswer == header.ancount)
543 return std::make_pair((unsigned char*)NULL,"No valid answers");
545 if (i + rr.rdlength > (unsigned int)length)
546 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
548 if (rr.rdlength > 1023)
549 return std::make_pair((unsigned char*)NULL,"Resource record too large");
556 while (q == 0 && i < length && o + 256 < 1023)
558 if (header.payload[i] > 63)
560 memcpy(&ptr,&header.payload[i],2);
561 i = ntohs(ptr) - 0xC000 - 12;
565 if (header.payload[i] == 0)
574 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
575 o += header.payload[i];
576 i += header.payload[i] + 1;
583 memcpy(res,&header.payload[i],rr.rdlength);
584 res[rr.rdlength] = 0;
587 memcpy(res,&header.payload[i],rr.rdlength);
588 res[rr.rdlength] = 0;
591 return std::make_pair(res,"No error");;
594 /* Close the master socket */
597 shutdown(MasterSocket, 2);
601 /* High level abstraction of dns used by application at large */
602 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
606 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
607 this->myid = ServerInstance->Res->GetIP(source.c_str());
611 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
613 if (insp_aton(source.c_str(), &binip) > 0)
615 /* Valid ip address */
616 this->myid = ServerInstance->Res->GetName(&binip);
620 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
621 throw ModuleException("Resolver: Bad IP address");
625 if (this->myid == -1)
627 log(DEBUG,"Resolver::Resolver: Could not get an id!");
628 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
629 throw ModuleException("Resolver: Couldnt get an id to make a request");
630 /* We shouldnt get here really */
634 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
637 void Resolver::OnError(ResolverError e, const std::string &errormessage)
639 /* Nothing in here */
642 Resolver::~Resolver()
644 /* Nothing here (yet) either */
647 /* Get the request id associated with this class */
648 int Resolver::GetId()
653 /* Process a socket read event */
654 void DNS::MarshallReads(int fd)
656 /* We are only intrested in our single fd */
657 if (fd == GetMasterSocket())
659 /* Fetch the id and result of the next available packet */
660 DNSResult res = this->GetResult();
661 /* Is there a usable request id? */
664 /* Its an error reply */
665 if (res.first & ERROR_MASK)
667 /* Mask off the error bit */
668 res.first -= ERROR_MASK;
670 /* Marshall the error to the correct class */
671 log(DEBUG,"Error available, id=%d",res.first);
672 if (Classes[res.first])
674 if (ServerInstance && ServerInstance->stats)
675 ServerInstance->stats->statsDnsBad++;
676 Classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
677 delete Classes[res.first];
678 Classes[res.first] = NULL;
683 /* It is a non-error result */
684 log(DEBUG,"Result available, id=%d",res.first);
685 /* Marshall the result to the correct class */
686 if (Classes[res.first])
688 if (ServerInstance && ServerInstance->stats)
689 ServerInstance->stats->statsDnsGood++;
690 Classes[res.first]->OnLookupComplete(res.second);
691 delete Classes[res.first];
692 Classes[res.first] = NULL;
696 if (ServerInstance && ServerInstance->stats)
697 ServerInstance->stats->statsDns++;
703 /* Add a derived Resolver to the working set */
704 bool DNS::AddResolverClass(Resolver* r)
706 /* Check the pointers validity and the id's validity */
707 if ((r) && (r->GetId() > -1))
709 /* Check the slot isnt already occupied -
710 * This should NEVER happen unless we have
711 * a severely broken DNS server somewhere
713 if (!Classes[r->GetId()])
715 /* Set up the pointer to the class */
716 Classes[r->GetId()] = r;
725 /* Pointer or id not valid.
726 * Free the item and return