diff --git a/agents/MeshService.exe b/agents/MeshService.exe index 205d5f40..ec1aac33 100644 Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ diff --git a/agents/MeshService64.exe b/agents/MeshService64.exe index 6c71f817..961e691f 100644 Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ diff --git a/agents/meshagent_arm b/agents/meshagent_arm index 8909f721..713c1dd3 100644 Binary files a/agents/meshagent_arm and b/agents/meshagent_arm differ diff --git a/agents/meshagent_pi b/agents/meshagent_pi index ad3a6c6c..15e3e84b 100644 Binary files a/agents/meshagent_pi and b/agents/meshagent_pi differ diff --git a/agents/meshagent_pogo b/agents/meshagent_pogo index 7451b09a..79007df8 100644 Binary files a/agents/meshagent_pogo and b/agents/meshagent_pogo differ diff --git a/agents/meshagent_poky b/agents/meshagent_poky index 4f53aa40..8ccc7aeb 100644 Binary files a/agents/meshagent_poky and b/agents/meshagent_poky differ diff --git a/agents/meshagent_poky64 b/agents/meshagent_poky64 index e964f2b8..662ffb6e 100644 Binary files a/agents/meshagent_poky64 and b/agents/meshagent_poky64 differ diff --git a/agents/meshagent_x86 b/agents/meshagent_x86 index f4cd4211..4490a0a8 100644 Binary files a/agents/meshagent_x86 and b/agents/meshagent_x86 differ diff --git a/agents/meshagent_x86-64 b/agents/meshagent_x86-64 index a09b93fc..e93ba729 100644 Binary files a/agents/meshagent_x86-64 and b/agents/meshagent_x86-64 differ diff --git a/agents/meshagent_x86-64_nokvm b/agents/meshagent_x86-64_nokvm index 9ffdbcab..35bd7a5f 100644 Binary files a/agents/meshagent_x86-64_nokvm and b/agents/meshagent_x86-64_nokvm differ diff --git a/agents/meshagent_x86_nokvm b/agents/meshagent_x86_nokvm index 916c5604..ec91cc18 100644 Binary files a/agents/meshagent_x86_nokvm and b/agents/meshagent_x86_nokvm differ diff --git a/agents/meshcore.js b/agents/meshcore.js index 4948483a..6345354f 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -683,7 +683,7 @@ function createMeshCore(agent) { var obj; try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC'); return; } if (obj.type == 'close') { - sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close'); + //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close'); try { this.close(); } catch (e) { } try { this.xrtc.close(); } catch (e) { } } @@ -692,7 +692,7 @@ function createMeshCore(agent) { // Called when receiving control data on websocket function onTunnelControlData(data) { if (typeof data != 'string') return; - sendConsoleText('onTunnelControlData: ' + data); + //sendConsoleText('onTunnelControlData: ' + data); //console.log('onTunnelControlData: ' + data); var obj; @@ -700,10 +700,28 @@ function createMeshCore(agent) { if (obj.type == 'close') { // We received the close on the websocket - sendConsoleText('Tunnel #' + this.tunnel.index + ' WebSocket control close'); + //sendConsoleText('Tunnel #' + this.tunnel.index + ' WebSocket control close'); try { this.close(); } catch (e) { } + } else if (obj.type == 'webrtc0') { // Browser indicates we can start WebRTC switch-over. + if (this.websocket.httprequest.protocol == 1) { // Terminal + // This is a terminal data stream, unpipe the terminal now and indicate to the other side that terminal data will no longer be received over WebSocket + this.websocket.httprequest.process.stdout.unpipe(this.websocket); + this.websocket.httprequest.process.stderr.unpipe(this.websocket); + this.websocket.write("{\"type\":\"webrtc1\"}"); // End of data marker + } else if (this.websocket.httprequest.protocol == 2) { // Desktop + // This is a KVM data stream, unpipe the KVM now and indicate to the other side that KVM data will no longer be received over WebSocket + this.websocket.httprequest.desktop.kvm.unpipe(this.websocket); + this.websocket.write("{\"type\":\"webrtc1\"}"); // End of data marker + } + /* + else { + // Debug, just display on agent console + rtcchannel.on('data', function (buffer) { sendConsoleText("RTCReceived: " + buffer.length + " bytes"); }); + rtcchannel.on('end', function () { sendConsoleText("RTCChannel: " + this.name + " was closed"); }); + channel.write('WebRTC HELLO!'); + } + */ } else if (obj.type == 'webrtc1') { - this.write("{\"type\":\"webrtc2\"}"); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point. if (this.httprequest.protocol == 1) { // Terminal // Switch the user input from websocket to webrtc at this point. this.unpipe(this.httprequest.process.stdin); @@ -712,9 +730,10 @@ function createMeshCore(agent) { } else if (this.httprequest.protocol == 2) { // Desktop // Switch the user input from websocket to webrtc at this point. this.unpipe(this.httprequest.desktop.kvm); - this.webrtc.rtcchannel.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text. + try { this.webrtc.rtcchannel.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1 }); } catch (e) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text. this.resume(); // Resume the websocket to keep receiving control data } + this.write("{\"type\":\"webrtc2\"}"); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point. } else if (obj.type == 'webrtc2') { // Other side received websocket end of data marker, start sending data on WebRTC channel if (this.httprequest.protocol == 1) { // Terminal @@ -727,34 +746,17 @@ function createMeshCore(agent) { // This is a WebRTC offer. this.webrtc = rtc.createConnection(); this.webrtc.websocket = this; - this.webrtc.on('connected', function () { sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected'); }); - this.webrtc.on('disconnected', function () { sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected'); }); + this.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ }); + this.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ }); this.webrtc.on('dataChannel', function (rtcchannel) { - sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol); + //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol); rtcchannel.xrtc = this; rtcchannel.websocket = this.websocket; this.rtcchannel = rtcchannel; this.websocket.rtcchannel = rtcchannel; this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData); - this.websocket.rtcchannel.on('end', function () { sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed'); }); - if (this.websocket.httprequest.protocol == 1) { // Terminal - // This is a terminal data stream, unpipe the terminal now and indicate to the other side that terminal data will no longer be received over WebSocket - this.websocket.httprequest.process.stdout.unpipe(this.websocket); - this.websocket.httprequest.process.stderr.unpipe(this.websocket); - this.websocket.write("{\"type\":\"webrtc1\"}"); // End of data marker - } else if (this.websocket.httprequest.protocol == 2) { // Desktop - // This is a KVM data stream, unpipe the KVM now and indicate to the other side that KVM data will no longer be received over WebSocket - this.websocket.httprequest.desktop.kvm.unpipe(this.websocket); - this.websocket.write("{\"type\":\"webrtc1\"}"); // End of data marker - } - /* - else { - // Debug, just display on agent console - rtcchannel.on('data', function (buffer) { sendConsoleText("RTCReceived: " + buffer.length + " bytes"); }); - rtcchannel.on('end', function () { sendConsoleText("RTCChannel: " + this.name + " was closed"); }); - channel.write('WebRTC HELLO!'); - } - */ + this.websocket.rtcchannel.on('end', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');*/ }); + this.websocket.write("{\"type\":\"webrtc0\"}"); // Indicate we are ready for WebRTC switch-over. }); this.write({ type: "answer", sdp: this.webrtc.setOffer(obj.sdp) }); } diff --git a/agents/meshinstall-linux.sh b/agents/meshinstall-linux.sh index 1392fd5c..180045cd 100644 --- a/agents/meshinstall-linux.sh +++ b/agents/meshinstall-linux.sh @@ -87,7 +87,7 @@ DownloadAgent() { UpdateMshFile if [ $starttype -eq 1 ] then - echo -e "[Unit]\nDescription=MeshCentral Agent\n[Service]\nExecStart=/usr/local/mesh/meshagent\nStandardOutput=null\n[Install]\nWantedBy=multi-user.target\nAlias=meshagent.service\n" > /lib/systemd/system/meshagent.service + echo -e "[Unit]\nDescription=MeshCentral Agent\n[Service]\nExecStart=/usr/local/mesh/meshagent\nStandardOutput=null\nRestart=always\nRestartSec=3\n[Install]\nWantedBy=multi-user.target\nAlias=meshagent.service\n" > /lib/systemd/system/meshagent.service systemctl enable meshagent systemctl start meshagent else diff --git a/interceptor.js b/interceptor.js index 1c55262b..bc8d97c8 100644 --- a/interceptor.js +++ b/interceptor.js @@ -22,6 +22,7 @@ module.exports.CreateHttpInterceptor = function (args) { obj.args = args; obj.amt = { acc: "", mode: 0, count: 0, error: false }; // mode: 0:Header, 1:LengthBody, 2:ChunkedBody, 3:UntilClose obj.ws = { acc: "", mode: 0, count: 0, error: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 }; + obj.blockAmtStorage = false; // Private method obj.Debug = function (msg) { console.log(msg); } @@ -131,6 +132,8 @@ module.exports.CreateHttpInterceptor = function (args) { var headerlines = obj.ws.acc.substring(0, headerend).split('\r\n'); obj.ws.acc = obj.ws.acc.substring(headerend + 4); obj.ws.directive = headerlines[0].split(' '); + // If required, block access to amt-storage. This is needed when web storage is not supported on CIRA. + if ((obj.blockAmtStorage == true) && (obj.ws.directive.length > 1) && (obj.ws.directive[1].indexOf('/amt-storage') == 0)) { obj.ws.directive[1] = obj.ws.directive[1].replace('/amt-storage', '/amt-dummy-storage'); } var headers = headerlines.slice(1); obj.ws.headers = {}; obj.ws.mode = 3; // UntilClose diff --git a/meshagent.js b/meshagent.js index ef1574a1..fa45501d 100644 --- a/meshagent.js +++ b/meshagent.js @@ -442,7 +442,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { if (rights != null) { // TODO: Look at what rights are needed for message routing var sessions = obj.parent.wssessions[userid]; // Send the message to all users on this server - for (var i in sessions) { sessions[i].send(cmdstr); } + for (var i in sessions) { try { sessions[i].send(cmdstr); } catch (e) { } } } } } diff --git a/package.json b/package.json index 5c838af4..71493bd8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.1.3-x", + "version": "0.1.4-b", "keywords": [ "Remote Management", "Intel AMT", diff --git a/public/scripts/agent-desktop-0.0.2.js b/public/scripts/agent-desktop-0.0.2.js index 732dd772..3de618b7 100644 --- a/public/scripts/agent-desktop-0.0.2.js +++ b/public/scripts/agent-desktop-0.0.2.js @@ -173,12 +173,18 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { } obj.ProcessData = function (str) { + var ptr = 0; + while (ptr < str.length) { ptr += obj.ProcessDataEx(str.substring(ptr)); } + } + + obj.ProcessDataEx = function (str) { if (str.length < 4) return; var cmdmsg = null, X = 0, Y = 0, command = ReadShort(str, 0), cmdsize = ReadShort(str, 2); + if ((cmdsize != str.length) && (obj.debugmode == 1)) { console.log(cmdsize, str.length, cmdsize == str.length); } if (command >= 18) { console.error("Invalid KVM command " + command + " of size " + cmdsize); console.log("Invalid KVM data", str.length, str, rstr2hex(str)); return; } if (cmdsize > str.length) { console.error("KVM invalid command size", cmdsize, str.length); return; } //meshOnDebug("KVM Command: " + command + " Len:" + cmdsize); - if (obj.debugmode == 1) { console.log("KVM Command: " + command + " Len:" + cmdsize); } + //if (obj.debugmode == 1) { console.log("KVM Command: " + command + " Len:" + cmdsize); } if (command == 3 || command == 4 || command == 7) { cmdmsg = str.substring(4, cmdsize); @@ -244,6 +250,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) { if (obj.onMessage != null) obj.onMessage(str.substring(4, cmdsize), obj); break; } + return cmdsize; } // Keyboard and Mouse I/O. diff --git a/public/scripts/agent-redir-ws-0.1.0.js b/public/scripts/agent-redir-ws-0.1.0.js index 6749b4f1..481f528a 100644 --- a/public/scripts/agent-redir-ws-0.1.0.js +++ b/public/scripts/agent-redir-ws-0.1.0.js @@ -18,6 +18,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) { obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER, 4 = Files, 5 = FileTransfer obj.attemptWebRTC = false; obj.webRtcActive = false; + obj.webSwitchOk = false; obj.webrtc = null; obj.webchannel = null; obj.onStateChanged = null; @@ -56,6 +57,9 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) { if (obj.webrtc != null) { if (controlMsg.type == 'answer') { obj.webrtc.setRemoteDescription(new RTCSessionDescription(controlMsg), function () { /*console.log('WebRTC remote ok');*/ }, obj.xxCloseWebRTC); + } else if (controlMsg.type == 'webrtc0') { + obj.webSwitchOk = true; // Other side is ready for switch over + performWebRtcSwitch(); } else if (controlMsg.type == 'webrtc1') { obj.socket.send("{\"type\":\"webrtc2\"}"); // Confirm we got end of data marker, indicates data will no longer be received on websocket. } else if (controlMsg.type == 'webrtc2') { @@ -64,6 +68,14 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) { } } + function performWebRtcSwitch() { + if ((obj.webSwitchOk == true) && (obj.webRtcActive == true)) { + obj.socket.send("{\"type\":\"webrtc1\"}"); // Indicate to the other side that data traffic will no longer be sent over websocket. + // TODO: Hold/Stop sending data over websocket + if (obj.onStateChanged != null) { obj.onStateChanged(obj, obj.State); } + } + } + // Close the WebRTC connection, should be called if a problem occurs during WebRTC setup. obj.xxCloseWebRTC = function () { try { obj.webchannel.send("{\"type\":\"close\"}"); } catch (e) { } @@ -84,15 +96,12 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) { var configuration = null; //{ "iceServers": [ { 'urls': 'stun:stun.services.mozilla.com' }, { 'urls': 'stun:stun.l.google.com:19302' } ] }; if (typeof RTCPeerConnection !== 'undefined') { obj.webrtc = new RTCPeerConnection(configuration); } else if (typeof webkitRTCPeerConnection !== 'undefined') { obj.webrtc = new webkitRTCPeerConnection(configuration); } - if (obj.webrtc != null) { obj.webchannel = obj.webrtc.createDataChannel("DataChannel", {}); // { ordered: false, maxRetransmits: 2 } obj.webchannel.onmessage = function (event) { obj.xxOnMessage({ data: event.data }); }; obj.webchannel.onopen = function () { obj.webRtcActive = true; - obj.socket.send("{\"type\":\"webrtc1\"}"); // Indicate to the other side that data traffic will no longer be sent over websocket. - // TODO: Hold/Stop sending data over websocket - if (obj.onStateChanged != null) { obj.onStateChanged(obj, obj.State); } + performWebRtcSwitch(); }; obj.webchannel.onclose = function (event) { /*console.log('WebRTC close');*/ obj.Stop(); } obj.webrtc.onicecandidate = function (e) { @@ -104,7 +113,6 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) { } obj.webrtc.oniceconnectionstatechange = function () { if (obj.webrtc != null) { - //console.log(obj.webrtc.iceConnectionState) if ((obj.webrtc.iceConnectionState == 'disconnected') || (obj.webrtc.iceConnectionState == 'failed')) { obj.xxCloseWebRTC(); } } } @@ -205,8 +213,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) { obj.connectstate = -1; obj.xxCloseWebRTC(); if (obj.socket != null) { - try { obj.socket.send("{\"type\":\"close\"}"); } catch (e) { } - try { obj.socket.close(); } catch (e) { } + try { if (obj.socket.readyState == 1) { obj.socket.send("{\"type\":\"close\"}"); obj.socket.close(); } } catch (e) { } obj.socket = null; } obj.xxStateChange(0); diff --git a/views/default.handlebars b/views/default.handlebars index b9126222..c744de2a 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -650,7 +650,7 @@ var amtScanResults = null; var debugmode = false; var clickOnce = detectClickOnce(); - var attemptWebRTC = false; + var attemptWebRTC = true; function startup() { if ((features & 32) == 0) { @@ -663,7 +663,7 @@ // Check if we are in debug mode args = parseUriArgs(); debugmode = (args.debug == 1); - attemptWebRTC = (args.webrtc == 1); + //attemptWebRTC = (args.webrtc == 1); QV('p13AutoConnect', debugmode); // Files QV('autoconnectbutton2', debugmode); // Terminal QV('autoconnectbutton1', debugmode); // Desktop diff --git a/webserver.js b/webserver.js index 78f23638..12c0efec 100644 --- a/webserver.js +++ b/webserver.js @@ -1132,10 +1132,12 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate if (req.query.p == 1) { Debug(3, 'INTERCEPTOR1', { host: node.host, port: port, user: node.intelamt.user, pass: node.intelamt.pass }); ws.interceptor = obj.interceptor.CreateHttpInterceptor({ host: node.host, port: port, user: node.intelamt.user, pass: node.intelamt.pass }); + ws.interceptor.blockAmtStorage = true; } else if (req.query.p == 2) { Debug(3, 'INTERCEPTOR2', { user: node.intelamt.user, pass: node.intelamt.pass }); ws.interceptor = obj.interceptor.CreateRedirInterceptor({ user: node.intelamt.user, pass: node.intelamt.pass }); + ws.interceptor.blockAmtStorage = true; } return;