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 if (ServerInstance && ServerInstance->SE)
166 ServerInstance->SE->DelFd(fd);
168 log(DEBUG,"DNS: dns_close on fd %d",fd);
169 if (fd == lastcreate)
190 srand((unsigned int) TIME);
191 memset(servers4,'\0',sizeof(in_addr) * 8);
192 f = fopen("/etc/resolv.conf","r");
195 while (fgets(buf,1024,f) != NULL) {
196 if (strncmp(buf,"nameserver",10) == 0)
199 while (buf[i] == ' ' || buf[i] == '\t')
203 if (dns_aton4_s(&buf[i],&addr4) != NULL)
204 memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
211 void DNS::dns_init_2(const char* dnsserver)
215 srand((unsigned int) TIME);
216 memset(servers4,'\0',sizeof(in_addr) * 8);
217 if (dns_aton4_s(dnsserver,&addr4) != NULL)
218 memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
222 int dns_send_requests(const s_header *h, const s_connection *s, const int l)
226 unsigned char payload[sizeof(s_header)];
228 dns_empty_header(payload,h,l);
233 /* otherwise send via standard ipv4 boringness */
234 memset(&addr4,0,sizeof(addr4));
235 memcpy(&addr4.sin_addr,&servers4[i],sizeof(addr4.sin_addr));
236 addr4.sin_family = AF_INET;
237 addr4.sin_port = htons(53);
238 if (sendto(s->fd, payload, l + 12, 0, (sockaddr *) &addr4, sizeof(addr4)) == -1)
246 s_connection *dns_add_query(s_header *h)
249 s_connection * s = new s_connection;
250 int id = rand() % 65536;
252 /* set header flags */
253 h->id[0] = s->id[0] = id >> 8; /* verified by dns_getresult_s() */
254 h->id[1] = s->id[1] = id & 0xFF;
255 h->flags1 = 0 | FLAGS1_MASK_RD;
262 s->fd = socket(PF_INET, SOCK_DGRAM, 0);
265 if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0)
275 memset(&addr,0,sizeof(addr));
276 addr.sin_family = AF_INET;
278 addr.sin_addr.s_addr = INADDR_ANY;
279 if (bind(s->fd,(sockaddr *)&addr,sizeof(addr)) != 0)
291 /* create new connection object, add to linked list */
292 if (connections.find(s->fd) == connections.end())
293 connections[s->fd] = s;
297 shutdown(lastcreate,2);
305 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
308 const char * tempchr, * tempchr2;
314 /* split name up into labels, create query */
315 while ((tempchr = strchr(tempchr2,'.')) != NULL)
317 l = tempchr - tempchr2;
318 if (payloadpos + l + 1 > 507)
320 payload[payloadpos++] = l;
321 memcpy(&payload[payloadpos],tempchr2,l);
323 tempchr2 = &tempchr[1];
325 l = strlen(tempchr2);
328 if (payloadpos + l + 2 > 507)
330 payload[payloadpos++] = l;
331 memcpy(&payload[payloadpos],tempchr2,l);
333 payload[payloadpos++] = '\0';
335 if (payloadpos > 508)
338 memcpy(&payload[payloadpos],&l,2);
340 memcpy(&payload[payloadpos + 2],&l,2);
341 return payloadpos + 4;
344 in_addr* DNS::dns_aton4(const char * const ipstring)
347 return dns_aton4_s(ipstring,&ip);
350 in_addr* DNS::dns_aton4_r(const char *ipstring) { /* ascii to numeric (reentrant): convert string to new 4part IP addr struct */
353 if(dns_aton4_s(ipstring,ip) == NULL)
361 in_addr* DNS::dns_aton4_s(const char *ipstring, in_addr *ip) { /* ascii to numeric (buffered): convert string to given 4part IP addr struct */
362 inet_aton(ipstring,ip);
366 int DNS::dns_getip4(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
374 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
377 s = dns_add_query(&h);
382 if (dns_send_requests(&h,s,l) == -1)
388 int DNS::dns_getip4list(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
395 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
398 s = dns_add_query(&h);
404 if (dns_send_requests(&h,s,l) == -1)
410 int DNS::dns_getname4(const in_addr *ip) { /* build, add and send PTR query; retrieve result with dns_getresult() */
417 c = (unsigned char *)&ip->s_addr;
419 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
421 l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
424 s = dns_add_query(&h);
428 s->type = DNS_QRY_PTR;
429 if (dns_send_requests(&h,s,l) == -1)
435 char* DNS::dns_ntoa4(const in_addr * const ip) { /* numeric to ascii: convert 4part IP addr struct to static string */
437 return dns_ntoa4_s(ip,r);
440 char* DNS::dns_ntoa4_s(const in_addr *ip, char *r) { /* numeric to ascii (buffered): convert 4part IP addr struct to given string */
442 m = (unsigned char *)&ip->s_addr;
443 sprintf(r,"%d.%d.%d.%d",m[0],m[1],m[2],m[3]);
447 char* DNS::dns_getresult(const int cfd) { /* retrieve result of DNS query */
448 log(DEBUG,"DNS: dns_getresult with cfd=%d",cfd);
449 return dns_getresult_s(cfd,this->localbuf);
452 char* DNS::dns_getresult_s(const int cfd, char *res) { /* retrieve result of DNS query (buffered) */
455 int l, i, q, curanswer, o;
457 unsigned char buffer[sizeof(s_header)];
463 /* FireDNS used a linked list for this. How ugly (and slow). */
464 connlist_iter n_iter = connections.find(cfd);
465 if (n_iter == connections.end())
467 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d",cfd);
472 /* Remove the query from the list */
473 c = (s_connection*)n_iter->second;
474 /* We don't delete c here, because its done later when needed */
475 connections.erase(n_iter);
478 l = recv(c->fd,buffer,sizeof(s_header),0);
485 dns_fill_header(&h,buffer,l - 12);
486 if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
488 log(DEBUG,"DNS: id mismatch on query");
490 return NULL; /* ID mismatch */
492 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
494 log(DEBUG,"DNS: didnt get a query result");
498 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
500 log(DEBUG,"DNS: got an OPCODE and didnt want one");
504 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
506 log(DEBUG,"DNS lookup failed due to SERVFAIL");
512 log(DEBUG,"DNS: no answers!");
519 while ((unsigned)q < h.qdcount && i < l)
521 if (h.payload[i] > 63)
528 if (h.payload[i] == 0)
533 else i += h.payload[i] + 1;
537 while ((unsigned)curanswer < h.ancount)
540 while (q == 0 && i < l)
542 if (h.payload[i] > 63)
549 if (h.payload[i] == 0)
554 else i += h.payload[i] + 1; /* skip length and label */
562 dns_fill_rr(&rr,&h.payload[i]);
564 if (rr.type != c->type)
570 if (rr._class != c->_class)
578 if ((unsigned)curanswer == h.ancount)
580 if ((unsigned)i + rr.rdlength > (unsigned)l)
582 if (rr.rdlength > 1023)
588 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
591 while (q == 0 && i < l && o + 256 < 1023)
593 if (h.payload[i] > 63)
595 memcpy(&p,&h.payload[i],2);
596 i = ntohs(p) - 0xC000 - 12;
600 if (h.payload[i] == 0)
609 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
611 i += h.payload[i] + 1;
618 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
621 dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
622 while ((char *)alist - (char *)res < 700)
624 if (rr.type != DNS_QRY_A)
628 if (rr.rdlength != 4)
633 memcpy(&alist->ip,&h.payload[i],4);
634 if ((unsigned)++curanswer >= h.ancount)
638 while (q == 0 && i < l)
640 if (h.payload[i] > 63)
647 if (h.payload[i] == 0)
652 else i += h.payload[i] + 1;
660 dns_fill_rr(&rr,&h.payload[i]);
662 alist->next = (dns_ip4list *) dns_align(((char *) alist) + sizeof(dns_ip4list));
669 memcpy(res,&h.payload[i],rr.rdlength);
670 res[rr.rdlength] = '\0';
673 memcpy(res,&h.payload[i],rr.rdlength);
674 res[rr.rdlength] = '\0';
684 log(DEBUG,"Create blank DNS");
687 DNS::DNS(const std::string &dnsserver)
689 dns_init_2(dnsserver.c_str());
690 log(DEBUG,"Create DNS with server '%s'",dnsserver.c_str());
693 void DNS::SetNS(const std::string &dnsserver)
695 dns_init_2(dnsserver.c_str());
703 bool DNS::ReverseLookup(const std::string &ip)
705 if (ServerInstance && ServerInstance->stats)
706 ServerInstance->stats->statsDns++;
707 binip = dns_aton4(ip.c_str());
713 this->myfd = dns_getname4(binip);
714 if (this->myfd == -1)
718 log(DEBUG,"DNS: ReverseLookup, fd=%d",this->myfd);
720 if (ServerInstance && ServerInstance->SE)
721 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
726 bool DNS::ForwardLookup(const std::string &host)
728 if (ServerInstance && ServerInstance->stats)
729 ServerInstance->stats->statsDns++;
730 this->myfd = dns_getip4(host.c_str());
731 if (this->myfd == -1)
735 log(DEBUG,"DNS: ForwardLookup, fd=%d",this->myfd);
737 if (ServerInstance && ServerInstance->SE)
738 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
743 bool DNS::ForwardLookupWithFD(const std::string &host, int &fd)
745 if (ServerInstance && ServerInstance->stats)
746 ServerInstance->stats->statsDns++;
747 this->myfd = dns_getip4(host.c_str());
749 if (this->myfd == -1)
753 log(DEBUG,"DNS: ForwardLookupWithFD, fd=%d",this->myfd);
754 if (ServerInstance && ServerInstance->SE)
755 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_MODULE);
759 bool DNS::HasResult(int fd)
761 return (fd == this->myfd);
764 /* Only the multithreaded dns uses this poll() based
765 * check now. As its in another thread we dont have
766 * to worry about its performance that much.
768 bool DNS::HasResult()
770 log(DEBUG,"DNS: HasResult, fd=%d",this->myfd);
772 polls.fd = this->myfd;
773 polls.events = POLLIN;
774 int ret = poll(&polls,1,1);
775 log(DEBUG,"DNS: Hasresult returning %d",ret);
784 std::string DNS::GetResult()
786 log(DEBUG,"DNS: GetResult()");
787 result = dns_getresult(this->myfd);
790 if (ServerInstance && ServerInstance->stats)
791 ServerInstance->stats->statsDnsGood++;
792 dns_close(this->myfd);
797 if (ServerInstance && ServerInstance->stats)
798 ServerInstance->stats->statsDnsBad++;
799 if (this->myfd != -1)
801 dns_close(this->myfd);
807 std::string DNS::GetResultIP()
810 log(DEBUG,"DNS: GetResultIP()");
811 result = dns_getresult(this->myfd);
812 if (this->myfd != -1)
814 dns_close(this->myfd);
818 if (ServerInstance && ServerInstance->stats)
819 ServerInstance->stats->statsDnsGood++;
820 unsigned char a = (unsigned)result[0];
821 unsigned char b = (unsigned)result[1];
822 unsigned char c = (unsigned)result[2];
823 unsigned char d = (unsigned)result[3];
824 snprintf(r,1024,"%u.%u.%u.%u",a,b,c,d);
829 if (ServerInstance && ServerInstance->stats)
830 ServerInstance->stats->statsDnsBad++;
831 log(DEBUG,"DANGER WILL ROBINSON! NXDOMAIN for forward lookup, but we got a reverse lookup!");
839 void* dns_task(void* arg)
841 userrec* u = (userrec*)arg;
842 log(DEBUG,"DNS thread for user %s",u->nick);
847 if (dns1.ReverseLookup((char*)inet_ntoa(u->ip4)))
849 while (!dns1.HasResult())
853 host = dns1.GetResult();
856 if (dns2.ForwardLookup(host))
858 while (!dns2.HasResult())
862 ip = dns2.GetResultIP();
863 if (ip == std::string((char*)inet_ntoa(u->ip4)))
865 if (host.length() < 160)
867 strcpy(u->host,host.c_str());
868 strcpy(u->dhost,host.c_str());