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);
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, QueryType qt)
177 unsigned char payload[sizeof(s_header)];
182 dns_empty_header(payload,h,l);
184 memset(&addr,0,sizeof(addr));
186 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
187 addr.sin6_family = AF_FAMILY;
188 addr.sin6_port = htons(53);
190 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
191 addr.sin_family = AF_FAMILY;
192 addr.sin_port = htons(53);
194 if (sendto(master_socket, payload, l + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
196 log(DEBUG,"Error in sendto!");
203 s_connection* dns_add_query(s_header *h, int &id)
207 s_connection * s = new s_connection();
209 h->id[0] = s->id[0] = id >> 8;
210 h->id[1] = s->id[1] = id & 0xFF;
211 h->flags1 = 0 | FLAGS1_MASK_RD;
218 if (connections.find(id) == connections.end())
225 log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
227 srand((unsigned int) TIME);
228 memset(&myserver,0,sizeof(insp_inaddr));
229 if (insp_aton(Config->DNSServer,&addr) > 0)
230 memcpy(&myserver,&addr,sizeof(insp_inaddr));
232 master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
233 if (master_socket != -1)
235 log(DEBUG,"Set query socket nonblock");
236 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
238 shutdown(master_socket,2);
239 close(master_socket);
243 if (master_socket != -1)
247 memset(&addr,0,sizeof(addr));
248 addr.sin6_family = AF_FAMILY;
250 memset(&addr.sin6_addr,255,sizeof(in6_addr));
253 memset(&addr,0,sizeof(addr));
254 addr.sin_family = AF_FAMILY;
256 addr.sin_addr.s_addr = INADDR_ANY;
258 log(DEBUG,"Binding query port");
259 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
261 log(DEBUG,"Cant bind with source port = 0");
262 shutdown(master_socket,2);
263 close(master_socket);
267 if (master_socket >= 0)
269 log(DEBUG,"Attach query port to socket engine");
270 if (ServerInstance && ServerInstance->SE)
271 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
276 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
279 const char * tempchr, * tempchr2;
285 /* split name up into labels, create query */
286 while ((tempchr = strchr(tempchr2,'.')) != NULL)
288 l = tempchr - tempchr2;
289 if (payloadpos + l + 1 > 507)
291 payload[payloadpos++] = l;
292 memcpy(&payload[payloadpos],tempchr2,l);
294 tempchr2 = &tempchr[1];
296 l = strlen(tempchr2);
299 if (payloadpos + l + 2 > 507)
301 payload[payloadpos++] = l;
302 memcpy(&payload[payloadpos],tempchr2,l);
304 payload[payloadpos++] = '\0';
306 if (payloadpos > 508)
309 memcpy(&payload[payloadpos],&l,2);
311 memcpy(&payload[payloadpos + 2],&l,2);
312 return payloadpos + 4;
315 int DNS::dns_getip4(const char *name)
317 /* build, add and send A query; retrieve result with dns_getresult() */
322 int length = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char*)&h.payload);
325 req = dns_add_query(&h, id);
329 if (req->send_requests(&h,length,DNS_QRY_A) == -1)
335 int DNS::dns_getname4(const insp_inaddr *ip)
336 { /* build, add and send PTR query; retrieve result with dns_getresult() */
340 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 int length = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload);
354 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::dns_getresult()
368 /* retrieve result of DNS query (buffered) */
372 unsigned char buffer[sizeof(s_header)];
374 length = recv(master_socket,buffer,sizeof(s_header),0);
377 return std::make_pair(-1,"");
379 dns_fill_header(&h,buffer,length - 12);
381 // Get the id of this request
382 unsigned long this_id = h.id[1] + (h.id[0] << 8);
384 // Do we have a pending request for it?
386 connlist_iter n_iter = connections.find(this_id);
387 if (n_iter == connections.end())
389 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
390 return std::make_pair(-1,"");
394 /* Remove the query from the list */
395 c = (s_connection*)n_iter->second;
396 /* We don't delete c here, because its done later when needed */
397 connections.erase(n_iter);
399 unsigned char* a = c->result_ready(h, length);
400 std::string resultstr;
408 if (c->type == DNS_QRY_A)
410 char formatted[1024];
411 snprintf(formatted,1024,"%u.%u.%u.%u",a[0],a[1],a[2],a[3]);
412 resultstr = std::string(formatted);
416 resultstr = std::string((const char*)a);
421 return std::make_pair(this_id,resultstr);
424 /** A result is ready, process it
426 unsigned char* s_connection::result_ready(s_header &h, int length)
428 int i, q, curanswer, o;
432 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
434 log(DEBUG,"DNS: didnt get a query result");
437 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
439 log(DEBUG,"DNS: got an OPCODE and didnt want one");
442 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
444 log(DEBUG,"DNS lookup failed due to SERVFAIL");
449 log(DEBUG,"DNS: no answers!");
455 while ((unsigned)q < h.qdcount && i < length)
457 if (h.payload[i] > 63)
464 if (h.payload[i] == 0)
469 else i += h.payload[i] + 1;
473 while ((unsigned)curanswer < h.ancount)
476 while (q == 0 && i < length)
478 if (h.payload[i] > 63)
485 if (h.payload[i] == 0)
490 else i += h.payload[i] + 1; /* skip length and label */
497 dns_fill_rr(&rr,&h.payload[i]);
499 if (rr.type != this->type)
505 if (rr._class != this->_class)
513 if ((unsigned int)curanswer == h.ancount)
515 if (i + rr.rdlength > (unsigned int)length)
517 if (rr.rdlength > 1023)
523 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
526 while (q == 0 && i < length && o + 256 < 1023)
528 if (h.payload[i] > 63)
530 log(DEBUG,"DNS: h.payload[i] > 63");
531 memcpy(&p,&h.payload[i],2);
532 i = ntohs(p) - 0xC000 - 12;
536 if (h.payload[i] == 0)
545 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
547 i += h.payload[i] + 1;
554 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
555 memcpy(res,&h.payload[i],rr.rdlength);
556 res[rr.rdlength] = '\0';
559 memcpy(res,&h.payload[i],rr.rdlength);
560 res[rr.rdlength] = '\0';
568 log(DEBUG,"Create blank DNS");
575 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
579 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
580 this->myid = Res->dns_getip4(source.c_str());
584 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
586 if (insp_aton(source.c_str(), &binip) > 0)
588 /* Valid ip address */
589 this->myid = Res->dns_getname4(&binip);
592 if (this->myid == -1)
594 log(DEBUG,"Resolver::Resolver: Could not get an id!");
595 this->OnError(RESOLVER_NSDOWN);
596 throw ModuleException("Resolver: Couldnt get an id to make a request");
597 /* We shouldnt get here really */
601 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
604 void Resolver::OnLookupComplete(const std::string &result)
608 void Resolver::OnError(ResolverError e)
612 Resolver::~Resolver()
614 log(DEBUG,"Resolver::~Resolver");
617 int Resolver::GetId()
622 bool Resolver::ProcessResult(const std::string &result)
624 log(DEBUG,"Resolver::ProcessResult");
626 if (!result.length())
628 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
629 this->OnError(RESOLVER_NXDOMAIN);
635 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
636 this->OnLookupComplete(result);
641 void dns_deal_with_classes(int fd)
643 log(DEBUG,"dns_deal_with_classes(%d)",fd);
644 if (fd == master_socket)
646 DNSResult res = Res->dns_getresult();
649 log(DEBUG,"Result available, id=%d",res.first);
650 if (dns_classes[res.first])
652 dns_classes[res.first]->ProcessResult(res.second);
653 delete dns_classes[res.first];
654 dns_classes[res.first] = NULL;
660 bool dns_add_class(Resolver* r)
662 log(DEBUG,"dns_add_class");
663 if ((r) && (r->GetId() > -1))
665 if (!dns_classes[r->GetId()])
667 log(DEBUG,"dns_add_class: added class");
668 dns_classes[r->GetId()] = r;
673 log(DEBUG,"Space occupied!");
679 log(DEBUG,"Bad class");
688 memset(dns_classes,0,sizeof(dns_classes));