This commit is contained in:
2022-11-12 21:49:50 +00:00
commit 3e50a7c132
37 changed files with 3564 additions and 0 deletions

248
src/console.cpp Normal file
View File

@ -0,0 +1,248 @@
#include "std_include.hpp"
#include "console.hpp"
#ifdef _WIN32
#define COLOR_LOG_INFO 11
#define COLOR_LOG_WARN 14
#define COLOR_LOG_ERROR 12
#define COLOR_LOG_DEBUG 15
#else
#define COLOR_LOG_INFO "\033[0;36m"
#define COLOR_LOG_WARN "\033[0;33m"
#define COLOR_LOG_ERROR "\033[0;31m"
#define COLOR_LOG_DEBUG "\033[0m"
#endif
namespace console
{
namespace
{
std::mutex signal_mutex;
std::function<void()> signal_callback;
#ifdef _WIN32
#define COLOR(win, posix) win
using color_type = WORD;
#else
#define COLOR(win, posix) posix
using color_type = const char*;
#endif
const color_type color_array[] =
{
COLOR(0x8, "\033[0;90m"), // 0 - black
COLOR(0xC, "\033[0;91m"), // 1 - red
COLOR(0xA, "\033[0;92m"), // 2 - green
COLOR(0xE, "\033[0;93m"), // 3 - yellow
COLOR(0x9, "\033[0;94m"), // 4 - blue
COLOR(0xB, "\033[0;96m"), // 5 - cyan
COLOR(0xD, "\033[0;95m"), // 6 - pink
COLOR(0xF, "\033[0;97m"), // 7 - white
};
#ifdef _WIN32
BOOL WINAPI handler(const DWORD signal)
{
if (signal == CTRL_C_EVENT && signal_callback)
{
signal_callback();
}
return TRUE;
}
#else
void handler(int signal)
{
if (signal == SIGINT && signal_callback)
{
signal_callback();
}
}
#endif
std::string format(va_list* ap, const char* message)
{
static thread_local char buffer[0x1000];
#ifdef _WIN32
const int count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap);
#else
const int count = vsnprintf(buffer, sizeof(buffer), message, *ap);
#endif
if (count < 0) return {};
return {buffer, static_cast<size_t>(count)};
}
#ifdef _WIN32
HANDLE get_console_handle()
{
return GetStdHandle(STD_OUTPUT_HANDLE);
}
#endif
void set_color(const color_type color)
{
#ifdef _WIN32
SetConsoleTextAttribute(get_console_handle(), color);
#else
printf("%s", color);
#endif
}
bool apply_color(const std::string& data, const size_t index, const color_type base_color)
{
if (data[index] != '^' || (index + 1) >= data.size())
{
return false;
}
auto code = data[index + 1] - '0';
if (code < 0 || code > 11)
{
return false;
}
code = std::min(code, 7); // Everything above white is white
if (code == 7)
{
set_color(base_color);
}
else
{
set_color(color_array[code]);
}
return true;
}
void print_colored(const std::string& line, const color_type base_color)
{
lock _{};
set_color(base_color);
for (size_t i = 0; i < line.size(); ++i)
{
if (apply_color(line, i, base_color))
{
++i;
continue;
}
putchar(line[i]);
}
reset_color();
}
}
lock::lock()
{
#ifdef _WIN32
_lock_file(stdout);
#else
flockfile(stdout);
#endif
}
lock::~lock()
{
#ifdef _WIN32
_unlock_file(stdout);
#else
funlockfile(stdout);
#endif
}
void reset_color()
{
lock _{};
#ifdef _WIN32
SetConsoleTextAttribute(get_console_handle(), 7);
#else
printf("\033[0m");
#endif
fflush(stdout);
}
void info(const char* message, ...)
{
va_list ap;
va_start(ap, message);
const auto data = format(&ap, message);
print_colored("[+] " + data + "\n", COLOR_LOG_INFO);
va_end(ap);
}
void warn(const char* message, ...)
{
va_list ap;
va_start(ap, message);
const auto data = format(&ap, message);
print_colored("[!] " + data + "\n", COLOR_LOG_WARN);
va_end(ap);
}
void error(const char* message, ...)
{
va_list ap;
va_start(ap, message);
const auto data = format(&ap, message);
print_colored("[-] " + data + "\n", COLOR_LOG_ERROR);
va_end(ap);
}
void log(const char* message, ...)
{
va_list ap;
va_start(ap, message);
const auto data = format(&ap, message);
print_colored("[*] " + data + "\n", COLOR_LOG_DEBUG);
va_end(ap);
}
void set_title(const std::string& title)
{
lock _{};
#ifdef _WIN32
SetConsoleTitleA(title.data());
#else
printf("\033]0;%s\007", title.data());
fflush(stdout);
#endif
}
signal_handler::signal_handler(std::function<void()> callback)
: std::lock_guard<std::mutex>(signal_mutex)
{
signal_callback = std::move(callback);
#ifdef _WIN32
SetConsoleCtrlHandler(handler, TRUE);
#else
signal(SIGINT, handler);
#endif
}
signal_handler::~signal_handler()
{
#ifdef _WIN32
SetConsoleCtrlHandler(handler, FALSE);
#else
signal(SIGINT, SIG_DFL);
#endif
signal_callback = {};
}
}

32
src/console.hpp Normal file
View File

@ -0,0 +1,32 @@
#pragma once
namespace console
{
class lock
{
public:
lock();
~lock();
lock(lock&&) = delete;
lock(const lock&) = delete;
lock& operator=(lock&&) = delete;
lock& operator=(const lock&) = delete;
};
void reset_color();
void info(const char* message, ...);
void warn(const char* message, ...);
void error(const char* message, ...);
void log(const char* message, ...);
void set_title(const std::string& title);
class signal_handler : std::lock_guard<std::mutex>
{
public:
signal_handler(std::function<void()> callback);
~signal_handler();
};
}

76
src/crypto_key.cpp Normal file
View File

@ -0,0 +1,76 @@
#include "std_include.hpp"
#include "utils/io.hpp"
#include "utils/cryptography.hpp"
#include "crypto_key.hpp"
#include "console.hpp"
namespace crypto_key
{
namespace
{
bool load_key(utils::cryptography::ecc::key& key)
{
std::string data{};
if (!utils::io::read_file("./private.key", &data))
{
return false;
}
key.deserialize(data);
if (!key.is_valid())
{
console::warn("Loaded key is invalid!");
return false;
}
return true;
}
utils::cryptography::ecc::key generate_key()
{
auto key = utils::cryptography::ecc::generate_key(512);
if (!key.is_valid())
{
throw std::runtime_error("Failed to generate server key!");
}
if (!utils::io::write_file("./private.key", key.serialize()))
{
throw std::runtime_error("Failed to write server key!");
}
console::info("Generated cryptographic key: %llX", key.get_hash());
return key;
}
utils::cryptography::ecc::key load_or_generate_key()
{
utils::cryptography::ecc::key key{};
if (load_key(key))
{
console::log("Loaded cryptographic key: %llX", key.get_hash());
return key;
}
return generate_key();
}
utils::cryptography::ecc::key get_key_internal()
{
auto key = load_or_generate_key();
if (!utils::io::write_file("./public.key", key.get_public_key()))
{
console::error("Failed to write public key!");
}
return key;
}
}
const utils::cryptography::ecc::key& get()
{
static auto key = get_key_internal();
return key;
}
}

6
src/crypto_key.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
namespace crypto_key
{
const utils::cryptography::ecc::key& get();
}

132
src/main.cpp Normal file
View File

@ -0,0 +1,132 @@
#include <std_include.hpp>
#include "utils/cryptography.hpp"
#include "console.hpp"
#include "crypto_key.hpp"
#include "network/address.hpp"
#include "network/socket.hpp"
using namespace std::literals;
namespace
{
utils::cryptography::ecc::key key;
const char* master_address = "master.xlabs.dev";
std::uint16_t master_port = 20810;
const char* to_kill = nullptr;
const char* to_remove = nullptr;
const char* reason = "Admin abuse, please demote";
std::string format_command(const bool add)
{
std::string cmd = "\xFF\xFF\xFF\xFFpatchkill";
const auto current_timestamp = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch());
const std::string to_sign = std::to_string(current_timestamp.count());
cmd += " " + to_sign;
const auto signed_timestamp = utils::cryptography::ecc::sign_message(key, to_sign);
cmd += " " + utils::cryptography::base64::encode(signed_timestamp);
cmd += " " + (add ? "add"s : "remove"s);
cmd += " " + (add ? std::string(to_kill) : std::string(to_remove));
cmd += " " + std::string(reason);
return cmd;
}
void unsafe_main()
{
key = crypto_key::get();
console::log("Loaded cryptographic key %llX", key.get_hash());
network::address master(master_address);
master.set_port(master_port);
const network::socket s;
if (to_kill != nullptr)
{
const auto packet = format_command(true);
s.send(master, packet);
console::log("Sent kill command to %s", master.to_string().data());
}
else if (to_remove != nullptr)
{
const auto packet = format_command(false);
s.send(master, packet);
console::log("Sent remove command to %s", master.to_string().data());
}
}
void usage(const char* prog)
{
console::error("X Labs Master Tool\n"
"Usage: %s OPTIONS\n"
" -master IP - set the target, default: '%s'\n"
" -port PORT - set the port, default: %hu\n"
" -add IP - remove ip address from the server list\n"
" -remove IP - allow ip address back on the server list\n"
" -reason - set a reason, default: '%s'",
prog, master_address, master_port, reason
);
}
}
int main(const int argc, const char** argv)
{
console::set_title("X Labs Master Tool");
console::log("Starting X Labs Master Tool");
// Parse command-line flags
for (auto i = 1; i < argc; i++)
{
if(std::strcmp(argv[i], "-master") == 0)
{
master_address = argv[++i];
}
else if (std::strcmp(argv[i], "-port") == 0)
{
master_port = static_cast<std::uint16_t>(std::strtol(argv[++i], nullptr, 0));
}
else if (std::strcmp(argv[i], "-add") == 0)
{
to_kill = argv[++i];
}
else if (std::strcmp(argv[i], "-remove") == 0)
{
to_remove = argv[++i];
}
else if (std::strcmp(argv[i], "-reason") == 0)
{
reason = argv[++i];
}
else
{
usage(argv[0]);
return -1;
}
}
try
{
unsafe_main();
}
catch (const std::exception& ex)
{
console::error("Fatal error: %s", ex.what());
return -1;
}
console::log("Terminating tool...");
return 0;
}

161
src/network/address.cpp Normal file
View File

