--- ../ucspi-tcp-0.88.orig/00README.isp.patch	1969-12-31 19:00:00.000000000 -0500
+++ 00README.isp.patch	2006-12-23 00:04:41.722776000 -0500
@@ -0,0 +1,47 @@
+This is the ucspi-tcp-0.88.isp-2006122301.patch.  This patch includes
+the following patch(es):
+  rblsmtpd-nodefaultrbl.patch
+  rblsmtpd-nonrecursive-v4.patch
+
+and a more lightweight version of the IP limiting patch:
+  ucspi-tcp-0.88-periplimit.7.patch
+
+When a host goes over it's MAXCONNIP or MAXCONNC, RBLSMTPD is set with
+DROPMSG.  This requires you to have rblsmtpd before qmail-smtpd in
+your /service/qmail-smtpd/run.
+
+This patch adds the following functionality:
+tcpserver:
+  * Per IP limiting via MAXCONNIP environment variable
+  * Per "ClassC" limiting via MAXCONNC environment variable
+
+  * If either MAXCONNIP or MAXCONNC is reached, RBLSMTPD is set with
+    the contents of the DROPMSG environment variable.  DROPMSG defaults
+    to "Please try later." (which rblsmtpd changes into
+    "451 Please try later.".
+
+rblsmtpd:
+  * Removes the default rbl query to rbl.maps.vix.com.
+  * Terminates a socket after printing RBLSMTPD, if RBLSMTPD begins with
+    a "+" character.
+  * adds a "-R base:dns_server_ip" option to send non-recursive DNS
+    queries directly to the RBL content server
+
+to patch, simply:
+  wget http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz
+  tar -zxvf ucspi-tcp-0.88.tar.gz
+  patch -d ucspi-tcp-0.88/ < ucspi-tcp-0.88.isp.patch
+
+then just follow djb's installation instructions
+
+The latest version of this patch is available at:
+http://jeremy.kister.net./code/ucspi-tcp-0.88.isp.patch
+
+Example documentation for MAXCONNIP and MAXCONNC can be found at the
+original author's website at: http://linux.voyager.hr/ucspi-tcp/
+
+-- 
+
+Jeremy Kister
+http://jeremy.kister.net./
+
--- ../ucspi-tcp-0.88.orig/dns.h	2000-03-18 10:18:42.000000000 -0500
+++ dns.h	2006-12-23 00:07:32.623657000 -0500
@@ -64,6 +64,7 @@
 
 extern int dns_resolvconfip(char *);
 extern int dns_resolve(char *,char *);
+extern int dns_query(char *,char *,char *);
 extern struct dns_transmit dns_resolve_tx;
 
 extern int dns_ip4_packet(stralloc *,char *,unsigned int);
@@ -74,6 +75,7 @@
 extern int dns_name4(stralloc *,char *);
 extern int dns_txt_packet(stralloc *,char *,unsigned int);
 extern int dns_txt(stralloc *,stralloc *);
+extern int dns_txt_q(stralloc *,stralloc *,char *);
 extern int dns_mx_packet(stralloc *,char *,unsigned int);
 extern int dns_mx(stralloc *,stralloc *);
 
--- ../ucspi-tcp-0.88.orig/dns_resolve.c	2000-03-18 10:18:42.000000000 -0500
+++ dns_resolve.c	2006-12-23 00:08:05.453838000 -0500
@@ -27,3 +27,24 @@
     if (r == 1) return 0;
   }
 }
+
+int dns_query(char *q,char qtype[2],char *servers)
+{
+  struct taia stamp;
+  struct taia deadline;
+  iopause_fd x[1];
+  int r;
+
+  if (dns_transmit_start(&dns_resolve_tx,servers,0,q,qtype,"\0\0\0\0") == -1) return -1;
+
+  for (;;) {
+    taia_now(&stamp);
+    taia_uint(&deadline,120);
+    taia_add(&deadline,&deadline,&stamp);
+    dns_transmit_io(&dns_resolve_tx,x,&deadline);
+    iopause(x,1,&deadline,&stamp);
+    r = dns_transmit_get(&dns_resolve_tx,x,&stamp);
+    if (r == -1) return -1;
+    if (r == 1) return 0;
+  }
+}
--- ../ucspi-tcp-0.88.orig/dns_txt.c	2000-03-18 10:18:42.000000000 -0500
+++ dns_txt.c	2006-12-23 00:09:07.974175000 -0500
@@ -47,6 +47,7 @@
 }
 
 static char *q = 0;
