summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbrain <brain@e03df62e-2008-0410-955e-edbf42e46eb7>2006-08-03 14:25:49 +0000
committerbrain <brain@e03df62e-2008-0410-955e-edbf42e46eb7>2006-08-03 14:25:49 +0000
commit43759fd180caf1894e10e8adc2df86f029aa63b1 (patch)
treead2c530e324266a08b1a39f98b1e6120889b9d47
parentbcc3c8566cc5fcff6c39c94ac823941ad1e60b83 (diff)
Check for spoofed DNS replies where the source-port is invalid, or the ip is not that of the nameserver we're configured to use
git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@4668 e03df62e-2008-0410-955e-edbf42e46eb7
-rw-r--r--include/dns.h7
-rw-r--r--src/dns.cpp35
2 files changed, 35 insertions, 7 deletions
diff --git a/include/dns.h b/include/dns.h
index 41f42f46a..9d6f161df 100644
--- a/include/dns.h
+++ b/include/dns.h
@@ -190,7 +190,7 @@ class DNS : public Extensible
static const int MAX_REQUEST_ID = 0xFFFF;
/**
- * Requests that are currently 'in flight
+ * Requests that are currently 'in flight'
*/
requestlist requests;
@@ -220,6 +220,11 @@ class DNS : public Extensible
int MakePayload(const char* name, const QueryType rr, const unsigned short rr_class, unsigned char* payload);
public:
+ /**
+ * The port number DNS requests are made on,
+ * and replies have as a source-port number.
+ */
+ static const int QUERY_PORT = 53;
/**
* Fill an rr (resource record) with data from input
diff --git a/src/dns.cpp b/src/dns.cpp
index 648e8f4a4..0329ea712 100644
--- a/src/dns.cpp
+++ b/src/dns.cpp
@@ -184,11 +184,11 @@ int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryTyp
#ifdef IPV6
memcpy(&addr.sin6_addr,&myserver,sizeof(addr.sin6_addr));
addr.sin6_family = AF_FAMILY;
- addr.sin6_port = htons(53);
+ addr.sin6_port = htons(DNS::QUERY_PORT);
#else
memcpy(&addr.sin_addr.s_addr,&myserver,sizeof(addr.sin_addr));
addr.sin_family = AF_FAMILY;
- addr.sin_port = htons(53);
+ addr.sin_port = htons(DNS::QUERY_PORT);
#endif
if (sendto(DNS::GetMasterSocket(), payload, length + 12, 0, (sockaddr *) &addr, sizeof(addr)) == -1)
{
@@ -202,13 +202,13 @@ int DNSRequest::SendRequests(const DNSHeader *header, const int length, QueryTyp
/* Add a query with a predefined header, and allocate an ID for it. */
DNSRequest* DNS::AddQuery(DNSHeader *header, int &id)
{
- id = this->PRNG() & 0xFFFF;
+ id = this->PRNG() & DNS::MAX_REQUEST_ID;
/* This id is already 'in flight', pick another.
* -- Thanks jilles
*/
while (requests.find(id) != requests.end())
- id = this->PRNG() & 0xFFFF;
+ id = this->PRNG() & DNS::MAX_REQUEST_ID;
DNSRequest* req = new DNSRequest(this->myserver);
@@ -391,15 +391,38 @@ DNSResult DNS::GetResult()
DNSHeader header;
DNSRequest *req;
unsigned char buffer[sizeof(DNSHeader)];
+ sockaddr from;
+ socklen_t x = sizeof(from);
+ const char* ipaddr_from = "";
+ unsigned short int port_from = 0;
- /* Attempt to read a header */
- int length = recv(MasterSocket,buffer,sizeof(DNSHeader),0);
+ int length = recvfrom(MasterSocket,buffer,sizeof(DNSHeader),0,&from,&x);
/* Did we get the whole header? */
if (length < 12)
/* Nope - something screwed up. */
return std::make_pair(-1,"");
+ /* Check wether the reply came from a different DNS
+ * server to the one we sent it to, or the source-port
+ * is not 53.
+ * A user could in theory still spoof dns packets anyway
+ * but this is less trivial than just sending garbage
+ * to the client, which is possible without this check.
+ *
+ * -- Thanks jilles for pointing this one out.
+ */
+#ifdef IPV6
+ ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin6_addr);
+ port_from = ntohs(((sockaddr_in*)&from)->sin6_port);
+#else
+ ipaddr_from = insp_ntoa(((sockaddr_in*)&from)->sin_addr);
+ port_from = ntohs(((sockaddr_in*)&from)->sin_port);
+#endif
+
+ if ((port_from != DNS::QUERY_PORT) || (strcasecmp(ipaddr_from, Config->DNSServer)))
+ return std::make_pair(-1,"");
+
/* Put the read header info into a header class */
DNS::FillHeader(&header,buffer,length - 12);