fix(network): make DNS routing TUN-aware (issue #38)

- Share literal-target policy and route selection across metadata, Aria2, Torrent, and yt-dlp paths.
- Start direct Aria2 resolution with system DNS and fence a bounded alternate resolver fallback.
- Validate Torrent trackers/web seeds and harden magnet probe cleanup and lifecycle handling.
- Add route, resolver, fallback, and smoke coverage without changing browser IPC payloads.

Refs: #38
This commit is contained in:
NimBold
2026-08-28 17:25:52 +03:30
parent 300e225a3a
commit 9bf02f2ee2
9 changed files with 1273 additions and 337 deletions
+39 -1
View File
@@ -69,6 +69,14 @@ async function rpc(port, secret, method, params = []) {
return body.result;
}
async function forceRemoveIfPresent(port, secret, gid) {
try {
await rpc(port, secret, 'aria2.forceRemove', [gid]);
} catch (error) {
if (!/not found|no such download|active download not found/i.test(error.message)) throw error;
}
}
function childExited(child) {
return child.exitCode !== null || child.signalCode !== null;
}
@@ -218,7 +226,37 @@ try {
throw new Error(`aria2.addTorrent did not retain async-dns=false: ${JSON.stringify(torrentOptions)}`);
}
console.log('[PASS] Aria2 retained system-resolver mode for normal and Torrent transfers');
const proxyRoute = 'http://127.0.0.1:9';
const normalizedProxyRoute = new URL(proxyRoute).toString();
const proxiedUriResult = await rpc(rpcPort, secret, 'aria2.addUri', [['https://route-owned.invalid/file'], {
'all-proxy': proxyRoute,
pause: 'true',
out: 'resolver-proxied.bin',
}]);
const proxiedUriOptions = await rpc(rpcPort, secret, 'aria2.getOption', [proxiedUriResult]);
if (proxiedUriOptions['all-proxy'] !== normalizedProxyRoute) {
throw new Error(`aria2.addUri did not retain the configured proxy route: ${JSON.stringify(proxiedUriOptions)}`);
}
if (proxiedUriOptions['async-dns'] === 'false') {
throw new Error(`proxied aria2.addUri unexpectedly forced system DNS resolution: ${JSON.stringify(proxiedUriOptions)}`);
}
const proxiedTorrentResult = await rpc(rpcPort, secret, 'aria2.addTorrent', [torrent, [], {
'all-proxy': proxyRoute,
pause: 'true',
dir: tempRoot,
}]);
const proxiedTorrentOptions = await rpc(rpcPort, secret, 'aria2.getOption', [proxiedTorrentResult]);
if (proxiedTorrentOptions['all-proxy'] !== normalizedProxyRoute) {
throw new Error(`aria2.addTorrent did not retain the configured proxy route: ${JSON.stringify(proxiedTorrentOptions)}`);
}
if (proxiedTorrentOptions['async-dns'] === 'false') {
throw new Error(`proxied aria2.addTorrent unexpectedly forced system DNS resolution: ${JSON.stringify(proxiedTorrentOptions)}`);
}
await forceRemoveIfPresent(rpcPort, secret, proxiedUriResult);
await forceRemoveIfPresent(rpcPort, secret, proxiedTorrentResult);
console.log('[PASS] Aria2 retained system-resolver mode for direct normal/Torrent transfers and configured proxy mode for proxied transfers');
} catch (error) {
const detail = stderr.trim();
throw new Error(`${error.message}${detail ? `\n${detail}` : ''}`);
+19 -1
View File
@@ -790,12 +790,15 @@ async function main() {
dir: probeDir,
'bt-metadata-only': 'true',
'bt-save-metadata': 'true',
'async-dns': 'false',
'max-tries': '3',
'retry-wait': '1',
'connect-timeout': '5',
timeout: '15',
'auto-file-renaming': 'false',
}]);
const probeOptions = await rpc(client.rpcPort, client.secret, 'aria2.getOption', [probeGid]);
assert(probeOptions['async-dns'] === 'false', 'direct magnet metadata probe did not retain system DNS resolution');
const probeStatus = await waitForTerminal(client, probeGid, 30000);
assert(probeStatus.status === 'complete', 'magnet metadata probe did not complete');
const savedTorrentPaths = fs.readdirSync(probeDir)
@@ -841,7 +844,22 @@ async function main() {
if (probeRemoved) await waitForRemoved(client, probeGid);
fs.rmSync(probeDir, { recursive: true, force: true });
fs.mkdirSync(probeDir, { recursive: true });
console.log('[OK] metadata probe was removed after resolution');
const proxiedProbeRoute = 'http://127.0.0.1:9';
const normalizedProxiedProbeRoute = new URL(proxiedProbeRoute).toString();
const proxiedProbeGid = await rpc(client.rpcPort, client.secret, 'aria2.addUri', [[magnet], {
dir: probeDir,
'bt-metadata-only': 'true',
'bt-save-metadata': 'true',
'all-proxy': proxiedProbeRoute,
pause: 'true',
'auto-file-renaming': 'false',
}]);
const proxiedProbeOptions = await rpc(client.rpcPort, client.secret, 'aria2.getOption', [proxiedProbeGid]);
assert(proxiedProbeOptions['all-proxy'] === normalizedProxiedProbeRoute, 'proxied magnet metadata probe did not retain the configured proxy route');
assert(proxiedProbeOptions['async-dns'] !== 'false', 'proxied magnet metadata probe unexpectedly forced system DNS resolution');
assert(await forceRemoveIfPresent(client, proxiedProbeGid), 'proxied magnet metadata probe was not removable');
await waitForRemoved(client, proxiedProbeGid);
console.log('[OK] metadata probe was removed after resolution; direct and proxied resolver modes were retained');
const finalGid = await rpc(client.rpcPort, client.secret, 'aria2.addTorrent', [
trackerlessTorrentBytes.toString('base64'),