--- ucspi-tcp-0.88/tcpserver.c.orig	2009-06-01 19:36:13.000000000 +0200
+++ ucspi-tcp-0.88/tcpserver.c	2013-09-25 09:51:40.056265471 +0200
@@ -3,6 +3,7 @@
 #include <netdb.h>
 #include <openssl/ssl.h>
 #include "uint16.h"
+#include "uint32.h"
 #include "str.h"
 #include "byte.h"
 #include "fmt.h"
@@ -290,6 +291,7 @@
 tcpserver: usage: tcpserver \
 [ -461UXpPhHrRoOdDqQv ] \
 [ -c limit ] \
+[ -s perip limit ] \
 [ -x rules.cdb ] \
 [ -B banner ] \
 [ -g gid ] \
@@ -321,8 +323,24 @@
 }
 
 unsigned long limit = 40;
+unsigned long periplimit = 0;
 unsigned long numchildren = 0;
 
+typedef struct
+{
+  pid_t pid;
+  int offset;
+} connections;
+
+typedef struct
+{
+  uint32 ipaddr;
+  unsigned long num;
+} ipchildren;
+
+connections *children;
+ipchildren *numipchildren;
+
 int flag1 = 0;
 unsigned long backlog = 20;
 unsigned long uid = 0;
@@ -345,6 +363,7 @@
 {
   int wstat;
   int pid;
+  int i;
  
   while ((pid = wait_nohang(&wstat)) > 0) {
     if (verbosity >= 2) {
@@ -353,6 +372,12 @@
       strerr_warn4("tcpserver: end ",strnum," status ",strnum2,0);
     }
     if (numchildren) --numchildren; printstatus();
+    for (i=0;i<limit;++i)
+      if (children[i].pid == pid) {
+        children[i].pid=0;
+        numipchildren[children[i].offset].num--;
+        break;
+      }
   }
 }
 
@@ -379,13 +404,14 @@
     strerr_die2x(111,FATAL,"out of memory");
   if (!stralloc_copys(&jaildir, JAILDIR) || !stralloc_0(&jaildir) )
     strerr_die2x(111,FATAL,"out of memory");
-  while ((opt = getopt(argc,argv,"dDvqQhHrRsS1UXx:t:u:g:l:b:B:c:n:a:j:J:pPoO")) != opteof)
+  while ((opt = getopt(argc,argv,"dDvqQhHrRsS1UXx:t:u:g:l:b:B:c:n:a:j:J:s:pPoO")) != opteof)
 #else
-  while ((opt = getopt(argc,argv,"46dDvqQhHrR1UXx:t:u:g:l:b:B:c:I:pPoO")) != opteof)
+  while ((opt = getopt(argc,argv,"46dDvqQhHrR1UXx:t:u:g:l:b:B:c:I:s:pPoO")) != opteof)
 #endif
     switch(opt) {
       case 'b': scan_ulong(optarg,&backlog); break;
       case 'c': scan_ulong(optarg,&limit); break;
+      case 's': scan_ulong(optarg,&periplimit); break;
       case 'X': flagallownorules = 1; break;
       case 'x': fnrules = optarg; break;
       case 'B': banner = optarg; break;
@@ -436,6 +462,11 @@
 
   if (!verbosity)
     buffer_2->fd = -1;
+
+  if (!periplimit)
+    periplimit = limit;
+  if (limit<periplimit)
+    strerr_die2x(111,FATAL,"periplimit must be smaller than or equal to connection limit");
  
   hostname = *argv++;
   if (!hostname) usage();
@@ -458,6 +489,13 @@
   sig_catch(sig_child,sigchld);
   sig_catch(sig_term,sigterm);
   sig_ignore(sig_pipe);
+
+  if (!(numipchildren = (ipchildren*)malloc(sizeof(ipchildren)*limit)))
+    strerr_die2x(111,FATAL,"out of memory");
+  byte_zero(numipchildren,sizeof(ipchildren)*limit);
+  if (!(children = (connections*)malloc(sizeof(connections)*limit)))
+    strerr_die2x(111,FATAL,"out of memory");
+  byte_zero(children,sizeof(connections)*limit);
  
   if (str_equal(hostname,"0")) {
     byte_zero(localip,sizeof localip);
@@ -527,6 +565,13 @@
   printstatus();
  
   for (;;) {
+    uint32 ipaddr;
+    pid_t pid;
+    int i;
+    int lastempty = 0;
+    int freechild = 0;
+    ipchildren *ipcount;
+
     while (numchildren >= limit) sig_pause();
 
     sig_unblock(sig_child);
@@ -534,9 +579,43 @@
     sig_block(sig_child);
 
     if (t == -1) continue;
+
+    for (i=0;i<limit;++i) {
+      if (children[i].pid == 0) {
+        freechild = i;
+        break;
+      }
+    }
+    uint32_unpack(remoteip,&ipaddr);
+    for (i=0;i<limit;++i) {
+      ipcount = &numipchildren[i];
+      if (!ipcount->ipaddr || !ipcount->num)
+        lastempty = i;
+      else if (ipcount->ipaddr == ipaddr) {
+        ++ipcount->num;
+        break;
+      }
+    }
+    if (i == limit) {
+      if (lastempty) {
+        i = lastempty;
+        ipcount = &numipchildren[i];
+        ipcount->ipaddr = ipaddr;
+        ipcount->num = 1;
+      } else
+        /* never reached */
+        strerr_die2x(111,DROP,"internal problem");
+    }
+    if (ipcount->num > periplimit) {
+      remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0;
+      strerr_warn3(DROP, "per ip limit reached for ", remoteipstr, 0);
+      close(t);
+      --ipcount->num;
+      continue;
+    }
     ++numchildren; printstatus();
  
-    switch(fork()) {
+    switch(pid = fork()) {
       case 0:
         close(s);
         doit(t);
@@ -600,6 +679,10 @@
       case -1:
         strerr_warn2(DROP,"unable to fork: ",&strerr_sys);
         --numchildren; printstatus();
+        break;
+      default:
+      	children[freechild].pid = pid;
+	children[freechild].offset = i;
     }
     close(t);
   }
