replace recursion with loop for local redirects, fix wolfSSL build
Some checks are pending
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Waiting to run
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Waiting to run
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Waiting to run

This commit is contained in:
Vladimir Dubrovin 2026-07-31 15:44:22 +03:00
parent 5f1ed7363b
commit 44c47ee7d9
16 changed files with 85 additions and 51 deletions

View File

@ -24,13 +24,13 @@ void * autochild(struct clientparam* param) {
}
if(*param->clibuf == 4 || *param->clibuf == 5) {
param->service = S_SOCKS;
return sockschild(param);
return (void *)sockschild;
}
if(*param->clibuf == 22) {
param->service = S_TLSPR;
return tlsprchild(param);
return (void *)tlsprchild;
}
param->service = S_PROXY;
return proxychild(param);
return (void *)proxychild;
}

View File

@ -222,7 +222,6 @@ CLEANRET:
#ifndef _WIN32
param->clisock = INVALID_SOCKET;
#endif
freeparam(param);
return (NULL);
}

View File

@ -325,7 +325,6 @@ CLEANRET:
}
if(req) free(req);
if(buf) free(buf);
freeparam(param);
return (NULL);
}

View File

@ -64,7 +64,7 @@ void * imappchild(struct clientparam* param) {
if(socksend(param, param->clisock, buf, (int)strlen((char *)buf), conf.timeouts[STRING_S]) <= 0) {RETURN(698);}
param->clientstarttls = S_IMAPP;
if(!param->srv->targetport) param->srv->targetport = htons(143);
return tlsprchild(param);
return (void *)tlsprchild;
}
#endif
if(!strncasecmp((char *)cmd, "LOGIN ", 6)){
@ -238,7 +238,6 @@ CLEANRET:
socksend(param, param->clisock, buf, (int)strlen((char *)buf),conf.timeouts[STRING_S]);
}
}
freeparam(param);
return (NULL);
}

View File

@ -338,7 +338,7 @@ void lognone(struct clientparam * param, const unsigned char *s) {
clearstat(param);
}
void logstdout(struct clientparam * param, const unsigned char *s) {
NOINLINE void logstdout(struct clientparam * param, const unsigned char *s) {
FILE *log;
unsigned char tmpbuf[8192];

View File

@ -38,7 +38,7 @@ void * pop3pchild(struct clientparam* param) {
if(socksend(param, param->clisock, (unsigned char *)"+OK Begin TLS negotiation\r\n", 27, conf.timeouts[STRING_S])!=27) {RETURN(623);}
param->clientstarttls = S_POP3P;
if(!param->srv->targetport) param->srv->targetport = htons(110);
return tlsprchild(param);
return (void *)tlsprchild;
}
#endif
socksend(param, param->clisock, (unsigned char *)"-ERR need USER first\r\n", 22, conf.timeouts[STRING_S]);
@ -74,7 +74,6 @@ CLEANRET:
if(param->clisock != INVALID_SOCKET) {
if ((param->res > 0 && param->res < 100) || (param->res > 611 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"-ERR\r\n", 6,conf.timeouts[STRING_S]);
}
freeparam(param);
return (NULL);
}

View File

@ -864,7 +864,7 @@ for(;;){
if(isconnect && param->redirtype != R_HTTP) {
if(param->redirectfunc) {
freeptr(&req); freeptr(&buf); freeptr(&ftpbase);
return (*param->redirectfunc)(param);
return (void *)param->redirectfunc;
}
param->res = mapsocket(param, conf.timeouts[CONNECTION_L]);
RETURN(param->res);
@ -1226,7 +1226,6 @@ CLEANRET:
}
logurl(param, (char *)buf, (char *)req, ftp);
freeptr(&req); freeptr(&buf); freeptr(&ftpbase);
freeparam(param);
return (NULL);
}

View File

@ -112,6 +112,18 @@ void daemonize(void);
#endif
#endif
/* Keeps a callee with a large frame out of the caller's frame, e.g. the
8K log buffer of logstdout() out of dolog(), which calls it in a branch
taken only when there is no service.
*/
#if defined(__GNUC__)
#define NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define NOINLINE __declspec(noinline)
#else
#define NOINLINE
#endif
/* Thread stack size, stacksize command value is added to it. BSD libc uses
significantly more stack, e.g. in vfprintf() called by syslog().
*/
@ -213,7 +225,7 @@ void dolog(struct clientparam * param, const unsigned char *s);
int dobuf(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec);
int dobuf2(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec, struct tm* tm, char * format);
extern FILE * stdlog;
void logstdout(struct clientparam * param, const unsigned char *s);
NOINLINE void logstdout(struct clientparam * param, const unsigned char *s);
void logsyslog(struct clientparam * param, const unsigned char *s);
void lognone(struct clientparam * param, const unsigned char *s);
void logradius(struct clientparam * param, const unsigned char *s);
@ -357,6 +369,13 @@ void * udppmchild(struct clientparam * param);
void * adminchild(struct clientparam * param);
void * ftpprchild(struct clientparam * param);
void * tlsprchild(struct clientparam * param);
/* Child functions return the child to redirect the request to, or NULL if
the request is complete. childfunc() calls them and releases param.
Recursive redirection, e.g. a socks service redirected to socks5, used to
be limited by the stack size only, MAXCHILDREDIRECTS limits it now.
*/
#define MAXCHILDREDIRECTS 16
void * childfunc(struct clientparam * param);
struct datatype;

View File

@ -13,13 +13,19 @@
/* Child functions do not call each other, a child requesting redirection to
another child returns it instead of calling it, to keep the stack flat.
NULL is returned by the child which completed the request, it has already
released param and it must not be accessed after the call.
The child which completed the request returns NULL. param is logged by
the child and released here.
*/
void * childfunc(struct clientparam * param){
PROXYFUNC pf = param->srv->pf;
int i;
while(pf) pf = (PROXYFUNC)(*pf)(param);
for(i = 0; pf && i < MAXCHILDREDIRECTS; i++) pf = (PROXYFUNC)(*pf)(param);
if(pf){
param->res = 101;
dolog(param, (unsigned char *)"Redirection loop");
}
freeparam(param);
return NULL;
}
@ -133,7 +139,7 @@ void * threadfunc (void *p) {
}
}
}
((struct clientparam *) p)->srv->pf((struct clientparam *)p);
childfunc((struct clientparam *)p);
}
#ifdef _WIN32
return 0;
@ -701,7 +707,7 @@ int MODULEMAINFUNC (int argc, char** argv){
return 2;
};
*newparam = defparam;
return((*srv.pf)((void *)newparam)? 1:0);
return(childfunc(newparam)? 1:0);
}
#endif

