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"
56 extern InspIRCd* ServerInstance;
57 extern ServerConfig* Config;
60 enum QueryType { DNS_QRY_A = 1, DNS_QRY_PTR = 12 };
61 enum QueryFlags1 { FLAGS1_MASK_RD = 0x01, FLAGS1_MASK_TC = 0x02, FLAGS1_MASK_AA = 0x04, FLAGS1_MASK_OPCODE = 0x78, FLAGS1_MASK_QR = 0x80 };
62 enum QueryFlags2 { FLAGS2_MASK_RCODE = 0x0F, FLAGS2_MASK_Z = 0x70, FLAGS2_MASK_RA = 0x80 };
66 typedef std::map<int,s_connection*> connlist;
67 typedef connlist::iterator connlist_iter;
70 Resolver* dns_classes[MAX_DESCRIPTORS];
72 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);
188 srand((unsigned int) TIME);
189 memset(servers4,'\0',sizeof(in_addr) * 8);
190 f = fopen("/etc/resolv.conf","r");
193 while (fgets(buf,1024,f) != NULL) {
194 if (strncmp(buf,"nameserver",10) == 0)
197 while (buf[i] == ' ' || buf[i] == '\t')
201 if (dns_aton4_s(&buf[i],&addr4) != NULL)
202 memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
209 void DNS::dns_init_2(const char* dnsserver)
213 srand((unsigned int) TIME);
214 memset(servers4,'\0',sizeof(in_addr) * 8);
215 if (dns_aton4_s(dnsserver,&addr4) != NULL)
216 memcpy(&servers4[i4++],&addr4,sizeof(in_addr));
220 int dns_send_requests(const s_header *h, const s_connection *s, const int l)
224 unsigned char payload[sizeof(s_header)];
226 dns_empty_header(payload,h,l);
231 /* otherwise send via standard ipv4 boringness */
232 memset(&addr4,0,sizeof(addr4));
233 memcpy(&addr4.sin_addr,&servers4[i],sizeof(addr4.sin_addr));
234 addr4.sin_family = AF_INET;
235 addr4.sin_port = htons(53);
236 if (sendto(s->fd, payload, l + 12, 0, (sockaddr *) &addr4, sizeof(addr4)) == -1)
244 s_connection *dns_add_query(s_header *h)
247 s_connection * s = new s_connection;
248 int id = rand() % 65536;
250 /* set header flags */
251 h->id[0] = s->id[0] = id >> 8; /* verified by dns_getresult_s() */
252 h->id[1] = s->id[1] = id & 0xFF;
253 h->flags1 = 0 | FLAGS1_MASK_RD;
260 s->fd = socket(PF_INET, SOCK_DGRAM, 0);
263 if (fcntl(s->fd, F_SETFL, O_NONBLOCK) != 0)
273 memset(&addr,0,sizeof(addr));
274 addr.sin_family = AF_INET;
276 addr.sin_addr.s_addr = INADDR_ANY;
277 if (bind(s->fd,(sockaddr *)&addr,sizeof(addr)) != 0)
289 /* create new connection object, add to linked list */
290 if (connections.find(s->fd) == connections.end())
291 connections[s->fd] = s;
297 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
300 const char * tempchr, * tempchr2;
306 /* split name up into labels, create query */
307 while ((tempchr = strchr(tempchr2,'.')) != NULL)
309 l = tempchr - tempchr2;
310 if (payloadpos + l + 1 > 507)
312 payload[payloadpos++] = l;
313 memcpy(&payload[payloadpos],tempchr2,l);
315 tempchr2 = &tempchr[1];
317 l = strlen(tempchr2);
320 if (payloadpos + l + 2 > 507)
322 payload[payloadpos++] = l;
323 memcpy(&payload[payloadpos],tempchr2,l);
325 payload[payloadpos++] = '\0';
327 if (payloadpos > 508)
330 memcpy(&payload[payloadpos],&l,2);
332 memcpy(&payload[payloadpos + 2],&l,2);
333 return payloadpos + 4;
336 in_addr* DNS::dns_aton4(const char * const ipstring)
339 return dns_aton4_s(ipstring,&ip);
342 in_addr* DNS::dns_aton4_r(const char *ipstring) { /* ascii to numeric (reentrant): convert string to new 4part IP addr struct */
345 if(dns_aton4_s(ipstring,ip) == NULL)
353 in_addr* DNS::dns_aton4_s(const char *ipstring, in_addr *ip) { /* ascii to numeric (buffered): convert string to given 4part IP addr struct */
354 inet_aton(ipstring,ip);
358 int DNS::dns_getip4(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
366 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
369 s = dns_add_query(&h);
374 if (dns_send_requests(&h,s,l) == -1)
380 int DNS::dns_getip4list(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
387 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
390 s = dns_add_query(&h);
396 if (dns_send_requests(&h,s,l) == -1)
402 int DNS::dns_getname4(const in_addr *ip) { /* build, add and send PTR query; retrieve result with dns_getresult() */
409 c = (unsigned char *)&ip->s_addr;
411 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
413 l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
416 s = dns_add_query(&h);
420 s->type = DNS_QRY_PTR;
421 if (dns_send_requests(&h,s,l) == -1)
427 char* DNS::dns_ntoa4(const in_addr * const ip) { /* numeric to ascii: convert 4part IP addr struct to static string */
429 return dns_ntoa4_s(ip,r);
432 char* DNS::dns_ntoa4_s(const in_addr *ip, char *r) { /* numeric to ascii (buffered): convert 4part IP addr struct to given string */
434 m = (unsigned char *)&ip->s_addr;
435 sprintf(r,"%d.%d.%d.%d",m[0],m[1],m[2],m[3]);
439 char* DNS::dns_getresult(const int cfd) { /* retrieve result of DNS query */
440 log(DEBUG,"DNS: dns_getresult with cfd=%d",cfd);
441 return dns_getresult_s(cfd,this->localbuf);
444 char* DNS::dns_getresult_s(const int cfd, char *res) { /* retrieve result of DNS query (buffered) */
447 int l, i, q, curanswer, o;
449 unsigned char buffer[sizeof(s_header)];
455 /* FireDNS used a linked list for this. How ugly (and slow). */
456 connlist_iter n_iter = connections.find(cfd);
457 if (n_iter == connections.end())
459 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d",cfd);
464 /* Remove the query from the list */
465 c = (s_connection*)n_iter->second;
466 /* We don't delete c here, because its done later when needed */
467 connections.erase(n_iter);
470 l = recv(c->fd,buffer,sizeof(s_header),0);
477 dns_fill_header(&h,buffer,l - 12);
478 if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
480 log(DEBUG,"DNS: id mismatch on query");
482 return NULL; /* ID mismatch */
484 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
486 log(DEBUG,"DNS: didnt get a query result");
490 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
492 log(DEBUG,"DNS: got an OPCODE and didnt want one");
496 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
498 log(DEBUG,"DNS lookup failed due to SERVFAIL");
504 log(DEBUG,"DNS: no answers!");
511 while ((unsigned)q < h.qdcount && i < l)
513 if (h.payload[i] > 63)
520 if (h.payload[i] == 0)
525 else i += h.payload[i] + 1;
529 while ((unsigned)curanswer < h.ancount)
532 while (q == 0 && i < l)
534 if (h.payload[i] > 63)
541 if (h.payload[i] == 0)
546 else i += h.payload[i] + 1; /* skip length and label */
554 dns_fill_rr(&rr,&h.payload[i]);
556 if (rr.type != c->type)
562 if (rr._class != c->_class)
570 if ((unsigned)curanswer == h.ancount)
572 if ((unsigned)i + rr.rdlength > (unsigned)l)
574 if (rr.rdlength > 1023)
580 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
583 while (q == 0 && i < l && o + 256 < 1023)
585 if (h.payload[i] > 63)
587 log(DEBUG,"DNS: h.payload[i] > 63");
588 memcpy(&p,&h.payload[i],2);
589 i = ntohs(p) - 0xC000 - 12;
593 if (h.payload[i] == 0)
602 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
604 i += h.payload[i] + 1;
611 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
614 dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
615 while ((char *)alist - (char *)res < 700)
617 if (rr.type != DNS_QRY_A)
621 if (rr.rdlength != 4)
626 memcpy(&alist->ip,&h.payload[i],4);
627 if ((unsigned)++curanswer >= h.ancount)
631 while (q == 0 && i < l)
633 if (h.payload[i] > 63)
640 if (h.payload[i] == 0)
645 else i += h.payload[i] + 1;
653 dns_fill_rr(&rr,&h.payload[i]);
655 alist->next = (dns_ip4list *) dns_align(((char *) alist) + sizeof(dns_ip4list));
662 memcpy(res,&h.payload[i],rr.rdlength);
663 res[rr.rdlength] = '\0';
666 memcpy(res,&h.payload[i],rr.rdlength);
667 res[rr.rdlength] = '\0';
677 log(DEBUG,"Create blank DNS");
680 DNS::DNS(const std::string &dnsserver)
682 dns_init_2(dnsserver.c_str());
683 log(DEBUG,"Create DNS with server '%s'",dnsserver.c_str());
686 void DNS::SetNS(const std::string &dnsserver)
688 dns_init_2(dnsserver.c_str());
696 bool DNS::ReverseLookup(const std::string &ip, bool ins)
698 if (ServerInstance && ServerInstance->stats)
699 ServerInstance->stats->statsDns++;
700 binip = dns_aton4(ip.c_str());
706 this->myfd = dns_getname4(binip);
707 if (this->myfd == -1)
711 log(DEBUG,"DNS: ReverseLookup, fd=%d",this->myfd);
715 if (ServerInstance && ServerInstance->SE)
716 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
722 bool DNS::ForwardLookup(const std::string &host, bool ins)
724 if (ServerInstance && ServerInstance->stats)
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);
735 if (ServerInstance && ServerInstance->SE)
736 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
742 bool DNS::ForwardLookupWithFD(const std::string &host, int &fd)
744 if (ServerInstance && ServerInstance->stats)
745 ServerInstance->stats->statsDns++;
746 this->myfd = dns_getip4(host.c_str());
748 if (this->myfd == -1)
752 log(DEBUG,"DNS: ForwardLookupWithFD, fd=%d",this->myfd);
753 if (ServerInstance && ServerInstance->SE)
754 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_MODULE);
758 bool DNS::HasResult(int fd)
760 return (fd == this->myfd);
763 /* Only the multithreaded dns uses this poll() based
764 * check now. As its in another thread we dont have
765 * to worry about its performance that much.
767 bool DNS::HasResult()
769 log(DEBUG,"DNS: HasResult, fd=%d",this->myfd);
771 polls.fd = this->myfd;
772 polls.events = POLLIN;
773 int ret = poll(&polls,1,1);
774 log(DEBUG,"DNS: Hasresult returning %d",ret);
783 std::string DNS::GetResult()
785 log(DEBUG,"DNS: GetResult()");
786 result = dns_getresult(this->myfd);
789 if (ServerInstance && ServerInstance->stats)
790 ServerInstance->stats->statsDnsGood++;
791 dns_close(this->myfd);
797 if (ServerInstance && ServerInstance->stats)
798 ServerInstance->stats->statsDnsBad++;
799 if (this->myfd != -1)
801 dns_close(this->myfd);
808 std::string DNS::GetResultIP()
811 log(DEBUG,"DNS: GetResultIP()");
812 result = dns_getresult(this->myfd);
813 if (this->myfd != -1)
815 dns_close(this->myfd);
820 if (ServerInstance && ServerInstance->stats)
821 ServerInstance->stats->statsDnsGood++;
822 unsigned char a = (unsigned)result[0];
823 unsigned char b = (unsigned)result[1];
824 unsigned char c = (unsigned)result[2];
825 unsigned char d = (unsigned)result[3];
826 snprintf(r,1024,"%u.%u.%u.%u",a,b,c,d);
831 if (ServerInstance && ServerInstance->stats)
832 ServerInstance->stats->statsDnsBad++;
833 log(DEBUG,"DANGER WILL ROBINSON! NXDOMAIN for forward lookup, but we got a reverse lookup!");
841 void* dns_task(void* arg)
843 userrec* u = (userrec*)arg;
846 log(DEBUG,"DNS thread for user %s",u->nick);
851 if (dns1.ReverseLookup((char*)inet_ntoa(u->ip4)))
853 while (!dns1.HasResult())
857 host = dns1.GetResult();
860 if (dns2.ForwardLookup(host), false)
862 while (!dns2.HasResult())
866 ip = dns2.GetResultIP();
867 if (ip == std::string(inet_ntoa(u->ip4)))
869 if (host.length() < 160)
871 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
873 strcpy(u->host,host.c_str());
874 strcpy(u->dhost,host.c_str());
881 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
887 Resolver::Resolver(const std::string &source, bool forward, const std::string &dnsserver = "") : input(source), fwd(forward), server(dnsserver)
889 if (this->server != "")
890 Query.SetNS(this->server);
892 Query.SetNS(Config->DNSServer);
896 Query.ForwardLookup(input.c_str(), false);
897 this->fd = Query.GetFD();
901 Query.ReverseLookup(input.c_str(), false);
902 this->fd = Query.GetFD();
906 log(DEBUG,"Resolver::Resolver: RESOLVER_NSDOWN");
907 this->OnError(RESOLVER_NSDOWN);
908 ModuleException e("Resolver: Nameserver is down");
910 /* We shouldnt get here really */
914 if (ServerInstance && ServerInstance->SE)
916 log(DEBUG,"Resolver::Resolver: this->fd=%d",this->fd);
917 ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_CLASSDNS);
921 log(DEBUG,"Resolver::Resolver: RESOLVER_NOTREADY");
922 this->OnError(RESOLVER_NOTREADY);
923 ModuleException e("Resolver: Core not initialized yet");
925 /* We shouldnt get here really */
930 Resolver::~Resolver()
932 log(DEBUG,"Resolver::~Resolver");
933 if (ServerInstance && ServerInstance->SE)
934 ServerInstance->SE->DelFd(this->fd);
937 int Resolver::GetFd()
942 bool Resolver::ProcessResult()
944 log(DEBUG,"Resolver::ProcessResult");
946 result = Query.GetResultIP();
948 result = Query.GetResult();
952 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
953 this->OnLookupComplete(result);
958 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
959 this->OnError(RESOLVER_NXDOMAIN);
964 void Resolver::OnLookupComplete(const std::string &result)
968 void Resolver::OnError(ResolverError e)
972 void dns_deal_with_classes(int fd)
974 log(DEBUG,"dns_deal_with_classes(%d)",fd);
975 if ((fd > -1) && (dns_classes[fd]))
977 log(DEBUG,"Valid fd %d",fd);
978 dns_classes[fd]->ProcessResult();
979 delete dns_classes[fd];
980 dns_classes[fd] = NULL;
984 bool dns_add_class(Resolver* r)
986 log(DEBUG,"dns_add_class");
987 if ((r) && (r->GetFd() > -1))
989 if (!dns_classes[r->GetFd()])
991 log(DEBUG,"dns_add_class: added class");
992 dns_classes[r->GetFd()] = r;
997 log(DEBUG,"Space occupied!");
1003 log(DEBUG,"Bad class");
1011 memset(dns_classes,0,sizeof(dns_classes));