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 insp_inaddr servers[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(servers,'\0',sizeof(insp_inaddr) * 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 (insp_aton(&buf[i],&addr) > 0)
208 log(DEBUG,"Add server %d, %s",i4,&buf[i]);
209 memcpy(&servers[i4++],&addr,sizeof(insp_inaddr));
217 void DNS::dns_init_2(const char* dnsserver)
219 log(DEBUG,"*************** DNS INIT 2 **************");
222 srand((unsigned int) TIME);
223 memset(servers,'\0',sizeof(insp_inaddr) * 8);
224 log(DEBUG,"ADD DNS: %s",dnsserver);
225 if (insp_aton(dnsserver,&addr) > 0)
227 unsigned char* n = (unsigned char*)&addr;
228 log(DEBUG,"***** Add server %d, %s: %d, %d, %d, %d",i4,dnsserver,n[0],n[1],n[2],n[3]);
229 memcpy(&servers[i4++],&addr,sizeof(insp_inaddr));
234 int dns_send_requests(const s_header *h, const s_connection *s, const int l)
238 unsigned char payload[sizeof(s_header)];
240 dns_empty_header(payload,h,l);
245 /* otherwise send via standard ipv4 boringness */
246 memset(&addr,0,sizeof(addr));
248 memcpy(&addr.sin6_addr,&servers[i],sizeof(addr.sin6_addr));
249 addr.sin6_family = AF_FAMILY;
250 addr.sin6_port = htons(53);
252 memcpy(&addr.sin_addr.s_addr,&servers[i],sizeof(addr.sin_addr));
253 addr.sin_family = AF_FAMILY;
254 addr.sin_port = htons(53);
256 if (sendto(s->fd, payload, l + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
258 log(DEBUG,"Error in sendto!");
265 s_connection *dns_add_query(s_header *h)
268 s_connection * s = new s_connection;
269 int id = rand() % 65536;
271 /* set header flags */
272 h->id[0] = s->id[0] = id >> 8; /* verified by dns_getresult_s() */
273 h->id[1] = s->id[1] = id & 0xFF;
274 h->flags1 = 0 | FLAGS1_MASK_RD;
281 s->fd = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
284 log(DEBUG,"Set query socket nonblock");
285 if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0)
296 memset(&addr,0,sizeof(addr));
297 addr.sin6_family = AF_FAMILY;
299 memset(&addr.sin6_addr,255,sizeof(in6_addr));
302 memset(&addr,0,sizeof(addr));
303 addr.sin_family = AF_FAMILY;
305 addr.sin_addr.s_addr = INADDR_ANY;
306 if (bind(s->fd,(sockaddr *)&addr,sizeof(addr)) != 0)
308 log(DEBUG,"Cant bind with source port = 0");
320 /* create new connection object, add to linked list */
322 pthread_mutex_lock(&connmap_lock);
324 if (connections.find(s->fd) == connections.end())
325 connections[s->fd] = s;
327 pthread_mutex_unlock(&connmap_lock);
333 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
336 const char * tempchr, * tempchr2;
342 /* split name up into labels, create query */
343 while ((tempchr = strchr(tempchr2,'.')) != NULL)
345 l = tempchr - tempchr2;
346 if (payloadpos + l + 1 > 507)
348 payload[payloadpos++] = l;
349 memcpy(&payload[payloadpos],tempchr2,l);
351 tempchr2 = &tempchr[1];
353 l = strlen(tempchr2);
356 if (payloadpos + l + 2 > 507)
358 payload[payloadpos++] = l;
359 memcpy(&payload[payloadpos],tempchr2,l);
361 payload[payloadpos++] = '\0';
363 if (payloadpos > 508)
366 memcpy(&payload[payloadpos],&l,2);
368 memcpy(&payload[payloadpos + 2],&l,2);
369 return payloadpos + 4;
372 int DNS::dns_getip4(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
380 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
383 s = dns_add_query(&h);
388 if (dns_send_requests(&h,s,l) == -1)
394 int DNS::dns_getip4list(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
401 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
404 s = dns_add_query(&h);
410 if (dns_send_requests(&h,s,l) == -1)
416 int DNS::dns_getname4(const insp_inaddr *ip)
417 { /* build, add and send PTR query; retrieve result with dns_getresult() */
421 log(DEBUG,"DNS::dns_getname4");
428 c = (unsigned char *)&ip->s_addr;
430 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
432 l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
435 s = dns_add_query(&h);
439 s->type = DNS_QRY_PTR;
440 if (dns_send_requests(&h,s,l) == -1)
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). */
466 /* XXX: STL really does NOT like being poked and prodded in more than
467 * one orifice by threaded apps. Make sure we remain nice to it, and
468 * lock a mutex around any access to the std::map.
470 pthread_mutex_lock(&connmap_lock);
472 connlist_iter n_iter = connections.find(cfd);
473 if (n_iter == connections.end())
475 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d",cfd);
477 pthread_mutex_unlock(&connmap_lock);
483 /* Remove the query from the list */
484 c = (s_connection*)n_iter->second;
485 /* We don't delete c here, because its done later when needed */
486 connections.erase(n_iter);
489 pthread_mutex_unlock(&connmap_lock);
492 l = recv(c->fd,buffer,sizeof(s_header),0);
499 dns_fill_header(&h,buffer,l - 12);
500 if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
502 log(DEBUG,"DNS: id mismatch on query");
504 return NULL; /* ID mismatch */
506 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
508 log(DEBUG,"DNS: didnt get a query result");
512 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
514 log(DEBUG,"DNS: got an OPCODE and didnt want one");
518 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
520 log(DEBUG,"DNS lookup failed due to SERVFAIL");
526 log(DEBUG,"DNS: no answers!");
533 while ((unsigned)q < h.qdcount && i < l)
535 if (h.payload[i] > 63)
542 if (h.payload[i] == 0)
547 else i += h.payload[i] + 1;
551 while ((unsigned)curanswer < h.ancount)
554 while (q == 0 && i < l)
556 if (h.payload[i] > 63)
563 if (h.payload[i] == 0)
568 else i += h.payload[i] + 1; /* skip length and label */
576 dns_fill_rr(&rr,&h.payload[i]);
578 if (rr.type != c->type)
584 if (rr._class != c->_class)
592 if ((unsigned)curanswer == h.ancount)
594 if ((unsigned)i + rr.rdlength > (unsigned)l)
596 if (rr.rdlength > 1023)
602 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
605 while (q == 0 && i < l && o + 256 < 1023)
607 if (h.payload[i] > 63)
609 log(DEBUG,"DNS: h.payload[i] > 63");
610 memcpy(&p,&h.payload[i],2);
611 i = ntohs(p) - 0xC000 - 12;
615 if (h.payload[i] == 0)
624 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
626 i += h.payload[i] + 1;
633 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
636 dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
637 while ((char *)alist - (char *)res < 700)
639 if (rr.type != DNS_QRY_A)
643 if (rr.rdlength != 4)
648 memcpy(&alist->ip,&h.payload[i],4);
649 if ((unsigned)++curanswer >= h.ancount)
653 while (q == 0 && i < l)
655 if (h.payload[i] > 63)
662 if (h.payload[i] == 0)
667 else i += h.payload[i] + 1;
675 dns_fill_rr(&rr,&h.payload[i]);
677 alist->next = (dns_ip4list *) dns_align(((char *) alist) + sizeof(dns_ip4list));
684 memcpy(res,&h.payload[i],rr.rdlength);
685 res[rr.rdlength] = '\0';
688 memcpy(res,&h.payload[i],rr.rdlength);
689 res[rr.rdlength] = '\0';
699 log(DEBUG,"Create blank DNS");
702 DNS::DNS(const std::string &dnsserver)
704 dns_init_2(dnsserver.c_str());
705 log(DEBUG,"Create DNS with server '%s'",dnsserver.c_str());
708 void DNS::SetNS(const std::string &dnsserver)
710 dns_init_2(dnsserver.c_str());
718 bool DNS::ReverseLookup(const std::string &ip, bool ins)
720 if (ServerInstance && ServerInstance->stats)
721 ServerInstance->stats->statsDns++;
723 if (insp_aton(ip.c_str(), &binip) < 1)
728 this->myfd = dns_getname4(&binip);
729 if (this->myfd == -1)
733 log(DEBUG,"DNS: ReverseLookup, fd=%d",this->myfd);
737 if (ServerInstance && ServerInstance->SE)
738 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
744 bool DNS::ForwardLookup(const std::string &host, bool ins)
746 if (ServerInstance && ServerInstance->stats)
747 ServerInstance->stats->statsDns++;
748 this->myfd = dns_getip4(host.c_str());
749 if (this->myfd == -1)
753 log(DEBUG,"DNS: ForwardLookup, fd=%d",this->myfd);
757 if (ServerInstance && ServerInstance->SE)
758 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
764 bool DNS::ForwardLookupWithFD(const std::string &host, int &fd)
766 if (ServerInstance && ServerInstance->stats)
767 ServerInstance->stats->statsDns++;
768 this->myfd = dns_getip4(host.c_str());
770 if (this->myfd == -1)
774 log(DEBUG,"DNS: ForwardLookupWithFD, fd=%d",this->myfd);
775 if (ServerInstance && ServerInstance->SE)
776 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_MODULE);
780 bool DNS::HasResult(int fd)
782 return (fd == this->myfd);
785 /* Only the multithreaded dns uses this poll() based
786 * check now. As its in another thread we dont have
787 * to worry about its performance that much.
789 bool DNS::HasResult()
791 log(DEBUG,"DNS: HasResult, fd=%d",this->myfd);
793 polls.fd = this->myfd;
794 polls.events = POLLIN;
795 int ret = poll(&polls,1,1);
796 log(DEBUG,"DNS: Hasresult returning %d",ret);
805 std::string DNS::GetResult()
807 log(DEBUG,"DNS: GetResult()");
808 result = dns_getresult(this->myfd);
811 if (ServerInstance && ServerInstance->stats)
812 ServerInstance->stats->statsDnsGood++;
813 dns_close(this->myfd);
819 if (ServerInstance && ServerInstance->stats)
820 ServerInstance->stats->statsDnsBad++;
821 if (this->myfd != -1)
823 dns_close(this->myfd);
830 std::string DNS::GetResultIP()
833 log(DEBUG,"DNS: GetResultIP()");
834 result = dns_getresult(this->myfd);
835 if (this->myfd != -1)
837 dns_close(this->myfd);
842 if (ServerInstance && ServerInstance->stats)
843 ServerInstance->stats->statsDnsGood++;
844 unsigned char a = (unsigned)result[0];
845 unsigned char b = (unsigned)result[1];
846 unsigned char c = (unsigned)result[2];
847 unsigned char d = (unsigned)result[3];
848 snprintf(r,1024,"%u.%u.%u.%u",a,b,c,d);
853 if (ServerInstance && ServerInstance->stats)
854 ServerInstance->stats->statsDnsBad++;
855 log(DEBUG,"DANGER WILL ROBINSON! NXDOMAIN for forward lookup, but we got a reverse lookup!");
864 /* This function is a thread function which can be thought of as a lightweight process
865 * to all you non-threaded people. In actuality its so much more, and pretty damn cool.
866 * With threaded dns enabled, each user which connects gets a thread attached to their
867 * user record when their DNS lookup starts. This function starts in parallel, and
868 * commences a blocking dns lookup. Because its a seperate thread, this occurs without
869 * actually blocking the main application. Once the dns lookup is completed, the thread
870 * checks if the user is still around by checking their fd against the reference table,
871 * and if they are, writes the hostname into the struct and terminates, after setting
872 * userrec::dns_done to true. Because this is multi-threaded it can make proper use of
873 * SMP setups (like the one i have here *grin*).
874 * This is in comparison to the non-threaded dns, which must monitor the thread sockets
875 * in a nonblocking fashion, consuming more resources to do so.
877 * NB: Yes this does scale, thank you. Even with large numbers of connecting clients
878 * in any one timeframe, they wont all connect *at the same time* therefore any argument
879 * of "but there will be thousands of threads it'll blow up" is moot, ive tested this and
880 * there will only ever be somewhere around the listen backlog in number of pending
881 * lookups at any one time. This is significant on any modern SMP system.
883 void* dns_task(void* arg)
885 userrec* u = (userrec*)arg;
888 log(DEBUG,"DNS thread for user %s on ip %s",u->nick,insp_ntoa(u->ip4));
889 DNS dns1(Config->DNSServer);
890 DNS dns2(Config->DNSServer);
895 if (dns1.ReverseLookup(insp_ntoa(u->ip4),false))
897 /* FIX: Dont make these infinite! */
898 while ((!dns1.HasResult()) && (++iterations < 20))
903 if (dns1.GetFD() != -1)
905 host = dns1.GetResult();
908 if (dns2.ForwardLookup(host, false))
911 while ((!dns2.HasResult()) && (++iterations < 20))
916 if (dns2.GetFD() != -1)
918 ip = dns2.GetResultIP();
919 if (ip == std::string(insp_ntoa(u->ip4)))
921 if (host.length() < 65)
923 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
927 strcpy(u->host,host.c_str());
928 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
930 strcpy(u->dhost,host.c_str());
943 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
945 log(DEBUG,"THREAD EXIT");
950 Resolver::Resolver(const std::string &source, bool forward, const std::string &dnsserver = "") : input(source), fwd(forward), server(dnsserver)
952 if (this->server != "")
953 Query.SetNS(this->server);
955 Query.SetNS(Config->DNSServer);
959 Query.ForwardLookup(input.c_str(), false);
960 this->fd = Query.GetFD();
964 Query.ReverseLookup(input.c_str(), false);
965 this->fd = Query.GetFD();
969 log(DEBUG,"Resolver::Resolver: RESOLVER_NSDOWN");
970 this->OnError(RESOLVER_NSDOWN);
971 ModuleException e("Resolver: Nameserver is down");
973 /* We shouldnt get here really */
977 if (ServerInstance && ServerInstance->SE)
979 log(DEBUG,"Resolver::Resolver: this->fd=%d",this->fd);
980 ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_CLASSDNS);
984 log(DEBUG,"Resolver::Resolver: RESOLVER_NOTREADY");
985 this->OnError(RESOLVER_NOTREADY);
986 ModuleException e("Resolver: Core not initialized yet");
988 /* We shouldnt get here really */
993 Resolver::~Resolver()
995 log(DEBUG,"Resolver::~Resolver");
996 if (ServerInstance && ServerInstance->SE)
997 ServerInstance->SE->DelFd(this->fd);
1000 int Resolver::GetFd()
1005 bool Resolver::ProcessResult()
1007 log(DEBUG,"Resolver::ProcessResult");
1009 result = Query.GetResultIP();
1011 result = Query.GetResult();
1015 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
1016 this->OnLookupComplete(result);
1021 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
1022 this->OnError(RESOLVER_NXDOMAIN);
1027 void Resolver::OnLookupComplete(const std::string &result)
1031 void Resolver::OnError(ResolverError e)
1035 void dns_deal_with_classes(int fd)
1037 log(DEBUG,"dns_deal_with_classes(%d)",fd);
1038 if ((fd > -1) && (dns_classes[fd]))
1040 log(DEBUG,"Valid fd %d",fd);
1041 dns_classes[fd]->ProcessResult();
1042 delete dns_classes[fd];
1043 dns_classes[fd] = NULL;
1047 bool dns_add_class(Resolver* r)
1049 log(DEBUG,"dns_add_class");
1050 if ((r) && (r->GetFd() > -1))
1052 if (!dns_classes[r->GetFd()])
1054 log(DEBUG,"dns_add_class: added class");
1055 dns_classes[r->GetFd()] = r;
1060 log(DEBUG,"Space occupied!");
1066 log(DEBUG,"Bad class");
1074 memset(dns_classes,0,sizeof(dns_classes));