mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
Add multiplexer for VLESS outbound
This commit is contained in:
parent
6b64ebd3c0
commit
50827bcff1
@ -20,6 +20,7 @@ type VLESSOutboundOptions struct {
|
||||
Flow string `json:"flow,omitempty"`
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||
Multiplex *MultiplexOptions `json:"multiplex,omitempty"`
|
||||
Transport *V2RayTransportOptions `json:"transport,omitempty"`
|
||||
PacketEncoding *string `json:"packet_encoding,omitempty"`
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
@ -24,13 +25,14 @@ var _ adapter.Outbound = (*VLESS)(nil)
|
||||
|
||||
type VLESS struct {
|
||||
myOutboundAdapter
|
||||
dialer N.Dialer
|
||||
client *vless.Client
|
||||
serverAddr M.Socksaddr
|
||||
tlsConfig tls.Config
|
||||
transport adapter.V2RayClientTransport
|
||||
packetAddr bool
|
||||
xudp bool
|
||||
dialer N.Dialer
|
||||
client *vless.Client
|
||||
serverAddr M.Socksaddr
|
||||
multiplexDialer *mux.Client
|
||||
tlsConfig tls.Config
|
||||
transport adapter.V2RayClientTransport
|
||||
packetAddr bool
|
||||
xudp bool
|
||||
}
|
||||
|
||||
func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (*VLESS, error) {
|
||||
@ -75,10 +77,65 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outbound.multiplexDialer, err = mux.NewClientWithOptions((*vlessDialer)(outbound), common.PtrValueOrDefault(options.Multiplex))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outbound, nil
|
||||
}
|
||||
|
||||
func (h *VLESS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
if h.multiplexDialer == nil {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||
case N.NetworkUDP:
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
}
|
||||
return (*vlessDialer)(h).DialContext(ctx, network, destination)
|
||||
} else {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
h.logger.InfoContext(ctx, "outbound multiplex connection to ", destination)
|
||||
case N.NetworkUDP:
|
||||
h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
|
||||
}
|
||||
return h.multiplexDialer.DialContext(ctx, network, destination)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if h.multiplexDialer == nil {
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
return (*vlessDialer)(h).ListenPacket(ctx, destination)
|
||||
} else {
|
||||
h.logger.InfoContext(ctx, "outbound multiplex packet connection to ", destination)
|
||||
return h.multiplexDialer.ListenPacket(ctx, destination)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return NewConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VLESS) InterfaceUpdated() error {
|
||||
if h.multiplexDialer != nil {
|
||||
h.multiplexDialer.Reset()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *VLESS) Close() error {
|
||||
return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport)
|
||||
}
|
||||
|
||||
type vlessDialer VLESS
|
||||
|
||||
func (h *vlessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
metadata.Outbound = h.tag
|
||||
metadata.Destination = destination
|
||||
@ -120,7 +177,7 @@ func (h *VLESS) DialContext(ctx context.Context, network string, destination M.S
|
||||
}
|
||||
}
|
||||
|
||||
func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||
ctx, metadata := adapter.AppendContext(ctx)
|
||||
metadata.Outbound = h.tag
|
||||
@ -154,15 +211,3 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.
|
||||
return h.client.DialEarlyPacketConn(conn, destination)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *VLESS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
return NewConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VLESS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||
return NewPacketConnection(ctx, h, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *VLESS) Close() error {
|
||||
return common.Close(h.transport)
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ require (
|
||||
github.com/docker/docker v20.10.18+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/gofrs/uuid/v5 v5.0.0
|
||||
github.com/sagernet/sing v0.2.4
|
||||
github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507
|
||||
github.com/spyzhov/ajson v0.7.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
@ -71,6 +71,7 @@ require (
|
||||
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 // indirect
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc // indirect
|
||||
github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b // indirect
|
||||
github.com/sagernet/sing-tun v0.1.5-0.20230422121432-209ec123ca7b // indirect
|
||||
github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 // indirect
|
||||
|
@ -126,10 +126,12 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing v0.2.4 h1:gC8BR5sglbJZX23RtMyFa8EETP9YEUADhfbEzU1yVbo=
|
||||
github.com/sagernet/sing v0.2.4/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207 h1:+dDVjW20IT+e8maKryaDeRY2+RFmTFdrQeIzqE2WOss=
|
||||
github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc h1:hmbuqKv48SAjiKPoqtJGvS5pEHVPZjTHq9CPwQY2cZ4=
|
||||
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY=
|
||||
github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 h1:87jyxzTjq01VgEiUVSMNRKjCfsSfp/QwyUVT37eXY50=
|
||||
github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 h1:bAHZCdWqJkb8LEW98+YsMVDXGRMUVjka8IC+St6ot88=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507/go.mod h1:UJjvQGw0lyYaDGIDvUraL16fwaAEH1WFw1Y6sUcMPog=
|
||||
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4=
|
||||
|
108
test/mux_test.go
108
test/mux_test.go
@ -4,7 +4,6 @@ import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/sagernet/sing-box/common/mux"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
@ -12,45 +11,32 @@ import (
|
||||
"github.com/gofrs/uuid/v5"
|
||||
)
|
||||
|
||||
var muxProtocols = []mux.Protocol{
|
||||
mux.ProtocolYAMux,
|
||||
mux.ProtocolSMux,
|
||||
var muxProtocols = []string{
|
||||
"smux",
|
||||
"yamux",
|
||||
"h2mux",
|
||||
}
|
||||
|
||||
func TestVMessSMux(t *testing.T) {
|
||||
testVMessMux(t, option.MultiplexOptions{
|
||||
Enabled: true,
|
||||
Protocol: mux.ProtocolSMux.String(),
|
||||
})
|
||||
}
|
||||
|
||||
func TestShadowsocksMux(t *testing.T) {
|
||||
func TestMux(t *testing.T) {
|
||||
for _, protocol := range muxProtocols {
|
||||
t.Run(protocol.String(), func(t *testing.T) {
|
||||
testShadowsocksMux(t, option.MultiplexOptions{
|
||||
t.Run(protocol, func(t *testing.T) {
|
||||
options := option.MultiplexOptions{
|
||||
Enabled: true,
|
||||
Protocol: protocol.String(),
|
||||
Protocol: protocol,
|
||||
}
|
||||
t.Run("shadowsocks", func(t *testing.T) {
|
||||
testShadowsocksMux(t, options)
|
||||
})
|
||||
t.Run("vmess", func(t *testing.T) {
|
||||
testVMessMux(t, options)
|
||||
})
|
||||
t.Run("vless", func(t *testing.T) {
|
||||
testVLESSMux(t, options)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShadowsockH2Mux(t *testing.T) {
|
||||
testShadowsocksMux(t, option.MultiplexOptions{
|
||||
Enabled: true,
|
||||
Protocol: mux.ProtocolH2Mux.String(),
|
||||
Padding: true,
|
||||
})
|
||||
}
|
||||
|
||||
func TestShadowsockSMuxPadding(t *testing.T) {
|
||||
testShadowsocksMux(t, option.MultiplexOptions{
|
||||
Enabled: true,
|
||||
Protocol: mux.ProtocolSMux.String(),
|
||||
Padding: true,
|
||||
})
|
||||
}
|
||||
|
||||
func testShadowsocksMux(t *testing.T, options option.MultiplexOptions) {
|
||||
method := shadowaead_2022.List[0]
|
||||
password := mkBase64(t, 16)
|
||||
@ -170,3 +156,63 @@ func testVMessMux(t *testing.T, options option.MultiplexOptions) {
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
|
||||
func testVLESSMux(t *testing.T, options option.MultiplexOptions) {
|
||||
user, _ := uuid.NewV4()
|
||||
startInstance(t, option.Options{
|
||||
Inbounds: []option.Inbound{
|
||||
{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: clientPort,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.TypeVLESS,
|
||||
VLESSOptions: option.VLESSInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||
ListenPort: serverPort,
|
||||
},
|
||||
Users: []option.VLESSUser{
|
||||
{
|
||||
UUID: user.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbounds: []option.Outbound{
|
||||
{
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Type: C.TypeVLESS,
|
||||
Tag: "vless-out",
|
||||
VLESSOptions: option.VLESSOutboundOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: "127.0.0.1",
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
UUID: user.String(),
|
||||
Multiplex: &options,
|
||||
},
|
||||
},
|
||||
},
|
||||
Route: &option.RouteOptions{
|
||||
Rules: []option.Rule{
|
||||
{
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Inbound: []string{"mixed-in"},
|
||||
Outbound: "vless-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
testSuit(t, clientPort, testPort)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user