diff --git a/go.mod b/go.mod index 0771a4ef..358c6bc5 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-20220916073459-0032242c9617 - github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 + github.com/sagernet/sing-vmess v0.0.0-20220917033734-9b634758039d 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 b22273ee..c18c0d72 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-20220916073459-0032242c9617 h1:fNTNmylhB/UjoBLusmrFu2B1fat4OCkDkQXTgrE7ZsE= github.com/sagernet/sing-tun v0.0.0-20220916073459-0032242c9617/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM= -github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y= -github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0= +github.com/sagernet/sing-vmess v0.0.0-20220917033734-9b634758039d h1:/GNWxSrQj4chFYk2jahIXNPdvUa9DW8IdgyqYFbq9oQ= +github.com/sagernet/sing-vmess v0.0.0-20220917033734-9b634758039d/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0= 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/option/vmess.go b/option/vmess.go index c1e25db2..a2ba2103 100644 --- a/option/vmess.go +++ b/option/vmess.go @@ -23,7 +23,7 @@ type VMessOutboundOptions struct { AuthenticatedLength bool `json:"authenticated_length,omitempty"` Network NetworkList `json:"network,omitempty"` TLS *OutboundTLSOptions `json:"tls,omitempty"` - PacketAddr bool `json:"packet_addr,omitempty"` + PacketEncoding string `json:"packet_encoding,omitempty"` Multiplex *MultiplexOptions `json:"multiplex,omitempty"` Transport *V2RayTransportOptions `json:"transport,omitempty"` } diff --git a/outbound/vless.go b/outbound/vless.go index 389df1fe..70f6a23b 100644 --- a/outbound/vless.go +++ b/outbound/vless.go @@ -12,6 +12,7 @@ import ( "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/transport/v2ray" "github.com/sagernet/sing-box/transport/vless" + "github.com/sagernet/sing-dns" "github.com/sagernet/sing-vmess/packetaddr" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" @@ -131,7 +132,7 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net. if h.xudp { return h.client.DialXUDPPacketConn(conn, destination), nil } else if h.packetAddr { - return packetaddr.NewConn(h.client.DialPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil + return dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(h.client.DialPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination)), nil } else { return h.client.DialPacketConn(conn, destination), nil } diff --git a/outbound/vmess.go b/outbound/vmess.go index f2f23c5a..6efa4529 100644 --- a/outbound/vmess.go +++ b/outbound/vmess.go @@ -12,6 +12,7 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/transport/v2ray" + "github.com/sagernet/sing-dns" "github.com/sagernet/sing-vmess" "github.com/sagernet/sing-vmess/packetaddr" "github.com/sagernet/sing/common" @@ -31,6 +32,7 @@ type VMess struct { tlsConfig tls.Config transport adapter.V2RayClientTransport packetAddr bool + xudp bool } func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (*VMess, error) { @@ -62,8 +64,14 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg if err != nil { return nil, err } - if outbound.multiplexDialer == nil && options.PacketAddr { + switch options.PacketEncoding { + case "": + case "packetaddr": outbound.packetAddr = true + case "xudp": + outbound.xudp = true + default: + return nil, E.New("unknown packet encoding: ", options.PacketEncoding) } var clientOptions []vmess.ClientOption if options.GlobalPadding { @@ -176,7 +184,9 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) return nil, err } if h.packetAddr { - return packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil + return dialer.NewResolvePacketConn(ctx, h.router, dns.DomainStrategyAsIS, packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination)), nil + } else if h.xudp { + return h.client.DialEarlyXUDPPacketConn(conn, destination), nil } else { return h.client.DialEarlyPacketConn(conn, destination), nil } diff --git a/test/config/vmess-mux-client.json b/test/config/vmess-mux-client.json new file mode 100644 index 00000000..9cb6654b --- /dev/null +++ b/test/config/vmess-mux-client.json @@ -0,0 +1,41 @@ +{ + "log": { + "loglevel": "debug" + }, + "inbounds": [ + { + "listen": "127.0.0.1", + "port": "1080", + "protocol": "socks", + "settings": { + "auth": "noauth", + "udp": true, + "ip": "127.0.0.1" + } + } + ], + "outbounds": [ + { + "protocol": "vmess", + "settings": { + "vnext": [ + { + "address": "127.0.0.1", + "port": 1234, + "users": [ + { + "id": "", + "alterId": 0, + "security": "none", + "experiments": "" + } + ] + } + ] + }, + "mux": { + "enabled": true + } + } + ] +} \ No newline at end of file diff --git a/test/go.mod b/test/go.mod index cee8defa..a80b9c69 100644 --- a/test/go.mod +++ b/test/go.mod @@ -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 v4.3.0+incompatible - github.com/sagernet/sing v0.0.0-20220914045234-93cc53b60cee + github.com/sagernet/sing v0.0.0-20220916071326-834794b006ea github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 github.com/spyzhov/ajson v0.7.1 github.com/stretchr/testify v1.8.0 @@ -21,6 +21,7 @@ require ( require ( berty.tech/go-libtor v1.0.385 // indirect + github.com/Dreamacro/clash v1.11.8 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.4 // indirect @@ -65,13 +66,12 @@ require ( github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb // indirect - github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0 // indirect - github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8 // indirect - github.com/sagernet/sing-tun v0.0.0-20220914100102-057dd738a7f7 // indirect - github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 // indirect + github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b // indirect + github.com/sagernet/sing-tun v0.0.0-20220916073459-0032242c9617 // indirect + github.com/sagernet/sing-vmess v0.0.0-20220917033734-9b634758039d // indirect github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 // indirect github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e // indirect - github.com/sirupsen/logrus v1.8.1 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.10.0 // indirect @@ -86,7 +86,7 @@ require ( golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect - golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 // indirect + golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b // indirect google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.1 // indirect diff --git a/test/go.sum b/test/go.sum index 8aaa3d54..81d234b3 100644 --- a/test/go.sum +++ b/test/go.sum @@ -5,6 +5,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Dreamacro/clash v1.11.8 h1:t/sy3/tiihRlvV3SsliYFjj8rKpbLw5IJ2PymiHcwS8= +github.com/Dreamacro/clash v1.11.8/go.mod h1:LsWCcJFoKuL1C5F2c0m/1690wihTHYSU3J+im09yTwQ= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -105,8 +107,8 @@ github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= @@ -161,27 +163,25 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6E github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk= github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4= -github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0 h1:lQ4RFWY/wBYmXl/zJJCwQbhiEIbgEqC7j+nhZYkgwmU= -github.com/sagernet/shadowsocksr v0.0.0-20220912092645-c9ab93f81bb0/go.mod h1:xSHLCsdgy5QIozXFEv6uDgMWzyrRdFFjr3TgL+juu6g= github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.0.0-20220914045234-93cc53b60cee h1:+3w7+QWnhWi3Qz7+Xcais8zViHRUPIkmxq3eYZm/zvk= -github.com/sagernet/sing v0.0.0-20220914045234-93cc53b60cee/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY= -github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8 h1:Iyfl+Rm5jcDvXuy/jpOBI3eu35ujci50tkqYHHwwg+8= -github.com/sagernet/sing-dns v0.0.0-20220913115644-aebff1dfbba8/go.mod h1:bPVnJ5gJ0WmUfN1bJP9Cis0ab8SSByx6JVzyLJjDMwA= +github.com/sagernet/sing v0.0.0-20220916071326-834794b006ea h1:ZAWvZdeByPBBz3Vs+w3Erbh+DDo7D4biokoPhXl0nNU= +github.com/sagernet/sing v0.0.0-20220916071326-834794b006ea/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY= +github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b h1:cXCMNJ9heZ+c6l+qUcku60x9KyXo4SOAaJfg/6spOmU= +github.com/sagernet/sing-dns v0.0.0-20220915084601-812e0864b45b/go.mod h1:SrvWLfOSlnFmH32CWXicfilAGgIXR0VjrH6yRbuXYww= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= -github.com/sagernet/sing-tun v0.0.0-20220914100102-057dd738a7f7 h1:zdvFDYMz8s0e9UmOxMk0wNGOKh64KfeWpx8UAbJJI60= -github.com/sagernet/sing-tun v0.0.0-20220914100102-057dd738a7f7/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM= -github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12 h1:4HYGbTDDemgBVTmaspXbkgjJlXc3hYVjNxSddJndq8Y= -github.com/sagernet/sing-vmess v0.0.0-20220913015714-c4ab86d40e12/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0= +github.com/sagernet/sing-tun v0.0.0-20220916073459-0032242c9617 h1:fNTNmylhB/UjoBLusmrFu2B1fat4OCkDkQXTgrE7ZsE= +github.com/sagernet/sing-tun v0.0.0-20220916073459-0032242c9617/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM= +github.com/sagernet/sing-vmess v0.0.0-20220917033734-9b634758039d h1:/GNWxSrQj4chFYk2jahIXNPdvUa9DW8IdgyqYFbq9oQ= +github.com/sagernet/sing-vmess v0.0.0-20220917033734-9b634758039d/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0= 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= github.com/sagernet/websocket v0.0.0-20220913015213-615516348b4e/go.mod h1:45TUl8+gH4SIKr4ykREbxKWTxkDlSzFENzctB1dVRRY= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spyzhov/ajson v0.7.1 h1:1MDIlPc6x0zjNtpa7tDzRAyFAvRX+X8ZsvtYz5lZg6A= github.com/spyzhov/ajson v0.7.1/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA= @@ -218,7 +218,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= @@ -266,8 +265,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -295,10 +294,10 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220913120320-3275c407cedc h1:dpclq5m2YrqPGStKmtw7IcNbKLfbIqKXvNxDJKdIKYc= golang.org/x/sys v0.0.0-20220913120320-3275c407cedc/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -334,8 +333,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= -golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 h1:5ZkdpbduT/g+9OtbSDvbF3KvfQG45CtH/ppO8FUmvCQ= -golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg= +golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b h1:qgrKnOfe1zyURRNdmDlGbN32i38Zjmw0B1+TMdHcOvg= +golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b/go.mod h1:6y4CqPAy54NwiN4nC8K+R1eMpQDB1P2d25qmunh2RSA= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/test/mux_cool_test.go b/test/mux_cool_test.go new file mode 100644 index 00000000..2c20a105 --- /dev/null +++ b/test/mux_cool_test.go @@ -0,0 +1,109 @@ +package main + +import ( + "net/netip" + "os" + "testing" + + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/option" + + "github.com/spyzhov/ajson" + "github.com/stretchr/testify/require" +) + +func TestMuxCoolServer(t *testing.T) { + userId := newUUID() + content, err := os.ReadFile("config/vmess-mux-client.json") + require.NoError(t, err) + config, err := ajson.Unmarshal(content) + require.NoError(t, err) + + config.MustKey("inbounds").MustIndex(0).MustKey("port").SetNumeric(float64(clientPort)) + outbound := config.MustKey("outbounds").MustIndex(0).MustKey("settings").MustKey("vnext").MustIndex(0) + outbound.MustKey("port").SetNumeric(float64(serverPort)) + user := outbound.MustKey("users").MustIndex(0) + user.MustKey("id").SetString(userId.String()) + + content, err = ajson.Marshal(config) + require.NoError(t, err) + + startDockerContainer(t, DockerOptions{ + Image: ImageV2RayCore, + Ports: []uint16{serverPort, testPort}, + EntryPoint: "v2ray", + Stdin: content, + }) + + startInstance(t, option.Options{ + Inbounds: []option.Inbound{ + { + Type: C.TypeVMess, + VMessOptions: option.VMessInboundOptions{ + ListenOptions: option.ListenOptions{ + Listen: option.ListenAddress(netip.IPv4Unspecified()), + ListenPort: serverPort, + }, + Users: []option.VMessUser{ + { + Name: "sekai", + UUID: userId.String(), + }, + }, + }, + }, + }, + }) + + testSuit(t, clientPort, testPort) +} + +func TestMuxCoolClient(t *testing.T) { + user := newUUID() + content, err := os.ReadFile("config/vmess-server.json") + require.NoError(t, err) + config, err := ajson.Unmarshal(content) + require.NoError(t, err) + + inbound := config.MustKey("inbounds").MustIndex(0) + inbound.MustKey("port").SetNumeric(float64(serverPort)) + inbound.MustKey("settings").MustKey("clients").MustIndex(0).MustKey("id").SetString(user.String()) + + content, err = ajson.Marshal(config) + require.NoError(t, err) + + startDockerContainer(t, DockerOptions{ + Image: ImageXRayCore, + Ports: []uint16{serverPort, testPort}, + EntryPoint: "xray", + Stdin: content, + }) + + 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.TypeVMess, + VMessOptions: option.VMessOutboundOptions{ + ServerOptions: option.ServerOptions{ + Server: "127.0.0.1", + ServerPort: serverPort, + }, + UUID: user.String(), + PacketEncoding: "xudp", + }, + }, + }, + }) + testSuit(t, clientPort, testPort) +} diff --git a/test/vmess_test.go b/test/vmess_test.go index 416a8ecd..e4cc2097 100644 --- a/test/vmess_test.go +++ b/test/vmess_test.go @@ -306,7 +306,7 @@ func testVMessSelf(t *testing.T, security string, alterId int, globalPadding boo AlterId: alterId, GlobalPadding: globalPadding, AuthenticatedLength: authenticatedLength, - PacketAddr: packetAddr, + PacketEncoding: "packetaddr", }, }, }, diff --git a/transport/vless/client.go b/transport/vless/client.go index 825fd6e7..a03c0a46 100644 --- a/transport/vless/client.go +++ b/transport/vless/client.go @@ -5,6 +5,7 @@ import ( "io" "net" + "github.com/sagernet/sing-vmess" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" M "github.com/sagernet/sing/common/metadata" @@ -25,20 +26,21 @@ func NewClient(userId string) (*Client, error) { } func (c *Client) DialEarlyConn(conn net.Conn, destination M.Socksaddr) *Conn { - return &Conn{Conn: conn, key: c.key, destination: destination} + return &Conn{Conn: conn, key: c.key, command: vmess.CommandTCP, destination: destination} } func (c *Client) DialPacketConn(conn net.Conn, destination M.Socksaddr) *PacketConn { return &PacketConn{Conn: conn, key: c.key, destination: destination} } -func (c *Client) DialXUDPPacketConn(conn net.Conn, destination M.Socksaddr) *XUDPConn { - return &XUDPConn{Conn: conn, key: c.key, destination: destination} +func (c *Client) DialXUDPPacketConn(conn net.Conn, destination M.Socksaddr) vmess.PacketConn { + return vmess.NewXUDPConn(&Conn{Conn: conn, key: c.key, command: vmess.CommandMux, destination: destination}, destination) } type Conn struct { net.Conn key []byte + command byte destination M.Socksaddr requestWritten bool responseRead bool @@ -57,7 +59,7 @@ func (c *Conn) Read(b []byte) (n int, err error) { func (c *Conn) Write(b []byte) (n int, err error) { if !c.requestWritten { - err = WriteRequest(c.Conn, Request{c.key, CommandTCP, c.destination}, b) + err = WriteRequest(c.Conn, Request{c.key, c.command, c.destination}, b) if err == nil { n = len(b) } @@ -100,7 +102,7 @@ func (c *PacketConn) Read(b []byte) (n int, err error) { func (c *PacketConn) Write(b []byte) (n int, err error) { if !c.requestWritten { - err = WritePacketRequest(c.Conn, Request{c.key, CommandUDP, c.destination}, b) + err = WritePacketRequest(c.Conn, Request{c.key, vmess.CommandUDP, c.destination}, b) if err == nil { n = len(b) } @@ -119,7 +121,7 @@ func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) er dataLen := buffer.Len() binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(dataLen)) if !c.requestWritten { - err := WritePacketRequest(c.Conn, Request{c.key, CommandUDP, c.destination}, buffer.Bytes()) + err := WritePacketRequest(c.Conn, Request{c.key, vmess.CommandUDP, c.destination}, buffer.Bytes()) c.requestWritten = true return err } diff --git a/transport/vless/protocol.go b/transport/vless/protocol.go index 357b672b..7913e989 100644 --- a/transport/vless/protocol.go +++ b/transport/vless/protocol.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "io" + "github.com/sagernet/sing-vmess" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" @@ -11,20 +12,7 @@ import ( "github.com/sagernet/sing/common/rw" ) -const ( - Version = 0 - CommandTCP = 1 - CommandUDP = 2 - CommandMux = 3 - NetworkUDP = 2 -) - -var AddressSerializer = M.NewSerializer( - M.AddressFamilyByte(0x01, M.AddressFamilyIPv4), - M.AddressFamilyByte(0x02, M.AddressFamilyFqdn), - M.AddressFamilyByte(0x03, M.AddressFamilyIPv6), - M.PortThenAddress(), -) +const Version = 0 type Request struct { UUID []byte @@ -38,7 +26,9 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error { requestLen += 16 // uuid requestLen += 1 // protobuf length requestLen += 1 // command - requestLen += AddressSerializer.AddrPortLen(request.Destination) + if request.Command != vmess.CommandMux { + requestLen += vmess.AddressSerializer.AddrPortLen(request.Destination) + } requestLen += len(payload) _buffer := buf.StackNewSize(requestLen) defer common.KeepAlive(_buffer) @@ -48,10 +38,14 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error { buffer.WriteByte(Version), common.Error(buffer.Write(request.UUID)), buffer.WriteByte(0), - buffer.WriteByte(CommandTCP), - AddressSerializer.WriteAddrPort(buffer, request.Destination), - common.Error(buffer.Write(payload)), + buffer.WriteByte(request.Command), ) + + if request.Command != vmess.CommandMux { + common.Must(vmess.AddressSerializer.WriteAddrPort(buffer, request.Destination)) + } + + common.Must1(buffer.Write(payload)) return common.Error(writer.Write(buffer.Bytes())) } @@ -61,7 +55,7 @@ func WritePacketRequest(writer io.Writer, request Request, payload []byte) error requestLen += 16 // uuid requestLen += 1 // protobuf length requestLen += 1 // command - requestLen += AddressSerializer.AddrPortLen(request.Destination) + requestLen += vmess.AddressSerializer.AddrPortLen(request.Destination) if len(payload) > 0 { requestLen += 2 requestLen += len(payload) @@ -74,8 +68,8 @@ func WritePacketRequest(writer io.Writer, request Request, payload []byte) error buffer.WriteByte(Version), common.Error(buffer.Write(request.UUID)), buffer.WriteByte(0), - buffer.WriteByte(CommandUDP), - AddressSerializer.WriteAddrPort(buffer, request.Destination), + buffer.WriteByte(vmess.CommandUDP), + vmess.AddressSerializer.WriteAddrPort(buffer, request.Destination), binary.Write(buffer, binary.BigEndian, uint16(len(payload))), common.Error(buffer.Write(payload)), ) diff --git a/transport/vless/xudp.go b/transport/vless/xudp.go deleted file mode 100644 index 3ded8319..00000000 --- a/transport/vless/xudp.go +++ /dev/null @@ -1,174 +0,0 @@ -package vless - -import ( - "encoding/binary" - "io" - "net" - "os" - - "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/buf" - E "github.com/sagernet/sing/common/exceptions" - M "github.com/sagernet/sing/common/metadata" -) - -type XUDPConn struct { - net.Conn - key []byte - destination M.Socksaddr - requestWritten bool - responseRead bool -} - -func (c *XUDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - return 0, nil, os.ErrInvalid -} - -func (c *XUDPConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) { - start := buffer.Start() - if !c.responseRead { - err = ReadResponse(c.Conn) - if err != nil { - return - } - c.responseRead = true - buffer.FullReset() - } - _, err = buffer.ReadFullFrom(c.Conn, 6) - if err != nil { - return - } - var length uint16 - err = binary.Read(buffer, binary.BigEndian, &length) - if err != nil { - return - } - header, err := buffer.ReadBytes(4) - if err != nil { - return - } - switch header[2] { - case 1: - // frame new - return M.Socksaddr{}, E.New("unexpected frame new") - case 2: - // frame keep - if length != 4 { - _, err = buffer.ReadFullFrom(c.Conn, int(length)-2) - if err != nil { - return - } - buffer.Advance(1) - destination, err = AddressSerializer.ReadAddrPort(buffer) - if err != nil { - return - } - } else { - _, err = buffer.ReadFullFrom(c.Conn, 2) - if err != nil { - return - } - destination = c.destination - } - case 3: - // frame end - return M.Socksaddr{}, io.EOF - case 4: - // frame keep alive - default: - return M.Socksaddr{}, E.New("unexpected frame: ", buffer.Byte(2)) - } - // option error - if header[3]&2 == 2 { - return M.Socksaddr{}, E.Cause(net.ErrClosed, "remote closed") - } - // option data - if header[3]&1 != 1 { - buffer.Resize(start, 0) - return c.ReadPacket(buffer) - } else { - err = binary.Read(buffer, binary.BigEndian, &length) - if err != nil { - return - } - buffer.Resize(start, 0) - _, err = buffer.ReadFullFrom(c.Conn, int(length)) - return - } -} - -func (c *XUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - destination := M.SocksaddrFromNet(addr) - headerLen := c.frontHeadroom(AddressSerializer.AddrPortLen(destination)) - buffer := buf.NewSize(headerLen + len(p)) - buffer.Advance(headerLen) - common.Must1(buffer.Write(p)) - err = c.WritePacket(buffer, destination) - if err == nil { - n = len(p) - } - return -} - -func (c *XUDPConn) frontHeadroom(addrLen int) int { - if !c.requestWritten { - var headerLen int - headerLen += 1 // version - headerLen += 16 // uuid - headerLen += 1 // protobuf length - headerLen += 1 // command - headerLen += 2 // frame len - headerLen += 5 // frame header - headerLen += addrLen - headerLen += 2 // payload len - return headerLen - } else { - return 7 + addrLen + 2 - } -} - -func (c *XUDPConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { - defer buffer.Release() - dataLen := buffer.Len() - addrLen := M.SocksaddrSerializer.AddrPortLen(destination) - if !c.requestWritten { - header := buf.With(buffer.ExtendHeader(c.frontHeadroom(addrLen))) - common.Must( - header.WriteByte(Version), - common.Error(header.Write(c.key)), - header.WriteByte(0), - header.WriteByte(CommandMux), - binary.Write(header, binary.BigEndian, uint16(5+addrLen)), - header.WriteByte(0), - header.WriteByte(0), - header.WriteByte(1), // frame type new - header.WriteByte(1), // option data - header.WriteByte(NetworkUDP), - AddressSerializer.WriteAddrPort(header, destination), - binary.Write(header, binary.BigEndian, uint16(dataLen)), - ) - c.requestWritten = true - } else { - header := buffer.ExtendHeader(c.frontHeadroom(addrLen)) - binary.BigEndian.PutUint16(header, uint16(5+addrLen)) - header[2] = 0 - header[3] = 0 - header[4] = 2 // frame keep - header[5] = 1 // option data - header[6] = NetworkUDP - err := AddressSerializer.WriteAddrPort(buf.With(header[7:]), destination) - if err != nil { - return err - } - binary.BigEndian.PutUint16(header[7+addrLen:], uint16(dataLen)) - } - return common.Error(c.Conn.Write(buffer.Bytes())) -} - -func (c *XUDPConn) FrontHeadroom() int { - return c.frontHeadroom(M.MaxSocksaddrLength) -} - -func (c *XUDPConn) Upstream() any { - return c.Conn -}