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

- Keep hostname resolution on the active OS, TUN, proxy, or Aria2 route while blocking literal local targets.\n- Propagate and attest the Firelink Aria2 resolver and network-policy contract across normal, Torrent, and magnet paths.\n- Fence magnet probe cleanup, redirect/range policy, lifecycle races, and engine staging with regression and smoke coverage.
This commit is contained in:
NimBold
2026-08-31 07:36:24 +03:30
parent 725f5e40ec
commit 4777f1e3c3
15 changed files with 745 additions and 92 deletions
+89
View File
@@ -0,0 +1,89 @@
export const ARIA2_FIRELINK_REVISION = 'firelink-native-dns-v1';
export const ARIA2_DNS_RESOLVER = 'native-async';
export const ARIA2_NETWORK_TARGET_POLICY = 'firelink-v1';
export const ARIA2_NETWORK_TARGET_POLICY_DIGEST =
'sha256:064503d30f1a043e79113f7e44ddfb517fbf2c578a332896355180743eaf1705';
export const ARIA2_ROUTE_DAEMON_ARGS = Object.freeze([
'--async-dns=true',
`--dns-resolver=${ARIA2_DNS_RESOLVER}`,
`--network-target-policy=${ARIA2_NETWORK_TARGET_POLICY}`,
]);
export const ARIA2_ROUTE_OPTIONS = Object.freeze({
'async-dns': 'true',
'dns-resolver': ARIA2_DNS_RESOLVER,
'network-target-policy': ARIA2_NETWORK_TARGET_POLICY,
});
// Local HTTP servers are used only by engine smoke tests. Product transfers
// never apply this override; Firelink keeps the literal-target policy enabled.
export const ARIA2_LOCAL_FIXTURE_OPTIONS = Object.freeze({
...ARIA2_ROUTE_OPTIONS,
'network-target-policy': 'none',
});
export function assertAria2RouteCapabilities(version) {
if (!Array.isArray(version?.enabledFeatures)
|| !version.enabledFeatures.includes('Async DNS')) {
throw new Error(`aria2 does not advertise asynchronous DNS: ${JSON.stringify(version)}`);
}
const expected = {
firelinkRevision: ARIA2_FIRELINK_REVISION,
firelinkDnsResolver: ARIA2_DNS_RESOLVER,
firelinkNetworkTargetPolicyDigest: ARIA2_NETWORK_TARGET_POLICY_DIGEST,
};
for (const [field, value] of Object.entries(expected)) {
if (version?.[field] !== value) {
throw new Error(`aria2 route capability mismatch for ${field}: ${JSON.stringify(version)}`);
}
}
if (!Array.isArray(version.firelinkNetworkTargetPolicies)
|| !version.firelinkNetworkTargetPolicies.includes(ARIA2_NETWORK_TARGET_POLICY)) {
throw new Error(`aria2 does not advertise the Firelink target policy: ${JSON.stringify(version)}`);
}
}
export function assertAria2RouteContract(version) {
assertAria2RouteCapabilities(version);
const expected = {
firelinkRevision: ARIA2_FIRELINK_REVISION,
firelinkDnsResolver: ARIA2_DNS_RESOLVER,
firelinkNetworkTargetPolicy: ARIA2_NETWORK_TARGET_POLICY,
firelinkNetworkTargetPolicyDigest: ARIA2_NETWORK_TARGET_POLICY_DIGEST,
};
for (const [field, value] of Object.entries(expected)) {
if (version?.[field] !== value) {
throw new Error(`aria2 route contract mismatch for ${field}: ${JSON.stringify(version)}`);
}
}
if (version.firelinkNetworkTargetPolicyEnforced !== true) {
throw new Error(`aria2 is not enforcing the Firelink network target policy: ${JSON.stringify(version)}`);
}
}
export function assertAria2RouteOptions(options, context = 'aria2 transfer') {
for (const [field, value] of Object.entries(ARIA2_ROUTE_OPTIONS)) {
if (options?.[field] !== value) {
throw new Error(`${context} did not retain ${field}=${value}: ${JSON.stringify(options)}`);
}
}
}
export function assertAria2RouteSource(source, target) {
const contract = source?.firelinkRouteContract;
const expected = {
revision: ARIA2_FIRELINK_REVISION,
dnsResolver: ARIA2_DNS_RESOLVER,
networkTargetPolicy: ARIA2_NETWORK_TARGET_POLICY,
networkTargetPolicyDigest: ARIA2_NETWORK_TARGET_POLICY_DIGEST,
};
for (const [field, value] of Object.entries(expected)) {
if (contract?.[field] !== value) {
throw new Error(
`aria2c source for ${target} is not a Firelink route-contract build; `
+ `expected firelinkRouteContract.${field}=${value}`,
);
}
}
}
+64
View File
@@ -0,0 +1,64 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import {
ARIA2_DNS_RESOLVER,
ARIA2_FIRELINK_REVISION,
ARIA2_NETWORK_TARGET_POLICY,
ARIA2_NETWORK_TARGET_POLICY_DIGEST,
assertAria2RouteCapabilities,
assertAria2RouteContract,
assertAria2RouteSource,
} from './aria2-route-contract.js';
const secureVersion = {
enabledFeatures: ['Async DNS'],
firelinkRevision: ARIA2_FIRELINK_REVISION,
firelinkDnsResolver: ARIA2_DNS_RESOLVER,
firelinkNetworkTargetPolicies: ['none', ARIA2_NETWORK_TARGET_POLICY],
firelinkNetworkTargetPolicy: ARIA2_NETWORK_TARGET_POLICY,
firelinkNetworkTargetPolicyDigest: ARIA2_NETWORK_TARGET_POLICY_DIGEST,
firelinkNetworkTargetPolicyEnforced: true,
};
test('Aria2 source metadata must identify the Firelink route contract', () => {
const source = {
firelinkRouteContract: {
revision: ARIA2_FIRELINK_REVISION,
dnsResolver: ARIA2_DNS_RESOLVER,
networkTargetPolicy: ARIA2_NETWORK_TARGET_POLICY,
networkTargetPolicyDigest: ARIA2_NETWORK_TARGET_POLICY_DIGEST,
},
};
assert.doesNotThrow(() => assertAria2RouteSource(source, 'test-target'));
assert.throws(
() => assertAria2RouteSource({}, 'test-target'),
/not a Firelink route-contract build/,
);
assert.throws(
() => assertAria2RouteSource({
firelinkRouteContract: {
...source.firelinkRouteContract,
networkTargetPolicyDigest: 'sha256:wrong',
},
}, 'test-target'),
/networkTargetPolicyDigest/,
);
});
test('route capabilities are distinct from the active local-fixture policy', () => {
assert.doesNotThrow(() => assertAria2RouteCapabilities({
...secureVersion,
firelinkNetworkTargetPolicy: 'none',
firelinkNetworkTargetPolicyEnforced: false,
}));
assert.doesNotThrow(() => assertAria2RouteContract(secureVersion));
assert.throws(
() => assertAria2RouteContract({
...secureVersion,
firelinkNetworkTargetPolicy: 'none',
firelinkNetworkTargetPolicyEnforced: false,
}),
/route contract mismatch for firelinkNetworkTargetPolicy/,
);
});
+16 -1
View File
@@ -12,6 +12,7 @@ import {
removeOrphanedProvisioningDirectories,
removePathWithRetry,
} from './engine-payload-promotion.js';
import { assertAria2RouteSource } from './aria2-route-contract.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(__dirname, '..');
@@ -39,6 +40,17 @@ if (!targetSources) {
process.exit(1);
}
try {
// Firelink passes route-contract options to every production Aria2 daemon.
// A stock archive would either reject those options or silently omit the
// literal-target policy on non-macOS targets, so fail before downloading or
// staging an unusable payload.
assertAria2RouteSource(targetSources.aria2c, target);
} catch (error) {
console.error(error.message);
process.exit(1);
}
const destination = path.join(repoRoot, 'src-tauri', 'provisioned-engines', target);
const isWindows = target.includes('windows');
const executableSuffix = isWindows ? '.exe' : '';
@@ -134,7 +146,10 @@ function writePayloadManifest() {
{
version: source.version,
url: source.url || source.sourceUrl,
sha256: source.sha256 || source.sourceSha256
sha256: source.sha256 || source.sourceSha256,
...(name === 'aria2c' && source.firelinkRouteContract
? { firelinkRouteContract: source.firelinkRouteContract }
: {})
}
])
),
+19 -17
View File
@@ -8,6 +8,12 @@ import os from 'node:os';
import path from 'node:path';
import { execFileSync, spawn } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import {
ARIA2_ROUTE_DAEMON_ARGS,
ARIA2_ROUTE_OPTIONS,
assertAria2RouteContract,
assertAria2RouteOptions,
} from './aria2-route-contract.js';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const arch = { x64: 'x86_64', arm64: 'aarch64' }[os.arch()];
@@ -195,7 +201,7 @@ const child = spawn(binaryPath, [
`--dir=${tempRoot}`,
'--file-allocation=none',
'--enable-dht=false',
'--async-dns=true',
...ARIA2_ROUTE_DAEMON_ARGS,
'--console-log-level=error',
'--quiet=true',
], { env: environment, stdio: ['ignore', 'ignore', 'pipe'] });
@@ -204,18 +210,17 @@ child.stderr.on('data', chunk => { stderr += chunk.toString(); });
try {
const version = await waitForRpc(rpcPort, secret);
const features = Array.isArray(version.enabledFeatures) ? version.enabledFeatures : [];
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`);
assertAria2RouteContract(version);
console.log(`[INFO] aria2 ${version.version || 'unknown'}; Firelink route contract verified`);
const uriResult = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${contentPort}/file`], {
...ARIA2_ROUTE_OPTIONS,
'network-target-policy': 'none',
out: 'resolver-normal.bin',
}]);
const uriOptions = await rpc(rpcPort, secret, 'aria2.getOption', [uriResult]);
if (uriOptions['async-dns'] === 'false') {
throw new Error(`direct aria2.addUri unexpectedly disabled asynchronous DNS: ${JSON.stringify(uriOptions)}`);
if (uriOptions['async-dns'] !== 'true' || uriOptions['dns-resolver'] !== 'native-async') {
throw new Error(`direct fixture transfer did not retain native asynchronous DNS: ${JSON.stringify(uriOptions)}`);
}
const torrent = bencode({
@@ -227,16 +232,16 @@ try {
},
}).toString('base64');
const torrentResult = await rpc(rpcPort, secret, 'aria2.addTorrent', [torrent, [], {
...ARIA2_ROUTE_OPTIONS,
dir: tempRoot,
}]);
const torrentOptions = await rpc(rpcPort, secret, 'aria2.getOption', [torrentResult]);
if (torrentOptions['async-dns'] === 'false') {
throw new Error(`direct aria2.addTorrent unexpectedly disabled asynchronous DNS: ${JSON.stringify(torrentOptions)}`);
}
assertAria2RouteOptions(torrentOptions, 'direct aria2.addTorrent');
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'], {
...ARIA2_ROUTE_OPTIONS,
'all-proxy': proxyRoute,
pause: 'true',
out: 'resolver-proxied.bin',
@@ -245,11 +250,10 @@ try {
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)}`);
}
assertAria2RouteOptions(proxiedUriOptions, 'proxied aria2.addUri');
const proxiedTorrentResult = await rpc(rpcPort, secret, 'aria2.addTorrent', [torrent, [], {
...ARIA2_ROUTE_OPTIONS,
'all-proxy': proxyRoute,
pause: 'true',
dir: tempRoot,
@@ -258,9 +262,7 @@ try {
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)}`);
}
assertAria2RouteOptions(proxiedTorrentOptions, 'proxied aria2.addTorrent');
await forceRemoveIfPresent(rpcPort, secret, proxiedUriResult);
await forceRemoveIfPresent(rpcPort, secret, proxiedTorrentResult);
+20 -1
View File
@@ -8,6 +8,11 @@ import os from 'node:os';
import path from 'node:path';
import { execFileSync, spawn } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import {
ARIA2_LOCAL_FIXTURE_OPTIONS,
ARIA2_ROUTE_DAEMON_ARGS,
assertAria2RouteContract,
} from './aria2-route-contract.js';
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const arch = { x64: 'x86_64', arm64: 'aarch64' }[os.arch()];
@@ -326,6 +331,7 @@ const child = spawn(binaryPath, [
'--enable-dht=false',
'--console-log-level=error',
'--quiet=true',
...ARIA2_ROUTE_DAEMON_ARGS,
`--server-stat-if=${serverStatPath}`,
`--server-stat-of=${serverStatPath}`,
], { env: environment, stdio: ['ignore', 'ignore', 'pipe'] });
@@ -334,9 +340,11 @@ child.stderr.on('data', chunk => { stderr += chunk.toString(); });
try {
const version = await waitForRpc(rpcPort, secret);
assertAria2RouteContract(version);
console.log(`[INFO] aria2 ${version.version || 'unknown'} normal-transfer smoke`);
const rangeGid = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${fixturePort}/range`], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'range.bin', split: '4', 'max-connection-per-server': '4', 'min-split-size': '1M',
}]);
const rangeStatus = await waitForTerminal(rpcPort, secret, rangeGid);
@@ -345,6 +353,7 @@ try {
}
const noRangeGid = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${fixturePort}/no-range`], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'no-range.bin', split: '1', 'max-connection-per-server': '1',
}]);
if ((await waitForTerminal(rpcPort, secret, noRangeGid)).status !== 'complete') {
@@ -352,6 +361,7 @@ try {
}
const authenticatedGid = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${fixturePort}/authenticated`], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'authenticated.bin', 'http-user': 'fixture-user', 'http-passwd': 'fixture-password',
header: ['Cookie: fixture-cookie=present', 'X-Firelink-Auth: present'],
}]);
@@ -360,6 +370,7 @@ try {
}
const resumeGid = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${fixturePort}/throttled`], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'resume.bin', split: '1', continue: 'true',
}]);
await waitForProgress(rpcPort, secret, resumeGid);
@@ -374,6 +385,7 @@ try {
}
const cancelGid = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${fixturePort}/throttled`], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'cancel.bin', split: '1',
}]);
await waitForProgress(rpcPort, secret, cancelGid);
@@ -386,17 +398,19 @@ try {
const mirrorGid = await rpc(rpcPort, secret, 'aria2.addUri', [[
`http://127.0.0.1:${fixturePort}/missing`,
`http://127.0.0.1:${fixturePort}/range`,
], { out: 'mirror.bin', split: '1', 'max-tries': '1', 'uri-selector': 'adaptive' }]);
], { ...ARIA2_LOCAL_FIXTURE_OPTIONS, out: 'mirror.bin', split: '1', 'max-tries': '1', 'uri-selector': 'adaptive' }]);
const mirrorStatus = await waitForTerminal(rpcPort, secret, mirrorGid);
if (mirrorStatus.status !== 'complete') throw new Error(`adaptive mirror failover failed: ${JSON.stringify(mirrorStatus)}`);
const checksumGid = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${fixturePort}/range`], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'checksum.bin', checksum: `sha-256=${checksum}`, 'check-integrity': 'true',
}]);
if ((await waitForTerminal(rpcPort, secret, checksumGid)).status !== 'complete') {
throw new Error('valid checksum transfer did not complete');
}
const mismatchGid = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${fixturePort}/range`], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'checksum-mismatch.bin', checksum: `sha-256=${'0'.repeat(64)}`, 'check-integrity': 'true',
}]);
const mismatchStatus = await waitForTerminal(rpcPort, secret, mismatchGid);
@@ -423,6 +437,7 @@ try {
if (!redirectLocation) throw new Error('redirect preflight returned no Location header');
const resolvedRedirect = new URL(redirectLocation, redirectProbe.url);
const redirectGid = await rpc(rpcPort, secret, 'aria2.addUri', [[resolvedRedirect.toString()], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'redirect.bin',
}]);
const redirectStatus = await waitForTerminal(rpcPort, secret, redirectGid);
@@ -431,6 +446,7 @@ try {
}
const missingGid = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${fixturePort}/missing`], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'missing.bin', 'max-tries': '1',
}]);
const missingStatus = await waitForTerminal(rpcPort, secret, missingGid);
@@ -439,6 +455,7 @@ try {
}
const lowSpeedGid = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${fixturePort}/slow`], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'low-speed.bin', 'max-tries': '1', 'lowest-speed-limit': '1M', timeout: '20',
}]);
const lowSpeedStatus = await waitForTerminal(rpcPort, secret, lowSpeedGid, 25000);
@@ -447,6 +464,7 @@ try {
}
const malformedGid = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${fixturePort}/malformed`], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'malformed.bin', 'max-tries': '1',
}]);
if ((await waitForTerminal(rpcPort, secret, malformedGid)).status !== 'error') {
@@ -454,6 +472,7 @@ try {
}
const proxyGid = await rpc(rpcPort, secret, 'aria2.addUri', [[`http://127.0.0.1:${fixturePort}/range`], {
...ARIA2_LOCAL_FIXTURE_OPTIONS,
out: 'proxy.bin', 'all-proxy': `http://127.0.0.1:${unavailableProxyPort}`, 'max-tries': '1',
}]);
if ((await waitForTerminal(rpcPort, secret, proxyGid)).status !== 'error') {
+20 -6
View File
@@ -8,6 +8,10 @@ import os from 'node:os';
import path from 'node:path';
import { execFileSync, spawn } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import {
ARIA2_DNS_RESOLVER,
assertAria2RouteCapabilities,
} from './aria2-route-contract.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(__dirname, '..');
@@ -68,6 +72,14 @@ let signalTerminationRequested = false;
class DaemonExitedError extends Error {}
function assertFixtureRouteOptions(options, context) {
if (options['async-dns'] !== 'true'
|| options['dns-resolver'] !== ARIA2_DNS_RESOLVER
|| options['network-target-policy'] !== 'none') {
throw new Error(`${context} did not retain the local-fixture resolver contract: ${JSON.stringify(options)}`);
}
}
function childExited(child) {
return child.exitCode !== null || child.signalCode !== null;
}
@@ -309,6 +321,8 @@ async function startDaemon({ name, rpcPort, listenPort, directory, extraArgs = [
'--enable-peer-exchange=false',
'--bt-enable-lpd=false',
'--async-dns=true',
`--dns-resolver=${ARIA2_DNS_RESOLVER}`,
'--network-target-policy=none',
'--console-log-level=error',
'--quiet=true',
...(selectedListenPort ? [`--listen-port=${selectedListenPort}`] : []),
@@ -330,15 +344,15 @@ async function startDaemon({ name, rpcPort, listenPort, directory, extraArgs = [
};
activeDaemons.add(daemon);
try {
await waitFor(`${name} Aria2 RPC`, async () => {
const version = await waitFor(`${name} Aria2 RPC`, async () => {
if (exit) throw new DaemonExitedError(`${name} exited: ${exit.error?.message || `${exit.code}/${exit.signal}`}`);
try {
await rpc(selectedRpcPort, secret, 'aria2.getVersion');
return true;
return await rpc(selectedRpcPort, secret, 'aria2.getVersion');
} catch {
return false;
}
}, 10000);
assertAria2RouteCapabilities(version);
return daemon;
} catch (error) {
lastError = error;
@@ -770,7 +784,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', 'fresh direct Torrent unexpectedly disabled asynchronous DNS');
assertFixtureRouteOptions(directOptions, 'fresh direct Torrent');
await rpc(client.rpcPort, client.secret, 'aria2.removeDownloadResult', [directGid]);
try {
await rpc(client.rpcPort, client.secret, 'aria2.removeDownloadResult', [directHandoff.childGid]);
@@ -800,7 +814,7 @@ async function main() {
'auto-file-renaming': 'false',
}]);
const probeOptions = await rpc(client.rpcPort, client.secret, 'aria2.getOption', [probeGid]);
assert(probeOptions['async-dns'] !== 'false', 'fresh direct magnet probe unexpectedly disabled asynchronous DNS');
assertFixtureRouteOptions(probeOptions, 'fresh direct magnet probe');
const probeStatus = await waitForTerminal(client, probeGid, 30000);
assert(probeStatus.status === 'complete', 'magnet metadata probe did not complete');
const savedTorrentPaths = fs.readdirSync(probeDir)
@@ -858,7 +872,7 @@ async function main() {
}]);
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');
assertFixtureRouteOptions(proxiedProbeOptions, 'proxied magnet metadata probe');
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 probes kept asynchronous DNS');
+14
View File
@@ -9,6 +9,7 @@ import {
resolveOutputRoot,
resolveTargetTriple,
} from './engine-workspace.js';
import { assertAria2RouteSource } from './aria2-route-contract.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(__dirname, '..');
@@ -45,6 +46,13 @@ if (!source) {
}
if (targetLock) {
try {
assertAria2RouteSource(targetLock.engines?.aria2c, target);
} catch (error) {
console.error(error.message);
process.exit(1);
}
for (const engine of engines) {
const name = `${engine}-${target}${suffix}`;
const expected = targetLock.engines?.[engine]?.sha256;
@@ -78,6 +86,12 @@ if (targetLock) {
console.error(`Payload manifest target mismatch: ${manifest.target}`);
process.exit(1);
}
try {
assertAria2RouteSource(manifest.generatedFrom?.aria2c, target);
} catch (error) {
console.error(error.message);
process.exit(1);
}
for (const [relative, expected] of Object.entries(manifest.files || {})) {
const file = path.join(source, relative);
if (!fs.existsSync(file) || sha256(file) !== expected) {
+6
View File
@@ -9,6 +9,10 @@ import {
resolveOutputRoot,
resolveTargetTriple,
} from './engine-workspace.js';
import {
ARIA2_ROUTE_DAEMON_ARGS,
assertAria2RouteContract,
} from './aria2-route-contract.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -469,6 +473,7 @@ if (canExecuteTarget) {
'--quiet',
'--console-log-level=error',
'--rpc-listen-all=false',
...ARIA2_ROUTE_DAEMON_ARGS,
], {
env: engineEnv('aria2c'),
stdio: ['ignore', 'ignore', 'pipe'],
@@ -563,6 +568,7 @@ if (canExecuteTarget) {
try {
const resp = JSON.parse(result.data);
if (resp?.result?.version) {
assertAria2RouteContract(resp.result);
ok(`aria2 RPC version: ${resp.result.version}`);
} else {
fail(`aria2 RPC unexpected response: ${result.data}`);