2019-01-22 01:05:50 +03:00
var http = require ( 'http' ) ;
var childProcess = require ( 'child_process' ) ;
2020-01-08 00:56:26 +03:00
var meshCoreObj = { action : 'coreinfo' , value : "MeshCore Recovery" , caps : 14 } ; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript
2019-01-22 01:05:50 +03:00
var nextTunnelIndex = 1 ;
var tunnels = { } ;
2019-01-23 04:47:51 +03:00
var fs = require ( 'fs' ) ;
2021-01-19 03:11:15 +03:00
if ( require ( 'MeshAgent' ) . ARCHID == null ) {
2021-01-15 04:08:02 +03:00
var id = null ;
2021-01-19 03:11:15 +03:00
switch ( process . platform ) {
2021-01-15 04:08:02 +03: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-19 03:11:15 +03:00
try {
2021-01-17 01:33:06 +03:00
id = require ( 'os' ) . arch ( ) == 'x64' ? 16 : 29 ;
}
2021-01-19 03:11:15 +03:00
catch ( xx ) {
2021-01-17 01:33:06 +03:00
id = 16 ;
}
2021-01-15 04:08:02 +03:00
break ;
}
if ( id != null ) { Object . defineProperty ( require ( 'MeshAgent' ) , 'ARCHID' , { value : id } ) ; }
}
2019-01-23 04:47:51 +03:00
//attachDebugger({ webport: 9994, wait: 1 }).then(function (p) { console.log('Debug on port: ' + p); });
2019-01-22 01:05:50 +03:00
2021-01-19 03:11:15 +03:00
function sendConsoleText ( msg , sessionid ) {
if ( sessionid != null ) {
2021-01-15 04:08:02 +03:00
require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : msg , sessionid : sessionid } ) ;
}
2021-01-19 03:11:15 +03:00
else {
2021-01-15 04:08:02 +03:00
require ( 'MeshAgent' ) . SendCommand ( { action : 'msg' , type : 'console' , value : msg } ) ;
}
}
2021-01-19 03:11:15 +03:00
function sendAgentMessage ( msg , icon ) {
if ( sendAgentMessage . messages == null ) {
2021-01-15 04:08:02 +03:00
sendAgentMessage . messages = { } ;
sendAgentMessage . nextid = 1 ;
}
sendAgentMessage . messages [ sendAgentMessage . nextid ++ ] = { msg : msg , icon : icon } ;
require ( 'MeshAgent' ) . SendCommand ( { action : 'sessions' , type : 'msg' , value : sendAgentMessage . messages } ) ;
2019-01-22 01:05:50 +03:00
}
2021-01-15 04:08:02 +03:00
2021-01-18 05:36:53 +03:00
// Add to the server event log
2021-01-19 03:11:15 +03:00
function MeshServerLog ( msg , state ) {
2021-01-18 05:36:53 +03:00
if ( typeof msg == 'string' ) { msg = { action : 'log' , msg : msg } ; } else { msg . action = 'log' ; }
2021-01-19 03:11:15 +03:00
if ( state ) {
2021-01-18 05:36:53 +03:00
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 ; }
}
require ( 'MeshAgent' ) . SendCommand ( msg ) ;
}
// Add to the server event log, use internationalized events
2021-01-19 03:11:15 +03:00
function MeshServerLogEx ( id , args , msg , state ) {
2021-01-18 05:36:53 +03:00
var msg = { action : 'log' , msgid : id , msgArgs : args , msg : msg } ;
2021-01-19 03:11:15 +03:00
if ( state ) {
2021-01-18 05:36:53 +03:00
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 ; }
}
require ( 'MeshAgent' ) . SendCommand ( msg ) ;
}
2021-01-27 11:43:58 +03:00
function getOpenDescriptors ( )
{
switch ( process . platform )
{
case "freebsd" :
var child = require ( 'child_process' ) . execFile ( '/bin/sh' , [ 'sh' ] ) ;
child . stdout . str = '' ; child . stdout . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stderr . on ( 'data' , function ( c ) { } ) ;
child . stdin . write ( "procstat -f " + process . pid + " | tr '\\n' '`' | awk -F'`' '" ) ;
child . stdin . write ( '{' ) ;
child . stdin . write ( ' DEL="";' ) ;
child . stdin . write ( ' printf "[";' ) ;
child . stdin . write ( ' for(i=1;i<NF;++i)' ) ;
child . stdin . write ( ' {' ) ;
child . stdin . write ( ' A=split($i,B," ");' ) ;
child . stdin . write ( ' if(B[3] ~ /^[0-9]/)' ) ;
child . stdin . write ( ' {' ) ;
child . stdin . write ( ' printf "%s%s", DEL, B[3];' ) ;
child . stdin . write ( ' DEL=",";' ) ;
child . stdin . write ( ' }' ) ;
child . stdin . write ( ' }' ) ;
child . stdin . write ( ' printf "]";' ) ;
child . stdin . write ( "}'" ) ;
child . stdin . write ( '\nexit\n' ) ;
child . waitExit ( ) ;
try
{
return ( JSON . parse ( child . stdout . str . trim ( ) ) ) ;
}
catch ( e )
{
return ( [ ] ) ;
}
break ;
case "linux" :
var child = require ( 'child_process' ) . execFile ( '/bin/sh' , [ 'sh' ] ) ;
child . stdout . str = '' ; child . stdout . on ( 'data' , function ( c ) { this . str += c . toString ( ) ; } ) ;
child . stderr . on ( 'data' , function ( c ) { } ) ;
child . stdin . write ( "ls /proc/" + process . pid + "/fd | tr '\\n' '`' | awk -F'`' '" ) ;
child . stdin . write ( '{' ) ;
child . stdin . write ( ' printf "[";' ) ;
child . stdin . write ( ' DEL="";' ) ;
child . stdin . write ( ' for(i=1;i<NF;++i)' ) ;
child . stdin . write ( ' {' ) ;
child . stdin . write ( ' printf "%s%s",DEL,$i;' ) ;
child . stdin . write ( ' DEL=",";' ) ;
child . stdin . write ( ' }' ) ;
child . stdin . write ( ' printf "]";' ) ;
child . stdin . write ( "}'" ) ;
child . stdin . write ( '\nexit\n' ) ;
child . waitExit ( ) ;
try
{
return ( JSON . parse ( child . stdout . str . trim ( ) ) ) ;
}
catch ( e )
{
return ( [ ] ) ;
}
break ;
default :
return ( [ ] ) ;
}
}
2021-01-19 03:11:15 +03:00
function pathjoin ( ) {
2021-01-18 05:36:53 +03:00
var x = [ ] ;
2021-01-19 03:11:15 +03:00
for ( var i in arguments ) {
2021-01-18 05:36:53 +03:00
var w = arguments [ i ] ;
2021-01-19 03:11:15 +03:00
if ( w != null ) {
2021-01-18 05:36:53 +03:00
while ( w . endsWith ( '/' ) || w . endsWith ( '\\' ) ) { w = w . substring ( 0 , w . length - 1 ) ; }
2021-01-19 03:11:15 +03:00
if ( i != 0 ) {
2021-01-18 05:36:53 +03:00
while ( w . startsWith ( '/' ) || w . startsWith ( '\\' ) ) { w = w . substring ( 1 ) ; }
}
x . push ( w ) ;
}
}
if ( x . length == 0 ) return '/' ;
return x . join ( '/' ) ;
}
2021-01-18 06:38:42 +03: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 ; }
2021-01-18 05:36:53 +03:00
2021-01-27 11:43:58 +03:00
function closeDescriptors ( libc , descriptors )
{
var fd = null ;
while ( descriptors . length > 0 )
{
fd = descriptors . pop ( ) ;
if ( fd > 2 )
{
libc . close ( fd ) ;
}
}
}
2021-01-29 08:32:59 +03:00
function linux _execv ( name , agentfilename , sessionid )
{
2021-01-19 03:05:28 +03:00
var libs = require ( 'monitor-info' ) . getLibInfo ( 'libc' ) ;
var libc = null ;
2021-01-29 08:32:59 +03:00
if ( ( libs . length == 0 || libs . length == null ) && require ( 'MeshAgent' ) . ARCHID == 33 )
{
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 ( "ls /lib/libc.* | tr '\\n' '`' | awk -F'`' '{ " + ' printf "["; DEL=""; for(i=1;i<NF;++i) { printf "%s{\\"path\\":\\"%s\\"}",DEL,$i; DEL=""; } printf "]"; }\'\nexit\n' ) ;
child . waitExit ( ) ;
try
{
libs = JSON . parse ( child . stdout . str . trim ( ) ) ;
}
catch ( e )
{
}
}
while ( libs . length > 0 )
{
2021-01-19 03:11:15 +03:00
try {
2021-01-19 03:05:28 +03:00
libc = require ( '_GenericMarshal' ) . CreateNativeProxy ( libs . pop ( ) . path ) ;
break ;
}
2021-01-19 03:11:15 +03:00
catch ( e ) {
2021-01-19 03:05:28 +03:00
libc = null ;
continue ;
}
}
2021-01-19 03:11:15 +03:00
if ( libc != null ) {
2021-01-27 11:43:58 +03:00
try
{
2021-01-19 03:05:28 +03:00
libc . CreateMethod ( 'execv' ) ;
2021-01-27 11:43:58 +03:00
libc . CreateMethod ( 'close' ) ;
2021-01-19 03:05:28 +03:00
}
2021-01-19 03:11:15 +03:00
catch ( e ) {
2021-01-19 03:05:28 +03:00
libc = null ;
}
}
2021-01-19 03:11:15 +03:00
if ( libc == null ) {
2021-01-19 03:05:28 +03:00
// Couldn't find libc.so, fallback to using service manager to restart agent
if ( sessionid != null ) { sendConsoleText ( 'Restarting service via service-manager...' , sessionid ) }
2021-01-19 03:11:15 +03:00
try {
2021-01-19 03:05:28 +03:00
// restart service
var s = require ( 'service-manager' ) . manager . getService ( name ) ;
s . restart ( ) ;
}
2021-01-19 03:11:15 +03:00
catch ( zz ) {
2021-01-19 03:05:28 +03:00
sendConsoleText ( 'Self Update encountered an error trying to restart service' , sessionid ) ;
sendAgentMessage ( 'Self Update encountered an error trying to restart service' , 3 ) ;
}
return ;
}
if ( sessionid != null ) { sendConsoleText ( 'Restarting service via execv()...' , sessionid ) }
var i ;
var args ;
2021-02-03 09:32:09 +03:00
var argtmp = [ ] ;
2021-01-27 11:43:58 +03:00
var argarr = [ process . execPath ] ;
2021-01-19 03:05:28 +03:00
var path = require ( '_GenericMarshal' ) . CreateVariable ( process . execPath ) ;
2021-01-19 03:11:15 +03:00
if ( require ( 'MeshAgent' ) . getStartupOptions != null ) {
2021-01-19 03:05:28 +03:00
var options = require ( 'MeshAgent' ) . getStartupOptions ( ) ;
2021-01-19 03:11:15 +03:00
for ( i in options ) {
2021-01-19 03:05:28 +03:00
argarr . push ( '--' + i + '="' + options [ i ] + '"' ) ;
}
}
args = require ( '_GenericMarshal' ) . CreateVariable ( ( 1 + argarr . length ) * require ( '_GenericMarshal' ) . PointerSize ) ;
2021-01-19 03:11:15 +03:00
for ( i = 0 ; i < argarr . length ; ++ i ) {
2021-01-19 03:05:28 +03:00
var arg = require ( '_GenericMarshal' ) . CreateVariable ( argarr [ i ] ) ;
2021-02-03 09:32:09 +03:00
argtmp . push ( arg ) ;
2021-01-19 03:05:28 +03:00
arg . pointerBuffer ( ) . copy ( args . toBuffer ( ) , i * require ( '_GenericMarshal' ) . PointerSize ) ;
}
2021-01-27 11:43:58 +03:00
var descriptors = getOpenDescriptors ( ) ;
closeDescriptors ( libc , descriptors ) ;
2021-01-19 03:05:28 +03:00
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-18 05:36:53 +03:00
2021-01-19 03:11:15 +03:00
function bsd _execv ( name , agentfilename , sessionid ) {
2021-01-18 02:11:56 +03:00
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 ( ) ;
2021-01-19 03:11:15 +03:00
if ( child . stdout . str . trim ( ) == '' ) {
2021-01-18 02:11:56 +03:00
if ( sessionid != null ) { sendConsoleText ( 'Self Update failed because cannot find libc.so' , sessionid ) }
sendAgentMessage ( 'Self Update failed because cannot find libc.so' , 3 ) ;
return ;
}
var libc = null ;
2021-01-27 11:43:58 +03:00
try
{
2021-01-18 02:11:56 +03:00
libc = require ( '_GenericMarshal' ) . CreateNativeProxy ( child . stdout . str . trim ( ) ) ;
libc . CreateMethod ( 'execv' ) ;
2021-01-27 11:43:58 +03:00
libc . CreateMethod ( 'close' ) ;
2021-01-18 02:11:56 +03:00
}
2021-01-19 03:11:15 +03:00
catch ( e ) {
2021-01-18 02:11:56 +03:00
if ( sessionid != null ) { sendConsoleText ( 'Self Update failed: ' + e . toString ( ) , sessionid ) }
sendAgentMessage ( 'Self Update failed: ' + e . toString ( ) , 3 ) ;
return ;
}
var i ;
var path = require ( '_GenericMarshal' ) . CreateVariable ( process . execPath ) ;
2021-01-27 11:43:58 +03:00
var argarr = [ process . execPath ] ;
2021-02-03 09:32:09 +03:00
var argtmp = [ ] ;
2021-01-18 02:11:56 +03:00
var args ;
var options = require ( 'MeshAgent' ) . getStartupOptions ( ) ;
2021-01-19 03:11:15 +03:00
for ( i in options ) {
2021-01-18 02:11:56 +03:00
argarr . push ( '--' + i + '="' + options [ i ] + '"' ) ;
}
args = require ( '_GenericMarshal' ) . CreateVariable ( ( 1 + argarr . length ) * require ( '_GenericMarshal' ) . PointerSize ) ;
2021-01-19 03:11:15 +03:00
for ( i = 0 ; i < argarr . length ; ++ i ) {
2021-01-18 02:11:56 +03:00
var arg = require ( '_GenericMarshal' ) . CreateVariable ( argarr [ i ] ) ;
2021-02-03 09:32:09 +03:00
argtmp . push ( arg ) ;
2021-01-18 02:11:56 +03:00
arg . pointerBuffer ( ) . copy ( args . toBuffer ( ) , i * require ( '_GenericMarshal' ) . PointerSize ) ;
}
2021-01-19 03:11:15 +03:00
2021-01-27 08:53:20 +03:00
if ( sessionid != null ) { sendConsoleText ( 'Restarting service via execv()' , sessionid ) }
2021-01-27 11:43:58 +03:00
var descriptors = getOpenDescriptors ( ) ;
closeDescriptors ( libc , descriptors ) ;
2021-01-18 02:11:56 +03:00
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-19 03:11:15 +03:00
function windows _execve ( name , agentfilename , sessionid ) {
2021-01-16 13:00:40 +03:00
var libc ;
2021-01-19 03:11:15 +03:00
try {
2021-01-16 13:00:40 +03:00
libc = require ( '_GenericMarshal' ) . CreateNativeProxy ( 'msvcrt.dll' ) ;
libc . CreateMethod ( '_wexecve' ) ;
}
2021-01-19 03:11:15 +03:00
catch ( xx ) {
2021-01-16 13:00:40 +03: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 ) ;
libc . _wexecve ( cmd , args , 0 ) ;
}
2021-01-15 10:57:52 +03:00
// Start a JavaScript based Agent Self-Update
2021-01-29 02:49:42 +03:00
function agentUpdate _Start ( updateurl , updateoptions ) {
2021-01-15 10:57:52 +03:00
// If this value is null
2021-01-17 10:35:39 +03:00
var sessionid = ( updateoptions != null ) ? updateoptions . sessionid : null ; // If this is null, messages will be broadcast. Otherwise they will be unicasted
2021-01-15 04:08:02 +03:00
2021-01-29 02:49:42 +03:00
// If the url starts with *, switch it to use the same protoco, host and port as the control channel.
updateurl = getServerTargetUrlEx ( updateurl ) ;
if ( updateurl . startsWith ( "wss://" ) ) { updateurl = "https://" + updateurl . substring ( 6 ) ; }
if ( agentUpdate _Start . _selfupdate != null ) {
2021-01-15 10:57:52 +03:00
// We were already called, so we will ignore this duplicate request
2021-01-15 04:08:02 +03:00
if ( sessionid != null ) { sendConsoleText ( 'Self update already in progress...' , sessionid ) ; }
}
2021-01-29 02:49:42 +03:00
else {
2021-01-19 09:08:12 +03:00
if ( agentUpdate _Start . _retryCount == null ) { agentUpdate _Start . _retryCount = 0 ; }
2021-01-29 02:49:42 +03:00
if ( require ( 'MeshAgent' ) . ARCHID == null && updateurl == null ) {
2021-01-15 10:57:52 +03: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 04:08:02 +03:00
}
2021-01-29 02:49:42 +03:00
else {
2021-01-15 10:57:52 +03:00
var agentfilename = process . execPath . split ( process . platform == 'win32' ? '\\' : '/' ) . pop ( ) ; // Local File Name, ie: MeshAgent.exe
2021-01-15 04:08:02 +03:00
var name = require ( 'MeshAgent' ) . serviceName ;
2021-01-29 02:49:42 +03:00
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-15 04:08:02 +03:00
var s = require ( 'service-manager' ) . manager . getService ( name ) ;
2021-01-29 02:49:42 +03:00
if ( ! s . isMe ( ) ) {
2021-01-15 04:08:02 +03:00
if ( process . platform == 'win32' ) { s . close ( ) ; }
2021-01-15 10:57:52 +03:00
sendConsoleText ( 'Self Update cannot continue, this agent is not an instance of (' + name + ')' , sessionid ) ;
2021-01-15 04:08:02 +03:00
return ;
}
if ( process . platform == 'win32' ) { s . close ( ) ; }
}
2021-01-29 02:49:42 +03:00
catch ( zz ) {
2021-01-15 10:57:52 +03:00
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 ) ;
2021-01-15 04:08:02 +03:00
return ;
}
2021-01-29 02:49:42 +03:00
if ( sessionid != null ) { sendConsoleText ( 'Downloading update from: ' + updateurl , sessionid ) ; }
2021-01-15 04:08:02 +03:00
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 ;
2021-01-29 02:49:42 +03:00
options . checkServerIdentity = function checkServerIdentity ( certs ) {
2021-01-15 04:08:02 +03:00
// 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.
2021-01-29 02:49:42 +03:00
if ( checkServerIdentity . servertlshash == null ) {
2021-01-18 08:57:32 +03:00
if ( require ( 'MeshAgent' ) . ServerInfo == null || require ( 'MeshAgent' ) . ServerInfo . ControlChannelCertificate == null ) { return ; }
2021-01-29 02:49:42 +03:00
sendConsoleText ( 'Self Update failed, because the url cannot be verified: ' + updateurl , sessionid ) ;
sendAgentMessage ( 'Self Update failed, because the url cannot be verified: ' + updateurl , 3 ) ;
2021-01-15 04:08:02 +03:00
throw new Error ( 'BadCert' ) ;
}
2021-01-19 03:05:28 +03:00
if ( certs [ 0 ] . digest == null ) { return ; }
2021-01-29 02:49:42 +03:00
if ( ( checkServerIdentity . servertlshash != null ) && ( checkServerIdentity . servertlshash . toLowerCase ( ) != certs [ 0 ] . digest . split ( ':' ) . join ( '' ) . toLowerCase ( ) ) ) {
2021-01-15 10:57:52 +03: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 ) ;
2021-01-15 04:08:02 +03:00
throw new Error ( 'BadCert' )
}
}
options . checkServerIdentity . servertlshash = ( updateoptions != null ? updateoptions . tlshash : null ) ;
2021-01-19 09:08:12 +03:00
agentUpdate _Start . _selfupdate = require ( 'https' ) . get ( options ) ;
2021-01-29 02:49:42 +03:00
agentUpdate _Start . _selfupdate . on ( 'error' , function ( e ) {
sendConsoleText ( 'Self Update failed, because there was a problem trying to download the update from ' + updateurl , sessionid ) ;
sendAgentMessage ( 'Self Update failed, because there was a problem trying to download the update from ' + updateurl , 3 ) ;
2021-01-29 00:41:19 +03:00
agentUpdate _Start . _selfupdate = null ;
2021-01-15 04:08:02 +03:00
} ) ;
2021-01-29 02:49:42 +03:00
agentUpdate _Start . _selfupdate . on ( 'response' , function ( img ) {
2021-01-15 04:08:02 +03:00
this . _file = require ( 'fs' ) . createWriteStream ( agentfilename + '.update' , { flags : 'wb' } ) ;
this . _filehash = require ( 'SHA384Stream' ) . create ( ) ;
2021-01-29 02:49:42 +03:00
this . _filehash . on ( 'hash' , function ( h ) {
if ( updateoptions != null && updateoptions . hash != null ) {
if ( updateoptions . hash . toLowerCase ( ) == h . toString ( 'hex' ) . toLowerCase ( ) ) {
2021-01-17 10:35:39 +03:00
if ( sessionid != null ) { sendConsoleText ( 'Download complete. HASH verified.' , sessionid ) ; }
2021-01-15 04:08:02 +03:00
}
2021-01-29 02:49:42 +03:00
else {
2021-01-19 09:08:12 +03:00
agentUpdate _Start . _retryCount ++ ;
2021-01-29 02:49:42 +03:00
sendConsoleText ( 'Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate _Start . _retryCount + '), URL: ' + updateurl , sessionid ) ;
sendAgentMessage ( 'Self Update FAILED because the downloaded agent FAILED hash check (' + agentUpdate _Start . _retryCount + '), URL: ' + updateurl , 3 ) ;
2021-01-19 09:08:12 +03:00
agentUpdate _Start . _selfupdate = null ;
2021-01-29 02:49:42 +03:00
if ( agentUpdate _Start . _retryCount < 4 ) {
2021-01-19 09:08:12 +03: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-29 02:49:42 +03:00
else {
2021-01-19 09:08:12 +03:00
sendConsoleText ( 'Self Update giving up, too many failures...' , sessionid ) ;
sendAgentMessage ( 'Self Update giving up, too many failures...' , 3 ) ;
}
2021-01-15 04:08:02 +03:00
return ;
}
}
2021-01-29 02:49:42 +03:00
else {
2021-01-15 10:57:52 +03:00
sendConsoleText ( 'Download complete. HASH=' + h . toString ( 'hex' ) , sessionid ) ;
2021-01-15 04:08:02 +03:00
}
2021-01-17 10:35:39 +03:00
// Send an indication to the server that we got the update download correctly.
2021-01-18 02:11:56 +03:00
try { require ( 'MeshAgent' ) . SendCommand ( { action : 'agentupdatedownloaded' } ) ; } catch ( e ) { }
2021-01-17 10:35:39 +03:00
if ( sessionid != null ) { sendConsoleText ( 'Updating and restarting agent...' , sessionid ) ; }
2021-01-29 02:49:42 +03:00
if ( process . platform == 'win32' ) {
2021-01-15 10:57:52 +03:00
// Use _wexecve() equivalent to perform the update
2021-01-16 13:00:40 +03:00
windows _execve ( name , agentfilename , sessionid ) ;
2021-01-15 04:08:02 +03:00
}
2021-01-29 02:49:42 +03:00
else {
2021-01-17 22:54:23 +03:00
var m = require ( 'fs' ) . statSync ( process . execPath ) . mode ;
require ( 'fs' ) . chmodSync ( process . cwd ( ) + agentfilename + '.update' , m ) ;
2021-01-15 04:08:02 +03:00
// remove binary
require ( 'fs' ) . unlinkSync ( process . execPath ) ;
// copy update
require ( 'fs' ) . copyFileSync ( process . cwd ( ) + agentfilename + '.update' , process . execPath ) ;
2021-01-18 02:11:56 +03:00
require ( 'fs' ) . chmodSync ( process . execPath , m ) ;
2021-01-15 04:08:02 +03:00
// erase update
require ( 'fs' ) . unlinkSync ( process . cwd ( ) + agentfilename + '.update' ) ;
2021-01-29 02:49:42 +03:00
switch ( process . platform ) {
2021-01-19 03:05:28 +03:00
case 'freebsd' :
bsd _execv ( name , agentfilename , sessionid ) ;
break ;
case 'linux' :
linux _execv ( name , agentfilename , sessionid ) ;
break ;
default :
2021-01-29 02:49:42 +03:00
try {
2021-01-19 03:05:28 +03:00
// restart service
var s = require ( 'service-manager' ) . manager . getService ( name ) ;
s . restart ( ) ;
}
2021-01-29 02:49:42 +03:00
catch ( zz ) {
2021-01-19 03:05:28 +03:00
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 04:08:02 +03:00
}
}
} ) ;
img . pipe ( this . _file ) ;
img . pipe ( this . _filehash ) ;
} ) ;
}
}
}
2019-01-23 04:47:51 +03:00
// Return p number of spaces
function addPad ( p , ret ) { var r = '' ; for ( var i = 0 ; i < p ; i ++ ) { r += ret ; } return r ; }
2020-01-08 00:56:26 +03:00
setInterval ( function ( ) { sendConsoleText ( 'Timer!' ) ; } , 2000 ) ;
2019-01-23 04:47:51 +03:00
var path =
{
2020-01-08 00:56:26 +03:00
join : function ( ) {
2019-01-23 04:47:51 +03:00
var x = [ ] ;
2020-01-08 00:56:26 +03:00
for ( var i in arguments ) {
2019-01-23 04:47:51 +03:00
var w = arguments [ i ] ;
2020-01-08 00:56:26 +03:00
if ( w != null ) {
2019-01-23 04:47:51 +03:00
while ( w . endsWith ( '/' ) || w . endsWith ( '\\' ) ) { w = w . substring ( 0 , w . length - 1 ) ; }
2020-01-08 00:56:26 +03:00
if ( i != 0 ) { while ( w . startsWith ( '/' ) || w . startsWith ( '\\' ) ) { w = w . substring ( 1 ) ; } }
2019-01-23 04:47:51 +03:00
x . push ( w ) ;
}
}
if ( x . length == 0 ) return '/' ;
return x . join ( '/' ) ;
}
} ;
// 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-01-22 01:05:50 +03:00
// Split a string taking into account the quoats. Used for command line parsing
2020-01-08 00:56:26 +03:00
function splitArgs ( str ) {
2019-01-22 01:05:50 +03:00
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 ;
}
// Parse arguments string array into an object
2021-01-19 03:11:15 +03:00
function parseArgs ( argv ) {
2019-01-22 01:05:50 +03:00
var results = { '_' : [ ] } , current = null ;
2021-01-19 03:11:15 +03:00
for ( var i = 1 , len = argv . length ; i < len ; i ++ ) {
2019-01-22 01:05:50 +03:00
var x = argv [ i ] ;
2021-01-19 03:11:15 +03:00
if ( x . length > 2 && x [ 0 ] == '-' && x [ 1 ] == '-' ) {
2019-01-22 01:05:50 +03:00
if ( current != null ) { results [ current ] = true ; }
current = x . substring ( 2 ) ;
2021-01-19 03:11:15 +03:00
} else {
2019-01-22 01:05:50 +03:00
if ( current != null ) { results [ current ] = toNumberIfNumber ( x ) ; current = null ; } else { results [ '_' ] . push ( toNumberIfNumber ( x ) ) ; }
}
}
if ( current != null ) { results [ current ] = true ; }
return results ;
}
2021-01-18 06:38:42 +03:00
2019-01-22 01:05:50 +03:00
// Get server target url with a custom path
2020-01-08 00:56:26 +03:00
function getServerTargetUrl ( path ) {
2019-01-22 01:05:50 +03:00
var x = require ( 'MeshAgent' ) . 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 ;
}
// Get server url. If the url starts with "*/..." change it, it not use the url as is.
2020-01-08 00:56:26 +03:00
function getServerTargetUrlEx ( url ) {
2019-01-22 01:05:50 +03:00
if ( url . substring ( 0 , 2 ) == '*/' ) { return getServerTargetUrl ( url . substring ( 2 ) ) ; }
return url ;
}
2020-01-08 00:56:26 +03:00
require ( 'MeshAgent' ) . on ( 'Connected' , function ( ) {
require ( 'os' ) . name ( ) . then ( function ( v ) {
2021-01-15 07:27:15 +03:00
//sendConsoleText("Mesh Agent Recovery Console, OS: " + v);
2019-01-22 01:05:50 +03:00
require ( 'MeshAgent' ) . SendCommand ( meshCoreObj ) ;
} ) ;
} ) ;
2020-04-23 12:15:04 +03:00
// Called when receiving control data on websocket
2021-01-19 03:11:15 +03:00
function onTunnelControlData ( data , ws ) {
2020-04-23 12:15:04 +03:00
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));
2021-01-19 03:11:15 +03:00
if ( obj . action ) {
switch ( obj . action ) {
2020-04-23 12:15:04 +03:00
case 'lock' : {
// Lock the current user out of the desktop
2021-01-19 03:11:15 +03:00
try {
if ( process . platform == 'win32' ) {
2020-04-23 12:15:04 +03:00
MeshServerLog ( "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 ;
}
default :
// Unknown action, ignore it.
break ;
}
return ;
}
2021-01-19 03:11:15 +03:00
switch ( obj . type ) {
2020-04-23 12:15:04 +03:00
case 'options' : {
// These are additional connection options passed in the control channel.
//sendConsoleText('options: ' + JSON.stringify(obj));
delete obj . type ;
ws . httprequest . xoptions = obj ;
// Set additional user consent options if present
if ( ( obj != null ) && ( typeof obj . consent == 'number' ) ) { ws . httprequest . consent |= obj . consent ; }
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
2021-01-19 03:11:15 +03:00
if ( process . platform == 'win32' ) {
2020-04-23 12:15:04 +03:00
if ( ws . httprequest . _dispatcher == null ) return ;
if ( ws . httprequest . _dispatcher . invoke ) { ws . httprequest . _dispatcher . invoke ( 'resizeTerminal' , [ obj . cols , obj . rows ] ) ; }
}
2021-01-19 03:11:15 +03:00
else {
2020-04-23 12:15:04 +03:00
if ( ws . httprequest . process == null || ws . httprequest . process . pty == 0 ) return ;
if ( ws . httprequest . process . tcsetsize ) { ws . httprequest . process . tcsetsize ( obj . rows , obj . cols ) ; }
}
break ;
}
}
}
2021-01-19 03:11:15 +03:00
require ( 'MeshAgent' ) . AddCommandHandler ( function ( data ) {
if ( typeof data == 'object' ) {
2019-01-22 01:05:50 +03:00
// If this is a console command, parse it and call the console handler
2021-01-19 03:11:15 +03:00
switch ( data . action ) {
2021-01-15 04:08:02 +03:00
case 'agentupdate' :
2021-01-17 10:35:39 +03:00
agentUpdate _Start ( data . url , { hash : data . hash , tlshash : data . servertlshash , sessionid : data . sessionid } ) ;
2021-01-15 04:08:02 +03:00
break ;
2019-01-22 01:05:50 +03:00
case 'msg' :
{
2021-01-19 03:11:15 +03:00
switch ( data . type ) {
2020-01-08 00:56:26 +03:00
case 'console' : { // Process a console command
2021-01-19 03:11:15 +03:00
if ( data . value && data . sessionid ) {
2020-01-08 00:56:26 +03:00
var args = splitArgs ( data . value ) ;
processConsoleCommand ( args [ 0 ] . toLowerCase ( ) , parseArgs ( args ) , data . rights , data . sessionid ) ;
}
break ;
2019-01-22 01:05:50 +03:00
}
case 'tunnel' :
{
2021-01-19 03:11:15 +03:00
if ( data . value != null ) { // Process a new tunnel connection request
2020-01-08 00:56:26 +03:00
// Create a new tunnel object
2021-01-19 03:11:15 +03:00
if ( data . rights != 4294967295 ) {
2021-01-18 05:36:53 +03:00
MeshServerLog ( 'Tunnel Error: RecoveryCore requires admin rights for tunnels' ) ;
break ;
}
2020-01-08 00:56:26 +03:00
var xurl = getServerTargetUrlEx ( data . value ) ;
2021-01-19 03:11:15 +03:00
if ( xurl != null ) {
2020-01-08 00:56:26 +03:00
var woptions = http . parseUri ( xurl ) ;
woptions . rejectUnauthorized = 0 ;
2021-01-18 05:36:53 +03:00
woptions . perMessageDeflate = false ;
2021-01-19 03:11:15 +03:00
woptions . checkServerIdentity = function checkServerIdentity ( certs ) {
2021-01-18 05:36:53 +03:00
// 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' ) }
}
woptions . checkServerIdentity . servertlshash = data . servertlshash ;
2020-01-08 00:56:26 +03:00
//sendConsoleText(JSON.stringify(woptions));
var tunnel = http . request ( woptions ) ;
2021-01-19 03:11:15 +03:00
tunnel . on ( 'upgrade' , function ( response , s , head ) {
if ( require ( 'MeshAgent' ) . idleTimeout != null ) {
2021-01-18 05:36:53 +03:00
s . setTimeout ( require ( 'MeshAgent' ) . idleTimeout * 1000 ) ;
2021-01-19 03:11:15 +03:00
s . on ( 'timeout' , function ( ) {
2021-01-18 05:36:53 +03:00
this . ping ( ) ;
this . setTimeout ( require ( 'MeshAgent' ) . idleTimeout * 1000 ) ;
} ) ;
}
2020-01-08 00:56:26 +03:00
this . s = s ;
s . httprequest = this ;
s . tunnel = this ;
2021-01-19 03:11:15 +03:00
s . on ( 'end' , function ( ) {
2020-01-08 00:56:26 +03:00
if ( tunnels [ this . httprequest . index ] == null ) return ; // Stop duplicate calls.
2019-01-23 04:47:51 +03:00
2020-01-08 00:56:26 +03:00
// If there is a upload or download active on this connection, close the file
2021-01-18 05:36:53 +03:00
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 ; }
2019-01-23 04:47:51 +03:00
2020-01-08 00:56:26 +03:00
//sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
delete tunnels [ this . httprequest . index ] ;
2019-01-22 01:05:50 +03:00
2020-01-08 00:56:26 +03:00
// Clean up WebSocket
this . removeAllListeners ( 'data' ) ;
} ) ;
2021-01-19 03:11:15 +03:00
s . on ( 'data' , function ( data ) {
2020-01-08 00:56:26 +03:00
// If this is upload data, save it to file
2021-01-19 03:11:15 +03:00
if ( ( this . httprequest . uploadFile ) && ( typeof data == 'object' ) && ( data [ 0 ] != 123 ) ) {
2021-01-18 05:36:53 +03:00
// Save the data to file being uploaded.
2021-01-19 03:11:15 +03:00
if ( data [ 0 ] == 0 ) {
2021-01-18 05:36:53 +03:00
// 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.
2021-01-19 03:11:15 +03:00
} else {
2021-01-18 05:36:53 +03:00
// 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.
}
this . write ( Buffer . from ( JSON . stringify ( { action : 'uploadack' , reqid : this . httprequest . uploadFileid } ) ) ) ; // Ask for more data.
2020-01-08 00:56:26 +03:00
return ;
}
2019-01-23 04:47:51 +03:00
2021-01-19 03:11:15 +03:00
if ( this . httprequest . state == 0 ) {
2020-01-08 00:56:26 +03:00
// Check if this is a relay connection
2021-01-18 05:36:53 +03:00
if ( ( data == 'c' ) || ( data == 'cr' ) ) { this . httprequest . state = 1 ; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
}
2021-01-19 03:11:15 +03:00
else {
2020-01-08 00:56:26 +03:00
// Handle tunnel data
2021-01-19 03:11:15 +03:00
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
2020-01-08 00:56:26 +03:00
// Take a look at the protocol
2021-01-18 05:36:53 +03:00
if ( ( data . length > 3 ) && ( data [ 0 ] == '{' ) ) { onTunnelControlData ( data , this ) ; return ; }
2020-01-08 00:56:26 +03:00
this . httprequest . protocol = parseInt ( data ) ;
if ( typeof this . httprequest . protocol != 'number' ) { this . httprequest . protocol = 0 ; }
2021-01-19 03:11:15 +03:00
if ( this . httprequest . protocol == 10 ) {
2021-01-18 05:36:53 +03:00
//
// 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 ) { }
2021-01-19 03:11:15 +03:00
if ( this . httprequest . downloadFile ) {
2021-01-18 05:36:53 +03:00
//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 ( ) { }
2021-01-19 03:11:15 +03:00
} else {
2021-01-18 05:36:53 +03:00
//sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
this . write ( JSON . stringify ( { op : 'cancel' } ) ) ;
}
}
2021-01-19 03:11:15 +03:00
else if ( ( this . httprequest . protocol == 1 ) || ( this . httprequest . protocol == 6 ) || ( this . httprequest . protocol == 8 ) || ( this . httprequest . protocol == 9 ) ) {
2021-01-18 05:36:53 +03:00
//
// Remote Terminal
//
2021-01-19 03:11:15 +03:00
if ( process . platform == "win32" ) {
2020-04-23 12:15:04 +03:00
var cols = 80 , rows = 25 ;
2021-01-19 03:11:15 +03:00
if ( this . httprequest . xoptions ) {
2020-04-23 12:15:04 +03:00
if ( this . httprequest . xoptions . rows ) { rows = this . httprequest . xoptions . rows ; }
if ( this . httprequest . xoptions . cols ) { cols = this . httprequest . xoptions . cols ; }
}
// Admin Terminal
2021-01-19 03:11:15 +03:00
if ( require ( 'win-virtual-terminal' ) . supported ) {
2020-04-23 12:15:04 +03:00
// 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 : 'Start' , args : [ cols , rows ] } } ) ;
this . httprequest . _dispatcher . ws = this ;
2021-01-19 03:11:15 +03:00
this . httprequest . _dispatcher . on ( 'connection' , function ( c ) {
2020-04-23 12:15:04 +03:00
this . ws . _term = c ;
c . pipe ( this . ws , { dataTypeSkip : 1 } ) ;
this . ws . pipe ( c , { dataTypeSkip : 1 } ) ;
} ) ;
}
2021-01-19 03:11:15 +03:00
else {
2020-04-23 12:15:04 +03:00
// Legacy Terminal
this . httprequest . _term = require ( 'win-terminal' ) . Start ( 80 , 25 ) ;
this . httprequest . _term . pipe ( this , { dataTypeSkip : 1 } ) ;
this . pipe ( this . httprequest . _term , { dataTypeSkip : 1 , end : false } ) ;
this . prependListener ( 'end' , function ( ) { this . httprequest . _term . end ( function ( ) { sendConsoleText ( 'Terminal was closed' ) ; } ) ; } ) ;
}
2020-01-08 00:56:26 +03:00
}
2021-01-19 03:11:15 +03:00
else {
2020-04-23 12:15:04 +03:00
var env = { HISTCONTROL : 'ignoreboth' } ;
2021-01-19 03:11:15 +03:00
if ( this . httprequest . xoptions ) {
2020-04-23 12:15:04 +03:00
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 , env : env } ;
2020-04-23 19:50:55 +03:00
2021-01-19 03:11:15 +03:00
if ( require ( 'fs' ) . existsSync ( '/bin/bash' ) ) {
2020-04-23 19:50:55 +03:00
this . httprequest . process = childProcess . execFile ( '/bin/bash' , [ 'bash' ] , options ) ; // Start bash
}
2021-01-19 03:11:15 +03:00
else {
2020-04-23 19:50:55 +03:00
this . httprequest . process = childProcess . execFile ( '/bin/sh' , [ 'sh' ] , options ) ; // Start sh
}
2020-04-23 12:15:04 +03:00
// Spaces at the beginning of lines are needed to hide commands from the command history
if ( process . platform == 'linux' ) { this . httprequest . process . stdin . write ( ' alias ls=\'ls --color=auto\';clear\n' ) ; }
2020-01-08 00:56:26 +03:00
this . httprequest . process . tunnel = this ;
this . httprequest . process . on ( 'exit' , function ( ecode , sig ) { this . tunnel . end ( ) ; } ) ;
this . httprequest . process . stderr . on ( 'data' , function ( chunk ) { this . parent . tunnel . write ( chunk ) ; } ) ;
this . httprequest . process . stdout . pipe ( this , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
this . pipe ( this . httprequest . process . stdin , { dataTypeSkip : 1 , end : false } ) ; // 0 = Binary, 1 = Text.
this . prependListener ( 'end' , function ( ) { this . httprequest . process . kill ( ) ; } ) ;
}
2019-01-22 01:05:50 +03:00
}
2020-01-08 00:56:26 +03:00
}
2021-01-19 03:11:15 +03:00
else if ( this . httprequest . protocol == 5 ) {
2020-01-08 00:56:26 +03:00
// 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 ) ) ) { return ; } // If this is control data, handle it now.
if ( cmd . action == undefined ) { return ; }
console . log ( 'action: ' , cmd . action ) ;
2019-01-23 04:47:51 +03:00
2020-01-08 00:56:26 +03:00
//sendConsoleText('CMD: ' + JSON.stringify(cmd));
2019-01-23 04:47:51 +03:00
2020-01-08 00:56:26 +03:00
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, ' '));
2021-01-19 03:11:15 +03:00
switch ( cmd . action ) {
2020-01-08 00:56:26 +03:00
case 'ls' :
// Send the folder content to the browser
var response = getDirectoryInfo ( cmd . path ) ;
if ( cmd . reqid != undefined ) { response . reqid = cmd . reqid ; }
2020-08-06 23:23:17 +03:00
this . write ( Buffer . from ( JSON . stringify ( response ) ) ) ;
2020-01-08 00:56:26 +03:00
break ;
2021-01-18 05:36:53 +03:00
case 'mkdir' :
2021-01-15 04:08:02 +03:00
{
2021-01-18 05:36:53 +03:00
// Create a new empty folder
fs . mkdirSync ( cmd . path ) ;
break ;
}
case 'rm' :
{
// Delete, possibly recursive delete
2021-01-19 03:11:15 +03:00
for ( var i in cmd . delfiles ) {
2021-01-18 05:36:53 +03:00
try { deleteFolderRecursive ( path . join ( cmd . path , cmd . delfiles [ i ] ) , cmd . rec ) ; } catch ( e ) { }
}
break ;
}
case 'rename' :
{
// Rename a file or folder
var oldfullpath = path . join ( cmd . path , cmd . oldname ) ;
var newfullpath = path . join ( cmd . path , cmd . newname ) ;
try { fs . renameSync ( oldfullpath , newfullpath ) ; } catch ( e ) { console . log ( e ) ; }
break ;
}
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 ) { } } ) ;
break ;
}
case 'cancelfindfile' :
{
if ( this . _search ) { this . _search . cancel ( ) ; this . _search = null ; }
break ;
}
case 'download' :
{
// Download a file
var sendNextBlock = 0 ;
2021-01-19 03:11:15 +03:00
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' ) {
2021-01-18 05:36:53 +03:00
if ( fs . existsSync ( process . coreDumpLocation ) ) { cmd . path = process . coreDumpLocation ; }
2021-01-19 03:11:15 +03:00
} else {
2021-01-18 05:36:53 +03:00
if ( ( process . cwd ( ) != '//' ) && fs . existsSync ( process . cwd ( ) + 'core' ) ) { cmd . path = process . cwd ( ) + 'core' ; }
}
}
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 } ) ; }
2021-01-19 03:11:15 +03:00
} else if ( ( this . filedownload != null ) && ( cmd . id == this . filedownload . id ) ) { // Download commands
2021-01-18 05:36:53 +03:00
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 ; }
}
// Send the next download block(s)
2021-01-19 03:11:15 +03:00
while ( sendNextBlock > 0 ) {
2021-01-18 05:36:53 +03: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
}
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 ? pathjoin ( 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
2021-01-19 03:11:15 +03:00
if ( this . httprequest . uploadFile ) {
2021-01-18 05:36:53 +03: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 ;
}
break ;
}
case 'uploadcancel' :
{
// Indicates that an upload is canceled
2021-01-19 03:11:15 +03:00
if ( this . httprequest . uploadFile ) {
2021-01-18 05:36:53 +03: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 ;
}
break ;
2020-01-08 00:56:26 +03:00
}
case 'copy' : {
// Copy a bunch of files from scpath to dspath
2021-01-19 03:11:15 +03:00
for ( var i in cmd . names ) {
2020-01-08 00:56:26 +03:00
var sc = path . join ( cmd . scpath , cmd . names [ i ] ) , ds = path . join ( cmd . dspath , cmd . names [ i ] ) ;
if ( sc != ds ) { try { fs . copyFileSync ( sc , ds ) ; } catch ( e ) { } }
}
break ;
}
case 'move' : {
// Move a bunch of files from scpath to dspath
2021-01-19 03:11:15 +03:00
for ( var i in cmd . names ) {
2020-01-08 00:56:26 +03:00
var sc = path . join ( cmd . scpath , cmd . names [ i ] ) , ds = path . join ( cmd . dspath , cmd . names [ i ] ) ;
if ( sc != ds ) { try { fs . copyFileSync ( sc , ds ) ; fs . unlinkSync ( sc ) ; } catch ( e ) { } }
}
break ;
}
2019-01-23 04:47:51 +03:00
}
}
}
2020-01-08 00:56:26 +03:00
} ) ;
} ) ;
tunnel . onerror = function ( e ) { sendConsoleText ( "ERROR: " + JSON . stringify ( e ) ) ; }
tunnel . sessionid = data . sessionid ;
tunnel . rights = data . rights ;
tunnel . state = 0 ;
tunnel . url = xurl ;
tunnel . protocol = 0 ;
tunnel . tcpaddr = data . tcpaddr ;
tunnel . tcpport = data . tcpport ;
tunnel . end ( ) ;
// Put the tunnel in the tunnels list
var index = nextTunnelIndex ++ ;
tunnel . index = index ;
tunnels [ index ] = tunnel ;
2019-01-22 01:05:50 +03:00
2020-01-08 00:56:26 +03:00
//sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
}
}
break ;
2019-01-22 01:05:50 +03:00
}
2020-01-08 00:56:26 +03:00
default :
// Unknown action, ignore it.
break ;
}
break ;
2019-01-22 01:05:50 +03:00
}
default :
// Unknown action, ignore it.
break ;
}
}
} ) ;
2020-01-08 00:56:26 +03:00
function processConsoleCommand ( cmd , args , rights , sessionid ) {
try {
2019-01-22 01:05:50 +03:00
var response = null ;
2020-01-08 00:56:26 +03:00
switch ( cmd ) {
2019-01-22 01:05:50 +03:00
case 'help' :
2021-01-18 05:43:21 +03:00
response = "Available commands are: agentupdate, dbkeys, dbget, dbset, dbcompact, eval, netinfo, osinfo, setdebug, versions." ;
2021-01-15 04:08:02 +03:00
break ;
2021-01-27 11:43:58 +03:00
case '_descriptors' :
response = 'Open Descriptors: ' + JSON . stringify ( getOpenDescriptors ( ) ) ;
break ;
2021-01-15 04:08:02 +03:00
case 'versions' :
response = JSON . stringify ( process . versions , null , ' ' ) ;
break ;
case 'agentupdate' :
2021-01-15 07:27:15 +03:00
// Request that the server send a agent update command
2021-01-17 10:35:39 +03:00
require ( 'MeshAgent' ) . SendCommand ( { action : 'agentupdate' , sessionid : sessionid } ) ;
2021-01-15 07:27:15 +03:00
break ;
case 'agentupdateex' :
// Perform an direct agent update without requesting any information from the server, this should not typically be used.
2021-01-17 10:35:39 +03:00
agentUpdate _Start ( null , { sessionid : sessionid } ) ;
2019-01-22 01:05:50 +03:00
break ;
2021-01-18 05:43:21 +03:00
case 'eval' :
{ // Eval JavaScript
2021-01-19 03:11:15 +03:00
if ( args [ '_' ] . length < 1 ) {
2021-01-18 05:43:21 +03:00
response = 'Proper usage: eval "JavaScript code"' ; // Display correct command usage
2021-01-19 03:11:15 +03:00
} else {
2021-01-18 05:59:21 +03:00
response = JSON . stringify ( require ( 'MeshAgent' ) . eval ( args [ '_' ] [ 0 ] ) ) ; // This can only be run by trusted administrator.
2021-01-18 05:43:21 +03: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 ;
}
2019-01-22 01:05:50 +03:00
case 'osinfo' : { // Return the operating system information
var i = 1 ;
2020-04-23 12:15:04 +03:00
if ( args [ '_' ] . length > 0 ) { i = parseInt ( args [ '_' ] [ 0 ] ) ; if ( i > 8 ) { i = 8 ; } response = 'Calling ' + i + ' times.' ; }
2021-01-19 03:11:15 +03:00
for ( var j = 0 ; j < i ; j ++ ) {
2019-01-22 01:05:50 +03:00
var pr = require ( 'os' ) . name ( ) ;
pr . sessionid = sessionid ;
2021-01-19 03:11:15 +03:00
pr . then ( function ( v ) {
2020-04-23 12:15:04 +03:00
sendConsoleText ( "OS: " + v + ( process . platform == 'win32' ? ( require ( 'win-virtual-terminal' ) . supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]' ) : '' ) , this . sessionid ) ;
} ) ;
2019-01-22 01:05:50 +03: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
2020-01-08 00:56:26 +03:00
if ( db == null ) { response = "Database not accessible." ; break ; }
2019-01-22 01:05:50 +03:00
if ( args [ '_' ] . length != 1 ) {
2020-01-08 00:56:26 +03:00
response = "Proper usage: dbget (key)" ; // Display the value for a given database key
2019-01-22 01:05:50 +03:00
} else {
response = db . Get ( args [ '_' ] [ 0 ] ) ;
}
break ;
}
case 'dbset' : { // Set a data store key and value pair
2020-01-08 00:56:26 +03:00
if ( db == null ) { response = "Database not accessible." ; break ; }
2019-01-22 01:05:50 +03:00
if ( args [ '_' ] . length != 2 ) {
2020-01-08 00:56:26 +03:00
response = "Proper usage: dbset (key) (value)" ; // Set a database key
2019-01-22 01:05:50 +03:00
} else {
var r = db . Put ( args [ '_' ] [ 0 ] , args [ '_' ] [ 1 ] ) ;
2020-01-08 00:56:26 +03:00
response = "Key set: " + r ;
2019-01-22 01:05:50 +03:00
}
break ;
}
case 'dbcompact' : { // Compact the data store
2020-01-08 00:56:26 +03:00
if ( db == null ) { response = "Database not accessible." ; break ; }
2019-01-22 01:05:50 +03:00
var r = db . Compact ( ) ;
2020-01-08 00:56:26 +03:00
response = "Database compacted: " + r ;
2019-01-22 01:05:50 +03:00
break ;
}
case 'tunnels' : { // Show the list of current tunnels
response = '' ;
2020-01-08 00:56:26 +03:00
for ( var i in tunnels ) { response += "Tunnel #" + i + ", " + tunnels [ i ] . url + '\r\n' ; }
if ( response == '' ) { response = "No websocket sessions." ; }
2019-01-22 01:05:50 +03:00
break ;
}
case 'netinfo' : { // Show network interface information
//response = objToString(mesh.NetInfo, 0, ' ');
var interfaces = require ( 'os' ) . networkInterfaces ( ) ;
response = objToString ( interfaces , 0 , ' ' , true ) ;
break ;
}
default : { // This is an unknown command, return an error message
2020-12-24 07:03:44 +03:00
response = 'Unknown command \"' + cmd + '\", type \"help\" for list of available commands.' ;
2019-01-22 01:05:50 +03:00
break ;
}
}
2020-01-08 00:56:26 +03:00
} catch ( e ) { response = "Command returned an exception error: " + e ; console . log ( e ) ; }
2019-01-22 01:05:50 +03:00
if ( response != null ) { sendConsoleText ( response , sessionid ) ; }
}
2019-01-23 04:47:51 +03:00
// Get a formated response for a given directory path
2020-01-08 00:56:26 +03:00
function getDirectoryInfo ( reqpath ) {
2019-01-23 04:47:51 +03:00
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 = 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 = 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 } ) ;
}
}
}
}
}
}
return response ;
}
// Delete a directory with a files and directories within it
function deleteFolderRecursive ( path , rec ) {
if ( fs . existsSync ( path ) ) {
if ( rec == true ) {
fs . readdirSync ( path . join ( path , '*' ) ) . forEach ( function ( file , index ) {
var curPath = path . join ( path , file ) ;
if ( fs . statSync ( curPath ) . isDirectory ( ) ) { // recurse
deleteFolderRecursive ( curPath , true ) ;
} else { // delete file
fs . unlinkSync ( curPath ) ;
}
} ) ;
}
fs . unlinkSync ( path ) ;
}
} ;