@ -0,0 +1,161 @@
#include <std_include.hpp>
#include "network/address.hpp"
using namespace std::literals;
namespace network
{
address::address()
{
std::memset(&this->address_, 0, sizeof(this->address_));
}
address::address(const std::string& addr)
: address()
{
this->parse(addr);
}
address::address(sockaddr_in& addr)
{
this->address_ = addr;
}
bool address::operator==(const address& obj) const
{
return this->address_.sin_family == obj.address_.sin_family //
&& this->address_.sin_addr.s_addr == obj.address_.sin_addr.s_addr //
&& this->address_.sin_port == obj.address_.sin_port;
}
void address::set_ipv4(const in_addr addr)
{
this->address_.sin_family = AF_INET;
this->address_.sin_addr = addr;
}
void address::set_port(const unsigned short port)
{
this->address_.sin_port = htons(port);
}
unsigned short address::get_port() const
{
return ntohs(this->address_.sin_port);
}
std::string address::to_string(bool with_port) const
{
char buffer[1000]{};
inet_ntop(this->address_.sin_family, &this->address_.sin_addr, buffer, sizeof(buffer));
auto address = std::string(buffer);
if (with_port)
{
address += ":"s + std::to_string(this->get_port());
}
return address;
}
bool address::is_local() const
{
// According to: https://en.wikipedia.org/wiki/Private_network
std::uint8_t bytes[4];
*reinterpret_cast<uint32_t*>(&bytes) = this->address_.sin_addr.s_addr;
// 10.X.X.X
if (bytes[0] == 10)
{
return true;
}
// 192.168.X.X
if (bytes[0] == 192
&& bytes[1] == 168)
{
return true;
}
// 172.16.X.X - 172.31.X.X
if (bytes[0] == 172
&& bytes[1] >= 16
&& bytes[1] < 32)
{
return true;
}
// 127.0.0.1
if (this->address_.sin_addr.s_addr == 0x0100007F)
{
return true;
}
return false;
}
sockaddr& address::get_addr()
{
return reinterpret_cast<sockaddr&>(this->get_in_addr());
}
const sockaddr& address::get_addr() const
{
return reinterpret_cast<const sockaddr&>(this->get_in_addr());
}
sockaddr_in& address::get_in_addr()
{
return this->address_;
}
const sockaddr_in& address::get_in_addr() const
{
return this->address_;
}
void address::parse(std::string addr)
{
const auto pos = addr.find_last_of(':');
if (pos != std::string::npos)
{
auto port = addr.substr(pos + 1);
this->set_port(static_cast<std::uint16_t>(std::strtol(port.data(), nullptr, 10)));
addr = addr.substr(0, pos);
}
this->resolve(addr);
}
void address::resolve(const std::string& hostname)
{
addrinfo* result = nullptr;
if (!getaddrinfo(hostname.data(), nullptr, nullptr, &result))
{
for (auto* i = result; i; i = i->ai_next)
{
if (i->ai_addr->sa_family == AF_INET)
{
const auto port = this->get_port();
std::memcpy(&this->address_, i->ai_addr, sizeof(this->address_));
this->set_port(port);
break;
}
}
freeaddrinfo(result);
}
}
}
std::size_t std::hash<network::address>::operator()(const network::address& a) const noexcept
{
const auto h1 = std::hash<decltype(a.get_in_addr().sin_family)>{}(a.get_in_addr().sin_family);
const auto h2 = std::hash<decltype(a.get_in_addr().sin_addr.s_addr)>{}(a.get_in_addr().sin_addr.s_addr);
const auto h3 = std::hash<decltype(a.get_in_addr().sin_port)>{}(a.get_in_addr().sin_port);
return h1 ^ (h2 << 1) ^ (h3 << 2);
}

46
src/network/address.hpp Normal file
View File

@ -0,0 +1,46 @@
#pragma once
namespace network
{
class address
{
public:
address();
address(const std::string& addr);
address(sockaddr_in& addr);
void set_ipv4(in_addr addr);
void set_port(unsigned short port);
[[nodiscard]] unsigned short get_port() const;
[[nodiscard]] sockaddr& get_addr();
[[nodiscard]] const sockaddr& get_addr() const;
[[nodiscard]] sockaddr_in& get_in_addr();
[[nodiscard]] const sockaddr_in& get_in_addr() const;
[[nodiscard]] bool is_local() const;
[[nodiscard]] std::string to_string(bool with_port = true) const;
bool operator==(const address& obj) const;
bool operator!=(const address& obj) const
{
return !(*this == obj);
}
private:
sockaddr_in address_{};
void parse(std::string addr);
void resolve(const std::string& hostname);
};
}
namespace std
{
template <>
struct hash<network::address>
{
std::size_t operator()(const network::address& a) const noexcept;
};
}

131
src/network/socket.cpp Normal file
View File

@ -0,0 +1,131 @@
#include <std_include.hpp>
#include "address.hpp"
#include "socket.hpp"
using namespace std::literals;
namespace network
{
namespace
{
#ifdef _WIN32
[[maybe_unused]] class wsa_initializer
{
public:
wsa_initializer()
{
WSADATA wsa_data;
if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
{
throw std::runtime_error("Unable to initialize WSA");
}
}
~wsa_initializer()
{
WSACleanup();
}
} _;
#endif
}
socket::socket()
{
this->socket_ = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
}
socket::~socket()
{
if (this->socket_ != INVALID_SOCKET)
{
#ifdef _WIN32
closesocket(this->socket_);
#else
close(this->socket_);
#endif
}
}
socket::socket(socket&& obj) noexcept
{
this->operator=(std::move(obj));
}
socket& socket::operator=(socket&& obj) noexcept
{
if (this != &obj)
{
this->~socket();
this->socket_ = obj.socket_;
obj.socket_ = INVALID_SOCKET;
}
return *this;
}
bool socket::bind(const address& target)
{
return ::bind(this->socket_, &target.get_addr(), sizeof(target.get_addr())) == 0;
}
void socket::send(const address& target, const std::string& data) const
{
::sendto(this->socket_, data.data(), static_cast<int>(data.size()), 0, &target.get_addr(), sizeof(target.get_addr()));
}
bool socket::receive(address& source, std::string& data) const
{
char buffer[0x2000]{};
socklen_t len = sizeof(source.get_in_addr());
const auto result = recvfrom(this->socket_, buffer, sizeof(buffer), 0, &source.get_addr(), &len);
if (result == SOCKET_ERROR) // Probably WSAEWOULDBLOCK
{
return false;
}
data.assign(buffer, buffer + result);
return true;
}
bool socket::set_blocking(const bool blocking)
{
#ifdef _WIN32
unsigned long mode = blocking ? 0 : 1;
return ioctlsocket(this->socket_, FIONBIO, &mode) == 0;
#else
int flags = fcntl(this->socket_, F_GETFL, 0);
if (flags == -1) return false;
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
return fcntl(this->socket_, F_SETFL, flags) == 0;
#endif
}
bool socket::sleep(const std::chrono::milliseconds timeout) const
{
fd_set fdr;
FD_ZERO(&fdr);
FD_SET(this->socket_, &fdr);
const auto msec = timeout.count();
timeval tv{};
tv.tv_sec = static_cast<long>(msec / 1000ll);
tv.tv_usec = static_cast<long>((msec % 1000) * 1000);
const auto ret_val = select(static_cast<int>(this->socket_) + 1, &fdr, nullptr, nullptr, &tv);
if (ret_val == SOCKET_ERROR)
{
std::this_thread::sleep_for(1ms);
return socket_is_ready;
}
if (ret_val > 0)
{
return socket_is_ready;
}
return !socket_is_ready;
}
}

