mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 11:15:03 +03:00
fix(shadowsocks): keep a 2022 inbound with no enabled client locked
When every client of a Shadowsocks-2022 inbound was disabled the panel handed the core an empty client list, from which xray-core builds a single-user server keyed by the inbound's server key alone. That key is the first half of every link the panel ever issued, so a client cut off for quota, expiry or by the operator connected again with it. An inbound left without clients now gets a placeholder user whose key is random and held only in memory, in the config and in the running core alike. The core stays multi-user, and disabling the last real client takes the same remove-and-restart path as any other client, so its live sessions end as well.
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package xray
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
"github.com/xtls/xray-core/proxy/shadowsocks"
|
||||
"github.com/xtls/xray-core/proxy/shadowsocks_2022"
|
||||
"google.golang.org/protobuf/proto"
|
||||
@@ -147,3 +149,45 @@ func TestBuildUserAccountShadowsocksMissingPassword(t *testing.T) {
|
||||
t.Fatal("expected an error for a shadowsocks user without a password")
|
||||
}
|
||||
}
|
||||
|
||||
// With no client the core builds a single-user Shadowsocks-2022 server keyed by the server
|
||||
// key alone, which is the first half of every link handed out; the lock must prevent that.
|
||||
func TestLockShadowsocks2022KeepsTheCoreMultiUser(t *testing.T) {
|
||||
const key = "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY="
|
||||
const user = `[{"email":"a@x","password":"` + key + `"}]`
|
||||
for name, tc := range map[string]struct {
|
||||
settings string
|
||||
locks bool
|
||||
}{
|
||||
"empty clients": {`{"method":"2022-blake3-aes-256-gcm","password":"` + key + `","clients":[]}`, true},
|
||||
"no clients key": {`{"method":"2022-blake3-aes-256-gcm","password":"` + key + `"}`, true},
|
||||
"upper-case method": {`{"method":"2022-BLAKE3-AES-256-GCM","password":"` + key + `","clients":[]}`, true},
|
||||
"aes-128": {`{"method":"2022-blake3-aes-128-gcm","password":"MDEyMzQ1Njc4OWFiY2RlZg==","clients":[]}`, true},
|
||||
"empty clients hide users": {`{"method":"2022-blake3-aes-256-gcm","password":"` + key + `","clients":[],"users":` + user + `}`, true},
|
||||
"one client": {`{"method":"2022-blake3-aes-256-gcm","password":"` + key + `","clients":` + user + `}`, false},
|
||||
"users only": {`{"method":"2022-blake3-aes-256-gcm","password":"` + key + `","users":` + user + `}`, false},
|
||||
"legacy method": {`{"method":"aes-256-gcm","password":"x","clients":[]}`, false},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
var settings map[string]any
|
||||
if err := json.Unmarshal([]byte(tc.settings), &settings); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
if got := LockShadowsocks2022(settings); got != tc.locks {
|
||||
t.Fatalf("LockShadowsocks2022 = %v, want %v", got, tc.locks)
|
||||
}
|
||||
raw, _ := json.Marshal(settings)
|
||||
var parsed conf.ShadowsocksServerConfig
|
||||
if err := json.Unmarshal(raw, &parsed); err != nil {
|
||||
t.Fatalf("unmarshal locked settings: %v", err)
|
||||
}
|
||||
built, err := parsed.Build()
|
||||
if err != nil {
|
||||
t.Fatalf("core refuses the locked settings: %v", err)
|
||||
}
|
||||
if _, singleUser := built.(*shadowsocks_2022.ServerConfig); singleUser {
|
||||
t.Fatalf("core serves %s to the server key alone", name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user