From 087f0d12401651d45d069145fb178db94c849f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Mon, 23 May 2022 11:55:48 +0800 Subject: [PATCH] Add shadowsocks-2022 inbound/outbound (#1061) --- common/session/context.go | 18 +- go.mod | 18 +- go.sum | 27 +- infra/conf/shadowsocks_2022.go | 46 ++++ infra/conf/xray.go | 40 +-- proxy/shadowsocks_2022/config.pb.go | 283 +++++++++++++++++++++ proxy/shadowsocks_2022/config.proto | 24 ++ proxy/shadowsocks_2022/errors.generated.go | 9 + proxy/shadowsocks_2022/inbound.go | 158 ++++++++++++ proxy/shadowsocks_2022/outbound.go | 166 ++++++++++++ proxy/shadowsocks_2022/shadowsocks_2022.go | 143 +++++++++++ 11 files changed, 897 insertions(+), 35 deletions(-) create mode 100644 infra/conf/shadowsocks_2022.go create mode 100644 proxy/shadowsocks_2022/config.pb.go create mode 100644 proxy/shadowsocks_2022/config.proto create mode 100644 proxy/shadowsocks_2022/errors.generated.go create mode 100644 proxy/shadowsocks_2022/inbound.go create mode 100644 proxy/shadowsocks_2022/outbound.go create mode 100644 proxy/shadowsocks_2022/shadowsocks_2022.go diff --git a/common/session/context.go b/common/session/context.go index 0961daf3..2959807e 100644 --- a/common/session/context.go +++ b/common/session/context.go @@ -1,6 +1,10 @@ package session -import "context" +import ( + "context" + + "github.com/xtls/xray-core/features/routing" +) type sessionKey int @@ -12,6 +16,7 @@ const ( muxPreferedSessionKey sockoptSessionKey trackedConnectionErrorKey + dispatcherKey ) // ContextWithID returns a new context with the given ID. @@ -115,3 +120,14 @@ func SubmitOutboundErrorToOriginator(ctx context.Context, err error) { func TrackedConnectionError(ctx context.Context, tracker TrackedRequestErrorFeedback) context.Context { return context.WithValue(ctx, trackedConnectionErrorKey, tracker) } + +func ContextWithDispatcher(ctx context.Context, dispatcher routing.Dispatcher) context.Context { + return context.WithValue(ctx, dispatcherKey, dispatcher) +} + +func DispatcherFromContext(ctx context.Context) routing.Dispatcher { + if dispatcher, ok := ctx.Value(dispatcherKey).(routing.Dispatcher); ok { + return dispatcher + } + return nil +} diff --git a/go.mod b/go.mod index 36a55682..fdfacd50 100644 --- a/go.mod +++ b/go.mod @@ -9,19 +9,23 @@ require ( github.com/google/go-cmp v0.5.8 github.com/gorilla/websocket v1.5.0 github.com/lucas-clemente/quic-go v0.27.0 + github.com/marten-seemann/qtls-go1-16 v0.1.5 + github.com/marten-seemann/qtls-go1-17 v0.1.1 + github.com/marten-seemann/qtls-go1-18 v0.1.1 github.com/miekg/dns v1.1.49 github.com/pelletier/go-toml v1.9.5 github.com/pires/go-proxyproto v0.6.2 github.com/refraction-networking/utls v1.1.0 + github.com/sagernet/sing v0.0.0-20220523024450-2216901ec429 github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb github.com/stretchr/testify v1.7.1 github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 - golang.org/x/net v0.0.0-20220421235706-1d1ef9303861 + golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 + golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a google.golang.org/grpc v1.46.2 google.golang.org/protobuf v1.28.0 h12.io/socks v1.0.3 @@ -34,19 +38,19 @@ require ( github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.5.3 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect - github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect - github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect + github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.10 // indirect + golang.org/x/tools v0.1.11-0.20220325154526-54af36eca237 // indirect golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect + golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d // indirect google.golang.org/genproto v0.0.0-20220422154200-b37d22cd5731 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + lukechampine.com/blake3 v1.1.7 // indirect ) diff --git a/go.sum b/go.sum index ddb61615..0d0adca3 100644 --- a/go.sum +++ b/go.sum @@ -111,6 +111,9 @@ github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= 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= @@ -171,6 +174,10 @@ github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sagernet/sing v0.0.0-20220522025344-b21aa294fca3 h1:z6cVzCta0vPVKo4lsiHMEDmw3cSFC5TGojXobbTS1DA= +github.com/sagernet/sing v0.0.0-20220522025344-b21aa294fca3/go.mod h1:7aFl1bLxdWLIhptc1DwhMWpRfT62rJYzKPYuYlzYs5w= +github.com/sagernet/sing v0.0.0-20220523024450-2216901ec429 h1:31vx64WHtycaqw0bGLl6ImJvmHsXqHoPzkuwKmTqbx8= +github.com/sagernet/sing v0.0.0-20220523024450-2216901ec429/go.mod h1:7aFl1bLxdWLIhptc1DwhMWpRfT62rJYzKPYuYlzYs5w= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -226,8 +233,8 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0= +golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -255,8 +262,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220421235706-1d1ef9303861 h1:yssD99+7tqHWO5Gwh81phT+67hg+KttniBr6UnEXOY8= -golang.org/x/net v0.0.0-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -292,8 +299,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -317,14 +324,16 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.11-0.20220325154526-54af36eca237 h1:mAhaIX1KEgotq+ju3XYdXUHvll7bzJDTgiDzIAKDdPc= +golang.org/x/tools v0.1.11-0.20220325154526-54af36eca237/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d h1:q4JksJ2n0fmbXC0Aj0eOs6E0AcPqnKglxWXWFqGD6x0= +golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -393,5 +402,7 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= +lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/infra/conf/shadowsocks_2022.go b/infra/conf/shadowsocks_2022.go new file mode 100644 index 00000000..594e2af9 --- /dev/null +++ b/infra/conf/shadowsocks_2022.go @@ -0,0 +1,46 @@ +package conf + +import ( + "github.com/golang/protobuf/proto" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/proxy/shadowsocks_2022" +) + +type Shadowsocks2022ServerConfig struct { + Cipher string `json:"method"` + Key string `json:"key"` + NetworkList *NetworkList `json:"network"` +} + +func (v *Shadowsocks2022ServerConfig) Build() (proto.Message, error) { + var network []net.Network + if v.NetworkList != nil { + network = v.NetworkList.Build() + } + return &shadowsocks_2022.ServerConfig{ + Method: v.Cipher, + Key: v.Key, + Network: network, + }, nil +} + +type Shadowsocks2022ClientConfig struct { + Address *Address `json:"address"` + Port uint16 `json:"port"` + Cipher string `json:"method"` + Key string `json:"key"` + ReducedIvHeadEntropy bool `json:"reducedIvHeadEntropy"` +} + +func (v *Shadowsocks2022ClientConfig) Build() (proto.Message, error) { + if v.Address == nil { + return nil, newError("shadowsocks 2022: missing server address") + } + return &shadowsocks_2022.ClientConfig{ + Address: v.Address.Build(), + Port: uint32(v.Port), + Method: v.Cipher, + Key: v.Key, + ReducedIvHeadEntropy: v.ReducedIvHeadEntropy, + }, nil +} diff --git a/infra/conf/xray.go b/infra/conf/xray.go index 76f00804..a47d59ba 100644 --- a/infra/conf/xray.go +++ b/infra/conf/xray.go @@ -18,28 +18,30 @@ import ( var ( inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{ - "dokodemo-door": func() interface{} { return new(DokodemoConfig) }, - "http": func() interface{} { return new(HTTPServerConfig) }, - "shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) }, - "socks": func() interface{} { return new(SocksServerConfig) }, - "vless": func() interface{} { return new(VLessInboundConfig) }, - "vmess": func() interface{} { return new(VMessInboundConfig) }, - "trojan": func() interface{} { return new(TrojanServerConfig) }, - "mtproto": func() interface{} { return new(MTProtoServerConfig) }, + "dokodemo-door": func() interface{} { return new(DokodemoConfig) }, + "http": func() interface{} { return new(HTTPServerConfig) }, + "shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) }, + "socks": func() interface{} { return new(SocksServerConfig) }, + "vless": func() interface{} { return new(VLessInboundConfig) }, + "vmess": func() interface{} { return new(VMessInboundConfig) }, + "trojan": func() interface{} { return new(TrojanServerConfig) }, + "mtproto": func() interface{} { return new(MTProtoServerConfig) }, + "shadowsocks-2022": func() interface{} { return new(Shadowsocks2022ServerConfig) }, }, "protocol", "settings") outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{ - "blackhole": func() interface{} { return new(BlackholeConfig) }, - "loopback": func() interface{} { return new(LoopbackConfig) }, - "freedom": func() interface{} { return new(FreedomConfig) }, - "http": func() interface{} { return new(HTTPClientConfig) }, - "shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) }, - "socks": func() interface{} { return new(SocksClientConfig) }, - "vless": func() interface{} { return new(VLessOutboundConfig) }, - "vmess": func() interface{} { return new(VMessOutboundConfig) }, - "trojan": func() interface{} { return new(TrojanClientConfig) }, - "mtproto": func() interface{} { return new(MTProtoClientConfig) }, - "dns": func() interface{} { return new(DNSOutboundConfig) }, + "blackhole": func() interface{} { return new(BlackholeConfig) }, + "loopback": func() interface{} { return new(LoopbackConfig) }, + "freedom": func() interface{} { return new(FreedomConfig) }, + "http": func() interface{} { return new(HTTPClientConfig) }, + "shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) }, + "socks": func() interface{} { return new(SocksClientConfig) }, + "vless": func() interface{} { return new(VLessOutboundConfig) }, + "vmess": func() interface{} { return new(VMessOutboundConfig) }, + "trojan": func() interface{} { return new(TrojanClientConfig) }, + "mtproto": func() interface{} { return new(MTProtoClientConfig) }, + "dns": func() interface{} { return new(DNSOutboundConfig) }, + "shadowsocks-2022": func() interface{} { return new(Shadowsocks2022ClientConfig) }, }, "protocol", "settings") ctllog = log.New(os.Stderr, "xctl> ", 0) diff --git a/proxy/shadowsocks_2022/config.pb.go b/proxy/shadowsocks_2022/config.pb.go new file mode 100644 index 00000000..a31c28e1 --- /dev/null +++ b/proxy/shadowsocks_2022/config.pb.go @@ -0,0 +1,283 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc v3.20.1 +// source: proxy/shadowsocks_2022/config.proto + +package shadowsocks_2022 + +import ( + net "github.com/xtls/xray-core/common/net" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ServerConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Network []net.Network `protobuf:"varint,3,rep,packed,name=network,proto3,enum=xray.common.net.Network" json:"network,omitempty"` +} + +func (x *ServerConfig) Reset() { + *x = ServerConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ServerConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerConfig) ProtoMessage() {} + +func (x *ServerConfig) ProtoReflect() protoreflect.Message { + mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead. +func (*ServerConfig) Descriptor() ([]byte, []int) { + return file_proxy_shadowsocks_2022_config_proto_rawDescGZIP(), []int{0} +} + +func (x *ServerConfig) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *ServerConfig) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *ServerConfig) GetNetwork() []net.Network { + if x != nil { + return x.Network + } + return nil +} + +type ClientConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` + Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` + Key string `protobuf:"bytes,4,opt,name=key,proto3" json:"key,omitempty"` + ReducedIvHeadEntropy bool `protobuf:"varint,5,opt,name=reduced_iv_head_entropy,json=reducedIvHeadEntropy,proto3" json:"reduced_iv_head_entropy,omitempty"` +} + +func (x *ClientConfig) Reset() { + *x = ClientConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClientConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClientConfig) ProtoMessage() {} + +func (x *ClientConfig) ProtoReflect() protoreflect.Message { + mi := &file_proxy_shadowsocks_2022_config_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead. +func (*ClientConfig) Descriptor() ([]byte, []int) { + return file_proxy_shadowsocks_2022_config_proto_rawDescGZIP(), []int{1} +} + +func (x *ClientConfig) GetAddress() *net.IPOrDomain { + if x != nil { + return x.Address + } + return nil +} + +func (x *ClientConfig) GetPort() uint32 { + if x != nil { + return x.Port + } + return 0 +} + +func (x *ClientConfig) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *ClientConfig) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *ClientConfig) GetReducedIvHeadEntropy() bool { + if x != nil { + return x.ReducedIvHeadEntropy + } + return false +} + +var File_proxy_shadowsocks_2022_config_proto protoreflect.FileDescriptor + +var file_proxy_shadowsocks_2022_config_proto_rawDesc = []byte{ + 0x0a, 0x23, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, + 0x63, 0x6b, 0x73, 0x5f, 0x32, 0x30, 0x32, 0x32, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x32, 0x30, + 0x32, 0x32, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6c, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x32, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x22, 0xba, 0x01, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x35, 0x0a, 0x17, 0x72, 0x65, + 0x64, 0x75, 0x63, 0x65, 0x64, 0x5f, 0x69, 0x76, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x65, 0x6e, + 0x74, 0x72, 0x6f, 0x70, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x72, 0x65, 0x64, + 0x75, 0x63, 0x65, 0x64, 0x49, 0x76, 0x48, 0x65, 0x61, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x6f, 0x70, + 0x79, 0x42, 0x72, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x5f, + 0x32, 0x30, 0x32, 0x32, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, + 0x63, 0x6b, 0x73, 0x5f, 0x32, 0x30, 0x32, 0x32, 0xaa, 0x02, 0x1a, 0x58, 0x72, 0x61, 0x79, 0x2e, + 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, + 0x73, 0x32, 0x30, 0x32, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proxy_shadowsocks_2022_config_proto_rawDescOnce sync.Once + file_proxy_shadowsocks_2022_config_proto_rawDescData = file_proxy_shadowsocks_2022_config_proto_rawDesc +) + +func file_proxy_shadowsocks_2022_config_proto_rawDescGZIP() []byte { + file_proxy_shadowsocks_2022_config_proto_rawDescOnce.Do(func() { + file_proxy_shadowsocks_2022_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_shadowsocks_2022_config_proto_rawDescData) + }) + return file_proxy_shadowsocks_2022_config_proto_rawDescData +} + +var file_proxy_shadowsocks_2022_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_proxy_shadowsocks_2022_config_proto_goTypes = []interface{}{ + (*ServerConfig)(nil), // 0: xray.proxy.shadowsocks_2022.ServerConfig + (*ClientConfig)(nil), // 1: xray.proxy.shadowsocks_2022.ClientConfig + (net.Network)(0), // 2: xray.common.net.Network + (*net.IPOrDomain)(nil), // 3: xray.common.net.IPOrDomain +} +var file_proxy_shadowsocks_2022_config_proto_depIdxs = []int32{ + 2, // 0: xray.proxy.shadowsocks_2022.ServerConfig.network:type_name -> xray.common.net.Network + 3, // 1: xray.proxy.shadowsocks_2022.ClientConfig.address:type_name -> xray.common.net.IPOrDomain + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_proxy_shadowsocks_2022_config_proto_init() } +func file_proxy_shadowsocks_2022_config_proto_init() { + if File_proxy_shadowsocks_2022_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proxy_shadowsocks_2022_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proxy_shadowsocks_2022_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proxy_shadowsocks_2022_config_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_proxy_shadowsocks_2022_config_proto_goTypes, + DependencyIndexes: file_proxy_shadowsocks_2022_config_proto_depIdxs, + MessageInfos: file_proxy_shadowsocks_2022_config_proto_msgTypes, + }.Build() + File_proxy_shadowsocks_2022_config_proto = out.File + file_proxy_shadowsocks_2022_config_proto_rawDesc = nil + file_proxy_shadowsocks_2022_config_proto_goTypes = nil + file_proxy_shadowsocks_2022_config_proto_depIdxs = nil +} diff --git a/proxy/shadowsocks_2022/config.proto b/proxy/shadowsocks_2022/config.proto new file mode 100644 index 00000000..4f3c620c --- /dev/null +++ b/proxy/shadowsocks_2022/config.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package xray.proxy.shadowsocks_2022; +option csharp_namespace = "Xray.Proxy.Shadowsocks2022"; +option go_package = "github.com/xtls/xray-core/proxy/shadowsocks_2022"; +option java_package = "com.xray.proxy.shadowsocks_2022"; +option java_multiple_files = true; + +import "common/net/network.proto"; +import "common/net/address.proto"; + +message ServerConfig { + string method = 1; + string key = 2; + repeated xray.common.net.Network network = 3; +} + +message ClientConfig { + xray.common.net.IPOrDomain address = 1; + uint32 port = 2; + string method = 3; + string key = 4; + bool reduced_iv_head_entropy = 5; +} diff --git a/proxy/shadowsocks_2022/errors.generated.go b/proxy/shadowsocks_2022/errors.generated.go new file mode 100644 index 00000000..90db3d5e --- /dev/null +++ b/proxy/shadowsocks_2022/errors.generated.go @@ -0,0 +1,9 @@ +package shadowsocks_2022 + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/shadowsocks_2022/inbound.go b/proxy/shadowsocks_2022/inbound.go new file mode 100644 index 00000000..1c25c3b7 --- /dev/null +++ b/proxy/shadowsocks_2022/inbound.go @@ -0,0 +1,158 @@ +package shadowsocks_2022 + +import ( + "context" + "encoding/base64" + + C "github.com/sagernet/sing/common" + B "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/random" + "github.com/sagernet/sing/protocol/shadowsocks" + "github.com/sagernet/sing/protocol/shadowsocks/shadowaead_2022" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/log" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/features/routing" + "github.com/xtls/xray-core/transport/internet/stat" +) + +func init() { + common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return NewServer(ctx, config.(*ServerConfig)) + })) +} + +type Inbound struct { + networks []net.Network + service shadowsocks.Service +} + +func NewServer(ctx context.Context, config *ServerConfig) (*Inbound, error) { + networks := config.Network + if len(networks) == 0 { + networks = []net.Network{ + net.Network_TCP, + net.Network_UDP, + } + } + inbound := &Inbound{ + networks: networks, + } + if !C.Contains(shadowaead_2022.List, config.Method) { + return nil, newError("unsupported method ", config.Method) + } + if config.Key == "" { + return nil, newError("missing key") + } + psk, err := base64.StdEncoding.DecodeString(config.Key) + if err != nil { + return nil, newError("parse config").Base(err) + } + service, err := shadowaead_2022.NewService(config.Method, psk, "", random.Default, 500, inbound) + if err != nil { + return nil, newError("create service").Base(err) + } + inbound.service = service + return inbound, nil +} + +func (i *Inbound) Network() []net.Network { + return i.networks +} + +func (i *Inbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error { + inbound := session.InboundFromContext(ctx) + if inbound == nil { + panic("no inbound metadata") + } + + var metadata M.Metadata + if inbound.Source.IsValid() { + metadata.Source = M.ParseSocksaddr(inbound.Source.NetAddr()) + } + + ctx = session.ContextWithDispatcher(ctx, dispatcher) + + if network == net.Network_TCP { + return i.service.NewConnection(ctx, connection, metadata) + } else { + reader := buf.NewReader(connection) + pc := &natPacketConn{connection} + for { + mb, err := reader.ReadMultiBuffer() + if err != nil { + return err + } + for _, buffer := range mb { + err = i.service.NewPacket(ctx, pc, B.As(buffer.Bytes()), metadata) + if err != nil { + return err + } + } + } + } +} + +func (i *Inbound) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { + ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ + From: metadata.Source, + To: metadata.Destination, + Status: log.AccessAccepted, + }) + newError("tunnelling request to tcp:", metadata.Destination).WriteToLog(session.ExportIDToError(ctx)) + dispatcher := session.DispatcherFromContext(ctx) + link, err := dispatcher.Dispatch(ctx, toDestination(metadata.Destination, net.Network_TCP)) + if err != nil { + return err + } + outConn := &pipeConnWrapper{ + &buf.BufferedReader{Reader: link.Reader}, + link.Writer, + conn, + } + return bufio.CopyConn(ctx, conn, outConn) +} + +func (i *Inbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error { + ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ + From: metadata.Source, + To: metadata.Destination, + Status: log.AccessAccepted, + }) + newError("tunnelling request to udp:", metadata.Destination).WriteToLog(session.ExportIDToError(ctx)) + dispatcher := session.DispatcherFromContext(ctx) + destination := toDestination(metadata.Destination, net.Network_UDP) + link, err := dispatcher.Dispatch(ctx, destination) + if err != nil { + return err + } + outConn := &packetConnWrapper{ + Reader: link.Reader, + Writer: link.Writer, + Dest: destination, + } + return bufio.CopyPacketConn(ctx, conn, outConn) +} + +func (i *Inbound) HandleError(err error) { + newError(err).AtWarning().WriteToLog() +} + +type natPacketConn struct { + net.Conn +} + +func (c *natPacketConn) ReadPacket(buffer *B.Buffer) (addr M.Socksaddr, err error) { + _, err = buffer.ReadFrom(c) + return +} + +func (c *natPacketConn) WritePacket(buffer *B.Buffer, addr M.Socksaddr) error { + _, err := buffer.WriteTo(c) + return err +} diff --git a/proxy/shadowsocks_2022/outbound.go b/proxy/shadowsocks_2022/outbound.go new file mode 100644 index 00000000..2c1c13e0 --- /dev/null +++ b/proxy/shadowsocks_2022/outbound.go @@ -0,0 +1,166 @@ +package shadowsocks_2022 + +import ( + "context" + "encoding/base64" + "io" + "runtime" + "strings" + "time" + + C "github.com/sagernet/sing/common" + B "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/random" + "github.com/sagernet/sing/protocol/shadowsocks" + "github.com/sagernet/sing/protocol/shadowsocks/shadowaead_2022" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/transport" + "github.com/xtls/xray-core/transport/internet" +) + +func init() { + common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return NewClient(ctx, config.(*ClientConfig)) + })) +} + +type Outbound struct { + ctx context.Context + server net.Destination + method shadowsocks.Method +} + +func NewClient(ctx context.Context, config *ClientConfig) (*Outbound, error) { + o := &Outbound{ + ctx: ctx, + server: net.Destination{ + Address: config.Address.AsAddress(), + Port: net.Port(config.Port), + Network: net.Network_TCP, + }, + } + if C.Contains(shadowaead_2022.List, config.Method) { + if config.Key == "" { + return nil, newError("missing psk") + } + var pskList [][]byte + for _, ks := range strings.Split(config.Key, ":") { + psk, err := base64.StdEncoding.DecodeString(ks) + if err != nil { + return nil, newError("decode key ", ks).Base(err) + } + pskList = append(pskList, psk) + } + var rng io.Reader = random.Default + if config.ReducedIvHeadEntropy { + rng = &shadowsocks.ReducedEntropyReader{ + Reader: rng, + } + } + method, err := shadowaead_2022.New(config.Method, pskList, "", rng) + if err != nil { + return nil, newError("create method").Base(err) + } + o.method = method + } else { + return nil, newError("unknown method ", config.Method) + } + return o, nil +} + +func (o *Outbound) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { + var inboundConn net.Conn + inbound := session.InboundFromContext(ctx) + if inbound != nil { + inboundConn = inbound.Conn + } + + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified") + } + /*if statConn, ok := inboundConn.(*internet.StatCounterConn); ok { + inboundConn = statConn.Connection + }*/ + destination := outbound.Target + network := destination.Network + + newError("tunneling request to ", destination, " via ", o.server.NetAddr()).WriteToLog(session.ExportIDToError(ctx)) + + serverDestination := o.server + serverDestination.Network = network + connection, err := dialer.Dial(ctx, serverDestination) + if err != nil { + return newError("failed to connect to server").Base(err) + } + + if network == net.Network_TCP { + serverConn := o.method.DialEarlyConn(connection, toSocksaddr(destination)) + var handshake bool + if timeoutReader, isTimeoutReader := link.Reader.(buf.TimeoutReader); isTimeoutReader { + mb, err := timeoutReader.ReadMultiBufferTimeout(time.Millisecond * 100) + if err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { + return newError("read payload").Base(err) + } + _payload := B.StackNew() + payload := C.Dup(_payload) + for { + payload.FullReset() + nb, n := buf.SplitBytes(mb, payload.FreeBytes()) + if n > 0 { + payload.Truncate(n) + _, err = serverConn.Write(payload.Bytes()) + if err != nil { + return newError("write payload").Base(err) + } + handshake = true + } + if nb.IsEmpty() { + break + } else { + mb = nb + } + } + runtime.KeepAlive(_payload) + } + if !handshake { + _, err = serverConn.Write(nil) + if err != nil { + return newError("client handshake").Base(err) + } + } + conn := &pipeConnWrapper{ + W: link.Writer, + Conn: inboundConn, + } + if ir, ok := link.Reader.(io.Reader); ok { + conn.R = ir + } else { + conn.R = &buf.BufferedReader{Reader: link.Reader} + } + + return bufio.CopyConn(ctx, conn, serverConn) + } else { + var packetConn N.PacketConn + if pc, isPacketConn := inboundConn.(N.PacketConn); isPacketConn { + packetConn = pc + } else if nc, isNetPacket := inboundConn.(net.PacketConn); isNetPacket { + packetConn = &bufio.PacketConnWrapper{PacketConn: nc} + } else { + packetConn = &packetConnWrapper{ + Reader: link.Reader, + Writer: link.Writer, + Conn: inboundConn, + Dest: destination, + } + } + + serverConn := o.method.DialPacketConn(connection) + return bufio.CopyPacketConn(ctx, packetConn, serverConn) + } +} diff --git a/proxy/shadowsocks_2022/shadowsocks_2022.go b/proxy/shadowsocks_2022/shadowsocks_2022.go new file mode 100644 index 00000000..b221312a --- /dev/null +++ b/proxy/shadowsocks_2022/shadowsocks_2022.go @@ -0,0 +1,143 @@ +package shadowsocks_2022 + +import ( + "io" + + B "github.com/sagernet/sing/common/buf" + M "github.com/sagernet/sing/common/metadata" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/net" +) + +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen + +func toDestination(socksaddr M.Socksaddr, network net.Network) net.Destination { + if socksaddr.Family().IsFqdn() { + return net.Destination{ + Network: network, + Address: net.DomainAddress(socksaddr.Fqdn), + Port: net.Port(socksaddr.Port), + } + } else { + return net.Destination{ + Network: network, + Address: net.IPAddress(socksaddr.Addr.AsSlice()), + Port: net.Port(socksaddr.Port), + } + } +} + +func toSocksaddr(destination net.Destination) M.Socksaddr { + var addr M.Socksaddr + switch destination.Address.Family() { + case net.AddressFamilyDomain: + addr.Fqdn = destination.Address.Domain() + default: + addr.Addr = M.AddrFromIP(destination.Address.IP()) + } + addr.Port = uint16(destination.Port) + return addr +} + +type pipeConnWrapper struct { + R io.Reader + W buf.Writer + net.Conn +} + +func (w *pipeConnWrapper) Close() error { + common.Interrupt(w.R) + common.Interrupt(w.W) + common.Close(w.Conn) + return nil +} + +func (w *pipeConnWrapper) Read(b []byte) (n int, err error) { + return w.R.Read(b) +} + +func (w *pipeConnWrapper) Write(p []byte) (n int, err error) { + n = len(p) + var mb buf.MultiBuffer + pLen := len(p) + for pLen > 0 { + buffer := buf.New() + if pLen > buf.Size { + _, err = buffer.Write(p[:buf.Size]) + p = p[buf.Size:] + } else { + buffer.Write(p) + } + pLen -= int(buffer.Len()) + mb = append(mb, buffer) + } + err = w.W.WriteMultiBuffer(mb) + if err != nil { + n = 0 + buf.ReleaseMulti(mb) + } + return +} + +type packetConnWrapper struct { + buf.Reader + buf.Writer + net.Conn + Dest net.Destination + cached buf.MultiBuffer +} + +func (w *packetConnWrapper) ReadPacket(buffer *B.Buffer) (M.Socksaddr, error) { + if w.cached != nil { + mb, bb := buf.SplitFirst(w.cached) + if bb == nil { + w.cached = nil + } else { + buffer.Write(bb.Bytes()) + w.cached = mb + var destination net.Destination + if bb.UDP != nil { + destination = *bb.UDP + } else { + destination = w.Dest + } + bb.Release() + return toSocksaddr(destination), nil + } + } + mb, err := w.ReadMultiBuffer() + if err != nil { + return M.Socksaddr{}, err + } + nb, bb := buf.SplitFirst(mb) + if bb == nil { + return M.Socksaddr{}, nil + } else { + buffer.Write(bb.Bytes()) + w.cached = nb + var destination net.Destination + if bb.UDP != nil { + destination = *bb.UDP + } else { + destination = w.Dest + } + bb.Release() + return toSocksaddr(destination), nil + } +} + +func (w *packetConnWrapper) WritePacket(buffer *B.Buffer, destination M.Socksaddr) error { + vBuf := buf.New() + vBuf.Write(buffer.Bytes()) + endpoint := toDestination(destination, net.Network_UDP) + vBuf.UDP = &endpoint + return w.Writer.WriteMultiBuffer(buf.MultiBuffer{vBuf}) +} + +func (w *packetConnWrapper) Close() error { + common.Interrupt(w.Reader) + common.Close(w.Conn) + buf.ReleaseMulti(w.cached) + return nil +}