38
src/network/socket.hpp Normal file
View File

@ -0,0 +1,38 @@
#pragma once
namespace network
{
class socket
{
public:
socket();
~socket();
socket(const socket& obj) = delete;
socket& operator=(const socket& obj) = delete;
socket(socket&& obj) noexcept;
socket& operator=(socket&& obj) noexcept;
bool bind(const address& target);
void send(const address& target, const std::string& data) const;
bool receive(address& source, std::string& data) const;
bool set_blocking(bool blocking);
static const bool socket_is_ready = true;
bool sleep(std::chrono::milliseconds timeout) const;
private:
#ifdef _WIN32
using socklen_t = int;
#else
using SOCKET = int;
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR (-1)
#endif
SOCKET socket_ = INVALID_SOCKET;
};
}

33
src/std_include.cpp Normal file
View File

@ -0,0 +1,33 @@
#include <std_include.hpp>
extern "C"
{
int s_read_arc4random(void*, std::size_t)
{
return -1;
}
#if !defined(__linux__)
int s_read_getrandom(void*, std::size_t)
{
return -1;
}
#endif
#ifdef _WIN32
int s_read_urandom(void*, std::size_t)
{
return -1;
}
int s_read_ltm_rng(void*, std::size_t)
{
return -1;
}
#else
int s_read_wincsp(void*, std::size_t)
{
return -1;
}
#endif
}

96
src/std_include.hpp Normal file
View File

@ -0,0 +1,96 @@
#pragma once
#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable: 4018)
#pragma warning(disable: 4100)
#pragma warning(disable: 4127)
#pragma warning(disable: 4244)
#pragma warning(disable: 4297)
#pragma warning(disable: 4458)
#pragma warning(disable: 4702)
#pragma warning(disable: 4804)
#pragma warning(disable: 4806)
#pragma warning(disable: 4996)
#pragma warning(disable: 5054)
#pragma warning(disable: 6011)
#pragma warning(disable: 6297)
#pragma warning(disable: 6385)
#pragma warning(disable: 6386)
#pragma warning(disable: 6387)
#pragma warning(disable: 26110)
#pragma warning(disable: 26451)
#pragma warning(disable: 26444)
#pragma warning(disable: 26451)
#pragma warning(disable: 26489)
#pragma warning(disable: 26495)
#pragma warning(disable: 26498)
#pragma warning(disable: 26812)
#pragma warning(disable: 28020)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#else
#pragma GCC diagnostic push
#ifndef __APPLE__
#pragma GCC diagnostic ignored "-Wbool-compare"
#endif
#pragma GCC diagnostic ignored "-Wlogical-not-parentheses"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#endif
// min and max is required by gdi, therefore NOMINMAX won't work
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#include <cstdio>
#include <cstdint>
#include <csignal>
#include <cstdarg>
#include <cassert>
#include <mutex>
#include <atomic>
#include <chrono>
#include <vector>
#include <thread>
#include <fstream>
#include <iostream>
#include <utility>
#include <filesystem>
#include <functional>
#include <sstream>
#include <optional>
#include <gsl/gsl>
#ifdef _WIN32
#pragma warning(pop)
#pragma warning(disable: 4100)
#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "urlmon.lib" )
#pragma comment(lib, "iphlpapi.lib")
#else
#pragma GCC diagnostic pop
#endif

639
src/utils/cryptography.cpp Normal file
View File

