This commit is contained in:
yazhog 2026-08-10 10:09:29 +00:00 committed by GitHub
commit 8e86183787
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 86 additions and 10 deletions

View File

@ -55,6 +55,7 @@ option(3PROXY_USE_SPLICE "Build Linux splice() support, slower than read/write f
option(3PROXY_USE_POLL "Use poll() instead of select() (Unix only)" ON) option(3PROXY_USE_POLL "Use poll() instead of select() (Unix only)" ON)
option(3PROXY_USE_WSAPOLL "Use WSAPoll instead of select() (Windows only)" ON) option(3PROXY_USE_WSAPOLL "Use WSAPoll instead of select() (Windows only)" ON)
option(3PROXY_USE_NETFILTER "Enable Linux netfilter support (Linux only)" ON) option(3PROXY_USE_NETFILTER "Enable Linux netfilter support (Linux only)" ON)
option(3PROXY_USE_LOCAL_PORT_RANGE "Enable per-connection local port ranges (Linux 6.3+ only)" ON)
option(3PROXY_USE_UNIX_SOCKETS "Enable Unix domain socket support (Unix only)" ON) option(3PROXY_USE_UNIX_SOCKETS "Enable Unix domain socket support (Unix only)" ON)
if(NOT WIN32 AND NOT APPLE) if(NOT WIN32 AND NOT APPLE)
@ -185,6 +186,10 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_compile_definitions(WITH_NETFILTER) add_compile_definitions(WITH_NETFILTER)
endif() endif()
if(3PROXY_USE_LOCAL_PORT_RANGE)
add_compile_definitions(WITH_LOCAL_PORT_RANGE)
endif()
if(3PROXY_USE_UNIX_SOCKETS) if(3PROXY_USE_UNIX_SOCKETS)
add_compile_definitions(WITH_UN) add_compile_definitions(WITH_UN)
endif() endif()

View File

@ -25,6 +25,10 @@ LDFLAGS += -fno-strict-aliasing -pthread
# makefile, including the += above and the STATIC/LIBSTATIC handling below. # makefile, including the += above and the STATIC/LIBSTATIC handling below.
CFLAGS += $(EXTRA_CFLAGS) CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS) LDFLAGS += $(EXTRA_LDFLAGS)
LOCAL_PORT_RANGE ?= true
ifeq ($(LOCAL_PORT_RANGE),true)
CFLAGS += -DWITH_LOCAL_PORT_RANGE
endif
DLFLAGS ?= -shared DLFLAGS ?= -shared
DLSUFFICS = .ld.so DLSUFFICS = .ld.so
# -lpthreads may be reuqired on some platforms instead of -pthreads # -lpthreads may be reuqired on some platforms instead of -pthreads

View File

@ -817,7 +817,7 @@ with probability of 0.7) for outgoing web connections. Chains are only applied t
.br .br
type is one of: type is one of:
.br .br
\fBextip\fR does not actually redirect the request; it sets the external address for this request to \fI<ip>\fR. It can be chained with another parent type. It's useful to set the external IP based on ACL or make it random. \fBextip\fR does not actually redirect the request; it sets the external address for this request to \fI<ip>\fR. It can be chained with another parent type. It's useful to set the external IP based on ACL or make it random. Its \fI<port>\fR argument is optional and defaults to 0. When built with \fBWITH_LOCAL_PORT_RANGE\fR on Linux 6.3 or newer, \fI<port>\fR may be an inclusive IPv4 local port range \fIFIRST-LAST\fR. The range is applied to outgoing sockets for requests matching this parent; DNS resolver traffic is not affected. For example, \fBparent 1000 extip 192.0.2.10 20000-30000\fR.
.br .br
\fBtcp\fR simply redirect connection. TCP is always last in chain. This type of proxy is a simple TCP redirection, it does not support parent authentication. \fBtcp\fR simply redirect connection. TCP is always last in chain. This type of proxy is a simple TCP redirection, it does not support parent authentication.
.br .br

View File

