mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-17 08:55:49 +08:00
A reload reads the file into a new configuration, allocated from the defaults, and prepares its services without starting them. Only if the whole file is read without an error does it come into force: the old configuration's services stop accepting, the new ones start. On an error everything prepared is dropped and the old configuration, which never stopped, stays in force. Each service holds its configuration until it is freed, after its last client; a configuration no longer in force is released when the last holder lets go (confhold/confput). freeconf() now only releases what a configuration holds, touching nothing global. What runs without a service of its own reads the configuration in force, runconf, rather than conf, which a reload points at the one being read: the resolver, which holds it while it waits (a client resolves with its own service's), the log, the thread pool, the admin pages, which walk the services under config_mutex, and the signal handlers. The users table, havelog and the resolver choice are per configuration now, the resolver applied when the configuration comes into force, so a refused file leaves the users as they were. The log file, the configuration file name, the string table, daemon mode and the filter list belong to the process and are handed from one configuration to the next. Settings left out of a new file go back to their defaults, where before they kept the old values. The gap between the old and the new listening sockets remains: a connection queued on an old service's socket when it closes is reset. Handing the socket over is the next phase. A test case reloads a broken file (nothing changes, alice still gets in, bob from the refused file does not) and a good one (bob only, the service on its new port, the old one closed, a download through the old service finishing whole). 40 reloads, a third refused, under load show no memory errors under AddressSanitizer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
313 lines
8.5 KiB
C
313 lines
8.5 KiB
C
/*
|
|
3APA3A simplest proxy server
|
|
(c) 2002-2026 by Vladimir Dubrovin <vlad@3proxy.org>
|
|
|
|
please read License Agreement
|
|
|
|
*/
|
|
|
|
#include "proxy.h"
|
|
#include "mdhash.h"
|
|
|
|
void initbandlims(struct clientparam *param);
|
|
|
|
int alwaysauth(struct clientparam * param){
|
|
int res;
|
|
struct trafcount * tc;
|
|
int countout = 0;
|
|
|
|
|
|
if(param->srv->config->connlimiter && !param->connlim && startconnlims(param)) return 10;
|
|
#ifdef WITH_HTTPSRV
|
|
/* The http server answers the request itself, so authorization must not
|
|
try to reach a destination that does not exist. A request it has handed
|
|
to another child does have one, and that child needs it opened. */
|
|
res = (param->srv->service == S_HTTPSRV && !param->onerequest)? 0 : doconnect(param);
|
|
#else
|
|
res = doconnect(param);
|
|
#endif
|
|
if(!res){
|
|
if(param->srv->config->bandlimfunc && (param->srv->config->bandlimiter||param->srv->config->bandlimiterout)){
|
|
_3proxy_mutex_lock(&bandlim_mutex);
|
|
initbandlims(param);
|
|
_3proxy_mutex_unlock(&bandlim_mutex);
|
|
}
|
|
|
|
if(param->srv->config->trafcountfunc && param->srv->config->trafcounter) {
|
|
_3proxy_mutex_lock(&tc_mutex);
|
|
for(tc = param->srv->config->trafcounter; tc; tc = tc->next) {
|
|
if(tc->disabled) continue;
|
|
if(ACLmatches(tc->ace, param)){
|
|
if(tc->ace->action == NOCOUNTIN) {
|
|
countout = 1;
|
|
break;
|
|
}
|
|
if(tc->ace->action == NOCOUNTALL) break;
|
|
if(tc->ace->action != COUNTIN) {
|
|
countout = 1;
|
|
if(tc->ace->action != COUNTALL) continue;
|
|
}
|
|
if(tc->traflim64 <= tc->v->traf64) {
|
|
_3proxy_mutex_unlock(&tc_mutex);
|
|
return 10;
|
|
}
|
|
param->trafcountfunc = param->srv->config->trafcountfunc;
|
|
param->maxtrafin64 = tc->traflim64 - tc->v->traf64;
|
|
}
|
|
}
|
|
if(countout)for(tc = param->srv->config->trafcounter; tc; tc = tc->next) {
|
|
if(tc->disabled) continue;
|
|
if(ACLmatches(tc->ace, param)){
|
|
if(tc->ace->action == NOCOUNTOUT || tc->ace->action == NOCOUNTALL) break;
|
|
if(tc->ace->action != COUNTOUT && tc->ace->action != COUNTALL) {
|
|
continue;
|
|
}
|
|
if(tc->traflim64 <= tc->v->traf64) {
|
|
_3proxy_mutex_unlock(&tc_mutex);
|
|
return 10;
|
|
}
|
|
param->trafcountfunc = param->srv->config->trafcountfunc;
|
|
param->maxtrafout64 = tc->traflim64 - tc->v->traf64;
|
|
}
|
|
}
|
|
_3proxy_mutex_unlock(&tc_mutex);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int cacheauth(struct clientparam * param){
|
|
struct authcache ac;
|
|
uint32_t ttl;
|
|
unsigned type = param->srv->authcachetype;
|
|
|
|
|
|
if(
|
|
((type & 2) && !*param->username) ||
|
|
((type & 4) && !*param->password) ||
|
|
(
|
|
(type & 1) && *SAFAMILY(¶m->sincr) != AF_INET
|
|
#ifndef NOIPV6
|
|
&& *SAFAMILY(¶m->sincr) != AF_INET6
|
|
#endif
|
|
) || (!hashresolv(&auth_table, param, &ac, &ttl))) {
|
|
return 4;
|
|
}
|
|
if((type & 1) &&(type & 8) &&
|
|
(ac.sincr_family != *SAFAMILY(¶m->sincr) ||
|
|
memcmp(ac.sincr_addr, SAADDR(¶m->sincr), SAADDRLEN(¶m->sincr))
|
|
)) {
|
|
return 10;
|
|
}
|
|
|
|
if(!(type&2) && *ac.username && PARAMSTR(param, username, ac.username)) return 4;
|
|
if((type & 32)){
|
|
memset(¶m->sinsl, 0, sizeof(param->sinsl));
|
|
*(SAFAMILY(¶m->sinsl)) = ac.sinsl_family;
|
|
memcpy(SAADDR(¶m->sinsl), ac.sinsl_addr, SAADDRLEN(¶m->sinsl));
|
|
}
|
|
if(param->preauth == 1 && param->srv->acl) param->dstindep = 0;
|
|
return 0;
|
|
}
|
|
|
|
int doauth(struct clientparam * param){
|
|
int res = 0;
|
|
struct auth *authfuncs;
|
|
int ret = 0;
|
|
|
|
if(param->preauth == 1 && !param->srv->acl) param->dstindep = 1;
|
|
for(authfuncs=param->srv->authfuncs; authfuncs; authfuncs=authfuncs->next){
|
|
res = authfuncs->authenticate?(*authfuncs->authenticate)(param):0;
|
|
if(!res) {
|
|
if(authfuncs->authorize &&
|
|
(res = (*authfuncs->authorize)(param)))
|
|
return res;
|
|
if(param->srv->authcachetype && authfuncs->authenticate && authfuncs->authenticate != cacheauth && *param->username && (!(param->srv->authcachetype&4) || (!param->pwtype && *param->password))){
|
|
struct authcache ac={.username=""};
|
|
|
|
strncpy((char *)ac.username, (char *)param->username, 64);
|
|
ac.username[63] = 0;
|
|
if(*SAFAMILY(¶m->sincr) == AF_INET
|
|
#ifndef NOIPV6
|
|
|| *SAFAMILY(¶m->sincr) == AF_INET6
|
|
#endif
|
|
) {
|
|
ac.sincr_family = *SAFAMILY(¶m->sincr);
|
|
memcpy(ac.sincr_addr, SAADDR(¶m->sincr), SAADDRLEN(¶m->sincr));
|
|
}
|
|
|
|
if(*SAFAMILY(¶m->sinsl) == AF_INET
|
|
#ifndef NOIPV6
|
|
|| *SAFAMILY(¶m->sinsl) == AF_INET6
|
|
#endif
|
|
) {
|
|
ac.sinsl_family = *SAFAMILY(¶m->sinsl);
|
|
memcpy(ac.sinsl_addr, SAADDR(¶m->sinsl), SAADDRLEN(¶m->sinsl));
|
|
}
|
|
hashadd(&auth_table, param, &ac, curtime + param->srv->authcachetime);
|
|
}
|
|
break;
|
|
}
|
|
if(res > ret) ret = res;
|
|
if(ret > 9) return ret;
|
|
}
|
|
if(!res){
|
|
ret = (param->preauth == 2)? 0 : alwaysauth(param);
|
|
if (param->nafterauthfilters){
|
|
FILTER_ACTION action;
|
|
|
|
action = handleafterauthflt(param);
|
|
if(action != PASS) return 19;
|
|
}
|
|
}
|
|
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int ipauth(struct clientparam * param){
|
|
int res;
|
|
unsigned char c;
|
|
|
|
/* the rules are matched as if no name had been given */
|
|
c = *param->username;
|
|
*param->username = 0;
|
|
res = checkACL(param);
|
|
*param->username = c;
|
|
return res;
|
|
}
|
|
|
|
int userauth(struct clientparam * param){
|
|
return (*param->username)? 0:4;
|
|
}
|
|
|
|
int dnsauth(struct clientparam * param){
|
|
char buf[128];
|
|
char addr[16];
|
|
char dig[]="0123456789abcdef";
|
|
|
|
unsigned u;
|
|
int i;
|
|
|
|
if(*SAFAMILY(¶m->sincr)!=AF_INET){
|
|
char *s = buf;
|
|
for(i=15; i>=0; i--){
|
|
unsigned char c=((unsigned char *)SAADDR(¶m->sincr))[i];
|
|
*s++ = dig[(c&0xf)];
|
|
*s++ = '.';
|
|
*s++ = dig[(c>>4)];
|
|
*s++ = '.';
|
|
}
|
|
sprintf(s, "ip6.arpa");
|
|
}
|
|
else {
|
|
u = ntohl(*(uint32_t *)SAADDR(¶m->sincr));
|
|
|
|
sprintf(buf, "%u.%u.%u.%u.in-addr.arpa",
|
|
((u&0x000000FF)),
|
|
((u&0x0000FF00)>>8),
|
|
((u&0x00FF0000)>>16),
|
|
((u&0xFF000000)>>24));
|
|
|
|
}
|
|
if(!udpresolve(*SAFAMILY(¶m->sincr), (unsigned char *)buf, (unsigned char *)addr, NULL, param, 1)) {
|
|
return 3;
|
|
}
|
|
if(memcmp(SAADDR(¶m->sincr), addr, SAADDRLEN(¶m->sincr))) {
|
|
return 3;
|
|
}
|
|
|
|
return *param->username? 0:3;
|
|
}
|
|
|
|
static int ctmemcmp(const void *a, const void *b, size_t len){
|
|
const unsigned char *pa = (const unsigned char *)a, *pb = (const unsigned char *)b;
|
|
unsigned char diff = 0;
|
|
size_t i;
|
|
for(i = 0; i < len; i++) diff |= (unsigned char)(pa[i] ^ pb[i]);
|
|
return diff;
|
|
}
|
|
|
|
static int ctstrcmp(const char *a, const char *b, size_t maxlen){
|
|
unsigned char diff = 0;
|
|
size_t i;
|
|
for(i = 0; i < maxlen; i++){
|
|
diff |= (unsigned char)((unsigned char)a[i] ^ (unsigned char)b[i]);
|
|
if(!a[i] && !b[i]) break;
|
|
}
|
|
return diff;
|
|
}
|
|
|
|
int strongauth(struct clientparam * param){
|
|
unsigned char buf[256];
|
|
char pass[256] = {0};
|
|
|
|
if (!*param->username) return 4;
|
|
if (!param->pwtype && *param->password) {
|
|
struct hashtable *pwl = ¶m->srv->config->pwl_table;
|
|
|
|
if (pwl->ihashtable && hashresolv(pwl, param->username, pass, NULL)) {
|
|
switch(pass[0]){
|
|
case CL: {
|
|
int pwlen = strlen((char *)param->password);
|
|
if(pwlen > 255) pwlen = 255;
|
|
if((unsigned)pwlen < pwl->recsize) {
|
|
memset(buf, 0, pwl->recsize - 1);
|
|
memcpy(buf, param->password, pwlen);
|
|
if(!ctmemcmp(pass + 1, buf, pwl->recsize - 1)) return 0;
|
|
} else {
|
|
mdh_ctx *bctx;
|
|
unsigned hashsz;
|
|
unsigned int blen;
|
|
hashsz = pwl->recsize - 1 < 64 ? pwl->recsize - 1 : 64;
|
|
memset(buf, 0, pwl->recsize - 1);
|
|
bctx = mdh_init(MDH_BLAKE2, hashsz);
|
|
if(!bctx) return 6;
|
|
mdh_update(bctx, param->password, pwlen + 1);
|
|
blen = hashsz;
|
|
mdh_final(bctx, buf, &blen);
|
|
mdh_free(bctx);
|
|
if(!ctmemcmp(pass + 1, buf, pwl->recsize - 1)) return 0;
|
|
}
|
|
return 6;
|
|
}
|
|
case CR:
|
|
if (mycrypt(param->password, (unsigned char *)pass + 1, buf) &&
|
|
!ctstrcmp(pass + 1, (char *)buf, sizeof(pass) - 1))
|
|
return 0;
|
|
else return 7;
|
|
#ifdef WITH_SSL
|
|
case NT:
|
|
if(ntpwdhash(buf, param->password, 1) && !ctstrcmp(pass + 1, (char *)buf, sizeof(pass) - 1)) return 0;
|
|
else return 8;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return 5;
|
|
}
|
|
|
|
int radauth(struct clientparam * param);
|
|
|
|
struct auth authfuncs[] = {
|
|
{authfuncs+1, NULL, NULL, ""},
|
|
{authfuncs+2, ipauth, NULL, "iponly"},
|
|
{authfuncs+3, userauth, checkACL, "useronly"},
|
|
{authfuncs+4, dnsauth, checkACL, "dnsname"},
|
|
{authfuncs+5, strongauth, checkACL, "strong"},
|
|
{authfuncs+6, cacheauth, checkACL, "cache"},
|
|
{authfuncs+7, cacheauth, NULL, "cacheacl"},
|
|
#ifndef NORADIUS
|
|
#define AUTHOFFSET 1
|
|
{authfuncs+8, radauth, checkACL, "radius"},
|
|
#else
|
|
#define AUTHOFFSET 0
|
|
#endif
|
|
{authfuncs+8+AUTHOFFSET, NULL, NULL, "none"},
|
|
{NULL, NULL, NULL, ""}
|
|
};
|
|
|