feat(finalmask): header-custom variables, sudoku UDP mask and noise reset ranges

xray-core 26.6 grew the finalmask surface the editor could not express,
and the finalmask watch drops any key without a registered field, so a
config written by hand lost these keys on the next save:

- header-custom items can capture their bytes into a variable, reuse one
  or emit a transform expression; a Kind select keeps each item to exactly
  one kind, as the core requires, and only parsed JSON reaches transform;
- TCP header-custom gains the errors group (the reply sent when a client
  group does not match), UDP header-custom its prefix/standalone mode;
- sudoku is offered as a UDP mask, and share links keep it in fm=;
- the noise reset is an Int32Range, so it now takes "60-120" as well.

The new golden fixture is saved unchanged by the outbound editor and
built by xray-core in TestGoldenStreamFixturesBuildInXray.
This commit is contained in:
MHSanaei
2026-10-11 15:27:19 +02:00
parent ec0a57fc4f
commit 1c050f872a
7 changed files with 531 additions and 52 deletions
+1
View File
@@ -2700,6 +2700,7 @@ var validFinalMaskUDPTypes = map[string]struct{}{
"header-custom": {},
"realm": {},
"udphop": {},
"sudoku": {},
}
var validFinalMaskTCPTypes = map[string]struct{}{
+14
View File
@@ -1181,6 +1181,20 @@ func TestMarshalFinalMask_KeepsUdpHopMask(t *testing.T) {
}
}
// xray-core 26.6 accepts sudoku as a UDP mask too; the link must not drop it from fm=.
func TestMarshalFinalMask_KeepsSudokuUdpMask(t *testing.T) {
fm := map[string]any{
"udp": []any{map[string]any{"type": "sudoku", "settings": map[string]any{"password": "s3cr3t"}}},
}
out, ok := marshalFinalMask(fm)
if !ok {
t.Fatal("expected ok=true for a sudoku udp mask")
}
if want := `{"udp":[{"settings":{"password":"s3cr3t"},"type":"sudoku"}]}`; out != want {
t.Fatalf("marshalFinalMask = %s, want %s", out, want)
}
}
func TestHasFinalMaskContent(t *testing.T) {
if hasFinalMaskContent(nil) {
t.Fatal("nil should not count as content")