]> granicus.if.org Git - pdns/commitdiff
add max-tcp-per-client to limit number of TCP connections per client
authorBert Hubert <bert.hubert@netherlabs.nl>
Sat, 8 Apr 2006 20:23:38 +0000 (20:23 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Sat, 8 Apr 2006 20:23:38 +0000 (20:23 +0000)
git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@669 d19b8d6e-7fed-0310-83ef-9ca221ded41b

pdns/docs/pdns.sgml
pdns/pdns_recursor.cc
pdns/rec_channel_rec.cc
pdns/syncres.hh

index f937db84acc8f49238d3f3b7b128cf24a4e0a037..41fb4571b0b87f31305fc4c2e1ad32ddf4c5e695 100644 (file)
@@ -5991,6 +5991,14 @@ local0.err                        /var/log/pdns.err
              </para>
            </listitem>
          </varlistentry>
+         <varlistentry>
+           <term>max-tcp-per-client</term>
+           <listitem>
+             <para>
+             Maximum number of simultaneous incoming TCP connections allowed per client (remote IP address). Defaults to 0, which means unlimited.
+             </para>
+           </listitem>
+         </varlistentry>
          <varlistentry>
            <term>query-local-address</term>
            <listitem>
index 9e853ccefb28c8229c61ecdab3228e862a5b5e11..03f97ae57dca8aa800e836ea1f6b12aa7a419d85 100644 (file)
@@ -601,6 +601,7 @@ static void houseKeeping(void *)
   }
 }
 
+map<uint32_t, uint32_t> g_tcpClientCounts;
 struct TCPConnection
 {
   int fd;
@@ -610,6 +611,13 @@ struct TCPConnection
   struct sockaddr_in remote;
   char data[65535];
   time_t startTime;
+
+  void closeAndCleanup()
+  {
+    close(fd);
+    if(!g_tcpClientCounts[remote.sin_addr.s_addr]--) 
+      g_tcpClientCounts.erase(remote.sin_addr.s_addr);
+  }
 };
 
 #if 0
@@ -690,6 +698,7 @@ int main(int argc, char **argv)
     ::arg().set("hint-file", "If set, load root hints from this file")="";
     ::arg().set("max-cache-entries", "If set, maximum number of entries in the main cache")="0";
     ::arg().set("allow-from", "If set, only allow these comma separated netmasks to recurse")="";
+    ::arg().set("max-tcp-per-client", "If set, maximum number of TCP sessions per client (IP address)")="0";
 
     ::arg().setCmd("help","Provide a helpful message");
     L.toConsole(Logger::Warning);
@@ -786,7 +795,10 @@ int main(int argc, char **argv)
     counter=0;
     struct timeval now;
     unsigned int maxTcpClients=::arg().asNum("max-tcp-clients");
-    int tcpLimit=::arg().asNum("client-tcp-timeout");
+    int tcpTimeout=::arg().asNum("client-tcp-timeout");
+
+    unsigned int maxTCPPerClient=::arg().asNum("max-tcp-per-client");
+
     for(;;) {
       while(MT->schedule()); // housekeeping, let threads do their thing
       
@@ -816,14 +828,14 @@ int main(int argc, char **argv)
       vector<TCPConnection> sweeped;
 
       for(vector<TCPConnection>::iterator i=tcpconnections.begin();i!=tcpconnections.end();++i) {
-       if(now.tv_sec < i->startTime + tcpLimit) {
+       if(now.tv_sec < i->startTime + tcpTimeout) {
          FD_SET(i->fd, &readfds);
          fdmax=max(fdmax,i->fd);
          sweeped.push_back(*i);
        }
        else {
          L<<Logger::Error<<"TCP timeout from client "<<inet_ntoa(i->remote.sin_addr)<<endl;
-         close(i->fd);
+         i->closeAndCleanup();
        }
       }
       sweeped.swap(tcpconnections);
@@ -932,6 +944,12 @@ int main(int argc, char **argv)
              continue;
            }
 
+           if(maxTCPPerClient && g_tcpClientCounts.count(addr.sin_addr.s_addr) && g_tcpClientCounts[addr.sin_addr.s_addr] >= maxTCPPerClient) {
+             g_stats.tcpClientOverflow++;
+             close(newsock); // don't call TCPConnection::closeAndCleanup here - did not enter it in the counts yet!
+             continue;
+           }
+           g_tcpClientCounts[addr.sin_addr.s_addr]++;
            Utility::setNonBlocking(newsock);
            TCPConnection tc;
            tc.fd=newsock;
@@ -1015,7 +1033,7 @@ int main(int argc, char **argv)
              i->state=TCPConnection::GETQUESTION;
            }
            if(!bytes || bytes < 0) {
-             close(i->fd);
+             i->closeAndCleanup();
              tcpconnections.erase(i);
              break;
            }
@@ -1029,7 +1047,7 @@ int main(int argc, char **argv)
            }
            if(!bytes || bytes < 0) {
              L<<Logger::Error<<"TCP Remote "<<sockAddrToString(&i->remote,sizeof(i->remote))<<" disconnected after first byte"<<endl;
-             close(i->fd);
+             i->closeAndCleanup();
              tcpconnections.erase(i);
              break;
            }
@@ -1039,7 +1057,7 @@ int main(int argc, char **argv)
            int bytes=read(i->fd,i->data + i->bytesread,i->qlen - i->bytesread);
            if(!bytes || bytes < 0) {
              L<<Logger::Error<<"TCP Remote "<<sockAddrToString(&i->remote,sizeof(i->remote))<<" disconnected while reading question body"<<endl;
-             close(i->fd);
+             i->closeAndCleanup();
              tcpconnections.erase(i);
              break;
            }
@@ -1052,7 +1070,7 @@ int main(int argc, char **argv)
              }
              catch(MOADNSException &mde) {
                L<<Logger::Error<<"Unable to parse packet from remote TCP client "<<sockAddrToString(&i->remote,sizeof(i->remote))<<endl;
-               close(i->fd);
+               i->closeAndCleanup();
                tcpconnections.erase(i);
                break;
              }
index cf851586dbad217b4b2e115a85d3b2f2a6b68b4c..b52e073217ad21c1571aa2611ad13406ba2152e6 100644 (file)
@@ -141,6 +141,7 @@ RecursorControlParser::RecursorControlParser()
 
   addGetStat("unauthorized-udp", &g_stats.unauthorizedUDP);
   addGetStat("unauthorized-tcp", &g_stats.unauthorizedTCP);
+  addGetStat("tcp-client-overflow", &g_stats.tcpClientOverflow);
 
   addGetStat("answers0-1", &g_stats.answers0_1);
   addGetStat("answers1-10", &g_stats.answers1_10);
index 8dc6d6efaaf3a246b8e7a5a183cab730f5ea780f..c060648e896409901a0ad88f994ce2354983d2b2 100644 (file)
@@ -354,6 +354,7 @@ struct RecursorStats
   uint64_t tcpqcounter;
   uint64_t unauthorizedUDP;
   uint64_t unauthorizedTCP;
+  uint64_t tcpClientOverflow;
 };
 
 extern RecursorStats g_stats;