diff --git a/man/3proxy.cfg.5 b/man/3proxy.cfg.5 index 416bb17..11d4cdb 100644 --- a/man/3proxy.cfg.5 +++ b/man/3proxy.cfg.5 @@ -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. diff --git a/src/common.c b/src/common.c index ea5e472..f85135e 100644 --- a/src/common.c +++ b/src/common.c @@ -52,6 +52,11 @@ int switch_ns(struct srvparam *srv, int target_fd) { } #endif +#ifdef __linux__ +#ifndef IP_LOCAL_PORT_RANGE +#define IP_LOCAL_PORT_RANGE 51 +#endif +#endif char * copyright = COPYRIGHT; @@ -722,6 +727,7 @@ int doconnect(struct clientparam * param){ } *SAPORT(¶m->sinsl) = 0; setopts(param->remsock, param->srv->srvsockopts); + if(set_local_port_range(param->srv, param->remsock, (struct sockaddr *)¶m->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 @@ -767,6 +773,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; diff --git a/src/dnspr.c b/src/dnspr.c index d6372e7..afbbf1b 100644 --- a/src/dnspr.c +++ b/src/dnspr.c @@ -149,6 +149,10 @@ void * dnsprchild(struct clientparam* param) { } memset(¶m->sinsl, 0, sizeof(param->sinsl)); *SAFAMILY(¶m->sinsl) = *SAFAMILY(&nservers[0].addr); + if(set_local_port_range(param->srv, param->remsock, (struct sockaddr *)¶m->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 *)¶m->sinsl,SASIZE(¶m->sinsl))) { RETURN(819); } @@ -222,4 +226,3 @@ CLEANRET: #endif return (NULL); } - diff --git a/src/ftppr.c b/src/ftppr.c index fbd2b4f..aafea01 100644 --- a/src/ftppr.c +++ b/src/ftppr.c @@ -121,6 +121,7 @@ void * ftpprchild(struct clientparam* param) { } if ((clidatasock=socket(SASOCK(¶m->sincl), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN(821);} *SAPORT(¶m->sincl) = 0; + if(set_local_port_range(param->srv, clidatasock, (struct sockaddr *)¶m->sincl, LOCAL_PORT_RANGE_TCP)){RETURN(822);} if(param->srv->so._bind(param->sostate, clidatasock, (struct sockaddr *)¶m->sincl, SASIZE(¶m->sincl))){RETURN(822);} if (pasv) { if(param->srv->so._listen(param->sostate, clidatasock, 1)) {RETURN(823);} diff --git a/src/proxy.h b/src/proxy.h index 7a65f75..fada5ad 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -352,6 +352,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); @@ -433,4 +437,3 @@ extern char * ceargv[32]; #define WEBBANNERS 35 #endif - diff --git a/src/proxymain.c b/src/proxymain.c index a09bcaa..520b1d4 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -345,6 +345,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" @@ -568,6 +569,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; @@ -590,7 +620,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); @@ -1797,5 +1847,3 @@ FILTER_ACTION handledatfltsrv(struct clientparam *cparam, unsigned char ** buf_p } return PASS; } - - diff --git a/src/resolve.c b/src/resolve.c index 5b9da88..0998195 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -60,6 +60,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); @@ -228,4 +232,4 @@ uint32_t fakeresolver (int af, unsigned char *name, unsigned char * value){ value[3] = 2; } return 1; -} \ No newline at end of file +} diff --git a/src/socks.c b/src/socks.c index 76e77d3..b25ac0e 100644 --- a/src/socks.c +++ b/src/socks.c @@ -218,6 +218,8 @@ void * sockschild(struct clientparam* param) { if((res = udpbind(param))) {RETURN(res);} } else if(command == 2) { + if(set_local_port_range(param->srv, param->remsock, (struct sockaddr *)¶m->sinsl, + LOCAL_PORT_RANGE_TCP)) RETURN(12); if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl))) { *SAPORT(¶m->sinsl) = 0; if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl)))RETURN (12); @@ -243,6 +245,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); diff --git a/src/structures.h b/src/structures.h index 21cea79..259468b 100644 --- a/src/structures.h +++ b/src/structures.h @@ -590,6 +590,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 { diff --git a/src/udpsockmap.c b/src/udpsockmap.c index 46048f7..be864c5 100644 --- a/src/udpsockmap.c +++ b/src/udpsockmap.c @@ -121,6 +121,12 @@ int udpbind(struct clientparam *param) fcntl(s, F_SETFL, O_NONBLOCK | fcntl(s, F_GETFL)); #endif param->remsock = s; + if (set_local_port_range(param->srv, param->remsock, + (struct sockaddr *)¶m->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, (struct sockaddr *)¶m->sinsl, SASIZE(¶m->sinsl))) { *SAPORT(¶m->sinsl) = 0;