@ -0,0 +1,639 @@
#include <std_include.hpp>
#include "string.hpp"
#include "cryptography.hpp"
using namespace std::literals;
/// http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/Source/libtomcrypt/doc/libTomCryptDoc.pdf
namespace utils::cryptography
{
namespace
{
struct __
{
__()
{
ltc_mp = ltm_desc;
register_cipher(&aes_desc);
register_cipher(&des3_desc);
register_prng(&sprng_desc);
register_prng(&fortuna_desc);
register_prng(&yarrow_desc);
register_hash(&sha1_desc);
register_hash(&sha256_desc);
register_hash(&sha512_desc);
}
} ___;
[[maybe_unused]] const char* cs(const std::uint8_t* data)
{
return reinterpret_cast<const char*>(data);
}
[[maybe_unused]] char* cs(std::uint8_t* data)
{
return reinterpret_cast<char*>(data);
}
[[maybe_unused]] const std::uint8_t* cs(const char* data)
{
return reinterpret_cast<const std::uint8_t*>(data);
}
[[maybe_unused]] std::uint8_t* cs(char* data)
{
return reinterpret_cast<std::uint8_t*>(data);
}
[[maybe_unused]] unsigned long ul(const std::size_t value)
{
return static_cast<unsigned long>(value);
}
class prng
{
public:
prng(const ltc_prng_descriptor& descriptor, const bool autoseed = true)
: state_(std::make_unique<prng_state>())
, descriptor_(descriptor)
{
this->id_ = register_prng(&descriptor);
if (this->id_ == -1)
{
throw std::runtime_error("PRNG "s + this->descriptor_.name + " could not be registered!");
}
if (autoseed)
{
this->auto_seed();
}
else
{
this->descriptor_.start(this->state_.get());
}
}
~prng()
{
this->descriptor_.done(this->state_.get());
}
prng_state* get_state() const
{
this->descriptor_.ready(this->state_.get());
return this->state_.get();
}
int get_id() const
{
return this->id_;
}
void add_entropy(const void* data, const std::size_t length) const
{
this->descriptor_.add_entropy(static_cast<const std::uint8_t*>(data), ul(length), this->state_.get());
}
void read(void* data, const std::size_t length) const
{
this->descriptor_.read(static_cast<unsigned char*>(data), ul(length), this->get_state());
}
private:
int id_;
std::unique_ptr<prng_state> state_;
const ltc_prng_descriptor& descriptor_;
void auto_seed() const
{
rng_make_prng(128, this->id_, this->state_.get(), nullptr);
int i[4]; // uninitialized data
auto* i_ptr = &i;
this->add_entropy(reinterpret_cast<std::uint8_t*>(&i), sizeof(i));
this->add_entropy(reinterpret_cast<std::uint8_t*>(&i_ptr), sizeof(i_ptr));
auto t = time(nullptr);
this->add_entropy(reinterpret_cast<std::uint8_t*>(&t), sizeof(t));
}
};
const prng prng_(fortuna_desc);
}
ecc::key::key()
{
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
}
ecc::key::~key()
{
this->free();
}
ecc::key::key(key&& obj) noexcept
: key()
{
this->operator=(std::move(obj));
}
ecc::key::key(const key& obj)
: key()
{
this->operator=(obj);
}
ecc::key& ecc::key::operator=(key&& obj) noexcept
{
if (this != &obj)
{
std::memmove(&this->key_storage_, &obj.key_storage_, sizeof(this->key_storage_));
ZeroMemory(&obj.key_storage_, sizeof(obj.key_storage_));
}
return *this;
}
ecc::key& ecc::key::operator=(const key& obj)
{
if (this != &obj && obj.is_valid())
{
this->deserialize(obj.serialize(obj.key_storage_.type));
}
return *this;
}
bool ecc::key::is_valid() const
{
return (!memory::is_set(&this->key_storage_, 0, sizeof(this->key_storage_)));
}
ecc_key& ecc::key::get()
{
return this->key_storage_;
}
const ecc_key& ecc::key::get() const
{
return this->key_storage_;
}
std::string ecc::key::get_public_key() const
{
std::uint8_t buffer[512]{};
unsigned long length = sizeof(buffer);
if (ecc_ansi_x963_export(&this->key_storage_, buffer, &length) == CRYPT_OK)
{
return std::string(cs(buffer), length);
}
return {};
}
void ecc::key::set(const std::string& pub_key_buffer)
{
this->free();
if (ecc_ansi_x963_import(cs(pub_key_buffer.data()),
ul(pub_key_buffer.size()),
&this->key_storage_) != CRYPT_OK)
{
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
}
}
void ecc::key::deserialize(const std::string& key)
{
this->free();
if (ecc_import(cs(key.data()), ul(key.size()),
&this->key_storage_) != CRYPT_OK
)
{
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
}
}
std::string ecc::key::serialize(const int type) const
{
std::uint8_t buffer[4096]{};
unsigned long length = sizeof(buffer);
if (ecc_export(buffer, &length, type, &this->key_storage_) == CRYPT_OK)
{
return std::string(cs(buffer), length);
}
return "";
}
void ecc::key::free()
{
if (this->is_valid())
{
ecc_free(&this->key_storage_);
}
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
}
bool ecc::key::operator==(key& key) const
{
return (this->is_valid() && key.is_valid() && this->serialize(PK_PUBLIC) == key.serialize(PK_PUBLIC));
}
std::uint64_t ecc::key::get_hash() const
{
const auto hash = sha1::compute(this->get_public_key());
if (hash.size() >= 8)
{
return *reinterpret_cast<const std::uint64_t*>(hash.data());
}
return 0;
}
ecc::key ecc::generate_key(const int bits)
{
key key;
ecc_make_key(prng_.get_state(), prng_.get_id(), bits / 8, &key.get());
return key;
}
ecc::key ecc::generate_key(const int bits, const std::string& entropy)
{
key key{};
const prng yarrow(yarrow_desc, false);
yarrow.add_entropy(entropy.data(), entropy.size());
ecc_make_key(yarrow.get_state(), yarrow.get_id(), bits / 8, &key.get());
return key;
}
std::string ecc::sign_message(const key& key, const std::string& message)
{
if (!key.is_valid()) return "";
std::uint8_t buffer[512]{};
unsigned long length = sizeof(buffer);
ecc_sign_hash(cs(message.data()), ul(message.size()), buffer, &length, prng_.get_state(), prng_.get_id(),
&key.get());
return std::string(cs(buffer), length);
}
bool ecc::verify_message(const key& key, const std::string& message, const std::string& signature)
{
if (!key.is_valid()) return false;
auto result = 0;
return (ecc_verify_hash(cs(signature.data()),
ul(signature.size()),
cs(message.data()),
ul(message.size()), &result,
&key.get()) == CRYPT_OK && result != 0);
}
bool ecc::encrypt(const key& key, std::string& data)
{
std::string out_data{};
out_data.resize(std::max(ul(data.size() * 3), ul(0x100)));
auto out_len = ul(out_data.size());
auto crypt = [&]()
{
return ecc_encrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len,
prng_.get_state(), prng_.get_id(), find_hash("sha512"), &key.get());
};
auto res = crypt();
if (res == CRYPT_BUFFER_OVERFLOW)
{
out_data.resize(out_len);
res = crypt();
}
if (res != CRYPT_OK)
{
return false;
}
out_data.resize(out_len);
data = std::move(out_data);
return true;
}
bool ecc::decrypt(const key& key, std::string& data)
{
std::string out_data{};
out_data.resize(std::max(ul(data.size() * 3), ul(0x100)));
auto out_len = ul(out_data.size());
auto crypt = [&]()
{
return ecc_decrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len, &key.get());
};
auto res = crypt();
if (res == CRYPT_BUFFER_OVERFLOW)
{
out_data.resize(out_len);
res = crypt();
}
if (res != CRYPT_OK)
{
return false;
}
out_data.resize(out_len);
data = std::move(out_data);
return true;
}
std::string rsa::encrypt(const std::string& data, const std::string& hash, const std::string& key)
{
rsa_key new_key;
rsa_import(cs(key.data()), ul(key.size()), &new_key);
const auto _ = gsl::finally([&]()
{
rsa_free(&new_key);
});
std::string out_data{};
out_data.resize(std::max(ul(data.size() * 3), ul(0x100)));
auto out_len = ul(out_data.size());
auto crypt = [&]()
{
return rsa_encrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len, cs(hash.data()),
ul(hash.size()), prng_.get_state(), prng_.get_id(), find_hash("sha512"), &new_key);
};
auto res = crypt();
if (res == CRYPT_BUFFER_OVERFLOW)
{
out_data.resize(out_len);
res = crypt();
}
if (res == CRYPT_OK)
{
out_data.resize(out_len);
return out_data;
}
return {};
}
std::string des3::encrypt(const std::string& data, const std::string& iv, const std::string& key)
{
std::string enc_data;
enc_data.resize(data.size());
symmetric_CBC cbc;
const auto des3 = find_cipher("3des");
cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast<int>(key.size()), 0, &cbc);
cbc_encrypt(cs(data.data()), cs(enc_data.data()), ul(data.size()), &cbc);
cbc_done(&cbc);
return enc_data;
}
std::string des3::decrypt(const std::string& data, const std::string& iv, const std::string& key)
{
std::string dec_data;
dec_data.resize(data.size());
symmetric_CBC cbc;
const auto des3 = find_cipher("3des");
cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast<int>(key.size()), 0, &cbc);
cbc_decrypt(cs(data.data()), cs(dec_data.data()), ul(data.size()), &cbc);
cbc_done(&cbc);
return dec_data;
}
std::string tiger::compute(const std::string& data, const bool hex)
{
return compute(cs(data.data()), data.size(), hex);
}
std::string tiger::compute(const std::uint8_t* data, const std::size_t length, const bool hex)
{
std::uint8_t buffer[24]{};
hash_state state;
tiger_init(&state);
tiger_process(&state, data, ul(length));
tiger_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex) return hash;
return string::dump_hex(hash, "");
}
std::string aes::encrypt(const std::string& data, const std::string& iv, const std::string& key)
{
std::string enc_data;
enc_data.resize(data.size());
symmetric_CBC cbc;
const auto aes = find_cipher("aes");
cbc_start(aes, cs(iv.data()), cs(key.data()),
static_cast<int>(key.size()), 0, &cbc);
cbc_encrypt(cs(data.data()),
cs(enc_data.data()),
ul(data.size()), &cbc);
cbc_done(&cbc);
return enc_data;
}
std::string aes::decrypt(const std::string& data, const std::string& iv, const std::string& key)
{
std::string dec_data;
dec_data.resize(data.size());
symmetric_CBC cbc;
const auto aes = find_cipher("aes");
cbc_start(aes, cs(iv.data()), cs(key.data()),
static_cast<int>(key.size()), 0, &cbc);
cbc_decrypt(cs(data.data()),
cs(dec_data.data()),
ul(data.size()), &cbc);
cbc_done(&cbc);
return dec_data;
}
std::string hmac_sha1::compute(const std::string& data, const std::string& key)
{
std::string buffer;
buffer.resize(20);
hmac_state state;
hmac_init(&state, find_hash("sha1"), cs(key.data()), ul(key.size()));
hmac_process(&state, cs(data.data()), static_cast<int>(data.size()));
auto out_len = ul(buffer.size());
hmac_done(&state, cs(buffer.data()), &out_len);
buffer.resize(out_len);
return buffer;
}
std::string sha1::compute(const std::string& data, const bool hex)
{
return compute(cs(data.data()), data.size(), hex);
}
std::string sha1::compute(const std::uint8_t* data, const size_t length, const bool hex)
{
std::uint8_t buffer[20]{};
hash_state state;
sha1_init(&state);
sha1_process(&state, data, ul(length));
sha1_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex) return hash;
return string::dump_hex(hash, "");
}
std::string sha256::compute(const std::string& data, const bool hex)
{
return compute(cs(data.data()), data.size(), hex);
}
std::string sha256::compute(const std::uint8_t* data, const std::size_t length, const bool hex)
{
std::uint8_t buffer[32]{};
hash_state state;
sha256_init(&state);
sha256_process(&state, data, ul(length));
sha256_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex) return hash;
return string::dump_hex(hash, "");
}
std::string sha512::compute(const std::string& data, const bool hex)
{
return compute(cs(data.data()), data.size(), hex);
}
std::string sha512::compute(const std::uint8_t* data, const std::size_t length, const bool hex)
{
std::uint8_t buffer[64]{};
hash_state state;
sha512_init(&state);
sha512_process(&state, data, ul(length));
sha512_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex) return hash;
return string::dump_hex(hash, "");
}
std::string base64::encode(const std::uint8_t* data, const std::size_t len)
{
std::string result;
result.resize((len + 2) * 2);
auto out_len = ul(result.size());
if (base64_encode(data, ul(len), result.data(), &out_len) != CRYPT_OK)
{
return {};
}
result.resize(out_len);
return result;
}
std::string base64::encode(const std::string& data)
{
return base64::encode(cs(data.data()), static_cast<unsigned>(data.size()));
}
std::string base64::decode(const std::string& data)
{
std::string result;
result.resize((data.size() + 2) * 2);
auto out_len = ul(result.size());
if (base64_decode(data.data(), ul(data.size()), cs(result.data()), &out_len) != CRYPT_OK)
{
return {};
}
result.resize(out_len);
return result;
}
unsigned int jenkins_one_at_a_time::compute(const std::string& data)
{
return compute(data.data(), data.size());
}
unsigned int jenkins_one_at_a_time::compute(const char* key, const std::size_t len)
{
unsigned int hash, i;
for (hash = i = 0; i < len; ++i)
{
hash += key[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
std::uint32_t random::get_integer()
{
std::uint32_t result;
get_data(&result, sizeof(result));
return result;
}
std::string random::get_challenge()
{
std::string result;
result.resize(sizeof(std::uint32_t));
get_data(result.data(), result.size());
return string::dump_hex(result, "");
}
void random::get_data(void* data, const std::size_t size)
{
prng_.read(data, size);
}
}

118
src/utils/cryptography.hpp Normal file
View File

@ -0,0 +1,118 @@
#pragma once
#include <string>
#include <tomcrypt.h>
namespace utils::cryptography
{
namespace ecc
{
class key final
{
public:
key();
~key();
key(key&& obj) noexcept;
key(const key& obj);
key& operator=(key&& obj) noexcept;
key& operator=(const key& obj);
[[nodiscard]] bool is_valid() const;
ecc_key& get();
[[nodiscard]] const ecc_key& get() const;
[[nodiscard]] std::string get_public_key() const;
void set(const std::string& pub_key_buffer);
void deserialize(const std::string& key);
[[nodiscard]] std::string serialize(int type = PK_PRIVATE) const;
void free();
bool operator==(key& key) const;
[[nodiscard]] std::uint64_t get_hash() const;
private:
ecc_key key_storage_{};
};
key generate_key(int bits);
key generate_key(int bits, const std::string& entropy);
std::string sign_message(const key& key, const std::string& message);
bool verify_message(const key& key, const std::string& message, const std::string& signature);
bool encrypt(const key& key, std::string& data);
bool decrypt(const key& key, std::string& data);
}
namespace rsa
{
std::string encrypt(const std::string& data, const std::string& hash, const std::string& key);
}
namespace des3
{
std::string encrypt(const std::string& data, const std::string& iv, const std::string& key);
std::string decrypt(const std::string& data, const std::string& iv, const std::string& key);
}
namespace tiger
{
std::string compute(const std::string& data, bool hex = false);
std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false);
}
namespace aes
{
std::string encrypt(const std::string& data, const std::string& iv, const std::string& key);
std::string decrypt(const std::string& data, const std::string& iv, const std::string& key);
}
namespace hmac_sha1
{
std::string compute(const std::string& data, const std::string& key);
}
namespace sha1
{
std::string compute(const std::string& data, bool hex = false);
std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false);
}
namespace sha256
{
std::string compute(const std::string& data, bool hex = false);
std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false);
}
namespace sha512
{
std::string compute(const std::string& data, bool hex = false);
std::string compute(const std::uint8_t* data, std::size_t length, bool hex = false);
}
namespace base64
{
std::string encode(const std::uint8_t* data, std::size_t len);
std::string encode(const std::string& data);
std::string decode(const std::string& data);
}
namespace jenkins_one_at_a_time
{
std::uint32_t compute(const std::string& data);
std::uint32_t compute(const char* key, std::size_t len);
};
namespace random
{
std::uint32_t get_integer();
std::string get_challenge();
void get_data(void* data, std::size_t size);
}
}

