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 /* A set of requests keyed by request id */
139 typedef std::map<int,DNSRequest*> requestlist;
141 /* An iterator into a set of requests */
142 typedef requestlist::iterator requestlist_iter;
144 /* Declare our map */
145 requestlist requests;
148 * Optimized by brain, these were using integer division and modulus.
149 * We can use logic shifts and logic AND to replace these even divisions
150 * and multiplications, it should be a bit faster (probably not noticably,
151 * but of course, more impressive). Also made these inline.
155 /* Fill a ResourceRecord class based on raw data input */
156 inline void DNSFillResourceRecord(ResourceRecord* rr, const unsigned char *input)
158 rr->type = (QueryType)((input[0] << 8) + input[1]);
159 rr->rr_class = (input[2] << 8) + input[3];
160 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
161 rr->rdlength = (input[8] << 8) + input[9];
164 /* Fill a DNSHeader class based on raw data input of a given length */
165 inline void DNSFillHeader(DNSHeader *header, const unsigned char *input, const int length)
167 header->id[0] = input[0];
168 header->id[1] = input[1];
169 header->flags1 = input[2];
170 header->flags2 = input[3];
171 header->qdcount = (input[4] << 8) + input[5];
172 header->ancount = (input[6] << 8) + input[7];
173 header->nscount = (input[8] << 8) + input[9];
174 header->arcount = (input[10] << 8) + input[11];
175 memcpy(header->payload,&input[12],length);
178 /* Empty a DNSHeader class out into raw data, ready for transmission */
179 inline void DNSEmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
181 output[0] = header->id[0];
182 output[1] = header->id[1];
183 output[2] = header->flags1;
184 output[3] = header->flags2;
185 output[4] = header->qdcount >> 8;
186 output[5] = header->qdcount & 0xFF;
187 output[6] = header->ancount >> 8;
188 output[7] = header->ancount & 0xFF;
189 output[8] = header->nscount >> 8;
190 output[9] = header->nscount & 0xFF;
191 output[10] = header->arcount >> 8;
192 output[11] = header->arcount & 0xFF;
193 memcpy(&output[12],header->payload,length);
196 /* Send requests we have previously built down the UDP socket */
197 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
200 unsigned char payload[sizeof(DNSHeader)];
205 DNSEmptyHeader(payload,header,length);
207 memset(&addr,0,sizeof(addr));
209 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
210 addr.sin6_family = AF_FAMILY;
211 addr.sin6_port = htons(53);
213 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
214 addr.sin_family = AF_FAMILY;
215 addr.sin_port = htons(53);
217 if (sendto(DNS::GetMasterSocket(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
219 log(DEBUG,"Error in sendto!");
226 /* Add a query with a predefined header, and allocate an ID for it. */
227 DNSRequest* DNS::DNSAddQuery(DNSHeader *header, int &id)
230 DNSRequest* req = new DNSRequest(this->myserver);
232 header->id[0] = req->id[0] = id >> 8;
233 header->id[1] = req->id[1] = id & 0xFF;
234 header->flags1 = FLAGS_MASK_RD;
241 if (requests.find(id) == requests.end())
244 /* According to the C++ spec, new never returns NULL. */
248 int DNS::GetMasterSocket()
253 /* Initialise the DNS UDP socket so that we can send requests */
256 log(DEBUG,"----- Initialize dns class ----- ");
257 memset(Classes,0,sizeof(Classes));
259 srand((unsigned int)time(NULL));
260 memset(&myserver,0,sizeof(insp_inaddr));
261 if (insp_aton(Config->DNSServer,&addr) > 0)
262 memcpy(&myserver,&addr,sizeof(insp_inaddr));
264 MasterSocket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
265 if (MasterSocket != -1)
267 log(DEBUG,"Set query socket nonblock");
268 if (fcntl(MasterSocket, F_SETFL, O_NONBLOCK) != 0)
270 shutdown(MasterSocket,2);
275 if (MasterSocket != -1)
279 memset(&addr,0,sizeof(addr));
280 addr.sin6_family = AF_FAMILY;
282 memset(&addr.sin6_addr,255,sizeof(in6_addr));
285 memset(&addr,0,sizeof(addr));
286 addr.sin_family = AF_FAMILY;
288 addr.sin_addr.s_addr = INADDR_ANY;
290 log(DEBUG,"Binding query port");
291 if (bind(MasterSocket,(sockaddr *)&addr,sizeof(addr)) != 0)
293 log(DEBUG,"Cant bind with source port = 0");
294 shutdown(MasterSocket,2);
299 if (MasterSocket >= 0)
301 log(DEBUG,"Attach query port to socket engine");
302 if (ServerInstance && ServerInstance->SE)
303 ServerInstance->SE->AddFd(MasterSocket,true,X_ESTAB_DNS);
308 int DNSMakePayload(const char * const name, const unsigned short rr, const unsigned short rr_class, unsigned char * const payload)
311 const char * tempchr, * tempchr2;
317 /* split name up into labels, create query */
318 while ((tempchr = strchr(tempchr2,'.')) != NULL)
320 l = tempchr - tempchr2;
321 if (payloadpos + l + 1 > 507)
323 payload[payloadpos++] = l;
324 memcpy(&payload[payloadpos],tempchr2,l);
326 tempchr2 = &tempchr[1];
328 l = strlen(tempchr2);
331 if (payloadpos + l + 2 > 507)
333 payload[payloadpos++] = l;
334 memcpy(&payload[payloadpos],tempchr2,l);
336 payload[payloadpos++] = 0;
338 if (payloadpos > 508)
341 memcpy(&payload[payloadpos],&l,2);
343 memcpy(&payload[payloadpos + 2],&l,2);
344 return payloadpos + 4;
347 int DNS::GetIP(const char *name)
354 if ((length = DNSMakePayload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
357 req = DNSAddQuery(&h, id);
359 if (req->SendRequests(&h,length,DNS_QRY_A) == -1)
365 int DNS::GetName(const insp_inaddr *ip)
376 unsigned char* c = (unsigned char*)&ip->s_addr;
378 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
380 if ((length = DNSMakePayload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
383 req = DNSAddQuery(&h, id);
385 if (req->SendRequests(&h,length,DNS_QRY_PTR) == -1)
392 /* Return the next id which is ready, and the result attached to it
394 DNSResult DNS::GetResult()
396 /* Fetch dns query response and decide where it belongs */
400 unsigned char buffer[sizeof(DNSHeader)];
402 /* Attempt to read a header */
403 length = recv(MasterSocket,buffer,sizeof(DNSHeader),0);
405 /* Did we get the whole header? */
407 /* Nope - something screwed up. */
408 return std::make_pair(-1,"");
410 /* Put the read header info into a header class */
411 DNSFillHeader(&header,buffer,length - 12);
413 /* Get the id of this request.
414 * Its a 16 bit value stored in two char's,
415 * so we use logic shifts to create the value.
417 unsigned long this_id = header.id[1] + (header.id[0] << 8);
419 /* Do we have a pending request matching this id? */
420 requestlist_iter n_iter = requests.find(this_id);
421 if (n_iter == requests.end())
423 /* Somehow we got a DNS response for a request we never made... */
424 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",MasterSocket,this_id);
425 return std::make_pair(-1,"");
429 /* Remove the query from the list of pending queries */
430 req = (DNSRequest*)n_iter->second;
431 requests.erase(n_iter);
434 /* Inform the DNSRequest class that it has a result to be read.
435 * When its finished it will return a DNSInfo which is a pair of
436 * unsigned char* resource record data, and an error message.
438 DNSInfo data = req->ResultIsReady(header, length);
439 std::string resultstr;
441 /* Check if we got a result, if we didnt, its an error */
442 if (data.first == NULL)
445 * Mask the ID with the value of ERROR_MASK, so that
446 * the dns_deal_with_classes() function knows that its
447 * an error response and needs to be treated uniquely.
448 * Put the error message in the second field.
451 return std::make_pair(this_id | ERROR_MASK, data.second);
455 /* Forward lookups come back as binary data. We must format them into ascii */
456 if (req->type == DNS_QRY_A)
459 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
460 resultstr = formatted;
464 /* Reverse lookups just come back as char* */
465 resultstr = std::string((const char*)data.first);
468 /* Build the reply with the id and hostname/ip in it */
470 return std::make_pair(this_id,resultstr);
474 /* A result is ready, process it */
475 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
483 if (!(header.flags1 & FLAGS_MASK_QR))
484 return std::make_pair((unsigned char*)NULL,"Not a query result");
486 if (header.flags1 & FLAGS_MASK_OPCODE)
487 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
489 if (header.flags2 & FLAGS_MASK_RCODE)
490 return std::make_pair((unsigned char*)NULL,"Domain name not found");
492 if (header.ancount < 1)
493 return std::make_pair((unsigned char*)NULL,"No resource records returned");
495 /* Subtract the length of the header from the length of the packet */
498 while ((unsigned int)q < header.qdcount && i < length)
500 if (header.payload[i] > 63)
507 if (header.payload[i] == 0)
512 else i += header.payload[i] + 1;
516 while ((unsigned)curanswer < header.ancount)
519 while (q == 0 && i < length)
521 if (header.payload[i] > 63)
528 if (header.payload[i] == 0)
533 else i += header.payload[i] + 1; /* skip length and label */
537 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
539 DNSFillResourceRecord(&rr,&header.payload[i]);
541 if (rr.type != this->type)
547 if (rr.rr_class != this->rr_class)
555 if ((unsigned int)curanswer == header.ancount)
556 return std::make_pair((unsigned char*)NULL,"No valid answers");
558 if (i + rr.rdlength > (unsigned int)length)
559 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
561 if (rr.rdlength > 1023)
562 return std::make_pair((unsigned char*)NULL,"Resource record too large");
569 while (q == 0 && i < length && o + 256 < 1023)
571 if (header.payload[i] > 63)
573 memcpy(&p,&header.payload[i],2);
574 i = ntohs(p) - 0xC000 - 12;
578 if (header.payload[i] == 0)
587 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
588 o += header.payload[i];
589 i += header.payload[i] + 1;
596 memcpy(res,&header.payload[i],rr.rdlength);
597 res[rr.rdlength] = 0;
600 memcpy(res,&header.payload[i],rr.rdlength);
601 res[rr.rdlength] = 0;
604 return std::make_pair(res,"No error");;
609 shutdown(MasterSocket, 2);
613 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
617 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
618 this->myid = ServerInstance->Res->GetIP(source.c_str());
622 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
624 if (insp_aton(source.c_str(), &binip) > 0)
626 /* Valid ip address */
627 this->myid = ServerInstance->Res->GetName(&binip);
631 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
632 throw ModuleException("Resolver: Bad IP address");
636 if (this->myid == -1)
638 log(DEBUG,"Resolver::Resolver: Could not get an id!");
639 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
640 throw ModuleException("Resolver: Couldnt get an id to make a request");
641 /* We shouldnt get here really */
645 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
648 void Resolver::OnError(ResolverError e, const std::string &errormessage)
652 Resolver::~Resolver()
654 log(DEBUG,"Resolver::~Resolver");
657 int Resolver::GetId()
662 void DNS::MarshallReads(int fd)
664 log(DEBUG,"dns_deal_with_classes(%d)",fd);
665 if (fd == GetMasterSocket())
667 DNSResult res = this->GetResult();
670 if (res.first & ERROR_MASK)
672 res.first -= ERROR_MASK;
674 log(DEBUG,"Error available, id=%d",res.first);
675 if (Classes[res.first])
677 Classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
678 delete Classes[res.first];
679 Classes[res.first] = NULL;
684 log(DEBUG,"Result available, id=%d",res.first);
685 if (Classes[res.first])
687 Classes[res.first]->OnLookupComplete(res.second);
688 delete Classes[res.first];
689 Classes[res.first] = NULL;
696 bool DNS::AddResolverClass(Resolver* r)
698 if ((r) && (r->GetId() > -1))
700 if (!Classes[r->GetId()])
702 Classes[r->GetId()] = r;