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 = 8600; let nextUdpPort = 8601; 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('SOA records should be returned for non-existent domains', 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'); 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) => { client.on('message', (msg) => { const dnsResponse = dnsPacket.decode(msg); resolve(dnsResponse); client.close(); }); client.on('error', (err) => { reject(err); client.close(); }); client.send(query, udpPort, 'localhost', (err) => { if (err) { reject(err); client.close(); } }); }); const dnsResponse = await responsePromise; console.log('✅ SOA response 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 mname:', soaData.mname); console.log('✅ SOA rname:', soaData.rname); expect(soaData.mname).toEqual('ns1.example.com'); expect(soaData.rname).toEqual('hostmaster.example.com'); await stopServer(dnsServer); dnsServer = null; }); tap.test('Primary nameserver should be configurable', 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', primaryNameserver: 'custom-ns.example.com', }); await dnsServer.start(); const client = dgram.createSocket('udp4'); const query = dnsPacket.encode({ type: 'query', id: 2, flags: dnsPacket.RECURSION_DESIRED, questions: [ { name: 'nonexistent.example.com', type: 'A', class: 'IN', }, ], }); const responsePromise = new Promise((resolve, reject) => { client.on('message', (msg) => { const dnsResponse = dnsPacket.decode(msg); resolve(dnsResponse); client.close(); }); client.on('error', (err) => { reject(err); client.close(); }); client.send(query, udpPort, 'localhost', (err) => { if (err) { reject(err); client.close(); } }); }); const dnsResponse = await responsePromise; const soaAnswers = dnsResponse.answers.filter(a => a.type === 'SOA'); expect(soaAnswers.length).toEqual(1); const soaData = (soaAnswers[0] as any).data; console.log('✅ Custom primary nameserver:', soaData.mname); expect(soaData.mname).toEqual('custom-ns.example.com'); await stopServer(dnsServer); dnsServer = null; }); tap.test('Default primary nameserver with FQDN', 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', primaryNameserver: 'ns.example.com.', // FQDN with trailing dot }); await dnsServer.start(); const client = dgram.createSocket('udp4'); const query = dnsPacket.encode({ type: 'query', id: 3, flags: dnsPacket.RECURSION_DESIRED, questions: [ { name: 'nonexistent.example.com', type: 'A', class: 'IN', }, ], }); const responsePromise = new Promise((resolve, reject) => { client.on('message', (msg) => { const dnsResponse = dnsPacket.decode(msg); resolve(dnsResponse); client.close(); }); client.on('error', (err) => { reject(err); client.close(); }); client.send(query, udpPort, 'localhost', (err) => { if (err) { reject(err); client.close(); } }); }); const dnsResponse = await responsePromise; const soaAnswers = dnsResponse.answers.filter(a => a.type === 'SOA'); const soaData = (soaAnswers[0] as any).data; console.log('✅ FQDN primary nameserver:', soaData.mname); expect(soaData.mname).toEqual('ns.example.com.'); await stopServer(dnsServer); dnsServer = null; }); export default tap.start();