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 very loosely based on the firedns library,
20 Copyright (C) 2002 Ian Gulliver. This file is no
21 longer anything like firedns, there are many major
22 differences between this code and the original.
23 Please do not assume that firedns works like this,
24 looks like this, walks like this or tastes like this.
31 #include <sys/types.h>
32 #include <sys/socket.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
41 #include "helperfuncs.h"
42 #include "inspircd_config.h"
43 #include "socketengine.h"
44 #include "configreader.h"
47 extern InspIRCd* ServerInstance;
48 extern ServerConfig* Config;
50 /* Master file descriptor */
51 int DNS::MasterSocket;
53 /* Query and resource record types */
56 DNS_QRY_A = 1, /* 'A' record: an IP address */
57 DNS_QRY_PTR = 12 /* 'PTR' record: a hostname */
60 /* Masks to mask off the responses we get from the DNSRequest methods */
63 ERROR_MASK = 0x10000 /* Result is an error */
66 /* Flags which can be ORed into a request or reply for different meanings */
69 FLAGS_MASK_RD = 0x01, /* Recursive */
71 FLAGS_MASK_AA = 0x04, /* Authoritative */
72 FLAGS_MASK_OPCODE = 0x78,
74 FLAGS_MASK_RCODE = 0x0F, /* Request */
80 /* Lookup table of Resolver classes. Because the request ID can be between
81 * 0 and 65535 of these, we have 65536 of them. This could be a map, saving
82 * some ram, but that will also slow down DNS requests, and the DNS request
83 * mechanism needs to be pretty fast.
85 Resolver* dns_classes[65536];
87 /* Represents a dns resource record (rr) */
91 QueryType type; /* Record type */
92 unsigned int rr_class; /* Record class */
93 unsigned long ttl; /* Time to live */
94 unsigned int rdlength; /* Record length */
97 /* Represents a dns request/reply header, and its payload as opaque data.
102 unsigned char id[2]; /* Request id */
103 unsigned int flags1; /* Flags */
104 unsigned int flags2; /* Flags */
105 unsigned int qdcount;
106 unsigned int ancount; /* Answer count */
107 unsigned int nscount; /* Nameserver count */
108 unsigned int arcount;
109 unsigned char payload[512]; /* Packet payload */
112 /* Represents a request 'on the wire' with routing information relating to
113 * where to call when we get a result
118 unsigned char id[2]; /* Request id */
119 unsigned char* res; /* Result processing buffer */
120 unsigned int rr_class; /* Request class */
121 QueryType type; /* Request type */
122 insp_inaddr myserver; /* DNS server address*/
124 /* Allocate the processing buffer */
125 DNSRequest(insp_inaddr server)
127 res = new unsigned char[512];
129 memcpy(&myserver, &server, sizeof(insp_inaddr));
132 /* Deallocate the processing buffer */
138 /* Called when a result is ready to be processed which matches this id */
139 DNSInfo ResultIsReady(DNSHeader &h, int length);
141 /* Called when there are requests to be sent out */
142 int SendRequests(const DNSHeader *header, const int length, QueryType qt);
145 /* A set of requests keyed by request id */
146 typedef std::map<int,DNSRequest*> requestlist;
148 /* An iterator into a set of requests */
149 typedef requestlist::iterator requestlist_iter;
151 /* Declare our map */
152 requestlist requests;
155 * Optimized by brain, these were using integer division and modulus.
156 * We can use logic shifts and logic AND to replace these even divisions
157 * and multiplications, it should be a bit faster (probably not noticably,
158 * but of course, more impressive). Also made these inline.
162 /* Fill a ResourceRecord class based on raw data input */
163 inline void DNSFillResourceRecord(ResourceRecord* rr, const unsigned char *input)
165 rr->type = (QueryType)((input[0] << 8) + input[1]);
166 rr->rr_class = (input[2] << 8) + input[3];
167 rr->ttl = (input[4] << 24) + (input[5] << 16) + (input[6] << 8) + input[7];
168 rr->rdlength = (input[8] << 8) + input[9];
171 /* Fill a DNSHeader class based on raw data input of a given length */
172 inline void DNSFillHeader(DNSHeader *header, const unsigned char *input, const int length)
174 header->id[0] = input[0];
175 header->id[1] = input[1];
176 header->flags1 = input[2];
177 header->flags2 = input[3];
178 header->qdcount = (input[4] << 8) + input[5];
179 header->ancount = (input[6] << 8) + input[7];
180 header->nscount = (input[8] << 8) + input[9];
181 header->arcount = (input[10] << 8) + input[11];
182 memcpy(header->payload,&input[12],length);
185 /* Empty a DNSHeader class out into raw data, ready for transmission */
186 inline void DNSEmptyHeader(unsigned char *output, const DNSHeader *header, const int length)
188 output[0] = header->id[0];
189 output[1] = header->id[1];
190 output[2] = header->flags1;
191 output[3] = header->flags2;
192 output[4] = header->qdcount >> 8;
193 output[5] = header->qdcount & 0xFF;
194 output[6] = header->ancount >> 8;
195 output[7] = header->ancount & 0xFF;
196 output[8] = header->nscount >> 8;
197 output[9] = header->nscount & 0xFF;
198 output[10] = header->arcount >> 8;
199 output[11] = header->arcount & 0xFF;
200 memcpy(&output[12],header->payload,length);
203 /* Send requests we have previously built down the UDP socket */
204 int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryType qt)
207 unsigned char payload[sizeof(DNSHeader)];
212 DNSEmptyHeader(payload,header,length);
214 memset(&addr,0,sizeof(addr));
216 memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
217 addr.sin6_family = AF_FAMILY;
218 addr.sin6_port = htons(53);
220 memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
221 addr.sin_family = AF_FAMILY;
222 addr.sin_port = htons(53);
224 if (sendto(DNS::GetMasterSocket(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
226 log(DEBUG,"Error in sendto!");
233 /* Add a query with a predefined header, and allocate an ID for it. */
234 DNSRequest* DNS::DNSAddQuery(DNSHeader *header, int &id)
237 DNSRequest* req = new DNSRequest(this->myserver);
239 header->id[0] = req->id[0] = id >> 8;
240 header->id[1] = req->id[1] = id & 0xFF;
241 header->flags1 = FLAGS_MASK_RD;
248 if (requests.find(id) == requests.end())
251 /* According to the C++ spec, new never returns NULL. */
255 int DNS::GetMasterSocket()
260 /* Initialise the DNS UDP socket so that we can send requests */
263 log(DEBUG,"----- Initialize dns class ----- ");
264 memset(dns_classes,0,sizeof(dns_classes));
266 srand((unsigned int)time(NULL));
267 memset(&myserver,0,sizeof(insp_inaddr));
268 if (insp_aton(Config->DNSServer,&addr) > 0)
269 memcpy(&myserver,&addr,sizeof(insp_inaddr));
271 MasterSocket = socket(PF_PROTOCOL, SOCK_DGRAM, 0);
272 if (MasterSocket != -1)
274 log(DEBUG,"Set query socket nonblock");
275 if (fcntl(MasterSocket, F_SETFL, O_NONBLOCK) != 0)
277 shutdown(MasterSocket,2);
282 if (MasterSocket != -1)
286 memset(&addr,0,sizeof(addr));
287 addr.sin6_family = AF_FAMILY;
289 memset(&addr.sin6_addr,255,sizeof(in6_addr));
292 memset(&addr,0,sizeof(addr));
293 addr.sin_family = AF_FAMILY;
295 addr.sin_addr.s_addr = INADDR_ANY;
297 log(DEBUG,"Binding query port");
298 if (bind(MasterSocket,(sockaddr *)&addr,sizeof(addr)) != 0)
300 log(DEBUG,"Cant bind with source port = 0");
301 shutdown(MasterSocket,2);
306 if (MasterSocket >= 0)
308 log(DEBUG,"Attach query port to socket engine");
309 if (ServerInstance && ServerInstance->SE)
310 ServerInstance->SE->AddFd(MasterSocket,true,X_ESTAB_DNS);
315 int DNSMakePayload(const char * const name, const unsigned short rr, const unsigned short rr_class, unsigned char * const payload)
318 const char * tempchr, * tempchr2;
324 /* split name up into labels, create query */
325 while ((tempchr = strchr(tempchr2,'.')) != NULL)
327 l = tempchr - tempchr2;
328 if (payloadpos + l + 1 > 507)
330 payload[payloadpos++] = l;
331 memcpy(&payload[payloadpos],tempchr2,l);
333 tempchr2 = &tempchr[1];
335 l = strlen(tempchr2);
338 if (payloadpos + l + 2 > 507)
340 payload[payloadpos++] = l;
341 memcpy(&payload[payloadpos],tempchr2,l);
343 payload[payloadpos++] = '\0';
345 if (payloadpos > 508)
348 memcpy(&payload[payloadpos],&l,2);
350 memcpy(&payload[payloadpos + 2],&l,2);
351 return payloadpos + 4;
354 int DNS::GetIP(const char *name)
361 if ((length = DNSMakePayload(name,DNS_QRY_A,1,(unsigned char*)&h.payload)) == -1)
364 req = DNSAddQuery(&h, id);
366 if (req->SendRequests(&h,length,DNS_QRY_A) == -1)
372 int DNS::GetName(const insp_inaddr *ip)
383 unsigned char* c = (unsigned char*)&ip->s_addr;
385 sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
387 if ((length = DNSMakePayload(query,DNS_QRY_PTR,1,(unsigned char*)&h.payload)) == -1)
390 req = DNSAddQuery(&h, id);
392 if (req->SendRequests(&h,length,DNS_QRY_PTR) == -1)
399 /* Return the next id which is ready, and the result attached to it
401 DNSResult DNS::GetResult()
403 /* Fetch dns query response and decide where it belongs */
407 unsigned char buffer[sizeof(DNSHeader)];
409 /* Attempt to read a header */
410 length = recv(MasterSocket,buffer,sizeof(DNSHeader),0);
412 /* Did we get the whole header? */
414 /* Nope - something screwed up. */
415 return std::make_pair(-1,"");
417 /* Put the read header info into a header class */
418 DNSFillHeader(&header,buffer,length - 12);
420 /* Get the id of this request.
421 * Its a 16 bit value stored in two char's,
422 * so we use logic shifts to create the value.
424 unsigned long this_id = header.id[1] + (header.id[0] << 8);
426 /* Do we have a pending request matching this id? */
427 requestlist_iter n_iter = requests.find(this_id);
428 if (n_iter == requests.end())
430 /* Somehow we got a DNS response for a request we never made... */
431 log(DEBUG,"DNS: got a response for a query we didnt send with fd=%d queryid=%d",MasterSocket,this_id);
432 return std::make_pair(-1,"");
436 /* Remove the query from the list of pending queries */
437 req = (DNSRequest*)n_iter->second;
438 requests.erase(n_iter);
441 /* Inform the DNSRequest class that it has a result to be read.
442 * When its finished it will return a DNSInfo which is a pair of
443 * unsigned char* resource record data, and an error message.
445 DNSInfo data = req->ResultIsReady(header, length);
446 std::string resultstr;
448 /* Check if we got a result, if we didnt, its an error */
449 if (data.first == NULL)
452 * Mask the ID with the value of ERROR_MASK, so that
453 * the dns_deal_with_classes() function knows that its
454 * an error response and needs to be treated uniquely.
455 * Put the error message in the second field.
458 return std::make_pair(this_id | ERROR_MASK, data.second);
462 /* Forward lookups come back as binary data. We must format them into ascii */
463 if (req->type == DNS_QRY_A)
466 snprintf(formatted,16,"%u.%u.%u.%u",data.first[0],data.first[1],data.first[2],data.first[3]);
467 resultstr = formatted;
471 /* Reverse lookups just come back as char* */
472 resultstr = std::string((const char*)data.first);
475 /* Build the reply with the id and hostname/ip in it */
477 return std::make_pair(this_id,resultstr);
481 /* A result is ready, process it */
482 DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
490 if (!(header.flags1 & FLAGS_MASK_QR))
491 return std::make_pair((unsigned char*)NULL,"Not a query result");
493 if (header.flags1 & FLAGS_MASK_OPCODE)
494 return std::make_pair((unsigned char*)NULL,"Unexpected value in DNS reply packet");
496 if (header.flags2 & FLAGS_MASK_RCODE)
497 return std::make_pair((unsigned char*)NULL,"Domain name not found");
499 if (header.ancount < 1)
500 return std::make_pair((unsigned char*)NULL,"No resource records returned");
502 /* Subtract the length of the header from the length of the packet */
505 while ((unsigned int)q < header.qdcount && i < length)
507 if (header.payload[i] > 63)
514 if (header.payload[i] == 0)
519 else i += header.payload[i] + 1;
523 while ((unsigned)curanswer < header.ancount)
526 while (q == 0 && i < length)
528 if (header.payload[i] > 63)
535 if (header.payload[i] == 0)
540 else i += header.payload[i] + 1; /* skip length and label */
544 return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
546 DNSFillResourceRecord(&rr,&header.payload[i]);
548 if (rr.type != this->type)
554 if (rr.rr_class != this->rr_class)
562 if ((unsigned int)curanswer == header.ancount)
563 return std::make_pair((unsigned char*)NULL,"No valid answers");
565 if (i + rr.rdlength > (unsigned int)length)
566 return std::make_pair((unsigned char*)NULL,"Resource record larger than stated");
568 if (rr.rdlength > 1023)
569 return std::make_pair((unsigned char*)NULL,"Resource record too large");
576 while (q == 0 && i < length && o + 256 < 1023)
578 if (header.payload[i] > 63)
580 memcpy(&p,&header.payload[i],2);
581 i = ntohs(p) - 0xC000 - 12;
585 if (header.payload[i] == 0)
594 memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
595 o += header.payload[i];
596 i += header.payload[i] + 1;
603 memcpy(res,&header.payload[i],rr.rdlength);
604 res[rr.rdlength] = '\0';
607 memcpy(res,&header.payload[i],rr.rdlength);
608 res[rr.rdlength] = '\0';
611 return std::make_pair(res,"No error");;
616 shutdown(MasterSocket, 2);
620 Resolver::Resolver(const std::string &source, bool forward) : input(source), fwd(forward)
624 log(DEBUG,"Resolver: Forward lookup on %s",source.c_str());
625 this->myid = ServerInstance->Res->GetIP(source.c_str());
629 log(DEBUG,"Resolver: Reverse lookup on %s",source.c_str());
631 if (insp_aton(source.c_str(), &binip) > 0)
633 /* Valid ip address */
634 this->myid = ServerInstance->Res->GetName(&binip);
638 this->OnError(RESOLVER_BADIP, "Bad IP address for reverse lookup");
639 throw ModuleException("Resolver: Bad IP address");
643 if (this->myid == -1)
645 log(DEBUG,"Resolver::Resolver: Could not get an id!");
646 this->OnError(RESOLVER_NSDOWN, "Nameserver is down");
647 throw ModuleException("Resolver: Couldnt get an id to make a request");
648 /* We shouldnt get here really */
652 log(DEBUG,"Resolver::Resolver: this->myid=%d",this->myid);
655 void Resolver::OnError(ResolverError e, const std::string &errormessage)
659 Resolver::~Resolver()
661 log(DEBUG,"Resolver::~Resolver");
664 int Resolver::GetId()
669 void DNS::MarshallReads(int fd)
671 log(DEBUG,"dns_deal_with_classes(%d)",fd);
672 if (fd == GetMasterSocket())
674 DNSResult res = this->GetResult();
677 if (res.first & ERROR_MASK)
679 res.first -= ERROR_MASK;
681 log(DEBUG,"Error available, id=%d",res.first);
682 if (dns_classes[res.first])
684 dns_classes[res.first]->OnError(RESOLVER_NXDOMAIN, res.second);
685 delete dns_classes[res.first];
686 dns_classes[res.first] = NULL;
691 log(DEBUG,"Result available, id=%d",res.first);
692 if (dns_classes[res.first])
694 dns_classes[res.first]->OnLookupComplete(res.second);
695 delete dns_classes[res.first];
696 dns_classes[res.first] = NULL;
703 bool DNS::AddResolverClass(Resolver* r)
705 if ((r) && (r->GetId() > -1))
707 if (!dns_classes[r->GetId()])
709 dns_classes[r->GetId()] = r;