1
0
Эх сурвалжийг харах

server/proxy: simplify the code (#3488)

fatedier 1 жил өмнө
parent
commit
e1cef053be

+ 1 - 0
pkg/nathole/controller.go

@@ -130,6 +130,7 @@ func (c *Controller) ListenClient(name string, sk string, allowUsers []string) c
 	}
 	c.mu.Lock()
 	defer c.mu.Unlock()
+	// TODO(fatedier): return error if name already exists
 	c.clientCfgs[name] = cfg
 	return cfg.sidCh
 }

+ 16 - 5
server/proxy/http.go

@@ -17,10 +17,10 @@ package proxy
 import (
 	"io"
 	"net"
+	"reflect"
 	"strings"
 
 	libio "github.com/fatedier/golib/io"
-	"golang.org/x/time/rate"
 
 	"github.com/fatedier/frp/pkg/config"
 	"github.com/fatedier/frp/pkg/util/limit"
@@ -30,6 +30,10 @@ import (
 	"github.com/fatedier/frp/server/metrics"
 )
 
+func init() {
+	RegisterProxyFactory(reflect.TypeOf(&config.HTTPProxyConf{}), NewHTTPProxy)
+}
+
 type HTTPProxy struct {
 	*BaseProxy
 	cfg *config.HTTPProxyConf
@@ -37,6 +41,17 @@ type HTTPProxy struct {
 	closeFuncs []func()
 }
 
+func NewHTTPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
+	unwrapped, ok := cfg.(*config.HTTPProxyConf)
+	if !ok {
+		return nil
+	}
+	return &HTTPProxy{
+		BaseProxy: baseProxy,
+		cfg:       unwrapped,
+	}
+}
+
 func (pxy *HTTPProxy) Run() (remoteAddr string, err error) {
 	xl := pxy.xl
 	routeConfig := vhost.RouteConfig{
@@ -137,10 +152,6 @@ func (pxy *HTTPProxy) GetConf() config.ProxyConf {
 	return pxy.cfg
 }
 
-func (pxy *HTTPProxy) GetLimiter() *rate.Limiter {
-	return pxy.limiter
-}
-
 func (pxy *HTTPProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err error) {
 	xl := pxy.xl
 	rAddr, errRet := net.ResolveTCPAddr("tcp", remoteAddr)

+ 17 - 7
server/proxy/https.go

@@ -15,20 +15,34 @@
 package proxy
 
 import (
+	"reflect"
 	"strings"
 
-	"golang.org/x/time/rate"
-
 	"github.com/fatedier/frp/pkg/config"
 	"github.com/fatedier/frp/pkg/util/util"
 	"github.com/fatedier/frp/pkg/util/vhost"
 )
 
+func init() {
+	RegisterProxyFactory(reflect.TypeOf(&config.HTTPSProxyConf{}), NewHTTPSProxy)
+}
+
 type HTTPSProxy struct {
 	*BaseProxy
 	cfg *config.HTTPSProxyConf
 }
 
+func NewHTTPSProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
+	unwrapped, ok := cfg.(*config.HTTPSProxyConf)
+	if !ok {
+		return nil
+	}
+	return &HTTPSProxy{
+		BaseProxy: baseProxy,
+		cfg:       unwrapped,
+	}
+}
+
 func (pxy *HTTPSProxy) Run() (remoteAddr string, err error) {
 	xl := pxy.xl
 	routeConfig := &vhost.RouteConfig{}
@@ -67,7 +81,7 @@ func (pxy *HTTPSProxy) Run() (remoteAddr string, err error) {
 		addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHTTPSPort))
 	}
 
-	pxy.startListenHandler(pxy, HandleUserTCPConnection)
+	pxy.startCommonTCPListenersHandler()
 	remoteAddr = strings.Join(addrs, ",")
 	return
 }
@@ -76,10 +90,6 @@ func (pxy *HTTPSProxy) GetConf() config.ProxyConf {
 	return pxy.cfg
 }
 
-func (pxy *HTTPSProxy) GetLimiter() *rate.Limiter {
-	return pxy.limiter
-}
-
 func (pxy *HTTPSProxy) Close() {
 	pxy.BaseProxy.Close()
 }

+ 57 - 83
server/proxy/proxy.go

@@ -19,6 +19,7 @@ import (
 	"fmt"
 	"io"
 	"net"
+	"reflect"
 	"strconv"
 	"sync"
 	"time"
@@ -36,6 +37,12 @@ import (
 	"github.com/fatedier/frp/server/metrics"
 )
 
+var proxyFactoryRegistry = map[reflect.Type]func(*BaseProxy, config.ProxyConf) Proxy{}
+
+func RegisterProxyFactory(proxyConfType reflect.Type, factory func(*BaseProxy, config.ProxyConf) Proxy) {
+	proxyFactoryRegistry[proxyConfType] = factory
+}
+
 type GetWorkConnFn func() (net.Conn, error)
 
 type Proxy interface {
@@ -63,6 +70,7 @@ type BaseProxy struct {
 	limiter       *rate.Limiter
 	userInfo      plugin.UserInfo
 	loginMsg      *msg.Login
+	pxyConf       config.ProxyConf
 
 	mu  sync.RWMutex
 	xl  *xlog.Logger
@@ -93,6 +101,10 @@ func (pxy *BaseProxy) GetLoginMsg() *msg.Login {
 	return pxy.loginMsg
 }
 
+func (pxy *BaseProxy) GetLimiter() *rate.Limiter {
+	return pxy.limiter
+}
+
 func (pxy *BaseProxy) Close() {
 	xl := xlog.FromContextSafe(pxy.ctx)
 	xl.Info("proxy closing")
@@ -155,10 +167,8 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn,
 	return
 }
 
-// startListenHandler start a goroutine handler for each listener.
-// p: p will just be passed to handler(Proxy, utilnet.Conn).
-// handler: each proxy type can set different handler function to deal with connections accepted from listeners.
-func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, net.Conn, config.ServerCommonConf)) {
+// startCommonTCPListenersHandler start a goroutine handler for each listener.
+func (pxy *BaseProxy) startCommonTCPListenersHandler() {
 	xl := xlog.FromContextSafe(pxy.ctx)
 	for _, listener := range pxy.listeners {
 		go func(l net.Listener) {
@@ -187,97 +197,25 @@ func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, net.Conn,
 					return
 				}
 				xl.Info("get a user connection [%s]", c.RemoteAddr().String())
-				go handler(p, c, pxy.serverCfg)
+				go pxy.handleUserTCPConnection(c)
 			}
 		}(listener)
 	}
 }
 
-func NewProxy(ctx context.Context, userInfo plugin.UserInfo, rc *controller.ResourceController, poolCount int,
-	getWorkConnFn GetWorkConnFn, pxyConf config.ProxyConf, serverCfg config.ServerCommonConf, loginMsg *msg.Login,
-) (pxy Proxy, err error) {
-	xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(pxyConf.GetBaseConfig().ProxyName)
-
-	var limiter *rate.Limiter
-	limitBytes := pxyConf.GetBaseConfig().BandwidthLimit.Bytes()
-	if limitBytes > 0 && pxyConf.GetBaseConfig().BandwidthLimitMode == config.BandwidthLimitModeServer {
-		limiter = rate.NewLimiter(rate.Limit(float64(limitBytes)), int(limitBytes))
-	}
-
-	basePxy := BaseProxy{
-		name:          pxyConf.GetBaseConfig().ProxyName,
-		rc:            rc,
-		listeners:     make([]net.Listener, 0),
-		poolCount:     poolCount,
-		getWorkConnFn: getWorkConnFn,
-		serverCfg:     serverCfg,
-		limiter:       limiter,
-		xl:            xl,
-		ctx:           xlog.NewContext(ctx, xl),
-		userInfo:      userInfo,
-		loginMsg:      loginMsg,
-	}
-	switch cfg := pxyConf.(type) {
-	case *config.TCPProxyConf:
-		basePxy.usedPortsNum = 1
-		pxy = &TCPProxy{
-			BaseProxy: &basePxy,
-			cfg:       cfg,
-		}
-	case *config.TCPMuxProxyConf:
-		pxy = &TCPMuxProxy{
-			BaseProxy: &basePxy,
-			cfg:       cfg,
-		}
-	case *config.HTTPProxyConf:
-		pxy = &HTTPProxy{
-			BaseProxy: &basePxy,
-			cfg:       cfg,
-		}
-	case *config.HTTPSProxyConf:
-		pxy = &HTTPSProxy{
-			BaseProxy: &basePxy,
-			cfg:       cfg,
-		}
-	case *config.UDPProxyConf:
-		basePxy.usedPortsNum = 1
-		pxy = &UDPProxy{
-			BaseProxy: &basePxy,
-			cfg:       cfg,
-		}
-	case *config.STCPProxyConf:
-		pxy = &STCPProxy{
-			BaseProxy: &basePxy,
-			cfg:       cfg,
-		}
-	case *config.XTCPProxyConf:
-		pxy = &XTCPProxy{
-			BaseProxy: &basePxy,
-			cfg:       cfg,
-		}
-	case *config.SUDPProxyConf:
-		pxy = &SUDPProxy{
-			BaseProxy: &basePxy,
-			cfg:       cfg,
-		}
-	default:
-		return pxy, fmt.Errorf("proxy type not support")
-	}
-	return
-}
-
 // HandleUserTCPConnection is used for incoming user TCP connections.
-// It can be used for tcp, http, https type.
-func HandleUserTCPConnection(pxy Proxy, userConn net.Conn, serverCfg config.ServerCommonConf) {
+func (pxy *BaseProxy) handleUserTCPConnection(userConn net.Conn) {
 	xl := xlog.FromContextSafe(pxy.Context())
 	defer userConn.Close()
 
+	serverCfg := pxy.serverCfg
+	cfg := pxy.pxyConf.GetBaseConfig()
 	// server plugin hook
 	rc := pxy.GetResourceController()
 	content := &plugin.NewUserConnContent{
 		User:       pxy.GetUserInfo(),
 		ProxyName:  pxy.GetName(),
-		ProxyType:  pxy.GetConf().GetBaseConfig().ProxyType,
+		ProxyType:  cfg.ProxyType,
 		RemoteAddr: userConn.RemoteAddr().String(),
 	}
 	_, err := rc.PluginManager.NewUserConn(content)
@@ -294,7 +232,6 @@ func HandleUserTCPConnection(pxy Proxy, userConn net.Conn, serverCfg config.Serv
 	defer workConn.Close()
 
 	var local io.ReadWriteCloser = workConn
-	cfg := pxy.GetConf().GetBaseConfig()
 	xl.Trace("handler user tcp connection, use_encryption: %t, use_compression: %t", cfg.UseEncryption, cfg.UseCompression)
 	if cfg.UseEncryption {
 		local, err = libio.WithEncryption(local, []byte(serverCfg.Token))
@@ -317,7 +254,7 @@ func HandleUserTCPConnection(pxy Proxy, userConn net.Conn, serverCfg config.Serv
 		workConn.RemoteAddr().String(), userConn.LocalAddr().String(), userConn.RemoteAddr().String())
 
 	name := pxy.GetName()
-	proxyType := pxy.GetConf().GetBaseConfig().ProxyType
+	proxyType := cfg.ProxyType
 	metrics.Server.OpenConnection(name, proxyType)
 	inCount, outCount, _ := libio.Join(local, userConn)
 	metrics.Server.CloseConnection(name, proxyType)
@@ -326,6 +263,43 @@ func HandleUserTCPConnection(pxy Proxy, userConn net.Conn, serverCfg config.Serv
 	xl.Debug("join connections closed")
 }
 
+func NewProxy(ctx context.Context, userInfo plugin.UserInfo, rc *controller.ResourceController, poolCount int,
+	getWorkConnFn GetWorkConnFn, pxyConf config.ProxyConf, serverCfg config.ServerCommonConf, loginMsg *msg.Login,
+) (pxy Proxy, err error) {
+	xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(pxyConf.GetBaseConfig().ProxyName)
+
+	var limiter *rate.Limiter
+	limitBytes := pxyConf.GetBaseConfig().BandwidthLimit.Bytes()
+	if limitBytes > 0 && pxyConf.GetBaseConfig().BandwidthLimitMode == config.BandwidthLimitModeServer {
+		limiter = rate.NewLimiter(rate.Limit(float64(limitBytes)), int(limitBytes))
+	}
+
+	basePxy := BaseProxy{
+		name:          pxyConf.GetBaseConfig().ProxyName,
+		rc:            rc,
+		listeners:     make([]net.Listener, 0),
+		poolCount:     poolCount,
+		getWorkConnFn: getWorkConnFn,
+		serverCfg:     serverCfg,
+		limiter:       limiter,
+		xl:            xl,
+		ctx:           xlog.NewContext(ctx, xl),
+		userInfo:      userInfo,
+		loginMsg:      loginMsg,
+		pxyConf:       pxyConf,
+	}
+
+	factory := proxyFactoryRegistry[reflect.TypeOf(pxyConf)]
+	if factory == nil {
+		return pxy, fmt.Errorf("proxy type not support")
+	}
+	pxy = factory(&basePxy, pxyConf)
+	if pxy == nil {
+		return nil, fmt.Errorf("proxy not created")
+	}
+	return pxy, nil
+}
+
 type Manager struct {
 	// proxies indexed by proxy name
 	pxys map[string]Proxy

+ 17 - 6
server/proxy/stcp.go

@@ -15,16 +15,31 @@
 package proxy
 
 import (
-	"golang.org/x/time/rate"
+	"reflect"
 
 	"github.com/fatedier/frp/pkg/config"
 )
 
+func init() {
+	RegisterProxyFactory(reflect.TypeOf(&config.STCPProxyConf{}), NewSTCPProxy)
+}
+
 type STCPProxy struct {
 	*BaseProxy
 	cfg *config.STCPProxyConf
 }
 
+func NewSTCPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
+	unwrapped, ok := cfg.(*config.STCPProxyConf)
+	if !ok {
+		return nil
+	}
+	return &STCPProxy{
+		BaseProxy: baseProxy,
+		cfg:       unwrapped,
+	}
+}
+
 func (pxy *STCPProxy) Run() (remoteAddr string, err error) {
 	xl := pxy.xl
 	allowUsers := pxy.cfg.AllowUsers
@@ -40,7 +55,7 @@ func (pxy *STCPProxy) Run() (remoteAddr string, err error) {
 	pxy.listeners = append(pxy.listeners, listener)
 	xl.Info("stcp proxy custom listen success")
 
-	pxy.startListenHandler(pxy, HandleUserTCPConnection)
+	pxy.startCommonTCPListenersHandler()
 	return
 }
 
@@ -48,10 +63,6 @@ func (pxy *STCPProxy) GetConf() config.ProxyConf {
 	return pxy.cfg
 }
 
-func (pxy *STCPProxy) GetLimiter() *rate.Limiter {
-	return pxy.limiter
-}
-
 func (pxy *STCPProxy) Close() {
 	pxy.BaseProxy.Close()
 	pxy.rc.VisitorManager.CloseListener(pxy.GetName())

+ 17 - 6
server/proxy/sudp.go

@@ -15,16 +15,31 @@
 package proxy
 
 import (
-	"golang.org/x/time/rate"
+	"reflect"
 
 	"github.com/fatedier/frp/pkg/config"
 )
 
+func init() {
+	RegisterProxyFactory(reflect.TypeOf(&config.SUDPProxyConf{}), NewSUDPProxy)
+}
+
 type SUDPProxy struct {
 	*BaseProxy
 	cfg *config.SUDPProxyConf
 }
 
+func NewSUDPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
+	unwrapped, ok := cfg.(*config.SUDPProxyConf)
+	if !ok {
+		return nil
+	}
+	return &SUDPProxy{
+		BaseProxy: baseProxy,
+		cfg:       unwrapped,
+	}
+}
+
 func (pxy *SUDPProxy) Run() (remoteAddr string, err error) {
 	xl := pxy.xl
 	allowUsers := pxy.cfg.AllowUsers
@@ -40,7 +55,7 @@ func (pxy *SUDPProxy) Run() (remoteAddr string, err error) {
 	pxy.listeners = append(pxy.listeners, listener)
 	xl.Info("sudp proxy custom listen success")
 
-	pxy.startListenHandler(pxy, HandleUserTCPConnection)
+	pxy.startCommonTCPListenersHandler()
 	return
 }
 
@@ -48,10 +63,6 @@ func (pxy *SUDPProxy) GetConf() config.ProxyConf {
 	return pxy.cfg
 }
 
-func (pxy *SUDPProxy) GetLimiter() *rate.Limiter {
-	return pxy.limiter
-}
-
 func (pxy *SUDPProxy) Close() {
 	pxy.BaseProxy.Close()
 	pxy.rc.VisitorManager.CloseListener(pxy.GetName())

+ 27 - 16
server/proxy/tcp.go

@@ -17,24 +17,39 @@ package proxy
 import (
 	"fmt"
 	"net"
+	"reflect"
 	"strconv"
 
-	"golang.org/x/time/rate"
-
 	"github.com/fatedier/frp/pkg/config"
 )
 
+func init() {
+	RegisterProxyFactory(reflect.TypeOf(&config.TCPProxyConf{}), NewTCPProxy)
+}
+
 type TCPProxy struct {
 	*BaseProxy
 	cfg *config.TCPProxyConf
 
-	realPort int
+	realBindPort int
+}
+
+func NewTCPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
+	unwrapped, ok := cfg.(*config.TCPProxyConf)
+	if !ok {
+		return nil
+	}
+	baseProxy.usedPortsNum = 1
+	return &TCPProxy{
+		BaseProxy: baseProxy,
+		cfg:       unwrapped,
+	}
 }
 
 func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
 	xl := pxy.xl
 	if pxy.cfg.Group != "" {
-		l, realPort, errRet := pxy.rc.TCPGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort)
+		l, realBindPort, errRet := pxy.rc.TCPGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort)
 		if errRet != nil {
 			err = errRet
 			return
@@ -44,20 +59,20 @@ func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
 				l.Close()
 			}
 		}()
-		pxy.realPort = realPort
+		pxy.realBindPort = realBindPort
 		pxy.listeners = append(pxy.listeners, l)
 		xl.Info("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.Group)
 	} else {
-		pxy.realPort, err = pxy.rc.TCPPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
+		pxy.realBindPort, err = pxy.rc.TCPPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
 		if err != nil {
 			return
 		}
 		defer func() {
 			if err != nil {
-				pxy.rc.TCPPortManager.Release(pxy.realPort)
+				pxy.rc.TCPPortManager.Release(pxy.realBindPort)
 			}
 		}()
-		listener, errRet := net.Listen("tcp", net.JoinHostPort(pxy.serverCfg.ProxyBindAddr, strconv.Itoa(pxy.realPort)))
+		listener, errRet := net.Listen("tcp", net.JoinHostPort(pxy.serverCfg.ProxyBindAddr, strconv.Itoa(pxy.realBindPort)))
 		if errRet != nil {
 			err = errRet
 			return
@@ -66,9 +81,9 @@ func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
 		xl.Info("tcp proxy listen port [%d]", pxy.cfg.RemotePort)
 	}
 
-	pxy.cfg.RemotePort = pxy.realPort
-	remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
-	pxy.startListenHandler(pxy, HandleUserTCPConnection)
+	pxy.cfg.RemotePort = pxy.realBindPort
+	remoteAddr = fmt.Sprintf(":%d", pxy.realBindPort)
+	pxy.startCommonTCPListenersHandler()
 	return
 }
 
@@ -76,13 +91,9 @@ func (pxy *TCPProxy) GetConf() config.ProxyConf {
 	return pxy.cfg
 }
 
-func (pxy *TCPProxy) GetLimiter() *rate.Limiter {
-	return pxy.limiter
-}
-
 func (pxy *TCPProxy) Close() {
 	pxy.BaseProxy.Close()
 	if pxy.cfg.Group == "" {
-		pxy.rc.TCPPortManager.Release(pxy.realPort)
+		pxy.rc.TCPPortManager.Release(pxy.realBindPort)
 	}
 }

+ 17 - 7
server/proxy/tcpmux.go

@@ -17,21 +17,35 @@ package proxy
 import (
 	"fmt"
 	"net"
+	"reflect"
 	"strings"
 
-	"golang.org/x/time/rate"
-
 	"github.com/fatedier/frp/pkg/config"
 	"github.com/fatedier/frp/pkg/consts"
 	"github.com/fatedier/frp/pkg/util/util"
 	"github.com/fatedier/frp/pkg/util/vhost"
 )
 
+func init() {
+	RegisterProxyFactory(reflect.TypeOf(&config.TCPMuxProxyConf{}), NewTCPMuxProxy)
+}
+
 type TCPMuxProxy struct {
 	*BaseProxy
 	cfg *config.TCPMuxProxyConf
 }
 
+func NewTCPMuxProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
+	unwrapped, ok := cfg.(*config.TCPMuxProxyConf)
+	if !ok {
+		return nil
+	}
+	return &TCPMuxProxy{
+		BaseProxy: baseProxy,
+		cfg:       unwrapped,
+	}
+}
+
 func (pxy *TCPMuxProxy) httpConnectListen(
 	domain, routeByHTTPUser, httpUser, httpPwd string, addrs []string) ([]string, error,
 ) {
@@ -78,7 +92,7 @@ func (pxy *TCPMuxProxy) httpConnectRun() (remoteAddr string, err error) {
 		}
 	}
 
-	pxy.startListenHandler(pxy, HandleUserTCPConnection)
+	pxy.startCommonTCPListenersHandler()
 	remoteAddr = strings.Join(addrs, ",")
 	return remoteAddr, err
 }
@@ -101,10 +115,6 @@ func (pxy *TCPMuxProxy) GetConf() config.ProxyConf {
 	return pxy.cfg
 }
 
-func (pxy *TCPMuxProxy) GetLimiter() *rate.Limiter {
-	return pxy.limiter
-}
-
 func (pxy *TCPMuxProxy) Close() {
 	pxy.BaseProxy.Close()
 }

+ 24 - 12
server/proxy/udp.go

@@ -19,12 +19,12 @@ import (
 	"fmt"
 	"io"
 	"net"
+	"reflect"
 	"strconv"
 	"time"
 
 	"github.com/fatedier/golib/errors"
 	libio "github.com/fatedier/golib/io"
-	"golang.org/x/time/rate"
 
 	"github.com/fatedier/frp/pkg/config"
 	"github.com/fatedier/frp/pkg/msg"
@@ -34,11 +34,15 @@ import (
 	"github.com/fatedier/frp/server/metrics"
 )
 
+func init() {
+	RegisterProxyFactory(reflect.TypeOf(&config.UDPProxyConf{}), NewUDPProxy)
+}
+
 type UDPProxy struct {
 	*BaseProxy
 	cfg *config.UDPProxyConf
 
-	realPort int
+	realBindPort int
 
 	// udpConn is the listener of udp packages
 	udpConn *net.UDPConn
@@ -59,21 +63,33 @@ type UDPProxy struct {
 	isClosed bool
 }
 
+func NewUDPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
+	unwrapped, ok := cfg.(*config.UDPProxyConf)
+	if !ok {
+		return nil
+	}
+	baseProxy.usedPortsNum = 1
+	return &UDPProxy{
+		BaseProxy: baseProxy,
+		cfg:       unwrapped,
+	}
+}
+
 func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
 	xl := pxy.xl
-	pxy.realPort, err = pxy.rc.UDPPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
+	pxy.realBindPort, err = pxy.rc.UDPPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
 	if err != nil {
 		return "", fmt.Errorf("acquire port %d error: %v", pxy.cfg.RemotePort, err)
 	}
 	defer func() {
 		if err != nil {
-			pxy.rc.UDPPortManager.Release(pxy.realPort)
+			pxy.rc.UDPPortManager.Release(pxy.realBindPort)
 		}
 	}()
 
-	remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
-	pxy.cfg.RemotePort = pxy.realPort
-	addr, errRet := net.ResolveUDPAddr("udp", net.JoinHostPort(pxy.serverCfg.ProxyBindAddr, strconv.Itoa(pxy.realPort)))
+	remoteAddr = fmt.Sprintf(":%d", pxy.realBindPort)
+	pxy.cfg.RemotePort = pxy.realBindPort
+	addr, errRet := net.ResolveUDPAddr("udp", net.JoinHostPort(pxy.serverCfg.ProxyBindAddr, strconv.Itoa(pxy.realBindPort)))
 	if errRet != nil {
 		err = errRet
 		return
@@ -233,10 +249,6 @@ func (pxy *UDPProxy) GetConf() config.ProxyConf {
 	return pxy.cfg
 }
 
-func (pxy *UDPProxy) GetLimiter() *rate.Limiter {
-	return pxy.limiter
-}
-
 func (pxy *UDPProxy) Close() {
 	pxy.mu.Lock()
 	defer pxy.mu.Unlock()
@@ -254,5 +266,5 @@ func (pxy *UDPProxy) Close() {
 		close(pxy.readCh)
 		close(pxy.sendCh)
 	}
-	pxy.rc.UDPPortManager.Release(pxy.realPort)
+	pxy.rc.UDPPortManager.Release(pxy.realBindPort)
 }

+ 16 - 5
server/proxy/xtcp.go

@@ -16,14 +16,18 @@ package proxy
 
 import (
 	"fmt"
+	"reflect"
 
 	"github.com/fatedier/golib/errors"
-	"golang.org/x/time/rate"
 
 	"github.com/fatedier/frp/pkg/config"
 	"github.com/fatedier/frp/pkg/msg"
 )
 
+func init() {
+	RegisterProxyFactory(reflect.TypeOf(&config.XTCPProxyConf{}), NewXTCPProxy)
+}
+
 type XTCPProxy struct {
 	*BaseProxy
 	cfg *config.XTCPProxyConf
@@ -31,6 +35,17 @@ type XTCPProxy struct {
 	closeCh chan struct{}
 }
 
+func NewXTCPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
+	unwrapped, ok := cfg.(*config.XTCPProxyConf)
+	if !ok {
+		return nil
+	}
+	return &XTCPProxy{
+		BaseProxy: baseProxy,
+		cfg:       unwrapped,
+	}
+}
+
 func (pxy *XTCPProxy) Run() (remoteAddr string, err error) {
 	xl := pxy.xl
 
@@ -72,10 +87,6 @@ func (pxy *XTCPProxy) GetConf() config.ProxyConf {
 	return pxy.cfg
 }
 
-func (pxy *XTCPProxy) GetLimiter() *rate.Limiter {
-	return pxy.limiter
-}
-
 func (pxy *XTCPProxy) Close() {
 	pxy.BaseProxy.Close()
 	pxy.rc.NatHoleController.CloseClient(pxy.GetName())