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
34 #include <sys/types.h>
35 #include <sys/socket.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
51 #include "helperfuncs.h"
52 #include "inspircd_config.h"
53 #include "socketengine.h"
54 #include "configreader.h"
56 extern InspIRCd* ServerInstance;
57 extern ServerConfig* Config;
59 extern userrec* fd_ref_table[MAX_DESCRIPTORS];
61 enum QueryType { DNS_QRY_A = 1, DNS_QRY_PTR = 12 };
62 enum QueryFlags1 { FLAGS1_MASK_RD = 0x01, FLAGS1_MASK_TC = 0x02, FLAGS1_MASK_AA = 0x04, FLAGS1_MASK_OPCODE = 0x78, FLAGS1_MASK_QR = 0x80 };
63 enum QueryFlags2 { FLAGS2_MASK_RCODE = 0x0F, FLAGS2_MASK_Z = 0x70, FLAGS2_MASK_RA = 0x80 };
67 typedef std::map<int,s_connection*> connlist;
68 typedef connlist::iterator connlist_iter;
73 int master_socket = -1;
74 Resolver* dns_classes[65536];
83 unsigned int rdlength;
96 unsigned char payload[512];
103 unsigned char res[512];
112 unsigned char* result_ready(s_header &h, int length);
113 int send_requests(const s_header *h, const int l, QueryType qt);
117 * Optimized by brain, these were using integer division and modulus.
118 * We can use logic shifts and logic AND to replace these even divisions
119 * and multiplications, it should be a bit faster (probably not noticably,
120 * but of course, more impressive). Also made these inline.
123 inline void dns_fill_rr(s_rr_middle* rr, const unsigned char *input)
125 rr->type = (QueryType)((input[0] << 8) + input[1]);
126 rr->_class = (input[2] << 8) + input[3];
127 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
128 rr->rdlength = (input[8] << 8) + input[9];
131 inline void dns_fill_header(s_header *header, const unsigned char *input, const int l)
133 header->id[0] = input[0];
134 header->id[1] = input[1];
135 header->flags1 = input[2];
136 header->flags2 = input[3];
137 header->qdcount = (input[4] << 8) + input[5];
138 header->ancount = (input[6] << 8) + input[7];
139 header->nscount = (input[8] << 8) + input[9];
140 header->arcount = (input[10] << 8) + input[11];
141 memcpy(header->payload,&input[12],l);
144 inline void dns_empty_header(unsigned char *output, const s_header *header, const int l)
146 output[0] = header->id[0];
147 output[1] = header->id[1];
148 output[2] = header->flags1;
149 output[3] = header->flags2;
150 output[4] = header->qdcount >> 8;
151 output[5] = header->qdcount & 0xFF;
152 output[6] = header->ancount >> 8;
153 output[7] = header->ancount & 0xFF;
154 output[8] = header->nscount >> 8;
155 output[9] = header->nscount & 0xFF;
156 output[10] = header->arcount >> 8;
157 output[11] = header->arcount & 0xFF;
158 memcpy(&output[12],header->payload,l);
162 int s_connection::send_requests(const s_header *h, const int l, QueryType qt)
165 unsigned char payload[sizeof(s_header)];
170 dns_empty_header(payload,h,l);
172 memset(&addr,0,sizeof(addr));
174 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
175 addr.sin6_family = AF_FAMILY;
176 addr.sin6_port = htons(53);
178 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
179 addr.sin_family = AF_FAMILY;
180 addr.sin_port = htons(53);
182 if (sendto(master_socket, payload, l + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
184 log(DEBUG,"Error in sendto!");
191 s_connection* dns_add_query(s_header *h, int &id)
195 s_connection * s = new s_connection();
197 h->id[0] = s->id[0] = id >> 8;
198 h->id[1] = s->id[1] = id & 0xFF;
199 h->flags1 = 0 | FLAGS1_MASK_RD;
206 if (connections.find(id) == connections.end())
213 log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
215 srand((unsigned int) TIME);
216 memset(&myserver,0,sizeof(insp_inaddr));
217 if (insp_aton(Config->DNSServer,&addr) > 0)
218 memcpy(&myserver,&addr,sizeof(insp_inaddr));
220 master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
221 if (master_socket != -1)
223 log(DEBUG,"Set query socket nonblock");
224 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
226 shutdown(master_socket,2);
227 close(master_socket);
231 if (master_socket != -1)
235 memset(&addr,0,sizeof(addr));
236 addr.sin6_family = AF_FAMILY;
238 memset(&addr.sin6_addr,255,sizeof(in6_addr));
241 memset(&addr,0,sizeof(addr));
242 addr.sin_family = AF_FAMILY;
244 addr.sin_addr.s_addr = INADDR_ANY;
246 log(DEBUG,"Binding query port");
247 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
249 log(DEBUG,"Cant bind with source port = 0");
250 shutdown(master_socket,2);
251 close(master_socket);
255 if (master_socket >= 0)
257 log(DEBUG,"Attach query port to socket engine");
258 if (ServerInstance && ServerInstance->SE)
259 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
264 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
267 const char * tempchr, * tempchr2;
273 /* split name up into labels, create query */
274 while ((tempchr = strchr(tempchr2,'.')) != NULL)
276 l = tempchr - tempchr2;
277 if (payloadpos + l + 1 > 507)
279 payload[payloadpos++] = l;
280 memcpy(&payload[payloadpos],tempchr2,l);
282 tempchr2 = &tempchr[1];
284 l = strlen(tempchr2);
287 if (payloadpos + l + 2 > 507)
289 payload[payloadpos++] = l;
290 memcpy(&payload[payloadpos],tempchr2,l);
292 payload[payloadpos++] = '\0';
294 if (payloadpos > 508)
297 memcpy(&payload[payloadpos],&l,2);
299 memcpy(&payload[payloadpos + 2],&l,2);
300 return payloadpos + 4;
303 int DNS::dns_getip4(const char *name)
310 if ((length = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
313 if ((req = dns_add_query(&h, id)) == NULL)
316 if (req->send_requests(&h,length,DNS_QRY_A) == -1)
322 int DNS::dns_getname4(const insp_inaddr *ip)
333 unsigned char* c = (unsigned char*)&ip->s_addr;
335 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
337 if ((length = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
340 if ((req = dns_add_query(&h, id)) == NULL)
343 if (req->send_requests(&h,length,DNS_QRY_PTR) == -1)
350 /* Return the next id which is ready, and the result attached to it
352 DNSResult DNS::dns_getresult()
354 /* retrieve result of DNS query (buffered) */
358 unsigned char buffer[sizeof(s_header)];
360 length = recv(master_socket,buffer,sizeof(s_header),0);
363 return std::make_pair(-1,"");
365 dns_fill_header(&h,buffer,length - 12);
367 // Get the id of this request
368 unsigned long this_id = h.id[1] + (h.id[0] << 8);
370 // Do we have a pending request for it?
372 connlist_iter n_iter = connections.find(this_id);
373 if (n_iter == connections.end())
375 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
376 return std::make_pair(-1,"");
380 /* Remove the query from the list */
381 c = (s_connection*)n_iter->second;
382 /* We don't delete c here, because its done later when needed */
383 connections.erase(n_iter);
385 unsigned char* a = c->result_ready(h, length);
386 std::string resultstr;
394 if (c->type == DNS_QRY_A)
396 char formatted[1024];
397 snprintf(formatted,1024,"%u.%u.%u.%u",a[0],a[1],a[2],a[3]);
398 resultstr = std::string(formatted);
402 resultstr = std::string((const char*)a);
407 return std::make_pair(this_id,resultstr);
410 /** A result is ready, process it
412 unsigned char* s_connection::result_ready(s_header &h, int length)
414 int i, q, curanswer, o;
418 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
420 log(DEBUG,"DNS: didnt get a query result");
423 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
425 log(DEBUG,"DNS: got an OPCODE and didnt want one");
428 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
430 log(DEBUG,"DNS lookup failed due to SERVFAIL");
435 log(DEBUG,"DNS: no answers!");
441 while ((unsigned)q < h.qdcount && i < length)
443 if (h.payload[i] > 63)
450 if (h.payload[i] == 0)
455 else i += h.payload[i] + 1;
459 while ((unsigned)curanswer < h.ancount)
462 while (q == 0 && i < length)
464 if (h.payload[i] > 63)
471 if (h.payload[i] == 0)
476 else i += h.payload[i] + 1; /* skip length and label */
483 dns_fill_rr(&rr,&h.payload[i]);
485 if (rr.type != this->type)
491 if (rr._class != this->_class)
499 if ((unsigned int)curanswer == h.ancount)
501 if (i + rr.rdlength > (unsigned int)length)
503 if (rr.rdlength > 1023)
509 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
512 while (q == 0 && i < length && o + 256 < 1023)
514 if (h.payload[i] > 63)
516 log(DEBUG,"DNS: h.payload[i] > 63");
517 memcpy(&p,&h.payload[i],2);
518 i = ntohs(p) - 0xC000 - 12;
522 if (h.payload[i] == 0)
531 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
533 i += h.payload[i] + 1;
540 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
541 memcpy(res,&h.payload[i],rr.rdlength);
542 res[rr.rdlength] = '\0';
545 memcpy(res,&h.payload[i],rr.rdlength);
546 res[rr.rdlength] = '\0';
554 log(DEBUG,"Create blank DNS");
561 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
565 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
566 this->myid = Res->dns_getip4(source.c_str());
570 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
572 if (insp_aton(source.c_str(), &binip) > 0)
574 /* Valid ip address */
575 this->myid = Res->dns_getname4(&binip);
578 if (this->myid == -1)
580 log(DEBUG,"Resolver::Resolver: Could not get an id!");
581 this->OnError(RESOLVER_NSDOWN);
582 throw ModuleException("Resolver: Couldnt get an id to make a request");
583 /* We shouldnt get here really */
587 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
590 void Resolver::OnLookupComplete(const std::string &result)
594 void Resolver::OnError(ResolverError e)
598 Resolver::~Resolver()
600 log(DEBUG,"Resolver::~Resolver");
603 int Resolver::GetId()
608 bool Resolver::ProcessResult(const std::string &result)
610 log(DEBUG,"Resolver::ProcessResult");
612 if (!result.length())
614 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
615 this->OnError(RESOLVER_NXDOMAIN);
621 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
622 this->OnLookupComplete(result);
627 void dns_deal_with_classes(int fd)
629 log(DEBUG,"dns_deal_with_classes(%d)",fd);
630 if (fd == master_socket)
632 DNSResult res = Res->dns_getresult();
635 log(DEBUG,"Result available, id=%d",res.first);
636 if (dns_classes[res.first])
638 dns_classes[res.first]->ProcessResult(res.second);
639 delete dns_classes[res.first];
640 dns_classes[res.first] = NULL;
646 bool dns_add_class(Resolver* r)
648 log(DEBUG,"dns_add_class");
649 if ((r) && (r->GetId() > -1))
651 if (!dns_classes[r->GetId()])
653 log(DEBUG,"dns_add_class: added class");
654 dns_classes[r->GetId()] = r;
659 log(DEBUG,"Space occupied!");
665 log(DEBUG,"Bad class");
674 memset(dns_classes,0,sizeof(dns_classes));