Add per-service local port ranges

This commit is contained in:
yazhog 2026-08-08 23:22:02 +03:00
parent 8f62e42e7d
commit a5584f9bdf
10 changed files with 104 additions and 6 deletions

View File

@ -176,6 +176,9 @@ listen on given local HOST:port for incoming connections instead of making remot
.B -r\fIHOST\fB:\fIport\fR .B -r\fIHOST\fB:\fIport\fR
connect to given remote HOST:port instead of listening local connection on -p or default port. Can be used with another 3proxy service running -R option for connect back functionality. Most commonly used with proxy or socks. HOST can be given as IP or hostname, useful in case of dynamic DNS. connect to given remote HOST:port instead of listening local connection on -p or default port. Can be used with another 3proxy service running -R option for connect back functionality. Most commonly used with proxy or socks. HOST can be given as IP or hostname, useful in case of dynamic DNS.
.br .br
.B -Tr\fIFIRST\fB-\fILAST\fR, -Ur\fIFIRST\fB-\fILAST\fR, -UAr\fIFIRST\fB-\fILAST\fR
(Linux) use the inclusive local ephemeral port range FIRST-LAST for outgoing IPv4 TCP (-Tr) or UDP (-Ur) sockets of this service. -UAr applies only to the UDP relay socket returned to a SOCKS5 UDP ASSOCIATE client. Use it when this port range must be forwarded by NAT and allowed by a firewall. Requires kernel support for IP_LOCAL_PORT_RANGE (Linux 6.3 or newer). The option is applied before bind(2); no system-wide sysctl is changed. For example, \fBsocks -p1080 -Ur20000-30000 -UAr30000-31000\fR.
.br
.B -oc\fIOPTIONS\fB, -os\fIOPTIONS\fB, -ol\fIOPTIONS\fB, -or\fIOPTIONS\fB, -oR\fIOPTIONS\fR .B -oc\fIOPTIONS\fB, -os\fIOPTIONS\fB, -ol\fIOPTIONS\fB, -or\fIOPTIONS\fB, -oR\fIOPTIONS\fR
options for proxy-to-client (\fB-oc\fR), proxy-to-server (\fB-os\fR), proxy listening (\fB-ol\fR), connect back client (\fB-or\fR), connect back listening (\fB-oR\fR) sockets. options for proxy-to-client (\fB-oc\fR), proxy-to-server (\fB-os\fR), proxy listening (\fB-ol\fR), connect back client (\fB-or\fR), connect back listening (\fB-oR\fR) sockets.
Options like TCP_CORK, TCP_NODELAY, TCP_DEFER_ACCEPT, TCP_QUICKACK, TCP_TIMESTAMPS, TCP_FASTOPEN, SO_REUSEADDR, SO_REUSEPORT, SO_EXCLUSIVEADDRUSE, SO_PORT_SCALABILITY, SO_REUSE_UNICASTPORT, SO_KEEPALIVE, SO_DONTROUTE may be supported depending on OS. Options like TCP_CORK, TCP_NODELAY, TCP_DEFER_ACCEPT, TCP_QUICKACK, TCP_TIMESTAMPS, TCP_FASTOPEN, SO_REUSEADDR, SO_REUSEPORT, SO_EXCLUSIVEADDRUSE, SO_PORT_SCALABILITY, SO_REUSE_UNICASTPORT, SO_KEEPALIVE, SO_DONTROUTE may be supported depending on OS.

View File

@ -52,6 +52,11 @@ int switch_ns(struct srvparam *srv, int target_fd) {
} }
#endif #endif
#ifdef __linux__
#ifndef IP_LOCAL_PORT_RANGE
#define IP_LOCAL_PORT_RANGE 51
#endif
#endif
char * copyright = COPYRIGHT; char * copyright = COPYRIGHT;
@ -722,6 +727,7 @@ 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);
if(set_local_port_range(param->srv, param->remsock, (struct sockaddr *)&param->sinsl, LOCAL_PORT_RANGE_TCP)) return 12;
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
@ -767,6 +773,24 @@ int doconnect(struct clientparam * param){
return 0; return 0;
} }
int set_local_port_range(struct srvparam *srv, SOCKET sock, const struct sockaddr *sa, int range_type){
#ifdef __linux__
uint32_t range = range_type == LOCAL_PORT_RANGE_TCP ? srv->tcp_local_port_range :
range_type == LOCAL_PORT_RANGE_UDP ? srv->udp_local_port_range : srv->udp_associate_port_range;
if(range && sa->sa_family == AF_INET &&
((const struct sockaddr_in *)sa)->sin_port == 0 &&
srv->so._setsockopt(srv->so.state, sock, IPPROTO_IP, IP_LOCAL_PORT_RANGE,
(char *)&range, sizeof(range))) return -1;
#else
(void)srv;
(void)sock;
(void)sa;
(void)range_type;
#endif
return 0;
}
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

