309 lines
13 KiB
C++
309 lines
13 KiB
C++
#include <algorithm>
|
|
#include <log/LogUtils.h>
|
|
#include <misc/memtracker.h>
|
|
#include <protocol/Packet.h>
|
|
#include <ThreadPool/Timer.h>
|
|
|
|
#include "../../server/VoiceServer.h"
|
|
#include "./VoiceClientConnection.h"
|
|
#include "./VoiceClient.h"
|
|
|
|
|
|
//#define LOG_AUTO_ACK_AUTORESPONSE
|
|
//#define FUZZING_TESTING_INCOMMING
|
|
//#define FUZZING_TESTING_OUTGOING
|
|
//#define FIZZING_TESTING_DISABLE_HANDSHAKE
|
|
#define FUZZING_TESTING_DROP 8
|
|
#define FUZZING_TESTING_DROP_MAX 10
|
|
|
|
//#define CONNECTION_NO_STATISTICS
|
|
|
|
#define QLZ_COMPRESSION_LEVEL 1
|
|
#include "qlz/QuickLZ.h"
|
|
|
|
using namespace std;
|
|
using namespace std::chrono;
|
|
using namespace ts;
|
|
using namespace ts::connection;
|
|
using namespace ts::protocol;
|
|
using namespace ts::server;
|
|
|
|
VoiceClientConnection::VoiceClientConnection(VoiceClient* client)
|
|
: 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;
|
|
this->packet_decoder_.callback_decoded_packet = VoiceClientConnection::callback_packet_decoded;
|
|
this->packet_decoder_.callback_decoded_command = VoiceClientConnection::callback_command_decoded;
|
|
this->packet_decoder_.callback_send_acknowledge = VoiceClientConnection::callback_send_acknowledge;
|
|
|
|
this->packet_encoder_.callback_data = this;
|
|
this->packet_encoder_.callback_request_write = VoiceClientConnection::callback_request_write;
|
|
this->packet_encoder_.callback_crypt_error = VoiceClientConnection::callback_encode_crypt_error;
|
|
this->packet_encoder_.callback_resend_failed = VoiceClientConnection::callback_resend_failed;
|
|
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->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->current_client->voice_server)
|
|
this->current_client->voice_server->triggerWrite(dynamic_pointer_cast<VoiceClient>(this->current_client->_this.lock()));
|
|
}
|
|
|
|
void VoiceClientConnection::handle_incoming_datagram(const pipes::buffer_view& buffer) {
|
|
ClientPacketParser packet_parser{buffer};
|
|
if(!packet_parser.valid())
|
|
return;
|
|
|
|
#ifndef CONNECTION_NO_STATISTICS
|
|
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());
|
|
#endif
|
|
|
|
std::string error{};
|
|
auto result = this->packet_decoder_.process_incoming_data(packet_parser, error);
|
|
using PacketProcessResult = server::server::udp::PacketProcessResult;
|
|
switch (result) {
|
|
case PacketProcessResult::SUCCESS:
|
|
case PacketProcessResult::FUZZ_DROPPED: /* maybe some kind of log? */
|
|
case PacketProcessResult::DECRYPT_FAILED: /* Silently drop this packet */
|
|
case PacketProcessResult::DUPLICATED_PACKET: /* no action needed, acknowledge should be send already */
|
|
break;
|
|
|
|
case PacketProcessResult::DECRYPT_KEY_GEN_FAILED:
|
|
/* no action needed, acknowledge should be send */
|
|
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->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->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->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->virtual_server_id_, "{} Failed to decompress a command packet. Dropping command.", this->log_prefix());
|
|
break;
|
|
|
|
case PacketProcessResult::COMMAND_TOO_LARGE:
|
|
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->virtual_server_id_, "{} Received a too long command sequence. Dropping client.", this->log_prefix());
|
|
goto disconnect_client;
|
|
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
|
|
disconnect_client:;
|
|
/* FIXME: Disconnect the client */
|
|
}
|
|
|
|
void VoiceClientConnection::callback_send_acknowledge(void *ptr_this, uint16_t packet_id, bool 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->handlePacketVoice(packet);
|
|
break;
|
|
|
|
case protocol::VOICE_WHISPER:
|
|
connection->handlePacketVoiceWhisper(packet);
|
|
break;
|
|
|
|
case protocol::ACK:
|
|
connection->handlePacketAck(packet);
|
|
break;
|
|
|
|
case protocol::ACK_LOW:
|
|
connection->handlePacketAckLow(packet);
|
|
break;
|
|
|
|
case protocol::PING:
|
|
connection->handlePacketPing(packet);
|
|
break;
|
|
|
|
case protocol::PONG:
|
|
connection->handlePacketPong(packet);
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
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, ReassembledCommand *&command) {
|
|
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
|
|
|
|
/* we're exchanging the command so we're taking the ownership */
|
|
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);
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
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();
|
|
this->ping_handler_.reset();
|
|
this->packet_decoder_.reset();
|
|
this->packet_encoder_.reset();
|
|
}
|
|
|
|
void VoiceClientConnection::reset_remote_address() {
|
|
memset(&this->remote_address_, 0, sizeof(this->remote_address_));
|
|
memset(&this->remote_address_info_, 0, sizeof(this->remote_address_info_));
|
|
}
|
|
|
|
void VoiceClientConnection::send_packet(protocol::PacketType type, protocol::PacketFlag::PacketFlags flag, const void *payload, size_t payload_size) {
|
|
this->packet_encoder_.send_packet(type, flag, payload, payload_size);
|
|
}
|
|
|
|
void VoiceClientConnection::send_packet(protocol::OutgoingServerPacket* packet) {
|
|
this->packet_encoder_.send_packet(packet);
|
|
}
|
|
|
|
void VoiceClientConnection::send_command(const std::string_view &cmd, bool b, std::unique_ptr<threads::Future<bool>> cb) {
|
|
this->packet_encoder_.send_command(cmd, b, std::move(cb));
|
|
}
|
|
|
|
void VoiceClientConnection::callback_encode_crypt_error(void *ptr_this,
|
|
const PacketEncoder::CryptError &error,
|
|
const std::string &detail) {
|
|
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
|
|
switch (error) {
|
|
case PacketEncoder::CryptError::ENCRYPT_FAILED:
|
|
logError(connection->virtual_server_id_, "{} Failed to encrypt packet. Error: {}", connection->log_prefix(), detail);
|
|
break;
|
|
|
|
case PacketEncoder::CryptError::KEY_GENERATION_FAILED:
|
|
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:
|
|
assert(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void VoiceClientConnection::callback_request_write(void *ptr_this) {
|
|
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
|
|
connection->triggerWrite();
|
|
}
|
|
|
|
void VoiceClientConnection::callback_resend_failed(void *ptr_this, const shared_ptr<AcknowledgeManager::Entry> &entry) {
|
|
auto connection = reinterpret_cast<VoiceClientConnection*>(ptr_this);
|
|
|
|
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 {
|
|
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->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 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) {
|
|
(void) ptr_this;
|
|
/* doing nothing a packet resend failed will cause the client to disconnect */
|
|
} |