mirror of https://github.com/ospab/ostp.git
feat(gui): TTL-desync toggle in client settings
Adds a TTL Desync toggle to the obfuscation section of the desktop client settings, wired the same way as the Junk/TCP-fragmentation toggles: persisted in client settings, applied into transport.ttl_desync in the built config, included in the hot-reload fingerprint so flipping it while connected re-applies, and labelled in both EN and RU. ttl_desync_auto is passed true, so the engine measures the hop distance and aims the decoys itself — the user just flips it on.
This commit is contained in:
parent
1e9111ab9f
commit
bfd079ff48
|
|
@ -28,6 +28,8 @@ const translations = {
|
||||||
label_kill_switch: 'Kill Switch',
|
label_kill_switch: 'Kill Switch',
|
||||||
kill_switch_hint: 'Block non-VPN traffic when tunnel drops',
|
kill_switch_hint: 'Block non-VPN traffic when tunnel drops',
|
||||||
label_transport: 'Transport Protocol',
|
label_transport: 'Transport Protocol',
|
||||||
|
label_ttl_desync: 'TTL Desync',
|
||||||
|
hint_ttl_desync: 'Decoy packets that die before the server (UDP, auto-tuned)',
|
||||||
label_mtu: 'MTU Size',
|
label_mtu: 'MTU Size',
|
||||||
label_transport: 'Transport Protocol',
|
label_transport: 'Transport Protocol',
|
||||||
label_sni: 'Stealth SNI (Fake Host)',
|
label_sni: 'Stealth SNI (Fake Host)',
|
||||||
|
|
@ -93,6 +95,8 @@ const translations = {
|
||||||
label_kill_switch: 'Kill Switch',
|
label_kill_switch: 'Kill Switch',
|
||||||
kill_switch_hint: 'Блокировать трафик вне VPN при обрыве связи',
|
kill_switch_hint: 'Блокировать трафик вне VPN при обрыве связи',
|
||||||
label_transport: 'Транспортный протокол',
|
label_transport: 'Транспортный протокол',
|
||||||
|
label_ttl_desync: 'TTL-десинхронизация',
|
||||||
|
hint_ttl_desync: 'Пакеты-приманки, умирающие до сервера (UDP, авто-настройка)',
|
||||||
label_mtu: 'Размер MTU',
|
label_mtu: 'Размер MTU',
|
||||||
label_transport: 'Транспортный протокол',
|
label_transport: 'Транспортный протокол',
|
||||||
label_sni: 'Маскировочный SNI',
|
label_sni: 'Маскировочный SNI',
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="toggle-row" style="border-top:none;">
|
||||||
|
<div class="toggle-text">
|
||||||
|
<span class="toggle-name" data-i18n="label_ttl_desync">TTL Desync</span>
|
||||||
|
<span class="toggle-hint" data-i18n="hint_ttl_desync">Decoy packets that die before the server (UDP, auto-tuned)</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle">
|
||||||
|
<input type="checkbox" id="cs-ttl-desync" />
|
||||||
|
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div> <!-- client-settings-card -->
|
</div> <!-- client-settings-card -->
|
||||||
|
|
||||||
<div class="app-version" id="app-version">OSTP GUI</div>
|
<div class="app-version" id="app-version">OSTP GUI</div>
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,7 @@ const btnJunkDone = $('btn-junk-done');
|
||||||
|
|
||||||
const inTcpFrag = $('cs-tcp-frag');
|
const inTcpFrag = $('cs-tcp-frag');
|
||||||
const btnFragSettings = $('btn-frag-settings');
|
const btnFragSettings = $('btn-frag-settings');
|
||||||
|
const inTtlDesync = $('cs-ttl-desync');
|
||||||
const fragModal = $('frag-modal');
|
const fragModal = $('frag-modal');
|
||||||
const inFragChunk = $('cs-frag-chunk');
|
const inFragChunk = $('cs-frag-chunk');
|
||||||
const inFragSleep = $('cs-frag-sleep');
|
const inFragSleep = $('cs-frag-sleep');
|
||||||
|
|
@ -318,7 +319,9 @@ function buildConfig() {
|
||||||
frag_chunk: s.tcpFrag ? (s.fragChunk || 2) : (active.frag_chunk || 2),
|
frag_chunk: s.tcpFrag ? (s.fragChunk || 2) : (active.frag_chunk || 2),
|
||||||
frag_sleep: s.tcpFrag ? (!isNaN(parseInt(s.fragSleep)) ? s.fragSleep : 2) : (active.frag_sleep !== undefined ? active.frag_sleep : 2),
|
frag_sleep: s.tcpFrag ? (!isNaN(parseInt(s.fragSleep)) ? s.fragSleep : 2) : (active.frag_sleep !== undefined ? active.frag_sleep : 2),
|
||||||
junk_pc: s.junkEnabled ? [s.junkPcMin || 2, s.junkPcMax || 5] : (active.junk_pc || [2, 5]),
|
junk_pc: s.junkEnabled ? [s.junkPcMin || 2, s.junkPcMax || 5] : (active.junk_pc || [2, 5]),
|
||||||
junk_ps: s.junkEnabled ? [s.junkPsMin || 100, s.junkPsMax || 1000] : (active.junk_ps || [100, 1000])
|
junk_ps: s.junkEnabled ? [s.junkPsMin || 100, s.junkPsMax || 1000] : (active.junk_ps || [100, 1000]),
|
||||||
|
ttl_desync: !!s.ttlDesync,
|
||||||
|
ttl_desync_auto: true
|
||||||
},
|
},
|
||||||
tun: {
|
tun: {
|
||||||
enable: !!s.tun,
|
enable: !!s.tun,
|
||||||
|
|
@ -657,6 +660,7 @@ function loadSettingsIntoForm() {
|
||||||
inTcpFrag.checked = !!s.tcpFrag;
|
inTcpFrag.checked = !!s.tcpFrag;
|
||||||
inFragChunk.value = s.fragChunk || 2;
|
inFragChunk.value = s.fragChunk || 2;
|
||||||
inFragSleep.value = !isNaN(parseInt(s.fragSleep)) ? s.fragSleep : 2;
|
inFragSleep.value = !isNaN(parseInt(s.fragSleep)) ? s.fragSleep : 2;
|
||||||
|
if (inTtlDesync) inTtlDesync.checked = !!s.ttlDesync;
|
||||||
updateClientVisibility();
|
updateClientVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -692,6 +696,7 @@ function collectAndSaveSettings() {
|
||||||
tcpFrag: inTcpFrag.checked,
|
tcpFrag: inTcpFrag.checked,
|
||||||
fragChunk: parseInt(inFragChunk.value) || 2,
|
fragChunk: parseInt(inFragChunk.value) || 2,
|
||||||
fragSleep: !isNaN(parseInt(inFragSleep.value)) ? parseInt(inFragSleep.value) : 2,
|
fragSleep: !isNaN(parseInt(inFragSleep.value)) ? parseInt(inFragSleep.value) : 2,
|
||||||
|
ttlDesync: inTtlDesync ? inTtlDesync.checked : false,
|
||||||
};
|
};
|
||||||
// Cheap and local: safe to run on every debounced keystroke.
|
// Cheap and local: safe to run on every debounced keystroke.
|
||||||
saveClientSettings(s);
|
saveClientSettings(s);
|
||||||
|
|
@ -715,7 +720,7 @@ function collectAndSaveSettings() {
|
||||||
const tunnelRelevant = JSON.stringify([
|
const tunnelRelevant = JSON.stringify([
|
||||||
s.tun, s.killSwitch, s.mux, s.muxSessions, s.mtu, s.dns, s.socks,
|
s.tun, s.killSwitch, s.mux, s.muxSessions, s.mtu, s.dns, s.socks,
|
||||||
s.exDomains, s.exIps, s.exProcs, s.junkEnabled, s.junkPcMin, s.junkPcMax,
|
s.exDomains, s.exIps, s.exProcs, s.junkEnabled, s.junkPcMin, s.junkPcMax,
|
||||||
s.junkPsMin, s.junkPsMax, s.tcpFrag, s.fragChunk, s.fragSleep,
|
s.junkPsMin, s.junkPsMax, s.tcpFrag, s.fragChunk, s.fragSleep, s.ttlDesync,
|
||||||
]);
|
]);
|
||||||
if (tunnelRelevant !== lastAppliedTunnelConfig) {
|
if (tunnelRelevant !== lastAppliedTunnelConfig) {
|
||||||
clearTimeout(hotReloadTimer);
|
clearTimeout(hotReloadTimer);
|
||||||
|
|
@ -901,7 +906,8 @@ window.addEventListener('DOMContentLoaded', async () => {
|
||||||
wintunModal.addEventListener('click', e => { if (e.target === wintunModal) wintunModal.classList.add('hidden'); });
|
wintunModal.addEventListener('click', e => { if (e.target === wintunModal) wintunModal.classList.add('hidden'); });
|
||||||
|
|
||||||
// Client settings — wire all inputs
|
// Client settings — wire all inputs
|
||||||
[inTun, inKillSwitch, inMux, inAutoconnect, inLaunchStartup, inDebug, inShowRtt, inShowSpeed, inJunkEnabled, inTcpFrag]
|
[inTun, inKillSwitch, inMux, inAutoconnect, inLaunchStartup, inDebug, inShowRtt, inShowSpeed, inJunkEnabled, inTcpFrag, inTtlDesync]
|
||||||
|
.filter(Boolean)
|
||||||
.forEach(el => el.addEventListener('change', collectAndSaveSettings));
|
.forEach(el => el.addEventListener('change', collectAndSaveSettings));
|
||||||
[inMuxSessions, inMtu, inDns, inSocks, inExDomains, inExIps, inExProcs, inJunkPcMin, inJunkPcMax, inJunkPsMin, inJunkPsMax, inFragChunk, inFragSleep]
|
[inMuxSessions, inMtu, inDns, inSocks, inExDomains, inExIps, inExProcs, inJunkPcMin, inJunkPcMax, inJunkPsMin, inJunkPsMax, inFragChunk, inFragSleep]
|
||||||
.forEach(el => {
|
.forEach(el => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue