add UDP support for reverse proxy tunnel

This commit is contained in:
ginuerzh
2023-02-02 19:18:10 +08:00
parent 9750998940
commit 416405b1f0
29 changed files with 226 additions and 87 deletions

View File

@ -17,7 +17,7 @@ import (
// Bind implements connector.Binder.
func (c *relayConnector) Bind(ctx context.Context, conn net.Conn, network, address string, opts ...connector.BindOption) (net.Listener, error) {
if !c.md.tunnelID.IsZero() {
return c.tunnel(ctx, conn, c.options.Logger)
return c.bindTunnel(ctx, conn, network, c.options.Logger)
}
log := c.options.Logger.WithFields(map[string]any{
@ -43,31 +43,36 @@ func (c *relayConnector) Bind(ctx context.Context, conn net.Conn, network, addre
}
}
func (c *relayConnector) tunnel(ctx context.Context, conn net.Conn, log logger.Logger) (net.Listener, error) {
addr, cid, err := c.initTunnel(conn)
func (c *relayConnector) bindTunnel(ctx context.Context, conn net.Conn, network string, log logger.Logger) (net.Listener, error) {
addr, cid, err := c.initTunnel(conn, network)
if err != nil {
return nil, err
}
log.Debugf("create tunnel %s connector %s OK", c.md.tunnelID.String(), cid)
log.Debugf("create tunnel %s connector %s/%s OK", c.md.tunnelID.String(), cid, network)
session, err := mux.ServerSession(conn)
if err != nil {
return nil, err
}
return &tcpListener{
return &bindListener{
network: network,
addr: addr,
session: session,
logger: log,
}, nil
}
func (c *relayConnector) initTunnel(conn net.Conn) (addr net.Addr, cid relay.ConnectorID, err error) {
func (c *relayConnector) initTunnel(conn net.Conn, network string) (addr net.Addr, cid relay.ConnectorID, err error) {
req := relay.Request{
Version: relay.Version1,
Cmd: relay.CmdBind,
}
if network == "udp" {
req.Cmd |= relay.FUDP
}
if c.options.Auth != nil {
pwd, _ := c.options.Auth.Password()
req.Features = append(req.Features, &relay.UserAuthFeature{
@ -77,7 +82,7 @@ func (c *relayConnector) initTunnel(conn net.Conn) (addr net.Addr, cid relay.Con
}
req.Features = append(req.Features, &relay.TunnelFeature{
ID: c.md.tunnelID,
ID: c.md.tunnelID.ID(),
})
if _, err = req.WriteTo(conn); err != nil {
return
@ -102,7 +107,7 @@ func (c *relayConnector) initTunnel(conn net.Conn) (addr net.Addr, cid relay.Con
}
case relay.FeatureTunnel:
if feature, _ := f.(*relay.TunnelFeature); feature != nil {
cid = feature.ID
cid = relay.NewConnectorID(feature.ID[:])
}
}
}
@ -122,7 +127,7 @@ func (c *relayConnector) bindTCP(ctx context.Context, conn net.Conn, network, ad
return nil, err
}
return &tcpListener{
return &bindListener{
addr: laddr,
session: session,
logger: log,

View File

@ -10,6 +10,7 @@ import (
"net"
"sync"
"github.com/go-gost/core/common/bufpool"
mdata "github.com/go-gost/core/metadata"
"github.com/go-gost/relay"
)
@ -67,9 +68,11 @@ func (c *udpConn) Read(b []byte) (n int, err error) {
if len(b) >= dlen {
return io.ReadFull(c.Conn, b[:dlen])
}
buf := make([]byte, dlen)
_, err = io.ReadFull(c.Conn, buf)
n = copy(b, buf)
buf := bufpool.Get(dlen)
defer bufpool.Put(buf)
_, err = io.ReadFull(c.Conn, *buf)
n = copy(b, *buf)
return
}
@ -137,3 +140,72 @@ func (c *bindConn) RemoteAddr() net.Addr {
func (c *bindConn) Metadata() mdata.Metadata {
return c.md
}
type bindUDPConn struct {
net.Conn
localAddr net.Addr
remoteAddr net.Addr
md mdata.Metadata
}
func (c *bindUDPConn) Read(b []byte) (n int, err error) {
// 2-byte data length header
var bh [2]byte
_, err = io.ReadFull(c.Conn, bh[:])
if err != nil {
return
}
dlen := int(binary.BigEndian.Uint16(bh[:]))
if len(b) >= dlen {
n, err = io.ReadFull(c.Conn, b[:dlen])
return
}
buf := bufpool.Get(dlen)
defer bufpool.Put(buf)
_, err = io.ReadFull(c.Conn, *buf)
n = copy(b, *buf)
return
}
func (c *bindUDPConn) Write(b []byte) (n int, err error) {
if len(b) > math.MaxUint16 {
err = errors.New("write: data maximum exceeded")
return
}
// 2-byte data length header
var bh [2]byte
binary.BigEndian.PutUint16(bh[:], uint16(len(b)))
_, err = c.Conn.Write(bh[:])
if err != nil {
return
}
return c.Conn.Write(b)
}
func (c *bindUDPConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
addr = c.remoteAddr
n, err = c.Read(b)
return
}
func (c *bindUDPConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
return c.Write(b)
}
func (c *bindUDPConn) LocalAddr() net.Addr {
return c.localAddr
}
func (c *bindUDPConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
// Metadata implements metadata.Metadatable interface.
func (c *bindUDPConn) Metadata() mdata.Metadata {
return c.md
}

View File

@ -88,7 +88,7 @@ func (c *relayConnector) Connect(ctx context.Context, conn net.Conn, network, ad
if !c.md.tunnelID.IsZero() {
req.Features = append(req.Features, &relay.TunnelFeature{
ID: c.md.tunnelID,
ID: c.md.tunnelID.ID(),
})
}

View File

@ -6,18 +6,20 @@ import (
"strconv"
"github.com/go-gost/core/logger"
mdata "github.com/go-gost/core/metadata"
"github.com/go-gost/relay"
"github.com/go-gost/x/internal/util/mux"
mdx "github.com/go-gost/x/metadata"
)
type tcpListener struct {
type bindListener struct {
network string
addr net.Addr
session *mux.Session
logger logger.Logger
}
func (p *tcpListener) Accept() (net.Conn, error) {
func (p *bindListener) Accept() (net.Conn, error) {
cc, err := p.session.Accept()
if err != nil {
return nil, err
@ -33,7 +35,7 @@ func (p *tcpListener) Accept() (net.Conn, error) {
return conn, nil
}
func (p *tcpListener) getPeerConn(conn net.Conn) (net.Conn, error) {
func (p *bindListener) getPeerConn(conn net.Conn) (net.Conn, error) {
// second reply, peer connected
resp := relay.Response{}
if _, err := resp.ReadFrom(conn); err != nil {
@ -64,21 +66,33 @@ func (p *tcpListener) getPeerConn(conn net.Conn) (net.Conn, error) {
return nil, err
}
var md mdata.Metadata
if host != "" {
md = mdx.NewMetadata(map[string]any{"host": host})
}
if p.network == "udp" {
return &bindUDPConn{
Conn: conn,
localAddr: p.addr,
remoteAddr: raddr,
md: md,
}, nil
}
cn := &bindConn{
Conn: conn,
localAddr: p.addr,
remoteAddr: raddr,
}
if host != "" {
cn.md = mdx.NewMetadata(map[string]any{"host": host})
md: md,
}
return cn, nil
}
func (p *tcpListener) Addr() net.Addr {
func (p *bindListener) Addr() net.Addr {
return p.addr
}
func (p *tcpListener) Close() error {
func (p *bindListener) Close() error {
return p.session.Close()
}