View File

@ -132,7 +132,7 @@ void * smtppchild(struct clientparam* param) {
if(socksend(param, param->clisock, (unsigned char *)"220 2.0.0 Ready to start TLS\r\n", 30, conf.timeouts[STRING_S])!=30) {RETURN(673);}
param->clientstarttls = S_SMTPP;
if(!param->srv->targetport) param->srv->targetport = htons(587);
return tlsprchild(param);
return (void *)tlsprchild;
}
#endif
else if(!param->hostname) socksend(param, param->clisock, (unsigned char *)"571 need AUTH first\r\n", 22, conf.timeouts[STRING_S]);
@ -319,7 +319,6 @@ CLEANRET:
if ((param->res > 0 && param->res < 100) || (param->res > 661 && param->res <700)) socksend(param, param->clisock, (unsigned char *)"571 \r\n", 6,conf.timeouts[STRING_S]);
}
if(command) free(command);
freeparam(param);
return (NULL);
}

View File

@ -385,9 +385,8 @@ fflush(stderr);
switch(command) {
case 1:
if(param->redirectfunc){
void *ret = (*param->redirectfunc)(param);
if(buf)free(buf);
return ret;
return (void *)param->redirectfunc;
}
param->res = mapsocket(param, conf.timeouts[CONNECTION_L]);
break;
@ -467,7 +466,6 @@ fflush(stderr);
dolog(param, buf);
free(buf);
}
freeparam(param);
return (NULL);
}

View File