128
src/utils/io.cpp Normal file
View File

@ -0,0 +1,128 @@
#include <std_include.hpp>
#include "io.hpp"
namespace utils::io
{
bool remove_file(const std::string& file)
{
return remove(file.data()) == 0;
}
bool move_file(const std::string& src, const std::string& target)
{
return rename(src.data(), target.data()) == 0;
}
bool file_exists(const std::string& file)
{
return std::ifstream(file).good();
}
bool write_file(const std::string& file, const std::string& data, const bool append)
{
const auto pos = file.find_last_of("/\\");
if (pos != std::string::npos)
{
create_directory(file.substr(0, pos));
}
auto mode = std::ios::binary | std::ofstream::out;
if (append)
{
mode |= std::ofstream::app;
}
std::ofstream stream(file, mode);
if (stream.is_open())
{
stream.write(data.data(), static_cast<std::streamsize>(data.size()));
stream.close();
return true;
}
return false;
}
std::string read_file(const std::string& file)
{
std::string data;
read_file(file, &data);
return data;
}
bool read_file(const std::string& file, std::string* data)
{
if (!data) return false;
data->clear();
if (file_exists(file))
{
std::ifstream stream(file, std::ios::binary);
if (!stream.is_open()) return false;
stream.seekg(0, std::ios::end);
const std::streamsize size = stream.tellg();
stream.seekg(0, std::ios::beg);
if (size > -1)
{
data->resize(static_cast<std::uint32_t>(size));
stream.read(const_cast<char*>(data->data()), size);
stream.close();
return true;
}
}
return false;
}
std::size_t file_size(const std::string& file)
{
if (file_exists(file))
{
std::ifstream stream(file, std::ios::binary);
if (stream.good())
{
stream.seekg(0, std::ios::end);
return static_cast<std::size_t>(stream.tellg());
}
}
return 0;
}
bool create_directory(const std::string& directory)
{
return std::filesystem::create_directories(directory);
}
bool directory_exists(const std::string& directory)
{
return std::filesystem::is_directory(directory);
}
bool directory_is_empty(const std::string& directory)
{
return std::filesystem::is_empty(directory);
}
std::vector<std::string> list_files(const std::string& directory)
{
std::vector<std::string> files;
for (auto& file : std::filesystem::directory_iterator(directory))
{
files.push_back(file.path().generic_string());
}
return files;
}
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target)
{
std::filesystem::copy(src, target, std::filesystem::copy_options::overwrite_existing |
std::filesystem::copy_options::recursive);
}
}

