x/connector/socks/v5/bind.go
2024-07-08 22:38:21 +08:00

133 lines
3.4 KiB
Go

package v5
import (
"context"
"fmt"
"net"
"github.com/go-gost/core/connector"
"github.com/go-gost/core/logger"
"github.com/go-gost/gosocks5"
"github.com/go-gost/x/internal/net/udp"
"github.com/go-gost/x/internal/util/mux"
"github.com/go-gost/x/internal/util/socks"
)
// Bind implements connector.Binder.
func (c *socks5Connector) Bind(ctx context.Context, conn net.Conn, network, address string, opts ...connector.BindOption) (net.Listener, error) {
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
"address": address,
})
log.Debugf("bind on %s/%s", address, network)
options := connector.BindOptions{}
for _, opt := range opts {
opt(&options)
}
switch network {
case "tcp", "tcp4", "tcp6":
if options.Mux {
return c.muxBindTCP(ctx, conn, network, address, log)
}
return c.bindTCP(ctx, conn, network, address, log)
case "udp", "udp4", "udp6":
return c.bindUDP(ctx, conn, network, address, &options, log)
default:
err := fmt.Errorf("network %s is unsupported", network)
log.Error(err)
return nil, err
}
}
func (c *socks5Connector) bindTCP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (net.Listener, error) {
laddr, err := c.bind(conn, gosocks5.CmdBind, network, address, log)
if err != nil {
return nil, err
}
return &tcpListener{
addr: laddr,
conn: conn,
logger: log,
}, nil
}
func (c *socks5Connector) muxBindTCP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (net.Listener, error) {
laddr, err := c.bind(conn, socks.CmdMuxBind, network, address, log)
if err != nil {
return nil, err
}
session, err := mux.ServerSession(conn, c.md.muxCfg)
if err != nil {
return nil, err
}
return &tcpMuxListener{
addr: laddr,
session: session,
logger: log,
}, nil
}
func (c *socks5Connector) bindUDP(ctx context.Context, conn net.Conn, network, address string, opts *connector.BindOptions, log logger.Logger) (net.Listener, error) {
laddr, err := c.bind(conn, socks.CmdUDPTun, network, address, log)
if err != nil {
return nil, err
}
ln := udp.NewListener(socks.UDPTunClientPacketConn(conn),
&udp.ListenConfig{
Addr: laddr,
Backlog: opts.Backlog,
ReadQueueSize: opts.UDPDataQueueSize,
ReadBufferSize: opts.UDPDataBufferSize,
TTL: opts.UDPConnTTL,
KeepAlive: true,
Logger: log,
})
return ln, nil
}
func (l *socks5Connector) bind(conn net.Conn, cmd uint8, network, address string, log logger.Logger) (net.Addr, error) {
addr := gosocks5.Addr{}
addr.ParseFrom(address)
req := gosocks5.NewRequest(cmd, &addr)
log.Trace(req)
if err := req.Write(conn); err != nil {
return nil, err
}
// first reply, bind status
reply, err := gosocks5.ReadReply(conn)
if err != nil {
return nil, err
}
log.Trace(reply)
if reply.Rep != gosocks5.Succeeded {
return nil, fmt.Errorf("bind on %s/%s failed", address, network)
}
var baddr net.Addr
switch network {
case "tcp", "tcp4", "tcp6":
baddr, err = net.ResolveTCPAddr(network, reply.Addr.String())
case "udp", "udp4", "udp6":
baddr, err = net.ResolveUDPAddr(network, reply.Addr.String())
default:
err = fmt.Errorf("unknown network %s", network)
}
if err != nil {
return nil, err
}
log.Debugf("bind on %s/%s OK", baddr, baddr.Network())
return baddr, nil
}