mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-21 10:27:14 +00:00
fix(xhttp): stop injecting scMaxEachPostBytes/scMinPostsIntervalMs defaults (#5141)
The panel seeded xhttp configs with scMaxEachPostBytes=1000000 and scMinPostsIntervalMs=30 — xray-core''s own defaults — and emitted them into every generated config and share link. The literal scMinPostsIntervalMs=30 is a stable DPI fingerprint that Russia''s TSPU keys on to block connections on mobile networks. New configs no longer seed these values (empty schema/template defaults, so xray-core applies its internal defaults). For configs already stored with the old defaults, the link/subscription builders now drop values equal to xray-core''s defaults instead of advertising them — covering panel share links, the raw subscription, and the JSON subscription without requiring every inbound to be re-saved. Non-default values the user set deliberately are still emitted.
This commit is contained in:
@@ -8,3 +8,4 @@ DockerEntrypoint.sh text eol=lf
|
|||||||
# with core.autocrlf=true doesn't show phantom CRLF-only "modified" diffs.
|
# with core.autocrlf=true doesn't show phantom CRLF-only "modified" diffs.
|
||||||
frontend/src/generated/** text eol=lf
|
frontend/src/generated/** text eol=lf
|
||||||
frontend/public/openapi.json text eol=lf
|
frontend/public/openapi.json text eol=lf
|
||||||
|
frontend\src\test\__snapshots__\** text eol=lf
|
||||||
@@ -59,9 +59,15 @@ function buildXhttpExtra(xhttp: XHttpStreamSettings | undefined): Record<string,
|
|||||||
'uplinkDataKey',
|
'uplinkDataKey',
|
||||||
'scMaxEachPostBytes',
|
'scMaxEachPostBytes',
|
||||||
] as const;
|
] as const;
|
||||||
|
// Values matching xray-core's own defaults stay off the wire — old panels
|
||||||
|
// seeded them into every config and the literal values are a DPI
|
||||||
|
// fingerprint (#5141). Mirrors the sub service's filter.
|
||||||
|
const coreDefaults: Partial<Record<(typeof stringFields)[number], string>> = {
|
||||||
|
scMaxEachPostBytes: '1000000',
|
||||||
|
};
|
||||||
for (const k of stringFields) {
|
for (const k of stringFields) {
|
||||||
const v = xhttp[k];
|
const v = xhttp[k];
|
||||||
if (typeof v === 'string' && v.length > 0) extra[k] = v;
|
if (typeof v === 'string' && v.length > 0 && v !== coreDefaults[k]) extra[k] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Headers on the wire are a record; emit them as a map upstream's
|
// Headers on the wire are a record; emit them as a map upstream's
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ function buildStream(network: string, security: string): Raw {
|
|||||||
case 'xhttp':
|
case 'xhttp':
|
||||||
stream.xhttpSettings = {
|
stream.xhttpSettings = {
|
||||||
path: '/', host: '', mode: 'auto', headers: {},
|
path: '/', host: '', mode: 'auto', headers: {},
|
||||||
xPaddingBytes: '100-1000', scMaxEachPostBytes: '1000000',
|
xPaddingBytes: '100-1000',
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ export function normalizeXhttpForWire(
|
|||||||
}
|
}
|
||||||
|
|
||||||
dropEmptyStrings(out, PLACEMENT_STRING_FIELDS);
|
dropEmptyStrings(out, PLACEMENT_STRING_FIELDS);
|
||||||
|
// Empty tuning fields mean "use xray-core's default" — never emit them.
|
||||||
|
dropEmptyStrings(out, ['scMaxEachPostBytes', 'scMinPostsIntervalMs', 'scStreamUpServerSecs']);
|
||||||
|
|
||||||
if (!hasMeaningfulHeaders(out.headers)) {
|
if (!hasMeaningfulHeaders(out.headers)) {
|
||||||
delete out.headers;
|
delete out.headers;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export function newStreamSlice(network: string): Record<string, unknown> {
|
|||||||
network: 'xhttp',
|
network: 'xhttp',
|
||||||
xhttpSettings: {
|
xhttpSettings: {
|
||||||
path: '/', host: '', mode: '', headers: [],
|
path: '/', host: '', mode: '', headers: [],
|
||||||
xPaddingBytes: '100-1000', scMaxEachPostBytes: '1000000',
|
xPaddingBytes: '100-1000',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
case 'hysteria':
|
case 'hysteria':
|
||||||
|
|||||||
@@ -41,7 +41,10 @@ export const XHttpStreamSettingsSchema = z.object({
|
|||||||
seqKey: z.string().default(''),
|
seqKey: z.string().default(''),
|
||||||
uplinkDataPlacement: z.string().default(''),
|
uplinkDataPlacement: z.string().default(''),
|
||||||
uplinkDataKey: z.string().default(''),
|
uplinkDataKey: z.string().default(''),
|
||||||
scMaxEachPostBytes: z.string().default('1000000'),
|
// Empty default on purpose: xray-core already defaults to 1MB/30ms, and
|
||||||
|
// baking the literal values into every config and share link gives DPI a
|
||||||
|
// stable fingerprint (#5141 — TSPU keys on scMinPostsIntervalMs=30).
|
||||||
|
scMaxEachPostBytes: z.string().default(''),
|
||||||
noSSEHeader: z.boolean().default(false),
|
noSSEHeader: z.boolean().default(false),
|
||||||
scMaxBufferedPosts: z.number().int().min(0).default(30),
|
scMaxBufferedPosts: z.number().int().min(0).default(30),
|
||||||
scStreamUpServerSecs: z.string().default('20-80'),
|
scStreamUpServerSecs: z.string().default('20-80'),
|
||||||
@@ -51,7 +54,7 @@ export const XHttpStreamSettingsSchema = z.object({
|
|||||||
// Outbound-only fields. Server (inbound) listener ignores these. The
|
// Outbound-only fields. Server (inbound) listener ignores these. The
|
||||||
// panel embeds them in share-link `extra` blobs so the same xhttp
|
// panel embeds them in share-link `extra` blobs so the same xhttp
|
||||||
// config can roundtrip on both sides.
|
// config can roundtrip on both sides.
|
||||||
scMinPostsIntervalMs: z.string().default('30'),
|
scMinPostsIntervalMs: z.string().default(''),
|
||||||
uplinkChunkSize: z.number().int().min(0).default(0),
|
uplinkChunkSize: z.number().int().min(0).default(0),
|
||||||
noGRPCHeader: z.boolean().default(false),
|
noGRPCHeader: z.boolean().default(false),
|
||||||
xmux: XHttpXmuxSchema.optional(),
|
xmux: XHttpXmuxSchema.optional(),
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ exports[`NetworkSettingsSchema fixtures > parses xhttp-basic byte-stably 1`] = `
|
|||||||
"noSSEHeader": false,
|
"noSSEHeader": false,
|
||||||
"path": "/sp",
|
"path": "/sp",
|
||||||
"scMaxBufferedPosts": 30,
|
"scMaxBufferedPosts": 30,
|
||||||
"scMaxEachPostBytes": "1000000",
|
"scMaxEachPostBytes": "",
|
||||||
"scMinPostsIntervalMs": "30",
|
"scMinPostsIntervalMs": "",
|
||||||
"scStreamUpServerSecs": "20-80",
|
"scStreamUpServerSecs": "20-80",
|
||||||
"seqKey": "",
|
"seqKey": "",
|
||||||
"seqPlacement": "",
|
"seqPlacement": "",
|
||||||
@@ -81,8 +81,8 @@ exports[`NetworkSettingsSchema fixtures > parses xhttp-extra-padding byte-stably
|
|||||||
"noSSEHeader": false,
|
"noSSEHeader": false,
|
||||||
"path": "/sp",
|
"path": "/sp",
|
||||||
"scMaxBufferedPosts": 30,
|
"scMaxBufferedPosts": 30,
|
||||||
"scMaxEachPostBytes": "1000000",
|
"scMaxEachPostBytes": "",
|
||||||
"scMinPostsIntervalMs": "30",
|
"scMinPostsIntervalMs": "",
|
||||||
"scStreamUpServerSecs": "20-80",
|
"scStreamUpServerSecs": "20-80",
|
||||||
"seqKey": "",
|
"seqKey": "",
|
||||||
"seqPlacement": "",
|
"seqPlacement": "",
|
||||||
@@ -115,8 +115,8 @@ exports[`NetworkSettingsSchema fixtures > parses xhttp-extra-placement byte-stab
|
|||||||
"noSSEHeader": false,
|
"noSSEHeader": false,
|
||||||
"path": "/sp",
|
"path": "/sp",
|
||||||
"scMaxBufferedPosts": 30,
|
"scMaxBufferedPosts": 30,
|
||||||
"scMaxEachPostBytes": "1000000",
|
"scMaxEachPostBytes": "",
|
||||||
"scMinPostsIntervalMs": "30",
|
"scMinPostsIntervalMs": "",
|
||||||
"scStreamUpServerSecs": "20-80",
|
"scStreamUpServerSecs": "20-80",
|
||||||
"seqKey": "X-Seq",
|
"seqKey": "X-Seq",
|
||||||
"seqPlacement": "cookie",
|
"seqPlacement": "cookie",
|
||||||
|
|||||||
@@ -217,6 +217,15 @@ func (s *SubJsonService) streamData(stream string) map[string]any {
|
|||||||
delete(xhttp, "scMaxBufferedPosts")
|
delete(xhttp, "scMaxBufferedPosts")
|
||||||
delete(xhttp, "scStreamUpServerSecs")
|
delete(xhttp, "scStreamUpServerSecs")
|
||||||
delete(xhttp, "serverMaxHeaderBytes")
|
delete(xhttp, "serverMaxHeaderBytes")
|
||||||
|
// Values matching xray-core's own defaults stay off the wire:
|
||||||
|
// old panels seeded them into every stored config and the
|
||||||
|
// literal scMinPostsIntervalMs=30 is a DPI fingerprint (#5141).
|
||||||
|
if v, _ := xhttp["scMaxEachPostBytes"].(string); v == "" || v == "1000000" {
|
||||||
|
delete(xhttp, "scMaxEachPostBytes")
|
||||||
|
}
|
||||||
|
if v, _ := xhttp["scMinPostsIntervalMs"].(string); v == "" || v == "30" {
|
||||||
|
delete(xhttp, "scMinPostsIntervalMs")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return streamSettings
|
return streamSettings
|
||||||
|
|||||||
@@ -1661,8 +1661,16 @@ func buildXhttpExtra(xhttp map[string]any) map[string]any {
|
|||||||
"uplinkDataPlacement", "uplinkDataKey",
|
"uplinkDataPlacement", "uplinkDataKey",
|
||||||
"scMaxEachPostBytes", "scMinPostsIntervalMs",
|
"scMaxEachPostBytes", "scMinPostsIntervalMs",
|
||||||
}
|
}
|
||||||
|
// Values matching xray-core's own defaults are redundant on the wire and
|
||||||
|
// the literal scMinPostsIntervalMs=30 is a known DPI fingerprint (#5141).
|
||||||
|
// Old panels seeded these defaults into every xhttp inbound, so filter
|
||||||
|
// them here instead of requiring every stored config to be re-saved.
|
||||||
|
coreDefaults := map[string]string{
|
||||||
|
"scMaxEachPostBytes": "1000000",
|
||||||
|
"scMinPostsIntervalMs": "30",
|
||||||
|
}
|
||||||
for _, field := range stringFields {
|
for _, field := range stringFields {
|
||||||
if v, ok := xhttp[field].(string); ok && len(v) > 0 {
|
if v, ok := xhttp[field].(string); ok && len(v) > 0 && v != coreDefaults[field] {
|
||||||
extra[field] = v
|
extra[field] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -545,9 +545,11 @@ func buildStream(network, security string) map[string]any {
|
|||||||
case "httpupgrade":
|
case "httpupgrade":
|
||||||
stream["httpupgradeSettings"] = map[string]any{"path": "/", "host": "", "headers": map[string]any{}}
|
stream["httpupgradeSettings"] = map[string]any{"path": "/", "host": "", "headers": map[string]any{}}
|
||||||
case "xhttp":
|
case "xhttp":
|
||||||
|
// No scMaxEachPostBytes/scMinPostsIntervalMs seed: xray-core's own
|
||||||
|
// defaults apply, and the literal values fingerprint traffic (#5141).
|
||||||
stream["xhttpSettings"] = map[string]any{
|
stream["xhttpSettings"] = map[string]any{
|
||||||
"path": "/", "host": "", "mode": "auto", "headers": map[string]any{},
|
"path": "/", "host": "", "mode": "auto", "headers": map[string]any{},
|
||||||
"xPaddingBytes": "100-1000", "scMaxEachPostBytes": "1000000",
|
"xPaddingBytes": "100-1000",
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
stream["tcpSettings"] = map[string]any{"header": map[string]any{"type": "none"}}
|
stream["tcpSettings"] = map[string]any{"header": map[string]any{"type": "none"}}
|
||||||
|
|||||||
Reference in New Issue
Block a user