2017-08-28 19:27:45 +03:00
/ *
2019-01-04 03:22:15 +03:00
Copyright 2018 - 2019 Intel Corporation
2017-08-28 19:27:45 +03: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-17 04:30:34 +03:00
http : //www.apache.org/licenses/LICENSE-2.0
2017-08-28 19:27:45 +03: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 .
* /
2018-09-20 21:45:12 +03:00
process . on ( 'uncaughtException' , function ( ex ) {
require ( 'MeshAgent' ) . SendCommand ( { "action" : "msg" , "type" : "console" , "value" : "uncaughtException1: " + ex } ) ;
} ) ;
2018-11-28 21:50:25 +03:00
// NOTE: This seems to cause big problems, don't enable the debugger in the server's meshcore.
2018-11-27 04:12:27 +03:00
//attachDebugger({ webport: 9999, wait: 1 }).then(function (prt) { console.log('Point Browser for Debug to port: ' + prt); });
2018-09-20 21:45:12 +03:00
2018-11-28 04:13:01 +03:00
// Mesh Rights
2018-12-30 02:24:33 +03: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 22:47:58 +03:00
var MESHRIGHT _NOTERMINAL = 512 ;
var MESHRIGHT _NOFILES = 1024 ;
var MESHRIGHT _NOAMT = 2048 ;
2019-03-10 01:28:08 +03:00
var MESHRIGHT _LIMITEDINPUT = 4096 ;
2018-11-28 04:13:01 +03:00
2018-09-01 01:23:42 +03:00
function createMeshCore ( agent ) {
var obj = { } ;
2018-09-15 03:42:39 +03:00
2019-05-14 00:27:40 +03: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' ] ) ;
child . stdout . str = '' ;
child . stdout . on ( 'data' , function ( chunk ) { this . str += chunk . toString ( ) ; } ) ;
child . stdin . write ( "launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n" ) ;
child . waitExit ( ) ;
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" ) ;
2019-05-14 00:39:12 +03:00
fix += ( " child.stdin.write('rm " + process . cwd ( ) + "/macosRepair.js\\n');\n" ) ;
2019-05-14 00:27:40 +03:00
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 ( ) ;
}
}
}
}
2018-09-20 21:45:12 +03:00
/ *
2018-09-01 01:23:42 +03:00
function borderController ( ) {
this . container = null ;
this . Start = function Start ( user ) {
if ( this . container == null ) {
if ( process . platform == 'win32' ) {
2018-09-22 02:34:35 +03:00
try {
this . container = require ( 'ScriptContainer' ) . Create ( { processIsolation : 1 , sessionId : user . SessionId } ) ;
} catch ( ex ) {
this . container = require ( 'ScriptContainer' ) . Create ( { processIsolation : 1 } ) ;
}
} else {
2018-09-01 01:23:42 +03:00
this . container = require ( 'ScriptContainer' ) . Create ( { processIsolation : 1 , sessionId : user . uid } ) ;
}
this . container . parent = this ;
this . container . addModule ( 'monitor-info' , getJSModule ( 'monitor-info' ) ) ;
this . container . addModule ( 'monitor-border' , getJSModule ( 'monitor-border' ) ) ;
this . container . addModule ( 'promise' , getJSModule ( 'promise' ) ) ;
this . container . once ( 'exit' , function ( code ) { sendConsoleText ( 'Border Process Exited with code: ' + code ) ; this . parent . container = this . parent . _container = null ; } ) ;
this . container . ExecuteString ( "var border = require('monitor-border'); border.Start();" ) ;
2018-08-30 04:47:22 +03:00
}
}
2018-09-01 01:23:42 +03:00
this . Stop = function Stop ( ) {
if ( this . container != null ) {
this . _container = this . container ;
this . _container . parent = this ;
this . container = null ;
this . _container . exit ( ) ;
}
2018-08-30 04:47:22 +03:00
}
}
2018-09-22 02:34:35 +03:00
obj . borderManager = new borderController ( ) ;
2018-09-20 21:45:12 +03:00
* /
2018-08-30 04:47:22 +03:00
2017-08-28 19:27:45 +03:00
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
2019-02-26 01:35:08 +03:00
var meshCoreObj = { "action" : "coreinfo" , "value" : "MeshCore v6" , "caps" : 14 } ; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
2018-11-25 23:59:42 +03:00
// Get the operating system description string
try { require ( 'os' ) . name ( ) . then ( function ( v ) { meshCoreObj . osdesc = v ; } ) ; } catch ( ex ) { }
2018-08-30 04:47:22 +03:00
2017-08-28 19:27:45 +03:00
var meshServerConnectionState = 0 ;
var tunnels = { } ;
2018-09-22 02:34:35 +03:00
var lastMeInfo = null ;
2017-08-28 19:27:45 +03:00
var lastNetworkInfo = null ;
2017-08-28 22:48:53 +03:00
var lastPublicLocationInfo = null ;
2017-08-28 19:27:45 +03:00
var selfInfoUpdateTimer = null ;
var http = require ( 'http' ) ;
2017-10-24 00:09:58 +03:00
var net = require ( 'net' ) ;
2017-08-28 19:27:45 +03:00
var fs = require ( 'fs' ) ;
2017-10-15 09:22:19 +03:00
var rtc = require ( 'ILibWebRTC' ) ;
2019-03-05 10:48:45 +03:00
var amt = null ;
2018-04-11 23:49:05 +03:00
var processManager = require ( 'process-manager' ) ;
2017-10-05 06:35:52 +03:00
var wifiScannerLib = null ;
var wifiScanner = null ;
2017-12-13 03:04:54 +03:00
var networkMonitor = null ;
2018-01-10 07:13:41 +03:00
var amtscanner = null ;
2018-01-19 02:43:43 +03:00
var nextTunnelIndex = 1 ;
2019-02-08 02:00:10 +03:00
var amtPolicy = null ;
2018-09-28 02:17:05 +03:00
// If we are running in Duktape, agent will be null
if ( agent == null ) {
// Running in native agent, 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
2018-11-25 23:59:42 +03:00
try { if ( ( process . platform == 'win32' ) || ( process . platform == 'darwin' ) || ( require ( 'monitor-info' ) . kvm _x11 _support ) ) { meshCoreObj . caps |= 1 ; } } catch ( ex ) { }
2018-09-28 02:17:05 +03:00
}
} else {
// Running in nodejs
2018-11-25 23:59:42 +03:00
meshCoreObj . value += '-NodeJS' ;
meshCoreObj . caps = 8 ;
2018-09-28 02:17:05 +03:00
mesh = agent . getMeshApi ( ) ;
}
2018-01-10 07:13:41 +03:00
/ *
var AMTScanner = require ( "AMTScanner" ) ;
var scan = new AMTScanner ( ) ;
scan . on ( "found" , function ( data ) {
if ( typeof data === 'string' ) {
console . log ( data ) ;
} else {
console . log ( JSON . stringify ( data , null , " " ) ) ;
}
} ) ;
scan . scan ( "10.2.55.140" , 1000 ) ;
scan . scan ( "10.2.55.139-10.2.55.145" , 1000 ) ;
scan . scan ( "10.2.55.128/25" , 2000 ) ;
* /
2018-01-17 04:30:34 +03:00
/ *
2017-12-13 03:04:54 +03: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 ; }
2018-01-17 04:30:34 +03:00
* /
2018-01-10 07:13:41 +03:00
// Try to load up the Intel AMT scanner
try {
var AMTScannerModule = require ( 'amt-scanner' ) ;
amtscanner = new AMTScannerModule ( ) ;
//amtscanner.on('found', function (data) { if (typeof data != 'string') { data = JSON.stringify(data, null, " "); } sendConsoleText(data); });
2018-09-22 02:34:35 +03:00
} catch ( ex ) { amtscanner = null ; }
2018-09-28 02:17:05 +03:00
// Fetch the SMBios Tables
var SMBiosTables = null ;
var SMBiosTablesRaw = null ;
2018-11-25 23:59:42 +03:00
try {
2018-12-31 00:32:40 +03:00
var SMBiosModule = null ;
try { SMBiosModule = require ( 'smbios' ) ; } catch ( ex ) { }
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 } ) ; }
// If SMBios tables say that AMT is present, try to connect MEI
2019-03-05 10:48:45 +03:00
if ( SMBiosTables . amtInfo && ( SMBiosTables . amtInfo . AMT == true ) ) {
var amtmodule = require ( 'amt-manage' ) ;
amt = new amtmodule ( mesh , db , true ) ;
amt . onStateChange = function ( state ) { if ( state == 2 ) { sendPeriodicServerUpdate ( 1 ) ; } }
if ( amtPolicy != null ) { amt . setPolicy ( amtPolicy ) ; }
amt . start ( ) ;
}
2018-12-31 00:32:40 +03:00
}
} ) ;
}
2019-03-05 10:48:45 +03:00
} catch ( ex ) { sendConsoleText ( "ex1: " + ex ) ; }
2018-01-04 23:15:21 +03:00
2017-10-05 06:35:52 +03:00
// Try to load up the WIFI scanner
try {
2018-01-31 05:23:57 +03:00
var wifiScannerLib = require ( 'wifi-scanner' ) ;
2017-10-05 06:35:52 +03:00
wifiScanner = new wifiScannerLib ( ) ;
2019-03-05 10:48:45 +03:00
wifiScanner . on ( 'accessPoint' , function ( data ) { sendConsoleText ( "wifiScanner: " + data ) ; } ) ;
2018-09-22 02:34:35 +03:00
} catch ( ex ) { wifiScannerLib = null ; wifiScanner = null ; }
2018-12-28 08:31:20 +03:00
2017-08-28 22:48:53 +03:00
// Get our location (lat/long) using our public IP address
2017-09-01 21:23:22 +03:00
var getIpLocationDataExInProgress = false ;
2017-10-24 00:09:58 +03:00
var getIpLocationDataExCounts = [ 0 , 0 ] ;
2017-08-28 22:48:53 +03:00
function getIpLocationDataEx ( func ) {
2017-09-01 21:23:22 +03:00
if ( getIpLocationDataExInProgress == true ) { return false ; }
2017-08-28 22:48:53 +03:00
try {
2017-09-01 21:23:22 +03:00
getIpLocationDataExInProgress = true ;
getIpLocationDataExCounts [ 0 ] ++ ;
2018-07-24 03:34:24 +03:00
var options = http . parseUri ( "http://ipinfo.io/json" ) ;
options . method = 'GET' ;
http . request ( options , function ( resp ) {
2018-01-04 23:15:21 +03:00
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 ( ) ;
2017-09-01 21:23:22 +03:00
return true ;
2017-08-28 22:48:53 +03:00
}
2017-09-01 21:23:22 +03:00
catch ( e ) { return false ; }
2017-08-28 22:48:53 +03:00
}
2018-01-04 23:15:21 +03:00
2017-08-28 22:48:53 +03: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 ) ;
}
2018-01-04 23:15:21 +03:00
2017-08-28 22:48:53 +03:00
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 ) {
2017-09-02 03:34:02 +03:00
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
}
2017-08-28 22:48:53 +03:00
} ) ;
} else {
// Check the cache
if ( clearGatewayMac ( publicLocationInfo . netInfoStr ) == clearGatewayMac ( lastNetworkInfo ) ) {
// Cache match
if ( func ) func ( publicLocationInfo . locationData ) ;
} else {
// Cache mismatch
getIpLocationDataEx ( function ( locationData ) {
2017-09-02 03:34:02 +03:00
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 ( publicLocationInfo . locationData ) ; // Can't get new location, report the old location
}
2017-08-28 22:48:53 +03:00
} ) ;
}
}
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03: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 ;
} ;
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// Polyfill path.join
obj . path = {
join : function ( ) {
var x = [ ] ;
for ( var i in arguments ) {
var w = arguments [ i ] ;
if ( w != null ) {
while ( w . endsWith ( '/' ) || w . endsWith ( '\\' ) ) { w = w . substring ( 0 , w . length - 1 ) ; }
2017-10-24 00:09:58 +03:00
if ( i != 0 ) {
while ( w . startsWith ( '/' ) || w . startsWith ( '\\' ) ) { w = w . substring ( 1 ) ; }
}
2017-08-28 19:27:45 +03:00
x . push ( w ) ;
}
}
if ( x . length == 0 ) return '/' ;
return x . join ( '/' ) ;
}
} ;
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// Replace a string with a number if the string is an exact number
2017-09-21 00:44:22 +03:00
function toNumberIfNumber ( x ) { if ( ( typeof x == 'string' ) && ( + parseInt ( x ) === x ) ) { x = parseInt ( x ) ; } return x ; }
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// Convert decimal to hex
function char2hex ( i ) { return ( i + 0x100 ) . toString ( 16 ) . substr ( - 2 ) . toUpperCase ( ) ; }
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03: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 ; }
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03: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 ; }
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03: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
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// Convert an object to string with all functions
2018-03-13 04:16:06 +03:00
function objToString ( x , p , pad , ret ) {
2017-08-28 19:27:45 +03:00
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' : ' ' ) ;
2018-03-13 04:16:06 +03:00
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 ) + '}' ;
2017-08-28 19:27:45 +03:00
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// Return p number of spaces
function addPad ( p , ret ) { var r = '' ; for ( var i = 0 ; i < p ; i ++ ) { r += ret ; } return r ; }
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03: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 ;
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03: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 ) ) ; }
}
}
if ( current != null ) { results [ current ] = true ; }
return results ;
}
2018-01-04 23:15:21 +03:00
2017-09-21 00:44:22 +03: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 ;
}
2018-01-04 23:15:21 +03:00
2017-09-21 00:44:22 +03: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 ;
}
2018-01-04 23:15:21 +03:00
2017-09-21 00:44:22 +03:00
// Send a wake-on-lan packet
function sendWakeOnLan ( hexMac ) {
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' ) ;
2018-01-04 23:15:21 +03:00
2017-09-21 00:44:22 +03:00
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' ) ) {
var socket = require ( 'dgram' ) . createSocket ( { type : "udp4" } ) ;
socket . bind ( { address : addr . address } ) ;
socket . setBroadcast ( true ) ;
socket . send ( magicbin , 7 , "255.255.255.255" ) ;
count ++ ;
}
}
}
}
} catch ( e ) { }
return count ;
2017-08-28 19:27:45 +03:00
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03: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
2017-08-28 22:48:53 +03:00
switch ( data . action ) {
case 'msg' : {
2018-04-11 23:49:05 +03:00
switch ( data . type ) {
case 'console' : { // Process a console command
if ( data . value && data . sessionid ) {
var args = splitArgs ( data . value ) ;
processConsoleCommand ( args [ 0 ] . toLowerCase ( ) , parseArgs ( args ) , data . rights , data . sessionid ) ;
}
break ;
2017-08-28 22:48:53 +03:00
}
2018-04-11 23:49:05 +03:00
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 ) {
var woptions = http . parseUri ( xurl ) ;
woptions . rejectUnauthorized = 0 ;
2018-12-01 08:23:10 +03:00
//sendConsoleText(JSON.stringify(woptions));
2019-05-07 22:39:49 +03:00
//sendConsoleText('TUNNEL: ' + JSON.stringify(data));
2018-04-11 23:49:05 +03:00
var tunnel = http . request ( woptions ) ;
tunnel . upgrade = onTunnelUpgrade ;
2019-03-02 04:13:11 +03:00
tunnel . on ( 'error' , function ( e ) { sendConsoleText ( 'ERROR: ' + JSON . stringify ( e ) ) ; } ) ;
2018-04-11 23:49:05 +03:00
tunnel . sessionid = data . sessionid ;
tunnel . rights = data . rights ;
2019-04-13 00:19:03 +03:00
tunnel . consent = data . consent ;
tunnel . username = data . username ;
2018-04-11 23:49:05 +03:00
tunnel . state = 0 ;
tunnel . url = xurl ;
tunnel . protocol = 0 ;
tunnel . tcpaddr = data . tcpaddr ;
tunnel . tcpport = data . tcpport ;
2019-05-05 08:53:57 +03:00
tunnel . udpaddr = data . udpaddr ;
tunnel . udpport = data . udpport ;
2018-04-11 23:49:05 +03:00
tunnel . end ( ) ;
// Put the tunnel in the tunnels list
2018-11-28 04:13:01 +03:00
var index = nextTunnelIndex ++ ;
2018-04-11 23:49:05 +03:00
tunnel . index = index ;
tunnels [ index ] = tunnel ;
2018-11-28 04:13:01 +03:00
//sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
2018-04-11 23:49:05 +03:00
}
}
break ;
}
case 'ps' : {
2018-12-08 03:36:27 +03:00
// Return the list of running processes
2018-04-11 23:49:05 +03:00
if ( data . sessionid ) {
processManager . getProcesses ( function ( plist ) { mesh . SendCommand ( { "action" : "msg" , "type" : "ps" , "value" : JSON . stringify ( plist ) , "sessionid" : data . sessionid } ) ; } ) ;
}
break ;
}
case 'pskill' : {
2018-12-08 03:36:27 +03:00
// Kill a process
if ( data . value ) {
2019-03-05 10:48:45 +03:00
try { process . kill ( data . value ) ; } catch ( e ) { sendConsoleText ( "pskill: " + JSON . stringify ( e ) ) ; }
2018-12-08 03:36:27 +03:00
}
break ;
}
case 'openUrl' : {
// Open a local web browser and return success/fail
sendConsoleText ( 'OpenURL: ' + data . url ) ;
if ( data . url ) { mesh . SendCommand ( { "action" : "msg" , "type" : "openUrl" , "url" : data . url , "sessionid" : data . sessionid , "success" : ( openUserDesktopUrl ( data . url ) != null ) } ) ; }
2018-04-11 23:49:05 +03:00
break ;
2017-08-28 22:48:53 +03:00
}
2019-02-17 08:16:39 +03:00
case 'getclip' : {
// Send the load clipboard back to the user
2019-03-19 19:54:52 +03:00
//sendConsoleText('getClip: ' + JSON.stringify(data));
2019-03-15 23:55:53 +03:00
if ( require ( 'MeshAgent' ) . isService )
{
require ( 'clipboard' ) . dispatchRead ( ) . then ( function ( str ) { mesh . SendCommand ( { "action" : "msg" , "type" : "getclip" , "sessionid" : data . sessionid , "data" : str } ) ; } ) ;
}
else
{
require ( "clipboard" ) . read ( ) . then ( function ( str ) { mesh . SendCommand ( { "action" : "msg" , "type" : "getclip" , "sessionid" : data . sessionid , "data" : str } ) ; } ) ;
}
2019-02-17 08:16:39 +03:00
break ;
}
case 'setclip' : {
// Set the load clipboard to a user value
2019-03-19 19:54:52 +03:00
//sendConsoleText('setClip: ' + JSON.stringify(data));
2019-03-15 23:55:53 +03:00
if ( typeof data . data == 'string' )
{
if ( require ( 'MeshAgent' ) . isService )
{
require ( 'clipboard' ) . dispatchWrite ( data . data ) ;
}
else
{
require ( "clipboard" ) ( data . data ) ; // Set the clipboard
}
2019-02-17 08:16:39 +03:00
mesh . SendCommand ( { "action" : "msg" , "type" : "setclip" , "sessionid" : data . sessionid , "success" : true } ) ;
}
break ;
}
2019-01-05 04:59:13 +03:00
default :
// Unknown action, ignore it.
break ;
2017-08-28 19:27:45 +03:00
}
2018-04-12 21:15:01 +03:00
break ;
2017-08-28 22:48:53 +03:00
}
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 ( ', ' ) ) ;
2017-09-21 00:44:22 +03:00
for ( var i in data . macs ) { sendWakeOnLan ( data . macs [ i ] ) ; }
2017-08-28 22:48:53 +03:00
break ;
}
2017-09-01 21:23:22 +03:00
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 ) ;
sendConsoleText ( 'Performing power action=' + data . actiontype + ', forced=' + forced + '.' ) ;
var r = mesh . ExecPowerState ( data . actiontype , forced ) ;
sendConsoleText ( 'ExecPowerState returned code: ' + r ) ;
}
break ;
}
2017-09-06 03:19:28 +03:00
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 } ) ; } ) ;
2017-08-28 22:48:53 +03:00
break ;
2017-08-28 19:27:45 +03:00
}
2018-04-20 04:19:15 +03:00
case 'toast' : {
// Display a toast message
2019-05-09 02:18:44 +03:00
if ( data . title && data . msg ) {
try { require ( 'toaster' ) . Toast ( data . title , data . msg ) ; } catch ( ex ) { }
}
2018-04-20 04:19:15 +03:00
break ;
}
2018-12-08 03:36:27 +03:00
case 'openUrl' : {
// Open a local web browser and return success/fail
sendConsoleText ( 'OpenURL: ' + data . url ) ;
if ( data . url ) { mesh . SendCommand ( { "action" : "openUrl" , "url" : data . url , "sessionid" : data . sessionid , "success" : ( openUserDesktopUrl ( data . url ) != null ) } ) ; }
break ;
}
2019-02-08 02:00:10 +03:00
case 'amtPolicy' : {
// Store the latest Intel AMT policy
amtPolicy = data . amtPolicy ;
if ( data . amtPolicy != null ) { db . Put ( 'amtPolicy' , JSON . stringify ( data . amtPolicy ) ) ; } else { db . Put ( 'amtPolicy' , null ) ; }
2019-03-05 10:48:45 +03:00
if ( amt != null ) { amt . setPolicy ( amtPolicy ) ; }
2019-02-08 02:00:10 +03:00
break ;
}
2019-02-08 07:06:01 +03:00
case 'getScript' : {
// Received a configuration script from the server
sendConsoleText ( 'getScript: ' + JSON . stringify ( data ) ) ;
break ;
}
2019-05-28 20:58:46 +03:00
case 'ping' : { mesh . SendCommand ( '{"action":"pong"}' ) ; break ; }
case 'pong' : { break ; }
2019-01-05 04:59:13 +03:00
default :
// Unknown action, ignore it.
break ;
2017-08-28 19:27:45 +03:00
}
}
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// Called when a file changed in the file system
2017-12-15 01:57:52 +03:00
/ *
2017-08-28 19:27:45 +03:00
function onFileWatcher ( a , b ) {
2017-10-24 00:09:58 +03:00
console . log ( 'onFileWatcher' , a , b , this . path ) ;
2017-08-28 19:27:45 +03:00
var response = getDirectoryInfo ( this . path ) ;
if ( ( response != undefined ) && ( response != null ) ) { this . tunnel . s . write ( JSON . stringify ( response ) ) ; }
}
2017-12-15 01:57:52 +03:00
* /
2017-08-28 19:27:45 +03:00
// 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 = '/' ; }
2018-07-06 20:13:19 +03:00
var results = null , xpath = obj . path . join ( reqpath , '*' ) ;
2018-07-27 02:31:43 +03:00
//if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
2017-08-28 19:27:45 +03:00
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 ] ) ;
2018-07-27 02:31:43 +03:00
//if (process.platform == "win32") { p = p.split('/').join('\\'); }
2017-08-28 19:27:45 +03:00
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 ;
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// Tunnel callback operations
2017-10-24 00:09:58 +03:00
function onTunnelUpgrade ( response , s , head ) {
this . s = s ;
s . httprequest = this ;
s . end = onTunnelClosed ;
2018-01-19 02:43:43 +03:00
s . tunnel = this ;
2019-01-05 01:35:01 +03:00
2019-05-07 04:44:23 +03:00
//sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
2019-01-05 01:35:01 +03:00
2017-10-24 00:09:58 +03:00
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 ;
2019-05-05 08:53:57 +03:00
} if ( this . udpport != null ) {
// This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
s . data = onUdpRelayServerTunnelData ;
2019-05-07 04:44:23 +03:00
s . udprelay = require ( 'dgram' ) . createSocket ( { type : 'udp4' } ) ;
2019-05-05 08:53:57 +03:00
s . udprelay . bind ( { port : 0 } ) ;
s . udprelay . peerindex = this . index ;
s . udprelay . on ( 'message' , onUdpRelayTargetTunnelConnect ) ;
2019-05-07 04:44:23 +03:00
s . udprelay . udpport = this . udpport ;
s . udprelay . udpaddr = this . udpaddr ;
s . udprelay . first = true ;
2017-10-24 00:09:58 +03:00
} else {
// This is a normal connect for KVM/Terminal/Files
s . data = onTunnelData ;
}
}
2019-05-05 08:53:57 +03:00
// Called when UDP relay data is received // TODO****
function onUdpRelayTargetTunnelConnect ( data ) {
var peerTunnel = tunnels [ this . peerindex ] ;
2019-05-07 22:39:49 +03:00
peerTunnel . s . write ( data ) ;
2019-05-05 08:53:57 +03: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 ) {
2019-05-07 04:44:23 +03:00
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' ) ;
}
2019-05-05 08:53:57 +03:00
}
2017-10-24 00:09:58 +03: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 ( ) ;
}
2018-01-04 23:15:21 +03:00
2017-10-24 00:09:58 +03: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 onTcpRelayServerTunnelData ( data ) {
if ( this . first == true ) { this . first = false ; this . pipe ( this . tcprelay ) ; } // Pipe Server --> Target
}
2019-05-05 08:53:57 +03:00
2017-08-28 19:27:45 +03:00
function onTunnelClosed ( ) {
2018-01-19 02:43:43 +03:00
if ( tunnels [ this . httprequest . index ] == null ) return ; // Stop duplicate calls.
2018-11-28 04:13:01 +03:00
//sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
2017-08-28 19:27:45 +03:00
delete tunnels [ this . httprequest . index ] ;
2018-01-04 23:15:21 +03:00
2017-12-15 01:57:52 +03:00
/ *
2017-08-28 19:27:45 +03: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!!!!
2017-10-24 00:09:58 +03:00
delete this . httprequest . watcher ;
2017-08-28 19:27:45 +03:00
}
2017-12-15 01:57:52 +03:00
* /
2017-08-28 19:27:45 +03:00
// If there is a upload or download active on this connection, close the file
if ( this . httprequest . uploadFile ) { fs . closeSync ( this . httprequest . uploadFile ) ; this . httprequest . uploadFile = undefined ; }
if ( this . httprequest . downloadFile ) { fs . closeSync ( this . httprequest . downloadFile ) ; this . httprequest . downloadFile = undefined ; }
2018-01-19 02:43:43 +03:00
// 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 ;
}
// Clean up WebSocket
this . removeAllListeners ( 'data' ) ;
2017-08-28 19:27:45 +03:00
}
2018-11-28 04:13:01 +03:00
function onTunnelSendOk ( ) { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
2017-08-28 19:27:45 +03:00
function onTunnelData ( data ) {
2017-12-19 19:50:19 +03:00
//console.log("OnTunnelData");
2018-07-06 20:13:19 +03:00
//sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// If this is upload data, save it to file
if ( this . httprequest . uploadFile ) {
2018-01-03 03:52:49 +03:00
try { fs . writeSync ( this . httprequest . uploadFile , data ) ; } catch ( e ) { this . write ( new Buffer ( JSON . stringify ( { action : 'uploaderror' } ) ) ) ; return ; } // Write to the file, if there is a problem, error out.
this . write ( new Buffer ( JSON . stringify ( { action : 'uploadack' , reqid : this . httprequest . uploadFileid } ) ) ) ; // Ask for more data
2017-08-28 19:27:45 +03:00
return ;
}
2018-02-12 04:13:26 +03:00
/ *
2017-08-28 19:27:45 +03:00
// If this is a download, send more of the file
if ( this . httprequest . downloadFile ) {
var buf = new Buffer ( 4096 ) ;
var len = fs . readSync ( this . httprequest . downloadFile , buf , 0 , 4096 , null ) ;
this . httprequest . downloadFilePtr += len ;
if ( len > 0 ) { this . write ( buf . slice ( 0 , len ) ) ; } else { fs . closeSync ( this . httprequest . downloadFile ) ; this . httprequest . downloadFile = undefined ; this . end ( ) ; }
return ;
}
2018-02-12 04:13:26 +03:00
* /
2017-08-28 19:27:45 +03:00
if ( this . httprequest . state == 0 ) {
// Check if this is a relay connection
2018-11-28 04:13:01 +03:00
if ( data == 'c' ) { this . httprequest . state = 1 ; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
2017-08-28 19:27:45 +03:00
} else {
// Handle tunnel data
2019-03-01 22:47:58 +03:00
if ( this . httprequest . protocol == 0 ) { // 1 = Terminal, 2 = Desktop, 5 = Files
2018-02-12 04:13:26 +03:00
// Take a look at the protocol
2017-08-28 19:27:45 +03:00
this . httprequest . protocol = parseInt ( data ) ;
if ( typeof this . httprequest . protocol != 'number' ) { this . httprequest . protocol = 0 ; }
if ( this . httprequest . protocol == 1 ) {
2019-03-01 22:47:58 +03:00
// Check user access rights for terminal
if ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) == 0 ) || ( ( this . httprequest . rights != 0xFFFFFFFF ) && ( ( this . httprequest . rights & MESHRIGHT _NOTERMINAL ) != 0 ) ) ) {
2018-11-28 04:13:01 +03:00
// Disengage this tunnel, user does not have the rights to do this!!
this . httprequest . protocol = 999999 ;
2019-03-01 22:47:58 +03:00
this . httprequest . s . end ( ) ;
sendConsoleText ( 'Error: No Terminal Control Rights.' ) ;
2018-11-28 04:13:01 +03:00
return ;
}
2019-05-21 02:00:33 +03:00
// Test the console messaging system
//this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'This is a sample test for remote terminal...' })); // Send a console message back using the console channel, "\n" is supported.
2019-05-09 02:18:44 +03:00
// Perform notification if needed. Toast messages may not be supported on all platforms.
2019-04-13 00:19:03 +03:00
if ( this . httprequest . consent && ( this . httprequest . consent & 2 ) ) {
2019-05-09 02:18:44 +03:00
try { require ( 'toaster' ) . Toast ( 'MeshCentral' , this . httprequest . username + ' started a remote terminal session.' ) ; } catch ( ex ) { }
2019-04-13 00:19:03 +03:00
}
2018-01-17 04:30:34 +03:00
// Remote terminal using native pipes
2018-12-13 02:34:42 +03:00
if ( process . platform == "win32" )
{
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 } ) ;
2019-01-17 04:58:48 +03:00
this . prependListener ( 'end' , function ( ) { this . httprequest . _term . end ( function ( ) { console . log ( 'Terminal was closed' ) ; } ) ; } ) ;
2018-12-13 02:34:42 +03:00
//this.httprequest.process = childProcess.execFile("%windir%\\system32\\cmd.exe");
2019-01-17 04:58:48 +03:00
} else {
2019-05-30 19:40:59 +03:00
if ( fs . existsSync ( "/bin/bash" ) ) {
this . httprequest . process = childProcess . execFile ( "/bin/bash" , [ "bash" , "-i" ] , { type : childProcess . SpawnTypes . TERM } ) ;
if ( process . platform == 'linux' ) { this . httprequest . process . stdin . write ( "stty erase ^H\nalias ls='ls --color=auto'\nPS1='\\u@\\h:\\w\\$ '\nclear\n" ) ; }
} else {
this . httprequest . process = childProcess . execFile ( "/bin/sh" , [ "sh" ] , { type : childProcess . SpawnTypes . TERM } ) ;
}
//if (this.httprequest.process == null) { }
2018-12-13 02:34:42 +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 ( ) ; } ) ;
2017-08-28 19:27:45 +03:00
}
2018-11-28 04:13:01 +03:00
2019-01-17 04:58:48 +03:00
this . end = function ( ) {
if ( process . platform == "win32" ) {
// Unpipe the web socket
this . unpipe ( this . httprequest . _term ) ;
this . httprequest . _term . unpipe ( this ) ;
// 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 ) ;
this . httprequest . _term . unpipe ( this . rtcchannel ) ;
}
// Clean up
this . httprequest . _term . end ( ) ;
this . httprequest . _term = null ;
} else {
// TODO!!
}
} ;
2018-01-17 04:30:34 +03:00
this . removeAllListeners ( 'data' ) ;
this . on ( 'data' , onTunnelControlData ) ;
2018-02-08 05:45:14 +03:00
//this.write('MeshCore Terminal Hello');
2019-05-21 02:00:33 +03:00
} else if ( this . httprequest . protocol == 2 ) {
2019-03-01 22:47:58 +03:00
// Check user access rights for desktop
2018-11-28 04:13:01 +03:00
if ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) == 0 ) && ( ( this . httprequest . rights & MESHRIGHT _REMOTEVIEW ) == 0 ) ) {
// Disengage this tunnel, user does not have the rights to do this!!
this . httprequest . protocol = 999999 ;
2019-03-01 22:47:58 +03:00
this . httprequest . s . end ( ) ;
sendConsoleText ( 'Error: No Desktop Control Rights.' ) ;
2018-11-28 04:13:01 +03:00
return ;
}
2019-05-21 02:00:33 +03:00
// Test the console messaging system
//this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'This is a sample test for remote desktop...' })); // Send a console message back using the console channel, "\n" is supported.
2019-05-09 02:18:44 +03:00
// Perform notification if needed. Toast messages may not be supported on all platforms.
2019-04-13 00:19:03 +03:00
if ( this . httprequest . consent && ( this . httprequest . consent & 1 ) ) {
2019-05-09 02:18:44 +03:00
try { require ( 'toaster' ) . Toast ( 'MeshCentral' , this . httprequest . username + ' started a remote desktop session.' ) ; } catch ( ex ) { }
2019-04-13 00:19:03 +03:00
}
2018-01-17 04:30:34 +03:00
// Remote desktop using native pipes
this . httprequest . desktop = { state : 0 , kvm : mesh . getRemoteDesktopStream ( ) , tunnel : this } ;
this . httprequest . desktop . kvm . parent = this . httprequest . desktop ;
this . desktop = this . httprequest . desktop ;
2018-04-20 04:19:15 +03:00
2018-01-17 04:30:34 +03:00
this . end = function ( ) {
-- this . desktop . kvm . connectionCount ;
2019-01-04 01:46:52 +03:00
// Unpipe the web socket
2018-01-17 04:30:34 +03:00
this . unpipe ( this . httprequest . desktop . kvm ) ;
this . httprequest . desktop . kvm . unpipe ( this ) ;
2019-01-04 01:46:52 +03: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 . desktop . kvm ) ;
this . httprequest . desktop . kvm . unpipe ( this . rtcchannel ) ;
}
2018-04-20 04:19:15 +03:00
if ( this . desktop . kvm . connectionCount == 0 ) {
2019-05-09 02:18:44 +03:00
// Display a toast message. This may not be supported on all platforms.
// try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (ex) { }
2018-04-20 04:19:15 +03:00
this . httprequest . desktop . kvm . end ( ) ;
}
2018-01-17 04:30:34 +03:00
} ;
if ( this . httprequest . desktop . kvm . hasOwnProperty ( "connectionCount" ) ) { this . httprequest . desktop . kvm . connectionCount ++ ; } else { this . httprequest . desktop . kvm . connectionCount = 1 ; }
2018-11-28 04:13:01 +03:00
2019-03-10 01:28:08 +03:00
if ( ( this . httprequest . rights == 0xFFFFFFFF ) || ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) != 0 ) && ( ( this . httprequest . rights & MESHRIGHT _REMOTEVIEW ) == 0 ) ) ) {
2018-11-28 04:13:01 +03:00
// 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.
2019-03-10 01:28:08 +03:00
//sendConsoleText('Warning: No Remote Desktop Input Rights.');
2018-11-28 04:13:01 +03:00
// TODO!!!
}
this . httprequest . desktop . kvm . pipe ( this , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text. Pipe the KVM --> Browser images.
2018-01-17 04:30:34 +03:00
this . removeAllListeners ( 'data' ) ;
this . on ( 'data' , onTunnelControlData ) ;
//this.write('MeshCore KVM Hello!1');
2019-05-21 02:00:33 +03:00
2018-02-12 04:13:26 +03:00
} else if ( this . httprequest . protocol == 5 ) {
2019-05-21 02:00:33 +03:00
2019-03-01 22:47:58 +03:00
// Check user access rights for files
if ( ( ( this . httprequest . rights & MESHRIGHT _REMOTECONTROL ) == 0 ) || ( ( this . httprequest . rights != 0xFFFFFFFF ) && ( ( this . httprequest . rights & MESHRIGHT _NOFILES ) != 0 ) ) ) {
2018-11-28 04:13:01 +03:00
// Disengage this tunnel, user does not have the rights to do this!!
this . httprequest . protocol = 999999 ;
2019-03-01 22:47:58 +03:00
this . httprequest . s . end ( ) ;
sendConsoleText ( 'Error: No Files Control Rights.' ) ;
2018-11-28 04:13:01 +03:00
return ;
}
2019-05-21 02:00:33 +03:00
// Test the console messaging system
//this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'This is a sample test for remote files...' })); // Send a console message back using the console channel, "\n" is supported.
2019-04-13 00:19:03 +03:00
// Perform notification if needed
if ( this . httprequest . consent && ( this . httprequest . consent & 4 ) ) {
2019-05-09 02:18:44 +03:00
try { require ( 'toaster' ) . Toast ( 'MeshCentral' , this . httprequest . username + ' started a remote file access.' ) ; } catch ( ex ) { }
2019-04-13 00:19:03 +03:00
}
2017-08-28 19:27:45 +03: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)
this . httprequest . process . write ( data ) ;
} else if ( this . httprequest . protocol == 2 ) {
// Send data into remote desktop
if ( this . httprequest . desktop . state == 0 ) {
this . write ( new Buffer ( 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 ) { } ;
2018-02-12 04:13:26 +03:00
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));
2017-10-17 06:11:03 +03:00
if ( ( cmd . path != null ) && ( process . platform != 'win32' ) && ( cmd . path [ 0 ] != '/' ) ) { cmd . path = '/' + cmd . path ; } // Add '/' to paths on non-windows
2018-02-12 04:13:26 +03:00
//console.log(objToString(cmd, 0, ' '));
2017-08-28 19:27:45 +03:00
switch ( cmd . action ) {
case 'ls' : {
2017-12-15 01:57:52 +03:00
/ *
2017-08-28 19:27:45 +03:00
// 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 ;
}
2017-12-15 01:57:52 +03:00
* /
2017-08-28 19:27:45 +03:00
// Send the folder content to the browser
var response = getDirectoryInfo ( cmd . path ) ;
if ( cmd . reqid != undefined ) { response . reqid = cmd . reqid ; }
2018-01-03 03:52:49 +03:00
this . write ( new Buffer ( JSON . stringify ( response ) ) ) ;
2018-01-04 23:15:21 +03:00
2017-12-15 01:57:52 +03:00
/ *
2017-08-28 19:27:45 +03:00
// 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);
}
2017-12-15 01:57:52 +03:00
* /
2017-08-28 19:27:45 +03:00
break ;
}
case 'mkdir' : {
// Create a new empty folder
fs . mkdirSync ( cmd . path ) ;
break ;
}
case 'rm' : {
2018-09-25 21:51:40 +03:00
// Delete, possibly recursive delete
2017-08-28 19:27:45 +03:00
for ( var i in cmd . delfiles ) {
2018-09-25 21:51:40 +03:00
try { deleteFolderRecursive ( obj . path . join ( cmd . path , cmd . delfiles [ i ] ) , cmd . rec ) ; } catch ( e ) { }
2017-08-28 19:27:45 +03:00
}
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 ) ;
try { fs . renameSync ( oldfullpath , newfullpath ) ; } catch ( e ) { console . log ( e ) ; }
break ;
}
2018-02-12 04:13:26 +03:00
case 'download' : {
// Download a file
var sendNextBlock = 0 ;
if ( cmd . sub == 'start' ) { // Setup the download
if ( 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 = 8 ; } else if ( cmd . sub == 'stop' ) { delete this . filedownload ; } else if ( cmd . sub == 'ack' ) { sendNextBlock = 1 ; }
}
// Send the next download block(s)
while ( sendNextBlock > 0 ) {
sendNextBlock -- ;
var buf = new Buffer ( 4096 ) ;
var len = fs . readSync ( this . filedownload . f , buf , 4 , 4092 , null ) ;
this . filedownload . ptr += len ;
2018-02-13 23:28:11 +03:00
if ( len < 4092 ) { 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
2018-02-12 04:13:26 +03:00
}
break ;
}
/ *
2017-08-28 19:27:45 +03:00
case 'download' : {
// Packet download of a file, agent to browser
if ( cmd . path == undefined ) break ;
var filepath = cmd . name ? obj . path . join ( cmd . path , cmd . name ) : cmd . path ;
//console.log('Download: ' + filepath);
2018-01-03 03:52:49 +03:00
try { this . httprequest . downloadFile = fs . openSync ( filepath , 'rbN' ) ; } catch ( e ) { this . write ( new Buffer ( JSON . stringify ( { action : 'downloaderror' , reqid : cmd . reqid } ) ) ) ; break ; }
2017-08-28 19:27:45 +03:00
this . httprequest . downloadFileId = cmd . reqid ;
this . httprequest . downloadFilePtr = 0 ;
2018-01-03 03:52:49 +03:00
if ( this . httprequest . downloadFile ) { this . write ( new Buffer ( JSON . stringify ( { action : 'downloadstart' , reqid : this . httprequest . downloadFileId } ) ) ) ; }
2017-08-28 19:27:45 +03:00
break ;
}
case 'download2' : {
// Stream download of a file, agent to browser
if ( cmd . path == undefined ) break ;
var filepath = cmd . name ? obj . path . join ( cmd . path , cmd . name ) : cmd . path ;
try { this . httprequest . downloadFile = fs . createReadStream ( filepath , { flags : 'rbN' } ) ; } catch ( e ) { console . log ( e ) ; }
this . httprequest . downloadFile . pipe ( this ) ;
this . httprequest . downloadFile . end = function ( ) { }
break ;
}
2018-02-12 04:13:26 +03:00
* /
2017-08-28 19:27:45 +03:00
case 'upload' : {
// Upload a file, browser to agent
if ( this . httprequest . uploadFile != undefined ) { fs . closeSync ( this . httprequest . uploadFile ) ; this . httprequest . uploadFile = undefined ; }
if ( cmd . path == undefined ) break ;
var filepath = cmd . name ? obj . path . join ( cmd . path , cmd . name ) : cmd . path ;
2018-01-03 03:52:49 +03:00
try { this . httprequest . uploadFile = fs . openSync ( filepath , 'wbN' ) ; } catch ( e ) { this . write ( new Buffer ( JSON . stringify ( { action : 'uploaderror' , reqid : cmd . reqid } ) ) ) ; break ; }
2017-08-28 19:27:45 +03:00
this . httprequest . uploadFileid = cmd . reqid ;
2018-01-03 03:52:49 +03:00
if ( this . httprequest . uploadFile ) { this . write ( new Buffer ( JSON . stringify ( { action : 'uploadstart' , reqid : this . httprequest . uploadFileid } ) ) ) ; }
2017-08-28 19:27:45 +03:00
break ;
}
2018-04-03 01:34:32 +03:00
case 'copy' : {
// Copy a bunch of files from scpath to dspath
for ( var i in cmd . names ) {
var sc = obj . path . join ( cmd . scpath , cmd . names [ i ] ) , ds = obj . 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
for ( var i in cmd . names ) {
var sc = obj . path . join ( cmd . scpath , cmd . names [ i ] ) , ds = obj . path . join ( cmd . dspath , cmd . names [ i ] ) ;
if ( sc != ds ) { try { fs . copyFileSync ( sc , ds ) ; fs . unlinkSync ( sc ) ; } catch ( e ) { } }
}
break ;
}
2019-01-05 04:59:13 +03:00
default :
// Unknown action, ignore it.
break ;
2017-08-28 19:27:45 +03:00
}
}
//sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
}
}
2018-01-17 04:30:34 +03:00
2019-03-14 22:28:58 +03:00
// Delete a directory with a files and directories within it
function deleteFolderRecursive ( path , rec ) {
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
deleteFolderRecursive ( curPath , true ) ;
} else { // delete file
fs . unlinkSync ( curPath ) ;
}
} ) ;
}
fs . unlinkSync ( path ) ;
}
} ;
2018-01-19 02:43:43 +03:00
// Called when receiving control data on WebRTC
function onTunnelWebRTCControlData ( data ) {
if ( typeof data != 'string' ) return ;
var obj ;
2018-07-24 03:34:24 +03:00
try { obj = JSON . parse ( data ) ; } catch ( e ) { sendConsoleText ( 'Invalid control JSON on WebRTC: ' + data ) ; return ; }
2018-01-19 02:43:43 +03:00
if ( obj . type == 'close' ) {
2018-02-05 22:56:29 +03:00
//sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2018-01-19 02:43:43 +03:00
try { this . close ( ) ; } catch ( e ) { }
try { this . xrtc . close ( ) ; } catch ( e ) { }
}
}
// Called when receiving control data on websocket
2018-02-12 04:13:26 +03:00
function onTunnelControlData ( data , ws ) {
2018-01-19 02:43:43 +03:00
var obj ;
2018-02-12 04:13:26 +03:00
if ( ws == null ) { ws = this ; }
2018-07-24 03:34:24 +03:00
if ( typeof data == 'string' ) { try { obj = JSON . parse ( data ) ; } catch ( e ) { sendConsoleText ( 'Invalid control JSON: ' + data ) ; return ; } }
2018-02-12 04:13:26 +03:00
else if ( typeof data == 'object' ) { obj = data ; } else { return ; }
//sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
//console.log('onTunnelControlData: ' + JSON.stringify(data));
2018-01-19 02:43:43 +03:00
2018-07-24 03:34:24 +03:00
if ( obj . action ) {
switch ( obj . action ) {
case 'lock' : {
// Lock the current user out of the desktop
try {
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 } ) ;
}
} catch ( e ) { }
break ;
}
2019-01-05 04:59:13 +03:00
default :
// Unknown action, ignore it.
break ;
2018-07-24 03:34:24 +03:00
}
return ;
}
2018-01-19 02:43:43 +03:00
if ( obj . type == 'close' ) {
// We received the close on the websocket
2018-02-12 04:13:26 +03:00
//sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
try { ws . close ( ) ; } catch ( e ) { }
2018-02-05 22:56:29 +03:00
} else if ( obj . type == 'webrtc0' ) { // Browser indicates we can start WebRTC switch-over.
2018-02-12 04:13:26 +03:00
if ( ws . httprequest . protocol == 1 ) { // Terminal
2018-02-05 22:56:29 +03:00
// 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
2018-12-13 02:34:42 +03:00
if ( process . platform == 'win32' )
{
ws . httprequest . _term . unpipe ( ws ) ;
}
else
{
ws . httprequest . process . stdout . unpipe ( ws ) ;
ws . httprequest . process . stderr . unpipe ( ws ) ;
}
2018-02-12 04:13:26 +03:00
} else if ( ws . httprequest . protocol == 2 ) { // Desktop
2018-02-05 22:56:29 +03:00
// 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
2018-02-12 04:13:26 +03:00
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 ) ;
2018-02-05 22:56:29 +03:00
}
2018-02-12 04:13:26 +03:00
ws . write ( "{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}" ) ; // End of data marker
2018-01-19 02:43:43 +03:00
} else if ( obj . type == 'webrtc1' ) {
2018-02-12 04:13:26 +03:00
if ( ws . httprequest . protocol == 1 ) { // Terminal
2018-01-19 02:43:43 +03:00
// Switch the user input from websocket to webrtc at this point.
2018-12-13 02:34:42 +03:00
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-02-12 04:13:26 +03:00
ws . resume ( ) ; // Resume the websocket to keep receiving control data
} else if ( ws . httprequest . protocol == 2 ) { // Desktop
2018-01-19 02:43:43 +03:00
// Switch the user input from websocket to webrtc at this point.
2018-02-12 04:13:26 +03:00
ws . unpipe ( ws . httprequest . desktop . kvm ) ;
2018-04-18 05:00:31 +03:00
try { ws . webrtc . rtcchannel . pipe ( ws . httprequest . desktop . kvm , { dataTypeSkip : 1 , end : false } ) ; } catch ( e ) { sendConsoleText ( 'EX2' ) ; } // 0 = Binary, 1 = Text.
2018-02-12 04:13:26 +03:00
ws . resume ( ) ; // Resume the websocket to keep receiving control data
2018-01-19 02:43:43 +03:00
}
2018-02-12 04:13:26 +03:00
ws . write ( "{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}" ) ; // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
2018-01-19 02:43:43 +03:00
} else if ( obj . type == 'webrtc2' ) {
// Other side received websocket end of data marker, start sending data on WebRTC channel
2018-02-12 04:13:26 +03:00
if ( ws . httprequest . protocol == 1 ) { // Terminal
2018-12-13 02:34:42 +03:00
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-02-12 04:13:26 +03:00
} else if ( ws . httprequest . protocol == 2 ) { // Desktop
ws . httprequest . desktop . kvm . pipe ( ws . webrtc . rtcchannel , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
2018-01-19 02:43:43 +03:00
}
} else if ( obj . type == 'offer' ) {
2018-01-17 04:30:34 +03:00
// This is a WebRTC offer.
2019-01-17 04:58:48 +03:00
if ( ws . httprequest . protocol == 1 ) return ; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
2018-02-12 04:13:26 +03:00
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 ) {
2018-02-05 22:56:29 +03:00
//sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
2018-01-19 02:43:43 +03:00
rtcchannel . xrtc = this ;
2018-01-24 01:15:59 +03:00
rtcchannel . websocket = this . websocket ;
2018-01-17 04:30:34 +03:00
this . rtcchannel = rtcchannel ;
2018-01-24 01:15:59 +03:00
this . websocket . rtcchannel = rtcchannel ;
this . websocket . rtcchannel . on ( 'data' , onTunnelWebRTCControlData ) ;
2019-01-04 01:46:52 +03:00
this . websocket . rtcchannel . on ( 'end' , function ( ) {
// The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
2019-01-17 04:58:48 +03:00
//sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
if ( this . websocket . desktop && this . websocket . desktop . kvm ) {
this . unpipe ( this . websocket . desktop . kvm ) ;
this . websocket . httprequest . desktop . kvm . unpipe ( this ) ;
}
2019-01-04 01:46:52 +03:00
} ) ;
2018-02-12 04:13:26 +03:00
this . websocket . write ( "{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}" ) ; // Indicate we are ready for WebRTC switch-over.
2018-01-17 04:30:34 +03:00
} ) ;
2018-04-13 04:14:03 +03:00
var sdp = null ;
try { sdp = ws . webrtc . setOffer ( obj . sdp ) ; } catch ( ex ) { }
if ( sdp != null ) { ws . write ( { type : 'answer' , ctrlChannel : '102938' , sdp : sdp } ) ; }
2018-01-17 04:30:34 +03:00
}
}
2017-08-28 19:27:45 +03:00
// Console state
var consoleWebSockets = { } ;
var consoleHttpRequest = null ;
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// 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 ; }
} ;
2018-03-13 04:16:06 +03:00
2018-12-08 03:36:27 +03:00
// Open a web browser to a specified URL on current user's desktop
function openUserDesktopUrl ( url ) {
var child = null ;
try {
switch ( process . platform ) {
case 'win32' :
2018-12-20 23:12:24 +03:00
//child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ["/c", "start", url], { type: childProcess.SpawnTypes.USER, uid: require('user-sessions').Current().Active[0].SessionId });
2018-12-14 21:19:25 +03:00
child = require ( 'child_process' ) . execFile ( process . env [ 'windir' ] + '\\system32\\cmd.exe' , [ "/c" , "start" , url ] , { type : childProcess . SpawnTypes . USER } ) ;
2018-12-08 03:36:27 +03:00
break ;
case 'linux' :
2019-01-15 20:20:21 +03:00
child = require ( 'child_process' ) . execFile ( '/usr/bin/xdg-open' , [ 'xdg-open' , url ] , { uid : require ( 'user-sessions' ) . consoleUid ( ) } ) ;
2018-12-08 03:36:27 +03:00
break ;
case 'darwin' :
child = require ( 'child_process' ) . execFile ( '/usr/bin/open' , [ 'open' , url ] , { uid : require ( 'user-sessions' ) . consoleUid ( ) } ) ;
break ;
2019-01-05 04:59:13 +03:00
default :
// Unknown platform, ignore this command.
break ;
2018-12-08 03:36:27 +03:00
}
} catch ( ex ) { }
return child ;
}
2017-08-28 19:27:45 +03:00
// Process a mesh agent console command
function processConsoleCommand ( cmd , args , rights , sessionid ) {
try {
var response = null ;
switch ( cmd ) {
case 'help' : { // Displays available commands
2019-02-17 08:16:39 +03:00
response = 'Available commands: help, info, osinfo,args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, ps, kill, amt, netinfo, location, power, wakeonlan, scanwifi,\r\nscanamt, setdebug, smbios, rawsmbios, toast, lock, users, sendcaps, openurl, amtreset, amtccm, amtdeactivate,\r\namtpolicy, getscript, getclip, setclip.' ;
2018-04-20 04:19:15 +03:00
break ;
}
2018-09-20 21:45:12 +03:00
/ *
2018-08-30 04:47:22 +03:00
case 'border' :
{
if ( ( args [ '_' ] . length == 1 ) && ( args [ '_' ] [ 0 ] == 'on' ) ) {
2018-11-25 23:59:42 +03:00
if ( meshCoreObj . users . length > 0 ) {
obj . borderManager . Start ( meshCoreObj . users [ 0 ] ) ;
2018-08-30 04:47:22 +03:00
response = 'Border blinking is on.' ;
} else {
response = 'Cannot turn on border blinking, no logged in users.' ;
}
} 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
}
}
break ;
2018-09-20 21:45:12 +03:00
* /
2019-03-18 05:34:52 +03:00
case 'getclip' :
if ( require ( 'MeshAgent' ) . isService ) {
require ( 'clipboard' ) . dispatchRead ( ) . then ( function ( str ) { sendConsoleText ( str , sessionid ) ; } ) ;
2019-03-19 19:54:52 +03:00
} else {
2019-03-18 05:34:52 +03:00
require ( "clipboard" ) . read ( ) . then ( function ( str ) { sendConsoleText ( str , sessionid ) ; } ) ;
}
2019-02-17 08:16:39 +03:00
break ;
case 'setclip' : {
2019-03-18 05:34:52 +03:00
if ( args [ '_' ] . length != 1 ) {
2019-03-19 19:54:52 +03:00
response = 'Proper usage: setclip "sample text"' ;
} else {
2019-03-18 05:34:52 +03:00
if ( require ( 'MeshAgent' ) . isService ) {
require ( 'clipboard' ) . dispatchWrite ( args [ '_' ] [ 0 ] ) ;
2019-03-19 19:54:52 +03:00
response = 'Setting clipboard to: "' + args [ '_' ] [ 0 ] + '"' ;
2019-03-18 05:34:52 +03:00
}
else {
2019-03-19 19:54:52 +03:00
require ( "clipboard" ) ( args [ '_' ] [ 0 ] ) ; response = 'Setting clipboard to: "' + args [ '_' ] [ 0 ] + '"' ;
2019-03-18 05:34:52 +03:00
}
}
2019-02-17 08:16:39 +03:00
break ;
}
2018-12-28 08:31:20 +03:00
case 'amtreset' : {
2019-03-05 10:48:45 +03:00
if ( amt != null ) { amt . reset ( ) ; response = 'Done.' ; }
break ;
}
case 'amtlmsreset' : {
if ( amt != null ) { amt . lmsreset ( ) ; response = 'Done.' ; }
2018-12-28 08:31:20 +03:00
break ;
}
case 'amtccm' : {
2019-03-05 10:48:45 +03:00
if ( amt == null ) { response = 'Intel AMT not supported.' ; } else {
2018-12-28 08:31:20 +03:00
if ( args [ '_' ] . length != 1 ) { response = 'Proper usage: amtccm (adminPassword)' ; } // Display usage
2019-03-05 10:48:45 +03:00
else { amt . setPolicy ( { type : 0 } ) ; amt . activeToCCM ( args [ '_' ] [ 0 ] ) ; }
2018-12-28 08:31:20 +03:00
}
break ;
}
case 'amtdeactivate' : {
2019-03-05 10:48:45 +03:00
if ( amt == null ) { response = 'Intel AMT not supported.' ; } else { amt . setPolicy ( { type : 0 } ) ; amt . deactivateCCM ( ) ; }
2018-12-28 08:31:20 +03:00
break ;
}
2019-02-08 02:00:10 +03:00
case 'amtpolicy' : {
if ( amtPolicy == null ) {
response = 'No Intel(R) AMT policy.' ;
} else {
response = JSON . stringify ( amtPolicy ) ;
}
break ;
}
2018-12-08 03:36:27 +03:00
case 'openurl' : {
if ( args [ '_' ] . length != 1 ) { response = 'Proper usage: openurl (url)' ; } // Display usage
2018-12-14 21:19:25 +03:00
else { if ( openUserDesktopUrl ( args [ '_' ] [ 0 ] ) == null ) { response = 'Failed.' ; } else { response = 'Success.' ; } }
2018-12-08 03:36:27 +03:00
break ;
}
2018-11-25 23:59:42 +03:00
case 'users' : {
if ( meshCoreObj . users == null ) { response = 'Active users are unknown.' ; } else { response = 'Active Users: ' + meshCoreObj . users . join ( ', ' ) + '.' ; }
2018-12-20 23:12:24 +03:00
require ( 'user-sessions' ) . enumerateUsers ( ) . then ( function ( u ) { for ( var i in u ) { sendConsoleText ( u [ i ] ) ; } } ) ;
2018-08-30 04:47:22 +03:00
break ;
2018-11-25 23:59:42 +03:00
}
2018-04-20 04:19:15 +03:00
case 'toast' : {
2018-04-21 02:05:06 +03:00
if ( process . platform == 'win32' ) {
if ( args [ '_' ] . length < 1 ) { response = 'Proper usage: toast "message"' ; } else {
2019-05-09 02:18:44 +03:00
try { require ( 'toaster' ) . Toast ( 'MeshCentral' , args [ '_' ] [ 0 ] ) ; response = 'ok' ; } catch ( ex ) { response = ex ; }
2018-04-21 02:05:06 +03:00
}
} else {
response = 'Only supported on Windows.' ;
}
2018-03-09 04:58:22 +03:00
break ;
}
case 'setdebug' : {
2018-08-30 04:47:22 +03:00
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 ) ; } }
2018-01-12 22:41:26 +03:00
break ;
}
2018-04-11 23:49:05 +03:00
case 'ps' : {
processManager . getProcesses ( function ( plist ) {
var x = '' ;
for ( var i in plist ) { x += i + ', ' + plist [ i ] . cmd + ( ( plist [ i ] . user ) ? ( ', ' + plist [ i ] . user ) : '' ) + '\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 ] + '.' ;
}
break ;
}
2018-03-13 04:16:06 +03:00
case 'smbios' : {
2018-09-28 02:17:05 +03:00
if ( SMBiosTables == null ) { response = 'SMBios tables not available.' ; } else { response = objToString ( SMBiosTables , 0 , ' ' , true ) ; }
2018-03-13 04:16:06 +03:00
break ;
}
case 'rawsmbios' : {
2018-09-28 02:17:05 +03:00
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-13 04:16:06 +03:00
}
}
2018-09-28 02:17:05 +03:00
}
}
2018-03-13 04:16:06 +03:00
break ;
}
2018-01-12 22:41:26 +03:00
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 ] ) ) ;
}
2017-08-28 19:27:45 +03: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 'info' : { // Return information about the agent and agent core module
2018-11-25 23:59:42 +03:00
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 + '.' ;
2019-03-05 10:48:45 +03:00
if ( amt != null ) { response += '\r\nBuilt-in LMS: ' + [ 'Disabled' , 'Connecting..' , 'Connected' ] [ amt . lmsstate ] + '.' ; }
2018-11-25 23:59:42 +03:00
if ( meshCoreObj . osdesc ) { response += '\r\nOS: ' + meshCoreObj . osdesc + '.' ; }
2018-09-19 05:41:59 +03:00
response += '\r\nModules: ' + addedModules . join ( ', ' ) + '.' ;
2018-09-20 21:45:12 +03:00
response += '\r\nServer Connection: ' + mesh . isControlChannelConnected + ', State: ' + meshServerConnectionState + '.' ;
2018-09-22 02:34:35 +03:00
response += '\r\lastMeInfo: ' + lastMeInfo + '.' ;
2017-11-08 04:05:22 +03:00
var oldNodeId = db . Get ( 'OldNodeId' ) ;
2017-12-13 03:04:54 +03:00
if ( oldNodeId != null ) { response += '\r\nOldNodeID: ' + oldNodeId + '.' ; }
2018-09-20 21:45:12 +03:00
if ( process . platform != 'win32' ) { response += '\r\nX11 support: ' + require ( 'monitor-info' ) . kvm _x11 _support + '.' ; }
break ;
}
case 'osinfo' : { // Return the operating system information
var i = 1 ;
2018-09-22 02:34:35 +03:00
if ( args [ '_' ] . length > 0 ) { i = parseInt ( args [ '_' ] [ 0 ] ) ; if ( i > 8 ) { i = 8 ; } response = 'Calling ' + i + ' times.' ; }
2018-09-20 21:45:12 +03:00
for ( var j = 0 ; j < i ; j ++ ) {
var pr = require ( 'os' ) . name ( ) ;
pr . sessionid = sessionid ;
pr . then ( function ( v ) { sendConsoleText ( "OS: " + v , this . sessionid ) ; } ) ;
2018-09-19 05:41:59 +03:00
}
2017-08-28 19:27:45 +03:00
break ;
}
2018-09-20 21:45:12 +03:00
case 'sendcaps' : { // Send capability flags to the server
if ( args [ '_' ] . length == 0 ) {
response = 'Proper usage: sendcaps (number)' ; // Display correct command usage
} else {
2018-11-25 23:59:42 +03:00
meshCoreObj . caps = parseInt ( args [ '_' ] [ 0 ] ) ;
mesh . SendCommand ( meshCoreObj ) ;
response = JSON . stringify ( meshCoreObj ) ;
2018-09-20 21:45:12 +03:00
}
break ;
}
2018-09-22 02:34:35 +03:00
case 'sendosdesc' : { // Send OS description
2018-11-25 23:59:42 +03:00
if ( args [ '_' ] . length > 0 ) {
meshCoreObj . osdesc = args [ '_' ] [ 0 ] ;
mesh . SendCommand ( meshCoreObj ) ;
response = JSON . stringify ( meshCoreObj ) ;
} else {
response = 'Proper usage: sendosdesc [os description]' ; // Display correct command usage
}
2018-09-22 02:34:35 +03:00
break ;
}
2017-08-28 19:27:45 +03:00
case 'args' : { // Displays parsed command arguments
2018-03-21 03:48:03 +03:00
response = 'args ' + objToString ( args , 0 , ' ' , true ) ;
2017-08-28 19:27:45 +03:00
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 ;
2019-01-03 05:34:50 +03:00
var buf = Buffer . alloc ( max ) , fd = fs . openSync ( args [ '_' ] [ 0 ] , "r" ) , r = fs . readSync ( fd , buf , 0 , max ) ; // Read the file content
2017-08-28 19:27:45 +03:00
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 ) ;
}
break ;
}
2017-09-25 21:00:57 +03:00
case 'dbkeys' : { // Return all data store keys
response = JSON . stringify ( db . Keys ) ;
break ;
}
2017-08-28 19:27:45 +03:00
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 ] ) ;
}
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 ;
}
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 {
2017-09-21 00:44:22 +03:00
var options = http . parseUri ( args [ '_' ] [ 0 ] ) ;
2017-08-28 19:27:45 +03:00
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 ( ) ;
2017-09-21 00:44:22 +03:00
response = 'HTTPGET ' + options . protocol + '//' + options . host + ':' + options . port + options . path ;
2017-08-28 19:27:45 +03: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' ;
}
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 ;
2017-09-02 03:34:02 +03:00
try {
2018-01-04 23:15:21 +03:00
var options = http . parseUri ( args [ '_' ] [ 0 ] ) ;
options . rejectUnauthorized = 0 ;
httprequest = http . request ( options ) ;
2017-09-02 03:34:02 +03:00
} catch ( e ) { response = 'Invalid HTTP websocket request' ; }
2017-08-28 19:27:45 +03:00
if ( httprequest != null ) {
httprequest . upgrade = onWebSocketUpgrade ;
2019-03-02 04:13:11 +03:00
httprequest . on ( 'error' , function ( e ) { sendConsoleText ( 'ERROR: ' + JSON . stringify ( e ) ) ; } ) ;
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
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 ;
}
}
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 ) {
var httprequest = consoleWebSockets [ i ] ;
response += 'Websocket #' + i + ', ' + httprequest . url + '\r\n' ;
}
} 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' ;
}
}
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 ) {
2017-09-02 03:34:02 +03:00
if ( httprequest . s != null ) { httprequest . s . end ( ) ; } else { httprequest . end ( ) ; }
2017-08-28 19:27:45 +03:00
response = 'ok' ;
} else {
response = 'Invalid web socket number' ;
}
}
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-10-24 00:09:58 +03:00
}
2017-08-28 19:27:45 +03:00
}
break ;
}
2018-07-24 03:34:24 +03:00
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 ;
}
2017-08-28 19:27:45 +03:00
case 'amt' : { // Show Intel AMT status
2019-03-05 10:48:45 +03:00
if ( amt != null ) {
amt . getAmtInfo ( function ( state ) {
var resp = 'Intel AMT not detected.' ;
if ( state != null ) { resp = objToString ( state , 0 , ' ' , true ) ; }
sendConsoleText ( resp , sessionid ) ;
} ) ;
}
2017-08-28 19:27:45 +03:00
break ;
}
case 'netinfo' : { // Show network interface information
2018-02-12 04:13:26 +03:00
//response = objToString(mesh.NetInfo, 0, ' ');
2017-09-21 00:44:22 +03:00
var interfaces = require ( 'os' ) . networkInterfaces ( ) ;
2018-03-21 03:48:03 +03:00
response = objToString ( interfaces , 0 , ' ' , true ) ;
break ;
}
case 'netinfo2' : { // Show network interface information
response = objToString ( mesh . NetInfo , 0 , ' ' , true ) ;
2017-09-21 00:44:22 +03:00
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-08-28 19:27:45 +03:00
break ;
}
case 'sendall' : { // Send a message to all consoles on this mesh
sendConsoleText ( args [ '_' ] . join ( ' ' ) ) ;
break ;
}
2017-09-01 21:23:22 +03:00
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 ) || ( typeof args [ '_' ] [ 0 ] != 'number' ) ) {
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
} else {
var r = mesh . ExecPowerState ( args [ '_' ] [ 0 ] , args [ '_' ] [ 1 ] ) ;
response = 'Power action executed with return code: ' + r + '.' ;
}
}
2017-08-28 22:48:53 +03:00
break ;
}
2017-09-13 21:25:57 +03:00
case 'location' : {
getIpLocationData ( function ( location ) {
2018-02-12 04:13:26 +03:00
sendConsoleText ( objToString ( { "action" : "iplocation" , "type" : "publicip" , "value" : location } , 0 , ' ' ) ) ;
2017-09-13 21:25:57 +03:00
} ) ;
break ;
}
2017-09-21 00:44:22 +03:00
case 'parseuri' : {
response = JSON . stringify ( http . parseUri ( args [ '_' ] [ 0 ] ) ) ;
break ;
}
2017-10-05 06:35:52 +03:00
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." ; }
2017-10-04 04:31:20 +03:00
break ;
}
2018-01-10 07:13:41 +03:00
case 'scanamt' : {
if ( amtscanner != null ) {
if ( args [ '_' ] . length != 1 ) {
response = 'Usage examples:\r\n scanamt 1.2.3.4\r\n scanamt 1.2.3.0-1.2.3.255\r\n scanamt 1.2.3.0/24\r\n' ; // Display correct command usage
} else {
response = 'Scanning: ' + args [ '_' ] [ 0 ] + '...' ;
amtscanner . scan ( args [ '_' ] [ 0 ] , 2000 , function ( data ) {
if ( data . length > 0 ) {
var r = '' , pstates = [ 'NotActivated' , 'InActivation' , 'Activated' ] ;
for ( var i in data ) {
var x = data [ i ] ;
if ( r != '' ) { r += '\r\n' ; }
r += x . address + ' - Intel AMT v' + x . majorVersion + '.' + x . minorVersion ;
if ( x . provisioningState < 3 ) { r += ( ', ' + pstates [ x . provisioningState ] ) ; }
if ( x . provisioningState == 2 ) { r += ( ', ' + x . openPorts . join ( ', ' ) ) ; }
r += '.' ;
}
} else {
r = 'No Intel AMT found.' ;
}
sendConsoleText ( r ) ;
} ) ;
}
} else { response = "Intel AMT scanner module not present." ; }
break ;
}
case 'modules' : {
response = JSON . stringify ( addedModules ) ;
break ;
2019-02-08 07:06:01 +03:00
}
case 'getscript' : {
if ( args [ '_' ] . length != 1 ) {
response = 'Proper usage: getscript [scriptNumber].' ;
} else {
mesh . SendCommand ( { "action" : "getScript" , "type" : args [ '_' ] [ 0 ] } ) ;
}
break ;
2018-01-10 07:13:41 +03:00
}
2017-08-28 19:27:45 +03:00
default : { // This is an unknown command, return an error message
response = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.' ;
break ;
}
}
} catch ( e ) { response = 'Command returned an exception error: ' + e ; console . log ( e ) ; }
if ( response != null ) { sendConsoleText ( response , sessionid ) ; }
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// Send a mesh agent console command
2017-08-28 22:48:53 +03:00
function sendConsoleText ( text , sessionid ) {
if ( typeof text == 'object' ) { text = JSON . stringify ( text ) ; }
mesh . SendCommand ( { "action" : "msg" , "type" : "console" , "value" : text , "sessionid" : sessionid } ) ;
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// Called before the process exits
//process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03: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
2017-11-08 04:05:22 +03:00
var oldNodeId = db . Get ( 'OldNodeId' ) ;
if ( oldNodeId != null ) { mesh . SendCommand ( { action : 'mc1migration' , oldnodeid : oldNodeId } ) ; }
2018-09-22 02:34:35 +03:00
2018-11-25 23:59:42 +03:00
// Update the server with basic info, logged in users and more.
mesh . SendCommand ( meshCoreObj ) ;
2018-09-22 02:34:35 +03:00
2018-09-28 02:17:05 +03:00
// Send SMBios tables if present
if ( SMBiosTablesRaw != null ) { mesh . SendCommand ( { "action" : "smbios" , "value" : SMBiosTablesRaw } ) ; }
2018-09-22 02:34:35 +03:00
// Update the server on more advanced stuff, like Intel ME and Network Settings
meInfoStr = null ;
sendPeriodicServerUpdate ( ) ;
//if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
}
2017-08-28 19:27:45 +03:00
}
2018-09-22 02:34:35 +03:00
2017-12-13 03:04:54 +03:00
// 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 ;
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
// Update the network interfaces information data
var netInfo = mesh . NetInfo ;
2019-05-09 02:18:44 +03:00
if ( netInfo ) {
netInfo . action = 'netinfo' ;
var netInfoStr = JSON . stringify ( netInfo ) ;
if ( ( force == true ) || ( clearGatewayMac ( netInfoStr ) != clearGatewayMac ( lastNetworkInfo ) ) ) { mesh . SendCommand ( netInfo ) ; lastNetworkInfo = netInfoStr ; }
}
2017-08-28 19:27:45 +03:00
}
2018-01-04 23:15:21 +03:00
2017-12-13 03:04:54 +03:00
// Called periodically to check if we need to send updates to the server
2018-09-22 02:34:35 +03:00
function sendPeriodicServerUpdate ( flags ) {
if ( meshServerConnectionState == 0 ) return ; // Not connected to server, do nothing.
if ( ! flags ) { flags = 0xFFFFFFFF ; }
2019-03-05 10:48:45 +03:00
if ( ( flags & 1 ) && ( amt != null ) ) {
2018-09-22 02:34:35 +03:00
// If we have a connected MEI, get Intel ME information
2019-03-05 10:48:45 +03:00
amt . getAmtInfo ( function ( meinfo ) {
2018-09-22 02:34:35 +03:00
try {
if ( meinfo == null ) return ;
var intelamt = { } , p = false ;
if ( meinfo . Versions && meinfo . Versions . AMT ) { intelamt . ver = meinfo . Versions . AMT ; p = true ; }
if ( meinfo . ProvisioningState ) { intelamt . state = meinfo . ProvisioningState ; p = true ; }
if ( meinfo . Flags ) { intelamt . flags = meinfo . Flags ; p = true ; }
if ( meinfo . OsHostname ) { intelamt . host = meinfo . OsHostname ; p = true ; }
if ( meinfo . UUID ) { intelamt . uuid = meinfo . UUID ; p = true ; }
if ( p == true ) {
2018-11-25 23:59:42 +03:00
var meInfoStr = JSON . stringify ( intelamt ) ;
if ( meInfoStr != lastMeInfo ) {
meshCoreObj . intelamt = intelamt ;
mesh . SendCommand ( meshCoreObj ) ;
lastMeInfo = meInfoStr ;
}
2018-09-22 02:34:35 +03:00
}
} catch ( ex ) { }
2018-02-12 04:13:26 +03:00
} ) ;
2017-12-13 03:04:54 +03:00
}
2018-09-22 02:34:35 +03:00
if ( flags & 2 ) {
// Update network information
sendNetworkUpdateNagle ( false ) ;
}
2017-12-13 03:04:54 +03:00
}
2018-01-04 23:15:21 +03:00
2018-12-28 08:31:20 +03:00
// Starting function
obj . start = function ( ) {
// Setup the mesh agent event handlers
mesh . AddCommandHandler ( handleServerCommand ) ;
mesh . AddConnectHandler ( handleServerConnection ) ;
// Parse input arguments
//var args = parseArgs(process.argv);
//console.log(args);
2019-03-05 10:48:45 +03:00
//resetMicroLms();
2018-01-12 22:41:26 +03:00
2018-12-14 21:19:25 +03:00
// Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
2018-09-22 02:34:35 +03:00
try {
var userSession = require ( 'user-sessions' ) ;
userSession . on ( 'changed' , function onUserSessionChanged ( ) {
userSession . enumerateUsers ( ) . then ( function ( users ) {
var u = [ ] , a = users . Active ;
2018-09-25 03:47:03 +03:00
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.
}
2018-11-25 23:59:42 +03:00
meshCoreObj . users = u ;
mesh . SendCommand ( meshCoreObj ) ;
2018-09-22 02:34:35 +03:00
} ) ;
2018-08-30 04:47:22 +03:00
} ) ;
2018-09-22 02:34:35 +03: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 ( ex ) { }
2017-08-28 19:27:45 +03:00
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03:00
obj . stop = function ( ) {
mesh . AddCommandHandler ( null ) ;
mesh . AddConnectHandler ( null ) ;
}
2018-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03: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-01-04 23:15:21 +03:00
2017-08-28 19:27:45 +03: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 ;
}
2018-07-03 00:34:10 +03:00
2017-08-28 19:27:45 +03:00
return obj ;
}
2018-07-03 00:34:10 +03:00
//
// Module startup
//
2018-09-20 21:45:12 +03:00
try {
var xexports = null , mainMeshCore = null ;
try { xexports = module . exports ; } catch ( e ) { }
2017-08-28 19:27:45 +03:00
2018-09-20 21:45:12 +03:00
if ( xexports != null ) {
// If we are running within NodeJS, export the core
module . exports . createMeshCore = createMeshCore ;
} else {
// If we are not running in NodeJS, launch the core
mainMeshCore = createMeshCore ( ) ;
mainMeshCore . start ( null ) ;
}
} catch ( ex ) {
require ( 'MeshAgent' ) . SendCommand ( { "action" : "msg" , "type" : "console" , "value" : "uncaughtException2: " + ex } ) ;
}