mirror of
https://github.com/3proxy/3proxy.git
synced 2026-08-26 01:45:49 +08:00
port range support (parent extport / intport)
This commit is contained in:
parent
1565c67c13
commit
6ca4a2686d
@ -879,6 +879,10 @@ with probability of 0.7) for outgoing web connections. Chains are only applied t
|
||||
type is one of:
|
||||
.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.
|
||||
.br
|
||||
\fBextport\fR does not redirect the request; it sets the range the local port of outgoing connections is taken from, given as \fIFIRST-LAST\fR inclusive in place of the port argument, with 0.0.0.0 as the address, for example \fBparent 1000 extport 0.0.0.0 40000-40100\fR. Where the system can be asked to pick the port itself (Linux \fBIP_LOCAL_PORT_RANGE\fR) it does, otherwise a port is picked at random from the range and retried if it is already in use, up to ten times. It can be chained with another parent type, and the access rule it belongs to decides which requests it applies to, so \fBallow * * * * UDPASSOC\fR followed by \fBparent 1000 extport 0.0.0.0 40000-40100\fR limits it to UDP associations. The range is applied when the outgoing connection is made, so a kept alive connection carrying several requests uses the rule that matched when it was opened.
|
||||
.br
|
||||
\fBintport\fR is the same for sockets bound on the side facing the client: the port a UDP association tells the client to send its datagrams to, and the FTP proxy data connection.
|
||||
.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.
|
||||
.br
|
||||
|
||||
@ -164,7 +164,10 @@ int checkACL(struct clientparam * param){
|
||||
continue;
|
||||
}
|
||||
param->lastace = acentry;
|
||||
if(param->preauth) return 2;
|
||||
if(param->preauth) {
|
||||
applyportranges(param, acentry);
|
||||
return 2;
|
||||
}
|
||||
if((param->operation == UDPASSOC)? (param->ctrlsocksrv != INVALID_SOCKET) : (param->remsock != INVALID_SOCKET)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
42
src/common.c
42
src/common.c
@ -746,7 +746,7 @@ int doconnect(struct clientparam * param){
|
||||
#ifdef WITH_UN
|
||||
if(*SAFAMILY(¶m->sinsl) != AF_UNIX)
|
||||
#endif
|
||||
if(param->srv->so._bind(param->sostate, param->remsock, (struct sockaddr*)¶m->sinsl, SASIZE(¶m->sinsl))==-1) {
|
||||
if(bindwithrange(param, param->remsock, ¶m->sinsl, param->extport)==-1) {
|
||||
return 12;
|
||||
}
|
||||
|
||||
@ -767,6 +767,46 @@ int doconnect(struct clientparam * param){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Number of ports tried before giving up when the range has to be searched by
|
||||
* hand. The kernel option picks a free port itself and needs no retries. */
|
||||
#define RANGETRIES 10
|
||||
|
||||
/* Bind sock to sa, taking the local port from the range if one is set. The
|
||||
* range is packed as first | last << 16.
|
||||
*
|
||||
* IP_LOCAL_PORT_RANGE leaves the choice to the kernel, which knows which ports
|
||||
* are free. Where the option does not exist, or the kernel refuses it, or the
|
||||
* address family is not one it covers, pick a port at random instead and retry
|
||||
* on failure, since the one picked may already be taken.
|
||||
*/
|
||||
int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range)
|
||||
{
|
||||
uint16_t first, last;
|
||||
int i;
|
||||
|
||||
if(!range) return param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa));
|
||||
|
||||
#ifdef IP_LOCAL_PORT_RANGE
|
||||
if(*SAFAMILY(sa) == AF_INET &&
|
||||
!param->srv->so._setsockopt(param->sostate, sock, IPPROTO_IP, IP_LOCAL_PORT_RANGE,
|
||||
(char *)&range, sizeof(range))){
|
||||
*SAPORT(sa) = 0;
|
||||
return param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa));
|
||||
}
|
||||
#endif
|
||||
|
||||
first = (uint16_t)(range & 0xffff);
|
||||
last = (uint16_t)(range >> 16);
|
||||
|
||||
for(i = 0; i < RANGETRIES; i++){
|
||||
*SAPORT(sa) = htons((uint16_t)(first + (myrand() % (unsigned)(last - first + 1))));
|
||||
if(!param->srv->so._bind(param->sostate, sock, (struct sockaddr *)sa, SASIZE(sa))) return 0;
|
||||
}
|
||||
|
||||
*SAPORT(sa) = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int scanaddr(const unsigned char *s, uint32_t * ip, uint32_t * mask) {
|
||||
unsigned d1, d2, d3, d4, m;
|
||||
int res;
|
||||
|
||||
36
src/conf.c
36
src/conf.c
@ -767,12 +767,32 @@ struct redirdesc redirs[] = {
|
||||
{R_SOCKS5B, "socks5b", sockschild},
|
||||
{R_ADMIN, "admin", adminchild},
|
||||
{R_EXTIP, "extip", NULL},
|
||||
{R_EXTPORT, "extport", NULL},
|
||||
{R_INTPORT, "intport", NULL},
|
||||
{R_TLS, "tls", tlsprchild},
|
||||
{R_HA, "ha", NULL},
|
||||
{R_DNS, "dns", dnsprchild},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Parses an inclusive FIRST-LAST local port range into first | last << 16. */
|
||||
static int parserange(unsigned char *arg, uint32_t *range)
|
||||
{
|
||||
char *end;
|
||||
unsigned long first, last;
|
||||
|
||||
errno = 0;
|
||||
first = strtoul((char *)arg, &end, 10);
|
||||
if(errno || *end != '-' || !first || first > 65535) return 1;
|
||||
|
||||
errno = 0;
|
||||
last = strtoul(end + 1, &end, 10);
|
||||
if(errno || *end || !last || last > 65535 || last < first) return 1;
|
||||
|
||||
*range = (uint32_t)first | ((uint32_t)last << 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h_parent(int argc, unsigned char **argv){
|
||||
struct ace *acl = NULL;
|
||||
struct chain *chains;
|
||||
@ -838,7 +858,21 @@ static int h_parent(int argc, unsigned char **argv){
|
||||
*cidr = '/';
|
||||
chains->cidr = atoi(cidr + 1);
|
||||
}
|
||||
*SAPORT(&chains->addr) = htons((uint16_t)atoi((char *)argv[4]));
|
||||
if(chains->type == R_EXTPORT || chains->type == R_INTPORT){
|
||||
if(!SAISNULL(&chains->addr)){
|
||||
fprintf(stderr, "Chaining error: chain type (%s) sets a local port range, it requires 0.0.0.0 as address on line %d\n", argv[2], linenum);
|
||||
free(chains->exthost);
|
||||
free(chains);
|
||||
return(4);
|
||||
}
|
||||
if(parserange(argv[4], &chains->range)){
|
||||
fprintf(stderr, "Chaining error: bad port range (%s) on line %d\n", argv[4], linenum);
|
||||
free(chains->exthost);
|
||||
free(chains);
|
||||
return(3);
|
||||
}
|
||||
}
|
||||
else *SAPORT(&chains->addr) = htons((uint16_t)atoi((char *)argv[4]));
|
||||
switch(chains->type){
|
||||
case R_POP3:
|
||||
case R_SMTP:
|
||||
|
||||
@ -149,7 +149,7 @@ void * dnsprchild(struct clientparam* param) {
|
||||
}
|
||||
memset(¶m->sinsl, 0, sizeof(param->sinsl));
|
||||
*SAFAMILY(¶m->sinsl) = *SAFAMILY(&nservers[0].addr);
|
||||
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl))) {
|
||||
if(bindwithrange(param, param->remsock, ¶m->sinsl, param->extport)) {
|
||||
RETURN(819);
|
||||
}
|
||||
param->sinsr = nservers[0].addr;
|
||||
|
||||
@ -121,7 +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(param->srv->so._bind(param->sostate, clidatasock, (struct sockaddr *)¶m->sincl, SASIZE(¶m->sincl))){RETURN(822);}
|
||||
if(bindwithrange(param, clidatasock, ¶m->sincl, param->intport)){RETURN(822);}
|
||||
if (pasv) {
|
||||
if(param->srv->so._listen(param->sostate, clidatasock, 1)) {RETURN(823);}
|
||||
sasize = sizeof(param->sincl);
|
||||
|
||||
@ -352,6 +352,8 @@ 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);
|
||||
int bindwithrange(struct clientparam *param, SOCKET sock, PROXYSOCKADDRTYPE *sa, uint32_t range);
|
||||
void applyportranges(struct clientparam * param, struct ace * acentry);
|
||||
|
||||
|
||||
uint32_t myrand(void);
|
||||
|
||||
@ -259,6 +259,20 @@ int clientnegotiate(struct chain * redir, struct clientparam * param, struct soc
|
||||
}
|
||||
|
||||
|
||||
/* The local port ranges do not depend on the destination, so they can be taken
|
||||
* as soon as a rule matches. UDP ASSOCIATE is authorized before the destination
|
||||
* is known and returns before the chain is walked, which would otherwise leave
|
||||
* the socket the client sends its datagrams to outside the configured range.
|
||||
*/
|
||||
void applyportranges(struct clientparam * param, struct ace * acentry){
|
||||
struct chain *cur;
|
||||
|
||||
for(cur = acentry->chains; cur; cur = cur->next){
|
||||
if(cur->type == R_EXTPORT) param->extport = cur->range;
|
||||
else if(cur->type == R_INTPORT) param->intport = cur->range;
|
||||
}
|
||||
}
|
||||
|
||||
int handleredirect(struct clientparam * param, struct ace * acentry){
|
||||
int connected = 0;
|
||||
int weight = 1000;
|
||||
@ -285,7 +299,8 @@ int handleredirect(struct clientparam * param, struct ace * acentry){
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(cur->type != R_EXTIP && cur->type != R_HA) param->redirected++;
|
||||
if(cur->type != R_EXTIP && cur->type != R_HA &&
|
||||
cur->type != R_EXTPORT && cur->type != R_INTPORT) param->redirected++;
|
||||
done = 1;
|
||||
if(weight <= 0) {
|
||||
weight += 1000;
|
||||
@ -293,6 +308,12 @@ int handleredirect(struct clientparam * param, struct ace * acentry){
|
||||
r2 = (myrand()%1000);
|
||||
}
|
||||
if(!connected){
|
||||
if(cur->type == R_EXTPORT || cur->type == R_INTPORT){
|
||||
if(cur->type == R_EXTPORT) param->extport = cur->range;
|
||||
else param->intport = cur->range;
|
||||
if(cur->next)continue;
|
||||
return 0;
|
||||
}
|
||||
if(cur->type == R_EXTIP){
|
||||
param->sinsl = cur->addr;
|
||||
if(SAISNULL(¶m->sinsl) && (*SAFAMILY(¶m->sincr) == AF_INET || *SAFAMILY(¶m->sincr) == AF_INET6))param->sinsl = param->sincr;
|
||||
|
||||
@ -218,7 +218,10 @@ void * sockschild(struct clientparam* param) {
|
||||
if((res = udpbind(param))) {RETURN(res);}
|
||||
}
|
||||
else if(command == 2) {
|
||||
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl))) {
|
||||
if(bindwithrange(param, param->remsock, ¶m->sinsl, param->extport)) {
|
||||
/* a range has already been searched, retrying on any port would
|
||||
ignore what was asked for */
|
||||
if(param->extport) RETURN (12);
|
||||
*SAPORT(¶m->sinsl) = 0;
|
||||
if(param->srv->so._bind(param->sostate, param->remsock,(struct sockaddr *)¶m->sinsl,SASIZE(¶m->sinsl)))RETURN (12);
|
||||
#if SOCKSTRACE > 0
|
||||
@ -243,7 +246,8 @@ fflush(stderr);
|
||||
#endif
|
||||
sin = param->sincl;
|
||||
*SAPORT(&sin) = 0;
|
||||
if(param->srv->so._bind(param->sostate, param->clisock,(struct sockaddr *)&sin,SASIZE(&sin))) {RETURN (12);}
|
||||
/* the port the client is told to send its datagrams to */
|
||||
if(bindwithrange(param, param->clisock, &sin, param->intport)) {RETURN (12);}
|
||||
sasize = SASIZE(&sin);
|
||||
param->srv->so._getsockname(param->sostate, param->clisock, (struct sockaddr *)&sin, &sasize);
|
||||
#if SOCKSTRACE > 0
|
||||
|
||||
@ -313,7 +313,9 @@ typedef enum {
|
||||
R_TLS,
|
||||
R_HA,
|
||||
R_DNS,
|
||||
R_IMAP
|
||||
R_IMAP,
|
||||
R_EXTPORT,
|
||||
R_INTPORT
|
||||
} REDIRTYPE;
|
||||
|
||||
struct redirdesc {
|
||||
@ -335,6 +337,8 @@ struct chain {
|
||||
unsigned char * extpass;
|
||||
unsigned short weight;
|
||||
unsigned short cidr;
|
||||
/* local port range for extport/intport, first in the low half */
|
||||
uint32_t range;
|
||||
};
|
||||
|
||||
struct period {
|
||||
@ -669,6 +673,7 @@ struct clientparam {
|
||||
maxtrafout64;
|
||||
PROXYSOCKADDRTYPE sincl, sincr;
|
||||
PROXYSOCKADDRTYPE sinsl, sinsr, req;
|
||||
uint32_t extport, intport;
|
||||
|
||||
uint64_t statscli64,
|
||||
statssrv64;
|
||||
|
||||
@ -121,8 +121,12 @@ int udpbind(struct clientparam *param)
|
||||
fcntl(s, F_SETFL, O_NONBLOCK | fcntl(s, F_GETFL));
|
||||
#endif
|
||||
param->remsock = s;
|
||||
if (param->srv->so._bind(param->sostate, param->remsock,
|
||||
(struct sockaddr *)¶m->sinsl, SASIZE(¶m->sinsl))) {
|
||||
if (bindwithrange(param, param->remsock, ¶m->sinsl, param->extport)) {
|
||||
if (param->extport) {
|
||||
param->srv->so._closesocket(param->sostate, param->remsock);
|
||||
param->remsock = INVALID_SOCKET;
|
||||
return 12;
|
||||
}
|
||||
*SAPORT(¶m->sinsl) = 0;
|
||||
if (param->srv->so._bind(param->sostate, param->remsock,
|
||||
(struct sockaddr *)¶m->sinsl, SASIZE(¶m->sinsl))) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user