Add per-service local port ranges

This commit is contained in:
yazhog 2026-08-08 23:22:02 +03:00
parent c31fe8367f
commit adc6319ea4
10 changed files with 101 additions and 8 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
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
.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
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.

View File

@ -10,6 +10,11 @@
#include "proxy.h"
#ifdef __linux__
#ifndef IP_LOCAL_PORT_RANGE
#define IP_LOCAL_PORT_RANGE 51
#endif
#endif
char * copyright = COPYRIGHT;
@ -679,6 +684,7 @@ int doconnect(struct clientparam * param){
}
*SAPORT(&param->sinsl) = 0;
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));
#if defined SO_BINDTODEVICE
@ -724,6 +730,24 @@ int doconnect(struct clientparam * param){
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) {
unsigned d1, d2, d3, d4, m;
int res;

View File

@ -149,6 +149,10 @@ void * dnsprchild(struct clientparam* param) {
}
memset(&param->sinsl, 0, sizeof(param->sinsl));
*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))) {
RETURN(819);
}
@ -222,4 +226,3 @@ CLEANRET:
#endif
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);}
*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 (pasv) {
if(param->srv->so._listen(param->sostate, clidatasock, 1)) {RETURN(823);}

View File

@ -347,6 +347,10 @@ unsigned char * dologname (unsigned char *buf, unsigned char *name, const unsign
int readconfig(FILE * fp);
void initcommands(void);
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);
@ -428,4 +432,3 @@ extern char * ceargv[32];
#define WEBBANNERS 35
#endif

View File

@ -342,6 +342,7 @@ int MODULEMAINFUNC (int argc, char** argv){
"\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"
" -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 PORT to listen connect back proxy connection to pass data to\n"
" -4 Use IPv4 for outgoing connections\n"
@ -502,8 +503,8 @@ int MODULEMAINFUNC (int argc, char** argv){
}
break;
case 'N':
if(argv[i][3] == '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);
if(argv[i][2] == 'e') getip46(46, (unsigned char *)argv[i]+3, (struct sockaddr *)&srv.extNat);
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);
break;
case 'n':
@ -565,6 +566,35 @@ int MODULEMAINFUNC (int argc, char** argv){
srv.needuser = 0;
if(*(argv[i] + 2)) srv.needuser = atoi(argv[i] + 2);
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':
srv.nostarttls = 1;
break;
@ -587,7 +617,27 @@ int MODULEMAINFUNC (int argc, char** argv){
}
break;
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;
case 'S':
srv.stacksize = atoi(argv[i]+2);
@ -1791,5 +1841,3 @@ FILTER_ACTION handledatfltsrv(struct clientparam *cparam, unsigned char ** buf_p
}
return PASS;
}

View File

@ -44,6 +44,10 @@ uint32_t udpresolve(int af, unsigned char * name, unsigned char * value, uint32_
*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(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))){
so._shutdown(so.state, sock, SHUT_RDWR);
so._closesocket(so.state, sock);
@ -198,4 +202,4 @@ uint32_t fakeresolver (int af, unsigned char *name, unsigned char * value){
value[3] = 2;
}
return 1;
}
}

View File

@ -250,6 +250,8 @@ void * sockschild(struct clientparam* param) {
}
if(command > 1) {
if(set_local_port_range(param->srv, param->remsock, (struct sockaddr *)&param->sinsl,
command == 2 ? LOCAL_PORT_RANGE_TCP : LOCAL_PORT_RANGE_UDP)) RETURN(12);
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)&param->sinsl,SASIZE(&param->sinsl))) {
*SAPORT(&param->sinsl) = 0;
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)&param->sinsl,SASIZE(&param->sinsl)))RETURN (12);
@ -274,6 +276,7 @@ fflush(stderr);
#endif
sin = param->sincl;
*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);}
sasize = SASIZE(&sin);
param->srv->so._getsockname(param->sostate, param->clisock, (struct sockaddr *)&sin, &sasize);

View File

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

View File

@ -46,6 +46,7 @@ void * udppmchild(struct clientparam* param) {
*SAPORT(&param->sinsl) = 0;
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(set_local_port_range(param->srv, param->remsock, (struct sockaddr *)&param->sinsl, LOCAL_PORT_RANGE_UDP)) { RETURN(203); }
if(param->srv->so._bind(param->srv->so.state, param->remsock, (struct sockaddr *)&param->sinsl, SASIZE(&param->sinsl))) { RETURN(203); }
#ifdef _WIN32
{ unsigned long ul2 = 1; ioctlsocket(param->remsock, FIONBIO, &ul2); }