mirror of
https://github.com/nimbold/Firelink.git
synced 2026-09-09 17:25:42 +00:00
fix(network): harden route-aware torrent resolution
- keep reqwest metadata on its supported proxy route and require Aria2 asynchronous DNS - fence magnet probe ownership and delayed RPC cleanup without cross-transfer removal - validate Torrent DHT nodes, web seeds, proxy inputs, and literal IPv4/IPv6 targets - extend regression and smoke coverage for issue #38 Refs: #38
This commit is contained in:
@@ -189,6 +189,7 @@ const child = spawn(binaryPath, [
|
||||
`--dir=${tempRoot}`,
|
||||
'--file-allocation=none',
|
||||
'--enable-dht=false',
|
||||
'--async-dns=true',
|
||||
'--console-log-level=error',
|
||||
'--quiet=true',
|
||||
], { env: environment, stdio: ['ignore', 'ignore', 'pipe'] });
|
||||
@@ -198,15 +199,17 @@ child.stderr.on('data', chunk => { stderr += chunk.toString(); });
|
||||
try {
|
||||
const version = await waitForRpc(rpcPort, secret);
|
||||
const features = Array.isArray(version.enabledFeatures) ? version.enabledFeatures : [];
|
||||
console.log(`[INFO] aria2 ${version.version || 'unknown'}; Async DNS: ${features.includes('Async DNS') ? 'supported' : 'not advertised'}`);
|
||||
if (!features.includes('Async DNS')) {
|
||||
throw new Error(`packaged aria2 must advertise Async DNS for route-safe transfers: ${JSON.stringify(version)}`);
|
||||
}
|
||||
console.log(`[INFO] aria2 ${version.version || 'unknown'}; Async DNS: supported`);
|
||||
|
||||
const uriResult = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${contentPort}/file`], {
|
||||
'async-dns': 'false',
|
||||
out: 'resolver-normal.bin',
|
||||
}]);
|
||||
const uriOptions = await rpc(rpcPort, secret, 'aria2.getOption', [uriResult]);
|
||||
if (uriOptions['async-dns'] !== 'false') {
|
||||
throw new Error(`aria2.addUri did not retain async-dns=false: ${JSON.stringify(uriOptions)}`);
|
||||
if (uriOptions['async-dns'] === 'false') {
|
||||
throw new Error(`direct aria2.addUri unexpectedly disabled asynchronous DNS: ${JSON.stringify(uriOptions)}`);
|
||||
}
|
||||
|
||||
const torrent = bencode({
|
||||
@@ -218,12 +221,11 @@ try {
|
||||
},
|
||||
}).toString('base64');
|
||||
const torrentResult = await rpc(rpcPort, secret, 'aria2.addTorrent', [torrent, [], {
|
||||
'async-dns': 'false',
|
||||
dir: tempRoot,
|
||||
}]);
|
||||
const torrentOptions = await rpc(rpcPort, secret, 'aria2.getOption', [torrentResult]);
|
||||
if (torrentOptions['async-dns'] !== 'false') {
|
||||
throw new Error(`aria2.addTorrent did not retain async-dns=false: ${JSON.stringify(torrentOptions)}`);
|
||||
if (torrentOptions['async-dns'] === 'false') {
|
||||
throw new Error(`direct aria2.addTorrent unexpectedly disabled asynchronous DNS: ${JSON.stringify(torrentOptions)}`);
|
||||
}
|
||||
|
||||
const proxyRoute = 'http://127.0.0.1:9';
|
||||
@@ -256,7 +258,7 @@ try {
|
||||
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');
|
||||
console.log('[PASS] Aria2 kept asynchronous DNS for fresh direct and proxied normal/Torrent transfers');
|
||||
} catch (error) {
|
||||
const detail = stderr.trim();
|
||||
throw new Error(`${error.message}${detail ? `\n${detail}` : ''}`);
|
||||
|
||||
@@ -305,6 +305,7 @@ async function startDaemon({ name, rpcPort, listenPort, directory, extraArgs = [
|
||||
'--enable-dht=false',
|
||||
'--enable-peer-exchange=false',
|
||||
'--bt-enable-lpd=false',
|
||||
'--async-dns=true',
|
||||
'--console-log-level=error',
|
||||
'--quiet=true',
|
||||
...(selectedListenPort ? [`--listen-port=${selectedListenPort}`] : []),
|
||||
@@ -720,7 +721,6 @@ async function main() {
|
||||
'bt-metadata-only': 'false',
|
||||
'bt-save-metadata': 'false',
|
||||
'follow-torrent': 'false',
|
||||
'async-dns': 'false',
|
||||
'max-tries': '3',
|
||||
'retry-wait': '2',
|
||||
'connect-timeout': '20',
|
||||
@@ -767,7 +767,7 @@ async function main() {
|
||||
assert(directHandoff.parent.status === 'complete', `normal magnet parent did not complete metadata: ${JSON.stringify(directHandoff.parent)}`);
|
||||
assert(directHandoff.parent.files?.some(file => String(file.path).startsWith('[METADATA]')), 'normal magnet parent did not expose a metadata file');
|
||||
assert(directOptions['bt-metadata-only'] === 'false', 'normal magnet child did not retain payload mode');
|
||||
assert(directOptions['async-dns'] === 'false', 'direct Torrent did not retain system DNS resolution');
|
||||
assert(directOptions['async-dns'] !== 'false', 'fresh direct Torrent unexpectedly disabled asynchronous DNS');
|
||||
await rpc(client.rpcPort, client.secret, 'aria2.removeDownloadResult', [directGid]);
|
||||
try {
|
||||
await rpc(client.rpcPort, client.secret, 'aria2.removeDownloadResult', [directHandoff.childGid]);
|
||||
@@ -790,7 +790,6 @@ 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',
|
||||
@@ -798,7 +797,7 @@ async function main() {
|
||||
'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');
|
||||
assert(probeOptions['async-dns'] !== 'false', 'fresh direct magnet probe unexpectedly disabled asynchronous DNS');
|
||||
const probeStatus = await waitForTerminal(client, probeGid, 30000);
|
||||
assert(probeStatus.status === 'complete', 'magnet metadata probe did not complete');
|
||||
const savedTorrentPaths = fs.readdirSync(probeDir)
|
||||
@@ -859,7 +858,7 @@ async function main() {
|
||||
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');
|
||||
console.log('[OK] metadata probe was removed after resolution; direct and proxied probes kept asynchronous DNS');
|
||||
|
||||
const finalGid = await rpc(client.rpcPort, client.secret, 'aria2.addTorrent', [
|
||||
trackerlessTorrentBytes.toString('base64'),
|
||||
|
||||
Reference in New Issue
Block a user