WebRTC is now used by default and fully automatic.

This commit is contained in:
Ylian Saint-Hilaire 2018-02-05 11:56:29 -08:00
parent c210b926bc
commit 143d4cb647
20 changed files with 61 additions and 40 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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) });
}

View File

@ -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

View File

@ -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

View File

@ -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) { } }
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.1.3-x",
"version": "0.1.4-b",
"keywords": [
"Remote Management",
"Intel AMT",

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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;