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 /* Query and resource record types */
56 DNS_QRY_A = 1, /* 'A' record: an IP address */
57 DNS_QRY_PTR = 12 /* 'PTR' record: a hostname */
60 /* Masks to mask off the responses we get from the DNSRequest methods */
63 ERROR_MASK = 0x10000 /* Result is an error */
66 /* Flags which can be ORed into a request or reply for different meanings */
69 FLAGS_MASK_RD = 0x01, /* Recursive */
71 FLAGS_MASK_AA = 0x04, /* Authoritative */
72 FLAGS_MASK_OPCODE = 0x78,
74 FLAGS_MASK_RCODE = 0x0F, /* Request */
80 /* Represents a dns resource record (rr) */
84 QueryType type; /* Record type */
85 unsigned int rr_class; /* Record class */
86 unsigned long ttl; /* Time to live */
87 unsigned int rdlength; /* Record length */
90 /* Represents a dns request/reply header, and its payload as opaque data.
95 unsigned char id[2]; /* Request id */
96 unsigned int flags1; /* Flags */
97 unsigned int flags2; /* Flags */
99 unsigned int ancount; /* Answer count */
100 unsigned int nscount; /* Nameserver count */
101 unsigned int arcount;
102 unsigned char payload[512]; /* Packet payload */
105 /* Represents a request 'on the wire' with routing information relating to
106 * where to call when we get a result
111 unsigned char id[2]; /* Request id */
112 unsigned char* res; /* Result processing buffer */
113 unsigned int rr_class; /* Request class */
114 QueryType type; /* Request type */
115 insp_inaddr myserver; /* DNS server address*/
117 /* Allocate the processing buffer */
118 DNSRequest(insp_inaddr server)
120 res = new unsigned char[512];
122 memcpy(&myserver, &server, sizeof(insp_inaddr));
125 /* Deallocate the processing buffer */
131 /* Called when a result is ready to be processed which matches this id */
132 DNSInfo ResultIsReady(DNSHeader &h, int length);
134 /* Called when there are requests to be sent out */
135 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
138 /* Fill a ResourceRecord class based on raw data input */
139 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
141 rr->type = (QueryType)((input[0] << 8) + input[1]);
142 rr->rr_class = (input[2] << 8) + input[3];
143 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
144 rr->rdlength = (input[8] << 8) + input[9];
147 /* Fill a DNSHeader class based on raw data input of a given length */
148 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
150 header->id[0] = input[0];
151 header->id[1] = input[1];
152 header->flags1 = input[2];
153 header->flags2 = input[3];
154 header->qdcount = (input[4] << 8) + input[5];
155 header->ancount = (input[6] << 8) + input[7];
156 header->nscount = (input[8] << 8) + input[9];
157 header->arcount = (input[10] << 8) + input[11];
158 memcpy(header->payload,&input[12],length);
161 /* Empty a DNSHeader class out into raw data, ready for transmission */
162 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
164 output[0] = header->id[0];
165 output[1] = header->id[1];
166 output[2] = header->flags1;
167 output[3] = header->flags2;
168 output[4] = header->qdcount >> 8;
169 output[5] = header->qdcount & 0xFF;
170 output[6] = header->ancount >> 8;
171 output[7] = header->ancount & 0xFF;
172 output[8] = header->nscount >> 8;
173 output[9] = header->nscount & 0xFF;
174 output[10] = header->arcount >> 8;
175 output[11] = header->arcount & 0xFF;
176 memcpy(&output[12],header->payload,length);
179 /* Send requests we have previously built down the UDP socket */
180 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
183 unsigned char payload[sizeof(DNSHeader)];
188 DNS::EmptyHeader(payload,header,length);
190 memset(&addr,0,sizeof(addr));
192 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
193 addr.sin6_family = AF_FAMILY;
194 addr.sin6_port = htons(53);
196 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
197 addr.sin_family = AF_FAMILY;
198 addr.sin_port = htons(53);
200 if (sendto(DNS::GetMasterSocket(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
202 log(DEBUG,"Error in sendto!");
209 /* Add a query with a predefined header, and allocate an ID for it. */
210 DNSRequest* DNS::AddQuery(DNSHeader *header, int &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 if (requests.find(id) == requests.end())
227 /* According to the C++ spec, new never returns NULL. */
231 int DNS::GetMasterSocket()
236 /* Initialise the DNS UDP socket so that we can send requests */
241 /* Clear the Resolver class table */
242 memset(Classes,0,sizeof(Classes));
244 /* Seed random number generator, we use this to generate
247 srand((unsigned int)time(NULL));
249 /* Clear the namesever address */
250 memset(&myserver,0,sizeof(insp_inaddr));
252 /* Convert the nameserver address into an insp_inaddr */
253 if (insp_aton(Config->DNSServer,&addr) > 0)
254 memcpy(&myserver,&addr,sizeof(insp_inaddr));
256 /* Initialize mastersocket */
257 MasterSocket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
258 if (MasterSocket != -1)
260 /* Did it succeed? */
261 if (fcntl(MasterSocket, F_SETFL, O_NONBLOCK) != 0)
263 /* Couldn't make the socket nonblocking */
264 shutdown(MasterSocket,2);
269 /* Have we got a socket and is it nonblocking? */
270 if (MasterSocket != -1)
274 memset(&addr,0,sizeof(addr));
275 addr.sin6_family = AF_FAMILY;
277 memset(&addr.sin6_addr,255,sizeof(in6_addr));
280 memset(&addr,0,sizeof(addr));
281 addr.sin_family = AF_FAMILY;
283 addr.sin_addr.s_addr = INADDR_ANY;
286 if (bind(MasterSocket,(sockaddr *)&addr,sizeof(addr)) != 0)
289 log(DEBUG,"Cant bind DNS::MasterSocket");
290 shutdown(MasterSocket,2);
295 if (MasterSocket >= 0)
297 /* Hook the descriptor into the socket engine */
298 if (ServerInstance && ServerInstance->SE)
299 ServerInstance->SE->AddFd(MasterSocket,true,X_ESTAB_DNS);
304 /* Build a payload to be placed after the header, based upon input data, a resource type, a class and a pointer to a buffer */
305 int DNS::MakePayload(const char * const name, const unsigned short rr, const unsigned short rr_class, unsigned char * const payload)
307 short payloadpos = 0;
308 const char* tempchr, *tempchr2 = name;
309 unsigned short length;
311 /* split name up into labels, create query */
312 while ((tempchr = strchr(tempchr2,'.')) != NULL)
314 length = tempchr - tempchr2;
315 if (payloadpos + length + 1 > 507)
317 payload[payloadpos++] = length;
318 memcpy(&payload[payloadpos],tempchr2,length);
319 payloadpos += length;
320 tempchr2 = &tempchr[1];
322 length = strlen(tempchr2);
325 if (payloadpos + length + 2 > 507)
327 payload[payloadpos++] = length;
328 memcpy(&payload[payloadpos],tempchr2,length);
329 payloadpos += length;
330 payload[payloadpos++] = 0;
332 if (payloadpos > 508)
335 memcpy(&payload[payloadpos],&length,2);
336 length = htons(rr_class);
337 memcpy(&payload[payloadpos + 2],&length,2);
338 return payloadpos + 4;
341 /* Start lookup of an hostname to an IP address */
342 int DNS::GetIP(const char *name)
348 if ((length = this->MakePayload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
351 DNSRequest* req = this->AddQuery(&h, id);
353 if (req->SendRequests(&h,length,DNS_QRY_A) == -1)
359 /* Start lookup of an IP address to a hostname */
360 int DNS::GetName(const insp_inaddr *ip)
370 unsigned char* c = (unsigned char*)&ip->s_addr;
372 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
374 if ((length = this->MakePayload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
377 DNSRequest* req = this->AddQuery(&h, id);
379 if (req->SendRequests(&h,length,DNS_QRY_PTR) == -1)
386 /* Return the next id which is ready, and the result attached to it */
387 DNSResult DNS::GetResult()
389 /* Fetch dns query response and decide where it belongs */
392 unsigned char buffer[sizeof(DNSHeader)];
394 /* Attempt to read a header */
395 int length = recv(MasterSocket,buffer,sizeof(DNSHeader),0);
397 /* Did we get the whole header? */
399 /* Nope - something screwed up. */
400 return std::make_pair(-1,"");
402 /* Put the read header info into a header class */
403 DNS::FillHeader(&header,buffer,length - 12);
405 /* Get the id of this request.
406 * Its a 16 bit value stored in two char's,
407 * so we use logic shifts to create the value.
409 unsigned long this_id = header.id[1] + (header.id[0] << 8);
411 /* Do we have a pending request matching this id? */
412 requestlist_iter n_iter = requests.find(this_id);
413 if (n_iter == requests.end())
415 /* Somehow we got a DNS response for a request we never made... */
416 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",MasterSocket,this_id);
417 return std::make_pair(-1,"");
421 /* Remove the query from the list of pending queries */
422 req = (DNSRequest*)n_iter->second;
423 requests.erase(n_iter);
426 /* Inform the DNSRequest class that it has a result to be read.
427 * When its finished it will return a DNSInfo which is a pair of
428 * unsigned char* resource record data, and an error message.
430 DNSInfo data = req->ResultIsReady(header, length);
431 std::string resultstr;
433 /* Check if we got a result, if we didnt, its an error */
434 if (data.first == NULL)
437 * Mask the ID with the value of ERROR_MASK, so that
438 * the dns_deal_with_classes() function knows that its
439 * an error response and needs to be treated uniquely.
440 * Put the error message in the second field.
443 return std::make_pair(this_id | ERROR_MASK, data.second);
447 /* Forward lookups come back as binary data. We must format them into ascii */
448 if (req->type == DNS_QRY_A)
451 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
452 resultstr = formatted;
456 /* Reverse lookups just come back as char* */
457 resultstr = std::string((const char*)data.first);
460 /* Build the reply with the id and hostname/ip in it */
462 return std::make_pair(this_id,resultstr);
466 /* A result is ready, process it */
467 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
475 if (!(header.flags1 & FLAGS_MASK_QR))
476 return std::make_pair((unsigned char*)NULL,"Not a query result");
478 if (header.flags1 & FLAGS_MASK_OPCODE)
479 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
481 if (header.flags2 & FLAGS_MASK_RCODE)
482 return std::make_pair((unsigned char*)NULL,"Domain name not found");
484 if (header.ancount < 1)
485 return std::make_pair((unsigned char*)NULL,"No resource records returned");
487 /* Subtract the length of the header from the length of the packet */
490 while ((unsigned int)q < header.qdcount && i < length)
492 if (header.payload[i] > 63)
499 if (header.payload[i] == 0)
504 else i += header.payload[i] + 1;
508 while ((unsigned)curanswer < header.ancount)
511 while (q == 0 && i < length)
513 if (header.payload[i] > 63)
520 if (header.payload[i] == 0)
525 else i += header.payload[i] + 1; /* skip length and label */
529 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
531 DNS::FillResourceRecord(&rr,&header.payload[i]);
533 if (rr.type != this->type)
539 if (rr.rr_class != this->rr_class)
547 if ((unsigned int)curanswer == header.ancount)
548 return std::make_pair((unsigned char*)NULL,"No valid answers");
550 if (i + rr.rdlength > (unsigned int)length)
551 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
553 if (rr.rdlength > 1023)
554 return std::make_pair((unsigned char*)NULL,"Resource record too large");
561 while (q == 0 && i < length && o + 256 < 1023)
563 if (header.payload[i] > 63)
565 memcpy(&ptr,&header.payload[i],2);
566 i = ntohs(ptr) - 0xC000 - 12;
570 if (header.payload[i] == 0)
579 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
580 o += header.payload[i];
581 i += header.payload[i] + 1;
588 memcpy(res,&header.payload[i],rr.rdlength);
589 res[rr.rdlength] = 0;
592 memcpy(res,&header.payload[i],rr.rdlength);
593 res[rr.rdlength] = 0;
596 return std::make_pair(res,"No error");;
601 shutdown(MasterSocket, 2);
605 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
609 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
610 this->myid = ServerInstance->Res->GetIP(source.c_str());
614 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
616 if (insp_aton(source.c_str(), &binip) > 0)
618 /* Valid ip address */
619 this->myid = ServerInstance->Res->GetName(&binip);
623 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
624 throw ModuleException("Resolver: Bad IP address");
628 if (this->myid == -1)
630 log(DEBUG,"Resolver::Resolver: Could not get an id!");
631 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
632 throw ModuleException("Resolver: Couldnt get an id to make a request");
633 /* We shouldnt get here really */
637 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
640 void Resolver::OnError(ResolverError e, const std::string &errormessage)
644 Resolver::~Resolver()
646 log(DEBUG,"Resolver::~Resolver");
649 int Resolver::GetId()
654 void DNS::MarshallReads(int fd)
656 log(DEBUG,"dns_deal_with_classes(%d)",fd);
657 if (fd == GetMasterSocket())
659 DNSResult res = this->GetResult();
662 if (res.first & ERROR_MASK)
664 res.first -= ERROR_MASK;
666 log(DEBUG,"Error available, id=%d",res.first);
667 if (Classes[res.first])
669 Classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
670 delete Classes[res.first];
671 Classes[res.first] = NULL;
676 log(DEBUG,"Result available, id=%d",res.first);
677 if (Classes[res.first])
679 Classes[res.first]->OnLookupComplete(res.second);
680 delete Classes[res.first];
681 Classes[res.first] = NULL;
688 bool DNS::AddResolverClass(Resolver* r)
690 if ((r) && (r->GetId() > -1))
692 if (!Classes[r->GetId()])
694 Classes[r->GetId()] = r;