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);
139 * Optimized by brain, these were using integer division and modulus.
140 * We can use logic shifts and logic AND to replace these even divisions
141 * and multiplications, it should be a bit faster (probably not noticably,
142 * but of course, more impressive). Also made these inline.
146 /* Fill a ResourceRecord class based on raw data input */
147 inline void DNS::FillResourceRecord(ResourceRecord* rr, const unsigned char *input)
149 rr->type = (QueryType)((input[0] << 8) + input[1]);
150 rr->rr_class = (input[2] << 8) + input[3];
151 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
152 rr->rdlength = (input[8] << 8) + input[9];
155 /* Fill a DNSHeader class based on raw data input of a given length */
156 inline void DNS::FillHeader(DNSHeader *header, const unsigned char *input, const int length)
158 header->id[0] = input[0];
159 header->id[1] = input[1];
160 header->flags1 = input[2];
161 header->flags2 = input[3];
162 header->qdcount = (input[4] << 8) + input[5];
163 header->ancount = (input[6] << 8) + input[7];
164 header->nscount = (input[8] << 8) + input[9];
165 header->arcount = (input[10] << 8) + input[11];
166 memcpy(header->payload,&input[12],length);
169 /* Empty a DNSHeader class out into raw data, ready for transmission */
170 inline void DNS::EmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
172 output[0] = header->id[0];
173 output[1] = header->id[1];
174 output[2] = header->flags1;
175 output[3] = header->flags2;
176 output[4] = header->qdcount >> 8;
177 output[5] = header->qdcount & 0xFF;
178 output[6] = header->ancount >> 8;
179 output[7] = header->ancount & 0xFF;
180 output[8] = header->nscount >> 8;
181 output[9] = header->nscount & 0xFF;
182 output[10] = header->arcount >> 8;
183 output[11] = header->arcount & 0xFF;
184 memcpy(&output[12],header->payload,length);
187 /* Send requests we have previously built down the UDP socket */
188 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
191 unsigned char payload[sizeof(DNSHeader)];
196 DNS::EmptyHeader(payload,header,length);
198 memset(&addr,0,sizeof(addr));
200 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
201 addr.sin6_family = AF_FAMILY;
202 addr.sin6_port = htons(53);
204 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
205 addr.sin_family = AF_FAMILY;
206 addr.sin_port = htons(53);
208 if (sendto(DNS::GetMasterSocket(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
210 log(DEBUG,"Error in sendto!");
217 /* Add a query with a predefined header, and allocate an ID for it. */
218 DNSRequest* DNS::AddQuery(DNSHeader *header, int &id)
221 DNSRequest* req = new DNSRequest(this->myserver);
223 header->id[0] = req->id[0] = id >> 8;
224 header->id[1] = req->id[1] = id & 0xFF;
225 header->flags1 = FLAGS_MASK_RD;
232 if (requests.find(id) == requests.end())
235 /* According to the C++ spec, new never returns NULL. */
239 int DNS::GetMasterSocket()
244 /* Initialise the DNS UDP socket so that we can send requests */
247 log(DEBUG,"----- Initialize dns class ----- ");
248 memset(Classes,0,sizeof(Classes));
250 srand((unsigned int)time(NULL));
251 memset(&myserver,0,sizeof(insp_inaddr));
252 if (insp_aton(Config->DNSServer,&addr) > 0)
253 memcpy(&myserver,&addr,sizeof(insp_inaddr));
255 MasterSocket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
256 if (MasterSocket != -1)
258 log(DEBUG,"Set query socket nonblock");
259 if (fcntl(MasterSocket, F_SETFL, O_NONBLOCK) != 0)
261 shutdown(MasterSocket,2);
266 if (MasterSocket != -1)
270 memset(&addr,0,sizeof(addr));
271 addr.sin6_family = AF_FAMILY;
273 memset(&addr.sin6_addr,255,sizeof(in6_addr));
276 memset(&addr,0,sizeof(addr));
277 addr.sin_family = AF_FAMILY;
279 addr.sin_addr.s_addr = INADDR_ANY;
281 log(DEBUG,"Binding query port");
282 if (bind(MasterSocket,(sockaddr *)&addr,sizeof(addr)) != 0)
284 log(DEBUG,"Cant bind with source port = 0");
285 shutdown(MasterSocket,2);
290 if (MasterSocket >= 0)
292 log(DEBUG,"Attach query port to socket engine");
293 if (ServerInstance && ServerInstance->SE)
294 ServerInstance->SE->AddFd(MasterSocket,true,X_ESTAB_DNS);
299 int DNS::MakePayload(const char * const name, const unsigned short rr, const unsigned short rr_class, unsigned char * const payload)
302 const char * tempchr, * tempchr2;
308 /* split name up into labels, create query */
309 while ((tempchr = strchr(tempchr2,'.')) != NULL)
311 l = tempchr - tempchr2;
312 if (payloadpos + l + 1 > 507)
314 payload[payloadpos++] = l;
315 memcpy(&payload[payloadpos],tempchr2,l);
317 tempchr2 = &tempchr[1];
319 l = strlen(tempchr2);
322 if (payloadpos + l + 2 > 507)
324 payload[payloadpos++] = l;
325 memcpy(&payload[payloadpos],tempchr2,l);
327 payload[payloadpos++] = 0;
329 if (payloadpos > 508)
332 memcpy(&payload[payloadpos],&l,2);
334 memcpy(&payload[payloadpos + 2],&l,2);
335 return payloadpos + 4;
338 int DNS::GetIP(const char *name)
345 if ((length = this->MakePayload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
348 req = this->AddQuery(&h, id);
350 if (req->SendRequests(&h,length,DNS_QRY_A) == -1)
356 int DNS::GetName(const insp_inaddr *ip)
367 unsigned char* c = (unsigned char*)&ip->s_addr;
369 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
371 if ((length = this->MakePayload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
374 req = this->AddQuery(&h, id);
376 if (req->SendRequests(&h,length,DNS_QRY_PTR) == -1)
383 /* Return the next id which is ready, and the result attached to it
385 DNSResult DNS::GetResult()
387 /* Fetch dns query response and decide where it belongs */
391 unsigned char buffer[sizeof(DNSHeader)];
393 /* Attempt to read a header */
394 length = recv(MasterSocket,buffer,sizeof(DNSHeader),0);
396 /* Did we get the whole header? */
398 /* Nope - something screwed up. */
399 return std::make_pair(-1,"");
401 /* Put the read header info into a header class */
402 DNS::FillHeader(&header,buffer,length - 12);
404 /* Get the id of this request.
405 * Its a 16 bit value stored in two char's,
406 * so we use logic shifts to create the value.
408 unsigned long this_id = header.id[1] + (header.id[0] << 8);
410 /* Do we have a pending request matching this id? */
411 requestlist_iter n_iter = requests.find(this_id);
412 if (n_iter == requests.end())
414 /* Somehow we got a DNS response for a request we never made... */
415 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",MasterSocket,this_id);
416 return std::make_pair(-1,"");
420 /* Remove the query from the list of pending queries */
421 req = (DNSRequest*)n_iter->second;
422 requests.erase(n_iter);
425 /* Inform the DNSRequest class that it has a result to be read.
426 * When its finished it will return a DNSInfo which is a pair of
427 * unsigned char* resource record data, and an error message.
429 DNSInfo data = req->ResultIsReady(header, length);
430 std::string resultstr;
432 /* Check if we got a result, if we didnt, its an error */
433 if (data.first == NULL)
436 * Mask the ID with the value of ERROR_MASK, so that
437 * the dns_deal_with_classes() function knows that its
438 * an error response and needs to be treated uniquely.
439 * Put the error message in the second field.
442 return std::make_pair(this_id | ERROR_MASK, data.second);
446 /* Forward lookups come back as binary data. We must format them into ascii */
447 if (req->type == DNS_QRY_A)
450 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
451 resultstr = formatted;
455 /* Reverse lookups just come back as char* */
456 resultstr = std::string((const char*)data.first);
459 /* Build the reply with the id and hostname/ip in it */
461 return std::make_pair(this_id,resultstr);
465 /* A result is ready, process it */
466 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
474 if (!(header.flags1 & FLAGS_MASK_QR))
475 return std::make_pair((unsigned char*)NULL,"Not a query result");
477 if (header.flags1 & FLAGS_MASK_OPCODE)
478 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
480 if (header.flags2 & FLAGS_MASK_RCODE)
481 return std::make_pair((unsigned char*)NULL,"Domain name not found");
483 if (header.ancount < 1)
484 return std::make_pair((unsigned char*)NULL,"No resource records returned");
486 /* Subtract the length of the header from the length of the packet */
489 while ((unsigned int)q < header.qdcount && i < length)
491 if (header.payload[i] > 63)
498 if (header.payload[i] == 0)
503 else i += header.payload[i] + 1;
507 while ((unsigned)curanswer < header.ancount)
510 while (q == 0 && i < length)
512 if (header.payload[i] > 63)
519 if (header.payload[i] == 0)
524 else i += header.payload[i] + 1; /* skip length and label */
528 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
530 DNS::FillResourceRecord(&rr,&header.payload[i]);
532 if (rr.type != this->type)
538 if (rr.rr_class != this->rr_class)
546 if ((unsigned int)curanswer == header.ancount)
547 return std::make_pair((unsigned char*)NULL,"No valid answers");
549 if (i + rr.rdlength > (unsigned int)length)
550 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
552 if (rr.rdlength > 1023)
553 return std::make_pair((unsigned char*)NULL,"Resource record too large");
560 while (q == 0 && i < length && o + 256 < 1023)
562 if (header.payload[i] > 63)
564 memcpy(&p,&header.payload[i],2);
565 i = ntohs(p) - 0xC000 - 12;
569 if (header.payload[i] == 0)
578 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
579 o += header.payload[i];
580 i += header.payload[i] + 1;
587 memcpy(res,&header.payload[i],rr.rdlength);
588 res[rr.rdlength] = 0;
591 memcpy(res,&header.payload[i],rr.rdlength);
592 res[rr.rdlength] = 0;
595 return std::make_pair(res,"No error");;
600 shutdown(MasterSocket, 2);
604 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
608 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
609 this->myid = ServerInstance->Res->GetIP(source.c_str());
613 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
615 if (insp_aton(source.c_str(), &binip) > 0)
617 /* Valid ip address */
618 this->myid = ServerInstance->Res->GetName(&binip);
622 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
623 throw ModuleException("Resolver: Bad IP address");
627 if (this->myid == -1)
629 log(DEBUG,"Resolver::Resolver: Could not get an id!");
630 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
631 throw ModuleException("Resolver: Couldnt get an id to make a request");
632 /* We shouldnt get here really */
636 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
639 void Resolver::OnError(ResolverError e, const std::string &errormessage)
643 Resolver::~Resolver()
645 log(DEBUG,"Resolver::~Resolver");
648 int Resolver::GetId()
653 void DNS::MarshallReads(int fd)
655 log(DEBUG,"dns_deal_with_classes(%d)",fd);
656 if (fd == GetMasterSocket())
658 DNSResult res = this->GetResult();
661 if (res.first & ERROR_MASK)
663 res.first -= ERROR_MASK;
665 log(DEBUG,"Error available, id=%d",res.first);
666 if (Classes[res.first])
668 Classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
669 delete Classes[res.first];
670 Classes[res.first] = NULL;
675 log(DEBUG,"Result available, id=%d",res.first);
676 if (Classes[res.first])
678 Classes[res.first]->OnLookupComplete(res.second);
679 delete Classes[res.first];
680 Classes[res.first] = NULL;
687 bool DNS::AddResolverClass(Resolver* r)
689 if ((r) && (r->GetId() > -1))
691 if (!Classes[r->GetId()])
693 Classes[r->GetId()] = r;