import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import test from 'node:test'; import { buildDnsQuery, createDnsDiagnosticsService, parseDnsResponse, } from '../../dist/server/services/dnsDiagnosticsService.js'; function wireResponse(query, { rcode = 0, truncated = false, address = null } = {}) { const header = Buffer.alloc(12); query.copy(header, 0, 0, 2); header.writeUInt16BE(0x8180 | rcode | (truncated ? 0x0200 : 0), 2); header.writeUInt16BE(1, 4); header.writeUInt16BE(address ? 1 : 0, 6); const question = query.subarray(12); if (!address) return Buffer.concat([header, question]); const type = query.readUInt16BE(query.length - 4); const data = type === 1 ? Buffer.from(address.split('.').map(Number)) : Buffer.from(address.split(':').flatMap((part) => { const value = Number.parseInt(part || '0', 16); return [value >> 8, value & 0xff]; })); const answer = Buffer.alloc(12); answer.writeUInt16BE(0xc00c, 0); answer.writeUInt16BE(type, 2); answer.writeUInt16BE(1, 4); answer.writeUInt32BE(60, 6); answer.writeUInt16BE(data.length, 10); return Buffer.concat([header, question, answer, data]); } function addressFor(packet, path = 'direct') { return packet.readUInt16BE(packet.length - 4) === 1 ? path === 'direct' ? '8.8.8.8' : '9.9.9.9' : path === 'direct' ? '2001:db8:0:0:0:0:0:1' : '2001:db8:0:0:0:0:0:2'; } test('DNS wire codec parses compressed A/AAAA and rejects malformed packets', () => { const aQuery = buildDnsQuery('www.youtube.com', 'A', 17); const aaaaQuery = buildDnsQuery('www.youtube.com', 'AAAA', 18); assert.deepEqual(parseDnsResponse(wireResponse(aQuery, { address: '8.8.8.8' }), 17), { rcode: 'NOERROR', truncated: false, ipv4: ['8.8.8.8'], ipv6: [], }); assert.deepEqual(parseDnsResponse(wireResponse(aaaaQuery, { address: '2001:db8:0:0:0:0:0:1', }), 18).ipv6, ['2001:db8:0:0:0:0:0:1']); assert.equal(parseDnsResponse(wireResponse(aQuery, { rcode: 3 }), 17).rcode, 'NXDOMAIN'); assert.equal(parseDnsResponse(wireResponse(aQuery, { rcode: 2 }), 17).rcode, 'SERVFAIL'); assert.throws(() => parseDnsResponse(Buffer.from([0, 1]), 1), /malformed/); assert.throws(() => parseDnsResponse(wireResponse(aQuery), 99), /unexpected/); }); test('DNS checker keeps Direct and VPN independent and never probes VPN when it is off', async () => { const calls = []; const classicExchange = async ({ path: route, resolver, packet }) => { calls.push([route, resolver.endpoint, packet.readUInt16BE(packet.length - 4)]); return { response: wireResponse(packet, { address: addressFor(packet, route) }), transport: 'udp', latencyMs: route === 'direct' ? 10 : 20, }; }; const service = createDnsDiagnosticsService({ proxyPort: 18080, getServers: () => [], classicExchange, now: () => '2026-09-01T00:00:00.000Z', }); const running = await service.run({ vpnAvailable: true, domainId: 'youtube', resolverId: 'google-dns' }); assert.deepEqual(calls.map(([route]) => route).sort(), ['direct', 'direct', 'vpn', 'vpn']); assert.equal(running.results[0].comparison, 'different'); assert.deepEqual(running.results[0].direct.ipv4, ['8.8.8.8']); assert.deepEqual(running.results[0].vpn.ipv6, ['2001:db8:0:0:0:0:0:2']); calls.length = 0; const stopped = await service.run({ vpnAvailable: false, domainId: 'youtube', resolverId: 'google-dns' }); assert.deepEqual(calls.map(([route]) => route), ['direct', 'direct']); assert.equal(stopped.results[0].vpn.status, 'vpn-off'); }); test('DNS checker retries timeout once and uses TCP fallback on a truncated response', async () => { let attempts = 0; const timedOut = createDnsDiagnosticsService({ proxyPort: 18080, getServers: () => [], classicExchange: async () => { attempts += 1; const error = new Error('timeout'); error.code = 'ETIMEDOUT'; throw error; }, }); const timeoutResult = await timedOut.run({ vpnAvailable: false, domainId: 'youtube', resolverId: 'google-dns', }); assert.equal(attempts, 4); assert.equal(timeoutResult.results[0].direct.status, 'timeout'); const fallbackCalls = []; const fallback = createDnsDiagnosticsService({ proxyPort: 18080, getServers: () => [], classicExchange: async ({ packet }) => ({ response: wireResponse(packet, { truncated: true }), transport: 'udp', latencyMs: 2, }), tcpExchange: async ({ path: route, packet }) => { fallbackCalls.push(route); return { response: wireResponse(packet, { address: addressFor(packet, route) }), transport: 'tcp', latencyMs: 4, }; }, }); const fallbackResult = await fallback.run({ vpnAvailable: true, domainId: 'youtube', resolverId: 'google-dns', }); assert.deepEqual(fallbackCalls.sort(), ['direct', 'direct', 'vpn', 'vpn']); assert.equal(fallbackResult.results[0].direct.transport, 'tcp'); assert.equal(fallbackResult.results[0].vpn.transport, 'tcp'); }); test('custom DoH is pinned only to public bootstrap addresses', async () => { const resolver = { id: 'custom-dns-office', label: 'Office DoH', kind: 'doh', endpoint: 'https://dns.example/dns-query', }; const calls = []; const service = createDnsDiagnosticsService({ proxyPort: 18080, getServers: () => [], lookup: async () => [{ address: '93.184.216.34', family: 4 }], dohExchange: async ({ path: route, resolver: pinned, packet }) => { calls.push([route, pinned.bootstrap]); return { response: wireResponse(packet, { address: addressFor(packet, route) }), transport: 'doh', latencyMs: 7, }; }, }); const result = await service.run({ vpnAvailable: true, customResolvers: [resolver], domainId: 'chatgpt', resolverId: resolver.id, }); assert.deepEqual(calls, [ ['direct', '93.184.216.34'], ['direct', '93.184.216.34'], ['vpn', '93.184.216.34'], ['vpn', '93.184.216.34'], ]); assert.equal(result.results[0].direct.transport, 'doh'); let requests = 0; const blocked = createDnsDiagnosticsService({ proxyPort: 18080, getServers: () => [], lookup: async () => [{ address: '127.0.0.1', family: 4 }], dohExchange: async () => { requests += 1; throw new Error('unexpected'); }, }); const blockedResult = await blocked.run({ vpnAvailable: true, customResolvers: [resolver], domainId: 'chatgpt', resolverId: resolver.id, }); assert.equal(requests, 0); assert.equal(blockedResult.results[0].direct.error, 'DoH endpoint is not public'); }); test('full DNS run includes system and built-in rows with at most three resolver workers', async () => { const active = new Map(); let maximumResolvers = 0; const exchange = async ({ path: route, resolver, packet }) => { active.set(resolver.id, (active.get(resolver.id) || 0) + 1); maximumResolvers = Math.max(maximumResolvers, active.size); await new Promise((resolve) => setTimeout(resolve, 2)); const count = active.get(resolver.id) - 1; if (count) active.set(resolver.id, count); else active.delete(resolver.id); return { response: wireResponse(packet, { address: addressFor(packet, route) }), transport: resolver.kind === 'doh' ? 'doh' : 'udp', latencyMs: 2, }; }; const service = createDnsDiagnosticsService({ proxyPort: 18080, getServers: () => ['192.168.1.1'], classicExchange: exchange, dohExchange: exchange, }); const catalog = await service.catalog(); assert.equal(catalog.resolvers[0].system, true); assert.equal(catalog.resolvers.length, 7); assert.deepEqual(catalog.domains.map(({ hostname }) => hostname), ['www.youtube.com', 'chatgpt.com']); const result = await service.run({ vpnAvailable: false, domainId: 'youtube' }); assert.equal(result.results.length, 7); assert.ok(maximumResolvers <= 3); }); test('default transports enforce direct bypass, VPN proxy, binary DoH and no redirects', () => { const source = fs.readFileSync( path.resolve(import.meta.dirname, '../../src/server/services/dnsDiagnosticsService.ts'), 'utf8', ); assert.match(source, /path === 'direct'[\s\S]*udpExchange\(address, port/); assert.match(source, /socksHandshake\(3,[\s\S]*socksHandshake\(1,/); assert.match(source, /'--noproxy', '\*'/); assert.match(source, /'--proxy', `http:\/\/127\.0\.0\.1:\$\{proxyPort\}`/); assert.match(source, /'--data-binary', '@-'/); assert.match(source, /'--resolve'/); assert.doesNotMatch(source, /--location/); });