diff --git a/public/index.html b/public/index.html index 8e1595b..4f5dbd0 100644 --- a/public/index.html +++ b/public/index.html @@ -52,12 +52,22 @@
-
- +
+

Enter the address of the server you would like to connect to:

+
+ +

+ WARNING: The server you're about to connect to is insecure. + Your activity here may be available to anyone monitoring your network traffic! + Are you sure you want to continue? +

+ + +
diff --git a/public/scripts/main.js b/public/scripts/main.js index cc8bacb..3303067 100644 --- a/public/scripts/main.js +++ b/public/scripts/main.js @@ -6,11 +6,12 @@ document.addEventListener("DOMContentLoaded", () => { Terminal.start(); const dialog_backdrop = document.getElementById("dialog-backdrop"); + const connect_button = document.getElementById("connect"); const connect_dialog = document.getElementById("connect-dialog"); const connect_url = document.getElementById("connect-url"); const connect_submit = document.getElementById("connect-submit"); - const connect_close = document.getElementById("connect-close"); + const connect_close = connect_dialog.getElementsByClassName("dialog-close").item(0); connect_url.placeholder = window.location.host; @@ -24,8 +25,7 @@ document.addEventListener("DOMContentLoaded", () => { connect_submit.addEventListener("click", () => { connect_close.click(); - const new_server = connect_url.value; - if (!new_server) return; + const new_server = connect_url.value || window.location.host; Terminal.connect(new_server); }); @@ -41,10 +41,14 @@ document.addEventListener("DOMContentLoaded", () => { return; }); - connect_close.addEventListener("click", () => { - connect_dialog.classList.remove("show"); - dialog_backdrop.classList.remove("show"); - Terminal.set_enable_input(true); + [...document.getElementsByClassName("dialog-close")].forEach(dialog_close => { + dialog_close.addEventListener("click", () => { + [...document.getElementsByClassName("dialog")].forEach(element => { + element.classList.remove("show"); + }); + dialog_backdrop.classList.remove("show"); + Terminal.set_enable_input(true); + }); }); dialog_backdrop.addEventListener("click", () => { diff --git a/public/scripts/terminal.js b/public/scripts/terminal.js index da2f8a1..bf55389 100644 --- a/public/scripts/terminal.js +++ b/public/scripts/terminal.js @@ -111,11 +111,64 @@ export async function connect(server_url) { add_system_message("Connecting to the server...\n"); - if (!server_url.startsWith("wss://")) server_url = "wss://" + server_url; - client = new WebSocket(server_url); + let protocol = false; + + // check if user explicitly stated secure/insecure protocol + if (server_url.startsWith("wss://")) { + protocol = "wss://"; + server_url = server_url.split(6); + } else if (server_url.startsWith("ws://")) { + server_url = server_url.split(5); + protocol = "ws://"; + } + // otherwise, probe the url! + else protocol = await get_available_socket_protocol(server_url); + + // no server was found! + if (!protocol) { + return add_system_message(`\n[NO SERVER FOUND AT ${server_url}!]\n`); + } + + // server was found, but it's insecure! + if (protocol === "ws://") { + const warn_dialog = document.getElementById("warn-dialog"); + const warn_close = warn_dialog.getElementsByClassName("dialog-close").item(0); + const user_wants_insecure = await new Promise((Resolve, Reject) => { + const dialog_backdrop = document.getElementById("dialog-backdrop"); + const warn_proceed = document.getElementById("warn-proceed"); + const warn_cancel = document.getElementById("warn-cancel"); + + warn_dialog.classList.add("show"); + dialog_backdrop.classList.add("show"); + + set_enable_input(false); + + warn_close.addEventListener('click', () => { + Resolve(false); + }); + warn_cancel.addEventListener('click', () => { + Resolve(false); + }); + warn_proceed.addEventListener('click', () => { + Resolve(true); + }); + }); + + warn_close.click(); + + set_enable_input(true); + + if (!user_wants_insecure) { + server_indicator.innerText = "not connected"; + add_system_message(`\n[CONNECTION CLOSED]\n`); + return; + } + } + + client = new WebSocket(protocol + server_url); client.addEventListener('open', () => { - server_indicator.innerText = server_url.slice(6); + server_indicator.innerText = server_url; add_system_message(`Connection successful.\n\n`); add_system_message(`=== BEGIN SESSION ===\n\n`); new_caret(); @@ -134,6 +187,34 @@ export async function connect(server_url) { }); } +/** + * probes the `server_url` for a secure websocket connection first, an insecure websocket second, or resolves to `false` on failure. + * @param {string} server_url + * @returns a promise either resolving to the discovered protocol, or false on failure. + */ +function get_available_socket_protocol(server_url) { + return new Promise((Resolve, Reject) => { + const secure_client = new WebSocket("wss://" + server_url); + + secure_client.addEventListener('open', () => { + Resolve("wss://"); + }); + + secure_client.addEventListener('error', () => { + const insecure_client = new WebSocket("ws://" + server_url); + + insecure_client.addEventListener('open', () => { + Resolve("ws://"); + }); + + insecure_client.addEventListener('error', () => { + Reject(false); + }); + + }); + }); +} + /** * appends a client-side message to the text buffer. * @param {string} text - text to send to the buffer. diff --git a/public/styles/main.css b/public/styles/main.css index 85270bd..cb5438b 100644 --- a/public/styles/main.css +++ b/public/styles/main.css @@ -1,6 +1,7 @@ :root { --colour: #a6e3a1; --bgcolour: #1e1e2e; + --warn-colour: #e83737; } body { @@ -186,12 +187,10 @@ body.lcd pre#content { z-index: 99; } -#connect-dialog { +div.dialog { position: fixed; top: 50%; left: 50%; - max-width: calc(100vw - 4rem); - width: 16rem; margin: auto auto; padding: 1rem; display: none; @@ -203,37 +202,37 @@ body.lcd pre#content { z-index: 99; } -#dialog-backdrop.show, -#connect-dialog.show { - display: block; -} - -#connect-dialog p { +div.dialog p { margin: 0 0 1em 0; } -#connect-dialog input { +div.dialog input { font-family: inherit; - color: var(--colour); - border: 1px solid var(--colour); + color: inherit; + border: inherit; background: transparent; } -#connect-dialog input::placeholder { +div.dialog input::placeholder { font-family: inherit; - color: var(--colour); + color: inherit; opacity: .25; } -#connect-dialog button { +div.dialog button { font-family: inherit; - border: 1px solid var(--colour); - color: var(--colour); + border: inherit; + color: inherit; background: transparent; cursor: pointer; } -#connect-dialog #connect-close { +#dialog-backdrop.show, +div.dialog.show { + display: block; +} + +div.dialog .dialog-close { position: absolute; top: -0.7em; right: 1rem; @@ -241,6 +240,17 @@ body.lcd pre#content { background: var(--bgcolour); } +#connect-dialog { + max-width: calc(100vw - 4rem); + width: 16rem; +} + +#warn-dialog { + max-width: 18rem; + color: var(--warn-colour); + border-color: var(--warn-colour); +} + *:focus { outline: none; box-shadow: 0 0 4px; diff --git a/server/main.js b/server/main.js index aa63713..9993d64 100644 --- a/server/main.js +++ b/server/main.js @@ -1,13 +1,8 @@ const fs = require('fs'); -const https = require('https'); +const http = require('http'); const path = require('path'); const Websocket = require('ws'); -const config = { - cert: fs.readFileSync(process.env.SSL_CERT || './certs/cert.crt'), - key: fs.readFileSync(process.env.SSL_KEY || './certs/cert.key'), -} - const MIME_TYPES = { default: "application/octet-stream", html: "text/html; charset=UTF-8", @@ -65,7 +60,7 @@ This connection will now terminate. `; -const PORT = process.env.PORT || 8443; +const PORT = process.env.PORT || 8080; const PING_INTERVAL = 10000; let sockets = []; @@ -74,7 +69,7 @@ const MAX_BUFFER_SIZE = 1024 * 1000; const MAX_MESSAGE_LENGTH = 1024; /** - * simple file fetching for the HTTPS server + * simple file fetching for the HTTP server */ async function get_file(url) { // ignore query params...not very helpful when getting files! @@ -94,7 +89,7 @@ async function get_file(url) { return { stream, ext }; } -const server = https.createServer(config, async (req, res) => { +const server = http.createServer(async (req, res) => { const file = await get_file(req.url); if (!file) { res.writeHead(404); @@ -226,7 +221,7 @@ function generate_colour() { } server.listen(PORT, () => { - console.log(`OpenTerminal is now LIVE on https://127.0.0.1:${PORT}!`); + console.log(`OpenTerminal is now LIVE on http://127.0.0.1:${PORT}`); }); /**