@ -10,6 +10,11 @@
#include "proxy.h" #include "proxy.h"
#ifdef WITH_LOCAL_PORT_RANGE
#ifndef IP_LOCAL_PORT_RANGE
#define IP_LOCAL_PORT_RANGE 51
#endif
#endif
char * copyright = COPYRIGHT; char * copyright = COPYRIGHT;
@ -680,6 +685,9 @@ int doconnect(struct clientparam * param){
} }
*SAPORT(&param->sinsl) = 0; *SAPORT(&param->sinsl) = 0;
setopts(param->remsock, param->srv->srvsockopts); setopts(param->remsock, param->srv->srvsockopts);
#ifdef WITH_LOCAL_PORT_RANGE
if(set_local_port_range(param, param->remsock, (struct sockaddr *)&param->sinsl)) return 12;
#endif
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg)); param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
#if defined SO_BINDTODEVICE #if defined SO_BINDTODEVICE
@ -725,6 +733,16 @@ int doconnect(struct clientparam * param){
return 0; return 0;
} }
#ifdef WITH_LOCAL_PORT_RANGE
int set_local_port_range(struct clientparam *param, SOCKET sock, const struct sockaddr *sa){
if(param->local_port_range && sa->sa_family == AF_INET &&
((const struct sockaddr_in *)sa)->sin_port == 0 &&
param->srv->so._setsockopt(param->srv->so.state, sock, IPPROTO_IP, IP_LOCAL_PORT_RANGE,
(char *)&param->local_port_range, sizeof(param->local_port_range))) return -1;
return 0;
}
#endif
int scanaddr(const unsigned char *s, uint32_t * ip, uint32_t * mask) { int scanaddr(const unsigned char *s, uint32_t * ip, uint32_t * mask) {
unsigned d1, d2, d3, d4, m; unsigned d1, d2, d3, d4, m;
int res; int res;

View File

@ -814,6 +814,11 @@ static int h_parent(int argc, unsigned char **argv){
free(chains); free(chains);
return(4); return(4);
} }
if(argc < 5 && chains->type != R_EXTIP) {
fprintf(stderr, "Chaining error: port is required for parent type %s\n", argv[2]);
free(chains);
return(3);
}
#ifdef WITH_UN #ifdef WITH_UN
if(!strncmp((char *)argv[3], "unix:", 5)){ if(!strncmp((char *)argv[3], "unix:", 5)){
make_un(argv[3] + 5, (struct sockaddr_un*)&chains->addr); make_un(argv[3] + 5, (struct sockaddr_un*)&chains->addr);
@ -838,7 +843,34 @@ static int h_parent(int argc, unsigned char **argv){
*cidr = '/'; *cidr = '/';
chains->cidr = atoi(cidr + 1); chains->cidr = atoi(cidr + 1);
} }
*SAPORT(&chains->addr) = htons((uint16_t)atoi((char *)argv[4])); if(argc > 4) {
char *end;
unsigned long port;
errno = 0;
port = strtoul((char *)argv[4], &end, 10);
#ifdef WITH_LOCAL_PORT_RANGE
if(chains->type == R_EXTIP && *end == '-') {
unsigned long last;
errno = 0;
last = strtoul(end + 1, &end, 10);
if(errno || *end || !port || !last || port > last || last > 65535) {
free(chains->exthost);
free(chains);
return 3;
}
chains->local_port_range = ((uint32_t)last << 16) | (uint32_t)port;
port = 0;
}
#endif
if(errno || *end || port > 65535) {
free(chains->exthost);
free(chains);
return 3;
}
*SAPORT(&chains->addr) = htons((uint16_t)port);
}
if(argc > 5) chains->extuser = (unsigned char *)strdup((char *)argv[5]); if(argc > 5) chains->extuser = (unsigned char *)strdup((char *)argv[5]);
if(argc > 6) chains->extpass = (unsigned char *)strdup((char *)argv[6]); if(argc > 6) chains->extpass = (unsigned char *)strdup((char *)argv[6]);
if(!acl->chains) { if(!acl->chains) {
@ -1696,7 +1728,7 @@ struct commands commandhandlers[]={
{NULL, "system", h_system, 2, 2}, {NULL, "system", h_system, 2, 2},
{NULL, "pidfile", h_pidfile, 2, 2}, {NULL, "pidfile", h_pidfile, 2, 2},
{NULL, "monitor", h_monitor, 2, 2}, {NULL, "monitor", h_monitor, 2, 2},
{NULL, "parent", h_parent, 5, 0}, {NULL, "parent", h_parent, 4, 0},
{NULL, "allow", h_ace, 1, 0}, {NULL, "allow", h_ace, 1, 0},
{NULL, "deny", h_ace, 1, 0}, {NULL, "deny", h_ace, 1, 0},
{NULL, "redirect", h_ace, 3, 0}, {NULL, "redirect", h_ace, 3, 0},

View File

@ -222,4 +222,3 @@ CLEANRET:
#endif #endif
return (NULL); return (NULL);
} }

View File

@ -121,6 +121,9 @@ void * ftpprchild(struct clientparam* param) {
} }
if ((clidatasock=socket(SASOCK(&param->sincl), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN(821);} if ((clidatasock=socket(SASOCK(&param->sincl), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN(821);}
*SAPORT(&param->sincl) = 0; *SAPORT(&param->sincl) = 0;
#ifdef WITH_LOCAL_PORT_RANGE
if(set_local_port_range(param, clidatasock, (struct sockaddr *)&param->sincl)){RETURN(822);}
#endif
if(param->srv->so._bind(param->sostate, clidatasock, (struct sockaddr *)&param->sincl, SASIZE(&param->sincl))){RETURN(822);} if(param->srv->so._bind(param->sostate, clidatasock, (struct sockaddr *)&param->sincl, SASIZE(&param->sincl))){RETURN(822);}
if (pasv) { if (pasv) {
if(param->srv->so._listen(param->sostate, clidatasock, 1)) {RETURN(823);} if(param->srv->so._listen(param->sostate, clidatasock, 1)) {RETURN(823);}

View File

@ -347,6 +347,9 @@ unsigned char * dologname (unsigned char *buf, unsigned char *name, const unsign
int readconfig(FILE * fp); int readconfig(FILE * fp);
void initcommands(void); void initcommands(void);
int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to); int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to);
#ifdef WITH_LOCAL_PORT_RANGE
int set_local_port_range(struct clientparam *param, SOCKET sock, const struct sockaddr *sa);
#endif
uint32_t myrand(void); uint32_t myrand(void);
@ -428,4 +431,3 @@ extern char * ceargv[32];
#define WEBBANNERS 35 #define WEBBANNERS 35
#endif #endif

View File

@ -502,8 +502,8 @@ int MODULEMAINFUNC (int argc, char** argv){
} }
break; break;
case 'N': case 'N':
if(argv[i][3] == 'e') getip46(46, (unsigned char *)argv[i]+3, (struct sockaddr *)&srv.extNat); if(argv[i][2] == 'e') getip46(46, (unsigned char *)argv[i]+3, (struct sockaddr *)&srv.extNat);
else if(argv[i][3] == 'i') getip46(46, (unsigned char *)argv[i]+3, (struct sockaddr *)&srv.intNat); else if(argv[i][2] == 'i') getip46(46, (unsigned char *)argv[i]+3, (struct sockaddr *)&srv.intNat);
else getip46(46, (unsigned char *)argv[i]+2, (struct sockaddr *)&srv.extNat); else getip46(46, (unsigned char *)argv[i]+2, (struct sockaddr *)&srv.extNat);
break; break;
case 'n': case 'n':
@ -1791,5 +1791,3 @@ FILTER_ACTION handledatfltsrv(struct clientparam *cparam, unsigned char ** buf_p
} }
return PASS; return PASS;
} }

View File

@ -295,6 +295,9 @@ int handleredirect(struct clientparam * param, struct ace * acentry){
if(!connected){ if(!connected){
if(cur->type == R_EXTIP){ if(cur->type == R_EXTIP){
param->sinsl = cur->addr; param->sinsl = cur->addr;
#ifdef WITH_LOCAL_PORT_RANGE
param->local_port_range = cur->local_port_range;
#endif
if(SAISNULL(&param->sinsl) && (*SAFAMILY(&param->sincr) == AF_INET || *SAFAMILY(&param->sincr) == AF_INET6))param->sinsl = param->sincr; if(SAISNULL(&param->sinsl) && (*SAFAMILY(&param->sincr) == AF_INET || *SAFAMILY(&param->sincr) == AF_INET6))param->sinsl = param->sincr;
#ifndef NOIPV6 #ifndef NOIPV6
else if(cur->cidr && *SAFAMILY(&param->sinsl) == AF_INET6){ else if(cur->cidr && *SAFAMILY(&param->sinsl) == AF_INET6){

View File

@ -198,4 +198,4 @@ uint32_t fakeresolver (int af, unsigned char *name, unsigned char * value){
value[3] = 2; value[3] = 2;
} }
return 1; return 1;
} }

View File

@ -250,6 +250,9 @@ void * sockschild(struct clientparam* param) {
} }
if(command > 1) { if(command > 1) {
#ifdef WITH_LOCAL_PORT_RANGE
if(set_local_port_range(param, param->remsock, (struct sockaddr *)&param->sinsl)) RETURN(12);
#endif
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)&param->sinsl,SASIZE(&param->sinsl))) { if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)&param->sinsl,SASIZE(&param->sinsl))) {
*SAPORT(&param->sinsl) = 0; *SAPORT(&param->sinsl) = 0;
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)&param->sinsl,SASIZE(&param->sinsl)))RETURN (12); if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)&param->sinsl,SASIZE(&param->sinsl)))RETURN (12);

