Переглянути джерело

frps: new params max_ports_per_client

fatedier 7 роки тому
батько
коміт
8e719ff0ff
5 змінених файлів з 71 додано та 8 видалено
  1. 1 1
      client/control.go
  2. 3 0
      conf/frps_full.ini
  3. 24 2
      models/config/server_common.go
  4. 30 1
      server/control.go
  5. 13 4
      server/proxy.go

+ 1 - 1
client/control.go

@@ -427,7 +427,7 @@ func (ctl *Control) worker() {
 				go ctl.reader()
 
 				// start all configured proxies
-				ctl.pm.CheckAndStartProxy([]string{ProxyStatusNew})
+				ctl.pm.CheckAndStartProxy([]string{ProxyStatusNew, ProxyStatusClosed})
 
 				checkProxyTicker.Stop()
 				checkProxyTicker = time.NewTicker(checkInterval)

+ 3 - 0
conf/frps_full.ini

@@ -52,6 +52,9 @@ privilege_allow_ports = 2000-3000,3001,3003,4000-50000
 # pool_count in each proxy will change to max_pool_count if they exceed the maximum value
 max_pool_count = 5
 
+# max ports can be used for each client, default value is 0 means no limit
+max_ports_per_client = 0
+
 # authentication_timeout means the timeout interval (seconds) when the frpc connects frps
 # if authentication_timeout is zero, the time is not verified, default is 900s
 authentication_timeout = 900

+ 24 - 2
models/config/server_common.go

@@ -59,6 +59,7 @@ type ServerCommonConf struct {
 
 	PrivilegeAllowPorts map[int]struct{}
 	MaxPoolCount        int64
+	MaxPortsPerClient   int64
 	HeartBeatTimeout    int64
 	UserConnTimeout     int64
 }
@@ -89,6 +90,7 @@ func GetDefaultServerCommonConf() *ServerCommonConf {
 		TcpMux:              true,
 		PrivilegeAllowPorts: make(map[int]struct{}),
 		MaxPoolCount:        5,
+		MaxPortsPerClient:   0,
 		HeartBeatTimeout:    90,
 		UserConnTimeout:     10,
 	}
@@ -254,12 +256,32 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 
 	tmpStr, ok = conf.Get("common", "max_pool_count")
 	if ok {
-		v, err = strconv.ParseInt(tmpStr, 10, 64)
-		if err == nil && v >= 0 {
+		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
+			err = fmt.Errorf("Parse conf error: invalid max_pool_count")
+			return
+		} else {
+			if v < 0 {
+				err = fmt.Errorf("Parse conf error: invalid max_pool_count")
+				return
+			}
 			cfg.MaxPoolCount = v
 		}
 	}
 
+	tmpStr, ok = conf.Get("common", "max_ports_per_client")
+	if ok {
+		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
+			err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
+			return
+		} else {
+			if v < 0 {
+				err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
+				return
+			}
+			cfg.MaxPortsPerClient = v
+		}
+	}
+
 	tmpStr, ok = conf.Get("common", "authentication_timeout")
 	if ok {
 		v, errRet := strconv.ParseInt(tmpStr, 10, 64)

+ 30 - 1
server/control.go

@@ -55,6 +55,9 @@ type Control struct {
 	// pool count
 	poolCount int
 
+	// ports used, for limitations
+	portsUsedNum int
+
 	// last time got the Ping message
 	lastPing time.Time
 
@@ -84,6 +87,7 @@ func NewControl(svr *Service, ctlConn net.Conn, loginMsg *msg.Login) *Control {
 		workConnCh:      make(chan net.Conn, loginMsg.PoolCount+10),
 		proxies:         make(map[string]Proxy),
 		poolCount:       loginMsg.PoolCount,
+		portsUsedNum:    0,
 		lastPing:        time.Now(),
 		runId:           loginMsg.RunId,
 		status:          consts.Working,
@@ -348,6 +352,26 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
 		return remoteAddr, err
 	}
 
+	// Check ports used number in each client
+	if config.ServerCommonCfg.MaxPortsPerClient > 0 {
+		ctl.mu.Lock()
+		if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(config.ServerCommonCfg.MaxPortsPerClient) {
+			ctl.mu.Unlock()
+			err = fmt.Errorf("exceed the max_ports_per_client")
+			return
+		}
+		ctl.portsUsedNum = ctl.portsUsedNum + pxy.GetUsedPortsNum()
+		ctl.mu.Unlock()
+
+		defer func() {
+			if err != nil {
+				ctl.mu.Lock()
+				ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum()
+				ctl.mu.Unlock()
+			}
+		}()
+	}
+
 	remoteAddr, err = pxy.Run()
 	if err != nil {
 		return
@@ -371,16 +395,21 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
 
 func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
 	ctl.mu.Lock()
-	defer ctl.mu.Unlock()
 
 	pxy, ok := ctl.proxies[closeMsg.ProxyName]
 	if !ok {
+		ctl.mu.Unlock()
 		return
 	}
 
+	if config.ServerCommonCfg.MaxPortsPerClient > 0 {
+		ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum()
+	}
 	pxy.Close()
 	ctl.svr.DelProxy(pxy.GetName())
 	delete(ctl.proxies, closeMsg.ProxyName)
+	ctl.mu.Unlock()
+
 	StatsCloseProxy(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
 	return
 }

+ 13 - 4
server/proxy.go

@@ -40,15 +40,18 @@ type Proxy interface {
 	GetName() string
 	GetConf() config.ProxyConf
 	GetWorkConnFromPool() (workConn frpNet.Conn, err error)
+	GetUsedPortsNum() int
 	Close()
 	log.Logger
 }
 
 type BaseProxy struct {
-	name      string
-	ctl       *Control
-	listeners []frpNet.Listener
-	mu        sync.RWMutex
+	name         string
+	ctl          *Control
+	listeners    []frpNet.Listener
+	usedPortsNum int
+
+	mu sync.RWMutex
 	log.Logger
 }
 
@@ -60,6 +63,10 @@ func (pxy *BaseProxy) GetControl() *Control {
 	return pxy.ctl
 }
 
+func (pxy *BaseProxy) GetUsedPortsNum() int {
+	return pxy.usedPortsNum
+}
+
 func (pxy *BaseProxy) Close() {
 	pxy.Info("proxy closing")
 	for _, l := range pxy.listeners {
@@ -126,6 +133,7 @@ func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) {
 	}
 	switch cfg := pxyConf.(type) {
 	case *config.TcpProxyConf:
+		basePxy.usedPortsNum = 1
 		pxy = &TcpProxy{
 			BaseProxy: basePxy,
 			cfg:       cfg,
@@ -141,6 +149,7 @@ func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) {
 			cfg:       cfg,
 		}
 	case *config.UdpProxyConf:
+		basePxy.usedPortsNum = 1
 		pxy = &UdpProxy{
 			BaseProxy: basePxy,
 			cfg:       cfg,