diff options
Diffstat (limited to 'ercp.js')
-rw-r--r-- | ercp.js | 775 |
1 files changed, 422 insertions, 353 deletions
@@ -1,10 +1,13 @@ const { WebSocketServer } = require('ws'); -const { desktopCapturer, screen } = require('electron'); +const { desktopCapturer, screen, dialog } = require('electron'); const { mouse, keyboard, Point, Key } = require("@nut-tree/nut-js"); +const axios = require('axios'); const wss = new WebSocketServer({ host: "127.0.0.1", port: 38071 }); const clients = []; +const blocked = []; + keyboard.config.autoDelayMs = 0; mouse.config.autoDelayMs = 0; @@ -12,432 +15,498 @@ wss.on('connection', function connection(ws) { ws._setting_latency = 1000; ws._setting_quality = 50; ws._setting_minimalLatency = 1000; + ws._setting_height = 480; ws.screenWidth = 0; ws.screenHeight = 0; - - let currentScreen = 0; - clients.push(ws); - - ws.send(JSON.stringify({ - type: "command_stdout", - value: Buffer.from("Luna version " + luna_version + "\n * Interpreter: " + require('os').userInfo().shell + "\n * Working directory: " + require('os').userInfo().homedir + "\nYou may now start entering commands.\n").toString("base64") - })); - - ws.workingDirectory = require('os').userInfo().homedir; - - async function sendFrame() { - if (clients.length === 0) return; - - let captureStart = new Date(); - let displays = screen.getAllDisplays(); - let sources = await desktopCapturer.getSources({ types: ['screen'], thumbnailSize: { width: 1220, height: 720 } }); - if (sources.length <= currentScreen) currentScreen = 0; - - ws.screenWidth = displays[currentScreen].size.width; - ws.screenHeight = displays[currentScreen].size.height; - let b64 = sources[currentScreen].thumbnail.toJPEG(ws._setting_quality).toString("base64"); - let captureEnd = new Date(); - let captureTime = captureEnd.getTime() - captureStart.getTime(); - - console.log("Took " + captureTime + "ms to capture this frame"); - ws._setting_minimalLatency = captureTime * 5; - - let m = JSON.stringify({ - type: "image", - time: new Date().getTime(), - url: "data:image/jpeg;base64," + b64 - }); - - console.log("Sent frame, packet is " + m.length + " characters"); - ws.send(m); - } - - function setupClient(ws, rate) { - if (ws._interval) clearInterval(ws._interval); - ws._interval = setInterval(sendFrame, rate); - } + ws.initiated = false; ws.on('message', async (_data) => { - try { - let data = JSON.parse(_data); + if (ws.initiated) return; + ws.initiated = true; - if (data.type === "set_latency") { - if (data.value > ws._setting_minimalLatency) data.value = ws._setting_minimalLatency; + let data = JSON.parse(_data); - console.log("Latency is now " + data.value + "ms"); - ws._setting_latency = data.value; + if (data._init) { + let address = data._init[0]; + let name = data._init[1]; - setupClient(ws, ws._setting_latency, ws._setting_quality); - } else if (data.type === "increase_quality") { - if (ws._setting_quality < 90) { - ws._setting_quality += 5; - console.log("Quality is now " + ws._setting_quality + "%"); + if (blocked.includes(address)) { + ws.send(JSON.stringify({ + rejected: true + })) + ws.close(); + return; + } - setupClient(ws, ws._setting_latency, ws._setting_quality); + let ip = (await axios.get("https://api.iplocation.net/?ip=" + address)).data; + + dialog.showMessageBox({ + type: "question", + buttons: [ + "Accept", + "Reject" + ], + title: "Incoming connection", + message: "Do you want to allow " + name + " to see and control your screen?", + detail: "This will allow " + name + " (from " + ip['country_name'] + ", " + address + ") to see all content on your screen, including personal information, and fully control your computer, including running commands in the background. Make sure you trust them before allowing them to connect.", + cancelId: 1, + checkboxLabel: "Block until computer is restarted" + }).then((obj) => { + let allow = false; + if (obj.response === 0 && !obj.checkboxChecked) allow = true; + + if (!allow) { + ws.send(JSON.stringify({ + rejected: true + })) + ws.close(); + return; } - } else if (data.type === "reduce_quality") { - if (ws._setting_quality > 10) { - ws._setting_quality -= 5; - console.log("Quality is now " + ws._setting_quality + "%"); - setupClient(ws, ws._setting_latency, ws._setting_quality); + if (obj.checkboxChecked) { + blocked.push(address); } - } else if (data.type === "move_cursor") { - console.log("Moving cursor to X" + (data.x * ws.screenWidth) + ", Y" + (data.y * ws.screenHeight)); - await mouse.setPosition(new Point(data.x * ws.screenWidth, data.y * ws.screenHeight)) - } else if (data.type === "click") { - console.log("Clicking button " + data.button); - - switch (data.button) { - case 0: - await mouse.click(0); - sendFrame(); - break; - - case 1: - await mouse.click(2); - sendFrame(); - break; - - case 2: - await mouse.click(1); - sendFrame(); - break; - } - } else if (data.type === "start_drag") { - console.log("Dragging started"); - - await mouse.pressButton(0); - } else if (data.type === "stop_drag") { - console.log("Dragging stopped"); - await mouse.releaseButton(0); - } else if (data.type === "keyboard") { - if (data.key.length === 1) { - let keys = [ data.key ]; + setup(); + }) + } + }); - if (data.ctrl) keys.unshift(Key.LeftControl); - if (data.alt) keys.unshift(Key.LeftAlt); - if (data.meta) keys.unshift(Key.LeftSuper); + function setup() { + let currentScreen = 0; + clients.push(ws); - if (data.shift) { - keys[keys.length - 1] = data.key.toUpperCase(); - } + ws.send(JSON.stringify({ + type: "command_stdout", + value: Buffer.from("Luna version " + luna_version + "\n * Interpreter: " + require('os').userInfo().shell + "\n * Working directory: " + require('os').userInfo().homedir + "\nYou may now start entering commands.\n").toString("base64") + })); - await keyboard.type(...keys); - } else { - let keys = []; + ws.workingDirectory = require('os').userInfo().homedir; - if (data.ctrl) keys.unshift(Key.LeftControl); - if (data.alt) keys.unshift(Key.LeftAlt); - if (data.meta) keys.unshift(Key.LeftSuper); - if (data.shift) keys.unshift(Key.LeftShift); + async function sendFrame() { + if (clients.length === 0) return; - switch (data.key) { - case "Enter": - keys.push(Key.Enter); - await keyboard.type(...keys); - break; + let captureStart = new Date(); + let displays = screen.getAllDisplays(); + let sources = await desktopCapturer.getSources({ types: ['screen'], thumbnailSize: { width: Math.round(ws._setting_height * (16/9)), height: ws._setting_height } }); + if (sources.length <= currentScreen) currentScreen = 0; - case "Tab": - keys.push(Key.Tab); - await keyboard.type(...keys); - break; + ws.screenWidth = displays[currentScreen].size.width; + ws.screenHeight = displays[currentScreen].size.height; + let b64 = sources[currentScreen].thumbnail.toJPEG(ws._setting_quality).toString("base64"); + let captureEnd = new Date(); + let captureTime = captureEnd.getTime() - captureStart.getTime(); - case "Delete": - keys.push(Key.Delete); - await keyboard.type(...keys); - break; + //console.log("Took " + captureTime + "ms to capture this frame"); + ws._setting_minimalLatency = captureTime * 5; - case "Insert": - keys.push(Key.Insert); - await keyboard.type(...keys); - break; + let m = JSON.stringify({ + type: "image", + time: new Date().getTime(), + url: "data:image/jpeg;base64," + b64 + }); - case "Pause": - keys.push(Key.Pause); - await keyboard.type(...keys); - break; + //console.log("Sent frame, packet is " + m.length + " characters"); + ws.send(m); + } - case "PrintScreen": - keys.push(Key.Print); - await keyboard.type(...keys); - break; + function setupClient(ws, rate) { + if (ws._interval) clearInterval(ws._interval); + ws._interval = setInterval(sendFrame, rate); + } - case "NumLock": - keys.push(Key.NumLock); - await keyboard.type(...keys); - break; + ws.on('message', async (_data) => { + try { + let data = JSON.parse(_data); - case "CapsLock": - keys.push(Key.CapsLock); - await keyboard.type(...keys); - break; + if (data.type === "set_latency") { + if (data.value > ws._setting_minimalLatency) data.value = ws._setting_minimalLatency; - case "ArrowLeft": - keys.push(Key.Left); - await keyboard.type(...keys); - break; - - case "ArrowRight": - keys.push(Key.Right); - await keyboard.type(...keys); - break; + //console.log("Latency is now " + data.value + "ms"); + ws._setting_latency = data.value; - case "ArrowUp": - keys.push(Key.Up); - await keyboard.type(...keys); - break; - - case "ArrowDown": - keys.push(Key.Down); - await keyboard.type(...keys); - break; - - case "Home": - keys.push(Key.Home); - await keyboard.type(...keys); - break; + setupClient(ws, ws._setting_latency, ws._setting_quality); + } else if (data.type === "increase_quality") { + if (ws._setting_quality < 90) { + ws._setting_quality += 5; + //console.log("Quality is now " + ws._setting_quality + "%"); - case "End": - keys.push(Key.End); - await keyboard.type(...keys); - break; + if (ws._setting_height < 720) { + ws._setting_height += 5; + } - case "PageUp": - keys.push(Key.PageUp); - await keyboard.type(...keys); - break; + setupClient(ws, ws._setting_latency, ws._setting_quality); + } + } else if (data.type === "reduce_quality") { + if (ws._setting_quality > 10) { + ws._setting_quality -= 5; + //console.log("Quality is now " + ws._setting_quality + "%"); - case "PageDown": - keys.push(Key.PageDown); - await keyboard.type(...keys); - break; + if (ws._setting_height > 480) { + ws._setting_height -= 5; + } - case "Escape": - keys.push(Key.Escape); - await keyboard.type(...keys); - break; + setupClient(ws, ws._setting_latency, ws._setting_quality); + } + } else if (data.type === "move_cursor") { + //console.log("Moving cursor to X" + (data.x * ws.screenWidth) + ", Y" + (data.y * ws.screenHeight)); + await mouse.setPosition(new Point(data.x * ws.screenWidth, data.y * ws.screenHeight)) + } else if (data.type === "click") { + //console.log("Clicking button " + data.button); - case "F1": - keys.push(Key.F1); - await keyboard.type(...keys); + switch (data.button) { + case 0: + await mouse.click(0); + sendFrame(); break; - case "F2": - keys.push(Key.F2); - await keyboard.type(...keys); + case 1: + await mouse.click(2); + sendFrame(); break; - case "F3": - keys.push(Key.F3); - await keyboard.type(...keys); + case 2: + await mouse.click(1); + sendFrame(); break; + } + } else if (data.type === "start_drag") { + //console.log("Dragging started"); - case "F4": - keys.push(Key.F4); - await keyboard.type(...keys); - break; + await mouse.pressButton(0); + } else if (data.type === "stop_drag") { + //console.log("Dragging stopped"); - case "F5": - keys.push(Key.F5); - await keyboard.type(...keys); - break; + await mouse.releaseButton(0); + } else if (data.type === "keyboard") { + if (data.key.length === 1) { + let keys = [ data.key ]; - case "F6": - keys.push(Key.F6); - await keyboard.type(...keys); - break; + if (data.ctrl) keys.unshift(Key.LeftControl); + if (data.alt) keys.unshift(Key.LeftAlt); + if (data.meta) keys.unshift(Key.LeftSuper); - case "F7": - keys.push(Key.F7); - await keyboard.type(...keys); - break; + if (data.shift) { + keys[keys.length - 1] = data.key.toUpperCase(); + } - case "F8": - keys.push(Key.F8); - await keyboard.type(...keys); - break; + await keyboard.type(...keys); + } else { + let keys = []; + + if (data.ctrl) keys.unshift(Key.LeftControl); + if (data.alt) keys.unshift(Key.LeftAlt); + if (data.meta) keys.unshift(Key.LeftSuper); + if (data.shift) keys.unshift(Key.LeftShift); + + switch (data.key) { + case "Enter": + keys.push(Key.Enter); + await keyboard.type(...keys); + break; + + case "Tab": + keys.push(Key.Tab); + await keyboard.type(...keys); + break; + + case "Delete": + keys.push(Key.Delete); + await keyboard.type(...keys); + break; + + case "Insert": + keys.push(Key.Insert); + await keyboard.type(...keys); + break; + + case "Pause": + keys.push(Key.Pause); + await keyboard.type(...keys); + break; + + case "PrintScreen": + keys.push(Key.Print); + await keyboard.type(...keys); + break; + + case "NumLock": + keys.push(Key.NumLock); + await keyboard.type(...keys); + break; + + case "CapsLock": + keys.push(Key.CapsLock); + await keyboard.type(...keys); + break; + + case "ArrowLeft": + keys.push(Key.Left); + await keyboard.type(...keys); + break; + + case "ArrowRight": + keys.push(Key.Right); + await keyboard.type(...keys); + break; + + case "ArrowUp": + keys.push(Key.Up); + await keyboard.type(...keys); + break; + + case "ArrowDown": + keys.push(Key.Down); + await keyboard.type(...keys); + break; + + case "Home": + keys.push(Key.Home); + await keyboard.type(...keys); + break; + + case "End": + keys.push(Key.End); + await keyboard.type(...keys); + break; + + case "PageUp": + keys.push(Key.PageUp); + await keyboard.type(...keys); + break; + + case "PageDown": + keys.push(Key.PageDown); + await keyboard.type(...keys); + break; + + case "Escape": + keys.push(Key.Escape); + await keyboard.type(...keys); + break; + + case "F1": + keys.push(Key.F1); + await keyboard.type(...keys); + break; + + case "F2": + keys.push(Key.F2); + await keyboard.type(...keys); + break; + + case "F3": + keys.push(Key.F3); + await keyboard.type(...keys); + break; + + case "F4": + keys.push(Key.F4); + await keyboard.type(...keys); + break; + + case "F5": + keys.push(Key.F5); + await keyboard.type(...keys); + break; + + case "F6": + keys.push(Key.F6); + await keyboard.type(...keys); + break; + + case "F7": + keys.push(Key.F7); + await keyboard.type(...keys); + break; + + case "F8": + keys.push(Key.F8); + await keyboard.type(...keys); + break; + + case "F9": + keys.push(Key.F9); + await keyboard.type(...keys); + break; + + case "F10": + keys.push(Key.F10); + await keyboard.type(...keys); + break; + + case "F11": + keys.push(Key.F11); + await keyboard.type(...keys); + break; + + case "F12": + keys.push(Key.F12); + await keyboard.type(...keys); + break; + + case "OS": + keys.push(Key.LeftSuper); + await keyboard.type(...keys); + break; + + case "Backspace": + keys.push(Key.Backspace); + await keyboard.type(...keys); + break; + + default: + //console.log("Missing corresponding action for key " + data.key); + break; + } + } + } else if (data.type === "command") { + ws.send(JSON.stringify({ + type: "command_start" + })); - case "F9": - keys.push(Key.F9); - await keyboard.type(...keys); - break; + if (Buffer.from(data.value, "base64").toString().startsWith("cd ") || Buffer.from(data.value, "base64").toString().startsWith("chdir ")) { + try { + let path; - case "F10": - keys.push(Key.F10); - await keyboard.type(...keys); - break; + if (Buffer.from(data.value, "base64").toString().startsWith("cd ")) { + path = require('path').resolve(ws.workingDirectory + "/" + Buffer.from(data.value, "base64").toString().substring(3)); + } else { + path = require('path').resolve(ws.workingDirectory + "/" + Buffer.from(data.value, "base64").toString().substring(6)); + } - case "F11": - keys.push(Key.F11); - await keyboard.type(...keys); - break; + if (!require('fs').existsSync(path)) { + ws.send(JSON.stringify({ + type: "command_stderr", + value: Buffer.from(path + ": no such file or directory\n").toString("base64") + }), () => { + ws.send(JSON.stringify({ + type: "command_stop", + code: 1 + })); + }); + return; + } + + if ((!require('fs').lstatSync(path).isSymbolicLink() && !require('fs').lstatSync(path).isDirectory()) || (require('fs').lstatSync(path).isSymbolicLink() && !require('fs').lstatSync(require('fs').readlinkSync(path)).isDirectory())) { + ws.send(JSON.stringify({ + type: "command_stderr", + value: Buffer.from(path + ": not a directory\n").toString("base64") + }), () => { + ws.send(JSON.stringify({ + type: "command_stop", + code: 1 + })); + }); + return; + } + + ws.workingDirectory = path; - case "F12": - keys.push(Key.F12); - await keyboard.type(...keys); - break; + ws.send(JSON.stringify({ + type: "command_stdout", + value: Buffer.from(" * Working directory: " + ws.workingDirectory + "\n").toString("base64") + })); - case "OS": - keys.push(Key.LeftSuper); - await keyboard.type(...keys); - break; + ws.send(JSON.stringify({ + type: "command_stop", + code: 0 + })); + } catch (e) { + console.error(e); - case "Backspace": - keys.push(Key.Backspace); - await keyboard.type(...keys); - break; + ws.send(JSON.stringify({ + type: "command_stop", + code: null, + signal: e.name + })); + } - default: - console.log("Missing corresponding action for key " + data.key); - break; + return; } - } - } else if (data.type === "command") { - ws.send(JSON.stringify({ - type: "command_start" - })); - - if (Buffer.from(data.value, "base64").toString().startsWith("cd ") || Buffer.from(data.value, "base64").toString().startsWith("chdir ")) { - try { - let path; - if (Buffer.from(data.value, "base64").toString().startsWith("cd ")) { - path = require('path').resolve(ws.workingDirectory + "/" + Buffer.from(data.value, "base64").toString().substring(3)); - } else { - path = require('path').resolve(ws.workingDirectory + "/" + Buffer.from(data.value, "base64").toString().substring(6)); - } - - if (!require('fs').existsSync(path)) { + if (Buffer.from(data.value, "base64").toString().startsWith("pwd ") || Buffer.from(data.value, "base64").toString().trim() === "pwd") { + ws.send(JSON.stringify({ + type: "command_start" + }), () => { ws.send(JSON.stringify({ - type: "command_stderr", - value: Buffer.from(path + ": no such file or directory\n").toString("base64") + type: "command_stdout", + value: Buffer.from(ws.workingDirectory + "\n").toString("base64") }), () => { ws.send(JSON.stringify({ type: "command_stop", - code: 1 + code: 0 })); }); - return; - } + }); - if ((!require('fs').lstatSync(path).isSymbolicLink() && !require('fs').lstatSync(path).isDirectory()) || (require('fs').lstatSync(path).isSymbolicLink() && !require('fs').lstatSync(require('fs').readlinkSync(path)).isDirectory())) { - ws.send(JSON.stringify({ - type: "command_stderr", - value: Buffer.from(path + ": not a directory\n").toString("base64") - }), () => { - ws.send(JSON.stringify({ - type: "command_stop", - code: 1 - })); - }); - return; - } + return; + } + + ws.cmd = require('child_process').exec(Buffer.from(data.value, "base64").toString(), { cwd: ws.workingDirectory }); - ws.workingDirectory = path; + ws.send(JSON.stringify({ + type: "command_start" + })); + + ws.cmd.stdout.on('data', (data) => { + let send; + + if (data instanceof Buffer) { + send = data.toString("base64") + } else { + send = Buffer.from(data).toString("base64"); + } ws.send(JSON.stringify({ type: "command_stdout", - value: Buffer.from(" * Working directory: " + ws.workingDirectory + "\n").toString("base64") + value: send })); + }) - ws.send(JSON.stringify({ - type: "command_stop", - code: 0 - })); - } catch (e) { - console.error(e); + ws.cmd.stderr.on('data', (data) => { + let send; + + if (data instanceof Buffer) { + send = data.toString("base64") + } else { + send = Buffer.from(data).toString("base64"); + } ws.send(JSON.stringify({ - type: "command_stop", - code: null, - signal: e.name + type: "command_stderr", + value: send })); - } + }) - return; - } - - if (Buffer.from(data.value, "base64").toString().startsWith("pwd ") || Buffer.from(data.value, "base64").toString().trim() === "pwd") { - ws.send(JSON.stringify({ - type: "command_start" - }), () => { - ws.send(JSON.stringify({ - type: "command_stdout", - value: Buffer.from(ws.workingDirectory + "\n").toString("base64") - }), () => { + ws.cmd.on('close', (code, signal) => { + setTimeout(() => { + delete ws.cmd; ws.send(JSON.stringify({ type: "command_stop", - code: 0 + code, + signal })); - }); - }); - - return; - } - - ws.cmd = require('child_process').exec(Buffer.from(data.value, "base64").toString(), { cwd: ws.workingDirectory }); - - ws.send(JSON.stringify({ - type: "command_start" - })); - - ws.cmd.stdout.on('data', (data) => { - let send; - - if (data instanceof Buffer) { - send = data.toString("base64") + }, 100); + }) + } else if (data.type === "halt") { + if (ws.cmd) { + ws.cmd.kill("SIGKILL"); } else { - send = Buffer.from(data).toString("base64"); + console.log("Attempted to stop nonexistant command"); } - - ws.send(JSON.stringify({ - type: "command_stdout", - value: send - })); - }) - - ws.cmd.stderr.on('data', (data) => { - let send; - - if (data instanceof Buffer) { - send = data.toString("base64") - } else { - send = Buffer.from(data).toString("base64"); - } - - ws.send(JSON.stringify({ - type: "command_stderr", - value: send - })); - }) - - ws.cmd.on('close', (code, signal) => { - setTimeout(() => { - delete ws.cmd; - ws.send(JSON.stringify({ - type: "command_stop", - code, - signal - })); - }, 100); - }) - } else if (data.type === "halt") { - if (ws.cmd) { - ws.cmd.kill("SIGKILL"); } - } - } catch (e) {} - }); + } catch (e) {} + }); - ws.on('close', () => { - clients.splice(clients.indexOf(ws), 1); + ws.on('close', () => { + clients.splice(clients.indexOf(ws), 1); - if (ws.cmd) { - ws.cmd.kill("SIGKILL"); - } - }) + if (ws.cmd) { + ws.cmd.kill("SIGKILL"); + } + }) - setupClient(ws, ws._setting_latency, ws._setting_quality); + setupClient(ws, ws._setting_latency, ws._setting_quality); + } });
\ No newline at end of file |