View File

@ -335,6 +335,9 @@ struct chain {
unsigned char * extpass; unsigned char * extpass;
unsigned short weight; unsigned short weight;
unsigned short cidr; unsigned short cidr;
#ifdef WITH_LOCAL_PORT_RANGE
uint32_t local_port_range;
#endif
}; };
struct period { struct period {
@ -666,6 +669,9 @@ struct clientparam {
maxtrafout64; maxtrafout64;
PROXYSOCKADDRTYPE sincl, sincr; PROXYSOCKADDRTYPE sincl, sincr;
PROXYSOCKADDRTYPE sinsl, sinsr, req; PROXYSOCKADDRTYPE sinsl, sinsr, req;
#ifdef WITH_LOCAL_PORT_RANGE
uint32_t local_port_range;
#endif
uint64_t statscli64, uint64_t statscli64,
statssrv64; statssrv64;

View File

@ -46,6 +46,9 @@ void * udppmchild(struct clientparam* param) {
*SAPORT(&param->sinsl) = 0; *SAPORT(&param->sinsl) = 0;
param->remsock = param->srv->so._socket(param->srv->so.state, SASOCK(&param->sinsl), SOCK_DGRAM, IPPROTO_UDP); param->remsock = param->srv->so._socket(param->srv->so.state, SASOCK(&param->sinsl), SOCK_DGRAM, IPPROTO_UDP);
if(param->remsock == INVALID_SOCKET) { RETURN(202); } if(param->remsock == INVALID_SOCKET) { RETURN(202); }
#ifdef WITH_LOCAL_PORT_RANGE
if(set_local_port_range(param, param->remsock, (struct sockaddr *)&param->sinsl)) { RETURN(203); }
#endif
if(param->srv->so._bind(param->srv->so.state, param->remsock, (struct sockaddr *)&param->sinsl, SASIZE(&param->sinsl))) { RETURN(203); } if(param->srv->so._bind(param->srv->so.state, param->remsock, (struct sockaddr *)&param->sinsl, SASIZE(&param->sinsl))) { RETURN(203); }
#ifdef _WIN32 #ifdef _WIN32
{ unsigned long ul2 = 1; ioctlsocket(param->remsock, FIONBIO, &ul2); } { unsigned long ul2 = 1; ioctlsocket(param->remsock, FIONBIO, &ul2); }