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
.B authcache
<cachtype> <cachtime>
<cachtype> <cachtime> <cachesize>
.br
Cache authentication information for a given amount of time (cachetime) in seconds.
cachesize limits number of cache entries.
Cachetype is one of:
.br
ip - after successful authentication all connections during caching time

View File

@ -7,6 +7,7 @@
*/
#include "proxy.h"
#include "libs/blake2.h"
static FILTER_ACTION (*ext_ssl_parent)(struct clientparam * param) = NULL;
@ -776,73 +777,71 @@ int checkACL(struct clientparam * param){
}
struct authcache {
char * username;
char * password;
time_t expires;
PROXYSOCKADDRTYPE sa;
PROXYSOCKADDRTYPE sinsl;
struct ace *acl;
struct authcache *next;
} *authc = NULL;
unsigned char username[64];
#ifndef NOIPv6
uint8_t sincr_addr[16];
uint8_t sinsl_addr[16];
#else
uint8_t sincr_addr[4];
uint8_t sinsl_addr[4];
#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){
struct authcache *ac, *last=NULL;
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(
(!(conf.authcachetype&2) || (param->username && ac->username && !strcmp(ac->username, (char *)param->username))) &&
(!(conf.authcachetype&4) || (ac->password && param->password && !strcmp(ac->password, (char *)param->password))) &&
(!(conf.authcachetype&16) || (ac->acl == param->srv->acl))
) {
if(!(conf.authcachetype&1)
|| ((*SAFAMILY(&ac->sa) == *SAFAMILY(&param->sincr)
&& !memcmp(SAADDR(&ac->sa), SAADDR(&param->sincr), SAADDRLEN(&ac->sa))))){
if(conf.authcachetype&32) {
param->sinsl = ac->sinsl;
}
if(param->username){
myfree(param->username);
}
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;
struct authcache ac;
uint32_t ttl;
if(
((conf.authcachetype & 2) && !param->username) ||
((conf.authcachetype & 4) && !param->password) ||
(
(conf.authcachetype & 1) && *SAFAMILY(&param->sincr) != AF_INET
#ifndef NOIPv6
&& *SAFAMILY(&param->sincr) != AF_INET6
#endif
) || (!hashresolv(&auth_table, param, &ac, &ttl))) {
return 4;
}
pthread_mutex_unlock(&hash_mutex);
return 4;
if((conf.authcachetype & 1) &&(conf.authcachetype & 8) &&
(ac.sincr_family != *SAFAMILY(&param->sincr) ||
memcmp(ac.sincr_addr, SAADDR(&param->sincr), SAADDRLEN(&param->sincr))
)) {
return 10;
}
if(!(conf.authcachetype&2) && *ac.username){
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 res = 0;
struct auth *authfuncs;
struct authcache *ac;
char * tmp;
int ret = 0;
@ -853,51 +852,27 @@ int doauth(struct clientparam * param){
(res = (*authfuncs->authorize)(param)))
return res;
if(conf.authcachetype && authfuncs->authenticate && authfuncs->authenticate != cacheauth && param->username && (!(conf.authcachetype&4) || (!param->pwtype && param->password))){
pthread_mutex_lock(&hash_mutex);
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(!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);
struct authcache ac={};
if(param->username) strncpy((char *)ac.username, (char *)param->username, 64);
if(*SAFAMILY(&param->sincr) == AF_INET
#ifndef NOIPv6
|| *SAFAMILY(&param->sincr) == AF_INET6
#endif
) {
ac.sincr_family = *SAFAMILY(&param->sincr);
memcpy(ac.sincr_addr, SAADDR(&param->sincr), SAADDRLEN(&param->sincr));
}
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);
}
break;
}

View File

@ -1430,6 +1430,7 @@ static int h_radius(int argc, unsigned char **argv){
#endif
static int h_authcache(int argc, unsigned char **argv){
conf.authcachetype = 0;
int authcachesize;
if(strstr((char *) *(argv + 1), "ip")) conf.authcachetype |= 1;
if(strstr((char *) *(argv + 1), "user")) conf.authcachetype |= 2;
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), "ext")) conf.authcachetype |= 32;
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.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;
}
@ -1645,7 +1652,7 @@ struct commands commandhandlers[]={
{commandhandlers+53, "filtermaxsize", h_filtermaxsize, 2, 2},
{commandhandlers+54, "nolog", h_nolog, 1, 1},
{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+58, "delimchar",h_delimchar, 2, 2},
{commandhandlers+59, "authnserver", h_authnserver, 2, 2},
@ -1799,13 +1806,17 @@ int readconfig(FILE * fp){
res = 1;
for(cm = commandhandlers; cm; cm = cm->next){
if(!strcmp((char *)argv[0], (char *)cm->command) && argc >= cm->minargs && (!cm->maxargs || argc <= cm->maxargs)){
res = (*cm->handler)(argc, argv);
if(res > 0){
fprintf(stderr, "Command: '%s' failed with code %d, line %d\n", argv[0], res, linenum);
return(linenum);
}
if(!res) break;
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);
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;

View File

@ -28,7 +28,6 @@ int inithashtable(struct hashtable *ht, unsigned nhashsize){
unsigned tablesize, hashsize;
clock_t c;
#ifdef _WIN32
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[2] = myrand(&c, sizeof(c));
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));
for(i = 1; i < ht->hashsize; i++) {
@ -82,14 +81,35 @@ int inithashtable(struct hashtable *ht, unsigned nhashsize){
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){
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(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;
@ -102,55 +122,24 @@ static void hashgrow(struct hashtable *ht){
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){
uint32_t hen, he;
uint32_t *hep;
int overwrite = 0;
uint8_t hash[HASH_SIZE];
uint32_t index;
uint32_t last = 0;
pthread_mutex_lock(&hash_mutex);
if(!ht->ihashempty){
hashgrow(ht);
}
if(!ht||!value||!name||!ht->ihashtable||!ht->ihashempty) {
if(!ht||!value||!name||!ht->ihashtable) {
pthread_mutex_unlock(&hash_mutex);
return;
}
hen = ht->ihashempty;
ht->ihashempty = hvalue(ht,ht->ihashempty)->inext;
ht->index2hash(name, hvalue(ht,hen)->hash, (unsigned char *)ht->rnd);
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);
ht->index2hash(name, hash, (unsigned char *)ht->rnd);
index = hashindex(ht, hash);
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)) {
@ -159,10 +148,31 @@ void hashadd(struct hashtable *ht, const void* name, const void* value, time_t e
hvalue(ht,he)->inext = ht->ihashempty;
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);
}

View File

@ -31,7 +31,7 @@ char * proxy_stringtable[] = {
"Content-type: text/html; charset=utf-8\r\n"
"\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"
"Connection: close\r\n"

View File

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