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);
118 void *dns_align(void *inp)
120 char *p = (char*)inp;
121 int offby = ((char *)p - (char *)0) % (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long));
123 return p + ((sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long)) - offby);
129 * Optimized by brain, these were using integer division and modulus.
130 * We can use logic shifts and logic AND to replace these even divisions
131 * and multiplications, it should be a bit faster (probably not noticably,
132 * but of course, more impressive). Also made these inline.
135 inline void dns_fill_rr(s_rr_middle* rr, const unsigned char *input)
137 rr->type = (QueryType)((input[0] << 8) + input[1]);
138 rr->_class = (input[2] << 8) + input[3];
139 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
140 rr->rdlength = (input[8] << 8) + input[9];
143 inline void dns_fill_header(s_header *header, const unsigned char *input, const int l)
145 header->id[0] = input[0];
146 header->id[1] = input[1];
147 header->flags1 = input[2];
148 header->flags2 = input[3];
149 header->qdcount = (input[4] << 8) + input[5];
150 header->ancount = (input[6] << 8) + input[7];
151 header->nscount = (input[8] << 8) + input[9];
152 header->arcount = (input[10] << 8) + input[11];
153 memcpy(header->payload,&input[12],l);
156 inline void dns_empty_header(unsigned char *output, const s_header *header, const int l)
158 output[0] = header->id[0];
159 output[1] = header->id[1];
160 output[2] = header->flags1;
161 output[3] = header->flags2;
162 output[4] = header->qdcount >> 8;
163 output[5] = header->qdcount & 0xFF;
164 output[6] = header->ancount >> 8;
165 output[7] = header->ancount & 0xFF;
166 output[8] = header->nscount >> 8;
167 output[9] = header->nscount & 0xFF;
168 output[10] = header->arcount >> 8;
169 output[11] = header->arcount & 0xFF;
170 memcpy(&output[12],header->payload,l);
174 int s_connection::send_requests(const s_header *h, const int l)
177 unsigned char payload[sizeof(s_header)];
179 dns_empty_header(payload,h,l);
181 memset(&addr,0,sizeof(addr));
183 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
184 addr.sin6_family = AF_FAMILY;
185 addr.sin6_port = htons(53);
187 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
188 addr.sin_family = AF_FAMILY;
189 addr.sin_port = htons(53);
191 if (sendto(master_socket, payload, l + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
193 log(DEBUG,"Error in sendto!");
200 s_connection* dns_add_query(s_header *h, int &id)
204 s_connection * s = new s_connection();
206 h->id[0] = s->id[0] = id >> 8;
207 h->id[1] = s->id[1] = id & 0xFF;
208 h->flags1 = 0 | FLAGS1_MASK_RD;
215 if (connections.find(id) == connections.end())
222 log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
224 srand((unsigned int) TIME);
225 memset(&myserver,0,sizeof(insp_inaddr));
226 if (insp_aton(Config->DNSServer,&addr) > 0)
227 memcpy(&myserver,&addr,sizeof(insp_inaddr));
229 master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
230 if (master_socket != -1)
232 log(DEBUG,"Set query socket nonblock");
233 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
235 shutdown(master_socket,2);
236 close(master_socket);
240 if (master_socket != -1)
244 memset(&addr,0,sizeof(addr));
245 addr.sin6_family = AF_FAMILY;
247 memset(&addr.sin6_addr,255,sizeof(in6_addr));
250 memset(&addr,0,sizeof(addr));
251 addr.sin_family = AF_FAMILY;
253 addr.sin_addr.s_addr = INADDR_ANY;
255 log(DEBUG,"Binding query port");
256 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
258 log(DEBUG,"Cant bind with source port = 0");
259 shutdown(master_socket,2);
260 close(master_socket);
264 if (master_socket >= 0)
266 log(DEBUG,"Attach query port to socket engine");
267 if (ServerInstance && ServerInstance->SE)
268 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
273 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
276 const char * tempchr, * tempchr2;
282 /* split name up into labels, create query */
283 while ((tempchr = strchr(tempchr2,'.')) != NULL)
285 l = tempchr - tempchr2;
286 if (payloadpos + l + 1 > 507)
288 payload[payloadpos++] = l;
289 memcpy(&payload[payloadpos],tempchr2,l);
291 tempchr2 = &tempchr[1];
293 l = strlen(tempchr2);
296 if (payloadpos + l + 2 > 507)
298 payload[payloadpos++] = l;
299 memcpy(&payload[payloadpos],tempchr2,l);
301 payload[payloadpos++] = '\0';
303 if (payloadpos > 508)
306 memcpy(&payload[payloadpos],&l,2);
308 memcpy(&payload[payloadpos + 2],&l,2);
309 return payloadpos + 4;
312 int DNS::dns_getip4(const char *name)
314 /* build, add and send A query; retrieve result with dns_getresult() */
320 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
323 s = dns_add_query(&h, id);
328 if (s->send_requests(&h,l) == -1)
334 int DNS::dns_getname4(const insp_inaddr *ip)
335 { /* build, add and send PTR query; retrieve result with dns_getresult() */
339 log(DEBUG,"DNS::dns_getname4");
347 c = (unsigned char *)&ip->s_addr;
349 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
351 l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
354 s = dns_add_query(&h, id);
358 s->type = DNS_QRY_PTR;
359 if (s->send_requests(&h,l) == -1)
366 /* Return the next id which is ready, and the result attached to it
368 DNSResult DNS::dns_getresult()
370 /* retrieve result of DNS query (buffered) */
374 unsigned char buffer[sizeof(s_header)];
376 length = recv(master_socket,buffer,sizeof(s_header),0);
379 return std::make_pair(-1,"");
381 dns_fill_header(&h,buffer,length - 12);
383 // Get the id of this request
384 unsigned long this_id = h.id[1] + (h.id[0] << 8);
386 // Do we have a pending request for it?
388 connlist_iter n_iter = connections.find(this_id);
389 if (n_iter == connections.end())
391 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
392 return std::make_pair(-1,"");
396 /* Remove the query from the list */
397 c = (s_connection*)n_iter->second;
398 /* We don't delete c here, because its done later when needed */
399 connections.erase(n_iter);
401 unsigned char* a = c->result_ready(h, length);
402 std::string resultstr;
410 if (c->type == DNS_QRY_A)
412 char formatted[1024];
413 snprintf(formatted,1024,"%u.%u.%u.%u",a[0],a[1],a[2],a[3]);
414 resultstr = std::string(formatted);
418 resultstr = std::string((const char*)a);
423 return std::make_pair(this_id,resultstr);
426 /** A result is ready, process it
428 unsigned char* s_connection::result_ready(s_header &h, int length)
430 int i, q, curanswer, o;
434 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
436 log(DEBUG,"DNS: didnt get a query result");
439 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
441 log(DEBUG,"DNS: got an OPCODE and didnt want one");
444 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
446 log(DEBUG,"DNS lookup failed due to SERVFAIL");
451 log(DEBUG,"DNS: no answers!");
457 while ((unsigned)q < h.qdcount && i < length)
459 if (h.payload[i] > 63)
466 if (h.payload[i] == 0)
471 else i += h.payload[i] + 1;
475 while ((unsigned)curanswer < h.ancount)
478 while (q == 0 && i < length)
480 if (h.payload[i] > 63)
487 if (h.payload[i] == 0)
492 else i += h.payload[i] + 1; /* skip length and label */
499 dns_fill_rr(&rr,&h.payload[i]);
501 if (rr.type != this->type)
507 if (rr._class != this->_class)
515 if ((unsigned int)curanswer == h.ancount)
517 if (i + rr.rdlength > (unsigned int)length)
519 if (rr.rdlength > 1023)
525 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
528 while (q == 0 && i < length && o + 256 < 1023)
530 if (h.payload[i] > 63)
532 log(DEBUG,"DNS: h.payload[i] > 63");
533 memcpy(&p,&h.payload[i],2);
534 i = ntohs(p) - 0xC000 - 12;
538 if (h.payload[i] == 0)
547 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
549 i += h.payload[i] + 1;
556 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
557 memcpy(res,&h.payload[i],rr.rdlength);
558 res[rr.rdlength] = '\0';
561 memcpy(res,&h.payload[i],rr.rdlength);
562 res[rr.rdlength] = '\0';
570 log(DEBUG,"Create blank DNS");
577 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
581 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
582 this->myid = Res->dns_getip4(source.c_str());
586 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
588 if (insp_aton(source.c_str(), &binip) > 0)
590 /* Valid ip address */
591 this->myid = Res->dns_getname4(&binip);
594 if (this->myid == -1)
596 log(DEBUG,"Resolver::Resolver: Could not get an id!");
597 this->OnError(RESOLVER_NSDOWN);
598 throw ModuleException("Resolver: Couldnt get an id to make a request");
599 /* We shouldnt get here really */
603 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
606 void Resolver::OnLookupComplete(const std::string &result)
610 void Resolver::OnError(ResolverError e)
614 Resolver::~Resolver()
616 log(DEBUG,"Resolver::~Resolver");
619 int Resolver::GetId()
624 bool Resolver::ProcessResult(const std::string &result)
626 log(DEBUG,"Resolver::ProcessResult");
628 if (!result.length())
630 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
631 this->OnError(RESOLVER_NXDOMAIN);
637 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
638 this->OnLookupComplete(result);
643 void dns_deal_with_classes(int fd)
645 log(DEBUG,"dns_deal_with_classes(%d)",fd);
646 if (fd == master_socket)
648 DNSResult res = Res->dns_getresult();
651 log(DEBUG,"Result available, id=%d",res.first);
652 if (dns_classes[res.first])
654 dns_classes[res.first]->ProcessResult(res.second);
655 delete dns_classes[res.first];
656 dns_classes[res.first] = NULL;
662 bool dns_add_class(Resolver* r)
664 log(DEBUG,"dns_add_class");
665 if ((r) && (r->GetId() > -1))
667 if (!dns_classes[r->GetId()])
669 log(DEBUG,"dns_add_class: added class");
670 dns_classes[r->GetId()] = r;
675 log(DEBUG,"Space occupied!");
681 log(DEBUG,"Bad class");
690 memset(dns_classes,0,sizeof(dns_classes));