Finalizing the voice connection cleanup
This commit is contained in:
parent
4c91a7a3bf
commit
7a974677fb
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
273
server/src/client/voice/CryptSetupHandler.cpp
Normal file
273
server/src/client/voice/CryptSetupHandler.cpp
Normal 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;
|
||||
}
|
56
server/src/client/voice/CryptSetupHandler.h
Normal file
56
server/src/client/voice/CryptSetupHandler.h
Normal 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 */);
|
||||
};
|
||||
}
|
@ -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 */
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
||||
|
69
server/src/client/voice/PingHandler.cpp
Normal file
69
server/src/client/voice/PingHandler.cpp
Normal 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);
|
||||
}
|
45
server/src/client/voice/PingHandler.h
Normal file
45
server/src/client/voice/PingHandler.h
Normal 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();
|
||||
};
|
||||
}
|
97
server/src/client/voice/ServerCommandExecutor.cpp
Normal file
97
server/src/client/voice/ServerCommandExecutor.cpp
Normal 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);
|
||||
}
|
33
server/src/client/voice/ServerCommandExecutor.h
Normal file
33
server/src/client/voice/ServerCommandExecutor.h
Normal 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;
|
||||
}
|
@ -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) {
|
||||
|
@ -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 */);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
}
|
@ -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&);
|
||||
};
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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};
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
2
shared
@ -1 +1 @@
|
||||
Subproject commit a15eb9d25c5b3305bf7b06d6780e9dbf7d7f3bc0
|
||||
Subproject commit 143322575d0f6e13d9eff7912bf8ce8c34020838
|
Loading…
Reference in New Issue
Block a user