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;
72 /* As lookups are completed, they are pushed into this result_list */
73 std::map<int, std::string> result_list;
76 int master_socket = -1;
78 Resolver* dns_classes[65536];
80 insp_inaddr servers[8];
89 unsigned int rdlength;
100 unsigned int nscount;
101 unsigned int arcount;
102 unsigned char payload[512];
109 unsigned char res[512];
119 unsigned char* result_ready(s_header &h, int length);
120 int send_requests(const s_header *h, const int l);
125 void *dns_align(void *inp)
127 char *p = (char*)inp;
128 int offby = ((char *)p - (char *)0) % (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long));
130 return p + ((sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long)) - offby);
136 * Optimized by brain, these were using integer division and modulus.
137 * We can use logic shifts and logic AND to replace these even divisions
138 * and multiplications, it should be a bit faster (probably not noticably,
139 * but of course, more impressive). Also made these inline.
142 inline void dns_fill_rr(s_rr_middle* rr, const unsigned char *input)
144 rr->type = (QueryType)((input[0] << 8) + input[1]);
145 rr->_class = (input[2] << 8) + input[3];
146 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
147 rr->rdlength = (input[8] << 8) + input[9];
150 inline void dns_fill_header(s_header *header, const unsigned char *input, const int l)
152 header->id[0] = input[0];
153 header->id[1] = input[1];
154 header->flags1 = input[2];
155 header->flags2 = input[3];
156 header->qdcount = (input[4] << 8) + input[5];
157 header->ancount = (input[6] << 8) + input[7];
158 header->nscount = (input[8] << 8) + input[9];
159 header->arcount = (input[10] << 8) + input[11];
160 memcpy(header->payload,&input[12],l);
163 inline void dns_empty_header(unsigned char *output, const s_header *header, const int l)
165 output[0] = header->id[0];
166 output[1] = header->id[1];
167 output[2] = header->flags1;
168 output[3] = header->flags2;
169 output[4] = header->qdcount >> 8;
170 output[5] = header->qdcount & 0xFF;
171 output[6] = header->ancount >> 8;
172 output[7] = header->ancount & 0xFF;
173 output[8] = header->nscount >> 8;
174 output[9] = header->nscount & 0xFF;
175 output[10] = header->arcount >> 8;
176 output[11] = header->arcount & 0xFF;
177 memcpy(&output[12],header->payload,l);
181 int s_connection::send_requests(const s_header *h, const int l)
184 unsigned char payload[sizeof(s_header)];
186 dns_empty_header(payload,h,l);
188 /* otherwise send via standard ipv4 boringness */
189 memset(&addr,0,sizeof(addr));
191 memcpy(&addr.sin6_addr,&servers[0],sizeof(addr.sin6_addr));
192 addr.sin6_family = AF_FAMILY;
193 addr.sin6_port = htons(53);
195 memcpy(&addr.sin_addr.s_addr,&servers[0],sizeof(addr.sin_addr));
196 addr.sin_family = AF_FAMILY;
197 addr.sin_port = htons(53);
199 if (sendto(master_socket, payload, l + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
201 log(DEBUG,"Error in sendto!");
208 s_connection* dns_add_query(s_header *h, int &id)
212 s_connection * s = new s_connection();
214 /* set header flags */
215 h->id[0] = s->id[0] = id >> 8;
216 h->id[1] = s->id[1] = id & 0xFF;
217 h->flags1 = 0 | FLAGS1_MASK_RD;
224 if (connections.find(id) == connections.end())
231 log(DEBUG,"---- BEGIN DNS INITIALIZATION, SERVER=%s ---",Config->DNSServer);
234 srand((unsigned int) TIME);
235 memset(servers,'\0',sizeof(insp_inaddr) * 8);
236 if (insp_aton(Config->DNSServer,&addr) > 0)
237 memcpy(&servers[i4++],&addr,sizeof(insp_inaddr));
239 master_socket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
240 if (master_socket != -1)
242 log(DEBUG,"Set query socket nonblock");
243 if (fcntl(master_socket, F_SETFL, O_NONBLOCK) != 0)
245 shutdown(master_socket,2);
246 close(master_socket);
250 if (master_socket != -1)
254 memset(&addr,0,sizeof(addr));
255 addr.sin6_family = AF_FAMILY;
257 memset(&addr.sin6_addr,255,sizeof(in6_addr));
260 memset(&addr,0,sizeof(addr));
261 addr.sin_family = AF_FAMILY;
263 addr.sin_addr.s_addr = INADDR_ANY;
265 log(DEBUG,"Binding query port");
266 if (bind(master_socket,(sockaddr *)&addr,sizeof(addr)) != 0)
268 log(DEBUG,"Cant bind with source port = 0");
269 shutdown(master_socket,2);
270 close(master_socket);
274 if (master_socket >= 0)
276 log(DEBUG,"Attach query port to socket engine");
277 if (ServerInstance && ServerInstance->SE)
278 ServerInstance->SE->AddFd(master_socket,true,X_ESTAB_DNS);
283 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
286 const char * tempchr, * tempchr2;
292 /* split name up into labels, create query */
293 while ((tempchr = strchr(tempchr2,'.')) != NULL)
295 l = tempchr - tempchr2;
296 if (payloadpos + l + 1 > 507)
298 payload[payloadpos++] = l;
299 memcpy(&payload[payloadpos],tempchr2,l);
301 tempchr2 = &tempchr[1];
303 l = strlen(tempchr2);
306 if (payloadpos + l + 2 > 507)
308 payload[payloadpos++] = l;
309 memcpy(&payload[payloadpos],tempchr2,l);
311 payload[payloadpos++] = '\0';
313 if (payloadpos > 508)
316 memcpy(&payload[payloadpos],&l,2);
318 memcpy(&payload[payloadpos + 2],&l,2);
319 return payloadpos + 4;
322 int DNS::dns_getip4(const char *name)
324 /* build, add and send A query; retrieve result with dns_getresult() */
330 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
333 s = dns_add_query(&h, id);
338 if (s->send_requests(&h,l) == -1)
344 int DNS::dns_getname4(const insp_inaddr *ip)
345 { /* build, add and send PTR query; retrieve result with dns_getresult() */
349 log(DEBUG,"DNS::dns_getname4");
357 c = (unsigned char *)&ip->s_addr;
359 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
361 l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
364 s = dns_add_query(&h, id);
368 s->type = DNS_QRY_PTR;
369 if (s->send_requests(&h,l) == -1)
376 /* Return the next id which is ready.
377 * result_list[id] will have been populated with the result string.
379 int DNS::dns_getresult()
381 /* retrieve result of DNS query (buffered) */
385 unsigned char buffer[sizeof(s_header)];
387 length = recv(master_socket,buffer,sizeof(s_header),0);
392 dns_fill_header(&h,buffer,length - 12);
394 // Get the id of this request
395 unsigned long this_id = h.id[1] + (h.id[0] << 8);
397 // Do we have a pending request for it?
399 connlist_iter n_iter = connections.find(this_id);
400 if (n_iter == connections.end())
402 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",master_socket,this_id);
407 /* Remove the query from the list */
408 c = (s_connection*)n_iter->second;
409 /* We don't delete c here, because its done later when needed */
410 connections.erase(n_iter);
412 unsigned char* a = c->result_ready(h, length);
416 result_list[this_id] = "";
420 if (c->type == DNS_QRY_A)
422 char formatted[1024];
423 snprintf(formatted,1024,"%u.%u.%u.%u",a[0],a[1],a[2],a[3]);
424 result_list[this_id] = std::string(formatted);
428 result_list[this_id] = std::string((const char*)a);
436 /** A result is ready, process it
438 unsigned char* s_connection::result_ready(s_header &h, int length)
440 int i, q, curanswer, o;
444 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
446 log(DEBUG,"DNS: didnt get a query result");
449 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
451 log(DEBUG,"DNS: got an OPCODE and didnt want one");
454 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
456 log(DEBUG,"DNS lookup failed due to SERVFAIL");
461 log(DEBUG,"DNS: no answers!");
467 while ((unsigned)q < h.qdcount && i < length)
469 if (h.payload[i] > 63)
476 if (h.payload[i] == 0)
481 else i += h.payload[i] + 1;
485 while ((unsigned)curanswer < h.ancount)
488 while (q == 0 && i < length)
490 if (h.payload[i] > 63)
497 if (h.payload[i] == 0)
502 else i += h.payload[i] + 1; /* skip length and label */
509 dns_fill_rr(&rr,&h.payload[i]);
511 if (rr.type != this->type)
517 if (rr._class != this->_class)
525 if ((unsigned int)curanswer == h.ancount)
527 if (i + rr.rdlength > (unsigned int)length)
529 if (rr.rdlength > 1023)
535 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
538 while (q == 0 && i < length && o + 256 < 1023)
540 if (h.payload[i] > 63)
542 log(DEBUG,"DNS: h.payload[i] > 63");
543 memcpy(&p,&h.payload[i],2);
544 i = ntohs(p) - 0xC000 - 12;
548 if (h.payload[i] == 0)
557 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
559 i += h.payload[i] + 1;
566 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
569 dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
570 while ((char *)alist - (char *)res < 700)
572 if (rr.type != DNS_QRY_A)
576 if (rr.rdlength != 4)
580 memcpy(&alist->ip,&h.payload[i],4);
581 if ((unsigned)++curanswer >= h.ancount)
585 while (q == 0 && i < length)
587 if (h.payload[i] > 63)
594 if (h.payload[i] == 0)
599 else i += h.payload[i] + 1;
606 dns_fill_rr(&rr,&h.payload[i]);
608 alist->next = (dns_ip4list *) dns_align(((char *) alist) + sizeof(dns_ip4list));
615 memcpy(res,&h.payload[i],rr.rdlength);
616 res[rr.rdlength] = '\0';
619 memcpy(res,&h.payload[i],rr.rdlength);
620 res[rr.rdlength] = '\0';
628 log(DEBUG,"Create blank DNS");
635 Resolver::Resolver(const std::string &source, bool forward, const std::string &dnsserver = "") : input(source), fwd(forward), server(dnsserver)
639 this->myid = Res->dns_getip4(source.c_str());
644 if (insp_aton(source.c_str(), &binip) > 0)
646 /* Valid ip address */
647 this->myid = Res->dns_getname4(&binip);
650 if (this->myid == -1)
652 log(DEBUG,"Resolver::Resolver: Could not get an id!");
653 this->OnError(RESOLVER_NSDOWN);
654 ModuleException e("Resolver: Couldnt get an id to make a request");
656 /* We shouldnt get here really */
660 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
663 Resolver::~Resolver()
665 log(DEBUG,"Resolver::~Resolver");
668 int Resolver::GetId()
673 bool Resolver::ProcessResult()
675 log(DEBUG,"Resolver::ProcessResult");
677 std::map<int, std::string>::iterator x = result_list.find(this->myid);
679 if (x == result_list.end())
681 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
682 this->OnError(RESOLVER_NXDOMAIN);
688 log(DEBUG,"Resolver::OnLookupComplete(%s)",x->second.c_str());
689 this->OnLookupComplete(x->second);
690 result_list.erase(x);
695 void Resolver::OnLookupComplete(const std::string &result)
699 void Resolver::OnError(ResolverError e)
703 void dns_deal_with_classes(int fd)
705 log(DEBUG,"dns_deal_with_classes(%d)",fd);
706 if (fd == master_socket)
708 int id = Res->dns_getresult();
711 log(DEBUG,"Result available, id=%d",id);
712 dns_classes[id]->ProcessResult();
713 delete dns_classes[id];
714 dns_classes[id] = NULL;
719 bool dns_add_class(Resolver* r)
721 log(DEBUG,"dns_add_class");
722 if ((r) && (r->GetId() > -1))
724 if (!dns_classes[r->GetId()])
726 log(DEBUG,"dns_add_class: added class");
727 dns_classes[r->GetId()] = r;
732 log(DEBUG,"Space occupied!");
738 log(DEBUG,"Bad class");
747 memset(dns_classes,0,sizeof(dns_classes));