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 && ac->username && !strcmp(ac->username, (char *)param->username))) && ((conf.authcachetype & 2) && !param->username) ||
(!(conf.authcachetype&4) || (ac->password && param->password && !strcmp(ac->password, (char *)param->password))) && ((conf.authcachetype & 4) && !param->password) ||
(!(conf.authcachetype&16) || (ac->acl == param->srv->acl)) (
) { (conf.authcachetype & 1) && *SAFAMILY(&param->sincr) != AF_INET
#ifndef NOIPv6
if(!(conf.authcachetype&1) && *SAFAMILY(&param->sincr) != AF_INET6
|| ((*SAFAMILY(&ac->sa) == *SAFAMILY(&param->sincr) #endif
&& !memcmp(SAADDR(&ac->sa), SAADDR(&param->sincr), SAADDRLEN(&ac->sa))))){ ) || (!hashresolv(&auth_table, param, &ac, &ttl))) {
return 4;
if(conf.authcachetype&32) {
param->sinsl = ac->sinsl;
} }
if(param->username){ if((conf.authcachetype & 1) &&(conf.authcachetype & 8) &&
myfree(param->username); (ac.sincr_family != *SAFAMILY(&param->sincr) ||
} memcmp(ac.sincr_addr, SAADDR(&param->sincr), SAADDRLEN(&param->sincr))
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; 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( if(param->username) strncpy((char *)ac.username, (char *)param->username, 64);
(!(conf.authcachetype&2) || !strcmp(ac->username, (char *)param->username)) && if(*SAFAMILY(&param->sincr) == AF_INET
(!(conf.authcachetype&1) || (*SAFAMILY(&ac->sa) == *SAFAMILY(&param->sincr) && !memcmp(SAADDR(&ac->sa), SAADDR(&param->sincr), SAADDRLEN(&ac->sa)))) && #ifndef NOIPv6
(!(conf.authcachetype&4) || (ac->password && !strcmp(ac->password, (char *)param->password))) && || *SAFAMILY(&param->sincr) == AF_INET6
(!(conf.authcachetype&16) || (ac->acl == param->srv->acl)) #endif
) { ) {
ac->expires = conf.time + conf.authcachetime; ac.sincr_family = *SAFAMILY(&param->sincr);
if(strcmp(ac->username, (char *)param->username)){ memcpy(ac.sincr_addr, SAADDR(&param->sincr), SAADDRLEN(&param->sincr));
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(*SAFAMILY(&param->sinsl) == AF_INET
#ifndef NOIPv6
|| *SAFAMILY(&param->sinsl) == AF_INET6
#endif
) {
ac.sinsl_family = *SAFAMILY(&param->sinsl);
memcpy(ac.sinsl_addr, SAADDR(&param->sinsl), SAADDRLEN(&param->sinsl));
} }
} hashadd(&auth_table, param, &ac, conf.time + conf.authcachetime);
if(!ac){
ac = myalloc(sizeof(struct authcache));
if(ac){
ac->expires = conf.time + conf.authcachetime;
ac->username = param->username?mystrdup((char *)param->username):NULL;
ac->sa = param->sincr;
ac->password = NULL;
if((conf.authcachetype&4) && param->password) ac->password = mystrdup((char *)param->password);
if(conf.authcachetype&32) {
ac->sinsl = param->sinsl;
*SAPORT(&ac->sinsl) = 0;
}
}
ac->next = authc;
authc = ac;
}
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,7 +1806,11 @@ 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)){
if(argc < cm->minargs || (cm->maxargs && argc > cm->maxargs)){
fprintf(stderr, "Command: '%s' wrong number of arguments , line %d\n", argv[0], linenum);
return(linenum);
}
res = (*cm->handler)(argc, argv); res = (*cm->handler)(argc, argv);
if(res > 0){ if(res > 0){
fprintf(stderr, "Command: '%s' failed with code %d, line %d\n", argv[0], res, linenum); fprintf(stderr, "Command: '%s' failed with code %d, line %d\n", argv[0], res, linenum);

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,37 +81,11 @@ int inithashtable(struct hashtable *ht, unsigned nhashsize){
return 0; return 0;
} }
static void hashgrow(struct hashtable *ht){
unsigned newsize = (ht->hashsize + (ht->hashsize >> 1));
unsigned i;
void * newvalues;
if(!ht->tablesize || !ht->hashsize) return;
if(ht->hashsize >= ht->growlimit) return;
if(ht->hashsize / ht->tablesize > 100) return;
if(newsize > ht->growlimit) newsize = ht->growlimit;
newvalues = myrealloc(ht->hashvalues, newsize * (sizeof(struct hashentry) + ht->recsize - 4));
if(!newvalues) return;
memset(ht->hashvalues + (ht->hashsize * (sizeof(struct hashentry) + ht->recsize - 4)), 0, (newsize - ht->hashsize) * (sizeof(struct hashentry) + ht->recsize - 4));
for(i = ht->hashsize + 1; i < newsize; i++) {
hvalue(ht,i)->inext = i+1;
}
hvalue(ht,newsize)->inext = ht->ihashempty;
ht->ihashempty = ht->hashsize + 1;
ht->hashsize = newsize;
}
/*
static void hashcompact(struct hashtable *ht){ static void hashcompact(struct hashtable *ht){
int i; int i;
uint32_t he, *hep; uint32_t he, *hep;
if((conf.time - ht->compacted) < 60 || !ht->tablesize || !ht->hashsize || ht->ihashempty) return; if((conf.time - ht->compacted) < 300 || !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(i = 0; i < ht->tablesize; i++){
for(hep = ht->ihashtable + i; (he = *hep) != 0; ){ for(hep = ht->ihashtable + i; (he = *hep) != 0; ){
if(hvalue(ht,he)->expires < conf.time ) { if(hvalue(ht,he)->expires < conf.time ) {
@ -126,31 +99,47 @@ static void hashcompact(struct hashtable *ht){
} }
ht->compacted = conf.time; ht->compacted = conf.time;
if(ht->ihashempty) return; if(ht->ihashempty) return;
}
} }
*/
static void hashgrow(struct hashtable *ht){
unsigned newsize = (ht->hashsize + (ht->hashsize >> 1));
unsigned i;
void * newvalues;
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(newsize > ht->growlimit) newsize = ht->growlimit;
newvalues = myrealloc(ht->hashvalues, newsize * (sizeof(struct hashentry) + ht->recsize - 4));
if(!newvalues) return;
memset(ht->hashvalues + (ht->hashsize * (sizeof(struct hashentry) + ht->recsize - 4)), 0, (newsize - ht->hashsize) * (sizeof(struct hashentry) + ht->recsize - 4));
for(i = ht->hashsize + 1; i < newsize; i++) {
hvalue(ht,i)->inext = i+1;
}
hvalue(ht,newsize)->inext = ht->ihashempty;
ht->ihashempty = ht->hashsize + 1;
ht->hashsize = newsize;
}
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;
} }
}
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]; hvalue(ht,hen)->inext = ht->ihashtable[index];
ht->ihashtable[index] = hen; 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;