Version anchors
| Component | Upstream | Anchor | Our modification |
|---|
| Xray-core | https://github.com/XTLS/Xray-core | tag v26.7.28 (commit 5ca6f4b7d4dc) | infra/conf/shadowsocks.go — Shadowsocks-2022 support removed |
| REALITY | https://github.com/XTLS/REALITY | commit 9234c772ba8f | tls.go — rate-limit dependency replaced by an inline token bucket (with the corresponding go.mod / go.sum removals) |
本产品包含以 MPL-2.0 授权的 Xray-core 与 REALITY 组件,我们修改了其中各一个源文件。未修改部分的源码可在上表的上游仓库、对应版本锚点处获取;我们的修改以下方 patch 形式完整提供。
This product includes Xray-core and REALITY under the MPL-2.0. One source file in each was modified by us. Unmodified sources are available from the upstream repositories at the versions listed above; our modifications are provided in full as the patches below.
Patch 1 of 2 — Xray-core (MPL-2.0)
File: infra/conf/shadowsocks.go · Baseline: v26.7.28 (5ca6f4b7d4dc)
Download xray-core-conf-no-ss2022.patch
diff --git a/infra/conf/shadowsocks.go b/infra/conf/shadowsocks.go
index 18451ab..49fa7a2 100644
--- a/infra/conf/shadowsocks.go
+++ b/infra/conf/shadowsocks.go
@@ -3,17 +3,22 @@ package conf
import (
"strings"
- "github.com/sagernet/sing-shadowsocks/shadowaead_2022"
- C "github.com/sagernet/sing/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/protocol"
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/proxy/shadowsocks"
- "github.com/xtls/xray-core/proxy/shadowsocks_2022"
"google.golang.org/protobuf/proto"
)
+// isShadowsocks2022Cipher reports whether the cipher names a Shadowsocks
+// 2022 suite. This build strips SS2022 (its implementation pulls the
+// GPL-3.0 sagernet/sing stack); the check keeps the error explicit
+// instead of falling through to the legacy-cipher parser.
+func isShadowsocks2022Cipher(c string) bool {
+ return strings.HasPrefix(strings.ToLower(c), "2022-blake3-")
+}
+
func cipherFromString(c string) shadowsocks.CipherType {
switch strings.ToLower(c) {
case "aes-128-gcm", "aead_aes_128_gcm":
@@ -55,8 +60,8 @@ func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
v.Users = v.Clients
}
- if C.Contains(shadowaead_2022.List, v.Cipher) {
- return buildShadowsocks2022(v)
+ if isShadowsocks2022Cipher(v.Cipher) {
+ return nil, errors.New("shadowsocks 2022 is not supported in this build")
}
config := new(shadowsocks.ServerConfig)
@@ -110,72 +115,6 @@ func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
return config, nil
}
-func buildShadowsocks2022(v *ShadowsocksServerConfig) (proto.Message, error) {
- if len(v.Users) == 0 {
- config := new(shadowsocks_2022.ServerConfig)
- config.Method = v.Cipher
- config.Key = v.Password
- config.Network = v.NetworkList.Build()
- config.Email = v.Email
- return config, nil
- }
-
- if v.Cipher == "" {
- return nil, errors.New("shadowsocks 2022 (multi-user): missing server method")
- }
- if !strings.Contains(v.Cipher, "aes") {
- return nil, errors.New("shadowsocks 2022 (multi-user): only blake3-aes-*-gcm methods are supported")
- }
-
- if v.Users[0].Address == nil {
- config := new(shadowsocks_2022.MultiUserServerConfig)
- config.Method = v.Cipher
- config.Key = v.Password
- config.Network = v.NetworkList.Build()
-
- config.Users = make([]*protocol.User, len(v.Users))
- processUser := func(idx int) error {
- user := v.Users[idx]
- if user.Cipher != "" {
- return errors.New("shadowsocks 2022 (multi-user): users must have empty method")
- }
- account := &shadowsocks_2022.Account{
- Key: user.Password,
- }
- config.Users[idx] = &protocol.User{
- Email: user.Email,
- Level: uint32(user.Level),
- Account: serial.ToTypedMessage(account),
- }
- return nil
- }
- if err := task.ParallelForN(len(v.Users), processUser); err != nil {
- return nil, err
- }
- return config, nil
- }
-
- config := new(shadowsocks_2022.RelayServerConfig)
- config.Method = v.Cipher
- config.Key = v.Password
- config.Network = v.NetworkList.Build()
- for _, user := range v.Users {
- if user.Cipher != "" {
- return nil, errors.New("shadowsocks 2022 (relay): users must have empty method")
- }
- if user.Address == nil {
- return nil, errors.New("shadowsocks 2022 (relay): all users must have relay address")
- }
- config.Destinations = append(config.Destinations, &shadowsocks_2022.RelayDestination{
- Key: user.Password,
- Email: user.Email,
- Address: user.Address.Build(),
- Port: uint32(user.Port),
- })
- }
- return config, nil
-}
-
type ShadowsocksServerTarget struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
@@ -216,30 +155,15 @@ func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
if len(v.Servers) == 1 {
server := v.Servers[0]
- if C.Contains(shadowaead_2022.List, server.Cipher) {
- if server.Address == nil {
- return nil, errors.New("Shadowsocks server address is not set.")
- }
- if server.Port == 0 {
- return nil, errors.New("Invalid Shadowsocks port.")
- }
- if server.Password == "" {
- return nil, errors.New("Shadowsocks password is not specified.")
- }
-
- config := new(shadowsocks_2022.ClientConfig)
- config.Address = server.Address.Build()
- config.Port = uint32(server.Port)
- config.Method = server.Cipher
- config.Key = server.Password
- return config, nil
+ if isShadowsocks2022Cipher(server.Cipher) {
+ return nil, errors.New("shadowsocks 2022 is not supported in this build")
}
}
config := new(shadowsocks.ClientConfig)
for _, server := range v.Servers {
- if C.Contains(shadowaead_2022.List, server.Cipher) {
- return nil, errors.New("Shadowsocks 2022 accept no multi servers")
+ if isShadowsocks2022Cipher(server.Cipher) {
+ return nil, errors.New("shadowsocks 2022 is not supported in this build")
}
if server.Address == nil {
return nil, errors.New("Shadowsocks server address is not set.")
Patch 2 of 2 — REALITY (MPL-2.0)
Files: tls.go (with go.mod / go.sum) · Baseline: commit 9234c772ba8f
Download reality-tls-token-bucket.patch
diff --git a/go.mod b/go.mod
index 22f8f90..c906155 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,6 @@ go 1.24.0
require (
github.com/cloudflare/circl v1.6.3
- github.com/juju/ratelimit v1.0.2
github.com/pires/go-proxyproto v0.11.0
github.com/refraction-networking/utls v1.8.2
golang.org/x/crypto v0.48.0
@@ -14,5 +13,4 @@ require (
require (
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/klauspost/compress v1.17.4 // indirect
- gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)
diff --git a/go.sum b/go.sum
index 72fddb4..d4f64e7 100644
--- a/go.sum
+++ b/go.sum
@@ -2,15 +2,8 @@ github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sx
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
-github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
-github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
-github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-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/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4=
github.com/pires/go-proxyproto v0.11.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo=
@@ -19,5 +12,3 @@ golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/tls.go b/tls.go
index 4c8ef86..e85afd7 100644
--- a/tls.go
+++ b/tls.go
@@ -50,7 +50,6 @@ import (
"sync"
"time"
- "github.com/juju/ratelimit"
"github.com/pires/go-proxyproto"
"golang.org/x/crypto/curve25519"
"golang.org/x/crypto/hkdf"
@@ -101,10 +100,52 @@ func (c *MirrorConn) SetWriteDeadline(t time.Time) error {
return nil
}
+// tokenBucket is a license-clean replacement for the LGPL-3.0
+// github.com/juju/ratelimit dependency. It implements exactly the
+// NewBucketWithRate/Wait(count) semantics the fallback shaper below uses:
+// tokens refill continuously at rate per second up to capacity, Wait takes
+// count tokens immediately (going into debt if needed) and sleeps until the
+// debt would be repaid.
+type tokenBucket struct {
+ mu sync.Mutex
+ rate float64
+ capacity int64
+ tokens float64
+ last time.Time
+}
+
+func newTokenBucketWithRate(rate float64, capacity int64) *tokenBucket {
+ return &tokenBucket{
+ rate: rate,
+ capacity: capacity,
+ tokens: float64(capacity),
+ last: time.Now(),
+ }
+}
+
+func (b *tokenBucket) Wait(count int64) {
+ b.mu.Lock()
+ now := time.Now()
+ b.tokens += now.Sub(b.last).Seconds() * b.rate
+ if b.tokens > float64(b.capacity) {
+ b.tokens = float64(b.capacity)
+ }
+ b.last = now
+ b.tokens -= float64(count)
+ var wait time.Duration
+ if b.tokens < 0 && b.rate > 0 {
+ wait = time.Duration(-b.tokens / b.rate * float64(time.Second))
+ }
+ b.mu.Unlock()
+ if wait > 0 {
+ time.Sleep(wait)
+ }
+}
+
type RatelimitedConn struct {
net.Conn
After int64
- Bucket *ratelimit.Bucket
+ Bucket *tokenBucket
}
func (c *RatelimitedConn) Read(b []byte) (int, error) {
@@ -132,7 +173,7 @@ func NewRatelimitedConn(conn net.Conn, limit *LimitFallback) net.Conn {
return &RatelimitedConn{
Conn: conn,
After: int64(limit.AfterBytes),
- Bucket: ratelimit.NewBucketWithRate(float64(limit.BytesPerSec), int64(burstBytesPerSec)),
+ Bucket: newTokenBucketWithRate(float64(limit.BytesPerSec), int64(burstBytesPerSec)),
}
}
Self-verification offered by the delivering side
git clone --depth 1 --branch v26.7.28 https://github.com/XTLS/Xray-core.git xraycore-src
git clone https://github.com/XTLS/REALITY.git reality-src && git -C reality-src checkout 9234c772ba8f
git -C xraycore-src apply xray-core-conf-no-ss2022.patch
git -C reality-src apply reality-tls-token-bucket.patch
Both applying cleanly means the material is complete.