import * as plugins from '../ts_server/plugins.js'; import { expect, tap } from '@git.zone/tstest/tapbundle'; import { tapNodeTools } from '@git.zone/tstest/tapbundle_node'; import * as dnsPacket from 'dns-packet'; import * as dgram from 'dgram'; import * as smartdns from '../ts_server/index.js'; let dnsServer: smartdns.DnsServer; // Port management for tests let nextHttpsPort = 8800; let nextUdpPort = 8801; function getUniqueHttpsPort() { return nextHttpsPort++; } function getUniqueUdpPort() { return nextUdpPort++; } // Cleanup function for servers async function stopServer(server: smartdns.DnsServer | null | undefined) { if (!server) { return; } try { await server.stop(); } catch (e) { console.log('Handled error when stopping server:', e.message || e); } } tap.test('Simple SOA query without DNSSEC', async () => { const httpsData = await tapNodeTools.createHttpsCert(); const udpPort = getUniqueUdpPort(); dnsServer = new smartdns.DnsServer({ httpsKey: httpsData.key, httpsCert: httpsData.cert, httpsPort: getUniqueHttpsPort(), udpPort: udpPort, dnssecZone: 'example.com', }); await dnsServer.start(); const client = dgram.createSocket('udp4'); // Query for a non-existent domain WITHOUT DNSSEC const query = dnsPacket.encode({ type: 'query', id: 1, flags: dnsPacket.RECURSION_DESIRED, questions: [ { name: 'nonexistent.example.com', type: 'A', class: 'IN', }, ], }); const responsePromise = new Promise((resolve, reject) => { const timeout = setTimeout(() => { client.close(); reject(new Error('Query timed out')); }, 2000); client.on('message', (msg) => { clearTimeout(timeout); try { const dnsResponse = dnsPacket.decode(msg); resolve(dnsResponse); } catch (e) { reject(e); } client.close(); }); client.on('error', (err) => { clearTimeout(timeout); reject(err); client.close(); }); client.send(query, udpPort, 'localhost', (err) => { if (err) { clearTimeout(timeout); reject(err); client.close(); } }); }); const dnsResponse = await responsePromise; console.log('✅ SOA response without DNSSEC received'); const soaAnswers = dnsResponse.answers.filter(a => a.type === 'SOA'); expect(soaAnswers.length).toEqual(1); const soaData = (soaAnswers[0] as any).data; console.log('✅ SOA data:', soaData.mname); await stopServer(dnsServer); dnsServer = null; }); tap.test('Direct SOA query without DNSSEC', async () => { const httpsData = await tapNodeTools.createHttpsCert(); const udpPort = getUniqueUdpPort(); dnsServer = new smartdns.DnsServer({ httpsKey: httpsData.key, httpsCert: httpsData.cert, httpsPort: getUniqueHttpsPort(), udpPort: udpPort, dnssecZone: 'example.com', }); // Register direct SOA handler dnsServer.registerHandler('example.com', ['SOA'], (question) => { return { name: question.name, type: 'SOA', class: 'IN', ttl: 3600, data: { mname: 'ns1.example.com', rname: 'hostmaster.example.com', serial: 2024010101, refresh: 3600, retry: 600, expire: 604800, minimum: 86400, }, }; }); await dnsServer.start(); const client = dgram.createSocket('udp4'); const query = dnsPacket.encode({ type: 'query', id: 2, flags: dnsPacket.RECURSION_DESIRED, questions: [ { name: 'example.com', type: 'SOA', class: 'IN', }, ], }); const responsePromise = new Promise((resolve, reject) => { const timeout = setTimeout(() => { client.close(); reject(new Error('Query timed out')); }, 2000); client.on('message', (msg) => { clearTimeout(timeout); try { const dnsResponse = dnsPacket.decode(msg); resolve(dnsResponse); } catch (e) { reject(e); } client.close(); }); client.on('error', (err) => { clearTimeout(timeout); reject(err); client.close(); }); client.send(query, udpPort, 'localhost', (err) => { if (err) { clearTimeout(timeout); reject(err); client.close(); } }); }); const dnsResponse = await responsePromise; console.log('✅ Direct SOA query succeeded'); const soaAnswers = dnsResponse.answers.filter(a => a.type === 'SOA'); expect(soaAnswers.length).toEqual(1); await stopServer(dnsServer); dnsServer = null; }); export default tap.start();