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) { if(*param->clibuf == 4 || *param->clibuf == 5) {
param->service = S_SOCKS; param->service = S_SOCKS;
return sockschild(param); return (void *)sockschild;
} }
if(*param->clibuf == 22) { if(*param->clibuf == 22) {
param->service = S_TLSPR; param->service = S_TLSPR;
return tlsprchild(param); return (void *)tlsprchild;
} }
param->service = S_PROXY; param->service = S_PROXY;
return proxychild(param); return (void *)proxychild;
} }

View File

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

View File

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

View File

@ -338,7 +338,7 @@ void lognone(struct clientparam * param, const unsigned char *s) {
clearstat(param); clearstat(param);
} }
void logstdout(struct clientparam * param, const unsigned char *s) { NOINLINE void logstdout(struct clientparam * param, const unsigned char *s) {
FILE *log; FILE *log;
unsigned char tmpbuf[8192]; 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);} 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; param->clientstarttls = S_POP3P;
if(!param->srv->targetport) param->srv->targetport = htons(110); if(!param->srv->targetport) param->srv->targetport = htons(110);
return tlsprchild(param); return (void *)tlsprchild;
} }
#endif #endif
socksend(param, param->clisock, (unsigned char *)"-ERR need USER first\r\n", 22, conf.timeouts[STRING_S]); 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->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]); 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); return (NULL);
} }

View File

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

View File

@ -112,6 +112,18 @@ void daemonize(void);
#endif #endif
#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 /* Thread stack size, stacksize command value is added to it. BSD libc uses
significantly more stack, e.g. in vfprintf() called by syslog(). 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 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); int dobuf2(struct clientparam * param, unsigned char * buf, const unsigned char *s, const unsigned char * doublec, struct tm* tm, char * format);
extern FILE * stdlog; 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 logsyslog(struct clientparam * param, const unsigned char *s);
void lognone(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); void logradius(struct clientparam * param, const unsigned char *s);
@ -357,6 +369,13 @@ void * udppmchild(struct clientparam * param);
void * adminchild(struct clientparam * param); void * adminchild(struct clientparam * param);
void * ftpprchild(struct clientparam * param); void * ftpprchild(struct clientparam * param);
void * tlsprchild(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; struct datatype;

View File

@ -13,13 +13,19 @@
/* Child functions do not call each other, a child requesting redirection to /* 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. 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 The child which completed the request returns NULL. param is logged by
released param and it must not be accessed after the call. the child and released here.
*/ */
void * childfunc(struct clientparam * param){ void * childfunc(struct clientparam * param){
PROXYFUNC pf = param->srv->pf; 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; return NULL;
} }
@ -133,7 +139,7 @@ void * threadfunc (void *p) {
} }
} }
} }
((struct clientparam *) p)->srv->pf((struct clientparam *)p); childfunc((struct clientparam *)p);
} }
#ifdef _WIN32 #ifdef _WIN32
return 0; return 0;
@ -701,7 +707,7 @@ int MODULEMAINFUNC (int argc, char** argv){
return 2; return 2;
}; };
*newparam = defparam; *newparam = defparam;
return((*srv.pf)((void *)newparam)? 1:0); return(childfunc(newparam)? 1:0);
} }
#endif #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);} 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; param->clientstarttls = S_SMTPP;
if(!param->srv->targetport) param->srv->targetport = htons(587); if(!param->srv->targetport) param->srv->targetport = htons(587);
return tlsprchild(param); return (void *)tlsprchild;
} }
#endif #endif
else if(!param->hostname) socksend(param, param->clisock, (unsigned char *)"571 need AUTH first\r\n", 22, conf.timeouts[STRING_S]); 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 ((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); if(command) free(command);
freeparam(param);
return (NULL); return (NULL);
} }

View File

