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"
57 pthread_mutex_t connmap_lock = PTHREAD_MUTEX_INITIALIZER;
60 extern InspIRCd* ServerInstance;
61 extern ServerConfig* Config;
63 extern userrec* fd_ref_table[MAX_DESCRIPTORS];
65 enum QueryType { DNS_QRY_A = 1, DNS_QRY_PTR = 12 };
66 enum QueryFlags1 { FLAGS1_MASK_RD = 0x01, FLAGS1_MASK_TC = 0x02, FLAGS1_MASK_AA = 0x04, FLAGS1_MASK_OPCODE = 0x78, FLAGS1_MASK_QR = 0x80 };
67 enum QueryFlags2 { FLAGS2_MASK_RCODE = 0x0F, FLAGS2_MASK_Z = 0x70, FLAGS2_MASK_RA = 0x80 };
71 typedef std::map<int,s_connection*> connlist;
72 typedef connlist::iterator connlist_iter;
75 Resolver* dns_classes[MAX_DESCRIPTORS];
77 struct in_addr servers4[8];
98 unsigned int rdlength;
107 unsigned int qdcount;
108 unsigned int ancount;
109 unsigned int nscount;
110 unsigned int arcount;
111 unsigned char payload[512];
115 void *dns_align(void *inp)
117 char *p = (char*)inp;
118 int offby = ((char *)p - (char *)0) % (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long));
120 return p + ((sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long)) - offby);
126 * Optimized by brain, these were using integer division and modulus.
127 * We can use logic shifts and logic AND to replace these even divisions
128 * and multiplications, it should be a bit faster (probably not noticably,
129 * but of course, more impressive). Also made these inline.
132 inline void dns_fill_rr(s_rr_middle* rr, const unsigned char *input)
134 rr->type = (QueryType)((input[0] << 8) + input[1]);
135 rr->_class = (input[2] << 8) + input[3];
136 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
137 rr->rdlength = (input[8] << 8) + input[9];
140 inline void dns_fill_header(s_header *header, const unsigned char *input, const int l)
142 header->id[0] = input[0];
143 header->id[1] = input[1];
144 header->flags1 = input[2];
145 header->flags2 = input[3];
146 header->qdcount = (input[4] << 8) + input[5];
147 header->ancount = (input[6] << 8) + input[7];
148 header->nscount = (input[8] << 8) + input[9];
149 header->arcount = (input[10] << 8) + input[11];
150 memcpy(header->payload,&input[12],l);
153 inline void dns_empty_header(unsigned char *output, const s_header *header, const int l)
155 output[0] = header->id[0];
156 output[1] = header->id[1];
157 output[2] = header->flags1;
158 output[3] = header->flags2;
159 output[4] = header->qdcount >> 8;
160 output[5] = header->qdcount & 0xFF;
161 output[6] = header->ancount >> 8;
162 output[7] = header->ancount & 0xFF;
163 output[8] = header->nscount >> 8;
164 output[9] = header->nscount & 0xFF;
165 output[10] = header->arcount >> 8;
166 output[11] = header->arcount & 0xFF;
167 memcpy(&output[12],header->payload,l);
170 void dns_close(int fd)
173 if (ServerInstance && ServerInstance->SE)
174 ServerInstance->SE->DelFd(fd);
176 log(DEBUG,"DNS: dns_close on fd %d",fd);
193 srand((unsigned int) TIME);
194 memset(servers4,'\0',sizeof(in_addr) * 8);
195 f = fopen("/etc/resolv.conf","r");
198 while (fgets(buf,1024,f) != NULL) {
199 if (strncmp(buf,"nameserver",10) == 0)
202 while (buf[i] == ' ' || buf[i] == '\t')
206 if (dns_aton4_s(&buf[i],&addr4) != NULL)
207 memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
214 void DNS::dns_init_2(const char* dnsserver)
218 srand((unsigned int) TIME);
219 memset(servers4,'\0',sizeof(in_addr) * 8);
220 if (dns_aton4_s(dnsserver,&addr4) != NULL)
221 memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
225 int dns_send_requests(const s_header *h, const s_connection *s, const int l)
229 unsigned char payload[sizeof(s_header)];
231 dns_empty_header(payload,h,l);
236 /* otherwise send via standard ipv4 boringness */
237 memset(&addr4,0,sizeof(addr4));
238 memcpy(&addr4.sin_addr,&servers4[i],sizeof(addr4.sin_addr));
239 addr4.sin_family = AF_INET;
240 addr4.sin_port = htons(53);
241 if (sendto(s->fd, payload, l + 12, 0, (sockaddr *) &addr4, sizeof(addr4)) == -1)
249 s_connection *dns_add_query(s_header *h)
252 s_connection * s = new s_connection;
253 int id = rand() % 65536;
255 /* set header flags */
256 h->id[0] = s->id[0] = id >> 8; /* verified by dns_getresult_s() */
257 h->id[1] = s->id[1] = id & 0xFF;
258 h->flags1 = 0 | FLAGS1_MASK_RD;
265 s->fd = socket(PF_INET, SOCK_DGRAM, 0);
268 if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0)
278 memset(&addr,0,sizeof(addr));
279 addr.sin_family = AF_INET;
281 addr.sin_addr.s_addr = INADDR_ANY;
282 if (bind(s->fd,(sockaddr *)&addr,sizeof(addr)) != 0)
294 /* create new connection object, add to linked list */
296 pthread_mutex_lock(&connmap_lock);
298 if (connections.find(s->fd) == connections.end())
299 connections[s->fd] = s;
301 pthread_mutex_unlock(&connmap_lock);
307 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
310 const char * tempchr, * tempchr2;
316 /* split name up into labels, create query */
317 while ((tempchr = strchr(tempchr2,'.')) != NULL)
319 l = tempchr - tempchr2;
320 if (payloadpos + l + 1 > 507)
322 payload[payloadpos++] = l;
323 memcpy(&payload[payloadpos],tempchr2,l);
325 tempchr2 = &tempchr[1];
327 l = strlen(tempchr2);
330 if (payloadpos + l + 2 > 507)
332 payload[payloadpos++] = l;
333 memcpy(&payload[payloadpos],tempchr2,l);
335 payload[payloadpos++] = '\0';
337 if (payloadpos > 508)
340 memcpy(&payload[payloadpos],&l,2);
342 memcpy(&payload[payloadpos + 2],&l,2);
343 return payloadpos + 4;
346 in_addr* DNS::dns_aton4(const char * const ipstring)
349 return dns_aton4_s(ipstring,&ip);
352 in_addr* DNS::dns_aton4_r(const char *ipstring) { /* ascii to numeric (reentrant): convert string to new 4part IP addr struct */
355 if(dns_aton4_s(ipstring,ip) == NULL)
363 in_addr* DNS::dns_aton4_s(const char *ipstring, in_addr *ip) { /* ascii to numeric (buffered): convert string to given 4part IP addr struct */
364 inet_aton(ipstring,ip);
368 int DNS::dns_getip4(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
376 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
379 s = dns_add_query(&h);
384 if (dns_send_requests(&h,s,l) == -1)
390 int DNS::dns_getip4list(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
397 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
400 s = dns_add_query(&h);
406 if (dns_send_requests(&h,s,l) == -1)
412 int DNS::dns_getname4(const in_addr *ip) { /* build, add and send PTR query; retrieve result with dns_getresult() */
419 c = (unsigned char *)&ip->s_addr;
421 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
423 l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
426 s = dns_add_query(&h);
430 s->type = DNS_QRY_PTR;
431 if (dns_send_requests(&h,s,l) == -1)
437 char* DNS::dns_ntoa4(const in_addr * const ip) { /* numeric to ascii: convert 4part IP addr struct to static string */
439 return dns_ntoa4_s(ip,r);
442 char* DNS::dns_ntoa4_s(const in_addr *ip, char *r) { /* numeric to ascii (buffered): convert 4part IP addr struct to given string */
444 m = (unsigned char *)&ip->s_addr;
445 sprintf(r,"%d.%d.%d.%d",m[0],m[1],m[2],m[3]);
449 char* DNS::dns_getresult(const int cfd) { /* retrieve result of DNS query */
450 log(DEBUG,"DNS: dns_getresult with cfd=%d",cfd);
451 return dns_getresult_s(cfd,this->localbuf);
454 char* DNS::dns_getresult_s(const int cfd, char *res) { /* retrieve result of DNS query (buffered) */
457 int l, i, q, curanswer, o;
459 unsigned char buffer[sizeof(s_header)];
465 /* FireDNS used a linked list for this. How ugly (and slow). */
468 /* XXX: STL really does NOT like being poked and prodded in more than
469 * one orifice by threaded apps. Make sure we remain nice to it, and
470 * lock a mutex around any access to the std::map.
472 pthread_mutex_lock(&connmap_lock);
474 connlist_iter n_iter = connections.find(cfd);
475 if (n_iter == connections.end())
477 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d",cfd);
479 pthread_mutex_unlock(&connmap_lock);
485 /* Remove the query from the list */
486 c = (s_connection*)n_iter->second;
487 /* We don't delete c here, because its done later when needed */
488 connections.erase(n_iter);
491 pthread_mutex_unlock(&connmap_lock);
494 l = recv(c->fd,buffer,sizeof(s_header),0);
501 dns_fill_header(&h,buffer,l - 12);
502 if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
504 log(DEBUG,"DNS: id mismatch on query");
506 return NULL; /* ID mismatch */
508 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
510 log(DEBUG,"DNS: didnt get a query result");
514 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
516 log(DEBUG,"DNS: got an OPCODE and didnt want one");
520 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
522 log(DEBUG,"DNS lookup failed due to SERVFAIL");
528 log(DEBUG,"DNS: no answers!");
535 while ((unsigned)q < h.qdcount && i < l)
537 if (h.payload[i] > 63)
544 if (h.payload[i] == 0)
549 else i += h.payload[i] + 1;
553 while ((unsigned)curanswer < h.ancount)
556 while (q == 0 && i < l)
558 if (h.payload[i] > 63)
565 if (h.payload[i] == 0)
570 else i += h.payload[i] + 1; /* skip length and label */
578 dns_fill_rr(&rr,&h.payload[i]);
580 if (rr.type != c->type)
586 if (rr._class != c->_class)
594 if ((unsigned)curanswer == h.ancount)
596 if ((unsigned)i + rr.rdlength > (unsigned)l)
598 if (rr.rdlength > 1023)
604 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
607 while (q == 0 && i < l && o + 256 < 1023)
609 if (h.payload[i] > 63)
611 log(DEBUG,"DNS: h.payload[i] > 63");
612 memcpy(&p,&h.payload[i],2);
613 i = ntohs(p) - 0xC000 - 12;
617 if (h.payload[i] == 0)
626 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
628 i += h.payload[i] + 1;
635 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
638 dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
639 while ((char *)alist - (char *)res < 700)
641 if (rr.type != DNS_QRY_A)
645 if (rr.rdlength != 4)
650 memcpy(&alist->ip,&h.payload[i],4);
651 if ((unsigned)++curanswer >= h.ancount)
655 while (q == 0 && i < l)
657 if (h.payload[i] > 63)
664 if (h.payload[i] == 0)
669 else i += h.payload[i] + 1;
677 dns_fill_rr(&rr,&h.payload[i]);
679 alist->next = (dns_ip4list *) dns_align(((char *) alist) + sizeof(dns_ip4list));
686 memcpy(res,&h.payload[i],rr.rdlength);
687 res[rr.rdlength] = '\0';
690 memcpy(res,&h.payload[i],rr.rdlength);
691 res[rr.rdlength] = '\0';
701 log(DEBUG,"Create blank DNS");
704 DNS::DNS(const std::string &dnsserver)
706 dns_init_2(dnsserver.c_str());
707 log(DEBUG,"Create DNS with server '%s'",dnsserver.c_str());
710 void DNS::SetNS(const std::string &dnsserver)
712 dns_init_2(dnsserver.c_str());
720 bool DNS::ReverseLookup(const std::string &ip, bool ins)
722 if (ServerInstance && ServerInstance->stats)
723 ServerInstance->stats->statsDns++;
724 binip = dns_aton4(ip.c_str());
730 this->myfd = dns_getname4(binip);
731 if (this->myfd == -1)
735 log(DEBUG,"DNS: ReverseLookup, fd=%d",this->myfd);
739 if (ServerInstance && ServerInstance->SE)
740 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
746 bool DNS::ForwardLookup(const std::string &host, bool ins)
748 if (ServerInstance && ServerInstance->stats)
749 ServerInstance->stats->statsDns++;
750 this->myfd = dns_getip4(host.c_str());
751 if (this->myfd == -1)
755 log(DEBUG,"DNS: ForwardLookup, fd=%d",this->myfd);
759 if (ServerInstance && ServerInstance->SE)
760 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
766 bool DNS::ForwardLookupWithFD(const std::string &host, int &fd)
768 if (ServerInstance && ServerInstance->stats)
769 ServerInstance->stats->statsDns++;
770 this->myfd = dns_getip4(host.c_str());
772 if (this->myfd == -1)
776 log(DEBUG,"DNS: ForwardLookupWithFD, fd=%d",this->myfd);
777 if (ServerInstance && ServerInstance->SE)
778 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_MODULE);
782 bool DNS::HasResult(int fd)
784 return (fd == this->myfd);
787 /* Only the multithreaded dns uses this poll() based
788 * check now. As its in another thread we dont have
789 * to worry about its performance that much.
791 bool DNS::HasResult()
793 log(DEBUG,"DNS: HasResult, fd=%d",this->myfd);
795 polls.fd = this->myfd;
796 polls.events = POLLIN;
797 int ret = poll(&polls,1,1);
798 log(DEBUG,"DNS: Hasresult returning %d",ret);
807 std::string DNS::GetResult()
809 log(DEBUG,"DNS: GetResult()");
810 result = dns_getresult(this->myfd);
813 if (ServerInstance && ServerInstance->stats)
814 ServerInstance->stats->statsDnsGood++;
815 dns_close(this->myfd);
821 if (ServerInstance && ServerInstance->stats)
822 ServerInstance->stats->statsDnsBad++;
823 if (this->myfd != -1)
825 dns_close(this->myfd);
832 std::string DNS::GetResultIP()
835 log(DEBUG,"DNS: GetResultIP()");
836 result = dns_getresult(this->myfd);
837 if (this->myfd != -1)
839 dns_close(this->myfd);
844 if (ServerInstance && ServerInstance->stats)
845 ServerInstance->stats->statsDnsGood++;
846 unsigned char a = (unsigned)result[0];
847 unsigned char b = (unsigned)result[1];
848 unsigned char c = (unsigned)result[2];
849 unsigned char d = (unsigned)result[3];
850 snprintf(r,1024,"%u.%u.%u.%u",a,b,c,d);
855 if (ServerInstance && ServerInstance->stats)
856 ServerInstance->stats->statsDnsBad++;
857 log(DEBUG,"DANGER WILL ROBINSON! NXDOMAIN for forward lookup, but we got a reverse lookup!");
866 /* This function is a thread function which can be thought of as a lightweight process
867 * to all you non-threaded people. In actuality its so much more, and pretty damn cool.
868 * With threaded dns enabled, each user which connects gets a thread attached to their
869 * user record when their DNS lookup starts. This function starts in parallel, and
870 * commences a blocking dns lookup. Because its a seperate thread, this occurs without
871 * actually blocking the main application. Once the dns lookup is completed, the thread
872 * checks if the user is still around by checking their fd against the reference table,
873 * and if they are, writes the hostname into the struct and terminates, after setting
874 * userrec::dns_done to true. Because this is multi-threaded it can make proper use of
875 * SMP setups (like the one i have here *grin*).
876 * This is in comparison to the non-threaded dns, which must monitor the thread sockets
877 * in a nonblocking fashion, consuming more resources to do so.
879 * NB: Yes this does scale, thank you. Even with large numbers of connecting clients
880 * in any one timeframe, they wont all connect *at the same time* therefore any argument
881 * of "but there will be thousands of threads it'll blow up" is moot, ive tested this and
882 * there will only ever be somewhere around the listen backlog in number of pending
883 * lookups at any one time. This is significant on any modern SMP system.
885 void* dns_task(void* arg)
887 userrec* u = (userrec*)arg;
890 log(DEBUG,"DNS thread for user %s",u->nick);
891 DNS dns1(Config->DNSServer);
892 DNS dns2(Config->DNSServer);
897 if (dns1.ReverseLookup(inet_ntoa(u->ip4),false))
899 /* FIX: Dont make these infinite! */
900 while ((!dns1.HasResult()) && (++iterations < 20))
905 if (dns1.GetFD() != -1)
907 host = dns1.GetResult();
910 if (dns2.ForwardLookup(host, false))
913 while ((!dns2.HasResult()) && (++iterations < 20))
918 if (dns2.GetFD() != -1)
920 ip = dns2.GetResultIP();
921 if (ip == std::string(inet_ntoa(u->ip4)))
923 if (host.length() < 65)
925 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
929 strcpy(u->host,host.c_str());
930 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
932 strcpy(u->dhost,host.c_str());
945 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
947 log(DEBUG,"THREAD EXIT");
952 Resolver::Resolver(const std::string &source, bool forward, const std::string &dnsserver = "") : input(source), fwd(forward), server(dnsserver)
954 if (this->server != "")
955 Query.SetNS(this->server);
957 Query.SetNS(Config->DNSServer);
961 Query.ForwardLookup(input.c_str(), false);
962 this->fd = Query.GetFD();
966 Query.ReverseLookup(input.c_str(), false);
967 this->fd = Query.GetFD();
971 log(DEBUG,"Resolver::Resolver: RESOLVER_NSDOWN");
972 this->OnError(RESOLVER_NSDOWN);
973 ModuleException e("Resolver: Nameserver is down");
975 /* We shouldnt get here really */
979 if (ServerInstance && ServerInstance->SE)
981 log(DEBUG,"Resolver::Resolver: this->fd=%d",this->fd);
982 ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_CLASSDNS);
986 log(DEBUG,"Resolver::Resolver: RESOLVER_NOTREADY");
987 this->OnError(RESOLVER_NOTREADY);
988 ModuleException e("Resolver: Core not initialized yet");
990 /* We shouldnt get here really */
995 Resolver::~Resolver()
997 log(DEBUG,"Resolver::~Resolver");
998 if (ServerInstance && ServerInstance->SE)
999 ServerInstance->SE->DelFd(this->fd);
1002 int Resolver::GetFd()
1007 bool Resolver::ProcessResult()
1009 log(DEBUG,"Resolver::ProcessResult");
1011 result = Query.GetResultIP();
1013 result = Query.GetResult();
1017 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
1018 this->OnLookupComplete(result);
1023 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
1024 this->OnError(RESOLVER_NXDOMAIN);
1029 void Resolver::OnLookupComplete(const std::string &result)
1033 void Resolver::OnError(ResolverError e)
1037 void dns_deal_with_classes(int fd)
1039 log(DEBUG,"dns_deal_with_classes(%d)",fd);
1040 if ((fd > -1) && (dns_classes[fd]))
1042 log(DEBUG,"Valid fd %d",fd);
1043 dns_classes[fd]->ProcessResult();
1044 delete dns_classes[fd];
1045 dns_classes[fd] = NULL;
1049 bool dns_add_class(Resolver* r)
1051 log(DEBUG,"dns_add_class");
1052 if ((r) && (r->GetFd() > -1))
1054 if (!dns_classes[r->GetFd()])
1056 log(DEBUG,"dns_add_class: added class");
1057 dns_classes[r->GetFd()] = r;
1062 log(DEBUG,"Space occupied!");
1068 log(DEBUG,"Bad class");
1076 memset(dns_classes,0,sizeof(dns_classes));