UNPKG

@push.rocks/smartdns

Version:

A robust TypeScript library providing advanced DNS management and resolution capabilities including support for DNSSEC, custom DNS servers, and integration with various DNS providers.

404 lines 37 kB
import * as plugins from './plugins.js'; import { RustDnsBridge } from './classes.rustdnsbridge.js'; export class DnsServer extends plugins.events.EventEmitter { constructor(options) { super(); this.options = options; this.handlers = []; // Track initialization state this.bridgeSpawned = false; // Legacy server references (kept for backward-compatible test access) this.httpsServer = null; this.udpServer = null; this.tcpServer = null; this.bridge = new RustDnsBridge(); // Wire up the dnsQuery event to run TypeScript handlers this.bridge.on('dnsQuery', async (event) => { try { const startTime = Date.now(); const answers = this.resolveQuery(event); const responseTimeMs = Date.now() - startTime; this.emit('query', { questions: event.questions, answered: answers.answered, responseTimeMs, timestamp: startTime, }); await this.bridge.sendQueryResult(event.correlationId, answers.answers, answers.answered); } catch (err) { console.error('Error handling DNS query:', err); try { await this.bridge.sendQueryResult(event.correlationId, [], false); } catch (sendErr) { console.error('Error sending empty query result:', sendErr); } } }); } /** * Register a DNS handler for a domain pattern and record types. */ registerHandler(domainPattern, recordTypes, handler) { this.handlers.push({ domainPattern, recordTypes, handler }); } /** * Unregister a specific handler. */ unregisterHandler(domainPattern, recordTypes) { const initialLength = this.handlers.length; const normalizedDomainPattern = domainPattern.toLowerCase(); const normalizedRecordTypes = recordTypes.map(type => type.toUpperCase()); this.handlers = this.handlers.filter(handler => !(handler.domainPattern.toLowerCase() === normalizedDomainPattern && normalizedRecordTypes.every(type => handler.recordTypes.map(recordType => recordType.toUpperCase()).includes(type)))); return this.handlers.length < initialLength; } /** * Start the DNS server. */ async start() { // Validate interface addresses if provided const udpInterface = this.options.udpBindInterface || '0.0.0.0'; const tcpInterface = this.options.tcpBindInterface || udpInterface; const httpsInterface = this.options.httpsBindInterface || '0.0.0.0'; const tcpPort = this.options.tcpPort || this.options.udpPort; if (this.options.udpBindInterface && !this.isValidIpAddress(this.options.udpBindInterface)) { throw new Error(`Invalid UDP bind interface: ${this.options.udpBindInterface}`); } if (this.options.tcpBindInterface && !this.isValidIpAddress(this.options.tcpBindInterface)) { throw new Error(`Invalid TCP bind interface: ${this.options.tcpBindInterface}`); } if (this.options.httpsBindInterface && !this.isValidIpAddress(this.options.httpsBindInterface)) { throw new Error(`Invalid HTTPS bind interface: ${this.options.httpsBindInterface}`); } // Spawn the Rust binary if (!this.bridgeSpawned) { const spawned = await this.bridge.spawn(); if (!spawned) { throw new Error('Failed to spawn rustdns binary'); } this.bridgeSpawned = true; } // Build config for Rust const config = { udpPort: this.options.udpPort, tcpPort, httpsPort: this.options.httpsPort, udpBindInterface: udpInterface, tcpBindInterface: tcpInterface, httpsBindInterface: httpsInterface, httpsKey: this.options.httpsKey || '', httpsCert: this.options.httpsCert || '', dnssecZone: this.options.dnssecZone, dnssecAlgorithm: 'ECDSA', primaryNameserver: this.options.primaryNameserver || `ns1.${this.options.dnssecZone}`, enableLocalhostHandling: this.options.enableLocalhostHandling !== false, manualUdpMode: this.options.manualUdpMode || false, manualTcpMode: this.options.manualTcpMode || false, manualHttpsMode: this.options.manualHttpsMode || false, }; try { await this.bridge.startServer(config); } catch (error) { this.bridge.kill(); this.bridgeSpawned = false; throw error; } // Set legacy markers for backward-compatible test checks this.httpsServer = { _rustBridgeManaged: true }; this.udpServer = { _rustBridgeManaged: true }; this.tcpServer = { _rustBridgeManaged: true }; const udpManual = this.options.manualUdpMode || false; const tcpManual = this.options.manualTcpMode || false; const httpsManual = this.options.manualHttpsMode || false; if (udpManual && tcpManual && httpsManual) { console.log('DNS server started in full manual mode - ready to accept connections'); } else if (udpManual || tcpManual || httpsManual) { const activeTransports = []; const manualTransports = []; udpManual ? manualTransports.push('UDP') : activeTransports.push(`UDP ${udpInterface}:${this.options.udpPort}`); tcpManual ? manualTransports.push('TCP') : activeTransports.push(`TCP ${tcpInterface}:${tcpPort}`); httpsManual ? manualTransports.push('HTTPS') : activeTransports.push(`HTTPS ${httpsInterface}:${this.options.httpsPort}`); console.log(`DNS server started (${activeTransports.join(', ')}${manualTransports.length ? `; manual: ${manualTransports.join(', ')}` : ''})`); } else { console.log(`DNS server started (UDP: ${udpInterface}:${this.options.udpPort}, TCP: ${tcpInterface}:${tcpPort}, HTTPS: ${httpsInterface}:${this.options.httpsPort})`); } } /** * Stop the DNS server. */ async stop() { if (this.bridgeSpawned) { try { await this.bridge.stopServer(); } catch (err) { // Ignore errors during stop (process may have already exited) } this.bridge.kill(); this.bridgeSpawned = false; } this.httpsServer = null; this.udpServer = null; this.tcpServer = null; } /** * Initialize servers (no-op with Rust bridge, kept for API compatibility). */ initializeServers() { // No-op — Rust bridge handles server initialization via start() } /** * Initialize UDP server (no-op with Rust bridge). */ initializeUdpServer() { // No-op } /** * Initialize HTTPS server (no-op with Rust bridge). */ initializeHttpsServer() { // No-op } /** * Handle a raw TCP socket for HTTPS/DoH. * In Rust mode, this is not directly supported — use processRawDnsPacket instead. */ handleHttpsSocket(socket) { console.warn('handleHttpsSocket: direct socket handling not available with Rust bridge. Use processRawDnsPacket instead.'); socket.end(); } /** * Handle a UDP message manually. */ handleUdpMessage(msg, rinfo, responseCallback) { // Async processing via Rust bridge this.processRawDnsPacketAsync(msg) .then((responseData) => { if (responseCallback) { responseCallback(responseData, rinfo); } }) .catch((err) => { console.error('Error processing UDP DNS request:', err); }); } /** * Process a raw DNS packet asynchronously via Rust bridge. */ async processRawDnsPacketAsync(packet) { if (!this.bridgeSpawned) { throw new Error('DNS server not started — call start() first'); } return this.bridge.processPacket(packet); } /** * Retrieve SSL certificate for specified domains using Let's Encrypt */ async retrieveSslCertificate(domainNames, options = {}) { const opts = { email: options.email || 'admin@example.com', staging: options.staging !== undefined ? options.staging : false, certDir: options.certDir || './certs' }; if (!plugins.fs.existsSync(opts.certDir)) { plugins.fs.mkdirSync(opts.certDir, { recursive: true }); } const authorizedDomains = this.filterAuthorizedDomains(domainNames); if (authorizedDomains.length === 0) { console.error('None of the provided domains are authorized for this DNS server'); return { cert: '', key: '', success: false }; } console.log(`Retrieving SSL certificate for domains: ${authorizedDomains.join(', ')}`); try { // @ts-ignore - acmeClientOverride is added for testing purposes const acmeClient = this.acmeClientOverride || await import('acme-client'); const accountKeyPath = plugins.path.join(opts.certDir, 'account.key'); let accountKey; if (plugins.fs.existsSync(accountKeyPath)) { accountKey = plugins.fs.readFileSync(accountKeyPath); } else { const { privateKey } = plugins.crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); accountKey = Buffer.from(privateKey); plugins.fs.writeFileSync(accountKeyPath, accountKey); } const client = new acmeClient.Client({ directoryUrl: opts.staging ? acmeClient.directory.letsencrypt.staging : acmeClient.directory.letsencrypt.production, accountKey: accountKey }); await client.createAccount({ termsOfServiceAgreed: true, contact: [`mailto:${opts.email}`] }); const order = await client.createOrder({ identifiers: authorizedDomains.map(domain => ({ type: 'dns', value: domain })) }); const authorizations = await client.getAuthorizations(order); const challengeHandlers = []; for (const auth of authorizations) { const domain = auth.identifier.value; const challenge = auth.challenges.find((c) => c.type === 'dns-01'); if (!challenge) { throw new Error(`No DNS-01 challenge found for ${domain}`); } const keyAuthorization = await client.getChallengeKeyAuthorization(challenge); const recordValue = this.getDnsRecordValueForChallenge(keyAuthorization); const challengeDomain = `_acme-challenge.${domain}`; console.log(`Setting up TXT record for ${challengeDomain}: ${recordValue}`); this.registerHandler(challengeDomain, ['TXT'], (question) => { if (question.name === challengeDomain && question.type === 'TXT') { return { name: question.name, type: 'TXT', class: 'IN', ttl: 300, data: [recordValue] }; } return null; }); challengeHandlers.push({ domain, pattern: challengeDomain }); await new Promise(resolve => setTimeout(resolve, 2000)); await client.completeChallenge(challenge); await client.waitForValidStatus(challenge); console.log(`Challenge for ${domain} validated successfully!`); } const domainKeyPath = plugins.path.join(opts.certDir, `${authorizedDomains[0]}.key`); const { privateKey } = plugins.crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); plugins.fs.writeFileSync(domainKeyPath, privateKey); const csrResult = await acmeClient.forge.createCsr({ commonName: authorizedDomains[0], altNames: authorizedDomains }); await client.finalizeOrder(order, csrResult.csr); const certificate = await client.getCertificate(order); const certPath = plugins.path.join(opts.certDir, `${authorizedDomains[0]}.cert`); plugins.fs.writeFileSync(certPath, certificate); this.options.httpsCert = certificate; this.options.httpsKey = privateKey; // Update certs on Rust bridge if running if (this.bridgeSpawned) { try { await this.bridge.updateCerts(privateKey, certificate); } catch (err) { console.error('Error updating certs on Rust bridge:', err); } } for (const handler of challengeHandlers) { this.unregisterHandler(handler.pattern, ['TXT']); console.log(`Cleaned up challenge handler for ${handler.domain}`); } return { cert: certificate, key: privateKey, success: true }; } catch (error) { console.error('Error retrieving SSL certificate:', error); return { cert: '', key: '', success: false }; } } /** * Filter domains to include only those the server is authoritative for. */ filterAuthorizedDomains(domainNames) { const authorizedDomains = []; for (const domain of domainNames) { if (domain.startsWith('*.')) { const baseDomain = domain.substring(2); if (this.isAuthorizedForDomain(baseDomain)) { authorizedDomains.push(domain); } } else if (this.isAuthorizedForDomain(domain)) { authorizedDomains.push(domain); } } return authorizedDomains; } // ── Private helpers ─────────────────────────────────────────────── /** * Resolve a DNS query event from Rust using TypeScript handlers. */ resolveQuery(event) { const answers = []; let answered = false; for (const q of event.questions) { const normalizedQuestionName = q.name.toLowerCase(); const normalizedQuestionType = q.type.toUpperCase(); const question = { name: normalizedQuestionName, type: normalizedQuestionType, class: q.class, }; for (const handlerEntry of this.handlers) { const normalizedDomainPattern = handlerEntry.domainPattern.toLowerCase(); const normalizedRecordTypes = handlerEntry.recordTypes.map(recordType => recordType.toUpperCase()); if (plugins.minimatch.minimatch(normalizedQuestionName, normalizedDomainPattern) && normalizedRecordTypes.includes(normalizedQuestionType)) { const answer = handlerEntry.handler(question); if (answer) { answers.push({ name: answer.name, type: answer.type, class: typeof answer.class === 'number' ? 'IN' : (answer.class || 'IN'), ttl: answer.ttl || 300, data: answer.data, }); answered = true; } } } } return { answers, answered }; } getDnsRecordValueForChallenge(keyAuthorization) { const digest = plugins.crypto .createHash('sha256') .update(keyAuthorization) .digest('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); return digest; } isValidIpAddress(ip) { const ipv4Pattern = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; const ipv6Pattern = /^(::1|::)$|^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/; return ipv4Pattern.test(ip) || ipv6Pattern.test(ip); } isAuthorizedForDomain(domain) { const normalizedDomain = domain.toLowerCase(); for (const handler of this.handlers) { if (plugins.minimatch.minimatch(normalizedDomain, handler.domainPattern.toLowerCase())) { return true; } } const normalizedZone = this.options.dnssecZone.toLowerCase(); if (normalizedDomain === normalizedZone || normalizedDomain.endsWith(`.${normalizedZone}`)) { return true; } return false; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kbnNzZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19zZXJ2ZXIvY2xhc3Nlcy5kbnNzZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxFQUFFLGFBQWEsRUFBc0YsTUFBTSw0QkFBNEIsQ0FBQztBQThEL0ksTUFBTSxPQUFPLFNBQVUsU0FBUSxPQUFPLENBQUMsTUFBTSxDQUFDLFlBQVk7SUFZeEQsWUFBb0IsT0FBMEI7UUFDNUMsS0FBSyxFQUFFLENBQUM7UUFEVSxZQUFPLEdBQVAsT0FBTyxDQUFtQjtRQVZ0QyxhQUFRLEdBQWtCLEVBQUUsQ0FBQztRQUVyQyw2QkFBNkI7UUFDckIsa0JBQWEsR0FBWSxLQUFLLENBQUM7UUFFdkMsc0VBQXNFO1FBQzlELGdCQUFXLEdBQVEsSUFBSSxDQUFDO1FBQ3hCLGNBQVMsR0FBUSxJQUFJLENBQUM7UUFDdEIsY0FBUyxHQUFRLElBQUksQ0FBQztRQUk1QixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksYUFBYSxFQUFFLENBQUM7UUFFbEMsd0RBQXdEO1FBQ3hELElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLFVBQVUsRUFBRSxLQUFLLEVBQUUsS0FBcUIsRUFBRSxFQUFFO1lBQ3pELElBQUksQ0FBQztnQkFDSCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7Z0JBQzdCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQ3pDLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxTQUFTLENBQUM7Z0JBQzlDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFO29CQUNqQixTQUFTLEVBQUUsS0FBSyxDQUFDLFNBQVM7b0JBQzFCLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUTtvQkFDMUIs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