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 this->currid &= 0xFFFF;
208 DNSRequest* req = new DNSRequest(this->myserver);
210 header->id[0] = req->id[0] = id >> 8;
211 header->id[1] = req->id[1] = id & 0xFF;
212 header->flags1 = FLAGS_MASK_RD;
219 if (requests.find(id) == requests.end())
222 /* According to the C++ spec, new never returns NULL. */
226 int DNS::GetMasterSocket()
231 /* Initialise the DNS UDP socket so that we can send requests */
236 /* Clear the Resolver class table */
237 memset(Classes,0,sizeof(Classes));
239 /* Set the id of the next request to 0
243 /* Clear the namesever address */
244 memset(&myserver,0,sizeof(insp_inaddr));
246 /* Convert the nameserver address into an insp_inaddr */
247 if (insp_aton(Config->DNSServer,&addr) > 0)
248 memcpy(&myserver,&addr,sizeof(insp_inaddr));
250 /* Initialize mastersocket */
251 MasterSocket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
252 if (MasterSocket != -1)
254 /* Did it succeed? */
255 if (fcntl(MasterSocket, F_SETFL, O_NONBLOCK) != 0)
257 /* Couldn't make the socket nonblocking */
258 shutdown(MasterSocket,2);
263 /* Have we got a socket and is it nonblocking? */
264 if (MasterSocket != -1)
268 memset(&addr,0,sizeof(addr));
269 addr.sin6_family = AF_FAMILY;
271 memset(&addr.sin6_addr,255,sizeof(in6_addr));
274 memset(&addr,0,sizeof(addr));
275 addr.sin_family = AF_FAMILY;
277 addr.sin_addr.s_addr = INADDR_ANY;
280 if (bind(MasterSocket,(sockaddr *)&addr,sizeof(addr)) != 0)
283 log(DEBUG,"Cant bind DNS::MasterSocket");
284 shutdown(MasterSocket,2);
289 if (MasterSocket >= 0)
291 /* Hook the descriptor into the socket engine */
292 if (ServerInstance && ServerInstance->SE)
293 ServerInstance->SE->AddFd(MasterSocket,true,X_ESTAB_DNS);
298 /* Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
299 int DNS::MakePayload(const char * const name, const QueryType rr, const unsigned short rr_class, unsigned char * const payload)
301 short payloadpos = 0;
302 const char* tempchr, *tempchr2 = name;
303 unsigned short length;
305 /* split name up into labels, create query */
306 while ((tempchr = strchr(tempchr2,'.')) != NULL)
308 length = tempchr - tempchr2;
309 if (payloadpos + length + 1 > 507)
311 payload[payloadpos++] = length;
312 memcpy(&payload[payloadpos],tempchr2,length);
313 payloadpos += length;
314 tempchr2 = &tempchr[1];
316 length = strlen(tempchr2);
319 if (payloadpos + length + 2 > 507)
321 payload[payloadpos++] = length;
322 memcpy(&payload[payloadpos],tempchr2,length);
323 payloadpos += length;
324 payload[payloadpos++] = 0;
326 if (payloadpos > 508)
329 memcpy(&payload[payloadpos],&length,2);
330 length = htons(rr_class);
331 memcpy(&payload[payloadpos + 2],&length,2);
332 return payloadpos + 4;
335 /* Start lookup of an hostname to an IP address */
336 int DNS::GetIP(const char *name)
342 if ((length = this->MakePayload(name, DNS_QUERY_A, 1, (unsigned char*)&h.payload)) == -1)
345 DNSRequest* req = this->AddQuery(&h, id);
347 if (req->SendRequests(&h, length, DNS_QUERY_A) == -1)
353 /* Start lookup of an IP address to a hostname */
354 int DNS::GetName(const insp_inaddr *ip)
364 unsigned char* c = (unsigned char*)&ip->s_addr;
366 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
368 if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
371 DNSRequest* req = this->AddQuery(&h, id);
373 if (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)
380 /* Return the next id which is ready, and the result attached to it */
381 DNSResult DNS::GetResult()
383 /* Fetch dns query response and decide where it belongs */
386 unsigned char buffer[sizeof(DNSHeader)];
388 /* Attempt to read a header */
389 int length = recv(MasterSocket,buffer,sizeof(DNSHeader),0);
391 /* Did we get the whole header? */
393 /* Nope - something screwed up. */
394 return std::make_pair(-1,"");
396 /* Put the read header info into a header class */
397 DNS::FillHeader(&header,buffer,length - 12);
399 /* Get the id of this request.
400 * Its a 16 bit value stored in two char's,
401 * so we use logic shifts to create the value.
403 unsigned long this_id = header.id[1] + (header.id[0] << 8);
405 /* Do we have a pending request matching this id? */
406 requestlist_iter n_iter = requests.find(this_id);
407 if (n_iter == requests.end())
409 /* Somehow we got a DNS response for a request we never made... */
410 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",MasterSocket,this_id);
411 return std::make_pair(-1,"");
415 /* Remove the query from the list of pending queries */
416 req = (DNSRequest*)n_iter->second;
417 requests.erase(n_iter);
420 /* Inform the DNSRequest class that it has a result to be read.
421 * When its finished it will return a DNSInfo which is a pair of
422 * unsigned char* resource record data, and an error message.
424 DNSInfo data = req->ResultIsReady(header, length);
425 std::string resultstr;
427 /* Check if we got a result, if we didnt, its an error */
428 if (data.first == NULL)
431 * Mask the ID with the value of ERROR_MASK, so that
432 * the dns_deal_with_classes() function knows that its
433 * an error response and needs to be treated uniquely.
434 * Put the error message in the second field.
437 return std::make_pair(this_id | ERROR_MASK, data.second);
441 /* Forward lookups come back as binary data. We must format them into ascii */
442 if (req->type == DNS_QUERY_A)
445 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
446 resultstr = formatted;
450 /* Reverse lookups just come back as char* */
451 resultstr = std::string((const char*)data.first);
454 /* Build the reply with the id and hostname/ip in it */
456 return std::make_pair(this_id,resultstr);
460 /* A result is ready, process it */
461 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
469 if (!(header.flags1 & FLAGS_MASK_QR))
470 return std::make_pair((unsigned char*)NULL,"Not a query result");
472 if (header.flags1 & FLAGS_MASK_OPCODE)
473 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
475 if (header.flags2 & FLAGS_MASK_RCODE)
476 return std::make_pair((unsigned char*)NULL,"Domain name not found");
478 if (header.ancount < 1)
479 return std::make_pair((unsigned char*)NULL,"No resource records returned");
481 /* Subtract the length of the header from the length of the packet */
484 while ((unsigned int)q < header.qdcount && i < length)
486 if (header.payload[i] > 63)
493 if (header.payload[i] == 0)
498 else i += header.payload[i] + 1;
502 while ((unsigned)curanswer < header.ancount)
505 while (q == 0 && i < length)
507 if (header.payload[i] > 63)
514 if (header.payload[i] == 0)
519 else i += header.payload[i] + 1; /* skip length and label */
523 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
525 DNS::FillResourceRecord(&rr,&header.payload[i]);
527 if (rr.type != this->type)
533 if (rr.rr_class != this->rr_class)
541 if ((unsigned int)curanswer == header.ancount)
542 return std::make_pair((unsigned char*)NULL,"No valid answers");
544 if (i + rr.rdlength > (unsigned int)length)
545 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
547 if (rr.rdlength > 1023)
548 return std::make_pair((unsigned char*)NULL,"Resource record too large");
555 while (q == 0 && i < length && o + 256 < 1023)
557 if (header.payload[i] > 63)
559 memcpy(&ptr,&header.payload[i],2);
560 i = ntohs(ptr) - 0xC000 - 12;
564 if (header.payload[i] == 0)
573 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
574 o += header.payload[i];
575 i += header.payload[i] + 1;
582 memcpy(res,&header.payload[i],rr.rdlength);
583 res[rr.rdlength] = 0;
586 memcpy(res,&header.payload[i],rr.rdlength);
587 res[rr.rdlength] = 0;
590 return std::make_pair(res,"No error");;
593 /* Close the master socket */
596 shutdown(MasterSocket, 2);
600 /* High level abstraction of dns used by application at large */
601 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
605 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
606 this->myid = ServerInstance->Res->GetIP(source.c_str());
610 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
612 if (insp_aton(source.c_str(), &binip) > 0)
614 /* Valid ip address */
615 this->myid = ServerInstance->Res->GetName(&binip);
619 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
620 throw ModuleException("Resolver: Bad IP address");
624 if (this->myid == -1)
626 log(DEBUG,"Resolver::Resolver: Could not get an id!");
627 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
628 throw ModuleException("Resolver: Couldnt get an id to make a request");
629 /* We shouldnt get here really */
633 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
636 void Resolver::OnError(ResolverError e, const std::string &errormessage)
638 /* Nothing in here */
641 Resolver::~Resolver()
643 /* Nothing here (yet) either */
646 /* Get the request id associated with this class */
647 int Resolver::GetId()
652 /* Process a socket read event */
653 void DNS::MarshallReads(int fd)
655 /* We are only intrested in our single fd */
656 if (fd == GetMasterSocket())
658 /* Fetch the id and result of the next available packet */
659 DNSResult res = this->GetResult();
660 /* Is there a usable request id? */
663 /* Its an error reply */
664 if (res.first & ERROR_MASK)
666 /* Mask off the error bit */
667 res.first -= ERROR_MASK;
669 /* Marshall the error to the correct class */
670 log(DEBUG,"Error available, id=%d",res.first);
671 if (Classes[res.first])
673 Classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
674 delete Classes[res.first];
675 Classes[res.first] = NULL;
680 /* It is a non-error result */
681 log(DEBUG,"Result available, id=%d",res.first);
682 /* Marshall the result to the correct class */
683 if (Classes[res.first])
685 Classes[res.first]->OnLookupComplete(res.second);
686 delete Classes[res.first];
687 Classes[res.first] = NULL;
694 /* Add a derived Resolver to the working set */
695 bool DNS::AddResolverClass(Resolver* r)
697 /* Check the pointers validity and the id's validity */
698 if ((r) && (r->GetId() > -1))
700 /* Check the slot isnt already occupied -
701 * This should NEVER happen unless we have
702 * a severely broken DNS server somewhere
704 if (!Classes[r->GetId()])
706 /* Set up the pointer to the class */
707 Classes[r->GetId()] = r;
716 /* Pointer or id not valid.
717 * Free the item and return