@ -149,6 +149,10 @@ void * dnsprchild(struct clientparam* param) {
} }
memset(&param->sinsl, 0, sizeof(param->sinsl)); memset(&param->sinsl, 0, sizeof(param->sinsl));
*SAFAMILY(&param->sinsl) = *SAFAMILY(&nservers[0].addr); *SAFAMILY(&param->sinsl) = *SAFAMILY(&nservers[0].addr);
if(set_local_port_range(param->srv, param->remsock, (struct sockaddr *)&param->sinsl,
nservers[0].usetcp ? LOCAL_PORT_RANGE_TCP : LOCAL_PORT_RANGE_UDP)) {
RETURN(819);
}
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))) {
RETURN(819); RETURN(819);
} }
@ -222,4 +226,3 @@ CLEANRET:
#endif #endif
return (NULL); return (NULL);
} }

View File

@ -121,6 +121,7 @@ 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;
if(set_local_port_range(param->srv, clidatasock, (struct sockaddr *)&param->sincl, LOCAL_PORT_RANGE_TCP)){RETURN(822);}
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

@ -352,6 +352,10 @@ 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);
#define LOCAL_PORT_RANGE_TCP 1
#define LOCAL_PORT_RANGE_UDP 2
#define LOCAL_PORT_RANGE_UDP_ASSOCIATE 3
int set_local_port_range(struct srvparam *srv, SOCKET sock, const struct sockaddr *sa, int range_type);
uint32_t myrand(void); uint32_t myrand(void);
@ -433,4 +437,3 @@ extern char * ceargv[32];
#define WEBBANNERS 35 #define WEBBANNERS 35
#endif #endif

View File

@ -345,6 +345,7 @@ int MODULEMAINFUNC (int argc, char** argv){
"\n" "\n"
" -iIP ip address or internal interface (clients are expected to connect)\n" " -iIP ip address or internal interface (clients are expected to connect)\n"
" -eIP ip address or external interface (outgoing connection will have this)\n" " -eIP ip address or external interface (outgoing connection will have this)\n"
" -TrFIRST-LAST, -UrFIRST-LAST, -UArFIRST-LAST Linux: local TCP, UDP or UDP ASSOCIATE ranges\n"
" -rHOST:PORT Use IP:port for connect back proxy instead of listen port\n" " -rHOST:PORT Use IP:port for connect back proxy instead of listen port\n"
" -RHOST:PORT Use PORT to listen connect back proxy connection to pass data to\n" " -RHOST:PORT Use PORT to listen connect back proxy connection to pass data to\n"
" -4 Use IPv4 for outgoing connections\n" " -4 Use IPv4 for outgoing connections\n"
@ -568,6 +569,35 @@ int MODULEMAINFUNC (int argc, char** argv){
srv.needuser = 0; srv.needuser = 0;
if(*(argv[i] + 2)) srv.needuser = atoi(argv[i] + 2); if(*(argv[i] + 2)) srv.needuser = atoi(argv[i] + 2);
break; break;
case 'U':
#ifdef __linux__
{
char *end;
const char *range_arg;
unsigned long first, last;
uint32_t *range;
if(argv[i][2] == 'r') {
range_arg = argv[i] + 3;
range = &srv.udp_local_port_range;
}
else if(argv[i][2] == 'A' && argv[i][3] == 'r') {
range_arg = argv[i] + 4;
range = &srv.udp_associate_port_range;
}
else { error = 1; break; }
errno = 0;
first = strtoul(range_arg, &end, 10);
if(errno || end == range_arg || *end != '-') { error = 1; break; }
errno = 0;
last = strtoul(end + 1, &end, 10);
if(errno || *end || !first || !last || first > last || last > 65535) { error = 1; break; }
*range = ((uint32_t)last << 16) | (uint32_t)first;
}
#else
error = 1;
#endif
break;
case 'x': case 'x':
srv.nostarttls = 1; srv.nostarttls = 1;
break; break;
@ -590,7 +620,27 @@ int MODULEMAINFUNC (int argc, char** argv){
} }
break; break;
case 'T': case 'T':
srv.transparent = 1; if(!argv[i][2]) {
srv.transparent = 1;
break;
}
#ifdef __linux__
{
char *end;
unsigned long first, last;
if(argv[i][2] != 'r') { error = 1; break; }
errno = 0;
first = strtoul(argv[i] + 3, &end, 10);
if(errno || end == argv[i] + 3 || *end != '-') { error = 1; break; }
errno = 0;
last = strtoul(end + 1, &end, 10);
if(errno || *end || !first || !last || first > last || last > 65535) { error = 1; break; }
srv.tcp_local_port_range = ((uint32_t)last << 16) | (uint32_t)first;
}
#else
error = 1;
#endif
break; break;
case 'S': case 'S':
srv.stacksize = atoi(argv[i]+2); srv.stacksize = atoi(argv[i]+2);
@ -1797,5 +1847,3 @@ FILTER_ACTION handledatfltsrv(struct clientparam *cparam, unsigned char ** buf_p
} }
return PASS; return PASS;
} }

