-/* +------------------------------------+
- * | Inspire Internet Relay Chat Daemon |
- * +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2012 William Pitcock <nenolod@dereferenced.org>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2006, 2009 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007, 2009 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2005-2007 Craig Edwards <craigedwards@brainbox.cc>
*
- * InspIRCd: (C) 2002-2010 InspIRCd Development Team
- * See: http://wiki.inspircd.org/Credits
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
*
- * This program is free but copyrighted software; see
- * the file COPYING for details.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
*
- * ---------------------------------------------------
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
/* $Core */
/*
looks like this, walks like this or tastes like this.
*/
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include "configreader.h"
#include "socket.h"
+#define DN_COMP_BITMASK 0xC000 /* highest 6 bits in a DN label header */
+
/** Masks to mask off the responses we get from the DNSRequest methods
*/
enum QueryInfo
DNSRequest(DNS* dns, int id, const std::string &original);
~DNSRequest();
- DNSInfo ResultIsReady(DNSHeader &h, int length);
+ DNSInfo ResultIsReady(DNSHeader &h, unsigned length);
int SendRequests(const DNSHeader *header, const int length, QueryType qt);
};
DNS* dns;
public:
CacheTimer(DNS* thisdns)
- : Timer(3600, ServerInstance->Time(), true), dns(thisdns) { }
+ : Timer(5*60, ServerInstance->Time(), true), dns(thisdns) { }
virtual void Tick(time_t)
{
}
};
-CachedQuery::CachedQuery(const std::string &res, unsigned int ttl) : data(res)
+CachedQuery::CachedQuery(const std::string &res, QueryType qt, unsigned int ttl) : data(res), type(qt)
{
+ if (ttl > 5*60)
+ ttl = 5*60;
expires = ServerInstance->Time() + ttl;
}
/* Allocate the processing buffer */
DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns)
{
- res = new unsigned char[512];
+ /* hardening against overflow here: make our work buffer twice the theoretical
+ * maximum size so that hostile input doesn't screw us over.
+ */
+ res = new unsigned char[sizeof(DNSHeader) * 2];
*res = 0;
orig = original;
RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid);
return NULL;
/* Create an id */
+ unsigned int tries = 0;
do {
id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID);
+ if (++tries == DNS::MAX_REQUEST_ID*5)
+ {
+ // If we couldn't find an empty slot this many times, do a sequential scan as a last
+ // resort. If an empty slot is found that way, go on, otherwise throw an exception
+ id = -1;
+ for (int i = 0; i < DNS::MAX_REQUEST_ID; i++)
+ {
+ if (!requests[i])
+ {
+ id = i;
+ break;
+ }
+ }
+
+ if (id == -1)
+ throw ModuleException("DNS: All ids are in use");
+
+ break;
+ }
} while (requests[id]);
DNSRequest* req = new DNSRequest(this, id, original);
void DNS::Rehash()
{
- int portpass = 0;
-
if (this->GetFd() > -1)
{
- if (ServerInstance && ServerInstance->SE)
- ServerInstance->SE->DelFd(this);
+ ServerInstance->SE->DelFd(this);
ServerInstance->SE->Shutdown(this, 2);
ServerInstance->SE->Close(this);
this->SetFd(-1);
{
ServerInstance->SE->SetReuse(s);
ServerInstance->SE->NonBlocking(s);
- /* Bind the port - port 0 INADDR_ANY */
- if (!ServerInstance->BindSocket(this->GetFd(), portpass, "", false))
+ irc::sockets::sockaddrs bindto;
+ memset(&bindto, 0, sizeof(bindto));
+ bindto.sa.sa_family = myserver.sa.sa_family;
+ if (ServerInstance->SE->Bind(this->GetFd(), bindto) < 0)
{
/* Failed to bind */
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error binding dns socket");
+ ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error binding dns socket - hostnames will NOT resolve");
ServerInstance->SE->Shutdown(this, 2);
ServerInstance->SE->Close(this);
this->SetFd(-1);
}
-
- if (this->GetFd() >= 0)
+ else if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
{
- /* Hook the descriptor into the socket engine */
- if (ServerInstance && ServerInstance->SE)
- {
- if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
- {
- ServerInstance->Logs->Log("RESOLVER",SPARSE,"Internal error starting DNS - hostnames will NOT resolve.");
- ServerInstance->SE->Shutdown(this, 2);
- ServerInstance->SE->Close(this);
- this->SetFd(-1);
- }
- }
+ ServerInstance->Logs->Log("RESOLVER",SPARSE,"Internal error starting DNS - hostnames will NOT resolve.");
+ ServerInstance->SE->Shutdown(this, 2);
+ ServerInstance->SE->Close(this);
+ this->SetFd(-1);
}
}
else
/* Clear the requests class table */
memset(requests,0,sizeof(requests));
- /* Set the id of the next request to 0
- */
- currid = 0;
-
/* DNS::Rehash() sets this to a valid ptr
*/
this->cache = NULL;
/* Did we get the whole header? */
if (length < 12)
{
+ ServerInstance->Logs->Log("RESOLVER",DEBUG,"GetResult didn't get a full packet (len=%d)", length);
/* Nope - something screwed up. */
return DNSResult(-1,"",0,"");
}
*
* -- Thanks jilles for pointing this one out.
*/
- if (memcmp(&from, &myserver, sizeof(irc::sockets::sockaddrs)))
+ if (from != myserver)
{
+ std::string server1 = from.str();
+ std::string server2 = myserver.str();
+ ServerInstance->Logs->Log("RESOLVER",DEBUG,"Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
+ server1.c_str(), server2.c_str());
return DNSResult(-1,"",0,"");
}
if (!requests[this_id])
{
/* Somehow we got a DNS response for a request we never made... */
+ ServerInstance->Logs->Log("RESOLVER",DEBUG,"Hmm, got a result that we didn't ask for (id=%lx). Ignoring.", this_id);
return DNSResult(-1,"",0,"");
}
else
case DNS_QUERY_AAAA:
{
- inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted));
- char* c = strstr(formatted,":0:");
- if (c != NULL)
+ if (!inet_ntop(AF_INET6, data.first, formatted, sizeof(formatted)))
{
- memmove(c+1,c+2,strlen(c+2) + 1);
- c += 2;
- while (memcmp(c,"0:",2) == 0)
- memmove(c,c+2,strlen(c+2) + 1);
- if (memcmp(c,"0",2) == 0)
- *c = 0;
- if (memcmp(formatted,"0::",3) == 0)
- memmove(formatted,formatted + 1, strlen(formatted + 1) + 1);
+ std::string ro = req->orig;
+ delete req;
+ return DNSResult(this_id | ERROR_MASK, "inet_ntop() failed", 0, ro);
}
+
resultstr = formatted;
/* Special case. Sending ::1 around between servers
/* Identical handling to PTR */
case DNS_QUERY_PTR:
+ {
/* Reverse lookups just come back as char* */
resultstr = std::string((const char*)data.first);
+ if (resultstr.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-") != std::string::npos)
+ {
+ std::string ro = req->orig;
+ delete req;
+ return DNSResult(this_id | ERROR_MASK, "Invalid char(s) in reply", 0, ro);
+ }
+ }
break;
default:
/* Build the reply with the id and hostname/ip in it */
std::string ro = req->orig;
+ DNSResult result = DNSResult(this_id,resultstr,ttl,ro,req->type);
delete req;
- return DNSResult(this_id,resultstr,ttl,ro);
+ return result;
}
}
/** A result is ready, process it */
-DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
+DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, unsigned length)
{
- int i = 0;
+ unsigned i = 0, o;
int q = 0;
- int curanswer, o;
+ int curanswer;
ResourceRecord rr;
unsigned short ptr;
else i += header.payload[i] + 1; /* skip length and label */
}
}
- if (length - i < 10)
+ if (static_cast<int>(length - i) < 10)
return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");
/* XXX: We actually initialise 'rr' here including its ttl field */
switch (rr.type)
{
+ /*
+ * CNAME and PTR are compressed. We need to decompress them.
+ */
case DNS_QUERY_CNAME:
- /* CNAME and PTR have the same processing code */
case DNS_QUERY_PTR:
+ {
+ unsigned short lowest_pos = length;
o = 0;
q = 0;
while (q == 0 && i < length && o + 256 < 1023)
{
+ /* DN label found (byte over 63) */
if (header.payload[i] > 63)
{
memcpy(&ptr,&header.payload[i],2);
- i = ntohs(ptr) - 0xC000 - 12;
+
+ i = ntohs(ptr);
+
+ /* check that highest two bits are set. if not, we've been had */
+ if ((i & DN_COMP_BITMASK) != DN_COMP_BITMASK)
+ return std::make_pair((unsigned char *) NULL, "DN label decompression header is bogus");
+
+ /* mask away the two highest bits. */
+ i &= ~DN_COMP_BITMASK;
+
+ /* and decrease length by 12 bytes. */
+ i -= 12;
+
+ if (i >= lowest_pos)
+ return std::make_pair((unsigned char *) NULL, "Invalid decompression pointer");
+ lowest_pos = i;
}
else
{
res[o] = 0;
if (o != 0)
res[o++] = '.';
- memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
+
+ if (o + header.payload[i] > sizeof(DNSHeader))
+ return std::make_pair((unsigned char *) NULL, "DN label decompression is impossible -- malformed/hostile packet?");
+
+ memcpy(&res[o], &header.payload[i + 1], header.payload[i]);
o += header.payload[i];
i += header.payload[i] + 1;
}
}
}
res[o] = 0;
+ }
break;
case DNS_QUERY_AAAA:
+ if (rr.rdlength != sizeof(struct in6_addr))
+ return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 16 bytes for an ipv6 entry -- malformed/hostile packet?");
+
memcpy(res,&header.payload[i],rr.rdlength);
res[rr.rdlength] = 0;
break;
case DNS_QUERY_A:
+ if (rr.rdlength != sizeof(struct in_addr))
+ return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 4 bytes for an ipv4 entry -- malformed/hostile packet?");
+
memcpy(res,&header.payload[i],rr.rdlength);
res[rr.rdlength] = 0;
break;
default:
- memcpy(res,&header.payload[i],rr.rdlength);
- res[rr.rdlength] = 0;
+ return std::make_pair((unsigned char *) NULL, "don't know how to handle undefined type (" + ConvToStr(rr.type) + ") -- rejecting");
break;
}
return std::make_pair(res,"No error");
{
ServerInstance->Res->DelCache(source);
}
- else
+ else if (CQ->type == qt)
{
cached = true;
return;
}
+ CQ = NULL;
}
switch (querytype)
ServerInstance->stats->statsDnsGood++;
if (!this->GetCache(res.original.c_str()))
- this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.ttl)));
+ {
+ if (cache->size() >= MAX_CACHE_SIZE)
+ cache->clear();
+ this->cache->insert(std::make_pair(res.original.c_str(), CachedQuery(res.result, res.type, res.ttl)));
+ }
Classes[res.id]->OnLookupComplete(res.result, res.ttl, false);
delete Classes[res.id];
Classes[r->GetId()] = r;
return true;
}
- else
- /* Duplicate id */
- return false;
}
- else
- {
- /* Pointer or id not valid.
- * Free the item and return
- */
- if (r)
- delete r;
- return false;
- }
+ /* Pointer or id not valid, or duplicate id.
+ * Free the item and return
+ */
+ delete r;
+ return false;
}
void DNS::CleanResolvers(Module* module)