mirror of https://github.com/ospab/ostp.git
feat(gui): add junk and tcp fragmentation global settings
This commit is contained in:
parent
b89c6b0950
commit
2819e2b3c2
|
|
@ -302,6 +302,40 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="section-divider-mini"><span>Obfuscation</span></div>
|
||||||
|
|
||||||
|
<div class="toggle-row" style="border-top:none;">
|
||||||
|
<div class="toggle-text">
|
||||||
|
<span class="toggle-name">Junk Packets</span>
|
||||||
|
<span class="toggle-hint">Random noise before connection (UoT only)</span>
|
||||||
|
</div>
|
||||||
|
<div class="toggle-with-gear">
|
||||||
|
<label class="toggle">
|
||||||
|
<input type="checkbox" id="cs-junk-enabled" />
|
||||||
|
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
||||||
|
</label>
|
||||||
|
<button id="btn-junk-settings" class="gear-btn" title="Configure junk packets">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="toggle-row" style="border-top:none;">
|
||||||
|
<div class="toggle-text">
|
||||||
|
<span class="toggle-name">TCP Fragmentation</span>
|
||||||
|
<span class="toggle-hint">Split handshake into segments (UoT only)</span>
|
||||||
|
</div>
|
||||||
|
<div class="toggle-with-gear">
|
||||||
|
<label class="toggle">
|
||||||
|
<input type="checkbox" id="cs-tcp-frag" />
|
||||||
|
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
||||||
|
</label>
|
||||||
|
<button id="btn-frag-settings" class="gear-btn" title="Configure fragmentation">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
|
|
@ -457,6 +491,58 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ── JUNK PACKETS MODAL ──────────────────────────────────── -->
|
||||||
|
<div id="junk-modal" class="modal-overlay hidden">
|
||||||
|
<div class="modal-content compact">
|
||||||
|
<h3 class="modal-title">Junk Packets</h3>
|
||||||
|
<p class="modal-text">Sends random garbage frames before the Noise handshake to confuse DPI pattern matching. Active in UoT/TCP mode only.</p>
|
||||||
|
<div class="modal-row-2">
|
||||||
|
<div class="field-group">
|
||||||
|
<label class="field-label" for="cs-junk-pc-min">Count min</label>
|
||||||
|
<input id="cs-junk-pc-min" class="field-input" type="number" min="0" max="100" placeholder="2" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label class="field-label" for="cs-junk-pc-max">Count max</label>
|
||||||
|
<input id="cs-junk-pc-max" class="field-input" type="number" min="0" max="100" placeholder="5" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-row-2">
|
||||||
|
<div class="field-group">
|
||||||
|
<label class="field-label" for="cs-junk-ps-min">Size min (bytes)</label>
|
||||||
|
<input id="cs-junk-ps-min" class="field-input" type="number" min="1" placeholder="100" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label class="field-label" for="cs-junk-ps-max">Size max (bytes)</label>
|
||||||
|
<input id="cs-junk-ps-max" class="field-input" type="number" min="1" placeholder="1000" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button id="btn-junk-done" class="btn primary">Done</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── TCP FRAGMENTATION MODAL ──────────────────────────────── -->
|
||||||
|
<div id="frag-modal" class="modal-overlay hidden">
|
||||||
|
<div class="modal-content compact">
|
||||||
|
<h3 class="modal-title">TCP Fragmentation</h3>
|
||||||
|
<p class="modal-text">Splits the initial handshake into tiny TCP segments with delays between them. Prevents DPI from reading the full handshake signature. Active in UoT/TCP mode only.</p>
|
||||||
|
<div class="modal-row-2">
|
||||||
|
<div class="field-group">
|
||||||
|
<label class="field-label" for="cs-frag-chunk">Chunk size (bytes)</label>
|
||||||
|
<input id="cs-frag-chunk" class="field-input" type="number" min="1" placeholder="2" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label class="field-label" for="cs-frag-sleep">Delay (ms)</label>
|
||||||
|
<input id="cs-frag-sleep" class="field-input" type="number" min="0" placeholder="2" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button id="btn-frag-done" class="btn primary">Done</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Toast notification -->
|
<!-- Toast notification -->
|
||||||
<div id="toast" class="toast" role="status" aria-live="polite"></div>
|
<div id="toast" class="toast" role="status" aria-live="polite"></div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,22 @@ const inShowSpeed = $('in-show-speed');
|
||||||
const groupKillSwitch = $('group-kill-switch');
|
const groupKillSwitch = $('group-kill-switch');
|
||||||
const groupMuxSessions = $('group-mux-sessions');
|
const groupMuxSessions = $('group-mux-sessions');
|
||||||
|
|
||||||
|
const inJunkEnabled = $('cs-junk-enabled');
|
||||||
|
const btnJunkSettings = $('btn-junk-settings');
|
||||||
|
const junkModal = $('junk-modal');
|
||||||
|
const inJunkPcMin = $('cs-junk-pc-min');
|
||||||
|
const inJunkPcMax = $('cs-junk-pc-max');
|
||||||
|
const inJunkPsMin = $('cs-junk-ps-min');
|
||||||
|
const inJunkPsMax = $('cs-junk-ps-max');
|
||||||
|
const btnJunkDone = $('btn-junk-done');
|
||||||
|
|
||||||
|
const inTcpFrag = $('cs-tcp-frag');
|
||||||
|
const btnFragSettings = $('btn-frag-settings');
|
||||||
|
const fragModal = $('frag-modal');
|
||||||
|
const inFragChunk = $('cs-frag-chunk');
|
||||||
|
const inFragSleep = $('cs-frag-sleep');
|
||||||
|
const btnFragDone = $('btn-frag-done');
|
||||||
|
|
||||||
// ── UTILITIES ─────────────────────────────────────────────────────────
|
// ── UTILITIES ─────────────────────────────────────────────────────────
|
||||||
function fmtBytes(b) {
|
function fmtBytes(b) {
|
||||||
if (!b || b === 0) return '0 B';
|
if (!b || b === 0) return '0 B';
|
||||||
|
|
@ -307,11 +323,11 @@ function buildConfig() {
|
||||||
debug: !!s.debug,
|
debug: !!s.debug,
|
||||||
transport: {
|
transport: {
|
||||||
mode: active.transport || 'udp',
|
mode: active.transport || 'udp',
|
||||||
tcp_fragmentation: !!active.tcp_fragmentation,
|
tcp_fragmentation: s.tcpFrag || !!active.tcp_fragmentation,
|
||||||
frag_chunk: active.frag_chunk || 2,
|
frag_chunk: s.tcpFrag ? (s.fragChunk || 2) : (active.frag_chunk || 2),
|
||||||
frag_sleep: 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: active.junk_pc || [2, 5],
|
junk_pc: s.junkEnabled ? [s.junkPcMin || 2, s.junkPcMax || 5] : (active.junk_pc || [2, 5]),
|
||||||
junk_ps: active.junk_ps || [100, 1000]
|
junk_ps: s.junkEnabled ? [s.junkPsMin || 100, s.junkPsMax || 1000] : (active.junk_ps || [100, 1000])
|
||||||
},
|
},
|
||||||
tun: {
|
tun: {
|
||||||
enable: !!s.tun,
|
enable: !!s.tun,
|
||||||
|
|
@ -668,6 +684,14 @@ function loadSettingsIntoForm() {
|
||||||
inDebug.checked = !!s.debug;
|
inDebug.checked = !!s.debug;
|
||||||
inShowRtt.checked = s.showRtt !== false;
|
inShowRtt.checked = s.showRtt !== false;
|
||||||
inShowSpeed.checked = s.showSpeed !== false;
|
inShowSpeed.checked = s.showSpeed !== false;
|
||||||
|
inJunkEnabled.checked = !!s.junkEnabled;
|
||||||
|
inJunkPcMin.value = s.junkPcMin || 2;
|
||||||
|
inJunkPcMax.value = s.junkPcMax || 5;
|
||||||
|
inJunkPsMin.value = s.junkPsMin || 100;
|
||||||
|
inJunkPsMax.value = s.junkPsMax || 1000;
|
||||||
|
inTcpFrag.checked = !!s.tcpFrag;
|
||||||
|
inFragChunk.value = s.fragChunk || 2;
|
||||||
|
inFragSleep.value = !isNaN(parseInt(s.fragSleep)) ? s.fragSleep : 2;
|
||||||
updateClientVisibility();
|
updateClientVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -688,6 +712,14 @@ function collectAndSaveSettings() {
|
||||||
debug: inDebug.checked,
|
debug: inDebug.checked,
|
||||||
showRtt: inShowRtt.checked,
|
showRtt: inShowRtt.checked,
|
||||||
showSpeed: inShowSpeed.checked,
|
showSpeed: inShowSpeed.checked,
|
||||||
|
junkEnabled: inJunkEnabled.checked,
|
||||||
|
junkPcMin: parseInt(inJunkPcMin.value) || 2,
|
||||||
|
junkPcMax: parseInt(inJunkPcMax.value) || 5,
|
||||||
|
junkPsMin: parseInt(inJunkPsMin.value) || 100,
|
||||||
|
junkPsMax: parseInt(inJunkPsMax.value) || 1000,
|
||||||
|
tcpFrag: inTcpFrag.checked,
|
||||||
|
fragChunk: parseInt(inFragChunk.value) || 2,
|
||||||
|
fragSleep: !isNaN(parseInt(inFragSleep.value)) ? parseInt(inFragSleep.value) : 2,
|
||||||
};
|
};
|
||||||
saveClientSettings(s);
|
saveClientSettings(s);
|
||||||
updateClientVisibility();
|
updateClientVisibility();
|
||||||
|
|
@ -881,9 +913,9 @@ 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]
|
[inTun, inKillSwitch, inMux, inAutoconnect, inLaunchStartup, inDebug, inShowRtt, inShowSpeed, inJunkEnabled, inTcpFrag]
|
||||||
.forEach(el => el.addEventListener('change', collectAndSaveSettings));
|
.forEach(el => el.addEventListener('change', collectAndSaveSettings));
|
||||||
[inMuxSessions, inMtu, inDns, inSocks, inExDomains, inExIps, inExProcs]
|
[inMuxSessions, inMtu, inDns, inSocks, inExDomains, inExIps, inExProcs, inJunkPcMin, inJunkPcMax, inJunkPsMin, inJunkPsMax, inFragChunk, inFragSleep]
|
||||||
.forEach(el => {
|
.forEach(el => {
|
||||||
el.addEventListener('input', () => {
|
el.addEventListener('input', () => {
|
||||||
clearTimeout(el._saveTimer);
|
clearTimeout(el._saveTimer);
|
||||||
|
|
@ -891,6 +923,21 @@ window.addEventListener('DOMContentLoaded', async () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Junk and Frag modals
|
||||||
|
btnJunkSettings.addEventListener('click', () => junkModal.classList.remove('hidden'));
|
||||||
|
btnJunkDone.addEventListener('click', () => {
|
||||||
|
collectAndSaveSettings();
|
||||||
|
junkModal.classList.add('hidden');
|
||||||
|
});
|
||||||
|
junkModal.addEventListener('click', e => { if (e.target === junkModal) junkModal.classList.add('hidden'); });
|
||||||
|
|
||||||
|
btnFragSettings.addEventListener('click', () => fragModal.classList.remove('hidden'));
|
||||||
|
btnFragDone.addEventListener('click', () => {
|
||||||
|
collectAndSaveSettings();
|
||||||
|
fragModal.classList.add('hidden');
|
||||||
|
});
|
||||||
|
fragModal.addEventListener('click', e => { if (e.target === fragModal) fragModal.classList.add('hidden'); });
|
||||||
|
|
||||||
// ── Global Keyboard Shortcuts (TUI emulation) ─────────────────────
|
// ── Global Keyboard Shortcuts (TUI emulation) ─────────────────────
|
||||||
window.addEventListener('keydown', async e => {
|
window.addEventListener('keydown', async e => {
|
||||||
// Ignore if typing in an input or textarea
|
// Ignore if typing in an input or textarea
|
||||||
|
|
@ -922,6 +969,8 @@ window.addEventListener('DOMContentLoaded', async () => {
|
||||||
!profileModal.classList.contains('hidden') ||
|
!profileModal.classList.contains('hidden') ||
|
||||||
!shareModal.classList.contains('hidden') ||
|
!shareModal.classList.contains('hidden') ||
|
||||||
!wintunModal.classList.contains('hidden') ||
|
!wintunModal.classList.contains('hidden') ||
|
||||||
|
!junkModal.classList.contains('hidden') ||
|
||||||
|
!fragModal.classList.contains('hidden') ||
|
||||||
!addMenu.classList.contains('hidden')) {
|
!addMenu.classList.contains('hidden')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -604,6 +604,22 @@ a { text-decoration: none; }
|
||||||
background: var(--c-accent);
|
background: var(--c-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toggle-with-gear {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.gear-btn {
|
||||||
|
width: 26px; height: 26px;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
border-radius: var(--r-xs);
|
||||||
|
color: var(--c-txt-2);
|
||||||
|
transition: all var(--t-fast);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.gear-btn:hover { color: var(--c-txt-1); background: rgba(255,255,255,0.07); }
|
||||||
|
:root.light .gear-btn:hover { background: rgba(0,0,0,0.05); }
|
||||||
|
|
||||||
/* ── Inline field (label + input on one line) ────────────────────────── */
|
/* ── Inline field (label + input on one line) ────────────────────────── */
|
||||||
.inline-field {
|
.inline-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue