diff --git a/server/xrd_server.js b/server/xrd_server.js index e06d71e..70eb613 100644 --- a/server/xrd_server.js +++ b/server/xrd_server.js @@ -1,6 +1,8 @@ const storage = require('./storage'); const WebSocket = require('ws'); +const crypto = require('crypto'); const xrd = new WebSocket.Server({ noServer: true }); +const { serverConfig } = require('./server_config'); let currentUsers = 0; @@ -14,11 +16,44 @@ function send_xrd_online(fmusers) { send_to_xrd(`o${currentUsers},${fmusers}\n`); } -xrd.on('connection', (ws, request) => { +function randomString(length) { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + for (let i = 0; i < length; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return result; +} + +function xrd_auth(ws, salt) { + return new Promise((resolve, reject) => { + const expected = crypto.createHash('sha1') + .update(salt + serverConfig.password.adminPass).digest('hex'); + + const timeout = setTimeout(() => { + reject(new Error('Auth timeout')); + }, 10_000); + + ws.once('message', (message, isBinary) => { + clearTimeout(timeout); + const received = (isBinary ? message.toString() : message).trim(); + if (received === expected) resolve(); + else reject(new Error('Invalid credentials')); + }); + }); +} + +xrd.on('connection', async (ws, request) => { const { initialData } = require('./datahandler'); - const { isAdminAuthenticated } = request.session || {}; - if(!isAdminAuthenticated) { - ws.close(1008, "No admin"); + + const salt = randomString(16); + ws.send(`${salt}\n`); + + try { + await xrd_auth(ws, salt); + } catch (err) { + ws.send("a0\n"); + ws.close(1008, err.message); return; } @@ -32,8 +67,16 @@ xrd.on('connection', (ws, request) => { ws.send(`W${initialData.bw}\n`); ws.send(`OK\n`); - ws.on('message', (message) => storage.ctl_output.write(message)); - ws.on('close', (code, reason) => { + ws.on('message', (message, isBinary) => { + const data = isBinary ? message : message.toString(); + + if (!storage.ctl_output.write(data)) { + ws.pause(); + storage.ctl_output.once('drain', () => ws.resume()); + } + }); + + ws.on('close', () => { currentUsers--; send_xrd_online(initialData.users); });