2017-08-28 12:27:45 -04:00
/ *
2021-01-09 17:31:09 -05:00
Copyright 2018 - 2021 Intel Corporation
2017-08-28 12:27:45 -04:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
2018-01-16 20:30:34 -05:00
http : //www.apache.org/licenses/LICENSE-2.0
2017-08-28 12:27:45 -04:00
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2021-01-24 18:19:29 -05:00
process . on ( 'uncaughtException' , function ( ex )
{
2020-01-07 16:04:36 -05:00
require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : "uncaughtException1: " + ex } ) ;
2018-09-20 14:45:12 -04:00
} ) ;
2018-11-28 13:50:25 -05:00
// NOTE: This seems to cause big problems, don't enable the debugger in the server's meshcore.
2018-11-26 20:12:27 -05:00
//attachDebugger({ webport: 9999, wait: 1 }).then(function (prt) { console.log('Point Browser for Debug to port: ' + prt); });
2018-09-20 14:45:12 -04:00
2018-11-27 20:13:01 -05:00
// Mesh Rights
2019-06-14 19:33:53 -04:00
var MNG _ERROR = 65 ;
2018-12-29 18:24:33 -05:00
var MESHRIGHT _EDITMESH = 1 ;
var MESHRIGHT _MANAGEUSERS = 2 ;
var MESHRIGHT _MANAGECOMPUTERS = 4 ;
var MESHRIGHT _REMOTECONTROL = 8 ;
var MESHRIGHT _AGENTCONSOLE = 16 ;
var MESHRIGHT _SERVERFILES = 32 ;
var MESHRIGHT _WAKEDEVICE = 64 ;
var MESHRIGHT _SETNOTES = 128 ;
var MESHRIGHT _REMOTEVIEW = 256 ;
2019-03-01 14:47:58 -05:00
var MESHRIGHT _NOTERMINAL = 512 ;
var MESHRIGHT _NOFILES = 1024 ;
var MESHRIGHT _NOAMT = 2048 ;
2019-03-09 17:28:08 -05:00
var MESHRIGHT _LIMITEDINPUT = 4096 ;
2020-03-06 17:06:33 -05:00
var MESHRIGHT _LIMITEVENTS = 8192 ;
var MESHRIGHT _CHATNOTIFY = 16384 ;
var MESHRIGHT _UNINSTALL = 32768 ;
var MESHRIGHT _NODESKTOP = 65536 ;
2021-01-15 02:57:52 -05:00
2021-01-24 18:19:29 -05:00
if ( require ( 'MeshAgent' ) . ARCHID == null )
{
2021-01-14 04:18:28 -05:00
var id = null ;
2021-01-24 18:19:29 -05:00
switch ( process . platform )
{
2021-01-14 04:18:28 -05:00
case 'win32' :
id = require ( '_GenericMarshal' ) . PointerSize == 4 ? 3 : 4 ;
break ;
case 'freebsd' :
id = require ( '_GenericMarshal' ) . PointerSize == 4 ? 31 : 30 ;
break ;
case 'darwin' :
2021-01-24 18:19:29 -05:00
try
{
2021-01-16 17:33:06 -05:00
id = require ( 'os' ) . arch ( ) == 'x64' ? 16 : 29 ;
}
2021-01-24 18:19:29 -05:00
catch ( xx )
{
2021-01-16 17:33:06 -05:00
id = 16 ;
}
break ; break ;
2021-01-14 04:18:28 -05:00
}
if ( id != null ) { Object . defineProperty ( require ( 'MeshAgent' ) , 'ARCHID' , { value : id } ) ; }
}
2018-11-27 20:13:01 -05:00
2021-01-24 18:19:29 -05:00
var obj = { } ;
var agentFileHttpRequests = { } ; // Currently active agent HTTPS GET requests from the server.
var agentFileHttpPendingRequests = [ ] ; // Pending HTTPS GET requests from the server.
var debugConsole = ( _MSH ( ) . debugConsole == 1 ) ;
2021-01-05 12:47:47 -05:00
2021-01-24 18:19:29 -05:00
if ( process . platform == 'win32' && require ( 'user-sessions' ) . isRoot ( ) )
{
// Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
try
{
var writtenSize = 0 , actualSize = Math . floor ( require ( 'fs' ) . statSync ( process . execPath ) . size / 1024 ) ;
try { writtenSize = require ( 'win-registry' ) . QueryKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent' , 'EstimatedSize' ) ; } catch ( e ) { }
if ( writtenSize != actualSize ) { try { require ( 'win-registry' ) . WriteKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent' , 'EstimatedSize' , actualSize ) ; } catch ( e ) { } }
} catch ( x ) { }
// Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode
var svcname = process . platform == 'win32' ? 'Mesh Agent' : 'meshagent' ;
try
{
svcname = require ( 'MeshAgent' ) . serviceName ;
2020-01-06 14:00:37 -05:00
}
2021-01-24 18:19:29 -05:00
catch ( x )
{
}
try
{
var meshCheck = false ;
try { meshCheck = require ( 'service-manager' ) . manager . getService ( svcname ) . isMe ( ) ; } catch ( e ) { }
if ( meshCheck && require ( 'win-bcd' ) . isSafeModeService && ! require ( 'win-bcd' ) . isSafeModeService ( svcname ) ) { require ( 'win-bcd' ) . enableSafeModeService ( svcname ) ; }
} catch ( e ) { }
}
2018-09-14 20:42:39 -04:00
2021-01-24 18:19:29 -05:00
if ( process . platform == 'darwin' && ! process . versions )
{
// This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
var child = require ( 'child_process' ) . execFile ( '/bin/sh' , [ 'sh' ] ) ;
child . stdout . str = '' ;
child . stdout . on ( 'data' , function ( chunk ) { this . str += chunk . toString ( ) ; } ) ;
child . stdin . write ( "cat /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist | tr '\n' '\.' | awk '{split($0, a, \"<key>KeepAlive</key>\"); split(a[2], b, \"<\"); split(b[2], c, \">\"); " ) ;
child . stdin . write ( " if(c[1]==\"dict\"){ split(a[2], d, \"</dict>\"); if(split(d[1], truval, \"<true/>\")>1) { split(truval[1], kn1, \"<key>\"); split(kn1[2], kn2, \"</key>\"); print kn2[1]; } }" ) ;
child . stdin . write ( " else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n" ) ;
child . waitExit ( ) ;
if ( child . stdout . str . trim ( ) == 'Crashed' )
{
child = require ( 'child_process' ) . execFile ( '/bin/sh' , [ 'sh' ] ) ;
2019-05-13 17:27:40 -04:00
child . stdout . str = '' ;
child . stdout . on ( 'data' , function ( chunk ) { this . str += chunk . toString ( ) ; } ) ;
2021-01-24 18:19:29 -05:00
child . stdin . write ( "launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n" ) ;
2019-05-13 17:27:40 -04:00
child . waitExit ( ) ;
2021-01-24 18:19:29 -05:00
if ( parseInt ( child . stdout . str . trim ( ) ) == process . pid )
{
// The currently running MeshAgent is us, so we can continue with the update
var plist = require ( 'fs' ) . readFileSync ( '/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist' ) . toString ( ) ;
var tokens = plist . split ( '<key>KeepAlive</key>' ) ;
if ( tokens [ 1 ] . split ( '>' ) [ 0 ] . split ( '<' ) [ 1 ] == 'dict' )
{
var tmp = tokens [ 1 ] . split ( '</dict>' ) ;
tmp . shift ( ) ;
tokens [ 1 ] = '\n <true/>' + tmp . join ( '</dict>' ) ;
tokens = tokens . join ( '<key>KeepAlive</key>' ) ;
require ( 'fs' ) . writeFileSync ( '/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist' , tokens ) ;
var fix = '' ;
fix += ( "function macosRepair()\n" ) ;
fix += ( "{\n" ) ;
fix += ( " var child = require('child_process').execFile('/bin/sh', ['sh']);\n" ) ;
fix += ( " child.stdout.str = '';\n" ) ;
fix += ( " child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });\n" ) ;
fix += ( " child.stderr.on('data', function (chunk) { });\n" ) ;
fix += ( " child.stdin.write('launchctl unload /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n" ) ;
fix += ( " child.stdin.write('launchctl load /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n" ) ;
fix += ( " child.stdin.write('rm /Library/LaunchDaemons/meshagentRepair.plist\\n');\n" ) ;
fix += ( " child.stdin.write('rm " + process . cwd ( ) + "/macosRepair.js\\n');\n" ) ;
fix += ( " child.stdin.write('launchctl stop meshagentRepair\\nexit\\n');\n" ) ;
fix += ( " child.waitExit();\n" ) ;
fix += ( "}\n" ) ;
fix += ( "macosRepair();\n" ) ;
fix += ( "process.exit();\n" ) ;
require ( 'fs' ) . writeFileSync ( process . cwd ( ) + '/macosRepair.js' , fix ) ;
var plist = '<?xml version="1.0" encoding="UTF-8"?>\n' ;
plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n' ;
plist += '<plist version="1.0">\n' ;
plist += ' <dict>\n' ;
plist += ' <key>Label</key>\n' ;
plist += ( ' <string>meshagentRepair</string>\n' ) ;
plist += ' <key>ProgramArguments</key>\n' ;
plist += ' <array>\n' ;
plist += ( ' <string>' + process . execPath + '</string>\n' ) ;
plist += ' <string>macosRepair.js</string>\n' ;
plist += ' </array>\n' ;
plist += ' <key>WorkingDirectory</key>\n' ;
plist += ( ' <string>' + process . cwd ( ) + '</string>\n' ) ;
plist += ' <key>RunAtLoad</key>\n' ;
plist += ' <true/>\n' ;
plist += ' </dict>\n' ;
plist += '</plist>' ;
require ( 'fs' ) . writeFileSync ( '/Library/LaunchDaemons/meshagentRepair.plist' , plist ) ;
child = require ( 'child_process' ) . execFile ( '/bin/sh' , [ 'sh' ] ) ;
child . stdout . str = '' ;
child . stdout . on ( 'data' , function ( chunk ) { this . str += chunk . toString ( ) ; } ) ;
child . stdin . write ( "launchctl load /Library/LaunchDaemons/meshagentRepair.plist\nexit\n" ) ;
child . waitExit ( ) ;
2019-05-13 17:27:40 -04:00
}
}
}
2021-01-24 18:19:29 -05:00
}
2019-05-13 17:27:40 -04:00
2021-01-24 18:19:29 -05:00
// Add an Intel AMT event to the log
function addAmtEvent ( msg )
{
if ( obj . amtevents == null ) { obj . amtevents = [ ] ; }
var d = new Date ( ) ;
obj . amtevents . push ( zeroPad ( d . getHours ( ) , 2 ) + ':' + zeroPad ( d . getMinutes ( ) , 2 ) + ':' + zeroPad ( d . getSeconds ( ) , 2 ) + ', ' + msg ) ;
if ( obj . amtevents . length > 100 ) { obj . amtevents . splice ( 0 , obj . amtevents . length - 100 ) ; }
}
function zeroPad ( num , size ) { var s = '000000000' + num ; return s . substr ( s . length - size ) ; }
// Create Secure IPC for Diagnostic Agent Communications
obj . DAIPC = require ( 'net' ) . createServer ( ) ;
if ( process . platform != 'win32' ) { try { require ( 'fs' ) . unlinkSync ( process . cwd ( ) + '/DAIPC' ) ; } catch ( e ) { } }
obj . DAIPC . IPCPATH = process . platform == 'win32' ? ( '\\\\.\\pipe\\' + require ( '_agentNodeId' ) ( ) + '-DAIPC' ) : ( process . cwd ( ) + '/DAIPC' ) ;
try { obj . DAIPC . listen ( { path : obj . DAIPC . IPCPATH , writableAll : true , maxConnections : 5 } ) ; } catch ( e ) { }
obj . DAIPC . _daipc = [ ] ;
obj . DAIPC . on ( 'connection' , function ( c )
{
c . _send = function ( j )
{
var data = JSON . stringify ( j ) ;
var packet = Buffer . alloc ( data . length + 4 ) ;
packet . writeUInt32LE ( data . length + 4 , 0 ) ;
Buffer . from ( data ) . copy ( packet , 4 ) ;
this . write ( packet ) ;
} ;
this . _daipc . push ( c ) ;
c . parent = this ;
c . on ( 'end' , function ( ) { removeRegisteredApp ( this ) ; } ) ;
c . on ( 'data' , function ( chunk )
{
if ( chunk . length < 4 ) { this . unshift ( chunk ) ; return ; }
var len = chunk . readUInt32LE ( 0 ) ;
if ( len > 8192 ) { removeRegisteredApp ( this ) ; this . end ( ) ; return ; }
if ( chunk . length < len ) { this . unshift ( chunk ) ; return ; }
var data = chunk . slice ( 4 , len ) ;
try { data = JSON . parse ( data . toString ( ) ) ; } catch ( e ) { }
if ( ( data == null ) || ( typeof data . cmd != 'string' ) ) return ;
try
{
switch ( data . cmd )
{
case 'requesthelp' :
if ( this . _registered == null ) return ;
sendConsoleText ( 'Request Help (' + this . _registered + '): ' + data . value ) ;
var help = { } ;
help [ this . _registered ] = data . value ;
try { mesh . SendCommand ( { action : 'sessions' , type : 'help' , value : help } ) ; } catch ( e ) { }
MeshServerLogEx ( 98 , [ this . _registered , data . value ] , "Help Requested, user: " + this . _registered + ", details: " + data . value , null ) ;
break ;
case 'cancelhelp' :
if ( this . _registered == null ) return ;
sendConsoleText ( 'Cancel Help (' + this . _registered + ')' ) ;
try { mesh . SendCommand ( { action : 'sessions' , type : 'help' , value : { } } ) ; } catch ( e ) { }
break ;
case 'register' :
if ( typeof data . value == 'string' )
{
this . _registered = data . value ;
var apps = { } ;
apps [ data . value ] = 1 ;
try { mesh . SendCommand ( { action : 'sessions' , type : 'app' , value : apps } ) ; } catch ( e ) { }
this . _send ( { cmd : 'serverstate' , value : meshServerConnectionState , url : require ( 'MeshAgent' ) . ConnectedServer , amt : ( amt != null ) } ) ;
}
break ;
case 'query' :
switch ( data . value )
{
case 'connection' :
data . result = require ( 'MeshAgent' ) . ConnectedServer ;
this . _send ( data ) ;
break ;
case 'descriptors' :
require ( 'ChainViewer' ) . getSnapshot ( ) . then ( function ( f )
{
this . tag . payload . result = f ;
this . tag . ipc . _send ( this . tag . payload ) ;
} ) . parentPromise . tag = { ipc : this , payload : data } ;
break ;
case 'timerinfo' :
data . result = require ( 'ChainViewer' ) . getTimerInfo ( ) ;
this . _send ( data ) ;
break ;
}
break ;
case 'amtstate' :
if ( amt == null ) return ;
var func = function amtStateFunc ( state ) { if ( state != null ) { amtStateFunc . pipe . _send ( { cmd : 'amtstate' , value : state } ) ; } }
func . pipe = this ;
amt . getMeiState ( 11 , func ) ;
break ;
case 'sessions' :
this . _send ( { cmd : 'sessions' , sessions : tunnelUserCount } ) ;
break ;
case 'meshToolInfo' :
try { mesh . SendCommand ( { action : 'meshToolInfo' , name : data . name , hash : data . hash , cookie : data . cookie ? true : false , pipe : true } ) ; } catch ( e ) { }
break ;
case 'console' :
if ( debugConsole )
{
var args = splitArgs ( data . value ) ;
processConsoleCommand ( args [ 0 ] . toLowerCase ( ) , parseArgs ( args ) , 0 , 'pipe' ) ;
}
break ;
2019-06-14 19:33:53 -04:00
}
2021-01-24 18:19:29 -05:00
}
catch ( e ) { removeRegisteredApp ( this ) ; this . end ( ) ; return ; }
2019-06-14 19:33:53 -04:00
} ) ;
2021-01-24 18:19:29 -05:00
} ) ;
2020-10-12 01:24:30 -04:00
2021-01-24 18:19:29 -05:00
// Send current sessions to registered apps
function broadcastSessionsToRegisteredApps ( x )
{
broadcastToRegisteredApps ( { cmd : 'sessions' , sessions : tunnelUserCount } ) ;
}
2020-10-12 01:24:30 -04:00
2021-01-24 18:19:29 -05:00
// Send this object to all registered local applications
function broadcastToRegisteredApps ( x )
{
if ( ( obj . DAIPC == null ) || ( obj . DAIPC . _daipc == null ) ) return ;
for ( var i in obj . DAIPC . _daipc ) { if ( obj . DAIPC . _daipc [ i ] . _registered != null ) { obj . DAIPC . _daipc [ i ] . _send ( x ) ; } }
}
2020-10-12 01:24:30 -04:00
2021-01-24 18:19:29 -05:00
// Send this object to a specific registered local applications
function sendToRegisteredApp ( appid , x )
{
if ( ( obj . DAIPC == null ) || ( obj . DAIPC . _daipc == null ) ) return ;
for ( var i in obj . DAIPC . _daipc ) { if ( obj . DAIPC . _daipc [ i ] . _registered == appid ) { obj . DAIPC . _daipc [ i ] . _send ( x ) ; } }
}
2020-11-28 00:40:30 -05:00
2021-01-24 18:19:29 -05:00
// Send list of registered apps to the server
function updateRegisteredAppsToServer ( )
{
if ( ( obj . DAIPC == null ) || ( obj . DAIPC . _daipc == null ) ) return ;
var apps = { } ;
for ( var i in obj . DAIPC . _daipc ) { if ( apps [ obj . DAIPC . _daipc [ i ] . _registered ] == null ) { apps [ obj . DAIPC . _daipc [ i ] . _registered ] = 1 ; } else { apps [ obj . DAIPC . _daipc [ i ] . _registered ] ++ ; } }
try { mesh . SendCommand ( { action : 'sessions' , type : 'app' , value : apps } ) ; } catch ( e ) { }
}
// Remove a registered app
function removeRegisteredApp ( pipe )
{
for ( var i = obj . DAIPC . _daipc . length - 1 ; i >= 0 ; i -- ) { if ( obj . DAIPC . _daipc [ i ] === pipe ) { obj . DAIPC . _daipc . splice ( i , 1 ) ; } }
if ( pipe . _registered != null ) updateRegisteredAppsToServer ( ) ;
}
2020-10-12 01:24:30 -04:00
2021-01-24 18:19:29 -05:00
function diagnosticAgent _uninstall ( )
{
require ( 'service-manager' ) . manager . uninstallService ( 'meshagentDiagnostic' ) ;
require ( 'task-scheduler' ) . delete ( 'meshagentDiagnostic/periodicStart' ) ;
}
function diagnosticAgent _installCheck ( install )
{
try
{
var diag = require ( 'service-manager' ) . manager . getService ( 'meshagentDiagnostic' ) ;
return ( diag ) ;
}
catch ( e )
{
2020-10-12 01:24:30 -04:00
}
2021-01-24 18:19:29 -05:00
if ( ! install ) { return ( null ) ; }
2020-10-12 01:24:30 -04:00
2021-01-24 18:19:29 -05:00
var svc = null ;
try
{
require ( 'service-manager' ) . manager . installService (
{
name : 'meshagentDiagnostic' ,
displayName : "Mesh Agent Diagnostic Service" ,
description : "Mesh Agent Diagnostic Service" ,
servicePath : process . execPath ,
parameters : [ '-recovery' ]
//files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
} ) ;
svc = require ( 'service-manager' ) . manager . getService ( 'meshagentDiagnostic' ) ;
}
catch ( e )
{
return ( null ) ;
}
var proxyConfig = require ( 'global-tunnel' ) . proxyConfig ;
var cert = require ( 'MeshAgent' ) . GenerateAgentCertificate ( 'CN=MeshNodeDiagnosticCertificate' ) ;
var nodeid = require ( 'tls' ) . loadCertificate ( cert . root ) . getKeyHash ( ) . toString ( 'base64' ) ;
ddb = require ( 'SimpleDataStore' ) . Create ( svc . appWorkingDirectory ( ) . replace ( '\\' , '/' ) + '/meshagentDiagnostic.db' ) ;
ddb . Put ( 'disableUpdate' , '1' ) ;
ddb . Put ( 'MeshID' , Buffer . from ( require ( 'MeshAgent' ) . ServerInfo . MeshID , 'hex' ) ) ;
ddb . Put ( 'ServerID' , require ( 'MeshAgent' ) . ServerInfo . ServerID ) ;
ddb . Put ( 'MeshServer' , require ( 'MeshAgent' ) . ServerInfo . ServerUri ) ;
if ( cert . root . pfx ) { ddb . Put ( 'SelfNodeCert' , cert . root . pfx ) ; }
if ( cert . tls ) { ddb . Put ( 'SelfNodeTlsCert' , cert . tls . pfx ) ; }
if ( proxyConfig )
{
ddb . Put ( 'WebProxy' , proxyConfig . host + ':' + proxyConfig . port ) ;
}
else
{
ddb . Put ( 'ignoreProxyFile' , '1' ) ;
}
2019-06-14 19:33:53 -04:00
2021-01-24 18:19:29 -05:00
require ( 'MeshAgent' ) . SendCommand ( { action : 'diagnostic' , value : { command : 'register' , value : nodeid } } ) ;
require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : "Diagnostic Agent Registered [" + nodeid . length + "/" + nodeid + "]" } ) ;
2019-06-14 19:33:53 -04:00
2021-01-24 18:19:29 -05:00
delete ddb ;
2019-06-14 19:33:53 -04:00
2021-01-24 18:19:29 -05:00
// Set a recurrent task, to run the Diagnostic Agent every 2 days
require ( 'task-scheduler' ) . create ( { name : 'meshagentDiagnostic/periodicStart' , daily : 2 , time : require ( 'tls' ) . generateRandomInteger ( '0' , '23' ) + ':' + require ( 'tls' ) . generateRandomInteger ( '0' , '59' ) . padStart ( 2 , '0' ) , service : 'meshagentDiagnostic' } ) ;
//require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
2019-06-14 19:33:53 -04:00
2021-01-24 18:19:29 -05:00
return ( svc ) ;
}
2019-06-14 19:33:53 -04:00
2021-01-24 18:19:29 -05:00
// Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed.
if ( ( require ( 'fs' ) . existsSync ( process . cwd ( ) + 'batterystate.txt' ) ) && ( require ( 'fs' ) . watch != null ) )
{
// Setup manual battery monitoring
require ( 'MeshAgent' ) . _batteryFileWatcher = require ( 'fs' ) . watch ( process . cwd ( ) , function ( )
{
if ( require ( 'MeshAgent' ) . _batteryFileTimer != null ) return ;
require ( 'MeshAgent' ) . _batteryFileTimer = setTimeout ( function ( )
{
try
{
require ( 'MeshAgent' ) . _batteryFileTimer = null ;
var data = null ;
try { data = require ( 'fs' ) . readFileSync ( process . cwd ( ) + 'batterystate.txt' ) . toString ( ) ; } catch ( e ) { }
if ( ( data != null ) && ( data . length < 10 ) )
{
data = data . split ( ',' ) ;
if ( ( data . length == 2 ) && ( ( data [ 0 ] == 'ac' ) || ( data [ 0 ] == 'dc' ) ) )
{
var level = parseInt ( data [ 1 ] ) ;
if ( ( level >= 0 ) && ( level <= 100 ) ) { require ( 'MeshAgent' ) . SendCommand ( { action : 'battery' , state : data [ 0 ] , level : level } ) ; }
2020-08-28 23:01:06 -04:00
}
}
2021-01-24 18:19:29 -05:00
} catch ( e ) { }
} , 1000 ) ;
} ) ;
}
else
{
// Setup normal battery monitoring
if ( require ( 'identifiers' ) . isBatteryPowered && require ( 'identifiers' ) . isBatteryPowered ( ) )
{
require ( 'MeshAgent' ) . _battLevelChanged = function _battLevelChanged ( val )
{
_battLevelChanged . self . _currentBatteryLevel = val ;
_battLevelChanged . self . SendCommand ( { action : 'battery' , state : _battLevelChanged . self . _currentPowerState , level : val } ) ;
} ;
require ( 'MeshAgent' ) . _battLevelChanged . self = require ( 'MeshAgent' ) ;
require ( 'MeshAgent' ) . _powerChanged = function _powerChanged ( val )
{
_powerChanged . self . _currentPowerState = ( val == 'AC' ? 'ac' : 'dc' ) ;
_powerChanged . self . SendCommand ( { action : 'battery' , state : ( val == 'AC' ? 'ac' : 'dc' ) , level : _powerChanged . self . _currentBatteryLevel } ) ;
} ;
require ( 'MeshAgent' ) . _powerChanged . self = require ( 'MeshAgent' ) ;
require ( 'MeshAgent' ) . on ( 'Connected' , function ( status )
{
if ( status == 0 )
{
require ( 'power-monitor' ) . removeListener ( 'acdc' , this . _powerChanged ) ;
require ( 'power-monitor' ) . removeListener ( 'batteryLevel' , this . _battLevelChanged ) ;
} else
{
require ( 'power-monitor' ) . on ( 'acdc' , this . _powerChanged ) ;
require ( 'power-monitor' ) . on ( 'batteryLevel' , this . _battLevelChanged ) ;
}
} ) ;
2020-08-31 19:45:45 -04:00
}
2021-01-24 18:19:29 -05:00
}
2020-05-31 14:24:34 -04:00
2021-01-24 18:19:29 -05:00
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
var meshCoreObj = { action : 'coreinfo' , value : ( require ( 'MeshAgent' ) . coreHash ? ( 'MeshCore CRC-' + crc32c ( require ( 'MeshAgent' ) . coreHash ) ) : ( 'MeshCore v6' ) ) , caps : 14 , root : require ( 'user-sessions' ) . isRoot ( ) } ; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
// Get the operating system description string
try { require ( 'os' ) . name ( ) . then ( function ( v ) { meshCoreObj . osdesc = v ; meshCoreObjChanged ( ) ; } ) ; } catch ( e ) { }
// Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
try
{
var userSession = require ( 'user-sessions' ) ;
userSession . on ( 'changed' , function onUserSessionChanged ( )
{
userSession . enumerateUsers ( ) . then ( function ( users )
{
if ( process . platform == 'linux' )
{
if ( userSession . _startTime == null )
{
userSession . _startTime = Date . now ( ) ;
userSession . _count = users . length ;
}
else if ( Date . now ( ) - userSession . _startTime < 10000 && users . length == userSession . _count )
{
userSession . removeAllListeners ( 'changed' ) ;
return ;
2018-08-31 18:23:42 -04:00
}
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
var u = [ ] , a = users . Active ;
for ( var i = 0 ; i < a . length ; i ++ )
{
var un = a [ i ] . Domain ? ( a [ i ] . Domain + '\\' + a [ i ] . Username ) : ( a [ i ] . Username ) ;
if ( u . indexOf ( un ) == - 1 ) { u . push ( un ) ; } // Only push users in the list once.
}
meshCoreObj . users = u ;
meshCoreObjChanged ( ) ;
2020-11-02 03:44:07 -05:00
} ) ;
2021-01-24 18:19:29 -05:00
} ) ;
userSession . emit ( 'changed' ) ;
//userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
//userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
} catch ( e ) { }
var meshServerConnectionState = 0 ;
var tunnels = { } ;
var lastNetworkInfo = null ;
var lastPublicLocationInfo = null ;
var selfInfoUpdateTimer = null ;
var http = require ( 'http' ) ;
var net = require ( 'net' ) ;
var fs = require ( 'fs' ) ;
var rtc = require ( 'ILibWebRTC' ) ;
var amt = null ;
var processManager = require ( 'process-manager' ) ;
var wifiScannerLib = null ;
var wifiScanner = null ;
var networkMonitor = null ;
var nextTunnelIndex = 1 ;
var apftunnel = null ;
var tunnelUserCount = { terminal : { } , files : { } , tcp : { } , udp : { } , msg : { } } ; // List of userid->count sessions for terminal, files and TCP/UDP routing
// Add to the server event log
function MeshServerLog ( msg , state )
{
if ( typeof msg == 'string' ) { msg = { action : 'log' , msg : msg } ; } else { msg . action = 'log' ; }
if ( state )
{
if ( state . userid ) { msg . userid = state . userid ; }
if ( state . username ) { msg . username = state . username ; }
if ( state . sessionid ) { msg . sessionid = state . sessionid ; }
if ( state . remoteaddr ) { msg . remoteaddr = state . remoteaddr ; }
}
mesh . SendCommand ( msg ) ;
}
2018-08-29 21:47:22 -04:00
2021-01-24 18:19:29 -05:00
// Add to the server event log, use internationalized events
function MeshServerLogEx ( id , args , msg , state )
{
var msg = { action : 'log' , msgid : id , msgArgs : args , msg : msg } ;
if ( state )
{
if ( state . userid ) { msg . userid = state . userid ; }
if ( state . username ) { msg . username = state . username ; }
if ( state . sessionid ) { msg . sessionid = state . sessionid ; }
if ( state . remoteaddr ) { msg . remoteaddr = state . remoteaddr ; }
2020-09-08 21:59:58 -04:00
}
2021-01-24 18:19:29 -05:00
mesh . SendCommand ( msg ) ;
}
2020-09-08 21:59:58 -04:00
2021-01-24 18:19:29 -05:00
// Import libraries
db = require ( 'SimpleDataStore' ) . Shared ( ) ;
sha = require ( 'SHA256Stream' ) ;
mesh = require ( 'MeshAgent' ) ;
childProcess = require ( 'child_process' ) ;
if ( mesh . hasKVM == 1 )
{ // if the agent is compiled with KVM support
// Check if this computer supports a desktop
try
{
if ( ( process . platform == 'win32' ) || ( process . platform == 'darwin' ) || ( require ( 'monitor-info' ) . kvm _x11 _support ) )
{
meshCoreObj . caps |= 1 ; meshCoreObjChanged ( ) ;
} else if ( process . platform == 'linux' || process . platform == 'freebsd' )
{
require ( 'monitor-info' ) . on ( 'kvmSupportDetected' , function ( value ) { meshCoreObj . caps |= 1 ; meshCoreObjChanged ( ) ; } ) ;
2019-07-29 19:35:48 -04:00
}
2021-01-24 18:19:29 -05:00
} catch ( e ) { }
}
mesh . DAIPC = obj . DAIPC ;
2019-07-29 19:35:48 -04:00
2021-01-24 18:19:29 -05:00
/ *
// Try to load up the network monitor
try {
networkMonitor = require ( 'NetworkMonitor' ) ;
networkMonitor . on ( 'change' , function ( ) { sendNetworkUpdateNagle ( ) ; } ) ;
networkMonitor . on ( 'add' , function ( addr ) { sendNetworkUpdateNagle ( ) ; } ) ;
networkMonitor . on ( 'remove' , function ( addr ) { sendNetworkUpdateNagle ( ) ; } ) ;
} catch ( e ) { networkMonitor = null ; }
* /
2019-06-14 19:33:53 -04:00
2021-01-24 18:19:29 -05:00
// Fetch the SMBios Tables
var SMBiosTables = null ;
var SMBiosTablesRaw = null ;
try
{
var SMBiosModule = null ;
try { SMBiosModule = require ( 'smbios' ) ; } catch ( e ) { }
if ( SMBiosModule != null )
{
SMBiosModule . get ( function ( data )
{
if ( data != null )
{
SMBiosTablesRaw = data ;
SMBiosTables = require ( 'smbios' ) . parse ( data )
if ( mesh . isControlChannelConnected ) { mesh . SendCommand ( { action : 'smbios' , value : SMBiosTablesRaw } ) ; }
2018-01-09 23:13:41 -05:00
2021-01-24 18:19:29 -05:00
// If SMBios tables say that Intel AMT is present, try to connect MEI
if ( SMBiosTables . amtInfo && ( SMBiosTables . amtInfo . AMT == true ) )
{
var amtmodule = require ( 'amt-manage' ) ;
amt = new amtmodule ( mesh , db , false ) ;
amt . on ( 'portBinding_LMS' , function ( map ) { mesh . SendCommand ( { action : 'lmsinfo' , value : { ports : map . keys ( ) } } ) ; } ) ;
amt . on ( 'stateChange_LMS' , function ( v ) { if ( ! meshCoreObj . intelamt ) { meshCoreObj . intelamt = { } ; } meshCoreObj . intelamt . microlms = v ; meshCoreObjChanged ( ) ; } ) ; // 0 = Disabled, 1 = Connecting, 2 = Connected
amt . onStateChange = function ( state ) { if ( state == 2 ) { sendPeriodicServerUpdate ( 1 ) ; } } // MEI State
amt . reset ( ) ;
2018-12-30 16:32:40 -05:00
}
2021-01-24 18:19:29 -05:00
}
} ) ;
2017-08-28 15:48:53 -04:00
}
2021-01-24 18:19:29 -05:00
} catch ( e ) { sendConsoleText ( "ex1: " + e ) ; }
// Try to load up the WIFI scanner
try
{
var wifiScannerLib = require ( 'wifi-scanner' ) ;
wifiScanner = new wifiScannerLib ( ) ;
wifiScanner . on ( 'accessPoint' , function ( data ) { sendConsoleText ( "wifiScanner: " + data ) ; } ) ;
} catch ( e ) { wifiScannerLib = null ; wifiScanner = null ; }
// Get our location (lat/long) using our public IP address
var getIpLocationDataExInProgress = false ;
var getIpLocationDataExCounts = [ 0 , 0 ] ;
function getIpLocationDataEx ( func )
{
if ( getIpLocationDataExInProgress == true ) { return false ; }
try
{
getIpLocationDataExInProgress = true ;
getIpLocationDataExCounts [ 0 ] ++ ;
var options = http . parseUri ( "http://ipinfo.io/json" ) ;
options . method = 'GET' ;
http . request ( options , function ( resp )
{
if ( resp . statusCode == 200 )
{
var geoData = '' ;
resp . data = function ( geoipdata ) { geoData += geoipdata ; } ;
resp . end = function ( )
{
var location = null ;
try
{
if ( typeof geoData == 'string' )
{
var result = JSON . parse ( geoData ) ;
if ( result . ip && result . loc ) { location = result ; }
}
} catch ( e ) { }
if ( func ) { getIpLocationDataExCounts [ 1 ] ++ ; func ( location ) ; }
}
} else
{ func ( null ) ; }
getIpLocationDataExInProgress = false ;
} ) . end ( ) ;
return true ;
2017-08-28 15:48:53 -04:00
}
2021-01-24 18:19:29 -05:00
catch ( e ) { return false ; }
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
function clearGatewayMac ( str )
{
if ( str == null ) return null ;
var x = JSON . parse ( str ) ;
for ( var i in x . netif ) { if ( x . netif [ i ] . gatewaymac ) { delete x . netif [ i ] . gatewaymac } }
return JSON . stringify ( x ) ;
}
function getIpLocationData ( func )
{
// Get the location information for the cache if possible
var publicLocationInfo = db . Get ( 'publicLocationInfo' ) ;
if ( publicLocationInfo != null ) { publicLocationInfo = JSON . parse ( publicLocationInfo ) ; }
if ( publicLocationInfo == null )
{
// Nothing in the cache, fetch the data
getIpLocationDataEx ( function ( locationData )
{
if ( locationData != null )
{
publicLocationInfo = { } ;
publicLocationInfo . netInfoStr = lastNetworkInfo ;
publicLocationInfo . locationData = locationData ;
var x = db . Put ( 'publicLocationInfo' , JSON . stringify ( publicLocationInfo ) ) ; // Save to database
if ( func ) func ( locationData ) ; // Report the new location
}
else
{
if ( func ) func ( null ) ; // Report no location
}
} ) ;
}
else
{
// Check the cache
if ( clearGatewayMac ( publicLocationInfo . netInfoStr ) == clearGatewayMac ( lastNetworkInfo ) )
{
// Cache match
if ( func ) func ( publicLocationInfo . locationData ) ;
}
else
{
// Cache mismatch
getIpLocationDataEx ( function ( locationData )
{
if ( locationData != null )
{
2017-09-01 20:34:02 -04:00
publicLocationInfo = { } ;
publicLocationInfo . netInfoStr = lastNetworkInfo ;
publicLocationInfo . locationData = locationData ;
var x = db . Put ( 'publicLocationInfo' , JSON . stringify ( publicLocationInfo ) ) ; // Save to database
if ( func ) func ( locationData ) ; // Report the new location
2021-01-24 18:19:29 -05:00
}
else
{
if ( func ) func ( publicLocationInfo . locationData ) ; // Can't get new location, report the old location
2017-09-01 20:34:02 -04:00
}
2017-08-28 15:48:53 -04:00
} ) ;
}
}
2021-01-24 18:19:29 -05:00
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Polyfill String.endsWith
if ( ! String . prototype . endsWith )
{
String . prototype . endsWith = function ( searchString , position )
{
var subjectString = this . toString ( ) ;
if ( typeof position !== 'number' || ! isFinite ( position ) || Math . floor ( position ) !== position || position > subjectString . length ) { position = subjectString . length ; }
position -= searchString . length ;
var lastIndex = subjectString . lastIndexOf ( searchString , position ) ;
return lastIndex !== - 1 && lastIndex === position ;
} ;
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Polyfill path.join
obj . path =
{
join : function ( )
{
2017-08-28 12:27:45 -04:00
var x = [ ] ;
2021-01-24 18:19:29 -05:00
for ( var i in arguments )
{
2017-08-28 12:27:45 -04:00
var w = arguments [ i ] ;
2021-01-24 18:19:29 -05:00
if ( w != null )
{
2017-08-28 12:27:45 -04:00
while ( w . endsWith ( '/' ) || w . endsWith ( '\\' ) ) { w = w . substring ( 0 , w . length - 1 ) ; }
2021-01-24 18:19:29 -05:00
if ( i != 0 )
{
2017-10-23 17:09:58 -04:00
while ( w . startsWith ( '/' ) || w . startsWith ( '\\' ) ) { w = w . substring ( 1 ) ; }
}
2017-08-28 12:27:45 -04:00
x . push ( w ) ;
}
}
if ( x . length == 0 ) return '/' ;
return x . join ( '/' ) ;
}
} ;
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Replace a string with a number if the string is an exact number
function toNumberIfNumber ( x ) { if ( ( typeof x == 'string' ) && ( + parseInt ( x ) === x ) ) { x = parseInt ( x ) ; } return x ; }
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Convert decimal to hex
function char2hex ( i ) { return ( i + 0x100 ) . toString ( 16 ) . substr ( - 2 ) . toUpperCase ( ) ; }
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Convert a raw string to a hex string
function rstr2hex ( input ) { var r = '' , i ; for ( i = 0 ; i < input . length ; i ++ ) { r += char2hex ( input . charCodeAt ( i ) ) ; } return r ; }
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Convert a buffer into a string
function buf2rstr ( buf ) { var r = '' ; for ( var i = 0 ; i < buf . length ; i ++ ) { r += String . fromCharCode ( buf [ i ] ) ; } return r ; }
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
function hex2rstr ( d )
{
if ( typeof d != "string" || d . length == 0 ) return '' ;
var r = '' , m = ( '' + d ) . match ( /../g ) , t ;
while ( t = m . shift ( ) ) r += String . fromCharCode ( '0x' + t ) ;
return r
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Convert an object to string with all functions
function objToString ( x , p , pad , ret )
{
if ( ret == undefined ) ret = '' ;
if ( p == undefined ) p = 0 ;
if ( x == null ) { return '[null]' ; }
if ( p > 8 ) { return '[...]' ; }
if ( x == undefined ) { return '[undefined]' ; }
if ( typeof x == 'string' ) { if ( p == 0 ) return x ; return '"' + x + '"' ; }
if ( typeof x == 'buffer' ) { return '[buffer]' ; }
if ( typeof x != 'object' ) { return x ; }
var r = '{' + ( ret ? '\r\n' : ' ' ) ;
for ( var i in x ) { if ( i != '_ObjectID' ) { r += ( addPad ( p + 2 , pad ) + i + ': ' + objToString ( x [ i ] , p + 2 , pad , ret ) + ( ret ? '\r\n' : ' ' ) ) ; } }
return r + addPad ( p , pad ) + '}' ;
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Return p number of spaces
function addPad ( p , ret ) { var r = '' ; for ( var i = 0 ; i < p ; i ++ ) { r += ret ; } return r ; }
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Split a string taking into account the quoats. Used for command line parsing
function splitArgs ( str )
{
var myArray = [ ] , myRegexp = /[^\s"]+|"([^"]*)"/gi ;
do { var match = myRegexp . exec ( str ) ; if ( match != null ) { myArray . push ( match [ 1 ] ? match [ 1 ] : match [ 0 ] ) ; } } while ( match != null ) ;
return myArray ;
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Parse arguments string array into an object
function parseArgs ( argv )
{
var results = { '_' : [ ] } , current = null ;
for ( var i = 1 , len = argv . length ; i < len ; i ++ )
{
var x = argv [ i ] ;
if ( x . length > 2 && x [ 0 ] == '-' && x [ 1 ] == '-' )
{
if ( current != null ) { results [ current ] = true ; }
current = x . substring ( 2 ) ;
} else
{
if ( current != null ) { results [ current ] = toNumberIfNumber ( x ) ; current = null ; } else { results [ '_' ] . push ( toNumberIfNumber ( x ) ) ; }
2017-08-28 12:27:45 -04:00
}
}
2021-01-24 18:19:29 -05:00
if ( current != null ) { results [ current ] = true ; }
return results ;
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Get server target url with a custom path
function getServerTargetUrl ( path )
{
var x = mesh . ServerUrl ;
//sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
if ( x == null ) { return null ; }
if ( path == null ) { path = '' ; }
x = http . parseUri ( x ) ;
if ( x == null ) return null ;
return x . protocol + '//' + x . host + ':' + x . port + '/' + path ;
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Get server url. If the url starts with "*/..." change it, it not use the url as is.
function getServerTargetUrlEx ( url )
{
if ( url . substring ( 0 , 2 ) == '*/' ) { return getServerTargetUrl ( url . substring ( 2 ) ) ; }
return url ;
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Send a wake-on-lan packet
function sendWakeOnLan ( hexMac )
{
hexMac = hexMac . split ( ':' ) . join ( '' ) ;
var count = 0 ;
try
{
var interfaces = require ( 'os' ) . networkInterfaces ( ) ;
var magic = 'FFFFFFFFFFFF' ;
for ( var x = 1 ; x <= 16 ; ++ x ) { magic += hexMac ; }
var magicbin = Buffer . from ( magic , 'hex' ) ;
for ( var adapter in interfaces )
{
if ( interfaces . hasOwnProperty ( adapter ) )
{
for ( var i = 0 ; i < interfaces [ adapter ] . length ; ++ i )
{
var addr = interfaces [ adapter ] [ i ] ;
if ( ( addr . family == 'IPv4' ) && ( addr . mac != '00:00:00:00:00:00' ) )
{
try
{
var socket = require ( 'dgram' ) . createSocket ( { type : 'udp4' } ) ;
socket . bind ( { address : addr . address } ) ;
socket . setBroadcast ( true ) ;
socket . setMulticastInterface ( addr . address ) ;
socket . setMulticastTTL ( 1 ) ;
socket . send ( magicbin , 7 , '255.255.255.255' ) ;
socket . descriptorMetadata = 'WoL (' + addr . address + ' => ' + hexMac + ')' ;
count ++ ;
2017-09-20 17:44:22 -04:00
}
2021-01-24 18:19:29 -05:00
catch ( e ) { }
2017-09-20 17:44:22 -04:00
}
}
}
2021-01-24 18:19:29 -05:00
}
} catch ( e ) { }
return count ;
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Handle a mesh agent command
function handleServerCommand ( data )
{
if ( typeof data == 'object' )
{
// If this is a console command, parse it and call the console handler
switch ( data . action )
{
case 'agentupdate' :
agentUpdate _Start ( data . url , { hash : data . hash , tlshash : data . servertlshash , sessionid : data . sessionid } ) ;
break ;
case 'msg' : {
switch ( data . type )
{
case 'console' : { // Process a console command
if ( data . value && data . sessionid )
{
MeshServerLogEx ( 17 , [ data . value ] , "Processing console command: " + data . value , data ) ;
var args = splitArgs ( data . value ) ;
processConsoleCommand ( args [ 0 ] . toLowerCase ( ) , parseArgs ( args ) , data . rights , data . sessionid ) ;
2017-08-28 15:48:53 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'tunnel' : {
if ( data . value != null )
{ // Process a new tunnel connection request
// Create a new tunnel object
var xurl = getServerTargetUrlEx ( data . value ) ;
if ( xurl != null )
{
xurl = xurl . split ( '$' ) . join ( '%24' ) . split ( '@' ) . join ( '%40' ) ; // Escape the $ and @ characters
var woptions = http . parseUri ( xurl ) ;
woptions . perMessageDeflate = false ;
if ( typeof data . perMessageDeflate == 'boolean' ) { woptions . perMessageDeflate = data . perMessageDeflate ; }
// Perform manual server TLS certificate checking based on the certificate hash given by the server.
woptions . rejectUnauthorized = 0 ;
woptions . checkServerIdentity = function checkServerIdentity ( certs )
{
// If the tunnel certificate matches the control channel certificate, accept the connection
try { if ( require ( 'MeshAgent' ) . ServerInfo . ControlChannelCertificate . digest == certs [ 0 ] . digest ) return ; } catch ( ex ) { }
try { if ( require ( 'MeshAgent' ) . ServerInfo . ControlChannelCertificate . fingerprint == certs [ 0 ] . fingerprint ) return ; } catch ( ex ) { }
// Check that the certificate is the one expected by the server, fail if not.
if ( ( checkServerIdentity . servertlshash != null ) && ( checkServerIdentity . servertlshash . toLowerCase ( ) != certs [ 0 ] . digest . split ( ':' ) . join ( '' ) . toLowerCase ( ) ) ) { throw new Error ( 'BadCert' ) }
2018-04-11 16:49:05 -04:00
}
2021-01-24 18:19:29 -05:00
woptions . checkServerIdentity . servertlshash = data . servertlshash ;
//sendConsoleText(JSON.stringify(woptions));
//sendConsoleText('TUNNEL: ' + JSON.stringify(data));
var tunnel = http . request ( woptions ) ;
tunnel . upgrade = onTunnelUpgrade ;
tunnel . on ( 'error' , function ( e ) { sendConsoleText ( "ERROR: Unable to connect relay tunnel to: " + this . url + ", " + JSON . stringify ( e ) ) ; } ) ;
tunnel . sessionid = data . sessionid ;
tunnel . rights = data . rights ;
tunnel . consent = data . consent ;
tunnel . privacybartext = data . privacybartext ? data . privacybartext : "Sharing desktop with: {0}" ;
tunnel . username = data . username + ( data . guestname ? ( ' - ' + data . guestname ) : '' ) ;
tunnel . realname = ( data . realname ? data . realname : data . username ) + ( data . guestname ? ( ' - ' + data . guestname ) : '' ) ;
tunnel . userid = data . userid ;
tunnel . remoteaddr = data . remoteaddr ;
tunnel . state = 0 ;
tunnel . url = xurl ;
tunnel . protocol = 0 ;
tunnel . soptions = data . soptions ;
tunnel . tcpaddr = data . tcpaddr ;
tunnel . tcpport = data . tcpport ;
tunnel . udpaddr = data . udpaddr ;
tunnel . udpport = data . udpport ;
tunnel . end ( ) ;
// Put the tunnel in the tunnels list
var index = nextTunnelIndex ++ ;
tunnel . index = index ;
tunnels [ index ] = tunnel ;
//sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
2018-04-11 16:49:05 -04:00
}
2018-12-07 19:36:27 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'messagebox' : {
// Display a message box
if ( data . title && data . msg )
{
MeshServerLogEx ( 18 , [ data . title , data . msg ] , "Displaying message box, title=" + data . title + ", message=" + data . msg , data ) ;
data . msg = data . msg . split ( '\r' ) . join ( '\\r' ) . split ( '\n' ) . join ( '\\n' ) ;
try { require ( 'message-box' ) . create ( data . title , data . msg , 120 ) ; } catch ( e ) { }
2019-07-31 19:49:23 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'ps' : {
// Return the list of running processes
if ( data . sessionid )
{
processManager . getProcesses ( function ( plist )
{
mesh . SendCommand ( { action : 'msg' , type : 'ps' , value : JSON . stringify ( plist ) , sessionid : data . sessionid } ) ;
} ) ;
2019-07-31 19:49:23 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'pskill' : {
// Kill a process
if ( data . value )
{
MeshServerLogEx ( 19 , [ data . value ] , "Killing process " + data . value , data ) ;
try { process . kill ( data . value ) ; } catch ( e ) { sendConsoleText ( "pskill: " + JSON . stringify ( e ) ) ; }
2019-07-31 19:49:23 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'services' : {
// Return the list of installed services
var services = null ;
try { services = require ( 'service-manager' ) . manager . enumerateService ( ) ; } catch ( e ) { }
if ( services != null ) { mesh . SendCommand ( { action : 'msg' , type : 'services' , value : JSON . stringify ( services ) , sessionid : data . sessionid } ) ; }
break ;
}
case 'serviceStop' : {
// Stop a service
try
{
var service = require ( 'service-manager' ) . manager . getService ( data . serviceName ) ;
if ( service != null ) { service . stop ( ) ; }
} catch ( e ) { }
break ;
}
case 'serviceStart' : {
// Start a service
try
{
var service = require ( 'service-manager' ) . manager . getService ( data . serviceName ) ;
if ( service != null ) { service . start ( ) ; }
} catch ( e ) { }
break ;
}
case 'serviceRestart' : {
// Restart a service
try
{
var service = require ( 'service-manager' ) . manager . getService ( data . serviceName ) ;
if ( service != null ) { service . restart ( ) ; }
} catch ( e ) { }
break ;
}
case 'deskBackground' :
{
// Toggle desktop background
try
2019-12-10 13:24:10 -05:00
{
2021-01-24 18:19:29 -05:00
if ( process . platform == 'win32' )
{
var stype = require ( 'user-sessions' ) . getProcessOwnerName ( process . pid ) . tsid == 0 ? 1 : 0 ;
var sid = undefined ;
if ( stype == 1 )
{
if ( require ( 'MeshAgent' ) . _tsid != null )
{
stype = 5 ;
sid = require ( 'MeshAgent' ) . _tsid ;
2019-12-10 13:24:10 -05:00
}
}
2021-01-24 18:19:29 -05:00
var id = require ( 'user-sessions' ) . getProcessOwnerName ( process . pid ) . tsid == 0 ? 1 : 0 ;
var child = require ( 'child_process' ) . execFile ( process . execPath , [ process . execPath . split ( '\\' ) . pop ( ) , '-b64exec' , 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=' ] , { type : stype , uid : sid } ) ;
child . stdout . str = '' ; child . stdout . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stderr . on ( 'data' , function ( ) { } ) ;
child . waitExit ( ) ;
var current = child . stdout . str . trim ( ) ;
if ( current != '' ) { require ( 'MeshAgent' ) . _wallpaper = current ; }
child = require ( 'child_process' ) . execFile ( process . execPath , [ process . execPath . split ( '\\' ) . pop ( ) , '-b64exec' , 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=' , current != '' ? '""' : require ( 'MeshAgent' ) . _wallpaper ] , { type : stype , uid : sid } ) ;
child . stdout . str = '' ; child . stdout . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stderr . on ( 'data' , function ( ) { } ) ;
child . waitExit ( ) ;
} else
{
var id = require ( 'user-sessions' ) . consoleUid ( ) ;
var current = require ( 'linux-gnome-helpers' ) . getDesktopWallpaper ( id ) ;
if ( current != '/dev/null' ) { require ( 'MeshAgent' ) . _wallpaper = current ; }
require ( 'linux-gnome-helpers' ) . setDesktopWallpaper ( id , current != '/dev/null' ? undefined : require ( 'MeshAgent' ) . _wallpaper ) ;
2019-11-28 15:27:44 -05:00
}
2021-01-24 18:19:29 -05:00
} catch ( e )
{
sendConsoleText ( e ) ;
2019-03-15 16:55:53 -04:00
}
2019-02-17 00:16:39 -05:00
break ;
}
2021-01-24 18:19:29 -05:00
case 'openUrl' : {
// Open a local web browser and return success/fail
MeshServerLogEx ( 20 , [ data . url ] , "Opening: " + data . url , data ) ;
sendConsoleText ( "OpenURL: " + data . url ) ;
if ( data . url ) { mesh . SendCommand ( { action : 'msg' , type : 'openUrl' , url : data . url , sessionid : data . sessionid , success : ( openUserDesktopUrl ( data . url ) != null ) } ) ; }
break ;
}
case 'getclip' : {
// Send the load clipboard back to the user
//sendConsoleText('getClip: ' + JSON.stringify(data));
if ( require ( 'MeshAgent' ) . isService )
{
require ( 'clipboard' ) . dispatchRead ( ) . then ( function ( str )
{
if ( str )
{
MeshServerLogEx ( 21 , [ str . length ] , "Getting clipboard content, " + str . length + " byte(s)" , data ) ;
mesh . SendCommand ( { action : 'msg' , type : 'getclip' , sessionid : data . sessionid , data : str , tag : data . tag } ) ;
}
} ) ;
} else
{
require ( "clipboard" ) . read ( ) . then ( function ( str )
{
if ( str )
{
MeshServerLogEx ( 21 , [ str . length ] , "Getting clipboard content, " + str . length + " byte(s)" , data ) ;
mesh . SendCommand ( { action : 'msg' , type : 'getclip' , sessionid : data . sessionid , data : str , tag : data . tag } ) ;
}
} ) ;
2019-02-17 00:16:39 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'setclip' : {
// Set the load clipboard to a user value
sendConsoleText ( 'setClip: ' + JSON . stringify ( data ) ) ;
if ( typeof data . data == 'string' )
{
MeshServerLogEx ( 22 , [ data . data . length ] , "Setting clipboard content, " + data . data . length + " byte(s)" , data ) ;
if ( require ( 'MeshAgent' ) . isService ) { require ( 'clipboard' ) . dispatchWrite ( data . data ) ; } else { require ( "clipboard" ) ( data . data ) ; } // Set the clipboard
mesh . SendCommand ( { action : 'msg' , type : 'setclip' , sessionid : data . sessionid , success : true } ) ;
2019-12-12 20:45:42 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
case 'userSessions' : {
// Send back current user sessions list, this is Windows only.
//sendConsoleText('userSessions: ' + JSON.stringify(data));
if ( process . platform != 'win32' ) break ;
var p = require ( 'user-sessions' ) . enumerateUsers ( ) ;
p . sessionid = data . sessionid ;
p . then ( function ( u ) { mesh . SendCommand ( { action : 'msg' , type : 'userSessions' , sessionid : data . sessionid , data : u , tag : data . tag } ) ; } ) ;
break ;
2019-07-29 19:35:48 -04:00
}
2021-01-24 18:19:29 -05:00
case 'cpuinfo' :
// CPU & memory utilization
var cpuuse = require ( 'sysinfo' ) . cpuUtilization ( ) ;
cpuuse . sessionid = data . sessionid ;
cpuuse . tag = data . tag ;
cpuuse . then ( function ( data )
{
mesh . SendCommand ( JSON . stringify (
{
action : 'msg' ,
type : 'cpuinfo' ,
cpu : data ,
memory : require ( 'sysinfo' ) . memUtilization ( ) ,
thermals : require ( 'sysinfo' ) . thermals == null ? [ ] : require ( 'sysinfo' ) . thermals ( ) ,
sessionid : this . sessionid ,
tag : this . tag
} ) ) ;
} , function ( ex ) { } ) ;
break ;
case 'localapp' :
// Send a message to a local application
sendConsoleText ( 'localappMsg: ' + data . appid + ', ' + JSON . stringify ( data . value ) ) ;
if ( data . appid != null ) { sendToRegisteredApp ( data . appid , data . value ) ; } else { broadcastToRegisteredApps ( data . value ) ; }
break ;
default :
// Unknown action, ignore it.
break ;
2019-06-19 20:16:50 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'acmactivate' : {
if ( amt != null )
{
MeshServerLogEx ( 23 , null , "Attempting Intel AMT ACM mode activation" , data ) ;
amt . setAcmResponse ( data ) ;
2017-08-28 15:48:53 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'wakeonlan' : {
// Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
//sendConsoleText("Server requesting wake-on-lan for: " + data.macs.join(', '));
for ( var i in data . macs ) { sendWakeOnLan ( data . macs [ i ] ) ; }
break ;
}
case 'runcommands' : {
if ( mesh . cmdchild != null ) { sendConsoleText ( "Run commands can't execute, already busy." ) ; break ; }
sendConsoleText ( "Run commands (" + data . runAsUser + "): " + data . cmds ) ;
2020-09-29 16:20:08 -04:00
2021-01-24 18:19:29 -05:00
// data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly
var options = { } ;
if ( data . runAsUser > 0 )
{
try { options . uid = require ( 'user-sessions' ) . consoleUid ( ) ; } catch ( e ) { }
options . type = require ( 'child_process' ) . SpawnTypes . TERM ;
}
if ( data . runAsUser == 2 )
{
if ( options . uid == null ) break ;
if ( ( ( require ( 'user-sessions' ) . minUid != null ) && ( options . uid < require ( 'user-sessions' ) . minUid ( ) ) ) ) break ; // This command can only run as user.
}
if ( process . platform == 'win32' )
{
if ( data . type == 1 )
{
// Windows command shell
mesh . cmdchild = require ( 'child_process' ) . execFile ( process . env [ 'windir' ] + '\\system32\\cmd.exe' , [ 'cmd' ] , options ) ;
2020-07-11 02:12:43 -04:00
mesh . cmdchild . descriptorMetadata = 'UserCommandsShell' ;
mesh . cmdchild . stdout . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
mesh . cmdchild . stderr . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
2021-01-24 18:19:29 -05:00
mesh . cmdchild . stdin . write ( data . cmds + '\r\nexit\r\n' ) ;
mesh . cmdchild . on ( 'exit' , function ( ) { sendConsoleText ( "Run commands completed." ) ; delete mesh . cmdchild ; } ) ;
} else if ( data . type == 2 )
{
// Windows Powershell
mesh . cmdchild = require ( 'child_process' ) . execFile ( process . env [ 'windir' ] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe' , [ 'powershell' , '-noprofile' , '-nologo' , '-command' , '-' ] , options ) ;
mesh . cmdchild . descriptorMetadata = 'UserCommandsPowerShell' ;
mesh . cmdchild . stdout . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
mesh . cmdchild . stderr . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
mesh . cmdchild . stdin . write ( data . cmds + '\r\nexit\r\n' ) ;
2020-07-11 02:12:43 -04:00
mesh . cmdchild . on ( 'exit' , function ( ) { sendConsoleText ( "Run commands completed." ) ; delete mesh . cmdchild ; } ) ;
}
2021-01-24 18:19:29 -05:00
} else if ( data . type == 3 )
{
// Linux shell
mesh . cmdchild = require ( 'child_process' ) . execFile ( '/bin/sh' , [ 'sh' ] , options ) ;
mesh . cmdchild . descriptorMetadata = 'UserCommandsShell' ;
mesh . cmdchild . stdout . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
mesh . cmdchild . stderr . on ( 'data' , function ( c ) { sendConsoleText ( c . toString ( ) ) ; } ) ;
mesh . cmdchild . stdin . write ( data . cmds . split ( '\r' ) . join ( '' ) + '\nexit\n' ) ;
mesh . cmdchild . on ( 'exit' , function ( ) { sendConsoleText ( "Run commands completed." ) ; delete mesh . cmdchild ; } ) ;
2018-04-19 21:19:15 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'uninstallagent' :
// Uninstall this agent
var agentName = process . platform == 'win32' ? 'Mesh Agent' : 'meshagent' ;
try
{
agentName = require ( 'MeshAgent' ) . serviceName ;
2018-12-07 19:36:27 -05:00
}
2021-01-24 18:19:29 -05:00
catch ( x )
{
2019-02-07 18:00:10 -05:00
}
2021-01-24 18:19:29 -05:00
if ( require ( 'service-manager' ) . manager . getService ( agentName ) . isMe ( ) )
{
try { diagnosticAgent _uninstall ( ) ; } catch ( e ) { }
var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();" ;
this . child = require ( 'child_process' ) . execFile ( process . execPath , [ process . platform == 'win32' ? ( process . execPath . split ( '\\' ) . pop ( ) ) : ( process . execPath . split ( '/' ) . pop ( ) ) , '-b64exec' , Buffer . from ( js ) . toString ( 'base64' ) ] , { type : 4 , detached : true } ) ;
2019-02-07 23:06:01 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
case 'poweraction' : {
// Server telling us to execute a power action
if ( ( mesh . ExecPowerState != undefined ) && ( data . actiontype ) )
{
var forced = 0 ;
if ( data . forced == 1 ) { forced = 1 ; }
data . actiontype = parseInt ( data . actiontype ) ;
MeshServerLogEx ( 25 , [ data . actiontype , forced ] , "Performing power action=" + data . actiontype + ", forced=" + forced , data ) ;
sendConsoleText ( "Performing power action=" + data . actiontype + ", forced=" + forced + '.' ) ;
var r = mesh . ExecPowerState ( data . actiontype , forced ) ;
sendConsoleText ( "ExecPowerState returned code: " + r ) ;
2019-08-06 20:58:29 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'iplocation' : {
// Update the IP location information of this node. Only do this when requested by the server since we have a limited amount of time we can call this per day
getIpLocationData ( function ( location ) { mesh . SendCommand ( { action : 'iplocation' , type : 'publicip' , value : location } ) ; } ) ;
break ;
}
case 'toast' : {
// Display a toast message
if ( data . title && data . msg )
{
MeshServerLogEx ( 26 , [ data . title , data . msg ] , "Displaying toast message, title=" + data . title + ", message=" + data . msg , data ) ;
data . msg = data . msg . split ( '\r' ) . join ( '\\r' ) . split ( '\n' ) . join ( '\\n' ) ;
try { require ( 'toaster' ) . Toast ( data . title , data . msg ) ; } catch ( e ) { }
2019-10-10 14:13:25 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'openUrl' : {
// Open a local web browser and return success/fail
//sendConsoleText('OpenURL: ' + data.url);
MeshServerLogEx ( 20 , [ data . url ] , "Opening: " + data . url , data ) ;
if ( data . url ) { mesh . SendCommand ( { action : 'openUrl' , url : data . url , sessionid : data . sessionid , success : ( openUserDesktopUrl ( data . url ) != null ) } ) ; }
break ;
}
case 'amtconfig' : {
// Perform Intel AMT activation and/or configuration
if ( ( apftunnel != null ) || ( amt == null ) || ( typeof data . user != 'string' ) || ( typeof data . pass != 'string' ) ) break ;
amt . getMeiState ( 15 , function ( state )
{
if ( ( apftunnel != null ) || ( amt == null ) ) return ;
if ( ( state == null ) || ( state . ProvisioningState == null ) ) return ;
if ( ( state . UUID == null ) || ( state . UUID . length != 36 ) ) return ; // Bad UUID
var apfarg = {
mpsurl : mesh . ServerUrl . replace ( '/agent.ashx' , '/apf.ashx' ) ,
mpsuser : data . user , // Agent user name
mpspass : data . pass , // Encrypted login cookie
mpskeepalive : 60000 ,
clientname : state . OsHostname ,
clientaddress : '127.0.0.1' ,
clientuuid : state . UUID ,
conntype : 2 , // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
meiState : state // MEI state will be passed to MPS server
} ;
addAmtEvent ( 'LMS tunnel start.' ) ;
apftunnel = require ( 'amt-apfclient' ) ( { debug : false } , apfarg ) ;
apftunnel . onJsonControl = function ( data )
{
if ( data . action == 'console' ) { addAmtEvent ( data . msg ) ; } // Add console message to AMT event log
if ( data . action == 'mestate' ) { amt . getMeiState ( 15 , function ( state ) { apftunnel . updateMeiState ( state ) ; } ) ; } // Update the MEI state
if ( data . action == 'deactivate' )
{ // Request CCM deactivation
var amtMeiModule , amtMei ;
try { amtMeiModule = require ( 'amt-mei' ) ; amtMei = new amtMeiModule ( ) ; } catch ( ex ) { if ( apftunnel ) apftunnel . sendMeiDeactivationState ( 1 ) ; return ; }
amtMei . on ( 'error' , function ( e ) { if ( apftunnel ) apftunnel . sendMeiDeactivationState ( 1 ) ; } ) ;
amtMei . unprovision ( 1 , function ( status ) { if ( apftunnel ) apftunnel . sendMeiDeactivationState ( status ) ; } ) ; // 0 = Success
2020-10-27 19:40:12 -04:00
}
2021-01-24 18:19:29 -05:00
if ( data . action == 'close' ) { try { apftunnel . disconnect ( ) ; } catch ( e ) { } apftunnel = null ; } // Close the CIRA-LMS connection
2020-10-27 19:40:12 -04:00
}
2021-01-24 18:19:29 -05:00
apftunnel . onChannelClosed = function ( ) { addAmtEvent ( 'LMS tunnel closed.' ) ; apftunnel = null ; }
try { apftunnel . connect ( ) ; } catch ( ex ) { }
} ) ;
break ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
case 'getScript' : {
// Received a configuration script from the server
sendConsoleText ( 'getScript: ' + JSON . stringify ( data ) ) ;
break ;
2020-11-09 19:51:48 -05:00
}
2021-01-24 18:19:29 -05:00
case 'sysinfo' : {
// Fetch system information
getSystemInformation ( function ( results )
{
if ( ( results != null ) && ( data . hash != results . hash ) ) { mesh . SendCommand ( { action : 'sysinfo' , sessionid : this . sessionid , data : results } ) ; }
} ) ;
break ;
2019-09-23 19:46:26 -04:00
}
2021-01-24 18:19:29 -05:00
case 'ping' : { mesh . SendCommand ( '{"action":"pong"}' ) ; break ; }
case 'pong' : { break ; }
case 'plugin' : {
try { require ( data . plugin ) . consoleaction ( data , data . rights , data . sessionid , this ) ; } catch ( e ) { throw e ; }
break ;
}
case 'coredump' :
// Set the current agent coredump situation.
if ( data . value === true )
2021-01-20 15:49:15 -05:00
{
2021-01-24 18:19:29 -05:00
if ( process . platform == 'win32' )
2021-01-20 15:49:15 -05:00
{
2021-01-24 18:19:29 -05:00
// TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
process . coreDumpLocation = process . execPath . replace ( '.exe' , '.dmp' ) ;
} else
2021-01-20 15:49:15 -05:00
{
2021-01-24 18:19:29 -05:00
process . coreDumpLocation = ( process . cwd ( ) != '//' ) ? ( process . cwd ( ) + 'core' ) : null ;
2021-01-20 15:49:15 -05:00
}
2021-01-24 18:19:29 -05:00
} else if ( data . value === false )
{
process . coreDumpLocation = null ;
2021-01-20 15:49:15 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
case 'getcoredump' :
// Ask the agent if a core dump is currently available, if yes, also return the hash of the agent.
var r = { action : 'getcoredump' , value : ( process . coreDumpLocation != null ) } ;
var coreDumpPath = null ;
if ( process . platform == 'win32' ) { coreDumpPath = process . coreDumpLocation ; } else { coreDumpPath = ( process . cwd ( ) != '//' ) ? fs . existsSync ( process . cwd ( ) + 'core' ) : null ; }
if ( ( coreDumpPath != null ) && ( fs . existsSync ( coreDumpPath ) ) )
{
try
{
var coredate = fs . statSync ( coreDumpPath ) . mtime ;
var coretime = new Date ( coredate ) . getTime ( ) ;
var agenttime = new Date ( fs . statSync ( process . execPath ) . mtime ) . getTime ( ) ;
if ( coretime > agenttime ) { r . exists = ( db . Get ( 'CoreDumpTime' ) != coredate ) ; }
} catch ( ex ) { }
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
if ( r . exists == true )
{
r . agenthashhex = getSHA384FileHash ( process . execPath ) . toString ( 'hex' ) ; // Hash of current agent
r . corehashhex = getSHA384FileHash ( coreDumpPath ) . toString ( 'hex' ) ; // Hash of core dump file
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
mesh . SendCommand ( JSON . stringify ( r ) ) ;
break ;
case 'meshToolInfo' :
if ( data . pipe == true ) { delete data . pipe ; delete data . action ; data . cmd = 'meshToolInfo' ; broadcastToRegisteredApps ( data ) ; }
break ;
case 'wget' : // Server uses this command to tell the agent to download a file using HTTPS/GET and place it in a given path. This is used for one-to-many file uploads.
agentFileHttpPendingRequests . push ( data ) ;
serverFetchFile ( ) ;
break ;
default :
// Unknown action, ignore it.
break ;
2017-08-28 12:27:45 -04:00
}
}
2021-01-24 18:19:29 -05:00
}
// Agent just get a file from the server and save it locally.
function serverFetchFile ( )
{
if ( ( Object . keys ( agentFileHttpRequests ) . length > 4 ) || ( agentFileHttpPendingRequests . length == 0 ) ) return ; // No more than 4 active HTTPS requests to the server.
var data = agentFileHttpPendingRequests . shift ( ) ;
if ( ( data . overwrite !== true ) && fs . existsSync ( data . path ) ) return ; // Don't overwrite an existing file.
if ( data . createFolder ) { try { fs . mkdirSync ( data . folder ) ; } catch ( ex ) { } } // If requested, create the local folder.
data . url = 'http' + getServerTargetUrlEx ( '*/' ) . substring ( 2 ) ;
var agentFileHttpOptions = http . parseUri ( data . url ) ;
agentFileHttpOptions . path = data . urlpath ;
// Perform manual server TLS certificate checking based on the certificate hash given by the server.
agentFileHttpOptions . rejectUnauthorized = 0 ;
agentFileHttpOptions . checkServerIdentity = function checkServerIdentity ( certs )
{
// If the tunnel certificate matches the control channel certificate, accept the connection
try { if ( require ( 'MeshAgent' ) . ServerInfo . ControlChannelCertificate . digest == certs [ 0 ] . digest ) return ; } catch ( ex ) { }
try { if ( require ( 'MeshAgent' ) . ServerInfo . ControlChannelCertificate . fingerprint == certs [ 0 ] . fingerprint ) return ; } catch ( ex ) { }
// Check that the certificate is the one expected by the server, fail if not.
if ( ( checkServerIdentity . servertlshash != null ) && ( checkServerIdentity . servertlshash . toLowerCase ( ) != certs [ 0 ] . digest . split ( ':' ) . join ( '' ) . toLowerCase ( ) ) ) { throw new Error ( 'BadCert' ) }
}
agentFileHttpOptions . checkServerIdentity . servertlshash = data . servertlshash ;
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
if ( agentFileHttpOptions == null ) return ;
var agentFileHttpRequest = http . request ( agentFileHttpOptions ,
function ( response )
{
response . xparent = this ;
try
{
response . xfile = fs . createWriteStream ( this . xpath , { flags : 'wbN' } )
response . pipe ( response . xfile ) ;
response . end = function ( ) { delete agentFileHttpRequests [ this . xparent . xurlpath ] ; delete this . xparent ; serverFetchFile ( ) ; }
} catch ( ex ) { delete agentFileHttpRequests [ this . xurlpath ] ; delete response . xparent ; serverFetchFile ( ) ; return ; }
2020-02-01 00:35:47 -05:00
}
2021-01-24 18:19:29 -05:00
) ;
agentFileHttpRequest . on ( 'error' , function ( ex ) { delete agentFileHttpRequests [ this . xurlpath ] ; serverFetchFile ( ) ; } ) ;
agentFileHttpRequest . end ( ) ;
agentFileHttpRequest . xurlpath = data . urlpath ;
agentFileHttpRequest . xpath = data . path ;
agentFileHttpRequests [ data . urlpath ] = agentFileHttpRequest ;
}
2020-02-01 00:35:47 -05:00
2021-01-24 18:19:29 -05:00
// Called when a file changed in the file system
/ *
function onFileWatcher ( a , b ) {
console . log ( 'onFileWatcher' , a , b , this . path ) ;
var response = getDirectoryInfo ( this . path ) ;
if ( ( response != undefined ) && ( response != null ) ) { this . tunnel . s . write ( JSON . stringify ( response ) ) ; }
}
* /
function getSystemInformation ( func )
{
try
{
var results = { hardware : require ( 'identifiers' ) . get ( ) } ; // Hardware info
if ( results . hardware && results . hardware . windows )
{
// Remove extra entries and things that change quickly
var x = results . hardware . windows . osinfo ;
try { delete x . FreePhysicalMemory ; } catch ( e ) { }
try { delete x . FreeSpaceInPagingFiles ; } catch ( e ) { }
try { delete x . FreeVirtualMemory ; } catch ( e ) { }
try { delete x . LocalDateTime ; } catch ( e ) { }
try { delete x . MaxProcessMemorySize ; } catch ( e ) { }
try { delete x . TotalVirtualMemorySize ; } catch ( e ) { }
try { delete x . TotalVisibleMemorySize ; } catch ( e ) { }
try
{
if ( results . hardware . windows . memory ) { for ( var i in results . hardware . windows . memory ) { delete results . hardware . windows . memory [ i ] . Node ; } }
if ( results . hardware . windows . osinfo ) { delete results . hardware . windows . osinfo . Node ; }
if ( results . hardware . windows . partitions ) { for ( var i in results . hardware . windows . partitions ) { delete results . hardware . windows . partitions [ i ] . Node ; } }
} catch ( e ) { }
}
if ( process . platform == 'win32' )
{
results . pendingReboot = require ( 'win-info' ) . pendingReboot ( ) ; // Pending reboot
if ( require ( 'identifiers' ) . volumes != null )
{
try
{
results . volumes = require ( 'identifiers' ) . volumes ( ) ;
}
catch ( e )
{
}
2020-07-24 17:00:49 -04:00
}
2021-01-24 18:19:29 -05:00
}
/ *
if ( process . platform == 'win32' ) {
var defragResult = function ( r ) {
if ( typeof r == 'object' ) { results [ this . callname ] = r ; }
if ( this . callname == 'defrag' ) {
var pr = require ( 'win-info' ) . installedApps ( ) ; // Installed apps
pr . callname = 'installedApps' ;
pr . sessionid = data . sessionid ;
pr . then ( defragResult , defragResult ) ;
}
else {
results . winpatches = require ( 'win-info' ) . qfe ( ) ; // Windows patches
results . hash = require ( 'SHA384Stream' ) . create ( ) . syncHash ( JSON . stringify ( results ) ) . toString ( 'hex' ) ;
func ( results ) ;
}
2020-07-24 17:00:49 -04:00
}
2021-01-24 18:19:29 -05:00
var pr = require ( 'win-info' ) . defrag ( { volume : 'C:' } ) ; // Defrag TODO
pr . callname = 'defrag' ;
pr . sessionid = data . sessionid ;
pr . then ( defragResult , defragResult ) ;
2021-01-18 19:11:15 -05:00
} else {
2021-01-24 18:19:29 -05:00
* /
results . hash = require ( 'SHA384Stream' ) . create ( ) . syncHash ( JSON . stringify ( results ) ) . toString ( 'hex' ) ;
func ( results ) ;
//}
} catch ( e ) { func ( null , e ) ; }
}
// Get a formated response for a given directory path
function getDirectoryInfo ( reqpath )
{
var response = { path : reqpath , dir : [ ] } ;
if ( ( ( reqpath == undefined ) || ( reqpath == '' ) ) && ( process . platform == 'win32' ) )
{
// List all the drives in the root, or the root itself
var results = null ;
try { results = fs . readDrivesSync ( ) ; } catch ( e ) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
if ( results != null )
{
for ( var i = 0 ; i < results . length ; ++ i )
{
var drive = { n : results [ i ] . name , t : 1 } ;
if ( results [ i ] . type == 'REMOVABLE' ) { drive . dt = 'removable' ; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
response . dir . push ( drive ) ;
}
}
} else
{
// List all the files and folders in this path
if ( reqpath == '' ) { reqpath = '/' ; }
var results = null , xpath = obj . path . join ( reqpath , '*' ) ;
//if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
try { results = fs . readdirSync ( xpath ) ; } catch ( e ) { }
if ( results != null )
{
for ( var i = 0 ; i < results . length ; ++ i )
{
if ( ( results [ i ] != '.' ) && ( results [ i ] != '..' ) )
{
var stat = null , p = obj . path . join ( reqpath , results [ i ] ) ;
//if (process.platform == "win32") { p = p.split('/').join('\\'); }
try { stat = fs . statSync ( p ) ; } catch ( e ) { } // TODO: Get file size/date
if ( ( stat != null ) && ( stat != undefined ) )
{
if ( stat . isDirectory ( ) == true )
{
response . dir . push ( { n : results [ i ] , t : 2 , d : stat . mtime } ) ;
} else
{
response . dir . push ( { n : results [ i ] , t : 3 , s : stat . size , d : stat . mtime } ) ;
}
}
}
}
2017-10-23 17:09:58 -04:00
}
}
2021-01-24 18:19:29 -05:00
return response ;
}
// Tunnel callback operations
function onTunnelUpgrade ( response , s , head )
{
this . s = s ;
s . httprequest = this ;
s . end = onTunnelClosed ;
s . tunnel = this ;
s . descriptorMetadata = "MeshAgent_relayTunnel" ;
2019-05-05 01:53:57 -04:00
2021-01-24 18:19:29 -05:00
if ( require ( 'MeshAgent' ) . idleTimeout != null )
{
s . setTimeout ( require ( 'MeshAgent' ) . idleTimeout * 1000 ) ;
s . on ( 'timeout' , function ( )
{
this . ping ( ) ;
this . setTimeout ( require ( 'MeshAgent' ) . idleTimeout * 1000 ) ;
} ) ;
2019-05-05 01:53:57 -04:00
}
2021-01-24 18:19:29 -05:00
//sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
if ( this . tcpport != null )
{
// This is a TCP relay connection, pause now and try to connect to the target.
s . pause ( ) ;
s . data = onTcpRelayServerTunnelData ;
var connectionOptions = { port : parseInt ( this . tcpport ) } ;
if ( this . tcpaddr != null ) { connectionOptions . host = this . tcpaddr ; } else { connectionOptions . host = '127.0.0.1' ; }
s . tcprelay = net . createConnection ( connectionOptions , onTcpRelayTargetTunnelConnect ) ;
s . tcprelay . peerindex = this . index ;
// Add the TCP session to the count and update the server
if ( s . httprequest . userid != null )
{
if ( tunnelUserCount . tcp [ s . httprequest . userid ] == null ) { tunnelUserCount . tcp [ s . httprequest . userid ] = 1 ; } else { tunnelUserCount . tcp [ s . httprequest . userid ] ++ ; }
try { mesh . SendCommand ( { action : 'sessions' , type : 'tcp' , value : tunnelUserCount . tcp } ) ; } catch ( e ) { }
broadcastSessionsToRegisteredApps ( ) ;
}
} if ( this . udpport != null )
{
// This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
s . data = onUdpRelayServerTunnelData ;
s . udprelay = require ( 'dgram' ) . createSocket ( { type : 'udp4' } ) ;
s . udprelay . bind ( { port : 0 } ) ;
s . udprelay . peerindex = this . index ;
s . udprelay . on ( 'message' , onUdpRelayTargetTunnelConnect ) ;
s . udprelay . udpport = this . udpport ;
s . udprelay . udpaddr = this . udpaddr ;
s . udprelay . first = true ;
// Add the UDP session to the count and update the server
if ( s . httprequest . userid != null )
{
if ( tunnelUserCount . udp [ s . httprequest . userid ] == null ) { tunnelUserCount . udp [ s . httprequest . userid ] = 1 ; } else { tunnelUserCount . udp [ s . httprequest . userid ] ++ ; }
try { mesh . SendCommand ( { action : 'sessions' , type : 'udp' , value : tunnelUserCount . tcp } ) ; } catch ( e ) { }
broadcastSessionsToRegisteredApps ( ) ;
2019-05-06 21:44:23 -04:00
}
2021-01-24 18:19:29 -05:00
} else
{
// This is a normal connect for KVM/Terminal/Files
s . data = onTunnelData ;
2019-05-05 01:53:57 -04:00
}
2021-01-24 18:19:29 -05:00
}
// Called when UDP relay data is received // TODO****
function onUdpRelayTargetTunnelConnect ( data )
{
var peerTunnel = tunnels [ this . peerindex ] ;
peerTunnel . s . write ( data ) ;
}
2019-05-05 01:53:57 -04:00
2021-01-24 18:19:29 -05:00
// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
function onUdpRelayServerTunnelData ( data )
{
if ( this . udprelay . first === true )
{
delete this . udprelay . first ; // Skip the first 'c' that is received.
} else
{
this . udprelay . send ( data , parseInt ( this . udprelay . udpport ) , this . udprelay . udpaddr ? this . udprelay . udpaddr : '127.0.0.1' ) ;
2017-10-23 17:09:58 -04:00
}
2021-01-24 18:19:29 -05:00
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Called when the TCP relay target is connected
function onTcpRelayTargetTunnelConnect ( )
{
var peerTunnel = tunnels [ this . peerindex ] ;
this . pipe ( peerTunnel . s ) ; // Pipe Target --> Server
peerTunnel . s . first = true ;
peerTunnel . s . resume ( ) ;
}
// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
function onTcpRelayServerTunnelData ( data )
{
if ( this . first == true )
{
this . first = false ;
this . pipe ( this . tcprelay , { dataTypeSkip : 1 } ) ; // Pipe Server --> Target (don't pipe text type websocket frames)
2017-10-23 17:09:58 -04:00
}
2021-01-24 18:19:29 -05:00
}
2019-05-05 01:53:57 -04:00
2021-01-24 18:19:29 -05:00
function onTunnelClosed ( )
{
var tunnel = tunnels [ this . httprequest . index ] ;
if ( tunnel == null ) return ; // Stop duplicate calls.
2020-07-17 22:55:49 -04:00
2021-01-24 18:19:29 -05:00
// If this is a routing session, clean up and send the new session counts.
if ( this . httprequest . userid != null )
{
if ( this . httprequest . tcpport != null )
{
if ( tunnelUserCount . tcp [ this . httprequest . userid ] != null ) { tunnelUserCount . tcp [ this . httprequest . userid ] -- ; if ( tunnelUserCount . tcp [ this . httprequest . userid ] <= 0 ) { delete tunnelUserCount . tcp [ this . httprequest . userid ] ; } }
try { mesh . SendCommand ( { action : 'sessions' , type : 'tcp' , value : tunnelUserCount . tcp } ) ; } catch ( e ) { }
broadcastSessionsToRegisteredApps ( ) ;
} else if ( this . httprequest . udpport != null )
{
if ( tunnelUserCount . udp [ this . httprequest . userid ] != null ) { tunnelUserCount . udp [ this . httprequest . userid ] -- ; if ( tunnelUserCount . udp [ this . httprequest . userid ] <= 0 ) { delete tunnelUserCount . udp [ this . httprequest . userid ] ; } }
try { mesh . SendCommand ( { action : 'sessions' , type : 'udp' , value : tunnelUserCount . udp } ) ; } catch ( e ) { }
broadcastSessionsToRegisteredApps ( ) ;
2020-07-24 17:00:49 -04:00
}
2021-01-24 18:19:29 -05:00
}
2020-07-24 17:00:49 -04:00
2021-01-24 18:19:29 -05:00
// Sent tunnel statistics to the server, only send this if compression was used.
if ( ( this . bytesSent _uncompressed ) && ( this . bytesSent _uncompressed . toString ( ) != this . bytesSent _actual . toString ( ) ) )
{
mesh . SendCommand ( {
action : 'tunnelCloseStats' ,
url : tunnel . url ,
userid : tunnel . userid ,
protocol : tunnel . protocol ,
sessionid : tunnel . sessionid ,
sent : this . bytesSent _uncompressed . toString ( ) ,
sentActual : this . bytesSent _actual . toString ( ) ,
sentRatio : this . bytesSent _ratio ,
received : this . bytesReceived _uncompressed . toString ( ) ,
receivedActual : this . bytesReceived _actual . toString ( ) ,
receivedRatio : this . bytesReceived _ratio
} ) ;
}
2020-07-17 02:08:59 -04:00
2021-01-24 18:19:29 -05:00
//sendConsoleText("Tunnel #" + this.httprequest.index + " closed. Sent -> " + this.bytesSent_uncompressed + ' bytes (uncompressed), ' + this.bytesSent_actual + ' bytes (actual), ' + this.bytesSent_ratio + '% compression', this.httprequest.sessionid);
if ( this . httprequest . index ) { delete tunnels [ this . httprequest . index ] ; }
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
/ *
// Close the watcher if required
if ( this . httprequest . watcher != undefined ) {
//console.log('Closing watcher: ' + this.httprequest.watcher.path);
//this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
delete this . httprequest . watcher ;
}
* /
// If there is a upload or download active on this connection, close the file
if ( this . httprequest . uploadFile ) { fs . closeSync ( this . httprequest . uploadFile ) ; delete this . httprequest . uploadFile ; delete this . httprequest . uploadFileid ; delete this . httprequest . uploadFilePath ; }
if ( this . httprequest . downloadFile ) { delete this . httprequest . downloadFile ; }
// Clean up WebRTC
if ( this . webrtc != null )
{
if ( this . webrtc . rtcchannel ) { try { this . webrtc . rtcchannel . close ( ) ; } catch ( e ) { } this . webrtc . rtcchannel . removeAllListeners ( 'data' ) ; this . webrtc . rtcchannel . removeAllListeners ( 'end' ) ; delete this . webrtc . rtcchannel ; }
if ( this . webrtc . websocket ) { delete this . webrtc . websocket ; }
try { this . webrtc . close ( ) ; } catch ( e ) { }
this . webrtc . removeAllListeners ( 'connected' ) ;
this . webrtc . removeAllListeners ( 'disconnected' ) ;
this . webrtc . removeAllListeners ( 'dataChannel' ) ;
delete this . webrtc ;
}
2017-08-28 12:27:45 -04:00
2021-01-24 18:19:29 -05:00
// Clean up WebSocket
this . removeAllListeners ( 'data' ) ;
}
function onTunnelSendOk ( ) { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
function onTunnelData ( data )
{
//console.log("OnTunnelData");
//sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
// If this is upload data, save it to file
if ( ( this . httprequest . uploadFile ) && ( typeof data == 'object' ) && ( data [ 0 ] != 123 ) )
{
// Save the data to file being uploaded.
if ( data [ 0 ] == 0 )
{
// If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
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.
} else
{
// If data does not start with zero, save as-is.
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.
2018-01-18 18:43:43 -05:00
}
2021-01-24 18:19:29 -05:00
this . write ( Buffer . from ( JSON . stringify ( { action : 'uploadack' , reqid : this . httprequest . uploadFileid } ) ) ) ; // Ask for more data.
return ;
}
2018-01-18 18:43:43 -05:00
2021-01-24 18:19:29 -05:00
if ( this . httprequest . state == 0 )
{
// Check if this is a relay connection
if ( ( data == 'c' ) || ( data == 'cr' ) ) { this . httprequest . state = 1 ; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
else
{
// Handle tunnel data
if ( this . httprequest . protocol == 0 )
{ // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
// Take a look at the protocol
if ( ( data . length > 3 ) && ( data [ 0 ] == '{' ) ) { onTunnelControlData ( data , this ) ; return ; }
this . httprequest . protocol = parseInt ( data ) ;
if ( typeof this . httprequest . protocol != 'number' ) { this . httprequest . protocol = 0 ; }
if ( this . httprequest . protocol == 10 )
{
//
// Basic file transfer
//
var stats = null ;
if ( ( process . platform != 'win32' ) && ( this . httprequest . xoptions . file . startsWith ( '/' ) == false ) ) { this . httprequest . xoptions . file = '/' + this . httprequest . xoptions . file ; }
try { stats = require ( 'fs' ) . statSync ( this . httprequest . xoptions . file ) } catch ( e ) { }
try { if ( stats ) { this . httprequest . downloadFile = fs . createReadStream ( this . httprequest . xoptions . file , { flags : 'rbN' } ) ; } } catch ( e ) { }
if ( this . httprequest . downloadFile )
{
//sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
this . write ( JSON . stringify ( { op : 'ok' , size : stats . size } ) ) ;
this . httprequest . downloadFile . pipe ( this ) ;
this . httprequest . downloadFile . end = function ( ) { }
} else
{
//sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
this . write ( JSON . stringify ( { op : 'cancel' } ) ) ;
}
2020-04-20 23:12:35 -04:00
}
2021-01-24 18:19:29 -05:00
else if ( ( this . httprequest . protocol == 1 ) || ( this . httprequest . protocol == 6 ) || ( this . httprequest . protocol == 8 ) || ( this . httprequest . protocol == 9 ) )
{
//
// Remote Terminal
//
2018-02-11 20:13:26 -05:00
2021-01-24 18:19:29 -05:00
// Check user access rights for terminal
if ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) == 0 ) || ( ( this . httprequest . rights != 0xFFFFFFFF ) && ( ( this . httprequest . rights & MESHRIGHT _NOTERMINAL ) != 0 ) ) )
{
// Disengage this tunnel, user does not have the rights to do this!!
this . httprequest . protocol = 999999 ;
this . httprequest . s . end ( ) ;
sendConsoleText ( "Error: No Terminal Control Rights." ) ;
return ;
2020-08-26 21:42:41 -04:00
}
2018-11-27 20:13:01 -05:00
2021-01-24 18:19:29 -05:00
this . descriptorMetadata = "Remote Terminal" ;
2020-05-21 16:22:58 -04:00
2021-01-24 18:19:29 -05:00
if ( process . platform == 'win32' )
{
if ( ! require ( 'win-terminal' ) . PowerShellCapable ( ) && ( this . httprequest . protocol == 6 || this . httprequest . protocol == 9 ) )
{
this . httprequest . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : 'PowerShell is not supported on this version of windows' , msgid : 1 } ) ) ;
this . httprequest . s . end ( ) ;
return ;
2020-05-17 00:10:11 -04:00
}
2021-01-24 18:19:29 -05:00
}
2020-05-17 00:10:11 -04:00
2021-01-24 18:19:29 -05:00
var prom = require ( 'promise' ) ;
this . httprequest . tpromise = new prom ( function ( res , rej ) { this . _res = res ; this . _rej = rej ; } ) ;
this . httprequest . tpromise . that = this ;
this . httprequest . tpromise . httprequest = this . httprequest ;
2020-05-19 04:21:07 -04:00
2021-01-24 18:19:29 -05:00
this . end = function ( )
{
if ( this . httprequest . tpromise . _consent ) { this . httprequest . tpromise . _consent . close ( ) ; }
if ( this . httprequest . connectionPromise ) { this . httprequest . connectionPromise . _rej ( 'Closed' ) ; }
2020-05-12 03:01:42 -04:00
2021-01-24 18:19:29 -05:00
// Remove the terminal session to the count to update the server
if ( this . httprequest . userid != null )
{
if ( tunnelUserCount . terminal [ this . httprequest . userid ] != null ) { tunnelUserCount . terminal [ this . httprequest . userid ] -- ; if ( tunnelUserCount . terminal [ this . httprequest . userid ] <= 0 ) { delete tunnelUserCount . terminal [ this . httprequest . userid ] ; } }
try { mesh . SendCommand ( { action : 'sessions' , type : 'terminal' , value : tunnelUserCount . terminal } ) ; } catch ( e ) { }
broadcastSessionsToRegisteredApps ( ) ;
}
2019-06-14 19:33:53 -04:00
2021-01-24 18:19:29 -05:00
if ( process . platform == 'win32' )
{
// Unpipe the web socket
this . unpipe ( this . httprequest . _term ) ;
if ( this . httprequest . _term ) { this . httprequest . _term . unpipe ( this ) ; }
2019-05-20 19:00:33 -04:00
2021-01-24 18:19:29 -05:00
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
if ( this . rtcchannel )
{
this . rtcchannel . unpipe ( this . httprequest . _term ) ;
if ( this . httprequest . _term ) { this . httprequest . _term . unpipe ( this . rtcchannel ) ; }
2019-06-14 19:33:53 -04:00
}
2019-04-12 17:19:03 -04:00
2021-01-24 18:19:29 -05:00
// Clean up
if ( this . httprequest . _term ) { this . httprequest . _term . end ( ) ; }
this . httprequest . _term = null ;
2020-05-17 00:10:11 -04:00
}
2021-01-24 18:19:29 -05:00
} ;
// Perform User-Consent if needed.
if ( this . httprequest . consent && ( this . httprequest . consent & 16 ) )
{
this . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : "Waiting for user to grant access..." , msgid : 1 } ) ) ;
var consentMessage = this . httprequest . username + " requesting remote terminal access. Grant access?" , consentTitle = 'MeshCentral' ;
if ( this . httprequest . soptions != null )
{
if ( this . httprequest . soptions . consentTitle != null ) { consentTitle = this . httprequest . soptions . consentTitle ; }
if ( this . httprequest . soptions . consentMsgTerminal != null ) { consentMessage = this . httprequest . soptions . consentMsgTerminal . replace ( '{0}' , this . httprequest . realname ) . replace ( '{1}' , this . httprequest . username ) ; }
2020-05-17 00:10:11 -04:00
}
2021-01-24 18:19:29 -05:00
this . httprequest . tpromise . _consent = require ( 'message-box' ) . create ( consentTitle , consentMessage , 30 ) ;
this . httprequest . tpromise . _consent . retPromise = this . httprequest . tpromise ;
this . httprequest . tpromise . _consent . then (
function ( )
{
// Success
MeshServerLogEx ( 27 , null , "Local user accepted remote terminal request (" + this . retPromise . httprequest . remoteaddr + ")" , this . retPromise . that . httprequest ) ;
this . retPromise . that . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : null , msgid : 0 } ) ) ;
this . retPromise . _consent = null ;
this . retPromise . _res ( ) ;
} ,
function ( e )
{
// Denied
MeshServerLogEx ( 28 , null , "Local user rejected remote terminal request (" + this . retPromise . that . httprequest . remoteaddr + ")" , this . retPromise . that . httprequest ) ;
this . retPromise . that . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : e . toString ( ) , msgid : 2 } ) ) ;
this . retPromise . _rej ( e . toString ( ) ) ;
} ) ;
}
else
{
// User-Consent is not required, so just resolve this promise
this . httprequest . tpromise . _res ( ) ;
}
2020-01-28 18:08:51 -05:00
2021-01-24 18:19:29 -05:00
this . httprequest . tpromise . then (
function ( )
{
this . httprequest . connectionPromise = new prom ( function ( res , rej ) { this . _res = res ; this . _rej = rej ; } ) ;
this . httprequest . connectionPromise . ws = this . that ;
2020-05-17 00:10:11 -04:00
2021-01-24 18:19:29 -05:00
// Start Terminal
if ( process . platform == 'win32' )
{
try
{
var cols = 80 , rows = 25 ;
if ( this . httprequest . xoptions )
{
if ( this . httprequest . xoptions . rows ) { rows = this . httprequest . xoptions . rows ; }
if ( this . httprequest . xoptions . cols ) { cols = this . httprequest . xoptions . cols ; }
}
2020-05-17 00:10:11 -04:00
2021-01-24 18:19:29 -05:00
if ( ( this . httprequest . protocol == 1 ) || ( this . httprequest . protocol == 6 ) )
{
// Admin Terminal
if ( require ( 'win-virtual-terminal' ) . supported )
{
// ConPTY PseudoTerminal
// this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
// The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
this . httprequest . _dispatcher = require ( 'win-dispatcher' ) . dispatch ( { modules : [ { name : 'win-virtual-terminal' , script : getJSModule ( 'win-virtual-terminal' ) } ] , launch : { module : 'win-virtual-terminal' , method : ( this . httprequest . protocol == 6 ? 'StartPowerShell' : 'Start' ) , args : [ cols , rows ] } } ) ;
this . httprequest . _dispatcher . httprequest = this . httprequest ;
this . httprequest . _dispatcher . on ( 'connection' , function ( c )
{
if ( this . httprequest . connectionPromise . completed )
{
c . end ( ) ;
}
else
{
this . httprequest . connectionPromise . _res ( c ) ;
}
} ) ;
2020-05-17 00:10:11 -04:00
}
2021-01-24 18:19:29 -05:00
else
{
// Legacy Terminal
this . httprequest . connectionPromise . _res ( require ( 'win-terminal' ) [ this . httprequest . protocol == 6 ? 'StartPowerShell' : 'Start' ] ( cols , rows ) ) ;
}
}
else
{
// Logged in user
var userPromise = require ( 'user-sessions' ) . enumerateUsers ( ) ;
userPromise . that = this ;
userPromise . then ( function ( u )
{
var that = this . that ;
if ( u . Active . length > 0 )
{
var username = u . Active [ 0 ] . Username ;
if ( require ( 'win-virtual-terminal' ) . supported )
{
// ConPTY PseudoTerminal
that . httprequest . _dispatcher = require ( 'win-dispatcher' ) . dispatch ( { user : username , modules : [ { name : 'win-virtual-terminal' , script : getJSModule ( 'win-virtual-terminal' ) } ] , launch : { module : 'win-virtual-terminal' , method : ( that . httprequest . protocol == 9 ? 'StartPowerShell' : 'Start' ) , args : [ cols , rows ] } } ) ;
}
else
{
// Legacy Terminal
that . httprequest . _dispatcher = require ( 'win-dispatcher' ) . dispatch ( { user : username , modules : [ { name : 'win-terminal' , script : getJSModule ( 'win-terminal' ) } ] , launch : { module : 'win-terminal' , method : ( that . httprequest . protocol == 9 ? 'StartPowerShell' : 'Start' ) , args : [ cols , rows ] } } ) ;
}
that . httprequest . _dispatcher . ws = that ;
that . httprequest . _dispatcher . on ( 'connection' , function ( c )
{
if ( this . ws . httprequest . connectionPromise . completed )
{
2020-05-19 04:21:07 -04:00
c . end ( ) ;
}
2021-01-24 18:19:29 -05:00
else
{
this . ws . httprequest . connectionPromise . _res ( c ) ;
2020-05-19 04:21:07 -04:00
}
2020-05-17 00:10:11 -04:00
} ) ;
2020-01-28 18:08:51 -05:00
}
2021-01-24 18:19:29 -05:00
} ) ;
2020-05-17 00:10:11 -04:00
}
2020-01-28 18:08:51 -05:00
}
2021-01-24 18:19:29 -05:00
catch ( e )
{
this . httprequest . connectionPromise . _rej ( 'Failed to start remote terminal session, ' + e . toString ( ) ) ;
}
}
else
{
try
{
var bash = fs . existsSync ( '/bin/bash' ) ? '/bin/bash' : false ;
var sh = fs . existsSync ( '/bin/sh' ) ? '/bin/sh' : false ;
var login = process . platform == 'linux' ? '/bin/login' : '/usr/bin/login' ;
var env = { HISTCONTROL : 'ignoreboth' } ;
if ( this . httprequest . xoptions )
{
if ( this . httprequest . xoptions . rows ) { env . LINES = ( '' + this . httprequest . xoptions . rows ) ; }
if ( this . httprequest . xoptions . cols ) { env . COLUMNS = ( '' + this . httprequest . xoptions . cols ) ; }
}
var options = { type : childProcess . SpawnTypes . TERM , uid : ( this . httprequest . protocol == 8 ) ? require ( 'user-sessions' ) . consoleUid ( ) : null , env : env } ;
if ( this . httprequest . xoptions && this . httprequest . xoptions . requireLogin )
{
if ( ! require ( 'fs' ) . existsSync ( login ) ) { throw ( 'Unable to spawn login process' ) ; }
this . httprequest . connectionPromise . _res ( childProcess . execFile ( login , [ 'login' ] , options ) ) ; // Start login shell
}
else if ( bash )
{
var p = childProcess . execFile ( bash , [ 'bash' ] , options ) ; // Start bash
// Spaces at the beginning of lines are needed to hide commands from the command history
if ( process . platform == 'linux' ) { p . stdin . write ( ' alias ls=\'ls --color=auto\';clear\n' ) ; }
this . httprequest . connectionPromise . _res ( p ) ;
}
else if ( sh )
{
var p = childProcess . execFile ( sh , [ 'sh' ] , options ) ; // Start sh
// Spaces at the beginning of lines are needed to hide commands from the command history
if ( process . platform == 'linux' ) { p . stdin . write ( ' alias ls=\'ls --color=auto\';clear\n' ) ; }
this . httprequest . connectionPromise . _res ( p ) ;
2020-05-17 00:10:11 -04:00
}
2021-01-24 18:19:29 -05:00
else
{
this . httprequest . connectionPromise . _rej ( 'Failed to start remote terminal session, no shell found' ) ;
2020-05-17 00:10:11 -04:00
}
2019-11-25 19:11:53 -05:00
}
2021-01-24 18:19:29 -05:00
catch ( e )
{
this . httprequest . connectionPromise . _rej ( 'Failed to start remote terminal session, ' + e . toString ( ) ) ;
}
}
2020-01-28 18:08:51 -05:00
2021-01-24 18:19:29 -05:00
this . httprequest . connectionPromise . then (
function ( term )
{
// SUCCESS
var stdoutstream ;
var stdinstream ;
if ( process . platform == 'win32' )
{
this . ws . httprequest . _term = term ;
this . ws . httprequest . _term . tunnel = this . ws ;
stdoutstream = stdinstream = term ;
}
else
{
term . descriptorMetadata = 'Remote Terminal' ;
this . ws . httprequest . process = term ;
this . ws . httprequest . process . tunnel = this . ws ;
term . stderr . stdout = term . stdout ;
term . stderr . on ( 'data' , function ( c ) { this . stdout . write ( c ) ; } ) ;
stdoutstream = term . stdout ;
stdinstream = term . stdin ;
this . ws . prependListener ( 'end' , function ( ) { this . httprequest . process . kill ( ) ; } ) ;
term . prependListener ( 'exit' , function ( ) { this . tunnel . end ( ) ; } ) ;
}
2018-11-27 20:13:01 -05:00
2021-01-24 18:19:29 -05:00
this . ws . removeAllListeners ( 'data' ) ;
this . ws . on ( 'data' , onTunnelControlData ) ;
2020-05-12 03:01:42 -04:00
2021-01-24 18:19:29 -05:00
stdoutstream . pipe ( this . ws , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
this . ws . pipe ( stdinstream , { dataTypeSkip : 1 , end : false } ) ; // 0 = Binary, 1 = Text.
2019-06-14 19:33:53 -04:00
2021-01-24 18:19:29 -05:00
// Add the terminal session to the count to update the server
if ( this . ws . httprequest . userid != null )
{
if ( tunnelUserCount . terminal [ this . ws . httprequest . userid ] == null ) { tunnelUserCount . terminal [ this . ws . httprequest . userid ] = 1 ; } else { tunnelUserCount . terminal [ this . ws . httprequest . userid ] ++ ; }
try { mesh . SendCommand ( { action : 'sessions' , type : 'terminal' , value : tunnelUserCount . terminal } ) ; } catch ( e ) { }
broadcastSessionsToRegisteredApps ( ) ;
}
2020-05-17 00:10:11 -04:00
2021-01-24 18:19:29 -05:00
// Toast Notification, if required
if ( this . ws . httprequest . consent && ( this . ws . httprequest . consent & 2 ) )
{
// User Notifications is required
var notifyMessage = this . ws . httprequest . username + " started a remote terminal session." , notifyTitle = "MeshCentral" ;
if ( this . ws . httprequest . soptions != null )
{
if ( this . ws . httprequest . soptions . notifyTitle != null ) { notifyTitle = this . ws . httprequest . soptions . notifyTitle ; }
if ( this . ws . httprequest . soptions . notifyMsgTerminal != null ) { notifyMessage = this . ws . httprequest . soptions . notifyMsgTerminal . replace ( '{0}' , this . ws . httprequest . realname ) . replace ( '{1}' , this . ws . httprequest . username ) ; }
2020-04-15 22:05:46 -04:00
}
2021-01-24 18:19:29 -05:00
try { require ( 'toaster' ) . Toast ( notifyTitle , notifyMessage ) ; } catch ( e ) { }
}
} ,
function ( e )
{
// FAILED to connect terminal
this . ws . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : e . toString ( ) , msgid : 2 } ) ) ;
this . ws . end ( ) ;
} ) ;
} ,
function ( e )
{
// DO NOT start terminal
this . that . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : e . toString ( ) , msgid : 2 } ) ) ;
this . that . end ( ) ;
} ) ;
}
else if ( this . httprequest . protocol == 2 )
{
//
// Remote KVM
//
// Check user access rights for desktop
if ( ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) == 0 ) && ( ( this . httprequest . rights & MESHRIGHT _REMOTEVIEW ) == 0 ) ) || ( ( this . httprequest . rights != 0xFFFFFFFF ) && ( ( this . httprequest . rights & MESHRIGHT _NODESKTOP ) != 0 ) ) )
{
// Disengage this tunnel, user does not have the rights to do this!!
this . httprequest . protocol = 999999 ;
this . httprequest . s . end ( ) ;
sendConsoleText ( "Error: No Desktop Control Rights." ) ;
return ;
2020-05-17 00:10:11 -04:00
}
2018-11-27 20:13:01 -05:00
2021-01-24 18:19:29 -05:00
this . descriptorMetadata = "Remote KVM" ;
// Look for a TSID
var tsid = null ;
if ( ( this . httprequest . xoptions != null ) && ( typeof this . httprequest . xoptions . tsid == 'number' ) ) { tsid = this . httprequest . xoptions . tsid ; }
require ( 'MeshAgent' ) . _tsid = tsid ;
2020-05-21 16:22:58 -04:00
2021-01-24 18:19:29 -05:00
// Remote desktop using native pipes
this . httprequest . desktop = { state : 0 , kvm : mesh . getRemoteDesktopStream ( tsid ) , tunnel : this } ;
this . httprequest . desktop . kvm . parent = this . httprequest . desktop ;
this . desktop = this . httprequest . desktop ;
2019-12-12 20:45:42 -05:00
2021-01-24 18:19:29 -05:00
// Add ourself to the list of remote desktop sessions
if ( this . httprequest . desktop . kvm . tunnels == null ) { this . httprequest . desktop . kvm . tunnels = [ ] ; }
this . httprequest . desktop . kvm . tunnels . push ( this ) ;
2018-04-19 21:19:15 -04:00
2021-01-24 18:19:29 -05:00
// Send a metadata update to all desktop sessions
var users = { } ;
if ( this . httprequest . desktop . kvm . tunnels != null )
{
for ( var i in this . httprequest . desktop . kvm . tunnels ) { try { var userid = this . httprequest . desktop . kvm . tunnels [ i ] . httprequest . userid ; if ( users [ userid ] == null ) { users [ userid ] = 1 ; } else { users [ userid ] ++ ; } } catch ( e ) { } }
for ( var i in this . httprequest . desktop . kvm . tunnels ) { try { this . httprequest . desktop . kvm . tunnels [ i ] . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'metadata' , users : users } ) ) ; } catch ( e ) { } }
tunnelUserCount . desktop = users ;
try { mesh . SendCommand ( { action : 'sessions' , type : 'kvm' , value : users } ) ; } catch ( e ) { }
broadcastSessionsToRegisteredApps ( ) ;
}
this . end = function ( )
{
-- this . desktop . kvm . connectionCount ;
// Remove ourself from the list of remote desktop session
var i = this . desktop . kvm . tunnels . indexOf ( this ) ;
if ( i >= 0 ) { this . desktop . kvm . tunnels . splice ( i , 1 ) ; }
2020-05-07 02:23:27 -04:00
// Send a metadata update to all desktop sessions
var users = { } ;
2021-01-24 18:19:29 -05:00
if ( this . httprequest . desktop . kvm . tunnels != null )
{
2020-09-30 19:59:34 -04:00
for ( var i in this . httprequest . desktop . kvm . tunnels ) { try { var userid = this . httprequest . desktop . kvm . tunnels [ i ] . httprequest . userid ; if ( users [ userid ] == null ) { users [ userid ] = 1 ; } else { users [ userid ] ++ ; } } catch ( e ) { } }
for ( var i in this . httprequest . desktop . kvm . tunnels ) { try { this . httprequest . desktop . kvm . tunnels [ i ] . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'metadata' , users : users } ) ) ; } catch ( e ) { } }
2020-10-12 01:24:30 -04:00
tunnelUserCount . desktop = users ;
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'kvm' , value : users } ) ; } catch ( e ) { }
2020-10-12 01:24:30 -04:00
broadcastSessionsToRegisteredApps ( ) ;
2020-05-11 04:17:18 -04:00
}
2020-05-07 02:23:27 -04:00
2021-01-24 18:19:29 -05:00
// Unpipe the web socket
try
{
this . unpipe ( this . httprequest . desktop . kvm ) ;
this . httprequest . desktop . kvm . unpipe ( this ) ;
}
catch ( e ) { }
2020-05-07 02:23:27 -04:00
2021-01-24 18:19:29 -05:00
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
if ( this . rtcchannel )
{
try
{
this . rtcchannel . unpipe ( this . httprequest . desktop . kvm ) ;
this . httprequest . desktop . kvm . unpipe ( this . rtcchannel ) ;
2020-04-28 14:36:04 -04:00
}
2021-01-15 02:57:52 -05:00
catch ( e ) { }
2021-01-24 18:19:29 -05:00
}
2019-01-03 17:46:52 -05:00
2021-01-24 18:19:29 -05:00
// Place wallpaper back if needed
// TODO
2019-11-29 01:45:21 -05:00
2021-01-24 18:19:29 -05:00
if ( this . desktop . kvm . connectionCount == 0 )
{
// Display a toast message. This may not be supported on all platforms.
// try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (e) { }
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
this . httprequest . desktop . kvm . end ( ) ;
if ( this . httprequest . desktop . kvm . connectionBar )
{
this . httprequest . desktop . kvm . connectionBar . removeAllListeners ( 'close' ) ;
this . httprequest . desktop . kvm . connectionBar . close ( ) ;
this . httprequest . desktop . kvm . connectionBar = null ;
}
} else
{
for ( var i in this . httprequest . desktop . kvm . users )
{
if ( ( this . httprequest . desktop . kvm . users [ i ] == this . httprequest . username ) && this . httprequest . desktop . kvm . connectionBar )
{
for ( var j in this . httprequest . desktop . kvm . rusers ) { if ( this . httprequest . desktop . kvm . rusers [ j ] == this . httprequest . realname ) { this . httprequest . desktop . kvm . rusers . splice ( j , 1 ) ; break ; } }
this . httprequest . desktop . kvm . users . splice ( i , 1 ) ;
this . httprequest . desktop . kvm . connectionBar . removeAllListeners ( 'close' ) ;
this . httprequest . desktop . kvm . connectionBar . close ( ) ;
this . httprequest . desktop . kvm . connectionBar = require ( 'notifybar-desktop' ) ( this . httprequest . privacybartext . replace ( '{0}' , this . httprequest . desktop . kvm . users . join ( ', ' ) ) . replace ( '{1}' , this . httprequest . desktop . kvm . rusers . join ( ', ' ) ) , require ( 'MeshAgent' ) . _tsid ) ;
this . httprequest . desktop . kvm . connectionBar . httprequest = this . httprequest ;
this . httprequest . desktop . kvm . connectionBar . on ( 'close' , function ( )
{
MeshServerLogEx ( 29 , null , "Remote Desktop Connection forcefully closed by local user (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
for ( var i in this . httprequest . desktop . kvm . _pipedStreams )
{
this . httprequest . desktop . kvm . _pipedStreams [ i ] . end ( ) ;
}
this . httprequest . desktop . kvm . end ( ) ;
} ) ;
break ;
2019-10-11 13:53:42 -04:00
}
2018-04-19 21:19:15 -04:00
}
2019-10-11 13:53:42 -04:00
}
2021-01-24 18:19:29 -05:00
} ;
if ( this . httprequest . desktop . kvm . hasOwnProperty ( 'connectionCount' ) )
{
this . httprequest . desktop . kvm . connectionCount ++ ;
this . httprequest . desktop . kvm . rusers . push ( this . httprequest . realname ) ;
this . httprequest . desktop . kvm . users . push ( this . httprequest . username ) ;
this . httprequest . desktop . kvm . rusers . sort ( ) ;
this . httprequest . desktop . kvm . users . sort ( ) ;
} else
{
this . httprequest . desktop . kvm . connectionCount = 1 ;
this . httprequest . desktop . kvm . rusers = [ this . httprequest . realname ] ;
this . httprequest . desktop . kvm . users = [ this . httprequest . username ] ;
}
2018-11-27 20:13:01 -05:00
2021-01-24 18:19:29 -05:00
if ( ( this . httprequest . rights == 0xFFFFFFFF ) || ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) != 0 ) && ( ( this . httprequest . rights & MESHRIGHT _REMOTEVIEW ) == 0 ) ) )
{
// If we have remote control rights, pipe the KVM input
this . pipe ( this . httprequest . desktop . kvm , { dataTypeSkip : 1 , end : false } ) ; // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
} else
{
// We need to only pipe non-mouse & non-keyboard inputs.
//sendConsoleText('Warning: No Remote Desktop Input Rights.');
// TODO!!!
}
2018-11-27 20:13:01 -05:00
2021-01-24 18:19:29 -05:00
// Perform notification if needed. Toast messages may not be supported on all platforms.
if ( this . httprequest . consent && ( this . httprequest . consent & 8 ) )
{
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
this . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : "Waiting for user to grant access..." , msgid : 1 } ) ) ;
var consentMessage = this . httprequest . realname + " requesting remote desktop access. Grant access?" , consentTitle = 'MeshCentral' ;
if ( this . httprequest . soptions != null )
{
if ( this . httprequest . soptions . consentTitle != null ) { consentTitle = this . httprequest . soptions . consentTitle ; }
if ( this . httprequest . soptions . consentMsgDesktop != null ) { consentMessage = this . httprequest . soptions . consentMsgDesktop . replace ( '{0}' , this . httprequest . realname ) . replace ( '{1}' , this . httprequest . username ) ; }
}
var pr = require ( 'message-box' ) . create ( consentTitle , consentMessage , 30 , null , tsid ) ;
pr . ws = this ;
this . pause ( ) ;
this . _consentpromise = pr ;
this . prependOnceListener ( 'end' , function ( ) { if ( this . _consentpromise && this . _consentpromise . close ) { this . _consentpromise . close ( ) ; } } ) ;
pr . then (
function ( )
{
// Success
this . ws . _consentpromise = null ;
MeshServerLogEx ( 30 , null , "Starting remote desktop after local user accepted (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
this . ws . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : null , msgid : 0 } ) ) ;
if ( this . ws . httprequest . consent && ( this . ws . httprequest . consent & 1 ) )
{
// User Notifications is required
var notifyMessage = this . ws . httprequest . realname + " started a remote desktop session." , notifyTitle = "MeshCentral" ;
if ( this . ws . httprequest . soptions != null )
{
if ( this . ws . httprequest . soptions . notifyTitle != null ) { notifyTitle = this . ws . httprequest . soptions . notifyTitle ; }
if ( this . ws . httprequest . soptions . notifyMsgDesktop != null ) { notifyMessage = this . ws . httprequest . soptions . notifyMsgDesktop . replace ( '{0}' , this . ws . httprequest . realname ) . replace ( '{1}' , this . ws . httprequest . username ) ; }
2019-06-14 19:33:53 -04:00
}
2021-01-24 18:19:29 -05:00
try { require ( 'toaster' ) . Toast ( notifyTitle , notifyMessage , tsid ) ; } catch ( e ) { }
}
if ( this . ws . httprequest . consent && ( this . ws . httprequest . consent & 0x40 ) )
{
// Connection Bar is required
if ( this . ws . httprequest . desktop . kvm . connectionBar )
{
this . ws . httprequest . desktop . kvm . connectionBar . removeAllListeners ( 'close' ) ;
this . ws . httprequest . desktop . kvm . connectionBar . close ( ) ;
}
try
{
this . ws . httprequest . desktop . kvm . connectionBar = require ( 'notifybar-desktop' ) ( this . ws . httprequest . privacybartext . replace ( '{0}' , this . ws . httprequest . desktop . kvm . users . join ( ', ' ) ) . replace ( '{1}' , this . ws . httprequest . desktop . kvm . rusers . join ( ', ' ) ) , require ( 'MeshAgent' ) . _tsid ) ;
MeshServerLogEx ( 31 , null , "Remote Desktop Connection Bar Activated/Updated (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
}
catch ( e )
{
if ( process . platform != 'darwin' )
{
MeshServerLogEx ( 32 , null , "Remote Desktop Connection Bar Failed or Not Supported (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
2019-10-25 13:33:10 -04:00
}
2021-01-24 18:19:29 -05:00
}
if ( this . ws . httprequest . desktop . kvm . connectionBar )
{
this . ws . httprequest . desktop . kvm . connectionBar . httprequest = this . ws . httprequest ;
this . ws . httprequest . desktop . kvm . connectionBar . on ( 'close' , function ( )
{
MeshServerLogEx ( 33 , null , "Remote Desktop Connection forcefully closed by local user (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
for ( var i in this . httprequest . desktop . kvm . _pipedStreams )
{
this . httprequest . desktop . kvm . _pipedStreams [ i ] . end ( ) ;
2019-10-11 13:53:42 -04:00
}
2021-01-24 18:19:29 -05:00
this . httprequest . desktop . kvm . end ( ) ;
} ) ;
2019-10-11 13:53:42 -04:00
}
2020-06-13 14:36:50 -04:00
}
2021-01-24 18:19:29 -05:00
this . ws . httprequest . desktop . kvm . pipe ( this . ws , { dataTypeSkip : 1 } ) ;
this . ws . resume ( ) ;
} ,
function ( e )
{
// User Consent Denied/Failed
this . ws . _consentpromise = null ;
MeshServerLogEx ( 34 , null , "Failed to start remote desktop after local user rejected (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
this . ws . end ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : e . toString ( ) , msgid : 2 } ) ) ;
} ) ;
} else
{
// User Consent Prompt is not required
if ( this . httprequest . consent && ( this . httprequest . consent & 1 ) )
{
// User Notifications is required
MeshServerLogEx ( 35 , null , "Started remote desktop with toast notification (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
var notifyMessage = this . httprequest . realname + " started a remote desktop session." , notifyTitle = "MeshCentral" ;
if ( this . httprequest . soptions != null )
{
if ( this . httprequest . soptions . notifyTitle != null ) { notifyTitle = this . httprequest . soptions . notifyTitle ; }
if ( this . httprequest . soptions . notifyMsgDesktop != null ) { notifyMessage = this . httprequest . soptions . notifyMsgDesktop . replace ( '{0}' , this . httprequest . realname ) . replace ( '{1}' , this . httprequest . username ) ; }
2019-06-14 19:33:53 -04:00
}
2021-01-24 18:19:29 -05:00
try { require ( 'toaster' ) . Toast ( notifyTitle , notifyMessage , tsid ) ; } catch ( e ) { }
} else
{
MeshServerLogEx ( 36 , null , "Started remote desktop without notification (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
}
if ( this . httprequest . consent && ( this . httprequest . consent & 0x40 ) )
{
// Connection Bar is required
if ( this . httprequest . desktop . kvm . connectionBar )
{
this . httprequest . desktop . kvm . connectionBar . removeAllListeners ( 'close' ) ;
this . httprequest . desktop . kvm . connectionBar . close ( ) ;
}
try
{
this . httprequest . desktop . kvm . connectionBar = require ( 'notifybar-desktop' ) ( this . httprequest . privacybartext . replace ( '{0}' , this . httprequest . desktop . kvm . rusers . join ( ', ' ) ) . replace ( '{1}' , this . httprequest . desktop . kvm . users . join ( ', ' ) ) , require ( 'MeshAgent' ) . _tsid ) ;
MeshServerLogEx ( 37 , null , "Remote Desktop Connection Bar Activated/Updated (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
}
catch ( e )
{
MeshServerLogEx ( 38 , null , "Remote Desktop Connection Bar Failed or not Supported (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
}
if ( this . httprequest . desktop . kvm . connectionBar )
{
this . httprequest . desktop . kvm . connectionBar . httprequest = this . httprequest ;
this . httprequest . desktop . kvm . connectionBar . on ( 'close' , function ( )
{
MeshServerLogEx ( 39 , null , "Remote Desktop Connection forcefully closed by local user (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
for ( var i in this . httprequest . desktop . kvm . _pipedStreams )
{
this . httprequest . desktop . kvm . _pipedStreams [ i ] . end ( ) ;
}
this . httprequest . desktop . kvm . end ( ) ;
} ) ;
2019-10-11 13:53:42 -04:00
}
2019-06-14 19:33:53 -04:00
}
2021-01-24 18:19:29 -05:00
this . httprequest . desktop . kvm . pipe ( this , { dataTypeSkip : 1 } ) ;
}
2019-06-14 19:33:53 -04:00
2021-01-24 18:19:29 -05:00
this . removeAllListeners ( 'data' ) ;
this . on ( 'data' , onTunnelControlData ) ;
//this.write('MeshCore KVM Hello!1');
2019-05-20 19:00:33 -04:00
2021-01-24 18:19:29 -05:00
} else if ( this . httprequest . protocol == 5 )
{
//
// Remote Files
//
2020-05-21 16:22:58 -04:00
2021-01-24 18:19:29 -05:00
// Check user access rights for files
if ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) == 0 ) || ( ( this . httprequest . rights != 0xFFFFFFFF ) && ( ( this . httprequest . rights & MESHRIGHT _NOFILES ) != 0 ) ) )
{
// Disengage this tunnel, user does not have the rights to do this!!
this . httprequest . protocol = 999999 ;
this . httprequest . s . end ( ) ;
sendConsoleText ( "Error: No files control rights." ) ;
return ;
}
2018-11-27 20:13:01 -05:00
2021-01-24 18:19:29 -05:00
this . descriptorMetadata = "Remote Files" ;
2020-05-21 16:22:58 -04:00
2021-01-24 18:19:29 -05:00
// Add the files session to the count to update the server
if ( this . httprequest . userid != null )
{
if ( tunnelUserCount . files [ this . httprequest . userid ] == null ) { tunnelUserCount . files [ this . httprequest . userid ] = 1 ; } else { tunnelUserCount . files [ this . httprequest . userid ] ++ ; }
try { mesh . SendCommand ( { action : 'sessions' , type : 'files' , value : tunnelUserCount . files } ) ; } catch ( e ) { }
broadcastSessionsToRegisteredApps ( ) ;
}
this . end = function ( )
{
// Remove the files session from the count to update the server
if ( this . httprequest . userid != null )
{
if ( tunnelUserCount . files [ this . httprequest . userid ] != null ) { tunnelUserCount . files [ this . httprequest . userid ] -- ; if ( tunnelUserCount . files [ this . httprequest . userid ] <= 0 ) { delete tunnelUserCount . files [ this . httprequest . userid ] ; } }
2020-09-30 19:59:34 -04:00
try { mesh . SendCommand ( { action : 'sessions' , type : 'files' , value : tunnelUserCount . files } ) ; } catch ( e ) { }
2020-10-12 01:24:30 -04:00
broadcastSessionsToRegisteredApps ( ) ;
2020-05-15 18:23:37 -04:00
}
2021-01-24 18:19:29 -05:00
} ;
2020-05-15 18:23:37 -04:00
2021-01-24 18:19:29 -05:00
// Perform notification if needed. Toast messages may not be supported on all platforms.
if ( this . httprequest . consent && ( this . httprequest . consent & 32 ) )
{
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
this . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : "Waiting for user to grant access..." , msgid : 1 } ) ) ;
var consentMessage = this . httprequest . realname + " requesting remote file Access. Grant access?" , consentTitle = 'MeshCentral' ;
if ( this . httprequest . soptions != null )
{
if ( this . httprequest . soptions . consentTitle != null ) { consentTitle = this . httprequest . soptions . consentTitle ; }
if ( this . httprequest . soptions . consentMsgFiles != null ) { consentMessage = this . httprequest . soptions . consentMsgFiles . replace ( '{0}' , this . httprequest . realname ) . replace ( '{1}' , this . httprequest . username ) ; }
}
var pr = require ( 'message-box' ) . create ( consentTitle , consentMessage , 30 ) ;
pr . ws = this ;
this . pause ( ) ;
this . _consentpromise = pr ;
this . prependOnceListener ( 'end' , function ( ) { if ( this . _consentpromise && this . _consentpromise . close ) { this . _consentpromise . close ( ) ; } } ) ;
pr . then (
function ( )
{
// Success
this . ws . _consentpromise = null ;
MeshServerLogEx ( 40 , null , "Starting remote files after local user accepted (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
this . ws . write ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : null } ) ) ;
if ( this . ws . httprequest . consent && ( this . ws . httprequest . consent & 4 ) )
{
// User Notifications is required
var notifyMessage = this . ws . httprequest . realname + " started a remote file session." , notifyTitle = "MeshCentral" ;
if ( this . ws . httprequest . soptions != null )
{
if ( this . ws . httprequest . soptions . notifyTitle != null ) { notifyTitle = this . ws . httprequest . soptions . notifyTitle ; }
if ( this . ws . httprequest . soptions . notifyMsgFiles != null ) { notifyMessage = this . ws . httprequest . soptions . notifyMsgFiles . replace ( '{0}' , this . ws . httprequest . realname ) . replace ( '{1}' , this . ws . httprequest . username ) ; }
2019-06-14 19:33:53 -04:00
}
2021-01-24 18:19:29 -05:00
try { require ( 'toaster' ) . Toast ( notifyTitle , notifyMessage ) ; } catch ( e ) { }
2020-06-13 14:36:50 -04:00
}
2021-01-24 18:19:29 -05:00
this . ws . resume ( ) ;
} ,
function ( e )
{
// User Consent Denied/Failed
this . ws . _consentpromise = null ;
MeshServerLogEx ( 41 , null , "Failed to start remote files after local user rejected (" + this . ws . httprequest . remoteaddr + ")" , this . ws . httprequest ) ;
this . ws . end ( JSON . stringify ( { ctrlChannel : '102938' , type : 'console' , msg : e . toString ( ) , msgid : 2 } ) ) ;
} ) ;
} else
{
// User Consent Prompt is not required
if ( this . httprequest . consent && ( this . httprequest . consent & 4 ) )
{
// User Notifications is required
MeshServerLogEx ( 42 , null , "Started remote files with toast notification (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
var notifyMessage = this . httprequest . realname + " started a remote file session." , notifyTitle = "MeshCentral" ;
if ( this . httprequest . soptions != null )
{
if ( this . httprequest . soptions . notifyTitle != null ) { notifyTitle = this . httprequest . soptions . notifyTitle ; }
if ( this . httprequest . soptions . notifyMsgFiles != null ) { notifyMessage = this . httprequest . soptions . notifyMsgFiles . replace ( '{0}' , this . httprequest . realname ) . replace ( '{1}' , this . httprequest . username ) ; }
2019-06-14 19:33:53 -04:00
}
2021-01-24 18:19:29 -05:00
try { require ( 'toaster' ) . Toast ( notifyTitle , notifyMessage ) ; } catch ( e ) { }
} else
{
MeshServerLogEx ( 43 , null , "Started remote files without notification (" + this . httprequest . remoteaddr + ")" , this . httprequest ) ;
2019-04-12 17:19:03 -04:00
}
2021-01-24 18:19:29 -05:00
this . resume ( ) ;
}
2019-04-12 17:19:03 -04:00
2021-01-24 18:19:29 -05:00
// Setup files
// NOP
}
} else if ( this . httprequest . protocol == 1 )
{
// Send data into terminal stdin
//this.write(data); // Echo back the keys (Does not seem to be a good idea)
} else if ( this . httprequest . protocol == 2 )
{
// Send data into remote desktop
if ( this . httprequest . desktop . state == 0 )
{
this . write ( Buffer . from ( String . fromCharCode ( 0x11 , 0xFE , 0x00 , 0x00 , 0x4D , 0x45 , 0x53 , 0x48 , 0x00 , 0x00 , 0x00 , 0x00 , 0x02 ) ) ) ;
this . httprequest . desktop . state = 1 ;
} else
{
this . httprequest . desktop . write ( data ) ;
}
} else if ( this . httprequest . protocol == 5 )
{
// Process files commands
var cmd = null ;
try { cmd = JSON . parse ( data ) ; } catch ( e ) { } ;
if ( cmd == null ) { return ; }
if ( ( cmd . ctrlChannel == '102938' ) || ( ( cmd . type == 'offer' ) && ( cmd . sdp != null ) ) ) { onTunnelControlData ( cmd , this ) ; return ; } // If this is control data, handle it now.
if ( cmd . action == undefined ) { return ; }
//sendConsoleText('CMD: ' + JSON.stringify(cmd));
if ( ( cmd . path != null ) && ( process . platform != 'win32' ) && ( cmd . path [ 0 ] != '/' ) ) { cmd . path = '/' + cmd . path ; } // Add '/' to paths on non-windows
//console.log(objToString(cmd, 0, ' '));
switch ( cmd . action )
{
case 'ls' : {
/ *
// Close the watcher if required
var samepath = ( ( this . httprequest . watcher != undefined ) && ( cmd . path == this . httprequest . watcher . path ) ) ;
if ( ( this . httprequest . watcher != undefined ) && ( samepath == false ) ) {
//console.log('Closing watcher: ' + this.httprequest.watcher.path);
//this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
delete this . httprequest . watcher ;
}
* /
// Send the folder content to the browser
var response = getDirectoryInfo ( cmd . path ) ;
if ( cmd . reqid != undefined ) { response . reqid = cmd . reqid ; }
this . write ( Buffer . from ( JSON . stringify ( response ) ) ) ;
/ *
// Start the directory watcher
if ( ( cmd . path != '' ) && ( samepath == false ) ) {
var watcher = fs . watch ( cmd . path , onFileWatcher ) ;
watcher . tunnel = this . httprequest ;
watcher . path = cmd . path ;
this . httprequest . watcher = watcher ;
//console.log('Starting watcher: ' + this.httprequest.watcher.path);
}
* /
break ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
case 'mkdir' : {
// Create a new empty folder
fs . mkdirSync ( cmd . path ) ;
MeshServerLogEx ( 44 , [ cmd . path ] , "Create folder: \"" + cmd . path + "\"" , this . httprequest ) ;
break ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
case 'rm' : {
// Delete, possibly recursive delete
for ( var i in cmd . delfiles )
{
var p = obj . path . join ( cmd . path , cmd . delfiles [ i ] ) , delcount = 0 ;
try { delcount = deleteFolderRecursive ( p , cmd . rec ) ; } catch ( e ) { }
if ( ( delcount == 1 ) && ! cmd . rec )
{
MeshServerLogEx ( 45 , [ p ] , "Delete: \"" + p + "\"" , this . httprequest ) ;
} else
{
if ( cmd . rec )
{
MeshServerLogEx ( 46 , [ p , delcount ] , "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed" , this . httprequest ) ;
} else
{
MeshServerLogEx ( 47 , [ p , delcount ] , "Delete: \"" + p + "\", " + delcount + " element(s) removed" , this . httprequest ) ;
}
2017-08-28 12:27:45 -04:00
}
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'markcoredump' : {
// If we are asking for the coredump file, set the right path.
var coreDumpPath = null ;
if ( process . platform == 'win32' )
{
if ( fs . existsSync ( process . coreDumpLocation ) ) { coreDumpPath = process . coreDumpLocation ; }
} else
{
if ( ( process . cwd ( ) != '//' ) && fs . existsSync ( process . cwd ( ) + 'core' ) ) { coreDumpPath = process . cwd ( ) + 'core' ; }
}
if ( coreDumpPath != null ) { db . Put ( 'CoreDumpTime' , require ( 'fs' ) . statSync ( coreDumpPath ) . mtime ) ; }
break ;
}
case 'rename' :
{
// Rename a file or folder
var oldfullpath = obj . path . join ( cmd . path , cmd . oldname ) ;
var newfullpath = obj . path . join ( cmd . path , cmd . newname ) ;
MeshServerLogEx ( 48 , [ oldfullpath , cmd . newname ] , 'Rename: \"' + oldfullpath + '\" to \"' + cmd . newname + '\"' , this . httprequest ) ;
try { fs . renameSync ( oldfullpath , newfullpath ) ; } catch ( e ) { console . log ( e ) ; }
2017-08-28 12:27:45 -04:00
break ;
}
2021-01-24 18:19:29 -05:00
case 'findfile' :
{
// Search for files
var r = require ( 'file-search' ) . find ( '"' + cmd . path + '"' , cmd . filter ) ;
if ( ! r . cancel ) { r . cancel = function cancel ( ) { this . child . kill ( ) ; } ; }
this . _search = r ;
r . socket = this ;
r . socket . reqid = cmd . reqid ; // Search request id. This is used to send responses and cancel the request.
r . socket . path = cmd . path ; // Search path
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 ) { } } ) ;
r . then ( function ( ) { try { this . socket . write ( Buffer . from ( JSON . stringify ( { action : 'findfile' , r : null , reqid : this . socket . reqid } ) ) ) ; } catch ( ex ) { } } ) ;
2017-08-28 12:27:45 -04:00
break ;
}
2021-01-24 18:19:29 -05:00
case 'cancelfindfile' :
{
if ( this . _search ) { this . _search . cancel ( ) ; this . _search = null ; }
2020-07-08 02:56:08 -04:00
break ;
}
2021-01-24 18:19:29 -05:00
case 'download' :
{
// Download a file
var sendNextBlock = 0 ;
if ( cmd . sub == 'start' )
{ // Setup the download
if ( ( cmd . path == null ) && ( cmd . ask == 'coredump' ) )
{ // If we are asking for the coredump file, set the right path.
if ( process . platform == 'win32' )
{
if ( fs . existsSync ( process . coreDumpLocation ) ) { cmd . path = process . coreDumpLocation ; }
} else
{
if ( ( process . cwd ( ) != '//' ) && fs . existsSync ( process . cwd ( ) + 'core' ) ) { cmd . path = process . cwd ( ) + 'core' ; }
2021-01-17 21:36:53 -05:00
}
}
2021-01-24 18:19:29 -05:00
MeshServerLogEx ( ( cmd . ask == 'coredump' ) ? 104 : 49 , [ cmd . path ] , 'Download: \"' + cmd . path + '\"' , this . httprequest ) ;
if ( ( cmd . path == null ) || ( this . filedownload != null ) ) { this . write ( { action : 'download' , sub : 'cancel' , id : this . filedownload . id } ) ; delete this . filedownload ; }
this . filedownload = { id : cmd . id , path : cmd . path , ptr : 0 }
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 ; }
if ( this . filedownload ) { this . write ( { action : 'download' , sub : 'start' , id : cmd . id } ) ; }
} else if ( ( this . filedownload != null ) && ( cmd . id == this . filedownload . id ) )
{ // Download commands
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 ; }
2021-01-17 21:36:53 -05:00
}
2021-01-24 18:19:29 -05:00
// Send the next download block(s)
while ( sendNextBlock > 0 )
2021-01-17 21:36:53 -05:00
{
2021-01-24 18:19:29 -05:00
sendNextBlock -- ;
var buf = Buffer . alloc ( 16384 ) ;
var len = fs . readSync ( this . filedownload . f , buf , 4 , 16380 , null ) ;
this . filedownload . ptr += len ;
if ( len < 16380 ) { buf . writeInt32BE ( 0x01000001 , 0 ) ; fs . closeSync ( this . filedownload . f ) ; delete this . filedownload ; sendNextBlock = 0 ; } else { buf . writeInt32BE ( 0x01000000 , 0 ) ; }
this . write ( buf . slice ( 0 , len + 4 ) ) ; // Write as binary
2021-01-15 02:57:52 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'upload' :
{
// Upload a file, browser to agent
if ( this . httprequest . uploadFile != null ) { fs . closeSync ( this . httprequest . uploadFile ) ; delete this . httprequest . uploadFile ; }
if ( cmd . path == undefined ) break ;
var filepath = cmd . name ? obj . path . join ( cmd . path , cmd . name ) : cmd . path ;
this . httprequest . uploadFilePath = filepath ;
MeshServerLogEx ( 50 , [ filepath ] , 'Upload: \"' + filepath + '\"' , this . httprequest ) ;
try { this . httprequest . uploadFile = fs . openSync ( filepath , 'wbN' ) ; } catch ( e ) { this . write ( Buffer . from ( JSON . stringify ( { action : 'uploaderror' , reqid : cmd . reqid } ) ) ) ; break ; }
this . httprequest . uploadFileid = cmd . reqid ;
if ( this . httprequest . uploadFile ) { this . write ( Buffer . from ( JSON . stringify ( { action : 'uploadstart' , reqid : this . httprequest . uploadFileid } ) ) ) ; }
break ;
}
case 'uploaddone' :
{
// Indicates that an upload is done
if ( this . httprequest . uploadFile )
2021-01-15 02:57:52 -05:00
{
2021-01-24 18:19:29 -05:00
fs . closeSync ( this . httprequest . uploadFile ) ;
this . write ( Buffer . from ( JSON . stringify ( { action : 'uploaddone' , reqid : this . httprequest . uploadFileid } ) ) ) ; // Indicate that we closed the file.
delete this . httprequest . uploadFile ;
delete this . httprequest . uploadFileid ;
delete this . httprequest . uploadFilePath ;
2020-08-13 15:29:18 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'uploadcancel' :
{
// Indicates that an upload is canceled
if ( this . httprequest . uploadFile )
2021-01-15 02:57:52 -05:00
{
2021-01-24 18:19:29 -05:00
fs . closeSync ( this . httprequest . uploadFile ) ;
fs . unlinkSync ( this . httprequest . uploadFilePath ) ;
this . write ( Buffer . from ( JSON . stringify ( { action : 'uploadcancel' , reqid : this . httprequest . uploadFileid } ) ) ) ; // Indicate that we closed the file.
delete this . httprequest . uploadFile ;
delete this . httprequest . uploadFileid ;
delete this . httprequest . uploadFilePath ;
2020-08-13 15:29:18 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'copy' :
{
// Copy a bunch of files from scpath to dspath
for ( var i in cmd . names )
2021-01-15 02:57:52 -05:00
{
2021-01-24 18:19:29 -05:00
var sc = obj . path . join ( cmd . scpath , cmd . names [ i ] ) , ds = obj . path . join ( cmd . dspath , cmd . names [ i ] ) ;
MeshServerLogEx ( 51 , [ sc , ds ] , 'Copy: \"' + sc + '\" to \"' + ds + '\"' , this . httprequest ) ;
if ( sc != ds ) { try { fs . copyFileSync ( sc , ds ) ; } catch ( e ) { } }
2018-04-02 18:34:32 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'move' :
{
// Move a bunch of files from scpath to dspath
for ( var i in cmd . names )
2021-01-15 02:57:52 -05:00
{
2021-01-24 18:19:29 -05:00
var sc = obj . path . join ( cmd . scpath , cmd . names [ i ] ) , ds = obj . path . join ( cmd . dspath , cmd . names [ i ] ) ;
MeshServerLogEx ( 52 , [ sc , ds ] , 'Move: \"' + sc + '\" to \"' + ds + '\"' , this . httprequest ) ;
if ( sc != ds ) { try { fs . copyFileSync ( sc , ds ) ; fs . unlinkSync ( sc ) ; } catch ( e ) { } }
2018-04-02 18:34:32 -04:00
}
2019-10-10 14:13:25 -04:00
break ;
}
2021-01-24 18:19:29 -05:00
case 'zip' :
// Zip a bunch of files
if ( this . zip != null ) return ; // Zip operating is currently running, exit now.
// Check that the specified files exist & build full paths
var fp , stat , p = [ ] ;
for ( var i in cmd . files ) { fp = cmd . path + '/' + cmd . files [ i ] ; stat = null ; try { stat = fs . statSync ( fp ) ; } catch ( e ) { } if ( stat != null ) { p . push ( fp ) ; } }
if ( p . length == 0 ) return ; // No files, quit now.
// Setup file compression
var ofile = cmd . path + '/' + cmd . output ;
this . write ( Buffer . from ( JSON . stringify ( { action : 'dialogmessage' , msg : 'zipping' } ) ) ) ;
this . zipfile = ofile ;
delete this . zipcancel ;
var out = require ( 'fs' ) . createWriteStream ( ofile , { flags : 'wb' } ) ;
out . xws = this ;
out . on ( 'close' , function ( )
{
this . xws . write ( Buffer . from ( JSON . stringify ( { action : 'dialogmessage' , msg : null } ) ) ) ;
this . xws . write ( Buffer . from ( JSON . stringify ( { action : 'refresh' } ) ) ) ;
if ( this . xws . zipcancel === true ) { fs . unlinkSync ( this . xws . zipfile ) ; } // Delete the complete file.
delete this . xws . zipcancel ;
delete this . xws . zipfile ;
delete this . xws . zip ;
} ) ;
this . zip = require ( 'zip-writer' ) . write ( { files : p , basePath : cmd . path } ) ;
this . zip . xws = this ;
this . zip . on ( 'progress' , require ( 'events' ) . moderated ( function ( name , p ) { this . xws . write ( Buffer . from ( JSON . stringify ( { action : 'dialogmessage' , msg : 'zippingFile' , file : ( ( process . platform == 'win32' ) ? ( name . split ( '/' ) . join ( '\\' ) ) : name ) , progress : p } ) ) ) ; } , 1000 ) ) ;
this . zip . pipe ( out ) ;
break ;
case 'cancel' :
// Cancel zip operation if present
try { this . zipcancel = true ; this . zip . cancel ( function ( ) { } ) ; } catch ( e ) { }
this . zip = null ;
break ;
default :
// Unknown action, ignore it.
break ;
}
} else if ( this . httprequest . protocol == 7 )
{ // Plugin data exchange
var cmd = null ;
try { cmd = JSON . parse ( data ) ; } catch ( e ) { } ;
if ( cmd == null ) { return ; }
if ( ( cmd . ctrlChannel == '102938' ) || ( ( cmd . type == 'offer' ) && ( cmd . sdp != null ) ) ) { onTunnelControlData ( cmd , this ) ; return ; } // If this is control data, handle it now.
if ( cmd . action == undefined ) return ;
switch ( cmd . action )
{
case 'plugin' : {
try { require ( cmd . plugin ) . consoleaction ( cmd , null , null , this ) ; } catch ( e ) { throw e ; }
break ;
}
default : {
// probably shouldn't happen, but just in case this feature is expanded
2019-10-10 14:13:25 -04:00
}
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
//sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
}
2018-01-16 20:30:34 -05:00
2021-01-24 18:19:29 -05:00
// Delete a directory with a files and directories within it
function deleteFolderRecursive ( path , rec )
{
var count = 0 ;
if ( fs . existsSync ( path ) )
{
if ( rec == true )
{
fs . readdirSync ( obj . path . join ( path , '*' ) ) . forEach ( function ( file , index )
{
var curPath = obj . path . join ( path , file ) ;
if ( fs . statSync ( curPath ) . isDirectory ( ) )
{ // recurse
count += deleteFolderRecursive ( curPath , true ) ;
} else
{ // delete file
fs . unlinkSync ( curPath ) ;
count ++ ;
}
} ) ;
2019-03-14 15:28:58 -04:00
}
2021-01-24 18:19:29 -05:00
fs . unlinkSync ( path ) ;
count ++ ;
}
return count ;
}
2019-03-14 15:28:58 -04:00
2021-01-24 18:19:29 -05:00
// Called when receiving control data on WebRTC
function onTunnelWebRTCControlData ( data )
{
if ( typeof data != 'string' ) return ;
var obj ;
try { obj = JSON . parse ( data ) ; } catch ( e ) { sendConsoleText ( 'Invalid control JSON on WebRTC: ' + data ) ; return ; }
if ( obj . type == 'close' )
{
//sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
try { this . close ( ) ; } catch ( e ) { }
try { this . xrtc . close ( ) ; } catch ( e ) { }
2018-01-18 18:43:43 -05:00
}
2021-01-24 18:19:29 -05:00
}
2018-01-18 18:43:43 -05:00
2021-01-24 18:19:29 -05:00
// Called when receiving control data on websocket
function onTunnelControlData ( data , ws )
{
var obj ;
if ( ws == null ) { ws = this ; }
if ( typeof data == 'string' ) { try { obj = JSON . parse ( data ) ; } catch ( e ) { sendConsoleText ( 'Invalid control JSON: ' + data ) ; return ; } }
else if ( typeof data == 'object' ) { obj = data ; } else { return ; }
//sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
//console.log('onTunnelControlData: ' + JSON.stringify(data));
if ( obj . action )
{
switch ( obj . action )
{
case 'lock' : {
// Lock the current user out of the desktop
try
{
if ( process . platform == 'win32' )
{
MeshServerLogEx ( 53 , null , "Locking remote user out of desktop" , ws . httprequest ) ;
var child = require ( 'child_process' ) ;
child . execFile ( process . env [ 'windir' ] + '\\system32\\cmd.exe' , [ '/c' , 'RunDll32.exe user32.dll,LockWorkStation' ] , { type : 1 } ) ;
}
} catch ( e ) { }
break ;
2018-07-23 20:34:24 -04:00
}
2021-01-24 18:19:29 -05:00
default :
// Unknown action, ignore it.
break ;
2018-07-23 20:34:24 -04:00
}
2021-01-24 18:19:29 -05:00
return ;
}
2018-07-23 20:34:24 -04:00
2021-01-24 18:19:29 -05:00
switch ( obj . type )
{
case 'options' : {
// These are additional connection options passed in the control channel.
//sendConsoleText('options: ' + JSON.stringify(obj));
delete obj . type ;
ws . httprequest . xoptions = obj ;
2020-04-14 22:12:14 -04:00
2021-01-24 18:19:29 -05:00
// Set additional user consent options if present
if ( ( obj != null ) && ( typeof obj . consent == 'number' ) ) { ws . httprequest . consent |= obj . consent ; }
2020-04-14 22:12:14 -04:00
2021-01-24 18:19:29 -05:00
break ;
}
case 'close' : {
// We received the close on the websocket
//sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
try { ws . close ( ) ; } catch ( e ) { }
break ;
}
case 'termsize' : {
// Indicates a change in terminal size
if ( process . platform == 'win32' )
{
if ( ws . httprequest . _dispatcher == null ) return ;
//sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
if ( ws . httprequest . _dispatcher . invoke ) { ws . httprequest . _dispatcher . invoke ( 'resizeTerminal' , [ obj . cols , obj . rows ] ) ; }
} else
{
if ( ws . httprequest . process == null || ws . httprequest . process . pty == 0 ) return ;
//sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
if ( ws . httprequest . process . tcsetsize ) { ws . httprequest . process . tcsetsize ( obj . rows , obj . cols ) ; }
2018-02-05 14:56:29 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'webrtc0' : { // Browser indicates we can start WebRTC switch-over.
if ( ws . 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
if ( process . platform == 'win32' )
{
ws . httprequest . _term . unpipe ( ws ) ;
} else
{
ws . httprequest . process . stdout . unpipe ( ws ) ;
ws . httprequest . process . stderr . unpipe ( ws ) ;
}
} else if ( ws . 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
ws . httprequest . desktop . kvm . unpipe ( ws ) ;
} else
{
// Switch things around so all WebRTC data goes to onTunnelData().
ws . rtcchannel . httprequest = ws . httprequest ;
ws . rtcchannel . removeAllListeners ( 'data' ) ;
ws . rtcchannel . on ( 'data' , onTunnelData ) ;
2020-01-24 15:49:41 -05:00
}
2021-01-24 18:19:29 -05:00
ws . write ( "{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}" ) ; // End of data marker
break ;
}
case 'webrtc1' : {
if ( ( ws . httprequest . protocol == 1 ) || ( ws . httprequest . protocol == 6 ) )
{ // Terminal
// Switch the user input from websocket to webrtc at this point.
if ( process . platform == 'win32' )
{
ws . unpipe ( ws . httprequest . _term ) ;
ws . rtcchannel . pipe ( ws . httprequest . _term , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
} else
{
ws . unpipe ( ws . httprequest . process . stdin ) ;
ws . rtcchannel . pipe ( ws . httprequest . process . stdin , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
2018-12-12 18:34:42 -05:00
}
2021-01-24 18:19:29 -05:00
ws . resume ( ) ; // Resume the websocket to keep receiving control data
} else if ( ws . httprequest . protocol == 2 )
{ // Desktop
// Switch the user input from websocket to webrtc at this point.
ws . unpipe ( ws . httprequest . desktop . kvm ) ;
try { ws . webrtc . rtcchannel . pipe ( ws . httprequest . desktop . kvm , { dataTypeSkip : 1 , end : false } ) ; } catch ( e ) { sendConsoleText ( 'EX2' ) ; } // 0 = Binary, 1 = Text.
ws . resume ( ) ; // Resume the websocket to keep receiving control data
2018-01-18 18:43:43 -05:00
}
2021-01-24 18:19:29 -05:00
ws . write ( '{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}' ) ; // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
break ;
}
case 'webrtc2' : {
// Other side received websocket end of data marker, start sending data on WebRTC channel
if ( ( ws . httprequest . protocol == 1 ) || ( ws . httprequest . protocol == 6 ) )
{ // Terminal
if ( process . platform == 'win32' )
{
ws . httprequest . _term . pipe ( ws . webrtc . rtcchannel , { dataTypeSkip : 1 , end : false } ) ; // 0 = Binary, 1 = Text.
} else
{
ws . httprequest . process . stdout . pipe ( ws . webrtc . rtcchannel , { dataTypeSkip : 1 , end : false } ) ; // 0 = Binary, 1 = Text.
ws . httprequest . process . stderr . pipe ( ws . webrtc . rtcchannel , { dataTypeSkip : 1 , end : false } ) ; // 0 = Binary, 1 = Text.
2018-12-12 18:34:42 -05:00
}
2021-01-24 18:19:29 -05:00
} else if ( ws . httprequest . protocol == 2 )
{ // Desktop
ws . httprequest . desktop . kvm . pipe ( ws . webrtc . rtcchannel , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
2020-01-24 15:49:41 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'offer' : {
// This is a WebRTC offer.
if ( ( ws . httprequest . protocol == 1 ) || ( ws . httprequest . protocol == 6 ) ) return ; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
ws . webrtc = rtc . createConnection ( ) ;
ws . webrtc . websocket = ws ;
ws . webrtc . on ( 'connected' , function ( ) { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ } ) ;
ws . webrtc . on ( 'disconnected' , function ( ) { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ } ) ;
ws . webrtc . on ( 'dataChannel' , function ( rtcchannel )
{
//sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
rtcchannel . maxFragmentSize = 32768 ;
rtcchannel . xrtc = this ;
rtcchannel . websocket = this . websocket ;
this . rtcchannel = rtcchannel ;
this . websocket . rtcchannel = rtcchannel ;
this . websocket . rtcchannel . on ( 'data' , onTunnelWebRTCControlData ) ;
this . websocket . rtcchannel . on ( 'end' , function ( )
{
// The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
//sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
if ( this . websocket . desktop && this . websocket . desktop . kvm )
{
try
{
this . unpipe ( this . websocket . desktop . kvm ) ;
this . websocket . httprequest . desktop . kvm . unpipe ( this ) ;
}
catch ( e ) { }
2020-01-24 15:49:41 -05:00
}
2021-01-24 18:19:29 -05:00
} ) ;
this . websocket . write ( '{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}' ) ; // Indicate we are ready for WebRTC switch-over.
} ) ;
var sdp = null ;
try { sdp = ws . webrtc . setOffer ( obj . sdp ) ; } catch ( e ) { }
if ( sdp != null ) { ws . write ( { type : 'answer' , ctrlChannel : '102938' , sdp : sdp } ) ; }
break ;
}
case 'ping' : {
ws . write ( "{\"ctrlChannel\":\"102938\",\"type\":\"pong\"}" ) ; // Send pong response
break ;
}
case 'pong' : { // NOP
break ;
}
case 'rtt' : {
ws . write ( { type : 'rtt' , ctrlChannel : '102938' , time : obj . time } ) ;
break ;
}
}
}
// Console state
var consoleWebSockets = { } ;
var consoleHttpRequest = null ;
// Console HTTP response
function consoleHttpResponse ( response )
{
response . data = function ( data ) { sendConsoleText ( rstr2hex ( buf2rstr ( data ) ) , this . sessionid ) ; consoleHttpRequest = null ; }
response . close = function ( ) { sendConsoleText ( 'httprequest.response.close' , this . sessionid ) ; consoleHttpRequest = null ; }
}
// Open a web browser to a specified URL on current user's desktop
function openUserDesktopUrl ( url )
{
if ( ( url . toLowerCase ( ) . startsWith ( 'http://' ) == false ) && ( url . toLowerCase ( ) . startsWith ( 'https://' ) == false ) ) { return null ; }
var child = null ;
try
{
switch ( process . platform )
{
case 'win32' :
var user = require ( 'user-sessions' ) . getUsername ( require ( 'user-sessions' ) . consoleUid ( ) ) ;
child = require ( 'child_process' ) . execFile ( process . env [ 'windir' ] + '\\system32\\cmd.exe' , [ 'cmd' ] ) ;
child . stderr . on ( 'data' , function ( ) { } ) ;
child . stdout . on ( 'data' , function ( ) { } ) ;
child . stdin . write ( 'SCHTASKS /CREATE /F /TN MeshChatTask /SC ONCE /ST 00:00 /RU ' + user + ' /TR "' + process . env [ 'windir' ] + '\\system32\\cmd.exe /C START ' + url + '"\r\n' ) ;
child . stdin . write ( 'SCHTASKS /RUN /TN MeshChatTask\r\n' ) ;
child . stdin . write ( 'SCHTASKS /DELETE /F /TN MeshChatTask\r\n' ) ;
child . stdin . write ( 'exit\r\n' ) ;
child . waitExit ( ) ;
break ;
case 'linux' :
child = require ( 'child_process' ) . execFile ( '/usr/bin/xdg-open' , [ 'xdg-open' , url ] , { uid : require ( 'user-sessions' ) . consoleUid ( ) } ) ;
break ;
case 'darwin' :
child = require ( 'child_process' ) . execFile ( '/usr/bin/open' , [ 'open' , url ] , { uid : require ( 'user-sessions' ) . consoleUid ( ) } ) ;
break ;
default :
// Unknown platform, ignore this command.
break ;
}
} catch ( e ) { }
return child ;
}
// Process a mesh agent console command
function processConsoleCommand ( cmd , args , rights , sessionid )
{
try
{
var response = null ;
switch ( cmd )
{
case 'help' : { // Displays available commands
var fin = '' , f = '' , availcommands = 'agentupdate,msh,timerinfo,coreinfo,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,openurl,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,wallpaper,agentmsg' ;
if ( process . platform == 'win32' ) { availcommands += ',safemode,wpfhwacceleration,uac' ; }
if ( amt != null ) { availcommands += ',amt,amtconfig,amtevents' ; }
if ( process . platform != 'freebsd' ) { availcommands += ',vm' ; }
if ( require ( 'MeshAgent' ) . maxKvmTileSize != null ) { availcommands += ',kvmmode' ; }
try { require ( 'zip-reader' ) ; availcommands += ',zip,unzip' ; } catch ( e ) { }
availcommands = availcommands . split ( ',' ) . sort ( ) ;
while ( availcommands . length > 0 )
{
if ( f . length > 90 ) { fin += ( f + ',\r\n' ) ; f = '' ; }
f += ( ( ( f != '' ) ? ', ' : ' ' ) + availcommands . shift ( ) ) ;
2018-12-12 18:34:42 -05:00
}
2021-01-24 18:19:29 -05:00
if ( f != '' ) { fin += f ; }
response = "Available commands: \r\n" + fin + "." ;
2020-01-24 15:49:41 -05:00
break ;
2018-01-18 18:43:43 -05:00
}
2021-01-24 18:19:29 -05:00
case 'agentupdate' :
require ( 'MeshAgent' ) . SendCommand ( { action : 'agentupdate' , sessionid : sessionid } ) ;
break ;
case 'agentupdateex' :
// Perform an direct agent update without requesting any information from the server, this should not typically be used.
agentUpdate _Start ( null , { sessionid : sessionid } ) ;
break ;
case 'msh' :
response = JSON . stringify ( _MSH ( ) , null , 2 ) ;
break ;
case 'timerinfo' :
response = require ( 'ChainViewer' ) . getTimerInfo ( ) ;
break ;
case 'find' :
if ( args [ '_' ] . length <= 1 )
{
response = "Proper usage:\r\n find root criteria [criteria2] [criteria n...]" ;
}
else
{
var root = args [ '_' ] [ 0 ] ;
var p = args [ '_' ] . slice ( 1 ) ;
var r = require ( 'file-search' ) . find ( root , p ) ;
r . sid = sessionid ;
r . on ( 'result' , function ( str ) { sendConsoleText ( str , this . sid ) ; } ) ;
r . then ( function ( ) { sendConsoleText ( '*** End Results ***' , this . sid ) ; } ) ;
response = "Find: [" + root + "] " + JSON . stringify ( p ) ;
2020-01-24 15:49:41 -05:00
}
break ;
2021-01-24 18:19:29 -05:00
case 'coreinfo' : {
response = JSON . stringify ( meshCoreObj , null , 2 ) ;
2020-01-24 15:49:41 -05:00
break ;
}
2021-01-24 18:19:29 -05:00
case 'coreinfoupdate' : {
sendPeriodicServerUpdate ( ) ;
2020-07-19 17:45:29 -04:00
break ;
}
2021-01-24 18:19:29 -05:00
case 'agentmsg' : {
if ( args [ '_' ] . length == 0 )
{
response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [index]\r\n agentmsg list" ; // Display usage
} else
{
if ( ( args [ '_' ] [ 0 ] == 'add' ) && ( args [ '_' ] . length > 1 ) )
{
var msgIndex = 1 , iconIndex = 0 ;
while ( tunnelUserCount . msg [ msgIndex ] != null ) { msgIndex ++ ; }
if ( args [ '_' ] . length >= 3 ) { try { iconIndex = parseInt ( args [ '_' ] [ 2 ] ) ; } catch ( e ) { } }
if ( typeof iconIndex != 'number' ) { iconIndex = 0 ; }
tunnelUserCount . msg [ msgIndex ] = { msg : args [ '_' ] [ 1 ] , icon : iconIndex } ;
response = 'Agent message ' + msgIndex + ' added.' ;
} else if ( ( args [ '_' ] [ 0 ] == 'remove' ) && ( args [ '_' ] . length > 1 ) )
{
var msgIndex = 0 ;
try { msgIndex = parseInt ( args [ '_' ] [ 1 ] ) ; } catch ( x ) { }
if ( tunnelUserCount . msg [ msgIndex ] == null ) { response = "Message not found." ; } else { delete tunnelUserCount . msg [ msgIndex ] ; response = "Message removed." ; }
} else if ( args [ '_' ] [ 0 ] == 'list' )
{
response = JSON . stringify ( tunnelUserCount . msg , null , 2 ) ;
}
try { mesh . SendCommand ( { action : 'sessions' , type : 'msg' , value : tunnelUserCount . msg } ) ; } catch ( x ) { }
broadcastSessionsToRegisteredApps ( ) ;
}
2020-07-19 17:45:29 -04:00
break ;
}
2021-01-24 18:19:29 -05:00
case 'clearagentmsg' : {
tunnelUserCount . msg = { } ;
try { mesh . SendCommand ( { action : 'sessions' , type : 'msg' , value : tunnelUserCount . msg } ) ; } catch ( x ) { }
broadcastSessionsToRegisteredApps ( ) ;
2020-04-14 01:48:20 -04:00
break ;
}
2021-01-24 18:19:29 -05:00
case 'coredump' :
if ( args [ '_' ] . length != 1 )
{
response = "Proper usage: coredump on|off|status|clear" ; // Display usage
} else
{
switch ( args [ '_' ] [ 0 ] . toLowerCase ( ) )
{
case 'on' :
process . coreDumpLocation = ( process . platform == 'win32' ) ? ( process . execPath . replace ( '.exe' , '.dmp' ) ) : ( process . execPath + '.dmp' ) ;
response = 'coredump is now on' ;
break ;
case 'off' :
process . coreDumpLocation = null ;
response = 'coredump is now off' ;
break ;
case 'status' :
response = 'coredump is: ' + ( ( process . coreDumpLocation == null ) ? 'off' : 'on' ) ;
if ( process . coreDumpLocation != null )
{
if ( process . platform == 'win32' )
{
if ( fs . existsSync ( process . coreDumpLocation ) )
{
response += '\r\n CoreDump present at: ' + process . coreDumpLocation ;
response += '\r\n CoreDump Time: ' + new Date ( fs . statSync ( process . coreDumpLocation ) . mtime ) . getTime ( ) ;
response += '\r\n Agent Time : ' + new Date ( fs . statSync ( process . execPath ) . mtime ) . getTime ( ) ;
}
} else
{
if ( ( process . cwd ( ) != '//' ) && fs . existsSync ( process . cwd ( ) + 'core' ) )
{
response += '\r\n CoreDump present at: ' + process . cwd ( ) + 'core' ;
response += '\r\n CoreDump Time: ' + new Date ( fs . statSync ( process . cwd ( ) + 'core' ) . mtime ) . getTime ( ) ;
response += '\r\n Agent Time : ' + new Date ( fs . statSync ( process . execPath ) . mtime ) . getTime ( ) ;
}
}
}
break ;
case 'clear' :
db . Put ( 'CoreDumpTime' , null ) ;
response = 'coredump db cleared' ;
break ;
default :
response = "Proper usage: coredump on|off|status" ; // Display usage
break ;
2019-09-27 13:30:00 -04:00
}
2018-04-19 21:19:15 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
case 'service' :
if ( args [ '_' ] . length != 1 )
{
response = "Proper usage: service status|restart" ; // Display usage
}
else
{
var svcname = process . platform == 'win32' ? 'Mesh Agent' : 'meshagent' ;
try
{
svcname = require ( 'MeshAgent' ) . serviceName ;
2020-11-09 17:17:11 -05:00
}
2021-01-24 18:19:29 -05:00
catch ( x )
{
2020-11-09 17:17:11 -05:00
}
2021-01-24 18:19:29 -05:00
var s = require ( 'service-manager' ) . manager . getService ( svcname ) ;
switch ( args [ '_' ] [ 0 ] . toLowerCase ( ) )
{
case 'status' :
response = 'Service ' + ( s . isRunning ( ) ? ( s . isMe ( ) ? '[SELF]' : '[RUNNING]' ) : ( '[NOT RUNNING]' ) ) ;
break ;
case 'restart' :
if ( s . isMe ( ) )
{
s . restart ( ) ;
}
else
{
response = 'Restarting another agent instance is not allowed' ;
}
break ;
default :
response = "Proper usage: service status|restart" ; // Display usage
break ;
}
if ( process . platform == 'win32' ) { s . close ( ) ; }
2020-10-30 20:08:07 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
case 'zip' :
if ( args [ '_' ] . length == 0 )
{
response = "Proper usage: zip (output file name), input1 [, input n]" ; // Display usage
} else
{
var p = args [ '_' ] . join ( ' ' ) . split ( ',' ) ;
var ofile = p . shift ( ) ;
sendConsoleText ( 'Writing ' + ofile + '...' ) ;
var out = require ( 'fs' ) . createWriteStream ( ofile , { flags : 'wb' } ) ;
out . fname = ofile ;
out . sessionid = sessionid ;
out . on ( 'close' , function ( ) { sendConsoleText ( 'DONE writing ' + this . fname , this . sessionid ) ; } ) ;
var zip = require ( 'zip-writer' ) . write ( { files : p } ) ;
zip . pipe ( out ) ;
2020-11-02 03:44:07 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
case 'unzip' :
if ( args [ '_' ] . length == 0 )
{
response = "Proper usage: unzip input, destination" ; // Display usage
} else
{
var p = args [ '_' ] . join ( ' ' ) . split ( ',' ) ;
if ( p . length != 2 ) { response = "Proper usage: unzip input, destination" ; break ; } // Display usage
var prom = require ( 'zip-reader' ) . read ( p [ 0 ] ) ;
prom . _dest = p [ 1 ] ;
prom . self = this ;
prom . sessionid = sessionid ;
prom . then ( function ( zipped )
{
sendConsoleText ( 'Extracting to ' + this . _dest + '...' , this . sessionid ) ;
zipped . extractAll ( this . _dest ) . then ( function ( ) { sendConsoleText ( 'finished unzipping' , this . sessionid ) ; } , function ( e ) { sendConsoleText ( 'Error unzipping: ' + e , this . sessionid ) ; } ) . parentPromise . sessionid = this . sessionid ;
} , function ( e ) { sendConsoleText ( 'Error unzipping: ' + e , this . sessionid ) ; } ) ;
2020-07-28 21:45:17 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
case 'setbattery' :
// require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 });
if ( ( args [ '_' ] . length > 0 ) && ( ( args [ '_' ] [ 0 ] == 'ac' ) || ( args [ '_' ] [ 0 ] == 'dc' ) ) )
{
var b = { action : 'battery' , state : args [ '_' ] [ 0 ] } ;
if ( args [ '_' ] . length == 2 ) { b . level = parseInt ( args [ '_' ] [ 1 ] ) ; }
require ( 'MeshAgent' ) . SendCommand ( b ) ;
} else
{
require ( 'MeshAgent' ) . SendCommand ( { action : 'battery' } ) ;
}
break ;
case 'fdsnapshot' :
require ( 'ChainViewer' ) . getSnapshot ( ) . then ( function ( c ) { sendConsoleText ( c , this . sessionid ) ; } ) . parentPromise . sessionid = sessionid ;
break ;
case 'fdcount' :
require ( 'DescriptorEvents' ) . getDescriptorCount ( ) . then (
function ( c )
{
sendConsoleText ( 'Descriptor Count: ' + c , this . sessionid ) ;
} , function ( e )
{
sendConsoleText ( 'Error fetching descriptor count: ' + e , this . sessionid ) ;
} ) . parentPromise . sessionid = sessionid ;
break ;
case 'uac' :
if ( process . platform != 'win32' )
{
response = 'Unknown command "uac", type "help" for list of avaialble commands.' ;
2020-07-28 21:45:17 -04:00
break ;
}
2021-01-24 18:19:29 -05:00
if ( args [ '_' ] . length != 1 )
{
response = 'Proper usage: uac [get|interactive|secure]' ;
}
else
{
switch ( args [ '_' ] [ 0 ] . toUpperCase ( ) )
{
case 'GET' :
var secd = require ( 'win-registry' ) . QueryKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System' , 'PromptOnSecureDesktop' ) ;
response = "UAC mode: " + ( secd == 0 ? "Interactive Desktop" : "Secure Desktop" ) ;
break ;
case 'INTERACTIVE' :
try
{
require ( 'win-registry' ) . WriteKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System' , 'PromptOnSecureDesktop' , 0 ) ;
response = 'UAC mode changed to: Interactive Desktop' ;
}
catch ( e )
{
response = "Unable to change UAC Mode" ;
}
break ;
case 'SECURE' :
try
{
require ( 'win-registry' ) . WriteKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System' , 'PromptOnSecureDesktop' , 1 ) ;
response = 'UAC mode changed to: Secure Desktop' ;
}
catch ( e )
{
response = "Unable to change UAC Mode" ;
}
break ;
default :
response = 'Proper usage: uac [get|interactive|secure]' ;
break ;
2020-07-06 17:19:16 -04:00
}
2021-01-24 18:19:29 -05:00
}
break ;
case 'vm' :
response = 'Virtual Machine = ' + require ( 'identifiers' ) . isVM ( ) ;
break ;
case 'startupoptions' :
response = JSON . stringify ( require ( 'MeshAgent' ) . getStartupOptions ( ) ) ;
break ;
case 'kvmmode' :
if ( require ( 'MeshAgent' ) . maxKvmTileSize == null )
{
response = "Unknown command \"kvmmode\", type \"help\" for list of avaialble commands." ;
}
else
{
if ( require ( 'MeshAgent' ) . maxKvmTileSize == 0 )
{
response = 'KVM Mode: Full JUMBO' ;
2020-07-01 16:15:07 -04:00
}
2021-01-24 18:19:29 -05:00
else
{
response = 'KVM Mode: ' + ( require ( 'MeshAgent' ) . maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO' ) ;
response += ( ', TileLimit: ' + ( require ( 'MeshAgent' ) . maxKvmTileSize < 1024 ? ( require ( 'MeshAgent' ) . maxKvmTileSize + ' bytes' ) : ( Math . round ( require ( 'MeshAgent' ) . maxKvmTileSize / 1024 ) + ' Kbytes' ) ) ) ;
2020-07-01 16:15:07 -04:00
}
2021-01-24 18:19:29 -05:00
}
break ;
case 'alert' :
if ( args [ '_' ] . length == 0 )
{
response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]" ; // Display usage
}
else
{
var p = args [ '_' ] . join ( ' ' ) . split ( ',' ) ;
if ( p . length < 2 )
{
response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]" ; // Display usage
2020-06-24 19:04:52 -04:00
}
2021-01-24 18:19:29 -05:00
else
{
this . _alert = require ( 'message-box' ) . create ( p [ 0 ] , p [ 1 ] , p . length == 3 ? parseInt ( p [ 2 ] ) : 9999 , 1 ) ;
2020-06-24 19:04:52 -04:00
}
2021-01-24 18:19:29 -05:00
}
break ;
case 'agentsize' :
var actualSize = Math . floor ( require ( 'fs' ) . statSync ( process . execPath ) . size / 1024 ) ;
if ( process . platform == 'win32' )
{
// Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
var writtenSize = 0 ;
try { writtenSize = require ( 'win-registry' ) . QueryKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent' , 'EstimatedSize' ) ; } catch ( e ) { response = e ; }
if ( writtenSize != actualSize )
{
response = "Size updated from: " + writtenSize + " to: " + actualSize ;
try { require ( 'win-registry' ) . WriteKey ( require ( 'win-registry' ) . HKEY . LocalMachine , 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent' , 'EstimatedSize' , actualSize ) ; } catch ( e ) { response = e ; }
} else
{ response = "Agent Size: " + actualSize + " kb" ; }
} else
{ response = "Agent Size: " + actualSize + " kb" ; }
break ;
case 'versions' :
response = JSON . stringify ( process . versions , null , ' ' ) ;
break ;
case 'wpfhwacceleration' :
if ( process . platform != 'win32' ) { throw ( "wpfhwacceleration setting is only supported on Windows" ) ; }
if ( args [ '_' ] . length != 1 )
{
response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)" ; // Display usage
}
else
{
var reg = require ( 'win-registry' ) ;
var uname = require ( 'user-sessions' ) . getUsername ( require ( 'user-sessions' ) . consoleUid ( ) ) ;
var key = reg . usernameToUserKey ( uname ) ;
switch ( args [ '_' ] [ 0 ] . toUpperCase ( ) )
{
default :
response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)" ; // Display usage
break ;
case 'ON' :
try
{
reg . WriteKey ( reg . HKEY . Users , key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics' , 'DisableHWAcceleration' , 0 ) ;
response = "OK" ;
} catch ( e ) { response = "FAILED" ; }
break ;
case 'OFF' :
try
{
reg . WriteKey ( reg . HKEY . Users , key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics' , 'DisableHWAcceleration' , 1 ) ;
response = 'OK' ;
} catch ( e ) { response = 'FAILED' ; }
break ;
case 'STATUS' :
var s ;
try { s = reg . QueryKey ( reg . HKEY . Users , key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics' , 'DisableHWAcceleration' ) == 1 ? 'DISABLED' : 'ENABLED' ; } catch ( e ) { s = 'DEFAULT' ; }
response = "WPF Hardware Acceleration: " + s ;
break ;
case 'DEFAULT' :
try { reg . DeleteKey ( reg . HKEY . Users , key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics' , 'DisableHWAcceleration' ) ; } catch ( e ) { }
response = 'OK' ;
break ;
2020-05-30 20:21:45 -04:00
}
2021-01-24 18:19:29 -05:00
}
break ;
case 'tsid' :
if ( process . platform == 'win32' )
{
if ( args [ '_' ] . length != 1 )
{
response = "TSID: " + ( require ( 'MeshAgent' ) . _tsid == null ? "console" : require ( 'MeshAgent' ) . _tsid ) ;
} else
{
var i = parseInt ( args [ '_' ] [ 0 ] ) ;
require ( 'MeshAgent' ) . _tsid = ( isNaN ( i ) ? null : i ) ;
response = "TSID set to: " + ( require ( 'MeshAgent' ) . _tsid == null ? "console" : require ( 'MeshAgent' ) . _tsid ) ;
2020-05-17 03:22:26 -04:00
}
2021-01-24 18:19:29 -05:00
} else
{ response = "TSID command only supported on Windows" ; }
break ;
case 'activeusers' :
if ( process . platform == 'win32' )
{
var p = require ( 'user-sessions' ) . enumerateUsers ( ) ;
p . sessionid = sessionid ;
p . then ( function ( u )
{
var v = [ ] ;
for ( var i in u )
{
if ( u [ i ] . State == 'Active' ) { v . push ( { tsid : i , type : u [ i ] . StationName , user : u [ i ] . Username , domain : u [ i ] . Domain } ) ; }
}
sendConsoleText ( JSON . stringify ( v , null , 1 ) , this . sessionid ) ;
} ) ;
} else
{ response = "activeusers command only supported on Windows" ; }
break ;
case 'wallpaper' :
if ( process . platform != 'win32' && ! ( process . platform == 'linux' && require ( 'linux-gnome-helpers' ) . available ) )
{
response = "wallpaper command not supported on this platform" ;
}
else
{
if ( args [ '_' ] . length != 1 )
{
response = 'Proper usage: wallpaper (GET|TOGGLE)' ; // Display usage
2020-05-17 03:22:26 -04:00
}
2021-01-24 18:19:29 -05:00
else
{
switch ( args [ '_' ] [ 0 ] . toUpperCase ( ) )
{
default :
response = 'Proper usage: wallpaper (GET|TOGGLE)' ; // Display usage
2020-05-17 03:22:26 -04:00
break ;
2021-01-24 18:19:29 -05:00
case 'GET' :
case 'TOGGLE' :
if ( process . platform == 'win32' )
{
var id = require ( 'user-sessions' ) . getProcessOwnerName ( process . pid ) . tsid == 0 ? 1 : 0 ;
var child = require ( 'child_process' ) . execFile ( process . execPath , [ process . execPath . split ( '\\' ) . pop ( ) , '-b64exec' , 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=' ] , { type : id } ) ;
child . stdout . str = '' ; child . stdout . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stderr . on ( 'data' , function ( ) { } ) ;
child . waitExit ( ) ;
var current = child . stdout . str . trim ( ) ;
if ( args [ '_' ] [ 0 ] . toUpperCase ( ) == 'GET' )
{
response = current ;
break ;
}
if ( current != '' )
{
require ( 'MeshAgent' ) . _wallpaper = current ;
response = 'Wallpaper cleared' ;
} else
{
response = 'Wallpaper restored' ;
}
child = require ( 'child_process' ) . execFile ( process . execPath , [ process . execPath . split ( '\\' ) . pop ( ) , '-b64exec' , 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=' , current != '' ? '""' : require ( 'MeshAgent' ) . _wallpaper ] , { type : id } ) ;
child . stdout . str = '' ; child . stdout . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stderr . on ( 'data' , function ( ) { } ) ;
child . waitExit ( ) ;
2020-05-17 03:22:26 -04:00
}
2021-01-24 18:19:29 -05:00
else
{
var id = require ( 'user-sessions' ) . consoleUid ( ) ;
var current = require ( 'linux-gnome-helpers' ) . getDesktopWallpaper ( id ) ;
if ( args [ '_' ] [ 0 ] . toUpperCase ( ) == 'GET' )
{
response = current ;
break ;
}
if ( current != '/dev/null' )
{
require ( 'MeshAgent' ) . _wallpaper = current ;
response = 'Wallpaper cleared' ;
} else
{
response = 'Wallpaper restored' ;
}
require ( 'linux-gnome-helpers' ) . setDesktopWallpaper ( id , current != '/dev/null' ? undefined : require ( 'MeshAgent' ) . _wallpaper ) ;
2020-05-17 03:22:26 -04:00
}
break ;
2020-04-13 01:53:49 -04:00
}
}
2021-01-24 18:19:29 -05:00
}
break ;
case 'safemode' :
if ( process . platform != 'win32' )
{
response = 'safemode only supported on Windows Platforms'
}
else
{
if ( args [ '_' ] . length != 1 )
{
response = 'Proper usage: safemode (ON|OFF|STATUS)' ; // Display usage
2020-03-12 00:25:03 -04:00
}
2021-01-24 18:19:29 -05:00
else
{
var svcname = process . platform == 'win32' ? 'Mesh Agent' : 'meshagent' ;
try
{
svcname = require ( 'MeshAgent' ) . serviceName ;
2020-03-12 00:25:03 -04:00
}
2021-01-24 18:19:29 -05:00
catch ( x )
{
2020-03-12 00:25:03 -04:00
}
2019-12-10 17:15:14 -05:00
2021-01-24 18:19:29 -05:00
switch ( args [ '_' ] [ 0 ] . toUpperCase ( ) )
{
2019-12-10 17:15:14 -05:00
default :
2021-01-24 18:19:29 -05:00
response = 'Proper usage: safemode (ON|OFF|STATUS)' ; // Display usage
2019-12-10 17:15:14 -05:00
break ;
case 'ON' :
2021-01-24 18:19:29 -05:00
require ( 'win-bcd' ) . setKey ( 'safeboot' , 'Network' ) ;
require ( 'win-bcd' ) . enableSafeModeService ( svcname ) ;
2019-12-10 17:15:14 -05:00
break ;
case 'OFF' :
2021-01-24 18:19:29 -05:00
require ( 'win-bcd' ) . deleteKey ( 'safeboot' ) ;
2019-12-10 17:15:14 -05:00
break ;
case 'STATUS' :
2021-01-24 18:19:29 -05:00
var nextboot = require ( 'win-bcd' ) . getKey ( 'safeboot' ) ;
if ( nextboot )
{
switch ( nextboot )
{
case 'Network' :
case 'network' :
nextboot = 'SAFE_MODE_NETWORK' ;
2019-11-14 18:22:00 -05:00
break ;
2021-01-24 18:19:29 -05:00
default :
nextboot = 'SAFE_MODE' ;
2019-11-14 18:22:00 -05:00
break ;
2019-10-29 14:05:33 -04:00
}
2021-01-24 18:19:29 -05:00
}
response = 'Current: ' + require ( 'win-bcd' ) . bootMode + ', NextBoot: ' + ( nextboot ? nextboot : 'NORMAL' ) ;
break ;
2019-10-25 13:33:10 -04:00
}
}
2021-01-24 18:19:29 -05:00
}
break ;
2021-01-18 19:11:15 -05:00
/ *
case 'border' :
{
if ( ( args [ '_' ] . length == 1 ) && ( args [ '_' ] [ 0 ] == 'on' ) ) {
if ( meshCoreObj . users . length > 0 ) {
obj . borderManager . Start ( meshCoreObj . users [ 0 ] ) ;
response = 'Border blinking is on.' ;
2018-08-29 21:47:22 -04:00
} else {
2021-01-18 19:11:15 -05:00
response = 'Cannot turn on border blinking, no logged in users.' ;
2018-08-29 21:47:22 -04:00
}
2021-01-18 19:11:15 -05:00
} else if ( ( args [ '_' ] . length == 1 ) && ( args [ '_' ] [ 0 ] == 'off' ) ) {
obj . borderManager . Stop ( ) ;
response = 'Border blinking is off.' ;
} else {
response = 'Proper usage: border "on|off"' ; // Display correct command usage
2018-08-29 21:47:22 -04:00
}
2021-01-18 19:11:15 -05:00
}
break ;
* /
2021-01-24 18:19:29 -05:00
case 'av' :
if ( process . platform == 'win32' )
{
// Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
response = JSON . stringify ( require ( 'win-info' ) . av ( ) , null , 1 ) ;
} else
{
response = 'Not supported on the platform' ;
2018-12-07 19:36:27 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
case 'log' :
if ( args [ '_' ] . length != 1 ) { response = 'Proper usage: log "sample text"' ; } else { MeshServerLog ( args [ '_' ] [ 0 ] ) ; response = 'ok' ; }
break ;
case 'getclip' :
if ( require ( 'MeshAgent' ) . isService )
{
require ( 'clipboard' ) . dispatchRead ( ) . then ( function ( str ) { sendConsoleText ( str , sessionid ) ; } ) ;
} else
{
require ( "clipboard" ) . read ( ) . then ( function ( str ) { sendConsoleText ( str , sessionid ) ; } ) ;
2018-11-25 15:59:42 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
case 'setclip' : {
if ( args [ '_' ] . length != 1 )
{
response = 'Proper usage: setclip "sample text"' ;
} else
{
if ( require ( 'MeshAgent' ) . isService )
{
require ( 'clipboard' ) . dispatchWrite ( args [ '_' ] [ 0 ] ) ;
response = 'Setting clipboard to: "' + args [ '_' ] [ 0 ] + '"' ;
}
else
{
require ( "clipboard" ) ( args [ '_' ] [ 0 ] ) ; response = 'Setting clipboard to: "' + args [ '_' ] [ 0 ] + '"' ;
2018-04-20 19:05:06 -04:00
}
2018-04-11 16:49:05 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'openurl' : {
if ( args [ '_' ] . length != 1 ) { response = 'Proper usage: openurl (url)' ; } // Display usage
else { if ( openUserDesktopUrl ( args [ '_' ] [ 0 ] ) == null ) { response = 'Failed.' ; } else { response = 'Success.' ; } }
break ;
}
case 'users' : {
if ( meshCoreObj . users == null ) { response = 'Active users are unknown.' ; } else { response = 'Active Users: ' + meshCoreObj . users . join ( ', ' ) + '.' ; }
require ( 'user-sessions' ) . enumerateUsers ( ) . then ( function ( u ) { for ( var i in u ) { sendConsoleText ( u [ i ] ) ; } } ) ;
break ;
}
case 'toast' : {
if ( args [ '_' ] . length < 1 ) { response = 'Proper usage: toast "message"' ; } else {
if ( require ( 'MeshAgent' ) . _tsid == null )
{
require ( 'toaster' ) . Toast ( 'MeshCentral' , args [ '_' ] [ 0 ] ) . then ( sendConsoleText , sendConsoleText ) ;
}
else
{
require ( 'toaster' ) . Toast ( 'MeshCentral' , args [ '_' ] [ 0 ] , require ( 'MeshAgent' ) . _tsid ) . then ( sendConsoleText , sendConsoleText ) ;
2018-04-11 16:49:05 -04:00
}
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'setdebug' : {
if ( args [ '_' ] . length < 1 ) { response = 'Proper usage: setdebug (target), 0 = Disabled, 1 = StdOut, 2 = This Console, * = All Consoles, 4 = WebLog, 8 = Logfile' ; } // Display usage
else { if ( args [ '_' ] [ 0 ] == '*' ) { console . setDestination ( 2 ) ; } else { console . setDestination ( parseInt ( args [ '_' ] [ 0 ] ) , sessionid ) ; } }
break ;
}
case 'ps' : {
processManager . getProcesses ( function ( plist )
{
var x = '' ;
for ( var i in plist ) { x += i + ( ( plist [ i ] . user ) ? ( ', ' + plist [ i ] . user ) : '' ) + ', ' + plist [ i ] . cmd + '\r\n' ; }
sendConsoleText ( x , sessionid ) ;
} ) ;
break ;
}
case 'kill' : {
if ( ( args [ '_' ] . length < 1 ) )
{
response = 'Proper usage: kill [pid]' ; // Display correct command usage
} else
{
process . kill ( parseInt ( args [ '_' ] [ 0 ] ) ) ;
response = 'Killed process ' + args [ '_' ] [ 0 ] + '.' ;
2018-03-12 21:16:06 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'smbios' : {
if ( SMBiosTables == null ) { response = 'SMBios tables not available.' ; } else { response = objToString ( SMBiosTables , 0 , ' ' , true ) ; }
break ;
}
case 'rawsmbios' : {
if ( SMBiosTablesRaw == null ) { response = 'SMBios tables not available.' ; } else {
response = '' ;
for ( var i in SMBiosTablesRaw )
{
var header = false ;
for ( var j in SMBiosTablesRaw [ i ] )
{
if ( SMBiosTablesRaw [ i ] [ j ] . length > 0 )
{
if ( header == false ) { response += ( 'Table type #' + i + ( ( require ( 'smbios' ) . smTableTypes [ i ] == null ) ? '' : ( ', ' + require ( 'smbios' ) . smTableTypes [ i ] ) ) ) + '\r\n' ; header = true ; }
response += ( ' ' + SMBiosTablesRaw [ i ] [ j ] . toString ( 'hex' ) ) + '\r\n' ;
2018-03-12 21:16:06 -04:00
}
2018-09-27 19:17:05 -04:00
}
}
2018-03-12 21:16:06 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'eval' : { // Eval JavaScript
if ( args [ '_' ] . length < 1 )
{
response = 'Proper usage: eval "JavaScript code"' ; // Display correct command usage
} else
{
response = JSON . stringify ( mesh . eval ( args [ '_' ] [ 0 ] ) ) ; // This can only be run by trusted administrator.
}
break ;
}
case 'uninstallagent' : // Uninstall this agent
var agentName = process . platform == 'win32' ? 'Mesh Agent' : 'meshagent' ;
try
{
agentName = require ( 'MeshAgent' ) . serviceName ;
}
catch ( x )
{
2017-08-28 12:27:45 -04:00
}
2021-01-05 12:47:47 -05:00
2021-01-24 18:19:29 -05:00
if ( ! require ( 'service-manager' ) . manager . getService ( agentName ) . isMe ( ) )
{
response = 'Uininstall failed, this instance is not the service instance' ;
} else
{
try { diagnosticAgent _uninstall ( ) ; } catch ( e ) { }
var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();" ;
this . child = require ( 'child_process' ) . execFile ( process . execPath , [ process . platform == 'win32' ? ( process . execPath . split ( '\\' ) . pop ( ) ) : ( process . execPath . split ( '/' ) . pop ( ) ) , '-b64exec' , Buffer . from ( js ) . toString ( 'base64' ) ] , { type : 4 , detached : true } ) ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
case 'notify' : { // Send a notification message to the mesh
if ( args [ '_' ] . length != 1 )
{
response = 'Proper usage: notify "message" [--session]' ; // Display correct command usage
} else
{
var notification = { action : 'msg' , type : 'notify' , value : args [ '_' ] [ 0 ] , tag : 'console' } ;
if ( args . session ) { notification . sessionid = sessionid ; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
mesh . SendCommand ( notification ) ; // no sessionid or userid specified, notification will go to the entire mesh
response = "ok" ;
}
break ;
}
case 'cpuinfo' : { // Return system information
// CPU & memory utilization
pr = require ( 'sysinfo' ) . cpuUtilization ( ) ;
pr . sessionid = sessionid ;
pr . then ( function ( data )
{
sendConsoleText ( JSON . stringify (
{
cpu : data ,
memory : require ( 'sysinfo' ) . memUtilization ( ) ,
thermals : require ( 'sysinfo' ) . thermals == null ? [ ] : require ( 'sysinfo' ) . thermals ( )
} , null , 1 ) , this . sessionid ) ;
} , function ( e )
{
sendConsoleText ( e ) ;
} ) ;
break ;
}
case 'sysinfo' : { // Return system information
getSystemInformation ( function ( results , err )
{
if ( results == null ) { sendConsoleText ( err , this . sessionid ) ; } else {
sendConsoleText ( JSON . stringify ( results , null , 1 ) , this . sessionid ) ;
}
} ) ;
break ;
}
case 'info' : { // Return information about the agent and agent core module
response = 'Current Core: ' + meshCoreObj . value + '\r\nAgent Time: ' + Date ( ) + '.\r\nUser Rights: 0x' + rights . toString ( 16 ) + '.\r\nPlatform: ' + process . platform + '.\r\nCapabilities: ' + meshCoreObj . caps + '.\r\nServer URL: ' + mesh . ServerUrl + '.' ;
if ( amt != null ) { response += '\r\nBuilt-in LMS: ' + [ 'Disabled' , 'Connecting..' , 'Connected' ] [ amt . lmsstate ] + '.' ; }
if ( meshCoreObj . osdesc ) { response += '\r\nOS: ' + meshCoreObj . osdesc + '.' ; }
response += '\r\nModules: ' + addedModules . join ( ', ' ) + '.' ;
response += '\r\nServer Connection: ' + mesh . isControlChannelConnected + ', State: ' + meshServerConnectionState + '.' ;
var oldNodeId = db . Get ( 'OldNodeId' ) ;
if ( oldNodeId != null ) { response += '\r\nOldNodeID: ' + oldNodeId + '.' ; }
if ( process . platform == 'linux' || process . platform == 'freebsd' ) { response += '\r\nX11 support: ' + require ( 'monitor-info' ) . kvm _x11 _support + '.' ; }
//response += '\r\Debug Console: ' + debugConsole + '.';
break ;
}
case 'osinfo' : { // Return the operating system information
var i = 1 ;
if ( args [ '_' ] . length > 0 ) { i = parseInt ( args [ '_' ] [ 0 ] ) ; if ( i > 8 ) { i = 8 ; } response = 'Calling ' + i + ' times.' ; }
for ( var j = 0 ; j < i ; j ++ )
{
var pr = require ( 'os' ) . name ( ) ;
2019-08-06 20:58:29 -04:00
pr . sessionid = sessionid ;
2021-01-24 18:19:29 -05:00
pr . then ( function ( v )
2021-01-20 17:38:04 -05:00
{
2021-01-24 18:19:29 -05:00
sendConsoleText ( "OS: " + v + ( process . platform == 'win32' ? ( require ( 'win-virtual-terminal' ) . supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]' ) : '' ) , this . sessionid ) ;
2019-09-23 19:46:26 -04:00
} ) ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'args' : { // Displays parsed command arguments
response = 'args ' + objToString ( args , 0 , ' ' , true ) ;
break ;
}
case 'print' : { // Print a message on the mesh agent console, does nothing when running in the background
var r = [ ] ;
for ( var i in args [ '_' ] ) { r . push ( args [ '_' ] [ i ] ) ; }
console . log ( r . join ( ' ' ) ) ;
response = 'Message printed on agent console.' ;
break ;
}
case 'type' : { // Returns the content of a file
if ( args [ '_' ] . length == 0 )
{
response = 'Proper usage: type (filepath) [maxlength]' ; // Display correct command usage
} else
{
var max = 4096 ;
if ( ( args [ '_' ] . length > 1 ) && ( typeof args [ '_' ] [ 1 ] == 'number' ) ) { max = args [ '_' ] [ 1 ] ; }
if ( max > 4096 ) max = 4096 ;
var buf = Buffer . alloc ( max ) , fd = fs . openSync ( args [ '_' ] [ 0 ] , "r" ) , r = fs . readSync ( fd , buf , 0 , max ) ; // Read the file content
response = buf . toString ( ) ;
var i = response . indexOf ( '\n' ) ;
if ( ( i > 0 ) && ( response [ i - 1 ] != '\r' ) ) { response = response . split ( '\n' ) . join ( '\r\n' ) ; }
if ( r == max ) response += '...' ;
fs . closeSync ( fd ) ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'dbkeys' : { // Return all data store keys
response = JSON . stringify ( db . Keys ) ;
break ;
}
case 'dbget' : { // Return the data store value for a given key
if ( db == null ) { response = 'Database not accessible.' ; break ; }
if ( args [ '_' ] . length != 1 )
{
response = 'Proper usage: dbget (key)' ; // Display the value for a given database key
} else
{
response = db . Get ( args [ '_' ] [ 0 ] ) ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'dbset' : { // Set a data store key and value pair
if ( db == null ) { response = 'Database not accessible.' ; break ; }
if ( args [ '_' ] . length != 2 )
{
response = 'Proper usage: dbset (key) (value)' ; // Set a database key
} else
{
var r = db . Put ( args [ '_' ] [ 0 ] , args [ '_' ] [ 1 ] ) ;
response = 'Key set: ' + r ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'dbcompact' : { // Compact the data store
if ( db == null ) { response = 'Database not accessible.' ; break ; }
var r = db . Compact ( ) ;
response = 'Database compacted: ' + r ;
break ;
}
case 'httpget' : {
if ( consoleHttpRequest != null )
{
response = 'HTTP operation already in progress.' ;
} else
{
if ( args [ '_' ] . length != 1 )
{
response = 'Proper usage: httpget (url)' ;
} else
{
var options = http . parseUri ( args [ '_' ] [ 0 ] ) ;
options . method = 'GET' ;
if ( options == null )
{
response = 'Invalid url.' ;
} else
{
try { consoleHttpRequest = http . request ( options , consoleHttpResponse ) ; } catch ( e ) { response = 'Invalid HTTP GET request' ; }
consoleHttpRequest . sessionid = sessionid ;
if ( consoleHttpRequest != null )
{
consoleHttpRequest . end ( ) ;
response = 'HTTPGET ' + options . protocol + '//' + options . host + ':' + options . port + options . path ;
2017-08-28 12:27:45 -04:00
}
}
}
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'wslist' : { // List all web sockets
response = '' ;
for ( var i in consoleWebSockets )
{
var httprequest = consoleWebSockets [ i ] ;
response += 'Websocket #' + i + ', ' + httprequest . url + '\r\n' ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
if ( response == '' ) { response = 'no websocket sessions.' ; }
break ;
}
case 'wsconnect' : { // Setup a web socket
if ( args [ '_' ] . length == 0 )
{
response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc' ; // Display correct command usage
} else
{
var httprequest = null ;
try
{
var options = http . parseUri ( args [ '_' ] [ 0 ] . split ( '$' ) . join ( '%24' ) . split ( '@' ) . join ( '%40' ) ) ; // Escape the $ and @ characters in the URL
options . rejectUnauthorized = 0 ;
httprequest = http . request ( options ) ;
} catch ( e ) { response = 'Invalid HTTP websocket request' ; }
if ( httprequest != null )
{
httprequest . upgrade = onWebSocketUpgrade ;
httprequest . on ( 'error' , function ( e ) { sendConsoleText ( "ERROR: Unable to connect to: " + this . url + ", " + JSON . stringify ( e ) ) ; } ) ;
var index = 1 ;
while ( consoleWebSockets [ index ] ) { index ++ ; }
httprequest . sessionid = sessionid ;
httprequest . index = index ;
httprequest . url = args [ '_' ] [ 0 ] ;
consoleWebSockets [ index ] = httprequest ;
response = 'New websocket session #' + index ;
2017-08-28 12:27:45 -04:00
}
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'wssend' : { // Send data on a web socket
if ( args [ '_' ] . length == 0 )
{
response = 'Proper usage: wssend (socketnumber)\r\n' ; // Display correct command usage
for ( var i in consoleWebSockets )
{
2017-08-28 12:27:45 -04:00
var httprequest = consoleWebSockets [ i ] ;
2021-01-24 18:19:29 -05:00
response += 'Websocket #' + i + ', ' + httprequest . url + '\r\n' ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
} else
{
var i = parseInt ( args [ '_' ] [ 0 ] ) ;
var httprequest = consoleWebSockets [ i ] ;
if ( httprequest != undefined )
{
httprequest . s . write ( args [ '_' ] [ 1 ] ) ;
response = 'ok' ;
} else
{
response = 'Invalid web socket number' ;
2019-03-05 02:48:45 -05:00
}
2018-03-20 20:48:03 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'wsclose' : { // Close a websocket
if ( args [ '_' ] . length == 0 )
{
response = 'Proper usage: wsclose (socketnumber)' ; // Display correct command usage
} else
{
var i = parseInt ( args [ '_' ] [ 0 ] ) ;
var httprequest = consoleWebSockets [ i ] ;
if ( httprequest != undefined )
{
if ( httprequest . s != null ) { httprequest . s . end ( ) ; } else { httprequest . end ( ) ; }
response = 'ok' ;
} else
{
response = 'Invalid web socket number' ;
2017-09-20 17:44:22 -04:00
}
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'tunnels' : { // Show the list of current tunnels
response = '' ;
for ( var i in tunnels ) { response += 'Tunnel #' + i + ', ' + tunnels [ i ] . url + '\r\n' ; }
if ( response == '' ) { response = 'No websocket sessions.' ; }
break ;
}
case 'ls' : { // Show list of files and folders
response = '' ;
var xpath = '*' ;
if ( args [ '_' ] . length > 0 ) { xpath = obj . path . join ( args [ '_' ] [ 0 ] , '*' ) ; }
response = 'List of ' + xpath + '\r\n' ;
var results = fs . readdirSync ( xpath ) ;
for ( var i = 0 ; i < results . length ; ++ i )
{
var stat = null , p = obj . path . join ( args [ '_' ] [ 0 ] , results [ i ] ) ;
try { stat = fs . statSync ( p ) ; } catch ( e ) { }
if ( ( stat == null ) || ( stat == undefined ) )
{
response += ( results [ i ] + "\r\n" ) ;
} else
{
response += ( results [ i ] + " " + ( ( stat . isDirectory ( ) ) ? "(Folder)" : "(File)" ) + "\r\n" ) ;
2017-09-01 14:23:22 -04:00
}
2017-08-28 15:48:53 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'lsx' : { // Show list of files and folders
response = objToString ( getDirectoryInfo ( args [ '_' ] [ 0 ] ) , 0 , ' ' , true ) ;
break ;
}
case 'lock' : { // Lock the current user out of the desktop
if ( process . platform == 'win32' ) { var child = require ( 'child_process' ) ; child . execFile ( process . env [ 'windir' ] + '\\system32\\cmd.exe' , [ '/c' , 'RunDll32.exe user32.dll,LockWorkStation' ] , { type : 1 } ) ; response = 'Ok' ; }
else { response = 'Not supported on the platform' ; }
break ;
}
case 'amt' : { // Show Intel AMT status
if ( amt != null )
{
amt . getMeiState ( 9 , function ( state )
{
var resp = "Intel AMT not detected." ;
if ( state != null ) { resp = objToString ( state , 0 , ' ' , true ) ; }
sendConsoleText ( resp , sessionid ) ;
2017-09-13 14:25:57 -04:00
} ) ;
2021-01-24 18:19:29 -05:00
} else
{
response = "Intel AMT not detected." ;
2017-09-13 14:25:57 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'netinfo' : { // Show network interface information
var interfaces = require ( 'os' ) . networkInterfaces ( ) ;
response = objToString ( interfaces , 0 , ' ' , true ) ;
break ;
}
case 'wakeonlan' : { // Send wake-on-lan
if ( ( args [ '_' ] . length != 1 ) || ( args [ '_' ] [ 0 ] . length != 12 ) )
{
response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".' ;
} else
{
var count = sendWakeOnLan ( args [ '_' ] [ 0 ] ) ;
response = 'Sent wake-on-lan on ' + count + ' interface(s).' ;
2017-09-20 17:44:22 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'sendall' : { // Send a message to all consoles on this mesh
sendConsoleText ( args [ '_' ] . join ( ' ' ) ) ;
break ;
}
case 'power' : { // Execute a power action on this computer
if ( mesh . ExecPowerState == undefined )
{
response = 'Power command not supported on this agent.' ;
} else
{
if ( ( args [ '_' ] . length == 0 ) || isNaN ( Number ( args [ '_' ] [ 0 ] ) ) )
{
response = 'Proper usage: power (actionNumber), where actionNumber is:\r\n LOGOFF = 1\r\n SHUTDOWN = 2\r\n REBOOT = 3\r\n SLEEP = 4\r\n HIBERNATE = 5\r\n DISPLAYON = 6\r\n KEEPAWAKE = 7\r\n BEEP = 8\r\n CTRLALTDEL = 9\r\n VIBRATE = 13\r\n FLASH = 14' ; // Display correct command usage
2021-01-15 02:57:52 -05:00
} else
2021-01-24 18:19:29 -05:00
{
var r = mesh . ExecPowerState ( Number ( args [ '_' ] [ 0 ] ) , Number ( args [ '_' ] [ 1 ] ) ) ;
response = 'Power action executed with return code: ' + r + '.' ;
}
2019-02-07 23:06:01 -05:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'location' : {
getIpLocationData ( function ( location )
{
sendConsoleText ( objToString ( { action : 'iplocation' , type : 'publicip' , value : location } , 0 , ' ' ) ) ;
} ) ;
break ;
}
case 'parseuri' : {
response = JSON . stringify ( http . parseUri ( args [ '_' ] [ 0 ] ) ) ;
break ;
}
case 'scanwifi' : {
if ( wifiScanner != null )
{
var wifiPresent = wifiScanner . hasWireless ;
if ( wifiPresent ) { response = "Perfoming Wifi scan..." ; wifiScanner . Scan ( ) ; } else { response = "Wifi absent." ; }
} else
{ response = "Wifi module not present." ; }
break ;
}
case 'modules' : {
response = JSON . stringify ( addedModules ) ;
break ;
}
case 'listservices' : {
var services = require ( 'service-manager' ) . manager . enumerateService ( ) ;
response = JSON . stringify ( services , null , 1 ) ;
break ;
}
case 'getscript' : {
if ( args [ '_' ] . length != 1 )
{
response = "Proper usage: getscript [scriptNumber]." ;
} else
{
mesh . SendCommand ( { action : 'getScript' , type : args [ '_' ] [ 0 ] } ) ;
2019-07-31 19:49:23 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'diagnostic' :
{
if ( ! mesh . DAIPC . listening )
{
response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process . cwd ( ) + ') is not on a local file system' ;
break ;
2019-02-07 23:06:01 -05:00
}
2021-01-24 18:19:29 -05:00
var diag = diagnosticAgent _installCheck ( ) ;
if ( diag )
2019-06-14 19:33:53 -04:00
{
2021-01-24 18:19:29 -05:00
if ( args [ '_' ] . length == 1 && args [ '_' ] [ 0 ] == 'uninstall' )
{
diagnosticAgent _uninstall ( ) ;
response = 'Diagnostic Agent uninstalled' ;
}
else
{
response = 'Diagnostic Agent installed at: ' + diag . appLocation ( ) ;
2019-06-14 19:33:53 -04:00
}
2021-01-24 18:19:29 -05:00
}
else
{
if ( args [ '_' ] . length == 1 && args [ '_' ] [ 0 ] == 'install' )
{
diag = diagnosticAgent _installCheck ( true ) ;
if ( diag )
{
response = 'Diagnostic agent was installed at: ' + diag . appLocation ( ) ;
2019-06-14 19:33:53 -04:00
}
2021-01-24 18:19:29 -05:00
else
{
response = 'Diagnostic agent installation failed' ;
2019-06-14 19:33:53 -04:00
}
}
2021-01-24 18:19:29 -05:00
else
{
response = 'Diagnostic Agent Not installed. To install: diagnostic install' ;
}
}
if ( diag ) { diag . close ( ) ; diag = null ; }
break ;
}
case 'amtevents' : {
if ( obj . amtevents == null ) { response = 'No events.' ; } else { response = obj . amtevents . join ( '\r\n' ) ; }
break ;
}
case 'amtconfig' : {
if ( amt == null ) { response = "Intel AMT not detected." ; break ; }
if ( apftunnel != null ) { response = "Intel AMT server tunnel already active" ; break ; }
amt . getMeiState ( 15 , function ( state )
{
var rx = '' ;
if ( ( state == null ) || ( state . ProvisioningState == null ) ) { rx = "Intel AMT not ready for configuration." ; } else {
var apfarg = {
mpsurl : mesh . ServerUrl . replace ( 'agent.ashx' , 'apf.ashx' ) ,
mpsuser : Buffer . from ( mesh . ServerInfo . MeshID , 'hex' ) . toString ( 'base64' ) . substring ( 0 , 16 ) ,
mpspass : Buffer . from ( mesh . ServerInfo . MeshID , 'hex' ) . toString ( 'base64' ) . substring ( 0 , 16 ) ,
mpskeepalive : 60000 ,
clientname : state . OsHostname ,
clientaddress : '127.0.0.1' ,
clientuuid : state . UUID ,
conntype : 2 , // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
meiState : state // MEI state will be passed to MPS server
} ;
if ( ( state . UUID == null ) || ( state . UUID . length != 36 ) )
{
rx = "Unable to get Intel AMT UUID" ;
} else
{
addAmtEvent ( 'User LMS tunnel start.' ) ;
apftunnel = require ( 'amt-apfclient' ) ( { debug : false } , apfarg ) ;
apftunnel . onJsonControl = function ( data )
{
if ( data . action == 'console' ) { addAmtEvent ( data . msg ) ; require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : data . msg } ) ; } // Display a console message
if ( data . action == 'mestate' ) { amt . getMeiState ( 15 , function ( state ) { apftunnel . updateMeiState ( state ) ; } ) ; } // Update the MEI state
if ( data . action == 'deactivate' )
{ // Request CCM deactivation
var amtMeiModule , amtMei ;
try { amtMeiModule = require ( 'amt-mei' ) ; amtMei = new amtMeiModule ( ) ; } catch ( ex ) { apftunnel . sendMeiDeactivationState ( 1 ) ; return ; }
amtMei . on ( 'error' , function ( e ) { apftunnel . sendMeiDeactivationState ( 1 ) ; } ) ;
amtMei . unprovision ( 1 , function ( status ) { apftunnel . sendMeiDeactivationState ( status ) ; } ) ; // 0 = Success
2019-06-14 19:33:53 -04:00
}
2021-01-24 18:19:29 -05:00
if ( data . action == 'close' ) { try { apftunnel . disconnect ( ) ; } catch ( e ) { } apftunnel = null ; } // Close the CIRA-LMS connection
2019-06-14 19:33:53 -04:00
}
2021-01-24 18:19:29 -05:00
apftunnel . onChannelClosed = function ( ) { addAmtEvent ( 'User LMS tunnel closed.' ) ; apftunnel = null ; }
try
{
apftunnel . connect ( ) ;
rx = "Started Intel AMT configuration" ;
} catch ( ex )
{
rx = JSON . stringify ( ex ) ;
2019-06-14 19:33:53 -04:00
}
}
}
2021-01-24 18:19:29 -05:00
if ( rx != '' ) { require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : rx } ) ; }
} ) ;
break ;
}
case 'apf' : {
if ( meshCoreObj . intelamt !== null )
{
if ( args [ '_' ] . length == 1 )
{
var connType = - 1 , connTypeStr = args [ '_' ] [ 0 ] . toLowerCase ( ) ;
if ( connTypeStr == 'lms' ) { connType = 2 ; }
if ( connTypeStr == 'relay' ) { connType = 1 ; }
if ( connTypeStr == 'cira' ) { connType = 0 ; }
if ( connTypeStr == 'off' ) { connType = - 2 ; }
if ( connType >= 0 )
{ // Connect
2020-10-20 21:14:00 -04:00
var apfarg = {
mpsurl : mesh . ServerUrl . replace ( 'agent.ashx' , 'apf.ashx' ) ,
mpsuser : Buffer . from ( mesh . ServerInfo . MeshID , 'hex' ) . toString ( 'base64' ) . substring ( 0 , 16 ) ,
mpspass : Buffer . from ( mesh . ServerInfo . MeshID , 'hex' ) . toString ( 'base64' ) . substring ( 0 , 16 ) ,
mpskeepalive : 60000 ,
2021-01-24 18:19:29 -05:00
clientname : require ( 'os' ) . hostname ( ) ,
2020-10-20 21:14:00 -04:00
clientaddress : '127.0.0.1' ,
2021-01-24 18:19:29 -05:00
clientuuid : meshCoreObj . intelamt . uuid ,
conntype : connType // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
2020-10-20 21:14:00 -04:00
} ;
2021-01-24 18:19:29 -05:00
if ( ( apfarg . clientuuid == null ) || ( apfarg . clientuuid . length != 36 ) )
{
response = "Unable to get Intel AMT UUID: " + apfarg . clientuuid ;
} else
{
2020-10-28 02:55:00 -04:00
apftunnel = require ( 'amt-apfclient' ) ( { debug : false } , apfarg ) ;
2021-01-24 18:19:29 -05:00
apftunnel . onJsonControl = function ( data )
{
if ( data . action == 'console' ) { require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : data . msg } ) ; }
if ( data . action == 'close' ) { try { apftunnel . disconnect ( ) ; } catch ( e ) { } apftunnel = null ; }
2020-10-20 21:14:00 -04:00
}
2021-01-24 18:19:29 -05:00
apftunnel . onChannelClosed = function ( ) { apftunnel = null ; }
try
{
2020-10-20 21:14:00 -04:00
apftunnel . connect ( ) ;
2021-01-24 18:19:29 -05:00
response = "Started APF tunnel" ;
} catch ( e )
{
2020-10-12 12:29:34 -04:00
response = JSON . stringify ( e ) ;
2019-09-17 16:11:22 -04:00
}
}
2021-01-24 18:19:29 -05:00
} else if ( connType == - 2 )
{ // Disconnect
try
{
apftunnel . disconnect ( ) ;
response = "Stopped APF tunnel" ;
} catch ( e )
{
response = JSON . stringify ( e ) ;
}
apftunnel = null ;
} else
{
response = "Invalid command.\r\nUse: apf lms|relay|cira|off" ;
2019-09-17 16:11:22 -04:00
}
2021-01-24 18:19:29 -05:00
} else
{
response = "APF tunnel is " + ( apftunnel == null ? "off" : "on" ) + "\r\nUse: apf lms|relay|cira|off" ;
2019-09-17 16:11:22 -04:00
}
2021-01-24 18:19:29 -05:00
} else
{
response = "APF tunnel requires Intel AMT" ;
2019-09-17 16:11:22 -04:00
}
2021-01-24 18:19:29 -05:00
break ;
}
case 'plugin' : {
if ( typeof args [ '_' ] [ 0 ] == 'string' )
{
try
{
// Pass off the action to the plugin
// for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
// to control the output / actions here.
response = require ( args [ '_' ] [ 0 ] ) . consoleaction ( args , rights , sessionid , mesh ) ;
} catch ( e )
{
response = "There was an error in the plugin (" + e + ")" ;
2019-10-10 14:13:25 -04:00
}
2021-01-24 18:19:29 -05:00
} else
{
response = "Proper usage: plugin [pluginName] [args]." ;
2017-08-28 12:27:45 -04:00
}
2021-01-18 22:04:53 -05:00
break ;
}
2021-01-24 18:19:29 -05:00
default : { // This is an unknown command, return an error message
response = "Unknown command \"" + cmd + "\", type \"help\" for list of avaialble commands." ;
break ;
2021-01-18 22:04:53 -05:00
}
}
2021-01-24 18:19:29 -05:00
} catch ( e ) { response = "Command returned an exception error: " + e ; console . log ( e ) ; }
if ( response != null ) { sendConsoleText ( response , sessionid ) ; }
}
2021-01-18 22:04:53 -05:00
2021-01-24 18:19:29 -05:00
// Send a mesh agent console command
function sendConsoleText ( text , sessionid )
{
if ( typeof text == 'object' ) { text = JSON . stringify ( text ) ; }
if ( debugConsole && ( ( sessionid == null ) || ( sessionid == 'pipe' ) ) ) { broadcastToRegisteredApps ( { cmd : 'console' , value : text } ) ; }
if ( sessionid != 'pipe' ) { require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : text , sessionid : sessionid } ) ; }
}
2021-01-18 22:04:53 -05:00
2021-01-24 18:19:29 -05:00
// Send a mesh agent message to server, placing a bubble/badge on the agent device
function sendAgentMessage ( msg , icon )
{
if ( sendAgentMessage . messages == null )
{
sendAgentMessage . messages = { } ;
sendAgentMessage . nextid = 1 ;
}
sendAgentMessage . messages [ sendAgentMessage . nextid ++ ] = { msg : msg , icon : icon } ;
require ( 'MeshAgent' ) . SendCommand ( { action : 'sessions' , type : 'msg' , value : sendAgentMessage . messages } ) ;
}
2021-01-18 22:04:53 -05:00
2021-01-24 18:19:29 -05:00
function linux _execv ( name , agentfilename , sessionid )
{
var libs = require ( 'monitor-info' ) . getLibInfo ( 'libc' ) ;
var libc = null ;
2021-01-18 22:04:53 -05:00
2021-01-24 18:19:29 -05:00
while ( libs . length > 0 )
{
try
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
libc = require ( '_GenericMarshal' ) . CreateNativeProxy ( libs . pop ( ) . path ) ;
break ;
2021-01-18 22:04:53 -05:00
}
2021-01-24 18:19:29 -05:00
catch ( e )
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
libc = null ;
continue ;
2021-01-18 22:04:53 -05:00
}
}
2021-01-24 18:19:29 -05:00
if ( libc != null )
2021-01-18 22:04:53 -05:00
{
try
{
libc . CreateMethod ( 'execv' ) ;
}
catch ( e )
{
2021-01-24 18:19:29 -05:00
libc = null ;
2021-01-18 22:04:53 -05:00
}
2021-01-24 18:19:29 -05:00
}
2021-01-18 22:04:53 -05:00
2021-01-24 18:19:29 -05:00
if ( libc == null )
{
// Couldn't find libc.so, fallback to using service manager to restart agent
if ( sessionid != null ) { sendConsoleText ( 'Restarting service via service-manager...' , sessionid ) }
try
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
// restart service
var s = require ( 'service-manager' ) . manager . getService ( name ) ;
s . restart ( ) ;
2021-01-18 22:04:53 -05:00
}
2021-01-24 18:19:29 -05:00
catch ( zz )
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
sendConsoleText ( 'Self Update encountered an error trying to restart service' , sessionid ) ;
sendAgentMessage ( 'Self Update encountered an error trying to restart service' , 3 ) ;
2021-01-18 22:04:53 -05:00
}
2021-01-24 18:19:29 -05:00
return ;
2021-01-18 22:04:53 -05:00
}
2021-01-24 18:19:29 -05:00
if ( sessionid != null ) { sendConsoleText ( 'Restarting service via execv()...' , sessionid ) }
var i ;
var args ;
var argarr = [ ] ;
var path = require ( '_GenericMarshal' ) . CreateVariable ( process . execPath ) ;
if ( require ( 'MeshAgent' ) . getStartupOptions != null )
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
var options = require ( 'MeshAgent' ) . getStartupOptions ( ) ;
for ( i in options )
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
argarr . push ( '--' + i + '="' + options [ i ] + '"' ) ;
2021-01-16 05:00:40 -05:00
}
2021-01-24 18:19:29 -05:00
}
args = require ( '_GenericMarshal' ) . CreateVariable ( ( 1 + argarr . length ) * require ( '_GenericMarshal' ) . PointerSize ) ;
for ( i = 0 ; i < argarr . length ; ++ i )
{
var arg = require ( '_GenericMarshal' ) . CreateVariable ( argarr [ i ] ) ;
arg . pointerBuffer ( ) . copy ( args . toBuffer ( ) , i * require ( '_GenericMarshal' ) . PointerSize ) ;
}
libc . execv ( path , args ) ;
if ( sessionid != null ) { sendConsoleText ( 'Self Update failed because execv() failed' , sessionid ) }
sendAgentMessage ( 'Self Update failed because execv() failed' , 3 ) ;
}
2021-01-16 05:00:40 -05:00
2021-01-24 18:19:29 -05:00
function bsd _execv ( name , agentfilename , sessionid )
{
var child = require ( 'child_process' ) . execFile ( '/bin/sh' , [ 'sh' ] ) ;
child . stdout . str = '' ; child . stdout . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stderr . str = '' ; child . stderr . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stdin . write ( "cat /usr/lib/libc.so | awk '" ) ;
child . stdin . write ( '{' ) ;
child . stdin . write ( ' a=split($0, tok, "(");' ) ;
child . stdin . write ( ' if(a>1)' ) ;
child . stdin . write ( ' {' ) ;
child . stdin . write ( ' split(tok[2], b, ")");' ) ;
child . stdin . write ( ' split(b[1], c, " ");' ) ;
child . stdin . write ( ' print c[1];' ) ;
child . stdin . write ( ' }' ) ;
child . stdin . write ( "}'\nexit\n" ) ;
child . waitExit ( ) ;
if ( child . stdout . str . trim ( ) == '' )
{
if ( sessionid != null ) { sendConsoleText ( 'Self Update failed because cannot find libc.so' , sessionid ) }
sendAgentMessage ( 'Self Update failed because cannot find libc.so' , 3 ) ;
return ;
}
2021-01-16 05:00:40 -05:00
2021-01-24 18:19:29 -05:00
var libc = null ;
try
{
libc = require ( '_GenericMarshal' ) . CreateNativeProxy ( child . stdout . str . trim ( ) ) ;
libc . CreateMethod ( 'execv' ) ;
}
catch ( e )
{
if ( sessionid != null ) { sendConsoleText ( 'Self Update failed: ' + e . toString ( ) , sessionid ) }
sendAgentMessage ( 'Self Update failed: ' + e . toString ( ) , 3 ) ;
return ;
}
2021-01-16 05:00:40 -05:00
2021-01-24 18:19:29 -05:00
var i ;
var path = require ( '_GenericMarshal' ) . CreateVariable ( process . execPath ) ;
var argarr = [ ] ;
var args ;
var options = require ( 'MeshAgent' ) . getStartupOptions ( ) ;
for ( i in options )
{
argarr . push ( '--' + i + '="' + options [ i ] + '"' ) ;
}
args = require ( '_GenericMarshal' ) . CreateVariable ( ( 1 + argarr . length ) * require ( '_GenericMarshal' ) . PointerSize ) ;
for ( i = 0 ; i < argarr . length ; ++ i )
{
var arg = require ( '_GenericMarshal' ) . CreateVariable ( argarr [ i ] ) ;
arg . pointerBuffer ( ) . copy ( args . toBuffer ( ) , i * require ( '_GenericMarshal' ) . PointerSize ) ;
2021-01-16 05:00:40 -05:00
}
2021-01-24 18:19:29 -05:00
if ( sessionid != null ) { sendConsoleText ( 'Restarting service via service-manager' , sessionid ) }
libc . execv ( path , args ) ;
if ( sessionid != null ) { sendConsoleText ( 'Self Update failed because execv() failed' , sessionid ) }
sendAgentMessage ( 'Self Update failed because execv() failed' , 3 ) ;
}
function windows _execve ( name , agentfilename , sessionid )
{
var libc ;
try
{
libc = require ( '_GenericMarshal' ) . CreateNativeProxy ( 'msvcrt.dll' ) ;
libc . CreateMethod ( '_wexecve' ) ;
}
catch ( xx )
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
sendConsoleText ( 'Self Update failed because msvcrt.dll is missing' , sessionid ) ;
sendAgentMessage ( 'Self Update failed because msvcrt.dll is missing' , 3 ) ;
return ;
}
var cmd = require ( '_GenericMarshal' ) . CreateVariable ( process . env [ 'windir' ] + '\\system32\\cmd.exe' , { wide : true } ) ;
var args = require ( '_GenericMarshal' ) . CreateVariable ( 3 * require ( '_GenericMarshal' ) . PointerSize ) ;
var arg1 = require ( '_GenericMarshal' ) . CreateVariable ( 'cmd.exe' , { wide : true } ) ;
var arg2 = require ( '_GenericMarshal' ) . CreateVariable ( '/C wmic service "' + name + '" call stopservice & copy "' + process . cwd ( ) + agentfilename + '.update" "' + process . execPath + '" & wmic service "' + name + '" call startservice & erase "' + process . cwd ( ) + agentfilename + '.update"' , { wide : true } ) ;
arg1 . pointerBuffer ( ) . copy ( args . toBuffer ( ) ) ;
arg2 . pointerBuffer ( ) . copy ( args . toBuffer ( ) , require ( '_GenericMarshal' ) . PointerSize ) ;
2021-01-15 02:57:52 -05:00
2021-01-24 18:19:29 -05:00
libc . _wexecve ( cmd , args , 0 ) ;
}
// Start a JavaScript based Agent Self-Update
function agentUpdate _Start ( updateurl , updateoptions )
{
// If this value is null
var sessionid = ( updateoptions != null ) ? updateoptions . sessionid : null ; // If this is null, messages will be broadcast. Otherwise they will be unicasted
if ( agentUpdate _Start . _selfupdate != null )
{
// We were already called, so we will ignore this duplicate request
if ( sessionid != null ) { sendConsoleText ( 'Self update already in progress...' , sessionid ) ; }
}
else
{
if ( agentUpdate _Start . _retryCount == null ) { agentUpdate _Start . _retryCount = 0 ; }
if ( require ( 'MeshAgent' ) . ARCHID == null && updateurl == null )
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
// This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull
sendConsoleText ( 'Unable to initiate update, agent ARCHID is not defined' , sessionid ) ;
2021-01-15 02:57:52 -05:00
}
2021-01-18 22:04:53 -05:00
else
{
2021-01-24 18:19:29 -05:00
var agentfilename = process . execPath . split ( process . platform == 'win32' ? '\\' : '/' ) . pop ( ) ; // Local File Name, ie: MeshAgent.exe
var name = require ( 'MeshAgent' ) . serviceName ;
if ( name == null ) { name = process . platform == 'win32' ? 'Mesh Agent' : 'meshagent' ; } // This is an older agent that doesn't expose the service name, so use the default
try
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
var s = require ( 'service-manager' ) . manager . getService ( name ) ;
if ( ! s . isMe ( ) )
2021-01-18 22:04:53 -05:00
{
2021-01-15 02:57:52 -05:00
if ( process . platform == 'win32' ) { s . close ( ) ; }
2021-01-24 18:19:29 -05:00
sendConsoleText ( 'Self Update cannot continue, this agent is not an instance of (' + name + ')' , sessionid ) ;
2021-01-15 02:57:52 -05:00
return ;
}
2021-01-24 18:19:29 -05:00
if ( process . platform == 'win32' ) { s . close ( ) ; }
}
catch ( zz )
{
sendConsoleText ( 'Self Update Failed because this agent is not an instance of (' + name + ')' , sessionid ) ;
sendAgentMessage ( 'Self Update Failed because this agent is not an instance of (' + name + ')' , 3 ) ;
return ;
}
2021-01-15 02:57:52 -05:00
2021-01-24 18:19:29 -05:00
if ( sessionid != null ) { sendConsoleText ( 'Downloading update...' , sessionid ) ; }
var options = require ( 'http' ) . parseUri ( updateurl != null ? updateurl : require ( 'MeshAgent' ) . ServerUrl ) ;
options . protocol = 'https:' ;
if ( updateurl == null ) { options . path = ( '/meshagents?id=' + require ( 'MeshAgent' ) . ARCHID ) ; }
options . rejectUnauthorized = false ;
options . checkServerIdentity = function checkServerIdentity ( certs )
{
// If the tunnel certificate matches the control channel certificate, accept the connection
try { if ( require ( 'MeshAgent' ) . ServerInfo . ControlChannelCertificate . digest == certs [ 0 ] . digest ) return ; } catch ( ex ) { }
try { if ( require ( 'MeshAgent' ) . ServerInfo . ControlChannelCertificate . fingerprint == certs [ 0 ] . fingerprint ) return ; } catch ( ex ) { }
2021-01-15 02:57:52 -05:00
2021-01-24 18:19:29 -05:00
// Check that the certificate is the one expected by the server, fail if not.
if ( checkServerIdentity . servertlshash == null )
{
if ( require ( 'MeshAgent' ) . ServerInfo == null || require ( 'MeshAgent' ) . ServerInfo . ControlChannelCertificate == null ) { return ; }
2021-01-18 01:09:00 -05:00
2021-01-24 18:19:29 -05:00
sendConsoleText ( 'Self Update failed, because the url cannot be verified' , sessionid ) ;
sendAgentMessage ( 'Self Update failed, because the url cannot be verified' , 3 ) ;
throw new Error ( 'BadCert' ) ;
2021-01-15 02:57:52 -05:00
}
2021-01-24 18:19:29 -05:00
if ( certs [ 0 ] . digest == null ) { return ; }
if ( ( checkServerIdentity . servertlshash != null ) && ( checkServerIdentity . servertlshash . toLowerCase ( ) != certs [ 0 ] . digest . split ( ':' ) . join ( '' ) . toLowerCase ( ) ) )
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
sendConsoleText ( 'Self Update failed, because the supplied certificate does not match' , sessionid ) ;
sendAgentMessage ( 'Self Update failed, because the supplied certificate does not match' , 3 ) ;
throw new Error ( 'BadCert' )
}
}
options . checkServerIdentity . servertlshash = ( updateoptions != null ? updateoptions . tlshash : null ) ;
agentUpdate _Start . _selfupdate = require ( 'https' ) . get ( options ) ;
agentUpdate _Start . _selfupdate . on ( 'error' , function ( e )
{
sendConsoleText ( 'Self Update failed, because there was a problem trying to download the update' , sessionid ) ;
sendAgentMessage ( 'Self Update failed, because there was a problem trying to download the update' , 3 ) ;
} ) ;
agentUpdate _Start . _selfupdate . on ( 'response' , function ( img )
{
this . _file = require ( 'fs' ) . createWriteStream ( agentfilename + '.update' , { flags : 'wb' } ) ;
this . _filehash = require ( 'SHA384Stream' ) . create ( ) ;
this . _filehash . on ( 'hash' , function ( h )
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
if ( updateoptions != null && updateoptions . hash != null )
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
if ( updateoptions . hash . toLowerCase ( ) == h . toString ( 'hex' ) . toLowerCase ( ) )
{
if ( sessionid != null ) { sendConsoleText ( 'Download complete. HASH verified.' , sessionid ) ; }
}
else
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
agentUpdate _Start . _retryCount ++ ;
sendConsoleText ( 'Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate _Start . _retryCount + ')' , sessionid ) ;
sendAgentMessage ( 'Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate _Start . _retryCount + ')' , 3 ) ;
agentUpdate _Start . _selfupdate = null ;
if ( agentUpdate _Start . _retryCount < 4 )
2021-01-18 22:04:53 -05:00
{
2021-01-24 18:19:29 -05:00
// Retry the download again
sendConsoleText ( 'Self Update will try again in 60 seconds...' , sessionid ) ;
agentUpdate _Start . _timeout = setTimeout ( agentUpdate _Start , 60000 , updateurl , updateoptions ) ;
2021-01-15 02:57:52 -05:00
}
2021-01-18 22:04:53 -05:00
else
{
2021-01-24 18:19:29 -05:00
sendConsoleText ( 'Self Update giving up, too many failures...' , sessionid ) ;
sendAgentMessage ( 'Self Update giving up, too many failures...' , 3 ) ;
2021-01-15 02:57:52 -05:00
}
2021-01-24 18:19:29 -05:00
return ;
2021-01-15 02:57:52 -05:00
}
2021-01-24 18:19:29 -05:00
}
else
{
sendConsoleText ( 'Download complete. HASH=' + h . toString ( 'hex' ) , sessionid ) ;
}
2021-01-15 02:57:52 -05:00
2021-01-24 18:19:29 -05:00
// Send an indication to the server that we got the update download correctly.
try { require ( 'MeshAgent' ) . SendCommand ( { action : 'agentupdatedownloaded' } ) ; } catch ( e ) { }
2021-01-17 02:35:39 -05:00
2021-01-24 18:19:29 -05:00
if ( sessionid != null ) { sendConsoleText ( 'Updating and restarting agent...' , sessionid ) ; }
if ( process . platform == 'win32' )
{
// Use _wexecve() equivalent to perform the update
windows _execve ( name , agentfilename , sessionid ) ;
}
else
{
var m = require ( 'fs' ) . statSync ( process . execPath ) . mode ;
require ( 'fs' ) . chmodSync ( process . cwd ( ) + agentfilename + '.update' , m ) ;
2021-01-18 22:04:53 -05:00
2021-01-24 18:19:29 -05:00
// remove binary
require ( 'fs' ) . unlinkSync ( process . execPath ) ;
2021-01-15 02:57:52 -05:00
2021-01-24 18:19:29 -05:00
// copy update
require ( 'fs' ) . copyFileSync ( process . cwd ( ) + agentfilename + '.update' , process . execPath ) ;
require ( 'fs' ) . chmodSync ( process . execPath , m ) ;
2021-01-15 02:57:52 -05:00
2021-01-24 18:19:29 -05:00
// erase update
require ( 'fs' ) . unlinkSync ( process . cwd ( ) + agentfilename + '.update' ) ;
2021-01-15 02:57:52 -05:00
2021-01-24 18:19:29 -05:00
switch ( process . platform )
{
case 'freebsd' :
bsd _execv ( name , agentfilename , sessionid ) ;
break ;
case 'linux' :
linux _execv ( name , agentfilename , sessionid ) ;
break ;
default :
try
{
// restart service
var s = require ( 'service-manager' ) . manager . getService ( name ) ;
s . restart ( ) ;
}
catch ( zz )
{
sendConsoleText ( 'Self Update encountered an error trying to restart service' , sessionid ) ;
sendAgentMessage ( 'Self Update encountered an error trying to restart service' , 3 ) ;
}
break ;
2021-01-15 02:57:52 -05:00
}
2021-01-24 18:19:29 -05:00
}
2021-01-15 02:57:52 -05:00
} ) ;
2021-01-24 18:19:29 -05:00
img . pipe ( this . _file ) ;
img . pipe ( this . _filehash ) ;
} ) ;
2021-01-15 02:57:52 -05:00
}
}
2021-01-24 18:19:29 -05:00
}
2021-01-15 02:57:52 -05:00
2021-01-24 18:19:29 -05:00
// Called before the process exits
//process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Called when the server connection state changes
function handleServerConnection ( state )
{
meshServerConnectionState = state ;
if ( meshServerConnectionState == 0 )
{
// Server disconnected
if ( selfInfoUpdateTimer != null ) { clearInterval ( selfInfoUpdateTimer ) ; selfInfoUpdateTimer = null ; }
lastSelfInfo = null ;
} else
{
// Server connected, send mesh core information
var oldNodeId = db . Get ( 'OldNodeId' ) ;
if ( oldNodeId != null ) { mesh . SendCommand ( { action : 'mc1migration' , oldnodeid : oldNodeId } ) ; }
2020-07-31 15:56:39 -04:00
2021-01-24 18:19:29 -05:00
// Send SMBios tables if present
if ( SMBiosTablesRaw != null ) { mesh . SendCommand ( { action : 'smbios' , value : SMBiosTablesRaw } ) ; }
2020-10-01 13:30:26 -04:00
2021-01-24 18:19:29 -05:00
// Update the server on with basic info, logged in users and more advanced stuff, like Intel ME and Network Settings
meInfoStr = null ;
sendPeriodicServerUpdate ( null , true ) ;
if ( selfInfoUpdateTimer == null )
{
selfInfoUpdateTimer = setInterval ( sendPeriodicServerUpdate , 1200000 ) ; // 20 minutes
selfInfoUpdateTimer . metadata = 'meshcore (InfoUpdate Timer)' ;
2020-10-01 13:30:26 -04:00
}
2020-10-12 01:24:30 -04:00
2021-01-24 18:19:29 -05:00
// Send any state messages
if ( Object . keys ( tunnelUserCount . msg ) . length > 0 )
{
try { mesh . SendCommand ( { action : 'sessions' , type : 'msg' , value : tunnelUserCount . msg } ) ; } catch ( e ) { }
broadcastSessionsToRegisteredApps ( ) ;
2019-05-08 19:18:44 -04:00
}
2021-01-24 18:19:29 -05:00
// Send update of registered applications to the server
updateRegisteredAppsToServer ( ) ;
2017-08-28 12:27:45 -04:00
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Send server state update to registered applications
broadcastToRegisteredApps ( { cmd : 'serverstate' , value : meshServerConnectionState , url : require ( 'MeshAgent' ) . ConnectedServer } ) ;
}
// Update the server with the latest network interface information
var sendNetworkUpdateNagleTimer = null ;
function sendNetworkUpdateNagle ( ) { if ( sendNetworkUpdateNagleTimer != null ) { clearTimeout ( sendNetworkUpdateNagleTimer ) ; sendNetworkUpdateNagleTimer = null ; } sendNetworkUpdateNagleTimer = setTimeout ( sendNetworkUpdate , 5000 ) ; }
function sendNetworkUpdate ( force )
{
sendNetworkUpdateNagleTimer = null ;
// Update the network interfaces information data
var netInfo = { netif2 : require ( 'os' ) . networkInterfaces ( ) } ;
if ( netInfo . netif2 )
2021-01-21 13:01:09 -05:00
{
2021-01-24 18:19:29 -05:00
netInfo . action = 'netinfo' ;
var netInfoStr = JSON . stringify ( netInfo ) ;
if ( ( force == true ) || ( clearGatewayMac ( netInfoStr ) != clearGatewayMac ( lastNetworkInfo ) ) ) { mesh . SendCommand ( netInfo ) ; lastNetworkInfo = netInfoStr ; }
}
}
2018-09-21 19:34:35 -04:00
2021-01-24 18:19:29 -05:00
// Called periodically to check if we need to send updates to the server
function sendPeriodicServerUpdate ( flags , force )
{
if ( meshServerConnectionState == 0 ) return ; // Not connected to server, do nothing.
if ( ! flags ) { flags = 0xFFFFFFFF ; }
// If we have a connected MEI, get Intel ME information
if ( ( flags & 1 ) && ( amt != null ) && ( amt . state == 2 ) )
{
delete meshCoreObj . intelamt ;
amt . getMeiState ( 9 , function ( meinfo )
2021-01-21 13:01:09 -05:00
{
2021-01-24 18:19:29 -05:00
meshCoreObj . intelamt = meinfo ;
meshCoreObj . intelamt . microlms = amt . lmsstate ;
meshCoreObjChanged ( ) ;
} ) ;
}
2018-09-21 19:34:35 -04:00
2021-01-24 18:19:29 -05:00
// Update network information
if ( flags & 2 ) { sendNetworkUpdateNagle ( false ) ; }
2019-08-02 14:35:59 -04:00
2021-01-24 18:19:29 -05:00
// Update anti-virus information
if ( ( flags & 4 ) && ( process . platform == 'win32' ) )
{
// Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
try { meshCoreObj . av = require ( 'win-info' ) . av ( ) ; meshCoreObjChanged ( ) ; } catch ( e ) { av = null ; } // Antivirus
//if (process.platform == 'win32') { try { meshCoreObj.pr = require('win-info').pendingReboot(); meshCoreObjChanged(); } catch (e) { meshCoreObj.pr = null; } } // Pending reboot
}
if ( process . platform == 'win32' )
{
if ( require ( 'MeshAgent' ) . _securitycenter == null )
2021-01-21 13:01:09 -05:00
{
2021-01-24 18:19:29 -05:00
try
2021-01-21 13:01:09 -05:00
{
2021-01-24 18:19:29 -05:00
require ( 'MeshAgent' ) . _securitycenter = require ( 'win-securitycenter' ) . status ( ) ;
meshCoreObj [ 'windowsSecurityCenter' ] = require ( 'MeshAgent' ) . _securitycenter ;
require ( 'win-securitycenter' ) . on ( 'changed' , function ( )
2021-01-21 13:01:09 -05:00
{
require ( 'MeshAgent' ) . _securitycenter = require ( 'win-securitycenter' ) . status ( ) ;
meshCoreObj [ 'windowsSecurityCenter' ] = require ( 'MeshAgent' ) . _securitycenter ;
2021-01-24 18:19:29 -05:00
require ( 'MeshAgent' ) . SendCommand ( { action : 'coreinfo' , windowsSecurityCenter : require ( 'MeshAgent' ) . _securitycenter } ) ;
} ) ;
2021-01-21 13:01:09 -05:00
}
2021-01-24 18:19:29 -05:00
catch ( e )
2021-01-21 13:01:09 -05:00
{
2020-11-03 05:58:29 -05:00
}
2019-08-02 14:35:59 -04:00
}
2017-12-12 19:04:54 -05:00
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
// Send available data right now
if ( force )
2021-01-21 13:01:09 -05:00
{
2020-11-02 03:44:07 -05:00
meshCoreObj = sortObjRec ( meshCoreObj ) ;
var x = JSON . stringify ( meshCoreObj ) ;
2021-01-21 13:01:09 -05:00
if ( x != LastPeriodicServerUpdate )
{
2021-01-24 18:19:29 -05:00
LastPeriodicServerUpdate = x ;
mesh . SendCommand ( meshCoreObj ) ;
2020-11-03 05:58:29 -05:00
}
2020-11-02 03:44:07 -05:00
}
2021-01-24 18:19:29 -05:00
}
2020-11-02 03:44:07 -05:00
2021-01-24 18:19:29 -05:00
// Once we are done collecting all the data, send to server if needed
var LastPeriodicServerUpdate = null ;
var PeriodicServerUpdateNagleTimer = null ;
function meshCoreObjChanged ( )
{
if ( PeriodicServerUpdateNagleTimer == null )
{
PeriodicServerUpdateNagleTimer = setTimeout ( meshCoreObjChangedEx , 500 ) ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
}
function meshCoreObjChangedEx ( )
{
PeriodicServerUpdateNagleTimer = null ;
meshCoreObj = sortObjRec ( meshCoreObj ) ;
var x = JSON . stringify ( meshCoreObj ) ;
if ( x != LastPeriodicServerUpdate )
{
try { LastPeriodicServerUpdate = x ; mesh . SendCommand ( meshCoreObj ) ; } catch ( ex ) { }
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
}
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
function sortObjRec ( o ) { if ( ( typeof o != 'object' ) || ( Array . isArray ( o ) ) ) return o ; for ( var i in o ) { if ( typeof o [ i ] == 'object' ) { o [ i ] = sortObjRec ( o [ i ] ) ; } } return sortObj ( o ) ; }
function sortObj ( o ) { return Object . keys ( o ) . sort ( ) . reduce ( function ( result , key ) { result [ key ] = o [ key ] ; return result ; } , { } ) ; }
2019-10-10 14:13:25 -04:00
2021-01-24 18:19:29 -05:00
function onWebSocketClosed ( ) { sendConsoleText ( "WebSocket #" + this . httprequest . index + " closed." , this . httprequest . sessionid ) ; delete consoleWebSockets [ this . httprequest . index ] ; }
function onWebSocketData ( data ) { sendConsoleText ( "Got WebSocket #" + this . httprequest . index + " data: " + data , this . httprequest . sessionid ) ; }
function onWebSocketSendOk ( ) { sendConsoleText ( "WebSocket #" + this . index + " SendOK." , this . sessionid ) ; }
2018-07-02 17:34:10 -04:00
2021-01-24 18:19:29 -05:00
function onWebSocketUpgrade ( response , s , head )
{
sendConsoleText ( "WebSocket #" + this . index + " connected." , this . sessionid ) ;
this . s = s ;
s . httprequest = this ;
s . end = onWebSocketClosed ;
s . data = onWebSocketData ;
2017-08-28 12:27:45 -04:00
}
2021-01-24 18:19:29 -05:00
mesh . AddCommandHandler ( handleServerCommand ) ;
mesh . AddConnectHandler ( handleServerConnection ) ;