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"
46 extern InspIRCd* ServerInstance;
47 extern ServerConfig* Config;
49 extern userrec* fd_ref_table[MAX_DESCRIPTORS];
67 FLAGS_MASK_OPCODE = 0x78,
69 FLAGS_MASK_RCODE = 0x0F,
75 typedef std::map<int,DNSRequest*> connlist;
76 typedef connlist::iterator connlist_iter;
81 int master_socket = -1;
82 Resolver* dns_classes[65536];
85 /* Represents a dns resource record (rr) */
90 unsigned int rr_class;
92 unsigned int rdlength;
95 /* Represents a dns request/reply header,
96 * and its payload as opaque data.
104 unsigned int qdcount;
105 unsigned int ancount;
106 unsigned int nscount;
107 unsigned int arcount;
108 unsigned char payload[512];
111 /* Represents a request 'on the wire' with
112 * routing information relating to where to
113 * call when we get a result
120 unsigned int rr_class;
125 res = new unsigned char[512];
134 /* Called when a result is ready to be processed which matches this id */
135 DNSInfo ResultIsReady(DNSHeader &h, int length);
137 /* Called when there are requests to be sent out */
138 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
142 * Optimized by brain, these were using integer division and modulus.
143 * We can use logic shifts and logic AND to replace these even divisions
144 * and multiplications, it should be a bit faster (probably not noticably,
145 * but of course, more impressive). Also made these inline.
148 inline void dns_fill_rr(ResourceRecord* rr, const unsigned char *input)
150 rr->type = (QueryType)((input[0] << 8) + input[1]);
151 rr->rr_class = (input[2] << 8) + input[3];
152 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
153 rr->rdlength = (input[8] << 8) + input[9];
156 inline void dns_fill_header(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 inline void dns_empty_header(unsigned char *output, const DNSHeader *header, const int length)
171 output[0] = header->id[0];
172 output[1] = header->id[1];
173 output[2] = header->flags1;
174 output[3] = header->flags2;
175 output[4] = header->qdcount >> 8;
176 output[5] = header->qdcount & 0xFF;
177 output[6] = header->ancount >> 8;
178 output[7] = header->ancount & 0xFF;
179 output[8] = header->nscount >> 8;
180 output[9] = header->nscount & 0xFF;
181 output[10] = header->arcount >> 8;
182 output[11] = header->arcount & 0xFF;
183 memcpy(&output[12],header->payload,length);
187 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
190 unsigned char payload[sizeof(DNSHeader)];
195 dns_empty_header(payload,header,length);
197 memset(&addr,0,sizeof(addr));
199 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
200 addr.sin6_family = AF_FAMILY;
201 addr.sin6_port = htons(53);
203 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
204 addr.sin_family = AF_FAMILY;
205 addr.sin_port = htons(53);
207 if (sendto(master_socket, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
209 log(DEBUG,"Error in sendto!");
216 DNSRequest* DNSAddQuery(DNSHeader *header, int &id)
220 DNSRequest* req = new DNSRequest();
222 header->id[0] = req->id[0] = id >> 8;
223 header->id[1] = req->id[1] = id & 0xFF;
224 header->flags1 = FLAGS_MASK_RD;
231 if (connections.find(id) == connections.end())
232 connections[id] = req;
234 /* According to the C++ spec, new never returns NULL. */
238 void DNSCreateSocket()
240 log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
242 srand((unsigned int) TIME);
243 memset(&myserver,0,sizeof(insp_inaddr));
244 if (insp_aton(Config->DNSServer,&addr) > 0)
245 memcpy(&myserver,&addr,sizeof(insp_inaddr));
247 master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
248 if (master_socket != -1)
250 log(DEBUG,"Set query socket nonblock");
251 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
253 shutdown(master_socket,2);
254 close(master_socket);
258 if (master_socket != -1)
262 memset(&addr,0,sizeof(addr));
263 addr.sin6_family = AF_FAMILY;
265 memset(&addr.sin6_addr,255,sizeof(in6_addr));
268 memset(&addr,0,sizeof(addr));
269 addr.sin_family = AF_FAMILY;
271 addr.sin_addr.s_addr = INADDR_ANY;
273 log(DEBUG,"Binding query port");
274 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
276 log(DEBUG,"Cant bind with source port = 0");
277 shutdown(master_socket,2);
278 close(master_socket);
282 if (master_socket >= 0)
284 log(DEBUG,"Attach query port to socket engine");
285 if (ServerInstance && ServerInstance->SE)
286 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
291 int DNSMakePayload(const char * const name, const unsigned short rr, const unsigned short rr_class, unsigned char * const payload)
294 const char * tempchr, * tempchr2;
300 /* split name up into labels, create query */
301 while ((tempchr = strchr(tempchr2,'.')) != NULL)
303 l = tempchr - tempchr2;
304 if (payloadpos + l + 1 > 507)
306 payload[payloadpos++] = l;
307 memcpy(&payload[payloadpos],tempchr2,l);
309 tempchr2 = &tempchr[1];
311 l = strlen(tempchr2);
314 if (payloadpos + l + 2 > 507)
316 payload[payloadpos++] = l;
317 memcpy(&payload[payloadpos],tempchr2,l);
319 payload[payloadpos++] = '\0';
321 if (payloadpos > 508)
324 memcpy(&payload[payloadpos],&l,2);
326 memcpy(&payload[payloadpos + 2],&l,2);
327 return payloadpos + 4;
330 int DNS::GetIP(const char *name)
337 if ((length = DNSMakePayload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
340 req = DNSAddQuery(&h, id);
342 if (req->SendRequests(&h,length,DNS_QRY_A) == -1)
348 int DNS::GetName(const insp_inaddr *ip)
359 unsigned char* c = (unsigned char*)&ip->s_addr;
361 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
363 if ((length = DNSMakePayload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
366 req = DNSAddQuery(&h, id);
368 if (req->SendRequests(&h,length,DNS_QRY_PTR) == -1)
375 /* Return the next id which is ready, and the result attached to it
377 DNSResult DNS::GetResult()
379 /* Fetch dns query response and decide where it belongs */
383 unsigned char buffer[sizeof(DNSHeader)];
385 /* Attempt to read a header */
386 length = recv(master_socket,buffer,sizeof(DNSHeader),0);
388 /* Did we get the whole header? */
390 /* Nope - something screwed up. */
391 return std::make_pair(-1,"");
393 /* Put the read header info into a header class */
394 dns_fill_header(&header,buffer,length - 12);
396 /* Get the id of this request.
397 * Its a 16 bit value stored in two char's,
398 * so we use logic shifts to create the value.
400 unsigned long this_id = header.id[1] + (header.id[0] << 8);
402 /* Do we have a pending request matching this id? */
403 connlist_iter n_iter = connections.find(this_id);
404 if (n_iter == connections.end())
406 /* Somehow we got a DNS response for a request we never made... */
407 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
408 return std::make_pair(-1,"");
412 /* Remove the query from the list of pending queries */
413 req = (DNSRequest*)n_iter->second;
414 connections.erase(n_iter);
417 /* Inform the DNSRequest class that it has a result to be read.
418 * When its finished it will return a DNSInfo which is a pair of
419 * unsigned char* resource record data, and an error message.
421 DNSInfo data = req->ResultIsReady(header, length);
422 std::string resultstr;
424 /* Check if we got a result, if we didnt, its an error */
425 if (data.first == NULL)
428 * Mask the ID with the value of ERROR_MASK, so that
429 * the dns_deal_with_classes() function knows that its
430 * an error response and needs to be treated uniquely.
431 * Put the error message in the second field.
434 return std::make_pair(this_id | ERROR_MASK, data.second);
438 /* Forward lookups come back as binary data. We must format them into ascii */
439 if (req->type == DNS_QRY_A)
442 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
443 resultstr = formatted;
447 /* Reverse lookups just come back as char* */
448 resultstr = std::string((const char*)data.first);
451 /* Build the reply with the id and hostname/ip in it */
453 return std::make_pair(this_id,resultstr);
457 /* A result is ready, process it */
458 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
466 if (!(header.flags1 & FLAGS_MASK_QR))
467 return std::make_pair((unsigned char*)NULL,"Not a query result");
469 if (header.flags1 & FLAGS_MASK_OPCODE)
470 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
472 if (header.flags2 & FLAGS_MASK_RCODE)
473 return std::make_pair((unsigned char*)NULL,"Domain name not found");
475 if (header.ancount < 1)
476 return std::make_pair((unsigned char*)NULL,"No resource records returned");
478 /* Subtract the length of the header from the length of the packet */
481 while ((unsigned int)q < header.qdcount && i < length)
483 if (header.payload[i] > 63)
490 if (header.payload[i] == 0)
495 else i += header.payload[i] + 1;
499 while ((unsigned)curanswer < header.ancount)
502 while (q == 0 && i < length)
504 if (header.payload[i] > 63)
511 if (header.payload[i] == 0)
516 else i += header.payload[i] + 1; /* skip length and label */
520 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
522 dns_fill_rr(&rr,&header.payload[i]);
524 if (rr.type != this->type)
530 if (rr.rr_class != this->rr_class)
538 if ((unsigned int)curanswer == header.ancount)
539 return std::make_pair((unsigned char*)NULL,"No valid answers");
541 if (i + rr.rdlength > (unsigned int)length)
542 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
544 if (rr.rdlength > 1023)
545 return std::make_pair((unsigned char*)NULL,"Resource record too large");
552 while (q == 0 && i < length && o + 256 < 1023)
554 if (header.payload[i] > 63)
556 memcpy(&p,&header.payload[i],2);
557 i = ntohs(p) - 0xC000 - 12;
561 if (header.payload[i] == 0)
570 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
571 o += header.payload[i];
572 i += header.payload[i] + 1;
579 memcpy(res,&header.payload[i],rr.rdlength);
580 res[rr.rdlength] = '\0';
583 memcpy(res,&header.payload[i],rr.rdlength);
584 res[rr.rdlength] = '\0';
587 return std::make_pair(res,"No error");;
598 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
602 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
603 this->myid = Res->GetIP(source.c_str());
607 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
609 if (insp_aton(source.c_str(), &binip) > 0)
611 /* Valid ip address */
612 this->myid = Res->GetName(&binip);
616 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
617 throw ModuleException("Resolver: Bad IP address");
621 if (this->myid == -1)
623 log(DEBUG,"Resolver::Resolver: Could not get an id!");
624 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
625 throw ModuleException("Resolver: Couldnt get an id to make a request");
626 /* We shouldnt get here really */
630 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
633 //void Resolver::OnLookupComplete(const std::string &result)
637 void Resolver::OnError(ResolverError e, const std::string &errormessage)
641 Resolver::~Resolver()
643 log(DEBUG,"Resolver::~Resolver");
646 int Resolver::GetId()
651 void dns_deal_with_classes(int fd)
653 log(DEBUG,"dns_deal_with_classes(%d)",fd);
654 if (fd == master_socket)
656 DNSResult res = Res->GetResult();
659 if (res.first & ERROR_MASK)
661 res.first -= ERROR_MASK;
663 log(DEBUG,"Error available, id=%d",res.first);
664 if (dns_classes[res.first])
666 dns_classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
667 delete dns_classes[res.first];
668 dns_classes[res.first] = NULL;
673 log(DEBUG,"Result available, id=%d",res.first);
674 if (dns_classes[res.first])
676 dns_classes[res.first]->OnLookupComplete(res.second);
677 delete dns_classes[res.first];
678 dns_classes[res.first] = NULL;
685 bool dns_add_class(Resolver* r)
687 log(DEBUG,"dns_add_class");
688 if ((r) && (r->GetId() > -1))
690 if (!dns_classes[r->GetId()])
692 log(DEBUG,"dns_add_class: added class");
693 dns_classes[r->GetId()] = r;
698 log(DEBUG,"Space occupied!");
704 log(DEBUG,"Bad class");
713 memset(dns_classes,0,sizeof(dns_classes));