1. Updated recovery core 'files' implementation 2. Removed dead code from recovery core 3. Fixed bug in switch statement in meshcore

1. Updated recovery core 'files' implementation 2. Removed dead code from recovery core 3. Fixed bug in switch statement in meshcore

Bryan Roe committed Jan 17, 2021 at 18:36 UTC 70df889935cf3686bfaf9d9e3b4ed4e3cf291161
2 files changed +347 -184
agents/meshcore.js
+109 -121
@@ -2551,144 +2551,132 @@ function createMeshCore(agent)
2551 if (coreDumpPath != null) { db.Put('CoreDumpTime', require('fs').statSync(coreDumpPath).mtime); }
2552 break;
2553 }
2554 - case 'rename': {
2555 - // Rename a file or folder
2556 - var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
2557 - var newfullpath = obj.path.join(cmd.path, cmd.newname);
2558 - MeshServerLogEx(48, [oldfullpath, cmd.newname], 'Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
2559 - try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
2560 - break;
2561 - }
2562 - case 'findfile': {
2563 - // Search for files
2564 - var r = require('file-search').find('"' + cmd.path + '"', cmd.filter);
2565 - if (!r.cancel) { r.cancel = function cancel() { this.child.kill(); }; }
2566 - this._search = r;
2567 - r.socket = this;
2568 - r.socket.reqid = cmd.reqid; // Search request id. This is used to send responses and cancel the request.
2569 - r.socket.path = cmd.path; // Search path
2570 - r.on('result', function (str) { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: str.substring(this.socket.path.length), reqid: this.socket.reqid }))); } catch (ex) { } });
2571 - r.then(function () { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: null, reqid: this.socket.reqid }))); } catch (ex) { } });
2572 - break;
2573 - }
2574 - case 'cancelfindfile': {
2575 - if (this._search) { this._search.cancel(); this._search = null; }
2576 - }
2577 - case 'download': {
2578 - // Download a file
2579 - var sendNextBlock = 0;
2580 - if (cmd.sub == 'start')
2581 - { // Setup the download
2582 - if ((cmd.path == null) && (cmd.ask == 'coredump'))
2583 - { // If we are asking for the coredump file, set the right path.
2584 - if (process.platform == 'win32')
2585 - {
2586 - if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
2587 - } else
2588 - {
2589 - if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
2590 - }
2591 - }
2592 - MeshServerLogEx((cmd.ask == 'coredump') ? 104 : 49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
2593 - if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2594 - this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
2595 - try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2596 - if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
2597 - } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id))
2598 - { // Download commands
2599 - if (cmd.sub == 'startack') { sendNextBlock = ((typeof cmd.ack == 'number') ? cmd.ack : 8); } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
2554 + case 'rename':
2555 + {
2556 + // Rename a file or folder
2557 + var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
2558 + var newfullpath = obj.path.join(cmd.path, cmd.newname);
2559 + MeshServerLogEx(48, [oldfullpath, cmd.newname], 'Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
2560 + try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
2561 + break;
2562 }
2601 - // Send the next download block(s)
2602 - while (sendNextBlock > 0)
2563 + case 'findfile':
2564 {
2604 - sendNextBlock--;
2605 - var buf = Buffer.alloc(16384);
2606 - var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
2607 - this.filedownload.ptr += len;
2608 - if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
2609 - this.write(buf.slice(0, len + 4)); // Write as binary
2565 + // Search for files
2566 + var r = require('file-search').find('"' + cmd.path + '"', cmd.filter);
2567 + if (!r.cancel) { r.cancel = function cancel() { this.child.kill(); }; }
2568 + this._search = r;
2569 + r.socket = this;
2570 + r.socket.reqid = cmd.reqid; // Search request id. This is used to send responses and cancel the request.
2571 + r.socket.path = cmd.path; // Search path
2572 + r.on('result', function (str) { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: str.substring(this.socket.path.length), reqid: this.socket.reqid }))); } catch (ex) { } });
2573 + r.then(function () { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: null, reqid: this.socket.reqid }))); } catch (ex) { } });
2574 + break;
2575 }
2611 - break;
2612 - }
2613 - /*
2614 - case 'download': {
2615 - // Packet download of a file, agent to browser
2616 - if (cmd.path == undefined) break;
2617 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2618 - //console.log('Download: ' + filepath);
2619 - try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; }
2620 - this.httprequest.downloadFileId = cmd.reqid;
2621 - this.httprequest.downloadFilePtr = 0;
2622 - if (this.httprequest.downloadFile) { this.write(Buffer.from(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); }
2576 + case 'cancelfindfile':
2577 + {
2578 + if (this._search) { this._search.cancel(); this._search = null; }
2579 break;
2580 }
2625 - case 'download2': {
2626 - // Stream download of a file, agent to browser
2581 + case 'download':
2582 + {
2583 + // Download a file
2584 + var sendNextBlock = 0;
2585 + if (cmd.sub == 'start')
2586 + { // Setup the download
2587 + if ((cmd.path == null) && (cmd.ask == 'coredump'))
2588 + { // If we are asking for the coredump file, set the right path.
2589 + if (process.platform == 'win32')
2590 + {
2591 + if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
2592 + } else
2593 + {
2594 + if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
2595 + }
2596 + }
2597 + MeshServerLogEx((cmd.ask == 'coredump') ? 104 : 49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
2598 + if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2599 + this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
2600 + try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2601 + if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
2602 + } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id))
2603 + { // Download commands
2604 + if (cmd.sub == 'startack') { sendNextBlock = ((typeof cmd.ack == 'number') ? cmd.ack : 8); } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
2605 + }
2606 + // Send the next download block(s)
2607 + while (sendNextBlock > 0)
2608 + {
2609 + sendNextBlock--;
2610 + var buf = Buffer.alloc(16384);
2611 + var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
2612 + this.filedownload.ptr += len;
2613 + if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
2614 + this.write(buf.slice(0, len + 4)); // Write as binary
2615 + }
2616 + break;
2617 + }
2618 + case 'upload':
2619 + {
2620 + // Upload a file, browser to agent
2621 + if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
2622 if (cmd.path == undefined) break;
2623 var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2629 - try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
2630 - this.httprequest.downloadFile.pipe(this);
2631 - this.httprequest.downloadFile.end = function () { }
2624 + this.httprequest.uploadFilePath = filepath;
2625 + MeshServerLogEx(50, [filepath], 'Upload: \"' + filepath + '\"', this.httprequest);
2626 + try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
2627 + this.httprequest.uploadFileid = cmd.reqid;
2628 + if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
2629 break;
2630 }
2634 - */
2635 - case 'upload': {
2636 - // Upload a file, browser to agent
2637 - if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
2638 - if (cmd.path == undefined) break;
2639 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2640 - this.httprequest.uploadFilePath = filepath;
2641 - MeshServerLogEx(50, [filepath], 'Upload: \"' + filepath + '\"', this.httprequest);
2642 - try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
2643 - this.httprequest.uploadFileid = cmd.reqid;
2644 - if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
2645 - break;
2646 - }
2647 - case 'uploaddone': {
2648 - // Indicates that an upload is done
2649 - if (this.httprequest.uploadFile)
2631 + case 'uploaddone':
2632 {
2651 - fs.closeSync(this.httprequest.uploadFile);
2652 - this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2653 - delete this.httprequest.uploadFile;
2654 - delete this.httprequest.uploadFileid;
2655 - delete this.httprequest.uploadFilePath;
2633 + // Indicates that an upload is done
2634 + if (this.httprequest.uploadFile)
2635 + {
2636 + fs.closeSync(this.httprequest.uploadFile);
2637 + this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2638 + delete this.httprequest.uploadFile;
2639 + delete this.httprequest.uploadFileid;
2640 + delete this.httprequest.uploadFilePath;
2641 + }
2642 + break;
2643 }
2657 - break;
2658 - }
2659 - case 'uploadcancel': {
2660 - // Indicates that an upload is canceled
2661 - if (this.httprequest.uploadFile)
2644 + case 'uploadcancel':
2645 {
2663 - fs.closeSync(this.httprequest.uploadFile);
2664 - fs.unlinkSync(this.httprequest.uploadFilePath);
2665 - this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2666 - delete this.httprequest.uploadFile;
2667 - delete this.httprequest.uploadFileid;
2668 - delete this.httprequest.uploadFilePath;
2646 + // Indicates that an upload is canceled
2647 + if (this.httprequest.uploadFile)
2648 + {
2649 + fs.closeSync(this.httprequest.uploadFile);
2650 + fs.unlinkSync(this.httprequest.uploadFilePath);
2651 + this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2652 + delete this.httprequest.uploadFile;
2653 + delete this.httprequest.uploadFileid;
2654 + delete this.httprequest.uploadFilePath;
2655 + }
2656 + break;
2657 }
2670 - break;
2671 - }
2672 - case 'copy': {
2673 - // Copy a bunch of files from scpath to dspath
2674 - for (var i in cmd.names)
2658 + case 'copy':
2659 {
2676 - var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2677 - MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2678 - if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
2660 + // Copy a bunch of files from scpath to dspath
2661 + for (var i in cmd.names)
2662 + {
2663 + var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2664 + MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2665 + if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
2666 + }
2667 + break;
2668 }
2680 - break;
2681 - }
2682 - case 'move': {
2683 - // Move a bunch of files from scpath to dspath
2684 - for (var i in cmd.names)
2669 + case 'move':
2670 {
2686 - var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2687 - MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2688 - if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
2671 + // Move a bunch of files from scpath to dspath
2672 + for (var i in cmd.names)
2673 + {
2674 + var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2675 + MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2676 + if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
2677 + }
2678 + break;
2679 }
2690 - break;
2691 - }
2680 case 'zip':
2681 // Zip a bunch of files
2682 if (this.zip != null) return; // Zip operating is currently running, exit now.
agents/recoverycore.js
+238 -63
@@ -56,6 +56,55 @@ function sendAgentMessage(msg, icon)
56 require('MeshAgent').SendCommand({ action: 'sessions', type: 'msg', value: sendAgentMessage.messages });
57 }
58
59 +// Add to the server event log
60 +function MeshServerLog(msg, state)
61 +{
62 + if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
63 + if (state)
64 + {
65 + if (state.userid) { msg.userid = state.userid; }
66 + if (state.username) { msg.username = state.username; }
67 + if (state.sessionid) { msg.sessionid = state.sessionid; }
68 + if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
69 + }
70 + require('MeshAgent').SendCommand(msg);
71 +}
72 +
73 +// Add to the server event log, use internationalized events
74 +function MeshServerLogEx(id, args, msg, state)
75 +{
76 + var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg };
77 + if (state)
78 + {
79 + if (state.userid) { msg.userid = state.userid; }
80 + if (state.username) { msg.username = state.username; }
81 + if (state.sessionid) { msg.sessionid = state.sessionid; }
82 + if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
83 + }
84 + require('MeshAgent').SendCommand(msg);
85 +}
86 +
87 +function pathjoin()
88 +{
89 + var x = [];
90 + for (var i in arguments)
91 + {
92 + var w = arguments[i];
93 + if (w != null)
94 + {
95 + while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
96 + if (i != 0)
97 + {
98 + while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
99 + }
100 + x.push(w);
101 + }
102 + }
103 + if (x.length == 0) return '/';
104 + return x.join('/');
105 +}
106 +
107 +
108 function bsd_execv(name, agentfilename, sessionid)
109 {
110 var child = require('child_process').execFile('/bin/sh', ['sh']);
@@ -370,29 +419,6 @@ require('MeshAgent').on('Connected', function () {
419 });
420 });
421
373 -// Tunnel callback operations
374 -function onTunnelUpgrade(response, s, head) {
375 - this.s = s;
376 - s.httprequest = this;
377 - s.end = onTunnelClosed;
378 - s.tunnel = this;
379 -
380 - //sendConsoleText('onTunnelUpgrade');
381 -
382 - if (this.tcpport != null) {
383 - // This is a TCP relay connection, pause now and try to connect to the target.
384 - s.pause();
385 - s.data = onTcpRelayServerTunnelData;
386 - var connectionOptions = { port: parseInt(this.tcpport) };
387 - if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
388 - s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
389 - s.tcprelay.peerindex = this.index;
390 - } else {
391 - // This is a normal connect for KVM/Terminal/Files
392 - s.data = onTunnelData;
393 - }
394 -}
395 -
422 // Called when receiving control data on websocket
423 function onTunnelControlData(data, ws)
424 {
@@ -491,15 +517,44 @@ require('MeshAgent').AddCommandHandler(function (data)
517 if (data.value != null)
518 { // Process a new tunnel connection request
519 // Create a new tunnel object
520 + if (data.rights != 4294967295)
521 + {
522 + MeshServerLog('Tunnel Error: RecoveryCore requires admin rights for tunnels');
523 + break;
524 + }
525 +
526 var xurl = getServerTargetUrlEx(data.value);
527 if (xurl != null)
528 {
529 var woptions = http.parseUri(xurl);
530 woptions.rejectUnauthorized = 0;
531 + woptions.perMessageDeflate = false;
532 + woptions.checkServerIdentity = function checkServerIdentity(certs)
533 + {
534 + // If the tunnel certificate matches the control channel certificate, accept the connection
535 + try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
536 + try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
537 +
538 + // Check that the certificate is the one expected by the server, fail if not.
539 + if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
540 + }
541 + woptions.checkServerIdentity.servertlshash = data.servertlshash;
542 +
543 +
544 //sendConsoleText(JSON.stringify(woptions));
545 var tunnel = http.request(woptions);
546 tunnel.on('upgrade', function (response, s, head)
547 {
548 + if (require('MeshAgent').idleTimeout != null)
549 + {
550 + s.setTimeout(require('MeshAgent').idleTimeout * 1000);
551 + s.on('timeout', function ()
552 + {
553 + this.ping();
554 + this.setTimeout(require('MeshAgent').idleTimeout * 1000);
555 + });
556 + }
557 +
558 this.s = s;
559 s.httprequest = this;
560 s.tunnel = this;
@@ -508,9 +563,8 @@ require('MeshAgent').AddCommandHandler(function (data)
563 if (tunnels[this.httprequest.index] == null) return; // Stop duplicate calls.
564
565 // If there is a upload or download active on this connection, close the file
511 - if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
512 - if (this.httprequest.downloadFile) { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; }
513 -
566 + if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; delete this.httprequest.uploadFileid; delete this.httprequest.uploadFilePath; }
567 + if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; }
568
569 //sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
570 delete tunnels[this.httprequest.index];
@@ -521,29 +575,62 @@ require('MeshAgent').AddCommandHandler(function (data)
575 s.on('data', function (data)
576 {
577 // If this is upload data, save it to file
524 - if (this.httprequest.uploadFile)
578 + if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123))
579 {
526 - try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
527 - this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data
580 + // Save the data to file being uploaded.
581 + if (data[0] == 0)
582 + {
583 + // If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
584 + try { fs.writeSync(this.httprequest.uploadFile, data, 1, data.length - 1); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
585 + } else
586 + {
587 + // If data does not start with zero, save as-is.
588 + try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
589 + }
590 + this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data.
591 return;
592 }
593
594 if (this.httprequest.state == 0)
595 {
596 // Check if this is a relay connection
534 - if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid); }
535 - } else
597 + if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
598 + }
599 + else
600 {
601 // Handle tunnel data
602 if (this.httprequest.protocol == 0)
539 - {
540 - if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
603 + { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
604 // Take a look at the protocol
605 + if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
606 this.httprequest.protocol = parseInt(data);
607 if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
544 - if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9))
608 + if (this.httprequest.protocol == 10)
609 + {
610 + //
611 + // Basic file transfer
612 + //
613 + var stats = null;
614 + if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; }
615 + try { stats = require('fs').statSync(this.httprequest.xoptions.file) } catch (e) { }
616 + try { if (stats) { this.httprequest.downloadFile = fs.createReadStream(this.httprequest.xoptions.file, { flags: 'rbN' }); } } catch (e) { }
617 + if (this.httprequest.downloadFile)
618 + {
619 + //sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
620 + this.write(JSON.stringify({ op: 'ok', size: stats.size }));
621 + this.httprequest.downloadFile.pipe(this);
622 + this.httprequest.downloadFile.end = function () { }
623 + } else
624 + {
625 + //sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
626 + this.write(JSON.stringify({ op: 'cancel' }));
627 + }
628 + }
629 + else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9))
630 {
546 - // Remote terminal using native pipes
631 + //
632 + // Remote Terminal
633 + //
634 if (process.platform == "win32")
635 {
636 var cols = 80, rows = 25;
@@ -630,36 +717,124 @@ require('MeshAgent').AddCommandHandler(function (data)
717 if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
718 this.write(Buffer.from(JSON.stringify(response)));
719 break;
633 - case 'mkdir': {
634 - // Create a new empty folder
635 - fs.mkdirSync(cmd.path);
636 - break;
637 - }
638 - case 'rm': {
639 - // Delete, possibly recursive delete
640 - for (var i in cmd.delfiles)
720 + case 'mkdir':
721 {
642 - try { deleteFolderRecursive(path.join(cmd.path, cmd.delfiles[i]), cmd.rec); } catch (e) { }
722 + // Create a new empty folder
723 + fs.mkdirSync(cmd.path);
724 + break;
725 + }
726 + case 'rm':
727 + {
728 + // Delete, possibly recursive delete
729 + for (var i in cmd.delfiles)
730 + {
731 + try { deleteFolderRecursive(path.join(cmd.path, cmd.delfiles[i]), cmd.rec); } catch (e) { }
732 + }
733 + break;
734 + }
735 + case 'rename':
736 + {
737 + // Rename a file or folder
738 + var oldfullpath = path.join(cmd.path, cmd.oldname);
739 + var newfullpath = path.join(cmd.path, cmd.newname);
740 + try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
741 + break;
742 + }
743 + case 'findfile':
744 + {
745 + // Search for files
746 + var r = require('file-search').find('"' + cmd.path + '"', cmd.filter);
747 + if (!r.cancel) { r.cancel = function cancel() { this.child.kill(); }; }
748 + this._search = r;
749 + r.socket = this;
750 + r.socket.reqid = cmd.reqid; // Search request id. This is used to send responses and cancel the request.
751 + r.socket.path = cmd.path; // Search path
752 + r.on('result', function (str) { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: str.substring(this.socket.path.length), reqid: this.socket.reqid }))); } catch (ex) { } });
753 + r.then(function () { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: null, reqid: this.socket.reqid }))); } catch (ex) { } });
754 + break;
755 + }
756 + case 'cancelfindfile':
757 + {
758 + if (this._search) { this._search.cancel(); this._search = null; }
759 + break;
760 + }
761 + case 'download':
762 + {
763 + // Download a file
764 + var sendNextBlock = 0;
765 + if (cmd.sub == 'start')
766 + { // Setup the download
767 + if ((cmd.path == null) && (cmd.ask == 'coredump'))
768 + { // If we are asking for the coredump file, set the right path.
769 + if (process.platform == 'win32')
770 + {
771 + if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
772 + } else
773 + {
774 + if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
775 + }
776 + }
777 + MeshServerLogEx((cmd.ask == 'coredump') ? 104 : 49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
778 + if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
779 + this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
780 + try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
781 + if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
782 + } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id))
783 + { // Download commands
784 + if (cmd.sub == 'startack') { sendNextBlock = ((typeof cmd.ack == 'number') ? cmd.ack : 8); } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
785 + }
786 + // Send the next download block(s)
787 + while (sendNextBlock > 0)
788 + {
789 + sendNextBlock--;
790 + var buf = Buffer.alloc(16384);
791 + var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
792 + this.filedownload.ptr += len;
793 + if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
794 + this.write(buf.slice(0, len + 4)); // Write as binary
795 + }
796 + break;
797 + }
798 + case 'upload':
799 + {
800 + // Upload a file, browser to agent
801 + if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
802 + if (cmd.path == undefined) break;
803 + var filepath = cmd.name ? pathjoin(cmd.path, cmd.name) : cmd.path;
804 + this.httprequest.uploadFilePath = filepath;
805 + MeshServerLogEx(50, [filepath], 'Upload: \"' + filepath + '\"', this.httprequest);
806 + try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
807 + this.httprequest.uploadFileid = cmd.reqid;
808 + if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
809 + break;
810 + }
811 + case 'uploaddone':
812 + {
813 + // Indicates that an upload is done
814 + if (this.httprequest.uploadFile)
815 + {
816 + fs.closeSync(this.httprequest.uploadFile);
817 + this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
818 + delete this.httprequest.uploadFile;
819 + delete this.httprequest.uploadFileid;
820 + delete this.httprequest.uploadFilePath;
821 + }
822 + break;
823 + }
824 + case 'uploadcancel':
825 + {
826 + // Indicates that an upload is canceled
827 + if (this.httprequest.uploadFile)
828 + {
829 + fs.closeSync(this.httprequest.uploadFile);
830 + fs.unlinkSync(this.httprequest.uploadFilePath);
831 + this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
832 + delete this.httprequest.uploadFile;
833 + delete this.httprequest.uploadFileid;
834 + delete this.httprequest.uploadFilePath;
835 + }
836 + break;
837 }
644 - break;
645 - }
646 - case 'rename': {
647 - // Rename a file or folder
648 - var oldfullpath = path.join(cmd.path, cmd.oldname);
649 - var newfullpath = path.join(cmd.path, cmd.newname);
650 - try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
651 - break;
652 - }
653 - case 'upload': {
654 - // Upload a file, browser to agent
655 - if (this.httprequest.uploadFile != undefined) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
656 - if (cmd.path == undefined) break;
657 - var filepath = cmd.name ? path.join(cmd.path, cmd.name) : cmd.path;
658 - try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
659 - this.httprequest.uploadFileid = cmd.reqid;
660 - if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
661 - break;
662 - }
838 case 'copy': {
839 // Copy a bunch of files from scpath to dspath
840 for (var i in cmd.names)