diff --git a/go.mod b/go.mod index adddd701..83e1d6b4 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704 - github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9 + github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685 github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e github.com/spf13/cobra v1.5.0 diff --git a/go.sum b/go.sum index dec353a2..901affd5 100644 --- a/go.sum +++ b/go.sum @@ -153,8 +153,8 @@ github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDe github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704 h1:DOQQXQbB2gq4n2FuMHrL07HRs2naCCsuiu/9l1JFb9A= github.com/sagernet/sing-tun v0.0.0-20220922083325-80ee99472704/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM= -github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9 h1:w7JlEa4xHXuuPTL3Opb+Z5f/l66SYi54rSfobzuxSF8= -github.com/sagernet/sing-vmess v0.0.0-20220923035311-d70afe4ab2c9/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= +github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685 h1:AZzFNRR/ZwMTceUQ1b/mxx6oyKqmFymdMn/yleJmoVM= +github.com/sagernet/sing-vmess v0.0.0-20220925083655-063bc85ea685/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38= github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8= github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e h1:7uw2njHFGE+VpWamge6o56j2RWk4omF6uLKKxMmcWvs= diff --git a/test/clash_test.go b/test/clash_test.go index 0c40d1be..905618a3 100644 --- a/test/clash_test.go +++ b/test/clash_test.go @@ -38,6 +38,7 @@ const ( ImageShadowTLS = "ghcr.io/ihciah/shadow-tls:latest" ImageShadowsocksR = "teddysun/shadowsocks-r:latest" ImageXRayCore = "teddysun/xray:latest" + ImageShadowsocksLegacy = "mritd/shadowsocks:latest" ) var allImages = []string{ @@ -52,6 +53,7 @@ var allImages = []string{ ImageShadowTLS, ImageShadowsocksR, ImageXRayCore, + ImageShadowsocksLegacy, } var localIP = netip.MustParseAddr("127.0.0.1") diff --git a/test/ss_plugin_test.go b/test/ss_plugin_test.go new file mode 100644 index 00000000..d8d9183b --- /dev/null +++ b/test/ss_plugin_test.go @@ -0,0 +1,63 @@ +package main + +import ( + "net/netip" + "testing" + + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/option" +) + +func TestShadowsocksObfs(t *testing.T) { + for _, mode := range []string{ + "http", "tls", + } { + t.Run("obfs-local "+mode, func(t *testing.T) { + testShadowsocksPlugin(t, "obfs-local", "obfs="+mode, "--plugin obfs-server --plugin-opts obfs="+mode) + }) + } +} + +func TestShadowsocksV2RayPlugin(t *testing.T) { + testShadowsocksPlugin(t, "v2ray-plugin", "", "--plugin v2ray-plugin --plugin-opts=server") +} + +func testShadowsocksPlugin(t *testing.T, name string, opts string, args string) { + startDockerContainer(t, DockerOptions{ + Image: ImageShadowsocksLegacy, + Ports: []uint16{serverPort, testPort}, + Env: []string{ + "SS_MODULE=ss-server", + "SS_CONFIG=-s 0.0.0.0 -u -p 10000 -m chacha20-ietf-poly1305 -k FzcLbKs2dY9mhL " + args, + }, + }) + startInstance(t, option.Options{ + Inbounds: []option.Inbound{ + { + Type: C.TypeMixed, + MixedOptions: option.HTTPMixedInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.ListenAddress(netip.IPv4Unspecified()), + ListenPort: clientPort, + }, + }, + }, + }, + Outbounds: []option.Outbound{ + { + Type: C.TypeShadowsocks, + ShadowsocksOptions: option.ShadowsocksOutboundOptions{ + ServerOptions: option.ServerOptions{ + Server: "127.0.0.1", + ServerPort: serverPort, + }, + Method: "chacha20-ietf-poly1305", + Password: "FzcLbKs2dY9mhL", + Plugin: name, + PluginOptions: opts, + }, + }, + }, + }) + testSuitSimple(t, clientPort, testPort) +} diff --git a/transport/sip003/obfs.go b/transport/sip003/obfs.go index d13f8516..bb6ca502 100644 --- a/transport/sip003/obfs.go +++ b/transport/sip003/obfs.go @@ -19,7 +19,10 @@ func init() { } func newObfsLocal(pluginOpts Args, router adapter.Router, dialer N.Dialer, serverAddr M.Socksaddr) (Plugin, error) { - var plugin ObfsLocal + plugin := &ObfsLocal{ + dialer: dialer, + serverAddr: serverAddr, + } mode := "http" if obfsMode, loaded := pluginOpts.Get("obfs"); loaded { mode = obfsMode @@ -35,7 +38,7 @@ func newObfsLocal(pluginOpts Args, router adapter.Router, dialer N.Dialer, serve return nil, E.New("unknown obfs mode ", mode) } plugin.port = F.ToString(serverAddr.Port) - return &plugin, nil + return plugin, nil } type ObfsLocal struct { diff --git a/transport/sip003/v2ray.go b/transport/sip003/v2ray.go index 5d498cbe..19b17baa 100644 --- a/transport/sip003/v2ray.go +++ b/transport/sip003/v2ray.go @@ -2,12 +2,15 @@ package sip003 import ( "context" + "net" + "strconv" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/transport/v2ray" + "github.com/sagernet/sing-vmess" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" @@ -56,6 +59,7 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser } } + var mux int var transportOptions option.V2RayTransportOptions switch mode { case "websocket": @@ -68,6 +72,15 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser Path: path, }, } + if muxOpt, loaded := pluginOpts.Get("mux"); loaded { + muxVal, err := strconv.Atoi(muxOpt) + if err != nil { + return nil, E.Cause(err, "parse mux value") + } + mux = muxVal + } else { + mux = 1 + } case "quic": transportOptions = option.V2RayTransportOptions{ Type: C.V2RayTransportTypeQUIC, @@ -76,5 +89,28 @@ func newV2RayPlugin(pluginOpts Args, router adapter.Router, dialer N.Dialer, ser return nil, E.New("v2ray-plugin: unknown mode: " + mode) } - return v2ray.NewClientTransport(context.Background(), dialer, serverAddr, transportOptions, tlsClient) + transport, err := v2ray.NewClientTransport(context.Background(), dialer, serverAddr, transportOptions, tlsClient) + if err != nil { + return nil, err + } + + if mux > 0 { + return &v2rayMuxWrapper{transport}, nil + } + + return transport, nil +} + +var _ Plugin = (*v2rayMuxWrapper)(nil) + +type v2rayMuxWrapper struct { + adapter.V2RayClientTransport +} + +func (w *v2rayMuxWrapper) DialContext(ctx context.Context) (net.Conn, error) { + conn, err := w.V2RayClientTransport.DialContext(ctx) + if err != nil { + return nil, err + } + return vmess.NewMuxConnWrapper(conn, vmess.MuxDestination), nil }