2019-10-25 19:51:40 -04:00
# include "ServerConnection.h"
# include "ProtocolHandler.h"
# include "Socket.h"
# include "audio/VoiceConnection.h"
# include "audio/AudioSender.h"
# include "../logger.h"
# include "../hwuid.h"
# include <sstream>
# include <thread>
# include <iostream>
# include <misc/net.h>
# include <misc/base64.h>
# include <misc/endianness.h>
# include <misc/strobf.h>
# include <iomanip>
2019-11-24 12:38:50 -05:00
# include <include/NanGet.h>
2019-10-25 19:51:40 -04:00
//#define FUZZ_VOICE
//#define SHUFFLE_VOICE
using namespace std ;
using namespace std : : chrono ;
using namespace tc : : connection ;
string ErrorHandler : : get_message ( ErrorHandler : : error_id id ) {
if ( id = = 0 )
return " success " ;
auto index = ( - id - 1 ) % this - > error_varianz ;
assert ( index > = 0 & & index < this - > error_varianz ) ;
return this - > error_messages [ index ] ;
}
ErrorHandler : : error_id ErrorHandler : : register_error ( const string & message ) {
auto index = this - > error_index + + % this - > error_varianz ;
this - > error_messages [ index ] = message ;
return - index - 1 ;
}
ServerConnection : : ServerConnection ( ) {
logger : : debug ( category : : connection , tr ( " Allocated ServerConnection {}. " ) , ( void * ) this ) ;
}
ServerConnection : : ~ ServerConnection ( ) {
logger : : debug ( category : : connection , tr ( " Begin deallocating ServerConnection {}. " ) , ( void * ) this ) ;
if ( this - > protocol_handler & & this - > protocol_handler - > connection_state = = connection_state : : CONNECTED )
this - > protocol_handler - > disconnect ( " server connection has been destoryed " ) ;
this - > close_connection ( ) ;
this - > finalize ( ) ;
logger : : debug ( category : : connection , tr ( " Finished deallocating ServerConnection {}. " ) , ( void * ) this ) ;
}
NAN_MODULE_INIT ( ServerConnection : : Init ) {
auto klass = Nan : : New < v8 : : FunctionTemplate > ( ServerConnection : : new_instance ) ;
klass - > SetClassName ( Nan : : New ( " NativeServerConnection " ) . ToLocalChecked ( ) ) ;
klass - > InstanceTemplate ( ) - > SetInternalFieldCount ( 1 ) ;
Nan : : SetPrototypeMethod ( klass , " connect " , ServerConnection : : _connect ) ;
Nan : : SetPrototypeMethod ( klass , " connected " , ServerConnection : : _connected ) ;
Nan : : SetPrototypeMethod ( klass , " disconnect " , ServerConnection : : _disconnect ) ;
Nan : : SetPrototypeMethod ( klass , " error_message " , ServerConnection : : _error_message ) ;
Nan : : SetPrototypeMethod ( klass , " send_command " , ServerConnection : : _send_command ) ;
Nan : : SetPrototypeMethod ( klass , " send_voice_data " , ServerConnection : : _send_voice_data ) ;
Nan : : SetPrototypeMethod ( klass , " send_voice_data_raw " , ServerConnection : : _send_voice_data_raw ) ;
Nan : : SetPrototypeMethod ( klass , " current_ping " , ServerConnection : : _current_ping ) ;
constructor ( ) . Reset ( Nan : : GetFunction ( klass ) . ToLocalChecked ( ) ) ;
}
NAN_METHOD ( ServerConnection : : new_instance ) {
//info.GetReturnValue().Set(Nan::New<v8::String>("Hello World").ToLocalChecked());
if ( info . IsConstructCall ( ) ) {
auto instance = new ServerConnection ( ) ;
instance - > Wrap ( info . This ( ) ) ;
instance - > initialize ( ) ;
//Nan::Set(info.This(), New<String>("type").ToLocalChecked(), New<Number>(type));
info . GetReturnValue ( ) . Set ( info . This ( ) ) ;
} else {
v8 : : Local < v8 : : Function > cons = Nan : : New ( constructor ( ) ) ;
Nan : : TryCatch try_catch ;
auto result = Nan : : NewInstance ( cons , 0 , nullptr ) ;
if ( try_catch . HasCaught ( ) ) {
try_catch . ReThrow ( ) ;
return ;
}
info . GetReturnValue ( ) . Set ( result . ToLocalChecked ( ) ) ;
}
}
void ServerConnection : : initialize ( ) {
this - > event_loop_exit = false ;
this - > event_thread = thread ( & ServerConnection : : event_loop , this ) ;
this - > protocol_handler = make_unique < ProtocolHandler > ( this ) ;
this - > voice_connection = make_shared < VoiceConnection > ( this ) ;
this - > voice_connection - > _ref = this - > voice_connection ;
this - > voice_connection - > initialize_js_object ( ) ;
this - > execute_pending_commands = Nan : : async_callback ( [ & ] {
Nan : : HandleScope scope ;
this - > _execute_callback_commands ( ) ;
} ) ;
this - > execute_pending_voice = Nan : : async_callback ( [ & ] {
Nan : : HandleScope scope ;
this - > _execute_callback_voice ( ) ;
} ) ;
this - > execute_callback_disconnect = Nan : : async_callback ( [ & ] ( std : : string reason ) {
Nan : : HandleScope scope ;
this - > _execute_callback_disconnect ( reason ) ;
} ) ;
this - > call_connect_result = Nan : : async_callback ( [ & ] ( ErrorHandler : : error_id error_id ) {
Nan : : HandleScope scope ;
/* lets update the server type */
{
auto js_this = this - > handle ( ) ;
Nan : : Set ( js_this , Nan : : New < v8 : : String > ( " server_type " ) . ToLocalChecked ( ) , Nan : : New < v8 : : Number > ( this - > protocol_handler - > server_type ) ) ;
}
/* lets call the connect callback */
{
v8 : : Local < v8 : : Value > argv [ 1 ] ;
argv [ 0 ] = Nan : : New < v8 : : Number > ( error_id ) ;
if ( this - > callback_connect )
Nan : : Call ( * this - > callback_connect , 1 , argv ) ;
this - > callback_connect = nullptr ;
}
} ) ;
this - > call_disconnect_result = Nan : : async_callback ( [ & ] ( ErrorHandler : : error_id error_id ) {
Nan : : HandleScope scope ;
v8 : : Local < v8 : : Value > argv [ 1 ] ;
argv [ 0 ] = Nan : : New < v8 : : Number > ( error_id ) ;
if ( this - > callback_disconnect )
Nan : : Call ( * this - > callback_disconnect , 1 , argv ) ;
this - > callback_disconnect = nullptr ;
} ) ;
auto js_this = this - > handle ( ) ;
Nan : : Set ( js_this , Nan : : New < v8 : : String > ( " _voice_connection " ) . ToLocalChecked ( ) , this - > voice_connection - > js_handle ( ) ) ;
}
void ServerConnection : : finalize ( ) {
this - > event_loop_exit = true ;
this - > event_condition . notify_all ( ) ;
this - > event_thread . join ( ) ;
}
void ServerConnection : : event_loop ( ) {
auto eval_timeout = [ & ] {
auto best = this - > next_tick ;
if ( this - > next_resend < best )
best = this - > next_resend ;
if ( this - > event_loop_execute_connection_close )
return system_clock : : time_point { } ;
return best ;
} ;
while ( ! this - > event_loop_exit ) {
auto timeout = eval_timeout ( ) ;
{
unique_lock lock ( this - > event_lock ) ;
this - > event_condition . wait_until ( lock , timeout , [ & ] {
if ( eval_timeout ( ) ! = timeout )
return true ;
return this - > event_loop_exit ;
} ) ;
if ( this - > event_loop_exit )
break ;
}
if ( this - > event_loop_execute_connection_close ) {
this - > close_connection ( ) ;
this - > event_loop_execute_connection_close = false ;
}
auto date = chrono : : system_clock : : now ( ) ;
if ( this - > next_tick < = date ) {
this - > next_tick = date + chrono : : milliseconds ( 500 ) ;
this - > execute_tick ( ) ;
}
if ( this - > next_resend < = date ) {
this - > next_resend = date + chrono : : seconds ( 5 ) ;
if ( this - > protocol_handler )
this - > protocol_handler - > execute_resend ( ) ;
}
}
}
void ServerConnection : : schedule_resend ( const std : : chrono : : system_clock : : time_point & timeout ) {
if ( this - > next_resend > timeout ) {
this - > next_resend = timeout ;
this - > event_condition . notify_one ( ) ;
}
}
2020-04-04 06:25:01 -04:00
2020-04-20 15:32:05 -04:00
# ifdef WIN32
2020-04-04 06:25:01 -04:00
inline std : : string wsa_error_str ( int code ) {
int err ;
char msgbuf [ 256 ] ; // for a message up to 255 bytes.
msgbuf [ 0 ] = ' \0 ' ; // Microsoft doesn't guarantee this on man page.
err = WSAGetLastError ( ) ;
FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS , // flags
NULL , // lpsource
err , // message id
MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , // languageid
msgbuf , // output buffer
sizeof ( msgbuf ) , // size of msgbuf, bytes
NULL ) ; // va_list of arguments
if ( ! * msgbuf )
sprintf ( msgbuf , " %d " , err ) ; // provide error # if no string available
return std : : string { msgbuf } ;
}
2020-04-20 15:32:05 -04:00
# endif
2020-04-04 06:25:01 -04:00
2019-10-25 19:51:40 -04:00
NAN_METHOD ( ServerConnection : : connect ) {
if ( ! this - > protocol_handler ) {
Nan : : ThrowError ( " ServerConnection not initialized " ) ;
return ;
}
if ( info . Length ( ) ! = 1 ) {
Nan : : ThrowError ( tr ( " Invalid argument count " ) ) ;
return ;
}
v8 : : Local arguments = info [ 0 ] - > ToObject ( Nan : : GetCurrentContext ( ) ) . ToLocalChecked ( ) ;
if ( ! arguments - > IsObject ( ) ) {
Nan : : ThrowError ( tr ( " Invalid argument " ) ) ;
return ;
}
auto remote_host = Nan : : Get ( arguments , Nan : : New < v8 : : String > ( " remote_host " ) . ToLocalChecked ( ) ) . ToLocalChecked ( ) ;
auto remote_port = Nan : : Get ( arguments , Nan : : New < v8 : : String > ( " remote_port " ) . ToLocalChecked ( ) ) . ToLocalChecked ( ) ;
auto timeout = Nan : : Get ( arguments , Nan : : New < v8 : : String > ( " timeout " ) . ToLocalChecked ( ) ) . ToLocalChecked ( ) ;
auto callback = Nan : : Get ( arguments , Nan : : New < v8 : : String > ( " callback " ) . ToLocalChecked ( ) ) . ToLocalChecked ( ) ;
auto identity_key = Nan : : Get ( arguments , Nan : : New < v8 : : String > ( " identity_key " ) . ToLocalChecked ( ) ) . ToLocalChecked ( ) ;
auto teamspeak = Nan : : Get ( arguments , Nan : : New < v8 : : String > ( " teamspeak " ) . ToLocalChecked ( ) ) . ToLocalChecked ( ) ;
if ( ! identity_key - > IsString ( ) & & ! identity_key - > IsNullOrUndefined ( ) ) {
Nan : : ThrowError ( tr ( " Invalid identity " ) ) ;
return ;
}
if ( ! remote_host - > IsString ( ) | | ! remote_port - > IsNumber ( ) ) {
Nan : : ThrowError ( tr ( " Invalid argument host/port " ) ) ;
return ;
}
2020-03-02 08:33:34 -05:00
if ( ! callback - > IsFunction ( ) ) {
2019-10-25 19:51:40 -04:00
Nan : : ThrowError ( tr ( " Invalid callback " ) ) ;
return ;
}
unique_lock _disconnect_lock ( this - > disconnect_lock , defer_lock ) ;
if ( ! _disconnect_lock . try_lock_for ( chrono : : milliseconds ( 500 ) ) ) {
Nan : : ThrowError ( tr ( " failed to acquire disconnect lock " ) ) ;
return ;
}
this - > callback_connect = make_unique < Nan : : Callback > ( callback . As < v8 : : Function > ( ) ) ;
this - > voice_connection - > reset ( ) ;
this - > protocol_handler - > reset ( ) ;
if ( identity_key - > IsString ( ) ) {
auto & identity = this - > protocol_handler - > get_identity_key ( ) ;
auto key = base64 : : decode ( * Nan : : Utf8String ( identity_key - > ToString ( Nan : : GetCurrentContext ( ) ) . ToLocalChecked ( ) ) ) ;
if ( ecc_import ( ( u_char * ) key . data ( ) , ( unsigned long ) key . length ( ) , & identity ) ! = CRYPT_OK ) {
Nan : : ThrowError ( tr ( " failed to import identity " ) ) ;
return ;
}
} else {
auto & identity = this - > protocol_handler - > get_identity_key ( ) ;
prng_state rndState { } ;
memset ( & rndState , 0 , sizeof ( prng_state ) ) ;
int err ;
if ( ( err = ecc_make_key_ex ( & rndState , find_prng ( " sprng " ) , & identity , & ltc_ecc_sets [ 5 ] ) ) ! = CRYPT_OK ) {
Nan : : ThrowError ( tr ( " failed to generate ephemeral identity " ) ) ;
return ;
}
}
sockaddr_storage remote_address { } ;
/* resolve address */
{
addrinfo hints { } , * result ;
memset ( & hints , 0 , sizeof ( hints ) ) ;
hints . ai_family = AF_UNSPEC ;
auto _remote_host = Nan : : Utf8String ( remote_host - > ToString ( Nan : : GetCurrentContext ( ) ) . ToLocalChecked ( ) ) ;
if ( getaddrinfo ( * _remote_host , nullptr , & hints , & result ) ! = 0 | | ! result ) {
this - > call_connect_result ( this - > errors . register_error ( tr ( " failed to resolve hostname " ) ) ) ;
return ;
}
memcpy ( & remote_address , result - > ai_addr , result - > ai_addrlen ) ;
freeaddrinfo ( result ) ;
}
switch ( remote_address . ss_family ) {
case AF_INET :
( ( sockaddr_in * ) & remote_address ) - > sin_port = htons ( remote_port - > Int32Value ( Nan : : GetCurrentContext ( ) ) . FromMaybe ( 0 ) ) ;
case AF_INET6 :
( ( sockaddr_in6 * ) & remote_address ) - > sin6_port = htons ( remote_port - > Int32Value ( Nan : : GetCurrentContext ( ) ) . FromMaybe ( 0 ) ) ;
default : break ;
}
logger : : info ( category : : connection , tr ( " Connecting to {}. " ) , net : : to_string ( remote_address ) ) ;
this - > socket = make_shared < UDPSocket > ( remote_address ) ;
if ( ! this - > socket - > initialize ( ) ) {
this - > call_connect_result ( this - > errors . register_error ( " failed to initialize socket " ) ) ;
this - > socket = nullptr ;
return ;
}
this - > socket - > on_data = [ & ] ( const pipes : : buffer_view & buffer ) { this - > protocol_handler - > progress_packet ( buffer ) ; } ;
2020-04-04 06:25:01 -04:00
this - > socket - > on_fatal_error = [ & ] ( int code , int detail ) {
# if WIN32
auto message = wsa_error_str ( detail ) ;
# else
2020-04-20 15:32:05 -04:00
auto message = std : : string { strerror ( detail ) } ;
2020-04-04 06:25:01 -04:00
# endif
this - > execute_callback_disconnect . call ( ( code = = 1 ? tr ( " Failed to received data: " ) : tr ( " Failed to send data: " ) ) + message , false ) ;
this - > close_connection ( ) ;
} ;
2020-03-02 08:33:34 -05:00
2019-10-25 19:51:40 -04:00
if ( teamspeak - > IsBoolean ( ) & & teamspeak - > BooleanValue ( info . GetIsolate ( ) ) )
this - > protocol_handler - > server_type = server_type : : TEAMSPEAK ;
this - > protocol_handler - > connect ( ) ;
}
NAN_METHOD ( ServerConnection : : _connected ) {
return ObjectWrap : : Unwrap < ServerConnection > ( info . Holder ( ) ) - > connected ( info ) ;
}
NAN_METHOD ( ServerConnection : : _connect ) {
return ObjectWrap : : Unwrap < ServerConnection > ( info . Holder ( ) ) - > connect ( info ) ;
}
NAN_METHOD ( ServerConnection : : connected ) {
bool result = this - > protocol_handler & & this - > protocol_handler - > connection_state > = connection_state : : CONNECTING & & this - > protocol_handler - > connection_state < connection_state : : DISCONNECTING ;
info . GetReturnValue ( ) . Set ( result ) ;
}
NAN_METHOD ( ServerConnection : : _disconnect ) {
return ObjectWrap : : Unwrap < ServerConnection > ( info . Holder ( ) ) - > disconnect ( info ) ;
}
NAN_METHOD ( ServerConnection : : disconnect ) {
if ( info . Length ( ) ! = 2 ) {
Nan : : ThrowError ( " Invalid argument count " ) ;
return ;
}
if ( ! info [ 1 ] - > IsFunction ( ) | | ! info [ 0 ] - > IsString ( ) ) {
Nan : : ThrowError ( " Invalid argument " ) ;
return ;
}
this - > callback_disconnect = make_unique < Nan : : Callback > ( info [ 1 ] . As < v8 : : Function > ( ) ) ;
if ( ! this - > socket ) {
this - > call_disconnect_result ( 0 ) ; /* this->errors.register_error("not connected") */
return ;
}
if ( this - > protocol_handler ) {
this - > protocol_handler - > disconnect ( * Nan : : Utf8String ( info [ 0 ] ) ) ;
}
}
NAN_METHOD ( ServerConnection : : _error_message ) {
return ObjectWrap : : Unwrap < ServerConnection > ( info . Holder ( ) ) - > error_message ( info ) ;
}
NAN_METHOD ( ServerConnection : : error_message ) {
if ( info . Length ( ) ! = 1 | | ! info [ 0 ] - > IsNumber ( ) ) {
Nan : : ThrowError ( " Invalid argument " ) ;
return ;
}
auto error = this - > errors . get_message ( ( ErrorHandler : : error_id ) info [ 0 ] - > IntegerValue ( Nan : : GetCurrentContext ( ) ) . FromMaybe ( 0 ) ) ;
info . GetReturnValue ( ) . Set ( Nan : : New < v8 : : String > ( error ) . ToLocalChecked ( ) ) ;
}
//send_command(command: string, arguments: any[], switches: string[]);
template < typename T >
std : : string _to_string ( const T value ) {
std : : string result ( 15 , ' \0 ' ) ;
auto written = std : : snprintf ( & result [ 0 ] , result . size ( ) , " %.6f " , value ) ;
if ( written < 0 ) {
log_warn ( general , " Failed to format float value: {}; {} " , value , written ) ;
return " 0 " ;
}
result . resize ( written ) ;
return result ;
}
NAN_METHOD ( ServerConnection : : _send_command ) {
return ObjectWrap : : Unwrap < ServerConnection > ( info . Holder ( ) ) - > send_command ( info ) ;
}
struct TS3VersionSettings {
std : : string build ;
std : : string platform ;
std : : string sign ;
} ;
NAN_METHOD ( ServerConnection : : send_command ) {
if ( ! this - > protocol_handler ) {
Nan : : ThrowError ( " ServerConnection not initialized " ) ;
return ;
}
if ( info . Length ( ) ! = 3 ) {
Nan : : ThrowError ( " invalid argument count " ) ;
return ;
}
if ( ! info [ 0 ] - > IsString ( ) | | ! info [ 1 ] - > IsArray ( ) | | ! info [ 2 ] - > IsArray ( ) ) {
Nan : : ThrowError ( " invalid argument type " ) ;
return ;
}
auto begin = chrono : : system_clock : : now ( ) ;
auto command = info [ 0 ] - > ToString ( Nan : : GetCurrentContext ( ) ) . ToLocalChecked ( ) ;
auto arguments = info [ 1 ] . As < v8 : : Array > ( ) ;
auto switches = info [ 2 ] . As < v8 : : Array > ( ) ;
ts : : Command cmd ( * Nan : : Utf8String ( command ) ) ;
for ( size_t index = 0 ; index < arguments - > Length ( ) ; index + + ) {
2019-11-24 12:38:50 -05:00
auto object = Nan : : GetLocal < v8 : : Object > ( arguments , ( uint32_t ) index ) ;
if ( object . IsEmpty ( ) | | ! object - > IsObject ( ) ) {
2019-10-25 19:51:40 -04:00
Nan : : ThrowError ( Nan : : New < v8 : : String > ( " invalid parameter ( " + to_string ( index ) + " ) " ) . ToLocalChecked ( ) ) ;
return ;
}
v8 : : Local < v8 : : Array > properties = object - > ToObject ( Nan : : GetCurrentContext ( ) ) . ToLocalChecked ( ) - > GetOwnPropertyNames ( Nan : : GetCurrentContext ( ) ) . ToLocalChecked ( ) ;
for ( uint32_t i = 0 ; i < properties - > Length ( ) ; i + + ) {
2019-11-24 12:38:50 -05:00
auto key = Nan : : GetStringLocal ( properties , i ) ;
2019-10-25 19:51:40 -04:00
auto value = object - > ToObject ( Nan : : GetCurrentContext ( ) ) . ToLocalChecked ( ) - > Get ( Nan : : GetCurrentContext ( ) , key ) . ToLocalChecked ( ) ;
string key_string = * Nan : : Utf8String ( key ) ;
if ( value - > IsInt32 ( ) )
cmd [ index ] [ key_string ] = value - > Int32Value ( Nan : : GetCurrentContext ( ) ) . FromMaybe ( 0 ) ;
else if ( value - > IsNumber ( ) | | value - > IsNumberObject ( ) )
cmd [ index ] [ key_string ] = _to_string < double > ( value - > NumberValue ( Nan : : GetCurrentContext ( ) ) . FromMaybe ( 0 ) ) ; /* requires our own conversation because node overrides stuff to 0,0000*/
else if ( value - > IsString ( ) )
cmd [ index ] [ key_string ] = * Nan : : Utf8String ( value - > ToString ( Nan : : GetCurrentContext ( ) ) . ToLocalChecked ( ) ) ;
else if ( value - > IsBoolean ( ) | | value - > IsBooleanObject ( ) )
cmd [ index ] [ key_string ] = value - > BooleanValue ( info . GetIsolate ( ) ) ;
else if ( value - > IsNullOrUndefined ( ) )
cmd [ index ] [ key_string ] = " " ;
else {
Nan : : ThrowError ( Nan : : New < v8 : : String > ( " invalid parameter ( " + to_string ( index ) + " : " + key_string + " ) " ) . ToLocalChecked ( ) ) ;
return ;
}
}
}
for ( size_t index = 0 ; index < switches - > Length ( ) ; index + + ) {
2019-11-24 12:38:50 -05:00
auto object = Nan : : GetStringLocal ( switches , ( uint32_t ) index ) ;
if ( object . IsEmpty ( ) ) {
2019-10-25 19:51:40 -04:00
Nan : : ThrowError ( Nan : : New < v8 : : String > ( " invalid switch ( " + to_string ( index ) + " ) " ) . ToLocalChecked ( ) ) ;
return ;
}
cmd . enableParm ( * Nan : : Utf8String ( object ) ) ;
}
if ( this - > protocol_handler - > server_type = = server_type : : TEAMSPEAK ) {
if ( cmd . command ( ) = = " clientinit " ) {
/* If we have a return code here some strange stuff happens (Ghost client) */
if ( cmd [ 0 ] . has ( " return_code " ) )
cmd [ " return_code " ] = nullptr ;
TS3VersionSettings ts_version { } ;
# ifdef WIN32
/*
ts_version = {
" 0.0.1 [Build: 1549713549] " ,
" Linux " ,
" 7XvKmrk7uid2ixHFeERGqcC8vupeQqDypLtw2lY9slDNPojEv//F47UaDLG+TmVk4r6S0TseIKefzBpiRtLDAQ== "
} ;
*/
ts_version = {
" 3.?.? [Build: 5680278000] " ,
" Windows " ,
" DX5NIYLvfJEUjuIbCidnoeozxIDRRkpq3I9vVMBmE9L2qnekOoBzSenkzsg2lC9CMv8K5hkEzhr2TYUYSwUXCg== "
} ;
# else
/*
ts_version = {
" 0.0.1 [Build: 1549713549] " ,
" Linux " ,
" 7XvKmrk7uid2ixHFeERGqcC8vupeQqDypLtw2lY9slDNPojEv//F47UaDLG+TmVk4r6S0TseIKefzBpiRtLDAQ== "
} ;
*/
ts_version = {
" 3.?.? [Build: 5680278000] " ,
" Linux " ,
" Hjd+N58Gv3ENhoKmGYy2bNRBsNNgm5kpiaQWxOj5HN2DXttG6REjymSwJtpJ8muC2gSwRuZi0R+8Laan5ts5CQ== "
} ;
# endif
if ( std : : getenv ( " teaclient_ts3_build " ) & & std : : getenv ( " teaclient_ts3_platform " ) & & std : : getenv ( " teaclient_ts3_sign " ) ) {
ts_version = {
std : : getenv ( " teaclient_ts3_build " ) ,
std : : getenv ( " teaclient_ts3_platform " ) ,
std : : getenv ( " teaclient_ts3_sign " )
} ;
}
cmd [ " client_version " ] = ts_version . build ;
cmd [ " client_platform " ] = ts_version . platform ;
cmd [ " client_version_sign " ] = ts_version . sign ;
cmd [ strobf ( " hwid " ) . string ( ) ] = system_uuid ( ) ; /* we dont want anybody to patch this out */
}
}
2020-04-01 19:19:36 -04:00
2019-10-25 19:51:40 -04:00
this - > protocol_handler - > send_command ( cmd ) ;
auto end = chrono : : system_clock : : now ( ) ;
}
NAN_METHOD ( ServerConnection : : _send_voice_data ) {
return ObjectWrap : : Unwrap < ServerConnection > ( info . Holder ( ) ) - > send_voice_data ( info ) ;
}
NAN_METHOD ( ServerConnection : : send_voice_data ) {
if ( ! this - > protocol_handler ) {
Nan : : ThrowError ( " ServerConnection not initialized " ) ;
return ;
}
if ( info . Length ( ) ! = 3 ) {
Nan : : ThrowError ( " invalid argument count " ) ;
return ;
}
if ( ! info [ 0 ] - > IsUint8Array ( ) | | ! info [ 1 ] - > IsInt32 ( ) | | ! info [ 2 ] - > IsBoolean ( ) ) {
Nan : : ThrowError ( " invalid argument type " ) ;
return ;
}
auto voice_data = info [ 0 ] . As < v8 : : Uint8Array > ( ) - > Buffer ( ) ;
this - > send_voice_data ( voice_data - > GetContents ( ) . Data ( ) , voice_data - > GetContents ( ) . ByteLength ( ) , ( uint8_t ) info [ 1 ] - > Int32Value ( Nan : : GetCurrentContext ( ) ) . FromMaybe ( 0 ) , info [ 2 ] - > BooleanValue ( info . GetIsolate ( ) ) ) ;
}
NAN_METHOD ( ServerConnection : : _send_voice_data_raw ) {
return ObjectWrap : : Unwrap < ServerConnection > ( info . Holder ( ) ) - > send_voice_data_raw ( info ) ;
}
NAN_METHOD ( ServerConnection : : send_voice_data_raw ) {
//send_voice_data_raw(buffer: Float32Array, channels: number, sample_rate: number, header: boolean);
if ( info . Length ( ) ! = 4 ) {
Nan : : ThrowError ( " invalid argument count " ) ;
return ;
}
if ( ! info [ 0 ] - > IsFloat32Array ( ) | | ! info [ 1 ] - > IsInt32 ( ) | | ! info [ 2 ] - > IsInt32 ( ) ) {
Nan : : ThrowError ( " invalid argument type " ) ;
return ;
}
auto channels = info [ 1 ] - > Int32Value ( Nan : : GetCurrentContext ( ) ) . FromMaybe ( 0 ) ;
auto sample_rate = info [ 2 ] - > Int32Value ( Nan : : GetCurrentContext ( ) ) . FromMaybe ( 0 ) ;
auto flag_head = info [ 2 ] - > BooleanValue ( info . GetIsolate ( ) ) ;
auto voice_data = info [ 0 ] . As < v8 : : Float32Array > ( ) - > Buffer ( ) ;
auto vs = this - > voice_connection ? this - > voice_connection - > voice_sender ( ) : nullptr ;
if ( vs ) vs - > send_data ( voice_data - > GetContents ( ) . Data ( ) , voice_data - > GetContents ( ) . ByteLength ( ) / ( 4 * channels ) , sample_rate , channels ) ;
}
# ifdef SHUFFLE_VOICE
static shared_ptr < ts : : protocol : : ClientPacket > shuffle_cached_packet ;
# endif
void ServerConnection : : send_voice_data ( const void * buffer , size_t buffer_length , uint8_t codec , bool head ) {
auto _buffer = pipes : : buffer { ts : : protocol : : ClientPacket : : META_SIZE + buffer_length + 3 } ;
auto packet = ts : : protocol : : ClientPacket : : from_buffer ( _buffer ) ;
memset ( & _buffer [ ts : : protocol : : ClientPacket : : META_MAC_SIZE ] , 0 , ts : : protocol : : ClientPacket : : META_HEADER_SIZE ) ; /* reset all header data */
packet - > type ( ts : : protocol : : PacketTypeInfo : : Voice ) ;
auto data_buffer = packet - > data ( ) ;
le2be16 ( this - > voice_packet_id + + , & data_buffer [ 0 ] ) ; /* set voice packet id */
data_buffer [ 2 ] = ( uint8_t ) codec ; /* set voice codec */
if ( buffer_length > 0 & & buffer )
memcpy ( & data_buffer [ 3 ] , buffer , buffer_length ) ;
if ( head ) /* head packet */
packet - > enable_flag ( ts : : protocol : : PacketFlag : : Compressed ) ;
packet - > enable_flag ( ts : : protocol : : PacketFlag : : Unencrypted ) ;
# ifdef FUZZ_VOICE
if ( ( rand ( ) % 10 ) < 2 ) {
log_info ( category : : connection , tr ( " Dropping voice packet " ) ) ;
} else {
this - > protocol_handler - > send_packet ( packet ) ;
}
# elif defined(SHUFFLE_VOICE)
if ( shuffle_cached_packet ) {
this - > protocol_handler - > send_packet ( packet ) ;
this - > protocol_handler - > send_packet ( std : : exchange ( shuffle_cached_packet , nullptr ) ) ;
} else {
shuffle_cached_packet = packet ;
}
# else
this - > protocol_handler - > send_packet ( std : : shared_ptr < ts : : protocol : : ClientPacket > ( packet . release ( ) ) ) ;
# endif
}
void ServerConnection : : close_connection ( ) {
lock_guard lock ( this - > disconnect_lock ) ;
if ( this - > socket & & this_thread : : get_id ( ) = = this - > socket - > io_thread ( ) . get_id ( ) ) {
logger : : debug ( category : : connection , tr ( " close_connection() called in IO thread. Closing connection within event loop! " ) ) ;
if ( ! this - > event_loop_execute_connection_close ) {
this - > event_loop_execute_connection_close = true ;
this - > event_condition . notify_one ( ) ;
}
return ;
}
this - > event_loop_execute_connection_close = false ;
2020-12-02 12:08:49 -05:00
if ( this - > socket ) {
this - > protocol_handler - > do_close_connection ( ) ;
}
if ( this - > protocol_handler ) {
this - > protocol_handler - > do_close_connection ( ) ;
}
2019-10-25 19:51:40 -04:00
this - > socket = nullptr ;
2020-03-02 08:33:34 -05:00
2019-10-25 19:51:40 -04:00
this - > call_disconnect_result . call ( 0 , true ) ;
}
void ServerConnection : : execute_tick ( ) {
if ( this - > protocol_handler )
this - > protocol_handler - > execute_tick ( ) ;
2020-05-04 05:54:35 -04:00
if ( auto vc { this - > voice_connection } ; vc )
vc - > execute_tick ( ) ;
2019-10-25 19:51:40 -04:00
}
void ServerConnection : : _execute_callback_commands ( ) {
unique_ptr < ts : : Command > next_command ;
v8 : : Local < v8 : : Function > callback ;
while ( true ) {
{
lock_guard lock ( this - > pending_commands_lock ) ;
if ( this - > pending_commands . empty ( ) )
return ;
next_command = move ( this - > pending_commands . front ( ) ) ;
this - > pending_commands . pop_front ( ) ;
}
if ( ! next_command )
continue ;
if ( callback . IsEmpty ( ) ) {
callback = Nan : : Get ( this - > handle ( ) , Nan : : New < v8 : : String > ( " callback_command " ) . ToLocalChecked ( ) ) . ToLocalChecked ( ) . As < v8 : : Function > ( ) ;
if ( callback . IsEmpty ( ) ) {
logger : : warn ( category : : connection , tr ( " Missing command callback! Dropping commands. " ) ) ;
lock_guard lock ( this - > pending_commands_lock ) ;
this - > pending_commands . clear ( ) ;
return ;
}
}
v8 : : Local < v8 : : Value > arguments [ 3 ] ;
arguments [ 0 ] = Nan : : New < v8 : : String > ( next_command - > command ( ) ) . ToLocalChecked ( ) ;
auto parameters = Nan : : New < v8 : : Array > ( ( int ) next_command - > bulkCount ( ) ) ;
for ( size_t index = 0 ; index < next_command - > bulkCount ( ) ; index + + ) {
auto object = Nan : : New < v8 : : Object > ( ) ;
auto & bulk = next_command - > operator [ ] ( index ) ;
for ( const auto & key : bulk . keys ( ) )
Nan : : Set ( object , Nan : : New < v8 : : String > ( key ) . ToLocalChecked ( ) , Nan : : New < v8 : : String > ( bulk [ key ] . string ( ) ) . ToLocalChecked ( ) ) ;
2019-11-24 12:38:50 -05:00
Nan : : Set ( parameters , ( uint32_t ) index , object ) ;
2019-10-25 19:51:40 -04:00
}
arguments [ 1 ] = parameters ;
auto switched = Nan : : New < v8 : : Array > ( ( int ) next_command - > parms ( ) . size ( ) ) ;
for ( size_t index = 0 ; index < next_command - > parms ( ) . size ( ) ; index + + ) {
auto & key = next_command - > parms ( ) [ index ] ;
2019-11-24 12:38:50 -05:00
Nan : : Set ( parameters , ( uint32_t ) index , Nan : : New < v8 : : String > ( key ) . ToLocalChecked ( ) ) ;
2019-10-25 19:51:40 -04:00
}
arguments [ 2 ] = switched ;
callback - > Call ( Nan : : GetCurrentContext ( ) , Nan : : Undefined ( ) , 3 , arguments ) ;
}
}
void ServerConnection : : _execute_callback_voice ( ) {
unique_ptr < VoicePacket > next_packet ;
v8 : : Local < v8 : : Function > callback ;
while ( true ) {
{
lock_guard lock ( this - > pending_voice_lock ) ;
if ( this - > pending_voice . empty ( ) )
return ;
next_packet = move ( this - > pending_voice . front ( ) ) ;
this - > pending_voice . pop_front ( ) ;
}
if ( ! next_packet )
continue ;
if ( callback . IsEmpty ( ) ) {
auto _callback = Nan : : Get ( this - > handle ( ) , Nan : : New < v8 : : String > ( " callback_voice_data " ) . ToLocalChecked ( ) ) . ToLocalChecked ( ) ;
if ( _callback . IsEmpty ( ) | | _callback - > IsUndefined ( ) ) {
logger : : warn ( category : : audio , tr ( " Missing voice callback! Dropping packets! " ) ) ;
lock_guard lock ( this - > pending_voice_lock ) ;
this - > pending_voice . clear ( ) ;
return ;
}
callback = _callback . As < v8 : : Function > ( ) ;
}
v8 : : Local < v8 : : Value > arguments [ 5 ] ;
v8 : : Local < v8 : : ArrayBuffer > buffer = v8 : : ArrayBuffer : : New (
Nan : : GetCurrentContext ( ) - > GetIsolate ( ) ,
next_packet - > voice_data . length ( )
) ;
memcpy ( buffer - > GetContents ( ) . Data ( ) , next_packet - > voice_data . data_ptr ( ) , next_packet - > voice_data . length ( ) ) ;
arguments [ 0 ] = v8 : : Uint8Array : : New ( buffer , 0 , buffer - > ByteLength ( ) ) ;
arguments [ 1 ] = Nan : : New < v8 : : Integer > ( next_packet - > client_id ) ;
arguments [ 2 ] = Nan : : New < v8 : : Integer > ( next_packet - > codec_id ) ;
arguments [ 3 ] = Nan : : New < v8 : : Boolean > ( next_packet - > flag_head ) ;
arguments [ 4 ] = Nan : : New < v8 : : Integer > ( next_packet - > packet_id ) ;
callback - > Call ( Nan : : GetCurrentContext ( ) , Nan : : Undefined ( ) , 5 , arguments ) ;
}
}
void ServerConnection : : _execute_callback_disconnect ( const std : : string & reason ) {
auto callback = Nan : : Get ( this - > handle ( ) , Nan : : New < v8 : : String > ( " callback_disconnect " ) . ToLocalChecked ( ) ) . ToLocalChecked ( ) . As < v8 : : Function > ( ) ;
if ( callback . IsEmpty ( ) ) {
cout < < " Missing disconnect callback! " < < endl ;
return ;
}
v8 : : Local < v8 : : Value > arguments [ 1 ] ;
arguments [ 0 ] = Nan : : New < v8 : : String > ( reason ) . ToLocalChecked ( ) ;
callback - > Call ( Nan : : GetCurrentContext ( ) , Nan : : Undefined ( ) , 1 , arguments ) ;
}
NAN_METHOD ( ServerConnection : : _current_ping ) {
auto connection = ObjectWrap : : Unwrap < ServerConnection > ( info . Holder ( ) ) ;
auto & phandler = connection - > protocol_handler ;
if ( phandler )
info . GetReturnValue ( ) . Set ( ( uint32_t ) chrono : : floor < microseconds > ( phandler - > current_ping ( ) ) . count ( ) ) ;
else
info . GetReturnValue ( ) . Set ( - 1 ) ;
}