diff --git a/public/scripts/agent-desktop-0.0.2.js b/public/scripts/agent-desktop-0.0.2.js index 965987f6..ccac933a 100644 --- a/public/scripts/agent-desktop-0.0.2.js +++ b/public/scripts/agent-desktop-0.0.2.js @@ -103,13 +103,13 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { // KVM Control. // Routines for processing incoming packets from the AJAX server, and handling individual messages. - obj.ProcessPictureMsg = function (str, X, Y) { + obj.ProcessPictureMsg = function (data, X, Y) { //if (obj.targetnode != null) obj.Debug("ProcessPictureMsg " + X + "," + Y + " - " + obj.targetnode.substring(0, 8)); var tile = new Image(); tile.xcount = obj.tilesReceived++; //console.log('Tile #' + tile.xcount); var r = obj.tilesReceived; - tile.src = "data:image/jpeg;base64," + btoa(str.substring(4, str.length)); + tile.src = "data:image/jpeg;base64," + btoa(String.fromCharCode.apply(null, data.slice(4))); tile.onload = function () { //console.log('DecodeTile #' + this.xcount); if (obj.Canvas != null && obj.KillDraw < r && obj.State != 0) { @@ -185,72 +185,16 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); } } - obj.ProcessData = function (str) { - var ptr = 0; - while (ptr < str.length) { - var r = obj.ProcessDataEx(str.substring(ptr)); - if ((r == null) || (r == 0)) break; - ptr += r; - } - } + obj.ProcessBinaryCommand = function (cmd, cmdsize, view) { + var X, Y; + if ((cmd == 3) || (cmd == 4) || (cmd == 7)) { X = (view[4] << 8) + view[5]; Y = (view[6] << 8) + view[7]; } + //console.log('CMD', cmd, cmdsize, X, Y); - obj.ProcessDataEx = function (str) { - if (obj.accumulator != null) { - str = obj.accumulator + str; - //console.log('KVM using accumulated data, total size is now ' + str.length + ' bytes.'); - obj.accumulator = null; - } - if (obj.debugmode > 1) { console.log("KRecv(" + str.length + "): " + rstr2hex(str.substring(0, Math.min(str.length, 40)))); } - if (str.length < 4) return; - var cmdmsg = null, X = 0, Y = 0, command = ReadShort(str, 0), cmdsize = ReadShort(str, 2), jumboAdd = 0; - if (obj.recordedData != null) { obj.recordedData.push(recordingEntry(2, 1, str.length)); obj.recordedData.push(str); } - if ((command == 27) && (cmdsize == 8)) { - // Jumbo packet - if (str.length < 12) return; - command = ReadShort(str, 8) - cmdsize = ReadInt(str, 4); - //console.log('JUMBO cmd=' + command + ', cmdsize=' + cmdsize + ', data received=' + str.length); - if ((cmdsize + 8) > str.length) { - //console.log('KVM accumulator set to ' + str.length + ' bytes, need ' + cmdsize + ' bytes.'); - obj.accumulator = str; - return; - } - str = str.substring(8); - jumboAdd = 8; - } - if ((cmdsize != str.length) && (obj.debugmode > 0)) { console.log(cmdsize, str.length, cmdsize == str.length); } - if ((command >= 18) && (command != 65) && (command != 88)) { - console.error("Invalid KVM command " + command + " of size " + cmdsize); - console.log("Invalid KVM data", str.length, rstr2hex(str.substring(0, 40)) + '...'); - if (obj.parent && obj.parent.setConsoleMessage) { obj.parent.setConsoleMessage("Received invalid network data", 5); } - return; - } - if (cmdsize > str.length) { - //console.log('KVM accumulator set to ' + str.length + ' bytes, need ' + cmdsize + ' bytes.'); - obj.accumulator = str; - return; - } - //console.log("KVM Command: " + command + " Len:" + cmdsize); - - if (command == 3 || command == 4 || command == 7) { - cmdmsg = str.substring(4, cmdsize); - X = ((cmdmsg.charCodeAt(0) & 0xFF) << 8) + (cmdmsg.charCodeAt(1) & 0xFF); - Y = ((cmdmsg.charCodeAt(2) & 0xFF) << 8) + (cmdmsg.charCodeAt(3) & 0xFF); - if (obj.debugmode > 0) { console.log("CMD" + command + " at X=" + X + " Y=" + Y); } - } - - switch (command) { + switch (cmd) { case 3: // Tile if (obj.FirstDraw) obj.onResize(); - obj.ProcessPictureMsg(cmdmsg, X, Y); - break; - case 4: // Tile Copy - if (obj.FirstDraw) obj.onResize(); - if (obj.TilesDrawn == obj.tilesReceived) { - obj.ProcessCopyRectMsg(cmdmsg); - } else { - obj.PendingOperations.push([ ++tilesReceived, 1, cmdmsg ]); - } + //console.log('TILE', X, Y); + obj.ProcessPictureMsg(view.slice(4), X, Y); break; case 7: // Screen size obj.ProcessScreenMsg(X, Y); @@ -262,13 +206,13 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { obj.SendKeyMsgKC(obj.KeyAction.UP, 16); // Shift obj.send(String.fromCharCode(0x00, 0x0E, 0x00, 0x04)); break; - case 11: // GetDisplays - var selectedDisplay = 0, displays = { }, dcount = ((str.charCodeAt(4) & 0xFF) << 8) + (str.charCodeAt(5) & 0xFF); + case 11: // GetDisplays (TODO) + var selectedDisplay = 0, displays = {}, dcount = (view[4] << 8) + view[5]; if (dcount > 0) { // Many displays present - selectedDisplay = ((str.charCodeAt(6 + (dcount * 2)) & 0xFF) << 8) + (str.charCodeAt(7 + (dcount * 2)) & 0xFF); + selectedDisplay = (view[6 + (dcount * 2)] << 8) + view[7 + (dcount * 2)]; for (var i = 0; i < dcount; i++) { - var disp = ((str.charCodeAt(6 + (i * 2)) & 0xFF) << 8) + (str.charCodeAt(7 + (i * 2)) & 0xFF); + var disp = (view[6 + (i * 2)] << 8) + view[7 + (i * 2)]; if (disp == 65535) { displays[disp] = 'All Displays'; } else { displays[disp] = 'Display ' + disp; } } } @@ -287,17 +231,13 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { case 15: // KVM_TOUCH obj.TouchArray = {}; break; - case 16: // MNG_KVM_CONNECTCOUNT - obj.connectioncount = ReadInt(str, 4); - //obj.Debug("Got KVM Connect Count: " + obj.connectioncount); - if (obj.onConnectCountChanged != null) obj.onConnectCountChanged(obj.connectioncount, obj); - break; case 17: // MNG_KVM_MESSAGE - //obj.Debug("Got KVM Message: " + str.substring(4, cmdsize)); - if (obj.onMessage != null) obj.onMessage(str.substring(4, cmdsize), obj); + var str = String.fromCharCode.apply(null, data.slice(4)); + obj.Debug("Got KVM Message: " + str); + if (obj.onMessage != null) obj.onMessage(str, obj); break; case 65: // Alert - str = str.substring(4); + var str = String.fromCharCode.apply(null, data.slice(4)); if (str[0] != '.') { console.log(str); //alert('KVM: ' + str); if (obj.parent && obj.parent.setConsoleMessage) { obj.parent.setConsoleMessage(str); } @@ -307,15 +247,18 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { break; case 88: // MNG_KVM_MOUSE_CURSOR if (cmdsize != 5) break; - var cursorNum = str.charCodeAt(4); + var cursorNum = view[4]; if (cursorNum > mouseCursors.length) { cursorNum = 0; } xMouseCursorCurrent = mouseCursors[cursorNum]; if (xMouseCursorActive) { obj.CanvasId.style.cursor = xMouseCursorCurrent; } break; + default: + console.log('Unknown command', cmd, cmdsize); + break; } - return cmdsize + jumboAdd; - } + } + // Keyboard and Mouse I/O. obj.MouseButton = { "NONE": 0x00, "LEFT": 0x02, "RIGHT": 0x08, "MIDDLE": 0x20 }; obj.KeyAction = { "NONE": 0, "DOWN": 1, "UP": 2, "SCROLL": 3, "EXUP": 4, "EXDOWN": 5, "DBLCLICK": 6 }; diff --git a/public/scripts/agent-redir-ws-0.1.1.js b/public/scripts/agent-redir-ws-0.1.1.js index f905c89d..2497edf4 100644 --- a/public/scripts/agent-redir-ws-0.1.1.js +++ b/public/scripts/agent-redir-ws-0.1.1.js @@ -50,6 +50,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, au obj.nodeid = nodeid; obj.connectstate = 0; obj.socket = new WebSocket(url); + obj.socket.binaryType = 'arraybuffer'; obj.socket.onopen = obj.xxOnSocketConnected; obj.socket.onmessage = obj.xxOnMessage; //obj.socket.onmessage = function (e) { console.log('Websocket data', e.data); obj.xxOnMessage(e); } @@ -136,6 +137,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, au else if (typeof webkitRTCPeerConnection !== 'undefined') { obj.webrtc = new webkitRTCPeerConnection(configuration); } if ((obj.webrtc != null) && (obj.webrtc.createDataChannel)) { obj.webchannel = obj.webrtc.createDataChannel('DataChannel', {}); // { ordered: false, maxRetransmits: 2 } + obj.webchannel.binaryType = 'arraybuffer'; obj.webchannel.onmessage = obj.xxOnMessage; //obj.webchannel.onmessage = function (e) { console.log('WebRTC data', e.data); obj.xxOnMessage(e); } obj.webchannel.onopen = function () { obj.webRtcActive = true; performWebRtcSwitch(); }; @@ -165,66 +167,26 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, au } } + // Control messages, most likely WebRTC setup if (typeof e.data == 'string') { - // Control messages, most likely WebRTC setup obj.xxOnControlCommand(e.data); - return; - } - - if (typeof e.data == 'object') { - if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; } - if (fileReader.readAsBinaryString && (obj.m.ProcessBinaryData == null)) { - // Chrome & Firefox (Draft) - fileReaderInuse = true; - fileReader.readAsBinaryString(new Blob([e.data])); - } else if (fileReader.readAsArrayBuffer) { - // Chrome & Firefox (Spec) - fileReaderInuse = true; - fileReader.readAsArrayBuffer(e.data); - } else { - // IE10, readAsBinaryString does not exist, use an alternative. - var binary = '', bytes = new Uint8Array(e.data), length = bytes.byteLength; - for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); } - obj.xxOnSocketData(binary); - } } else { - // If we get a string object, it maybe the WebRTC confirm. Ignore it. - obj.xxOnSocketData(e.data); - } - - // Request RTT mesure, don't use this if WebRTC is active - if (obj.webRtcActive != true) { - var ticks = new Date().getTime(); - if ((obj.latency.lastSend == null) || ((ticks - obj.latency.lastSend) > 5000)) { obj.latency.lastSend = ticks; obj.sendCtrlMsg('{"ctrlChannel":"102938","type":"rtt","time":' + ticks + '}'); } + // Send the data to the module + if (obj.m.ProcessBinaryCommand) { + // Send as Binary Command + var view = new Uint8Array(e.data), cmd = (view[0] << 8) + view[1], cmdsize = (view[2] << 8) + view[3]; + if ((cmd == 27) && (cmdsize == 8)) { cmd = (view[8] << 8) + view[9]; cmdsize = (view[5] << 16) + (view[6] << 8) + view[7]; view = view.slice(8); } + if (cmdsize != view.byteLength) { console.log('REDIR-ERROR', cmd, cmdsize, view.byteLength); } else { obj.m.ProcessBinaryCommand(cmd, cmdsize, view); } + } else if (obj.m.ProcessBinaryData) { + // Send as Binary + obj.m.ProcessBinaryData(new Uint8Array(e.data)); + } else { + // Send as Text + obj.m.ProcessData(String.fromCharCode.apply(null, new Uint8Array(e.data))); + } } }; - // Setup the file reader - var fileReader = new FileReader(); - var fileReaderInuse = false, fileReaderAcc = []; - if (fileReader.readAsBinaryString && (obj.m.ProcessBinaryData == null)) { - // Chrome & Firefox (Draft) - fileReader.onload = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } } - } else if (fileReader.readAsArrayBuffer) { - // Chrome & Firefox (Spec) - fileReader.onloadend = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } } - } - - obj.xxOnSocketData = function (data) { - if (!data || obj.connectstate == -1) return; - if (typeof data === 'object') { - if (obj.m.ProcessBinaryData) { return obj.m.ProcessBinaryData(data); } - // This is an ArrayBuffer, convert it to a string array (used in IE) - var binary = '', bytes = new Uint8Array(data), length = bytes.byteLength; - for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); } - data = binary; - } - else if (typeof data !== 'string') return; - //console.log('xxOnSocketData', rstr2hex(data)); - if ((typeof args != 'undefined') && args.redirtrace) { console.log('RedirRecv', typeof data, data.length, (data[0] == '{')?data:rstr2hex(data).substring(0, 64)); } - return obj.m.ProcessData(data); - } - obj.sendText = function (x) { if (typeof x != 'string') { x = JSON.stringify(x); } // Turn into a string if needed obj.send(encode_utf8(x)); // Encode UTF8 correctly diff --git a/webserver.js b/webserver.js index f5213557..7d699185 100644 --- a/webserver.js +++ b/webserver.js @@ -4357,7 +4357,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Authenticates a session and forwards function PerformWSSessionAuth(ws, req, noAuthOk, func) { // Check if this is a banned ip address - if (obj.checkAllowLogin(req) == false) { try { ws.send(JSON.stringify({ action: 'close', cause: 'banned', msg: 'banned-1' })); ws.close(); } catch (e) { } return; } + if (obj.checkAllowLogin(req) == false) { parent.debug('web', 'WSERROR: Banned connection.'); try { ws.send(JSON.stringify({ action: 'close', cause: 'banned', msg: 'banned-1' })); ws.close(); } catch (e) { } return; } try { // Hold this websocket until we are ready. ws._socket.pause(); @@ -4366,11 +4366,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var domain = null; if (noAuthOk == true) { domain = getDomain(req); - if (domain == null) { try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'noauth-1' })); ws.close(); return; } catch (e) { } return; } + if (domain == null) { parent.debug('web', 'WSERROR: Got no domain, no auth ok.'); try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'noauth-1' })); ws.close(); return; } catch (e) { } return; } } else { // If authentication is required, enforce IP address filtering. domain = checkUserIpAddress(ws, req); - if (domain == null) { return; } + if (domain == null) { parent.debug('web', 'WSERROR: Got no domain, user auth required.'); return; } } var emailcheck = ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName.indexOf('.') != -1) && (obj.args.lanonly != true) && (domain.auth != 'sspi') && (domain.auth != 'ldap')) @@ -4405,17 +4405,20 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', sms2fa: sms2fa, sms2fasent: true })); ws.close(); } catch (e) { } } else { // Ask for a login token + parent.debug('web', 'Asking for login token'); try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { } } } else { checkUserOneTimePassword(req, domain, user, req.query.token, null, function (result) { if (result == false) { // Failed, ask for a login token again + parent.debug('web', 'Invalid login token, asking again'); try { ws.send(JSON.stringify({ action: 'close', cause: 'noauth', msg: 'tokenrequired', email2fa: email2fa })); ws.close(); } catch (e) { } } else { // We are authenticated with 2nd factor. // Check email verification if (emailcheck && (user.email != null) && (user.emailVerified !== true)) { + parent.debug('web', 'Invalid login, asking for email validation'); try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, email2fasent: true })); ws.close(); } catch (e) { } } else { func(ws, req, domain, user); @@ -4426,6 +4429,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } else { // Check email verification if (emailcheck && (user.email != null) && (user.emailVerified !== true)) { + parent.debug('web', 'Invalid login, asking for email validation'); try { ws.send(JSON.stringify({ action: 'close', cause: 'emailvalidation', msg: 'emailvalidationrequired', email2fa: email2fa, email2fasent: true })); ws.close(); } catch (e) { } } else { // We are authenticated