Make filtermaxsize, parentretries, delimchar and maxseg per service

Like the timeouts, each service now keeps the values given before it
instead of reading the global ones while its connections run. setopts()
takes the service's maxseg. The TLS handshakes wait the service's
CONNECT_TO, kept in its SSL configuration, rather than the global one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-13 15:33:35 +03:00
parent bab80ef684
commit ca0b5e0c88
10 changed files with 30 additions and 20 deletions

View File

@ -448,6 +448,7 @@ shutdown.
Sets the delimiter character used to separate username from hostname in proxy
authentication strings (e.g. for FTP, POP3 proxies). Default is \'@\'. For example,
to use \'#\' instead: delimchar #. This allows usernames to contain the \'@\' character.
Applies to the services started after it.
.br
.BR archiver
@ -506,7 +507,7 @@ RADIUS authentication, which serve all the services, use the values given last.
.br
Sets TCP maximum segment size (MSS) for outgoing connections. This can be used
to work around path MTU discovery issues or to optimize traffic for specific
network conditions.
network conditions. Applies to the services started after it.
.br
.BR radius
@ -1101,6 +1102,7 @@ than being sent without a parent.
Number of attempts to reach a parent proxy. Default is 2. Each attempt
picks a parent again, leaving out the ones which already failed, so this is
also the number of different parents a request may be tried through.
Applies to the services started after it.
.br
@ -1473,7 +1475,7 @@ data filtering will be performed through filtering plugins to avoid data
corruption and/or Content-Length changing. A body up to this size is read
whole into memory and filtered at once. 0 removes the limit, but then a body
is only filtered whole if it fits in the connection's 16K buffer.
Default is 1MB (1048576).
Default is 1MB (1048576). Applies to the services started after it.
.SH SSL/TLS SUPPORT
SSL/TLS support is built into 3proxy (since 0.9.7) when compiled with OpenSSL

View File

