2017-08-28 12:27:45 -04:00
/ * *
* @ description Meshcentral MeshRelay
* @ author Ylian Saint - Hilaire
* @ version v0 . 0.1
* /
// Construct a MeshRelay object, called upon connection
module . exports . CreateMeshRelayKey = function ( parent , func ) {
parent . crypto . randomBytes ( 16 , function ( err , buf ) {
var key = buf . toString ( 'hex' ) . toUpperCase ( ) + ':' + Date . now ( ) ;
key += ':' + parent . crypto . createHmac ( 'SHA256' , parent . relayRandom ) . update ( key ) . digest ( 'hex' ) ;
func ( key ) ;
} ) ;
}
module . exports . CreateMeshRelay = function ( parent , ws , req ) {
var obj = { } ;
obj . ws = ws ;
obj . peer = null ;
2017-09-01 14:23:22 -04:00
obj . parent = parent ;
2017-08-28 12:27:45 -04:00
obj . id = req . query [ 'id' ] ;
2017-09-01 14:23:22 -04:00
obj . remoteaddr = obj . ws . _socket . remoteAddress ;
if ( obj . remoteaddr . startsWith ( '::ffff:' ) ) { obj . remoteaddr = obj . remoteaddr . substring ( 7 ) ; }
2017-08-28 12:27:45 -04:00
if ( obj . id == undefined ) { obj . ws . close ( ) ; obj . id = null ; return null ; } // Attempt to connect without id, drop this.
2017-09-06 13:45:09 -04:00
// Check if this session is a logged in user, at least one of the two connections will need to be authenticated.
try { if ( ( req . session ) && ( req . session . userid ) || ( req . session . domainid == getDomain ( req ) . id ) ) { obj . authenticated = true ; } } catch ( e ) { }
2017-08-28 12:27:45 -04:00
// Validate that the id is valid, we only need to do this on non-authenticated sessions.
// TODO: Figure out when this needs to be done.
/ *
if ( ! parent . args . notls ) {
// Check the identifier, if running without TLS, skip this.
var ids = obj . id . split ( ':' ) ;
if ( ids . length != 3 ) { obj . ws . close ( ) ; obj . id = null ; return null ; } // Invalid ID, drop this.
if ( parent . crypto . createHmac ( 'SHA256' , parent . relayRandom ) . update ( ids [ 0 ] + ':' + ids [ 1 ] ) . digest ( 'hex' ) != ids [ 2 ] ) { obj . ws . close ( ) ; obj . id = null ; return null ; } // Invalid HMAC, drop this.
if ( ( Date . now ( ) - parseInt ( ids [ 1 ] ) ) > 120000 ) { obj . ws . close ( ) ; obj . id = null ; return null ; } // Expired time, drop this.
obj . id = ids [ 0 ] ;
}
* /
// Check the peer connection status
{
var relayinfo = parent . wsrelays [ obj . id ] ;
if ( relayinfo ) {
if ( relayinfo . state == 1 ) {
2017-09-06 13:45:09 -04:00
// Check that at least one connection is authenticated
if ( ( obj . authenticated != true ) && ( relayinfo . peer1 . authenticated != true ) ) {
obj . id = null ;
obj . ws . close ( ) ;
obj . parent . parent . debug ( 1 , 'Relay without-auth: ' + obj . id + ' (' + obj . remoteaddr + ')' ) ;
return null ;
}
2017-08-28 12:27:45 -04:00
// Connect to peer
obj . peer = relayinfo . peer1 ;
obj . peer . peer = obj ;
relayinfo . peer2 = obj ;
relayinfo . state = 2 ;
obj . ws . send ( 'c' ) ; // Send connect to both peers
relayinfo . peer1 . ws . send ( 'c' ) ;
relayinfo . peer1 . ws . peer = relayinfo . peer2 . ws ;
relayinfo . peer2 . ws . peer = relayinfo . peer1 . ws ;
2017-09-01 14:23:22 -04:00
obj . parent . parent . debug ( 1 , 'Relay connected: ' + obj . id + ' (' + obj . remoteaddr + ' --> ' + obj . peer . remoteaddr + ')' ) ;
2017-08-28 12:27:45 -04:00
} else {
// Connected already, drop (TODO: maybe we should re-connect?)
obj . id = null ;
obj . ws . close ( ) ;
2017-09-01 14:23:22 -04:00
obj . parent . parent . debug ( 1 , 'Relay duplicate: ' + obj . id + ' (' + obj . remoteaddr + ')' ) ;
2017-08-28 12:27:45 -04:00
return null ;
}
} else {
// Setup the connection, wait for peer
2017-09-01 14:23:22 -04:00
parent . wsrelays [ obj . id ] = { peer1 : obj , state : 1 } ;
obj . parent . parent . debug ( 1 , 'Relay holding: ' + obj . id + ' (' + obj . remoteaddr + ')' ) ;
2017-08-28 12:27:45 -04:00
}
}
ws . flushSink = function ( ) {
try { ws . resume ( ) ; } catch ( e ) { }
} ;
// When data is received from the mesh relay web socket
2017-09-01 14:23:22 -04:00
ws . on ( 'message' , function ( data ) {
if ( this . peer != null ) { try { this . pause ( ) ; this . peer . send ( data , ws . flushSink ) ; } catch ( e ) { } }
} ) ;
2017-08-28 12:27:45 -04:00
// If error, do nothing
ws . on ( 'error' , function ( err ) { console . log ( err ) ; } ) ;
// If the mesh relay web socket is closed
ws . on ( 'close' , function ( req ) {
if ( obj . id != null ) {
var relayinfo = parent . wsrelays [ obj . id ] ;
if ( relayinfo . state == 2 ) {
// Disconnect the peer
2017-09-01 14:23:22 -04:00
var peer = ( relayinfo . peer1 == obj ) ? relayinfo . peer2 : relayinfo . peer1 ;
obj . parent . parent . debug ( 1 , 'Relay disconnect: ' + obj . id + ' (' + obj . remoteaddr + ' --> ' + peer . remoteaddr + ')' ) ;
2017-08-28 12:27:45 -04:00
peer . id = null ;
2017-09-01 14:23:22 -04:00
try { peer . ws . close ( ) ; } catch ( e ) { } // Soft disconnect
try { peer . ws . _socket . _parent . end ( ) ; } catch ( e ) { } // Hard disconnect
} else {
obj . parent . parent . debug ( 1 , 'Relay disconnect: ' + obj . id + ' (' + obj . remoteaddr + ')' ) ;
2017-08-28 12:27:45 -04:00
}
delete parent . wsrelays [ obj . id ] ;
obj . peer = null ;
obj . id = null ;
}
} ) ;
return obj ;
}