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,dns_request*> connlist;
76 typedef connlist::iterator connlist_iter;
81 int master_socket = -1;
82 Resolver* dns_classes[65536];
89 unsigned int rr_class;
91 unsigned int rdlength;
100 unsigned int qdcount;
101 unsigned int ancount;
102 unsigned int nscount;
103 unsigned int arcount;
104 unsigned char payload[512];
112 unsigned int rr_class;
117 res = new unsigned char[512];
126 DNSInfo result_ready(dns_header &h, int length);
127 int send_requests(const dns_header *header, const int length, QueryType qt);
131 * Optimized by brain, these were using integer division and modulus.
132 * We can use logic shifts and logic AND to replace these even divisions
133 * and multiplications, it should be a bit faster (probably not noticably,
134 * but of course, more impressive). Also made these inline.
137 inline void dns_fill_rr(dns_rr_middle* rr, const unsigned char *input)
139 rr->type = (QueryType)((input[0] << 8) + input[1]);
140 rr->rr_class = (input[2] << 8) + input[3];
141 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
142 rr->rdlength = (input[8] << 8) + input[9];
145 inline void dns_fill_header(dns_header *header, const unsigned char *input, const int length)
147 header->id[0] = input[0];
148 header->id[1] = input[1];
149 header->flags1 = input[2];
150 header->flags2 = input[3];
151 header->qdcount = (input[4] << 8) + input[5];
152 header->ancount = (input[6] << 8) + input[7];
153 header->nscount = (input[8] << 8) + input[9];
154 header->arcount = (input[10] << 8) + input[11];
155 memcpy(header->payload,&input[12],length);
158 inline void dns_empty_header(unsigned char *output, const dns_header *header, const int length)
160 output[0] = header->id[0];
161 output[1] = header->id[1];
162 output[2] = header->flags1;
163 output[3] = header->flags2;
164 output[4] = header->qdcount >> 8;
165 output[5] = header->qdcount & 0xFF;
166 output[6] = header->ancount >> 8;
167 output[7] = header->ancount & 0xFF;
168 output[8] = header->nscount >> 8;
169 output[9] = header->nscount & 0xFF;
170 output[10] = header->arcount >> 8;
171 output[11] = header->arcount & 0xFF;
172 memcpy(&output[12],header->payload,length);
176 int dns_request::send_requests(const dns_header *header, const int length, QueryType qt)
179 unsigned char payload[sizeof(dns_header)];
184 dns_empty_header(payload,header,length);
186 memset(&addr,0,sizeof(addr));
188 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
189 addr.sin6_family = AF_FAMILY;
190 addr.sin6_port = htons(53);
192 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
193 addr.sin_family = AF_FAMILY;
194 addr.sin_port = htons(53);
196 if (sendto(master_socket, payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
198 log(DEBUG,"Error in sendto!");
205 dns_request* dns_add_query(dns_header *header, int &id)
209 dns_request* req = new dns_request();
211 header->id[0] = req->id[0] = id >> 8;
212 header->id[1] = req->id[1] = id & 0xFF;
213 header->flags1 = FLAGS_MASK_RD;
220 if (connections.find(id) == connections.end())
221 connections[id] = req;
223 /* According to the C++ spec, new never returns NULL. */
229 log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
231 srand((unsigned int) TIME);
232 memset(&myserver,0,sizeof(insp_inaddr));
233 if (insp_aton(Config->DNSServer,&addr) > 0)
234 memcpy(&myserver,&addr,sizeof(insp_inaddr));
236 master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
237 if (master_socket != -1)
239 log(DEBUG,"Set query socket nonblock");
240 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
242 shutdown(master_socket,2);
243 close(master_socket);
247 if (master_socket != -1)
251 memset(&addr,0,sizeof(addr));
252 addr.sin6_family = AF_FAMILY;
254 memset(&addr.sin6_addr,255,sizeof(in6_addr));
257 memset(&addr,0,sizeof(addr));
258 addr.sin_family = AF_FAMILY;
260 addr.sin_addr.s_addr = INADDR_ANY;
262 log(DEBUG,"Binding query port");
263 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
265 log(DEBUG,"Cant bind with source port = 0");
266 shutdown(master_socket,2);
267 close(master_socket);
271 if (master_socket >= 0)
273 log(DEBUG,"Attach query port to socket engine");
274 if (ServerInstance && ServerInstance->SE)
275 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
280 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short rr_class, unsigned char * const payload)
283 const char * tempchr, * tempchr2;
289 /* split name up into labels, create query */
290 while ((tempchr = strchr(tempchr2,'.')) != NULL)
292 l = tempchr - tempchr2;
293 if (payloadpos + l + 1 > 507)
295 payload[payloadpos++] = l;
296 memcpy(&payload[payloadpos],tempchr2,l);
298 tempchr2 = &tempchr[1];
300 l = strlen(tempchr2);
303 if (payloadpos + l + 2 > 507)
305 payload[payloadpos++] = l;
306 memcpy(&payload[payloadpos],tempchr2,l);
308 payload[payloadpos++] = '\0';
310 if (payloadpos > 508)
313 memcpy(&payload[payloadpos],&l,2);
315 memcpy(&payload[payloadpos + 2],&l,2);
316 return payloadpos + 4;
319 int DNS::GetIP(const char *name)
326 if ((length = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
329 req = dns_add_query(&h, id);
331 if (req->send_requests(&h,length,DNS_QRY_A) == -1)
337 int DNS::GetName(const insp_inaddr *ip)
348 unsigned char* c = (unsigned char*)&ip->s_addr;
350 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
352 if ((length = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
355 req = dns_add_query(&h, id);
357 if (req->send_requests(&h,length,DNS_QRY_PTR) == -1)
364 /* Return the next id which is ready, and the result attached to it
366 DNSResult DNS::GetResult()
368 /* Fetch dns query response and decide where it belongs */
372 unsigned char buffer[sizeof(dns_header)];
374 /* Attempt to read a header */
375 length = recv(master_socket,buffer,sizeof(dns_header),0);
377 /* Did we get the whole header? */
379 /* Nope - something screwed up. */
380 return std::make_pair(-1,"");
382 /* Put the read header info into a header class */
383 dns_fill_header(&header,buffer,length - 12);
385 /* Get the id of this request.
386 * Its a 16 bit value stored in two char's,
387 * so we use logic shifts to create the value.
389 unsigned long this_id = header.id[1] + (header.id[0] << 8);
391 /* Do we have a pending request matching this id? */
392 connlist_iter n_iter = connections.find(this_id);
393 if (n_iter == connections.end())
395 /* Somehow we got a DNS response for a request we never made... */
396 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
397 return std::make_pair(-1,"");
401 /* Remove the query from the list of pending queries */
402 req = (dns_request*)n_iter->second;
403 connections.erase(n_iter);
406 /* Inform the dns_request class that it has a result to be read.
407 * When its finished it will return a DNSInfo which is a pair of
408 * unsigned char* resource record data, and an error message.
410 DNSInfo data = req->result_ready(header, length);
411 std::string resultstr;
413 /* Check if we got a result, if we didnt, its an error */
414 if (data.first == NULL)
417 * Mask the ID with the value of ERROR_MASK, so that
418 * the dns_deal_with_classes() function knows that its
419 * an error response and needs to be treated uniquely.
420 * Put the error message in the second field.
423 return std::make_pair(this_id | ERROR_MASK, data.second);
427 /* Forward lookups come back as binary data. We must format them into ascii */
428 if (req->type == DNS_QRY_A)
431 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
432 resultstr = formatted;
436 /* Reverse lookups just come back as char* */
437 resultstr = std::string((const char*)data.first);
440 /* Build the reply with the id and hostname/ip in it */
442 return std::make_pair(this_id,resultstr);
446 /* A result is ready, process it */
447 DNSInfo dns_request::result_ready(dns_header &header, int length)
455 if (!(header.flags1 & FLAGS_MASK_QR))
456 return std::make_pair((unsigned char*)NULL,"Not a query result");
458 if (header.flags1 & FLAGS_MASK_OPCODE)
459 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
461 if (header.flags2 & FLAGS_MASK_RCODE)
462 return std::make_pair((unsigned char*)NULL,"Domain name not found");
464 if (header.ancount < 1)
465 return std::make_pair((unsigned char*)NULL,"No resource records returned");
467 /* Subtract the length of the header from the length of the packet */
470 while ((unsigned int)q < header.qdcount && i < length)
472 if (header.payload[i] > 63)
479 if (header.payload[i] == 0)
484 else i += header.payload[i] + 1;
488 while ((unsigned)curanswer < header.ancount)
491 while (q == 0 && i < length)
493 if (header.payload[i] > 63)
500 if (header.payload[i] == 0)
505 else i += header.payload[i] + 1; /* skip length and label */
509 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
511 dns_fill_rr(&rr,&header.payload[i]);
513 if (rr.type != this->type)
519 if (rr.rr_class != this->rr_class)
527 if ((unsigned int)curanswer == header.ancount)
528 return std::make_pair((unsigned char*)NULL,"No valid answers");
530 if (i + rr.rdlength > (unsigned int)length)
531 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
533 if (rr.rdlength > 1023)
534 return std::make_pair((unsigned char*)NULL,"Resource record too large");
541 while (q == 0 && i < length && o + 256 < 1023)
543 if (header.payload[i] > 63)
545 memcpy(&p,&header.payload[i],2);
546 i = ntohs(p) - 0xC000 - 12;
550 if (header.payload[i] == 0)
559 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
560 o += header.payload[i];
561 i += header.payload[i] + 1;
568 memcpy(res,&header.payload[i],rr.rdlength);
569 res[rr.rdlength] = '\0';
572 memcpy(res,&header.payload[i],rr.rdlength);
573 res[rr.rdlength] = '\0';
576 return std::make_pair(res,"No error");;
587 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
591 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
592 this->myid = Res->GetIP(source.c_str());
596 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
598 if (insp_aton(source.c_str(), &binip) > 0)
600 /* Valid ip address */
601 this->myid = Res->GetName(&binip);
605 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
606 throw ModuleException("Resolver: Bad IP address");
610 if (this->myid == -1)
612 log(DEBUG,"Resolver::Resolver: Could not get an id!");
613 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
614 throw ModuleException("Resolver: Couldnt get an id to make a request");
615 /* We shouldnt get here really */
619 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
622 //void Resolver::OnLookupComplete(const std::string &result)
626 void Resolver::OnError(ResolverError e, const std::string &errormessage)
630 Resolver::~Resolver()
632 log(DEBUG,"Resolver::~Resolver");
635 int Resolver::GetId()
640 void dns_deal_with_classes(int fd)
642 log(DEBUG,"dns_deal_with_classes(%d)",fd);
643 if (fd == master_socket)
645 DNSResult res = Res->GetResult();
648 if (res.first & ERROR_MASK)
650 res.first -= ERROR_MASK;
652 log(DEBUG,"Error available, id=%d",res.first);
653 if (dns_classes[res.first])
655 dns_classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
656 delete dns_classes[res.first];
657 dns_classes[res.first] = NULL;
662 log(DEBUG,"Result available, id=%d",res.first);
663 if (dns_classes[res.first])
665 dns_classes[res.first]->OnLookupComplete(res.second);
666 delete dns_classes[res.first];
667 dns_classes[res.first] = NULL;
674 bool dns_add_class(Resolver* r)
676 log(DEBUG,"dns_add_class");
677 if ((r) && (r->GetId() > -1))
679 if (!dns_classes[r->GetId()])
681 log(DEBUG,"dns_add_class: added class");
682 dns_classes[r->GetId()] = r;
687 log(DEBUG,"Space occupied!");
693 log(DEBUG,"Bad class");
702 memset(dns_classes,0,sizeof(dns_classes));