authcache switched to hashtables, overflow fixed

- authcache switched to use hashtables, size parameter added
- overflow fixed on hashinit
- hashtable prefers new values on insert if table is full
- hashtable is able to compact/grow
This commit is contained in:
Vladimir Dubrovin 2026-04-19 19:16:33 +03:00
parent a3729354b8
commit 7102afe856
6 changed files with 159 additions and 159 deletions

View File

@ -623,9 +623,10 @@ shared ones.
.br .br
.B authcache .B authcache
<cachtype> <cachtime> <cachtype> <cachtime> <cachesize>
.br .br
Cache authentication information for a given amount of time (cachetime) in seconds. Cache authentication information for a given amount of time (cachetime) in seconds.
cachesize limits number of cache entries.
Cachetype is one of: Cachetype is one of:
.br .br
ip - after successful authentication all connections during caching time ip - after successful authentication all connections during caching time

View File

@ -7,6 +7,7 @@
*/ */
#include "proxy.h" #include "proxy.h"
#include "libs/blake2.h"
static FILTER_ACTION (*ext_ssl_parent)(struct clientparam * param) = NULL; static FILTER_ACTION (*ext_ssl_parent)(struct clientparam * param) = NULL;
@ -776,73 +777,71 @@ int checkACL(struct clientparam * param){
} }
struct authcache { struct authcache {
char * username; unsigned char username[64];
char * password; #ifndef NOIPv6
time_t expires; uint8_t sincr_addr[16];
PROXYSOCKADDRTYPE sa; uint8_t sinsl_addr[16];
PROXYSOCKADDRTYPE sinsl; #else
struct ace *acl; uint8_t sincr_addr[4];
struct authcache *next; uint8_t sinsl_addr[4];
} *authc = NULL; #endif
uint16_t sincr_family;
uint16_t sinsl_family;
};
void param2hash(const void *index, uint8_t *hash, const unsigned char *rnd){
blake2b_state S;
const struct clientparam *param = (struct clientparam *)index;
blake2b_init_key(&S, HASH_SIZE, rnd, sizeof(unsigned)*4);
if((conf.authcachetype & 1) && !(conf.authcachetype & 8))blake2b_update(&S, SAADDR(&param->sincr), SAADDRLEN(&param->sincr));
if((conf.authcachetype & 2) && param->username)blake2b_update(&S, param->username, strlen((const char *)param->username));
if((conf.authcachetype & 4) && param->password)blake2b_update(&S, param->password, strlen((const char *)param->password));
if((conf.authcachetype & 16))blake2b_update(&S, &param->srv->acl, sizeof(param->srv->acl));
blake2b_final(&S, hash, HASH_SIZE);
}
struct hashtable auth_table = {0, sizeof(struct authcache), {0,0,0,0}, NULL, NULL, 0, param2hash};
int cacheauth(struct clientparam * param){ int cacheauth(struct clientparam * param){
struct authcache *ac, *last=NULL; struct authcache ac;
uint32_t ttl;
pthread_mutex_lock(&hash_mutex);
for(ac = authc; ac; ){
if(ac->expires <= conf.time){
if(ac->username)myfree(ac->username);
if(ac->password)myfree(ac->password);
if(!last){
authc = ac->next;
myfree(ac);
ac = authc;
}
else {
last->next = ac->next;
myfree(ac);
ac = last->next;
}
continue;
} if(
if( ((conf.authcachetype & 2) && !param->username) ||
(!(conf.authcachetype&2) || (param->username && ac->username && !strcmp(ac->username, (char *)param->username))) && ((conf.authcachetype & 4) && !param->password) ||
(!(conf.authcachetype&4) || (ac->password && param->password && !strcmp(ac->password, (char *)param->password))) && (
(!(conf.authcachetype&16) || (ac->acl == param->srv->acl)) (conf.authcachetype & 1) && *SAFAMILY(&param->sincr) != AF_INET
) { #ifndef NOIPv6
&& *SAFAMILY(&param->sincr) != AF_INET6
if(!(conf.authcachetype&1) #endif
|| ((*SAFAMILY(&ac->sa) == *SAFAMILY(&param->sincr) ) || (!hashresolv(&auth_table, param, &ac, &ttl))) {
&& !memcmp(SAADDR(&ac->sa), SAADDR(&param->sincr), SAADDRLEN(&ac->sa))))){ return 4;
}
if(conf.authcachetype&32) { if((conf.authcachetype & 1) &&(conf.authcachetype & 8) &&
param->sinsl = ac->sinsl; (ac.sincr_family != *SAFAMILY(&param->sincr) ||
} memcmp(ac.sincr_addr, SAADDR(&param->sincr), SAADDRLEN(&param->sincr))
if(param->username){ )) {
myfree(param->username); return 10;
}
param->username = (unsigned char *)mystrdup(ac->username);
pthread_mutex_unlock(&hash_mutex);
return 0;
}
else if ((conf.authcachetype&1) && (conf.authcachetype&8)) {
pthread_mutex_unlock(&hash_mutex);
return 10;
}
}
last = ac;
ac = ac->next;
} }
pthread_mutex_unlock(&hash_mutex); if(!(conf.authcachetype&2) && *ac.username){
return 4; if(param->username) myfree(param->username);
param->username = (unsigned char *)mystrdup((char *)ac.username);
}
if((conf.authcachetype & 32)){
memset(&param->sinsl, 0, sizeof(param->sinsl));
*(SAFAMILY(&param->sinsl)) = ac.sinsl_family;
memcpy(SAADDR(&param->sinsl), ac.sinsl_addr, SAADDRLEN(&param->sinsl));
}
return 0;
} }
int doauth(struct clientparam * param){ int doauth(struct clientparam * param){
int res = 0; int res = 0;
struct auth *authfuncs; struct auth *authfuncs;
struct authcache *ac;
char * tmp; char * tmp;
int ret = 0; int ret = 0;
@ -853,51 +852,27 @@ int doauth(struct clientparam * param){
(res = (*authfuncs->authorize)(param))) (res = (*authfuncs->authorize)(param)))
return res; return res;
if(conf.authcachetype && authfuncs->authenticate && authfuncs->authenticate != cacheauth && param->username && (!(conf.authcachetype&4) || (!param->pwtype && param->password))){ if(conf.authcachetype && authfuncs->authenticate && authfuncs->authenticate != cacheauth && param->username && (!(conf.authcachetype&4) || (!param->pwtype && param->password))){
pthread_mutex_lock(&hash_mutex); struct authcache ac={};
for(ac = authc; ac; ac = ac->next){
if(
(!(conf.authcachetype&2) || !strcmp(ac->username, (char *)param->username)) &&
(!(conf.authcachetype&1) || (*SAFAMILY(&ac->sa) == *SAFAMILY(&param->sincr) && !memcmp(SAADDR(&ac->sa), SAADDR(&param->sincr), SAADDRLEN(&ac->sa)))) &&
(!(conf.authcachetype&4) || (ac->password && !strcmp(ac->password, (char *)param->password))) &&
(!(conf.authcachetype&16) || (ac->acl == param->srv->acl))
) {
ac->expires = conf.time + conf.authcachetime;
if(strcmp(ac->username, (char *)param->username)){
tmp = ac->username;
ac->username = mystrdup((char *)param->username);
myfree(tmp);
}
if((conf.authcachetype&4)){
tmp = ac->password;
ac->password = mystrdup((char *)param->password);
myfree(tmp);
}
ac->sa = param->sincr;
if(conf.authcachetype&32) {
ac->sinsl = param-> sinsl;
*SAPORT(&ac->sinsl) = 0;
}
break; if(param->username) strncpy((char *)ac.username, (char *)param->username, 64);
} if(*SAFAMILY(&param->sincr) == AF_INET
} #ifndef NOIPv6
if(!ac){ || *SAFAMILY(&param->sincr) == AF_INET6
ac = myalloc(sizeof(struct authcache)); #endif
if(ac){ ) {
ac->expires = conf.time + conf.authcachetime; ac.sincr_family = *SAFAMILY(&param->sincr);
ac->username = param->username?mystrdup((char *)param->username):NULL; memcpy(ac.sincr_addr, SAADDR(&param->sincr), SAADDRLEN(&param->sincr));
ac->sa = param->sincr; }
ac->password = NULL;
if((conf.authcachetype&4) && param->password) ac->password = mystrdup((char *)param->password); if(*SAFAMILY(&param->sinsl) == AF_INET
if(conf.authcachetype&32) { #ifndef NOIPv6
ac->sinsl = param->sinsl; || *SAFAMILY(&param->sinsl) == AF_INET6
*SAPORT(&ac->sinsl) = 0; #endif
} ) {
} ac.sinsl_family = *SAFAMILY(&param->sinsl);
ac->next = authc; memcpy(ac.sinsl_addr, SAADDR(&param->sinsl), SAADDRLEN(&param->sinsl));
authc = ac; }
} hashadd(&auth_table, param, &ac, conf.time + conf.authcachetime);
pthread_mutex_unlock(&hash_mutex);
} }
break; break;
} }

