未验证 提交 47971369 编写于 作者: T Tank 提交者: GitHub

feat: support sudp proxy (#1730)

上级 6d78af61
...@@ -88,6 +88,7 @@ type StatusResp struct { ...@@ -88,6 +88,7 @@ type StatusResp struct {
Https []ProxyStatusResp `json:"https"` Https []ProxyStatusResp `json:"https"`
Stcp []ProxyStatusResp `json:"stcp"` Stcp []ProxyStatusResp `json:"stcp"`
Xtcp []ProxyStatusResp `json:"xtcp"` Xtcp []ProxyStatusResp `json:"xtcp"`
Sudp []ProxyStatusResp `json:"sudp"`
} }
type ProxyStatusResp struct { type ProxyStatusResp struct {
...@@ -155,6 +156,11 @@ func NewProxyStatusResp(status *proxy.ProxyStatus, serverAddr string) ProxyStatu ...@@ -155,6 +156,11 @@ func NewProxyStatusResp(status *proxy.ProxyStatus, serverAddr string) ProxyStatu
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort) psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort)
} }
psr.Plugin = cfg.Plugin psr.Plugin = cfg.Plugin
case *config.SudpProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort)
}
psr.Plugin = cfg.Plugin
} }
return psr return psr
} }
...@@ -171,6 +177,7 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) { ...@@ -171,6 +177,7 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
res.Https = make([]ProxyStatusResp, 0) res.Https = make([]ProxyStatusResp, 0)
res.Stcp = make([]ProxyStatusResp, 0) res.Stcp = make([]ProxyStatusResp, 0)
res.Xtcp = make([]ProxyStatusResp, 0) res.Xtcp = make([]ProxyStatusResp, 0)
res.Sudp = make([]ProxyStatusResp, 0)
log.Info("Http request [/api/status]") log.Info("Http request [/api/status]")
defer func() { defer func() {
...@@ -194,6 +201,8 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) { ...@@ -194,6 +201,8 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
res.Stcp = append(res.Stcp, NewProxyStatusResp(status, svr.cfg.ServerAddr)) res.Stcp = append(res.Stcp, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "xtcp": case "xtcp":
res.Xtcp = append(res.Xtcp, NewProxyStatusResp(status, svr.cfg.ServerAddr)) res.Xtcp = append(res.Xtcp, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "sudp":
res.Sudp = append(res.Sudp, NewProxyStatusResp(status, svr.cfg.ServerAddr))
} }
} }
sort.Sort(ByProxyStatusResp(res.Tcp)) sort.Sort(ByProxyStatusResp(res.Tcp))
...@@ -202,6 +211,7 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) { ...@@ -202,6 +211,7 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
sort.Sort(ByProxyStatusResp(res.Https)) sort.Sort(ByProxyStatusResp(res.Https))
sort.Sort(ByProxyStatusResp(res.Stcp)) sort.Sort(ByProxyStatusResp(res.Stcp))
sort.Sort(ByProxyStatusResp(res.Xtcp)) sort.Sort(ByProxyStatusResp(res.Xtcp))
sort.Sort(ByProxyStatusResp(res.Sudp))
return return
} }
......
...@@ -102,6 +102,12 @@ func NewProxy(ctx context.Context, pxyConf config.ProxyConf, clientCfg config.Cl ...@@ -102,6 +102,12 @@ func NewProxy(ctx context.Context, pxyConf config.ProxyConf, clientCfg config.Cl
BaseProxy: &baseProxy, BaseProxy: &baseProxy,
cfg: cfg, cfg: cfg,
} }
case *config.SudpProxyConf:
pxy = &SudpProxy{
BaseProxy: &baseProxy,
cfg: cfg,
closeCh: make(chan struct{}),
}
} }
return return
} }
...@@ -540,6 +546,151 @@ func (pxy *UdpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) { ...@@ -540,6 +546,151 @@ func (pxy *UdpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
udp.Forwarder(pxy.localAddr, pxy.readCh, pxy.sendCh) udp.Forwarder(pxy.localAddr, pxy.readCh, pxy.sendCh)
} }
type SudpProxy struct {
*BaseProxy
cfg *config.SudpProxyConf
localAddr *net.UDPAddr
closeCh chan struct{}
}
func (pxy *SudpProxy) Run() (err error) {
pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIp, pxy.cfg.LocalPort))
if err != nil {
return
}
return
}
func (pxy *SudpProxy) Close() {
pxy.mu.Lock()
defer pxy.mu.Unlock()
select {
case <-pxy.closeCh:
return
default:
close(pxy.closeCh)
}
}
func (pxy *SudpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
xl := pxy.xl
xl.Info("incoming a new work connection for sudp proxy, %s", conn.RemoteAddr().String())
if pxy.limiter != nil {
rwc := frpIo.WrapReadWriteCloser(limit.NewReader(conn, pxy.limiter), limit.NewWriter(conn, pxy.limiter), func() error {
return conn.Close()
})
conn = frpNet.WrapReadWriteCloserToConn(rwc, conn)
}
workConn := conn
readCh := make(chan *msg.UdpPacket, 1024)
sendCh := make(chan msg.Message, 1024)
isClose := false
mu := &sync.Mutex{}
closeFn := func() {
mu.Lock()
defer mu.Unlock()
if isClose {
return
}
isClose = true
if workConn != nil {
workConn.Close()
}
close(readCh)
close(sendCh)
}
// udp service <- frpc <- frps <- frpc visitor <- user
workConnReaderFn := func(conn net.Conn, readCh chan *msg.UdpPacket) {
defer closeFn()
for {
// first to check sudp proxy is closed or not
select {
case <-pxy.closeCh:
xl.Trace("frpc sudp proxy is closed")
return
default:
}
var udpMsg msg.UdpPacket
if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil {
xl.Warn("read from workConn for sudp error: %v", errRet)
return
}
if errRet := errors.PanicToError(func() {
readCh <- &udpMsg
}); errRet != nil {
xl.Warn("reader goroutine for sudp work connection closed: %v", errRet)
return
}
}
}
// udp service -> frpc -> frps -> frpc visitor -> user
workConnSenderFn := func(conn net.Conn, sendCh chan msg.Message) {
defer func() {
closeFn()
xl.Info("writer goroutine for sudp work connection closed")
}()
var errRet error
for rawMsg := range sendCh {
switch m := rawMsg.(type) {
case *msg.UdpPacket:
xl.Trace("frpc send udp package to frpc visitor, [udp local: %v, remote: %v], [tcp work conn local: %v, remote: %v]",
m.LocalAddr.String(), m.RemoteAddr.String(), conn.LocalAddr().String(), conn.RemoteAddr().String())
case *msg.Ping:
xl.Trace("frpc send ping message to frpc visitor")
}
if errRet = msg.WriteMsg(conn, rawMsg); errRet != nil {
xl.Error("sudp work write error: %v", errRet)
return
}
}
}
heartbeatFn := func(conn net.Conn, sendCh chan msg.Message) {
ticker := time.NewTicker(30 * time.Second)
defer func() {
ticker.Stop()
closeFn()
}()
var errRet error
for {
select {
case <-ticker.C:
if errRet = errors.PanicToError(func() {
sendCh <- &msg.Ping{}
}); errRet != nil {
xl.Warn("heartbeat goroutine for sudp work connection closed")
return
}
case <-pxy.closeCh:
xl.Trace("frpc sudp proxy is closed")
return
}
}
}
go workConnSenderFn(workConn, sendCh)
go workConnReaderFn(workConn, readCh)
go heartbeatFn(workConn, sendCh)
udp.Forwarder(pxy.localAddr, readCh, sendCh)
}
// Common handler for tcp work connections. // Common handler for tcp work connections.
func HandleTcpWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin, func HandleTcpWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin,
baseInfo *config.BaseProxyConf, limiter *rate.Limiter, workConn net.Conn, encKey []byte, m *msg.StartWorkConn) { baseInfo *config.BaseProxyConf, limiter *rate.Limiter, workConn net.Conn, encKey []byte, m *msg.StartWorkConn) {
......
...@@ -26,10 +26,12 @@ import ( ...@@ -26,10 +26,12 @@ import (
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/models/proto/udp"
frpNet "github.com/fatedier/frp/utils/net" frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/util" "github.com/fatedier/frp/utils/util"
"github.com/fatedier/frp/utils/xlog" "github.com/fatedier/frp/utils/xlog"
"github.com/fatedier/golib/errors"
frpIo "github.com/fatedier/golib/io" frpIo "github.com/fatedier/golib/io"
"github.com/fatedier/golib/pool" "github.com/fatedier/golib/pool"
fmux "github.com/hashicorp/yamux" fmux "github.com/hashicorp/yamux"
...@@ -58,6 +60,12 @@ func NewVisitor(ctx context.Context, ctl *Control, cfg config.VisitorConf) (visi ...@@ -58,6 +60,12 @@ func NewVisitor(ctx context.Context, ctl *Control, cfg config.VisitorConf) (visi
BaseVisitor: &baseVisitor, BaseVisitor: &baseVisitor,
cfg: cfg, cfg: cfg,
} }
case *config.SudpVisitorConf:
visitor = &SudpVisitor{
BaseVisitor: &baseVisitor,
cfg: cfg,
checkCloseCh: make(chan struct{}),
}
} }
return return
} }
...@@ -328,3 +336,204 @@ func (sv *XtcpVisitor) handleConn(userConn net.Conn) { ...@@ -328,3 +336,204 @@ func (sv *XtcpVisitor) handleConn(userConn net.Conn) {
frpIo.Join(userConn, muxConnRWCloser) frpIo.Join(userConn, muxConnRWCloser)
xl.Debug("join connections closed") xl.Debug("join connections closed")
} }
type SudpVisitor struct {
*BaseVisitor
checkCloseCh chan struct{}
// udpConn is the listener of udp packet
udpConn *net.UDPConn
readCh chan *msg.UdpPacket
sendCh chan *msg.UdpPacket
cfg *config.SudpVisitorConf
}
// SUDP Run start listen a udp port
func (sv *SudpVisitor) Run() (err error) {
xl := xlog.FromContextSafe(sv.ctx)
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort))
if err != nil {
return fmt.Errorf("sudp ResolveUDPAddr error: %v", err)
}
sv.udpConn, err = net.ListenUDP("udp", addr)
if err != nil {
return fmt.Errorf("listen udp port %s error: %v", addr.String(), err)
}
sv.sendCh = make(chan *msg.UdpPacket, 1024)
sv.readCh = make(chan *msg.UdpPacket, 1024)
xl.Info("sudp start to work")
go sv.dispatcher()
go udp.ForwardUserConn(sv.udpConn, sv.readCh, sv.sendCh)
return
}
func (sv *SudpVisitor) dispatcher() {
xl := xlog.FromContextSafe(sv.ctx)
for {
// loop for get frpc to frps tcp conn
// setup worker
// wait worker to finished
// retry or exit
visitorConn, err := sv.getNewVisitorConn()
if err != nil {
// check if proxy is closed
// if checkCloseCh is close, we will return, other case we will continue to reconnect
select {
case <-sv.checkCloseCh:
xl.Info("frpc sudp visitor proxy is closed")
return
default:
}
time.Sleep(3 * time.Second)
xl.Warn("newVisitorConn to frps error: %v, try to reconnect", err)
continue
}
sv.worker(visitorConn)
select {
case <-sv.checkCloseCh:
return
default:
}
}
}
func (sv *SudpVisitor) worker(workConn net.Conn) {
xl := xlog.FromContextSafe(sv.ctx)
xl.Debug("starting sudp proxy worker")
wg := &sync.WaitGroup{}
wg.Add(2)
closeCh := make(chan struct{})
// udp service -> frpc -> frps -> frpc visitor -> user
workConnReaderFn := func(conn net.Conn) {
defer func() {
conn.Close()
close(closeCh)
wg.Done()
}()
for {
var (
rawMsg msg.Message
errRet error
)
// frpc will send heartbeat in workConn to frpc visitor for keeping alive
conn.SetReadDeadline(time.Now().Add(60 * time.Second))
if rawMsg, errRet = msg.ReadMsg(conn); errRet != nil {
xl.Warn("read from workconn for user udp conn error: %v", errRet)
return
}
conn.SetReadDeadline(time.Time{})
switch m := rawMsg.(type) {
case *msg.Ping:
xl.Debug("frpc visitor get ping message from frpc")
continue
case *msg.UdpPacket:
if errRet := errors.PanicToError(func() {
sv.readCh <- m
xl.Trace("frpc visitor get udp packet from frpc")
}); errRet != nil {
xl.Info("reader goroutine for udp work connection closed")
return
}
}
}
}
// udp service <- frpc <- frps <- frpc visitor <- user
workConnSenderFn := func(conn net.Conn) {
defer func() {
conn.Close()
wg.Done()
}()
var errRet error
for {
select {
case udpMsg, ok := <-sv.sendCh:
if !ok {
xl.Info("sender goroutine for udp work connection closed")
return
}
if errRet = msg.WriteMsg(conn, udpMsg); errRet != nil {
xl.Warn("sender goroutine for udp work connection closed: %v", errRet)
return
}
case <-closeCh:
return
}
}
}
go workConnReaderFn(workConn)
go workConnSenderFn(workConn)
wg.Wait()
xl.Info("sudp worker is closed")
}
func (sv *SudpVisitor) getNewVisitorConn() (visitorConn net.Conn, err error) {
visitorConn, err = sv.ctl.connectServer()
if err != nil {
return nil, fmt.Errorf("frpc connect frps error: %v", err)
}
now := time.Now().Unix()
newVisitorConnMsg := &msg.NewVisitorConn{
ProxyName: sv.cfg.ServerName,
SignKey: util.GetAuthKey(sv.cfg.Sk, now),
Timestamp: now,
UseEncryption: sv.cfg.UseEncryption,
UseCompression: sv.cfg.UseCompression,
}
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
if err != nil {
return nil, fmt.Errorf("frpc send newVisitorConnMsg to frps error: %v", err)
}
var newVisitorConnRespMsg msg.NewVisitorConnResp
visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
if err != nil {
return nil, fmt.Errorf("frpc read newVisitorConnRespMsg error: %v", err)
}
visitorConn.SetReadDeadline(time.Time{})
if newVisitorConnRespMsg.Error != "" {
return nil, fmt.Errorf("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
}
return
}
func (sv *SudpVisitor) Close() {
sv.mu.Lock()
defer sv.mu.Unlock()
select {
case <-sv.checkCloseCh:
return
default:
close(sv.checkCloseCh)
}
if sv.udpConn != nil {
sv.udpConn.Close()
}
close(sv.readCh)
close(sv.sendCh)
}
// Copyright 2018 fatedier, fatedier@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sub
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/consts"
)
func init() {
sudpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
sudpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
sudpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket")
sudpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
sudpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
sudpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
sudpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
sudpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
sudpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
sudpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role")
sudpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key")
sudpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name")
sudpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
sudpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
sudpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr")
sudpCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "", 0, "bind port")
sudpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
sudpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
rootCmd.AddCommand(sudpCmd)
}
var sudpCmd = &cobra.Command{
Use: "sudp",
Short: "Run frpc with a single sudp proxy",
RunE: func(cmd *cobra.Command, args []string) error {
clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
proxyConfs := make(map[string]config.ProxyConf)
visitorConfs := make(map[string]config.VisitorConf)
var prefix string
if user != "" {
prefix = user + "."
}
if role == "server" {
cfg := &config.SudpProxyConf{}
cfg.ProxyName = prefix + proxyName
cfg.ProxyType = consts.SudpProxy
cfg.UseEncryption = useEncryption
cfg.UseCompression = useCompression
cfg.Role = role
cfg.Sk = sk
cfg.LocalIp = localIp
cfg.LocalPort = localPort
err = cfg.CheckForCli()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
proxyConfs[cfg.ProxyName] = cfg
} else if role == "visitor" {
cfg := &config.SudpVisitorConf{}
cfg.ProxyName = prefix + proxyName
cfg.ProxyType = consts.SudpProxy
cfg.UseEncryption = useEncryption
cfg.UseCompression = useCompression
cfg.Role = role
cfg.Sk = sk
cfg.ServerName = serverName
cfg.BindAddr = bindAddr
cfg.BindPort = bindPort
err = cfg.Check()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
visitorConfs[cfg.ProxyName] = cfg
} else {
fmt.Println("invalid role")
os.Exit(1)
}
err = startService(clientCfg, proxyConfs, visitorConfs, "")
if err != nil {
os.Exit(1)
}
return nil
},
}
...@@ -40,6 +40,7 @@ func init() { ...@@ -40,6 +40,7 @@ func init() {
proxyConfTypeMap[consts.HttpsProxy] = reflect.TypeOf(HttpsProxyConf{}) proxyConfTypeMap[consts.HttpsProxy] = reflect.TypeOf(HttpsProxyConf{})
proxyConfTypeMap[consts.StcpProxy] = reflect.TypeOf(StcpProxyConf{}) proxyConfTypeMap[consts.StcpProxy] = reflect.TypeOf(StcpProxyConf{})
proxyConfTypeMap[consts.XtcpProxy] = reflect.TypeOf(XtcpProxyConf{}) proxyConfTypeMap[consts.XtcpProxy] = reflect.TypeOf(XtcpProxyConf{})
proxyConfTypeMap[consts.SudpProxy] = reflect.TypeOf(SudpProxyConf{})
} }
// NewConfByType creates a empty ProxyConf object by proxyType. // NewConfByType creates a empty ProxyConf object by proxyType.
...@@ -875,6 +876,72 @@ func (cfg *HttpsProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) { ...@@ -875,6 +876,72 @@ func (cfg *HttpsProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) {
return return
} }
// SUDP
type SudpProxyConf struct {
BaseProxyConf
Role string `json:"role"`
Sk string `json:"sk"`
}
func (cfg *SudpProxyConf) Compare(cmp ProxyConf) bool {
cmpConf, ok := cmp.(*SudpProxyConf)
if !ok {
return false
}
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
cfg.Role != cmpConf.Role ||
cfg.Sk != cmpConf.Sk {
return false
}
return true
}
func (cfg *SudpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
return
}
cfg.Role = section["role"]
if cfg.Role != "server" {
return fmt.Errorf("Parse conf error: proxy [%s] incorrect role [%s]", name, cfg.Role)
}
cfg.Sk = section["sk"]
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
return
}
return
}
func (cfg *SudpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
cfg.BaseProxyConf.MarshalToMsg(pMsg)
pMsg.Sk = cfg.Sk
}
func (cfg *SudpProxyConf) CheckForCli() (err error) {
if err = cfg.BaseProxyConf.checkForCli(); err != nil {
return
}
if cfg.Role != "server" {
err = fmt.Errorf("role should be 'server'")
return
}
return
}
func (cfg *SudpProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) {
return
}
// Only for role server.
func (cfg *SudpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
cfg.Sk = pMsg.Sk
}
// STCP // STCP
type StcpProxyConf struct { type StcpProxyConf struct {
BaseProxyConf BaseProxyConf
......
...@@ -32,6 +32,7 @@ func init() { ...@@ -32,6 +32,7 @@ func init() {
visitorConfTypeMap = make(map[string]reflect.Type) visitorConfTypeMap = make(map[string]reflect.Type)
visitorConfTypeMap[consts.StcpProxy] = reflect.TypeOf(StcpVisitorConf{}) visitorConfTypeMap[consts.StcpProxy] = reflect.TypeOf(StcpVisitorConf{})
visitorConfTypeMap[consts.XtcpProxy] = reflect.TypeOf(XtcpVisitorConf{}) visitorConfTypeMap[consts.XtcpProxy] = reflect.TypeOf(XtcpVisitorConf{})
visitorConfTypeMap[consts.SudpProxy] = reflect.TypeOf(SudpVisitorConf{})
} }
type VisitorConf interface { type VisitorConf interface {
...@@ -152,6 +153,36 @@ func (cfg *BaseVisitorConf) UnmarshalFromIni(prefix string, name string, section ...@@ -152,6 +153,36 @@ func (cfg *BaseVisitorConf) UnmarshalFromIni(prefix string, name string, section
return nil return nil
} }
type SudpVisitorConf struct {
BaseVisitorConf
}
func (cfg *SudpVisitorConf) Compare(cmp VisitorConf) bool {
cmpConf, ok := cmp.(*SudpVisitorConf)
if !ok {
return false
}
if !cfg.BaseVisitorConf.compare(&cmpConf.BaseVisitorConf) {
return false
}
return true
}
func (cfg *SudpVisitorConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
if err = cfg.BaseVisitorConf.UnmarshalFromIni(prefix, name, section); err != nil {
return
}
return
}
func (cfg *SudpVisitorConf) Check() (err error) {
if err = cfg.BaseVisitorConf.check(); err != nil {
return
}
return
}
type StcpVisitorConf struct { type StcpVisitorConf struct {
BaseVisitorConf BaseVisitorConf
} }
......
...@@ -30,6 +30,7 @@ var ( ...@@ -30,6 +30,7 @@ var (
HttpsProxy string = "https" HttpsProxy string = "https"
StcpProxy string = "stcp" StcpProxy string = "stcp"
XtcpProxy string = "xtcp" XtcpProxy string = "xtcp"
SudpProxy string = "sudp"
// authentication method // authentication method
TokenAuthMethod string = "token" TokenAuthMethod string = "token"
......
...@@ -57,11 +57,11 @@ func ForwardUserConn(udpConn *net.UDPConn, readCh <-chan *msg.UdpPacket, sendCh ...@@ -57,11 +57,11 @@ func ForwardUserConn(udpConn *net.UDPConn, readCh <-chan *msg.UdpPacket, sendCh
for { for {
n, remoteAddr, err := udpConn.ReadFromUDP(buf) n, remoteAddr, err := udpConn.ReadFromUDP(buf)
if err != nil { if err != nil {
udpConn.Close()
return return
} }
// buf[:n] will be encoded to string, so the bytes can be reused // buf[:n] will be encoded to string, so the bytes can be reused
udpMsg := NewUdpPacket(buf[:n], nil, remoteAddr) udpMsg := NewUdpPacket(buf[:n], nil, remoteAddr)
select { select {
case sendCh <- udpMsg: case sendCh <- udpMsg:
default: default:
......
...@@ -219,6 +219,11 @@ func NewProxy(ctx context.Context, userInfo plugin.UserInfo, rc *controller.Reso ...@@ -219,6 +219,11 @@ func NewProxy(ctx context.Context, userInfo plugin.UserInfo, rc *controller.Reso
BaseProxy: &basePxy, BaseProxy: &basePxy,
cfg: cfg, cfg: cfg,
} }
case *config.SudpProxyConf:
pxy = &SudpProxy{
BaseProxy: &basePxy,
cfg: cfg,
}
default: default:
return pxy, fmt.Errorf("proxy type not support") return pxy, fmt.Errorf("proxy type not support")
} }
......
// Copyright 2019 fatedier, fatedier@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package proxy
import (
"github.com/fatedier/frp/models/config"
)
type SudpProxy struct {
*BaseProxy
cfg *config.SudpProxyConf
}
func (pxy *SudpProxy) Run() (remoteAddr string, err error) {
xl := pxy.xl
listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk)
if errRet != nil {
err = errRet
return
}
pxy.listeners = append(pxy.listeners, listener)
xl.Info("sudp proxy custom listen success")
pxy.startListenHandler(pxy, HandleUserTcpConnection)
return
}
func (pxy *SudpProxy) GetConf() config.ProxyConf {
return pxy.cfg
}
func (pxy *SudpProxy) Close() {
pxy.BaseProxy.Close()
pxy.rc.VisitorManager.CloseListener(pxy.GetName())
}
...@@ -72,6 +72,12 @@ local_port = 10701 ...@@ -72,6 +72,12 @@ local_port = 10701
use_encryption = true use_encryption = true
use_compression = true use_compression = true
[sudp]
type = sudp
sk = abcdefg
local_ip = 127.0.0.1
local_port = 10702
[web01] [web01]
type = http type = http
local_ip = 127.0.0.1 local_ip = 127.0.0.1
......
...@@ -23,3 +23,12 @@ bind_addr = 127.0.0.1 ...@@ -23,3 +23,12 @@ bind_addr = 127.0.0.1
bind_port = 10905 bind_port = 10905
use_encryption = true use_encryption = true
use_compression = true use_compression = true
[sudp_visitor]
type = sudp
role = visitor
server_name = sudp
sk = abcdefg
bind_addr = 127.0.0.1
bind_port = 10816
...@@ -118,6 +118,16 @@ func TestStcp(t *testing.T) { ...@@ -118,6 +118,16 @@ func TestStcp(t *testing.T) {
} }
} }
func TestSudp(t *testing.T) {
assert := assert.New(t)
// Normal
addr := fmt.Sprintf("127.0.0.1:%d", consts.TEST_SUDP_FRP_PORT)
res, err := util.SendUdpMsg(addr, consts.TEST_SUDP_ECHO_STR)
assert.NoError(err)
assert.Equal(consts.TEST_SUDP_ECHO_STR, res)
}
func TestHttp(t *testing.T) { func TestHttp(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
// web01 // web01
......
...@@ -46,6 +46,9 @@ var ( ...@@ -46,6 +46,9 @@ var (
TEST_STCP_EC_FRP_PORT int = 10905 TEST_STCP_EC_FRP_PORT int = 10905
TEST_STCP_ECHO_STR string = "stcp type:" + TEST_STR TEST_STCP_ECHO_STR string = "stcp type:" + TEST_STR
TEST_SUDP_FRP_PORT int = 10816
TEST_SUDP_ECHO_STR string = "sudp type:" + TEST_STR
ProxyTcpPortNotAllowed string = "tcp_port_not_allowed" ProxyTcpPortNotAllowed string = "tcp_port_not_allowed"
ProxyTcpPortUnavailable string = "tcp_port_unavailable" ProxyTcpPortUnavailable string = "tcp_port_unavailable"
ProxyTcpPortNormal string = "tcp_port_normal" ProxyTcpPortNormal string = "tcp_port_normal"
......
...@@ -71,6 +71,11 @@ func GetProxyStatus(statusAddr string, user string, passwd string, name string) ...@@ -71,6 +71,11 @@ func GetProxyStatus(statusAddr string, user string, passwd string, name string)
return &s, nil return &s, nil
} }
} }
for _, s := range allStatus.Sudp {
if s.Name == name {
return &s, nil
}
}
return status, errors.New("no proxy status found") return status, errors.New("no proxy status found")
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册