diff --git a/src/backend/exports.js b/src/backend/exports.js index 993cec011..72d19dd3e 100644 --- a/src/backend/exports.js +++ b/src/backend/exports.js @@ -42,6 +42,7 @@ const { KVStoreModule } = require("./src/modules/kvstore/KVStoreModule.js"); const { ExternalExtrasModule } = require("./src/modules/external-extras/ExternalExtrasModule.js"); const { FirebaseModule } = require("./src/modules/firebase/FirebaseModule.js"); const { DomainModule } = require("./src/modules/domain/DomainModule.js"); +const { DNSModule } = require("./src/modules/dns/DNSModule.js"); module.exports = { helloworld: () => { @@ -82,6 +83,8 @@ module.exports = { InternetModule, CaptchaModule, KVStoreModule, + FirebaseModule, + DNSModule, // Development modules PerfMonModule, diff --git a/src/backend/package.json b/src/backend/package.json index 07d44101a..e0410d7c5 100644 --- a/src/backend/package.json +++ b/src/backend/package.json @@ -36,6 +36,7 @@ "convertapi": "^1.15.0", "cookie-parser": "^1.4.6", "dedent": "^1.5.3", + "dns2": "^2.1.0", "express": "^4.18.2", "file-type": "^18.5.0", "firebase-admin": "^13.3.0", diff --git a/src/backend/src/modules/dns/DNSModule.js b/src/backend/src/modules/dns/DNSModule.js new file mode 100644 index 000000000..45abb606a --- /dev/null +++ b/src/backend/src/modules/dns/DNSModule.js @@ -0,0 +1,14 @@ +const { AdvancedBase } = require("@heyputer/putility"); + +class DNSModule extends AdvancedBase { + async install (context) { + const services = context.get('services'); + + const { DNSService } = require('./DNSService'); + services.registerService('dns', DNSService); + } +} + +module.exports = { + DNSModule, +}; diff --git a/src/backend/src/modules/dns/DNSService.js b/src/backend/src/modules/dns/DNSService.js new file mode 100644 index 000000000..5a1116d79 --- /dev/null +++ b/src/backend/src/modules/dns/DNSService.js @@ -0,0 +1,99 @@ +const BaseService = require("../../services/BaseService"); +const { sleep } = require("../../util/asyncutil"); + +class DNSService extends BaseService { + async _init () { + const dns2 = require('dns2'); + // this.dns = new dns2(this.config.client); + this.dns = new dns2({ + nameServers: ['127.0.0.1'], + port: 5300, + }); + + if ( this.config.test_server ) { + this.test_server_(); + } + } + + get_client () { + return this.dns; + } + + test_server_ () { + const dns2 = require('dns2'); + const { Packet } = dns2 + + const server = dns2.createServer({ + udp: true, + handle: (request, send, rinfo) => { + const { questions } = request; + const response = Packet.createResponseFromRequest(request); + for (const question of questions) { + if (question.type === Packet.TYPE.A || question.type === Packet.TYPE.ANY) { + response.answers.push({ + name: question.name, + type: Packet.TYPE.A, + class: Packet.CLASS.IN, + ttl: 300, + address: '127.0.0.11', + }); + } + + if (question.type === Packet.TYPE.TXT || question.type === Packet.TYPE.ANY) { + response.answers.push({ + name: question.name, + type: Packet.TYPE.TXT, + class: Packet.CLASS.IN, + ttl: 300, + data: [ + JSON.stringify({ username: 'ed3' }) + ], + }); + } + } + send(response); + } + }); + + server.on('listening', () => { + this.log.info('Fake DNS server listening', server.addresses()); + + if ( this.config.test_server_selftest ) (async () => { + await sleep(5000); + { + console.log('Trying first test') + const result = await this.dns.resolveA('test.local'); + console.log('Test 1', result); + } + { + console.log('Trying second test') + const result = await this.dns.resolve(`_puter-verify.test.local`, 'TXT'); + console.log('Test 2', result); + } + })(); + }); + + server.on('close', () => { + console.log('Fake DNS server closed'); + this.log.noticeme('Fake DNS server closed'); + }) + + server.on('request', (request, response, rinfo) => { + console.log(request.header.id, request.questions[0]); + }); + + server.on('requestError', (error) => { + console.log('Client sent an invalid request', error); + }); + + + server.listen({ + udp: { + port: 5300, + address: "127.0.0.1", + }, + }); + } +} + +module.exports = { DNSService }; diff --git a/src/backend/src/modules/domain/DomainVerificationService.js b/src/backend/src/modules/domain/DomainVerificationService.js index eded1424e..5668480c4 100644 --- a/src/backend/src/modules/domain/DomainVerificationService.js +++ b/src/backend/src/modules/domain/DomainVerificationService.js @@ -2,6 +2,9 @@ const { get_user } = require("../../helpers"); const BaseService = require("../../services/BaseService"); class DomainVerificationService extends BaseService { + _init () { + this._register_commands(); + } async get_controlling_user ({ domain }) { const svc_event = this.services.get('event'); @@ -19,6 +22,20 @@ class DomainVerificationService extends BaseService { // controlling user. return await get_user({ username: 'admin' }); } + + _register_commands (commands) { + const svc_commands = this.services.get('commands'); + svc_commands.registerCommands('domain', [ + { + id: 'user', + description: '', + handler: async (args, log) => { + const res = await this.get_controlling_user({ domain: args[0] }); + log.log(res); + } + } + ]); + } } module.exports = { diff --git a/src/backend/src/modules/domain/TXTVerifyService.js b/src/backend/src/modules/domain/TXTVerifyService.js index 583212fc3..7b535e043 100644 --- a/src/backend/src/modules/domain/TXTVerifyService.js +++ b/src/backend/src/modules/domain/TXTVerifyService.js @@ -1,17 +1,32 @@ const { get_user } = require("../../helpers"); const BaseService = require("../../services/BaseService"); +const { atimeout } = require("../../util/asyncutil"); class TXTVerifyService extends BaseService { - async _init () { - const require = this.require; - const dns = require('dns').promises; + ['__on_boot.consolidation'] () { + const svc_dns = this.services.get('dns'); + const dns = svc_dns.get_client(); const svc_event = this.services.get('event'); svc_event.on('domain.get-controlling-user', async (_, event) => { - let records = await dns.resolveTxt(`_puter-verify.${event.domain}`); - records = records.flat(); - - console.log('got records :: ', records); + const record_name = `_puter-verify.${event.domain}`; + try { + const result = await atimeout( + 5000, + dns.resolve(record_name, 'TXT'), + ); + + const answer = result.answers.filter( + a => a.name === record_name && + a.type === 16 + )[0]; + + const data_raw = answer.data; + const data = JSON.parse(data_raw); + event.user = await get_user({ username: data.username }); + } catch (e) { + console.error('ERROR', e); + } }) } } diff --git a/src/backend/src/util/asyncutil.js b/src/backend/src/util/asyncutil.js new file mode 100644 index 000000000..aabbef177 --- /dev/null +++ b/src/backend/src/util/asyncutil.js @@ -0,0 +1,18 @@ +const sleep = async ms => { + await new Promise(rslv => setTimeout(rslv, ms)); +} + +const atimeout = async (ms, p) => { + return await Promise.race([ + p, + new Promise(async (rslv, rjct) => { + await sleep(ms); + rjct("timeout"); + }), + ]) +}; + +module.exports = { + sleep, + atimeout, +};