@ -72,6 +72,18 @@ static size_t bin2hex (const unsigned char* bin, size_t bin_length, char* str, s
return p - str;
}
static int copy_ext(X509 *dst_cert, X509 *src_cert, int nid)
{
X509_EXTENSION *ext;
int idx;
idx = X509_get_ext_by_NID(src_cert, nid, -1);
if(idx < 0) return 0;
if(!(ext = X509_get_ext(src_cert, idx))) return 0;
return X509_add_ext(dst_cert, ext, -1) > 0;
}
#ifndef WITH_WOLFSSL
static int add_ext(X509 *cert, int nid, const char *value)
{
X509_EXTENSION *ex;
@ -93,11 +105,11 @@ static int add_ext(X509 *cert, int nid, const char *value)
X509_EXTENSION_free(ex);
return err > 0;
}
#endif
SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
{
int err = -1;
int san_idx;
BIO *fcache;
X509 *src_cert = (X509 *) cert;
X509 *dst_cert = NULL;
@ -153,7 +165,11 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
X509_free(dst_cert);
return NULL;
}
#if !defined(WITH_WOLFSSL) && OPENSSL_VERSION_NUMBER < 0x10100000L
/* wolfSSL has no X509_set1_notBefore/X509_set1_notAfter before 5.7.2,
X509_set_notBefore/X509_set_notAfter are available in every version and
copy the time the same way.
*/
#if defined(WITH_WOLFSSL) || OPENSSL_VERSION_NUMBER < 0x10100000L
if(!X509_set_notBefore(dst_cert, X509_get_notBefore(src_cert))
|| !X509_set_notAfter(dst_cert, X509_get_notAfter(src_cert))){
#else
@ -163,26 +179,30 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
X509_free(dst_cert);
return NULL;
}
san_idx = X509_get_ext_by_NID(src_cert, NID_subject_alt_name, -1);
if(san_idx >= 0){
X509_EXTENSION *san;
san = X509_get_ext(src_cert, san_idx);
if(san && !X509_add_ext(dst_cert, san, -1)){
X509_free(dst_cert);
return NULL;
}
}
/* Extensions required from an end entity certificate. Without EKU
* serverAuth Apple's TLS stack (and Chrome on macOS/iOS, which uses it)
* rejects the certificate. keyUsage is intentionally not set: it depends
* on the type of the key being reused for every generated certificate,
* and an absent keyUsage places no restriction.
/* Copy the extensions an end entity certificate is expected to have.
* The extensions which break chain validation (AKI, CRL distribution
* points, certificate policies, ...) are intentionally not copied.
* A copy may fail: wolfSSL keeps extKeyUsage in its own form and can
* not add back the one it returns, it is not fatal.
*/
if(!add_ext(dst_cert, NID_basic_constraints, "critical,CA:FALSE")
|| !add_ext(dst_cert, NID_ext_key_usage, "serverAuth")){
X509_free(dst_cert);
return NULL;
}
copy_ext(dst_cert, src_cert, NID_subject_alt_name);
#ifndef WITH_WOLFSSL
/* Without EKU serverAuth Apple's TLS stack (and Chrome on macOS/iOS,
* which uses it) rejects the certificate, generate the extensions the
* server certificate has no usable ones to copy. keyUsage is not set:
* it depends on the type of the key reused for every generated
* certificate, and an absent keyUsage places no restriction.
* wolfSSL_X509V3_EXT_conf_nid() is a stub returning NULL in every
* wolfSSL version, the extensions can not be generated there.
*/
if(!copy_ext(dst_cert, src_cert, NID_basic_constraints))
add_ext(dst_cert, NID_basic_constraints, "critical,CA:FALSE");
if(!copy_ext(dst_cert, src_cert, NID_ext_key_usage))
add_ext(dst_cert, NID_ext_key_usage, "serverAuth");
#else
copy_ext(dst_cert, src_cert, NID_basic_constraints);
copy_ext(dst_cert, src_cert, NID_ext_key_usage);
#endif
err = X509_sign(dst_cert, config->CA_key, EVP_sha256());
if(!err){
X509_free(dst_cert);
@ -256,10 +276,10 @@ void _ssl_cert_free(SSL_CERT cert)
#define LEGACY_SSL_THREADING 0
#endif
#if LEGACY_SSL_THREADING
/* This array will store all of the mutexes available to OpenSSL. */
static _3proxy_mutex_t *mutex_buf= NULL;
static void locking_function(int mode, int n, const char * file, int line)
{
if (mode & CRYPTO_LOCK)
@ -276,6 +296,7 @@ static unsigned long id_function(void)
return ((unsigned long)pthread_self());
#endif
}
#endif
int thread_setup(void)
{

View File

@ -40,14 +40,13 @@ void * tcppmchild(struct clientparam* param) {
if(action != PASS) RETURN(19);
}
if(param->redirectfunc){
return (*param->redirectfunc)(param);
return (void *)param->redirectfunc;
}
RETURN (mapsocket(param, conf.timeouts[CONNECTION_L]));
CLEANRET:
dolog(param, param->hostname);
freeparam(param);
return (NULL);
}

View File

@ -396,7 +396,7 @@ void * tlsprchild(struct clientparam* param) {
if(action != PASS) RETURN(19);
}
if(param->redirectfunc && param->redirectfunc != tlsprchild){
return (*param->redirectfunc)(param);
return (void *)param->redirectfunc;
}
if(stlsproto){
@ -471,7 +471,6 @@ CLEANRET:
sprintf(req, "%sv%d.%d %s %s", lv<0?"NONE":lv?"TLS":"SSL", lv<0?0:lv?1:3, lv<0?0:lv?lv-1:0, param->hostname?(char *)param->hostname:"-", proto);
dolog(param, (unsigned char *)req);
freeparam(param);
return (NULL);
}

View File

@ -87,7 +87,6 @@ CLEANRET:
_3proxy_sem_unlock(udpinit);
dolog(param, NULL);
param->clisock = INVALID_SOCKET;
freeparam(param);
return (NULL);
}

View File

@ -615,6 +615,5 @@ CLEANRET:
if(buf) free(buf);
dolog(param, (unsigned char *)req);
if(req)free(req);
freeparam(param);
return (NULL);
}