2019-07-17 19:37:18 +02:00
# include <memory>
# include <misc/endianness.h>
# include <log/LogUtils.h>
# include <ThreadPool/Timer.h>
# include <regex>
# include <src/build.h>
# include <Properties.h>
# include "src/channel/ClientChannelView.h"
# include "SpeakingClient.h"
# include "src/InstanceHandler.h"
# include "StringVariable.h"
# include "src/music/MusicBotManager.h"
# include "misc/timer.h"
using namespace std : : chrono ;
using namespace ts ;
using namespace ts : : server ;
using namespace ts : : protocol ;
//#define PKT_LOG_VOICE
//#define PKT_LOG_WHISPER
bool SpeakingClient : : shouldReceiveVoice ( const std : : shared_ptr < ConnectedClient > & sender ) {
2020-01-24 02:57:58 +01:00
//if(this->properties()[property::CLIENT_AWAY].as<bool>()) return false;
if ( ! this - > properties ( ) [ property : : CLIENT_OUTPUT_HARDWARE ] . as < bool > ( ) ) return false ;
if ( this - > properties ( ) [ property : : CLIENT_OUTPUT_MUTED ] . as < bool > ( ) ) return false ;
{
shared_lock client_lock ( this - > channel_lock ) ;
for ( const auto & entry : this - > mutedClients )
if ( entry . lock ( ) = = sender )
return false ;
}
return true ;
2019-07-17 19:37:18 +02:00
}
bool SpeakingClient : : shouldReceiveVoiceWhisper ( const std : : shared_ptr < ConnectedClient > & sender ) {
2020-01-24 02:57:58 +01:00
if ( ! this - > shouldReceiveVoice ( sender ) )
return false ;
2019-07-17 19:37:18 +02:00
2020-01-26 14:21:34 +01:00
return permission : : v2 : : permission_granted ( this - > cpmerission_needed_whisper_power , sender - > cpmerission_whisper_power ) ;
2019-07-17 19:37:18 +02:00
}
void SpeakingClient : : handlePacketVoice ( const pipes : : buffer_view & data , bool head , bool fragmented ) {
2020-01-24 02:57:58 +01:00
auto server = this - > getServer ( ) ;
auto self = _this . lock ( ) ;
if ( ! self | | ! server ) return ;
2019-07-17 19:37:18 +02:00
if ( data . length ( ) < 3 ) {
2020-01-24 02:57:58 +01:00
this - > disconnect ( " invalid packet (Voice; Length: " + to_string ( data . length ( ) ) + " ) " ) ;
return ;
2019-07-17 19:37:18 +02:00
}
auto current_channel = this - > currentChannel ;
if ( ! current_channel ) { return ; }
if ( ! this - > allowedToTalk ) { return ; }
2020-01-24 02:57:58 +01:00
this - > updateSpeak ( false , system_clock : : now ( ) ) ;
this - > resetIdleTime ( ) ;
2019-07-17 19:37:18 +02:00
auto target_clients = this - > server - > getClientsByChannel ( current_channel ) ;
target_clients . erase ( std : : remove_if ( target_clients . begin ( ) , target_clients . end ( ) , [ & ] ( const shared_ptr < ConnectedClient > & client ) {
2020-01-24 02:57:58 +01:00
if ( client = = this ) return true ;
auto speaking_client = dynamic_pointer_cast < SpeakingClient > ( client ) ;
if ( ! speaking_client ) return true ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
return ! speaking_client - > shouldReceiveVoice ( self ) ;
2019-07-17 19:37:18 +02:00
} ) , target_clients . end ( ) ) ;
if ( target_clients . empty ( ) ) {
2020-01-24 02:57:58 +01:00
return ;
2019-07-17 19:37:18 +02:00
}
2020-01-24 02:57:58 +01:00
VoicePacketFlags flags { } ;
flags . head = head ;
flags . fragmented = fragmented ;
flags . new_protocol = false ;
{
//crypt_mode = 1 | disabled
//crypt_mode = 2 | enabled
auto crypt_mode = this - > server - > voice_encryption_mode ( ) ;
if ( crypt_mode = = 0 )
flags . encrypted = ! current_channel - > properties ( ) [ property : : CHANNEL_CODEC_IS_UNENCRYPTED ] . as < bool > ( ) ;
else
flags . encrypted = crypt_mode = = 2 ;
}
2019-07-17 19:37:18 +02:00
uint16_t vpacketId = be2le16 ( ( char * ) data . data_ptr ( ) ) ;
2020-01-24 02:57:58 +01:00
auto codec = ( uint8_t ) data [ 2 ] ;
2019-07-17 19:37:18 +02:00
# ifdef PKT_LOG_VOICE
logTrace ( lstream < < CLIENT_LOG_PREFIX < < " Voice length: " < < data . length ( ) < < " -> id: " < < vpacketId < < " codec: " < < ( int ) codec < < " head: " < < head < < " fragmented: " < < fragmented ) ;
# endif
char buffer [ data . length ( ) + 2 ] ;
le2be16 ( vpacketId , & buffer [ 0 ] ) ;
le2be16 ( getClientId ( ) , & buffer [ 2 ] ) ;
buffer [ 4 ] = codec ;
if ( data . length ( ) - 3 > 0 ) {
2020-01-24 02:57:58 +01:00
memcpy ( & buffer [ 5 ] , & data [ 3 ] , data . length ( ) - 3 ) ;
2019-07-17 19:37:18 +02:00
}
2020-01-24 02:57:58 +01:00
for ( const auto & client : target_clients ) {
auto speaking_client = static_pointer_cast < SpeakingClient > ( client ) ;
speaking_client - > send_voice_packet ( pipes : : buffer_view { buffer , data . length ( ) + 2 } , flags ) ;
}
2019-07-17 19:37:18 +02:00
}
//2 + 2 + 8
# define OUT_WHISPER_PKT_OFFSET 5
//#define PKT_LOG_WHISPER
enum WhisperType {
2020-01-24 02:57:58 +01:00
SERVER_GROUP = 0 ,
CHANNEL_GROUP = 1 ,
CHANNEL_COMMANDER = 2 ,
ALL = 3
2019-07-17 19:37:18 +02:00
} ;
enum WhisperTarget {
2020-01-24 02:57:58 +01:00
CHANNEL_ALL = 0 ,
CHANNEL_CURRENT = 1 ,
CHANNEL_PARENT = 2 ,
CHANNEL_ALL_PARENT = 3 ,
CHANNEL_FAMILY = 4 ,
CHANNEL_COMPLETE_FAMILY = 5 ,
CHANNEL_SUBCHANNELS = 6
2019-07-17 19:37:18 +02:00
} ;
//All clients => type := SERVER_GROUP and target_id := 0
//Server group => type := SERVER_GROUP and target_id := <server group id>
//Channel group => type := CHANNEL_GROUP and target_id := <channel group id>
//Channel commander => type := CHANNEL_COMMANDER and target_id := 0
void SpeakingClient : : handlePacketVoiceWhisper ( const pipes : : buffer_view & data , bool new_packet ) {
2020-01-24 02:57:58 +01:00
if ( data . length ( ) < 5 ) {
this - > disconnect ( " Invalid packet (Voice whisper) " ) ;
logMessage ( this - > getServerId ( ) , " {} Tried to send a too short whisper packet. Length: {} " , CLIENT_STR_LOG_PREFIX , data . length ( ) ) ;
return ;
}
uint16_t offset = 0 ;
auto vpacketId = be2le16 ( ( char * ) data . data_ptr ( ) , offset , & offset ) ;
auto codec = ( uint8_t ) data [ offset + + ] ;
VoicePacketFlags flags { } ;
flags . head = false ;
flags . fragmented = false ;
flags . new_protocol = new_packet ;
if ( new_packet ) {
if ( data . length ( ) < 7 ) {
this - > disconnect ( " Invalid packet (Voice whisper | New) " ) ;
logMessage ( this - > getServerId ( ) , " {} Tried to send a too short whisper packet. Length: {} " , CLIENT_STR_LOG_PREFIX , data . length ( ) ) ;
return ;
}
auto type = ( WhisperType ) data [ offset + + ] ;
auto target = ( WhisperTarget ) data [ offset + + ] ;
auto type_id = be2le64 ( ( char * ) data . data_ptr ( ) , offset , & offset ) ;
this - > resetIdleTime ( ) ;
size_t data_length = data . length ( ) - offset ;
2019-07-17 19:37:18 +02:00
# ifdef PKT_LOG_WHISPER
2020-01-24 02:57:58 +01:00
logTrace ( this - > getServerId ( ) , " {} Whisper data length: {}. Type: {}. Target: {}. Target ID: {}. " , CLIENT_STR_LOG_PREFIX , data_length , type , target , type_id ) ;
2019-07-17 19:37:18 +02:00
# endif
2020-01-24 02:57:58 +01:00
deque < shared_ptr < SpeakingClient > > available_clients ;
for ( const auto & client : this - > server - > getClients ( ) ) {
auto speakingClient = dynamic_pointer_cast < SpeakingClient > ( client ) ;
if ( ! speakingClient | | client = = this ) continue ;
if ( ! speakingClient - > currentChannel ) continue ;
if ( type = = WhisperType : : ALL ) {
available_clients . push_back ( speakingClient ) ;
} else if ( type = = WhisperType : : SERVER_GROUP ) {
if ( type_id = = 0 )
available_clients . push_back ( speakingClient ) ;
else {
shared_lock client_lock ( this - > channel_lock ) ;
for ( const auto & id : client - > cached_server_groups ) {
if ( id = = type_id ) {
available_clients . push_back ( speakingClient ) ;
break ;
}
}
}
} else if ( type = = WhisperType : : CHANNEL_GROUP ) {
if ( client - > cached_channel_group = = type_id )
available_clients . push_back ( speakingClient ) ;
} else if ( type = = WhisperType : : CHANNEL_COMMANDER ) {
if ( client - > properties ( ) [ property : : CLIENT_IS_CHANNEL_COMMANDER ] . as < bool > ( ) )
available_clients . push_back ( speakingClient ) ;
}
}
if ( target = = WhisperTarget : : CHANNEL_CURRENT ) {
available_clients . erase ( std : : remove_if ( available_clients . begin ( ) , available_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
return target - > currentChannel ! = this - > currentChannel ;
} ) , available_clients . end ( ) ) ;
} else if ( target = = WhisperTarget : : CHANNEL_PARENT ) {
auto current_parent = this - > currentChannel - > parent ( ) ;
if ( ! current_parent ) return ;
available_clients . erase ( std : : remove_if ( available_clients . begin ( ) , available_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
return target - > currentChannel ! = current_parent ;
} ) , available_clients . end ( ) ) ;
} else if ( target = = WhisperTarget : : CHANNEL_ALL_PARENT ) {
shared_ptr < BasicChannel > current_parent ;
{
current_parent = this - > currentChannel - > parent ( ) ;
if ( ! current_parent ) return ;
}
available_clients . erase ( std : : remove_if ( available_clients . begin ( ) , available_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
auto tmp_parent = current_parent ;
while ( tmp_parent & & tmp_parent ! = target - > currentChannel )
tmp_parent = tmp_parent - > parent ( ) ;
return target - > currentChannel ! = tmp_parent ;
} ) , available_clients . end ( ) ) ;
} else if ( target = = WhisperTarget : : CHANNEL_FAMILY ) {
shared_ptr < BasicChannel > current = this - > currentChannel ;
available_clients . erase ( std : : remove_if ( available_clients . begin ( ) , available_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
auto tmp_current = target - > currentChannel ;
while ( tmp_current & & tmp_current ! = current )
tmp_current = tmp_current - > parent ( ) ;
return current ! = tmp_current ;
} ) , available_clients . end ( ) ) ;
} else if ( target = = WhisperTarget : : CHANNEL_COMPLETE_FAMILY ) {
shared_ptr < BasicChannel > current = this - > currentChannel ;
while ( current & & current - > parent ( ) ) current = current - > parent ( ) ;
available_clients . erase ( std : : remove_if ( available_clients . begin ( ) , available_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
auto tmp_current = target - > currentChannel ;
while ( tmp_current & & tmp_current ! = current )
tmp_current = tmp_current - > parent ( ) ;
return current ! = tmp_current ;
} ) , available_clients . end ( ) ) ;
} else if ( target = = WhisperTarget : : CHANNEL_SUBCHANNELS ) {
shared_ptr < BasicChannel > current = this - > currentChannel ;
available_clients . erase ( std : : remove_if ( available_clients . begin ( ) , available_clients . end ( ) , [ & ] ( const shared_ptr < SpeakingClient > & target ) {
return target - > currentChannel - > parent ( ) ! = current ;
} ) , available_clients . end ( ) ) ;
}
if ( available_clients . empty ( ) ) return ;
//Create the packet data
char packet_buffer [ OUT_WHISPER_PKT_OFFSET + data_length ] ;
if ( offset < data . length ( ) )
memcpy ( & packet_buffer [ OUT_WHISPER_PKT_OFFSET ] , & data [ offset ] , data_length ) ;
le2be16 ( vpacketId , packet_buffer , 0 ) ;
le2be16 ( this - > getClientId ( ) , packet_buffer , 2 ) ;
packet_buffer [ 4 ] = codec ;
VoicePacketFlags flags { } ;
auto data = pipes : : buffer_view ( packet_buffer , OUT_WHISPER_PKT_OFFSET + data_length ) ;
for ( const auto & cl : available_clients ) {
if ( cl - > shouldReceiveVoiceWhisper ( _this . lock ( ) ) )
cl - > send_voice_whisper_packet ( data , flags ) ;
}
this - > updateSpeak ( false , system_clock : : now ( ) ) ;
} else {
auto clientCount = ( uint8_t ) data [ offset + + ] ;
auto channelCount = ( uint8_t ) data [ offset + + ] ;
if ( data . length ( ) < 5 + channelCount * 2 + clientCount * 8 ) {
logMessage ( this - > getServerId ( ) , " {} Tried to send a too short whisper packet. Length: {} Required: {} " , CLIENT_STR_LOG_PREFIX , data . length ( ) , to_string ( 5 + channelCount * 2 + clientCount * 8 ) ) ;
return ;
}
this - > resetIdleTime ( ) ;
ChannelId channelIds [ clientCount ] ;
ClientId clientIds [ channelCount ] ;
for ( uint8_t index = 0 ; index < clientCount ; index + + )
channelIds [ index ] = be2le64 ( ( char * ) data . data_ptr ( ) , offset , & offset ) ;
for ( uint8_t index = 0 ; index < channelCount ; index + + )
clientIds [ index ] = be2le16 ( ( char * ) data . data_ptr ( ) , offset , & offset ) ;
size_t dataLength = data . length ( ) - offset ;
2019-07-17 19:37:18 +02:00
# ifdef PKT_LOG_WHISPER
2020-01-24 02:57:58 +01:00
logTrace ( this - > getServerId ( ) , " {} Whisper data length: {}. Client count: {}. Channel count: {}. " , CLIENT_STR_LOG_PREFIX , dataLength , clientCount , channelCount ) ;
2019-07-17 19:37:18 +02:00
# endif
2020-01-24 02:57:58 +01:00
//Create the packet data
char packetBuffer [ OUT_WHISPER_PKT_OFFSET + dataLength ] ;
if ( offset < data . length ( ) )
memcpy ( & packetBuffer [ OUT_WHISPER_PKT_OFFSET ] , & data [ offset ] , dataLength ) ;
le2be16 ( vpacketId , packetBuffer , 0 ) ;
le2be16 ( this - > getClientId ( ) , packetBuffer , 2 ) ;
packetBuffer [ 4 ] = codec ;
VoicePacketFlags flags { } ;
auto data = pipes : : buffer_view ( packetBuffer , OUT_WHISPER_PKT_OFFSET + dataLength ) ;
for ( const auto & cl : this - > server - > getClients ( ) ) { //Faster?
auto speakingClient = dynamic_pointer_cast < SpeakingClient > ( cl ) ;
if ( ! speakingClient | | cl = = this ) continue ;
if ( ! cl - > currentChannel ) continue ;
auto clientChannelId = cl - > currentChannel - > channelId ( ) ;
auto clientId = cl - > getClientId ( ) ;
for ( uint8_t index = 0 ; index < clientCount ; index + + )
if ( channelIds [ index ] = = clientChannelId ) goto handleSend ;
for ( uint8_t index = 0 ; index < channelCount ; index + + )
if ( clientIds [ index ] = = clientId ) goto handleSend ;
continue ;
handleSend :
if ( speakingClient - > shouldReceiveVoiceWhisper ( _this . lock ( ) ) )
speakingClient - > send_voice_whisper_packet ( data , flags ) ;
}
this - > updateSpeak ( false , system_clock : : now ( ) ) ;
}
2019-07-17 19:37:18 +02:00
}
# define TEST_PARM(type) \
do { \
2020-01-24 02:57:58 +01:00
if ( ! cmd [ 0 ] [ key ] . castable < type > ( ) ) \
return { findError ( " parameter_invalid " ) , " Invalid type for " + key } ; \
2019-07-17 19:37:18 +02:00
} while ( false )
auto regex_wildcard = std : : regex ( " .* " ) ;
# define S(x) #x
# define HWID_REGEX(name, pattern) \
auto regex_hwid_ # # name = [ ] ( ) { \
2020-01-24 02:57:58 +01:00
try { \
return std : : regex ( pattern ) ; \
} catch ( std : : exception & ex ) { \
logError ( 0 , " Failed to parse regex for " S ( name ) ) ; \
} \
return regex_wildcard ; \
2019-07-17 19:37:18 +02:00
} ( ) ;
HWID_REGEX ( windows , " ^[a-z0-9]{32},[a-z0-9]{32}$ " ) ;
HWID_REGEX ( unix , " ^[a-z0-9]{32}$ " ) ;
HWID_REGEX ( android , " ^[a-z0-9]{16}$ " ) ;
HWID_REGEX ( ios , " ^[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}$ " ) ;
2020-01-25 23:42:37 +01:00
command_result SpeakingClient : : handleCommandClientInit ( Command & cmd ) {
2020-01-24 02:57:58 +01:00
TIMING_START ( timings ) ;
2020-01-24 02:40:30 +01:00
{
lock_guard < threads : : Mutex > lock ( this - > server - > join_attempts_lock ) ;
auto inetAddr = this - > getPeerIp ( ) ;
if ( config : : voice : : clientConnectLimit > 0 & & this - > server - > join_attempts [ inetAddr ] + 1 > config : : voice : : clientConnectLimit )
2020-01-25 23:42:37 +01:00
return command_result { error : : client_join_rate_limit_reached } ;
2020-01-24 02:40:30 +01:00
if ( config : : voice : : connectLimit > 0 & & this - > server - > join_attempts [ " _ " ] + 1 > config : : voice : : connectLimit )
2020-01-25 23:42:37 +01:00
return command_result { error : : server_join_rate_limit_reached } ;
2020-01-24 02:40:30 +01:00
this - > server - > join_attempts [ inetAddr ] + + ;
this - > server - > join_attempts [ " _ " ] + + ;
}
TIMING_STEP ( timings , " join atmp c " ) ;
2020-01-25 23:42:37 +01:00
if ( ! DatabaseHelper : : assignDatabaseId ( this - > server - > getSql ( ) , this - > server - > getServerId ( ) , _this . lock ( ) ) )
return command_result { error : : vs_critical , " Could not assign database id! " } ;
2020-01-24 02:57:58 +01:00
TIMING_STEP ( timings , " db assign " ) ;
this - > server - > getGroupManager ( ) - > enableCache ( this - > getClientDatabaseId ( ) ) ;
TIMING_STEP ( timings , " gr cache " ) ;
2019-07-17 19:37:18 +02:00
2020-01-24 02:57:58 +01:00
const static vector < string > available_parameters = {
" client_nickname " ,
2019-07-17 19:37:18 +02:00
" client_version " ,
2020-01-24 02:57:58 +01:00
" client_platform " ,
" client_input_muted " ,
" client_input_hardware " ,
" client_output_hardware " ,
" client_output_muted " ,
" client_default_channel " ,
" client_default_channel_password " ,
" client_server_password " ,
" client_meta_data " ,
" client_version_sign " ,
" client_key_offset " ,
" client_nickname_phonetic " ,
" client_default_token " ,
" client_badges=badges " ,
" client_badges " ,
" client_myteamspeak_id " ,
" client_integrations " ,
" client_active_integrations_info " ,
" client_browser_engine " ,
" client_away " ,
" client_away_message " ,
" hwid " ,
" myTeamspeakId " ,
" acTime " ,
" userPubKey " ,
" authSign " ,
" pubSign " ,
" pubSignCert "
} ;
for ( const auto & key : cmd [ 0 ] . keys ( ) ) {
if ( key = = " return_code " ) continue ;
bool parm_allowed = false ;
for ( const auto & _allowed_key : available_parameters ) {
if ( _allowed_key = = key ) {
parm_allowed = true ;
break ;
}
}
if ( ! parm_allowed ) {
debugMessage ( this - > getServerId ( ) , " {} Tried to insert a not allowed parameter within clientinit (Key: {}, Value: {}) " , CLIENT_STR_LOG_PREFIX , key , cmd [ key ] . string ( ) ) ;
continue ;
}
if ( key = = " myTeamspeakId " ) {
this - > properties ( ) [ property : : CLIENT_MYTEAMSPEAK_ID ] = cmd [ key ] . string ( ) ;
continue ;
} else if ( key = = " acTime " ) continue ;
else if ( key = = " userPubKey " ) continue ;
else if ( key = = " authSign " ) continue ;
else if ( key = = " pubSign " ) continue ;
else if ( key = = " pubSignCert " ) continue ;
else if ( key = = " client_version " | | key = = " client_platform " ) {
for ( auto & character : cmd [ key ] . string ( ) )
2020-01-25 23:42:37 +01:00
if ( ! isascii ( character ) ) {
logWarning ( this - > getServerId ( ) , " {} Tried to join within an invalid supplied '{}' ({}) " , CLIENT_STR_LOG_PREFIX , key , cmd [ key ] . string ( ) ) ;
return command_result { error : : client_hacked } ;
}
2020-01-24 02:57:58 +01:00
}
const auto & info = property : : info < property : : ClientProperties > ( key ) ;
if ( * info = = property : : CLIENT_UNDEFINED ) {
2019-07-17 19:37:18 +02:00
logError ( this - > getServerId ( ) , " {} Tried to pass a unknown value {}. Please report this, if you're sure that this key should be known! " , CLIENT_STR_LOG_PREFIX , key ) ;
continue ;
2020-01-24 02:57:58 +01:00
//return {findError("parameter_invalid"), "Unknown property " + key};
}
2020-01-25 23:42:37 +01:00
if ( ! info - > validate_input ( cmd [ key ] . as < string > ( ) ) )
return command_result { error : : parameter_invalid } ;
2020-01-24 02:57:58 +01:00
this - > properties ( ) [ info ] = cmd [ key ] . as < std : : string > ( ) ;
}
debugMessage ( this - > getServerId ( ) , " {} Got client init. (HWID: {}) " , CLIENT_STR_LOG_PREFIX , this - > getHardwareId ( ) ) ;
TIMING_STEP ( timings , " props apply " ) ;
2020-01-26 14:21:34 +01:00
auto permissions_list = this - > calculate_permissions ( {
2020-01-24 02:57:58 +01:00
permission : : b_virtualserver_join_ignore_password ,
permission : : b_client_ignore_bans ,
permission : : b_client_ignore_vpn ,
permission : : i_client_max_clones_uid ,
permission : : i_client_max_clones_ip ,
permission : : i_client_max_clones_hwid ,
permission : : b_client_enforce_valid_hwid ,
permission : : b_client_use_reserved_slot
2020-01-26 14:21:34 +01:00
} , 0 ) ;
auto permissions = map < permission : : PermissionType , permission : : v2 : : PermissionFlaggedValue > ( permissions_list . begin ( ) , permissions_list . end ( ) ) ;
2020-01-24 02:57:58 +01:00
TIMING_STEP ( timings , " perm calc 1 " ) ;
2020-01-26 14:21:34 +01:00
if ( geoloc : : provider_vpn & & ! permission : : v2 : : permission_granted ( 1 , permissions [ permission : : b_client_ignore_vpn ] ) ) {
2020-01-24 02:57:58 +01:00
auto provider = this - > isAddressV4 ( ) ? geoloc : : provider_vpn - > resolveInfoV4 ( this - > getPeerIp ( ) , true ) : geoloc : : provider_vpn - > resolveInfoV6 ( this - > getPeerIp ( ) , true ) ;
if ( provider )
2020-01-25 23:42:37 +01:00
return command_result { error : : server_connect_banned , strvar : : transform ( ts : : config : : messages : : kick_vpn , strvar : : StringValue { " provider.name " , provider - > name } , strvar : : StringValue { " provider.website " , provider - > side } ) } ;
2020-01-24 02:57:58 +01:00
}
2020-01-26 14:21:34 +01:00
if ( this - > getType ( ) = = ClientType : : CLIENT_TEAMSPEAK & & permission : : v2 : : permission_granted ( 1 , permissions [ permission : : b_client_enforce_valid_hwid ] ) ) {
2020-01-24 02:57:58 +01:00
auto hwid = this - > properties ( ) [ property : : CLIENT_HARDWARE_ID ] . as < string > ( ) ;
if (
! std : : regex_match ( hwid , regex_hwid_windows ) & &
! std : : regex_match ( hwid , regex_hwid_unix ) & &
! std : : regex_match ( hwid , regex_hwid_android ) & &
! std : : regex_match ( hwid , regex_hwid_ios )
) {
2020-01-25 23:42:37 +01:00
return command_result { error : : parameter_invalid , config : : messages : : kick_invalid_hardware_id } ;
2020-01-24 02:57:58 +01:00
}
}
TIMING_STEP ( timings , " valid hw ip " ) ;
2020-01-26 14:21:34 +01:00
if ( ! permission : : v2 : : permission_granted ( 1 , permissions [ permission : : b_virtualserver_join_ignore_password ] ) )
2020-01-25 23:42:37 +01:00
if ( ! this - > server - > verifyServerPassword ( cmd [ " client_server_password " ] . string ( ) , true ) )
return command_result { error : : server_invalid_password } ;
2020-01-24 02:57:58 +01:00
size_t clones_uid = 0 ;
size_t clones_ip = 0 ;
size_t clones_hwid = 0 ;
auto _own_hwid = this - > getHardwareId ( ) ;
this - > server - > forEachClient ( [ & ] ( const shared_ptr < ConnectedClient > & client ) {
if ( client - > getExternalType ( ) ! = CLIENT_TEAMSPEAK ) return ;
if ( client - > getUid ( ) = = this - > getUid ( ) )
clones_uid + + ;
if ( client - > getPeerIp ( ) = = this - > getPeerIp ( ) )
clones_ip + + ;
2020-01-28 21:01:02 +01:00
if ( ! _own_hwid . empty ( ) & & client - > getHardwareId ( ) = = _own_hwid )
2020-01-24 02:57:58 +01:00
clones_hwid + + ;
} ) ;
2020-01-26 14:21:34 +01:00
if ( clones_uid > 0 & & permissions [ permission : : i_client_max_clones_uid ] . has_value & & ! permission : : v2 : : permission_granted ( clones_uid , permissions [ permission : : i_client_max_clones_uid ] ) ) {
2020-01-24 02:57:58 +01:00
logMessage ( this - > getServerId ( ) , " {} Disconnecting because there are already {} uid clones connected. (Allowed: {}) " , CLIENT_STR_LOG_PREFIX , clones_uid , permissions [ permission : : i_client_max_clones_uid ] ) ;
2020-01-25 23:42:37 +01:00
return command_result { error : : client_too_many_clones_connected , " too many clones connected (uid) " } ;
2020-01-24 02:57:58 +01:00
}
2020-01-26 14:21:34 +01:00
if ( clones_ip > 0 & & permissions [ permission : : i_client_max_clones_ip ] . has_value & & ! permission : : v2 : : permission_granted ( clones_ip , permissions [ permission : : i_client_max_clones_ip ] ) ) {
2020-01-24 02:57:58 +01:00
logMessage ( this - > getServerId ( ) , " {} Disconnecting because there are already {} ip clones connected. (Allowed: {}) " , CLIENT_STR_LOG_PREFIX , clones_ip , permissions [ permission : : i_client_max_clones_ip ] ) ;
2020-01-25 23:42:37 +01:00
return command_result { error : : client_too_many_clones_connected , " too many clones connected (ip) " } ;
2020-01-24 02:57:58 +01:00
}
2020-01-26 14:21:34 +01:00
if ( clones_hwid > 0 & & permissions [ permission : : i_client_max_clones_hwid ] . has_value & & ! permission : : v2 : : permission_granted ( clones_hwid , permissions [ permission : : i_client_max_clones_hwid ] ) ) {
2020-01-24 02:57:58 +01:00
logMessage ( this - > getServerId ( ) , " {} Disconnecting because there are already {} hwid clones connected. (Allowed: {}) " , CLIENT_STR_LOG_PREFIX , clones_hwid , permissions [ permission : : i_client_max_clones_hwid ] ) ;
2020-01-25 23:42:37 +01:00
return command_result { error : : client_too_many_clones_connected , " too many clones connected (hwid) " } ;
2020-01-24 02:57:58 +01:00
}
TIMING_STEP ( timings , " max clones " ) ;
auto banEntry = this - > resolveActiveBan ( this - > getPeerIp ( ) ) ;
if ( banEntry ) {
logMessage ( this - > getServerId ( ) , " {} Disconnecting while init because of ban record. Record id {} at server {} " ,
CLIENT_STR_LOG_PREFIX ,
banEntry - > banId ,
banEntry - > serverId ) ;
serverInstance - > banManager ( ) - > trigger_ban ( banEntry , this - > getServerId ( ) , this - > getUid ( ) , this - > getHardwareId ( ) , this - > getDisplayName ( ) , this - > getPeerIp ( ) ) ;
string fullReason = string ( ) + " You are banned " + ( banEntry - > serverId = = 0 ? " globally " : " from this server " ) + " . Reason: \" " + banEntry - > reason + " \" . Ban expires " ;
string time ;
if ( banEntry - > until . time_since_epoch ( ) . count ( ) ! = 0 ) {
time + = " in " ;
auto seconds = chrono : : ceil < chrono : : seconds > ( banEntry - > until - chrono : : system_clock : : now ( ) ) . count ( ) ;
tm p { } ;
memset ( & p , 0 , sizeof ( p ) ) ;
while ( seconds > = 365 * 24 * 60 * 60 ) {
p . tm_year + + ;
seconds - = 365 * 24 * 60 * 60 ;
}
while ( seconds > = 24 * 60 * 60 ) {
p . tm_yday + + ;
seconds - = 24 * 60 * 60 ;
}
while ( seconds > = 60 * 60 ) {
p . tm_hour + + ;
seconds - = 60 * 60 ;
}
while ( seconds > = 60 ) {
p . tm_min + + ;
seconds - = 60 ;
}
p . tm_sec = ( int ) seconds ;
if ( p . tm_year > 0 )
time + = to_string ( p . tm_year ) + " years, " ;
if ( p . tm_yday > 0 )
time + = to_string ( p . tm_yday ) + " days, " ;
if ( p . tm_hour > 0 )
time + = to_string ( p . tm_hour ) + " hours, " ;
if ( p . tm_min > 0 )
time + = to_string ( p . tm_min ) + " minutes, " ;
if ( p . tm_sec > 0 )
time + = to_string ( p . tm_sec ) + " seconds, " ;
if ( time . empty ( ) ) time = " now, " ;
time = time . substr ( 0 , time . length ( ) - 2 ) ;
} else time = " never " ;
fullReason + = time + " ! " ;
2020-01-25 23:42:37 +01:00
return command_result { error : : server_connect_banned , fullReason } ;
2020-01-24 02:57:58 +01:00
}
TIMING_STEP ( timings , " ban resolve " ) ;
size_t count = 0 ;
{
for ( const auto & cl : this - > server - > getClients ( ) )
if ( ( cl - > getType ( ) = = CLIENT_TEAMSPEAK | | cl - > getType ( ) = = CLIENT_WEB | | cl - > getType ( ) = = CLIENT_TEASPEAK | | cl - > getType ( ) = = CLIENT_MUSIC ) )
if ( cl - > connectionState ( ) < = ConnectionState : : CONNECTED & & cl - > connectionState ( ) > = ConnectionState : : INIT_HIGH )
count + + ;
}
auto maxClients = this - > server - > properties ( ) [ property : : VIRTUALSERVER_MAXCLIENTS ] . as < size_t > ( ) ;
auto reserved = this - > server - > properties ( ) [ property : : VIRTUALSERVER_RESERVED_SLOTS ] . as < size_t > ( ) ;
2020-01-26 14:21:34 +01:00
bool allowReserved = permission : : v2 : : permission_granted ( 1 , permissions [ permission : : b_client_use_reserved_slot ] ) ;
2020-01-24 02:57:58 +01:00
if ( reserved > maxClients ) {
2020-01-25 23:42:37 +01:00
if ( ! allowReserved )
return command_result { error : : server_maxclients_reached } ;
2020-01-24 02:57:58 +01:00
} else if ( maxClients - ( allowReserved ? 0 : reserved ) < = count )
2020-01-25 23:42:37 +01:00
return command_result { error : : server_maxclients_reached } ;
2020-01-24 02:57:58 +01:00
TIMING_STEP ( timings , " max clients " ) ;
auto old_last_connected = this - > properties ( ) [ property : : CLIENT_LASTCONNECTED ] . as < int64_t > ( ) ;
this - > properties ( ) [ property : : CONNECTION_CLIENT_IP ] = this - > getLoggingPeerIp ( ) ;
this - > properties ( ) [ property : : CLIENT_LASTCONNECTED ] = std : : chrono : : duration_cast < std : : chrono : : seconds > ( std : : chrono : : system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
this - > properties ( ) [ property : : CLIENT_TOTALCONNECTIONS ] + + ;
{
auto time_point = system_clock : : time_point ( ) + seconds ( old_last_connected ) ;
if ( time_point < build : : version ( ) - > timestamp ) {
logMessage ( this - > getServerId ( ) , " {} Client may cached a old permission list (Server is newer than the client's last join) " , CLIENT_STR_LOG_PREFIX ) ;
TIMING_STEP ( timings , " pre dummy p " ) ;
Command _dummy ( " dummy_permissionslist " ) ;
this - > handleCommandPermissionList ( _dummy ) ;
TIMING_STEP ( timings , " pst dummy p " ) ;
}
}
this - > postCommandHandler . emplace_back ( [ & ] ( ) {
auto self = dynamic_pointer_cast < SpeakingClient > ( _this . lock ( ) ) ;
std : : thread ( [ self ] ( ) {
threads : : MutexLock l1 ( self - > disconnectLock ) ;
if ( self - > state ! = ConnectionState : : INIT_HIGH ) return ;
try {
self - > processJoin ( ) ;
} catch ( std : : exception & ex ) {
logError ( self - > getServerId ( ) , " Failed to proceed client join for {}. Got exception with message {} " , CLIENT_STR_LOG_PREFIX_ ( self ) , ex . what ( ) ) ;
self - > closeConnection ( ) ;
}
} ) . detach ( ) ;
} ) ;
debugMessage ( this - > getServerId ( ) , " {} Client init timings: {} " , CLIENT_STR_LOG_PREFIX , TIMING_FINISH ( timings ) ) ;
2020-01-25 23:42:37 +01:00
return command_result { error : : ok } ;
2019-07-17 19:37:18 +02:00
}
/* must be triggered while helding an execute lock */
//Note: Client permissions are may not really
void SpeakingClient : : processJoin ( ) {
2020-01-24 02:57:58 +01:00
TIMING_START ( timings ) ;
auto ref_server = this - > server ;
this - > resetIdleTime ( ) ;
threads : : MutexLock lock ( this - > command_lock ) ; //Don't process any commands!
if ( this - > state ! = ConnectionState : : INIT_HIGH ) {
logError ( this - > getServerId ( ) , " {} Invalid processJoin() connection state! " , CLIENT_STR_LOG_PREFIX ) ;
return ;
}
TIMING_STEP ( timings , " setup " ) ;
ref_server - > registerClient ( _this . lock ( ) ) ;
TIMING_STEP ( timings , " server reg " ) ;
ref_server - > getGroupManager ( ) - > cleanupAssignments ( this - > getClientDatabaseId ( ) ) ;
TIMING_STEP ( timings , " grp cleanup " ) ;
ref_server - > getGroupManager ( ) - > update_server_group_property ( _this . lock ( ) , true , nullptr ) ;
TIMING_STEP ( timings , " grp apply " ) ;
this - > properties ( ) [ property : : CLIENT_COUNTRY ] = config : : geo : : countryFlag ;
if ( geoloc : : provider ) {
auto loc = this - > isAddressV4 ( ) ? geoloc : : provider - > resolveInfoV4 ( this - > getPeerIp ( ) , false ) : geoloc : : provider - > resolveInfoV6 ( this - > getPeerIp ( ) , false ) ;
if ( loc ) {
debugMessage ( this - > getServerId ( ) , " Client " + this - > getDisplayName ( ) + " | " + this - > getLoggingPeerIp ( ) + " comes from " + loc - > name + " | " + loc - > identifier ) ;
this - > properties ( ) [ property : : CLIENT_COUNTRY ] = loc - > identifier ;
} else {
logError ( ref_server ? ref_server - > getServerId ( ) : 0 , " Could not resolve country for ip " + this - > getLoggingPeerIp ( ) + " | " + this - > getDisplayName ( ) ) ;
}
}
//this->updateChannelClientProperties(false); /* will be already updated via assignChannel */
if ( ref_server - > properties ( ) [ property : : VIRTUALSERVER_HOSTMESSAGE_MODE ] . as < int > ( ) = = 3 & & ! ref_server - > properties ( ) [ property : : VIRTUALSERVER_HOSTMESSAGE ] . as < string > ( ) . empty ( ) ) {
auto weak = this - > _this ;
threads : : Thread ( [ weak ] ( ) {
threads : : self : : sleep_for ( milliseconds ( 2000 ) ) ;
auto client = weak . lock ( ) ;
if ( ! client | | ! client - > server ) return ;
client - > disconnect ( client - > server - > properties ( ) [ property : : VIRTUALSERVER_HOSTMESSAGE ] . as < string > ( ) ) ;
} ) . detach ( ) ;
}
TIMING_STEP ( timings , " ip 2 loc as " ) ; //IP to location assignment
this - > sendServerInit ( ) ;
debugMessage ( this - > getServerId ( ) , " Client id: " + to_string ( this - > getClientId ( ) ) ) ;
TIMING_STEP ( timings , " notify sini " ) ;
if ( ! ref_server - > assignDefaultChannel ( this - > ref ( ) , false ) ) {
2020-01-25 23:42:37 +01:00
auto result = command_result { error : : vs_critical , " Could not assign default channel! " } ;
this - > notifyError ( result ) ;
result . release_details ( ) ;
2020-01-24 02:57:58 +01:00
this - > closeConnection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
return ;
}
TIMING_STEP ( timings , " assign chan " ) ;
this - > sendChannelList ( true ) ;
this - > state = ConnectionState : : CONNECTED ;
TIMING_STEP ( timings , " send chan t " ) ;
/* trick the join method */
auto channel = this - > currentChannel ;
this - > currentChannel = nullptr ;
{
/* enforce an update of these properties */
this - > properties ( ) [ property : : CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID ] = " 0 " ;
this - > properties ( ) [ property : : CLIENT_CHANNEL_GROUP_ID ] = " 0 " ;
this - > properties ( ) [ property : : CLIENT_TALK_POWER ] = " 0 " ;
unique_lock server_channel_lock ( this - > server - > channel_tree_lock ) ;
this - > server - > client_move ( this - > ref ( ) , channel , nullptr , " " , ViewReasonId : : VREASON_USER_ACTION , false , server_channel_lock ) ;
this - > subscribeChannel ( { this - > currentChannel } , false , true ) ;
}
TIMING_STEP ( timings , " join move " ) ;
this - > properties ( ) - > triggerAllModified ( ) ;
this - > notifyServerGroupList ( ) ;
this - > notifyChannelGroupList ( ) ;
TIMING_STEP ( timings , " notify grou " ) ;
logMessage ( this - > getServerId ( ) , " Voice client {}/{} ({}) from {} joined. " ,
this - > getClientDatabaseId ( ) ,
this - > getUid ( ) ,
this - > getDisplayName ( ) ,
this - > getLoggingPeerIp ( ) + " : " + to_string ( this - > getPeerPort ( ) )
) ;
this - > connectTimestamp = chrono : : system_clock : : now ( ) ;
this - > idleTimestamp = chrono : : system_clock : : now ( ) ;
debugMessage ( this - > getServerId ( ) , " {} Client join timings: {} " , CLIENT_STR_LOG_PREFIX , TIMING_FINISH ( timings ) ) ;
2019-07-17 19:37:18 +02:00
}
void SpeakingClient : : processLeave ( ) {
2020-01-24 02:57:58 +01:00
auto ownLock = _this . lock ( ) ;
auto server = this - > getServer ( ) ;
if ( server ) {
logMessage ( this - > getServerId ( ) , " Voice client {}/{} ({}) from {} left. " , this - > getClientDatabaseId ( ) , this - > getUid ( ) , this - > getDisplayName ( ) , this - > getLoggingPeerIp ( ) + " : " + to_string ( this - > getPeerPort ( ) ) ) ;
{
unique_lock server_channel_lock ( this - > server - > channel_tree_lock ) ;
server - > unregisterClient ( ownLock , " disconnected " , server_channel_lock ) ; /* already moves client to void if needed */
}
server - > groups - > disableCache ( ownLock - > getClientDatabaseId ( ) ) ;
server - > musicManager - > cleanup_client_bots ( this - > getClientDatabaseId ( ) ) ;
//ref_server = nullptr; Removed caused nullptr exceptions
}
{ //Delete own viewing clients
/*
* No need , are only weak references !
threads : : MutexLock l ( this - > viewLock ) ;
this - > visibleClients . clear ( ) ;
this - > mutedClients . clear ( ) ;
*/
}
2019-07-17 19:37:18 +02:00
}
void SpeakingClient : : triggerVoiceEnd ( ) {
2020-01-24 02:57:58 +01:00
this - > properties ( ) [ property : : CLIENT_FLAG_TALKING ] = false ;
2019-07-17 19:37:18 +02:00
}
void SpeakingClient : : updateSpeak ( bool onlyUpdate , const std : : chrono : : system_clock : : time_point & now ) {
2020-01-24 02:57:58 +01:00
threads : : MutexLock lock ( this - > speak_lock ) ;
if ( this - > speak_last_packet + this - > speak_accuracy < now ) {
if ( this - > speak_last_packet > this - > speak_begin ) {
if ( ! this - > properties ( ) [ property : : CLIENT_FLAG_TALKING ] . as < bool > ( ) )
this - > properties ( ) [ property : : CLIENT_FLAG_TALKING ] = true ;
this - > speak_time + = duration_cast < milliseconds > ( this - > speak_last_packet - this - > speak_begin ) ;
} else {
if ( this - > properties ( ) [ property : : CLIENT_FLAG_TALKING ] . as < bool > ( ) )
this - > properties ( ) [ property : : CLIENT_FLAG_TALKING ] = false ;
}
this - > speak_begin = now ;
this - > speak_last_packet = now ;
}
if ( ! onlyUpdate )
this - > speak_last_packet = now ;
2019-07-17 19:37:18 +02:00
}
void SpeakingClient : : tick ( const std : : chrono : : system_clock : : time_point & time ) {
2020-01-24 02:57:58 +01:00
ConnectedClient : : tick ( time ) ;
ALARM_TIMER ( A1 , " SpeakingClient::tick " , milliseconds ( 2 ) ) ;
this - > updateSpeak ( true , time ) ;
if ( this - > state = = ConnectionState : : CONNECTED ) {
if ( this - > max_idle_time . has_value ) {
auto max_idle = this - > max_idle_time . value ;
if ( max_idle > 0 & & this - > idleTimestamp . time_since_epoch ( ) . count ( ) > 0 & & duration_cast < seconds > ( time - this - > idleTimestamp ) . count ( ) > max_idle ) {
this - > server - > notify_client_kick ( this - > ref ( ) , this - > server - > getServerRoot ( ) , ts : : config : : messages : : idle_time_exceeded , nullptr ) ;
this - > closeConnection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
}
}
}
2019-07-17 19:37:18 +02:00
}
void SpeakingClient : : updateChannelClientProperties ( bool channel_lock , bool notify ) {
2020-01-24 02:57:58 +01:00
ConnectedClient : : updateChannelClientProperties ( channel_lock , notify ) ;
2020-01-26 14:21:34 +01:00
this - > max_idle_time = this - > calculate_permission ( permission : : i_client_max_idletime , this - > currentChannel ? this - > currentChannel - > channelId ( ) : 0 ) ;
2019-07-17 19:37:18 +02:00
}
2020-01-25 23:42:37 +01:00
command_result SpeakingClient : : handleCommand ( Command & command ) {
2020-01-24 02:57:58 +01:00
if ( this - > connectionState ( ) = = ConnectionState : : INIT_HIGH ) {
if ( this - > handshake . state = = HandshakeState : : BEGIN | | this - > handshake . state = = HandshakeState : : IDENTITY_PROOF ) {
2020-01-25 23:42:37 +01:00
command_result result ;
2020-01-24 02:57:58 +01:00
if ( command . command ( ) = = " handshakebegin " )
result = this - > handleCommandHandshakeBegin ( command ) ;
else if ( command . command ( ) = = " handshakeindentityproof " )
result = this - > handleCommandHandshakeIdentityProof ( command ) ;
else
2020-01-25 23:42:37 +01:00
result = command_result { error : : client_not_logged_in } ;
2020-01-24 02:57:58 +01:00
2020-01-25 23:42:37 +01:00
if ( result . error_code ( ) )
this - > postCommandHandler . push_back ( [ & ] {
this - > closeConnection ( system_clock : : now ( ) + seconds ( 1 ) ) ;
} ) ;
2020-01-24 02:57:58 +01:00
return result ;
}
}
return ConnectedClient : : handleCommand ( command ) ;
2019-07-17 19:37:18 +02:00
}