relay: added private tunnel
This commit is contained in:
parent
93b40f4c86
commit
24037aba7b
@ -48,7 +48,7 @@ func (c *relayConnector) tunnel(ctx context.Context, conn net.Conn, log logger.L
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("create tunnel %s connector %s OK", c.md.tunnelID, cid)
|
log.Debugf("create tunnel %s connector %s OK", c.md.tunnelID.String(), cid)
|
||||||
|
|
||||||
session, err := mux.ServerSession(conn)
|
session, err := mux.ServerSession(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -90,7 +90,7 @@ func (c *relayConnector) initTunnel(conn net.Conn) (addr net.Addr, cid relay.Con
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resp.Status != relay.StatusOK {
|
if resp.Status != relay.StatusOK {
|
||||||
err = fmt.Errorf("%d: create tunnel %s failed", resp.Status, c.md.tunnelID)
|
err = fmt.Errorf("%d: create tunnel %s failed", resp.Status, c.md.tunnelID.String())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,11 +83,13 @@ func (c *relayConnector) Connect(ctx context.Context, conn net.Conn, network, ad
|
|||||||
if err := af.ParseFrom(address); err != nil {
|
if err := af.ParseFrom(address); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// forward mode if port is 0.
|
|
||||||
if af.Port > 0 {
|
|
||||||
req.Features = append(req.Features, af)
|
req.Features = append(req.Features, af)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !c.md.tunnelID.IsZero() {
|
||||||
|
req.Features = append(req.Features, &relay.TunnelFeature{
|
||||||
|
ID: c.md.tunnelID,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.md.noDelay {
|
if c.md.noDelay {
|
||||||
|
@ -30,7 +30,7 @@ func (c *relayConnector) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
copy(c.md.tunnelID[:], uuid[:])
|
c.md.tunnelID = relay.NewTunnelID(uuid[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
2
go.mod
2
go.mod
@ -10,7 +10,7 @@ require (
|
|||||||
github.com/go-gost/core v0.0.0-20230114050924-1a8c1ccb1dc5
|
github.com/go-gost/core v0.0.0-20230114050924-1a8c1ccb1dc5
|
||||||
github.com/go-gost/gosocks4 v0.0.1
|
github.com/go-gost/gosocks4 v0.0.1
|
||||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09
|
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09
|
||||||
github.com/go-gost/relay v0.2.0
|
github.com/go-gost/relay v0.3.1
|
||||||
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451
|
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
|
4
go.sum
4
go.sum
@ -97,8 +97,8 @@ github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2
|
|||||||
github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
|
github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
|
||||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgOuJkQ7QLfG0Hs5peWIUSysCDNz4pfe04=
|
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgOuJkQ7QLfG0Hs5peWIUSysCDNz4pfe04=
|
||||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
|
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
|
||||||
github.com/go-gost/relay v0.2.0 h1:8udTweykgDUdOY1j1U90fApNuG7Sp7pvKoiIp3eV6ME=
|
github.com/go-gost/relay v0.3.1 h1:mkKtvMT5n3mTSHbQo//DXXLxTsIUJKRQ4Fn4atma+Ds=
|
||||||
github.com/go-gost/relay v0.2.0/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
|
github.com/go-gost/relay v0.3.1/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
|
||||||
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451 h1:xj8gUZGYO3nb5+6Bjw9+tsFkA9sYynrOvDvvC4uDV2I=
|
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451 h1:xj8gUZGYO3nb5+6Bjw9+tsFkA9sYynrOvDvvC4uDV2I=
|
||||||
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
|
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/go-gost/core/chain"
|
"github.com/go-gost/core/chain"
|
||||||
"github.com/go-gost/core/handler"
|
"github.com/go-gost/core/handler"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
xchain "github.com/go-gost/x/chain"
|
|
||||||
netpkg "github.com/go-gost/x/internal/net"
|
netpkg "github.com/go-gost/x/internal/net"
|
||||||
"github.com/go-gost/x/internal/util/forward"
|
"github.com/go-gost/x/internal/util/forward"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
@ -46,13 +45,6 @@ func (h *forwardHandler) Init(md md.Metadata) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.hop == nil {
|
|
||||||
// dummy node used by relay connector.
|
|
||||||
h.hop = xchain.NewChainHop([]*chain.Node{
|
|
||||||
{Name: "dummy", Addr: ":0"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
h.router = h.options.Router
|
h.router = h.options.Router
|
||||||
if h.router == nil {
|
if h.router == nil {
|
||||||
h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger))
|
h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger))
|
||||||
@ -101,18 +93,23 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var target *chain.Node
|
if _, _, err := net.SplitHostPort(host); err != nil {
|
||||||
|
host = net.JoinHostPort(host, "0")
|
||||||
|
}
|
||||||
|
target := &chain.Node{
|
||||||
|
Addr: host,
|
||||||
|
}
|
||||||
if h.hop != nil {
|
if h.hop != nil {
|
||||||
target = h.hop.Select(ctx,
|
target = h.hop.Select(ctx,
|
||||||
chain.HostSelectOption(host),
|
chain.HostSelectOption(host),
|
||||||
chain.ProtocolSelectOption(protocol),
|
chain.ProtocolSelectOption(protocol),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
if target == nil {
|
if target == nil {
|
||||||
err := errors.New("target not available")
|
err := errors.New("target not available")
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log = log.WithFields(map[string]any{
|
log = log.WithFields(map[string]any{
|
||||||
"dst": fmt.Sprintf("%s/%s", target.Addr, network),
|
"dst": fmt.Sprintf("%s/%s", target.Addr, network),
|
||||||
|
@ -212,8 +212,7 @@ func (h *relayHandler) handleTunnel(ctx context.Context, conn net.Conn, tunnelID
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var connectorID relay.ConnectorID
|
connectorID := relay.NewTunnelID(uuid[:])
|
||||||
copy(connectorID[:], uuid[:])
|
|
||||||
|
|
||||||
af := &relay.AddrFeature{}
|
af := &relay.AddrFeature{}
|
||||||
err = af.ParseFrom(h.ep.Addr().String())
|
err = af.ParseFrom(h.ep.Addr().String())
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/relay"
|
"github.com/go-gost/relay"
|
||||||
netpkg "github.com/go-gost/x/internal/net"
|
netpkg "github.com/go-gost/x/internal/net"
|
||||||
|
xnet "github.com/go-gost/x/internal/net"
|
||||||
sx "github.com/go-gost/x/internal/util/selector"
|
sx "github.com/go-gost/x/internal/util/selector"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network
|
|||||||
"cmd": "connect",
|
"cmd": "connect",
|
||||||
})
|
})
|
||||||
|
|
||||||
log.Debugf("%s >> %s", conn.RemoteAddr(), address)
|
log.Debugf("%s >> %s/%s", conn.RemoteAddr(), address, network)
|
||||||
|
|
||||||
resp := relay.Response{
|
resp := relay.Response{
|
||||||
Version: relay.Version1,
|
Version: relay.Version1,
|
||||||
@ -94,3 +95,83 @@ func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *relayHandler) handleConnectTunnel(ctx context.Context, conn net.Conn, network, address string, tunnelID relay.TunnelID, log logger.Logger) error {
|
||||||
|
log = log.WithFields(map[string]any{
|
||||||
|
"dst": fmt.Sprintf("%s/%s", address, network),
|
||||||
|
"cmd": "connect",
|
||||||
|
"tunnel": tunnelID.String(),
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Debugf("%s >> %s/%s", conn.RemoteAddr(), address, network)
|
||||||
|
|
||||||
|
resp := relay.Response{
|
||||||
|
Version: relay.Version1,
|
||||||
|
Status: relay.StatusOK,
|
||||||
|
}
|
||||||
|
|
||||||
|
host, _, _ := net.SplitHostPort(address)
|
||||||
|
|
||||||
|
if h.options.Bypass != nil && h.options.Bypass.Contains(address) {
|
||||||
|
log.Debug("bypass: ", address)
|
||||||
|
resp.Status = relay.StatusForbidden
|
||||||
|
_, err := resp.WriteTo(conn)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var tid relay.TunnelID
|
||||||
|
if ingress := h.md.ingress; ingress != nil {
|
||||||
|
tid = parseTunnelID(ingress.Get(host))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tid.Equal(tunnelID) {
|
||||||
|
resp.Status = relay.StatusBadRequest
|
||||||
|
resp.WriteTo(conn)
|
||||||
|
err := fmt.Errorf("tunnel %s not found", tunnelID.String())
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cc, err := getTunnelConn(h.pool, tunnelID, 3, log)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer cc.Close()
|
||||||
|
|
||||||
|
log.Debugf("%s >> %s", conn.RemoteAddr(), cc.RemoteAddr())
|
||||||
|
|
||||||
|
if h.md.noDelay {
|
||||||
|
if _, err := resp.WriteTo(conn); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rc := &tcpConn{
|
||||||
|
Conn: conn,
|
||||||
|
}
|
||||||
|
// cache the header
|
||||||
|
if _, err := resp.WriteTo(&rc.wbuf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn = rc
|
||||||
|
}
|
||||||
|
|
||||||
|
af := &relay.AddrFeature{}
|
||||||
|
af.ParseFrom(conn.RemoteAddr().String())
|
||||||
|
resp = relay.Response{
|
||||||
|
Version: relay.Version1,
|
||||||
|
Status: relay.StatusOK,
|
||||||
|
Features: []relay.Feature{af},
|
||||||
|
}
|
||||||
|
resp.WriteTo(cc)
|
||||||
|
|
||||||
|
t := time.Now()
|
||||||
|
log.Debugf("%s <-> %s", conn.RemoteAddr(), cc.RemoteAddr())
|
||||||
|
xnet.Transport(conn, cc)
|
||||||
|
log.WithFields(map[string]any{
|
||||||
|
"duration": time.Since(t),
|
||||||
|
}).Debugf("%s >-< %s", conn.RemoteAddr(), cc.RemoteAddr())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
climiter "github.com/go-gost/x/limiter/conn/wrapper"
|
climiter "github.com/go-gost/x/limiter/conn/wrapper"
|
||||||
limiter "github.com/go-gost/x/limiter/traffic/wrapper"
|
limiter "github.com/go-gost/x/limiter/traffic/wrapper"
|
||||||
metrics "github.com/go-gost/x/metrics/wrapper"
|
metrics "github.com/go-gost/x/metrics/wrapper"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type epListener struct {
|
type epListener struct {
|
||||||
@ -118,30 +117,18 @@ func (h *epHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.H
|
|||||||
|
|
||||||
var tunnelID relay.TunnelID
|
var tunnelID relay.TunnelID
|
||||||
if h.ingress != nil {
|
if h.ingress != nil {
|
||||||
v := h.ingress.Get(host)
|
tunnelID = parseTunnelID(h.ingress.Get(host))
|
||||||
uuid, _ := uuid.Parse(v)
|
}
|
||||||
copy(tunnelID[:], uuid[:])
|
if tunnelID.IsPrivate() {
|
||||||
|
err := fmt.Errorf("tunnel %s is private", tunnelID)
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
log = log.WithFields(map[string]any{
|
log = log.WithFields(map[string]any{
|
||||||
"tunnel": tunnelID.String(),
|
"tunnel": tunnelID.String(),
|
||||||
})
|
})
|
||||||
|
|
||||||
var cc net.Conn
|
cc, err := getTunnelConn(h.pool, tunnelID, 3, log)
|
||||||
var err error
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
c := h.pool.Get(tunnelID)
|
|
||||||
if c == nil {
|
|
||||||
err = fmt.Errorf("tunnel %s not available", tunnelID.String())
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
cc, err = c.Session().GetConn()
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
|
@ -195,18 +195,23 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handle
|
|||||||
}
|
}
|
||||||
|
|
||||||
if h.hop != nil {
|
if h.hop != nil {
|
||||||
|
/*
|
||||||
if address != "" {
|
if address != "" {
|
||||||
resp.Status = relay.StatusForbidden
|
resp.Status = relay.StatusForbidden
|
||||||
log.Error("forward mode, connect is forbidden")
|
log.Error("forward mode, CONNECT method is forbidden")
|
||||||
_, err := resp.WriteTo(conn)
|
_, err := resp.WriteTo(conn)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
// forward mode
|
// forward mode
|
||||||
return h.handleForward(ctx, conn, network, log)
|
return h.handleForward(ctx, conn, network, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch req.Cmd & relay.CmdMask {
|
switch req.Cmd & relay.CmdMask {
|
||||||
case 0, relay.CmdConnect:
|
case relay.CmdConnect:
|
||||||
|
if !tunnelID.IsZero() {
|
||||||
|
return h.handleConnectTunnel(ctx, conn, network, address, tunnelID, log)
|
||||||
|
}
|
||||||
return h.handleConnect(ctx, conn, network, address, log)
|
return h.handleConnect(ctx, conn, network, address, log)
|
||||||
case relay.CmdBind:
|
case relay.CmdBind:
|
||||||
if !tunnelID.IsZero() {
|
if !tunnelID.IsZero() {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package relay
|
package relay
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -8,6 +10,7 @@ import (
|
|||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/relay"
|
"github.com/go-gost/relay"
|
||||||
"github.com/go-gost/x/internal/util/mux"
|
"github.com/go-gost/x/internal/util/mux"
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Connector struct {
|
type Connector struct {
|
||||||
@ -110,13 +113,13 @@ func (t *Tunnel) clean() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ConnectorPool struct {
|
type ConnectorPool struct {
|
||||||
tunnels map[relay.TunnelID]*Tunnel
|
tunnels map[string]*Tunnel
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConnectorPool() *ConnectorPool {
|
func NewConnectorPool() *ConnectorPool {
|
||||||
return &ConnectorPool{
|
return &ConnectorPool{
|
||||||
tunnels: make(map[relay.TunnelID]*Tunnel),
|
tunnels: make(map[string]*Tunnel),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,10 +127,12 @@ func (p *ConnectorPool) Add(tid relay.TunnelID, c *Connector) {
|
|||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
t := p.tunnels[tid]
|
s := tid.String()
|
||||||
|
|
||||||
|
t := p.tunnels[s]
|
||||||
if t == nil {
|
if t == nil {
|
||||||
t = NewTunnel(tid)
|
t = NewTunnel(tid)
|
||||||
p.tunnels[tid] = t
|
p.tunnels[s] = t
|
||||||
}
|
}
|
||||||
t.AddConnector(c)
|
t.AddConnector(c)
|
||||||
}
|
}
|
||||||
@ -140,10 +145,49 @@ func (p *ConnectorPool) Get(tid relay.TunnelID) *Connector {
|
|||||||
p.mu.RLock()
|
p.mu.RLock()
|
||||||
defer p.mu.RUnlock()
|
defer p.mu.RUnlock()
|
||||||
|
|
||||||
t := p.tunnels[tid]
|
t := p.tunnels[tid.String()]
|
||||||
if t == nil {
|
if t == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.GetConnector()
|
return t.GetConnector()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseTunnelID(s string) (tid relay.TunnelID) {
|
||||||
|
if s == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
private := false
|
||||||
|
if s[0] == '$' {
|
||||||
|
private = true
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
uuid, _ := uuid.Parse(s)
|
||||||
|
|
||||||
|
if private {
|
||||||
|
return relay.NewPrivateTunnelID(uuid[:])
|
||||||
|
}
|
||||||
|
return relay.NewTunnelID(uuid[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTunnelConn(pool *ConnectorPool, tunnelID relay.TunnelID, retry int, log logger.Logger) (conn net.Conn, err error) {
|
||||||
|
if retry <= 0 {
|
||||||
|
retry = 1
|
||||||
|
}
|
||||||
|
for i := 0; i < retry; i++ {
|
||||||
|
c := pool.Get(tunnelID)
|
||||||
|
if c == nil {
|
||||||
|
err = fmt.Errorf("tunnel %s not available", tunnelID.String())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err = c.Session().GetConn()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user