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"
53 #include "configreader.h"
55 extern InspIRCd* ServerInstance;
56 extern ServerConfig* Config;
59 enum QueryType { DNS_QRY_A = 1, DNS_QRY_PTR = 12 };
60 enum QueryFlags1 { FLAGS1_MASK_RD = 0x01, FLAGS1_MASK_TC = 0x02, FLAGS1_MASK_AA = 0x04, FLAGS1_MASK_OPCODE = 0x78, FLAGS1_MASK_QR = 0x80 };
61 enum QueryFlags2 { FLAGS2_MASK_RCODE = 0x0F, FLAGS2_MASK_Z = 0x70, FLAGS2_MASK_RA = 0x80 };
65 typedef std::map<int,s_connection*> connlist;
66 typedef connlist::iterator connlist_iter;
69 Resolver* dns_classes[MAX_DESCRIPTORS];
71 struct in_addr servers4[8];
93 unsigned int rdlength;
102 unsigned int qdcount;
103 unsigned int ancount;
104 unsigned int nscount;
105 unsigned int arcount;
106 unsigned char payload[512];
110 void *dns_align(void *inp)
112 char *p = (char*)inp;
113 int offby = ((char *)p - (char *)0) % (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long));
115 return p + ((sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long)) - offby);
121 * Optimized by brain, these were using integer division and modulus.
122 * We can use logic shifts and logic AND to replace these even divisions
123 * and multiplications, it should be a bit faster (probably not noticably,
124 * but of course, more impressive). Also made these inline.
127 inline void dns_fill_rr(s_rr_middle* rr, const unsigned char *input)
129 rr->type = (QueryType)((input[0] << 8) + input[1]);
130 rr->_class = (input[2] << 8) + input[3];
131 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
132 rr->rdlength = (input[8] << 8) + input[9];
135 inline void dns_fill_header(s_header *header, const unsigned char *input, const int l)
137 header->id[0] = input[0];
138 header->id[1] = input[1];
139 header->flags1 = input[2];
140 header->flags2 = input[3];
141 header->qdcount = (input[4] << 8) + input[5];
142 header->ancount = (input[6] << 8) + input[7];
143 header->nscount = (input[8] << 8) + input[9];
144 header->arcount = (input[10] << 8) + input[11];
145 memcpy(header->payload,&input[12],l);
148 inline void dns_empty_header(unsigned char *output, const s_header *header, const int l)
150 output[0] = header->id[0];
151 output[1] = header->id[1];
152 output[2] = header->flags1;
153 output[3] = header->flags2;
154 output[4] = header->qdcount >> 8;
155 output[5] = header->qdcount & 0xFF;
156 output[6] = header->ancount >> 8;
157 output[7] = header->ancount & 0xFF;
158 output[8] = header->nscount >> 8;
159 output[9] = header->nscount & 0xFF;
160 output[10] = header->arcount >> 8;
161 output[11] = header->arcount & 0xFF;
162 memcpy(&output[12],header->payload,l);
165 void dns_close(int fd)
168 if (ServerInstance && ServerInstance->SE)
169 ServerInstance->SE->DelFd(fd);
171 log(DEBUG,"DNS: dns_close on fd %d",fd);
172 if (fd == lastcreate)
192 srand((unsigned int) TIME);
193 memset(servers4,'\0',sizeof(in_addr) * 8);
194 f = fopen("/etc/resolv.conf","r");
197 while (fgets(buf,1024,f) != NULL) {
198 if (strncmp(buf,"nameserver",10) == 0)
201 while (buf[i] == ' ' || buf[i] == '\t')
205 if (dns_aton4_s(&buf[i],&addr4) != NULL)
206 memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
213 void DNS::dns_init_2(const char* dnsserver)
217 srand((unsigned int) TIME);
218 memset(servers4,'\0',sizeof(in_addr) * 8);
219 if (dns_aton4_s(dnsserver,&addr4) != NULL)
220 memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
224 int dns_send_requests(const s_header *h, const s_connection *s, const int l)
228 unsigned char payload[sizeof(s_header)];
230 dns_empty_header(payload,h,l);
235 /* otherwise send via standard ipv4 boringness */
236 memset(&addr4,0,sizeof(addr4));
237 memcpy(&addr4.sin_addr,&servers4[i],sizeof(addr4.sin_addr));
238 addr4.sin_family = AF_INET;
239 addr4.sin_port = htons(53);
240 if (sendto(s->fd, payload, l + 12, 0, (sockaddr *) &addr4, sizeof(addr4)) == -1)
248 s_connection *dns_add_query(s_header *h)
251 s_connection * s = new s_connection;
252 int id = rand() % 65536;
254 /* set header flags */
255 h->id[0] = s->id[0] = id >> 8; /* verified by dns_getresult_s() */
256 h->id[1] = s->id[1] = id & 0xFF;
257 h->flags1 = 0 | FLAGS1_MASK_RD;
264 s->fd = socket(PF_INET, SOCK_DGRAM, 0);
267 if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0)
277 memset(&addr,0,sizeof(addr));
278 addr.sin_family = AF_INET;
280 addr.sin_addr.s_addr = INADDR_ANY;
281 if (bind(s->fd,(sockaddr *)&addr,sizeof(addr)) != 0)
293 /* create new connection object, add to linked list */
294 if (connections.find(s->fd) == connections.end())
295 connections[s->fd] = s;
299 shutdown(lastcreate,2);
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). */
466 connlist_iter n_iter = connections.find(cfd);
467 if (n_iter == connections.end())
469 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d",cfd);
474 /* Remove the query from the list */
475 c = (s_connection*)n_iter->second;
476 /* We don't delete c here, because its done later when needed */
477 connections.erase(n_iter);
480 l = recv(c->fd,buffer,sizeof(s_header),0);
487 dns_fill_header(&h,buffer,l - 12);
488 if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
490 log(DEBUG,"DNS: id mismatch on query");
492 return NULL; /* ID mismatch */
494 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
496 log(DEBUG,"DNS: didnt get a query result");
500 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
502 log(DEBUG,"DNS: got an OPCODE and didnt want one");
506 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
508 log(DEBUG,"DNS lookup failed due to SERVFAIL");
514 log(DEBUG,"DNS: no answers!");
521 while ((unsigned)q < h.qdcount && i < l)
523 if (h.payload[i] > 63)
530 if (h.payload[i] == 0)
535 else i += h.payload[i] + 1;
539 while ((unsigned)curanswer < h.ancount)
542 while (q == 0 && i < l)
544 if (h.payload[i] > 63)
551 if (h.payload[i] == 0)
556 else i += h.payload[i] + 1; /* skip length and label */
564 dns_fill_rr(&rr,&h.payload[i]);
566 if (rr.type != c->type)
572 if (rr._class != c->_class)
580 if ((unsigned)curanswer == h.ancount)
582 if ((unsigned)i + rr.rdlength > (unsigned)l)
584 if (rr.rdlength > 1023)
590 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
593 while (q == 0 && i < l && o + 256 < 1023)
595 if (h.payload[i] > 63)
597 log(DEBUG,"DNS: h.payload[i] > 63");
598 memcpy(&p,&h.payload[i],2);
599 i = ntohs(p) - 0xC000 - 12;
603 if (h.payload[i] == 0)
612 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
614 i += h.payload[i] + 1;
621 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
624 dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
625 while ((char *)alist - (char *)res < 700)
627 if (rr.type != DNS_QRY_A)
631 if (rr.rdlength != 4)
636 memcpy(&alist->ip,&h.payload[i],4);
637 if ((unsigned)++curanswer >= h.ancount)
641 while (q == 0 && i < l)
643 if (h.payload[i] > 63)
650 if (h.payload[i] == 0)
655 else i += h.payload[i] + 1;
663 dns_fill_rr(&rr,&h.payload[i]);
665 alist->next = (dns_ip4list *) dns_align(((char *) alist) + sizeof(dns_ip4list));
672 memcpy(res,&h.payload[i],rr.rdlength);
673 res[rr.rdlength] = '\0';
676 memcpy(res,&h.payload[i],rr.rdlength);
677 res[rr.rdlength] = '\0';
687 log(DEBUG,"Create blank DNS");
690 DNS::DNS(const std::string &dnsserver)
692 dns_init_2(dnsserver.c_str());
693 log(DEBUG,"Create DNS with server '%s'",dnsserver.c_str());
696 void DNS::SetNS(const std::string &dnsserver)
698 dns_init_2(dnsserver.c_str());
706 bool DNS::ReverseLookup(const std::string &ip, bool ins)
708 if (ServerInstance && ServerInstance->stats)
709 ServerInstance->stats->statsDns++;
710 binip = dns_aton4(ip.c_str());
716 this->myfd = dns_getname4(binip);
717 if (this->myfd == -1)
721 log(DEBUG,"DNS: ReverseLookup, fd=%d",this->myfd);
725 if (ServerInstance && ServerInstance->SE)
726 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
732 bool DNS::ForwardLookup(const std::string &host, bool ins)
734 if (ServerInstance && ServerInstance->stats)
735 ServerInstance->stats->statsDns++;
736 this->myfd = dns_getip4(host.c_str());
737 if (this->myfd == -1)
741 log(DEBUG,"DNS: ForwardLookup, fd=%d",this->myfd);
745 if (ServerInstance && ServerInstance->SE)
746 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
752 bool DNS::ForwardLookupWithFD(const std::string &host, int &fd)
754 if (ServerInstance && ServerInstance->stats)
755 ServerInstance->stats->statsDns++;
756 this->myfd = dns_getip4(host.c_str());
758 if (this->myfd == -1)
762 log(DEBUG,"DNS: ForwardLookupWithFD, fd=%d",this->myfd);
763 if (ServerInstance && ServerInstance->SE)
764 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_MODULE);
768 bool DNS::HasResult(int fd)
770 return (fd == this->myfd);
773 /* Only the multithreaded dns uses this poll() based
774 * check now. As its in another thread we dont have
775 * to worry about its performance that much.
777 bool DNS::HasResult()
779 log(DEBUG,"DNS: HasResult, fd=%d",this->myfd);
781 polls.fd = this->myfd;
782 polls.events = POLLIN;
783 int ret = poll(&polls,1,1);
784 log(DEBUG,"DNS: Hasresult returning %d",ret);
793 std::string DNS::GetResult()
795 log(DEBUG,"DNS: GetResult()");
796 result = dns_getresult(this->myfd);
799 if (ServerInstance && ServerInstance->stats)
800 ServerInstance->stats->statsDnsGood++;
801 dns_close(this->myfd);
807 if (ServerInstance && ServerInstance->stats)
808 ServerInstance->stats->statsDnsBad++;
809 if (this->myfd != -1)
811 dns_close(this->myfd);
818 std::string DNS::GetResultIP()
821 log(DEBUG,"DNS: GetResultIP()");
822 result = dns_getresult(this->myfd);
823 if (this->myfd != -1)
825 dns_close(this->myfd);
830 if (ServerInstance && ServerInstance->stats)
831 ServerInstance->stats->statsDnsGood++;
832 unsigned char a = (unsigned)result[0];
833 unsigned char b = (unsigned)result[1];
834 unsigned char c = (unsigned)result[2];
835 unsigned char d = (unsigned)result[3];
836 snprintf(r,1024,"%u.%u.%u.%u",a,b,c,d);
841 if (ServerInstance && ServerInstance->stats)
842 ServerInstance->stats->statsDnsBad++;
843 log(DEBUG,"DANGER WILL ROBINSON! NXDOMAIN for forward lookup, but we got a reverse lookup!");
851 void* dns_task(void* arg)
853 userrec* u = (userrec*)arg;
856 log(DEBUG,"DNS thread for user %s",u->nick);
861 if (dns1.ReverseLookup((char*)inet_ntoa(u->ip4)))
863 while (!dns1.HasResult())
867 host = dns1.GetResult();
870 if (dns2.ForwardLookup(host), false)
872 while (!dns2.HasResult())
876 ip = dns2.GetResultIP();
877 if (ip == std::string(inet_ntoa(u->ip4)))
879 if (host.length() < 160)
881 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
883 strcpy(u->host,host.c_str());
884 strcpy(u->dhost,host.c_str());
891 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
897 Resolver::Resolver(const std::string &source, bool forward, const std::string &dnsserver = "") : input(source), fwd(forward), server(dnsserver)
899 if (this->server != "")
900 Query.SetNS(this->server);
902 Query.SetNS(Config->DNSServer);
906 Query.ForwardLookup(input.c_str(), false);
907 this->fd = Query.GetFD();
911 Query.ReverseLookup(input.c_str(), false);
912 this->fd = Query.GetFD();
916 log(DEBUG,"Resolver::Resolver: RESOLVER_NSDOWN");
917 this->OnError(RESOLVER_NSDOWN);
918 ModuleException e("Resolver: Nameserver is down");
920 /* We shouldnt get here really */
924 if (ServerInstance && ServerInstance->SE)
926 log(DEBUG,"Resolver::Resolver: this->fd=%d",this->fd);
927 ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_CLASSDNS);
931 log(DEBUG,"Resolver::Resolver: RESOLVER_NOTREADY");
932 this->OnError(RESOLVER_NOTREADY);
933 ModuleException e("Resolver: Core not initialized yet");
935 /* We shouldnt get here really */
940 Resolver::~Resolver()
942 log(DEBUG,"Resolver::~Resolver");
943 if (ServerInstance && ServerInstance->SE)
944 ServerInstance->SE->DelFd(this->fd);
947 int Resolver::GetFd()
952 bool Resolver::ProcessResult()
954 log(DEBUG,"Resolver::ProcessResult");
956 result = Query.GetResultIP();
958 result = Query.GetResult();
962 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
963 this->OnLookupComplete(result);
968 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
969 this->OnError(RESOLVER_NXDOMAIN);
974 void Resolver::OnLookupComplete(const std::string &result)
978 void Resolver::OnError(ResolverError e)
982 void dns_deal_with_classes(int fd)
984 log(DEBUG,"dns_deal_with_classes(%d)",fd);
985 if ((fd > -1) && (dns_classes[fd]))
987 log(DEBUG,"Valid fd %d",fd);
988 dns_classes[fd]->ProcessResult();
989 delete dns_classes[fd];
990 dns_classes[fd] = NULL;
994 bool dns_add_class(Resolver* r)
996 log(DEBUG,"dns_add_class");
997 if ((r) && (r->GetFd() > -1))
999 if (!dns_classes[r->GetFd()])
1001 log(DEBUG,"dns_add_class: added class");
1002 dns_classes[r->GetFd()] = r;
1007 log(DEBUG,"Space occupied!");
1013 log(DEBUG,"Bad class");
1021 memset(dns_classes,0,sizeof(dns_classes));