2017-10-31 23:19:58 +00:00
var fs = require ( 'fs' ) ;
var os = require ( 'os' ) ;
var net = require ( 'net' ) ;
var http = require ( 'http' ) ;
var dgram = require ( 'dgram' ) ;
var httpHeaders = require ( 'http-headers' ) ;
var tcpserver = null ;
var broadcastSockets = { } ;
var multicastSockets = { } ;
var discoveryInterval = null ;
var membershipIPv4 = '239.255.255.235' ;
var membershipIPv6 = 'FF02:0:0:0:0:0:0:FE' ;
/ *
// Route Settings
var settings = {
action : 'route' ,
localPort : 1234 ,
remoteName : 'AmtMachine7' ,
remoteNodeId : 'node//nmiPnDhT3vHKu$zg296YC5RjK53Trgh3Cimx3K8GVrFh$xch0UAAett2rbJpeddc' ,
remotePort : 3389 ,
username : 'a' ,
password : 'a' ,
serverUrl : 'wss://devbox.mesh.meshcentral.com:443/meshrelay.ashx' ,
serverId : 'D99362D5ED8BAEA8BF9E743B34B242256370C460FD66CB62373C6CFCB204D6D707403E396CF0EF6DC2B3A42F735135FD' , // SHA384 of server HTTPS public key
serverHttpsHash : 'D9DE9E27A229B5355708A3672FB23237CC994A680B3570D242A91E36B4AE5BC9' , // SHA256 of server HTTPS certificate
debugLevel : 0
}
* /
// Check the server certificate fingerprint
function onVerifyServer ( clientName , certs ) {
try { for ( var i in certs ) { if ( certs [ i ] . fingerprint . replace ( /:/g , '' ) == settings . serverHttpsHash ) { return ; } } } catch ( e ) { }
if ( serverhash != null ) { console . log ( 'Error: Failed to verify server certificate.' ) ; return false ; }
}
// Print a debug message
function debug ( level , message ) { if ( ( settings . debugLevel != null ) && ( settings . debugLevel >= level ) ) { console . log ( message ) ; } }
2017-11-03 01:44:27 +00:00
// Parse the input arguments into an object
function parceArguments ( argv ) {
var r = { } ;
for ( var i in argv ) {
i = parseInt ( i ) ;
if ( argv [ i ] . startsWith ( '--' ) == true ) {
var key = argv [ i ] . substring ( 2 ) . toLowerCase ( ) , val = true ;
if ( ( ( i + 1 ) < argv . length ) && ( argv [ i + 1 ] . startsWith ( '--' ) == false ) ) { val = argv [ i + 1 ] ; }
r [ key ] = val ;
}
}
return r ;
}
2017-10-31 23:19:58 +00:00
// Start the router, start by listening to the local port
function run ( argv ) {
2017-11-03 01:44:27 +00:00
var args = parceArguments ( argv ) ;
console . log ( 'MeshCentral Command v1.0b' ) ;
2017-10-31 23:19:58 +00:00
var actionpath = 'meshaction.txt' ;
2017-11-03 01:44:27 +00:00
if ( args . actionfile != null ) { actionpath = args . actionfile ; }
2017-10-31 23:19:58 +00:00
// Load the action file
var actionfile = null ;
try { actionfile = fs . readFileSync ( actionpath ) ; } catch ( e ) { }
2017-11-03 01:44:27 +00:00
if ( actionfile == null ) { console . log ( 'Unable to load \"' + actionpath + '\". Create this file or specify the location using --actionfile [filename].' ) ; process . exit ( 1 ) ; }
2017-10-31 23:19:58 +00:00
try { settings = JSON . parse ( actionfile ) ; } catch ( e ) { console . log ( actionpath , e ) ; process . exit ( 1 ) ; }
2017-11-03 01:44:27 +00:00
// Set the arguments
if ( ( typeof args . localport ) == 'string' ) { settings . localport = parseInt ( args . localport ) ; }
if ( ( typeof args . remotenodeid ) == 'string' ) { settings . remoteNodeId = args . remotenodeid ; }
if ( ( typeof args . username ) == 'string' ) { settings . username = args . username ; }
if ( ( typeof args . password ) == 'string' ) { settings . password = args . password ; }
if ( ( typeof args . user ) == 'string' ) { settings . username = args . user ; }
if ( ( typeof args . pass ) == 'string' ) { settings . password = args . pass ; }
if ( ( typeof args . serverid ) == 'string' ) { settings . serverId = args . serverid ; }
if ( ( typeof args . serverhttpshash ) == 'string' ) { settings . serverHttpsHash = args . serverhttpshash ; }
if ( ( typeof args . remoteport ) == 'string' ) { settings . remotePort = parseInt ( args . remoteport ) ; }
2017-10-31 23:19:58 +00:00
// Validate meshaction.txt
if ( settings . action == null ) { console . log ( 'No \"action\" specified.' ) ; process . exit ( 1 ) ; }
settings . action = settings . action . toLowerCase ( ) ;
if ( settings . action == 'route' ) {
2017-11-03 01:44:27 +00:00
if ( ( settings . localPort == null ) || ( typeof settings . localPort != 'number' ) || ( settings . localPort < 0 ) || ( settings . localPort > 65535 ) ) { console . log ( 'No or invalid \"localPort\" specified, use --localport [localport].' ) ; process . exit ( 1 ) ; }
2017-10-31 23:19:58 +00:00
if ( ( settings . remoteNodeId == null ) || ( typeof settings . remoteNodeId != 'string' ) ) { console . log ( 'No or invalid \"remoteNodeId\" specified.' ) ; process . exit ( 1 ) ; }
2017-11-03 01:44:27 +00:00
if ( ( settings . username == null ) || ( typeof settings . username != 'string' ) || ( settings . username == '' ) ) { console . log ( 'No or invalid \"username\" specified, use --username [username].' ) ; process . exit ( 1 ) ; }
if ( ( settings . password == null ) || ( typeof settings . password != 'string' ) || ( settings . password == '' ) ) { console . log ( 'No or invalid \"password\" specified, use --password [password].' ) ; process . exit ( 1 ) ; }
2017-10-31 23:19:58 +00:00
if ( ( settings . serverId == null ) || ( typeof settings . serverId != 'string' ) || ( settings . serverId . length != 96 ) ) { console . log ( 'No or invalid \"serverId\" specified.' ) ; process . exit ( 1 ) ; }
if ( ( settings . serverHttpsHash == null ) || ( typeof settings . serverHttpsHash != 'string' ) || ( settings . serverHttpsHash . length != 96 ) ) { console . log ( 'No or invalid \"serverHttpsHash\" specified.' ) ; process . exit ( 1 ) ; }
2017-11-03 01:44:27 +00:00
if ( ( settings . remotePort == null ) || ( typeof settings . remotePort != 'number' ) || ( settings . remotePort < 0 ) || ( settings . remotePort > 65535 ) ) { console . log ( 'No or invalid \"remotePort\" specified, use --remoteport [remoteport].' ) ; process . exit ( 1 ) ; }
2017-10-31 23:19:58 +00:00
} else {
console . log ( 'Invalid \"action\" specified.' ) ; process . exit ( 1 ) ;
}
debug ( 1 , "Settings: " + JSON . stringify ( settings ) ) ;
if ( settings . serverUrl != null ) { startRouter ( ) ; } else { discoverMeshServer ( ) ; }
}
// Starts the router
function startRouter ( ) {
tcpserver = net . createServer ( OnTcpClientConnected ) ;
tcpserver . on ( 'error' , function ( err ) { console . log ( err ) ; process . exit ( 0 ) ; } ) ;
tcpserver . listen ( settings . localPort , function ( ) {
// We started listening.
if ( settings . remoteName == null ) {
console . log ( 'Redirecting local port ' + settings . localPort + ' to remote port ' + settings . remotePort + '.' ) ;
} else {
console . log ( 'Redirecting local port ' + settings . localPort + ' to ' + settings . remoteName + ':' + settings . remotePort + '.' ) ;
}
console . log ( 'Press ctrl-c to terminal.' ) ;
// If settings has a "cmd", run it now.
//process.exec("notepad.exe");
} ) ;
}
// Called when a TCP connect is received on the local port. Launch a tunnel.
function OnTcpClientConnected ( c ) {
try {
// 'connection' listener
debug ( 1 , 'Client connected' ) ;
c . on ( 'end' , function ( ) { disconnectTunnel ( this , this . websocket , 'Client closed' ) ; } ) ;
c . pause ( ) ;
try {
options = http . parseUri ( settings . serverUrl + '?user=' + settings . username + '&pass=' + settings . password + '&nodeid=' + settings . remoteNodeId + '&tcpport=' + settings . remotePort ) ;
} catch ( e ) { console . log ( 'Unable to parse \"serverUrl\".' ) ; process . exit ( 1 ) ; }
options . checkServerIdentity = onVerifyServer ;
c . websocket = http . request ( options ) ;
c . websocket . tcp = c ;
c . websocket . tunneling = false ;
c . websocket . upgrade = OnWebSocket ;
c . websocket . on ( 'error' , function ( msg ) { console . log ( msg ) ; } ) ;
c . websocket . end ( ) ;
} catch ( e ) { debug ( 2 , e ) ; }
}
// Disconnect both TCP & WebSocket connections and display a message.
function disconnectTunnel ( tcp , ws , msg ) {
if ( ws != null ) { try { ws . end ( ) ; } catch ( e ) { debug ( 2 , e ) ; } }
if ( tcp != null ) { try { tcp . end ( ) ; } catch ( e ) { debug ( 2 , e ) ; } }
debug ( 1 , 'Tunnel disconnected: ' + msg ) ;
}
// Called when the web socket gets connected
function OnWebSocket ( msg , s , head ) {
debug ( 1 , 'Websocket connected' ) ;
s . on ( 'data' , function ( msg ) {
if ( this . parent . tunneling == false ) {
msg = msg . toString ( ) ;
if ( msg == 'c' ) {
this . parent . tunneling = true ; this . pipe ( this . parent . tcp ) ; this . parent . tcp . pipe ( this ) ; debug ( 1 , 'Tunnel active' ) ;
} else if ( ( msg . length > 6 ) && ( msg . substring ( 0 , 6 ) == 'error:' ) ) {
console . log ( msg . substring ( 6 ) ) ;
disconnectTunnel ( this . tcp , this , msg . substring ( 6 ) ) ;
}
}
} ) ;
s . on ( 'error' , function ( msg ) { disconnectTunnel ( this . tcp , this , 'Websocket error' ) ; } ) ;
s . on ( 'close' , function ( msg ) { disconnectTunnel ( this . tcp , this , 'Websocket closed' ) ; } ) ;
s . parent = this ;
}
// Try to discover the location of the mesh server
function discoverMeshServer ( ) { console . log ( 'Looking for server...' ) ; discoveryInterval = setInterval ( discoverMeshServerOnce , 5000 ) ; discoverMeshServerOnce ( ) ; }
// Try to discover the location of the mesh server only once
function discoverMeshServerOnce ( ) {
var interfaces = os . networkInterfaces ( ) ;
for ( var adapter in interfaces ) {
if ( interfaces . hasOwnProperty ( adapter ) ) {
for ( var i = 0 ; i < interfaces [ adapter ] . length ; ++ i ) {
var addr = interfaces [ adapter ] [ i ] ;
multicastSockets [ i ] = dgram . createSocket ( { type : ( addr . family == "IPv4" ? "udp4" : "udp6" ) } ) ;
multicastSockets [ i ] . bind ( { address : addr . address , exclusive : false } ) ;
if ( addr . family == "IPv4" ) {
multicastSockets [ i ] . addMembership ( membershipIPv4 ) ;
//multicastSockets[i].setMulticastLoopback(true);
multicastSockets [ i ] . once ( 'message' , OnMulticastMessage ) ;
multicastSockets [ i ] . send ( settings . serverId , 16989 , membershipIPv4 ) ;
}
}
}
}
}
// Called when a multicast packet is received
function OnMulticastMessage ( msg , rinfo ) {
var m = msg . toString ( ) . split ( '|' ) ;
if ( ( m . length == 3 ) && ( m [ 0 ] == 'MeshCentral2' ) && ( m [ 1 ] == settings . serverId ) ) {
settings . serverUrl = m [ 2 ] . replace ( '%s' , rinfo . address ) . replace ( '/agent.ashx' , '/meshrelay.ashx' ) ;
console . log ( 'Found server at ' + settings . serverUrl + '.' ) ;
if ( discoveryInterval != null ) { clearInterval ( discoveryInterval ) ; discoveryInterval = null ; }
startRouter ( ) ;
}
}
2017-11-03 01:44:27 +00:00
try { run ( process . argv ) ; } catch ( e ) { console . log ( e ) ; }