View File

@ -1430,6 +1430,7 @@ static int h_radius(int argc, unsigned char **argv){
#endif #endif
static int h_authcache(int argc, unsigned char **argv){ static int h_authcache(int argc, unsigned char **argv){
conf.authcachetype = 0; conf.authcachetype = 0;
int authcachesize;
if(strstr((char *) *(argv + 1), "ip")) conf.authcachetype |= 1; if(strstr((char *) *(argv + 1), "ip")) conf.authcachetype |= 1;
if(strstr((char *) *(argv + 1), "user")) conf.authcachetype |= 2; if(strstr((char *) *(argv + 1), "user")) conf.authcachetype |= 2;
if(strstr((char *) *(argv + 1), "pass")) conf.authcachetype |= 4; if(strstr((char *) *(argv + 1), "pass")) conf.authcachetype |= 4;
@ -1437,8 +1438,14 @@ static int h_authcache(int argc, unsigned char **argv){
if(strstr((char *) *(argv + 1), "acl")) conf.authcachetype |= 16; if(strstr((char *) *(argv + 1), "acl")) conf.authcachetype |= 16;
if(strstr((char *) *(argv + 1), "ext")) conf.authcachetype |= 32; if(strstr((char *) *(argv + 1), "ext")) conf.authcachetype |= 32;
if(argc > 2) conf.authcachetime = (unsigned) atoi((char *) *(argv + 2)); if(argc > 2) conf.authcachetime = (unsigned) atoi((char *) *(argv + 2));
if(argc > 3) authcachesize = (unsigned) atoi((char *) *(argv + 2));
if(!conf.authcachetype) conf.authcachetype = 6; if(!conf.authcachetype) conf.authcachetype = 6;
if(!conf.authcachetime) conf.authcachetime = 600; if(!conf.authcachetime) conf.authcachetime = 600;
if(inithashtable(&auth_table, authcachesize? authcachesize : 4096)){
fprintf(stderr, "Failed to initialize auth cache\n");
return 2;
}
if(!authcachesize)auth_table.growlimit = 65536*4;
return 0; return 0;
} }
@ -1645,7 +1652,7 @@ struct commands commandhandlers[]={
{commandhandlers+53, "filtermaxsize", h_filtermaxsize, 2, 2}, {commandhandlers+53, "filtermaxsize", h_filtermaxsize, 2, 2},
{commandhandlers+54, "nolog", h_nolog, 1, 1}, {commandhandlers+54, "nolog", h_nolog, 1, 1},
{commandhandlers+55, "weight", h_nolog, 2, 2}, {commandhandlers+55, "weight", h_nolog, 2, 2},
{commandhandlers+56, "authcache", h_authcache, 2, 3}, {commandhandlers+56, "authcache", h_authcache, 2, 4},
{commandhandlers+57, "smtpp", h_proxy, 1, 0}, {commandhandlers+57, "smtpp", h_proxy, 1, 0},
{commandhandlers+58, "delimchar",h_delimchar, 2, 2}, {commandhandlers+58, "delimchar",h_delimchar, 2, 2},
{commandhandlers+59, "authnserver", h_authnserver, 2, 2}, {commandhandlers+59, "authnserver", h_authnserver, 2, 2},
@ -1799,13 +1806,17 @@ int readconfig(FILE * fp){
res = 1; res = 1;
for(cm = commandhandlers; cm; cm = cm->next){ for(cm = commandhandlers; cm; cm = cm->next){
if(!strcmp((char *)argv[0], (char *)cm->command) && argc >= cm->minargs && (!cm->maxargs || argc <= cm->maxargs)){ if(!strcmp((char *)argv[0], (char *)cm->command)){
res = (*cm->handler)(argc, argv); if(argc < cm->minargs || (cm->maxargs && argc > cm->maxargs)){
if(res > 0){ fprintf(stderr, "Command: '%s' wrong number of arguments , line %d\n", argv[0], linenum);
fprintf(stderr, "Command: '%s' failed with code %d, line %d\n", argv[0], res, linenum); return(linenum);
return(linenum); }
} res = (*cm->handler)(argc, argv);
if(!res) break; if(res > 0){
fprintf(stderr, "Command: '%s' failed with code %d, line %d\n", argv[0], res, linenum);
return(linenum);
}
if(!res) break;
} }
} }
if(res != 1)continue; if(res != 1)continue;

View File

@ -28,7 +28,6 @@ int inithashtable(struct hashtable *ht, unsigned nhashsize){
unsigned tablesize, hashsize; unsigned tablesize, hashsize;
clock_t c; clock_t c;
#ifdef _WIN32 #ifdef _WIN32
struct timeb tb; struct timeb tb;
@ -71,7 +70,7 @@ int inithashtable(struct hashtable *ht, unsigned nhashsize){
ht->rnd[1] = myrand(ht->ihashtable, sizeof(ht->ihashtable)); ht->rnd[1] = myrand(ht->ihashtable, sizeof(ht->ihashtable));
ht->rnd[2] = myrand(&c, sizeof(c)); ht->rnd[2] = myrand(&c, sizeof(c));
ht->rnd[3] = myrand(ht->hashvalues,sizeof(ht->hashvalues)); ht->rnd[3] = myrand(ht->hashvalues,sizeof(ht->hashvalues));
memset(ht->ihashtable, 0, ht->tablesize * sizeof(struct hashentry *)); memset(ht->ihashtable, 0, ht->tablesize * sizeof(uint32_t));
memset(ht->hashvalues, 0, ht->hashsize * (sizeof(struct hashentry) + ht->recsize - 4)); memset(ht->hashvalues, 0, ht->hashsize * (sizeof(struct hashentry) + ht->recsize - 4));
for(i = 1; i < ht->hashsize; i++) { for(i = 1; i < ht->hashsize; i++) {
@ -82,14 +81,35 @@ int inithashtable(struct hashtable *ht, unsigned nhashsize){
return 0; return 0;
} }
static void hashcompact(struct hashtable *ht){
int i;
uint32_t he, *hep;
if((conf.time - ht->compacted) < 300 || !ht->tablesize || !ht->hashsize || ht->ihashempty) return;
for(i = 0; i < ht->tablesize; i++){
for(hep = ht->ihashtable + i; (he = *hep) != 0; ){
if(hvalue(ht,he)->expires < conf.time ) {
(*hep) = hvalue(ht,he)->inext;
hvalue(ht,he)->expires = 0;
hvalue(ht,he)->inext = ht->ihashempty;
ht->ihashempty = he;
}
else hep=&(hvalue(ht,he)->inext);
}
}
ht->compacted = conf.time;
if(ht->ihashempty) return;
}
static void hashgrow(struct hashtable *ht){ static void hashgrow(struct hashtable *ht){
unsigned newsize = (ht->hashsize + (ht->hashsize >> 1)); unsigned newsize = (ht->hashsize + (ht->hashsize >> 1));
unsigned i; unsigned i;
void * newvalues; void * newvalues;
if(!ht->tablesize || !ht->hashsize) return; if(!ht->tablesize || !ht->hashsize) return;
if(ht->hashsize / ht->tablesize < 4) hashcompact(ht);
if(ht->ihashempty) return;
if(ht->hashsize >= ht->growlimit) return; if(ht->hashsize >= ht->growlimit) return;
if(ht->hashsize / ht->tablesize > 100) return;
if(newsize > ht->growlimit) newsize = ht->growlimit; if(newsize > ht->growlimit) newsize = ht->growlimit;
newvalues = myrealloc(ht->hashvalues, newsize * (sizeof(struct hashentry) + ht->recsize - 4)); newvalues = myrealloc(ht->hashvalues, newsize * (sizeof(struct hashentry) + ht->recsize - 4));
if(!newvalues) return; if(!newvalues) return;
@ -102,55 +122,24 @@ static void hashgrow(struct hashtable *ht){
ht->hashsize = newsize; ht->hashsize = newsize;
} }
/*
static void hashcompact(struct hashtable *ht){
int i;
uint32_t he, *hep;
if((conf.time - ht->compacted) < 60 || !ht->tablesize || !ht->hashsize || ht->ihashempty) return;
if(ht->grow && ht->hashsize/ht->tablesize < 100){
hashgrow(ht);
if(ht->ihashempty) return;
}
if(ht->hashsize/ht->tablesize < 4){
for(i = 0; i < ht->tablesize; i++){
for(hep = ht->ihashtable + i; (he = *hep) != 0; ){
if(hvalue(ht,he)->expires < conf.time ) {
(*hep) = hvalue(ht,he)->inext;
hvalue(ht,he)->expires = 0;
hvalue(ht,he)->inext = ht->ihashempty;
ht->ihashempty = he;
}
else hep=&(hvalue(ht,he)->inext);
}
}
ht->compacted = conf.time;
if(ht->ihashempty) return;
}
}
*/
void hashadd(struct hashtable *ht, const void* name, const void* value, time_t expires){ void hashadd(struct hashtable *ht, const void* name, const void* value, time_t expires){
uint32_t hen, he; uint32_t hen, he;
uint32_t *hep; uint32_t *hep;
int overwrite = 0;
uint8_t hash[HASH_SIZE];
uint32_t index; uint32_t index;
uint32_t last = 0;
pthread_mutex_lock(&hash_mutex); pthread_mutex_lock(&hash_mutex);
if(!ht->ihashempty){ if(!ht||!value||!name||!ht->ihashtable) {
hashgrow(ht);
}
if(!ht||!value||!name||!ht->ihashtable||!ht->ihashempty) {
pthread_mutex_unlock(&hash_mutex); pthread_mutex_unlock(&hash_mutex);
return; return;
} }
hen = ht->ihashempty;
ht->ihashempty = hvalue(ht,ht->ihashempty)->inext; ht->index2hash(name, hash, (unsigned char *)ht->rnd);
ht->index2hash(name, hvalue(ht,hen)->hash, (unsigned char *)ht->rnd); index = hashindex(ht, hash);
memcpy(hvalue(ht,hen)->value, value, ht->recsize);
hvalue(ht,hen)->expires = expires;
hvalue(ht,hen)->inext = 0;
index = hashindex(ht, hvalue(ht,hen)->hash);
for(hep = ht->ihashtable + index; (he = *hep)!=0; ){ for(hep = ht->ihashtable + index; (he = *hep)!=0; ){
if(hvalue(ht,he)->expires < conf.time || !memcmp(hvalue(ht,hen)->hash, hvalue(ht,he)->hash, HASH_SIZE)) { if(hvalue(ht,he)->expires < conf.time || !memcmp(hvalue(ht,hen)->hash, hvalue(ht,he)->hash, HASH_SIZE)) {
@ -159,10 +148,31 @@ void hashadd(struct hashtable *ht, const void* name, const void* value, time_t e
hvalue(ht,he)->inext = ht->ihashempty; hvalue(ht,he)->inext = ht->ihashempty;
ht->ihashempty = he; ht->ihashempty = he;
} }
else hep=&(hvalue(ht,he)->inext); else {
hep=&(hvalue(ht,he)->inext);
last = he;
}
} }
hvalue(ht,hen)->inext = ht->ihashtable[index];
ht->ihashtable[index] = hen; if(!ht->ihashempty){
hashgrow(ht);
}
if(ht->ihashempty){
hen = ht->ihashempty;
ht->ihashempty = hvalue(ht,ht->ihashempty)->inext;
hvalue(ht,hen)->inext = ht->ihashtable[index];
ht->ihashtable[index] = hen;
}
else {
hen = last;
}
if(hen){
memcpy(hvalue(ht,hen)->hash, hash, HASH_SIZE);
memcpy(hvalue(ht,hen)->value, value, ht->recsize);
hvalue(ht,hen)->expires = expires;
}
pthread_mutex_unlock(&hash_mutex); pthread_mutex_unlock(&hash_mutex);
} }

View File

@ -31,7 +31,7 @@ char * proxy_stringtable[] = {
"Content-type: text/html; charset=utf-8\r\n" "Content-type: text/html; charset=utf-8\r\n"
"\r\n" "\r\n"
"<html><head><title>503 Service Unavailable</title></head>\r\n" "<html><head><title>503 Service Unavailable</title></head>\r\n"
"<body><h2>503 Service Unavailable</h2><h3>You have exceeded your traffic limit</h3></body></html>\r\n", "<body><h2>503 Service Unavailable</h2><h3>You have exceeded your limits</h3></body></html>\r\n",
/* 3 */ "HTTP/1.0 503 Service Unavailable\r\n" /* 3 */ "HTTP/1.0 503 Service Unavailable\r\n"
"Connection: close\r\n" "Connection: close\r\n"

View File

@ -773,10 +773,13 @@ struct hashtable {
void (*index2hash)(const void *index, unsigned char *hash, const unsigned char *rnd); void (*index2hash)(const void *index, unsigned char *hash, const unsigned char *rnd);
unsigned growlimit; unsigned growlimit;
int tablesize; int tablesize;
time_t compacted;
}; };
extern struct hashtable dns_table; extern struct hashtable dns_table;
extern struct hashtable dns6_table; extern struct hashtable dns6_table;
extern struct hashtable auth_table;
struct pluginlink { struct pluginlink {
struct symbol symbols; struct symbol symbols;
struct extparam *conf; struct extparam *conf;