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 "socketengine.h"
54 extern InspIRCd* ServerInstance;
55 extern ServerConfig* Config;
58 enum QueryType { DNS_QRY_A = 1, DNS_QRY_PTR = 12 };
59 enum QueryFlags1 { FLAGS1_MASK_RD = 0x01, FLAGS1_MASK_TC = 0x02, FLAGS1_MASK_AA = 0x04, FLAGS1_MASK_OPCODE = 0x78, FLAGS1_MASK_QR = 0x80 };
60 enum QueryFlags2 { FLAGS2_MASK_RCODE = 0x0F, FLAGS2_MASK_Z = 0x70, FLAGS2_MASK_RA = 0x80 };
64 typedef std::map<int,s_connection*> connlist;
65 typedef connlist::iterator connlist_iter;
68 struct in_addr servers4[8];
90 unsigned int rdlength;
100 unsigned int ancount;
101 unsigned int nscount;
102 unsigned int arcount;
103 unsigned char payload[512];
107 void *dns_align(void *inp)
109 char *p = (char*)inp;
110 int offby = ((char *)p - (char *)0) % (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long));
112 return p + ((sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long)) - offby);
118 * Optimized by brain, these were using integer division and modulus.
119 * We can use logic shifts and logic AND to replace these even divisions
120 * and multiplications, it should be a bit faster (probably not noticably,
121 * but of course, more impressive). Also made these inline.
124 inline void dns_fill_rr(s_rr_middle* rr, const unsigned char *input)
126 rr->type = (QueryType)((input[0] << 8) + input[1]);
127 rr->_class = (input[2] << 8) + input[3];
128 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
129 rr->rdlength = (input[8] << 8) + input[9];
132 inline void dns_fill_header(s_header *header, const unsigned char *input, const int l)
134 header->id[0] = input[0];
135 header->id[1] = input[1];
136 header->flags1 = input[2];
137 header->flags2 = input[3];
138 header->qdcount = (input[4] << 8) + input[5];
139 header->ancount = (input[6] << 8) + input[7];
140 header->nscount = (input[8] << 8) + input[9];
141 header->arcount = (input[10] << 8) + input[11];
142 memcpy(header->payload,&input[12],l);
145 inline void dns_empty_header(unsigned char *output, const s_header *header, const int l)
147 output[0] = header->id[0];
148 output[1] = header->id[1];
149 output[2] = header->flags1;
150 output[3] = header->flags2;
151 output[4] = header->qdcount >> 8;
152 output[5] = header->qdcount & 0xFF;
153 output[6] = header->ancount >> 8;
154 output[7] = header->ancount & 0xFF;
155 output[8] = header->nscount >> 8;
156 output[9] = header->nscount & 0xFF;
157 output[10] = header->arcount >> 8;
158 output[11] = header->arcount & 0xFF;
159 memcpy(&output[12],header->payload,l);
162 void dns_close(int fd)
165 ServerInstance->SE->DelFd(fd);
167 log(DEBUG,"DNS: dns_close on fd %d",fd);
168 if (fd == lastcreate)
189 srand((unsigned int) TIME);
190 memset(servers4,'\0',sizeof(in_addr) * 8);
191 f = fopen("/etc/resolv.conf","r");
194 while (fgets(buf,1024,f) != NULL) {
195 if (strncmp(buf,"nameserver",10) == 0)
198 while (buf[i] == ' ' || buf[i] == '\t')
202 if (dns_aton4_s(&buf[i],&addr4) != NULL)
203 memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
210 void DNS::dns_init_2(const char* dnsserver)
214 srand((unsigned int) TIME);
215 memset(servers4,'\0',sizeof(in_addr) * 8);
216 if (dns_aton4_s(dnsserver,&addr4) != NULL)
217 memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
221 int dns_send_requests(const s_header *h, const s_connection *s, const int l)
225 unsigned char payload[sizeof(s_header)];
227 dns_empty_header(payload,h,l);
232 /* otherwise send via standard ipv4 boringness */
233 memset(&addr4,0,sizeof(addr4));
234 memcpy(&addr4.sin_addr,&servers4[i],sizeof(addr4.sin_addr));
235 addr4.sin_family = AF_INET;
236 addr4.sin_port = htons(53);
237 if (sendto(s->fd, payload, l + 12, 0, (sockaddr *) &addr4, sizeof(addr4)) == -1)
245 s_connection *dns_add_query(s_header *h)
248 s_connection * s = new s_connection;
249 int id = rand() % 65536;
251 /* set header flags */
252 h->id[0] = s->id[0] = id >> 8; /* verified by dns_getresult_s() */
253 h->id[1] = s->id[1] = id & 0xFF;
254 h->flags1 = 0 | FLAGS1_MASK_RD;
261 s->fd = socket(PF_INET, SOCK_DGRAM, 0);
264 if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0)
274 memset(&addr,0,sizeof(addr));
275 addr.sin_family = AF_INET;
277 addr.sin_addr.s_addr = INADDR_ANY;
278 if (bind(s->fd,(sockaddr *)&addr,sizeof(addr)) != 0)
290 /* create new connection object, add to linked list */
291 if (connections.find(s->fd) == connections.end())
292 connections[s->fd] = s;
296 shutdown(lastcreate,2);
304 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
307 const char * tempchr, * tempchr2;
313 /* split name up into labels, create query */
314 while ((tempchr = strchr(tempchr2,'.')) != NULL)
316 l = tempchr - tempchr2;
317 if (payloadpos + l + 1 > 507)
319 payload[payloadpos++] = l;
320 memcpy(&payload[payloadpos],tempchr2,l);
322 tempchr2 = &tempchr[1];
324 l = strlen(tempchr2);
327 if (payloadpos + l + 2 > 507)
329 payload[payloadpos++] = l;
330 memcpy(&payload[payloadpos],tempchr2,l);
332 payload[payloadpos++] = '\0';
334 if (payloadpos > 508)
337 memcpy(&payload[payloadpos],&l,2);
339 memcpy(&payload[payloadpos + 2],&l,2);
340 return payloadpos + 4;
343 in_addr* DNS::dns_aton4(const char * const ipstring)
346 return dns_aton4_s(ipstring,&ip);
349 in_addr* DNS::dns_aton4_r(const char *ipstring) { /* ascii to numeric (reentrant): convert string to new 4part IP addr struct */
352 if(dns_aton4_s(ipstring,ip) == NULL)
360 in_addr* DNS::dns_aton4_s(const char *ipstring, in_addr *ip) { /* ascii to numeric (buffered): convert string to given 4part IP addr struct */
361 inet_aton(ipstring,ip);
365 int DNS::dns_getip4(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
373 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
376 s = dns_add_query(&h);
381 if (dns_send_requests(&h,s,l) == -1)
387 int DNS::dns_getip4list(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
394 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
397 s = dns_add_query(&h);
403 if (dns_send_requests(&h,s,l) == -1)
409 int DNS::dns_getname4(const in_addr *ip) { /* build, add and send PTR query; retrieve result with dns_getresult() */
416 c = (unsigned char *)&ip->s_addr;
418 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
420 l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
423 s = dns_add_query(&h);
427 s->type = DNS_QRY_PTR;
428 if (dns_send_requests(&h,s,l) == -1)
434 char* DNS::dns_ntoa4(const in_addr * const ip) { /* numeric to ascii: convert 4part IP addr struct to static string */
436 return dns_ntoa4_s(ip,r);
439 char* DNS::dns_ntoa4_s(const in_addr *ip, char *r) { /* numeric to ascii (buffered): convert 4part IP addr struct to given string */
441 m = (unsigned char *)&ip->s_addr;
442 sprintf(r,"%d.%d.%d.%d",m[0],m[1],m[2],m[3]);
446 char* DNS::dns_getresult(const int cfd) { /* retrieve result of DNS query */
447 log(DEBUG,"DNS: dns_getresult with cfd=%d",cfd);
448 return dns_getresult_s(cfd,this->localbuf);
451 char* DNS::dns_getresult_s(const int cfd, char *res) { /* retrieve result of DNS query (buffered) */
454 int l, i, q, curanswer, o;
456 unsigned char buffer[sizeof(s_header)];
462 /* FireDNS used a linked list for this. How ugly (and slow). */
463 connlist_iter n_iter = connections.find(cfd);
464 if (n_iter == connections.end())
466 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d",cfd);
471 /* Remove the query from the list */
472 c = (s_connection*)n_iter->second;
473 /* We don't delete c here, because its done later when needed */
474 connections.erase(n_iter);
477 l = recv(c->fd,buffer,sizeof(s_header),0);
484 dns_fill_header(&h,buffer,l - 12);
485 if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
487 log(DEBUG,"DNS: id mismatch on query");
489 return NULL; /* ID mismatch */
491 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
493 log(DEBUG,"DNS: didnt get a query result");
497 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
499 log(DEBUG,"DNS: got an OPCODE and didnt want one");
503 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
505 log(DEBUG,"DNS lookup failed due to SERVFAIL");
511 log(DEBUG,"DNS: no answers!");
518 while ((unsigned)q < h.qdcount && i < l)
520 if (h.payload[i] > 63)
527 if (h.payload[i] == 0)
532 else i += h.payload[i] + 1;
536 while ((unsigned)curanswer < h.ancount)
539 while (q == 0 && i < l)
541 if (h.payload[i] > 63)
548 if (h.payload[i] == 0)
553 else i += h.payload[i] + 1; /* skip length and label */
561 dns_fill_rr(&rr,&h.payload[i]);
563 if (rr.type != c->type)
569 if (rr._class != c->_class)
577 if ((unsigned)curanswer == h.ancount)
579 if ((unsigned)i + rr.rdlength > (unsigned)l)
581 if (rr.rdlength > 1023)
587 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
590 while (q == 0 && i < l && o + 256 < 1023)
592 if (h.payload[i] > 63)
594 memcpy(&p,&h.payload[i],2);
595 i = ntohs(p) - 0xC000 - 12;
599 if (h.payload[i] == 0)
608 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
610 i += h.payload[i] + 1;
617 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
620 dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
621 while ((char *)alist - (char *)res < 700)
623 if (rr.type != DNS_QRY_A)
627 if (rr.rdlength != 4)
632 memcpy(&alist->ip,&h.payload[i],4);
633 if ((unsigned)++curanswer >= h.ancount)
637 while (q == 0 && i < l)
639 if (h.payload[i] > 63)
646 if (h.payload[i] == 0)
651 else i += h.payload[i] + 1;
659 dns_fill_rr(&rr,&h.payload[i]);
661 alist->next = (dns_ip4list *) dns_align(((char *) alist) + sizeof(dns_ip4list));
668 memcpy(res,&h.payload[i],rr.rdlength);
669 res[rr.rdlength] = '\0';
672 memcpy(res,&h.payload[i],rr.rdlength);
673 res[rr.rdlength] = '\0';
683 log(DEBUG,"Create blank DNS");
686 DNS::DNS(std::string dnsserver)
688 dns_init_2(dnsserver.c_str());
689 log(DEBUG,"Create DNS");
692 void DNS::SetNS(std::string dnsserver)
694 dns_init_2(dnsserver.c_str());
702 bool DNS::ReverseLookup(std::string ip)
704 ServerInstance->stats->statsDns++;
705 binip = dns_aton4(ip.c_str());
711 this->myfd = dns_getname4(binip);
712 if (this->myfd == -1)
716 log(DEBUG,"DNS: ReverseLookup, fd=%d",this->myfd);
718 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
723 bool DNS::ForwardLookup(std::string host)
725 ServerInstance->stats->statsDns++;
726 this->myfd = dns_getip4(host.c_str());
727 if (this->myfd == -1)
731 log(DEBUG,"DNS: ForwardLookup, fd=%d",this->myfd);
733 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
738 bool DNS::HasResult(int fd)
740 return (fd == this->myfd);
743 /* Only the multithreaded dns uses this poll() based
744 * check now. As its in another thread we dont have
745 * to worry about its performance that much.
747 bool DNS::HasResult()
749 log(DEBUG,"DNS: HasResult, fd=%d",this->myfd);
751 polls.fd = this->myfd;
752 polls.events = POLLIN;
753 int ret = poll(&polls,1,1);
754 log(DEBUG,"DNS: Hasresult returning %d",ret);
763 std::string DNS::GetResult()
765 log(DEBUG,"DNS: GetResult()");
766 result = dns_getresult(this->myfd);
769 ServerInstance->stats->statsDnsGood++;
770 dns_close(this->myfd);
775 ServerInstance->stats->statsDnsBad++;
776 if (this->myfd != -1)
778 dns_close(this->myfd);
784 std::string DNS::GetResultIP()
787 log(DEBUG,"DNS: GetResultIP()");
788 result = dns_getresult(this->myfd);
789 if (this->myfd != -1)
791 dns_close(this->myfd);
795 ServerInstance->stats->statsDnsGood++;
796 unsigned char a = (unsigned)result[0];
797 unsigned char b = (unsigned)result[1];
798 unsigned char c = (unsigned)result[2];
799 unsigned char d = (unsigned)result[3];
800 snprintf(r,1024,"%u.%u.%u.%u",a,b,c,d);
805 ServerInstance->stats->statsDnsBad++;
806 log(DEBUG,"DANGER WILL ROBINSON! NXDOMAIN for forward lookup, but we got a reverse lookup!");
814 void* dns_task(void* arg)
816 userrec* u = (userrec*)arg;
817 log(DEBUG,"DNS thread for user %s",u->nick);
822 if (dns1.ReverseLookup(u->ip))
824 while (!dns1.HasResult())
828 host = dns1.GetResult();
831 if (dns2.ForwardLookup(host))
833 while (!dns2.HasResult())
837 ip = dns2.GetResultIP();
838 if (ip == std::string(u->ip))
840 if (host.length() < 160)
842 strcpy(u->host,host.c_str());
843 strcpy(u->dhost,host.c_str());