Download | Plain Text | Line Numbers
diff -Naur ucspi-tcp6-1.00.orig/src/CHANGES.tcpserver-limits-patch ucspi-tcp6-1.00/src/CHANGES.tcpserver-limits-patch
--- ucspi-tcp6-1.00.orig/src/CHANGES.tcpserver-limits-patch 1970-01-01 01:00:00.000000000 +0100
+++ ucspi-tcp6-1.00/src/CHANGES.tcpserver-limits-patch 2014-05-18 16:01:53.991206542 +0200
@@ -0,0 +1,28 @@
+20060126 Added support for "always reject" (setting MAXCONNIP and/or
+MAXCONNC to 0) and fixing a bug when sometimes DIEMSG would not be
+shown (by Mark Powell)
+
+20050903 Added support for Solaris (by Jorge Valdes).
+Moved MAXLOAD code to getprocla().
+Modified documentation a little to accommodate recent changes.
+
+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)
+
+
diff -Naur ucspi-tcp6-1.00.orig/src/README.tcpserver-limits-patch ucspi-tcp6-1.00/src/README.tcpserver-limits-patch
--- ucspi-tcp6-1.00.orig/src/README.tcpserver-limits-patch 1970-01-01 01:00:00.000000000 +0100
+++ ucspi-tcp6-1.00/src/README.tcpserver-limits-patch 2014-05-18 20:26:06.659001385 +0200
@@ -0,0 +1,122 @@
+See CHANGES.tcpserver-limits-patch for changes summary.
+
+:::COMPILING:::
+
+ For MAXLOAD variable to have effect, you have 3 options:
+
+(1) By default the patch assumes that you have working getloadavg(3)
+ (most modern UN*Xoids have, including Linux and FreeBSD). No changes
+ are needed to standard ucspi-tcp compilation procedures.
+
+(2) If you have a non-Solaris system without getloadavg(3), but with
+ readable '/proc/loadavg' (in linux-2.4.x/2.6.x syntax); for example
+ if you're compiling on Linux system with dietlibc:
+ - conf-cc needs to be modified to include "-DNO_GETLOADAVG"
+
+
+:::USING:::
+
+This patch makes tcpserver from Dr. Erwin Hoffmann's ucspi-tcp6 1.00a package
+(see http://www.fehcom.de/ipnet/ucspi-tcp6.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
+
+ See COMPILING instructions above for info on supported systems.
+
+(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.
+ 0 is valid value and means 'always reject'
+
+(3) MAXCONNC
+
+ maximum connections from whole /24 (IPv4) or /64 (IPv6). 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.
+ 0 is valid value and means 'always reject'
+
+(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.
+
+
+The origin patch is from Matija Nalis, http://linux.voyager.hr/ucspi-tcp/
diff -Naur ucspi-tcp6-1.00.orig/src/tcpserver.c ucspi-tcp6-1.00/src/tcpserver.c
--- ucspi-tcp6-1.00.orig/src/tcpserver.c 2014-01-08 13:05:43.000000000 +0100
+++ ucspi-tcp6-1.00/src/tcpserver.c 2014-05-18 20:29:12.437079915 +0200
@@ -1,3 +1,11 @@
+#ifdef __dietlibc__
+#define NO_GETLOADAVG
+#endif
+
+#include <stdlib.h>
+#ifdef NO_GETLOADAVG
+#include <unistd.h>
+#endif
#include <sys/types.h>
#include <unistd.h>
#include <sys/param.h>
@@ -64,11 +72,20 @@
static stralloc tmp;
static stralloc fqdn;
static stralloc addresses;
+static stralloc diemsg_buf;
+
+#define loaddouble(la) ((double)(la) / FSCALE)
char bspace[16];
buffer b;
+typedef struct
+{
+ char ip[16];
+ pid_t pid;
+} baby;
+baby *child;
/* ---------------------------- child */
@@ -77,6 +94,10 @@
int flagdeny = 0;
int flagallownorules = 0;
char *fnrules = 0;
+unsigned long maxload = 0;
+long maxconnip = -1;
+long maxconnc = -1;
+char *diemsg = "";
void drop_nomem(void)
{
@@ -114,6 +135,8 @@
strerr_die4sys(111,DROP,"unable to read ",fnrules,": ");
}
+unsigned long limit = 40;
+
void found(char *data,unsigned int datalen)
{
unsigned int next0;
@@ -129,6 +152,14 @@
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) {
+ if (!stralloc_copys(&diemsg_buf,data+1+split+1)) drop_nomem();
+ if (!stralloc_0(&diemsg_buf)) drop_nomem();
+ diemsg = diemsg_buf.s;
+ }
}
break;
}
@@ -137,6 +168,37 @@
}
}
+unsigned long getprocla(void)
+{
+#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 */
+ return (u1 * 100 + u2);
+ }
+ return (u1 * 100);
+ }
+ }
+#else
+ double result;
+ if (getloadavg(&result, 1) == 1) {
+ return (result * 100);
+ }
+#endif
+}
+
void doit(int t)
{
int mappedv4 = 0;
@@ -254,6 +316,29 @@
}
}
+ unsigned long curload = 0;
+ if (maxload) {
+ curload = getprocla();
+ if (curload > maxload) flagdeny = 2;
+ }
+
+ 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 (ipv4 || mappedv4 || ip6_isv4mapped(remoteip)) {
+ if (byte_equal(child[u].ip, 15, remoteip)) cc++;
+ }
+ else {
+ if (byte_equal(child[u].ip, 8, remoteip)) cc++;
+ }
+ if (byte_equal(child[u].ip, 16, remoteip)) 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();
@@ -266,11 +351,35 @@
cats(":"); safecats(stripaddr);
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);
+ }
}
@@ -297,7 +406,6 @@
_exit(100);
}
-unsigned long limit = 40;
unsigned long numchildren = 0;
int flag1 = 0;
@@ -322,6 +430,7 @@
{
int wstat;
int pid;
+ unsigned long u;
while ((pid = wait_nohang(&wstat)) > 0) {
if (verbosity >= 2) {
@@ -330,11 +439,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 */
}
}
int main(int argc,char **argv)
{
+ pid_t pid;
char *hostname;
int opt;
struct servent *se;
@@ -377,6 +489,10 @@
}
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;
@@ -397,6 +513,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);
@@ -459,7 +579,7 @@
if (t == -1) continue;
++numchildren; printstatus();
- switch(fork()) {
+ switch(pid=fork()) {
case 0:
close(s);
doit(t);
@@ -474,6 +594,16 @@
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,16,remoteip);
+ child[u].pid = pid;
+ break;
+ }
+ if (u == limit)
+ strerr_die1x(111,"tcpserver: ERROR: no empty space for new child?!"); /* never happens */
}
close(t);
}