diff --git a/git-teaspeak b/git-teaspeak index ed01d03..2da73d8 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit ed01d0360c5f980486e3bf83abdd3610d129aa7b +Subproject commit 2da73d81d1ee4dcd9e4e7e06a4b7fdf2ee6074ea diff --git a/server/src/VirtualServerManager.cpp b/server/src/VirtualServerManager.cpp index 09f7a34..705c950 100644 --- a/server/src/VirtualServerManager.cpp +++ b/server/src/VirtualServerManager.cpp @@ -13,7 +13,7 @@ using namespace std::chrono; using namespace ts::server; VirtualServerManager::VirtualServerManager(InstanceHandler* handle) : handle(handle) { - this->puzzles = new protocol::PuzzleManager(); + this->puzzles = new udp::PuzzleManager{}; this->handshakeTickers = new threads::Scheduler(1, "handshake ticker"); this->execute_loop = new event::EventExecutor("executor #"); //this->join_loop = new event::EventExecutor("joiner #"); @@ -67,7 +67,8 @@ bool VirtualServerManager::initialize(bool autostart) { this->state = State::STARTING; logMessage(LOG_INSTANCE, "Generating server puzzles..."); auto start = system_clock::now(); - this->puzzles->precomputePuzzles(config::voice::DefaultPuzzlePrecomputeSize); + if(!this->puzzles->precompute_puzzles(config::voice::DefaultPuzzlePrecomputeSize)) + logCritical(LOG_INSTANCE, "Failed to precompute RSA puzzles"); logMessage(LOG_INSTANCE, "Puzzles generated! Time required: " + to_string(duration_cast(system_clock::now() - start).count()) + "ms"); size_t serverCount = 0; diff --git a/server/src/VirtualServerManager.h b/server/src/VirtualServerManager.h index 887d533..ced73e9 100644 --- a/server/src/VirtualServerManager.h +++ b/server/src/VirtualServerManager.h @@ -57,7 +57,7 @@ namespace ts { bool createServerSnapshot(Command &cmd, std::shared_ptr server, int version, std::string &error); std::shared_ptr createServerFromSnapshot(std::shared_ptr old, std::string, uint16_t, const ts::Command &, std::string &); - protocol::PuzzleManager* rsaPuzzles() { return this->puzzles; } + udp::PuzzleManager* rsaPuzzles() { return this->puzzles; } event::EventExecutor* get_join_loop() { return this->join_loop; } event::EventExecutor* get_executor_loop() { return this->execute_loop; } @@ -80,7 +80,7 @@ namespace ts { InstanceHandler* handle; threads::Mutex instanceLock; std::deque> instances; - protocol::PuzzleManager* puzzles = nullptr; + udp::PuzzleManager* puzzles{nullptr}; event::EventExecutor* execute_loop = nullptr; event::EventExecutor* join_loop = nullptr; diff --git a/server/src/client/SpeakingClient.cpp b/server/src/client/SpeakingClient.cpp index e176dc9..0336504 100644 --- a/server/src/client/SpeakingClient.cpp +++ b/server/src/client/SpeakingClient.cpp @@ -742,7 +742,7 @@ void SpeakingClient::processJoin() { unique_lock server_channel_lock(this->server->channel_tree_lock); this->server->client_move(this->ref(), channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, false, server_channel_lock); - this->subscribeChannel({this->currentChannel}, false, true); + if(this->getType() != ClientType::CLIENT_TEAMSPEAK) this->subscribeChannel({this->currentChannel}, false, true); /* su "improve" the TS3 clients join speed we send the channel clients a bit later, when the TS3 client gets his own client variables */ } TIMING_STEP(timings, "join move "); diff --git a/server/src/client/command_handler/client.cpp b/server/src/client/command_handler/client.cpp index ced8596..14c0cc6 100644 --- a/server/src/client/command_handler/client.cpp +++ b/server/src/client/command_handler/client.cpp @@ -39,17 +39,21 @@ using namespace ts::token; command_result ConnectedClient::handleCommandClientGetVariables(Command &cmd) { CMD_REQ_SERVER; ConnectedLockedClient client{this->server->find_client_by_id(cmd["clid"].as())}; - shared_lock tree_lock(this->channel_lock); + { + shared_lock tree_lock(this->channel_lock); - if (!client || (client.client != this && !this->isClientVisible(client.client, false))) - return command_result{error::client_invalid_id, ""}; + if (!client || (client.client != this && !this->isClientVisible(client.client, false))) + return command_result{error::client_invalid_id, ""}; - deque props; - for (auto &prop : client->properties()->list_properties(property::FLAG_CLIENT_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { - props.push_back(&prop.type()); + deque props; + for (auto &prop : client->properties()->list_properties(property::FLAG_CLIENT_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { + props.push_back(&prop.type()); + } + + this->notifyClientUpdated(client.client, props, false); } - - this->notifyClientUpdated(client.client, props, false); + if(client.client == this && this->getType() == ClientType::CLIENT_TEAMSPEAK) + this->subscribeChannel({this->currentChannel}, true, true); /* lets show the clients in the current channel because we've not done that while joining (speed improvement ;))*/ return command_result{error::ok}; } diff --git a/server/src/client/voice/PacketStatistics.cpp b/server/src/client/voice/PacketStatistics.cpp index 2a7fad5..06f9372 100644 --- a/server/src/client/voice/PacketStatistics.cpp +++ b/server/src/client/voice/PacketStatistics.cpp @@ -89,6 +89,34 @@ void PacketStatistics::tick() { } } +void PacketStatistics::reset() { + std::lock_guard lock{this->data_mutex}; + this->calculator_command.reset(); + this->calculator_command_low.reset(); + + this->calculator_ack.reset(); + this->calculator_ack_low.reset(); + + this->calculator_voice.reset(); + this->calculator_voice_whisper.reset(); + + this->calculator_ping.reset(); +} + +void PacketStatistics::reset_offsets() { + std::lock_guard lock{this->data_mutex}; + this->calculator_command.reset_offsets(); + this->calculator_command_low.reset_offsets(); + + this->calculator_ack.reset_offsets(); + this->calculator_ack_low.reset_offsets(); + + this->calculator_voice.reset_offsets(); + this->calculator_voice_whisper.reset_offsets(); + + this->calculator_ping.reset_offsets(); +} + float PacketStatistics::current_packet_loss() const { auto report = this->loss_report(); return report.total_loss(); diff --git a/server/src/client/voice/PacketStatistics.h b/server/src/client/voice/PacketStatistics.h index 44c2594..5a93fd3 100644 --- a/server/src/client/voice/PacketStatistics.h +++ b/server/src/client/voice/PacketStatistics.h @@ -48,6 +48,8 @@ namespace ts::server::client { void received_packet(protocol::PacketType /* type */, uint32_t /* packet id */); void tick(); + void reset(); + void reset_offsets(); private: std::chrono::system_clock::time_point last_short{}; diff --git a/server/src/client/voice/PrecomputedPuzzles.cpp b/server/src/client/voice/PrecomputedPuzzles.cpp index 02ba330..d7a920a 100644 --- a/server/src/client/voice/PrecomputedPuzzles.cpp +++ b/server/src/client/voice/PrecomputedPuzzles.cpp @@ -1,50 +1,51 @@ -#include "PrecomputedPuzzles.h" -#include "../../Configuration.h" -#include "../ConnectedClient.h" +#include "./PrecomputedPuzzles.h" +#include "src/Configuration.h" #include using namespace std; -using namespace ts; -using namespace ts::protocol; +using namespace ts::server::udp; -PuzzleManager::PuzzleManager() {} -PuzzleManager::~PuzzleManager() {} +PuzzleManager::PuzzleManager() = default; +PuzzleManager::~PuzzleManager() = default; -size_t PuzzleManager::precomputedPuzzleCount() { return this->cached.size(); } - -bool PuzzleManager::precomputePuzzles(size_t limit) { - while(precomputedPuzzleCount() < limit) generatePuzzle(); - return true; +size_t PuzzleManager::precomputed_puzzle_count() { + std::lock_guard lock{this->cache_lock}; + return this->cached_puzzles.size(); } -std::shared_ptr PuzzleManager::nextPuzzle() { - this->indexLock.lock(); - size_t index = this->cacheIndex++ % this->cached.size(); - this->indexLock.unlock(); - return this->cached[index]; +bool PuzzleManager::precompute_puzzles(size_t amount) { + std::random_device rd{}; + std::mt19937 mt{rd()}; + + amount = 5; + while(this->precomputed_puzzle_count() < amount) + this->generate_puzzle(mt); + return this->precomputed_puzzle_count() > 0; } -inline void rndNum(mp_int *result, int byteLength){ - uint8_t buffer[byteLength]; +std::shared_ptr PuzzleManager::next_puzzle() { + std::lock_guard lock{this->cache_lock}; + return this->cached_puzzles[this->cache_index++ % this->cached_puzzles.size()]; +} - for(int index = 0; index < byteLength; index++) { - int rnd = rand(); - uint8_t urnd = static_cast(rnd & 0xFF); - buffer[index] = urnd; //TODO more secure! +inline void random_number(std::mt19937& generator, mp_int *result, int length){ + std::uniform_int_distribution dist{}; - } + uint8_t buffer[length]; + for(auto& byte : buffer) + byte = dist(generator); mp_zero(result); - mp_read_unsigned_bin(result, buffer, byteLength); + mp_read_unsigned_bin(result, buffer, length); } -inline bool solvePuzzle(Puzzle *puzzle){ +inline bool solve_puzzle(Puzzle *puzzle) { mp_int exp{}; mp_init(&exp); mp_2expt(&exp, puzzle->level); - if (mp_exptmod(&puzzle->x, &exp, &puzzle->n, &puzzle->result) != CRYPT_OK) { //Sometimes it fails (unknow why :D) + if (mp_exptmod(&puzzle->x, &exp, &puzzle->n, &puzzle->result) != CRYPT_OK) { //Sometimes it fails (unknown why :D) mp_clear(&exp); return false; } @@ -66,17 +67,17 @@ inline bool write_bin_data(mp_int& data, uint8_t* result, size_t length) { return true; } -void PuzzleManager::generatePuzzle() { +void PuzzleManager::generate_puzzle(std::mt19937& random_generator) { auto puzzle = new Puzzle{}; + puzzle->level = ts::config::voice::RsaPuzzleLevel; mp_init_multi(&puzzle->x, &puzzle->n, &puzzle->result, nullptr); generate_new: - rndNum(&puzzle->x, 64); - rndNum(&puzzle->n, 64); - puzzle->level = ts::config::voice::RsaPuzzleLevel; + random_number(random_generator, &puzzle->x, 64); + random_number(random_generator, &puzzle->n, 64); - if(!solvePuzzle(puzzle)) + if(!solve_puzzle(puzzle)) goto generate_new; auto valid_x = mp_unsigned_bin_size(&puzzle->x) <= 64; @@ -94,7 +95,7 @@ void PuzzleManager::generatePuzzle() { if(!write_bin_data(puzzle->result, puzzle->data_result, 64)) goto generate_new; - this->cached.push_back(shared_ptr(puzzle, [](Puzzle* elm){ + this->cached_puzzles.push_back(shared_ptr(puzzle, [](Puzzle* elm){ mp_clear_multi(&elm->n, &elm->x, &elm->result, nullptr); delete elm; })); diff --git a/server/src/client/voice/PrecomputedPuzzles.h b/server/src/client/voice/PrecomputedPuzzles.h index ef4a642..4822af0 100644 --- a/server/src/client/voice/PrecomputedPuzzles.h +++ b/server/src/client/voice/PrecomputedPuzzles.h @@ -1,44 +1,39 @@ #pragma once -#include #include #include -#include +#include +#include +#include -namespace ts { - namespace server { - class ConnectedClient; - } +namespace ts::server::udp { + struct Puzzle { + mp_int x; + mp_int n; + int level; - namespace protocol { - struct Puzzle { - mp_int x; - mp_int n; - int level; + mp_int result; - mp_int result; + uint8_t data_x[64]; + uint8_t data_n[64]; + uint8_t data_result[64]; + }; - uint8_t data_x[64]; - uint8_t data_n[64]; - uint8_t data_result[64]; - }; - class PuzzleManager { - public: - PuzzleManager(); - ~PuzzleManager(); + class PuzzleManager { + public: + PuzzleManager(); + ~PuzzleManager(); - bool precomputePuzzles(size_t limit); + [[nodiscard]] bool precompute_puzzles(size_t amount); - size_t precomputedPuzzleCount(); + [[nodiscard]] size_t precomputed_puzzle_count(); - std::shared_ptr nextPuzzle(); - private: - void generatePuzzle(); + [[nodiscard]] std::shared_ptr next_puzzle(); + private: + void generate_puzzle(std::mt19937&); - threads::Mutex indexLock; - size_t cacheIndex = 0; - - std::deque> cached; - }; - } + size_t cache_index{0}; + spin_lock cache_lock{}; + std::vector> cached_puzzles{}; + }; } \ No newline at end of file diff --git a/server/src/client/voice/VoiceClientHandschake.cpp b/server/src/client/voice/VoiceClientHandschake.cpp index 8c2e9a0..bb74be5 100644 --- a/server/src/client/voice/VoiceClientHandschake.cpp +++ b/server/src/client/voice/VoiceClientHandschake.cpp @@ -70,6 +70,7 @@ ts::command_result VoiceClient::handleCommandClientInitIv(Command& command) { this->connection->reset(); this->connection->register_initiv_packet(); + this->connection->packet_statistics().reset_offsets(); this->crypto.protocol_encrypted = false; bool use_teaspeak = command.hasParm("teaspeak"); diff --git a/server/src/server/POWHandler.cpp b/server/src/server/POWHandler.cpp index d1063eb..15a77f1 100644 --- a/server/src/server/POWHandler.cpp +++ b/server/src/server/POWHandler.cpp @@ -1,10 +1,8 @@ #include "POWHandler.h" #include "src/InstanceHandler.h" -#include "src/VirtualServerManager.h" #include "src/client/voice/VoiceClient.h" #include #include -#include using namespace std; using namespace std::chrono; @@ -21,7 +19,7 @@ void POWHandler::execute_tick() { lock_guard lock(this->pending_clients_lock); this->pending_clients.erase(remove_if(this->pending_clients.begin(), this->pending_clients.end(), [&, now](const shared_ptr& client) { - if(now - client->last_packet > seconds(5)) { + if(now - client->last_packet > std::chrono::seconds{5}) { #ifdef POW_ERROR if(client->state != LowHandshakeState::COMPLETED) { /* handshake succeeded */ debugMessage(this->get_server_id(), "[POW] Dropping connection from {} (Timeout)", net::to_string(client->address)); @@ -155,12 +153,6 @@ inline void generate_random(uint8_t *destination, size_t length) { *(destination++) = (uint8_t) rand(); } -inline void write_reversed(uint8_t* destination, uint8_t* source, size_t length) { - destination += length; - while(length-- > 0) - *(--destination) = *(source++); -} - void POWHandler::handle_cookie_get(const std::shared_ptr &client, const pipes::buffer_view &buffer) { if(buffer.length() != 21) { #ifdef POW_ERROR @@ -172,7 +164,7 @@ void POWHandler::handle_cookie_get(const std::shared_ptrserver_control_data[0] == 0) { generate_random(client->server_control_data, 16); - client->server_control_data[0] |= 1; + client->server_control_data[0] |= 1U; } /* parse values */ @@ -183,7 +175,7 @@ void POWHandler::handle_cookie_get(const std::shared_ptrserver_control_data, 16); - write_reversed(&response_buffer[17], client->client_control_data, 4); + *(uint32_t*) &response_buffer[17] = htonl(*(uint32_t*) &client->client_control_data); this->send_data(client, pipes::buffer_view{response_buffer, 21}); } @@ -211,7 +203,7 @@ void POWHandler::handle_puzzle_get(const std::shared_ptrrsa_challenge) - client->rsa_challenge = serverInstance->getVoiceServerManager()->rsaPuzzles()->nextPuzzle(); + client->rsa_challenge = serverInstance->getVoiceServerManager()->rsaPuzzles()->next_puzzle(); /* send response */ { @@ -262,6 +254,7 @@ void POWHandler::handle_puzzle_solve(const std::shared_ptrget_server_id(), "[POW][{}][Puzzle] Received an invalid puzzle solution! Resetting client", net::to_string(client->address)); #endif + client->rsa_challenge.reset(); /* get another RSA challenge */ this->reset_client(client); return; } diff --git a/server/src/server/POWHandler.h b/server/src/server/POWHandler.h index ed53b01..ea5c9f6 100644 --- a/server/src/server/POWHandler.h +++ b/server/src/server/POWHandler.h @@ -8,6 +8,7 @@ #include "VoiceServer.h" #include "src/VirtualServer.h" + namespace ts::server { class POWHandler { public: @@ -32,13 +33,13 @@ namespace ts::server { std::chrono::system_clock::time_point last_packet; LowHandshakeState state = LowHandshakeState::COOKIE_GET; - uint8_t client_control_data[4] = {0,0,0,0}; - uint8_t server_control_data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + uint8_t client_control_data[4]{0}; + uint8_t server_control_data[16]{0}; uint8_t server_data[100]; uint32_t client_version; - std::shared_ptr rsa_challenge; + std::shared_ptr rsa_challenge; }; explicit POWHandler(VoiceServer* /* server */); diff --git a/server/src/server/VoiceIOManager.h b/server/src/server/VoiceIOManager.h index 54c793e..4a4aeec 100644 --- a/server/src/server/VoiceIOManager.h +++ b/server/src/server/VoiceIOManager.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace ts { namespace server { diff --git a/server/src/server/VoiceServer.cpp b/server/src/server/VoiceServer.cpp index 1d2e857..5bab5d2 100644 --- a/server/src/server/VoiceServer.cpp +++ b/server/src/server/VoiceServer.cpp @@ -170,11 +170,11 @@ void VoiceServer::execute_resend(const std::chrono::system_clock::time_point &no } for(auto& entry : buffers) connection->packet_statistics().send_command((protocol::PacketType) entry->packet_type, entry->packet_id | entry->generation_id << 16U); - if(buffers.size() > 0) - logTrace(client->getServerId(), "{} Resending {} packets.", CLIENT_STR_LOG_PREFIX_(client), buffers.size()); - buffers.clear(); + //if(buffers.size() > 0) + // logTrace(client->getServerId(), "{} Resending {} packets.", CLIENT_STR_LOG_PREFIX_(client), buffers.size()); connection->triggerWrite(); } + buffers.clear(); } } diff --git a/shared b/shared index e0eb4c5..ee7f26b 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit e0eb4c5a16b900f6ed906c88a264d07479b13c5e +Subproject commit ee7f26b7ed883027f0a81ece1c2aacd41053ce1e