+static char *q2 = 0;
 
 int dns_txt(stralloc *out,stralloc *fqdn)
 {
@@ -57,3 +58,15 @@
   dns_domain_free(&q);
   return 0;
 }
+
+int dns_txt_q(stralloc *out,stralloc *fqdn, char *servers)
+{
+  int i;
+  
+  if (!dns_domain_fromdot(&q2,fqdn->s,fqdn->len)) return -1;
+  if (dns_query(q2,DNS_T_TXT,servers) == -1) return -1;
+  if (dns_txt_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1;
+  dns_transmit_free(&dns_resolve_tx);
+  dns_domain_free(&q2);
+  return 0;
+}
--- ../ucspi-tcp-0.88.orig/error.h	2000-03-18 10:18:42.000000000 -0500
+++ error.h	2006-12-23 00:09:29.784303000 -0500
@@ -1,7 +1,7 @@
 #ifndef ERROR_H
 #define ERROR_H
 
-extern int errno;
+#include <errno.h>
 
 extern int error_intr;
 extern int error_nomem;
--- ../ucspi-tcp-0.88.orig/rblsmtpd.c	2000-03-18 10:18:42.000000000 -0500
+++ rblsmtpd.c	2006-12-23 00:13:41.705562000 -0500
@@ -22,7 +22,7 @@
 }
 void usage(void)
 {
-  strerr_die1x(100,"rblsmtpd: usage: rblsmtpd [ -b ] [ -R ] [ -t timeout ] [ -r base ] [ -a base ] smtpd [ arg ... ]");
+  strerr_die1x(100,"rblsmtpd: usage: rblsmtpd [ -b ] [ -t timeout ] [ -r base ] [ -R base:ip ] [ -a base ] smtpd [ arg ... ]");
 }
 
 char *ip_env;
@@ -78,6 +78,47 @@
       decision = 2;
 }
 
