Updated macOS agent, more multiplexor work.

Ylian Saint-Hilaire committed Apr 27, 2020 at 13:58 UTC 3cc6d8d29717dc0ce1d13942fc9994214d2267db
3 files changed +181 -61
agents/meshagent_osx-x86-64
Binary files a/agents/meshagent_osx-x86-64 and b/agents/meshagent_osx-x86-64 differ
meshdesktopmultiplex.js
+178 -57
@@ -59,27 +59,31 @@ MNG_ENCAPSULATE_AGENT_COMMAND = 70
59
60 function CreateDesktopDecoder() {
61 var obj = {};
62 - obj.agent = null;
63 - obj.viewers = [];
64 - obj.width = 0;
65 - obj.height = 0;
66 - obj.swidth = 0;
67 - obj.sheight = 0;
68 - obj.screen = null;
69 - obj.counter = 1;
70 - obj.imagesCount = 0;
71 - obj.imagesCounters = {};
72 - obj.images = {};
73 - obj.lastScreenSizeCmd = null;
74 - obj.lastScreenSizeCounter = 0;
75 - obj.firstData = null;
76 - obj.lastData = null;
77 - obj.lastDisplayInfoData = null;
78 - obj.desktopPaused = true;
79 - obj.imageCompression = 50;
80 - obj.imageScaling = 1024;
81 - obj.imageFrameRate = 50;
82 -
62 + obj.agent = null; // Reference to the connection object that is the agent.
63 + obj.viewers = []; // Array of references to all viewers.
64 + obj.viewersSendingCount = 0; // Number of viewers currently activaly sending something.
65 + obj.width = 0; // Current width of the display in pixels.
66 + obj.height = 0; // Current height of the display in pixels.
67 + obj.swidth = 0; // Current width of the display in tiles.
68 + obj.sheight = 0; // Current height of the display in tiles.
69 + obj.screen = null; // The main screen, (x * y) --> tile index. Indicates this image is covering each tile on the screen.
70 + obj.counter = 1; // The main counter, used as index for the obj.images table when now images come in.
71 + obj.imagesCount = 0; // Total number of images in the obj.images table.
72 + obj.imagesCounters = {}; // Main table of indexes --> tile count, the number of tiles still using this image.
73 + obj.images = {}; // Main table of indexes --> image data object.
74 + obj.lastScreenSizeCmd = null; // Pointer to the last screen size command from the agent.
75 + obj.lastScreenSizeCounter = 0; // Index into the image table of the screen size command, this is generally also the first command.
76 + obj.firstData = null; // Index in the image table of the first image in the table, generally this points to the display resolution command.
77 + obj.lastData = null; // Index in the images table of the last image in the table.
78 + obj.lastDisplayInfoData = null; // Pointer to the last display information command from the agent (Number of displays).
79 + obj.desktopPaused = true; // Current desktop pause state, it's true if all viewers are paused.
80 + obj.imageCompression = 50; // Current image compression, this is the highest value of all viewers.
81 + obj.imageScaling = 1024; // Current image scaling, this is the highest value of all viewers.
82 + obj.imageFrameRate = 50; // Current framerate setting, this is the lowest values of all viewers.
83 + obj.protocolOptions = null; // Set to the protocol options of the first viewer that connected.
84 + obj.viewerConnected = false; // Set to true if one viewer attempted to connect to the agent.
85 +
86 + // Add an agent or viewer
87 obj.addPeer = function (peer) {
88 if (peer.req.query.browser) {
89 console.log('addPeer-viewer');
@@ -93,7 +97,10 @@ function CreateDesktopDecoder() {
97 peer.imageCompression = 30;
98 peer.imageScaling = 1024;
99 peer.imageFrameRate = 100;
100 + peer.lastImageNumberSent = null;
101 peer.dataPtr = obj.firstData;
102 + peer.sending = false;
103 + peer.sendQueue = [];
104 } else {
105 console.log('addPeer-agent');
106 if (obj.agent != null) return false;
@@ -102,11 +109,20 @@ function CreateDesktopDecoder() {
109 obj.agent = peer;
110
111 // Setup the agent
105 -
112 + peer.sending = false;
113 + peer.sendQueue = [];
114 + //peer.ws.send('{"tsid":10,"type":"options"}');
115 + //peer.ws.send('2');
116 +
117 + if (obj.viewerConnected == true) {
118 + if (obj.protocolOptions != null) { obj.sendToAgent(JSON.stringify(obj.protocolOptions)); } // Send connection options
119 + obj.sendToAgent('2'); // Send remote desktop connect
120 + }
121 }
122 return true;
123 }
109 -
124 +
125 + // Remove an agent or viewer
126 obj.removePeer = function (peer) {
127 if (peer == agent) {
128 console.log('removePeer-agent');
@@ -125,7 +141,75 @@ function CreateDesktopDecoder() {
141 }
142 return true;
143 }
128 -
144 +
145 + // Send data to the agent or queue it up for sending
146 + obj.sendToAgent = function (data) {
147 + if (obj.agent == null) return;
148 + //console.log('SendToAgent', data.length);
149 + if (obj.agent.sending) {
150 + obj.agent.sendQueue.push(data);
151 + // TODO: Flow control, stop all viewers
152 + } else {
153 + obj.agent.ws.send(data, sendAgentNext);
154 + }
155 + }
156 +
157 + // Send more data to the agent
158 + function sendAgentNext() {
159 + if (obj.agent.sendQueue.length > 0) {
160 + // Send from the pending send queue
161 + obj.agent.ws.send(obj.agent.sendQueue.shift(), sendAgentNext);
162 + } else {
163 + // Nothing to send
164 + obj.agent.sending = false;
165 + // TODO: Flow control, start all viewers
166 + }
167 + }
168 +
169 + // Send this command to all viewers
170 + obj.sendToAllViewers = function (data) {
171 + for (var i in obj.viewers) { obj.sendToViewer(obj.viewers[i], data); }
172 + }
173 +
174 + // Send data to the viewer or queue it up for sending
175 + obj.sendToViewer = function (viewer, data) {
176 + if (viewer == null) return;
177 + //console.log('SendToViewer', data.length);
178 + if (viewer.sending) {
179 + viewer.sendQueue.push(data);
180 + // TODO: Flow control, stop the agent
181 + } else {
182 + viewer.sending = true;
183 + obj.viewersSendingCount++;
184 + viewer.ws.send(data, function () { sendViewerNext(viewer); });
185 + }
186 + }
187 +
188 + // Send more data to the viewer
189 + function sendViewerNext(viewer) {
190 + if (viewer.sendQueue.length > 0) {
191 + // Send from the pending send queue
192 + if (viewer.sending == false) { viewer.sending = true; obj.viewersSendingCount++; }
193 + viewer.ws.send(viewer.sendQueue.shift(), function () { sendViewerNext(viewer); });
194 + } else {
195 + if (viewer.dataPtr != null) {
196 + // Send the next image
197 + if ((viewer.lastImageNumberSent != null) && ((viewer.lastImageNumberSent + 1) != (viewer.dataPtr))) { console.log('SVIEW-S1', viewer.lastImageNumberSent, viewer.dataPtr); } // DEBUG
198 + var image = obj.images[viewer.dataPtr];
199 + viewer.lastImageNumberSent = viewer.dataPtr;
200 + if ((image.next != null) && ((viewer.dataPtr + 1) != image.next)) { console.log('SVIEW-S2', viewer.dataPtr, image.next); } // DEBUG
201 + viewer.dataPtr = image.next;
202 + if (viewer.sending == false) { viewer.sending = true; obj.viewersSendingCount++; }
203 + viewer.ws.send(image.data, function () { sendViewerNext(viewer); });
204 + } else {
205 + // Nothing to send
206 + viewer.sending = false;
207 + obj.viewersSendingCount--;
208 + // TODO: Flow control, start agent
209 + }
210 + }
211 + }
212 +
213 // Process data coming from the agent or any viewers
214 obj.processData = function (peer, data) {
215 if (peer == obj.agent) { obj.processAgentData(data); } else { obj.processViewerData(peer, data); }
@@ -133,16 +217,37 @@ function CreateDesktopDecoder() {
217
218 // Process incoming viewer data
219 obj.processViewerData = function (viewer, data) {
136 - //console.log('ViewerData', data.length);
220 + if (typeof data == 'string') {
221 + if (data == '2') {
222 + if (obj.viewerConnected == false) {
223 + if (obj.agent != null) {
224 + if (obj.protocolOptions != null) { obj.sendToAgent(JSON.stringify(obj.protocolOptions)); } // Send connection options
225 + obj.sendToAgent('2'); // Send remote desktop connect
226 + }
227 + obj.viewerConnected = true;
228 + }
229 + return;
230 + }
231 + var json = null;
232 + try { json = JSON.parse(data); } catch (ex) { }
233 + if (json == null) return;
234 + if ((json.type == 'options') && (obj.protocolOptions == null)) { obj.protocolOptions = json; }
235 + return;
236 + }
237 +
238 + //console.log('ViewerData', data.length, typeof data, data);
239 if ((typeof data != 'object') || (data.length < 4)) return; // Ignore all control traffic for now (WebRTC)
240 var command = data.readUInt16BE(0);
241 var cmdsize = data.readUInt16BE(2);
242 + //console.log('ViewerData', data.length, command, cmdsize);
243 switch (command) {
244 case 1:// Key Events, forward to agent
245 //console.log('Viewer-Keys');
246 + obj.sendToAgent(data);
247 break;
248 case 2:// Mouse events, forward to agent
249 //console.log('Viewer-Mouse');
250 + obj.sendToAgent(data);
251 break;
252 case 5:// Compression
253 if (data.length < 10) return;
@@ -166,14 +271,21 @@ function CreateDesktopDecoder() {
271 obj.imageCompression = viewersimageCompression;
272 obj.imageScaling = viewersimageScaling;
273 obj.imageFrameRate = viewersimageFrameRate
169 - console.log('Send-Agent-Compression', obj.imageCompression, obj.imageScaling, obj.imageFrameRate);
170 - // obj.send(String.fromCharCode(0x00, 0x05, 0x00, 0x0A, type, obj.CompressionLevel) + obj.shortToStr(obj.ScalingLevel) + obj.shortToStr(obj.FrameRateTimer));
274 + //console.log('Send-Agent-Compression', obj.imageCompression, obj.imageScaling, obj.imageFrameRate);
275 + var cmd = Buffer.alloc(10);
276 + cmd.writeUInt16BE(5, 0); // Command 5, compression
277 + cmd.writeUInt16BE(10, 2); // Command size, 10 bytes long
278 + cmd[4] = 1; // Image type, 1 = JPEN
279 + cmd[5] = obj.imageCompression; // Image compression level
280 + cmd.writeUInt16BE(obj.imageScaling, 6); // Scaling level
281 + cmd.writeUInt16BE(obj.imageFrameRate, 8); // Frame rate timer
282 + obj.sendToAgent(cmd);
283 }
284 break;
285 case 6:// Refresh, handle this on the server
174 - console.log('Viewer-Refresh');
286 + //console.log('Viewer-Refresh');
287 viewer.dataPtr = obj.firstData; // Start over
176 - // TODO
288 + if (viewer.sending == false) { sendViewerNext(viewer); }
289 break;
290 case 8:// Pause and unpause
291 if (data.length != 5) break;
@@ -185,15 +297,13 @@ function CreateDesktopDecoder() {
297 for (var i in obj.viewers) { if (obj.viewers[i].desktopPaused == false) { viewersPaused = false; }; }
298 if (viewersPaused != obj.desktopPaused) {
299 obj.desktopPaused = viewersPaused;
188 - console.log('Send-Agent-' + ((viewersPaused == 1)?'Pause':'UnPause'));
189 - // TODO
190 - }
191 - if (viewer.desktopPaused == false) {
192 - viewer.dataPtr = obj.firstData; // Start over
193 - // TODO
300 + //console.log('Send-Agent-' + ((viewersPaused == true) ? 'Pause' : 'UnPause'));
301 + data[4] = (viewersPaused == true) ? 1 : 0;
302 + obj.sendToAgent(data);
303 }
304 break;
305 case 10:// CTRL-ALT-DEL, forward to agent
306 + obj.sendToAgent(data);
307 break;
308 case 14:// Touch setup
309 break;
@@ -202,13 +312,14 @@ function CreateDesktopDecoder() {
312 break;
313 }
314 }
205 -
315 +
316 // Process incoming agent data
317 obj.processAgentData = function (data) {
208 - //console.log('AgentData', data.length);
318 if ((typeof data != 'object') || (data.length < 4)) return; // Ignore all control traffic for now (WebRTC)
319 + const jumboData = data;
320 var command = data.readUInt16BE(0);
321 var cmdsize = data.readUInt16BE(2);
322 + //console.log('AgentData', data.length, command, cmdsize);
323 if ((command == 27) && (cmdsize == 8)) {
324 // Jumbo packet
325 if (data.length >= 12) {
@@ -232,33 +343,47 @@ function CreateDesktopDecoder() {
343 //console.log("Tile", x, y, dimensions.width, dimensions.height);
344
345 // Keep a reference to this image & how many tiles it covers
235 - obj.images[obj.counter] = { next: null, prev: obj.lastData, data: data };
346 + obj.images[obj.counter] = { next: null, prev: obj.lastData, data: jumboData };
347 obj.images[obj.lastData].next = obj.counter;
348 obj.lastData = obj.counter;
349 obj.imagesCounters[obj.counter] = (sw * sh);
350 obj.imagesCount++;
351 if (obj.imagesCount == 2000000000) { obj.imagesCount = 1; } // Loop the counter if needed
241 -
352 +
353 + //console.log('Adding Image ' + obj.counter);
354 +
355 + var skips = [];
356 +
357 // Update the screen with the correct pointers.
358 for (var i = 0; i < sw; i++) {
359 for (var j = 0; j < sh; j++) {
245 - var k = ((obj.swidth * (j + sy)) + (i + sx)), oi = obj.screen[k];
360 + var k = ((obj.swidth * (j + sy)) + (i + sx));
361 + const oi = obj.screen[k];
362 + obj.screen[k] = obj.counter;
363 if ((oi != null) && (--obj.imagesCounters[oi] == 0)) {
364 // Remove data from the link list
365 obj.imagesCount--;
366 var d = obj.images[oi];
367 + //console.log('Removing Image', oi, obj.images[oi].prev, obj.images[oi].next);
368 obj.images[d.prev].next = d.next;
369 obj.images[d.next].prev = d.prev;
370 delete obj.images[oi];
371 delete obj.imagesCounters[oi];
372
373 // If any viewers are currently on image "oi" must be moved to "d.next"
256 - // TODO
374 + for (var l in obj.viewers) { const v = obj.viewers[l]; if (v.dataPtr == oi) { skips.push(oi); v.dataPtr = d.next; } }
375 }
258 - obj.screen[k] = obj.counter;
376 }
377 }
261 -
378 +
379 + if (skips.length > 0) { console.log('SKIPS', skips.length); }
380 +
381 + // Any viewer on dataPtr null, change to this image
382 + for (var i in obj.viewers) {
383 + const v = obj.viewers[i];
384 + if (v.dataPtr == null) { v.dataPtr = obj.counter; if (v.sending == false) { sendViewerNext(v); } }
385 + }
386 +
387 // Debug, display the link list
388 //var xx = '', xptr = obj.firstData;
389 //while (xptr != null) { xx += '>' + xptr; xptr = obj.images[xptr].next; }
@@ -284,14 +409,15 @@ function CreateDesktopDecoder() {
409 obj.imagesCount = 0;
410 obj.imagesCounters = {};
411 obj.images = {};
287 - obj.images[obj.counter] = { next: null, prev: null, data: data};
412 + obj.images[obj.counter] = { next: null, prev: null, data: data };
413 obj.firstData = obj.counter;
414 obj.lastData = obj.counter;
415
416 // Add viewers must be set to start at "obj.counter"
417 for (var i in obj.viewers) {
293 - obj.viewers[i].dataPtr = obj.counter;
294 - // TODO
418 + const v = obj.viewers[i];
419 + v.dataPtr = obj.counter;
420 + if (v.sending == false) { sendViewerNext(v); }
421 }
422
423 //console.log("ScreenSize", obj.width, obj.height, obj.swidth, obj.sheight, obj.swidth * obj.sheight);
@@ -299,8 +425,7 @@ function CreateDesktopDecoder() {
425 case 11: // GetDisplays
426 // Store and send this to all viewers right away
427 obj.lastDisplayInfoData = data;
302 - // TODO
303 -
428 + obj.sendToAllViewers(data);
429 break;
430 case 14: // KVM_INIT_TOUCH
431 break;
@@ -309,22 +434,16 @@ function CreateDesktopDecoder() {
434 case 16: // MNG_KVM_CONNECTCOUNT
435 break;
436 case 17: // MNG_KVM_MESSAGE
312 -
437 // Send this to all viewers right away
314 - // TODO
315 -
438 + obj.sendToAllViewers(data);
439 break;
440 case 65: // Alert
318 -
441 // Send this to all viewers right away
320 - // TODO
321 -
442 + obj.sendToAllViewers(data);
443 break;
444 case 88: // MNG_KVM_MOUSE_CURSOR
324 -
445 // Send this to all viewers right away
326 - // TODO
327 -
446 + obj.sendToAllViewers(data);
447 break;
448 default:
449 console.log('Un-handled agent command: ' + command);
@@ -636,6 +755,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
755 // If this data was received by the agent, decode it.
756 if (this.me.deskDecoder != null) { this.me.deskDecoder.processData(this.me, data); }
757
758 + /*
759 //console.log(typeof data, data.length);
760 if (this.peer != null) {
761 //if (typeof data == 'string') { console.log('Relay: ' + data); } else { console.log('Relay:' + data.length + ' byte(s)'); }
@@ -651,6 +771,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
771 }
772 } catch (ex) { console.log(ex); }
773 }
774 + */
775 });
776
777 // If error, close both sides of the relay.
public/scripts/agent-desktop-0.0.2.js
+3 -4
@@ -180,11 +180,10 @@ var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
180
181 obj.ProcessData = function (str) {
182 var ptr = 0;
183 - //console.log('x0', str.length);
183 while (ptr < str.length) {
185 - //console.log('x1', ptr, str.length);
186 - ptr += obj.ProcessDataEx(str.substring(ptr));
187 - //console.log('x2', ptr, str.length);
184 + var r = obj.ProcessDataEx(str.substring(ptr));
185 + if ((r == null) || (r == 0)) break;
186 + ptr += r;
187 }
188 }
189