fix: disable gateway listeners in server mode
This commit is contained in:
@@ -9,7 +9,15 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const operatingModeKey = "OPERATING_MODE="
|
||||
type enforcedSetting struct {
|
||||
key string
|
||||
value string
|
||||
}
|
||||
|
||||
var serverSettings = []enforcedSetting{
|
||||
{key: "OPERATING_MODE=", value: "server"},
|
||||
{key: "PROXY_MODE=", value: "none"},
|
||||
}
|
||||
|
||||
var runtimeDirectories = []string{
|
||||
"bin",
|
||||
@@ -29,9 +37,9 @@ type Config struct {
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
CoreInitialized bool
|
||||
ConfigInitialized bool
|
||||
ServerModeChanged bool
|
||||
CoreInitialized bool
|
||||
ConfigInitialized bool
|
||||
ServerSettingsChanged bool
|
||||
}
|
||||
|
||||
func Prepare(config Config) (Result, error) {
|
||||
@@ -65,9 +73,9 @@ func Prepare(config Config) (Result, error) {
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("initialize config: %w", err)
|
||||
}
|
||||
result.ServerModeChanged, err = enforceServerMode(filepath.Join(root, ".ssclash", "settings"))
|
||||
result.ServerSettingsChanged, err = enforceServerSettings(filepath.Join(root, ".ssclash", "settings"))
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("enforce server mode: %w", err)
|
||||
return result, fmt.Errorf("enforce server settings: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -117,7 +125,7 @@ func copyIfAbsent(source, target string, mode os.FileMode) (bool, error) {
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
func enforceServerMode(path string) (bool, error) {
|
||||
func enforceServerSettings(path string) (bool, error) {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return false, fmt.Errorf("read settings %q: %w", path, err)
|
||||
@@ -127,20 +135,37 @@ func enforceServerMode(path string) (bool, error) {
|
||||
if len(content) > 0 {
|
||||
lines = strings.Split(strings.TrimSuffix(string(content), "\n"), "\n")
|
||||
}
|
||||
modeIndex := -1
|
||||
indexes := make(map[string]int, len(serverSettings))
|
||||
for _, setting := range serverSettings {
|
||||
indexes[setting.key] = -1
|
||||
}
|
||||
for index, line := range lines {
|
||||
if strings.HasPrefix(line, operatingModeKey) {
|
||||
if modeIndex >= 0 {
|
||||
return false, fmt.Errorf("multiple OPERATING_MODE entries in %q", path)
|
||||
for _, setting := range serverSettings {
|
||||
if !strings.HasPrefix(line, setting.key) {
|
||||
continue
|
||||
}
|
||||
modeIndex = index
|
||||
if indexes[setting.key] >= 0 {
|
||||
return false, fmt.Errorf("multiple %s entries in %q", strings.TrimSuffix(setting.key, "="), path)
|
||||
}
|
||||
indexes[setting.key] = index
|
||||
}
|
||||
}
|
||||
changed := modeIndex < 0 || lines[modeIndex] != operatingModeKey+"server"
|
||||
if modeIndex >= 0 {
|
||||
lines[modeIndex] = operatingModeKey + "server"
|
||||
} else {
|
||||
lines = append(lines, operatingModeKey+"server")
|
||||
changed := false
|
||||
for _, setting := range serverSettings {
|
||||
expected := setting.key + setting.value
|
||||
index := indexes[setting.key]
|
||||
if index >= 0 {
|
||||
if lines[index] != expected {
|
||||
lines[index] = expected
|
||||
changed = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
lines = append(lines, expected)
|
||||
changed = true
|
||||
}
|
||||
if !changed {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
settings := strings.Join(lines, "\n") + "\n"
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestPrepareInitializesServerRuntime(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Prepare() error = %v", err)
|
||||
}
|
||||
if !result.CoreInitialized || !result.ConfigInitialized || !result.ServerModeChanged {
|
||||
if !result.CoreInitialized || !result.ConfigInitialized || !result.ServerSettingsChanged {
|
||||
t.Errorf("Prepare() result = %+v, want all initialization flags", result)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestPrepareInitializesServerRuntime(t *testing.T) {
|
||||
|
||||
assertFileContent(t, filepath.Join(root, "bin", "clash"), "mihomo-binary")
|
||||
assertFileContent(t, filepath.Join(root, "config.yaml"), "mixed-port: 7890\n")
|
||||
assertFileContent(t, filepath.Join(root, ".ssclash", "settings"), "OPERATING_MODE=server\n")
|
||||
assertFileContent(t, filepath.Join(root, ".ssclash", "settings"), "OPERATING_MODE=server\nPROXY_MODE=none\n")
|
||||
|
||||
coreInfo, err := os.Stat(filepath.Join(root, "bin", "clash"))
|
||||
if err != nil {
|
||||
@@ -64,7 +64,7 @@ func TestPreparePreservesUserDataAndForcesServerMode(t *testing.T) {
|
||||
}
|
||||
writeFixture(t, filepath.Join(root, "bin"), "clash", "user-managed-core")
|
||||
writeFixture(t, root, "config.yaml", "user: config\n")
|
||||
writeFixture(t, filepath.Join(root, ".ssclash"), "settings", "LOG_LEVEL=debug\nOPERATING_MODE=gateway\n")
|
||||
writeFixture(t, filepath.Join(root, ".ssclash"), "settings", "LOG_LEVEL=debug\nOPERATING_MODE=gateway\nPROXY_MODE=tproxy\n")
|
||||
|
||||
result, err := Prepare(Config{
|
||||
Root: root,
|
||||
@@ -74,13 +74,13 @@ func TestPreparePreservesUserDataAndForcesServerMode(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Prepare() error = %v", err)
|
||||
}
|
||||
if result.CoreInitialized || result.ConfigInitialized || !result.ServerModeChanged {
|
||||
if result.CoreInitialized || result.ConfigInitialized || !result.ServerSettingsChanged {
|
||||
t.Errorf("Prepare() result = %+v, want only server mode changed", result)
|
||||
}
|
||||
|
||||
assertFileContent(t, filepath.Join(root, "bin", "clash"), "user-managed-core")
|
||||
assertFileContent(t, filepath.Join(root, "config.yaml"), "user: config\n")
|
||||
assertFileContent(t, filepath.Join(root, ".ssclash", "settings"), "LOG_LEVEL=debug\nOPERATING_MODE=server\n")
|
||||
assertFileContent(t, filepath.Join(root, ".ssclash", "settings"), "LOG_LEVEL=debug\nOPERATING_MODE=server\nPROXY_MODE=none\n")
|
||||
}
|
||||
|
||||
func TestPrepareRejectsUnsafeOrAmbiguousState(t *testing.T) {
|
||||
@@ -130,6 +130,22 @@ func TestPrepareRejectsUnsafeOrAmbiguousState(t *testing.T) {
|
||||
},
|
||||
wantErr: "multiple OPERATING_MODE",
|
||||
},
|
||||
{
|
||||
name: "duplicate proxy mode",
|
||||
config: Config{
|
||||
Root: filepath.Join(tempDir, "duplicate-proxy-mode"),
|
||||
CoreSource: coreSource,
|
||||
ConfigSource: configSource,
|
||||
},
|
||||
setup: func(t *testing.T, root string) {
|
||||
t.Helper()
|
||||
if err := os.MkdirAll(filepath.Join(root, ".ssclash"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeFixture(t, filepath.Join(root, ".ssclash"), "settings", "PROXY_MODE=tproxy\nPROXY_MODE=none\n")
|
||||
},
|
||||
wantErr: "multiple PROXY_MODE",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range tests {
|
||||
|
||||
Reference in New Issue
Block a user