+void rbl_ip(char *base_and_ip)
+{
+  stralloc base = {0};
+  char servers[64];
+  int i = -1;
+  int j;
+  int k;
+
+  if (decision) return;
+  
+  /* base_and_ip is base:dotted_quad */
+  if (!stralloc_copys(&base,base_and_ip)) nomem();
+  for (j=0;j<base.len;j++) {
+    if (*(base.s + j) == ':') {
+      base.len = j;
+      i = j;
+      break;
+    }
+  }
+  if (i == -1) strerr_die2x(111,FATAL,"optval was not base:ip");
+
+  k = ip4_scan(base_and_ip + i + 1,servers);
+  if (!k) strerr_die4x(111,FATAL,"failed to parse server IP address from \"", base_and_ip, "\"");
+
+  if (!stralloc_copy(&tmp,&ip_reverse)) nomem();
+  if (!stralloc_cat(&tmp,&base)) nomem();
+  if (dns_txt_q(&text,&tmp, servers) == -1) {
+    flagmustnotbounce = 1;
+    if (flagfailclosed) {
+      if (!stralloc_copys(&text,"temporary RBL lookup error")) nomem();
+      decision = 2;
+    }
+    return;
+  }
+  if (text.len)
+    if (flagrblbounce)
+      decision = 3;
+    else
+      decision = 2;
+}
+
 void antirbl(char *base)
 {
   if (decision) return;
@@ -119,7 +160,7 @@
 {
   int i;
 
-  if (flagmustnotbounce || (decision == 2)) {
+  if (flagmustnotbounce || (decision == 2) || (decision == 4)) {
     if (!stralloc_copys(&message,"451 ")) nomem();
   }
   else
@@ -142,6 +183,8 @@
 
   if (!stralloc_cats(&message,"\r\n")) nomem();
 
+  if (decision == 4) { reject(); drop(); }
+
   if (!timeout)
     reject();
   else {
@@ -155,7 +198,6 @@
 
 main(int argc,char **argv,char **envp)
 {
-  int flagwantdefaultrbl = 1;
   char *x;
   int opt;
 
@@ -169,20 +211,25 @@
       if (!stralloc_copys(&text,x + 1)) nomem();
       decision = 3;
     }
+    else if (*x == '+') {
+      if (!stralloc_copys(&text,x + 1)) nomem();
+      decision = 4;
+    }
     else {
       if (!stralloc_copys(&text,x)) nomem();
       decision = 2;
     }
   }
 
-  while ((opt = getopt(argc,argv,"bBcCt:r:a:")) != opteof)
+  while ((opt = getopt(argc,argv,"bBcCt:r:R:a:")) != opteof)
     switch(opt) {
       case 'b': flagrblbounce = 1; break;
       case 'B': flagrblbounce = 0; break;
       case 'c': flagfailclosed = 1; break;
       case 'C': flagfailclosed = 0; break;
       case 't': scan_ulong(optarg,&timeout); break;
-      case 'r': rbl(optarg); flagwantdefaultrbl = 0; break;
+      case 'r': rbl(optarg); break;
+      case 'R': rbl_ip(optarg); break;
       case 'a': antirbl(optarg); break;
       default: usage();
     }
@@ -190,7 +237,6 @@
   argv += optind;
   if (!*argv) usage();
 
-  if (flagwantdefaultrbl) rbl("rbl.maps.vix.com");
   if (decision >= 2) rblsmtpd();
 
   pathexec_run(*argv,argv,envp);
--- ../ucspi-tcp-0.88.orig/tcpserver.c	2000-03-18 10:18:42.000000000 -0500
+++ tcpserver.c	2006-12-23 00:01:32.871552000 -0500
@@ -59,11 +59,19 @@
 static stralloc tmp;
 static stralloc fqdn;
 static stralloc addresses;
+static stralloc dropmsg_buf;
 
 char bspace[16];
 buffer b;
 
 
+typedef struct
+{
+  char ip[4];
+  pid_t pid;
+} baby;
+
+baby *child;
 
 /* ---------------------------- child */
 
@@ -72,6 +80,10 @@
 int flagdeny = 0;
 int flagallownorules = 0;
 char *fnrules = 0;
+unsigned long maxload = 0;
+long maxconnip = -1;
+long maxconnc = -1;
+char *dropmsg = "+Please try later.";
 
 void drop_nomem(void)
 {
@@ -110,6 +122,8 @@
   strerr_die4sys(111,DROP,"unable to read ",fnrules,": ");
 }
 
+unsigned long limit = 40;
+
 void found(char *data,unsigned int datalen)
 {
   unsigned int next0;
@@ -125,6 +139,13 @@
 	if (data[1 + split] == '=') {
 	  data[1 + split] = 0;
 	  env(data + 1,data + 1 + split + 1);
+	  if (str_diff(data+1, "MAXCONNIP") == 0) scan_ulong(data+1+split+1,&maxconnip);
+	  if (str_diff(data+1, "MAXCONNC") == 0) scan_ulong(data+1+split+1,&maxconnc);
+	  if (str_diff(data+1, "DROPMSG") == 0) {
+	    if (!stralloc_copys(&dropmsg_buf,data+1+split+1)) drop_nomem();
+	    if (!stralloc_0(&dropmsg_buf)) drop_nomem();
+	    dropmsg = dropmsg_buf.s;
+	  }
 	}
 	break;
     }
@@ -211,6 +232,21 @@
     }
   }
 
+  if (!flagdeny && (maxconnip != -1 || maxconnc != -1)) {
+  	unsigned long u;
+  	long c1=0, cc=0;
+  	for (u=0; u < limit; u++) if (child[u].pid != 0) { 
+  		if ((child[u].ip[0] == remoteip[0]) &&
+  		    (child[u].ip[1] == remoteip[1]) &&
+  		    (child[u].ip[2] == remoteip[2]) ) {
+  		    cc++;
+  		    if (child[u].ip[3] == remoteip[3]) c1++;
+  		}
+  	}
+	if (maxconnc != -1 && (cc >= maxconnc)) flagdeny = 4;
+	if (maxconnip != -1 && (c1 >= maxconnip)) flagdeny = 3;
+  }
+
   if (verbosity >= 2) {
     strnum[fmt_ulong(strnum,getpid())] = 0;
     if (!stralloc_copys(&tmp,"tcpserver: ")) drop_nomem();
@@ -223,11 +259,25 @@
     cats(":"); safecats(remoteipstr);
     cats(":"); if (flagremoteinfo) safecats(tcpremoteinfo.s);
     cats(":"); safecats(remoteportstr);
+    if (flagdeny == 3) {
+    	char maxconstr[FMT_ULONG];
+    	maxconstr[fmt_ulong(maxconstr,maxconnip)] = 0;
+    	cats(" "); safecats ("MAXCONNIP"); cats(":"); safecats(maxconstr);
+    }
+    if (flagdeny == 4) {
+    	char maxconstr[FMT_ULONG];
+    	maxconstr[fmt_ulong(maxconstr,maxconnc)] = 0;
+    	cats(" "); safecats ("MAXCONNC"); cats(":"); safecats(maxconstr);
+    }
     cats("\n");
     buffer_putflush(buffer_2,tmp.s,tmp.len);
   }
 
-  if (flagdeny) _exit(100);
+  if (flagdeny) {
+    if (*dropmsg) {
+		env("RBLSMTPD",dropmsg);
+    }
+  }
 }
 
 
