fix(finalmask): refuse a salamander password xray-core cannot open

xray-core needs a salamander (and gecko) password of at least 4 bytes
(smPSKMinLen), but checks it only when a socket opens: xray run -test
reports Configuration OK. On an inbound the listener then fails and Xray
does not start at all, taking every other inbound down; on a client the
dial fails. The panel saved any password, and the finalmask editor
starts a new salamander mask with an empty one.

maskcompat.CheckSalamanderPasswords applies the core's byte rule, pinned
against the core's own ListenPacket, and every path that stores a
finalmask refuses a short password: inbound add and update, hosts, the
subscription's global finalmask, and the inbounds and outbounds of the
Xray template. The editor flags it inline as it is typed.
This commit is contained in:
MHSanaei
2026-10-11 18:26:45 +02:00
parent 6321297f68
commit 0466e8b0bd
9 changed files with 251 additions and 1 deletions
+27
View File
@@ -0,0 +1,27 @@
package maskcompat
import (
"fmt"
"strings"
)
// SalamanderMinPSK is xray-core's smPSKMinLen, in bytes; the core enforces it only when a socket
// opens, so a shorter password loads fine and then stops the listener or fails every dial.
const SalamanderMinPSK = 4
// CheckSalamanderPasswords refuses a salamander (or gecko) UDP mask the core would not open.
func CheckSalamanderPasswords(finalmask any) error {
fm, _ := finalmask.(map[string]any)
masks, _ := fm["udp"].([]any)
for _, entry := range masks {
mask, _ := entry.(map[string]any)
if maskType, _ := mask["type"].(string); !strings.EqualFold(maskType, "salamander") {
continue
}
settings, _ := mask["settings"].(map[string]any)
if password, _ := settings["password"].(string); len(password) < SalamanderMinPSK {
return fmt.Errorf("salamander finalmask password must be at least %d bytes; xray-core refuses a shorter one", SalamanderMinPSK)
}
}
return nil
}