View File

@ -60,6 +60,10 @@ uint32_t udpresolve(int af, unsigned char * name, unsigned char * value, uint32_
*SAFAMILY(sinsl) = *SAFAMILY(&nservers[i].addr); *SAFAMILY(sinsl) = *SAFAMILY(&nservers[i].addr);
} }
if((sock=so._socket(so.state, SASOCK(sinsl), usetcp?SOCK_STREAM:SOCK_DGRAM, usetcp?IPPROTO_TCP:IPPROTO_UDP)) == INVALID_SOCKET) break; if((sock=so._socket(so.state, SASOCK(sinsl), usetcp?SOCK_STREAM:SOCK_DGRAM, usetcp?IPPROTO_TCP:IPPROTO_UDP)) == INVALID_SOCKET) break;
if(param && set_local_port_range(param->srv, sock, (struct sockaddr *)sinsl, usetcp ? LOCAL_PORT_RANGE_TCP : LOCAL_PORT_RANGE_UDP)) {
so._closesocket(so.state, sock);
break;
}
if(so._bind(so.state, sock,(struct sockaddr *)sinsl,SASIZE(sinsl))){ if(so._bind(so.state, sock,(struct sockaddr *)sinsl,SASIZE(sinsl))){
so._shutdown(so.state, sock, SHUT_RDWR); so._shutdown(so.state, sock, SHUT_RDWR);
so._closesocket(so.state, sock); so._closesocket(so.state, sock);
@ -228,4 +232,4 @@ uint32_t fakeresolver (int af, unsigned char *name, unsigned char * value){
value[3] = 2; value[3] = 2;
} }
return 1; return 1;
} }

View File

@ -218,6 +218,8 @@ void * sockschild(struct clientparam* param) {
if((res = udpbind(param))) {RETURN(res);} if((res = udpbind(param))) {RETURN(res);}
} }
else if(command == 2) { else if(command == 2) {
if(set_local_port_range(param->srv, param->remsock, (struct sockaddr *)&param->sinsl,
LOCAL_PORT_RANGE_TCP)) RETURN(12);
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);
@ -243,6 +245,7 @@ fflush(stderr);
#endif #endif
sin = param->sincl; sin = param->sincl;
*SAPORT(&sin) = 0; *SAPORT(&sin) = 0;
if(set_local_port_range(param->srv, param->clisock, (struct sockaddr *)&sin, LOCAL_PORT_RANGE_UDP_ASSOCIATE)) {RETURN (12);}
if(param->srv->so._bind(param->sostate, param->clisock,(struct sockaddr *)&sin,SASIZE(&sin))) {RETURN (12);} if(param->srv->so._bind(param->sostate, param->clisock,(struct sockaddr *)&sin,SASIZE(&sin))) {RETURN (12);}
sasize = SASIZE(&sin); sasize = SASIZE(&sin);
param->srv->so._getsockname(param->sostate, param->clisock, (struct sockaddr *)&sin, &sasize); param->srv->so._getsockname(param->sostate, param->clisock, (struct sockaddr *)&sin, &sasize);

View File

@ -590,6 +590,9 @@ struct srvparam {
unsigned char *udpbuf; unsigned char *udpbuf;
unsigned char *udpbuf2; unsigned char *udpbuf2;
int udplen; int udplen;
uint32_t tcp_local_port_range;
uint32_t udp_local_port_range;
uint32_t udp_associate_port_range;
}; };
struct clientparam { struct clientparam {

View File

@ -121,6 +121,12 @@ int udpbind(struct clientparam *param)
fcntl(s, F_SETFL, O_NONBLOCK | fcntl(s, F_GETFL)); fcntl(s, F_SETFL, O_NONBLOCK | fcntl(s, F_GETFL));
#endif #endif
param->remsock = s; param->remsock = s;
if (set_local_port_range(param->srv, param->remsock,
(struct sockaddr *)&param->sinsl, LOCAL_PORT_RANGE_UDP)) {
param->srv->so._closesocket(param->sostate, param->remsock);
param->remsock = INVALID_SOCKET;
return 12;
}
if (param->srv->so._bind(param->sostate, param->remsock, if (param->srv->so._bind(param->sostate, param->remsock,
(struct sockaddr *)&param->sinsl, SASIZE(&param->sinsl))) { (struct sockaddr *)&param->sinsl, SASIZE(&param->sinsl))) {
*SAPORT(&param->sinsl) = 0; *SAPORT(&param->sinsl) = 0;