First pass at adding WebRTC support.

Ylian Saint-Hilaire committed Jan 16, 2018 at 17:30 UTC 92aaf754fb11d3313d223276c29c181f1cfed926
12 files changed +163 -140
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
agents/agent_debug/MeshService.exe
Binary files /dev/null and b/agents/agent_debug/MeshService.exe differ
agents/agent_debug/MeshService64.exe
Binary files /dev/null and b/agents/agent_debug/MeshService64.exe differ
agents/meshcore.js
+77 -75
@@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 - http://www.apache.org/licenses/LICENSE-2.0
8 + http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
@@ -20,7 +20,6 @@ function createMeshCore(agent) {
20 // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
21 obj.meshCoreInfo = "MeshCore v4";
22 obj.meshCoreCapabilities = 14; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript
23 - obj.useNativePipes = true; //(process.platform == 'win32');
23 var meshServerConnectionState = 0;
24 var tunnels = {};
25 var lastSelfInfo = null;
@@ -54,6 +53,7 @@ function createMeshCore(agent) {
53 scan.scan("10.2.55.128/25", 2000);
54 */
55
56 + /*
57 // Try to load up the network monitor
58 try {
59 networkMonitor = require('NetworkMonitor');
@@ -61,6 +61,7 @@ function createMeshCore(agent) {
61 networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
62 networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
63 } catch (e) { networkMonitor = null; }
64 + */
65
66 // Try to load up the Intel AMT scanner
67 try {
@@ -506,28 +507,7 @@ function createMeshCore(agent) {
507 if (len > 0) { this.write(buf.slice(0, len)); } else { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; this.end(); }
508 return;
509 }
509 -
510 - // Setup remote desktop & terminal without using native pipes
511 - if ((this.httprequest.desktop) && (obj.useNativePipes == false)) {
512 - if (data.length > 21 && data.toString().startsWith('**********%%%%%%###**')) {
513 - var controlMsg = JSON.parse(data.toString().substring(21));
514 - if (controlMsg.type == 'offer') {
515 - this.webrtc = rtc.createConnection();
516 - this.webrtc.on('connected', function () { sendConsoleText('OnWebRTC_Connected'); });
517 - this.webrtc.on('dataChannel', function () { sendConsoleText('OnWebRTC_DataChannel'); });
518 - var counterOffer = this.webrtc.setOffer(controlMsg.sdp);
519 - this.write('**********%%%%%%###**' + JSON.stringify({ type: 'answer', sdp: counterOffer }));
520 - sendConsoleText('counterOfferSent');
521 - } else {
522 - sendConsoleText(JSON.stringify(controlMsg));
523 - }
524 - } else {
525 - this.httprequest.desktop.kvm.write(data);
526 - }
527 - return;
528 - }
529 - if ((this.httprequest.terminal) && (obj.useNativePipes == false)) { this.httprequest.terminal.write(data); return; }
530 -
510 +
511 if (this.httprequest.state == 0) {
512 // Check if this is a relay connection
513 if (data == 'c') { this.httprequest.state = 1; sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid); }
@@ -538,59 +518,39 @@ function createMeshCore(agent) {
518 this.httprequest.protocol = parseInt(data);
519 if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
520 if (this.httprequest.protocol == 1) {
541 - if (obj.useNativePipes == false) {
542 - // Remote Terminal without using native pipes
543 - if (process.platform == "win32") {
544 - this.httprequest.terminal = childProcess.execFile("%windir%\\system32\\cmd.exe");
545 - } else {
546 - this.httprequest.terminal = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
547 - }
548 - this.httprequest.terminal.tunnel = this;
549 - this.httprequest.terminal.on('exit', function (ecode, sig) { this.tunnel.end(); });
550 - this.httprequest.terminal.stdout.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
551 - this.httprequest.terminal.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
521 + // Remote terminal using native pipes
522 + if (process.platform == "win32") {
523 + this.httprequest.process = childProcess.execFile("%windir%\\system32\\cmd.exe");
524 } else {
553 - // Remote terminal using native pipes
554 - if (process.platform == "win32") {
555 - this.httprequest.process = childProcess.execFile("%windir%\\system32\\cmd.exe");
556 - } else {
557 - this.httprequest.process = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
558 - }
559 - this.httprequest.process.tunnel = this;
560 - this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
561 - this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
562 - this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
563 - this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
564 - this.prependListener('end', function () { this.httprequest.process.kill(); });
525 + this.httprequest.process = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
526 }
527 + this.httprequest.process.tunnel = this;
528 + this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
529 + this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
530 + this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
531 + this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
532 + this.prependListener('end', function () { this.httprequest.process.kill(); });
533 + this.removeAllListeners('data');
534 + this.on('data', onTunnelControlData);
535 + //this.write('MeshCore Terminal Hello!1');
536 }
537 if (this.httprequest.protocol == 2) {
568 - if (obj.useNativePipes == false) {
569 - // Remote Desktop without using native pipes
570 - this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this };
571 - this.httprequest.desktop.kvm.tunnel = this;
572 - this.httprequest.desktop.kvm.on('data', function (data) { this.tunnel.write(data); });
573 - this.desktop = this.httprequest.desktop;
574 - this.end = function () { if (--this.desktop.kvm.connectionCount == 0) { this.httprequest.desktop.kvm.end(); } };
575 - if (this.httprequest.desktop.kvm.hasOwnProperty("connectionCount")) { this.httprequest.desktop.kvm.connectionCount++; } else { this.httprequest.desktop.kvm.connectionCount = 1; }
576 - } else {
577 - // Remote desktop using native pipes
578 - this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this };
579 - this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
580 - this.desktop = this.httprequest.desktop;
581 - this.end = function () {
582 - --this.desktop.kvm.connectionCount;
583 - this.unpipe(this.httprequest.desktop.kvm);
584 - this.httprequest.desktop.kvm.unpipe(this);
585 - if (this.desktop.kvm.connectionCount == 0) { this.httprequest.desktop.kvm.end(); }
586 - };
587 - if (this.httprequest.desktop.kvm.hasOwnProperty("connectionCount")) { this.httprequest.desktop.kvm.connectionCount++; } else { this.httprequest.desktop.kvm.connectionCount = 1; }
588 - //this.write('Hello!');
589 - //sendConsoleText('KVM WriteHello');
590 - this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
591 - this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
592 - //this.on('data', function (data) { sendConsoleText('KVM: ' + data); });
593 - }
538 + // Remote desktop using native pipes
539 + this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this };
540 + this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
541 + this.desktop = this.httprequest.desktop;
542 + this.end = function () {
543 + --this.desktop.kvm.connectionCount;
544 + this.unpipe(this.httprequest.desktop.kvm);
545 + this.httprequest.desktop.kvm.unpipe(this);
546 + if (this.desktop.kvm.connectionCount == 0) { this.httprequest.desktop.kvm.end(); }
547 + };
548 + if (this.httprequest.desktop.kvm.hasOwnProperty("connectionCount")) { this.httprequest.desktop.kvm.connectionCount++; } else { this.httprequest.desktop.kvm.connectionCount = 1; }
549 + this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
550 + this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
551 + this.removeAllListeners('data');
552 + this.on('data', onTunnelControlData);
553 + //this.write('MeshCore KVM Hello!1');
554 }
555 else if (this.httprequest.protocol == 5) {
556 // Setup files
@@ -700,7 +660,49 @@ function createMeshCore(agent) {
660 //sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
661 }
662 }
703 -
663 +
664 + // Attempt to setup and switch the tunnel over to WebRTC
665 + function onTunnelControlData(data) {
666 + sendConsoleText('onTunnelControlData: ' + data);
667 + var obj = JSON.parse(data);
668 + if (obj.type == 'offer') {
669 + // This is a WebRTC offer.
670 + this.webrtc = rtc.createConnection();
671 + this.webrtc.websocket = this;
672 + this.webrtc.on('connected', function () { sendConsoleText('WebRTC connected'); });
673 + this.webrtc.on('dataChannel', function (rtcchannel) {
674 + sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
675 + this.rtcchannel = rtcchannel;
676 + if (this.websocket.httprequest.protocol == 1) { // Terminal
677 + // This is a terminal data stream, re-setup the pipes
678 + // Un-pipe
679 + this.websocket.unpipe(this.websocket.httprequest.process.stdin);
680 + //this.websocket.httprequest.process.stdout.unpipe(this.websocket);
681 + // Re-pipe
682 + rtcchannel.pipe(this.websocket.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
683 + //this.websocket.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
684 + } else if (this.websocket.httprequest.protocol == 2) { // Desktop
685 + // This is a KVM data stream, re-setup the pipes
686 + // Un-pipe
687 + this.websocket.unpipe(this.websocket.httprequest.desktop.kvm);
688 + //this.websocket.httprequest.desktop.kvm.unpipe(this.websocket);
689 + // Re-pipe
690 + rtcchannel.pipe(this.websocket.httprequest.desktop.kvm, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
691 + //this.websocket.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
692 + }
693 + /*
694 + else {
695 + // Debug, just display on agent console
696 + rtcchannel.on('data', function (buffer) { sendConsoleText("RTCReceived: " + buffer.length + " bytes"); });
697 + rtcchannel.on('end', function () { sendConsoleText("RTCChannel: " + this.name + " was closed"); });
698 + channel.write('WebRTC HELLO!');
699 + }
700 + */
701 + });
702 + this.write({ type: "answer", sdp: this.webrtc.setOffer(obj.sdp) });
703 + }
704 + }
705 +
706 // Console state
707 var consoleWebSockets = {};
708 var consoleHttpRequest = null;
@@ -740,7 +742,7 @@ function createMeshCore(agent) {
742 break;
743 }
744 case 'info': { // Return information about the agent and agent core module
743 - response = 'Current Core: ' + obj.meshCoreInfo + '.\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform Info: ' + process.platform + '.\r\nCapabilities: ' + obj.meshCoreCapabilities + '.\r\nNative Pipes: ' + obj.useNativePipes + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
745 + response = 'Current Core: ' + obj.meshCoreInfo + '.\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform Info: ' + process.platform + '.\r\nCapabilities: ' + obj.meshCoreCapabilities + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
746 if (amtLmsState >= 0) { response += '\r\nBuilt -in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amtLmsState] + '.'; }
747 response += '\r\nModules: ' + JSON.stringify(addedModules) + '';
748 response += '\r\nServerConnected: ' + mesh.isControlChannelConnected + '';
certoperations.js
+14 -12
@@ -203,17 +203,19 @@ module.exports.CertificateOperations = function () {
203 }
204
205 // If CA certificates are present, load them
206 - var caok, caindex = 1, calist = [];
207 - do {
208 - caok = false;
209 - if (obj.fileExists(directory + '/webserver-cert-chain' + caindex + '.crt')) {
210 - var caCertificate = obj.fs.readFileSync(directory + '/webserver-cert-chain' + caindex + '.crt', 'utf8');
211 - calist.push(caCertificate);
212 - caok = true;
213 - }
214 - caindex++;
215 - } while (caok == true);
216 - r.web.ca = calist;
206 + if (r.web != null) {
207 + var caok, caindex = 1, calist = [];
208 + do {
209 + caok = false;
210 + if (obj.fileExists(directory + '/webserver-cert-chain' + caindex + '.crt')) {
211 + var caCertificate = obj.fs.readFileSync(directory + '/webserver-cert-chain' + caindex + '.crt', 'utf8');
212 + calist.push(caCertificate);
213 + caok = true;
214 + }
215 + caindex++;
216 + } while (caok == true);
217 + r.web.ca = calist;
218 + }
219
220 // Decode certificate arguments
221 var commonName = 'un-configured', country, organization, forceWebCertGen = 0;
@@ -374,7 +376,7 @@ module.exports.CertificateOperations = function () {
376 amtConsoleName = consoleCertAndKey.cert.subject.getField('CN').value;
377 }
378
377 - var r = { root: { cert: rootCertificate, key: rootPrivateKey }, web: { cert: webCertificate, key: webPrivateKey }, mps: { cert: mpsCertificate, key: mpsPrivateKey }, agent: { cert: agentCertificate, key: agentPrivateKey }, console: { cert: consoleCertificate, key: consolePrivateKey }, ca: calist, CommonName: commonName, RootName: rootName, AmtConsoleName: amtConsoleName, dns: {} };
379 + var r = { root: { cert: rootCertificate, key: rootPrivateKey }, web: { cert: webCertificate, key: webPrivateKey, ca: [] }, mps: { cert: mpsCertificate, key: mpsPrivateKey }, agent: { cert: agentCertificate, key: agentPrivateKey }, console: { cert: consoleCertificate, key: consolePrivateKey }, ca: calist, CommonName: commonName, RootName: rootName, AmtConsoleName: amtConsoleName, dns: {} };
380
381 // Look for domains with DNS names that have no certificates and generated them.
382 for (var i in config.domains) {
interceptor.js
+21 -10
@@ -183,21 +183,32 @@ module.exports.CreateHttpInterceptor = function (args) {
183 obj.ws.count -= rl;
184 if (obj.ws.count == 0) { obj.ws.mode = 0; }
185 return r;
186 - } else if (obj.ws.mode == 2) { // Chunked Body Mode
186 + } else if (obj.amt.mode == 2) { // Chunked Body Mode
187 // Send data one chunk at a time
188 - var headerend = obj.ws.acc.indexOf('\r\n');
188 + var headerend = obj.amt.acc.indexOf('\r\n');
189 if (headerend < 0) return "";
190 - var chunksize = parseInt(obj.ws.acc.substring(0, headerend), 16);
191 - if (chunksize == 0 && obj.ws.acc.length >= headerend + 4) {
190 + var chunksize = parseInt(obj.amt.acc.substring(0, headerend), 16);
191 + if (isNaN(chunksize)) { // TODO: Check this path
192 + // Chunk is not in this batch, move one
193 + var r = obj.amt.acc.substring(0, headerend + 2);
194 + obj.amt.acc = obj.amt.acc.substring(headerend + 2);
195 + // Peek if we next is the end of chunked transfer
196 + headerend = obj.amt.acc.indexOf('\r\n');
197 + if (headerend > 0) {
198 + chunksize = parseInt(obj.amt.acc.substring(0, headerend), 16);
199 + if (chunksize == 0) { obj.amt.mode = 0; }
200 + }
201 + return r;
202 + } else if (chunksize == 0 && obj.amt.acc.length >= headerend + 4) {
203 // Send the ending chunk (NOTE: We do not support trailing headers)
193 - var r = obj.ws.acc.substring(0, headerend + 4);
194 - obj.ws.acc = obj.ws.acc.substring(headerend + 4);
195 - obj.ws.mode = 0;
204 + var r = obj.amt.acc.substring(0, headerend + 4);
205 + obj.amt.acc = obj.amt.acc.substring(headerend + 4);
206 + obj.amt.mode = 0;
207 return r;
197 - } else if (chunksize > 0 && obj.ws.acc.length >= headerend + 4) {
208 + } else if (chunksize > 0 && obj.amt.acc.length >= headerend + 4) {
209 // Send a chunk
199 - var r = obj.ws.acc.substring(0, headerend + chunksize + 4);
200 - obj.ws.acc = obj.ws.acc.substring(headerend + chunksize + 4);
210 + var r = obj.amt.acc.substring(0, headerend + chunksize + 4);
211 + obj.amt.acc = obj.amt.acc.substring(headerend + chunksize + 4);
212 return r;
213 }
214 } else if (obj.ws.mode == 3) { // Until Close Mode
meshrelay.js
+6 -6
@@ -175,16 +175,16 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) {
175 }
176 }
177 }
178 -
179 - ws.flushSink = function () {
180 - try { ws.resume(); } catch (e) { }
181 - };
178 +
179 + ws.flushSink = function () { try { ws.resume(); } catch (e) { } };
180
181 // When data is received from the mesh relay web socket
182 ws.on('message', function (data) {
183 //console.log(typeof data, data.length);
186 - //if (typeof data == 'string') console.log(data);
187 - if (this.peer != null) { try { this.pause(); this.peer.send(data, ws.flushSink); } catch (e) { } }
184 + if (this.peer != null) {
185 + //if (typeof data == 'string') { console.log('Relay: ' + data); }
186 + try { this.pause(); this.peer.send(data, ws.flushSink); } catch (e) { }
187 + }
188 });
189
190 // If error, do nothing
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.1.2-s",
3 + "version": "0.1.2-t",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
public/scripts/agent-desktop-0.0.2.js
+1 -1
@@ -172,7 +172,7 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
172 obj.ProcessData = function (str) {
173 if (str.length < 4) return;
174 var cmdmsg = null, X = 0, Y = 0, command = ReadShort(str, 0), cmdsize = ReadShort(str, 2);
175 - if (command >= 18) { console.error("Invalid KVM command " + command + " of size " + cmdsize); obj.parent.Stop(); return; }
175 + if (command >= 18) { console.error("Invalid KVM command " + command + " of size " + cmdsize); console.log("Invalid KVM data", str.length, str, rstr2hex(str)); return; }
176 if (cmdsize > str.length) { console.error("KVM invalid command size", cmdsize, str.length); return; }
177 //meshOnDebug("KVM Command: " + command + " Len:" + cmdsize);
178 if (obj.debugmode == 1) { console.log("KVM Command: " + command + " Len:" + cmdsize); }
public/scripts/agent-redir-ws-0.1.0.js
+27 -27
@@ -17,6 +17,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
17 obj.tunnelid = Math.random().toString(36).substring(2); // Generate a random client tunnel id
18 obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER, 4 = Files, 5 = FileTransfer
19 obj.attemptWebRTC = false;
20 + obj.webRtcActive = false;
21 obj.webrtc = null;
22 obj.webchannel = null;
23 obj.onStateChanged = null;
@@ -49,12 +50,10 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
50
51 // Called to pass websocket control messages
52 obj.xxOnControlCommand = function (msg) {
52 - //console.log(msg);
53 - //obj.socket.send('hellobob');
53 var controlMsg = JSON.parse(msg);
54 if ((controlMsg.type == 'answer') && (obj.webrtc != null)) {
56 - console.log('gotAnswer', JSON.stringify(controlMsg));
57 - obj.webrtc.setRemoteDescription(new RTCSessionDescription(controlMsg), function () { console.log('WebRTC remote ok'); }, obj.xxCloseWebRTC);
55 + //console.log('gotAnswer', JSON.stringify(controlMsg));
56 + obj.webrtc.setRemoteDescription(new RTCSessionDescription(controlMsg), function () { /*console.log('WebRTC remote ok');*/ }, obj.xxCloseWebRTC);
57 }
58 }
59
@@ -65,7 +64,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
64 }
65
66 obj.xxOnMessage = function (e) {
68 - if (obj.debugmode == 1) { console.log('Recv', e.data); }
67 + //if (obj.debugmode == 1) { console.log('Recv', e.data); }
68 if (obj.State < 3) {
69 if (e.data == 'c') {
70 obj.socket.send(obj.protocol);
@@ -79,28 +78,27 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
78
79 if (obj.webrtc != null) {
80 obj.webchannel = obj.webrtc.createDataChannel("DataChannel", {}); // { ordered: false, maxRetransmits: 2 }
82 - obj.webchannel.onmessage = function (event) { console.log("DataChannel - onmessage", event.data); };
83 - obj.webchannel.onopen = function () { console.log("DataChannel - onopen"); };
84 - obj.webchannel.onclose = function (event) { console.log("DataChannel - onclose"); }
85 - obj.webrtc.ondatachannel = function (e) { console.log('ondatachannel'); } // TODO: Should not be needed
81 + obj.webchannel.onmessage = function (event) { console.log("DataChannel - onmessage", event.data); obj.xxOnMessage(event.data); };
82 + obj.webchannel.onopen = function () { obj.webRtcActive = true; if (obj.onStateChanged != null) { obj.onStateChanged(obj, obj.State); } /*obj.webchannel.send("Browser WebRTC Hello!!!");*/ };
83 + obj.webchannel.onclose = function (event) { obj.Stop(); }
84 obj.webrtc.onicecandidate = function (e) {
85 if (e.candidate == null) {
88 - console.log('createOffer', JSON.stringify(obj.webrtcoffer));
89 - obj.socket.send('**********%%%%%%###**' + JSON.stringify(obj.webrtcoffer)); // End of candidates, send the offer
86 + //console.log('createOffer', JSON.stringify(obj.webrtcoffer));
87 + obj.socket.send(JSON.stringify(obj.webrtcoffer)); // End of candidates, send the offer
88 } else {
89 obj.webrtcoffer.sdp += ("a=" + e.candidate.candidate + "\r\n"); // New candidate, add it to the SDP
90 }
91 }
92 obj.webrtc.oniceconnectionstatechange = function () {
93 if (obj.webrtc != null) {
96 - console.log('oniceconnectionstatechange', obj.webrtc.iceConnectionState);
94 + //console.log('WebRTC ICE', obj.webrtc.iceConnectionState);
95 if ((obj.webrtc.iceConnectionState == 'disconnected') || (obj.webrtc.iceConnectionState == 'failed')) { obj.xxCloseWebRTC(); }
96 }
97 }
98 obj.webrtc.createOffer(function (offer) {
99 // Got the offer
100 obj.webrtcoffer = offer;
103 - obj.webrtc.setLocalDescription(offer, function () { console.log('WebRTC local ok'); }, obj.xxCloseWebRTC);
101 + obj.webrtc.setLocalDescription(offer, function () { /*console.log('WebRTC local ok');*/ }, obj.xxCloseWebRTC);
102 }, obj.xxCloseWebRTC, { mandatory: { OfferToReceiveAudio: false, OfferToReceiveVideo: false } });
103 }
104 }
@@ -108,10 +106,14 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
106 return;
107 }
108 }
109 +
110 if (typeof e.data == 'string') {
111 // Control messages, most likely WebRTC setup
112 obj.xxOnControlCommand(e.data);
114 - } else if (typeof e.data == 'object') {
113 + return;
114 + }
115 +
116 + if (typeof e.data == 'object') {
117 var f = new FileReader();
118 if (f.readAsBinaryString) {
119 // Chrome & Firefox (Draft)
@@ -138,7 +140,6 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
140
141 obj.xxOnSocketData = function (data) {
142 if (!data || obj.connectstate == -1) return;
141 -
143 if (typeof data === 'object') {
144 // This is an ArrayBuffer, convert it to a string array (used in IE)
145 var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
@@ -146,39 +147,35 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
147 data = binary;
148 }
149 else if (typeof data !== 'string') return;
149 -
150 - // TODO: Don't use a prefix anymore, use string encoding instead
151 - if (data.length > 21 && data.startsWith('**********%%%%%%###**')) { obj.xxOnControlCommand(data.substring(21)); return; }
152 -
150 //console.log("xxOnSocketData", rstr2hex(data));
154 -
151 return obj.m.ProcessData(data);
152 }
153
154 obj.Send = function (x) {
159 - //obj.debug("Agent Redir Send(" + x.length + "): " + rstr2hex(x));
155 + //obj.debug("Agent Redir Send(" + obj.webRtcActive + ", " + x.length + "): " + rstr2hex(x));
156 + //console.log("Agent Redir Send(" + obj.webRtcActive + ", " + x.length + "): " + rstr2hex(x));
157 if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
158 if (typeof x == 'string') {
159 if (obj.debugmode == 1) {
160 var b = new Uint8Array(x.length), c = [];
161 for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); c.push(x.charCodeAt(i)); }
165 - obj.socket.send(b.buffer);
166 - console.log('Send', c);
162 + if (obj.webRtcActive == true) { obj.webchannel.send(b.buffer); } else { obj.socket.send(b.buffer); }
163 + //console.log('Send', c);
164 } else {
165 var b = new Uint8Array(x.length);
166 for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
170 - obj.socket.send(b.buffer);
167 + if (obj.webRtcActive == true) { obj.webchannel.send(b.buffer); } else { obj.socket.send(b.buffer); }
168 }
169 } else {
173 - if (obj.debugmode == 1) { console.log('Send', x); }
174 - obj.socket.send(x);
170 + //if (obj.debugmode == 1) { console.log('Send', x); }
171 + if (obj.webRtcActive == true) { obj.webchannel.send(x); } else { obj.socket.send(x); }
172 }
173 }
174 }
175
176 obj.xxOnSocketClosed = function () {
177 //obj.debug("Agent Redir Socket Closed");
181 - if (obj.debugmode == 1) { console.log('onSocketClosed'); }
178 + //if (obj.debugmode == 1) { console.log('onSocketClosed'); }
179 obj.Stop(1);
180 }
181
@@ -192,6 +189,9 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
189 obj.Stop = function (x) {
190 if (obj.debugmode == 1) { console.log('stop', x); }
191 //obj.debug("Agent Redir Socket Stopped");
192 + obj.webRtcActive = false;
193 + obj.webrtc = null;
194 + obj.webchannel = null;
195 obj.xxStateChange(0);
196 obj.connectstate = -1;
197 obj.xxCloseWebRTC();
views/default.handlebars
+16 -8
@@ -647,6 +647,7 @@
647 var amtScanResults = null;
648 var debugmode = false;
649 var clickOnce = detectClickOnce();
650 + var attemptWebRTC = false;
651
652 function startup() {
653 if ((features & 32) == 0) {
@@ -2934,7 +2935,7 @@
2935 desktop = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort);
2936 desktop.debugmode = debugmode;
2937 desktop.m.debugmode = debugmode;
2937 - //desktop.attemptWebRTC = debugmode;
2938 + desktop.attemptWebRTC = attemptWebRTC;
2939 desktop.onStateChanged = onDesktopStateChange;
2940 desktop.m.CompressionLevel = desktopsettings.quality; // Number from 1 to 100. 50 or less is best.
2941 desktop.m.ScalingLevel = desktopsettings.scaling;
@@ -2954,7 +2955,10 @@
2955 function onDesktopStateChange(xdesktop, state) {
2956 var xstate = state;
2957 if ((xstate == 3) && (xdesktop.contype == 2)) { xstate++; }
2957 - QH('deskstatus', StatusStrs[xstate]);
2958 + var str = StatusStrs[xstate];
2959 + if (desktop.webRtcActive == true) { str += ', WebRTC'; }
2960 + //if (desktop.m.stopInput == true) { str += ', Loopback'; }
2961 + QH('deskstatus', str);
2962 QE('deskSaveBtn', state == 3);
2963 QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (state != 0) && (desktopsettings.showfocus));
2964 QE('DeskCAD', state == 3);
@@ -3143,7 +3147,9 @@
3147 function onTerminalStateChange(xterminal, state) {
3148 var xstate = state;
3149 if ((xstate == 3) && (xterminal.contype == 2)) { xstate++; }
3146 - QH('termstatus', StatusStrs[xstate]);
3150 + var str = StatusStrs[xstate];
3151 + if (terminal.webRtcActive == true) { str += ', WebRTC'; }
3152 + QH('termstatus', str);
3153 switch (state) {
3154 case 0:
3155 // Disconnected, clear the terminal
@@ -3180,7 +3186,7 @@
3186 terminal = CreateAgentRedirect(meshserver, CreateAmtRemoteTerminal('Term'), serverPublicNamePort);
3187 terminal.debugmode = debugmode;
3188 terminal.m.debugmode = debugmode;
3183 - //terminal.attemptWebRTC = debugmode;
3189 + terminal.attemptWebRTC = attemptWebRTC;
3190 terminal.onStateChanged = onTerminalStateChange;
3191 terminal.Start(terminalNode._id);
3192 terminal.contype = 1;
@@ -3251,7 +3257,9 @@
3257
3258 function onFilesStateChange(xfiles, state) {
3259 p13Connect.value = (state == 0) ? 'Connect' : 'Disconnect';
3254 - Q('p13Status').textContent = StatusStrs[state];
3260 + var str = StatusStrs[state];
3261 + if (files.webRtcActive == true) { str += ', WebRTC'; }
3262 + Q('p13Status').textContent = str;
3263 switch (state) {
3264 case 0:
3265 // Disconnected, clear the files
@@ -3286,7 +3294,7 @@
3294 if (!files) {
3295 // Setup a mesh agent files
3296 files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort);
3289 - files.attemptWebRTC = debugmode;
3297 + files.attemptWebRTC = false;
3298 files.onStateChanged = onFilesStateChange;
3299 files.Start(filesNode._id);
3300 } else {
@@ -3492,7 +3500,7 @@
3500 // Called by the html page to start a download, arguments are: path, file name and file size.
3501 function p13downloadfile(x, y, z) {
3502 downloadFile = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotDownloadData), serverPublicNamePort); // Create our file transport
3495 - downloadFile.attemptWebRTC = debugmode;
3503 + downloadFile.attemptWebRTC = false;
3504 downloadFile.onStateChanged = onFileDownloadStateChange;
3505 downloadFile.xpath = decodeURIComponent(x);
3506 downloadFile.xfile = decodeURIComponent(y);
@@ -3571,7 +3579,7 @@
3579 // Connect again
3580 function p13uploadReconnect() {
3581 uploadFile.ws = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotUploadData), serverPublicNamePort);
3574 - uploadFile.ws.attemptWebRTC = debugmode;
3582 + uploadFile.ws.attemptWebRTC = false;
3583 uploadFile.ws.onStateChanged = onFileUploadStateChange;
3584 uploadFile.ws.Start(filesNode._id);
3585 }