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];
94 unsigned int rdlength;
103 unsigned int qdcount;
104 unsigned int ancount;
105 unsigned int nscount;
106 unsigned int arcount;
107 unsigned char payload[512];
111 void *dns_align(void *inp)
113 char *p = (char*)inp;
114 int offby = ((char *)p - (char *)0) % (sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long));
116 return p + ((sizeof(void *) > sizeof(long) ? sizeof(void *) : sizeof(long)) - offby);
122 * Optimized by brain, these were using integer division and modulus.
123 * We can use logic shifts and logic AND to replace these even divisions
124 * and multiplications, it should be a bit faster (probably not noticably,
125 * but of course, more impressive). Also made these inline.
128 inline void dns_fill_rr(s_rr_middle* rr, const unsigned char *input)
130 rr->type = (QueryType)((input[0] << 8) + input[1]);
131 rr->_class = (input[2] << 8) + input[3];
132 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
133 rr->rdlength = (input[8] << 8) + input[9];
136 inline void dns_fill_header(s_header *header, const unsigned char *input, const int l)
138 header->id[0] = input[0];
139 header->id[1] = input[1];
140 header->flags1 = input[2];
141 header->flags2 = input[3];
142 header->qdcount = (input[4] << 8) + input[5];
143 header->ancount = (input[6] << 8) + input[7];
144 header->nscount = (input[8] << 8) + input[9];
145 header->arcount = (input[10] << 8) + input[11];
146 memcpy(header->payload,&input[12],l);
149 inline void dns_empty_header(unsigned char *output, const s_header *header, const int l)
151 output[0] = header->id[0];
152 output[1] = header->id[1];
153 output[2] = header->flags1;
154 output[3] = header->flags2;
155 output[4] = header->qdcount >> 8;
156 output[5] = header->qdcount & 0xFF;
157 output[6] = header->ancount >> 8;
158 output[7] = header->ancount & 0xFF;
159 output[8] = header->nscount >> 8;
160 output[9] = header->nscount & 0xFF;
161 output[10] = header->arcount >> 8;
162 output[11] = header->arcount & 0xFF;
163 memcpy(&output[12],header->payload,l);
166 void dns_close(int fd)
169 if (ServerInstance && ServerInstance->SE)
170 ServerInstance->SE->DelFd(fd);
172 log(DEBUG,"DNS: dns_close on fd %d",fd);
173 if (fd == lastcreate)
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 */
295 if (connections.find(s->fd) == connections.end())
296 connections[s->fd] = s;
300 shutdown(lastcreate,2);
308 int dns_build_query_payload(const char * const name, const unsigned short rr, const unsigned short _class, unsigned char * const payload)
311 const char * tempchr, * tempchr2;
317 /* split name up into labels, create query */
318 while ((tempchr = strchr(tempchr2,'.')) != NULL)
320 l = tempchr - tempchr2;
321 if (payloadpos + l + 1 > 507)
323 payload[payloadpos++] = l;
324 memcpy(&payload[payloadpos],tempchr2,l);
326 tempchr2 = &tempchr[1];
328 l = strlen(tempchr2);
331 if (payloadpos + l + 2 > 507)
333 payload[payloadpos++] = l;
334 memcpy(&payload[payloadpos],tempchr2,l);
336 payload[payloadpos++] = '\0';
338 if (payloadpos > 508)
341 memcpy(&payload[payloadpos],&l,2);
343 memcpy(&payload[payloadpos + 2],&l,2);
344 return payloadpos + 4;
347 in_addr* DNS::dns_aton4(const char * const ipstring)
350 return dns_aton4_s(ipstring,&ip);
353 in_addr* DNS::dns_aton4_r(const char *ipstring) { /* ascii to numeric (reentrant): convert string to new 4part IP addr struct */
356 if(dns_aton4_s(ipstring,ip) == NULL)
364 in_addr* DNS::dns_aton4_s(const char *ipstring, in_addr *ip) { /* ascii to numeric (buffered): convert string to given 4part IP addr struct */
365 inet_aton(ipstring,ip);
369 int DNS::dns_getip4(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
377 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
380 s = dns_add_query(&h);
385 if (dns_send_requests(&h,s,l) == -1)
391 int DNS::dns_getip4list(const char *name) { /* build, add and send A query; retrieve result with dns_getresult() */
398 l = dns_build_query_payload(name,DNS_QRY_A,1,(unsigned char *)&h.payload);
401 s = dns_add_query(&h);
407 if (dns_send_requests(&h,s,l) == -1)
413 int DNS::dns_getname4(const in_addr *ip) { /* build, add and send PTR query; retrieve result with dns_getresult() */
420 c = (unsigned char *)&ip->s_addr;
422 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
424 l = dns_build_query_payload(query,DNS_QRY_PTR,1,(unsigned char *)&h.payload);
427 s = dns_add_query(&h);
431 s->type = DNS_QRY_PTR;
432 if (dns_send_requests(&h,s,l) == -1)
438 char* DNS::dns_ntoa4(const in_addr * const ip) { /* numeric to ascii: convert 4part IP addr struct to static string */
440 return dns_ntoa4_s(ip,r);
443 char* DNS::dns_ntoa4_s(const in_addr *ip, char *r) { /* numeric to ascii (buffered): convert 4part IP addr struct to given string */
445 m = (unsigned char *)&ip->s_addr;
446 sprintf(r,"%d.%d.%d.%d",m[0],m[1],m[2],m[3]);
450 char* DNS::dns_getresult(const int cfd) { /* retrieve result of DNS query */
451 log(DEBUG,"DNS: dns_getresult with cfd=%d",cfd);
452 return dns_getresult_s(cfd,this->localbuf);
455 char* DNS::dns_getresult_s(const int cfd, char *res) { /* retrieve result of DNS query (buffered) */
458 int l, i, q, curanswer, o;
460 unsigned char buffer[sizeof(s_header)];
466 /* FireDNS used a linked list for this. How ugly (and slow). */
467 connlist_iter n_iter = connections.find(cfd);
468 if (n_iter == connections.end())
470 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d",cfd);
475 /* Remove the query from the list */
476 c = (s_connection*)n_iter->second;
477 /* We don't delete c here, because its done later when needed */
478 connections.erase(n_iter);
481 l = recv(c->fd,buffer,sizeof(s_header),0);
488 dns_fill_header(&h,buffer,l - 12);
489 if (c->id[0] != h.id[0] || c->id[1] != h.id[1])
491 log(DEBUG,"DNS: id mismatch on query");
493 return NULL; /* ID mismatch */
495 if ((h.flags1 & FLAGS1_MASK_QR) == 0)
497 log(DEBUG,"DNS: didnt get a query result");
501 if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0)
503 log(DEBUG,"DNS: got an OPCODE and didnt want one");
507 if ((h.flags2 & FLAGS2_MASK_RCODE) != 0)
509 log(DEBUG,"DNS lookup failed due to SERVFAIL");
515 log(DEBUG,"DNS: no answers!");
522 while ((unsigned)q < h.qdcount && i < l)
524 if (h.payload[i] > 63)
531 if (h.payload[i] == 0)
536 else i += h.payload[i] + 1;
540 while ((unsigned)curanswer < h.ancount)
543 while (q == 0 && i < l)
545 if (h.payload[i] > 63)
552 if (h.payload[i] == 0)
557 else i += h.payload[i] + 1; /* skip length and label */
565 dns_fill_rr(&rr,&h.payload[i]);
567 if (rr.type != c->type)
573 if (rr._class != c->_class)
581 if ((unsigned)curanswer == h.ancount)
583 if ((unsigned)i + rr.rdlength > (unsigned)l)
585 if (rr.rdlength > 1023)
591 log(DEBUG,"DNS: got a result of type DNS_QRY_PTR");
594 while (q == 0 && i < l && o + 256 < 1023)
596 if (h.payload[i] > 63)
598 log(DEBUG,"DNS: h.payload[i] > 63");
599 memcpy(&p,&h.payload[i],2);
600 i = ntohs(p) - 0xC000 - 12;
604 if (h.payload[i] == 0)
613 memcpy(&res[o],&h.payload[i + 1],h.payload[i]);
615 i += h.payload[i] + 1;
622 log(DEBUG,"DNS: got a result of type DNS_QRY_A");
625 dns_ip4list *alist = (dns_ip4list *) res; /* we have to trust that this is aligned */
626 while ((char *)alist - (char *)res < 700)
628 if (rr.type != DNS_QRY_A)
632 if (rr.rdlength != 4)
637 memcpy(&alist->ip,&h.payload[i],4);
638 if ((unsigned)++curanswer >= h.ancount)
642 while (q == 0 && i < l)
644 if (h.payload[i] > 63)
651 if (h.payload[i] == 0)
656 else i += h.payload[i] + 1;
664 dns_fill_rr(&rr,&h.payload[i]);
666 alist->next = (dns_ip4list *) dns_align(((char *) alist) + sizeof(dns_ip4list));
673 memcpy(res,&h.payload[i],rr.rdlength);
674 res[rr.rdlength] = '\0';
677 memcpy(res,&h.payload[i],rr.rdlength);
678 res[rr.rdlength] = '\0';
688 log(DEBUG,"Create blank DNS");
691 DNS::DNS(const std::string &dnsserver)
693 dns_init_2(dnsserver.c_str());
694 log(DEBUG,"Create DNS with server '%s'",dnsserver.c_str());
697 void DNS::SetNS(const std::string &dnsserver)
699 dns_init_2(dnsserver.c_str());
707 bool DNS::ReverseLookup(const std::string &ip, bool ins)
709 if (ServerInstance && ServerInstance->stats)
710 ServerInstance->stats->statsDns++;
711 binip = dns_aton4(ip.c_str());
717 this->myfd = dns_getname4(binip);
718 if (this->myfd == -1)
722 log(DEBUG,"DNS: ReverseLookup, fd=%d",this->myfd);
726 if (ServerInstance && ServerInstance->SE)
727 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
733 bool DNS::ForwardLookup(const std::string &host, bool ins)
735 if (ServerInstance && ServerInstance->stats)
736 ServerInstance->stats->statsDns++;
737 this->myfd = dns_getip4(host.c_str());
738 if (this->myfd == -1)
742 log(DEBUG,"DNS: ForwardLookup, fd=%d",this->myfd);
746 if (ServerInstance && ServerInstance->SE)
747 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_DNS);
753 bool DNS::ForwardLookupWithFD(const std::string &host, int &fd)
755 if (ServerInstance && ServerInstance->stats)
756 ServerInstance->stats->statsDns++;
757 this->myfd = dns_getip4(host.c_str());
759 if (this->myfd == -1)
763 log(DEBUG,"DNS: ForwardLookupWithFD, fd=%d",this->myfd);
764 if (ServerInstance && ServerInstance->SE)
765 ServerInstance->SE->AddFd(this->myfd,true,X_ESTAB_MODULE);
769 bool DNS::HasResult(int fd)
771 return (fd == this->myfd);
774 /* Only the multithreaded dns uses this poll() based
775 * check now. As its in another thread we dont have
776 * to worry about its performance that much.
778 bool DNS::HasResult()
780 log(DEBUG,"DNS: HasResult, fd=%d",this->myfd);
782 polls.fd = this->myfd;
783 polls.events = POLLIN;
784 int ret = poll(&polls,1,1);
785 log(DEBUG,"DNS: Hasresult returning %d",ret);
794 std::string DNS::GetResult()
796 log(DEBUG,"DNS: GetResult()");
797 result = dns_getresult(this->myfd);
800 if (ServerInstance && ServerInstance->stats)
801 ServerInstance->stats->statsDnsGood++;
802 dns_close(this->myfd);
808 if (ServerInstance && ServerInstance->stats)
809 ServerInstance->stats->statsDnsBad++;
810 if (this->myfd != -1)
812 dns_close(this->myfd);
819 std::string DNS::GetResultIP()
822 log(DEBUG,"DNS: GetResultIP()");
823 result = dns_getresult(this->myfd);
824 if (this->myfd != -1)
826 dns_close(this->myfd);
831 if (ServerInstance && ServerInstance->stats)
832 ServerInstance->stats->statsDnsGood++;
833 unsigned char a = (unsigned)result[0];
834 unsigned char b = (unsigned)result[1];
835 unsigned char c = (unsigned)result[2];
836 unsigned char d = (unsigned)result[3];
837 snprintf(r,1024,"%u.%u.%u.%u",a,b,c,d);
842 if (ServerInstance && ServerInstance->stats)
843 ServerInstance->stats->statsDnsBad++;
844 log(DEBUG,"DANGER WILL ROBINSON! NXDOMAIN for forward lookup, but we got a reverse lookup!");
852 void* dns_task(void* arg)
854 userrec* u = (userrec*)arg;
857 log(DEBUG,"DNS thread for user %s",u->nick);
862 if (dns1.ReverseLookup((char*)inet_ntoa(u->ip4)))
864 while (!dns1.HasResult())
868 host = dns1.GetResult();
871 if (dns2.ForwardLookup(host), false)
873 while (!dns2.HasResult())
877 ip = dns2.GetResultIP();
878 if (ip == std::string(inet_ntoa(u->ip4)))
880 if (host.length() < 160)
882 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
884 strcpy(u->host,host.c_str());
885 strcpy(u->dhost,host.c_str());
892 if ((fd_ref_table[thisfd] == u) && (fd_ref_table[thisfd]))
898 Resolver::Resolver(const std::string &source, bool forward, const std::string &dnsserver = "") : input(source), fwd(forward), server(dnsserver)
900 if (this->server != "")
901 Query.SetNS(this->server);
903 Query.SetNS(Config->DNSServer);
907 Query.ForwardLookup(input.c_str(), false);
908 this->fd = Query.GetFD();
912 Query.ReverseLookup(input.c_str(), false);
913 this->fd = Query.GetFD();
917 log(DEBUG,"Resolver::Resolver: RESOLVER_NSDOWN");
918 this->OnError(RESOLVER_NSDOWN);
919 ModuleException e("Resolver: Nameserver is down");
921 /* We shouldnt get here really */
925 if (ServerInstance && ServerInstance->SE)
927 log(DEBUG,"Resolver::Resolver: this->fd=%d",this->fd);
928 ServerInstance->SE->AddFd(this->fd,true,X_ESTAB_CLASSDNS);
932 log(DEBUG,"Resolver::Resolver: RESOLVER_NOTREADY");
933 this->OnError(RESOLVER_NOTREADY);
934 ModuleException e("Resolver: Core not initialized yet");
936 /* We shouldnt get here really */
941 Resolver::~Resolver()
943 log(DEBUG,"Resolver::~Resolver");
944 if (ServerInstance && ServerInstance->SE)
945 ServerInstance->SE->DelFd(this->fd);
948 int Resolver::GetFd()
953 bool Resolver::ProcessResult()
955 log(DEBUG,"Resolver::ProcessResult");
957 result = Query.GetResultIP();
959 result = Query.GetResult();
963 log(DEBUG,"Resolver::OnLookupComplete(%s)",result.c_str());
964 this->OnLookupComplete(result);
969 log(DEBUG,"Resolver::OnError(RESOLVER_NXDOMAIN)");
970 this->OnError(RESOLVER_NXDOMAIN);
975 void Resolver::OnLookupComplete(const std::string &result)
979 void Resolver::OnError(ResolverError e)
983 void dns_deal_with_classes(int fd)
985 log(DEBUG,"dns_deal_with_classes(%d)",fd);
986 if ((fd > -1) && (dns_classes[fd]))
988 log(DEBUG,"Valid fd %d",fd);
989 dns_classes[fd]->ProcessResult();
990 delete dns_classes[fd];
991 dns_classes[fd] = NULL;
995 bool dns_add_class(Resolver* r)
997 log(DEBUG,"dns_add_class");
998 if ((r) && (r->GetFd() > -1))
1000 if (!dns_classes[r->GetFd()])
1002 log(DEBUG,"dns_add_class: added class");
1003 dns_classes[r->GetFd()] = r;
1008 log(DEBUG,"Space occupied!");
1014 log(DEBUG,"Bad class");
1022 memset(dns_classes,0,sizeof(dns_classes));