+++ /dev/null
-diff -purN ucspi-tcp-0.88.org/README.tcpserver-limits-patch ucspi-tcp-0.88.my/README.tcpserver-limits-patch
---- ucspi-tcp-0.88.org/README.tcpserver-limits-patch 1970-01-01 01:00:00.000000000 +0100
-+++ ucspi-tcp-0.88.my/README.tcpserver-limits-patch 2005-01-30 18:26:14.000000000 +0100
-@@ -0,0 +1,135 @@
-+20050130 reinstated /proc/loadavg support for those compiling on Linux
-+with dietlibc (see #define NO_GETLOADAVG at top of tcpserver.c).
-+Also, we now compile on 64bit platforms (we avoid including unistd.h if
-+using getloadavg(3), so we don't conflict with readwrite.h header file)
-+Needed if your compile was breaking with:
-+readwrite.h:4: error: syntax error before "read"
-+readwrite.h:4: warning: data definition has no type or storage class
-+SUMMARY: If 20040725 worked for you, there is no reason to upgrade
-+(no new features of bugfixes)
-+
-+20040725 adds a sleep(1) before terminating (to prevent too high load from
-+many rapid fork()/exit() calls. It also changes the method for checking
-+system load to getloadavg(3) instead of parsing /proc/loadavg, therefore
-+making it working on *BSD and other non-Linux systems in addition to Linux.
-+It also adds DIEMSG="xxx" support.
-+
-+20040327 fixes a bug in 20040124 related to MAXLOAD (it would not work
-+correctly when load was higher than 10.00)
-+
-+
-+This patch (20040725) makes tcpserver from DJB's ucspi-tcp-0.88 package (see
-+http://cr.yp.to/ucspi-tcp.html) to modify its behavior if some environment
-+variables are present.
-+
-+The variables can be preset before starting tcpserver (thus acting as
-+default for all connections), or, if you use 'tcpserver -x xxx.cdb', they
-+can be set (or overridden) from xxx.cdb. If none of the variables are set,
-+tcpserver behaves same as non patched version (except for negligible
-+performance loss). Any or all variables can be set, as soon as first limit
-+is reached the connection is dropped. I'd recommend using .cdb files
-+exclusively though, as you can then modify configuration without killing
-+tcpserver.
-+
-+The variables are:
-+
-+(1) MAXLOAD
-+ maximum 1-minute load average * 100. For example, if you have line
-+ :allow,MAXLOAD="350"
-+ in your rules file from which you created .cdb, the connection will be
-+ accepted only if load average is below 3.50
-+ For this variable to have effect, you have to have working getloadavg(3)
-+ (most modern UN*Xoids have, including Linux and FreeBSD)
-+ Otherwise, you have to uncomment #define NO_GETLOADAVG in tcpserver.c
-+ and have readable '/proc/loadavg' with linux-2.4.x/2.6.x syntax (see
-+ the source -- this is needed if you're compiling with dietlibc
-+ or such)
-+
-+(2) MAXCONNIP
-+ maximum connections from one IP address. tcpserver's -c flag defines
-+ maximum number of allowed connections, but it can be abused if
-+ just one host goes wild and eats all the connections - no other host
-+ would be able to connect then. If you created your .cdb with:
-+ :allow,MAXCONNIP="5"
-+ and run tcpserver -c 50, then each IP address would be able to have at
-+ most 5 concurrent connections, while there still could connect 50
-+ clients total
-+
-+(3) MAXCONNC
-+
-+ maximum connections from whole C-class (256 addresses). Extension of
-+ MAXCONNIP, as sometimes the problematic client has a whole farm of
-+ client machines with different IP addresses instead of just one IP
-+ address, and they all try to connect. It might have been more useful to
-+ be able to specify CIDR block than C-class, but I've decided to KISS.
-+
-+ for example tcpserver -c 200, and .cdb with:
-+ :allow,MAXCONNC="15"
-+ will allow at most 15 host from any x.y.z.0/24 address block, while
-+ still allowing up to 200 total connections.
-+
-+(4) DIEMSG
-+
-+ if set and one of the above limits is exceeded, this is the message
-+ to be sent to client (CRLF is always added to the text) before terminating
-+ connection. If unset, the connection simply terminates (after 1 sec delay)
-+ if limit is exceeded.
-+
-+ For example:
-+ DIEMSG="421 example.com Service temporarily not available, closing
-+ transmission channel"
-+
-+Notes:
-+
-+- if a connection is dropped due to some of those variables set, it will be
-+ flagged (if you run tcpserver -v) with "LOAD:", "MAXCONNIP:" or
-+ "MAXCONNC:" at the end of the "tcpserver: deny" line. If that bothers you
-+ (eg. you have a strict log parsers), don't apply that chunk of the patch.
-+
-+- the idea for this patch came from my previous experience with xinetd, and
-+ need to limit incoming bursts of virus/spam SMTP connections, since I was
-+ running qmail-scanner to scan incoming and outgoing messages for viruses
-+ and spam.
-+
-+When you make changes, please check that they work as expected.
-+
-+Examples (for tcprules created .cdb)
-+(a) 192.168.:allow,MAXLOAD="1000"
-+ :allow,MAXCONNIP="3"
-+
-+ this would allow any connection from your local LAN (192.168.*.*
-+ addresses) if system load is less than 10.00. non-LAN connections would
-+ be accepted only if clients from that IP address have not already opened
-+ more than 2 connections (as your connection would be last allowed -- 3rd)
-+
-+(b) 192.168.:allow
-+ 5.6.7.8:allow,MAXCONNIP="3"
-+ 1.2.:allow,MAXLOAD="500",MAXCONNIP="1",MAXCONNC="5"
-+ :allow,MAXLOAD="1000",MAXCONNIP="3",DIEMSG="421 example.com unavailable"
-+
-+ if client connects from 192.168.*.* (ex: your LAN), it is allowed.
-+ if it connects from 5.6.7.8 (ex: little abusive customer of yours),
-+ it is allowed unless there are already 3active connections from 5.6.7.8
-+ to this service
-+ if it connects from 1.2.*.* (ex: some problematic networks which caused
-+ you grief in the past) it will connect only if load is less than 5.0,
-+ there is less than 5 active connections from whole C class
-+ (1.2.*.0/24), and if that specific IP address does not already have
-+ connection open.
-+ in all other cases, the client will be permitted to connect if load is
-+ less than 10.00 and client has 2 or less connections open. If load is
-+ higher than 10.00 or there are 3 or more connections open from this
-+ client, the message "421 example.com unavailable" will be returned to
-+ the client and connection terminated.
-+
-+
-+Any bugs introduced are my own, do not bother DJB with them.
-+If you find any, or have neat ideas, or better documentation, or whatever,
-+contact me.
-+
-+the latest version of the patch can be found at:
-+http://linux.voyager.hr/ucspi-tcp/
-+
-+Enjoy,
-+Matija Nalis,
-+mnalis-tcpserver _at_ voyager.hr
-diff -N -ru ucspi-tcp-0.88.orig/tcpserver.c ucspi-tcp-0.88/tcpserver.c
---- ucspi-tcp-0.88.orig/tcpserver.c 2005-08-28 14:34:30.000000000 +0200
-+++ ucspi-tcp-0.88/tcpserver.c 2005-08-28 14:35:00.000000000 +0200
-@@ -1,3 +1,12 @@
-+#ifdef __dietlibc__
-+#define NO_GETLOADAVG
-+#endif
-+
-+#include <stdlib.h>
-+#ifdef NO_GETLOADAVG
-+#include <unistd.h>
-+#endif
-+
- #include <sys/types.h>
- #include <sys/param.h>
- #include <netdb.h>
-@@ -64,6 +73,13 @@
- buffer b;
-
-
-+typedef struct
-+{
-+ char ip[4];
-+ pid_t pid;
-+} baby;
-+
-+baby *child;
-
- /* ---------------------------- child */
-
-@@ -72,6 +88,10 @@
- int flagdeny = 0;
- int flagallownorules = 0;
- char *fnrules = 0;
-+unsigned long maxload = 0;
-+unsigned long maxconnip = 0;
-+unsigned long maxconnc = 0;
-+char *diemsg = "";
-
- void drop_nomem(void)
- {
-@@ -110,6 +130,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 +147,10 @@
- if (data[1 + split] == '=') {
- data[1 + split] = 0;
- env(data + 1,data + 1 + split + 1);
-+ if (str_diff(data+1, "MAXLOAD") == 0) scan_ulong(data+1+split+1,&maxload);
-+ 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, "DIEMSG") == 0) diemsg = data+1+split+1;
- }
- break;
- }
-@@ -210,6 +236,53 @@
- close(fdrules);
- }
- }
-+
-+ unsigned long curload;
-+
-+ if (maxload) {
-+#ifdef NO_GETLOADAVG
-+ int lret;
-+ int i;
-+ unsigned long u1, u2;
-+ char *s;
-+ static stralloc loadavg_data = {0};
-+
-+ lret = openreadclose("/proc/loadavg", &loadavg_data, 10);
-+ if (lret != -1) {
-+ /* /proc/loadavg format is:
-+ 13.08 3.04 1.00 34/170 14190 */
-+ s = loadavg_data.s;
-+ i = scan_ulong (s, &u1); s+=i;
-+ if ((i>0) && (i<5) && (*s == '.')) { /* load should be < 10000 */
-+ i = scan_ulong (s+1,&u2);
-+ if (i==2) { /* we require two decimal places */
-+ curload = u1 * 100 + u2;
-+ if (curload > maxload) flagdeny = 2;
-+ }
-+ }
-+ }
-+#else
-+ double result;
-+ if (getloadavg(&result, 1) == 1) {
-+ curload = result * 100;
-+ if (curload > maxload) flagdeny = 2;
-+ }
-+#endif
-+ }
-+
-+ if (!flagdeny && (maxconnip || maxconnc)) {
-+ unsigned long u, 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 && (cc >= maxconnc)) flagdeny = 4;
-+ if (maxconnip && (c1 >= maxconnip)) flagdeny = 3;
-+ }
-
- if (verbosity >= 2) {
- strnum[fmt_ulong(strnum,getpid())] = 0;
-@@ -223,11 +296,35 @@
- cats(":"); safecats(remoteipstr);
- cats(":"); if (flagremoteinfo) safecats(tcpremoteinfo.s);
- cats(":"); safecats(remoteportstr);
-+ if (flagdeny == 2) {
-+ char curloadstr[FMT_ULONG];
-+ curloadstr[fmt_ulong(curloadstr,curload)] = 0;
-+ cats(" "); safecats ("LOAD"); cats(":"); safecats(curloadstr);
-+ }
-+ 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 (*diemsg) {
-+ buffer_init(&b,write,t,bspace,sizeof bspace);
-+ buffer_puts(&b,diemsg);
-+ if (buffer_putsflush(&b,"\r\n") == -1)
-+ strerr_die2sys(111,DROP,"unable to print diemsg: ");
-+ }
-+ sleep(1);
-+ _exit(100);
-+ }
- }
-
-
-@@ -253,7 +350,6 @@
- _exit(100);
- }
-
--unsigned long limit = 40;
- unsigned long numchildren = 0;
-
- int flag1 = 0;
-@@ -278,6 +374,7 @@
- {
- int wstat;
- int pid;
-+ unsigned long u;
-
- while ((pid = wait_nohang(&wstat)) > 0) {
- if (verbosity >= 2) {
-@@ -286,11 +383,14 @@
- 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 */
- }
- }
-
- main(int argc,char **argv)
- {
-+ pid_t pid;
- char *hostname;
- char *portname;
- int opt;
-@@ -332,6 +432,11 @@
- argc -= optind;
- argv += optind;
-
-+ x = env_get("MAXLOAD"); if (x) scan_ulong(x,&maxload);
-+ x = env_get("MAXCONNIP"); if (x) scan_ulong(x,&maxconnip);
-+ x = env_get("MAXCONNC"); if (x) scan_ulong(x,&maxconnc);
-+ x = env_get("DIEMSG"); if (x) diemsg = x;
-+
- if (!verbosity)
- buffer_2->fd = -1;
-
-@@ -352,6 +457,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 +514,7 @@
- if (t == -1) continue;
- ++numchildren; printstatus();
-
-- switch(fork()) {
-+ switch(pid=fork()) {
- case 0:
- close(s);
- doit(t);
-@@ -420,6 +529,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);
- }