import { spawnSync } from 'node:child_process'; const options = { encoding: 'utf8' as const }; const CHAIN_PATTERN = /^[a-z0-9_-]{1,28}$/i; export function setGatewayInterception(enabled: boolean, chain: string, run: typeof spawnSync = spawnSync) { if (!CHAIN_PATTERN.test(chain)) throw new Error('Некорректная TProxy chain'); const rule = ['-w', '-t', 'mangle', 'PREROUTING', '-j', chain]; const exists = run('iptables', [...rule.slice(0, 3), '-C', ...rule.slice(3)], options).status === 0; if (!enabled) { if (exists) run('iptables', [...rule.slice(0, 3), '-D', ...rule.slice(3)], options); return; } if (exists) { const input = `*mangle\n-D PREROUTING -j ${chain}\n-I PREROUTING 1 -j ${chain}\nCOMMIT\n`; const result = run('iptables-restore', ['-w', '--noflush'], { ...options, input }); if (result.status !== 0) { // The transaction keeps the already-working jump intact; leave routing up. return; } return; } const result = run( 'iptables', [...rule.slice(0, 3), '-I', 'PREROUTING', '1', '-j', chain], options, ); if (result.status !== 0) { throw new Error((result.stderr || 'Не удалось включить Gateway VPN').trim()); } }