@@ -253,7 +303,6 @@
   _exit(100);
 }
 
-unsigned long limit = 40;
 unsigned long numchildren = 0;
 
 int flag1 = 0;
@@ -278,6 +327,7 @@
 {
   int wstat;
   int pid;
+  unsigned long u;
  
   while ((pid = wait_nohang(&wstat)) > 0) {
     if (verbosity >= 2) {
@@ -286,6 +336,8 @@
       strerr_warn4("tcpserver: end ",strnum," status ",strnum2,0);
     }
     if (numchildren) --numchildren; printstatus();
+    for (u=0; u < limit; u++) if (child[u].pid == pid) { child[u].pid = 0; break; }
+    if (u == limit) strerr_die1x(111,"tcpserver: ERROR: dead child not found?!"); /* never happens */
   }
 }
 
@@ -299,6 +351,7 @@
   unsigned long u;
   int s;
   int t;
+  pid_t pid;
  
   while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:pPoO")) != opteof)
     switch(opt) {
@@ -332,6 +385,10 @@
   argc -= optind;
   argv += optind;
 
+  x = env_get("MAXCONNIP"); if (x) scan_ulong(x,&maxconnip);
+  x = env_get("MAXCONNC"); if (x) scan_ulong(x,&maxconnc);
+  x = env_get("DROPMSG"); if (x) dropmsg = x;
+  
   if (!verbosity)
     buffer_2->fd = -1;
  
@@ -352,6 +409,10 @@
   }
 
   if (!*argv) usage();
+  
+  child = calloc(sizeof(baby),limit);
+  if (!child)
+    strerr_die2x(111,FATAL,"out of memory for MAXCONNIP tracking");
  
   sig_block(sig_child);
   sig_catch(sig_child,sigchld);
@@ -405,7 +466,7 @@
     if (t == -1) continue;
     ++numchildren; printstatus();
  
-    switch(fork()) {
+    switch(pid=fork()) {
       case 0:
         close(s);
         doit(t);
@@ -420,6 +481,10 @@
       case -1:
         strerr_warn2(DROP,"unable to fork: ",&strerr_sys);
         --numchildren; printstatus();
+        break;
+      default:
+        for (u=0; u < limit; u++) if (child[u].pid == 0) { byte_copy(child[u].ip,4,remoteip); child[u].pid = pid; break; }
+	if (u == limit) strerr_die1x(111,"tcpserver: ERROR: no empty space for new child?!"); /* never happens */
     }
     close(t);
   }
