import * as plugins from './plugins.js'; interface IDnsServerOptions { httpsKey: string; httpsCert: string; httpsPort: number; udpPort: number; } class DnsServer { private udpServer: plugins.dgram.Socket; private httpsServer: plugins.https.Server; constructor(private options: IDnsServerOptions) { this.udpServer = plugins.dgram.createSocket('udp4'); this.setupUdpServer(); this.httpsServer = plugins.https.createServer( { key: plugins.fs.readFileSync(options.httpsKey), cert: plugins.fs.readFileSync(options.httpsCert) }, this.handleHttpsRequest.bind(this) ); } private setupUdpServer(): void { this.udpServer.on('message', (msg, rinfo) => { const request = plugins.dnsPacket.decode(msg); const response = { type: 'response' as const, id: request.id, flags: plugins.dnsPacket.RECURSION_DESIRED | plugins.dnsPacket.RECURSION_AVAILABLE, questions: request.questions, answers: [] as plugins.dnsPacket.Answer[] }; const question = request.questions[0]; console.log(`UDP query for ${question.name} of type ${question.type}`); if (question.type === 'A') { response.answers.push({ name: question.name, type: 'A', class: 'IN', ttl: 300, data: '127.0.0.1' }); } const responseData = plugins.dnsPacket.encode(response); this.udpServer.send(responseData, rinfo.port, rinfo.address); }); this.udpServer.on('error', (err) => { console.error(`UDP Server error:\n${err.stack}`); this.udpServer.close(); }); this.udpServer.bind(this.options.udpPort, '0.0.0.0', () => { console.log(`UDP DNS server running on port ${this.options.udpPort}`); }); } private handleHttpsRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void { if (req.method === 'POST' && req.url === '/dns-query') { let body: Buffer[] = []; req.on('data', chunk => { body.push(chunk); }).on('end', () => { const msg = Buffer.concat(body); const request = plugins.dnsPacket.decode(msg); const response = { type: 'response' as const, id: request.id, flags: plugins.dnsPacket.RECURSION_DESIRED | plugins.dnsPacket.RECURSION_AVAILABLE, questions: request.questions, answers: [] as plugins.dnsPacket.Answer[] }; const question = request.questions[0]; console.log(`DoH query for ${question.name} of type ${question.type}`); if (question.type === 'A') { response.answers.push({ name: question.name, type: 'A', class: 'IN', ttl: 300, data: '127.0.0.1' }); } const responseData = plugins.dnsPacket.encode(response); res.writeHead(200, { 'Content-Type': 'application/dns-message' }); res.end(responseData); }); } else { res.writeHead(404); res.end(); } } public start(): void { this.httpsServer.listen(this.options.httpsPort, () => { console.log(`DoH server running on port ${this.options.httpsPort}`); }); } public stop(): void { this.udpServer.close(() => { console.log('UDP DNS server stopped'); }); this.httpsServer.close(() => { console.log('HTTPS DNS server stopped'); }); } }