20
src/utils/io.hpp Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <string>
#include <vector>
namespace utils::io
{
bool remove_file(const std::string& file);
bool move_file(const std::string& src, const std::string& target);
bool file_exists(const std::string& file);
bool write_file(const std::string& file, const std::string& data, bool append = false);
bool read_file(const std::string& file, std::string* data);
std::string read_file(const std::string& file);
std::size_t file_size(const std::string& file);
bool create_directory(const std::string& directory);
bool directory_exists(const std::string& directory);
bool directory_is_empty(const std::string& directory);
std::vector<std::string> list_files(const std::string& directory);
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target);
}

110
src/utils/memory.cpp Normal file
View File

@ -0,0 +1,110 @@
#include <std_include.hpp>
#include "memory.hpp"
namespace utils
{
memory::allocator memory::mem_allocator_;
memory::allocator::~allocator()
{
this->clear();
}
void memory::allocator::clear()
{
std::lock_guard _(this->mutex_);
for (auto& data : this->pool_)
{
memory::free(data);
}
this->pool_.clear();
}
void memory::allocator::free(void* data)
{
std::lock_guard _(this->mutex_);
const auto j = std::find(this->pool_.begin(), this->pool_.end(), data);
if (j != this->pool_.end())
{
memory::free(data);
this->pool_.erase(j);
}
}
void memory::allocator::free(const void* data)
{
this->free(const_cast<void*>(data));
}
void* memory::allocator::allocate(const size_t length)
{
std::lock_guard _(this->mutex_);
const auto data = memory::allocate(length);
this->pool_.push_back(data);
return data;
}
bool memory::allocator::empty() const
{
return this->pool_.empty();
}
char* memory::allocator::duplicate_string(const std::string& string)
{
std::lock_guard _(this->mutex_);
const auto data = memory::duplicate_string(string);
this->pool_.push_back(data);
return data;
}
void* memory::allocate(const size_t length)
{
return calloc(length, 1);
}
char* memory::duplicate_string(const std::string& string)
{
const auto new_string = allocate_array<char>(string.size() + 1);
std::memcpy(new_string, string.data(), string.size());
return new_string;
}
void memory::free(void* data)
{
if (data)
{
::free(data);
}
}
void memory::free(const void* data)
{
free(const_cast<void*>(data));
}
bool memory::is_set(const void* mem, const char chr, const size_t length)
{
auto* const mem_arr = static_cast<const char*>(mem);
for (size_t i = 0; i < length; ++i)
{
if (mem_arr[i] != chr)
{
return false;
}
}
return true;
}
memory::allocator* memory::get_allocator()
{
return &memory::mem_allocator_;
}
}

