2017-08-28 12:27:45 -04:00
/ *
2018-01-04 15:15:21 -05:00
Copyright 2018 Intel Corporation
2017-08-28 12:27:45 -04:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
2018-01-04 15:15:21 -05:00
http : //www.apache.org/licenses/LICENSE-2.0
2017-08-28 12:27:45 -04:00
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
function createMeshCore ( agent ) {
var obj = { } ;
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
2017-10-23 17:09:58 -04:00
obj . meshCoreInfo = "MeshCore v4" ;
2017-08-28 12:27:45 -04:00
obj . meshCoreCapabilities = 14 ; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript
2017-10-04 23:35:52 -04:00
obj . useNativePipes = true ; //(process.platform == 'win32');
2017-08-28 12:27:45 -04:00
var meshServerConnectionState = 0 ;
var tunnels = { } ;
var lastSelfInfo = null ;
var lastNetworkInfo = null ;
2017-08-28 15:48:53 -04:00
var lastPublicLocationInfo = null ;
2017-08-28 12:27:45 -04:00
var selfInfoUpdateTimer = null ;
var http = require ( 'http' ) ;
2017-10-23 17:09:58 -04:00
var net = require ( 'net' ) ;
2017-08-28 12:27:45 -04:00
var fs = require ( 'fs' ) ;
2017-10-15 02:22:19 -04:00
var rtc = require ( 'ILibWebRTC' ) ;
2017-12-12 19:04:54 -05:00
var amtMei = null , amtLms = null , amtLmsState = 0 ;
var amtMeiConnected = 0 , amtMeiState = null , amtMeiTmpState = null ;
2017-10-04 23:35:52 -04:00
var wifiScannerLib = null ;
var wifiScanner = null ;
2017-12-12 19:04:54 -05:00
var networkMonitor = null ;
2018-01-09 23:13:41 -05:00
var amtscanner = null ;
/ *
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 ) ;
* /
2017-12-12 19:04:54 -05:00
// Try to load up the network monitor
try {
networkMonitor = require ( 'NetworkMonitor' ) ;
networkMonitor . on ( 'change' , function ( ) { sendNetworkUpdateNagle ( ) ; } ) ;
networkMonitor . on ( 'add' , function ( addr ) { sendNetworkUpdateNagle ( ) ; } ) ;
networkMonitor . on ( 'remove' , function ( addr ) { sendNetworkUpdateNagle ( ) ; } ) ;
} catch ( e ) { networkMonitor = null ; }
2018-01-09 23:13:41 -05: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); });
} catch ( e ) { amtscanner = null ; }
2018-01-04 15:15:21 -05:00
2017-12-12 19:04:54 -05:00
// Try to load up the MEI module
try {
var amtMeiLib = require ( 'amt_heci' ) ;
amtMeiConnected = 1 ;
amtMei = new amtMeiLib ( ) ;
amtMei . on ( 'error' , function ( e ) { amtMeiLib = null ; amtMei = null ; sendPeriodicServerUpdate ( ) ; } ) ;
amtMei . on ( 'connect' , function ( ) { amtMeiConnected = 2 ; getAmtInfo ( ) ; } ) ;
} catch ( e ) { amtMeiLib = null ; amtMei = null ; amtMeiConnected = - 1 ; }
2018-01-04 15:15:21 -05:00
2017-10-04 23:35:52 -04:00
// Try to load up the WIFI scanner
try {
2017-12-12 19:04:54 -05:00
var wifiScannerLib = require ( 'WifiScanner' ) ;
2017-10-04 23:35:52 -04:00
wifiScanner = new wifiScannerLib ( ) ;
wifiScanner . on ( 'accessPoint' , function ( data ) { sendConsoleText ( JSON . stringify ( data ) ) ; } ) ;
2017-12-12 19:04:54 -05:00
} catch ( e ) { wifiScannerLib = null ; wifiScanner = null ; }
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// If we are running in Duktape, agent will be null
if ( agent == null ) {
// Running in native agent, Import libraries
2017-12-19 11:50:19 -05:00
db = require ( 'SimpleDataStore' ) . Shared ( ) ;
sha = require ( 'SHA256Stream' ) ;
mesh = require ( 'MeshAgent' ) ;
2018-01-04 15:15:21 -05:00
childProcess = require ( 'child_process' ) ;
2017-08-28 12:27:45 -04:00
if ( mesh . hasKVM == 1 ) { obj . meshCoreCapabilities |= 1 ; }
} else {
// Running in nodejs
obj . meshCoreInfo += '-NodeJS' ;
obj . meshCoreCapabilities = 8 ;
2017-12-19 11:50:19 -05:00
mesh = agent . getMeshApi ( ) ;
2017-08-28 12:27:45 -04:00
}
2018-01-04 15:15:21 -05:00
2017-08-28 15:48:53 -04:00
// Get our location (lat/long) using our public IP address
2017-09-01 14:23:22 -04:00
var getIpLocationDataExInProgress = false ;
2017-10-23 17:09:58 -04:00
var getIpLocationDataExCounts = [ 0 , 0 ] ;
2017-08-28 15:48:53 -04:00
function getIpLocationDataEx ( func ) {
2017-09-01 14:23:22 -04:00
if ( getIpLocationDataExInProgress == true ) { return false ; }
2017-08-28 15:48:53 -04:00
try {
2017-09-01 14:23:22 -04:00
getIpLocationDataExInProgress = true ;
getIpLocationDataExCounts [ 0 ] ++ ;
2017-08-28 15:48:53 -04:00
http . request ( {
host : 'ipinfo.io' , // TODO: Use a HTTP proxy if needed!!!!
port : 80 ,
path : 'http://ipinfo.io/json' , // Use this service to get our geolocation
headers : { Host : "ipinfo.io" }
} ,
function ( resp ) {
2018-01-04 15:15:21 -05: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 14:23:22 -04:00
return true ;
2017-08-28 15:48:53 -04:00
}
2017-09-01 14:23:22 -04:00
catch ( e ) { return false ; }
2017-08-28 15:48:53 -04:00
}
2018-01-04 15:15:21 -05:00
2017-08-28 15:48:53 -04: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 15:15:21 -05:00
2017-08-28 15:48:53 -04: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-01 20:34:02 -04: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 15:48:53 -04: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-01 20:34:02 -04: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 15:48:53 -04:00
} ) ;
}
}
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04: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 15:15:21 -05:00
2017-08-28 12:27:45 -04: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-23 17:09:58 -04:00
if ( i != 0 ) {
while ( w . startsWith ( '/' ) || w . startsWith ( '\\' ) ) { w = w . substring ( 1 ) ; }
}
2017-08-28 12:27:45 -04:00
x . push ( w ) ;
}
}
if ( x . length == 0 ) return '/' ;
return x . join ( '/' ) ;
}
} ;
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Replace a string with a number if the string is an exact number
2017-09-20 17:44:22 -04:00
function toNumberIfNumber ( x ) { if ( ( typeof x == 'string' ) && ( + parseInt ( x ) === x ) ) { x = parseInt ( x ) ; } return x ; }
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Convert decimal to hex
function char2hex ( i ) { return ( i + 0x100 ) . toString ( 16 ) . substr ( - 2 ) . toUpperCase ( ) ; }
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04: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 15:15:21 -05:00
2017-08-28 12:27:45 -04: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 15:15:21 -05:00
2017-08-28 12:27:45 -04: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 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Convert an object to string with all functions
function objToString ( x , p , ret ) {
if ( ret == undefined ) ret = '' ;
if ( p == undefined ) p = 0 ;
if ( x == null ) { return '[null]' ; }
if ( p > 8 ) { return '[...]' ; }
if ( x == undefined ) { return '[undefined]' ; }
if ( typeof x == 'string' ) { if ( p == 0 ) return x ; return '"' + x + '"' ; }
if ( typeof x == 'buffer' ) { return '[buffer]' ; }
if ( typeof x != 'object' ) { return x ; }
var r = '{' + ( ret ? '\r\n' : ' ' ) ;
for ( var i in x ) { r += ( addPad ( p + 2 , ret ) + i + ': ' + objToString ( x [ i ] , p + 2 , ret ) + ( ret ? '\r\n' : ' ' ) ) ; }
return r + addPad ( p , ret ) + '}' ;
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04: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 15:15:21 -05:00
2017-08-28 12:27:45 -04: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 15:15:21 -05:00
2017-08-28 12:27:45 -04: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 15:15:21 -05:00
2017-09-20 17:44:22 -04: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 15:15:21 -05:00
2017-09-20 17:44:22 -04: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 15:15:21 -05:00
2017-09-20 17:44:22 -04: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 15:15:21 -05:00
2017-09-20 17:44:22 -04: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 12:27:45 -04:00
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04: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 15:48:53 -04:00
switch ( data . action ) {
case 'msg' : {
if ( data . type == '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 ) ;
}
2017-08-28 12:27:45 -04:00
}
2017-10-23 17:09:58 -04:00
else if ( ( data . type == 'tunnel' ) && ( data . value != null ) ) { // Process a new tunnel connection request
// Create a new tunnel object
var xurl = getServerTargetUrlEx ( data . value ) ;
if ( xurl != null ) {
2017-11-09 20:18:30 -05:00
var woptions = http . parseUri ( xurl ) ;
2018-01-04 15:15:21 -05:00
woptions . rejectUnauthorized = 0 ;
2017-11-09 20:18:30 -05:00
sendConsoleText ( JSON . stringify ( woptions ) ) ;
var tunnel = http . request ( woptions ) ;
2017-10-23 17:09:58 -04:00
tunnel . upgrade = onTunnelUpgrade ;
2017-11-09 20:18:30 -05:00
tunnel . onerror = function ( e ) { sendConsoleText ( 'ERROR: ' + JSON . stringify ( e ) ) ; }
2017-10-23 17:09:58 -04:00
tunnel . sessionid = data . sessionid ;
tunnel . rights = data . rights ;
tunnel . state = 0 ;
tunnel . url = xurl ;
tunnel . protocol = 0 ;
tunnel . tcpaddr = data . tcpaddr ;
tunnel . tcpport = data . tcpport ;
2018-01-04 15:15:21 -05:00
tunnel . end ( ) ;
sendConsoleText ( 'tunnel.end() called' ) ;
2017-10-23 17:09:58 -04:00
// Put the tunnel in the tunnels list
var index = 1 ;
while ( tunnels [ index ] ) { index ++ ; }
tunnel . index = index ;
tunnels [ index ] = tunnel ;
2018-01-04 15:15:21 -05:00
2017-10-23 17:09:58 -04:00
sendConsoleText ( 'New tunnel connection #' + index + ': ' + tunnel . url + ', rights: ' + tunnel . rights , data . sessionid ) ;
2017-08-28 15:48:53 -04:00
}
2017-08-28 12:27:45 -04:00
}
2017-08-28 15:48:53 -04:00
break ;
}
case 'wakeonlan' : {
// Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
sendConsoleText ( 'Server requesting wake-on-lan for: ' + data . macs . join ( ', ' ) ) ;
2017-09-20 17:44:22 -04:00
for ( var i in data . macs ) { sendWakeOnLan ( data . macs [ i ] ) ; }
2017-08-28 15:48:53 -04:00
break ;
}
2017-09-01 14:23:22 -04: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-05 20:19:28 -04: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 15:48:53 -04:00
break ;
2017-08-28 12:27:45 -04:00
}
}
}
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Called when a file changed in the file system
2017-12-14 17:57:52 -05:00
/ *
2017-08-28 12:27:45 -04:00
function onFileWatcher ( a , b ) {
2017-10-23 17:09:58 -04:00
console . log ( 'onFileWatcher' , a , b , this . path ) ;
2017-08-28 12:27:45 -04:00
var response = getDirectoryInfo ( this . path ) ;
if ( ( response != undefined ) && ( response != null ) ) { this . tunnel . s . write ( JSON . stringify ( response ) ) ; }
}
2017-12-14 17:57:52 -05:00
* /
2017-08-28 12:27:45 -04: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.
//console.log('a', objToString(results, 0, '.'));
if ( results != null ) {
for ( var i = 0 ; i < results . length ; ++ i ) {
var drive = { n : results [ i ] . name , t : 1 } ;
if ( results [ i ] . type == 'REMOVABLE' ) { drive . dt = 'removable' ; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
response . dir . push ( drive ) ;
}
}
} else {
// List all the files and folders in this path
if ( reqpath == '' ) { reqpath = '/' ; }
var xpath = obj . path . join ( reqpath , '*' ) ;
var results = null ;
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04: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 ] ) ;
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 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Tunnel callback operations
2017-10-23 17:09:58 -04:00
function onTunnelUpgrade ( response , s , head ) {
this . s = s ;
s . httprequest = this ;
s . end = onTunnelClosed ;
2018-01-04 15:15:21 -05:00
2017-10-23 17:09:58 -04: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 ;
} else {
// This is a normal connect for KVM/Terminal/Files
s . data = onTunnelData ;
}
}
2018-01-04 15:15:21 -05:00
2017-10-23 17:09:58 -04: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 15:15:21 -05:00
2017-10-23 17:09:58 -04: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
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
function onTunnelClosed ( ) {
sendConsoleText ( "Tunnel #" + this . httprequest . index + " closed." , this . httprequest . sessionid ) ;
delete tunnels [ this . httprequest . index ] ;
2018-01-04 15:15:21 -05:00
2017-12-14 17:57:52 -05:00
/ *
2017-08-28 12:27:45 -04: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-23 17:09:58 -04:00
delete this . httprequest . watcher ;
2017-08-28 12:27:45 -04:00
}
2017-12-14 17:57:52 -05:00
* /
2017-08-28 12:27:45 -04: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 ; }
}
function onTunnelSendOk ( ) { sendConsoleText ( "Tunnel #" + this . index + " SendOK." , this . sessionid ) ; }
function onTunnelData ( data ) {
2017-12-19 11:50:19 -05:00
//console.log("OnTunnelData");
//sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// If this is upload data, save it to file
if ( this . httprequest . uploadFile ) {
2018-01-02 19:52:49 -05: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 12:27:45 -04:00
return ;
}
// 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-01-04 15:15:21 -05:00
2017-09-05 20:19:28 -04:00
// Setup remote desktop & terminal without using native pipes
2017-10-15 02:22:19 -04:00
if ( ( this . httprequest . desktop ) && ( obj . useNativePipes == false ) ) {
if ( data . length > 21 && data . toString ( ) . startsWith ( '**********%%%%%%###**' ) ) {
var controlMsg = JSON . parse ( data . toString ( ) . substring ( 21 ) ) ;
if ( controlMsg . type == 'offer' ) {
this . webrtc = rtc . createConnection ( ) ;
this . webrtc . on ( 'connected' , function ( ) { sendConsoleText ( 'OnWebRTC_Connected' ) ; } ) ;
this . webrtc . on ( 'dataChannel' , function ( ) { sendConsoleText ( 'OnWebRTC_DataChannel' ) ; } ) ;
var counterOffer = this . webrtc . setOffer ( controlMsg . sdp ) ;
this . write ( '**********%%%%%%###**' + JSON . stringify ( { type : 'answer' , sdp : counterOffer } ) ) ;
sendConsoleText ( 'counterOfferSent' ) ;
} else {
sendConsoleText ( JSON . stringify ( controlMsg ) ) ;
}
} else {
this . httprequest . desktop . kvm . write ( data ) ;
}
return ;
}
2017-09-06 13:08:01 -04:00
if ( ( this . httprequest . terminal ) && ( obj . useNativePipes == false ) ) { this . httprequest . terminal . write ( data ) ; return ; }
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
if ( this . httprequest . state == 0 ) {
// Check if this is a relay connection
if ( data == 'c' ) { this . httprequest . state = 1 ; sendConsoleText ( "Tunnel #" + this . httprequest . index + " now active" , this . httprequest . sessionid ) ; }
} else {
// Handle tunnel data
if ( this . httprequest . protocol == 0 ) { // 1 = SOL, 2 = KVM, 3 = IDER, 4 = Files, 5 = FileTransfer
// Take a look at the protocolab
this . httprequest . protocol = parseInt ( data ) ;
if ( typeof this . httprequest . protocol != 'number' ) { this . httprequest . protocol = 0 ; }
if ( this . httprequest . protocol == 1 ) {
2017-09-06 13:08:01 -04:00
if ( obj . useNativePipes == false ) {
2017-09-05 20:19:28 -04:00
// Remote Terminal without using native pipes
if ( process . platform == "win32" ) {
2018-01-04 15:15:21 -05:00
this . httprequest . terminal = childProcess . execFile ( "%windir%\\system32\\cmd.exe" ) ;
2017-09-05 20:19:28 -04:00
} else {
2018-01-04 15:15:21 -05:00
this . httprequest . terminal = childProcess . execFile ( "/bin/sh" , [ "sh" ] , { type : childProcess . SpawnTypes . TERM } ) ;
2017-09-05 20:19:28 -04:00
}
this . httprequest . terminal . tunnel = this ;
2018-01-04 15:15:21 -05:00
this . httprequest . terminal . on ( 'exit' , function ( ecode , sig ) { this . tunnel . end ( ) ; } ) ;
this . httprequest . terminal . stdout . on ( 'data' , function ( chunk ) { this . parent . tunnel . write ( chunk ) ; } ) ;
this . httprequest . terminal . stderr . on ( 'data' , function ( chunk ) { this . parent . tunnel . write ( chunk ) ; } ) ;
2017-08-28 12:27:45 -04:00
} else {
2017-09-05 20:19:28 -04:00
// Remote terminal using native pipes
if ( process . platform == "win32" ) {
2018-01-04 15:15:21 -05:00
this . httprequest . process = childProcess . execFile ( "%windir%\\system32\\cmd.exe" ) ;
2017-09-05 20:19:28 -04:00
} else {
2018-01-04 15:15:21 -05:00
this . httprequest . process = childProcess . execFile ( "/bin/sh" , [ "sh" ] , { type : childProcess . SpawnTypes . TERM } ) ;
2017-09-05 20:19:28 -04:00
}
this . httprequest . process . tunnel = this ;
2018-01-04 15:15:21 -05:00
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.
2018-01-12 14:41:26 -05:00
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 12:27:45 -04:00
}
}
if ( this . httprequest . protocol == 2 ) {
2017-09-06 13:08:01 -04:00
if ( obj . useNativePipes == false ) {
2017-09-05 20:19:28 -04:00
// Remote Desktop without using native pipes
this . httprequest . desktop = { state : 0 , kvm : mesh . getRemoteDesktopStream ( ) , tunnel : this } ;
this . httprequest . desktop . kvm . tunnel = this ;
this . httprequest . desktop . kvm . on ( 'data' , function ( data ) { this . tunnel . write ( data ) ; } ) ;
this . desktop = this . httprequest . desktop ;
this . end = function ( ) { if ( -- this . desktop . kvm . connectionCount == 0 ) { this . httprequest . desktop . kvm . end ( ) ; } } ;
if ( this . httprequest . desktop . kvm . hasOwnProperty ( "connectionCount" ) ) { this . httprequest . desktop . kvm . connectionCount ++ ; } else { this . httprequest . desktop . kvm . connectionCount = 1 ; }
} else {
// 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 ;
this . end = function ( ) {
-- this . desktop . kvm . connectionCount ;
this . unpipe ( this . httprequest . desktop . kvm ) ;
this . httprequest . desktop . kvm . unpipe ( this ) ;
if ( this . desktop . kvm . connectionCount == 0 ) { this . httprequest . desktop . kvm . end ( ) ; }
} ;
if ( this . httprequest . desktop . kvm . hasOwnProperty ( "connectionCount" ) ) { this . httprequest . desktop . kvm . connectionCount ++ ; } else { this . httprequest . desktop . kvm . connectionCount = 1 ; }
2017-12-19 11:50:19 -05:00
//this.write('Hello!');
//sendConsoleText('KVM WriteHello');
this . pipe ( this . httprequest . desktop . kvm , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
this . httprequest . desktop . kvm . pipe ( this , { dataTypeSkip : 1 } ) ; // 0 = Binary, 1 = Text.
//this.on('data', function (data) { sendConsoleText('KVM: ' + data); });
2017-09-05 20:19:28 -04:00
}
2017-08-28 12:27:45 -04:00
}
else if ( this . httprequest . protocol == 5 ) {
// 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
// TODO ADD REMOTE DESKTOP (This is test code)
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 ) { } ;
if ( ( cmd == null ) || ( cmd . action == undefined ) ) { return ; }
2017-10-16 23:11:03 -04:00
if ( ( cmd . path != null ) && ( process . platform != 'win32' ) && ( cmd . path [ 0 ] != '/' ) ) { cmd . path = '/' + cmd . path ; } // Add '/' to paths on non-windows
2018-01-02 19:52:49 -05:00
//console.log(objToString(cmd, 0, '.'));
2017-08-28 12:27:45 -04:00
switch ( cmd . action ) {
case 'ls' : {
2017-12-14 17:57:52 -05:00
/ *
2017-08-28 12:27:45 -04: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-14 17:57:52 -05:00
* /
2017-08-28 12:27:45 -04:00
// Send the folder content to the browser
var response = getDirectoryInfo ( cmd . path ) ;
if ( cmd . reqid != undefined ) { response . reqid = cmd . reqid ; }
2018-01-02 19:52:49 -05:00
this . write ( new Buffer ( JSON . stringify ( response ) ) ) ;
2018-01-04 15:15:21 -05:00
2017-12-14 17:57:52 -05:00
/ *
2017-08-28 12:27:45 -04: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-14 17:57:52 -05:00
* /
2017-08-28 12:27:45 -04:00
break ;
}
case 'mkdir' : {
// Create a new empty folder
fs . mkdirSync ( cmd . path ) ;
break ;
}
case 'rm' : {
// Remove many files or folders
for ( var i in cmd . delfiles ) {
var fullpath = obj . path . join ( cmd . path , cmd . delfiles [ i ] ) ;
try { fs . unlinkSync ( fullpath ) ; } catch ( e ) { console . log ( e ) ; }
}
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 ;
}
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-02 19:52:49 -05: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 12:27:45 -04:00
this . httprequest . downloadFileId = cmd . reqid ;
this . httprequest . downloadFilePtr = 0 ;
2018-01-02 19:52:49 -05:00
if ( this . httprequest . downloadFile ) { this . write ( new Buffer ( JSON . stringify ( { action : 'downloadstart' , reqid : this . httprequest . downloadFileId } ) ) ) ; }
2017-08-28 12:27:45 -04: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 ;
}
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-02 19:52:49 -05: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 12:27:45 -04:00
this . httprequest . uploadFileid = cmd . reqid ;
2018-01-02 19:52:49 -05:00
if ( this . httprequest . uploadFile ) { this . write ( new Buffer ( JSON . stringify ( { action : 'uploadstart' , reqid : this . httprequest . uploadFileid } ) ) ) ; }
2017-08-28 12:27:45 -04:00
break ;
}
}
}
//sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
}
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Console state
var consoleWebSockets = { } ;
var consoleHttpRequest = null ;
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04: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-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Process a mesh agent console command
function processConsoleCommand ( cmd , args , rights , sessionid ) {
try {
var response = null ;
switch ( cmd ) {
case 'help' : { // Displays available commands
2018-01-12 14:41:26 -05:00
response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, amt, netinfo, location, power, wakeonlan, scanwifi, scanamt.' ;
break ;
}
case 'eval' : { // Eval JavaScript
if ( args [ '_' ] . length < 1 ) {
response = 'Proper usage: eval "JavaScript code"' ; // Display correct command usage
} else {
response = JSON . stringify ( mesh . eval ( args [ '_' ] [ 0 ] ) ) ;
}
2017-08-28 12:27:45 -04: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
2017-12-12 19:04:54 -05:00
response = 'Current Core: ' + obj . meshCoreInfo + '.\r\nAgent Time: ' + Date ( ) + '.\r\nUser Rights: 0x' + rights . toString ( 16 ) + '.\r\nPlatform Info: ' + process . platform + '.\r\nCapabilities: ' + obj . meshCoreCapabilities + '.\r\nNative Pipes: ' + obj . useNativePipes + '.\r\nServer URL: ' + mesh . ServerUrl + '.' ;
if ( amtLmsState >= 0 ) { response += '\r\nBuilt -in LMS: ' + [ 'Disabled' , 'Connecting..' , 'Connected' ] [ amtLmsState ] + '.' ; }
response += '\r\nModules: ' + JSON . stringify ( addedModules ) + '' ;
2018-01-12 14:41:26 -05:00
response += '\r\nServerConnected: ' + mesh . isControlChannelConnected + '' ;
2017-11-07 20:05:22 -05:00
var oldNodeId = db . Get ( 'OldNodeId' ) ;
2017-12-12 19:04:54 -05:00
if ( oldNodeId != null ) { response += '\r\nOldNodeID: ' + oldNodeId + '.' ; }
2017-12-19 11:50:19 -05:00
response += '\r\ServerState: ' + meshServerConnectionState + '.' ;
2017-08-28 12:27:45 -04:00
break ;
}
case 'selfinfo' : { // Return self information block
response = JSON . stringify ( buildSelfInfo ( ) ) ;
break ;
}
case 'args' : { // Displays parsed command arguments
response = 'args ' + objToString ( args , 0 , '.' ) ;
break ;
}
case 'print' : { // Print a message on the mesh agent console, does nothing when running in the background
var r = [ ] ;
for ( var i in args [ '_' ] ) { r . push ( args [ '_' ] [ i ] ) ; }
console . log ( r . join ( ' ' ) ) ;
response = 'Message printed on agent console.' ;
break ;
}
case 'type' : { // Returns the content of a file
if ( args [ '_' ] . length == 0 ) {
response = 'Proper usage: type (filepath) [maxlength]' ; // Display correct command usage
} else {
var max = 4096 ;
if ( ( args [ '_' ] . length > 1 ) && ( typeof args [ '_' ] [ 1 ] == 'number' ) ) { max = args [ '_' ] [ 1 ] ; }
if ( max > 4096 ) max = 4096 ;
var buf = new Buffer ( max ) , fd = fs . openSync ( args [ '_' ] [ 0 ] , "r" ) , r = fs . readSync ( fd , buf , 0 , max ) ; // Read the file content
response = buf . toString ( ) ;
var i = response . indexOf ( '\n' ) ;
if ( ( i > 0 ) && ( response [ i - 1 ] != '\r' ) ) { response = response . split ( '\n' ) . join ( '\r\n' ) ; }
if ( r == max ) response += '...' ;
fs . closeSync ( fd ) ;
}
break ;
}
2017-09-25 14:00:57 -04:00
case 'dbkeys' : { // Return all data store keys
response = JSON . stringify ( db . Keys ) ;
break ;
}
2017-08-28 12:27:45 -04: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-20 17:44:22 -04:00
var options = http . parseUri ( args [ '_' ] [ 0 ] ) ;
2017-08-28 12:27:45 -04: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-20 17:44:22 -04:00
response = 'HTTPGET ' + options . protocol + '//' + options . host + ':' + options . port + options . path ;
2017-08-28 12:27:45 -04: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-01 20:34:02 -04:00
try {
2018-01-04 15:15:21 -05:00
var options = http . parseUri ( args [ '_' ] [ 0 ] ) ;
options . rejectUnauthorized = 0 ;
httprequest = http . request ( options ) ;
2017-09-01 20:34:02 -04:00
} catch ( e ) { response = 'Invalid HTTP websocket request' ; }
2017-08-28 12:27:45 -04:00
if ( httprequest != null ) {
httprequest . upgrade = onWebSocketUpgrade ;
2017-11-09 20:18:30 -05:00
httprequest . onerror = function ( e ) { sendConsoleText ( 'ERROR: ' + JSON . stringify ( e ) ) ; }
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04: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-01 20:34:02 -04:00
if ( httprequest . s != null ) { httprequest . s . end ( ) ; } else { httprequest . end ( ) ; }
2017-08-28 12:27:45 -04: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-23 17:09:58 -04:00
}
2017-08-28 12:27:45 -04:00
}
break ;
}
case 'amt' : { // Show Intel AMT status
2017-12-12 19:04:54 -05:00
if ( amtMeiState != null ) {
response = objToString ( amtMeiState , 0 , '.' ) ;
2017-08-28 12:27:45 -04:00
} else {
response = 'This mesh agent does not support Intel AMT.' ;
}
break ;
}
case 'netinfo' : { // Show network interface information
2017-09-20 17:44:22 -04:00
//response = objToString(mesh.NetInfo, 0, '.');
var interfaces = require ( 'os' ) . networkInterfaces ( ) ;
response = objToString ( interfaces , 0 , '.' ) ;
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 12:27:45 -04:00
break ;
}
case 'sendall' : { // Send a message to all consoles on this mesh
sendConsoleText ( args [ '_' ] . join ( ' ' ) ) ;
break ;
}
2017-09-01 14:23:22 -04: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 15:48:53 -04:00
break ;
}
2017-09-13 14:25:57 -04:00
case 'location' : {
getIpLocationData ( function ( location ) {
sendConsoleText ( objToString ( { "action" : "iplocation" , "type" : "publicip" , "value" : location } , 0 , '.' ) ) ;
} ) ;
break ;
}
2017-09-20 17:44:22 -04:00
case 'parseuri' : {
response = JSON . stringify ( http . parseUri ( args [ '_' ] [ 0 ] ) ) ;
break ;
}
2017-10-04 23:35:52 -04: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-03 21:31:20 -04:00
break ;
}
2018-01-09 23:13:41 -05: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 ;
}
2017-08-28 12:27:45 -04: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 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Send a mesh agent console command
2017-08-28 15:48:53 -04: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 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Called before the process exits
//process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04: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-07 20:05:22 -05:00
var oldNodeId = db . Get ( 'OldNodeId' ) ;
if ( oldNodeId != null ) { mesh . SendCommand ( { action : 'mc1migration' , oldnodeid : oldNodeId } ) ; }
2017-08-28 12:27:45 -04:00
sendPeriodicServerUpdate ( true ) ;
2017-12-12 19:04:54 -05:00
//if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 60000); } // Should be a long time, like 20 minutes. For now, 1 minute.
2017-08-28 12:27:45 -04:00
}
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Build a bunch a self information data that will be sent to the server
// We need to do this periodically and if anything changes, send the update to the server.
function buildSelfInfo ( ) {
var r = { "action" : "coreinfo" , "value" : obj . meshCoreInfo , "caps" : obj . meshCoreCapabilities } ;
if ( mesh . hasHECI == 1 ) {
2017-12-12 19:04:54 -05:00
var meinfo = amtMeiState ;
2017-08-28 12:27:45 -04:00
var amtPresent = false , intelamt = { } ;
2017-12-12 19:04:54 -05:00
if ( meinfo != null ) {
if ( meinfo . Versions && meinfo . Versions . AMT ) { intelamt . ver = meinfo . Versions . AMT ; amtPresent = true ; }
if ( meinfo . ProvisioningState ) { intelamt . state = meinfo . ProvisioningState ; amtPresent = true ; }
if ( meinfo . flags ) { intelamt . flags = meinfo . Flags ; amtPresent = true ; }
if ( meinfo . OsHostname ) { intelamt . host = meinfo . OsHostname ; amtPresent = true ; }
if ( amtPresent == true ) { r . intelamt = intelamt }
}
2017-08-28 12:27:45 -04:00
}
return JSON . stringify ( r ) ;
}
2018-01-04 15:15:21 -05:00
2017-12-12 19:04:54 -05: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 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Update the network interfaces information data
var netInfo = mesh . NetInfo ;
netInfo . action = 'netinfo' ;
var netInfoStr = JSON . stringify ( netInfo ) ;
2017-08-28 15:48:53 -04:00
if ( ( force == true ) || ( clearGatewayMac ( netInfoStr ) != clearGatewayMac ( lastNetworkInfo ) ) ) { mesh . SendCommand ( netInfo ) ; lastNetworkInfo = netInfoStr ; }
2017-08-28 12:27:45 -04:00
}
2018-01-04 15:15:21 -05:00
2017-12-12 19:04:54 -05:00
// Called periodically to check if we need to send updates to the server
function sendPeriodicServerUpdate ( force ) {
2017-12-19 11:50:19 -05:00
if ( ( amtMeiConnected != 1 ) || ( force == true ) ) { // If we are pending MEI connection, hold off on updating the server on self-info
2017-12-12 19:04:54 -05:00
// Update the self information data
var selfInfo = buildSelfInfo ( ) , selfInfoStr = JSON . stringify ( selfInfo ) ;
if ( ( force == true ) || ( selfInfoStr != lastSelfInfo ) ) { mesh . SendCommand ( selfInfo ) ; lastSelfInfo = selfInfoStr ; }
}
// Update network information
sendNetworkUpdateNagle ( force ) ;
}
2018-01-04 15:15:21 -05:00
2017-12-12 19:04:54 -05:00
// Get Intel AMT information using MEI
function getAmtInfo ( func ) {
if ( amtMei == null || amtMeiConnected != 2 ) { if ( func != null ) { func ( null ) ; } return ; }
amtMeiTmpState = { Flags : 0 } ; // Flags: 1=EHBC, 2=CCM, 4=ACM
amtMei . getProtocolVersion ( function ( result ) { if ( result != null ) { amtMeiTmpState . MeiVersion = result ; } } ) ;
amtMei . getVersion ( function ( val ) { amtMeiTmpState . Versions = { } ; for ( var version in val . Versions ) { amtMeiTmpState . Versions [ val . Versions [ version ] . Description ] = val . Versions [ version ] . Version ; } } ) ;
amtMei . getProvisioningMode ( function ( result ) { amtMeiTmpState . ProvisioningMode = result . mode ; } ) ;
amtMei . getProvisioningState ( function ( result ) { amtMeiTmpState . ProvisioningState = result . state ; } ) ;
amtMei . getEHBCState ( function ( result ) { if ( result . EHBC == true ) { amtMeiTmpState . Flags += 1 ; } } ) ;
amtMei . getControlMode ( function ( result ) { if ( result . controlMode == 1 ) { amtMeiTmpState . Flags += 2 ; } if ( result . controlMode == 2 ) { amtMeiTmpState . Flags += 4 ; } } ) ;
//amtMei.getMACAddresses(function (result) { amtMeiTmpState.mac = result; });
amtMei . getDnsSuffix ( function ( result ) { if ( result != null ) { amtMeiTmpState . dns = result ; } amtMeiState = amtMeiTmpState ; sendPeriodicServerUpdate ( ) ; if ( func != null ) { func ( amtMeiState ) ; } } ) ;
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Called on MicroLMS Intel AMT user notification
function handleAmtNotification ( notification ) {
var amtMessage = notification . messageId ;
var amtMessageArg = notification . messageArguments ;
var notify = null ;
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
switch ( amtMessage ) {
case 'iAMT0050' : {
// Serial over lan
if ( amtMessageArg == '48' ) {
// Connected
notify = 'Intel® AMT Serial-over-LAN connected' ;
}
else if ( amtMessageArg == '49' ) {
// Disconnected
notify = 'Intel® AMT Serial-over-LAN disconnected' ;
}
}
case 'iAMT0052' : {
// HWKVM
if ( amtMessageArg == '1' ) {
// Connected
notify = 'Intel® AMT KVM connected' ;
}
else if ( amtMessageArg == '2' ) {
// Disconnected
notify = 'Intel® AMT KVM disconnected' ;
}
break ;
}
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
if ( notify != null ) {
var notification = { "action" : "msg" , "type" : "notify" , "value" : notify , "tag" : "general" } ;
//mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
//console.log("handleAmtNotification", JSON.stringify(notification));
}
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
// Starting function
obj . start = function ( ) {
// Setup the mesh agent event handlers
mesh . AddCommandHandler ( handleServerCommand ) ;
mesh . AddConnectHandler ( handleServerConnection ) ;
2018-01-12 14:41:26 -05:00
2017-08-28 12:27:45 -04:00
// Parse input arguments
//var args = parseArgs(process.argv);
//console.log(args);
2018-01-04 15:15:21 -05:00
2017-12-12 19:04:54 -05:00
// Launch LMS
try {
var lme _heci = require ( 'lme_heci' ) ;
amtLmsState = 1 ;
amtLms = new lme _heci ( ) ;
amtLms . on ( 'error' , function ( e ) { amtLmsState = 0 ; amtLms = null ; } ) ;
amtLms . on ( 'connect' , function ( ) { amtLmsState = 2 ; } ) ;
} catch ( e ) { amtLmsState = - 1 ; amtLms = null ; }
2018-01-12 14:41:26 -05:00
// Check if the control channel is connected
if ( mesh . isControlChannelConnected ) {
sendPeriodicServerUpdate ( true ) ; // Send the server update
}
//console.log('Stopping.');
//process.exit();
2017-08-28 12:27:45 -04:00
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
obj . stop = function ( ) {
mesh . AddCommandHandler ( null ) ;
mesh . AddConnectHandler ( null ) ;
}
2018-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04: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 15:15:21 -05:00
2017-08-28 12:27:45 -04: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-01-04 15:15:21 -05:00
2017-08-28 12:27:45 -04:00
return obj ;
}
var xexports = null ;
try { xexports = module . exports ; } catch ( e ) { }
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
createMeshCore ( ) . start ( null ) ;
}