Download | Plain Text | No Line Numbers


  1. diff -Naur ucspi-tcp6-1.00.orig/src/CHANGES.tcpserver-limits-patch ucspi-tcp6-1.00/src/CHANGES.tcpserver-limits-patch
  2. --- ucspi-tcp6-1.00.orig/src/CHANGES.tcpserver-limits-patch 1970-01-01 01:00:00.000000000 +0100
  3. +++ ucspi-tcp6-1.00/src/CHANGES.tcpserver-limits-patch 2014-05-18 16:01:53.991206542 +0200
  4. @@ -0,0 +1,28 @@
  5. +20060126 Added support for "always reject" (setting MAXCONNIP and/or
  6. +MAXCONNC to 0) and fixing a bug when sometimes DIEMSG would not be
  7. +shown (by Mark Powell)
  8. +
  9. +20050903 Added support for Solaris (by Jorge Valdes).
  10. +Moved MAXLOAD code to getprocla().
  11. +Modified documentation a little to accommodate recent changes.
  12. +
  13. +20050130 reinstated /proc/loadavg support for those compiling on Linux
  14. +with dietlibc (see #define NO_GETLOADAVG at top of tcpserver.c).
  15. +Also, we now compile on 64bit platforms (we avoid including unistd.h if
  16. +using getloadavg(3), so we don't conflict with readwrite.h header file)
  17. +Needed if your compile was breaking with:
  18. +readwrite.h:4: error: syntax error before "read"
  19. +readwrite.h:4: warning: data definition has no type or storage class
  20. +SUMMARY: If 20040725 worked for you, there is no reason to upgrade
  21. +(no new features of bugfixes)
  22. +
  23. +20040725 adds a sleep(1) before terminating (to prevent too high load from
  24. +many rapid fork()/exit() calls. It also changes the method for checking
  25. +system load to getloadavg(3) instead of parsing /proc/loadavg, therefore
  26. +making it working on *BSD and other non-Linux systems in addition to Linux.
  27. +It also adds DIEMSG="xxx" support.
  28. +
  29. +20040327 fixes a bug in 20040124 related to MAXLOAD (it would not work
  30. +correctly when load was higher than 10.00)
  31. +
  32. +
  33. diff -Naur ucspi-tcp6-1.00.orig/src/README.tcpserver-limits-patch ucspi-tcp6-1.00/src/README.tcpserver-limits-patch
  34. --- ucspi-tcp6-1.00.orig/src/README.tcpserver-limits-patch 1970-01-01 01:00:00.000000000 +0100
  35. +++ ucspi-tcp6-1.00/src/README.tcpserver-limits-patch 2014-05-18 20:26:06.659001385 +0200
  36. @@ -0,0 +1,122 @@
  37. +See CHANGES.tcpserver-limits-patch for changes summary.
  38. +
  39. +:::COMPILING:::
  40. +
  41. + For MAXLOAD variable to have effect, you have 3 options:
  42. +
  43. +(1) By default the patch assumes that you have working getloadavg(3)
  44. + (most modern UN*Xoids have, including Linux and FreeBSD). No changes
  45. + are needed to standard ucspi-tcp compilation procedures.
  46. +
  47. +(2) If you have a non-Solaris system without getloadavg(3), but with
  48. + readable '/proc/loadavg' (in linux-2.4.x/2.6.x syntax); for example
  49. + if you're compiling on Linux system with dietlibc:
  50. + - conf-cc needs to be modified to include "-DNO_GETLOADAVG"
  51. +
  52. +
  53. +:::USING:::
  54. +
  55. +This patch makes tcpserver from Dr. Erwin Hoffmann's ucspi-tcp6 1.00a package
  56. +(see http://www.fehcom.de/ipnet/ucspi-tcp6.html) to modify its behavior if
  57. +some environment variables are present.
  58. +
  59. +The variables can be preset before starting tcpserver (thus acting as
  60. +default for all connections), or, if you use 'tcpserver -x xxx.cdb', they
  61. +can be set (or overridden) from xxx.cdb. If none of the variables are set,
  62. +tcpserver behaves same as non patched version (except for negligible
  63. +performance loss). Any or all variables can be set, as soon as first limit
  64. +is reached the connection is dropped. I'd recommend using .cdb files
  65. +exclusively though, as you can then modify configuration without killing
  66. +tcpserver.
  67. +
  68. +The variables are:
  69. +
  70. +(1) MAXLOAD
  71. + maximum 1-minute load average * 100. For example, if you have line
  72. + :allow,MAXLOAD="350"
  73. + in your rules file from which you created .cdb, the connection will be
  74. + accepted only if load average is below 3.50
  75. +
  76. + See COMPILING instructions above for info on supported systems.
  77. +
  78. +(2) MAXCONNIP
  79. + maximum connections from one IP address. tcpserver's -c flag defines
  80. + maximum number of allowed connections, but it can be abused if
  81. + just one host goes wild and eats all the connections - no other host
  82. + would be able to connect then. If you created your .cdb with:
  83. + :allow,MAXCONNIP="5"
  84. + and run tcpserver -c 50, then each IP address would be able to have at
  85. + most 5 concurrent connections, while there still could connect 50
  86. + clients total.
  87. + 0 is valid value and means 'always reject'
  88. +
  89. +(3) MAXCONNC
  90. +
  91. + maximum connections from whole /24 (IPv4) or /64 (IPv6). Extension of
  92. + MAXCONNIP, as sometimes the problematic client has a whole farm of
  93. + client machines with different IP addresses instead of just one IP
  94. + address, and they all try to connect. It might have been more useful to
  95. + be able to specify CIDR block than C-class, but I've decided to KISS.
  96. +
  97. + for example tcpserver -c 200, and .cdb with:
  98. + :allow,MAXCONNC="15"
  99. + will allow at most 15 host from any x.y.z.0/24 address block, while
  100. + still allowing up to 200 total connections.
  101. + 0 is valid value and means 'always reject'
  102. +
  103. +(4) DIEMSG
  104. +
  105. + if set and one of the above limits is exceeded, this is the message
  106. + to be sent to client (CRLF is always added to the text) before terminating
  107. + connection. If unset, the connection simply terminates (after 1 sec delay)
  108. + if limit is exceeded.
  109. +
  110. + For example:
  111. + DIEMSG="421 example.com Service temporarily not available, closing
  112. + transmission channel"
  113. +
  114. +Notes:
  115. +
  116. +- if a connection is dropped due to some of those variables set, it will be
  117. + flagged (if you run tcpserver -v) with "LOAD:", "MAXCONNIP:" or
  118. + "MAXCONNC:" at the end of the "tcpserver: deny" line. If that bothers you
  119. + (eg. you have a strict log parsers), don't apply that chunk of the patch.
  120. +
  121. +- the idea for this patch came from my previous experience with xinetd, and
  122. + need to limit incoming bursts of virus/spam SMTP connections, since I was
  123. + running qmail-scanner to scan incoming and outgoing messages for viruses
  124. + and spam.
  125. +
  126. +When you make changes, please check that they work as expected.
  127. +
  128. +Examples (for tcprules created .cdb)
  129. +(a) 192.168.:allow,MAXLOAD="1000"
  130. + :allow,MAXCONNIP="3"
  131. +
  132. + this would allow any connection from your local LAN (192.168.*.*
  133. + addresses) if system load is less than 10.00. non-LAN connections would
  134. + be accepted only if clients from that IP address have not already opened
  135. + more than 2 connections (as your connection would be last allowed -- 3rd)
  136. +
  137. +(b) 192.168.:allow
  138. + 5.6.7.8:allow,MAXCONNIP="3"
  139. + 1.2.:allow,MAXLOAD="500",MAXCONNIP="1",MAXCONNC="5"
  140. + :allow,MAXLOAD="1000",MAXCONNIP="3",DIEMSG="421 example.com unavailable"
  141. +
  142. + if client connects from 192.168.*.* (ex: your LAN), it is allowed.
  143. + if it connects from 5.6.7.8 (ex: little abusive customer of yours),
  144. + it is allowed unless there are already 3active connections from 5.6.7.8
  145. + to this service
  146. + if it connects from 1.2.*.* (ex: some problematic networks which caused
  147. + you grief in the past) it will connect only if load is less than 5.0,
  148. + there is less than 5 active connections from whole C class
  149. + (1.2.*.0/24), and if that specific IP address does not already have
  150. + connection open.
  151. + in all other cases, the client will be permitted to connect if load is
  152. + less than 10.00 and client has 2 or less connections open. If load is
  153. + higher than 10.00 or there are 3 or more connections open from this
  154. + client, the message "421 example.com unavailable" will be returned to
  155. + the client and connection terminated.
  156. +
  157. +
  158. +The origin patch is from Matija Nalis, http://linux.voyager.hr/ucspi-tcp/
  159. diff -Naur ucspi-tcp6-1.00.orig/src/tcpserver.c ucspi-tcp6-1.00/src/tcpserver.c
  160. --- ucspi-tcp6-1.00.orig/src/tcpserver.c 2014-01-08 13:05:43.000000000 +0100
  161. +++ ucspi-tcp6-1.00/src/tcpserver.c 2014-05-18 20:29:12.437079915 +0200
  162. @@ -1,3 +1,11 @@
  163. +#ifdef __dietlibc__
  164. +#define NO_GETLOADAVG
  165. +#endif
  166. +
  167. +#include <stdlib.h>
  168. +#ifdef NO_GETLOADAVG
  169. +#include <unistd.h>
  170. +#endif
  171. #include <sys/types.h>
  172. #include <unistd.h>
  173. #include <sys/param.h>
  174. @@ -64,11 +72,20 @@
  175. static stralloc tmp;
  176. static stralloc fqdn;
  177. static stralloc addresses;
  178. +static stralloc diemsg_buf;
  179. +
  180. +#define loaddouble(la) ((double)(la) / FSCALE)
  181.  
  182. char bspace[16];
  183. buffer b;
  184.  
  185. +typedef struct
  186. +{
  187. + char ip[16];
  188. + pid_t pid;
  189. +} baby;
  190.  
  191. +baby *child;
  192.  
  193. /* ---------------------------- child */
  194.  
  195. @@ -77,6 +94,10 @@
  196. int flagdeny = 0;
  197. int flagallownorules = 0;
  198. char *fnrules = 0;
  199. +unsigned long maxload = 0;
  200. +long maxconnip = -1;
  201. +long maxconnc = -1;
  202. +char *diemsg = "";
  203.  
  204. void drop_nomem(void)
  205. {
  206. @@ -114,6 +135,8 @@
  207. strerr_die4sys(111,DROP,"unable to read ",fnrules,": ");
  208. }
  209.  
  210. +unsigned long limit = 40;
  211. +
  212. void found(char *data,unsigned int datalen)
  213. {
  214. unsigned int next0;
  215. @@ -129,6 +152,14 @@
  216. if (data[1 + split] == '=') {
  217. data[1 + split] = 0;
  218. env(data + 1,data + 1 + split + 1);
  219. + if (str_diff(data+1, "MAXLOAD") == 0) scan_ulong(data+1+split+1,&maxload);
  220. + if (str_diff(data+1, "MAXCONNIP") == 0) scan_ulong(data+1+split+1,&maxconnip);
  221. + if (str_diff(data+1, "MAXCONNC") == 0) scan_ulong(data+1+split+1,&maxconnc);
  222. + if (str_diff(data+1, "DIEMSG") == 0) {
  223. + if (!stralloc_copys(&diemsg_buf,data+1+split+1)) drop_nomem();
  224. + if (!stralloc_0(&diemsg_buf)) drop_nomem();
  225. + diemsg = diemsg_buf.s;
  226. + }
  227. }
  228. break;
  229. }
  230. @@ -137,6 +168,37 @@
  231. }
  232. }
  233.  
  234. +unsigned long getprocla(void)
  235. +{
  236. +#ifdef NO_GETLOADAVG
  237. + int lret;
  238. + int i;
  239. + unsigned long u1, u2;
  240. + char *s;
  241. + static stralloc loadavg_data = {0};
  242. +
  243. + lret = openreadclose("/proc/loadavg", &loadavg_data, 10);
  244. + if (lret != -1) {
  245. + /* /proc/loadavg format is:
  246. + * 13.08 3.04 1.00 34/170 14190 */
  247. + s = loadavg_data.s;
  248. + i = scan_ulong (s, &u1); s+=i;
  249. + if ((i>0) && (i<5) && (*s == '.')) { /* load should be < 10000 */
  250. + i = scan_ulong (s+1,&u2);
  251. + if (i==2) { /* we require two decimal places */
  252. + return (u1 * 100 + u2);
  253. + }
  254. + return (u1 * 100);
  255. + }
  256. + }
  257. +#else
  258. + double result;
  259. + if (getloadavg(&result, 1) == 1) {
  260. + return (result * 100);
  261. + }
  262. +#endif
  263. +}
  264. +
  265. void doit(int t)
  266. {
  267. int mappedv4 = 0;
  268. @@ -254,6 +316,29 @@
  269. }
  270. }
  271.  
  272. + unsigned long curload = 0;
  273. + if (maxload) {
  274. + curload = getprocla();
  275. + if (curload > maxload) flagdeny = 2;
  276. + }
  277. +
  278. + if (!flagdeny && (maxconnip != -1 || maxconnc != -1)) {
  279. + unsigned long u;
  280. + long c1=0, cc=0;
  281. + for (u=0; u < limit; u++)
  282. + if (child[u].pid != 0) {
  283. + if (ipv4 || mappedv4 || ip6_isv4mapped(remoteip)) {
  284. + if (byte_equal(child[u].ip, 15, remoteip)) cc++;
  285. + }
  286. + else {
  287. + if (byte_equal(child[u].ip, 8, remoteip)) cc++;
  288. + }
  289. + if (byte_equal(child[u].ip, 16, remoteip)) c1++;
  290. + }
  291. + if (maxconnc != -1 && (cc >= maxconnc)) flagdeny = 4;
  292. + if (maxconnip != -1 && (c1 >= maxconnip)) flagdeny = 3;
  293. + }
  294. +
  295. if (verbosity >= 2) {
  296. strnum[fmt_ulong(strnum,getpid())] = 0;
  297. if (!stralloc_copys(&tmp,"tcpserver: ")) drop_nomem();
  298. @@ -266,11 +351,35 @@
  299. cats(":"); safecats(stripaddr);
  300. cats(":"); if (flagremoteinfo) safecats(tcpremoteinfo.s);
  301. cats(":"); safecats(remoteportstr);
  302. + if (flagdeny == 2) {
  303. + char curloadstr[FMT_ULONG];
  304. + curloadstr[fmt_ulong(curloadstr,curload)] = 0;
  305. + cats(" "); safecats ("LOAD"); cats(":"); safecats(curloadstr);
  306. + }
  307. + if (flagdeny == 3) {
  308. + char maxconstr[FMT_ULONG];
  309. + maxconstr[fmt_ulong(maxconstr,maxconnip)] = 0;
  310. + cats(" "); safecats ("MAXCONNIP"); cats(":"); safecats(maxconstr);
  311. + }
  312. + if (flagdeny == 4) {
  313. + char maxconstr[FMT_ULONG];
  314. + maxconstr[fmt_ulong(maxconstr,maxconnc)] = 0;
  315. + cats(" "); safecats ("MAXCONNC"); cats(":"); safecats(maxconstr);
  316. + }
  317. cats("\n");
  318. buffer_putflush(buffer_2,tmp.s,tmp.len);
  319. }
  320.  
  321. - if (flagdeny) _exit(100);
  322. + if (flagdeny) {
  323. + if (*diemsg) {
  324. + buffer_init(&b,write,t,bspace,sizeof bspace);
  325. + buffer_puts(&b,diemsg);
  326. + if (buffer_putsflush(&b,"\r\n") == -1)
  327. + strerr_die2sys(111,DROP,"unable to print diemsg: ");
  328. + }
  329. + sleep(1);
  330. + _exit(100);
  331. + }
  332. }
  333.  
  334.  
  335. @@ -297,7 +406,6 @@
  336. _exit(100);
  337. }
  338.  
  339. -unsigned long limit = 40;
  340. unsigned long numchildren = 0;
  341.  
  342. int flag1 = 0;
  343. @@ -322,6 +430,7 @@
  344. {
  345. int wstat;
  346. int pid;
  347. + unsigned long u;
  348.  
  349. while ((pid = wait_nohang(&wstat)) > 0) {
  350. if (verbosity >= 2) {
  351. @@ -330,11 +439,14 @@
  352. strerr_warn4("tcpserver: end ",strnum," status ",strnum2,0);
  353. }
  354. if (numchildren) --numchildren; printstatus();
  355. + for (u=0; u < limit; u++) if (child[u].pid == pid) { child[u].pid = 0; break; }
  356. + if (u == limit) strerr_die1x(111,"tcpserver: ERROR: dead child not found?!"); /* never happens */
  357. }
  358. }
  359.  
  360. int main(int argc,char **argv)
  361. {
  362. + pid_t pid;
  363. char *hostname;
  364. int opt;
  365. struct servent *se;
  366. @@ -377,6 +489,10 @@
  367. }
  368. argc -= optind;
  369. argv += optind;
  370. + x = env_get("MAXLOAD"); if (x) scan_ulong(x,&maxload);
  371. + x = env_get("MAXCONNIP"); if (x) scan_ulong(x,&maxconnip);
  372. + x = env_get("MAXCONNC"); if (x) scan_ulong(x,&maxconnc);
  373. + x = env_get("DIEMSG"); if (x) diemsg = x;
  374.  
  375. if (!verbosity)
  376. buffer_2->fd = -1;
  377. @@ -397,6 +513,10 @@
  378. }
  379.  
  380. if (!*argv) usage();
  381. +
  382. + child = calloc(sizeof(baby),limit);
  383. + if (!child)
  384. + strerr_die2x(111,FATAL,"out of memory for MAXCONNIP tracking");
  385.  
  386. sig_block(sig_child);
  387. sig_catch(sig_child,sigchld);
  388. @@ -459,7 +579,7 @@
  389. if (t == -1) continue;
  390. ++numchildren; printstatus();
  391.  
  392. - switch(fork()) {
  393. + switch(pid=fork()) {
  394. case 0:
  395. close(s);
  396. doit(t);
  397. @@ -474,6 +594,16 @@
  398. case -1:
  399. strerr_warn2(DROP,"unable to fork: ",&strerr_sys);
  400. --numchildren; printstatus();
  401. + break;
  402. + default:
  403. + for (u=0; u < limit; u++)
  404. + if (child[u].pid == 0) {
  405. + byte_copy(child[u].ip,16,remoteip);
  406. + child[u].pid = pid;
  407. + break;
  408. + }
  409. + if (u == limit)
  410. + strerr_die1x(111,"tcpserver: ERROR: no empty space for new child?!"); /* never happens */
  411. }
  412. close(t);
  413. }
  414.