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 loosely based on the firedns library,
20 Copyright (C) 2002 Ian Gulliver.
22 There have been so many modifications to this file
23 to make it fit into InspIRCd and make it object
24 orientated that you should not take this code as
25 being what firedns really looks like. It used to
26 look very different to this! :-P
33 #include <sys/types.h>
34 #include <sys/socket.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
43 #include "helperfuncs.h"
44 #include "inspircd_config.h"
45 #include "socketengine.h"
46 #include "configreader.h"
48 extern InspIRCd* ServerInstance;
49 extern ServerConfig* Config;
51 extern userrec* fd_ref_table[MAX_DESCRIPTORS];
69 FLAGS_MASK_OPCODE = 0x78,
71 FLAGS_MASK_RCODE = 0x0F,
77 typedef std::map<int,dns_connection*> connlist;
78 typedef connlist::iterator connlist_iter;
83 int master_socket = -1;
84 Resolver* dns_classes[65536];
91 unsigned int rr_class;
93 unsigned int rdlength;
102 unsigned int qdcount;
103 unsigned int ancount;
104 unsigned int nscount;
105 unsigned int arcount;
106 unsigned char payload[512];
114 unsigned int rr_class;
119 res = new unsigned char[512];
128 DNSInfo result_ready(dns_header &h, int length);
129 int send_requests(const dns_header *header, const int length, QueryType qt);
133 * Optimized by brain, these were using integer division and modulus.
134 * We can use logic shifts and logic AND to replace these even divisions
135 * and multiplications, it should be a bit faster (probably not noticably,
136 * but of course, more impressive). Also made these inline.
139 inline void dns_fill_rr(dns_rr_middle* 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 inline void dns_fill_header(dns_header *header, const unsigned char *input, const int length)
149 header->id[0] = input[0];
150 header->id[1] = input[1];
151 header->flags1 = input[2];
152 header->flags2 = input[3];
153 header->qdcount = (input[4] << 8) + input[5];
154 header->ancount = (input[6] << 8) + input[7];
155 header->nscount = (input[8] << 8) + input[9];
156 header->arcount = (input[10] << 8) + input[11];
157 memcpy(header->payload,&input[12],length);
160 inline void dns_empty_header(unsigned char *output, const dns_header *header, const int length)
162 output[0] = header->id[0];
163 output[1] = header->id[1];
164 output[2] = header->flags1;
165 output[3] = header->flags2;
166 output[4] = header->qdcount >> 8;
167 output[5] = header->qdcount & 0xFF;
168 output[6] = header->ancount >> 8;
169 output[7] = header->ancount & 0xFF;
170 output[8] = header->nscount >> 8;
171 output[9] = header->nscount & 0xFF;
172 output[10] = header->arcount >> 8;
173 output[11] = header->arcount & 0xFF;
174 memcpy(&output[12],header->payload,length);
178 int dns_connection::send_requests(const dns_header *header, const int length, QueryType qt)
181 unsigned char payload[sizeof(dns_header)];
186 dns_empty_header(payload,header,length);
188 memset(&addr,0,sizeof(addr));
190 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
191 addr.sin6_family = AF_FAMILY;
192 addr.sin6_port = htons(53);
194 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
195 addr.sin_family = AF_FAMILY;
196 addr.sin_port = htons(53);
198 if (sendto(master_socket, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
200 log(DEBUG,"Error in sendto!");
207 dns_connection* dns_add_query(dns_header *header, int &id)
211 dns_connection* req = new dns_connection();
213 header->id[0] = req->id[0] = id >> 8;
214 header->id[1] = req->id[1] = id & 0xFF;
215 header->flags1 = FLAGS_MASK_RD;
222 if (connections.find(id) == connections.end())
223 connections[id] = req;
225 /* According to the C++ spec, new never returns NULL. */
231 log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
233 srand((unsigned int) TIME);
234 memset(&myserver,0,sizeof(insp_inaddr));
235 if (insp_aton(Config->DNSServer,&addr) > 0)
236 memcpy(&myserver,&addr,sizeof(insp_inaddr));
238 master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
239 if (master_socket != -1)
241 log(DEBUG,"Set query socket nonblock");
242 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
244 shutdown(master_socket,2);
245 close(master_socket);
249 if (master_socket != -1)
253 memset(&addr,0,sizeof(addr));
254 addr.sin6_family = AF_FAMILY;
256 memset(&addr.sin6_addr,255,sizeof(in6_addr));
259 memset(&addr,0,sizeof(addr));
260 addr.sin_family = AF_FAMILY;
262 addr.sin_addr.s_addr = INADDR_ANY;
264 log(DEBUG,"Binding query port");
265 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
267 log(DEBUG,"Cant bind with source port = 0");
268 shutdown(master_socket,2);
269 close(master_socket);
273 if (master_socket >= 0)
275 log(DEBUG,"Attach query port to socket engine");
276 if (ServerInstance && ServerInstance->SE)
277 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
282 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short rr_class, unsigned char * const payload)
285 const char * tempchr, * tempchr2;
291 /* split name up into labels, create query */
292 while ((tempchr = strchr(tempchr2,'.')) != NULL)
294 l = tempchr - tempchr2;
295 if (payloadpos + l + 1 > 507)
297 payload[payloadpos++] = l;
298 memcpy(&payload[payloadpos],tempchr2,l);
300 tempchr2 = &tempchr[1];
302 l = strlen(tempchr2);
305 if (payloadpos + l + 2 > 507)
307 payload[payloadpos++] = l;
308 memcpy(&payload[payloadpos],tempchr2,l);
310 payload[payloadpos++] = '\0';
312 if (payloadpos > 508)
315 memcpy(&payload[payloadpos],&l,2);
317 memcpy(&payload[payloadpos + 2],&l,2);
318 return payloadpos + 4;
321 int DNS::dns_getip(const char *name)
328 if ((length = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
331 req = dns_add_query(&h, id);
333 if (req->send_requests(&h,length,DNS_QRY_A) == -1)
339 int DNS::dns_getname(const insp_inaddr *ip)
350 unsigned char* c = (unsigned char*)&ip->s_addr;
352 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
354 if ((length = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
357 req = dns_add_query(&h, id);
359 if (req->send_requests(&h,length,DNS_QRY_PTR) == -1)
366 /* Return the next id which is ready, and the result attached to it
368 DNSResult DNS::dns_getresult()
370 /* Fetch dns query response and decide where it belongs */
374 unsigned char buffer[sizeof(dns_header)];
376 /* Attempt to read a header */
377 length = recv(master_socket,buffer,sizeof(dns_header),0);
379 /* Did we get the whole header? */
381 /* Nope - something screwed up. */
382 return std::make_pair(-1,"");
384 /* Put the read header info into a header class */
385 dns_fill_header(&header,buffer,length - 12);
387 /* Get the id of this request.
388 * Its a 16 bit value stored in two char's,
389 * so we use logic shifts to create the value.
391 unsigned long this_id = header.id[1] + (header.id[0] << 8);
393 /* Do we have a pending request matching this id? */
394 connlist_iter n_iter = connections.find(this_id);
395 if (n_iter == connections.end())
397 /* Somehow we got a DNS response for a request we never made... */
398 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
399 return std::make_pair(-1,"");
403 /* Remove the query from the list of pending queries */
404 req = (dns_connection*)n_iter->second;
405 connections.erase(n_iter);
408 /* Inform the dns_connection class that it has a result to be read.
409 * When its finished it will return a DNSInfo which is a pair of
410 * unsigned char* resource record data, and an error message.
412 DNSInfo data = req->result_ready(header, length);
413 std::string resultstr;
415 /* Check if we got a result, if we didnt, its an error */
416 if (data.first == NULL)
419 * Mask the ID with the value of ERROR_MASK, so that
420 * the dns_deal_with_classes() function knows that its
421 * an error response and needs to be treated uniquely.
422 * Put the error message in the second field.
425 return std::make_pair(this_id | ERROR_MASK, data.second);
429 /* Forward lookups come back as binary data. We must format them into ascii */
430 if (req->type == DNS_QRY_A)
433 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
434 resultstr = formatted;
438 /* Reverse lookups just come back as char* */
439 resultstr = std::string((const char*)data.first);
442 /* Build the reply with the id and hostname/ip in it */
444 return std::make_pair(this_id,resultstr);
448 /* A result is ready, process it */
449 DNSInfo dns_connection::result_ready(dns_header &header, int length)
457 if (!(header.flags1 & FLAGS_MASK_QR))
458 return std::make_pair((unsigned char*)NULL,"Not a query result");
460 if (header.flags1 & FLAGS_MASK_OPCODE)
461 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
463 if (header.flags2 & FLAGS_MASK_RCODE)
464 return std::make_pair((unsigned char*)NULL,"Internal server error (SERVFAIL)");
466 if (header.ancount < 1)
467 return std::make_pair((unsigned char*)NULL,"No resource records returned");
469 /* Subtract the length of the header from the length of the packet */
472 while ((unsigned int)q < header.qdcount && i < length)
474 if (header.payload[i] > 63)
481 if (header.payload[i] == 0)
486 else i += header.payload[i] + 1;
490 while ((unsigned)curanswer < header.ancount)
493 while (q == 0 && i < length)
495 if (header.payload[i] > 63)
502 if (header.payload[i] == 0)
507 else i += header.payload[i] + 1; /* skip length and label */
511 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
513 dns_fill_rr(&rr,&header.payload[i]);
515 if (rr.type != this->type)
521 if (rr.rr_class != this->rr_class)
529 if ((unsigned int)curanswer == header.ancount)
530 return std::make_pair((unsigned char*)NULL,"No valid answers");
532 if (i + rr.rdlength > (unsigned int)length)
533 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
535 if (rr.rdlength > 1023)
536 return std::make_pair((unsigned char*)NULL,"Resource record too large");
543 while (q == 0 && i < length && o + 256 < 1023)
545 if (header.payload[i] > 63)
547 memcpy(&p,&header.payload[i],2);
548 i = ntohs(p) - 0xC000 - 12;
552 if (header.payload[i] == 0)
561 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
562 o += header.payload[i];
563 i += header.payload[i] + 1;
570 memcpy(res,&header.payload[i],rr.rdlength);
571 res[rr.rdlength] = '\0';
574 memcpy(res,&header.payload[i],rr.rdlength);
575 res[rr.rdlength] = '\0';
578 return std::make_pair(res,"No error");;
589 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
593 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
594 this->myid = Res->dns_getip(source.c_str());
598 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
600 if (insp_aton(source.c_str(), &binip) > 0)
602 /* Valid ip address */
603 this->myid = Res->dns_getname(&binip);
607 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
608 throw ModuleException("Resolver: Bad IP address");
612 if (this->myid == -1)
614 log(DEBUG,"Resolver::Resolver: Could not get an id!");
615 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
616 throw ModuleException("Resolver: Couldnt get an id to make a request");
617 /* We shouldnt get here really */
621 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
624 //void Resolver::OnLookupComplete(const std::string &result)
628 void Resolver::OnError(ResolverError e, const std::string &errormessage)
632 Resolver::~Resolver()
634 log(DEBUG,"Resolver::~Resolver");
637 int Resolver::GetId()
642 void dns_deal_with_classes(int fd)
644 log(DEBUG,"dns_deal_with_classes(%d)",fd);
645 if (fd == master_socket)
647 DNSResult res = Res->dns_getresult();
650 if (res.first & ERROR_MASK)
652 res.first -= ERROR_MASK;
654 log(DEBUG,"Error available, id=%d",res.first);
655 if (dns_classes[res.first])
657 dns_classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
658 delete dns_classes[res.first];
659 dns_classes[res.first] = NULL;
664 log(DEBUG,"Result available, id=%d",res.first);
665 if (dns_classes[res.first])
667 dns_classes[res.first]->OnLookupComplete(res.second);
668 delete dns_classes[res.first];
669 dns_classes[res.first] = NULL;
676 bool dns_add_class(Resolver* r)
678 log(DEBUG,"dns_add_class");
679 if ((r) && (r->GetId() > -1))
681 if (!dns_classes[r->GetId()])
683 log(DEBUG,"dns_add_class: added class");
684 dns_classes[r->GetId()] = r;
689 log(DEBUG,"Space occupied!");
695 log(DEBUG,"Bad class");
704 memset(dns_classes,0,sizeof(dns_classes));