@ -383,7 +383,7 @@ int checkACL(struct clientparam * param){
if((param->operation == UDPASSOC)? (param->ctrlsocksrv != INVALID_SOCKET) : (param->remsock != INVALID_SOCKET)) {
return 0;
}
for(; i < conf.parentretries; i++){
for(; i < param->srv->parentretries; i++){
dup = *acentry;
res = handleredirect(param, &dup);
if(!res) break;

View File

@ -670,15 +670,15 @@ int parseusername(char *username, struct clientparam *param, int extpasswd){
int parseconnusername(char *username, struct clientparam *param, int extpasswd, uint16_t port){
char *sb, *se;
if(!username || !*username) return 1;
if ((sb=strchr(username, conf.delimchar)) == NULL){
if ((sb=strchr(username, param->srv->delimchar)) == NULL){
if(!*param->hostname && param->remsock == INVALID_SOCKET) return 2;
if(*param->hostname)parsehostname((char *)param->hostname, param, *SAPORT(&param->req)? ntohs(*SAPORT(&param->req)) : port);
return parseusername(username, param, extpasswd);
}
while ((se=strchr(sb+1, conf.delimchar)))sb=se;
while ((se=strchr(sb+1, param->srv->delimchar)))sb=se;
*(sb) = 0;
if(parseusername(username, param, extpasswd)) return 3;
*(sb) = conf.delimchar;
*(sb) = param->srv->delimchar;
if(parsehostname(sb+1, param, *SAPORT(&param->req)? ntohs(*SAPORT(&param->req)) : port)) return 4;
return 0;
}
@ -803,7 +803,7 @@ int doconnect(struct clientparam * param){
param->sinsl = param->srv->extsa;
}
*SAPORT(&param->sinsl) = 0;
setopts(param->remsock, &param->srv->srvsockopts);
setopts(param->remsock, &param->srv->srvsockopts, param->srv->maxseg);
#ifndef _WIN32
/* not on Windows, see the accepting side in proxymain.c */

View File

@ -180,7 +180,7 @@ static int growbuf(unsigned char **buf, int *bufsize, int need){
with no limit set a body is only read whole if it fits the buffer.
Returns how much is there, less than len if the body did not all come. */
static uint64_t fillwhole(struct clientparam *param, DIRECTION which, uint64_t len){
if(conf.filtermaxsize && bufroom(param, which, (unsigned long)len)) return 0;
if(param->srv->filtermaxsize && bufroom(param, which, (unsigned long)len)) return 0;
return sockfillbuf(param, which, (unsigned long)len, CONNECTION_S);
}
#endif
@ -727,7 +727,7 @@ for(;;){
if(action != PASS) RETURN(19);
}
if (conf.filtermaxsize && contentlength64 > (uint64_t)conf.filtermaxsize) {
if (param->srv->filtermaxsize && contentlength64 > (uint64_t)param->srv->filtermaxsize) {
param->nolongdatfilter = 1;
}
else if(param->ndatfilterscli > 0 && contentlength64 > 0 && contentlength64 == (uint64_t)(unsigned long)contentlength64){
@ -1228,7 +1228,7 @@ for(;;){
param->nolongdatfilter = 0;
if (conf.filtermaxsize && contentlength64 > (uint64_t)conf.filtermaxsize) {
if (param->srv->filtermaxsize && contentlength64 > (uint64_t)param->srv->filtermaxsize) {
param->nolongdatfilter = 1;
}
else if(param->ndatfilterssrv > 0 && contentlength64 > 0 && contentlength64 == (uint64_t)(unsigned long)contentlength64 && param->operation != HTTP_HEAD && res != 204 && res != 304){

View File

@ -513,7 +513,7 @@ extern struct socketoptions {
char * optname;
} sockopts[];
int getopts(const char *s, struct sockoptset *opts);
void setopts(SOCKET s, const struct sockoptset *opts);
void setopts(SOCKET s, const struct sockoptset *opts, int maxseg);
char * printopts(char *sep);
#ifdef _WINCE

View File

@ -342,7 +342,7 @@ int getopts(const char *s, struct sockoptset *opts){
return 0;
}
void setopts(SOCKET s, const struct sockoptset *opts){
void setopts(SOCKET s, const struct sockoptset *opts, int maxseg){
int i, value;
for(i = 0; sockopts[i].optname && i < MAXSOCKOPTS; i++){
@ -350,7 +350,7 @@ void setopts(SOCKET s, const struct sockoptset *opts){
value = opts->value[i];
#ifdef TCP_MAXSEG
if(sockopts[i].opt == TCP_MAXSEG && *sockopts[i].optname == 'T'){
if(!value) value = conf.maxseg;
if(!value) value = maxseg;
if(!value) continue;
}
#endif
@ -880,7 +880,7 @@ static int srvlisten(struct srvstart *s){
perror("socket()");
return -2;
}
setopts(sock, &srv->lissockopts);
setopts(sock, &srv->lissockopts, srv->maxseg);
#ifdef _WIN32
ioctlsocket(sock, FIONBIO, &ul);
#else
@ -940,7 +940,7 @@ static int cblisten(struct srvstart *s){
dolog(defparam, (unsigned char *)"Failed to allocate connect back socket");
return -6;
}
setopts(srv->cbsock, &srv->cbssockopts);
setopts(srv->cbsock, &srv->cbssockopts, srv->maxseg);
if(srv->so._bind(srv->so.state, srv->cbsock, (struct sockaddr*)&cbsa, SASIZE(&cbsa))==-1) {
dolog(defparam, (unsigned char *)"Failed to bind connect back socket");
@ -981,7 +981,7 @@ static int tcpnext(struct srvstart *s, SOCKET *client, int *nlog){
usleep(SLEEPTIME);
return NEXT_RETRY;
}
setopts(new_sock, &srv->cbcsockopts);
setopts(new_sock, &srv->cbcsockopts, srv->maxseg);
parsehost(srv->family, s->cbc_string, (struct sockaddr *)&defparam->sincr);
#if defined SO_BINDTODEVICE || defined IP_BOUND_IF
@ -1051,7 +1051,7 @@ static int tcpnext(struct srvstart *s, SOCKET *client, int *nlog){
}
return NEXT_RETRY;
}
setopts(new_sock, &srv->clisockopts);
setopts(new_sock, &srv->clisockopts, srv->maxseg);
}
size = sizeof(defparam->sincl);
if(srv->so._getsockname(srv->so.state, new_sock, (struct sockaddr *)&defparam->sincl, &size)){
@ -1478,6 +1478,10 @@ void srvinit(struct srvparam * srv, struct clientparam *param){
srv->backlog = conf.backlog;
srv->stacksize = conf.stacksize;
memcpy(srv->timeouts, conf.timeouts, sizeof(srv->timeouts));
srv->filtermaxsize = conf.filtermaxsize;
srv->parentretries = conf.parentretries;
srv->maxseg = conf.maxseg;
srv->delimchar = conf.delimchar;
srv->time_start = time(NULL);
if(havelog && conf.logtarget){
srv->logtarget = (unsigned char *)strdup((char *)conf.logtarget);

View File

@ -180,7 +180,7 @@ void * sockschild(struct clientparam* param) {
param->operation = command == 2?BIND:UDPASSOC;
if(command == 2){
if ((param->remsock=nssocket(param->srv, &param->srv->so, param->sostate, NS_OUT, SASOCK(&param->req), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN (11);}
setopts(param->remsock, &param->srv->lissockopts);
setopts(param->remsock, &param->srv->lissockopts, param->srv->maxseg);
if(extbindtodevice(param, param->remsock)) {RETURN (12);}
}
break;

View File

@ -297,7 +297,7 @@ SSL_CONN ssl_handshake_to_server(SOCKET s, char * hostname, SSL_CONFIG *config,
fds[0].events = POLLOUT;
}
else break;
if(sso._poll(sso.state, fds, 1, pl->conf->timeouts[CONNECT_TO]*1000) <= 0 || !(fds[0].revents & (POLLOUT|POLLIN))) break;
if(sso._poll(sso.state, fds, 1, config->timeout*1000) <= 0 || !(fds[0].revents & (POLLOUT|POLLIN))) break;
} while (err == -1);
if ( err != 1 ) {
@ -372,7 +372,7 @@ SSL_CONN ssl_handshake_to_client(SOCKET s, SSL_CONFIG *config, X509 *server_cert
fds[0].events = POLLOUT;
}
else break;
if(sso._poll(sso.state, fds, 1, pl->conf->timeouts[CONNECT_TO]*1000) <= 0 || !(fds[0].revents & (POLLOUT|POLLIN))) break;
if(sso._poll(sso.state, fds, 1, config->timeout*1000) <= 0 || !(fds[0].revents & (POLLOUT|POLLIN))) break;
} while (err == -1);
@ -589,6 +589,7 @@ static void* ssl_filter_open(void * idata, struct srvparam * srv){
sc->server_min_proto_version = server_min_proto_version;
sc->server_max_proto_version = server_max_proto_version;
sc->client_mode = client_mode;
sc->timeout = srv->timeouts[CONNECT_TO];
sc->client_verify = client_verify;
sc->server_verify = server_verify;
if(client_ciphersuites) sc->client_ciphersuites = strdup(client_ciphersuites);

View File

@ -52,6 +52,7 @@ struct ssl_config {
int client_verify;
int server_verify;
int client_mode;
int timeout; /* seconds a handshake step may wait: the service's CONNECT_TO */
};
typedef struct ssl_config SSL_CONFIG;

View File

@ -622,6 +622,8 @@ struct srvparam {
int family;
int stacksize;
int timeouts[12]; /* conf.timeouts as they were when the service started */
int filtermaxsize, parentretries, maxseg; /* and these */
char delimchar;
int noforce;
int anonymous;
struct sockoptset clisockopts, srvsockopts, lissockopts, cbcsockopts, cbssockopts;