-/* +------------------------------------+
- * | 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);
};
/* 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",DEFAULT,"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
{
- ServerInstance->Logs->Log("RESOLVER",DEBUG,"Error creating dns socket");
+ ServerInstance->Logs->Log("RESOLVER",SPARSE,"Error creating DNS socket - hostnames will NOT resolve");
}
}
/* 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;
DNS::MakeIP6Int(query, &i);
}
else
+ {
+ ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv6 bad format for '%s'", ip);
/* Invalid IP address */
return -1;
+ }
}
else
{
sprintf(query,"%d.%d.%d.%d.in-addr.arpa",c[3],c[2],c[1],c[0]);
}
else
+ {
+ ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce IPv4 bad format for '%s'", ip);
/* Invalid IP address */
return -1;
+ }
}
- if ((length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload)) == -1)
+ length = this->MakePayload(query, DNS_QUERY_PTR, 1, (unsigned char*)&h.payload);
+ if (length == -1)
+ {
+ ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't query '%s' using '%s' because it's too long", ip, query);
return -1;
+ }
DNSRequest* req = this->AddQuery(&h, id, ip);
- if ((!req) || (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1))
+ if (!req)
+ {
+ ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't add query (resolver down?)");
+ return -1;
+ }
+
+ if (req->SendRequests(&h, length, DNS_QUERY_PTR) == -1)
+ {
+ ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS::GetNameForce can't send (firewall?)");
return -1;
+ }
return id;
}
/* 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
}
/** 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");
break;
default:
+ ServerInstance->Logs->Log("RESOLVER",DEBUG,"DNS request with unknown query type %d", querytype);
this->myid = -1;
break;
}
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)