Finalizing the voice connection cleanup

This commit is contained in:
WolverinDEV 2020-07-29 22:53:40 +02:00
parent 4c91a7a3bf
commit 7a974677fb
25 changed files with 864 additions and 566 deletions

View File

@ -47,8 +47,7 @@ set(SERVER_SOURCE_FILES
src/client/voice/VoiceClient.cpp
src/client/voice/VoiceClientHandschake.cpp
src/client/voice/VoiceClientCommandHandler.cpp
src/client/voice/VoiceClientPacketHandler.cpp
src/client/voice/VoiceClientView.cpp
src/client/voice/VoiceClientConnectionPacketHandler.cpp
src/client/voice/PacketStatistics.cpp
src/TS3ServerClientManager.cpp
src/VirtualServer.cpp
@ -150,6 +149,9 @@ set(SERVER_SOURCE_FILES
src/client/voice/PacketDecoder.cpp
src/client/voice/PacketEncoder.cpp
src/client/voice/ServerCommandExecutor.cpp
src/client/voice/PingHandler.cpp
src/client/voice/CryptSetupHandler.cpp
)
if (COMPILE_WEB_CLIENT)

View File

@ -279,6 +279,7 @@ namespace ts {
inline int voice_encryption_mode() { return this->_voice_encryption_mode; }
inline std::shared_ptr<conversation::ConversationManager> conversation_manager() { return this->_conversation_manager; }
inline auto& get_channel_tree_lock() { return this->channel_tree_lock; }
void update_channel_from_permissions(const std::shared_ptr<BasicChannel>& /* channel */, const std::shared_ptr<ConnectedClient>& /* issuer */);
protected:

View File

@ -603,42 +603,7 @@ bool ConnectedClient::notifyError(const command_result& result, const std::strin
}
void ConnectedClient::writeCommandResult(ts::command_builder &cmd_builder, const command_result &result, const std::string& errorCodeKey) {
switch(result.type()) {
case command_result_type::error:
write_command_result_error(cmd_builder.bulk(0), result, errorCodeKey);
break;
case command_result_type::detailed:
write_command_result_detailed(cmd_builder.bulk(0), result, errorCodeKey);
break;
case command_result_type::bulked: {
auto bulks = result.bulks();
cmd_builder.reserve_bulks(bulks->size());
for(size_t index{0}; index < bulks->size(); index++) {
auto& entry = bulks->at(index);
switch (entry.type()) {
case command_result_type::error:
write_command_result_error(cmd_builder.bulk(index), entry, errorCodeKey);
break;
case command_result_type::detailed:
write_command_result_detailed(cmd_builder.bulk(index), entry, errorCodeKey);
break;
case command_result_type::bulked:
assert(false);
break;
}
}
if(bulks->empty()) {
logWarning(this->getServerId(), "{} Trying to send empty error bulk.", CLIENT_STR_LOG_PREFIX_(this));
cmd_builder.put_unchecked(0, errorCodeKey, (uint32_t) error::ok);
cmd_builder.put_unchecked(0, "msg", findError(error::ok).message);
}
break;
}
default:
assert(false);
break;
}
result.build_error_response(cmd_builder, errorCodeKey);
}
inline std::shared_ptr<ViewEntry> pop_view_entry(std::deque<std::shared_ptr<ViewEntry>>& pool, ChannelId id) {

View File

@ -2135,6 +2135,9 @@ command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) {
};
command_builder result{this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyserverlog" : ""};
result.put_unchecked(0, "last_pos", 0);
result.put_unchecked(0, "file_size", 0);
size_t index{0};
if(lagacy) {
for(const auto& message : log_output) {

View File

@ -0,0 +1,273 @@
//
// Created by WolverinDEV on 29/07/2020.
//
#include <log/LogUtils.h>
#include <misc/base64.h>
#include <misc/digest.h>
#include <src/InstanceHandler.h>
#include <misc/endianness.h>
#include "CryptSetupHandler.h"
#include "./VoiceClientConnection.h"
using namespace ts;
using namespace ts::connection;
using namespace ts::server::server::udp;
inline void generate_random(uint8_t *destination, size_t length) {
while(length-- > 0)
*(destination++) = (uint8_t) rand();
}
CryptSetupHandler::CryptSetupHandler(VoiceClientConnection *connection) : connection{connection} { }
CryptSetupHandler::CommandHandleResult CryptSetupHandler::handle_command(const std::string_view &payload) {
std::variant<ts::command_result, CommandHandleResult>(CryptSetupHandler::*command_handler)(const ts::command_parser&) = nullptr;
if(payload.starts_with("clientinitiv"))
command_handler = &CryptSetupHandler::handleCommandClientInitIv;
else if(payload.starts_with("clientek"))
command_handler = &CryptSetupHandler::handleCommandClientEk;
else if(payload.starts_with("clientinit"))
command_handler = &CryptSetupHandler::handleCommandClientInit;
if(!command_handler)
return CommandHandleResult::PASS_THROUGH;
this->last_command_ = std::chrono::system_clock::now();
ts::command_parser parser{payload};
try {
std::unique_lock cmd_lock{this->command_lock};
auto result = (this->*command_handler)(parser);
CommandHandleResult handle_result;
if(std::holds_alternative<CommandHandleResult>(result)) {
handle_result = std::get<CommandHandleResult>(result);
} else {
auto cmd_result = std::move(std::get<ts::command_result>(result));
ts::command_builder notify{"error"};
cmd_result.build_error_response(notify, "id");
if(parser.has_key("return_code"))
notify.put_unchecked(0, "return_code", parser.value("return_code"));
this->connection->send_command(notify.build(), false, nullptr);
handle_result = cmd_result.has_error() ? CommandHandleResult::CLOSE_CONNECTION : CommandHandleResult::CONSUME_COMMAND;
cmd_result.release_data();
}
return handle_result;
} catch (std::exception& ex) {
debugMessage(this->connection->virtual_server_id(), "{} Failed to handle connection command: {}. Closing connection.", this->connection->log_prefix(), ex.what());
return CommandHandleResult::CLOSE_CONNECTION;
}
}
CryptSetupHandler::CommandResult CryptSetupHandler::handleCommandClientInitIv(const ts::command_parser &cmd) {
auto client = this->connection->getCurrentClient();
assert(client);
std::unique_lock state_lock{client->state_lock};
if(client->connectionState() == ConnectionState::CONNECTED) { /* we've a reconnect */
auto lastPingResponse = this->connection->ping_handler().last_ping_response();
if(std::chrono::system_clock::now() - lastPingResponse < std::chrono::seconds(5)) {
logMessage(this->connection->virtual_server_id(), "{} Client initialized session reconnect, but last ping response is not older then 5 seconds ({}). Ignoring attempt",
this->connection->log_prefix(),
duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - lastPingResponse).count()
);
return ts::command_result{error::ok};
} else if(!config::voice::allow_session_reinitialize) {
logMessage(this->connection->virtual_server_id(), "{} Client initialized session reconnect and last ping response is older then 5 seconds ({}). Dropping attempt because its not allowed due to config settings",
this->connection->log_prefix(),
duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - lastPingResponse).count()
);
return ts::command_result{error::ok};
}
logMessage(this->connection->virtual_server_id(), "{} Client initialized reconnect and last ping response is older then 5 seconds ({}). Allowing attempt",
this->connection->log_prefix(),
duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - lastPingResponse).count()
);
state_lock.unlock();
{
std::unique_lock server_channel_lock(client->server->get_channel_tree_lock()); /* we cant get moved if this is locked! */
if(client->currentChannel)
client->server->client_move(client->ref(), nullptr, nullptr, config::messages::timeout::connection_reinitialized, ViewReasonId::VREASON_TIMEOUT, false, server_channel_lock);
}
client->finalDisconnect();
state_lock.lock();
} else if(client->state >= ConnectionState::DISCONNECTING) {
state_lock.unlock();
std::shared_lock disconnect_finish{client->finalDisconnectLock}; /* await until the last disconnect has been processed */
state_lock.lock();
client->state = ConnectionState::INIT_HIGH;
} else if(client->state == ConnectionState::INIT_HIGH) {
logTrace(client->getServerId(), "{} Received a duplicated initiv. It seems like our initivexpand2 hasn't yet reached the client. The acknowledge handler should handle this issue for us.", CLIENT_STR_LOG_PREFIX_(client));
return command_result{error::ok};
} else {
client->state = ConnectionState::INIT_HIGH;
}
state_lock.unlock();
this->connection->reset();
this->connection->packet_decoder().register_initiv_packet();
this->connection->packet_statistics().reset_offsets();
bool use_teaspeak = cmd.has_switch("teaspeak");
if(use_teaspeak ? !config::server::clients::teaspeak : !config::server::clients::teamspeak)
return command_result{error::client_type_is_not_allowed};
if(use_teaspeak) {
debugMessage(this->connection->virtual_server_id(), "{} Client using TeaSpeak.", this->connection->log_prefix());
client->properties()[property::CLIENT_TYPE_EXACT] = ClientType::CLIENT_TEASPEAK;
}
/* normal TeamSpeak handling */
this->seed_client = base64::decode(cmd.value("alpha"));
if(this->seed_client.length() != 10)
return ts::command_result{error::parameter_invalid, "alpha"};
std::string clientOmega = base64::decode(cmd.value("omega")); //The identity public key
std::string ip = cmd.value("ip");
bool ot = cmd.has_key("ot") ? cmd.value_as<bool>("ot") : false;
{
this->remote_key = std::shared_ptr<ecc_key>(new ecc_key{}, [](ecc_key* key){
if(!key) return;
ecc_free(key);
delete key;
});
auto state = ecc_import((const unsigned char *) clientOmega.data(), clientOmega.length(), &*this->remote_key);
if(state != CRYPT_OK) {
this->remote_key = nullptr;
return ts::command_result{error::client_could_not_validate_identity};
}
client->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1(clientOmega));
}
this->new_protocol = !use_teaspeak && ot && config::experimental_31 && (this->client_protocol_time_ >= 173265950ULL || this->client_protocol_time_ == (uint32_t) 5680278000ULL);
this->new_protocol = true;
{
size_t server_seed_length = this->new_protocol ? 54 : 10;
char beta[server_seed_length];
generate_random((uint8_t *) beta, server_seed_length);
this->seed_server = std::string{beta, server_seed_length};
}
if(this->new_protocol) {
//Pre setup
//Generate chain
debugMessage(this->connection->virtual_server_id(), "{} Got client 3.1 protocol with build timestamp {}", this->connection->log_prefix(), this->client_protocol_time_);
this->chain_data = serverInstance->getTeamSpeakLicense()->license();
this->chain_data->chain->addEphemeralEntry();
auto crypto_chain = this->chain_data->chain->exportChain();
auto crypto_chain_hash = digest::sha256(crypto_chain);
size_t sign_buffer_size{128};
char sign_buffer[sign_buffer_size];
prng_state prng_state{};
memset(&prng_state, 0, sizeof(prng_state));
auto sign_result = ecc_sign_hash(
(u_char*) crypto_chain_hash.data(), crypto_chain_hash.length(),
(u_char*) sign_buffer, &sign_buffer_size,
&prng_state, find_prng("sprng"),
client->getServer()->serverKey());
if(sign_result != CRYPT_OK) {
return ts::command_result{error::vs_critical, "failed to sign crypto chain"};
}
ts::command_builder answer{"initivexpand2"};
answer.put_unchecked(0, "time", std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count());
answer.put_unchecked(0, "l", base64::encode(crypto_chain));
answer.put_unchecked(0, "beta", base64::encode(this->seed_server));
answer.put_unchecked(0, "omega", client->getServer()->publicServerKey());
answer.put_unchecked(0, "proof", base64::encode((const char*) sign_buffer, sign_buffer_size));
answer.put_unchecked(0, "tvd", "");
answer.put_unchecked(0, "root", base64::encode((char*) this->chain_data->public_key, 32));
answer.put_unchecked(0, "ot", "1");
this->connection->send_command(answer.build(), false, nullptr);
client->handshake.state = SpeakingClient::HandshakeState::SUCCEEDED; /* we're doing the verify via TeamSpeak */
return CommandHandleResult::CONSUME_COMMAND; /* we don't want to send an error id=0 msg=ok */
} else {
debugMessage(this->connection->virtual_server_id(), "{} Got non client 3.1 protocol with build timestamp {}", this->connection->log_prefix(), this->client_protocol_time_, this->client_protocol_time_);
auto server_public_key = client->getServer()->publicServerKey();
if(server_public_key.empty()) {
return ts::command_result{error::vs_critical, "failed to export server public key"};
}
{
ts::command_builder answer{"initivexpand"};
answer.put_unchecked(0, "alpha", base64::encode(this->seed_client));
answer.put_unchecked(0, "beta", base64::encode(this->seed_server));
answer.put_unchecked(0, "omega", server_public_key);
if(use_teaspeak) {
answer.put_unchecked(0, "teaspeak", "1");
client->handshake.state = SpeakingClient::HandshakeState::BEGIN; /* we need to start the handshake */
} else {
client->handshake.state = SpeakingClient::HandshakeState::SUCCEEDED; /* we're using the provided identity as identity */
}
this->connection->send_command(answer.build(), false, nullptr);
this->connection->packet_encoder().encrypt_pending_packets();
}
std::string error;
if(!this->connection->getCryptHandler()->setupSharedSecret(this->seed_client, this->seed_server, &*this->remote_key, client->getServer()->serverKey(), error)){
logError(this->connection->virtual_server_id(), "{} Failed to calculate shared secret {}. Dropping client.",
this->connection->log_prefix(), error);
return ts::command_result{error::vs_critical};
}
return CommandHandleResult::CONSUME_COMMAND; /* we don't want to send an error id=0 msg=ok */
}
}
CryptSetupHandler::CommandResult CryptSetupHandler::handleCommandClientEk(const ts::command_parser &cmd) {
debugMessage(this->connection->virtual_server_id(), "{} Got client ek!", this->connection->log_prefix());
auto client_key = base64::decode(cmd.value("ek"));
auto private_key = this->chain_data->chain->generatePrivateKey(this->chain_data->root_key, this->chain_data->root_index);
this->connection->getCryptHandler()->setupSharedSecretNew(this->seed_client, this->seed_server, (char*) private_key.data(), client_key.data());
this->connection->packet_encoder().acknowledge_manager().reset();
{
char buffer[2];
le2be16(1, buffer);
auto pflags = protocol::PacketFlag::NewProtocol;
this->connection->send_packet(protocol::PacketType::ACK, (protocol::PacketFlag::PacketFlag) pflags, buffer, 2);
//Send the encrypted acknowledge (most the times the second packet; If not we're going into the resend loop)
//We cant use the send_packet_acknowledge function since it sends the acknowledge unencrypted
}
return CommandHandleResult::CONSUME_COMMAND; /* we don't want to send an error id=0 msg=ok */
}
CryptSetupHandler::CommandResult CryptSetupHandler::handleCommandClientInit(const ts::command_parser &) {
/* the client must have received everything */
this->connection->packet_encoder().acknowledge_manager().reset();
this->seed_client.clear();
this->seed_server.clear();
this->chain_data = nullptr;
return CommandHandleResult::PASS_THROUGH;
}

View File

@ -0,0 +1,56 @@
#pragma once
#include <string_view>
#include <query/command3.h>
#include <Error.h>
#include <src/lincense/TeamSpeakLicense.h>
#include <tomcrypt.h>
#include <variant>
namespace ts::connection {
class VoiceClientConnection;
}
namespace ts::server::server::udp {
class CryptSetupHandler {
public:
enum struct CommandHandleResult {
CONSUME_COMMAND,
CLOSE_CONNECTION,
PASS_THROUGH
};
using CommandResult = std::variant<ts::command_result, CommandHandleResult>;
explicit CryptSetupHandler(connection::VoiceClientConnection*);
[[nodiscard]] inline const auto& identity_key() const { return this->remote_key; }
void set_client_protocol_time(uint32_t time) { this->client_protocol_time_ = time; }
[[nodiscard]] inline auto client_protocol_time() const { return this->client_protocol_time_; }
[[nodiscard]] inline auto last_handled_command() const { return this->last_command_; }
/* Attention this method gets from the voice IO thread. It's not thread save! */
[[nodiscard]] CommandHandleResult handle_command(const std::string_view& /* command */);
private:
connection::VoiceClientConnection* connection;
std::chrono::system_clock::time_point last_command_{};
std::mutex command_lock{};
bool new_protocol{false};
uint32_t client_protocol_time_{0};
std::string seed_client{}; /* alpha */
std::string seed_server{}; /* beta */
std::shared_ptr<LicenseChainData> chain_data{};
std::shared_ptr<ecc_key> remote_key{};
CommandResult handleCommandClientInitIv(const ts::command_parser& /* command */);
CommandResult handleCommandClientEk(const ts::command_parser& /* command */);
CommandResult handleCommandClientInit(const ts::command_parser& /* command */);
};
}

View File

@ -17,7 +17,7 @@ namespace ts::stats {
class ConnectionStatistics;
}
namespace ts::server::server::udp {
namespace ts::server::server::udp {
struct CommandFragment {
uint16_t packet_id{0};
uint16_t packet_generation{0};
@ -116,7 +116,7 @@ namespace ts::server::server::udp {
callback_send_acknowledge_t callback_send_acknowledge{[](auto, auto, auto){}}; /* needs to be valid all the time! */
private:
connection::CryptHandler* crypt_handler_{nullptr};
spin_mutex incoming_generation_estimator_lock{};
std::array<protocol::generation_estimator, 9> incoming_generation_estimators{}; /* implementation is thread save */

View File

@ -77,6 +77,23 @@ void PacketEncoder::send_packet(ts::protocol::OutgoingServerPacket *packet) {
this->callback_connection_stats(this->callback_data, category, packet->packet_length() + 96); /* 96 for the UDP packet overhead */
}
void PacketEncoder::send_packet(protocol::PacketType type, protocol::PacketFlag::PacketFlags flag, const void *payload, size_t payload_size) {
auto packet = protocol::allocate_outgoing_packet(payload_size);
packet->type_and_flags = (uint8_t) type | (uint8_t) flag;
memcpy(packet->payload, payload, payload_size);
this->send_packet(packet);
}
void PacketEncoder::send_packet_acknowledge(uint16_t pid, bool low) {
char buffer[2];
le2be16(pid, buffer);
auto pflags = PacketFlag::Unencrypted | PacketFlag::NewProtocol;
this->send_packet(low ? protocol::PacketType::ACK_LOW : protocol::PacketType::ACK, (PacketFlag::PacketFlag) pflags, buffer, 2);
}
#define MAX_COMMAND_PACKET_PAYLOAD_LENGTH (487)
void PacketEncoder::send_command(const std::string_view &command, bool low, std::unique_ptr<threads::Future<bool>> ack_listener) {

View File

@ -43,8 +43,11 @@ namespace ts::server::server::udp {
void reset();
void send_packet(protocol::OutgoingServerPacket* /* packet */); /* will claim ownership */
void send_packet(protocol::PacketType /* type */, protocol::PacketFlag::PacketFlags /* flags */, const void* /* payload */, size_t /* payload length */);
void send_command(const std::string_view& /* build command command */, bool /* command low */, std::unique_ptr<threads::Future<bool>> /* acknowledge listener */);
void send_packet_acknowledge(uint16_t /* packet id */, bool /* acknowledge low */);
void execute_resend(const std::chrono::system_clock::time_point &now, std::chrono::system_clock::time_point &next);
void encrypt_pending_packets();

View File

@ -0,0 +1,69 @@
//
// Created by WolverinDEV on 11/03/2020.
//
#include "PingHandler.h"
using namespace ts::server::server::udp;
void PingHandler::reset() {
this->last_ping_id = 0;
this->current_ping_ = std::chrono::milliseconds{0};
this->last_recovery_command_send = std::chrono::system_clock::time_point{};
this->last_command_acknowledge_ = std::chrono::system_clock::time_point{};
this->last_response_ = std::chrono::system_clock::time_point{};
this->last_request_ = std::chrono::system_clock::time_point{};
}
void PingHandler::received_pong(uint16_t ping_id) {
if(this->last_ping_id != ping_id) return;
auto now = std::chrono::system_clock::now();
this->current_ping_ = std::chrono::floor<std::chrono::milliseconds>(this->last_request_ - now);
this->last_response_ = now;
this->last_command_acknowledge_ = now; /* That's here for purpose!*/
}
void PingHandler::received_command_acknowledged() {
this->last_command_acknowledge_ = std::chrono::system_clock::now();
}
void PingHandler::tick(const std::chrono::system_clock::time_point& now) {
if(this->last_request_ + PingHandler::kPingRequestInterval < now)
this->send_ping_request(); /* may update last_response_ */
if(this->last_response_ + PingHandler::kPingTimeout < now) {
if(this->last_recovery_command_send + PingHandler::kRecoveryRequestInterval < now)
this->send_recovery_request();
if(this->last_command_acknowledge_ + PingHandler::kRecoveryTimeout < now) {
if(auto callback{this->callback_time_outed}; callback)
callback(this->callback_argument);
}
}
}
void PingHandler::send_ping_request() {
auto now = std::chrono::system_clock::now();
if(this->last_response_.time_since_epoch().count() == 0)
this->last_response_ = now;
this->last_request_ = now;
if(auto callback{this->callback_send_ping}; callback)
callback(this->callback_argument, this->last_ping_id);
}
void PingHandler::send_recovery_request() {
auto now = std::chrono::system_clock::now();
if(this->last_command_acknowledge_.time_since_epoch().count() == 0)
this->last_command_acknowledge_ = now;
this->last_recovery_command_send = now;
if(auto callback{this->callback_send_recovery_command}; callback)
callback(this->callback_argument);
}

View File

@ -0,0 +1,45 @@
#pragma once
#include <cstdint>
#include <chrono>
namespace ts::server::server::udp {
class PingHandler {
public:
typedef void(*callback_time_outed_t)(void* /* cb data */);
typedef void(*callback_send_ping_t)(void* /* cb data */, uint16_t& /* ping id */);
typedef void(*callback_send_recovery_command_t)(void* /* cb data */);
void reset();
void tick(const std::chrono::system_clock::time_point&);
void received_pong(uint16_t /* ping id */);
void received_command_acknowledged();
[[nodiscard]] inline std::chrono::milliseconds current_ping() const { return this->current_ping_; }
[[nodiscard]] inline std::chrono::system_clock::time_point last_ping_response() const { return this->last_response_; }
void* callback_argument{nullptr};
callback_send_ping_t callback_send_ping{nullptr};
callback_send_recovery_command_t callback_send_recovery_command{nullptr};
callback_time_outed_t callback_time_outed{nullptr};
private:
constexpr static std::chrono::milliseconds kPingRequestInterval{2500};
constexpr static std::chrono::milliseconds kPingTimeout{15 * 1000};
constexpr static std::chrono::milliseconds kRecoveryRequestInterval{1000};
constexpr static std::chrono::milliseconds kRecoveryTimeout{15 * 1000};
std::chrono::milliseconds current_ping_{0};
uint16_t last_ping_id{0};
std::chrono::system_clock::time_point last_response_{};
std::chrono::system_clock::time_point last_request_{};
std::chrono::system_clock::time_point last_command_acknowledge_{};
std::chrono::system_clock::time_point last_recovery_command_send{};
void send_ping_request();
void send_recovery_request();
};
}

View File

@ -0,0 +1,97 @@
//
// Created by WolverinDEV on 29/07/2020.
//
#include "./ServerCommandExecutor.h"
#include "./PacketDecoder.h"
#include "./VoiceClientConnection.h"
using namespace ts;
using namespace ts::server::server::udp;
ServerCommandExecutor::ServerCommandExecutor(VoiceClient *client) : client{client} {}
ServerCommandExecutor::~ServerCommandExecutor() {
this->reset();
}
void ServerCommandExecutor::reset() {
std::unique_lock pc_lock{this->pending_commands_lock};
auto head = std::exchange(this->pending_commands_head, nullptr);
this->pending_commands_tail = &this->pending_commands_head;
pc_lock.unlock();
while(head) {
auto cmd = head->next_command;
ReassembledCommand::free(head);
head = cmd;
}
}
void ServerCommandExecutor::force_insert_command(const pipes::buffer_view &buffer) {
auto command = ReassembledCommand::allocate(buffer.length());
memcpy(command->command(), buffer.data_ptr(), command->length());
this->enqueue_command_execution(command);
}
void ServerCommandExecutor::enqueue_command_execution(ReassembledCommand *command) {
assert(!command->next_command);
bool command_handling_scheduled{false};
{
std::lock_guard pc_lock{this->pending_commands_lock};
*this->pending_commands_tail = command;
this->pending_commands_tail = &command->next_command;
command_handling_scheduled = std::exchange(this->has_command_handling_scheduled, true);
}
if(!command_handling_scheduled) {
auto voice_server = this->client->getVoiceServer();
if(voice_server)
voice_server->schedule_command_handling(&*client);
}
}
void ServerCommandExecutor::execute_handle_command_packets(const std::chrono::system_clock::time_point& /* scheduled */) {
if(!this->client->getServer() || this->client->connectionState() >= ConnectionState::DISCONNECTING)
return;
std::unique_ptr<ReassembledCommand, void(*)(ReassembledCommand*)> pending_command{nullptr, ReassembledCommand::free};
while(true) {
{
std::lock_guard pc_lock{this->pending_commands_lock};
pending_command.reset(this->pending_commands_head);
if(!pending_command) {
this->has_command_handling_scheduled = false;
return;
} else if(pending_command->next_command) {
this->pending_commands_head = pending_command->next_command;
} else {
this->pending_commands_head = nullptr;
this->pending_commands_tail = &this->pending_commands_head;
}
}
auto startTime = std::chrono::system_clock::now();
try {
this->client->handlePacketCommand(pipes::buffer_view{pending_command->command(), pending_command->length()});
} catch (std::exception& ex) {
logCritical(this->client->getServerId(), "{} Exception reached root tree! {}", CLIENT_STR_LOG_PREFIX_(this->client), ex.what());
}
auto end = std::chrono::system_clock::now();
if(end - startTime > std::chrono::milliseconds(10)) {
logError(this->client->getServerId(),
"{} Handling of command packet needs more than 10ms ({}ms)",
CLIENT_STR_LOG_PREFIX_(this->client),
duration_cast<std::chrono::milliseconds>(end - startTime).count()
);
}
break; /* Maybe handle more than one command? Maybe some kind of time limit? */
}
auto voice_server = this->client->getVoiceServer();
if(voice_server)
voice_server->schedule_command_handling(client);
}

View File

@ -0,0 +1,33 @@
#pragma once
#include <misc/spin_mutex.h>
#include <pipes/buffer.h>
namespace ts::server {
class VoiceClient;
}
namespace ts::server::server::udp {
struct ReassembledCommand;
class ServerCommandExecutor {
public:
explicit ServerCommandExecutor(VoiceClient*);
~ServerCommandExecutor();
void reset();
void force_insert_command(const pipes::buffer_view& /* payload */);
void enqueue_command_execution(ReassembledCommand*); /* Attention: The method will take ownership of the command */
void execute_handle_command_packets(const std::chrono::system_clock::time_point& /* scheduled */);
private:
VoiceClient* client;
spin_mutex pending_commands_lock{};
ReassembledCommand* pending_commands_head{nullptr};
ReassembledCommand** pending_commands_tail{&pending_commands_head};
bool has_command_handling_scheduled{false}; /* locked by pending_commands_lock */
};
struct ReassembledCommand;
}

View File

@ -57,54 +57,22 @@ void VoiceClient::sendCommand0(const std::string_view& cmd, bool low, std::uniqu
#endif
}
void VoiceClient::sendAcknowledge(uint16_t packetId, bool low) {
char buffer[2];
le2be16(packetId, buffer);
auto pflags = PacketFlag::Unencrypted | PacketFlag::NewProtocol;
this->connection->send_packet(low ? protocol::PacketType::ACK_LOW : protocol::PacketType::ACK, (PacketFlag::PacketFlag) pflags, buffer, 2);
#ifdef PKT_LOG_ACK
logTrace(this->getServerId(), "{}[Acknowledge][Server -> Client] Sending acknowledge for {}", CLIENT_STR_LOG_PREFIX, packetId);
#endif
}
void VoiceClient::tick(const std::chrono::system_clock::time_point &time) {
SpeakingClient::tick(time);
{
ALARM_TIMER(A1, "VoiceClient::tick", milliseconds(3));
if(this->state == ConnectionState::CONNECTED) {
if(this->lastPingRequest > this->lastPingResponse) { //Client is behind :)
if(this->lastPingRequest - this->lastPingResponse > chrono::seconds(20)) {
debugMessage(this->getServerId(), "{} Got a ping timeout. (Last successful ping: {}ms ago. Last request {}ms. Last response {}ms). Trying to recover via command acknowledge.",
CLIENT_STR_LOG_PREFIX,
duration_cast<milliseconds>(this->lastPingRequest - this->lastPingResponse).count(),
duration_cast<milliseconds>(time - this->lastPingRequest).count(),
duration_cast<milliseconds>(time - this->lastPingResponse).count());
bool force;
this->request_connection_info(nullptr, force);
this->lastPingResponse = system_clock::now();
return;
}
}
if(time - this->lastPingRequest >= chrono::milliseconds(1000)) {
//TODO calculate the ping smooth
if(this->lastPingResponse < this->lastPingRequest){
if(time - this->lastPingRequest >= chrono::milliseconds(1500)) { //Max
this->sendPingRequest();
}
} else
this->sendPingRequest();
}
this->connection->ping_handler().tick(time);
this->connection->packet_statistics().tick();
} else if(this->state == ConnectionState::INIT_LOW || this->state == ConnectionState::INIT_HIGH) {
if(this->last_packet_handshake.time_since_epoch().count() != 0) {
if(time - this->last_packet_handshake > seconds(5)) {
/* FIXME: Handshake timeout */
auto last_command = this->connection->crypt_setup_handler().last_handled_command();
if(last_command.time_since_epoch().count() != 0) {
if(time - last_command > seconds(5)) {
debugMessage(this->getServerId(), "{} Got handshake timeout. {}. State: {} Time: {}", CLIENT_STR_LOG_PREFIX,
this->getLoggingPeerIp() + ":" + to_string(this->getPeerPort()),
this->state == ConnectionState::INIT_HIGH ? "INIT_HIGH" : "INIT_LOW",
duration_cast<seconds>(time - this->last_packet_handshake).count()
duration_cast<seconds>(time - last_command).count()
);
this->close_connection(system_clock::now() + seconds(1));
}
@ -113,6 +81,10 @@ void VoiceClient::tick(const std::chrono::system_clock::time_point &time) {
}
}
std::chrono::milliseconds VoiceClient::current_ping() {
return this->connection->ping_handler().current_ping();
}
bool VoiceClient::disconnect(const std::string &reason) {
return this->disconnect(VREASON_SERVER_KICK, reason, this->server->serverRoot, true);
}
@ -273,7 +245,7 @@ void VoiceClient::finalDisconnect() {
}
void VoiceClient::execute_handle_packet(const std::chrono::system_clock::time_point &time) {
this->connection->execute_handle_command_packets(time);
this->server_command_executor_.execute_handle_command_packets(time);
}
void VoiceClient::send_voice_packet(const pipes::buffer_view &voice_buffer, const SpeakingClient::VoicePacketFlags &flags) {

View File

@ -15,6 +15,7 @@
#include "VoiceClientConnection.h"
#include "src/server/PrecomputedPuzzles.h"
#include "../../lincense/TeamSpeakLicense.h"
#include "./ServerCommandExecutor.h"
//#define LOG_INCOMPING_PACKET_FRAGMENTS
//#define LOG_AUTO_ACK_AUTORESPONSE
@ -36,6 +37,11 @@ namespace ts {
class VoiceClientConnection;
}
namespace server {
namespace server::udp {
class ServerCommandExecutor;
class CryptSetupHandler;
}
class VirtualServer;
class VoiceClient : public SpeakingClient {
@ -45,6 +51,9 @@ namespace ts {
friend class ts::connection::VoiceClientConnection;
friend class ConnectedClient;
friend class io::IOServerHandler;
friend class server::udp::ServerCommandExecutor;
friend class server::udp::CryptSetupHandler;
using ServerCommandExecutor = ts::server::server::udp::ServerCommandExecutor;
public:
VoiceClient(const std::shared_ptr<VoiceServer>& server,const sockaddr_storage*);
~VoiceClient() override;
@ -58,15 +67,16 @@ namespace ts {
/* Note: Order is only guaranteed if progressDirectly is on! */
virtual void sendCommand0(const std::string_view& /* data */, bool low = false, std::unique_ptr<threads::Future<bool>> listener = nullptr);
virtual void sendAcknowledge(uint16_t packetId, bool low = false);
connection::VoiceClientConnection* getConnection(){ return connection; }
std::shared_ptr<VoiceServer> getVoiceServer(){ return voice_server; }
[[nodiscard]] inline std::chrono::milliseconds current_ping(){ return ping; }
[[nodiscard]] std::chrono::milliseconds current_ping();
[[nodiscard]] float current_ping_deviation();
[[nodiscard]] float current_packet_loss() const;
[[nodiscard]] inline auto& server_command_executor() { return this->server_command_executor_; }
private:
connection::VoiceClientConnection* connection;
@ -78,29 +88,12 @@ namespace ts {
/* Attention these handle callbacks are not thread save! */
void handlePacketCommand(const pipes::buffer_view&);
void handlePacketAck(const protocol::ClientPacketParser&);
void handlePacketVoice(const protocol::ClientPacketParser&);
void handlePacketVoiceWhisper(const protocol::ClientPacketParser&);
void handlePacketPing(const protocol::ClientPacketParser&);
void handlePacketInit(const protocol::ClientPacketParser&);
public:
void send_voice_packet(const pipes::buffer_view &packet, const VoicePacketFlags &flags) override;
void send_voice_whisper_packet(const pipes::buffer_view &packet, const VoicePacketFlags &flags) override;
protected:
virtual command_result handleCommand(Command &command) override;
//Some helper method
void sendPingRequest();
//Ping/pong
uint16_t lastPingId = 0;
std::chrono::milliseconds ping = std::chrono::milliseconds(0);
std::chrono::system_clock::time_point lastPingResponse;
std::chrono::system_clock::time_point lastPingRequest;
std::chrono::system_clock::time_point last_packet_handshake;
private:
int socket = 0;
io::pktinfo_storage address_info;
@ -109,26 +102,13 @@ namespace ts {
bool final_disconnected = false;
//General TS3 manager commands
command_result handleCommandClientInitIv(Command&);
command_result handleCommandClientEk(Command&);
command_result handleCommandClientInit(Command&) override;
command_result handleCommandClientDisconnect(Command&);
//Locked by finalDisconnect, disconnect and close connection
std::shared_ptr<threads::Thread> flushing_thread;
struct {
bool client_init = false;
bool new_protocol = false;
bool protocol_encrypted = false;
bool is_teaspeak_client = false;
uint32_t client_time = 0;
std::string alpha;
std::string beta;
std::shared_ptr<LicenseChainData> chain_data;
std::shared_ptr<ecc_key> remote_key;
} crypto;
ServerCommandExecutor server_command_executor_{this};
std::shared_ptr<event::ProxiedEventEntry<VoiceClient>> event_handle_packet;
void execute_handle_packet(const std::chrono::system_clock::time_point& /* scheduled */);

View File

@ -6,7 +6,6 @@
#include <src/client/SpeakingClient.h>
#include "../../InstanceHandler.h"
#include "../../geo/GeoLocation.h"
#include "VoiceClient.h"
using namespace std;
@ -15,6 +14,25 @@ using namespace ts::server;
using namespace ts::protocol;
using namespace ts;
void VoiceClient::handlePacketCommand(const pipes::buffer_view& command_string) {
std::unique_ptr<Command> command;
command_result result{};
try {
command = make_unique<Command>(Command::parse(command_string, true, !ts::config::server::strict_ut8_mode));
} catch(std::invalid_argument& ex) {
result.reset(command_result{error::parameter_convert, std::string{ex.what()}});
goto handle_error;
} catch(std::exception& ex) {
result.reset(command_result{error::parameter_convert, std::string{ex.what()}});
goto handle_error;
}
this->handleCommandFull(*command, true);
return;
handle_error:
this->notifyError(result);
result.release_data();
}
command_result VoiceClient::handleCommand(ts::Command &command) {
threads::MutexLock l2(this->command_lock);
@ -56,23 +74,25 @@ inline bool calculate_security_level(int& result, ecc_key* pubKey, size_t offset
}
command_result VoiceClient::handleCommandClientInit(Command &cmd) {
this->crypto.client_init = true;
this->connection->packet_encoder().acknowledge_manager().reset();
if(this->getType() == ClientType::CLIENT_TEAMSPEAK) {
int securityLevel;
if(!calculate_security_level(securityLevel, this->crypto.remote_key.get(), cmd["client_key_offset"])) {
logError(this->getServerId(), "[{}] Failed to calculate security level. Error code: {}", CLIENT_STR_LOG_PREFIX, securityLevel);
auto client_identity = this->connection->crypt_setup_handler().identity_key();
int security_level;
if(!calculate_security_level(security_level, &*client_identity, cmd["client_key_offset"])) {
logError(this->getServerId(), "[{}] Failed to calculate security level. Error code: {}", CLIENT_STR_LOG_PREFIX, security_level);
return command_result{error::vs_critical};
}
if(securityLevel < 8)
if(security_level < 8) {
return command_result{error::client_could_not_validate_identity};
}
auto requiredLevel = this->getServer()->properties()[property::VIRTUALSERVER_NEEDED_IDENTITY_SECURITY_LEVEL].as<uint8_t>();
if(securityLevel < requiredLevel) return command_result{error::client_could_not_validate_identity, to_string(requiredLevel)};
if(security_level < requiredLevel) {
return command_result{error::client_could_not_validate_identity, to_string(requiredLevel)};
}
}
this->lastPingResponse = std::chrono::system_clock::now();
return SpeakingClient::handleCommandClientInit(cmd);
}

View File

@ -28,7 +28,11 @@ using namespace ts::protocol;
using namespace ts::server;
VoiceClientConnection::VoiceClientConnection(VoiceClient* client)
: client{client}, crypt_handler{}, packet_decoder_{&this->crypt_handler}, packet_encoder_{&this->crypt_handler, &this->packet_statistics_} {
: current_client{client},
crypt_handler{},
packet_decoder_{&this->crypt_handler},
packet_encoder_{&this->crypt_handler, &this->packet_statistics_},
crypt_setup_handler_{this} {
memtrack::allocated<VoiceClientConnection>(this);
this->packet_decoder_.callback_argument = this;
@ -43,18 +47,31 @@ VoiceClientConnection::VoiceClientConnection(VoiceClient* client)
this->packet_encoder_.callback_resend_stats = VoiceClientConnection::callback_resend_statistics;
this->packet_encoder_.callback_connection_stats = VoiceClientConnection::callback_outgoing_connection_statistics;
this->ping_handler_.callback_argument = this;
this->ping_handler_.callback_send_ping = VoiceClientConnection::callback_ping_send;
this->ping_handler_.callback_send_recovery_command = VoiceClientConnection::callback_ping_send_recovery;
this->ping_handler_.callback_time_outed = VoiceClientConnection::callback_ping_timeout;
this->virtual_server_id_ = client->getServerId();
debugMessage(client->getServer()->getServerId(), "Allocated new voice client connection at {}", (void*) this);
}
VoiceClientConnection::~VoiceClientConnection() {
this->reset();
this->client = nullptr;
this->current_client = nullptr;
memtrack::freed<VoiceClientConnection>(this);
}
std::string VoiceClientConnection::log_prefix() {
auto client = this->getCurrentClient();
if(!client) return "[unknown / unknown]"; /* FIXME: Get the IP address here! */
return CLIENT_STR_LOG_PREFIX_(client);
}
void VoiceClientConnection::triggerWrite() {
if(this->client->voice_server)
this->client->voice_server->triggerWrite(dynamic_pointer_cast<VoiceClient>(this->client->_this.lock()));
if(this->current_client->voice_server)
this->current_client->voice_server->triggerWrite(dynamic_pointer_cast<VoiceClient>(this->current_client->_this.lock()));
}
#ifdef CLIENT_LOG_PREFIX
@ -69,8 +86,8 @@ void VoiceClientConnection::handle_incoming_datagram(const pipes::buffer_view& b
return;
#ifndef CONNECTION_NO_STATISTICS
if(this->client) {
auto stats = this->client->connectionStatistics;
if(this->current_client) {
auto stats = this->current_client->connectionStatistics;
stats->logIncomingPacket(stats::ConnectionStatistics::category::from_type(packet_parser.type()), buffer.length() + 96); /* 96 for the UDP packet overhead */
}
this->packet_statistics().received_packet((protocol::PacketType) packet_parser.type(), packet_parser.full_packet_id());
@ -88,39 +105,39 @@ void VoiceClientConnection::handle_incoming_datagram(const pipes::buffer_view& b
case PacketProcessResult::DECRYPT_KEY_GEN_FAILED:
/* no action needed, acknowledge should be send */
logCritical(this->client->getServerId(), "{} Failed to generate decrypt key. Dropping packet.", CLIENT_STR_LOG_PREFIX_(this->client));
logCritical(this->virtual_server_id_, "{} Failed to generate decrypt key. Dropping packet.", this->log_prefix());
break;
case PacketProcessResult::BUFFER_OVERFLOW:
case PacketProcessResult::BUFFER_UNDERFLOW:
debugMessage(this->client->getServerId(), "{} Dropping command packet because command assembly buffer has an {}: {}",
CLIENT_STR_LOG_PREFIX_(this->client),
debugMessage(this->virtual_server_id_, "{} Dropping command packet because command assembly buffer has an {}: {}",
this->log_prefix(),
result == PacketProcessResult::BUFFER_UNDERFLOW ? "underflow" : "overflow",
error
);
break;
case PacketProcessResult::UNKNOWN_ERROR:
logCritical(this->client->getServerId(), "{} Having an unknown error while processing a incoming packet: {}",
CLIENT_STR_LOG_PREFIX_(this->client),
logCritical(this->virtual_server_id_, "{} Having an unknown error while processing a incoming packet: {}",
this->log_prefix(),
error
);
goto disconnect_client;
case PacketProcessResult::COMMAND_BUFFER_OVERFLOW:
debugMessage(this->client->getServerId(), "{} Having a command buffer overflow. This might cause the client to drop.", CLIENT_STR_LOG_PREFIX_(this->client));
debugMessage(this->virtual_server_id_, "{} Having a command buffer overflow. This might cause the client to drop.", this->log_prefix());
break;
case PacketProcessResult::COMMAND_DECOMPRESS_FAILED:
logWarning(this->client->getServerId(), "{} Failed to decompress a command packet. Dropping command.", CLIENT_STR_LOG_PREFIX_(this->client));
logWarning(this->virtual_server_id_, "{} Failed to decompress a command packet. Dropping command.", this->log_prefix());
break;
case PacketProcessResult::COMMAND_TOO_LARGE:
logWarning(this->client->getServerId(), "{} Received a too large command. Dropping client.", CLIENT_STR_LOG_PREFIX_(this->client));
logWarning(this->virtual_server_id_, "{} Received a too large command. Dropping client.", this->log_prefix());
goto disconnect_client;
case PacketProcessResult::COMMAND_SEQUENCE_LENGTH_TOO_LONG:
logWarning(this->client->getServerId(), "{} Received a too long command sequence. Dropping client.", CLIENT_STR_LOG_PREFIX_(this->client));
logWarning(this->virtual_server_id_, "{} Received a too long command sequence. Dropping client.", this->log_prefix());
goto disconnect_client;
default:
@ -135,160 +152,72 @@ void VoiceClientConnection::handle_incoming_datagram(const pipes::buffer_view& b
}
void VoiceClientConnection::callback_send_acknowledge(void *ptr_this, uint16_t packet_id, bool command_low) {
/* FIXME: Move this to the connection! */
reinterpret_cast<VoiceClientConnection*>(ptr_this)->client->sendAcknowledge(packet_id, command_low);
reinterpret_cast<VoiceClientConnection*>(ptr_this)->packet_encoder_.send_packet_acknowledge(packet_id, command_low);
}
void VoiceClientConnection::callback_packet_decoded(void *ptr_this, const ts::protocol::ClientPacketParser &packet) {
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
switch (packet.type()) {
case protocol::VOICE:
connection->client->handlePacketVoice(packet);
connection->handlePacketVoice(packet);
break;
case protocol::VOICE_WHISPER:
connection->client->handlePacketVoiceWhisper(packet);
connection->handlePacketVoiceWhisper(packet);
break;
case protocol::ACK:
connection->handlePacketAck(packet);
break;
case protocol::ACK_LOW:
connection->client->handlePacketAck(packet);
connection->handlePacketAckLow(packet);
break;
case protocol::PING:
connection->handlePacketPing(packet);
break;
case protocol::PONG:
connection->client->handlePacketPing(packet);
connection->handlePacketPong(packet);
break;
default:
assert(false);
logError(connection->client->getServerId(), "{} Received hand decoded packet, but we've no method to handle it. Dropping packet.", CLIENT_STR_LOG_PREFIX_(connection->client));
logError(connection->virtual_server_id_, "{} Received hand decoded packet, but we've no method to handle it. Dropping packet.", connection->log_prefix());
break;
}
}
void VoiceClientConnection::callback_command_decoded(void *ptr_this, ts::server::server::udp::ReassembledCommand *&command) {
void VoiceClientConnection::callback_command_decoded(void *ptr_this, ReassembledCommand *&command) {
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
/* we're exchanging the command so we're taking the ownership */
connection->enqueue_command_execution(std::exchange(command, nullptr));
connection->handlePacketCommand(std::exchange(command, nullptr));
}
bool VoiceClientConnection::verify_encryption(const pipes::buffer_view &buffer /* incl. mac etc */) {
return this->packet_decoder_.verify_encryption(buffer);
}
void VoiceClientConnection::enqueue_command_execution(ReassembledCommand *command) {
assert(!command->next_command);
bool command_handling_scheduled{false};
{
std::lock_guard pc_lock{this->pending_commands_lock};
*this->pending_commands_tail = command;
this->pending_commands_tail = &command->next_command;
command_handling_scheduled = std::exchange(this->has_command_handling_scheduled, true);
}
if(!command_handling_scheduled) {
auto voice_server = this->client->voice_server;
if(voice_server)
voice_server->schedule_command_handling(this->client);
}
std::shared_ptr<ts::server::VoiceClient> VoiceClientConnection::getCurrentClient() {
if(!this->current_client) return nullptr;
return std::dynamic_pointer_cast<server::VoiceClient>(this->current_client->ref());
}
void VoiceClientConnection::execute_handle_command_packets(const std::chrono::system_clock::time_point& /* scheduled */) {
if(this->client->state >= ConnectionState::DISCONNECTING || !this->client->getServer())
return;
std::unique_ptr<ReassembledCommand, void(*)(ReassembledCommand*)> pending_command{nullptr, ReassembledCommand::free};
while(true) {
{
std::lock_guard pc_lock{this->pending_commands_lock};
pending_command.reset(this->pending_commands_head);
if(!pending_command) {
this->has_command_handling_scheduled = false;
return;
} else if(pending_command->next_command) {
this->pending_commands_head = pending_command->next_command;
} else {
this->pending_commands_head = nullptr;
this->pending_commands_tail = &this->pending_commands_head;
}
}
auto startTime = system_clock::now();
try {
this->client->handlePacketCommand(pipes::buffer_view{pending_command->command(), pending_command->length()});
} catch (std::exception& ex) {
logCritical(this->client->getServerId(), "{} Exception reached root tree! {}", CLIENT_STR_LOG_PREFIX_(this->client), ex.what());
}
auto end = system_clock::now();
if(end - startTime > milliseconds(10)) {
logError(this->client->getServerId(),
"{} Handling of command packet needs more than 10ms ({}ms)",
CLIENT_STR_LOG_PREFIX_(this->client),
duration_cast<milliseconds>(end - startTime).count()
);
}
break; /* Maybe handle more than one command? Maybe some kind of time limit? */
}
auto voice_server = this->client->voice_server;
if(voice_server)
voice_server->schedule_command_handling(this->client);
}
bool VoiceClientConnection::wait_empty_write_and_prepare_queue(chrono::time_point<chrono::system_clock> until) {
return this->packet_encoder_.wait_empty_write_and_prepare_queue(until);
}
void VoiceClientConnection::reset() {
this->crypt_handler.reset();
{
std::unique_lock pc_lock{this->pending_commands_lock};
auto head = std::exchange(this->pending_commands_head, nullptr);
this->pending_commands_tail = &this->pending_commands_head;
pc_lock.unlock();
while(head) {
auto cmd = head->next_command;
ReassembledCommand::free(head);
head = cmd;
}
}
this->ping_handler_.reset();
this->packet_decoder_.reset();
this->packet_encoder_.reset();
}
void VoiceClientConnection::force_insert_command(const pipes::buffer_view &buffer) {
auto command = ReassembledCommand::allocate(buffer.length());
memcpy(command->command(), buffer.data_ptr(), command->length());
this->enqueue_command_execution(command);
}
void VoiceClientConnection::send_packet(protocol::OutgoingServerPacket *packet) {
this->packet_encoder_.send_packet(packet);
auto statistics = this->client ? this->client->connectionStatistics : nullptr;
if(statistics) {
auto category = stats::ConnectionStatistics::category::from_type(packet->packet_type());
statistics->logOutgoingPacket(category, packet->packet_length() + 96); /* 96 for the UDP packet overhead */
}
}
void VoiceClientConnection::send_packet(protocol::PacketType type, protocol::PacketFlag::PacketFlags flag, const void *payload, size_t payload_size) {
auto packet = protocol::allocate_outgoing_packet(payload_size);
packet->type_and_flags = (uint8_t) type | (uint8_t) flag;
memcpy(packet->payload, payload, payload_size);
this->send_packet(packet);
this->packet_encoder_.send_packet(type, flag, payload, payload_size);
}
void VoiceClientConnection::send_command(const std::string_view &cmd, bool b, std::unique_ptr<threads::Future<bool>> cb) {
@ -301,11 +230,11 @@ void VoiceClientConnection::callback_encode_crypt_error(void *ptr_this,
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
switch (error) {
case PacketEncoder::CryptError::ENCRYPT_FAILED:
logError(connection->client->getServerId(), "{} Failed to encrypt packet. Error: {}", CLIENT_STR_LOG_PREFIX_(connection->client), detail);
logError(connection->virtual_server_id_, "{} Failed to encrypt packet. Error: {}", connection->log_prefix(), detail);
break;
case PacketEncoder::CryptError::KEY_GENERATION_FAILED:
logError(connection->client->getServerId(), "{} Failed to generate crypt key/nonce for sending a packet. This should never happen! Dropping packet.", CLIENT_STR_LOG_PREFIX_(connection->client));
logError(connection->virtual_server_id_, "{} Failed to generate crypt key/nonce for sending a packet. This should never happen! Dropping packet.", connection->log_prefix());
break;
default:
@ -322,27 +251,54 @@ void VoiceClientConnection::callback_request_write(void *ptr_this) {
void VoiceClientConnection::callback_resend_failed(void *ptr_this, const shared_ptr<AcknowledgeManager::Entry> &entry) {
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
debugMessage(connection->client->getServerId(), "{} Failed to execute packet resend of packet {}. Dropping connection.", CLIENT_STR_LOG_PREFIX_(connection->client), entry->packet_full_id);
if(connection->client->state == ConnectionState::CONNECTED) {
connection->client->disconnect(ViewReasonId::VREASON_TIMEOUT, config::messages::timeout::packet_resend_failed, nullptr, true);
debugMessage(connection->virtual_server_id_, "{} Failed to execute packet resend of packet {}. Dropping connection.", connection->log_prefix(), entry->packet_full_id);
auto client = connection->getCurrentClient();
assert(client); /* TIXME! */
if(client->state == ConnectionState::CONNECTED) {
client->disconnect(ViewReasonId::VREASON_TIMEOUT, config::messages::timeout::packet_resend_failed, nullptr, true);
} else {
connection->client->close_connection(system_clock::now() + seconds(1));
client->close_connection(system_clock::now() + seconds(1));
}
}
void VoiceClientConnection::callback_resend_statistics(void *ptr_this, size_t send_count) {
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
logTrace(connection->client->getServerId(), "{} Resending {} packets.", CLIENT_STR_LOG_PREFIX_(connection->client), send_count);
logTrace(connection->virtual_server_id_, "{} Resending {} packets.", connection->log_prefix(), send_count);
}
void VoiceClientConnection::callback_outgoing_connection_statistics(void *ptr_this,
ts::stats::ConnectionStatistics::category::value category,
size_t send_count) {
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
auto statistics = connection->client->connectionStatistics;
auto client = connection->getCurrentClient();
if(!client) return;
auto statistics = client->connectionStatistics;
if(!statistics) return;
statistics->logOutgoingPacket(category, send_count);
}
void VoiceClientConnection::callback_ping_send(void *ptr_this, uint16_t &id) {
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
auto packet = protocol::allocate_outgoing_packet(0);
packet->ref();
packet->type_and_flags = (uint8_t) PacketType::PING | (uint8_t) PacketFlag::Unencrypted;
connection->packet_encoder_.send_packet(packet);
id = packet->packet_id();
packet->unref();
}
void VoiceClientConnection::callback_ping_send_recovery(void *ptr_this) {
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
connection->send_command("notifyconnectioninforequest invokerids=0", false, nullptr);
}
void VoiceClientConnection::callback_ping_timeout(void *ptr_this) {
/* doing nothing a packet resend failed will cause the client to disconnect */
}

View File

@ -18,6 +18,9 @@
#include "./PacketStatistics.h"
#include "./PacketDecoder.h"
#include "./PacketEncoder.h"
#include "./ServerCommandExecutor.h"
#include "CryptSetupHandler.h"
#include "PingHandler.h"
//#define LOG_ACK_SYSTEM
#ifdef LOG_ACK_SYSTEM
@ -43,37 +46,43 @@ namespace ts {
using PacketDecoder = server::server::udp::PacketDecoder;
using PacketEncoder = server::server::udp::PacketEncoder;
using PingHandler = server::server::udp::PingHandler;
using CryptSetupHandler = server::server::udp::CryptSetupHandler;
using ReassembledCommand = server::server::udp::ReassembledCommand;
using StatisticsCategory = stats::ConnectionStatistics::category;
public:
explicit VoiceClientConnection(server::VoiceClient*);
virtual ~VoiceClientConnection();
/* Do not send command packets via send_packet! The send_packet will take ownership of the packet! */
void send_packet(protocol::OutgoingServerPacket* /* packet */);
void send_packet(protocol::PacketType /* type */, protocol::PacketFlag::PacketFlags /* flags */, const void* /* payload */, size_t /* payload length */);
void send_command(const std::string_view& /* build command command */, bool /* command low */, std::unique_ptr<threads::Future<bool>> /* acknowledge listener */);
CryptHandler* getCryptHandler(){ return &crypt_handler; }
server::VoiceClient* getClient(){ return client; }
std::shared_ptr<server::VoiceClient> getCurrentClient();
bool wait_empty_write_and_prepare_queue(std::chrono::time_point<std::chrono::system_clock> until = std::chrono::time_point<std::chrono::system_clock>());
void reset();
[[nodiscard]] std::string log_prefix();
void force_insert_command(const pipes::buffer_view& /* payload */);
[[nodiscard]] inline auto virtual_server_id() const { return this->virtual_server_id_; }
[[nodiscard]] inline auto& packet_statistics() { return this->packet_statistics_; }
[[nodiscard]] inline auto& packet_decoder() { return this->packet_decoder_; }
[[nodiscard]] inline auto& packet_encoder() { return this->packet_encoder_; }
[[nodiscard]] inline auto& ping_handler() { return this->ping_handler_; }
[[nodiscard]] inline auto& crypt_setup_handler() { return this->crypt_setup_handler_; }
protected:
void handle_incoming_datagram(const pipes::buffer_view &buffer);
bool verify_encryption(const pipes::buffer_view& /* full packet */);
void triggerWrite();
private:
server::VoiceClient* client = nullptr;
ServerId virtual_server_id_;
server::VoiceClient* current_client;
CryptHandler crypt_handler; /* access to CryptHandler is thread save */
server::client::PacketStatistics packet_statistics_{};
@ -81,13 +90,8 @@ namespace ts {
PacketDecoder packet_decoder_;
PacketEncoder packet_encoder_;
spin_mutex pending_commands_lock{};
ReassembledCommand* pending_commands_head{nullptr};
ReassembledCommand** pending_commands_tail{&pending_commands_head};
bool has_command_handling_scheduled{false}; /* locked by pending_commands_lock */
void enqueue_command_execution(ReassembledCommand*);
void execute_handle_command_packets(const std::chrono::system_clock::time_point& /* scheduled */);
CryptSetupHandler crypt_setup_handler_;
PingHandler ping_handler_{};
static void callback_packet_decoded(void*, const protocol::ClientPacketParser&);
static void callback_command_decoded(void*, ReassembledCommand*&);
@ -97,6 +101,18 @@ namespace ts {
static void callback_resend_failed(void*, const std::shared_ptr<AcknowledgeManager::Entry>&);
static void callback_resend_statistics(void*, size_t);
static void callback_outgoing_connection_statistics(void*, StatisticsCategory::value, size_t /* bytes */);
static void callback_ping_send(void*, uint16_t&);
static void callback_ping_send_recovery(void*);
static void callback_ping_timeout(void*);
/* Attention: All packet callbacks are called from the IO threads and are not thread save! */
void handlePacketCommand(ReassembledCommand* /* command */); /* The ownership will be transferred */
void handlePacketAck(const protocol::ClientPacketParser&);
void handlePacketAckLow(const protocol::ClientPacketParser&);
void handlePacketVoice(const protocol::ClientPacketParser&);
void handlePacketVoiceWhisper(const protocol::ClientPacketParser&);
void handlePacketPing(const protocol::ClientPacketParser&);
void handlePacketPong(const protocol::ClientPacketParser&);
};
}
}

View File

@ -0,0 +1,84 @@
#include <tommath.h>
#include <misc/endianness.h>
#include <log/LogUtils.h>
#include "../web/WebClient.h"
#include "VoiceClient.h"
using namespace std;
using namespace std::chrono;
using namespace ts::connection;
using namespace ts::protocol;
void VoiceClientConnection::handlePacketPong(const ts::protocol::ClientPacketParser &packet) {
if(packet.payload_length() < 2) return;
this->ping_handler_.received_pong(be2le16((char*) packet.payload().data_ptr()));
}
void VoiceClientConnection::handlePacketPing(const protocol::ClientPacketParser& packet) {
#ifdef PKT_LOG_PING
logMessage(this->getServerId(), "{}[Ping] Sending pong for client requested ping {}", CLIENT_STR_LOG_PREFIX, packet->packetId());
#endif
char buffer[2];
le2be16(packet.packet_id(), buffer);
this->send_packet(PacketType::PONG, PacketFlag::Unencrypted, buffer, 2);
}
void VoiceClientConnection::handlePacketVoice(const protocol::ClientPacketParser& packet) {
auto client = this->getCurrentClient();
if(!client) return;
client->handlePacketVoice(packet.payload(), (packet.flags() & PacketFlag::Compressed) > 0, (packet.flags() & PacketFlag::Fragmented) > 0);
}
void VoiceClientConnection::handlePacketVoiceWhisper(const ts::protocol::ClientPacketParser &packet) {
auto client = this->getCurrentClient();
if(!client) return;
client->handlePacketVoiceWhisper(packet.payload(), (packet.flags() & PacketFlag::NewProtocol) > 0);
}
void VoiceClientConnection::handlePacketAck(const protocol::ClientPacketParser& packet) {
if(packet.payload_length() < 2) return;
uint16_t target_id{be2le16(packet.payload().data_ptr<char>())};
this->ping_handler_.received_command_acknowledged();
this->packet_statistics().received_acknowledge((protocol::PacketType) packet.type(), target_id | (uint32_t) (packet.estimated_generation() << 16U));
string error{};
if(!this->packet_encoder().acknowledge_manager().process_acknowledge(packet.type(), target_id, error))
debugMessage(this->virtual_server_id_, "{} Failed to handle acknowledge: {}", this->log_prefix(), error);
}
void VoiceClientConnection::handlePacketAckLow(const ts::protocol::ClientPacketParser &packet) {
this->handlePacketAck(packet);
}
void VoiceClientConnection::handlePacketCommand(ReassembledCommand* command) {
{
using CommandHandleResult = CryptSetupHandler::CommandHandleResult ;
auto result = this->crypt_setup_handler_.handle_command(command->command_view());
switch (result) {
case CommandHandleResult::PASS_THROUGH:
break;
case CommandHandleResult::CONSUME_COMMAND:
return;
case CommandHandleResult::CLOSE_CONNECTION:
auto client = this->getCurrentClient();
assert(client); /* FIXME! */
client->close_connection(std::chrono::system_clock::time_point{});
return;
}
}
auto client = this->getCurrentClient();
if(!client) {
/* TODO! */
return;
}
client->server_command_executor().enqueue_command_execution(command);
}

View File

@ -14,184 +14,3 @@ using namespace std::chrono;
using namespace ts::server;
using namespace ts::protocol;
using namespace ts::connection;
inline void generate_random(uint8_t *destination, size_t length) {
while(length-- > 0)
*(destination++) = (uint8_t) rand();
}
ts::command_result VoiceClient::handleCommandClientInitIv(Command& command) {
this->last_packet_handshake = system_clock::now();
std::unique_lock state_lock{this->state_lock};
if(this->state == ConnectionState::CONNECTED) { /* we've a reconnect */
if(system_clock::now() - this->lastPingResponse < seconds(5)) {
logMessage(this->getServerId(), "{} Client initialized session reconnect, but last ping response is not older then 5 seconds ({}). Ignoring attempt",
CLIENT_STR_LOG_PREFIX,
duration_cast<milliseconds>(system_clock::now() - this->lastPingResponse).count()
);
return ts::command_result{error::ok};
} else if(!config::voice::allow_session_reinitialize) {
logMessage(this->getServerId(), "{} Client initialized session reconnect and last ping response is older then 5 seconds ({}). Dropping attempt because its not allowed due to config settings",
CLIENT_STR_LOG_PREFIX,
duration_cast<milliseconds>(system_clock::now() - this->lastPingResponse).count()
);
return ts::command_result{error::ok};
}
logMessage(this->getServerId(), "{} Client initialized reconnect and last ping response is older then 5 seconds ({}). Allowing attempt",
CLIENT_STR_LOG_PREFIX,
duration_cast<milliseconds>(system_clock::now() - this->lastPingResponse).count()
);
state_lock.unlock();
{
unique_lock server_channel_lock(this->server->channel_tree_lock); /* we cant get moved if this is locked! */
if(this->currentChannel)
this->server->client_move(this->ref(), nullptr, nullptr, config::messages::timeout::connection_reinitialized, ViewReasonId::VREASON_TIMEOUT, false, server_channel_lock);
}
this->finalDisconnect();
state_lock.lock();
} else if(this->state >= ConnectionState::DISCONNECTING) {
state_lock.unlock();
std::shared_lock disconnect_finish{this->finalDisconnectLock}; /* await until the last disconnect has been processed */
state_lock.lock();
this->state = ConnectionState::INIT_HIGH;
} else if(this->state == ConnectionState::INIT_HIGH) {
logTrace(this->getServerId(), "{} Received a duplicated initiv. It seems like our initivexpand2 hasn't yet reached the client. The acknowledge handler should handle this issue for us.", CLIENT_STR_LOG_PREFIX);
return command_result{error::ok};
} else {
this->state = ConnectionState::INIT_HIGH;
}
state_lock.unlock();
this->connection->reset();
this->connection->packet_decoder().register_initiv_packet();
this->connection->packet_statistics().reset_offsets();
this->crypto.protocol_encrypted = false;
bool use_teaspeak = command.hasParm("teaspeak");
if(use_teaspeak ? !config::server::clients::teaspeak : !config::server::clients::teamspeak)
return command_result{error::client_type_is_not_allowed};
if(use_teaspeak) {
debugMessage(this->getServerId(), "{} Client using TeaSpeak with auth type {}", CLIENT_STR_LOG_PREFIX, command["verify_type"].string());
this->properties()[property::CLIENT_TYPE_EXACT] = ClientType::CLIENT_TEASPEAK;
}
/* normal TeamSpeak handling */
string clientAlpha = base64::decode(command["alpha"]); //random
if(clientAlpha.length() != 10) return ts::command_result{error::parameter_invalid};
string clientOmega = base64::decode(command["omega"]); //The identity public key
string ip = command["ip"];
bool ot = command[0].has("ot") ? command["ot"] : false;
this->crypto.remote_key = std::shared_ptr<ecc_key>(new ecc_key{}, [](ecc_key* key){
if(!key) return;
ecc_free(key);
delete key;
});
auto state = ecc_import((const unsigned char *) clientOmega.data(), clientOmega.length(), this->crypto.remote_key.get());
if(state != CRYPT_OK) {
this->crypto.remote_key.reset();
return ts::command_result{error::client_could_not_validate_identity};
}
this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = base64::encode(digest::sha1(command["omega"].string()));
this->crypto.alpha = clientAlpha;
this->crypto.new_protocol = !use_teaspeak && ot && config::experimental_31 && (this->crypto.client_time >= 173265950 || this->crypto.client_time == 5680278000UL);
{
size_t beta_length = this->crypto.new_protocol ? 54 : 10;
char beta[beta_length];
generate_random((uint8_t *) beta, beta_length);
this->crypto.beta = string(beta, beta_length);
}
if(this->crypto.new_protocol) {
//Pre setup
//Generate chain
debugMessage(this->getServerId(), "{} Got client 3.1 protocol with build timestamp {}", CLIENT_STR_LOG_PREFIX, this->crypto.client_time);
this->crypto.chain_data = serverInstance->getTeamSpeakLicense()->license();
this->crypto.chain_data->chain->addEphemeralEntry();
auto rawLicense = this->crypto.chain_data->chain->exportChain();
//Sign license
auto serverOmega = this->getServer()->publicServerKey();
auto rawServerOmega = base64::decode(serverOmega);
auto licenseHash = digest::sha256(rawLicense);
size_t signBufferLength = 128;
char signBuffer[signBufferLength];
prng_state prngState{};
memset(&prngState, 0, sizeof(prngState));
if(ecc_sign_hash((u_char*) licenseHash.data(), licenseHash.length(), (u_char*) signBuffer, &signBufferLength, &prngState, find_prng("sprng"), this->getServer()->serverKey()) != CRYPT_OK) {
logError(this->getServerId(), "Failed to sign crypto chain!");
return ts::command_result{error::vs_critical};
}
auto proof = base64::encode(signBuffer, signBufferLength);
Command initivexpand2("initivexpand2");
initivexpand2["time"] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
initivexpand2["l"] = base64::encode(rawLicense);
initivexpand2["beta"] = base64::encode(this->crypto.beta);
initivexpand2["omega"] = serverOmega;
initivexpand2["proof"] = proof;
initivexpand2["tvd"] = "";
initivexpand2["root"] = base64::encode((char*) this->crypto.chain_data->public_key, 32);
initivexpand2["ot"] = 1;
this->sendCommand(initivexpand2);
this->handshake.state = HandshakeState::SUCCEEDED; /* we're doing the verify via TeamSpeak */
} else {
debugMessage(this->getServerId(), "{} Got non client 3.1 protocol with build timestamp {}", CLIENT_STR_LOG_PREFIX, this->crypto.client_time);
auto serverOmega = this->getServer()->publicServerKey();
if(serverOmega.empty()) {
logError(this->getServerId(), "Failed to export server public key!");
return ts::command_result{error::vs_critical};;
}
{
Command initivexpand("initivexpand");
initivexpand["alpha"] = base64::encode(clientAlpha);
initivexpand["beta"] = base64::encode(this->crypto.beta);
initivexpand["omega"] = serverOmega;
if(use_teaspeak) {
initivexpand["teaspeak"] = 1;
this->handshake.state = HandshakeState::BEGIN; /* we need to start the handshake */
} else {
this->handshake.state = HandshakeState::SUCCEEDED; /* we're doing the verify via TeamSpeak */
}
this->sendCommand0(initivexpand.build()); //If we setup the encryption now
this->connection->packet_encoder().encrypt_pending_packets();
}
{
string error;
if(!this->connection->getCryptHandler()->setupSharedSecret(this->crypto.alpha, this->crypto.beta, this->crypto.remote_key.get(), this->server->serverKey(), error)){
logError(this->server->getServerId(), "Could not setup shared secret! (" + error + ")");
return ts::command_result{error::vs_critical};
}
this->crypto.protocol_encrypted = true;
}
}
return ts::command_result{error::ok};
}
ts::command_result VoiceClient::handleCommandClientEk(Command& cmd) {
this->last_packet_handshake = system_clock::now();
debugMessage(this->getServerId(), "{} Got client ek!", CLIENT_STR_LOG_PREFIX);
auto client_key = base64::decode(cmd["ek"]);
auto private_key = this->crypto.chain_data->chain->generatePrivateKey(this->crypto.chain_data->root_key, this->crypto.chain_data->root_index);
this->connection->getCryptHandler()->setupSharedSecretNew(this->crypto.alpha, this->crypto.beta, (char*) private_key.data(), client_key.data());
this->connection->packet_encoder().acknowledge_manager().reset();
this->crypto.protocol_encrypted = true;
this->sendAcknowledge(1); //Send the encrypted acknowledge (most the times the second packet; If not we're going into the resend loop)
return ts::command_result{error::ok};
}

View File

@ -1,90 +0,0 @@
#include <tommath.h>
#include <misc/endianness.h>
#include <algorithm>
#include <log/LogUtils.h>
#include "../web/WebClient.h"
#include "VoiceClient.h"
using namespace std;
using namespace std::chrono;
using namespace ts::server;
using namespace ts::protocol;
//#define PKT_LOG_PING
/* should never happen! */
void VoiceClient::handlePacketInit(const ts::protocol::ClientPacketParser &) {}
//TODO Packet handlers -> move back to voice client?
void VoiceClient::handlePacketCommand(const pipes::buffer_view& command_string) {
std::unique_ptr<Command> command;
command_result result{};
try {
command = make_unique<Command>(Command::parse(command_string, true, !ts::config::server::strict_ut8_mode));
} catch(std::invalid_argument& ex) {
result.reset(command_result{error::parameter_convert, std::string{ex.what()}});
goto handle_error;
} catch(std::exception& ex) {
result.reset(command_result{error::parameter_convert, std::string{ex.what()}});
goto handle_error;
}
if(command->command() == "clientek") {
result.reset(this->handleCommandClientEk(*command));
if(result.has_error()) goto handle_error;
} else if(command->command() == "clientinitiv") {
result.reset(this->handleCommandClientInitIv(*command));
if(result.has_error()) goto handle_error;
} else this->handleCommandFull(*command, true);
return;
handle_error:
this->notifyError(result);
result.release_data();
}
void VoiceClient::handlePacketPing(const protocol::ClientPacketParser& packet) {
if (packet.type() == protocol::PONG) {
if(packet.payload_length() < 2) return;
uint16_t id = be2le16((char*) packet.payload().data_ptr());
if (this->lastPingId == id) {
#ifdef PKT_LOG_PING
logMessage(this->getServerId(), "{}[Ping] Got a valid pong for ping {}. Required time: {}", CLIENT_STR_LOG_PREFIX, id, duration_cast<microseconds>(system_clock::now() - this->lastPingRequest).count() / 1000.f);
#endif
this->lastPingResponse = system_clock::now();
this->ping = std::chrono::duration_cast<std::chrono::milliseconds>(this->lastPingResponse - this->lastPingRequest);
}
#ifdef PKT_LOG_PING
else {
logMessage(this->getServerId(), "{}[Ping] Got invalid pong. (Responded pong id {} but expected {})", CLIENT_STR_LOG_PREFIX, packet->packetId(), this->lastPingId);
}
#endif
return;
}
#ifdef PKT_LOG_PING
logMessage(this->getServerId(), "{}[Ping] Sending pong for client requested ping {}", CLIENT_STR_LOG_PREFIX, packet->packetId());
#endif
char buffer[2];
le2be16(packet.packet_id(), buffer);
this->connection->send_packet(PacketType::PONG, PacketFlag::Unencrypted, buffer, 2);
}
void VoiceClient::handlePacketVoice(const protocol::ClientPacketParser& packet) {
SpeakingClient::handlePacketVoice(packet.payload(), (packet.flags() & PacketFlag::Compressed) > 0, (packet.flags() & PacketFlag::Fragmented) > 0);
}
void VoiceClient::handlePacketVoiceWhisper(const ts::protocol::ClientPacketParser &packet) {
SpeakingClient::handlePacketVoiceWhisper(packet.payload(), (packet.flags() & PacketFlag::NewProtocol) > 0);
}
void VoiceClient::handlePacketAck(const protocol::ClientPacketParser& packet) {
if(packet.payload_length() < 2) return;
uint16_t target_id{be2le16(packet.payload().data_ptr<char>())};
this->connection->packet_statistics().received_acknowledge((protocol::PacketType) packet.type(), target_id | (packet.estimated_generation() << 16U));
string error{};
if(!this->connection->packet_encoder().acknowledge_manager().process_acknowledge(packet.type(), target_id, error))
debugMessage(this->getServerId(), "{} Failed to handle acknowledge: {}", CLIENT_STR_LOG_PREFIX, error);
}

View File

@ -1,25 +0,0 @@
#include <Definitions.h>
#include <log/LogUtils.h>
#include "../../InstanceHandler.h"
#include "VoiceClient.h"
using namespace std;
using namespace ts::server;
using namespace ts::protocol;
void VoiceClient::sendPingRequest() {
this->lastPingRequest = std::chrono::system_clock::now();
auto packet = protocol::allocate_outgoing_packet(0);
packet->ref(); /* extra ref for ourself */
packet->type_and_flags = (uint8_t) PacketType::PING | (uint8_t) PacketFlag::Unencrypted;
this->connection->send_packet(packet);
this->lastPingId = packet->packet_id();
packet->unref();
#ifdef PKT_LOG_PING
logMessage(this->getServerId(), "{}[Ping] Sending a ping request with it {}", CLIENT_STR_LOG_PREFIX, this->lastPingId);
#endif
}

View File

@ -270,7 +270,9 @@ void POWHandler::handle_puzzle_solve(const std::shared_ptr<ts::server::POWHandle
auto voice_client = this->register_verified_client(client);
if(voice_client) {
voice_client->connection->force_insert_command(command);
auto rcommand = server::udp::ReassembledCommand::allocate(command.length());
memcpy(rcommand->command(), command.data_ptr(), rcommand->length());
voice_client->connection->handlePacketCommand(rcommand);
client->state = LowHandshakeState::COMPLETED;
} else {
#ifdef POW_ERROR
@ -307,8 +309,8 @@ shared_ptr<VoiceClient> POWHandler::register_verified_client(const std::shared_p
debugMessage(this->get_server_id(), "Having new voice client. Remote address: {}", voice_client->getLoggingPeerIp() +":" + to_string(voice_client->getPeerPort()));
}
voice_client->crypto.client_time = client->client_version;
voice_client->last_packet_handshake = system_clock::now();
voice_client->getConnection()->crypt_setup_handler().set_client_protocol_time(client->client_version);
//voice_client->last_packet_handshake = system_clock::now();
return voice_client;
}

View File

@ -330,7 +330,7 @@ void VoiceServer::handleMessageRead(int fd, short events, void *_event_handle) {
auto new_address = net::to_string(remote_address);
auto command = "dummy_ipchange old_ip=" + old_address + " new_ip=" + new_address;
client->connection->force_insert_command(pipes::buffer_view{command.data(), command.length()});
client->server_command_executor().force_insert_command(pipes::buffer_view{command.data(), command.length()});
memcpy(&client->remote_address, &remote_address, sizeof(remote_address));
io::DatagramPacket::extract_info(message, client->address_info);
}

2
shared

@ -1 +1 @@
Subproject commit a15eb9d25c5b3305bf7b06d6780e9dbf7d7f3bc0
Subproject commit 143322575d0f6e13d9eff7912bf8ce8c34020838