@ -385,9 +385,8 @@ fflush(stderr);
switch(command) { switch(command) {
case 1: case 1:
if(param->redirectfunc){ if(param->redirectfunc){
void *ret = (*param->redirectfunc)(param);
if(buf)free(buf); if(buf)free(buf);
return ret; return (void *)param->redirectfunc;
} }
param->res = mapsocket(param, conf.timeouts[CONNECTION_L]); param->res = mapsocket(param, conf.timeouts[CONNECTION_L]);
break; break;
@ -467,7 +466,6 @@ fflush(stderr);
dolog(param, buf); dolog(param, buf);
free(buf); free(buf);
} }
freeparam(param);
return (NULL); 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; 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) static int add_ext(X509 *cert, int nid, const char *value)
{ {
X509_EXTENSION *ex; X509_EXTENSION *ex;
@ -93,11 +105,11 @@ static int add_ext(X509 *cert, int nid, const char *value)
X509_EXTENSION_free(ex); X509_EXTENSION_free(ex);
return err > 0; return err > 0;
} }
#endif
SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config) SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
{ {
int err = -1; int err = -1;
int san_idx;
BIO *fcache; BIO *fcache;
X509 *src_cert = (X509 *) cert; X509 *src_cert = (X509 *) cert;
X509 *dst_cert = NULL; X509 *dst_cert = NULL;
@ -153,7 +165,11 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
X509_free(dst_cert); X509_free(dst_cert);
return NULL; 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)) if(!X509_set_notBefore(dst_cert, X509_get_notBefore(src_cert))
|| !X509_set_notAfter(dst_cert, X509_get_notAfter(src_cert))){ || !X509_set_notAfter(dst_cert, X509_get_notAfter(src_cert))){
#else #else
@ -163,26 +179,30 @@ SSL_CERT ssl_copy_cert(SSL_CERT cert, SSL_CONFIG *config)
X509_free(dst_cert); X509_free(dst_cert);
return NULL; return NULL;
} }
san_idx = X509_get_ext_by_NID(src_cert, NID_subject_alt_name, -1); /* Copy the extensions an end entity certificate is expected to have.
if(san_idx >= 0){ * The extensions which break chain validation (AKI, CRL distribution
X509_EXTENSION *san; * points, certificate policies, ...) are intentionally not copied.
san = X509_get_ext(src_cert, san_idx); * A copy may fail: wolfSSL keeps extKeyUsage in its own form and can
if(san && !X509_add_ext(dst_cert, san, -1)){ * not add back the one it returns, it is not fatal.
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.
*/ */
if(!add_ext(dst_cert, NID_basic_constraints, "critical,CA:FALSE") copy_ext(dst_cert, src_cert, NID_subject_alt_name);
|| !add_ext(dst_cert, NID_ext_key_usage, "serverAuth")){ #ifndef WITH_WOLFSSL
X509_free(dst_cert); /* Without EKU serverAuth Apple's TLS stack (and Chrome on macOS/iOS,
return NULL; * 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()); err = X509_sign(dst_cert, config->CA_key, EVP_sha256());
if(!err){ if(!err){
X509_free(dst_cert); X509_free(dst_cert);
@ -256,10 +276,10 @@ void _ssl_cert_free(SSL_CERT cert)
#define LEGACY_SSL_THREADING 0 #define LEGACY_SSL_THREADING 0
#endif #endif
#if LEGACY_SSL_THREADING
/* This array will store all of the mutexes available to OpenSSL. */ /* This array will store all of the mutexes available to OpenSSL. */
static _3proxy_mutex_t *mutex_buf= NULL; static _3proxy_mutex_t *mutex_buf= NULL;
static void locking_function(int mode, int n, const char * file, int line) static void locking_function(int mode, int n, const char * file, int line)
{ {
if (mode & CRYPTO_LOCK) if (mode & CRYPTO_LOCK)
@ -276,6 +296,7 @@ static unsigned long id_function(void)
return ((unsigned long)pthread_self()); return ((unsigned long)pthread_self());
#endif #endif
} }
#endif
int thread_setup(void) int thread_setup(void)
{ {

View File

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

View File

@ -396,7 +396,7 @@ void * tlsprchild(struct clientparam* param) {
if(action != PASS) RETURN(19); if(action != PASS) RETURN(19);
} }
if(param->redirectfunc && param->redirectfunc != tlsprchild){ if(param->redirectfunc && param->redirectfunc != tlsprchild){
return (*param->redirectfunc)(param); return (void *)param->redirectfunc;
} }
if(stlsproto){ 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); 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); dolog(param, (unsigned char *)req);
freeparam(param);
return (NULL); return (NULL);
} }

View File

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

View File

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