From ff91a6fe7237bf216db2c90741a45e7925aed829 Mon Sep 17 00:00:00 2001 From: z3APA3A <3APA3A@3proxy.ru> Date: Fri, 12 Jan 2018 19:09:42 +0300 Subject: [PATCH] connlim / noconnlim commands added to support connection / connectio rate limits --- man/3proxy.cfg.3 | 25 +++++++++++++++++ src/3proxy.c | 1 + src/auth.c | 69 +++++++++++++++++++++++++++++++++++++++++---- src/common.c | 1 + src/conf.c | 73 ++++++++++++++++++++++++++++++++++++++---------- src/proxy.h | 6 ++++ src/proxymain.c | 1 + src/structures.h | 15 ++++++++-- 8 files changed, 168 insertions(+), 23 deletions(-) diff --git a/man/3proxy.cfg.3 b/man/3proxy.cfg.3 index cdc213c..bf17dd8 100644 --- a/man/3proxy.cfg.3 +++ b/man/3proxy.cfg.3 @@ -795,6 +795,31 @@ if you want, for example, to limit all speed ecept access to POP3 you can use .br before the rest of bandlim rules. +.br +.B connlim + +.br +.B noconnlim + +.br +connlim sets connections rate limit per time period for traffic +pattern controlled by ACL. Period is in seconds. If period is 0, +connlim limits a number of parallel connections. +.br + connlim 100 60 * 127.0.0.1 +.br +allows 100 connections per minute for 127.0.0.1. +.br + connlim 20 0 * 127.0.0.1 +.br +allows 20 simulationeous connections for 127.0.0.1. +.br +Like with bandlimin, if individual limit is required per client, separate +rule mustbe added for every client. Like with nobanlimin, noconnlim adds an +exception. + + + .br .B counter diff --git a/src/3proxy.c b/src/3proxy.c index c46156b..5c89b8b 100644 --- a/src/3proxy.c +++ b/src/3proxy.c @@ -515,6 +515,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int pthread_mutex_init(&config_mutex, NULL); pthread_mutex_init(&bandlim_mutex, NULL); + pthread_mutex_init(&connlim_mutex, NULL); pthread_mutex_init(&hash_mutex, NULL); pthread_mutex_init(&tc_mutex, NULL); pthread_mutex_init(&pwl_mutex, NULL); diff --git a/src/auth.c b/src/auth.c index 80cde38..7a46da5 100644 --- a/src/auth.c +++ b/src/auth.c @@ -437,6 +437,60 @@ int ACLmatches(struct ace* acentry, struct clientparam * param){ return 1; } + +int startconnlims (struct clientparam *param){ + struct connlim * ce; + time_t delta; + uint64_t rating; + int ret = 0; + + pthread_mutex_lock(&connlim_mutex); + for(ce = conf.connlimiter; ce; ce = ce->next) { + if(ACLmatches(ce->ace, param)){ + if(ce->ace->action == NOCONNLIM)break; + if(!ce->period){ + if(ce->rate <= ce->rating) { + ret = 1; + break; + } + ce->rating++; + continue; + } + delta = conf.time - ce->basetime; + if(ce->period <= delta || ce->basetime > conf.time){ + ce->basetime = conf.time; + ce->rating = 0x100000; + continue; + } + rating = delta? ((ce->rating * (ce->period - delta)) / ce->period) + 0x100000 : ce->rating + 0x100000; + if (rating > (ce->rate<<20)) { + ret = 2; + break; + } + ce->rating = rating; + ce->basetime = conf.time; + } + } + pthread_mutex_unlock(&connlim_mutex); + return ret; +} + +void stopconnlims (struct clientparam *param){ + struct connlim * ce; + + pthread_mutex_lock(&connlim_mutex); + for(ce = conf.connlimiter; ce; ce = ce->next) { + if(ACLmatches(ce->ace, param)){ + if(ce->ace->action == NOCONNLIM)break; + if(!ce->period && ce->rating){ + ce->rating--; + continue; + } + } + } + pthread_mutex_unlock(&connlim_mutex); +} + static void initbandlims (struct clientparam *param){ struct bandlim * be; int i; @@ -464,7 +518,7 @@ static void initbandlims (struct clientparam *param){ unsigned bandlimitfunc(struct clientparam *param, unsigned nbytesin, unsigned nbytesout){ unsigned sleeptime = 0, nsleeptime; - unsigned long sec; + time_t sec; unsigned msec; unsigned now; int i; @@ -504,7 +558,7 @@ unsigned bandlimitfunc(struct clientparam *param, unsigned nbytesin, unsigned nb param->bandlims[i]->nexttime = 0; continue; } - now = ((sec - param->bandlims[i]->basetime) * 1000000) + msec; + now = (unsigned)((sec - param->bandlims[i]->basetime) * 1000000) + msec; nsleeptime = (param->bandlims[i]->nexttime > now)? param->bandlims[i]->nexttime - now : 0; sleeptime = (nsleeptime > sleeptime)? nsleeptime : sleeptime; @@ -521,7 +575,7 @@ unsigned bandlimitfunc(struct clientparam *param, unsigned nbytesin, unsigned nb param->bandlimsout[i]->nexttime = 0; continue; } - now = ((sec - param->bandlimsout[i]->basetime) * 1000000) + msec; + now = (unsigned)((sec - param->bandlimsout[i]->basetime) * 1000000) + msec; nsleeptime = (param->bandlimsout[i]->nexttime > now)? param->bandlimsout[i]->nexttime - now : 0; sleeptime = (nsleeptime > sleeptime)? nsleeptime : sleeptime; @@ -571,6 +625,8 @@ int alwaysauth(struct clientparam * param){ struct trafcount * tc; int countout = 0; + + if(conf.connlimiter && param->remsock == INVALID_SOCKET && startconnlims(param)) return 95; res = doconnect(param); if(!res){ initbandlims(param); @@ -610,7 +666,7 @@ int checkACL(struct clientparam * param){ struct ace* acentry; if(!param->srv->acl) { - return alwaysauth(param); + return 0; } for(acentry = param->srv->acl; acentry; acentry = acentry->next) { if(ACLmatches(acentry, param)) { @@ -868,11 +924,12 @@ struct auth authfuncs[] = { {authfuncs+5, strongauth, checkACL, "strong"}, {authfuncs+6, cacheauth, checkACL, "cache"}, #ifndef NORADIUS +#define AUTHOFFSET 1 {authfuncs+7, radauth, checkACL, "radius"}, - {authfuncs+8, NULL, NULL, "none"}, #else - {authfuncs+7, NULL, NULL, "none"}, +#define AUTHOFFSET 0 #endif + {authfuncs+7+AUTHOFFSET, NULL, NULL, "none"}, {NULL, NULL, NULL, ""} }; diff --git a/src/common.c b/src/common.c index 6511543..dd33020 100644 --- a/src/common.c +++ b/src/common.c @@ -71,6 +71,7 @@ struct extparam conf = { NULL, NULL, NULL, NULL, + NULL, #ifdef __FreeBSD__ 8192, #else diff --git a/src/conf.c b/src/conf.c index 55fd01b..5d37975 100644 --- a/src/conf.c +++ b/src/conf.c @@ -19,6 +19,7 @@ #endif pthread_mutex_t bandlim_mutex; +pthread_mutex_t connlim_mutex; pthread_mutex_t tc_mutex; pthread_mutex_t pwl_mutex; pthread_mutex_t hash_mutex; @@ -1093,6 +1094,7 @@ static int h_ace(int argc, unsigned char **argv){ struct ace *acl = NULL; struct bandlim * nbl; struct trafcount * tl; + struct connlim * ncl; if(!strcmp((char *)argv[0], "allow")){ res = ALLOW; @@ -1125,6 +1127,13 @@ static int h_ace(int argc, unsigned char **argv){ else if(!strcmp((char *)argv[0], "nocountout")){ res = NOCOUNTOUT; } + else if(!strcmp((char *)argv[0], "connlim")){ + res = CONNLIM; + offset = 2; + } + else if(!strcmp((char *)argv[0], "noconnlim")){ + res = NOCONNLIM; + } acl = make_ace(argc - (offset+1), argv + (offset + 1)); if(!acl) { fprintf(stderr, "Unable to parse ACL entry, line %d\n", linenum); @@ -1155,6 +1164,32 @@ static int h_ace(int argc, unsigned char **argv){ acei->next = acl; } break; + case CONNLIM: + case NOCONNLIM: + ncl = myalloc(sizeof(struct connlim)); + if(!ncl) { + fprintf(stderr, "No memory to create connection limit filter\n"); + return(3); + } + memset(ncl, 0, sizeof(struct connlim)); + ncl->ace = acl; + if(acl->action == CONNLIM) { + sscanf((char *)argv[1], "%u", &ncl->rate); + sscanf((char *)argv[2], "%u", &ncl->period); + } + pthread_mutex_lock(&connlim_mutex); + if(!conf.connlimiter){ + conf.connlimiter = ncl; + } + else { + struct connlim * cli; + + for(cli = conf.connlimiter; cli->next; cli = cli->next); + cli->next = ncl; + } + pthread_mutex_unlock(&connlim_mutex); + break; + case BANDLIM: case NOBANDLIM: @@ -1460,22 +1495,24 @@ struct commands commandhandlers[]={ {commandhandlers+44, "nocountin", h_ace, 1, 0}, {commandhandlers+45, "countout", h_ace, 4, 0}, {commandhandlers+46, "nocountout", h_ace, 1, 0}, - {commandhandlers+47, "plugin", h_plugin, 3, 0}, - {commandhandlers+48, "logdump", h_logdump, 2, 3}, - {commandhandlers+49, "filtermaxsize", h_filtermaxsize, 2, 2}, - {commandhandlers+50, "nolog", h_nolog, 1, 1}, - {commandhandlers+51, "weight", h_nolog, 2, 2}, - {commandhandlers+52, "authcache", h_authcache, 2, 3}, - {commandhandlers+53, "smtpp", h_proxy, 1, 0}, - {commandhandlers+54, "icqpr", h_proxy, 4, 0}, - {commandhandlers+55, "msnpr", h_proxy, 4, 0}, - {commandhandlers+56, "delimchar",h_delimchar, 2, 2}, - {commandhandlers+57, "authnserver", h_authnserver, 2, 2}, - {commandhandlers+58, "stacksize", h_stacksize, 2, 2}, - {commandhandlers+59, "force", h_force, 1, 1}, - {commandhandlers+60, "noforce", h_noforce, 1, 1}, + {commandhandlers+47, "connlim", h_ace, 4, 0}, + {commandhandlers+48, "noconnlim", h_ace, 1, 0}, + {commandhandlers+49, "plugin", h_plugin, 3, 0}, + {commandhandlers+50, "logdump", h_logdump, 2, 3}, + {commandhandlers+51, "filtermaxsize", h_filtermaxsize, 2, 2}, + {commandhandlers+52, "nolog", h_nolog, 1, 1}, + {commandhandlers+53, "weight", h_nolog, 2, 2}, + {commandhandlers+54, "authcache", h_authcache, 2, 3}, + {commandhandlers+55, "smtpp", h_proxy, 1, 0}, + {commandhandlers+56, "icqpr", h_proxy, 4, 0}, + {commandhandlers+57, "msnpr", h_proxy, 4, 0}, + {commandhandlers+58, "delimchar",h_delimchar, 2, 2}, + {commandhandlers+59, "authnserver", h_authnserver, 2, 2}, + {commandhandlers+60, "stacksize", h_stacksize, 2, 2}, + {commandhandlers+51, "force", h_force, 1, 1}, + {commandhandlers+62, "noforce", h_noforce, 1, 1}, #ifndef NORADIUS - {commandhandlers+61, "radius", h_radius, 3, 0}, + {commandhandlers+63, "radius", h_radius, 3, 0}, #endif {specificcommands, "", h_noop, 1, 0} }; @@ -1644,6 +1681,7 @@ void freepwl(struct passwords *pwl){ void freeconf(struct extparam *confp){ struct bandlim * bl; struct bandlim * blout; + struct connlim * cl; struct trafcount * tc; struct passwords *pw; struct ace *acl; @@ -1674,6 +1712,10 @@ void freeconf(struct extparam *confp){ confp->bandlimiterout = NULL; confp->bandlimfunc = NULL; pthread_mutex_unlock(&bandlim_mutex); + pthread_mutex_lock(&connlim_mutex); + cl = confp->connlimiter; + confp->connlimiter = NULL; + pthread_mutex_unlock(&connlim_mutex); pthread_mutex_lock(&pwl_mutex); pw = confp->pwl; @@ -1731,6 +1773,7 @@ void freeconf(struct extparam *confp){ freepwl(pw); for(; bl; bl = (struct bandlim *) itfree(bl, bl->next)) freeacl(bl->ace); for(; blout; blout = (struct bandlim *) itfree(blout, blout->next))freeacl(blout->ace); + for(; cl; cl = (struct connlim *) itfree(cl, cl->next)) freeacl(cl->ace); if(counterd != -1) { close(counterd); diff --git a/src/proxy.h b/src/proxy.h index 039de9b..37155d0 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -38,6 +38,8 @@ #define NOCOUNTIN 6 #define COUNTOUT 7 #define NOCOUNTOUT 8 +#define CONNLIM 9 +#define NOCONNLIM 10 #define UDPBUFSIZE 16384 #define TCPBUFSIZE 8192 @@ -206,6 +208,9 @@ void freeparam(struct clientparam * param); void clearstat(struct clientparam * param); void dumpcounters(struct trafcount *tl, int counterd); +int startconnlims (struct clientparam *param); +void stopconnlims (struct clientparam *param); + extern struct auth authfuncs[]; @@ -319,6 +324,7 @@ struct property; extern unsigned char tmpbuf[8192]; extern pthread_mutex_t config_mutex; extern pthread_mutex_t bandlim_mutex; +extern pthread_mutex_t connlim_mutex; extern pthread_mutex_t hash_mutex; extern pthread_mutex_t tc_mutex; extern pthread_mutex_t pwl_mutex; diff --git a/src/proxymain.c b/src/proxymain.c index 80dd0d4..0a009d5 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -961,6 +961,7 @@ void freeparam(struct clientparam * param) { } myfree(param->filters); } + if(conf.connlimiter && (param->res != 95 || param->remsock != INVALID_SOCKET)) stopconnlims(param); #endif if(param->clibuf) myfree(param->clibuf); if(param->srvbuf) myfree(param->srvbuf); diff --git a/src/structures.h b/src/structures.h index 89b83cc..3238253 100644 --- a/src/structures.h +++ b/src/structures.h @@ -299,11 +299,21 @@ struct ace { struct bandlim { struct bandlim *next; struct ace *ace; - unsigned basetime; - unsigned rate; + time_t basetime; unsigned nexttime; + unsigned rate; }; +struct connlim { + struct connlim *next; + struct ace *ace; + time_t basetime; + uint64_t rating; + unsigned period; + unsigned rate; +}; + + typedef enum {NONE, MINUTELY, HOURLY, DAILY, WEEKLY, MONTHLY, ANNUALLY, NEVER} ROTATION; struct schedule { @@ -528,6 +538,7 @@ struct extparam { struct ace * acl; char * conffile; struct bandlim * bandlimiter, *bandlimiterout; + struct connlim * connlimiter; struct trafcount * trafcounter; struct srvparam *services; int stacksize,