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 { execSync } from 'child_process'; import * as smartdns from '../ts_server/index.js'; let dnsServer: smartdns.DnsServer; // Port management for tests const testPort = 8753; // 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('Test SOA timeout with real dig command', async (tools) => { const httpsData = await tapNodeTools.createHttpsCert(); dnsServer = new smartdns.DnsServer({ httpsKey: httpsData.key, httpsCert: httpsData.cert, httpsPort: 8752, udpPort: testPort, dnssecZone: 'example.com', }); await dnsServer.start(); console.log(`DNS server started on port ${testPort}`); // Test with dig command try { console.log('Testing SOA query with dig...'); const result = execSync(`dig @localhost -p ${testPort} example.com SOA +timeout=3`, { encoding: 'utf8' }); console.log('Dig SOA query result:', result); // Check if we got an answer section expect(result).toInclude('ANSWER SECTION'); expect(result).toInclude('SOA'); } catch (error) { console.error('Dig command failed:', error.message); throw error; } // Test nonexistent domain SOA try { console.log('Testing nonexistent domain SOA query with dig...'); const result = execSync(`dig @localhost -p ${testPort} nonexistent.example.com A +timeout=3`, { encoding: 'utf8' }); console.log('Dig nonexistent query result:', result); // Should get AUTHORITY section with SOA expect(result).toInclude('AUTHORITY SECTION'); } catch (error) { console.error('Dig nonexistent query failed:', error.message); throw error; } await stopServer(dnsServer); dnsServer = null; }); tap.test('Test SOA with DNSSEC timing', async () => { const httpsData = await tapNodeTools.createHttpsCert(); const udpPort = 8754; dnsServer = new smartdns.DnsServer({ httpsKey: httpsData.key, httpsCert: httpsData.cert, httpsPort: 8755, udpPort: udpPort, dnssecZone: 'example.com', }); await dnsServer.start(); const client = dgram.createSocket('udp4'); // Test with DNSSEC enabled const query = dnsPacket.encode({ type: 'query', id: 1, flags: dnsPacket.RECURSION_DESIRED, questions: [ { name: 'nonexistent.example.com', type: 'A', class: 'IN', }, ], additionals: [ { name: '.', type: 'OPT', ttl: 0, flags: 0x8000, // DO bit set for DNSSEC data: Buffer.alloc(0), } as any, ], }); const startTime = Date.now(); console.log('Sending DNSSEC query for nonexistent domain...'); const responsePromise = new Promise((resolve, reject) => { const timeout = setTimeout(() => { client.close(); const elapsed = Date.now() - startTime; reject(new Error(`Query timed out after ${elapsed}ms`)); }, 3000); client.on('message', (msg) => { clearTimeout(timeout); const elapsed = Date.now() - startTime; console.log(`Response received in ${elapsed}ms`); try { const dnsResponse = dnsPacket.decode(msg); resolve(dnsResponse); } catch (e) { reject(new Error(`Failed to decode response: ${e.message}`)); } client.close(); }); client.on('error', (err) => { clearTimeout(timeout); const elapsed = Date.now() - startTime; console.error(`Error after ${elapsed}ms:`, err); reject(err); client.close(); }); client.send(query, udpPort, 'localhost', (err) => { if (err) { clearTimeout(timeout); reject(err); client.close(); } }); }); try { const dnsResponse = await responsePromise; console.log('Response details:'); console.log('- Answers:', dnsResponse.answers.length); console.log('- Answer types:', dnsResponse.answers.map(a => a.type)); const soaAnswers = dnsResponse.answers.filter(a => a.type === 'SOA'); const rrsigAnswers = dnsResponse.answers.filter(a => a.type === 'RRSIG'); console.log('- SOA records:', soaAnswers.length); console.log('- RRSIG records:', rrsigAnswers.length); // With the fix, SOA should have its RRSIG if (soaAnswers.length > 0) { expect(rrsigAnswers.length).toBeGreaterThan(0); } } catch (error) { console.error('DNSSEC SOA query failed:', error); throw error; } await stopServer(dnsServer); dnsServer = null; }); tap.test('Check DNSSEC signing performance for SOA', async () => { const httpsData = await tapNodeTools.createHttpsCert(); dnsServer = new smartdns.DnsServer({ httpsKey: httpsData.key, httpsCert: httpsData.cert, httpsPort: 8756, udpPort: 8757, dnssecZone: 'example.com', }); // Time SOA serialization const soaData = { mname: 'ns1.example.com', rname: 'hostmaster.example.com', serial: 2024010101, refresh: 3600, retry: 600, expire: 604800, minimum: 86400, }; console.log('Testing SOA serialization performance...'); const serializeStart = Date.now(); try { // @ts-ignore - accessing private method for testing const serialized = dnsServer.serializeRData('SOA', soaData); const serializeTime = Date.now() - serializeStart; console.log(`SOA serialization took ${serializeTime}ms`); // Test DNSSEC signing const signStart = Date.now(); // @ts-ignore - accessing private property const signature = dnsServer.dnsSec.signData(serialized); const signTime = Date.now() - signStart; console.log(`DNSSEC signing took ${signTime}ms`); expect(serializeTime).toBeLessThan(100); // Should be fast expect(signTime).toBeLessThan(500); // Signing can take longer but shouldn't timeout } catch (error) { console.error('Performance test failed:', error); throw error; } }); export default tap.start();