From 8e719ff0ff16289ad4f487283fe094d402a937ef Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 26 Jan 2018 14:56:55 +0800 Subject: [PATCH] frps: new params max_ports_per_client --- client/control.go | 2 +- conf/frps_full.ini | 3 +++ models/config/server_common.go | 26 ++++++++++++++++++++++++-- server/control.go | 31 ++++++++++++++++++++++++++++++- server/proxy.go | 17 +++++++++++++---- 5 files changed, 71 insertions(+), 8 deletions(-) diff --git a/client/control.go b/client/control.go index a9c4511b..1b31ace7 100644 --- a/client/control.go +++ b/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) diff --git a/conf/frps_full.ini b/conf/frps_full.ini index eac88269..e2a7c3f0 100644 --- a/conf/frps_full.ini +++ b/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 diff --git a/models/config/server_common.go b/models/config/server_common.go index 614b95cb..51880a70 100644 --- a/models/config/server_common.go +++ b/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) diff --git a/server/control.go b/server/control.go index 7492ce4b..dbb99ad9 100644 --- a/server/control.go +++ b/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 } diff --git a/server/proxy.go b/server/proxy.go index 554e8181..715bf0c0 100644 --- a/server/proxy.go +++ b/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,