Initial commit

This commit is contained in:
WolverinDEV
2019-06-26 22:11:22 +02:00
commit ff36addda5
99 changed files with 18395 additions and 0 deletions
+157
View File
@@ -0,0 +1,157 @@
#include "AcknowledgeManager.h"
#include <math.h>
#include <misc/endianness.h>
using namespace ts;
using namespace ts::connection;
using namespace ts::protocol;
using namespace std;
using namespace std::chrono;
AcknowledgeManager::AcknowledgeManager() {}
AcknowledgeManager::~AcknowledgeManager() {
{
lock_guard<recursive_mutex> lock(this->entry_lock);
for(const auto& entry : this->entries)
if(entry->acknowledge_listener)
entry->acknowledge_listener->executionFailed("deleted");
this->entries.clear();
}
}
void AcknowledgeManager::reset() {
{
lock_guard<recursive_mutex> lock(this->entry_lock);
for(const auto& entry : this->entries)
if(entry->acknowledge_listener)
entry->acknowledge_listener->executionFailed("reset");
this->entries.clear();
}
}
size_t AcknowledgeManager::awaiting_acknowledge() {
lock_guard<recursive_mutex> lock(this->entry_lock);
return this->entries.size();
}
void AcknowledgeManager::process_packet(ts::protocol::BasicPacket &packet) {
if(!packet.type().requireAcknowledge()) return;
auto entry = make_shared<Entry>();
entry->acknowledge_listener = std::move(packet.getListener());
entry->buffer = packet.buffer();
entry->resend_count = 0;
entry->resend_period = milliseconds((int) ceil(this->average_response * 3/2));
entry->first_send = system_clock::now();
entry->next_resend = entry->first_send + entry->resend_period;
entry->packet_type = packet.type().type();
entry->packet_id = packet.packetId();
entry->acknowledged = false;
entry->send_count = 1;
{
lock_guard<recursive_mutex> lock(this->entry_lock);
this->entries.push_front(std::move(entry));
}
}
bool AcknowledgeManager::process_acknowledge(const ts::protocol::BasicPacket &packet, std::string& error) {
PacketType target_type = PacketType::UNDEFINED;
uint16_t target_id = 0;
if(packet.type().type() == PacketType::ACK_LOW) target_type = PacketType::COMMAND_LOW;
else if(packet.type().type() == PacketType::ACK) target_type = PacketType::COMMAND;
target_id = be2le16((char*) packet.data().data_ptr());
//debugMessage(0, "Got ack for {} {}", target_type, target_id);
if(target_type == PacketType::UNDEFINED) {
error = "Invalid packet type (" + to_string(target_type) + ")";
return false;
}
std::shared_ptr<Entry> entry;
{
lock_guard<recursive_mutex> lock(this->entry_lock);
for(auto it = this->entries.begin(); it != this->entries.end(); it++) {
if((*it)->packet_type == target_type && (*it)->packet_id == target_id) {
entry = *it;
entry->send_count--;
if(entry->send_count == 0)
this->entries.erase(it);
break;
}
}
}
if(!entry) {
error = "Missing packet id (" + to_string(target_id) + ")";
return false;
}
auto time = system_clock::now() - entry->next_resend + entry->resend_period;
auto ms_time = duration_cast<milliseconds>(time).count();
if(ms_time > 5) {
this->average_response = this->average_response / 2 + ms_time / 2;
}
entry->acknowledged = true;
if(entry->acknowledge_listener)
entry->acknowledge_listener->executionSucceed(true);
entry->acknowledge_listener.reset();
return true;
}
ssize_t AcknowledgeManager::execute_resend(const system_clock::time_point& now , std::chrono::system_clock::time_point &next_resend,std::deque<pipes::buffer>& buffers, string& error) {
ssize_t resend_count = 0;
deque<shared_ptr<Entry>> need_resend;
{
deque<shared_ptr<Entry>> erase;
lock_guard<recursive_mutex> lock(this->entry_lock);
for (auto &entry : this->entries) {
if(!entry->acknowledged && entry->next_resend <= now) {
entry->resend_period = entry->resend_period + milliseconds((int) ceil(this->average_response * 2));
if(entry->resend_period.count() > 1000)
entry->resend_period = milliseconds(1000);
else if(entry->resend_period.count() < 25)
entry->resend_period = milliseconds(25);
entry->next_resend = now + entry->resend_period;
need_resend.push_front(entry);
}
if(entry->acknowledged) {
if(entry->next_resend + entry->resend_period <= now) { //Timeout for may (more acknowledges)
erase.push_back(entry);
}
} else {
if(next_resend > entry->next_resend)
next_resend = entry->next_resend;
}
}
for(const auto& e : erase) {
auto it = find(this->entries.begin(), this->entries.end(), e);
if(it != this->entries.end())
this->entries.erase(it);
}
}
for(const auto& packet : need_resend) {
if(packet->resend_count > 15 && packet->first_send + seconds(15) < now) { //FIXME configurable
error = "Failed to receive acknowledge for packet " + to_string(packet->packet_id) + " of type " + PacketTypeInfo::fromid(packet->packet_type).name();
return -1;
}
resend_count++;
packet->resend_count++;
packet->send_count++;
buffers.push_back(packet->buffer);
}
return resend_count;
}
+51
View File
@@ -0,0 +1,51 @@
#pragma once
#include <memory>
#include <protocol/Packet.h>
#define DEBUG_ACKNOWLEDGE
namespace ts {
namespace connection {
class VoiceClientConnection;
class AcknowledgeManager {
struct Entry {
uint16_t packet_id = 0;
uint8_t packet_type = 0xFF;
uint8_t resend_count = 0;
bool acknowledged : 1;
uint8_t send_count : 7;
pipes::buffer buffer;
std::chrono::system_clock::time_point first_send;
std::chrono::system_clock::time_point next_resend;
std::chrono::milliseconds resend_period;
std::unique_ptr<threads::Future<bool>> acknowledge_listener;
};
public:
AcknowledgeManager();
virtual ~AcknowledgeManager();
size_t awaiting_acknowledge();
void reset();
void process_packet(ts::protocol::BasicPacket& /* packet */);
bool process_acknowledge(const ts::protocol::BasicPacket& /* packet */, std::string& /* error */);
ssize_t execute_resend(
const std::chrono::system_clock::time_point& /* now */,
std::chrono::system_clock::time_point& /* next resend */,
std::deque<pipes::buffer>& /* buffers to resend */,
std::string& /* error */
);
private:
std::recursive_mutex entry_lock;
std::deque<std::shared_ptr<Entry>> entries;
std::chrono::milliseconds resend_delay{500};
double average_response = 20;
};
}
}
+81
View File
@@ -0,0 +1,81 @@
#include <csignal>
#include <misc/memtracker.h>
#include "CompressionHandler.h"
#define QLZ_COMPRESSION_LEVEL 1
#define QLZ_MEMORY_SAFE
#include "qlz/QuickLZ.h"
#include "buffers.h"
using namespace ts;
using namespace ts::connection;
using namespace std;
bool CompressionHandler::compress(protocol::BasicPacket* packet, std::string &error) {
//// "Always allocate size + 400 bytes for the destination buffer when compressing." <= http://www.quicklz.com/manual.html
auto length = packet->data().length();
auto header_length = packet->header().length() + packet->mac().length();
size_t expected_bytes = min(length * 2, length + 400);
auto buffer = buffer::allocate_buffer(expected_bytes + header_length);
qlz_state_compress state_compress{};
size_t actualLength = qlz_compress(packet->data().data_ptr(), (char*) &buffer[header_length], packet->data().length(), &state_compress);
if(actualLength > buffer.length()) {
logCritical(0, "Buffer overflow! Compressed data is longer than expected. (Expected: {}, Written: {}, Source length: {}, Allocated block size: {})",
expected_bytes,
actualLength,
packet->data().length(),
buffer.capacity()
);
error = "overflow";
return false;
}
if(actualLength <= 0){
error = "Cloud not compress packet";
return false;
}
memcpy(buffer.data_ptr(), packet->buffer().data_ptr(), header_length);
packet->buffer(buffer.range(0, actualLength + header_length));
return true;
}
bool CompressionHandler::decompress(protocol::BasicPacket* packet, std::string &error) {
qlz_state_decompress state_decompress{};
size_t expected_length = qlz_size_decompressed((char*) packet->data().data_ptr());
if(expected_length > this->max_packet_size){ //Max 16MB. (97% Compression!)
error = "Invalid packet size. (Calculated target length of " + to_string(expected_length) + ". Max length: " + to_string(this->max_packet_size) + ")";
return false;
}
auto header_length = packet->header().length() + packet->mac().length();
auto buffer = buffer::allocate_buffer(expected_length + header_length);
size_t data_length = qlz_decompress((char*) packet->data().data_ptr(), &buffer[header_length], &state_decompress);
if(data_length <= 0){
error = "Could not decompress packet.";
return false;
}
memcpy(buffer.data_ptr(), packet->buffer().data_ptr(), header_length);
packet->buffer(buffer.range(0, data_length + header_length));
return true;
}
bool CompressionHandler::progressPacketIn(protocol::BasicPacket* packet, std::string &error) {
if(packet->isCompressed()) {
if(!this->decompress(packet, error)) return false;
packet->setCompressed(false);
}
return true;
}
bool CompressionHandler::progressPacketOut(protocol::BasicPacket* packet, std::string& error) {
if(packet->hasFlag(protocol::PacketFlag::Compressed) && !packet->isCompressed()) {
if(!this->compress(packet, error)) return false;
packet->setCompressed(true);
}
return true;
}
CompressionHandler::CompressionHandler() { }
CompressionHandler::~CompressionHandler() { }
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include "Packet.h"
namespace ts {
namespace connection {
class CompressionHandler {
public:
CompressionHandler();
virtual ~CompressionHandler();
bool progressPacketOut(protocol::BasicPacket*, std::string&);
bool progressPacketIn(protocol::BasicPacket*, std::string&);
size_t max_packet_size = 16 * 1024;
private:
bool compress(protocol::BasicPacket*, std::string &error);
bool decompress(protocol::BasicPacket*, std::string &error);
};
}
}
+378
View File
@@ -0,0 +1,378 @@
#define NO_OPEN_SSL /* because we're lazy and dont want to build this lib extra for the TeaClient */
#define FIXEDINT_H_INCLUDED /* else it will be included by ge */
#include "misc/endianness.h"
#include <ed25519/ed25519.h>
#include <ed25519/ge.h>
#include <log/LogUtils.h>
#include "misc/memtracker.h"
#include "misc/digest.h"
#include "CryptionHandler.h"
using namespace std;
using namespace ts;
using namespace ts::connection;
using namespace ts::protocol;
CryptionHandler::CryptionHandler() {
memtrack::allocated<CryptionHandler>(this);
}
CryptionHandler::~CryptionHandler() {
memtrack::freed<CryptionHandler>(this);
}
void CryptionHandler::reset() {
this->useDefaultChipherKeyNonce = true;
this->iv_struct_length = 0;
memset(this->iv_struct, 0, sizeof(this->iv_struct));
memcpy(this->current_mac, this->default_mac, sizeof(this->default_mac));
for(auto& cache : this->cache_key_client)
cache.generation = 0xFFEF;
for(auto& cache : this->cache_key_server)
cache.generation = 0xFFEF;
}
bool CryptionHandler::setupSharedSecret(const std::string& alpha, const std::string& beta, ecc_key *publicKey, ecc_key *ownKey, std::string &error) {
size_t bufferLength = 128;
uint8_t* buffer = new uint8_t[bufferLength];
int err;
if((err = ecc_shared_secret(ownKey, publicKey, buffer, (unsigned long*) &bufferLength)) != CRYPT_OK){
delete[] buffer;
error = "Could not calculate shared secret. Message: " + string(error_to_string(err));
return false;
}
auto result = this->setupSharedSecret(alpha, beta, string((const char*) buffer, bufferLength), error);
delete[] buffer;
return result;
}
bool CryptionHandler::setupSharedSecret(const std::string& alpha, const std::string& beta, std::string sharedKey, std::string &error) {
auto secret_hash = digest::sha1(sharedKey);
char ivStruct[SHA_DIGEST_LENGTH];
memcpy(ivStruct, alpha.data(), 10);
memcpy(&ivStruct[10], beta.data(), 10);
for (int index = 0; index < SHA_DIGEST_LENGTH; index++) {
ivStruct[index] ^= (uint8_t) secret_hash[index];
}
{
lock_guard lock(this->cache_key_lock);
memcpy(this->iv_struct, ivStruct, SHA_DIGEST_LENGTH);
this->iv_struct_length = SHA_DIGEST_LENGTH;
auto iv_hash = digest::sha1(ivStruct, SHA_DIGEST_LENGTH);
memcpy(this->current_mac, iv_hash.data(), 8);
this->useDefaultChipherKeyNonce = false;
}
return true;
}
void _fe_neg(fe h, const fe f) {
int32_t f0 = f[0];
int32_t f1 = f[1];
int32_t f2 = f[2];
int32_t f3 = f[3];
int32_t f4 = f[4];
int32_t f5 = f[5];
int32_t f6 = f[6];
int32_t f7 = f[7];
int32_t f8 = f[8];
int32_t f9 = f[9];
int32_t h0 = -f0;
int32_t h1 = -f1;
int32_t h2 = -f2;
int32_t h3 = -f3;
int32_t h4 = -f4;
int32_t h5 = -f5;
int32_t h6 = -f6;
int32_t h7 = -f7;
int32_t h8 = -f8;
int32_t h9 = -f9;
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
}
inline std::string keyMul(const uint8_t* publicKey /* compressed */, const uint8_t* privateKey /* uncompressed */, bool negate){
ge_p3 keyA{};
ge_p2 result{};
ge_frombytes_negate_vartime(&keyA, publicKey);
if(negate) {
_fe_neg(*(fe*) &keyA.X, *(const fe*) &keyA.X); /* undo negate */
_fe_neg(*(fe*) &keyA.T, *(const fe*) &keyA.T); /* undo negate */
}
ge_scalarmult_vartime(&result, privateKey, &keyA);
char buffer[32];
ge_tobytes((uint8_t*) buffer, &result);
return string(buffer, 32);
}
bool CryptionHandler::setupSharedSecretNew(const std::string &alpha, const std::string &beta, const char* privateKey /* uncompressed */, const char* publicKey /* compressed */) {
assert(alpha.length() == 10);
assert(beta.length() == 54);
string shared;
string sharedIv;
shared.resize(32, '\0');
sharedIv.resize(64, '\0');
ed25519_key_exchange((uint8_t*) shared.data(), (uint8_t*) publicKey, (uint8_t*) privateKey);
shared = keyMul(reinterpret_cast<const uint8_t *>(publicKey), reinterpret_cast<const uint8_t *>(privateKey), true); //Remote key get negated
sharedIv = digest::sha512(shared);
auto xor_key = alpha + beta;
for(int i = 0; i < 64; i++)
sharedIv[i] ^= xor_key[i];
{
lock_guard lock(this->cache_key_lock);
memcpy(this->iv_struct, sharedIv.data(), 64);
this->iv_struct_length = 64;
auto digest_buffer = digest::sha1((char*) this->iv_struct, 64);
memcpy(this->current_mac, digest_buffer.data(), 8);
this->useDefaultChipherKeyNonce = false;
}
return true;
}
bool CryptionHandler::generate_key_nonce(protocol::BasicPacket* packet, bool use_default, uint8_t(& key)[16], uint8_t(& nonce)[16]){
return this->generate_key_nonce(
dynamic_cast<protocol::ClientPacket *>(packet) != nullptr,
packet->type().type(),
packet->packetId(),
packet->generationId(),
use_default,
key,
nonce
);
}
bool CryptionHandler::generate_key_nonce(
bool to_server, /* its from the client to the server */
protocol::PacketType type,
uint16_t packet_id,
uint16_t generation,
bool use_default,
uint8_t (& key)[16],
uint8_t (& nonce)[16]
) {
if (this->useDefaultChipherKeyNonce || use_default) {
memcpy(key, this->default_key, 16);
memcpy(nonce, this->default_nonce, 16);
return true;
}
auto& key_cache_array = to_server ? this->cache_key_client : this->cache_key_server;
if(type < 0 || type >= key_cache_array.max_size()) {
logError(0, "Tried to generate a crypt key with invalid type ({})!", type);
return false;
}
auto& key_cache = key_cache_array[type];
if(key_cache.generation != generation) {
const size_t buffer_length = 6 + this->iv_struct_length;
char* buffer = new char[buffer_length];
memset(buffer, 0, 6 + this->iv_struct_length);
if (to_server) {
buffer[0] = 0x31;
} else {
buffer[0] = 0x30;
}
buffer[1] = (char) (type & 0xF);
le2be32(generation, buffer, 2);
memcpy(&buffer[6], this->iv_struct, this->iv_struct_length);
auto key_nonce = digest::sha256(buffer, 6 + this->iv_struct_length);
memcpy(key_cache.key, key_nonce.data(), 16);
memcpy(key_cache.nonce, key_nonce.data() + 16, 16);
key_cache.generation = generation;
delete[] buffer;
}
memcpy(key, key_cache.key, 16);
memcpy(nonce, key_cache.nonce, 16);
//Xor the key
key[0] ^= (uint8_t) ((packet_id >> 8) & 0xFF);
key[1] ^=(packet_id & 0xFF);
return true;
}
bool CryptionHandler::verify_encryption(const pipes::buffer_view &packet, uint16_t packet_id, uint16_t generation) {
int err;
int success = false;
uint8_t key[16], nonce[16];
if(!generate_key_nonce(true, (protocol::PacketType) (packet[12] & 0xF), packet_id, generation, false, key, nonce))
return false;
auto mac = packet.view(0, 8);
auto header = packet.view(8, 5);
auto data = packet.view(13);
auto length = data.length();
const unsigned long target_length = 2048;
uint8_t target_buffer[2048];
if(target_length < length)
return false;
err = eax_decrypt_verify_memory(find_cipher("rijndael"),
(uint8_t *) key, /* the key */
(size_t) 16, /* key is 16 bytes */
(uint8_t *) nonce, /* the nonce */
(size_t) 16, /* nonce is 16 bytes */
(uint8_t *) header.data_ptr(), /* example header */
(unsigned long) header.length(), /* header length */
(const unsigned char *) data.data_ptr(),
(unsigned long) data.length(),
(unsigned char *) target_buffer,
(unsigned char *) mac.data_ptr(),
(unsigned long) mac.length(),
&success
);
return err == CRYPT_OK && success;
}
bool CryptionHandler::decryptPacket(protocol::BasicPacket *packet, std::string &error, bool use_default) {
int err;
int success = false;
auto header = packet->header();
auto data = packet->data();
uint8_t key[16], nonce[16];
if(!generate_key_nonce(packet, use_default, key, nonce)) {
error = "Could not generate key/nonce";
return false;
}
size_t target_length = 2048;
uint8_t target_buffer[2048];
auto length = data.length();
if(target_length < length) {
error = "buffer too large";
return false;
}
err = eax_decrypt_verify_memory(find_cipher("rijndael"),
(uint8_t *) key, /* the key */
(unsigned long) 16, /* key is 16 bytes */
(uint8_t *) nonce, /* the nonce */
(unsigned long) 16, /* nonce is 16 bytes */
(uint8_t *) header.data_ptr(), /* example header */
(unsigned long) header.length(), /* header length */
(const unsigned char *) data.data_ptr(),
(unsigned long) data.length(),
(unsigned char *) target_buffer,
(unsigned char *) packet->mac().data_ptr(),
(unsigned long) packet->mac().length(),
&success
);
if((err) != CRYPT_OK){
error = "eax_decrypt_verify_memory(...) returned " + to_string(err) + "/" + error_to_string(err);
return false;
}
if(!success){
error = "memory verify failed!";
return false;
}
packet->data(pipes::buffer_view{target_buffer, length});
packet->setEncrypted(false);
return true;
}
bool CryptionHandler::encryptPacket(protocol::BasicPacket *packet, std::string &error, bool use_default) {
uint8_t key[16], nonce[16];
if(!generate_key_nonce(packet, use_default, key, nonce)) {
error = "Could not generate key/nonce";
return false;
}
size_t length = packet->data().length();
size_t tag_length = 8;
char tag_buffer[8];
size_t target_length = 2048;
uint8_t target_buffer[2048];
if(target_length < length) {
error = "buffer too large";
return false;
}
int err;
if((err = eax_encrypt_authenticate_memory(find_cipher("rijndael"),
(uint8_t *) key, /* the key */
(unsigned long) 16, /* key is 16 bytes */
(uint8_t *) nonce, /* the nonce */
(unsigned long) 16, /* nonce is 16 bytes */
(uint8_t *) packet->header().data_ptr(), /* example header */
(unsigned long) packet->header().length(), /* header length */
(uint8_t *) packet->data().data_ptr(), /* The plain text */
(unsigned long) packet->data().length(), /* Plain text length */
(uint8_t *) target_buffer, /* The result buffer */
(uint8_t *) tag_buffer,
(unsigned long *) &tag_length
)) != CRYPT_OK){
error = "eax_encrypt_authenticate_memory(...) returned " + to_string(err) + "/" + error_to_string(err);
return false;
}
assert(tag_length == 8);
packet->data(pipes::buffer_view{target_buffer, length});
packet->mac().write(tag_buffer, tag_length);
packet->setEncrypted(true);
return true;
}
bool CryptionHandler::progressPacketIn(protocol::BasicPacket* packet, std::string& error, bool use_default) {
while(blocked)
this_thread::sleep_for(chrono::microseconds(100));
if(packet->isEncrypted()){
bool success = decryptPacket(packet, error, use_default);
if(success) packet->setEncrypted(false);
return success;
}
return true;
}
bool CryptionHandler::progressPacketOut(protocol::BasicPacket* packet, std::string& error, bool use_default) {
while(blocked)
this_thread::sleep_for(chrono::microseconds(100));
if(packet->hasFlag(PacketFlag::Unencrypted)) {
packet->mac().write(this->current_mac, 8);
} else {
bool success = encryptPacket(packet, error, use_default);
if(success) packet->setEncrypted(true);
return success;
}
return true;
}
+74
View File
@@ -0,0 +1,74 @@
#pragma once
#include <array>
#include <string>
#include <tomcrypt.h>
#include "Packet.h"
namespace ts {
namespace connection {
class CryptionHandler {
enum Methode {
TEAMSPEAK_3_1,
TEAMSPEAK_3
};
struct KeyCache {
uint16_t generation = 0xFFEF;
uint8_t key[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
uint8_t nonce[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
};
public:
CryptionHandler();
~CryptionHandler();
void reset();
//TeamSpeak old
bool setupSharedSecret(const std::string& alpha, const std::string& beta, ecc_key* publicKey, ecc_key* ownKey, std::string &error);
bool setupSharedSecret(const std::string& alpha, const std::string& beta, std::string sharedKey, std::string &error);
//TeamSpeak new
bool setupSharedSecretNew(const std::string& alpha, const std::string& beta, const char privateKey[32], const char publicKey[32]);
bool progressPacketOut(protocol::BasicPacket*, std::string&, bool use_default);
bool progressPacketIn(protocol::BasicPacket*, std::string&, bool use_default);
bool verify_encryption(const pipes::buffer_view& data, uint16_t packet_id, uint16_t generation);
bool block(){ blocked = true; return true; }
bool unblock(){ blocked = false; return true; }
bool isBlocked(){ return blocked; }
bool use_default() { return this->useDefaultChipherKeyNonce; }
private:
static constexpr char default_key[16] = {'c', ':', '\\', 'w', 'i', 'n', 'd', 'o', 'w', 's', '\\', 's', 'y', 's', 't', 'e'}; //c:\windows\syste
static constexpr char default_nonce[16] = {'m', '\\', 'f', 'i', 'r', 'e', 'w', 'a', 'l', 'l', '3', '2', '.', 'c', 'p', 'l'}; //m\firewall32.cpl
static constexpr char default_mac[8] = {'T', 'S', '3', 'I', 'N', 'I', 'T', '1'}; //TS3INIT1
bool decryptPacket(protocol::BasicPacket *, std::string &, bool use_default);
bool encryptPacket(protocol::BasicPacket *, std::string &, bool use_default);
bool generate_key_nonce(bool /* to server */, protocol::PacketType /* type */, uint16_t /* packet id */, uint16_t /* generation */, bool /* use default */, uint8_t(&)[16] /* key */, uint8_t(&)[16] /* nonce */);
bool generate_key_nonce(protocol::BasicPacket* packet, bool use_default, uint8_t(&)[16] /* key */, uint8_t(&)[16] /* nonce */);
//The default key and nonce
bool useDefaultChipherKeyNonce = true;
bool blocked = false;
/* for the old protocol SHA1 length for the new 64 bytes */
uint8_t iv_struct[64];
uint8_t iv_struct_length = 0;
uint8_t current_mac[8];
std::mutex cache_key_lock;
std::array<KeyCache, protocol::PACKET_MAX> cache_key_client;
std::array<KeyCache, protocol::PACKET_MAX> cache_key_server;
static_assert(sizeof(current_mac) == sizeof(default_mac), "invalid mac");
static_assert(sizeof(iv_struct) == 64, "invalid iv struct");
};
}
}
+233
View File
@@ -0,0 +1,233 @@
//
// Created by wolverindev on 07.10.17.
//
#include <cstring>
#include <iostream>
#include <bitset>
#include "Packet.h"
#include "buffers.h"
#include "misc/endianness.h"
using namespace std;
namespace ts {
namespace protocol {
PacketTypeInfo::PacketTypeInfo(std::string name, PacketType type, bool ack, int max_length) noexcept {
this->data = new PacketTypeProperties{name, type, max_length, ack};
if(type < 0x0F)
types.insert({type, *this});
}
PacketTypeInfo::PacketTypeInfo(PacketTypeInfo &red) : data(red.data) { }
PacketTypeInfo::PacketTypeInfo(const PacketTypeInfo &red) : data(red.data) { }
std::map<int, PacketTypeInfo> PacketTypeInfo::types;
PacketTypeInfo PacketTypeInfo::fromid(int id) {
for(auto elm : types)
if(elm.first == id) return elm.second;
return PacketTypeInfo::Undefined;
}
PacketTypeInfo PacketTypeInfo::Voice = {"Voice", PacketType::VOICE, false, 1024};
PacketTypeInfo PacketTypeInfo::VoiceWhisper = {"VoiceWhisper", PacketType::VOICE_WHISPER, false, 1024};
PacketTypeInfo PacketTypeInfo::Command = {"Command", PacketType::COMMAND, true, 487};
PacketTypeInfo PacketTypeInfo::CommandLow = {"CommandLow", PacketType::COMMAND_LOW, true, 487};
PacketTypeInfo PacketTypeInfo::Ping = {"Ping", PacketType::PING, false, 1024};
PacketTypeInfo PacketTypeInfo::Pong = {"Pong", PacketType::PONG, false, 1024};
PacketTypeInfo PacketTypeInfo::Ack = {"Ack", PacketType::ACK, false, 1024};
PacketTypeInfo PacketTypeInfo::AckLow = {"AckLow", PacketType::ACK_LOW, false, 1024};
PacketTypeInfo PacketTypeInfo::Init1 = {"Init1", PacketType::INIT1, false, 1024};
PacketTypeInfo PacketTypeInfo::Undefined = {"Undefined", PacketType::UNDEFINED, false, 1024};
namespace PacketFlag {
std::string to_string(PacketFlag flag){
switch(flag){
case Fragmented:
return "Fragmented";
case NewProtocol:
return "NewProtocol";
case Compressed:
return "Compressed";
case Unencrypted:
return "Unencrypted";
default:
return "None";
}
}
}
BasicPacket::BasicPacket(size_t header_length, size_t data_length) {
this->_header_length = header_length;
this->_buffer = pipes::buffer(MAC_SIZE + this->_header_length + data_length);
memset(this->_buffer.data_ptr(), 0, this->_buffer.length());
}
BasicPacket::BasicPacket(size_t header_length, const pipes::buffer &buffer) {
this->_header_length = header_length;
this->_buffer = buffer;
}
BasicPacket::~BasicPacket() {}
void BasicPacket::append_data(const std::vector<pipes::buffer> &data) {
size_t length = 0;
for(const auto& buffer : data)
length += buffer.length();
/* we've to allocate a new buffer because out buffer is fixed in size */
size_t index = this->_buffer.length();
auto new_buffer = buffer::allocate_buffer(length + index);
new_buffer.write(this->_buffer, index);
for(const auto& buffer : data) {
new_buffer.write(buffer, buffer.length(), index);
index += buffer.length();
}
this->_buffer = new_buffer;
}
std::string BasicPacket::flags() const {
std::string result;
if(hasFlag(PacketFlag::Unencrypted)) result += string(result.empty() ? "" : " | ") + "Unencrypted";
if(hasFlag(PacketFlag::Compressed)) result += string(result.empty() ? "" : " | ") + "Compressed";
if(hasFlag(PacketFlag::Fragmented)) result += string(result.empty() ? "" : " | ") + "Fragmented";
if(hasFlag(PacketFlag::NewProtocol)) result += string(result.empty() ? "" : " | ") + "NewProtocol";
if(result.empty()) result = "none";
return result;
}
void BasicPacket::applyPacketId(PacketIdManager& manager) {
this->applyPacketId(manager.nextPacketId(this->type()), manager.generationId(this->type()));
}
void BasicPacket::applyPacketId(uint16_t packetId, uint16_t generationId) {
if(this->memory_state.id_branded)
throw std::logic_error("Packet already got a packet id!");
this->memory_state.id_branded = true;
this->setPacketId(packetId, generationId);
}
Command BasicPacket::asCommand() {
return Command::parse(this->data());
}
/**
* @param buffer -> [mac][Header [uint16 BE packetId | [uint8](4bit flags | 4bit type)]][Data]
* @return
*/
ServerPacket::ServerPacket(const pipes::buffer_view& buffer) : BasicPacket(SERVER_HEADER_SIZE, buffer.own_buffer()) { }
ServerPacket::ServerPacket(uint8_t flagMask, const pipes::buffer_view& data) : BasicPacket(SERVER_HEADER_SIZE, data.length()) {
this->header()[2] = flagMask;
memcpy(this->data().data_ptr(), data.data_ptr(), data.length());
}
ServerPacket::ServerPacket(PacketTypeInfo type, const pipes::buffer_view& data) : BasicPacket(SERVER_HEADER_SIZE, data.length()) {
this->header()[2] |= type.type();
memcpy(this->data().data_ptr(), data.data_ptr(), data.length());
}
ServerPacket::~ServerPacket() {}
uint16_t ServerPacket::packetId() const {
return be2le16(&this->header()[0]);
}
void ServerPacket::setPacketId(uint16_t pkId, uint16_t gen) {
le2be16(pkId, &this->header()[0]);
this->genId = gen;
}
uint16_t ServerPacket::generationId() const {
return this->genId;
}
PacketTypeInfo ServerPacket::type() const {
return PacketTypeInfo::fromid(this->header()[2] & 0xF);
}
uint8_t ServerPacket::flagMask() const {
return this->header()[2];
}
bool ServerPacket::hasFlag(PacketFlag::PacketFlag flag) const {
return (this->header()[2] & flag) > 0;
}
void ServerPacket::toggle(PacketFlag::PacketFlag flag, bool state) {
if(state){
this->header()[2] |= flag;
} else {
this->header()[2] &= ~flag;
}
}
ClientPacket::ClientPacket(const pipes::buffer_view& buffer) : BasicPacket(CLIENT_HEADER_SIZE, buffer.own_buffer()) { }
ClientPacket::ClientPacket(const PacketTypeInfo &type, const pipes::buffer_view& data) : BasicPacket(CLIENT_HEADER_SIZE, data.length()) {
this->header()[4] = type.type() & 0xF;
memcpy(this->data().data_ptr(), data.data_ptr(), data.length());
}
ClientPacket::ClientPacket(const PacketTypeInfo &type, uint8_t flag_mask, const pipes::buffer_view& data) : ClientPacket(type, data) {
this->header()[4] |= flag_mask;
}
ClientPacket::~ClientPacket() {}
uint16_t ClientPacket::packetId() const {
return be2le16(&this->header()[0]);
}
uint16_t ClientPacket::generationId() const {
return this->genId;
}
PacketTypeInfo ClientPacket::type() const {
return PacketTypeInfo::fromid(this->header()[4] & 0xF);
}
void ClientPacket::type(const ts::protocol::PacketTypeInfo &type) {
auto& field = this->header().data_ptr<uint8_t>()[4];
field &= ~0xF;
field |= type.type();
}
bool ClientPacket::hasFlag(PacketFlag::PacketFlag flag) const {
return (this->header()[4] & flag) > 0;
}
uint8_t ClientPacket::flagMask() const {
return this->header()[4];
}
void ClientPacket::toggle(PacketFlag::PacketFlag flag, bool state) {
if(state){
this->header()[4] |= flag;
} else {
this->header()[4] &= ~flag;
}
}
void ClientPacket::setPacketId(uint16_t pkId, uint16_t gen) {
this->header()[0] = (uint8_t) ((pkId >> 8) & 0xFF);
this->header()[1] = (uint8_t) ((pkId >> 0) & 0xFF);
this->genId = gen;
}
uint16_t ClientPacket::clientId() const {
return be2le16(&this->header()[2]);
}
void ClientPacket::clientId(uint16_t clId) {
this->header()[2] = clId >> 8;
this->header()[3] = clId & 0xFF;
}
}
}
+290
View File
@@ -0,0 +1,290 @@
#pragma once
#include <cstring>
#include <string>
#include <map>
#include <utility>
#include <ThreadPool/Future.h>
#include <pipes/buffer.h>
#include "../query/Command.h"
namespace ts {
namespace protocol {
enum PacketType : uint8_t {
VOICE = 0x00,
VOICE_WHISPER = 0x01,
COMMAND = 0x02,
COMMAND_LOW = 0x03,
PING = 0x04,
PONG = 0x05,
ACK = 0x06,
ACK_LOW = 0x07,
INIT1 = 0x08,
PACKET_MAX = INIT1,
UNDEFINED = 0xFF
};
struct PacketTypeProperties {
std::string name;
PacketType type;
int max_length;
bool requireAcknowledge;
};
class PacketTypeInfo {
public:
static PacketTypeInfo Voice;
static PacketTypeInfo VoiceWhisper;
static PacketTypeInfo Command;
static PacketTypeInfo CommandLow;
static PacketTypeInfo Ping;
static PacketTypeInfo Pong;
static PacketTypeInfo Ack;
static PacketTypeInfo AckLow;
static PacketTypeInfo Init1;
static PacketTypeInfo Undefined;
static PacketTypeInfo fromid(int id);
std::string name() const { return data->name; }
PacketType type() const { return data->type; }
bool requireAcknowledge(){ return data->requireAcknowledge; }
bool operator==(const PacketTypeInfo& other) const {
return other.data->type == this->data->type;
}
bool operator!=(const PacketTypeInfo& other){
return other.data->type != this->data->type;
}
int max_length() const { return data->max_length; }
inline bool fragmentable() { return *this == PacketTypeInfo::Command || *this == PacketTypeInfo::CommandLow; }
inline bool compressable() { return *this == PacketTypeInfo::Command || *this == PacketTypeInfo::CommandLow; }
PacketTypeInfo(PacketTypeInfo&);
PacketTypeInfo(const PacketTypeInfo&);
PacketTypeInfo(PacketTypeInfo&& remote) : data(remote.data) {}
private:
static std::map<int, PacketTypeInfo> types;
PacketTypeInfo(std::string, PacketType, bool, int) noexcept;
PacketTypeProperties* data;
};
struct PacketIdManagerData {
PacketIdManagerData(){
memset(this->packetCounter, 0, sizeof(uint32_t) * 16);
}
uint32_t packetCounter[16]{};
};
class PacketIdManager {
public:
PacketIdManager() : data(new PacketIdManagerData){}
~PacketIdManager() = default;
PacketIdManager(const PacketIdManager& ref) : data(ref.data) {}
PacketIdManager(PacketIdManager& ref) : data(ref.data) {}
uint16_t nextPacketId(const PacketTypeInfo &type){
return static_cast<uint16_t>(data->packetCounter[type.type()]++ & 0xFFFF);
}
uint16_t currentPacketId(const PacketTypeInfo &type){
return static_cast<uint16_t>(data->packetCounter[type.type()] & 0xFFFF);
}
uint16_t generationId(const PacketTypeInfo &type){
return static_cast<uint16_t>((data->packetCounter[type.type()] >> 16) & 0xFFFF);
}
void reset() {
memset(&data->packetCounter[0], 0, sizeof(uint32_t) * 16);
}
private:
std::shared_ptr<PacketIdManagerData> data;
};
namespace PacketFlag {
enum PacketFlag {
None = 0x00,
Fragmented = 0x10, //If packet type voice then its toggle the CELT Mono
NewProtocol = 0x20,
Compressed = 0x40, //If packet type voice than its the header
Unencrypted = 0x80
};
std::string to_string(PacketFlag flag);
}
#define MAC_SIZE 8
#define SERVER_HEADER_SIZE 3
#define CLIENT_HEADER_SIZE 5
class BasicPacket {
public:
explicit BasicPacket(size_t header_length, size_t data_length);
explicit BasicPacket(size_t header_length, const pipes::buffer& buffer);
virtual ~BasicPacket();
BasicPacket(const BasicPacket&) = delete;
BasicPacket(BasicPacket&&) = delete;
virtual uint16_t packetId() const = 0;
virtual uint16_t generationId() const = 0;
virtual PacketTypeInfo type() const = 0;
virtual bool hasFlag(PacketFlag::PacketFlag flag) const = 0;
virtual uint8_t flagMask() const = 0;
virtual std::string flags() const;
virtual void enableFlag(PacketFlag::PacketFlag flag){ toggle(flag, true); }
virtual void toggle(PacketFlag::PacketFlag flag, bool state) = 0;
virtual void applyPacketId(PacketIdManager &);
virtual void applyPacketId(uint16_t, uint16_t);
void setListener(std::unique_ptr<threads::Future<bool>> listener){
if(!this->type().requireAcknowledge())
throw std::logic_error("Packet type does not support a acknowledge listener!");
this->listener = std::move(listener);
}
inline std::unique_ptr<threads::Future<bool>>& getListener() { return this->listener; }
inline pipes::buffer_view mac() const { return this->_buffer.view(0, MAC_SIZE); }
inline pipes::buffer mac() { return this->_buffer.range(0, MAC_SIZE); }
inline pipes::buffer_view header() const { return this->_buffer.view(MAC_SIZE, this->_header_length); }
inline pipes::buffer header() { return this->_buffer.range(MAC_SIZE, this->_header_length); }
inline size_t data_length() const { return this->_buffer.length() - MAC_SIZE - this->_header_length; }
inline pipes::buffer_view data() const { return this->_buffer.view(MAC_SIZE + this->_header_length); }
inline pipes::buffer data() { return this->_buffer.range(MAC_SIZE + this->_header_length); }
void append_data(const std::vector<pipes::buffer> &data);
inline void data(const pipes::buffer_view &data){
this->_buffer.resize(MAC_SIZE + this->_header_length + data.length());
memcpy((char*) this->_buffer.data_ptr() + MAC_SIZE + this->_header_length, data.data_ptr(), data.length());
}
inline void mac(const pipes::buffer_view &_new){
assert(_new.length() >= MAC_SIZE);
memcpy(this->_buffer.data_ptr(), _new.data_ptr(), MAC_SIZE);
}
inline bool isEncrypted() const { return this->memory_state.encrypted; }
inline void setEncrypted(bool flag){ this->memory_state.encrypted = flag; }
inline bool isCompressed() const { return this->memory_state.compressed; }
inline void setCompressed(bool flag){ this->memory_state.compressed = flag; }
inline bool isFragmentEntry() const { return this->memory_state.fragment_entry; }
inline void setFragmentedEntry(bool flag){ this->memory_state.fragment_entry = flag; }
Command asCommand();
//Has the size of a byte
union {
#ifdef WIN32
__pragma(pack(push, 1))
#endif
struct {
bool encrypted: 1;
bool compressed: 1;
bool fragment_entry: 1;
bool id_branded: 1;
}
#ifdef WIN32
__pragma(pack(pop));
#else
__attribute__((packed));
#endif
uint8_t flags = 0;
} memory_state;
pipes::buffer buffer() { return this->_buffer; }
void buffer(pipes::buffer buffer) { this->_buffer = std::move(buffer); }
protected:
virtual void setPacketId(uint16_t, uint16_t) = 0;
uint8_t _header_length;
pipes::buffer _buffer;
//std::unique_ptr<uint8_t, decltype(::free)*> buffer;
/*
std::string _header;
std::string _data;
std::string _mac;
*/
uint16_t genId = 0;
std::unique_ptr<threads::Future<bool>> listener;
};
/**
* Packet from the client
*/
class ClientPacket : public BasicPacket {
public:
static constexpr size_t META_MAC_SIZE = 8;
static constexpr size_t META_HEADER_SIZE = CLIENT_HEADER_SIZE;
static constexpr size_t META_SIZE = META_MAC_SIZE + META_HEADER_SIZE;
explicit ClientPacket(const pipes::buffer_view& buffer);
ClientPacket(const PacketTypeInfo& type, const pipes::buffer_view& data);
ClientPacket(const PacketTypeInfo& type, uint8_t flag_mask, const pipes::buffer_view& data);
~ClientPacket() override;
ClientPacket(const ClientPacket&) = delete;
ClientPacket(ClientPacket&&) = delete;
void toggle(PacketFlag::PacketFlag flag, bool state) override;
uint16_t clientId() const;
void clientId(uint16_t);
uint16_t packetId() const override;
uint16_t generationId() const override;
void generationId(uint16_t generation) { this->genId = generation; }
PacketTypeInfo type() const override;
void type(const PacketTypeInfo&);
bool hasFlag(PacketFlag::PacketFlag flag) const override;
uint8_t flagMask() const override;
private:
void setPacketId(uint16_t, uint16_t) override;
};
/**
* Packet from the server
*/
class ServerPacket : public BasicPacket {
public:
static constexpr size_t META_MAC_SIZE = 8;
static constexpr size_t META_HEADER_SIZE = SERVER_HEADER_SIZE;
static constexpr size_t META_SIZE = META_MAC_SIZE + META_HEADER_SIZE;
explicit ServerPacket(const pipes::buffer_view& buffer);
ServerPacket(uint8_t flagMask, const pipes::buffer_view& data);
ServerPacket(PacketTypeInfo type, const pipes::buffer_view& data);
~ServerPacket() override;
ServerPacket(const ServerPacket&) = delete;
ServerPacket(ServerPacket&&) = delete;
uint16_t packetId() const override;
uint16_t generationId() const override;
void generationId(uint16_t generation) { this->genId = generation; }
PacketTypeInfo type() const override;
bool hasFlag(PacketFlag::PacketFlag flag) const override;
uint8_t flagMask() const override;
void toggle(PacketFlag::PacketFlag flag, bool state) override;
private:
void setPacketId(uint16_t, uint16_t) override;
};
}
}
+18
View File
@@ -0,0 +1,18 @@
#include "buffers.h"
using namespace std;
using namespace ts;
using namespace ts::protocol;
using namespace ts::buffer;
#pragma GCC optimize ("O3")
meminfo buffer::buffer_memory() {
size_t bytes_buffer = 0;
size_t bytes_buffer_used = 0;
size_t bytes_internal = 0;
size_t nodes = 0;
size_t nodes_full = 0;
return {bytes_buffer, bytes_buffer_used, bytes_internal, nodes, nodes_full};
}
+269
View File
@@ -0,0 +1,269 @@
#pragma once
#include <chrono>
#include <memory>
#include <list>
#include <cstring>
#include <ThreadPool/Mutex.h>
#include <sstream>
#include "Packet.h"
#include "../misc/queue.h"
#include <cassert>
#include <utility>
#ifndef NO_LOG
#include <log/LogUtils.h>
#endif
namespace ts {
namespace buffer {
struct RawBuffer {
public:
RawBuffer() : RawBuffer(0) {}
RawBuffer(size_t length) : index(0), length(length) {
if(length > 0) buffer = (char *) malloc(length);
else buffer = nullptr;
this->length = length;
this->index = 0;
}
RawBuffer(const RawBuffer &other) : RawBuffer(other.length) {
if(other.length > 0) memcpy(this->buffer, other.buffer, this->length);
this->index = other.index;
}
virtual ~RawBuffer() {
if(buffer)
free(buffer);
this->buffer = nullptr;
}
void slice(size_t length) {
char *oldBuff = this->buffer;
this->buffer = (char *) malloc(length);
memcpy(this->buffer, oldBuff, length);
this->length = length;
free(oldBuff);
}
char *buffer = nullptr;
size_t length = 0;
size_t index = 0;
TAILQ_ENTRY(ts::buffer::RawBuffer) tail;
};
template <typename PktType>
struct SortedBufferQueue {
SortedBufferQueue(ts::protocol::PacketTypeInfo type, bool ignoreOrder) : _type(std::move(type)), ignoreOrder(ignoreOrder) {
this->current.index = 0;
}
SortedBufferQueue(const SortedBufferQueue &ref) = delete;
SortedBufferQueue(SortedBufferQueue&&ref) = delete;
~SortedBufferQueue() = default;
ts::protocol::PacketTypeInfo type() { return this->_type; }
void skipPacket(){
threads::MutexLock lock(this->lock);
this->current.index++;
}
size_t available(){
threads::MutexLock lock(this->lock);
if(this->ignoreOrder) return this->packets.size();
uint16_t index = 0;
while(true) {
if(!this->find_packet(this->current.index + index))
return index;
else
index++;
}
}
std::shared_ptr<PktType> find_packet(uint32_t pktId){
pktId &= 0xFFFF;
threads::MutexLock lock(this->lock);
for(const auto& elm : this->packets)
if(elm->packetId() == pktId)
return elm;
return nullptr;
}
std::shared_ptr<PktType> peekNext(uint32_t index) {
threads::MutexLock lock(this->lock);
if(this->ignoreOrder) {
if(this->packets.size() > index)
return this->packets[index];
else
return nullptr;
}
return this->find_packet(this->current.index + index);
}
void pop_packets(int32_t count = -1) {
if(count == -1) count = 1;
threads::MutexLock lock(this->lock);
if(this->ignoreOrder) {
while(count-- > 0 && !this->packets.empty()) this->packets.pop_front();
return;
}
auto until = this->current.index + count;
while(this->current.index < until) {
for(auto it = this->packets.begin(); it != this->packets.end(); it++) {
if((*it)->packetId() == this->current.packet_id) {
this->packets.erase(it);
break;
}
}
this->current.index++;
}
}
bool push_pack(const std::shared_ptr<PktType>& pkt){
threads::MutexLock lock(this->lock);
if(this->ignoreOrder) {
this->packets.push_back(pkt);
if(this->current.packet_id > pkt->packetId()) {
if(this->current.packet_id > 0xFF00 && pkt->packetId() < 0xFF) {
this->current.packet_id = pkt->packetId();
this->current.generation++;
}
} else this->current.packet_id = pkt->packetId();
return true;
}
if(this->current.packet_id > pkt->packetId()) {
if(this->current.packet_id < 0xFF00 || pkt->packetId() > 0xFF) {
#ifndef NO_LOG
debugMessage(0, "Invalid packed pushpack! Current index {} (generation {}) Packet index {}", this->current.packet_id, this->current.generation, pkt->packetId());
#endif
return false;
}
}
this->packets.push_back(pkt);
return true;
}
void reset(){
this->current.index = 0;
}
std::unique_lock<std::recursive_mutex> try_lock_queue() {
threads::MutexTryLock lock(this->lock);
if(!lock) return {};
return std::unique_lock(this->lock);
}
std::unique_lock<std::recursive_mutex> try_lock_execute() {
threads::MutexTryLock lock(this->execute_lock);
if(!lock) return {};
return std::unique_lock(this->execute_lock);
}
uint16_t current_packet_id() { return this->current.packet_id; }
uint16_t current_generation_id() { return this->current.generation; }
uint16_t calculate_generation(uint16_t packetId) {
if(packetId >= this->current.packet_id) return this->current.generation;
if(packetId < 0xFF && this->current.packet_id > 0xFF00)
return this->current.generation + 1;
return this->current.generation;
}
union PacketPair {
uint32_t index;
struct {
uint16_t packet_id;
uint16_t generation;
};
};
PacketPair current{0};
private:
ts::protocol::PacketTypeInfo _type;
bool ignoreOrder = false;
std::deque<std::shared_ptr<PktType>> packets{};
std::recursive_mutex lock;
std::recursive_mutex execute_lock;
};
struct size {
enum value : uint8_t {
unset,
min,
Bytes_512 = min,
Bytes_1024,
Bytes_1536,
max
};
static inline size_t byte_length(value size) {
switch (size) {
case Bytes_512:
return 512;
case Bytes_1024:
return 1024;
case Bytes_1536:
return 1536;
default:
return 0;
}
}
};
//typedef std::unique_ptr<pipes::buffer, void(*)(pipes::buffer*)> buffer_t;
typedef pipes::buffer buffer_t;
extern buffer_t allocate_buffer(size::value /* size */);
inline buffer_t allocate_buffer(size_t length) {
pipes::buffer result;
if(length <= 512)
result = allocate_buffer(size::Bytes_512);
else if(length <= 1024)
result = allocate_buffer(size::Bytes_1024);
else if(length <= 1536)
result = allocate_buffer(size::Bytes_1536);
else {
return pipes::buffer{length};
}
result.resize(length);
return result;
}
struct cleaninfo {
size_t bytes_freed_internal;
size_t bytes_freed_buffer;
};
struct cleanmode {
enum value {
CHUNKS = 0x01,
BLOCKS = 0x02,
CHUNKS_BLOCKS = 0x03
};
};
extern cleaninfo cleanup_buffers(cleanmode::value /* mode */);
struct meminfo {
size_t bytes_buffer = 0;
size_t bytes_buffer_used = 0;
size_t bytes_internal = 0;
size_t nodes = 0;
size_t nodes_full = 0;
};
extern meminfo buffer_memory();
}
}
+392
View File
@@ -0,0 +1,392 @@
#include "buffers.h"
#include <unistd.h>
using namespace std;
using namespace ts;
using namespace ts::protocol;
using namespace ts::buffer;
#pragma GCC optimize ("O3")
#define BLOCK_BUFFER_MASK 0xFFFFFFFF
#define BLOCK_BUFFERS 32
#define MEM_BUFFER_MAGIC 0xABCD
#define MEM_BLOCK_MAGIC 0xCDEF
class spin_lock {
std::atomic_flag locked = ATOMIC_FLAG_INIT;
public:
void lock() {
uint8_t round = 0;
while (locked.test_and_set(std::memory_order_acquire)) {
//Yield when we're using this lock for a longer time, which we usually not doing
if(round++ % 8 == 0)
std::this_thread::yield();
}
}
inline bool try_lock() {
return !locked.test_and_set(std::memory_order_acquire);
}
void unlock() {
locked.clear(std::memory_order_release);
}
};
typedef spin_lock fast_lock_t;
#pragma pack(push, 1)
struct buffer_entry_head {
#ifdef MEM_BUFFER_MAGIC
uint16_t magic;
#endif
uint32_t base_offset : 24;
uint8_t base_index : 8;
};
struct buffer_entry : buffer_entry_head {
char buffer[0];
};
struct block_chain;
/* 32 buffers/block */
struct buffer_block {
#ifdef MEM_BLOCK_MAGIC
uint16_t magic;
#endif
uint8_t block_index; /* block index within the chain */
block_chain* chain_entry;
fast_lock_t block_lock{};
size::value type = size::unset;
union {
uint32_t flag_free = 0;
uint8_t flag_used8[4];
};
buffer_entry buffers[0];
};
#pragma pack(pop)
struct block_chain {
uint8_t type_index[buffer::size::max - buffer::size::min];
block_chain* previous = nullptr;
block_chain* next = nullptr;
uint8_t block_count = 0;
buffer_block* blocks[0];
};
fast_lock_t chain_lock;
block_chain* chain_head = nullptr;
inline void destroy_block(buffer_block* block) {
block->block_lock.~fast_lock_t();
free(block);
}
inline buffer_block* allocate_block(size::value type) {
auto base_size = sizeof(buffer_block);
auto buffer_size = size::byte_length(type);
auto base_entry_size = sizeof(buffer_entry) + buffer_size;
auto size = base_size + BLOCK_BUFFERS * base_entry_size;
auto block = (buffer_block*) malloc(size);
new (&block->block_lock) fast_lock_t(); /* initialize spin lock */
#ifdef MEM_BLOCK_MAGIC
block->magic = MEM_BLOCK_MAGIC;
#endif
block->type = type;
for(uint8_t index = 0; index < BLOCK_BUFFERS; index++) {
auto entry_ptr = (uintptr_t) block->buffers + index * (uintptr_t) base_entry_size;
((buffer_entry*) entry_ptr)->base_offset = index * (uintptr_t) base_entry_size + sizeof(buffer_block);
((buffer_entry*) entry_ptr)->base_index = index;
}
block->flag_free = BLOCK_BUFFER_MASK;
return block;
}
inline void destroy_chain_entry(block_chain* chain) {
free(chain);
}
inline block_chain* allocate_chain_entry(uint8_t entries) {
auto base_size = sizeof(block_chain);
auto chain = (block_chain*) malloc(base_size + sizeof(void*) * entries);
chain->next = nullptr;
chain->previous = nullptr;
chain->block_count = entries;
for(auto& index : chain->type_index)
index = 0;
for(uint8_t index = 0; index < entries; index++)
chain->blocks[index] = nullptr;
return chain;
}
struct BufferDeallocator {
bool operator()(void* buffer) {
buffer::release_buffer(buffer);
return true;
}
};
struct BufferAllocator {
bool operator()(size_t& /* length */, void*& /* result ptr */) {
__throw_logic_error("Cant reallocate a fixed buffer");
}
};
buffer_t buffer::allocate_buffer(size::value size) {
return pipes::buffer{size};
fast_lock_t* block_lock = nullptr;
buffer_block* block;
{
block_chain* tmp_chain;
lock_guard lock_chain(chain_lock);
auto head = chain_head;
auto type_index = (size::value) (size - size::min);
iterate_head:
while(head) {
uint8_t& index = head->type_index[type_index];
while(index < head->block_count) {
auto entry = head->blocks[index];
if(entry) {
if(entry->type != size || (entry->flag_free & BLOCK_BUFFER_MASK) == 0)
goto next_block;
block_lock = &entry->block_lock;
if(!block_lock->try_lock() || (entry->flag_free & BLOCK_BUFFER_MASK) == 0) {
block_lock->unlock();
goto next_block;
}
block = entry;
/* we've found an entry with a free block */
goto break_head_loop;
} else {
/* lets insert a new block */
head->blocks[index] = (entry = allocate_block(size));
entry->chain_entry = head;
entry->block_index = index;
block_lock = &entry->block_lock;
block_lock->lock();
block = entry;
/* we've a new block which has to have free slots */
goto break_head_loop;
}
next_block:
index++;
}
if(!head->next)
break;
head = head->next;
}
tmp_chain = allocate_chain_entry(128);
tmp_chain->previous = head;
if(!head) { /* we've to create a chain head */
chain_head = (head = tmp_chain);
} else { /* we've to append a new entry */
head = (head->next = tmp_chain);
}
goto iterate_head;
/* insert new entry */
break_head_loop:;
}
auto index = __builtin_ctz(block->flag_free);
block->flag_free &= ~(1 << index);
block_lock->unlock();
auto buffer_size = size::byte_length(size);
auto buffer_entry_size = buffer_size + sizeof(buffer_entry_head);
auto entry = (buffer_entry_head*) ((uintptr_t) block->buffers + (uintptr_t) buffer_entry_size * index);
#ifdef MEM_BUFFER_MAGIC
entry->magic = MEM_BUFFER_MAGIC;
#endif
return pipes::buffer{(void*) entry, buffer_entry_size, false, BufferAllocator(), BufferDeallocator()}.range(sizeof(buffer_entry_head));
}
inline bool valid_buffer_size(size_t size) {
return size == 512 || size == 1024 || size == 1536;
}
void buffer::release_buffer(void *buffer) {
return;
auto head = (buffer_entry_head*) buffer;
#ifdef MEM_BUFFER_MAGIC
assert(head->magic == MEM_BUFFER_MAGIC);
#endif
auto block = (buffer_block*) ((uintptr_t) head - (uintptr_t) head->base_offset);
#ifdef MEM_BLOCK_MAGIC
assert(block->magic == MEM_BLOCK_MAGIC);
#endif
block->flag_free |= (1 << head->base_index); /* set the slot free flag */
auto type_index = (size::value) (block->type - size::min);
auto& index = block->chain_entry->type_index[type_index];
if(index > block->block_index)
index = block->block_index;
}
void buffer::release_buffer(pipes::buffer *buffer) {
assert(buffer->capacity_origin() > sizeof(buffer_entry_head));
assert(valid_buffer_size(buffer->capacity_origin() - sizeof(buffer_entry_head)));
release_buffer(buffer->data_ptr_origin());
delete buffer;
}
meminfo buffer::buffer_memory() {
size_t bytes_buffer = 0;
size_t bytes_buffer_used = 0;
size_t bytes_internal = 0;
size_t nodes = 0;
size_t nodes_full = 0;
{
lock_guard lock_chain(chain_lock);
auto head = chain_head;
bool full;
while(head) {
full = true;
nodes++;
bytes_internal += sizeof(chain_head) + sizeof(void*) * head->block_count;
for(uint8_t index = 0; index < head->block_count; index++) {
auto block = head->blocks[index];
if(block) {
bytes_internal += sizeof(buffer_block);
bytes_internal += sizeof(buffer_entry) * BLOCK_BUFFERS;
full = full && (block->flag_free & BLOCK_BUFFER_MASK) == 0;
auto length = size::byte_length(block->type);
bytes_buffer += length * BLOCK_BUFFERS;
bytes_buffer_used += (BLOCK_BUFFERS - __builtin_popcount(block->flag_free & BLOCK_BUFFER_MASK)) * length;
} else
full = false;
}
head = head->next;
}
}
return {bytes_buffer, bytes_buffer_used, bytes_internal, nodes, nodes_full};
}
cleaninfo buffer::cleanup_buffers(cleanmode::value mode) {
std::deque<buffer_block*> orphaned_blocks;
std::deque<block_chain*> orphaned_chunks;
bool flag_blocks = (mode & cleanmode::BLOCKS) > 0;
bool flag_chunks = (mode & cleanmode::CHUNKS) > 0;
{
lock_guard lock_chain(chain_lock);
auto head = chain_head;
uint32_t free_value = BLOCK_BUFFER_MASK;
vector<unique_lock<fast_lock_t>> block_locks;
while(head) {
bool flag_used = false;
if(flag_blocks) {
for(uint8_t index = 0; index < head->block_count; index++) {
auto block = head->blocks[index];
if(!block) continue; /* block isn't set */
if(block->flag_free == free_value) {
lock_guard block_lock(block->block_lock);
if(block->flag_free != free_value) { /* block had been used while locking */
flag_used |= true;
continue;
}
head->blocks[index] = nullptr;
orphaned_blocks.push_back(block);
} else {
flag_used |= true;
}
}
}
if(flag_chunks) {
if(!flag_blocks) { /* we've to calculate flag_used */
block_locks.resize(head->block_count);
for(uint8_t index = 0; index < head->block_count; index++) {
auto block = head->blocks[index];
if(block) {
block_locks[index] = unique_lock{block->block_lock};
if(block->flag_free == free_value) {
/* delete that block later */
continue;
}
flag_used = true;
break;
}
}
}
if(!flag_used) {
if(head->previous)
head->previous->next = head->next;
else if(head == chain_head)
chain_head = head->next;
if(head->next)
head->next->previous = head->previous;
orphaned_chunks.push_back(head);
}
block_locks.clear();
}
head = head->next;
}
}
size_t bytes_internal = 0;
size_t bytes_buffer = 0;
for(auto chain : orphaned_chunks) {
for(uint8_t index = 0; index < chain->block_count; index++) {
if(chain->blocks[index])
orphaned_blocks.push_back(chain->blocks[index]);
}
bytes_internal += sizeof(block_chain);
destroy_chain_entry(chain);
}
for(auto block : orphaned_blocks) {
bytes_buffer += size::byte_length(block->type) * BLOCK_BUFFERS;
bytes_internal += sizeof(buffer_entry_head) * BLOCK_BUFFERS;
bytes_internal += sizeof(buffer_block);
destroy_block(block);
}
return {bytes_internal, bytes_buffer};
}
+179
View File
@@ -0,0 +1,179 @@
#include "buffers.h"
#include <unistd.h>
using namespace std;
using namespace ts;
using namespace ts::protocol;
using namespace ts::buffer;
#pragma GCC optimize ("O3")
class spin_lock {
std::atomic_flag locked = ATOMIC_FLAG_INIT;
public:
void lock() {
uint8_t round = 0;
while (locked.test_and_set(std::memory_order_acquire)) {
//Yield when we're using this lock for a longer time, which we usually not doing
if(round++ % 8 == 0)
std::this_thread::yield();
}
}
inline bool try_lock() {
return !locked.test_and_set(std::memory_order_acquire);
}
void unlock() {
locked.clear(std::memory_order_release);
}
};
typedef spin_lock fast_lock_t;
struct BufferAllocator {
bool operator()(size_t& length, void*& result) {
__throw_logic_error("Cant reallocate a fixed buffer");
}
};
struct Freelist {
struct Buffer {
Buffer* next_buffer;
char data_ptr[0];
};
std::function<bool(void*)> deallocator;
BufferAllocator allocator{};
fast_lock_t lock{};
Buffer* head = nullptr;
Buffer* tail = nullptr;
ssize_t length = 0;
size_t buffer_size;
ssize_t max_length;
#ifdef DEBUG_FREELIST
std::atomic<size_t> extra_alloc = 0;
std::atomic<size_t> extra_free = 0;
std::atomic<size_t> total_alloc = 0;
std::atomic<size_t> total_free = 0;
#endif
pipes::buffer next_buffer() {
#ifdef DEBUG_FREELIST
this->total_alloc++;
#endif
Buffer* buffer = nullptr;
{
lock_guard lock(this->lock);
if(this->head) {
buffer = this->head;
if(buffer == this->tail) {
this->tail = nullptr;
this->head = nullptr;
assert(this->length == 0);
} else {
this->head = buffer->next_buffer;
assert(this->length > 0);
}
this->length--;
}
}
if(!buffer) {
#ifdef DEBUG_FREELIST
this->extra_alloc++;
#endif
buffer = (Buffer*) malloc(sizeof(Buffer) + this->buffer_size);
}
return pipes::buffer{(void*) buffer, this->buffer_size + sizeof(Buffer), false, allocator, deallocator}.range(sizeof(Buffer));
}
bool release_buffer(void* ptr) {
#ifdef DEBUG_FREELIST
this->total_free++;
#endif
auto buffer = (Buffer*) ptr;
if(this->max_length > 0 && this->length > this->max_length) {
/* dont push anymore stuff into the freelist! */
#ifdef DEBUG_FREELIST
this->extra_free++;
#endif
free(ptr);
return true;
}
{
lock_guard lock(this->lock);
if(this->tail) {
this->tail->next_buffer = buffer;
} else {
this->head = buffer;
}
this->tail = buffer;
buffer->next_buffer = nullptr;
this->length++;
}
return true;
}
cleaninfo cleanup() {
size_t buffers_deallocated = 0;
{
lock_guard lock(this->lock);
while(this->head) {
auto buffer = this->head;
this->head = buffer->next_buffer;
free(buffer);
buffers_deallocated++;
}
this->tail = nullptr;
this->head = nullptr;
this->length = 0;
}
return {buffers_deallocated * 8, buffers_deallocated * this->buffer_size};
}
explicit Freelist(size_t size) {
this->buffer_size = size;
this->max_length = 1024 * 1024 * 1024; /* 1GB */
this->deallocator = bind(&Freelist::release_buffer, this, placeholders::_1);
}
};
Freelist freelists[size::max - size::min] = {
Freelist(size::byte_length(size::Bytes_512)),
Freelist(size::byte_length(size::Bytes_1024)),
Freelist(size::byte_length(size::Bytes_1536))
};
buffer_t buffer::allocate_buffer(size::value size) {
assert((size - size::min) > 0 && (size - size::min) < (size::max - size::min));
return freelists[size - size::min].next_buffer();
}
cleaninfo& operator+=(cleaninfo& self, const cleaninfo& other) {
self.bytes_freed_internal += other.bytes_freed_internal;
self.bytes_freed_buffer += other.bytes_freed_buffer;
return self;
}
cleaninfo buffer::cleanup_buffers(cleanmode::value mode) {
cleaninfo info{0, 0};
info += freelists[0].cleanup();
info += freelists[1].cleanup();
info += freelists[2].cleanup();
return info;
}
//cleanup_buffers
//$4 = {bytes_freed_internal = 8389144, bytes_freed_buffer = 536948736}
+16
View File
@@ -0,0 +1,16 @@
#include "buffers.h"
using namespace std;
using namespace ts;
using namespace ts::protocol;
using namespace ts::buffer;
#pragma GCC optimize ("O3")
buffer_t buffer::allocate_buffer(size::value size) {
return pipes::buffer{buffer::size::byte_length(size)};
}
cleaninfo buffer::cleanup_buffers(cleanmode::value mode) {
return {0, 0};
}
+5
View File
@@ -0,0 +1,5 @@
//
// Created by wolverindev on 14.01.19.
//
//#include "ringbuffer.h"
+225
View File
@@ -0,0 +1,225 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <cassert>
#include <utility>
#include <memory>
#include <mutex>
namespace ts {
namespace protocol {
template <typename T>
struct RingEntry {
bool flag_set = false;
T entry{};
};
template <typename E, size_t capacity_t, typename size_type>
class RingBuffer {
public:
static constexpr size_type _max_index = ~((size_type) 0);
RingBuffer() : _capacity(capacity_t) {
this->_ring_index_full = 0;
this->_ring_base_index = 0;
}
inline size_type current_index() { return this->_ring_index; }
/**
* @param index
* @return -1 underflow | 0 success | 1 overflow
*/
inline int accept_index(size_type index) {
size_t relative_index;
if(this->calculate_index(index, relative_index))
return 0;
if(index < this->current_index())
return -1;
return 1;
}
inline bool index_set(size_type index) {
size_t relative_index = 0;
if(!this->calculate_index(index, relative_index))
return false;
return this->index(relative_index).flag_set;
}
inline E& index_value(size_type index) {
size_t relative_index = 0;
if(!this->calculate_index(index, relative_index))
return {};
return this->index(relative_index).entry;
}
inline size_t current_slot() { return this->_ring_base_index; }
inline bool slot_set(size_t slot) {
return this->index(slot).flag_set;
}
inline E& slot_value(size_t slot) {
return this->index(slot).entry;
}
inline bool front_set() { return this->_slots[this->_ring_base_index].flag_set; }
inline E pop_front() {
auto& slot = this->_slots[this->_ring_base_index];
slot.flag_set = false;
auto entry = std::move(slot.entry);
this->_ring_base_index += 1;
this->_ring_index += 1;
if(this->_ring_base_index >= this->_capacity)
this->_ring_base_index -= this->_capacity;
return entry;
}
inline void push_front(E&& entry) {
/* go to the back of the ring and set this as front */
if(this->_ring_base_index == 0)
this->_ring_base_index = (size_type) this->_capacity;
this->_ring_base_index -= 1;
this->_ring_index -= 1;
auto& slot = this->_slots[this->_ring_base_index];
slot.entry = std::forward<E>(entry);
slot.flag_set = 1;
}
inline bool push_back(E&& entry) {
size_t count = 0;
size_t index = this->_ring_base_index;
while(count < this->_capacity) {
if(index >= this->_capacity)
index -= this->_capacity;
auto& slot = this->_slots[index];
if(slot.flag_set) {
count++;
index++;
continue;
}
slot.entry = std::forward<E>(entry);
slot.flag_set = 1;
break;
}
return count != this->_capacity;
}
inline void set_full_index_to(size_type index) {
if(index > this->_ring_index)
this->_ring_index = index;
else if(index < 100 && this->_ring_index > std::numeric_limits<size_type>::max() - 100) {
this->_ring_index += 200; /* let the index overflow into the generation counter */
this->_ring_index = index; /* set the lover (16) bytes */
}
}
inline bool insert_index(size_type index, E&& entry) {
size_t relative_index = 0;
if(!this->calculate_index(index, relative_index))
return false;
auto& slot = this->index(relative_index);
if(slot.flag_set)
return false;
slot.entry = std::forward<E>(entry);
slot.flag_set = true;
return true;
}
inline size_t capacity() { return this->_capacity; }
inline void clear() {
this->_ring_base_index = 0;
for(RingEntry<E>& element : this->_slots) {
element.flag_set = false;
(void) std::move(element.entry);
}
}
inline void reset() {
this->clear();
this->_ring_index_full = 0;
}
protected:
size_t _capacity;
size_t _ring_base_index; /* index of slot 0 */
union {
uint64_t _ring_index_full;
struct {
static_assert(8 - sizeof(size_type) > 0, "Invalid size type!");
uint8_t padding[8 - sizeof(size_type)];
size_type _ring_index; /* index of the insert index | overflow is welcome here */
};
};
std::array<RingEntry<E>, capacity_t> _slots;
inline RingEntry<E>& index(size_t relative_index) {
assert(relative_index < this->_capacity);
auto index = this->_ring_base_index + relative_index;
if(index >= this->_capacity)
index -= this->_capacity;
return this->_slots[index];
}
inline bool calculate_index(size_type index, size_t& relative_index) {
if(this->_ring_index > index) { /* test if index is an overflow of the counter */
if(index >= (size_type) (this->_ring_index + this->_capacity)) /* not anymore in bounds */
return false;
else
relative_index = index + (_max_index - this->_ring_index + 1);
if(relative_index >= this->_capacity)
return false;
} else if(this->_ring_index < index) {
/* check upper bounds */
relative_index = index - this->_ring_index;
if(relative_index >= this->_capacity)
return false;
} else {
/* index is equal, do nothing */
relative_index = 0;
}
return true;
}
};
template <typename E, uint16_t SIZE = 32, typename PTR_TYPE = std::shared_ptr<E>>
class PacketRingBuffer : public RingBuffer<PTR_TYPE, SIZE, uint16_t> {
public:
std::recursive_timed_mutex buffer_lock;
std::recursive_timed_mutex execute_lock;
inline uint16_t generation(uint16_t packet_id) {
size_t relative_index = 0;
if(!this->calculate_index(packet_id, relative_index))
relative_index = 0; /* okey we dont give a fuck */
return ((this->_ring_index_full + relative_index) >> 16) & 0xFFFF;
}
inline void set_generation(uint16_t value) {
this->_ring_index_full = (value << 16) | (this->_ring_index_full & 0xFFFF);
}
inline void set_generation_packet(uint16_t generation, uint16_t packet) {
this->_ring_index_full = (generation << 16) | (packet & 0xFFFF);
}
inline uint64_t full_index() { return this->_ring_index_full; }
};
}
}