71
src/utils/memory.hpp Normal file
View File

@ -0,0 +1,71 @@
#pragma once
#include <mutex>
#include <vector>
namespace utils
{
class memory final
{
public:
class allocator final
{
public:
~allocator();
void clear();
void free(void* data);
void free(const void* data);
void* allocate(size_t length);
template <typename T>
inline T* allocate()
{
return this->allocate_array<T>(1);
}
template <typename T>
inline T* allocate_array(const size_t count = 1)
{
return static_cast<T*>(this->allocate(count * sizeof(T)));
}
bool empty() const;
char* duplicate_string(const std::string& string);
private:
std::mutex mutex_;
std::vector<void*> pool_;
};
static void* allocate(size_t length);
template <typename T>
static inline T* allocate()
{
return allocate_array<T>(1);
}
template <typename T>
static inline T* allocate_array(const size_t count = 1)
{
return static_cast<T*>(allocate(count * sizeof(T)));
}
static char* duplicate_string(const std::string& string);
static void free(void* data);
static void free(const void* data);
static bool is_set(const void* mem, char chr, size_t length);
static allocator* get_allocator();
private:
static allocator mem_allocator_;
};
}

102
src/utils/string.cpp Normal file
View File

@ -0,0 +1,102 @@
#include <std_include.hpp>
#include "string.hpp"
#include <sstream>
#include <cstdarg>
#include <algorithm>
namespace utils::string
{
const char* va(const char* fmt, ...)
{
static thread_local va_provider<8, 256> provider;
va_list ap;
va_start(ap, fmt);
const char* result = provider.get(fmt, ap);
va_end(ap);
return result;
}
std::vector<std::string> split(const std::string& s, const char delim)
{
std::stringstream ss(s);
std::string item;
std::vector<std::string> elems;
while (std::getline(ss, item, delim))
{
elems.push_back(item);
item = std::string();
}
return elems;
}
std::string to_lower(std::string text)
{
std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input)
{
return static_cast<char>(std::tolower(input));
});
return text;
}
std::string to_upper(std::string text)
{
std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input)
{
return static_cast<char>(std::toupper(input));
});
return text;
}
bool starts_with(const std::string& text, const std::string& substring)
{
return text.find(substring) == 0;
}
bool ends_with(const std::string& text, const std::string& substring)
{
if (substring.size() > text.size()) return false;
return std::equal(substring.rbegin(), substring.rend(), text.rbegin());
}
std::string replace(std::string str, const std::string& from, const std::string& to)
{
if (from.empty())
{
return str;
}
std::size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos)
{
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
return str;
}
std::string dump_hex(const std::string& data, const std::string& separator)
{
std::string result;
for (unsigned int i = 0; i < data.size(); ++i)
{
if (i > 0)
{
result.append(separator);
}
result.append(va("%02X", data[i] & 0xFF));
}
return result;
}
}

97
src/utils/string.hpp Normal file
View File

@ -0,0 +1,97 @@
#pragma once
#include "memory.hpp"
#ifndef ARRAYSIZE
template <class Type, size_t n>
size_t ARRAYSIZE(Type (&)[n]) { return n; }
#endif
namespace utils::string
{
template <std::size_t Buffers, std::size_t MinBufferSize>
class va_provider final
{
public:
static_assert(Buffers != 0 && MinBufferSize != 0, "Buffers and MinBufferSize mustn't be 0");
va_provider() : current_buffer_(0)
{
}
char* get(const char* format, va_list ap)
{
++this->current_buffer_ %= ARRAYSIZE(this->string_pool_);
auto entry = &this->string_pool_[this->current_buffer_];
if (!entry->size || !entry->buffer)
{
throw std::runtime_error("String pool not initialized");
}
while (true)
{
#ifdef _WIN32
const int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
#else
const int res = vsnprintf(entry->buffer, entry->size, format, ap);
#endif
if (res > 0) break; // Success
if (res == 0) return nullptr; // Error
entry->double_size();
}
return entry->buffer;
}
private:
class entry final
{
public:
explicit entry(const std::size_t _size = MinBufferSize) : size(_size), buffer(nullptr)
{
if (this->size < MinBufferSize) this->size = MinBufferSize;
this->allocate();
}
~entry()
{
if (this->buffer) memory::get_allocator()->free(this->buffer);
this->size = 0;
this->buffer = nullptr;
}
void allocate()
{
if (this->buffer) memory::get_allocator()->free(this->buffer);
this->buffer = memory::get_allocator()->allocate_array<char>(this->size + 1);
}
void double_size()
{
this->size *= 2;
this->allocate();
}
std::size_t size;
char* buffer;
};
std::size_t current_buffer_;
entry string_pool_[Buffers];
};
const char* va(const char* fmt, ...);
std::vector<std::string> split(const std::string& s, char delim);
std::string to_lower(std::string text);
std::string to_upper(std::string text);
bool starts_with(const std::string& text, const std::string& substring);
bool ends_with(const std::string& text, const std::string& substring);
std::string replace(std::string str, const std::string& from, const std::string& to);
std::string dump_hex(const std::string& data, const std::string& separator = " ");
}