UNPKG

guacamole-lite

Version:

Lite version of guacamole server in node.js. RDP/VNC client for HTML5 browsers

167 lines (121 loc) 4.78 kB
const Url = require('url'); const DeepExtend = require('deep-extend'); const Moment = require('moment'); const GuacdClient = require('./GuacdClient.js'); const Crypt = require('./Crypt.js'); class ClientConnection { constructor(server, connectionId, webSocket) { this.STATE_OPEN = 1; this.STATE_CLOSED = 2; this.state = this.STATE_OPEN; this.server = server; this.connectionId = connectionId; this.webSocket = webSocket; this.query = Url.parse(this.webSocket.upgradeReq.url, true).query; this.lastActivity = Date.now(); this.activityCheckInterval = null; this.log(this.server.LOGLEVEL.VERBOSE, 'Client connection open'); try { this.connectionSettings = this.decryptToken(); this.connectionType = this.connectionSettings.connection.type; this.connectionSettings['connection'] = this.mergeConnectionOptions(); } catch (error) { this.log(this.server.LOGLEVEL.ERRORS, 'Token validation failed'); this.close(error); return; } server.callbacks.processConnectionSettings(this.connectionSettings, (err, settings) => { if (err) { return this.close(err); } this.connectionSettings = settings; this.log(this.server.LOGLEVEL.VERBOSE, 'Opening guacd connection'); this.guacdClient = new GuacdClient(server, this); webSocket.on('close', this.close.bind(this)); webSocket.on('message', this.processReceivedMessage.bind(this)); if (server.clientOptions.maxInactivityTime > 0) { this.activityCheckInterval = setInterval(this.checkActivity.bind(this), 1000); } }); } decryptToken() { const crypt = new Crypt(this.server); const encrypted = this.query.token; delete this.query.token; return crypt.decrypt(encrypted); } log(level, ...args) { if (level > this.server.clientOptions.log.level) { return; } const stdLogFunc = this.server.clientOptions.log.stdLog; const errorLogFunc = this.server.clientOptions.log.errorLog; let logFunc = stdLogFunc; if (level === this.server.LOGLEVEL.ERRORS) { logFunc = errorLogFunc; } logFunc(this.getLogPrefix(), ...args); } getLogPrefix() { return '[' + Moment().format('YYYY-MM-DD HH:mm:ss') + '] [Connection ' + this.connectionId + '] '; } close(error) { if (this.state == this.STATE_CLOSED) { return; } if (this.activityCheckInterval !== undefined && this.activityCheckInterval !== null) { clearInterval(this.activityCheckInterval); } if (error) { this.log(this.server.LOGLEVEL.ERRORS, 'Closing connection with error: ', error); } if (this.guacdClient) { this.guacdClient.close(); } this.webSocket.removeAllListeners('close'); this.webSocket.close(); this.server.activeConnections.delete(this.connectionId); this.state = this.STATE_CLOSED; this.log(this.server.LOGLEVEL.VERBOSE, 'Client connection closed'); } error(error) { this.server.emit('error', this, error); this.close(error); } processReceivedMessage(message) { this.lastActivity = Date.now(); this.guacdClient.send(message); } send(message) { if (this.state == this.STATE_CLOSED) { return; } this.log(this.server.LOGLEVEL.DEBUG, '>>>G2W> ' + message + '###'); this.webSocket.send(message, {binary: false, mask: false}, (error) => { if (error) { this.close(error); } }); } mergeConnectionOptions() { let unencryptedConnectionSettings = {}; Object .keys(this.query) .filter(key => this.server.clientOptions.allowedUnencryptedConnectionSettings[this.connectionType].includes(key)) .forEach(key => unencryptedConnectionSettings[key] = this.query[key]); let compiledSettings = {}; DeepExtend( compiledSettings, this.server.clientOptions.connectionDefaultSettings[this.connectionType], this.connectionSettings.connection.settings, unencryptedConnectionSettings ); return compiledSettings; } checkActivity() { if (Date.now() > (this.lastActivity + this.server.clientOptions.maxInactivityTime)) { this.close(new Error('WS was inactive for too long')); } } } module.exports = ClientConnection;