UNPKG

@cocalc/backend

Version:

CoCalc backend functionality: functionality used by either the hub, the next.js server or the project.

122 lines (121 loc) 4.53 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.connectToLockedSocket = exports.unlockSocket = void 0; const net_1 = require("net"); const awaiting_1 = require("awaiting"); const logger_1 = __importDefault(require("@cocalc/backend/logger")); const log = (0, logger_1.default)("locked-socket"); /* unlockSocket - Wait to receive token over the socket; when it is received, call cb(false), then send back "y". If any mistake is made (or the socket times out after 10 seconds), send back "n" and close the connection. */ async function unlockSocket(socket, token) { log.debug("unlockSocket: waiting for secret token..."); try { await (0, awaiting_1.callback)(unlock, socket, token); log.debug("unlockSocket: SUCCESS"); } catch (err) { log.debug("unlockSocket: FAILED"); throw err; } } exports.unlockSocket = unlockSocket; function unlock(socket, token, cb) { const timeout = setTimeout(() => { socket.destroy(); cb("Unlock socket -- timed out waiting for secret token"); }, 10000); let userToken = ""; function listener(data) { userToken += data.toString(); if (userToken.slice(0, token.length) === token) { socket.removeListener("data", listener); // got it! socket.write("y"); clearTimeout(timeout); cb(); } else if (userToken.length > token.length || token.slice(0, userToken.length) !== userToken) { socket.removeListener("data", listener); socket.write("n"); socket.write("Invalid secret token."); socket.destroy(); clearTimeout(timeout); cb("Invalid secret token."); } } socket.on("data", listener); } /* Connect to a locked socket on remove server. WARNING: Use only on a network where you do not have to worry about an attacker listening to all traffic, since this is not an *encryption* protocol, and it's just a symmetric key. In CoCalc this is used to allow a hub to connect to a project. It is not used in any other way. */ async function connectToLockedSocket({ port, host = "localhost", token, timeout = 5, // in seconds (not milliseconds) }) { if (port <= 0 || port >= 65536) { // little consistency check throw Error(`RangeError: port should be > 0 and < 65536: ${port}`); } log.debug("connectToLockedSocket:", `${host}:${port}`); return await (0, awaiting_1.callback)(connect, port, host, token, timeout); } exports.connectToLockedSocket = connectToLockedSocket; function connect(port, host, token, timeout, cb) { let timer = null; function finish(err) { // NOTE: we set cb to undefined after calling it, and only // call it if defined, since the event and timer callback stuff is // very hard to do right without calling cb more than once // (which is VERY bad to do). if (timer != null) { clearTimeout(timer); timer = null; } if (cb == null) return; if (err) { log.debug(`connectToLockedSocket: ERROR - ${err}`); cb(err); } else { log.debug("connectToLockedSocket: SUCCESS"); cb(undefined, socket); } cb = null; } const socket = (0, net_1.createConnection)({ host, port }, function onceConnected() { socket.once("data", (data) => { log.debug("connectToLockedSocket: got back response"); if (data.toString() === "y") { finish(); } else { socket.destroy(); finish("Permission denied (invalid secret token) when connecting to the local hub."); } }); log.debug("connectToLockedSocket: connected, now sending secret token"); socket.write(token); }); // This is called in case there is an error trying to make the connection, e.g., "connection refused". socket.on("error", (err) => { finish(err); }); function timedOut() { timer = null; finish(`connectToLockedSocket: timed out trying to connect to locked socket at ${host}:${port}`); socket.end(); } timer = setTimeout(timedOut, timeout * 1000); } //# sourceMappingURL=locked-socket.js.map