feat: Integrate UoT support in inbound and outbound handling

This commit is contained in:
n3t1zen
2026-02-26 10:40:25 +08:00
parent 72956fb4f4
commit 13754f26e4
2 changed files with 26 additions and 26 deletions

View File

@@ -10,6 +10,7 @@ import (
"github.com/sagernet/sing-box/common/listener" "github.com/sagernet/sing-box/common/listener"
"github.com/sagernet/sing-box/common/mux" "github.com/sagernet/sing-box/common/mux"
boxTLS "github.com/sagernet/sing-box/common/tls" boxTLS "github.com/sagernet/sing-box/common/tls"
"github.com/sagernet/sing-box/common/uot"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@@ -44,7 +45,7 @@ type Inbound struct {
func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.MySQLInboundOptions) (adapter.Inbound, error) { func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.MySQLInboundOptions) (adapter.Inbound, error) {
inbound := &Inbound{ inbound := &Inbound{
Adapter: inbound.NewAdapter(C.TypeMySQL, tag), Adapter: inbound.NewAdapter(C.TypeMySQL, tag),
router: router, router: uot.NewRouter(router, logger),
logger: logger, logger: logger,
identityProvider: server.NewInMemoryProvider(), identityProvider: server.NewInMemoryProvider(),
} }
@@ -197,7 +198,7 @@ func (h *Inbound) handleMuxStream0(ctx context.Context, conn net.Conn, source M.
h.router.RouteConnectionEx(ctx, conn, metadata, nil) h.router.RouteConnectionEx(ctx, conn, metadata, nil)
case commandUDP: case commandUDP:
metadata.Destination = destination metadata.Destination = destination
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination) h.logger.InfoContext(ctx, "inbound UoT packet connection to ", metadata.Destination)
h.router.RouteConnectionEx(ctx, conn, metadata, nil) h.router.RouteConnectionEx(ctx, conn, metadata, nil)
default: default:
return E.New("unknown command ", command) return E.New("unknown command ", command)

View File

@@ -14,11 +14,11 @@ import (
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/uot"
"github.com/sagernet/smux" "github.com/sagernet/smux"
"github.com/go-mysql-org/go-mysql/client" "github.com/go-mysql-org/go-mysql/client"
@@ -51,6 +51,13 @@ type muxSession struct {
conn net.Conn conn net.Conn
} }
func closeMuxSession(entry *muxSession) {
if entry == nil {
return
}
_ = common.Close(entry.session, entry.conn)
}
func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.MySQLOutboundOptions) (adapter.Outbound, error) { func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.MySQLOutboundOptions) (adapter.Outbound, error) {
outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain())
if err != nil { if err != nil {
@@ -153,7 +160,7 @@ func (h *Outbound) getSession(index int) (*smux.Session, error) {
return entry.session, nil return entry.session, nil
} }
if entry != nil { if entry != nil {
entry.conn.Close() closeMuxSession(entry)
h.sessions[index] = nil h.sessions[index] = nil
} }
@@ -171,7 +178,7 @@ func (h *Outbound) getSession(index int) (*smux.Session, error) {
h.sessions[index] = nil h.sessions[index] = nil
} }
h.sessionAccess.Unlock() h.sessionAccess.Unlock()
conn.Close() _ = common.Close(session, conn)
}(index, entry.session, entry.conn) }(index, entry.session, entry.conn)
return entry.session, nil return entry.session, nil
@@ -183,7 +190,7 @@ func (h *Outbound) invalidateSession(index int, session *smux.Session) {
if current := h.sessions[index]; current != nil && current.session == session { if current := h.sessions[index]; current != nil && current.session == session {
h.sessions[index] = nil h.sessions[index] = nil
current.conn.Close() closeMuxSession(current)
} }
} }
@@ -234,24 +241,30 @@ func (h *Outbound) DialContext(ctx context.Context, network string, destination
h.logger.InfoContext(ctx, "outbound connection to ", destination) h.logger.InfoContext(ctx, "outbound connection to ", destination)
return h.openStream(ctx, commandTCP, destination) return h.openStream(ctx, commandTCP, destination)
case N.NetworkUDP: case N.NetworkUDP:
h.logger.InfoContext(ctx, "outbound packet connection to ", destination) h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
conn, err := h.openStream(ctx, commandUDP, destination) conn, err := h.openStream(ctx, commandUDP, uot.RequestDestination(uot.Version))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bufio.NewBindPacketConn(&packetConn{conn}, destination), nil return uot.NewLazyConn(conn, uot.Request{
IsConnect: true,
Destination: destination,
}), nil
default: default:
return nil, E.New("unsupported network: ", network) return nil, E.New("unsupported network: ", network)
} }
} }
func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
h.logger.InfoContext(ctx, "outbound packet connection to ", destination) h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
conn, err := h.openStream(ctx, commandUDP, destination) conn, err := h.openStream(ctx, commandUDP, uot.RequestDestination(uot.Version))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &packetConn{conn}, nil return uot.NewLazyConn(conn, uot.Request{
IsConnect: false,
Destination: destination,
}), nil
} }
func (h *Outbound) InterfaceUpdated() { func (h *Outbound) InterfaceUpdated() {
@@ -280,17 +293,3 @@ func (h *Outbound) Close() error {
} }
return err return err
} }
// packetConn wraps a net.Conn as a net.PacketConn for UDP-over-TCP
type packetConn struct {
net.Conn
}
func (c *packetConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
n, err = c.Conn.Read(p)
return
}
func (c *packetConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
return c.Conn.Write(p)
}