Add multiplexer for VLESS outbound

This commit is contained in:
世界 2023-04-24 09:55:46 +08:00
parent 6b64ebd3c0
commit 50827bcff1
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
5 changed files with 149 additions and 54 deletions

View File

@ -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"`
}

View File

@ -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)
}

View File

@ -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

View File

@ -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=

View File

@ -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)
}