1
0
Fork 0
mirror of https://github.com/Ylianst/MeshCentral.git synced 2025-02-12 11:01:52 +00:00

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; var obj;
try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC'); return; } try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC'); return; }
if (obj.type == 'close') { 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.close(); } catch (e) { }
try { this.xrtc.close(); } catch (e) { } try { this.xrtc.close(); } catch (e) { }
} }
@ -692,7 +692,7 @@ function createMeshCore(agent) {
// Called when receiving control data on websocket // Called when receiving control data on websocket
function onTunnelControlData(data) { function onTunnelControlData(data) {
if (typeof data != 'string') return; if (typeof data != 'string') return;
sendConsoleText('onTunnelControlData: ' + data); //sendConsoleText('onTunnelControlData: ' + data);
//console.log('onTunnelControlData: ' + data); //console.log('onTunnelControlData: ' + data);
var obj; var obj;
@ -700,10 +700,28 @@ function createMeshCore(agent) {
if (obj.type == 'close') { if (obj.type == 'close') {
// We received the close on the websocket // 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) { } 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') { } 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 if (this.httprequest.protocol == 1) { // Terminal
// Switch the user input from websocket to webrtc at this point. // Switch the user input from websocket to webrtc at this point.
this.unpipe(this.httprequest.process.stdin); this.unpipe(this.httprequest.process.stdin);
@ -712,9 +730,10 @@ function createMeshCore(agent) {
} else if (this.httprequest.protocol == 2) { // Desktop } else if (this.httprequest.protocol == 2) { // Desktop
// Switch the user input from websocket to webrtc at this point. // Switch the user input from websocket to webrtc at this point.
this.unpipe(this.httprequest.desktop.kvm); 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.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') { } else if (obj.type == 'webrtc2') {
// Other side received websocket end of data marker, start sending data on WebRTC channel // Other side received websocket end of data marker, start sending data on WebRTC channel
if (this.httprequest.protocol == 1) { // Terminal if (this.httprequest.protocol == 1) { // Terminal
@ -727,34 +746,17 @@ function createMeshCore(agent) {
// This is a WebRTC offer. // This is a WebRTC offer.
this.webrtc = rtc.createConnection(); this.webrtc = rtc.createConnection();
this.webrtc.websocket = this; this.webrtc.websocket = this;
this.webrtc.on('connected', function () { sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected'); }); 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('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ });
this.webrtc.on('dataChannel', function (rtcchannel) { 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.xrtc = this;
rtcchannel.websocket = this.websocket; rtcchannel.websocket = this.websocket;
this.rtcchannel = rtcchannel; this.rtcchannel = rtcchannel;
this.websocket.rtcchannel = rtcchannel; this.websocket.rtcchannel = rtcchannel;
this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData); this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
this.websocket.rtcchannel.on('end', function () { sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed'); }); this.websocket.rtcchannel.on('end', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');*/ });
if (this.websocket.httprequest.protocol == 1) { // Terminal this.websocket.write("{\"type\":\"webrtc0\"}"); // Indicate we are ready for WebRTC switch-over.
// 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.write({ type: "answer", sdp: this.webrtc.setOffer(obj.sdp) }); this.write({ type: "answer", sdp: this.webrtc.setOffer(obj.sdp) });
} }

View file

@ -87,7 +87,7 @@ DownloadAgent() {
UpdateMshFile UpdateMshFile
if [ $starttype -eq 1 ] if [ $starttype -eq 1 ]
then 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 enable meshagent
systemctl start meshagent systemctl start meshagent
else else

View file

@ -22,6 +22,7 @@ module.exports.CreateHttpInterceptor = function (args) {
obj.args = args; obj.args = args;
obj.amt = { acc: "", mode: 0, count: 0, error: false }; // mode: 0:Header, 1:LengthBody, 2:ChunkedBody, 3:UntilClose 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.ws = { acc: "", mode: 0, count: 0, error: false, authCNonce: obj.randomValueHex(10), authCNonceCount: 1 };
obj.blockAmtStorage = false;
// Private method // Private method
obj.Debug = function (msg) { console.log(msg); } 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'); var headerlines = obj.ws.acc.substring(0, headerend).split('\r\n');
obj.ws.acc = obj.ws.acc.substring(headerend + 4); obj.ws.acc = obj.ws.acc.substring(headerend + 4);
obj.ws.directive = headerlines[0].split(' '); 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); var headers = headerlines.slice(1);
obj.ws.headers = {}; obj.ws.headers = {};
obj.ws.mode = 3; // UntilClose 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 if (rights != null) { // TODO: Look at what rights are needed for message routing
var sessions = obj.parent.wssessions[userid]; var sessions = obj.parent.wssessions[userid];
// Send the message to all users on this server // 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", "name": "meshcentral",
"version": "0.1.3-x", "version": "0.1.4-b",
"keywords": [ "keywords": [
"Remote Management", "Remote Management",
"Intel AMT", "Intel AMT",

View file

@ -173,12 +173,18 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
} }
obj.ProcessData = function (str) { 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; if (str.length < 4) return;
var cmdmsg = null, X = 0, Y = 0, command = ReadShort(str, 0), cmdsize = ReadShort(str, 2); 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 (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; } if (cmdsize > str.length) { console.error("KVM invalid command size", cmdsize, str.length); return; }
//meshOnDebug("KVM Command: " + command + " Len:" + cmdsize); //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) { if (command == 3 || command == 4 || command == 7) {
cmdmsg = str.substring(4, cmdsize); 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); if (obj.onMessage != null) obj.onMessage(str.substring(4, cmdsize), obj);
break; break;
} }
return cmdsize;
} }
// Keyboard and Mouse I/O. // 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.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER, 4 = Files, 5 = FileTransfer
obj.attemptWebRTC = false; obj.attemptWebRTC = false;
obj.webRtcActive = false; obj.webRtcActive = false;
obj.webSwitchOk = false;
obj.webrtc = null; obj.webrtc = null;
obj.webchannel = null; obj.webchannel = null;
obj.onStateChanged = null; obj.onStateChanged = null;
@ -56,6 +57,9 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
if (obj.webrtc != null) { if (obj.webrtc != null) {
if (controlMsg.type == 'answer') { if (controlMsg.type == 'answer') {
obj.webrtc.setRemoteDescription(new RTCSessionDescription(controlMsg), function () { /*console.log('WebRTC remote ok');*/ }, obj.xxCloseWebRTC); 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') { } 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. 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') { } 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. // Close the WebRTC connection, should be called if a problem occurs during WebRTC setup.
obj.xxCloseWebRTC = function () { obj.xxCloseWebRTC = function () {
try { obj.webchannel.send("{\"type\":\"close\"}"); } catch (e) { } 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' } ] }; 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); } if (typeof RTCPeerConnection !== 'undefined') { obj.webrtc = new RTCPeerConnection(configuration); }
else if (typeof webkitRTCPeerConnection !== 'undefined') { obj.webrtc = new webkitRTCPeerConnection(configuration); } else if (typeof webkitRTCPeerConnection !== 'undefined') { obj.webrtc = new webkitRTCPeerConnection(configuration); }
if (obj.webrtc != null) { if (obj.webrtc != null) {
obj.webchannel = obj.webrtc.createDataChannel("DataChannel", {}); // { ordered: false, maxRetransmits: 2 } obj.webchannel = obj.webrtc.createDataChannel("DataChannel", {}); // { ordered: false, maxRetransmits: 2 }
obj.webchannel.onmessage = function (event) { obj.xxOnMessage({ data: event.data }); }; obj.webchannel.onmessage = function (event) { obj.xxOnMessage({ data: event.data }); };
obj.webchannel.onopen = function () { obj.webchannel.onopen = function () {
obj.webRtcActive = true; obj.webRtcActive = true;
obj.socket.send("{\"type\":\"webrtc1\"}"); // Indicate to the other side that data traffic will no longer be sent over websocket. performWebRtcSwitch();
// TODO: Hold/Stop sending data over websocket
if (obj.onStateChanged != null) { obj.onStateChanged(obj, obj.State); }
}; };
obj.webchannel.onclose = function (event) { /*console.log('WebRTC close');*/ obj.Stop(); } obj.webchannel.onclose = function (event) { /*console.log('WebRTC close');*/ obj.Stop(); }
obj.webrtc.onicecandidate = function (e) { obj.webrtc.onicecandidate = function (e) {
@ -104,7 +113,6 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
} }
obj.webrtc.oniceconnectionstatechange = function () { obj.webrtc.oniceconnectionstatechange = function () {
if (obj.webrtc != null) { if (obj.webrtc != null) {
//console.log(obj.webrtc.iceConnectionState)
if ((obj.webrtc.iceConnectionState == 'disconnected') || (obj.webrtc.iceConnectionState == 'failed')) { obj.xxCloseWebRTC(); } 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.connectstate = -1;
obj.xxCloseWebRTC(); obj.xxCloseWebRTC();
if (obj.socket != null) { if (obj.socket != null) {
try { obj.socket.send("{\"type\":\"close\"}"); } catch (e) { } try { if (obj.socket.readyState == 1) { obj.socket.send("{\"type\":\"close\"}"); obj.socket.close(); } } catch (e) { }
try { obj.socket.close(); } catch (e) { }
obj.socket = null; obj.socket = null;
} }
obj.xxStateChange(0); obj.xxStateChange(0);

View file

@ -650,7 +650,7 @@
var amtScanResults = null; var amtScanResults = null;
var debugmode = false; var debugmode = false;
var clickOnce = detectClickOnce(); var clickOnce = detectClickOnce();
var attemptWebRTC = false; var attemptWebRTC = true;
function startup() { function startup() {
if ((features & 32) == 0) { if ((features & 32) == 0) {
@ -663,7 +663,7 @@
// Check if we are in debug mode // Check if we are in debug mode
args = parseUriArgs(); args = parseUriArgs();
debugmode = (args.debug == 1); debugmode = (args.debug == 1);
attemptWebRTC = (args.webrtc == 1); //attemptWebRTC = (args.webrtc == 1);
QV('p13AutoConnect', debugmode); // Files QV('p13AutoConnect', debugmode); // Files
QV('autoconnectbutton2', debugmode); // Terminal QV('autoconnectbutton2', debugmode); // Terminal
QV('autoconnectbutton1', debugmode); // Desktop QV('autoconnectbutton1', debugmode); // Desktop

View file

@ -1132,10 +1132,12 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
if (req.query.p == 1) { if (req.query.p == 1) {
Debug(3, 'INTERCEPTOR1', { host: node.host, port: port, user: node.intelamt.user, pass: node.intelamt.pass }); 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 = 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) { else if (req.query.p == 2) {
Debug(3, 'INTERCEPTOR2', { user: node.intelamt.user, pass: node.intelamt.pass }); 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 = obj.interceptor.CreateRedirInterceptor({ user: node.intelamt.user, pass: node.intelamt.pass });
ws.interceptor.blockAmtStorage = true;
} }
return; return;