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];
64 FLAGS_MASK_OPCODE = 0x78,
66 FLAGS_MASK_RCODE = 0x0F,
72 typedef std::map<int,dns_connection*> connlist;
73 typedef connlist::iterator connlist_iter;
78 int master_socket = -1;
79 Resolver* dns_classes[65536];
86 unsigned int rr_class;
88 unsigned int rdlength;
100 unsigned int arcount;
101 unsigned char payload[512];
109 unsigned int rr_class;
114 res = new unsigned char[512];
123 unsigned char* result_ready(dns_header &h, int length);
124 int send_requests(const dns_header *header, const int length, QueryType qt);
128 * Optimized by brain, these were using integer division and modulus.
129 * We can use logic shifts and logic AND to replace these even divisions
130 * and multiplications, it should be a bit faster (probably not noticably,
131 * but of course, more impressive). Also made these inline.
134 inline void dns_fill_rr(dns_rr_middle* rr, const unsigned char *input)
136 rr->type = (QueryType)((input[0] << 8) + input[1]);
137 rr->rr_class = (input[2] << 8) + input[3];
138 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
139 rr->rdlength = (input[8] << 8) + input[9];
142 inline void dns_fill_header(dns_header *header, const unsigned char *input, const int length)
144 header->id[0] = input[0];
145 header->id[1] = input[1];
146 header->flags1 = input[2];
147 header->flags2 = input[3];
148 header->qdcount = (input[4] << 8) + input[5];
149 header->ancount = (input[6] << 8) + input[7];
150 header->nscount = (input[8] << 8) + input[9];
151 header->arcount = (input[10] << 8) + input[11];
152 memcpy(header->payload,&input[12],length);
155 inline void dns_empty_header(unsigned char *output, const dns_header *header, const int length)
157 output[0] = header->id[0];
158 output[1] = header->id[1];
159 output[2] = header->flags1;
160 output[3] = header->flags2;
161 output[4] = header->qdcount >> 8;
162 output[5] = header->qdcount & 0xFF;
163 output[6] = header->ancount >> 8;
164 output[7] = header->ancount & 0xFF;
165 output[8] = header->nscount >> 8;
166 output[9] = header->nscount & 0xFF;
167 output[10] = header->arcount >> 8;
168 output[11] = header->arcount & 0xFF;
169 memcpy(&output[12],header->payload,length);
173 int dns_connection::send_requests(const dns_header *header, const int length, QueryType qt)
176 unsigned char payload[sizeof(dns_header)];
181 dns_empty_header(payload,header,length);
183 memset(&addr,0,sizeof(addr));
185 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
186 addr.sin6_family = AF_FAMILY;
187 addr.sin6_port = htons(53);
189 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
190 addr.sin_family = AF_FAMILY;
191 addr.sin_port = htons(53);
193 if (sendto(master_socket, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
195 log(DEBUG,"Error in sendto!");
202 dns_connection* dns_add_query(dns_header *header, int &id)
206 dns_connection* req = new dns_connection();
208 header->id[0] = req->id[0] = id >> 8;
209 header->id[1] = req->id[1] = id & 0xFF;
210 header->flags1 = FLAGS_MASK_RD;
217 if (connections.find(id) == connections.end())
218 connections[id] = req;
220 /* According to the C++ spec, new never returns NULL. */
226 log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
228 srand((unsigned int) TIME);
229 memset(&myserver,0,sizeof(insp_inaddr));
230 if (insp_aton(Config->DNSServer,&addr) > 0)
231 memcpy(&myserver,&addr,sizeof(insp_inaddr));
233 master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
234 if (master_socket != -1)
236 log(DEBUG,"Set query socket nonblock");
237 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
239 shutdown(master_socket,2);
240 close(master_socket);
244 if (master_socket != -1)
248 memset(&addr,0,sizeof(addr));
249 addr.sin6_family = AF_FAMILY;
251 memset(&addr.sin6_addr,255,sizeof(in6_addr));
254 memset(&addr,0,sizeof(addr));
255 addr.sin_family = AF_FAMILY;
257 addr.sin_addr.s_addr = INADDR_ANY;
259 log(DEBUG,"Binding query port");
260 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
262 log(DEBUG,"Cant bind with source port = 0");
263 shutdown(master_socket,2);
264 close(master_socket);
268 if (master_socket >= 0)
270 log(DEBUG,"Attach query port to socket engine");
271 if (ServerInstance && ServerInstance->SE)
272 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
277 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short rr_class, unsigned char * const payload)
280 const char * tempchr, * tempchr2;
286 /* split name up into labels, create query */
287 while ((tempchr = strchr(tempchr2,'.')) != NULL)
289 l = tempchr - tempchr2;
290 if (payloadpos + l + 1 > 507)
292 payload[payloadpos++] = l;
293 memcpy(&payload[payloadpos],tempchr2,l);
295 tempchr2 = &tempchr[1];
297 l = strlen(tempchr2);
300 if (payloadpos + l + 2 > 507)
302 payload[payloadpos++] = l;
303 memcpy(&payload[payloadpos],tempchr2,l);
305 payload[payloadpos++] = '\0';
307 if (payloadpos > 508)
310 memcpy(&payload[payloadpos],&l,2);
312 memcpy(&payload[payloadpos + 2],&l,2);
313 return payloadpos + 4;
316 int DNS::dns_getip4(const char *name)
323 if ((length = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
326 req = dns_add_query(&h, id);
328 if (req->send_requests(&h,length,DNS_QRY_A) == -1)
334 int DNS::dns_getname4(const insp_inaddr *ip)
345 unsigned char* c = (unsigned char*)&ip->s_addr;
347 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
349 if ((length = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
352 req = dns_add_query(&h, id);
354 if (req->send_requests(&h,length,DNS_QRY_PTR) == -1)
361 /* Return the next id which is ready, and the result attached to it
363 DNSResult DNS::dns_getresult()
365 /* retrieve result of DNS query (buffered) */
369 unsigned char buffer[sizeof(dns_header)];
371 length = recv(master_socket,buffer,sizeof(dns_header),0);
374 return std::make_pair(-1,"");
376 dns_fill_header(&header,buffer,length - 12);
378 // Get the id of this request
379 unsigned long this_id = header.id[1] + (header.id[0] << 8);
381 // Do we have a pending request for it?
383 connlist_iter n_iter = connections.find(this_id);
384 if (n_iter == connections.end())
386 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
387 return std::make_pair(-1,"");
391 /* Remove the query from the list */
392 req = (dns_connection*)n_iter->second;
393 /* We don't delete c here, because its done later when needed */
394 connections.erase(n_iter);
396 unsigned char* data = req->result_ready(header, length);
397 std::string resultstr;
405 if (req->type == DNS_QRY_A)
408 snprintf(formatted,16,"%u.%u.%u.%u",data[0],data[1],data[2],data[3]);
409 resultstr = formatted;
413 resultstr = std::string((const char*)data);
418 return std::make_pair(this_id,resultstr);
421 /** A result is ready, process it
423 unsigned char* dns_connection::result_ready(dns_header &header, int length)
431 if (!(header.flags1 & FLAGS_MASK_QR))
433 log(DEBUG,"DNS: didnt get a query result");
436 if (header.flags1 & FLAGS_MASK_OPCODE)
438 log(DEBUG,"DNS: got an OPCODE and didnt want one");
441 if (header.flags2 & FLAGS_MASK_RCODE)
443 log(DEBUG,"DNS lookup failed due to SERVFAIL");
446 if (header.ancount < 1)
448 log(DEBUG,"DNS: no answers!");
452 while ((unsigned int)q < header.qdcount && i < length)
454 if (header.payload[i] > 63)
461 if (header.payload[i] == 0)
466 else i += header.payload[i] + 1;
470 while ((unsigned)curanswer < header.ancount)
473 while (q == 0 && i < length)
475 if (header.payload[i] > 63)
482 if (header.payload[i] == 0)
487 else i += header.payload[i] + 1; /* skip length and label */
494 dns_fill_rr(&rr,&header.payload[i]);
496 if (rr.type != this->type)
502 if (rr.rr_class != this->rr_class)
510 if ((unsigned int)curanswer == header.ancount)
512 if (i + rr.rdlength > (unsigned int)length)
514 if (rr.rdlength > 1023)
520 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
523 while (q == 0 && i < length && o + 256 < 1023)
525 if (header.payload[i] > 63)
527 log(DEBUG,"DNS: h.payload[i] > 63");
528 memcpy(&p,&header.payload[i],2);
529 i = ntohs(p) - 0xC000 - 12;
533 if (header.payload[i] == 0)
542 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
543 o += header.payload[i];
544 i += header.payload[i] + 1;
551 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
552 memcpy(res,&header.payload[i],rr.rdlength);
553 res[rr.rdlength] = '\0';
556 memcpy(res,&header.payload[i],rr.rdlength);
557 res[rr.rdlength] = '\0';
571 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
575 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
576 this->myid = Res->dns_getip4(source.c_str());
580 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
582 if (insp_aton(source.c_str(), &binip) > 0)
584 /* Valid ip address */
585 this->myid = Res->dns_getname4(&binip);
588 if (this->myid == -1)
590 log(DEBUG,"Resolver::Resolver: Could not get an id!");
591 this->OnError(RESOLVER_NSDOWN);
592 throw ModuleException("Resolver: Couldnt get an id to make a request");
593 /* We shouldnt get here really */
597 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
600 void Resolver::OnLookupComplete(const std::string &result)
604 void Resolver::OnError(ResolverError e)
608 Resolver::~Resolver()
610 log(DEBUG,"Resolver::~Resolver");
613 int Resolver::GetId()
618 bool Resolver::ProcessResult(const std::string &result)
620 log(DEBUG,"Resolver::ProcessResult");
622 if (!result.length())
624 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
625 this->OnError(RESOLVER_NXDOMAIN);
631 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
632 this->OnLookupComplete(result);
637 void dns_deal_with_classes(int fd)
639 log(DEBUG,"dns_deal_with_classes(%d)",fd);
640 if (fd == master_socket)
642 DNSResult res = Res->dns_getresult();
645 log(DEBUG,"Result available, id=%d",res.first);
646 if (dns_classes[res.first])
648 dns_classes[res.first]->ProcessResult(res.second);
649 delete dns_classes[res.first];
650 dns_classes[res.first] = NULL;
656 bool dns_add_class(Resolver* r)
658 log(DEBUG,"dns_add_class");
659 if ((r) && (r->GetId() > -1))
661 if (!dns_classes[r->GetId()])
663 log(DEBUG,"dns_add_class: added class");
664 dns_classes[r->GetId()] = r;
669 log(DEBUG,"Space occupied!");
675 log(DEBUG,"Bad class");
684 memset(dns_classes,0,sizeof(dns_classes));