1.4.10 updates

This commit is contained in:
WolverinDEV 2020-02-28 11:24:07 +01:00
parent 2b929fd3c0
commit 0d456eea5d
48 changed files with 2267 additions and 781 deletions

@ -1 +1 @@
Subproject commit fec314cf2b73fc87b34bc68f26eb64e51982b26e Subproject commit ea81efb4130ad6c616b3b222a364fc8cd5ad9868

View File

@ -17,6 +17,7 @@ set(LICENCE_SOURCE_FILES
shared/LicenseRequest.cpp shared/LicenseRequest.cpp
shared/LicenseRequestHandler.cpp shared/LicenseRequestHandler.cpp
shared/License.cpp shared/License.cpp
shared/LicenseServerClient.cpp
../shared/src/log/LogUtils.cpp ../shared/src/log/LogUtils.cpp
) )
@ -36,13 +37,14 @@ add_executable(TeaLicenseServer ${LICENCE_SOURCE_FILES} ${PROTO_SRCS} ${PROTO_HD
server/KeyIdCache.cpp server/KeyIdCache.cpp
server/LicenseServer.cpp server/LicenseServer.cpp
server/LicenseServerHandler.cpp server/LicenseServerHandler.cpp
server/LicenseManager.cpp server/DatabaseHandler.cpp
LicenseServerMain.cpp LicenseServerMain.cpp
server/WebAPI.cpp server/WebAPI.cpp
server/StatisticManager.cpp server/StatisticManager.cpp
server/UserManager.cpp server/UserManager.cpp
MySQLLibSSLFix.c MySQLLibSSLFix.c
) )
target_compile_options(TeaLicenseServer PRIVATE "-Wl,--unresolved-symbols=ignore-in-object-files")
target_link_libraries(TeaLicenseServer target_link_libraries(TeaLicenseServer
threadpool::static #Static threadpool::static #Static
@ -68,44 +70,43 @@ target_link_libraries(TeaLicenseServer
mysqlclient.a mysqlclient.a
jsoncpp_lib jsoncpp_lib
${DataPipes_LIBRARIES_SHARED} # Also includes glib2.0 ${DataPipes_LIBRARIES_SHARED} # Also includes glib2.0
ffi
openssl::ssl::shared openssl::ssl::shared
openssl::crypto::shared openssl::crypto::shared
pthread pthread
dl dl
z z
) )
if (COMPILE_WEB_CLIENT)
target_link_libraries(TeaLicenseServer ${glib20_DIR}/lib/x86_64-linux-gnu/libffi.so.7 ${nice_DIR}/lib/libnice.so.10 ffi)
endif ()
include_directories(${LIBRARY_PATH}/boringssl/include/) include_directories(${LIBRARY_PATH}/boringssl/include/)
#The test license client #The test license client
add_executable(TeaLicenseClient add_executable(TeaLicenseClient
LicenseClientMain.cpp LicenseClientMain.cpp
${LICENCE_SOURCE_FILES} ${PROTO_SRCS} ${PROTO_HDRS} ${LICENCE_SOURCE_FILES} ${PROTO_SRCS} ${PROTO_HDRS})
)
target_link_libraries(TeaLicenseClient target_link_libraries(TeaLicenseClient
${LIBRARY_PATH_DATA_PIPES}
${LIBRARY_PATH_TERMINAL} #Static
${LIBRARY_PATH_THREAD_POOL} #Static
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent.a
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent_pthreads.a
pthread
${LIBRARY_PATH_VARIBALES}
${LIBRARY_PATH_BREAKPAD}
${LIBRARY_PATH_PROTOBUF}
${LIBRARY_TOM_MATH}
${LIBRARY_TOM_CRYPT}
stdc++fs.a
${LIBRARY_PATH_TERMINAL}
${LIBRARY_PATH_BORINGSSL_SSL}
${LIBRARY_PATH_BORINGSSL_CRYPTO}
${LIBRARY_PATH_BREAKPAD}
${TOM_LIBRARIES}
${LIBRARY_PATH_JDBC}
${LIBRARY_PATH_ED255}
TeaSpeak #Static TeaSpeak #Static
jsoncpp.a
protobuf::libprotobuf
stdc++fs
libevent::core libevent::pthreads
${ed25519_LIBRARIES_STATIC}
${StringVariable_LIBRARIES_STATIC}
CXXTerminal::static #Static
tomcrypt::static
tommath::static
${DataPipes_LIBRARIES_SHARED} # Also includes glib2.0
${glib20_DIR}/lib/x86_64-linux-gnu/libffi.so.7 ${nice_DIR}/lib/libnice.so.10 ffi
openssl::ssl::shared
openssl::crypto::shared
pthread
dl
z
) )
#The license manager #The license manager
@ -166,11 +167,12 @@ add_executable(LicenseCLI
) )
target_link_libraries(LicenseCLI target_link_libraries(LicenseCLI
${LIBRARY_PATH_THREAD_POOL} #Static TeaSpeak
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent.a libevent::core libevent::pthreads
${PROJECT_SOURCE_DIR}/../../libraries/event/build/lib/libevent_pthreads.a threadpool::static #Static
${LIBRARY_PATH_PROTOBUF} tomcrypt::static
${LIBRARY_TOM_MATH} #Static tommath::static
${LIBRARY_TOM_CRYPT} #Static protobuf::libprotobuf
${ed25519_LIBRARIES_STATIC}
pthread pthread
) )

View File

@ -2,6 +2,8 @@
#include <shared/License.h> #include <shared/License.h>
#include <shared/LicenseRequest.h> #include <shared/LicenseRequest.h>
#include <event2/thread.h> #include <event2/thread.h>
#include "shared/LicenseServerClient.h"
#include <random> #include <random>
#include <ed25519/ed25519.h> #include <ed25519/ed25519.h>
#include <misc/base64.h> #include <misc/base64.h>
@ -49,8 +51,8 @@ int main(int ac, char** av){
} }
cout << endl; cout << endl;
cout << "Intermediate: " << base64::encode(error) << endl; cout << "Intermediate: " << base64::encode(error) << endl;
#endif #endif
#if 0
auto license = v2::License::read(intermediate_key.data(), intermediate_key.size(), errc); auto license = v2::License::read(intermediate_key.data(), intermediate_key.size(), errc);
license->push_entry<v2::hierarchy::Server>(system_clock::now(), system_clock::now() + hours{24 * 365 * 1000}, "TeaSpeak Official server", "contact@teaspeak.de"); license->push_entry<v2::hierarchy::Server>(system_clock::now(), system_clock::now() + hours{24 * 365 * 1000}, "TeaSpeak Official server", "contact@teaspeak.de");
@ -60,6 +62,7 @@ int main(int ac, char** av){
__asm__("nop"); __asm__("nop");
cout << "Errc: " << errc << endl; cout << "Errc: " << errc << endl;
cout << "Write: " << base64::encode(error) << endl; cout << "Write: " << base64::encode(error) << endl;
#endif
#if 0 #if 0
std::array<uint8_t, 32> private_key, public_key; std::array<uint8_t, 32> private_key, public_key;
@ -84,7 +87,6 @@ int main(int ac, char** av){
return true; return true;
#endif #endif
#if 0 #if 0
srand(system_clock::now().time_since_epoch().count()); srand(system_clock::now().time_since_epoch().count());
cout << "Generating new license" << endl; cout << "Generating new license" << endl;
@ -121,29 +123,56 @@ int main(int ac, char** av){
auto data = make_shared<LicenseRequestData>(); auto data = make_shared<LicenseRequestData>();
data->license = license; data->license = license;
data->info = make_shared<ServerInfo>(); data->info = make_shared<ServerInfo>();
while(true) {
LicenceRequest request(data, serv_addr); LicenceRequest request(data, serv_addr);
try { try {
cout << "Requesting license" << endl; cout << "Requesting license" << endl;
auto info = request.requestInfo().waitAndGet(nullptr); auto info = request.requestInfo().waitAndGet(nullptr);
if(!info) { if(!info) {
cout << "Invalid result! Error: " << (request.exception() ? "yes => " + string(request.exception()->what()) : "no") << endl; cout << "Invalid result! Error: " << (request.exception() ? "yes => " + string(request.exception()->what()) : "no") << endl;
throw *request.exception(); throw *request.exception();
} }
cout << "Got result!" << endl; cout << "Got result!" << endl;
cout << "Valid: " << info->license_valid << endl; cout << "Valid: " << info->license_valid << endl;
if(info->license) { if(info->license) {
cout << "License:" << endl; cout << "License:" << endl;
cout << " Type: " << info->license->type << endl; cout << " Type: " << info->license->type << endl;
cout << " User name: " << info->license->username << endl; cout << " User name: " << info->license->username << endl;
cout << " First name: " << info->license->first_name << endl; cout << " First name: " << info->license->first_name << endl;
cout << " Last name: " << info->license->last_name << endl; cout << " Last name: " << info->license->last_name << endl;
cout << " EMail: " << info->license->email << endl; cout << " EMail: " << info->license->email << endl;
} else cout << "License: none"; } else cout << "License: none";
} catch (const std::exception& ex){ } catch (const std::exception& ex){
cerr << "Could not load info after throwing: " << endl << ex.what() << endl; cerr << "Could not load info after throwing: " << endl << ex.what() << endl;
} }
} #endif
#if 1
sockaddr_in serv_addr{};
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = ((in_addr*) gethostbyname("localhost")->h_addr)->s_addr;
serv_addr.sin_port = htons(27786);
client::LicenseServerClient client{serv_addr, 3};
client.callback_connected = [&]{
std::cout << "Connected" << std::endl;
client.disconnect("client closed", std::chrono::system_clock::now() + std::chrono::seconds{5});
};
client.callback_message = [&](auto type, const void* buffer, size_t length) {
std::cout << "Received an message" << std::endl;
};
client.callback_disconnected = [&](bool expected, const std::string& reason) {
std::cout << "Received disconnect (expected: " << expected << "): " << reason << std::endl;
};
if(!client.start_connection(error)) {
std::cout << "Failed to start connection" << std::endl;
return 0;
}
std::this_thread::sleep_for(std::chrono::seconds{2});
client.disconnect("client closed", std::chrono::system_clock::now() + std::chrono::seconds{5});
std::cout << "Disconnect result: " << client.await_disconnect() << std::endl;
#endif #endif
return 0; return 0;
} }

View File

@ -2,6 +2,7 @@
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
#include <event2/thread.h> #include <event2/thread.h>
#include <misc/base64.h>
#include "manager/ServerConnection.h" #include "manager/ServerConnection.h"
@ -63,16 +64,18 @@ class CLIParser {
std::vector<std::string> tokens; std::vector<std::string> tokens;
}; };
#define REQ_CMD(variable, message, ...) \ #define _str(x) #x
if(!options.option_exists({__VA_ARGS__}, false)) { \
cerr << message << endl; \ #define REQ_CMD(variable, message, skey, lkey, ...) \
return 1; \ if(!options.option_exists({skey, lkey, #__VA_ARGS__}, false)) { \
} \ cerr << message << " (" << _str(skey) << " or " << _str(lkey) << ")" << endl; \
auto variable = url_decode(options.get_option({__VA_ARGS__})) \ return 1; \
} \
auto variable = url_decode(options.get_option({skey, lkey, #__VA_ARGS__})) \
#define NO_OPEN_SSL #define NO_OPEN_SSL
#include <misc/digest.h> #include <misc/digest.h>
#include <misc/base64.h>
int main(int argc, char **argv) { int main(int argc, char **argv) {
CLIParser options(argc, argv); CLIParser options(argc, argv);
@ -88,7 +91,9 @@ int main(int argc, char **argv) {
REQ_CMD(auth_pass, "missing authentication user", "-ap", "--auth-pass"); REQ_CMD(auth_pass, "missing authentication user", "-ap", "--auth-pass");
REQ_CMD(server_host, "missing server host", "-h", "--server-host"); REQ_CMD(server_host, "missing server host", "-h", "--server-host");
REQ_CMD(server_port, "missing server port", "-p", "--server-port"); REQ_CMD(server_port, "missing server port", "-p", "--server-port");
REQ_CMD(old_key, "missing old license key", "-ol", "--old-license");
auto state = evthread_use_pthreads(); auto state = evthread_use_pthreads();
if(state != 0) { if(state != 0) {
@ -96,6 +101,7 @@ int main(int argc, char **argv) {
return 1; return 1;
} }
std::string error{};
ServerConnection connection; ServerConnection connection;
connection.verbose = false; connection.verbose = false;
try { try {
@ -112,15 +118,24 @@ int main(int argc, char **argv) {
{ {
auto future = connection.login(auth_user, auth_pass); auto future = connection.login(auth_user, auth_pass);
if(!future.waitAndGet(false, system_clock::now() + seconds(5))) { if(!future.waitAndGet(false, system_clock::now() + seconds(5))) {
cerr << "failed to athentificate (" << future.errorMegssage() << ")" << endl; cerr << "failed to authenticate (" << future.errorMegssage() << ")" << endl;
return 1; return 1;
} }
}; };
std::shared_ptr<License> old_license{nullptr};
if(!old_key.empty() && old_key != "none") {
old_license = license::readLocalLicence(old_key, error);
if(!old_license) {
cerr << "failed to decode old license: " << error << endl;
return 1;
}
}
try { try {
system_clock::time_point timestamp_begin = system_clock::time_point() + seconds(stoll(begin)); system_clock::time_point timestamp_begin = system_clock::time_point() + seconds(stoll(begin));
system_clock::time_point timestamp_end = system_clock::time_point() + seconds(stoll(end)); system_clock::time_point timestamp_end = system_clock::time_point() + seconds(stoll(end));
auto future = connection.registerLicense(first_name, last_name, user, email, (LicenseType) stoll(license_type), timestamp_end, timestamp_begin); auto future = connection.registerLicense(first_name, last_name, user, email, (LicenseType) stoll(license_type), timestamp_end, timestamp_begin, old_license ? old_license->key() : "none");
auto result = future.waitAndGet({nullptr, nullptr}, system_clock::now() + seconds(5)); auto result = future.waitAndGet({nullptr, nullptr}, system_clock::now() + seconds(5));
if(!result.first || !result.second) { if(!result.first || !result.second) {
cerr << "failed to create license! (" << future.errorMegssage() << ")" << endl; cerr << "failed to create license! (" << future.errorMegssage() << ")" << endl;

View File

@ -34,7 +34,7 @@ using namespace license;
*/ */
bool handle_command(string& line); bool handle_command(string& line);
shared_ptr<server::LicenseManager> license_manager; shared_ptr<server::database::DatabaseHandler> license_manager;
shared_ptr<stats::StatisticManager> statistic_manager; shared_ptr<stats::StatisticManager> statistic_manager;
shared_ptr<ts::ssl::SSLManager> ssl_manager; shared_ptr<ts::ssl::SSLManager> ssl_manager;
shared_ptr<license::web::WebStatistics> web_server; shared_ptr<license::web::WebStatistics> web_server;
@ -102,14 +102,16 @@ int main(int argc, char** argv) {
evthread_use_pthreads(); evthread_use_pthreads();
srand(system_clock::now().time_since_epoch().count()); srand(system_clock::now().time_since_epoch().count());
terminal::install();
if(!terminal::active()){ cerr << "could not setup terminal!" << endl; return -1; } //terminal::install();
//if(!terminal::active()){ cerr << "could not setup terminal!" << endl; return -1; }
auto config = std::make_shared<logger::LoggerConfig>(); auto config = std::make_shared<logger::LoggerConfig>();
config->vs_group_size = 0; config->vs_group_size = 0;
config->logfileLevel = spdlog::level::trace; config->logfileLevel = spdlog::level::trace;
config->terminalLevel = spdlog::level::trace; config->terminalLevel = spdlog::level::trace;
config->logPath = "logs/log_${time}(%Y-%m-%d_%H:%M:%S).log"; config->logPath = "logs/log_${time}(%Y-%m-%d_%H:%M:%S).log";
config->sync = !terminal::active();
logger::setup(config); logger::setup(config);
string error; string error;
@ -138,7 +140,7 @@ int main(int argc, char** argv) {
}, (void*) nullptr) << endl; }, (void*) nullptr) << endl;
#endif #endif
license_manager = make_shared<server::LicenseManager>(database); license_manager = make_shared<server::database::DatabaseHandler>(database);
if(!license_manager->setup(error)) { if(!license_manager->setup(error)) {
logError(LOG_GENERAL, "Could not start license manager! (" +error + ")"); logError(LOG_GENERAL, "Could not start license manager! (" +error + ")");
return 0; return 0;
@ -147,6 +149,7 @@ int main(int argc, char** argv) {
statistic_manager = make_shared<stats::StatisticManager>(license_manager); statistic_manager = make_shared<stats::StatisticManager>(license_manager);
#if false
/* /*
{ {
auto _now = system_clock::now(); auto _now = system_clock::now();
@ -163,6 +166,9 @@ int main(int argc, char** argv) {
} }
return 0; return 0;
*/ */
#endif
#if false
/* /*
{ {
ofstream _file_out("version_history.txt"); ofstream _file_out("version_history.txt");
@ -229,6 +235,7 @@ int main(int argc, char** argv) {
} }
return 0; return 0;
*/ */
#endif
ssl_manager = make_shared<ts::ssl::SSLManager>(); ssl_manager = make_shared<ts::ssl::SSLManager>();
{ {
@ -268,17 +275,20 @@ int main(int argc, char** argv) {
} }
while(db_connected && web_server->running() && license_server->isRunning()) { while(db_connected && web_server->running() && license_server->isRunning()) {
if(!terminal::instance()) {
std::this_thread::sleep_for(std::chrono::seconds{10});
continue;
}
auto line = terminal::instance()->readLine("§a> §f"); auto line = terminal::instance()->readLine("§a> §f");
if(line.empty()){ if(line.empty()){
usleep(500); usleep(500);
continue; continue;
} }
if(!handle_command(line)) { if(!handle_command(line))
terminal::instance()->writeMessage("§aStopping server...");
break; break;
}
} }
terminal::instance()->writeMessage("§aStopping server...");
web_server->stop(); web_server->stop();
license_server->stop(); license_server->stop();
if(database) database->disconnect(); if(database) database->disconnect();

View File

@ -50,8 +50,8 @@ threads::Future<bool> ServerConnection::connect(const std::string &host, uint16_
if(this->network.file_descriptor < 0) CERR("Socket setup failed"); if(this->network.file_descriptor < 0) CERR("Socket setup failed");
if(::connect(this->network.file_descriptor, reinterpret_cast<const sockaddr *>(&this->network.address_remote), sizeof(this->network.address_remote)) < 0) CERR("connect() failed (" + to_string(errno) + " | " + strerror(errno) + ")"); if(::connect(this->network.file_descriptor, reinterpret_cast<const sockaddr *>(&this->network.address_remote), sizeof(this->network.address_remote)) < 0) CERR("connect() failed (" + to_string(errno) + " | " + strerror(errno) + ")");
int enabled = 1, disabled = 0; int enabled = 1, disabled = 0;
if(setsockopt(this->network.file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) < 0) CERR("could not set reuse addr"); if(setsockopt(this->network.file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) < 0); // CERR("could not set reuse addr");
if(setsockopt(this->network.file_descriptor, IPPROTO_TCP, TCP_CORK, &disabled, sizeof(disabled)) < 0) CERR("could not set no push"); if(setsockopt(this->network.file_descriptor, IPPROTO_TCP, TCP_CORK, &disabled, sizeof(disabled)) < 0); // CERR("could not set no push");
this->network.event_base = event_base_new(); this->network.event_base = event_base_new();
this->network.event_read = event_new(this->network.event_base, this->network.file_descriptor, EV_READ | EV_PERSIST, ServerConnection::handleEventRead, this); this->network.event_read = event_new(this->network.event_base, this->network.file_descriptor, EV_READ | EV_PERSIST, ServerConnection::handleEventRead, this);
@ -64,7 +64,7 @@ threads::Future<bool> ServerConnection::connect(const std::string &host, uint16_
cout << "ev ended!" << endl; cout << "ev ended!" << endl;
}}; }};
this->network.state = ConnectionState::CONNECTED; this->network.state = ConnectionState::CONNECTED;
this->protocol.state = protocol::HANDSCAKE; this->protocol.state = protocol::HANDSCHAKE;
this->protocol.ping_thread = thread([&]{ this->protocol.ping_thread = thread([&]{
while(true) { while(true) {
{ {
@ -83,7 +83,7 @@ threads::Future<bool> ServerConnection::connect(const std::string &host, uint16_
handshakeBuffer[0] = 0xC0; handshakeBuffer[0] = 0xC0;
handshakeBuffer[1] = 0xFF; handshakeBuffer[1] = 0xFF;
handshakeBuffer[2] = 0xEE; handshakeBuffer[2] = 0xEE;
handshakeBuffer[3] = LICENSE_PROT_VERSION; handshakeBuffer[3] = 2;
handshakeBuffer[4] = 1; //Im a manager handshakeBuffer[4] = 1; //Im a manager
this->sendPacket(protocol::packet{protocol::PACKET_CLIENT_HANDSHAKE, string((const char*) handshakeBuffer, 5)}); //Initialise packet this->sendPacket(protocol::packet{protocol::PACKET_CLIENT_HANDSHAKE, string((const char*) handshakeBuffer, 5)}); //Initialise packet
}).detach(); }).detach();
@ -156,8 +156,8 @@ void ServerConnection::handleEventRead(int fd, short, void* _connection) {
auto connection = (ServerConnection*) _connection; auto connection = (ServerConnection*) _connection;
char buffer[1024]; char buffer[1024];
auto read = recv(fd, buffer, 1024, SOCK_NONBLOCK); auto read = recv(fd, buffer, 1024, MSG_DONTWAIT);
if(read < 0) { if(read <= 0) {
if(connection->verbose) if(connection->verbose)
cout << "Invalid read: " << strerror(errno) << endl; cout << "Invalid read: " << strerror(errno) << endl;
connection->local_disconnect_message = "invalid read"; connection->local_disconnect_message = "invalid read";

View File

@ -25,94 +25,93 @@ do { \
} while(0) } while(0)
namespace license { namespace license::manager {
namespace manager { enum ConnectionState {
enum ConnectionState { UNCONNECTED,
UNCONNECTED, CONNECTING,
CONNECTING, CONNECTED,
CONNECTED, DISCONNECTING
DISCONNECTING };
}; class ServerConnection {
class ServerConnection { public:
public: ServerConnection();
ServerConnection(); ~ServerConnection();
~ServerConnection();
threads::Future<bool> connect(const std::string& host, uint16_t port); threads::Future<bool> connect(const std::string& host, uint16_t port);
void disconnect(const std::string&); void disconnect(const std::string&);
void ping(); void ping();
threads::Future<bool> login(const std::string&, const std::string&); threads::Future<bool> login(const std::string&, const std::string&);
threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<license::LicenseInfo>>> registerLicense( threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<license::LicenseInfo>>> registerLicense(
const std::string& first_name, const std::string& first_name,
const std::string& last_name, const std::string& last_name,
const std::string& username, const std::string& username,
const std::string& email, const std::string& email,
license::LicenseType type, license::LicenseType type,
const std::chrono::system_clock::time_point& end, const std::chrono::system_clock::time_point& end,
const std::chrono::system_clock::time_point& begin = std::chrono::system_clock::now() const std::chrono::system_clock::time_point& begin,
); const std::string& old_license
);
threads::Future<std::map<std::string, std::shared_ptr<license::LicenseInfo>>> list(int offset, int count); threads::Future<std::map<std::string, std::shared_ptr<license::LicenseInfo>>> list(int offset, int count);
threads::Future<bool> deleteLicense(const std::string& key, bool full = false); threads::Future<bool> deleteLicense(const std::string& key, bool full = false);
bool verbose = true; bool verbose = true;
private: private:
struct { struct {
ConnectionState state = ConnectionState::UNCONNECTED; ConnectionState state = ConnectionState::UNCONNECTED;
sockaddr_in address_remote; sockaddr_in address_remote;
int file_descriptor = 0; int file_descriptor = 0;
event* event_read = nullptr; event* event_read = nullptr;
event* event_write = nullptr; event* event_write = nullptr;
struct event_base* event_base = nullptr; struct event_base* event_base = nullptr;
std::thread event_base_dispatch; std::thread event_base_dispatch;
threads::Thread* flush_thread = nullptr; threads::Thread* flush_thread = nullptr;
threads::Mutex queue_lock; threads::Mutex queue_lock;
std::deque<std::string> queue_write; std::deque<std::string> queue_write;
std::unique_ptr<protocol::packet> current_packet; std::unique_ptr<protocol::packet> current_packet;
std::string overhead; std::string overhead;
} network; } network;
struct { struct {
protocol::RequestState state; protocol::RequestState state;
std::string crypt_key = ""; std::string crypt_key = "";
std::mutex ping_lock; std::mutex ping_lock;
std::condition_variable ping_notify; std::condition_variable ping_notify;
std::thread ping_thread; std::thread ping_thread;
} protocol; } protocol;
struct { struct {
std::unique_ptr<threads::Future<bool>> future_connect; std::unique_ptr<threads::Future<bool>> future_connect;
std::unique_ptr<threads::Future<bool>> future_login; std::unique_ptr<threads::Future<bool>> future_login;
std::unique_ptr<threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<license::LicenseInfo>>>> future_register; std::unique_ptr<threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<license::LicenseInfo>>>> future_register;
std::unique_ptr<threads::Future<std::map<std::string, std::shared_ptr<license::LicenseInfo>>>> future_list; std::unique_ptr<threads::Future<std::map<std::string, std::shared_ptr<license::LicenseInfo>>>> future_list;
std::unique_ptr<threads::Future<bool>> future_delete; std::unique_ptr<threads::Future<bool>> future_delete;
} listener; } listener;
std::string local_disconnect_message; std::string local_disconnect_message;
static void handleEventRead(int, short, void*); static void handleEventRead(int, short, void*);
static void handleEventWrite(int, short, void*); static void handleEventWrite(int, short, void*);
void closeConnection(); void closeConnection();
void sendPacket(const protocol::packet&); void sendPacket(const protocol::packet&);
void handleMessage(const std::string&); void handleMessage(const std::string&);
void handlePacketDisconnect(const std::string&); void handlePacketDisconnect(const std::string&);
void handlePacketHandshake(const std::string&); void handlePacketHandshake(const std::string&);
void handlePacketAuthResponse(const std::string&); void handlePacketAuthResponse(const std::string&);
void handlePacketCreateResponse(const std::string&); void handlePacketCreateResponse(const std::string&);
void handlePacketListResponse(const std::string&); void handlePacketListResponse(const std::string&);
void handlePacketDeleteResponse(const std::string&); void handlePacketDeleteResponse(const std::string&);
}; };
}
} }

View File

@ -35,7 +35,8 @@ threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<lic
const std::string &email, const std::string &email,
license::LicenseType type, license::LicenseType type,
const std::chrono::system_clock::time_point& end, const std::chrono::system_clock::time_point& end,
const std::chrono::system_clock::time_point& start const std::chrono::system_clock::time_point& start,
const std::string& old_license
) { ) {
this->listener.future_register = std::make_unique<threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<license::LicenseInfo>>>>(); this->listener.future_register = std::make_unique<threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<license::LicenseInfo>>>>();
@ -48,6 +49,9 @@ threads::Future<std::pair<std::shared_ptr<license::License>, std::shared_ptr<lic
request.set_type(type); request.set_type(type);
request.set_begin(duration_cast<milliseconds>(start.time_since_epoch()).count()); request.set_begin(duration_cast<milliseconds>(start.time_since_epoch()).count());
request.set_end(duration_cast<milliseconds>(end.time_since_epoch()).count()); request.set_end(duration_cast<milliseconds>(end.time_since_epoch()).count());
if(!old_license.empty() && old_license != "none")
request.set_old_key(old_license);
this->sendPacket({protocol::PACKET_CLIENT_LICENSE_CREATE_REQUEST, request}); this->sendPacket({protocol::PACKET_CLIENT_LICENSE_CREATE_REQUEST, request});
if(this->network.state != ConnectionState::CONNECTED) if(this->network.state != ConnectionState::CONNECTED)

View File

@ -84,11 +84,11 @@ void ServerConnection::handlePacketDisconnect(const std::string& message) {
} }
void ServerConnection::handlePacketHandshake(const std::string& data) { void ServerConnection::handlePacketHandshake(const std::string& data) {
if(this->protocol.state != protocol::HANDSCAKE) LERROR("Protocol state mismatch"); if(this->protocol.state != protocol::HANDSCHAKE) LERROR("Protocol state mismatch");
if(data.length() < 3) LERROR("Invalid packet size"); if(data.length() < 3) LERROR("Invalid packet size");
if((uint8_t) data[0] != 0xAF || (uint8_t) data[1] != 0xFE) LERROR("Invalid handshake"); if((uint8_t) data[0] != 0xAF || (uint8_t) data[1] != 0xFE) LERROR("Invalid handshake");
if((uint8_t) data[2] != LICENSE_PROT_VERSION) LERROR("Invalid license protocol version. Please update TeaSpeak!"); if((uint8_t) data[2] != 2) LERROR("Invalid license protocol version. Please update this client!");
auto key_length = be2le16(data.data(), 3); auto key_length = be2le16(data.data(), 3);
if(data.length() < key_length + 3) LERROR("Invalid packet size"); if(data.length() < key_length + 3) LERROR("Invalid packet size");

View File

@ -32,6 +32,9 @@ message LicenseCreateRequest {
required int64 type = 5; required int64 type = 5;
required int64 begin = 6; required int64 begin = 6;
required int64 end = 7; required int64 end = 7;
/* if set the license will upgrade automatically */
optional bytes old_key = 8;
} }
message LicenseCreateResponse { message LicenseCreateResponse {
oneof result { oneof result {

View File

@ -40,13 +40,16 @@ message ServerValidation {
required bool licensed = 1; required bool licensed = 1;
required bool license_info = 2; required bool license_info = 2;
optional bytes license = 3; optional bytes license = 3;
optional ServerInfo info = 4; //Change somewhen to required but its currently for lagacy support optional ServerInfo info = 4; //Change somewhere to required but its currently for legacy support
} }
message LicenseResponse { message LicenseResponse {
required bool valid = 1; //If set the license is valid. The blacklist field could still be active. If the flag is false then the blacklist will container the message required bool valid = 1;
optional string invalid_reason = 5; /* in protocol version 2 the blacklist.reason field will contain the message */
required Blacklist blacklist = 2; required Blacklist blacklist = 2;
optional LicenseInfo license_info = 3; //Only availible when ServerValidation::license_info = true optional LicenseInfo license_info = 3; //Only available when ServerValidation::license_info = true
optional bool update_pending = 4; /* if an update is pending */
} }
message PropertyUpdateRequest { message PropertyUpdateRequest {
@ -64,6 +67,15 @@ message PropertyUpdateRequest {
optional bytes web_cert_revision = 12; optional bytes web_cert_revision = 12;
} }
message RequestLicenseUpgrade { }
message LicenseUpgradeResponse {
required bool valid = 1;
oneof result {
bytes license_key = 2;
string error_message = 3;
}
}
message WebCertificate { message WebCertificate {
required bytes revision = 1; required bytes revision = 1;
required string key = 2; required string key = 2;

View File

@ -1,16 +1,17 @@
#include "LicenseManager.h" #include "DatabaseHandler.h"
#include <misc/base64.h>
#include <log/LogUtils.h> #include <log/LogUtils.h>
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
using namespace license; using namespace license;
using namespace license::server; using namespace license::server::database;
using namespace sql; using namespace sql;
LicenseManager::LicenseManager(sql::SqlManager* database) : database(database){ DatabaseHandler::DatabaseHandler(sql::SqlManager* database) : database(database){
this->id_cache = make_shared<KeyIdCache>(this); this->id_cache = make_shared<KeyIdCache>(this);
} }
LicenseManager::~LicenseManager() { } DatabaseHandler::~DatabaseHandler() { }
/* /*
LicenseType type; LicenseType type;
@ -40,7 +41,7 @@ res = command(this->database, cmd).execute(); \
version = ver; \ version = ver; \
sql::command(this->database, "UPDATE `general` SET `value` = :version WHERE `key` = 'version'", variable{":version", version}).execute(); sql::command(this->database, "UPDATE `general` SET `value` = :version WHERE `key` = 'version'", variable{":version", version}).execute();
bool LicenseManager::setup(std::string& error) { bool DatabaseHandler::setup(std::string& error) {
result res; result res;
int version = -1; int version = -1;
sql::command(this->database, "SELECT `value` FROM `general` WHERE `key` = 'version'").query([](int* version, int lnegth, string* values, string* names) { sql::command(this->database, "SELECT `value` FROM `general` WHERE `key` = 'version'").query([](int* version, int lnegth, string* values, string* names) {
@ -106,20 +107,33 @@ bool LicenseManager::setup(std::string& error) {
case 4: case 4:
CTBL("CREATE TABLE IF NOT EXISTS `users` (`username` VARCHAR(64) NOT NULL PRIMARY KEY, `password_hash` VARCHAR(128), `status` INT, `owner` VARCHAR(64))"); CTBL("CREATE TABLE IF NOT EXISTS `users` (`username` VARCHAR(64) NOT NULL PRIMARY KEY, `password_hash` VARCHAR(128), `status` INT, `owner` VARCHAR(64))");
SET_VERSION(5);
case 5:
CTBL("CREATE TABLE `license_upgrades` (`upgrade_id` INT PRIMARY KEY NOT NULL, `old_key_id` INT, `new_key_id` INT, `timestamp_begin` BIGINT, `timestamp_end` BIGINT, `valid` INT, `use_count` INT, `license` BLOB);");
CTBL("ALTER TABLE `license_upgrades` ADD INDEX(`old_key_id`)");
CTBL("ALTER TABLE `license` ADD INDEX(`keyId`);");
CTBL("ALTER TABLE `license` ADD COLUMN `upgrade_id` INT DEFAULT 0;");
SET_VERSION(6);
case 6:
CTBL("CREATE TABLE license_upgrade_log (`upgrade_id` INT, `timestamp` INT, `unique_id` VARCHAR(64), `server_ip` INT, `succeeded` TINYINT);");
CIDX("CREATE INDEX `upgrade_id_timestamp` ON `license_upgrade_log` (`upgrade_id`, `timestamp`)");
SET_VERSION(7);
default:; default:;
} }
return true; return true;
} }
bool LicenseManager::registerLicense(const std::string& key, const shared_ptr<LicenseInfo>& info, const std::string& issuer) { bool DatabaseHandler::register_license(const std::string& key, const shared_ptr<LicenseInfo>& info, const std::string& issuer) {
result res; result res;
res = command(this->database, "INSERT INTO `license` (`key`, `type`, `deleted`, `issuer`) VALUES (:key, :type, :deleted, :issuer)", variable{":key", key}, variable{":type", (uint32_t) info->type}, variable{":deleted", false}, variable{":issuer", issuer}).execute(); res = command(this->database, "INSERT INTO `license` (`key`, `type`, `deleted`, `issuer`) VALUES (:key, :type, :deleted, :issuer)", variable{":key", key}, variable{":type", (uint32_t) info->type}, variable{":deleted", false}, variable{":issuer", issuer}).execute();
if(!res) { if(!res) {
logError(LOG_GENERAL, "Could not register new license (" + res.fmtStr() + ")"); logError(LOG_GENERAL, "Could not register new license (" + res.fmtStr() + ")");
return false; return false;
} }
auto keyId = this->id_cache->getKeyId(key); auto keyId = this->id_cache->get_key_id_from_key(key);
if(keyId == 0) return false; if(keyId == 0) return false;
res = command(this->database, "INSERT INTO `license_info` (`keyId`, `username`, `first_name`, `last_name`, `email`, `begin`, `end`, `generated`) VALUES (:key, :username, :first_name, :last_name, :email, :begin, :end, :generated)", res = command(this->database, "INSERT INTO `license_info` (`keyId`, `username`, `first_name`, `last_name`, `email`, `begin`, `end`, `generated`) VALUES (:key, :username, :first_name, :last_name, :email, :begin, :end, :generated)",
@ -139,9 +153,9 @@ bool LicenseManager::registerLicense(const std::string& key, const shared_ptr<Li
return true; return true;
} }
bool LicenseManager::deleteLicense(const std::string& key, bool full) { bool DatabaseHandler::delete_license(const std::string& key, bool full) {
if(full) { if(full) {
auto keyId = this->id_cache->getKeyId(key); auto keyId = this->id_cache->get_key_id_from_key(key);
if(keyId == 0) return false; //Never exists if(keyId == 0) return false; //Never exists
auto res = command(this->database, "DELETE FROM `license` WHERE `key` = :key", variable{":key", key}).execute(); auto res = command(this->database, "DELETE FROM `license` WHERE `key` = :key", variable{":key", key}).execute();
@ -156,7 +170,7 @@ bool LicenseManager::deleteLicense(const std::string& key, bool full) {
} }
} }
bool LicenseManager::validLicenseKey(const std::string& key) { bool DatabaseHandler::validLicenseKey(const std::string& key) {
bool valid = false; bool valid = false;
auto res = command(this->database, "SELECT * FROM `license` WHERE `key` = :key AND `deleted` = :false LIMIT 1", variable{":false", false}, variable{":key", key}).query([](bool* flag, int, char**, char**) { auto res = command(this->database, "SELECT * FROM `license` WHERE `key` = :key AND `deleted` = :false LIMIT 1", variable{":false", false}, variable{":key", key}).query([](bool* flag, int, char**, char**) {
*flag = true; *flag = true;
@ -166,10 +180,10 @@ bool LicenseManager::validLicenseKey(const std::string& key) {
return !!res && valid; return !!res && valid;
} }
inline std::map<std::string, std::shared_ptr<LicenseInfo>> query_license(SqlManager* mgr, std::string key, int offset, int length) { inline std::map<std::string, std::shared_ptr<LicenseInfo>> query_license(SqlManager* mgr, const std::string& key, int offset, int length) {
std::map<std::string, std::shared_ptr<LicenseInfo>> result; std::map<std::string, std::shared_ptr<LicenseInfo>> result;
auto query = string() + "SELECT `key`, `username`, `first_name`, `last_name`, `email`, `begin`, `end`, `generated`, `deleted` FROM `license_info` INNER JOIN `license` ON `license_info`.`keyId` = `license`.`keyId`"; auto query = string() + "SELECT `key`, `username`, `first_name`, `last_name`, `email`, `begin`, `end`, `generated`, `deleted`, `upgrade_id` FROM `license_info` INNER JOIN `license` ON `license_info`.`keyId` = `license`.`keyId`";
if(!key.empty()) if(!key.empty())
query += "WHERE `key` = :key"; query += "WHERE `key` = :key";
else else
@ -203,6 +217,8 @@ inline std::map<std::string, std::shared_ptr<LicenseInfo>> query_license(SqlMana
info->creation = system_clock::time_point() + milliseconds(stoll(values[index])); info->creation = system_clock::time_point() + milliseconds(stoll(values[index]));
else if(names[index] == "deleted") else if(names[index] == "deleted")
info->deleted = values[index] == "1" || values[index] == "true"; info->deleted = values[index] == "1" || values[index] == "true";
else if(names[index] == "upgrade_id")
info->upgrade_id = std::stol(values[index]);
else else
logError(LOG_GENERAL, "Unknown field {}", names[index]); logError(LOG_GENERAL, "Unknown field {}", names[index]);
} catch (std::exception& ex) { } catch (std::exception& ex) {
@ -218,20 +234,20 @@ inline std::map<std::string, std::shared_ptr<LicenseInfo>> query_license(SqlMana
return result; return result;
} }
std::shared_ptr<LicenseInfo> LicenseManager::licenseInfo(const std::string& key) { std::shared_ptr<LicenseInfo> DatabaseHandler::query_license_info(const std::string& key) {
auto result = query_license(this->database, key, 0, 0); auto result = query_license(this->database, key, 0, 0);
if(result.empty()) return nullptr; if(result.empty()) return nullptr;
return result.begin()->second; return result.begin()->second;
} }
std::map<std::string, std::shared_ptr<LicenseInfo>> LicenseManager::listLicenses(int offset, int limit) { std::map<std::string, std::shared_ptr<LicenseInfo>> DatabaseHandler::list_licenses(int offset, int limit) {
return query_license(this->database, "", offset, limit); return query_license(this->database, "", offset, limit);
} }
bool LicenseManager::logRequest(const std::string& key, const std::string& unique_id, const std::string& ip, const std::string& version, int state) { bool DatabaseHandler::logRequest(const std::string& key, const std::string& unique_id, const std::string& ip, const std::string& version, int state) {
result res; result res;
auto keyId = this->id_cache->getKeyId(key); auto keyId = this->id_cache->get_key_id_from_key(key);
if(keyId == 0) { if(keyId == 0) {
logError(LOG_GENERAL, "Failed to log license request (could not resolve key id)"); logError(LOG_GENERAL, "Failed to log license request (could not resolve key id)");
return false; return false;
@ -264,11 +280,11 @@ bool LicenseManager::logRequest(const std::string& key, const std::string& uniqu
return true; return true;
} }
bool LicenseManager::logStatistic(const std::string &key, const std::string& unique_id, const std::string &ip, bool DatabaseHandler::logStatistic(const std::string &key, const std::string& unique_id, const std::string &ip,
const ts::proto::license::PropertyUpdateRequest &data) { const ts::proto::license::PropertyUpdateRequest &data) {
result res; result res;
auto keyId = this->id_cache->getKeyId(key); auto keyId = this->id_cache->get_key_id_from_key(key);
if(keyId == 0) return false; if(keyId == 0) return false;
auto time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); auto time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
@ -305,7 +321,7 @@ bool LicenseManager::logStatistic(const std::string &key, const std::string& uni
return true; return true;
} }
std::shared_ptr<LicenseManager::UserHistory> LicenseManager::list_statistics_user(const system_clock::time_point &begin, const system_clock::time_point &end, const milliseconds &interval) { std::shared_ptr<DatabaseHandler::UserHistory> DatabaseHandler::list_statistics_user(const system_clock::time_point &begin, const system_clock::time_point &end, const milliseconds &interval) {
auto timeout = hours(2) + minutes(10); //TODO timeout configurable? auto timeout = hours(2) + minutes(10); //TODO timeout configurable?
/* initialize the result */ /* initialize the result */
@ -328,11 +344,11 @@ std::shared_ptr<LicenseManager::UserHistory> LicenseManager::list_statistics_use
auto info = &_result->history[0]; auto info = &_result->history[0];
/* temp db variables */ /* temp db variables */
map<std::string, LicenseManager::DatabaseUserStatistics> server_statistics; map<std::string, DatabaseHandler::DatabaseUserStatistics> server_statistics;
bool have_key, have_uid; bool have_key, have_uid;
LicenseManager::DatabaseUserStatistics temp_stats; /* temp struct for stats parsing */ DatabaseHandler::DatabaseUserStatistics temp_stats; /* temp struct for stats parsing */
LicenseManager::DatabaseUserStatistics* stats_ptr; /* pointer to the target stats */ DatabaseHandler::DatabaseUserStatistics* stats_ptr; /* pointer to the target stats */
std::chrono::system_clock::time_point current_timestamp = begin + interval; /* upper limit of the current interval */ std::chrono::system_clock::time_point current_timestamp = begin + interval; /* upper limit of the current interval */
auto state = command(this->database, "SELECT * FROM `history_online` WHERE `timestamp` >= :timestamp_start AND `timestamp` <= :timestamp_end ORDER BY `timestamp` ASC", auto state = command(this->database, "SELECT * FROM `history_online` WHERE `timestamp` >= :timestamp_start AND `timestamp` <= :timestamp_end ORDER BY `timestamp` ASC",
@ -440,8 +456,8 @@ std::shared_ptr<LicenseManager::UserHistory> LicenseManager::list_statistics_use
return result; return result;
} }
std::deque<std::unique_ptr<LicenseManager::GlobalVersionsStatistic>> LicenseManager::list_statistics_version(const std::chrono::system_clock::time_point &begin, const std::chrono::system_clock::time_point &end, const std::chrono::milliseconds &interval) { std::deque<std::unique_ptr<DatabaseHandler::GlobalVersionsStatistic>> DatabaseHandler::list_statistics_version(const std::chrono::system_clock::time_point &begin, const std::chrono::system_clock::time_point &end, const std::chrono::milliseconds &interval) {
map<std::string, deque<unique_ptr<LicenseManager::GlobalVersionsStatistic>>> server_statistics; map<std::string, deque<unique_ptr<DatabaseHandler::GlobalVersionsStatistic>>> server_statistics;
auto timeout = hours(2) + minutes(10); //TODO timeout configurable? auto timeout = hours(2) + minutes(10); //TODO timeout configurable?
auto state = command(this->database, "SELECT * FROM `history_version` WHERE `timestamp` >= :timestamp_start AND `timestamp` <= :timestamp_end ORDER BY `timestamp` ASC", auto state = command(this->database, "SELECT * FROM `history_version` WHERE `timestamp` >= :timestamp_start AND `timestamp` <= :timestamp_end ORDER BY `timestamp` ASC",
@ -450,7 +466,7 @@ std::deque<std::unique_ptr<LicenseManager::GlobalVersionsStatistic>> LicenseMana
).query([&server_statistics](int columns, std::string* values, std::string* names){ ).query([&server_statistics](int columns, std::string* values, std::string* names){
size_t key_id = 0; size_t key_id = 0;
std::string unique_id; std::string unique_id;
auto info = make_unique<LicenseManager::GlobalVersionsStatistic>(); auto info = make_unique<DatabaseHandler::GlobalVersionsStatistic>();
for(int index = 0; index < columns; index++) { for(int index = 0; index < columns; index++) {
try { try {
if(names[index] == "keyId") if(names[index] == "keyId")
@ -477,10 +493,10 @@ std::deque<std::unique_ptr<LicenseManager::GlobalVersionsStatistic>> LicenseMana
return {}; return {};
} }
std::deque<std::unique_ptr<LicenseManager::GlobalVersionsStatistic>> result; std::deque<std::unique_ptr<DatabaseHandler::GlobalVersionsStatistic>> result;
system_clock::time_point current_timestamp = begin; system_clock::time_point current_timestamp = begin;
while(current_timestamp <= end) { while(current_timestamp <= end) {
auto info = make_unique<LicenseManager::GlobalVersionsStatistic>(); auto info = make_unique<DatabaseHandler::GlobalVersionsStatistic>();
info->timestamp = current_timestamp; info->timestamp = current_timestamp;
for(auto& server : server_statistics) { for(auto& server : server_statistics) {
@ -511,4 +527,87 @@ std::deque<std::unique_ptr<LicenseManager::GlobalVersionsStatistic>> LicenseMana
} }
return result; return result;
}
bool DatabaseHandler::register_license_upgrade(license_key_id_t old_key_id, license_key_id_t new_key_id,
const std::chrono::system_clock::time_point &begin_timestamp, const std::chrono::system_clock::time_point &end_timestamp, const std::string &license_key) {
auto upgrade_id = std::chrono::system_clock::now().time_since_epoch().count();
auto sql_result = sql::command(this->sql(), "INSERT INTO `license_upgrades` (`upgrade_id`, `old_key_id`, `new_key_id`, `timestamp_begin`, `timestamp_end`, `valid`, `use_count`, `license`) VALUES"
"(:upgrade_id, :old_key_id, :new_key_id, :timestamp_begin, :timestamp_end, 1, 0, :license)",
variable{":upgrade_id", upgrade_id},
variable{":old_key_id", old_key_id},
variable{":new_key_id", new_key_id},
variable{":timestamp_begin", std::chrono::duration_cast<std::chrono::milliseconds>(begin_timestamp.time_since_epoch()).count()},
variable{":timestamp_end", std::chrono::duration_cast<std::chrono::milliseconds>(end_timestamp.time_since_epoch()).count()},
variable{":license", base64::decode(license_key)}).execute();
if(!sql_result) {
logError(LOG_GENERAL, "Failed to insert license upgrade: {}", sql_result.fmtStr());
return false;
}
sql_result = sql::command(this->sql(), "UPDATE `license` SET `upgrade_id` = :upgrade_id WHERE `keyId` = :key_id",
variable{":upgrade_id", upgrade_id},
variable{":key_id", old_key_id}).execute();
if(!sql_result) {
logError(LOG_GENERAL, "Failed to set license upgrade: {}", sql_result.fmtStr());
return false;
}
return true;
}
std::unique_ptr<LicenseUpgrade> DatabaseHandler::query_license_upgrade(upgrade_id_t id) {
std::unique_ptr<LicenseUpgrade> result{};
auto sql_result = sql::command(this->sql(), "SELECT `upgrade_id`, `old_key_id`, `new_key_id`, `timestamp_begin`, `timestamp_end`, `valid`, `use_count`, `license` FROM `license_upgrades` WHERE `upgrade_id` = :upgrade_id LIMIT 1",
variable{":upgrade_id", id}).query([&](int length, std::string* values, std::string* names) {
result = std::make_unique<LicenseUpgrade>();
for(size_t index = 0; index < length; index++) {
try {
if(names[index] == "upgrade_id")
result->upgrade_id = std::stoull(values[index]);
else if(names[index] == "old_key_id")
result->old_license_key_id = std::stoull(values[index]);
else if(names[index] == "new_key_id")
result->new_license_key_id = std::stoull(values[index]);
else if(names[index] == "timestamp_begin")
result->begin_timestamp = std::chrono::system_clock::time_point{} + std::chrono::milliseconds{std::stoull(values[index])};
else if(names[index] == "timestamp_end")
result->end_timestamp = std::chrono::system_clock::time_point{} + std::chrono::milliseconds{std::stoull(values[index])};
else if(names[index] == "use_count")
result->update_count = std::stoull(values[index]);
else if(names[index] == "valid")
result->valid = std::stoull(values[index]) > 0;
else if(names[index] == "license")
result->license_key = base64::encode(values[index]);
} catch(std::exception& ex) {
result = nullptr;
logWarning(LOG_GENERAL, "Failed to parse column {} for upgrade id {}. (Value: {})", names[index], id, values[index]);
return;
}
}
});
if(!sql_result) {
logWarning(LOG_GENERAL, "Failed to query license upgrade info for upgrade {}: {}", id, sql_result.fmtStr());
return nullptr;
}
return result;
}
void DatabaseHandler::log_license_upgrade_attempt(upgrade_id_t upgrade_id, bool succeeded, const std::string &unique_id, const std::string &ip) {
auto result = sql::command(this->sql(), "INSERT INTO `license_upgrade_log` (`upgrade_id`, `timestamp`, `unique_id`, `server_ip`, `succeeded`) VALUES (:upgrade_id, :timestamp, :unique_id, :server_ip, :succeeded);",
variable{":upgrade_id", upgrade_id},
variable{":timestamp", std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()},
variable{":unique_id", unique_id},
variable{":server_ip", ip},
variable{":succeeded", succeeded}).execute();
if(!result)
logWarning(LOG_GENERAL, "Failed to insert upgrade log into database ({})", result.fmtStr());
if(succeeded) {
result = sql::command(this->sql(), "UPDATE `license_upgrades` SET `use_count` = `use_count` + 1 WHERE `upgrade_id` = :upgrade_id",
variable{":upgrade_id", upgrade_id}).execute();
if(!result)
logWarning(LOG_GENERAL, "Failed to increase upgrade use count MySQL ({})", result.fmtStr());
}
} }

View File

@ -0,0 +1,125 @@
#pragma once
#include <sql/SqlQuery.h>
#include <shared/License.h>
#include <LicenseRequest.pb.h>
namespace license::server::database {
typedef size_t license_key_id_t;
typedef size_t upgrade_id_t;
class DatabaseHandler;
class KeyIdCache {
public:
explicit KeyIdCache(DatabaseHandler*);
std::string get_key_from_id(license_key_id_t keyId);
size_t get_key_id_from_key(const std::string &key);
void clear_cache();
private:
struct CacheEntry {
std::string key;
size_t keyId;
std::chrono::system_clock::time_point age;
};
int insert_entry(int, std::string*, std::string*);
DatabaseHandler* handle;
std::mutex entry_lock{};
std::deque<std::unique_ptr<CacheEntry>> entries;
};
struct LicenseUpgrade {
upgrade_id_t upgrade_id{0};
license_key_id_t old_license_key_id{0};
license_key_id_t new_license_key_id{0};
std::chrono::system_clock::time_point begin_timestamp{};
std::chrono::system_clock::time_point end_timestamp{};
bool valid{false};
size_t update_count{0};
std::string license_key{};
[[nodiscard]] inline bool not_yet_available() const {
return std::chrono::system_clock::now() < this->begin_timestamp && this->begin_timestamp.time_since_epoch().count() != 0;
}
[[nodiscard]] inline bool is_expired() const {
return std::chrono::system_clock::now() > this->end_timestamp && this->end_timestamp.time_since_epoch().count() != 0;
}
};
class DatabaseHandler {
public:
struct UserStatistics {
uint64_t clients_online = 0;
uint64_t web_clients_online = 0;
uint64_t bots_online = 0;
uint64_t queries_online = 0;
uint64_t servers_online = 0;
};
struct ExtendedUserStatistics : public UserStatistics {
std::string ip_address;
};
struct GlobalUserStatistics : public UserStatistics {
uint64_t instance_online = 0;
};
struct DatabaseUserStatistics : public UserStatistics {
std::chrono::system_clock::time_point timestamp{};
};
struct GlobalVersionsStatistic {
std::chrono::system_clock::time_point timestamp;
std::map<std::string, uint32_t> versions;
};
struct UserHistory {
std::chrono::system_clock::time_point begin{};
std::chrono::system_clock::time_point end{};
std::chrono::milliseconds interval{0};
size_t record_count = 0;
GlobalUserStatistics history[0];
};
static_assert(sizeof(UserHistory) == 32);
public:
explicit DatabaseHandler(sql::SqlManager* database);
~DatabaseHandler();
bool setup(std::string&);
[[nodiscard]] inline std::shared_ptr<KeyIdCache> key_id_cache() { return this->id_cache; }
bool validLicenseKey(const std::string& /* key */);
std::shared_ptr<LicenseInfo> query_license_info(const std::string&);
std::map<std::string, std::shared_ptr<LicenseInfo>> list_licenses(int offset = 0, int limit = -1);
bool register_license_upgrade(license_key_id_t /* old key */, license_key_id_t /* new key */, const std::chrono::system_clock::time_point& /* begin */, const std::chrono::system_clock::time_point& /* end */, const std::string& /* key */);
std::unique_ptr<LicenseUpgrade> query_license_upgrade(upgrade_id_t /* upgrade id */);
void log_license_upgrade_attempt(upgrade_id_t /* upgrade id */, bool /* succeeded */, const std::string& /* server unique id */, const std::string& /* ip address */);
bool register_license(const std::string& /* key */, const std::shared_ptr<LicenseInfo>& /* info */, const std::string& /* issuer */);
bool delete_license(const std::string& /* key */, bool /* full */ = false);
bool logRequest(const std::string& /* key */, const std::string& /* unique_id */, const std::string& /* ip */, const std::string& /* version */, int /* result */);
bool logStatistic(const std::string& /* key */, const std::string& /* unique_id */, const std::string& /* ip */, const ts::proto::license::PropertyUpdateRequest&);
//std::deque<std::unique_ptr<ExtendedUserStatistics>> list_statistics_user(const std::string& key, const std::chrono::system_clock::time_point& begin, const std::chrono::system_clock::time_point& end);
std::shared_ptr<UserHistory> list_statistics_user(const std::chrono::system_clock::time_point& begin, const std::chrono::system_clock::time_point& end, const std::chrono::milliseconds& /* interval */);
std::deque<std::unique_ptr<GlobalVersionsStatistic>> list_statistics_version(const std::chrono::system_clock::time_point& begin, const std::chrono::system_clock::time_point& end, const std::chrono::milliseconds& /* interval */);
inline sql::SqlManager* sql() { return this->database; }
private:
std::shared_ptr<KeyIdCache> id_cache;
sql::SqlManager* database;
};
}

View File

@ -1,26 +1,30 @@
#include "log/LogUtils.h" #include "log/LogUtils.h"
#include "LicenseManager.h" #include "DatabaseHandler.h"
using namespace license; using namespace license;
using namespace license::server; using namespace license::server::database;
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
using KeyIdCache = LicenseManager::KeyIdCache;
KeyIdCache::KeyIdCache(license::server::LicenseManager *handle) : handle(handle) {} KeyIdCache::KeyIdCache(DatabaseHandler *handle) : handle(handle) {}
std::string KeyIdCache::getKey(size_t keyId) { void KeyIdCache::clear_cache() {
std::lock_guard elock{this->entry_lock};
this->entries.clear();
}
std::string KeyIdCache::get_key_from_id(size_t keyId) {
{ {
threads::MutexLock lock(this->entry_lock); std::lock_guard elock{this->entry_lock};
for(const auto& entry : this->entries) for(const auto& entry : this->entries)
if(entry->keyId == keyId) return entry->key; if(entry->keyId == keyId) return entry->key;
} }
sql::command(this->handle->database, "SELECT `key`, `keyId` FROM `license` WHERE `keyId` = :key", variable{":key", keyId}) sql::command(this->handle->sql(), "SELECT `key`, `keyId` FROM `license` WHERE `keyId` = :key", variable{":key", keyId})
.query(&KeyIdCache::insert_entry, this); .query(&KeyIdCache::insert_entry, this);
{ {
threads::MutexLock lock(this->entry_lock); std::lock_guard elock{this->entry_lock};
for(const auto& entry : this->entries) for(const auto& entry : this->entries)
if(entry->keyId == keyId) return entry->key; if(entry->keyId == keyId) return entry->key;
@ -28,20 +32,20 @@ std::string KeyIdCache::getKey(size_t keyId) {
} }
} }
size_t KeyIdCache::getKeyId(const std::string &key) { size_t KeyIdCache::get_key_id_from_key(const std::string &key) {
{ {
threads::MutexLock lock(this->entry_lock); std::lock_guard elock{this->entry_lock};
for(const auto& entry : this->entries) for(const auto& entry : this->entries)
if(entry->key == key) return entry->keyId; if(entry->key == key) return entry->keyId;
} }
auto result = sql::command(this->handle->database, "SELECT `key`, `keyId` FROM `license` WHERE `key` = :key", variable{":key", key}) auto result = sql::command(this->handle->sql(), "SELECT `key`, `keyId` FROM `license` WHERE `key` = :key", variable{":key", key})
.query(&KeyIdCache::insert_entry, this); .query(&KeyIdCache::insert_entry, this);
if(!result) if(!result)
logError(LOG_GENERAL, "Failed to query key id for license. Query resulted in {}", result.fmtStr()); logError(LOG_GENERAL, "Failed to query key id for license. Query resulted in {}", result.fmtStr());
{ {
threads::MutexLock lock(this->entry_lock); std::lock_guard elock{this->entry_lock};
for(const auto& entry : this->entries) for(const auto& entry : this->entries)
if(entry->key == key) if(entry->key == key)
@ -52,16 +56,24 @@ size_t KeyIdCache::getKeyId(const std::string &key) {
} }
int KeyIdCache::insert_entry(int length, std::string *value, std::string *names) { int KeyIdCache::insert_entry(int length, std::string *value, std::string *names) {
string key; string key{"unknown"};
size_t keyId = 0; size_t keyId{0};
for(int index = 0; index < length; index++)
for(int index = 0; index < length; index++) {
if(names[index] == "key") if(names[index] == "key")
key = value[index]; key = value[index];
else if(names[index] == "keyId") else if(names[index] == "keyId")
keyId = stoll(value[index]); keyId = std::strtoll(value[index].c_str(), nullptr, 10);
}
if(!keyId) {
logWarning(LOG_GENERAL, "Failed to parse key id for key {}", key);
return 0;
}
{ {
threads::MutexLock lock(this->entry_lock); auto entry = new KeyIdCache::CacheEntry{key, keyId, system_clock::now()};
this->entries.push_back(new KeyIdCache::CacheEntry{key, keyId, system_clock::now()}); std::lock_guard elock{this->entry_lock};
this->entries.emplace_back(entry);
} }
return 0; return 0;
} }

View File

@ -1,95 +0,0 @@
#pragma once
#include <sql/SqlQuery.h>
#include <shared/License.h>
#include <LicenseRequest.pb.h>
namespace license {
namespace server {
class LicenseManager {
public:
class KeyIdCache {
struct CacheEntry {
std::string key;
size_t keyId;
std::chrono::system_clock::time_point age;
};
public:
KeyIdCache(LicenseManager*);
void cache_keys(const std::deque<std::string>& /* keys */);
std::string getKey(size_t keyId);
size_t getKeyId(const std::string&);
private:
int insert_entry(int, std::string*, std::string*);
LicenseManager* handle;
threads::Mutex entry_lock;
threads::Mutex entry_update_lock;
std::deque<CacheEntry*> entries;
};
struct UserStatistics {
uint64_t clients_online = 0;
uint64_t web_clients_online = 0;
uint64_t bots_online = 0;
uint64_t queries_online = 0;
uint64_t servers_online = 0;
};
struct ExtendedUserStatistics : public UserStatistics{
std::string ip_address;
};
struct GlobalUserStatistics : public UserStatistics {
uint64_t instance_online = 0;
};
struct DatabaseUserStatistics : public UserStatistics {
std::chrono::system_clock::time_point timestamp{};
};
struct GlobalVersionsStatistic {
std::chrono::system_clock::time_point timestamp;
std::map<std::string, uint32_t> versions;
};
struct UserHistory {
std::chrono::system_clock::time_point begin{};
std::chrono::system_clock::time_point end{};
std::chrono::milliseconds interval{0};
size_t record_count = 0;
GlobalUserStatistics history[0];
};
static_assert(sizeof(UserHistory) == 32);
public:
LicenseManager(sql::SqlManager* database);
~LicenseManager();
bool setup(std::string&);
bool validLicenseKey(const std::string& /* key */);
std::shared_ptr<LicenseInfo> licenseInfo(const std::string&);
std::map<std::string, std::shared_ptr<LicenseInfo>> listLicenses(int offset = 0, int limit = -1);
bool registerLicense(const std::string& /* key */, const std::shared_ptr<LicenseInfo>& /* info */, const std::string& /* issuer */);
bool updateLicenseInfo(const std::string&, const std::shared_ptr<LicenseInfo>&);
bool deleteLicense(const std::string& /* key */, bool /* full */ = false);
bool logRequest(const std::string& /* key */, const std::string& /* unique_id */, const std::string& /* ip */, const std::string& /* version */, int /* result */);
bool logStatistic(const std::string& /* key */, const std::string& /* unique_id */, const std::string& /* ip */, const ts::proto::license::PropertyUpdateRequest&);
//std::deque<std::unique_ptr<ExtendedUserStatistics>> list_statistics_user(const std::string& key, const std::chrono::system_clock::time_point& begin, const std::chrono::system_clock::time_point& end);
std::shared_ptr<UserHistory> list_statistics_user(const std::chrono::system_clock::time_point& begin, const std::chrono::system_clock::time_point& end, const std::chrono::milliseconds& /* interval */);
std::deque<std::unique_ptr<GlobalVersionsStatistic>> list_statistics_version(const std::chrono::system_clock::time_point& begin, const std::chrono::system_clock::time_point& end, const std::chrono::milliseconds& /* interval */);
inline sql::SqlManager* sql() { return this->database; }
private:
std::shared_ptr<KeyIdCache> id_cache;
sql::SqlManager* database;
};
}
}

View File

@ -9,10 +9,8 @@
#include <LicenseRequest.pb.h> #include <LicenseRequest.pb.h>
#include <shared/License.h> #include <shared/License.h>
#include <shared/crypt.h> #include <shared/crypt.h>
#include <misc/base64.h>
#include <ThreadPool/ThreadHelper.h> #include <ThreadPool/ThreadHelper.h>
#include "LicenseServer.h" #include "LicenseServer.h"
#include "crypt.h"
#include "UserManager.h" #include "UserManager.h"
using namespace std; using namespace std;
@ -21,7 +19,7 @@ using namespace license;
using namespace ts; using namespace ts;
LicenseServer::LicenseServer(const sockaddr_in& addr, LicenseServer::LicenseServer(const sockaddr_in& addr,
std::shared_ptr<server::LicenseManager> manager, std::shared_ptr<server::database::DatabaseHandler> manager,
shared_ptr<license::stats::StatisticManager> stats, shared_ptr<license::stats::StatisticManager> stats,
shared_ptr<license::web::WebStatistics> wstats, shared_ptr<license::web::WebStatistics> wstats,
std::shared_ptr<UserManager> user_manager) : manager{std::move(manager)}, statistics{std::move(stats)}, web_statistics{std::move(wstats)}, user_manager{std::move(user_manager)} { std::shared_ptr<UserManager> user_manager) : manager{std::move(manager)}, statistics{std::move(stats)}, web_statistics{std::move(wstats)}, user_manager{std::move(user_manager)} {
@ -226,7 +224,7 @@ void LicenseServer::handleEventRead(int fd, short, void* ptrServer) {
server->closeConnection(client); server->closeConnection(client);
return; return;
} else if(read == 0) { } else if(read == 0) {
logError(LOG_LICENSE_CONTROLL, "Invalid read. Disconnecting remote client"); logMessage(LOG_LICENSE_CONTROLL, "[CLIENT][" + client->address() + "] Received EOF for client. Removing client.");
event_del_noblock(client->network.readEvent); event_del_noblock(client->network.readEvent);
server->closeConnection(client); server->closeConnection(client);
return; return;
@ -253,7 +251,7 @@ void LicenseServer::handleEventAccept(int fd, short, void* ptrServer) {
return; return;
} }
client->protocol.state = protocol::HANDSCAKE; client->protocol.state = protocol::HANDSCHAKE;
{ {
lock_guard lock(server->client_lock); lock_guard lock(server->client_lock);
server->clients.push_back(client); server->clients.push_back(client);
@ -378,6 +376,8 @@ void LicenseServer::handleMessage(shared_ptr<ConnectedClient>& client, const std
success = this->handleServerValidation(client, packet, error); success = this->handleServerValidation(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_CLIENT_PROPERTY_ADJUSTMENT) { } else if(packet.header.packetId == protocol::PACKET_CLIENT_PROPERTY_ADJUSTMENT) {
success = this->handlePacketPropertyUpdate(client, packet, error); success = this->handlePacketPropertyUpdate(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_CLIENT_LICENSE_UPGRADE) {
success = this->handlePacketLicenseUpgrade(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_CLIENT_AUTH_REQUEST) { } else if(packet.header.packetId == protocol::PACKET_CLIENT_AUTH_REQUEST) {
success = this->handlePacketAuth(client, packet, error); success = this->handlePacketAuth(client, packet, error);
} else if(packet.header.packetId == protocol::PACKET_CLIENT_LICENSE_CREATE_REQUEST) { } else if(packet.header.packetId == protocol::PACKET_CLIENT_LICENSE_CREATE_REQUEST) {

View File

@ -11,7 +11,7 @@
#include <ThreadPool/Thread.h> #include <ThreadPool/Thread.h>
#include <shared/License.h> #include <shared/License.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include "LicenseManager.h" #include "DatabaseHandler.h"
namespace license { namespace license {
namespace web { namespace web {
@ -44,11 +44,14 @@ namespace license {
std::chrono::system_clock::time_point last_read; std::chrono::system_clock::time_point last_read;
std::string cryptKey = ""; std::string cryptKey = "";
int version{2}; /* current version is 3 */
} protocol; } protocol;
ClientType type = ClientType::SERVER; ClientType type = ClientType::SERVER;
std::string username; std::string username;
std::string key; std::string key;
uint64_t key_pending_upgrade{0};
std::string unique_identifier; std::string unique_identifier;
bool invalid_license = false; bool invalid_license = false;
@ -68,7 +71,7 @@ namespace license {
class LicenseServer { class LicenseServer {
public: public:
explicit LicenseServer(const sockaddr_in&, std::shared_ptr<server::LicenseManager> , std::shared_ptr<stats::StatisticManager> /* stats */, std::shared_ptr<web::WebStatistics> /* web stats */, std::shared_ptr<UserManager> /* user manager */); explicit LicenseServer(const sockaddr_in&, std::shared_ptr<server::database::DatabaseHandler> , std::shared_ptr<stats::StatisticManager> /* stats */, std::shared_ptr<web::WebStatistics> /* web stats */, std::shared_ptr<UserManager> /* user manager */);
~LicenseServer(); ~LicenseServer();
bool start(); bool start();
@ -91,7 +94,7 @@ namespace license {
std::shared_ptr<web::WebStatistics> web_statistics; std::shared_ptr<web::WebStatistics> web_statistics;
std::shared_ptr<stats::StatisticManager> statistics; std::shared_ptr<stats::StatisticManager> statistics;
std::shared_ptr<server::LicenseManager> manager; std::shared_ptr<server::database::DatabaseHandler> manager;
std::shared_ptr<UserManager> user_manager; std::shared_ptr<UserManager> user_manager;
std::mutex client_lock; std::mutex client_lock;
@ -117,6 +120,7 @@ namespace license {
bool handleDisconnect(std::shared_ptr<ConnectedClient>&, protocol::packet&, std::string& error); bool handleDisconnect(std::shared_ptr<ConnectedClient>&, protocol::packet&, std::string& error);
bool handleHandshake(std::shared_ptr<ConnectedClient>&, protocol::packet&, std::string& error); bool handleHandshake(std::shared_ptr<ConnectedClient>&, protocol::packet&, std::string& error);
bool handleServerValidation(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error); bool handleServerValidation(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error);
bool handlePacketLicenseUpgrade(std::shared_ptr<ConnectedClient> &client, protocol::packet &packet, std::string &error);
bool handlePacketPropertyUpdate(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error); bool handlePacketPropertyUpdate(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error);
bool handlePacketAuth(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error); bool handlePacketAuth(std::shared_ptr<ConnectedClient> &, protocol::packet &, std::string &error);

View File

@ -22,6 +22,8 @@ inline void generate(char* buffer, size_t length){
buffer[index] = rand(); buffer[index] = rand();
} }
#define _str(x) #x
#define TEST_PROTOCOL_STATE(expected) \ #define TEST_PROTOCOL_STATE(expected) \
if(client->protocol.state != protocol::expected) { \ if(client->protocol.state != protocol::expected) { \
error = "invalid protocol state"; \ error = "invalid protocol state"; \
@ -37,21 +39,23 @@ if(packet.data.length() < (expected)) { \
#define PARSE_PROTO(class, var) \ #define PARSE_PROTO(class, var) \
ts::proto::license::class var; \ ts::proto::license::class var; \
if(!var.ParseFromString(packet.data)) { \ if(!var.ParseFromString(packet.data)) { \
error = "invalid data!"; \ error = "invalid data (" _str(class) ")!"; \
return false; \ return false; \
} }
#define CRYPT_KEY_LENGTH 32 #define CRYPT_KEY_LENGTH 32
bool LicenseServer::handleHandshake(shared_ptr<ConnectedClient>& client, protocol::packet& packet, std::string &error) { bool LicenseServer::handleHandshake(shared_ptr<ConnectedClient>& client, protocol::packet& packet, std::string &error) {
TEST_PROTOCOL_STATE(HANDSCAKE); TEST_PROTOCOL_STATE(HANDSCHAKE);
ENSURE_PACKET_SIZE(4); ENSURE_PACKET_SIZE(4);
if((uint8_t) packet.data[0] != 0xC0 || (uint8_t) packet.data[1] != 0xFF || (uint8_t) packet.data[2] != 0xEE) { if((uint8_t) packet.data[0] != 0xC0 || (uint8_t) packet.data[1] != 0xFF || (uint8_t) packet.data[2] != 0xEE) {
error = "invalid magic!"; error = "invalid magic!";
return false; return false;
} }
if((uint8_t) packet.data[3] != LICENSE_PROT_VERSION) {
error = "invalid version!"; client->protocol.version = (uint8_t) packet.data[3];
return false; if(client->protocol.version < 2 || client->protocol.version > 3) {
error = "unsupported version";
return false;
} }
bool manager = false; bool manager = false;
@ -67,7 +71,7 @@ bool LicenseServer::handleHandshake(shared_ptr<ConnectedClient>& client, protoco
size_t buffer_index = 0; size_t buffer_index = 0;
buffer[buffer_index++] = 0xAF; buffer[buffer_index++] = 0xAF;
buffer[buffer_index++] = 0xFE; buffer[buffer_index++] = 0xFE;
buffer[buffer_index++] = LICENSE_PROT_VERSION; buffer[buffer_index++] = client->protocol.version;
le2be16(CRYPT_KEY_LENGTH, buffer, buffer_index, &buffer_index); le2be16(CRYPT_KEY_LENGTH, buffer, buffer_index, &buffer_index);
memcpy(&buffer[buffer_index], buffer_cryptkey, CRYPT_KEY_LENGTH); memcpy(&buffer[buffer_index], buffer_cryptkey, CRYPT_KEY_LENGTH);
buffer_index += CRYPT_KEY_LENGTH; buffer_index += CRYPT_KEY_LENGTH;
@ -134,94 +138,129 @@ std::string string_to_hex(const std::string& input)
} }
bool LicenseServer::handleServerValidation(shared_ptr<ConnectedClient> &client, protocol::packet &packet, std::string &error) { bool LicenseServer::handleServerValidation(shared_ptr<ConnectedClient> &client, protocol::packet &packet, std::string &error) {
TEST_PROTOCOL_STATE(SERVER_VALIDATION); if(client->protocol.state != protocol::LICENSE_UPGRADE) /* server may wants to verify new license */
TEST_PROTOCOL_STATE(SERVER_VALIDATION);
PARSE_PROTO(ServerValidation, pkt); PARSE_PROTO(ServerValidation, pkt);
shared_ptr<License> remoteLicense = nullptr; std::shared_ptr<License> remote_license{nullptr};
if(pkt.licensed() && !pkt.has_license()) { if(pkt.licensed() && (!pkt.has_license() || !pkt.has_license_info())) {
//TODO shutdown server error = "invalid/missing license data";
return false;
} }
if(!pkt.has_info()) { if(!pkt.has_info()) {
error = "invalid data or missing data"; error = "invalid data or missing data";
return false; return false;
} }
if(pkt.has_license()){ //Client has license
remoteLicense = readLocalLicence(pkt.license(), error); if(pkt.licensed()){ //Client has license
if(!remoteLicense) { remote_license = readLocalLicence(pkt.license(), error);
error = "Could not read remote key: " + error; if(!remote_license) {
error = "could not parse license (" + error + ")";
return false; return false;
}; }
logMessage(LOG_GENERAL, "[CLIENT][{}] Got remote license. Registered to {}. Key: {} (0x{})", client->address(), remoteLicense->owner(), base64::encode(remoteLicense->key()), string_to_hex(remoteLicense->key()));
client->key = remoteLicense->key(); logMessage(LOG_GENERAL, "[CLIENT][{}] Got remote license. Registered to {}. Key: {} (0x{})", client->address(), remote_license->owner(), base64::encode(remote_license->key()), string_to_hex(remote_license->key()));
} else { } client->key = remote_license->key();
if(pkt.licensed() && !pkt.has_license_info()) {
error = "Invalid content!";
return false;
} }
logMessage(LOG_GENERAL, "[CLIENT][{}] Got some server information. TeaSpeak-Version: {} uname: {}", client->address(), pkt.info().version(), pkt.info().uname()); logMessage(LOG_GENERAL, "[CLIENT][{}] Got some server information. TeaSpeak-Version: {} uname: {}", client->address(), pkt.info().version(), pkt.info().uname());
ts::proto::license::LicenseResponse response;
//Forces
ts::proto::license::LicenseResponse response{};
client->unique_identifier = pkt.info().has_unique_id() ? pkt.info().unique_id() : client->address(); client->unique_identifier = pkt.info().has_unique_id() ? pkt.info().unique_id() : client->address();
if(remoteLicense && pkt.licensed()) {
auto info = this->manager->licenseInfo(remoteLicense->key()); if(remote_license) {
auto info = this->manager->query_license_info(remote_license->key());
if(!info) { if(!info) {
response.mutable_blacklist()->set_reason("License hasn't been found."); response.set_invalid_reason("license has not been found");
response.set_valid(false); response.set_valid(false);
/*
logMessage(LOG_GENERAL, "[CLIENT][{}] Unknown license! Adding it to database!", client->address());
auto db_info = make_shared<LicenseInfo>();
db_info->start = system_clock::now();
db_info->end = remoteLicense->end();
db_info->last_name = "unknown";
db_info->first_name = "unknown";
db_info->username = remoteLicense->owner();
db_info->email = "unknonw@unknown";
db_info->creation = system_clock::now();
db_info->type = remoteLicense->data.type;
this->manager->registerLicense(remoteLicense->key(), db_info, "teaforo-fix");
info = this->manager->licenseInfo(remoteLicense->key());
if(!info) {
error = "could not insert key!";
return false;
}
*/
logMessage(LOG_GENERAL, "[CLIENT][{}] Remote license hasn't been found in database. Shutting down server!", client->address()); logMessage(LOG_GENERAL, "[CLIENT][{}] Remote license hasn't been found in database. Shutting down server!", client->address());
} else { } else {
response.set_update_pending(info->upgrade_id > 0);
client->key_pending_upgrade = info->upgrade_id;
if(info->deleted) { if(info->deleted) {
response.mutable_blacklist()->set_reason("License has been deleted."); response.set_invalid_reason("license has been deleted");
response.set_valid(false); response.set_valid(false);
logMessage(LOG_GENERAL, "[CLIENT][{}] Remote license has been deleted! Shutting down server!", client->address()); logMessage(LOG_GENERAL, "[CLIENT][{}] Remote license has been deleted! Shutting down server!", client->address());
} else { } else {
fill_info(response.mutable_license_info(), info, remoteLicense->data.licenceKey); fill_info(response.mutable_license_info(), info, remote_license->data.licenceKey);
response.set_valid(info->isValid()); auto is_invalid = !info->isValid();
if(is_invalid) {
response.set_invalid_reason("license is invalid");
response.set_valid(false);
} else {
response.set_valid(true);
}
} }
} }
this->manager->logRequest(remoteLicense->key(), client->unique_identifier, client->address(), pkt.info().version(), response.valid()); this->manager->logRequest(remote_license->key(), client->unique_identifier, client->address(), pkt.info().version(), response.valid());
} else { } else {
response.set_valid(true); response.set_valid(true);
} }
if(response.valid()) if(client->protocol.version == 2) {
response.mutable_blacklist()->set_state(ts::proto::license::VALID); if(response.valid())
else { response.mutable_blacklist()->set_state(ts::proto::license::VALID);
if(!response.has_license_info()) else {
fill_info(response.mutable_license_info(), nullptr, ""); /* "Hack" for old clients which require a license. Else the server would not be stopped */ response.mutable_blacklist()->set_reason(response.invalid_reason());
response.mutable_blacklist()->set_state(ts::proto::license::BLACKLISTED); /* "Hack" for all old clients */ response.mutable_blacklist()->set_state(ts::proto::license::BLACKLISTED); /* "Hack" for all old clients */
client->invalid_license = true;
if(!response.has_license_info()) fill_info(response.mutable_license_info(), nullptr, ""); /* "Hack" for old clients which require a license. Else the server would not be stopped */
client->invalid_license = true;
}
} else {
if(!response.has_blacklist())
response.mutable_blacklist()->set_state(ts::proto::license::VALID);
} }
client->invalid_license = !response.valid();
client->sendPacket(protocol::packet{protocol::PACKET_SERVER_VALIDATION_RESPONSE, response}); client->sendPacket(protocol::packet{protocol::PACKET_SERVER_VALIDATION_RESPONSE, response});
client->protocol.state = protocol::PROPERTY_ADJUSTMENT; client->protocol.state = protocol::PROPERTY_ADJUSTMENT;
return true; return true;
} }
bool LicenseServer::handlePacketLicenseUpgrade(shared_ptr<ConnectedClient> &client, license::protocol::packet &packet,
std::string &error) {
TEST_PROTOCOL_STATE(PROPERTY_ADJUSTMENT);
PARSE_PROTO(RequestLicenseUpgrade, pkt);
if(!client->key_pending_upgrade) {
error = "no update pending";
return false;
}
ts::proto::license::LicenseUpgradeResponse response;
auto license_upgrade = this->manager->query_license_upgrade(client->key_pending_upgrade);
response.set_valid(false);
if(license_upgrade) {
if(!license_upgrade->valid) {
response.set_error_message("upgrade has been invalidated");
} else if(license_upgrade->is_expired()) {
response.set_error_message("upgrade has been expired");
} else if(license_upgrade->not_yet_available()) {
response.set_error_message("upgrade is not yet active.");
} else {
response.set_valid(true);
response.set_license_key(license_upgrade->license_key);
}
this->manager->log_license_upgrade_attempt(license_upgrade->upgrade_id, response.valid(), client->unique_identifier, client->address());
logMessage(LOG_GENERAL, "[CLIENT][{}] Client requested license upgrade {}. Result: {}", client->key_pending_upgrade, response.valid() ? "granted" : "denied (" + response.error_message() + ")");
} else {
response.set_error_message("failed to find upgrade");
}
client->sendPacket(protocol::packet{protocol::PACKET_SERVER_LICENSE_UPGRADE_RESPONSE, response});
client->protocol.state = protocol::LICENSE_UPGRADE;
return true;
}
bool LicenseServer::handlePacketPropertyUpdate(shared_ptr<ConnectedClient> &client, protocol::packet &packet, std::string &error) { bool LicenseServer::handlePacketPropertyUpdate(shared_ptr<ConnectedClient> &client, protocol::packet &packet, std::string &error) {
TEST_PROTOCOL_STATE(PROPERTY_ADJUSTMENT); if(client->protocol.state != protocol::LICENSE_UPGRADE) /* LICENSE_UPGRADE could be skipped */
TEST_PROTOCOL_STATE(PROPERTY_ADJUSTMENT);
if(client->invalid_license) { if(client->invalid_license) {
ts::proto::license::PropertyUpdateResponse response; ts::proto::license::PropertyUpdateResponse response;
response.set_accepted(true); response.set_accepted(true);
@ -330,33 +369,58 @@ bool LicenseServer::handlePacketLicenseCreate(shared_ptr<ConnectedClient> &clien
TEST_PROTOCOL_STATE(MANAGER_CONNECTED); TEST_PROTOCOL_STATE(MANAGER_CONNECTED);
PARSE_PROTO(LicenseCreateRequest, pkt); PARSE_PROTO(LicenseCreateRequest, pkt);
logMessage(LOG_GENERAL, "[MANAGER][" + client->address() + "] Register new license to {} {} ({}). E-Mail: {}", pkt.issuer_first_name(), pkt.issuer_last_name(), pkt.issuer_username(), pkt.issuer_email()); auto old_license = pkt.has_old_key() ? hex::hex(pkt.old_key()) : "none";
logMessage(LOG_GENERAL, "[MANAGER][" + client->address() + "] Register new license to {} {} ({}). E-Mail: {}. Old license: {}", pkt.issuer_first_name(), pkt.issuer_last_name(), pkt.issuer_username(), pkt.issuer_email(), old_license);
ts::proto::license::LicenseCreateResponse response; auto old_key_id{0};
ts::proto::license::LicenseCreateResponse response{};
if(pkt.has_old_key()) {
old_key_id = this->manager->key_id_cache()->get_key_id_from_key(pkt.old_key());
if(old_key_id == 0) {
response.set_error("failed to find old license key in database");
goto _send_response;
}
}
auto db_info = make_shared<LicenseInfo>(); {
db_info->start = system_clock::time_point() + milliseconds(pkt.begin()); auto db_info = make_shared<LicenseInfo>();
db_info->end = system_clock::time_point() + milliseconds(pkt.end()); db_info->start = system_clock::time_point() + milliseconds(pkt.begin());
db_info->last_name = pkt.issuer_last_name(); db_info->end = system_clock::time_point() + milliseconds(pkt.end());
db_info->first_name = pkt.issuer_first_name(); db_info->last_name = pkt.issuer_last_name();
db_info->username = pkt.issuer_username(); db_info->first_name = pkt.issuer_first_name();
db_info->email = pkt.issuer_email(); db_info->username = pkt.issuer_username();
db_info->creation = system_clock::now(); db_info->email = pkt.issuer_email();
db_info->type = static_cast<LicenseType>(pkt.type()); db_info->creation = system_clock::now();
db_info->type = static_cast<LicenseType>(pkt.type());
auto license = license::createLocalLicence(db_info->type, db_info->end, db_info->first_name + db_info->last_name); auto license = license::createLocalLicence(db_info->type, db_info->end, db_info->first_name + db_info->last_name);
auto parsed_license = license::readLocalLicence(license, error); auto parsed_license = license::readLocalLicence(license, error);
if(!parsed_license) { if(!parsed_license) {
response.set_error("failed to register license (parse)"); response.set_error("failed to register license (parse)");
} else { } else {
if(!this->manager->register_license(parsed_license->key(), db_info, client->username)) {
response.set_error("failed to register license");
goto _send_response;
}
if(!this->manager->registerLicense(parsed_license->key(), db_info, client->username)) { if(old_key_id) {
response.set_error("failed to register license"); auto new_key_id = this->manager->key_id_cache()->get_key_id_from_key(parsed_license->key());
} else { if(!new_key_id) {
fill_info(response.mutable_license(), db_info, parsed_license->key()); response.set_error("failed to find new license in database");
response.set_exported_key(license); goto _send_response;
} }
}
if(!this->manager->register_license_upgrade(old_key_id, new_key_id, std::chrono::system_clock::now(), db_info->end, license)) {
response.set_error("failed to register license upgrade");
goto _send_response;
}
}
fill_info(response.mutable_license(), db_info, parsed_license->key());
response.set_exported_key(license);
}
}
_send_response:
client->sendPacket(protocol::packet{protocol::PACKET_SERVER_LICENSE_CREATE_RESPONSE, response}); client->sendPacket(protocol::packet{protocol::PACKET_SERVER_LICENSE_CREATE_RESPONSE, response});
return true; return true;
} }
@ -368,7 +432,7 @@ bool LicenseServer::handlePacketLicenseList(shared_ptr<ConnectedClient> &client,
proto::license::LicenseListResponse response; proto::license::LicenseListResponse response;
response.set_end(false); response.set_end(false);
for(const auto& info : this->manager->listLicenses(pkt.offset(), pkt.count())) { for(const auto& info : this->manager->list_licenses(pkt.offset(), pkt.count())) {
auto entry = response.add_entries(); auto entry = response.add_entries();
fill_info(entry, info.second, info.first); fill_info(entry, info.second, info.first);
} }
@ -382,7 +446,7 @@ bool LicenseServer::handlePacketLicenseDelete(shared_ptr<ConnectedClient> &clien
PARSE_PROTO(LicenseDeleteRequest, pkt); PARSE_PROTO(LicenseDeleteRequest, pkt);
proto::license::LicenseDeleteResponse response; proto::license::LicenseDeleteResponse response;
response.set_succeed(this->manager->deleteLicense(pkt.key(), pkt.full())); response.set_succeed(this->manager->delete_license(pkt.key(), pkt.full()));
client->sendPacket(protocol::packet{protocol::PACKET_CLIENT_DELETE_RESPONSE, response}); client->sendPacket(protocol::packet{protocol::PACKET_CLIENT_DELETE_RESPONSE, response});
return true; return true;

View File

@ -4,8 +4,10 @@
#include <sql/SqlQuery.h> #include <sql/SqlQuery.h>
#include <misc/std_unique_ptr.h> #include <misc/std_unique_ptr.h>
#include <utility>
#include "StatisticManager.h" #include "StatisticManager.h"
#include "LicenseManager.h" #include "DatabaseHandler.h"
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
@ -76,16 +78,16 @@ system_clock::time_point HistoryStatistics::align_type(license::stats::HistorySt
} }
} }
StatisticManager::StatisticManager(const std::shared_ptr<license::server::LicenseManager> &manager) : license_manager(manager) {} StatisticManager::StatisticManager(std::shared_ptr<license::server::database::DatabaseHandler> manager) : license_manager{std::move(manager)} {}
StatisticManager::~StatisticManager() {} StatisticManager::~StatisticManager() = default;
struct GeneralStatisticEntry { struct GeneralStatisticEntry {
std::chrono::system_clock::time_point age; std::chrono::system_clock::time_point age;
string unique_id = ""; string unique_id{""};
uint64_t key_id = 0; uint64_t key_id{0};
uint64_t servers = 0; uint64_t servers{0};
uint64_t clients = 0; uint64_t clients{0};
uint64_t bots = 0; uint64_t bots{0};
}; };
void StatisticManager::reset_cache_general() { void StatisticManager::reset_cache_general() {
@ -93,7 +95,7 @@ void StatisticManager::reset_cache_general() {
this->_general_statistics = nullptr; this->_general_statistics = nullptr;
} }
void parse_general_entry(deque<unique_ptr<GeneralStatisticEntry>>& entries, bool unique, int length, string* values, string* names) { void parse_general_entry(std::deque<std::unique_ptr<GeneralStatisticEntry>>& entries, bool unique, int length, string* values, string* names) {
auto entry = make_unique<GeneralStatisticEntry>(); auto entry = make_unique<GeneralStatisticEntry>();
for(int index = 0; index < length; index++) { for(int index = 0; index < length; index++) {
if(names[index] == "keyId") { if(names[index] == "keyId") {
@ -139,7 +141,7 @@ std::shared_ptr<GeneralStatistics> StatisticManager::general_statistics() {
auto result = sql::command(this->license_manager->sql(), "SELECT `keyId`, `unique_id`, `timestamp`,`server`,`clients`,`music` FROM `history_online` WHERE `timestamp` > :time ORDER BY `timestamp` ASC", auto result = sql::command(this->license_manager->sql(), "SELECT `keyId`, `unique_id`, `timestamp`,`server`,`clients`,`music` FROM `history_online` WHERE `timestamp` > :time ORDER BY `timestamp` ASC",
variable{":time", duration_cast<milliseconds>(system_clock::now().time_since_epoch() - hours(2) - minutes(10)).count()}) //10min as buffer variable{":time", duration_cast<milliseconds>(system_clock::now().time_since_epoch() - hours(2) - minutes(10)).count()}) //10min as buffer
.query(std::function<decltype(parse_general_entry)>(parse_general_entry), entries, true); .query(std::function<decltype(parse_general_entry)>{parse_general_entry}, entries, true);
auto stats = make_shared<GeneralStatistics>(); auto stats = make_shared<GeneralStatistics>();
for(auto& entry : entries) { for(auto& entry : entries) {

View File

@ -3,7 +3,7 @@
#include <mutex> #include <mutex>
#include <memory> #include <memory>
#include <chrono> #include <chrono>
#include "LicenseManager.h" #include "DatabaseHandler.h"
namespace license { namespace license {
namespace stats { namespace stats {
@ -49,19 +49,19 @@ namespace license {
std::chrono::milliseconds period; std::chrono::milliseconds period;
HistoryType type; HistoryType type;
std::shared_ptr<server::LicenseManager::UserHistory> statistics; std::shared_ptr<server::database::DatabaseHandler::UserHistory> statistics;
}; };
class StatisticManager { class StatisticManager {
public: public:
explicit StatisticManager(const std::shared_ptr<server::LicenseManager>& /* manager */); explicit StatisticManager(std::shared_ptr<server::database::DatabaseHandler> /* manager */);
virtual ~StatisticManager(); virtual ~StatisticManager();
void reset_cache_general(); void reset_cache_general();
std::shared_ptr<GeneralStatistics> general_statistics(); std::shared_ptr<GeneralStatistics> general_statistics();
std::shared_ptr<HistoryStatistics> history(HistoryStatistics::HistoryType); std::shared_ptr<HistoryStatistics> history(HistoryStatistics::HistoryType);
private: private:
std::shared_ptr<server::LicenseManager> license_manager; std::shared_ptr<server::database::DatabaseHandler> license_manager;
std::recursive_mutex _general_statistics_lock; std::recursive_mutex _general_statistics_lock;
std::recursive_mutex _general_statistics_generate_lock; std::recursive_mutex _general_statistics_generate_lock;

View File

@ -17,7 +17,7 @@ using namespace ts::ssl;
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
WebStatistics::WebStatistics(const shared_ptr<LicenseManager> &manager, const std::shared_ptr<stats::StatisticManager>& stats) : license_manager(manager), statistics_manager(stats) {} WebStatistics::WebStatistics(const shared_ptr<database::DatabaseHandler> &manager, const std::shared_ptr<stats::StatisticManager>& stats) : license_manager(manager), statistics_manager(stats) {}
WebStatistics::~WebStatistics() {} WebStatistics::~WebStatistics() {}
#define SFAIL(message) \ #define SFAIL(message) \

View File

@ -20,7 +20,7 @@
namespace license { namespace license {
namespace server { namespace server {
class LicenseManager; class DatabaseHandler;
} }
namespace stats { namespace stats {
class StatisticManager; class StatisticManager;
@ -47,7 +47,7 @@ namespace license {
inline std::string client_prefix() { return peer_address ? net::to_string(peer_address->sin_addr) : "unconnected"; } inline std::string client_prefix() { return peer_address ? net::to_string(peer_address->sin_addr) : "unconnected"; }
}; };
public: public:
WebStatistics(const std::shared_ptr<server::LicenseManager>& /* license manager */, const std::shared_ptr<stats::StatisticManager>& /* stats manager */); WebStatistics(const std::shared_ptr<server::database::DatabaseHandler>& /* license manager */, const std::shared_ptr<stats::StatisticManager>& /* stats manager */);
virtual ~WebStatistics(); virtual ~WebStatistics();
bool start(std::string& /* error */, uint16_t /* port */, const std::shared_ptr<ts::ssl::SSLContext>& /* ssl */); bool start(std::string& /* error */, uint16_t /* port */, const std::shared_ptr<ts::ssl::SSLContext>& /* ssl */);
@ -75,7 +75,7 @@ namespace license {
bool _running = false; bool _running = false;
std::recursive_mutex running_lock; std::recursive_mutex running_lock;
std::shared_ptr<server::LicenseManager> license_manager; std::shared_ptr<server::database::DatabaseHandler> license_manager;
std::shared_ptr<stats::StatisticManager> statistics_manager; std::shared_ptr<stats::StatisticManager> statistics_manager;
struct { struct {

View File

@ -3,7 +3,7 @@
#include <random> #include <random>
#include <ed25519/ed25519.h> #include <ed25519/ed25519.h>
//#define NO_OPEN_SSL #define NO_OPEN_SSL
#include <misc/digest.h> #include <misc/digest.h>
#include <cstring> #include <cstring>
#include <cassert> #include <cassert>

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <memory>
#include <string> #include <string>
#include <chrono> #include <chrono>
#include <memory> #include <memory>
@ -9,7 +10,7 @@
#include <Variable.h> #include <Variable.h>
#define LICENSE_VERSION 1 #define LICENSE_VERSION 1
#define LICENSE_PROT_VERSION 2 #define LICENSE_PROT_VERSION 3
#define MAGIC_NUMER 0xBADC0DED #define MAGIC_NUMER 0xBADC0DED
namespace license { namespace license {
@ -17,11 +18,11 @@ namespace license {
class LicenseException : public std::exception { class LicenseException : public std::exception {
public: public:
LicenseException() = delete; LicenseException() = delete;
LicenseException(std::string message) : errorMessage(std::move(message)) {} explicit LicenseException(std::string message) : errorMessage(std::move(message)) {}
LicenseException(const LicenseException& ref) : errorMessage(ref.errorMessage) {} LicenseException(const LicenseException& ref) : errorMessage(ref.errorMessage) {}
LicenseException(LicenseException&& ref) : errorMessage(std::move(ref.errorMessage)) {} explicit LicenseException(LicenseException&& ref) : errorMessage(std::move(ref.errorMessage)) {}
const char* what() const noexcept override; [[nodiscard]] const char* what() const noexcept override;
private: private:
std::string errorMessage; std::string errorMessage;
}; };
@ -36,7 +37,7 @@ namespace license {
struct LicenseHeader { struct LicenseHeader {
uint16_t version; /* first 16 bytes const version */ uint16_t version; /* first 16 bytes const version */
uint64_t crypt_key_seed; /* the seed */ uint64_t crypt_key_seed; /* the seed */
uint8_t crypt_key_verify_offset; /* the first 8 bits determine how much generations (n * 3) and the rest what value is expeted. Due to the loss of 8 bits does the highest 8 bits get xored with the lowerst and 56 bits get compared */ uint8_t crypt_key_verify_offset; /* the first 8 bits determine how much generations (n * 3) and the rest what value is expected. Due to the loss of 8 bits does the highest 8 bits get xored with the lowerst and 56 bits get compared */
uint8_t crypt_key_verify[5]; uint8_t crypt_key_verify[5];
} __attribute__ ((__packed__)); } __attribute__ ((__packed__));
static_assert(sizeof(LicenseHeader) == 16); static_assert(sizeof(LicenseHeader) == 16);
@ -62,7 +63,7 @@ namespace license {
/* Note for the write method: Write "private_buffer" if we're not able to resign the private data. As well enfore (assert) that we have a private buffer (e.g. by the read method) */ /* Note for the write method: Write "private_buffer" if we're not able to resign the private data. As well enfore (assert) that we have a private buffer (e.g. by the read method) */
~License(); ~License();
const std::vector<std::shared_ptr<const HierarchyEntry>> hierarchy() const { return this->_hierarchy; } [[nodiscard]] std::vector<std::shared_ptr<const HierarchyEntry>> hierarchy() const { return this->_hierarchy; }
bool push_entry(const std::shared_ptr<const HierarchyEntry>& /* entry */, size_t* /* index */ = nullptr); bool push_entry(const std::shared_ptr<const HierarchyEntry>& /* entry */, size_t* /* index */ = nullptr);
bool hierarchy_timestamps_valid(); bool hierarchy_timestamps_valid();
@ -86,7 +87,7 @@ namespace license {
std::string write(uint8_t& /* error */); std::string write(uint8_t& /* error */);
bool private_data_editable() const; [[nodiscard]] bool private_data_editable() const;
bool write_private_data(const LicensePrivateWriteOptions& /* write options */); bool write_private_data(const LicensePrivateWriteOptions& /* write options */);
[[nodiscard]] inline uint16_t version() const { return this->_version; } [[nodiscard]] inline uint16_t version() const { return this->_version; }
@ -118,17 +119,17 @@ namespace license {
bool private_key_chain_valid(); bool private_key_chain_valid();
bool has_meta(const std::string& key) const { return this->meta_data.count(key) > 0; } [[nodiscard]] bool has_meta(const std::string& key) const { return this->meta_data.count(key) > 0; }
std::string get_meta(const std::string& key) const { return this->meta_data.at(key); } [[nodiscard]] std::string get_meta(const std::string& key) const { return this->meta_data.at(key); }
void set_meta(const std::string& key, const std::string& value) { this->meta_data[key] = value; } void set_meta(const std::string& key, const std::string& value) { this->meta_data[key] = value; }
/* if target is null just increase the offset! */ /* if target is null just increase the offset! */
bool write(uint8_t* /* target */, size_t& /* offset */, size_t /* length */, const LicensePrivateWriteOptions& /* options */); bool write(uint8_t* /* target */, size_t& /* offset */, size_t /* length */, const LicensePrivateWriteOptions& /* options */);
void register_raw_private_key(uint8_t /* index */, const uint8_t* /* key */); void register_raw_private_key(uint8_t /* index */, const uint8_t* /* key */);
bool has_raw_private_key(uint8_t /* index */) const; [[nodiscard]] bool has_raw_private_key(uint8_t /* index */) const;
bool private_key_calculable(int /* index */) const; [[nodiscard]] bool private_key_calculable(int /* index */) const;
bool calculate_private_key(uint8_t* /* response */, uint8_t /* index */) const; bool calculate_private_key(uint8_t* /* response */, uint8_t /* index */) const;
private: private:
std::weak_ptr<License> _handle; std::weak_ptr<License> _handle;
@ -168,7 +169,7 @@ namespace license {
} }
inline const uint8_t* body() const { return this->read_body; } inline const uint8_t* body() const { return this->read_body; }
inline const size_t body_length() const { return this->read_body_length; } inline size_t body_length() const { return this->read_body_length; }
template <typename I> template <typename I>
inline I interpret_as() const { inline I interpret_as() const {
@ -240,7 +241,7 @@ namespace license {
const std::chrono::system_clock::time_point &end, const std::chrono::system_clock::time_point &end,
size_t buffer_size, uint8_t*& buffer_ptr size_t buffer_size, uint8_t*& buffer_ptr
) { ) {
auto result = std::shared_ptr<HierarchyEntry>(new HierarchyEntry{T::_type, pub_key, begin, end}); auto result = std::make_shared<HierarchyEntry>(HierarchyEntry{T::_type, pub_key, begin, end});
if(!result || !result->allocate_read_body(buffer_size)) return nullptr; if(!result || !result->allocate_read_body(buffer_size)) return nullptr;
result->read_body_length = buffer_size; result->read_body_length = buffer_size;
buffer_ptr = result->read_body; buffer_ptr = result->read_body;
@ -344,7 +345,8 @@ namespace license {
std::chrono::system_clock::time_point end; std::chrono::system_clock::time_point end;
std::chrono::system_clock::time_point creation; std::chrono::system_clock::time_point creation;
bool deleted = false; bool deleted{false};
uint32_t upgrade_id{0};
inline bool isValid() { return (end.time_since_epoch().count() == 0 || std::chrono::system_clock::now() < this->end); } inline bool isValid() { return (end.time_since_epoch().count() == 0 || std::chrono::system_clock::now() < this->end); }
}; };
@ -358,10 +360,13 @@ namespace license {
UNCONNECTED, UNCONNECTED,
CONNECTING, CONNECTING,
HANDSCAKE, HANDSCHAKE,
SERVER_VALIDATION, SERVER_VALIDATION,
LICENSE_INFO, LICENSE_INFO,
PROPERTY_ADJUSTMENT, PROPERTY_ADJUSTMENT,
LICENSE_UPGRADE,
MANAGER_AUTHORIZATION, MANAGER_AUTHORIZATION,
MANAGER_CONNECTED, MANAGER_CONNECTED,
@ -371,8 +376,10 @@ namespace license {
enum PacketType : uint8_t { enum PacketType : uint8_t {
PACKET_CLIENT_HANDSHAKE, PACKET_CLIENT_HANDSHAKE,
PACKET_SERVER_HANDSHAKE, PACKET_SERVER_HANDSHAKE,
PACKET_CLIENT_SERVER_VALIDATION, PACKET_CLIENT_SERVER_VALIDATION,
PACKET_SERVER_VALIDATION_RESPONSE, PACKET_SERVER_VALIDATION_RESPONSE,
PACKET_CLIENT_PROPERTY_ADJUSTMENT, PACKET_CLIENT_PROPERTY_ADJUSTMENT,
PACKET_SERVER_PROPERTY_ADJUSTMENT, PACKET_SERVER_PROPERTY_ADJUSTMENT,
@ -387,10 +394,18 @@ namespace license {
PACKET_CLIENT_DELETE_REQUEST, PACKET_CLIENT_DELETE_REQUEST,
PACKET_CLIENT_DELETE_RESPONSE, PACKET_CLIENT_DELETE_RESPONSE,
PACKET_CLIENT_LICENSE_UPGRADE,
PACKET_SERVER_LICENSE_UPGRADE_RESPONSE,
PACKET_PING = 0xF0, PACKET_PING = 0xF0,
PACKET_DISCONNECT = 0xFF PACKET_DISCONNECT = 0xFF
}; };
struct packet_header {
PacketType packetId{0};
uint16_t length{0};
};
struct packet { struct packet {
struct { struct {
PacketType packetId{0}; PacketType packetId{0};

View File

@ -1,11 +1,15 @@
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <fcntl.h>
#include <log/LogUtils.h> #include <log/LogUtils.h>
#include <misc/memtracker.h> #include <misc/memtracker.h>
#include "crypt.h" #include "crypt.h"
#define DEFINE_HELPER #define DEFINE_HELPER
#include "LicenseRequest.h" #include "LicenseRequest.h"
#include "License.h" #include "License.h"
#include <csignal> #include <csignal>
#include <ThreadPool/ThreadHelper.h>
#include <unistd.h>
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
@ -16,7 +20,7 @@ using namespace license;
#define CERR(message) LICENSE_FERR(this, CouldNotConnectException, message) #define CERR(message) LICENSE_FERR(this, CouldNotConnectException, message)
LicenceRequest::LicenceRequest(const std::shared_ptr<LicenseRequestData> & license, const sockaddr_in& remoteAddr) : data(license) { LicenceRequest::LicenceRequest(const std::shared_ptr<LicenseRequestData> & license, const sockaddr_in& remoteAddr) : data{license} {
#ifdef DEBUG_LICENSE_CLIENT #ifdef DEBUG_LICENSE_CLIENT
memtrack::allocated<LicenceRequest>(this); memtrack::allocated<LicenceRequest>(this);
#endif #endif
@ -31,13 +35,7 @@ LicenceRequest::~LicenceRequest() {
#endif #endif
this->abortRequest(); this->abortRequest();
if(this->closeThread) { threads::save_join(this->closeThread);
this->closeThread->join();
delete this->closeThread;
this->closeThread = nullptr;
}
delete this->currentFuture; delete this->currentFuture;
this->currentFuture = nullptr; this->currentFuture = nullptr;
} }
@ -179,7 +177,7 @@ void LicenceRequest::beginRequest() {
} }
void LicenceRequest::handleConnected() { void LicenceRequest::handleConnected() {
this->state = protocol::HANDSCAKE; this->state = protocol::HANDSCHAKE;
uint8_t handshakeBuffer[4]; uint8_t handshakeBuffer[4];
handshakeBuffer[0] = 0xC0; handshakeBuffer[0] = 0xC0;
@ -230,17 +228,16 @@ void LicenceRequest::disconnect(const std::string& message) {
} }
void LicenceRequest::closeConnection() { void LicenceRequest::closeConnection() {
event *event_read, *event_write; event *event_read{nullptr}, *event_write{nullptr};
{ {
lock_guard lock(this->lock); lock_guard slock(this->lock);
if(this->state == protocol::UNCONNECTED) return; if(this->state == protocol::UNCONNECTED) return;
if(this->event_dispatch.get_id() == this_thread::get_id()) { //We could not close in the same thread as we read/write (we're joining it later) if(this->event_dispatch.get_id() == this_thread::get_id()) { //We could not close in the same thread as we read/write (we're joining it later)
if(this->state == protocol::DISCONNECTING) return; if(this->state == protocol::DISCONNECTING) return;
this->state = protocol::DISCONNECTING; this->state = protocol::DISCONNECTING;
this->closeThread = new threads::Thread(THREAD_SAVE_OPERATIONS, [&]() { this->closeConnection(); }); this->closeThread = std::thread(&LicenceRequest::closeConnection, this);
#ifdef DEBUG_LICENSE_CLIENT #ifdef DEBUG_LICENSE_CLIENT
if(this->verbose) { if(this->verbose) {
debugMessage(LOG_GENERAL,"Running close in a new thread"); debugMessage(LOG_GENERAL,"Running close in a new thread");
@ -251,11 +248,8 @@ void LicenceRequest::closeConnection() {
} }
this->state = protocol::UNCONNECTED; this->state = protocol::UNCONNECTED;
event_read = this->event_read; std::swap(this->event_read, event_read);
event_write = this->event_write; std::swap(this->event_write, event_write);
this->event_write = nullptr;
this->event_read = nullptr;
} }
if(event_read) { if(event_read) {
@ -271,7 +265,7 @@ void LicenceRequest::closeConnection() {
/* close before base shutdown (else epoll hangup) */ /* close before base shutdown (else epoll hangup) */
if(this->file_descriptor > 0) { if(this->file_descriptor > 0) {
shutdown(this->file_descriptor, SHUT_RDWR); shutdown(this->file_descriptor, SHUT_RDWR);
close(this->file_descriptor); ::close(this->file_descriptor);
} }
this->file_descriptor = 0; this->file_descriptor = 0;

View File

@ -1,8 +1,7 @@
#pragma once #pragma once
#include <thread>
#include <protocol/buffers.h> #include <protocol/buffers.h>
#include <ThreadPool/Mutex.h>
#include <ThreadPool/Thread.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <event.h> #include <event.h>
@ -96,7 +95,8 @@ namespace license {
void sendPacket(const protocol::packet&); void sendPacket(const protocol::packet&);
std::function<void(const WebCertificate&)> callback_update_certificate{nullptr}; std::function<void(const WebCertificate& /* certificate */)> callback_update_certificate{nullptr};
std::function<void(const std::string& /* new key */)> callback_update_license{nullptr};
bool verbose = true; bool verbose = true;
private: private:
std::shared_ptr<LicenseRequestData> data; std::shared_ptr<LicenseRequestData> data;
@ -111,12 +111,12 @@ namespace license {
std::string buffer{}; std::string buffer{};
int file_descriptor = 0; int file_descriptor{0};
std::thread event_dispatch; std::thread event_dispatch{};
threads::Thread* closeThread = nullptr; std::thread closeThread{};
struct event_base* event_base = nullptr; struct event_base* event_base{nullptr};
event* event_read = nullptr; struct event* event_read{nullptr};
event* event_write = nullptr; struct event* event_write{nullptr};
TAILQ_HEAD(, ts::buffer::RawBuffer) writeQueue; TAILQ_HEAD(, ts::buffer::RawBuffer) writeQueue;

View File

@ -25,7 +25,7 @@ void LicenceRequest::handlePacketDisconnect(const std::string& message) {
} }
void LicenceRequest::handlePacketHandshake(const std::string& data) { void LicenceRequest::handlePacketHandshake(const std::string& data) {
if(this->state != protocol::HANDSCAKE) LICENSE_FERR(this, InvalidResponseException, "Protocol state mismatch"); if(this->state != protocol::HANDSCHAKE) LICENSE_FERR(this, InvalidResponseException, "Protocol state mismatch");
if(data.length() < 3) LICENSE_FERR(this, InvalidResponseException, "Invalid packet size"); if(data.length() < 3) LICENSE_FERR(this, InvalidResponseException, "Invalid packet size");
if((uint8_t) data[0] != 0xAF || (uint8_t) data[1] != 0xFE) LICENSE_FERR(this, InvalidResponseException, "Invalid handshake"); if((uint8_t) data[0] != 0xAF || (uint8_t) data[1] != 0xFE) LICENSE_FERR(this, InvalidResponseException, "Invalid handshake");
@ -54,14 +54,13 @@ void LicenceRequest::handlePacketHandshake(const std::string& data) {
} }
void LicenceRequest::handlePacketLicenseInfo(const std::string& message) { void LicenceRequest::handlePacketLicenseInfo(const std::string& message) {
ts::proto::license::LicenseResponse response; ts::proto::license::LicenseResponse response{};
if(!response.ParseFromString(message)) LICENSE_FERR(this, InvalidResponseException, "Could not parse response"); if(!response.ParseFromString(message)) LICENSE_FERR(this, InvalidResponseException, "Could not parse response");
auto result = make_shared<LicenseRequestResponse>(); auto result = make_shared<LicenseRequestResponse>();
auto licenseInfo = make_shared<LicenseInfo>(); auto licenseInfo = make_shared<LicenseInfo>();
if(!response.has_license_info() && this->data->license && response.valid() && response.blacklist().state() == ts::proto::license::VALID) { if(!response.has_license_info() && this->data->license && response.valid() && response.blacklist().state() == ts::proto::license::VALID)
LICENSE_FERR(this, InvalidResponseException, "Missing license info"); LICENSE_FERR(this, InvalidResponseException, "Missing license info");
}
if(this->data->license) { if(this->data->license) {
licenseInfo->type = (LicenseType) response.license_info().type(); licenseInfo->type = (LicenseType) response.license_info().type();
@ -88,7 +87,11 @@ void LicenceRequest::handlePacketLicenseInfo(const std::string& message) {
result->license = licenseInfo; result->license = licenseInfo;
this->response = result; this->response = result;
ts::proto::license::PropertyUpdateRequest infos; if(response.has_update_pending()) {
}
ts::proto::license::PropertyUpdateRequest infos{};
infos.set_speach_total(this->data->speach_total); infos.set_speach_total(this->data->speach_total);
infos.set_speach_dead(this->data->speach_dead); infos.set_speach_dead(this->data->speach_dead);
infos.set_speach_online(this->data->speach_online); infos.set_speach_online(this->data->speach_online);

View File

@ -0,0 +1,527 @@
//
// Created by WolverinDEV on 23/02/2020.
//
#include <csignal>
#include <netinet/tcp.h>
#include <event.h>
#include <ThreadPool/ThreadHelper.h>
#include <misc/endianness.h>
#include "LicenseServerClient.h"
#include "crypt.h"
using namespace license::client;
LicenseServerClient::Buffer* LicenseServerClient::Buffer::allocate(size_t capacity) {
static_assert(std::is_trivially_constructible<Buffer>::value);
const auto allocated_bytes = sizeof(LicenseServerClient::Buffer) + capacity;
auto result = malloc(allocated_bytes);
if(!result) return nullptr;
auto buffer = reinterpret_cast<LicenseServerClient::Buffer*>(result);
buffer->capacity = capacity;
buffer->fill = 0;
buffer->offset = 0;
buffer->data = (char*) result + sizeof(LicenseServerClient::Buffer);
return buffer;
}
void LicenseServerClient::Buffer::free(Buffer *ptr) {
static_assert(std::is_trivially_destructible<Buffer>::value);
::free(ptr);
}
LicenseServerClient::LicenseServerClient(const sockaddr_in &address, int pversion) : protocol_version{pversion} {
memcpy(&this->network.address, &address, sizeof(address));
TAILQ_INIT(&this->buffers.write);
if(!this->buffers.read)
this->buffers.read = Buffer::allocate(1024 * 8);
}
LicenseServerClient::~LicenseServerClient() {
this->close_connection();
if(this->buffers.read)
Buffer::free(this->buffers.read);
threads::save_join(this->network.event_dispatch, false);
}
bool LicenseServerClient::start_connection(std::string &error) {
bool event_dispatch_spawned{false};
std::unique_lock slock{this->connection_lock};
if(this->connection_state != ConnectionState::UNCONNECTED) {
error = "invalid connection state";
return false;
}
this->connection_state = ConnectionState::CONNECTING;
this->communication.initialized = false;
this->network.file_descriptor = socket(this->network.address.sin_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
if(this->network.file_descriptor < 0) {
error = "failed to allocate socket";
goto error_cleanup;
}
signal(SIGPIPE, SIG_IGN);
{
auto connect_state = ::connect(this->network.file_descriptor, reinterpret_cast<const sockaddr *>(&this->network.address), sizeof(this->network.address));
if(connect_state < 0 && errno != EINPROGRESS) {
error = "connect() failed (" + std::string{strerror(errno)} + ")";
goto error_cleanup;
}
}
{
int enabled{1}, disabled{0};
if(setsockopt(this->network.file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) < 0); //CERR("could not set reuse addr");
if(setsockopt(this->network.file_descriptor, IPPROTO_TCP, TCP_CORK, &disabled, sizeof(disabled)) < 0); // CERR("could not set no push");
if(fcntl(this->network.file_descriptor, F_SETFD, fcntl(this->network.file_descriptor, F_GETFL, 0) | FD_CLOEXEC | O_NONBLOCK) < 0); // CERR("Failed to set FD_CLOEXEC and O_NONBLOCK (" + std::to_string(errno) + ")");
}
this->network.event_base = event_base_new();
this->network.event_read = event_new(this->network.event_base, this->network.file_descriptor, EV_READ | EV_PERSIST, [](int, short e, void* _this) {
auto client = reinterpret_cast<LicenseServerClient*>(_this);
client->callback_read(e);
}, this);
this->network.event_write = event_new(this->network.event_base, this->network.file_descriptor, EV_WRITE, [](int, short e, void* _this) {
auto client = reinterpret_cast<LicenseServerClient*>(_this);
client->callback_write(e);
}, this);
event_dispatch_spawned = true;
this->network.event_dispatch = std::thread([&] {
signal(SIGPIPE, SIG_IGN);
event_add(this->network.event_read, nullptr);
timeval connect_timeout{5, 0};
event_add(this->network.event_write, &connect_timeout);
auto event_base{this->network.event_base};
event_base_loop(event_base, EVLOOP_NO_EXIT_ON_EMPTY);
event_base_free(event_base);
//this ptr might be dangling
});
return true;
error_cleanup:
this->cleanup_network_resources();
if(!event_dispatch_spawned) {
event_base_free(this->network.event_base);
this->network.event_base = nullptr;
}
this->connection_state = ConnectionState::UNCONNECTED;
return false;
}
void LicenseServerClient::close_connection() {
std::unique_lock slock{this->connection_lock};
if(this->connection_state == ConnectionState::UNCONNECTED) return;
this->connection_state = ConnectionState::UNCONNECTED;
this->cleanup_network_resources();
}
void LicenseServerClient::cleanup_network_resources() {
const auto is_event_loop = this->network.event_dispatch.get_id() == std::this_thread::get_id();
if(this->network.event_read) {
if(is_event_loop) event_del_noblock(this->network.event_read);
else event_del_block(this->network.event_read);
event_free(this->network.event_read);
this->network.event_read = nullptr;
}
if(this->network.event_write) {
if(is_event_loop) event_del_noblock(this->network.event_write);
else event_del_block(this->network.event_write);
event_free(this->network.event_write);
this->network.event_write = nullptr;
}
if(this->network.event_base) {
event_base_loopexit(this->network.event_base, nullptr);
if(!is_event_loop)
threads::save_join(this->network.event_dispatch, false);
this->network.event_base = nullptr; /* event base has been saved by the event dispatcher and will be freed there */
}
if(this->network.file_descriptor) {
::close(this->network.file_descriptor);
this->network.file_descriptor = 0;
}
{
std::lock_guard block{this->buffers.lock};
auto buffer = TAILQ_FIRST(&this->buffers.write);
while(buffer) {
auto next = TAILQ_NEXT(buffer, tail);
Buffer::free(next);
buffer = next;
}
TAILQ_INIT(&this->buffers.write);
this->buffers.notify_empty.notify_all();
}
}
void LicenseServerClient::callback_read(short events) {
constexpr static auto buffer_size{1024};
ssize_t read_bytes{0};
char buffer[buffer_size];
read_bytes = recv(this->network.file_descriptor, buffer, buffer_size, MSG_DONTWAIT);
if(read_bytes <= 0) {
if(errno == EAGAIN) return;
std::unique_lock slock{this->connection_lock};
std::string disconnect_reason{};
bool disconnect_expected{false};
switch (this->connection_state) {
case ConnectionState::CONNECTING:
disconnect_reason = "connect error (" + std::string{strerror(errno)} + ")";
disconnect_expected = false;
break;
case ConnectionState::INITIALIZING:
case ConnectionState::CONNECTED:
disconnect_reason = "read error (" + std::string{strerror(errno)} + ")";
disconnect_expected = false;
break;
case ConnectionState::DISCONNECTING:
disconnect_expected = true;
break;
case ConnectionState::UNCONNECTED:
return; /* we're obsolete */
}
if(auto callback{this->callback_disconnected}; callback) {
slock.unlock();
callback(disconnect_expected, disconnect_reason);
slock.lock();
}
if(this->connection_state != ConnectionState::UNCONNECTED) {
this->cleanup_network_resources();
this->connection_state = ConnectionState::UNCONNECTED;
}
return;
}
this->handle_data(buffer, (size_t) read_bytes);
}
void LicenseServerClient::callback_write(short events) {
bool add_write_event{this->connection_state == ConnectionState::DISCONNECTING};
if(events & EV_TIMEOUT) {
std::unique_lock slock{this->connection_lock};
if(this->connection_state == ConnectionState::CONNECTING || this->connection_state == ConnectionState::INITIALIZING) {
/* connect timeout */
if(auto callback{this->callback_disconnected}; callback) {
slock.unlock();
callback(false, "connect timeout");
slock.lock();
}
if(this->connection_state != ConnectionState::UNCONNECTED) {
this->cleanup_network_resources();
this->connection_state = ConnectionState::UNCONNECTED;
}
} else if(this->connection_state == ConnectionState::DISCONNECTING) {
/* disconnect timeout */
this->cleanup_network_resources();
this->connection_state = ConnectionState::UNCONNECTED;
}
return;
}
if(events & EV_WRITE) {
if(this->connection_state == ConnectionState::CONNECTING)
this->callback_socket_connected();
ssize_t written_bytes{0};
std::unique_lock block{this->buffers.lock};
auto buffer = TAILQ_FIRST(&this->buffers.write);
if(!buffer) {
this->buffers.notify_empty.notify_all();
return;
}
block.unlock();
written_bytes = send(this->network.file_descriptor, (char*) buffer->data + buffer->offset, buffer->fill - buffer->offset, MSG_DONTWAIT);
if(written_bytes <= 0) {
if(errno == EAGAIN) goto readd_event;
std::unique_lock slock{this->connection_lock};
std::string disconnect_reason{};
bool disconnect_expected{false};
switch (this->connection_state) {
case ConnectionState::CONNECTING:
case ConnectionState::INITIALIZING:
case ConnectionState::CONNECTED:
disconnect_reason = "write error (" + std::string{strerror(errno)} + ")";
disconnect_expected = false;
break;
case ConnectionState::DISCONNECTING:
disconnect_expected = true;
break;
case ConnectionState::UNCONNECTED:
return; /* we're obsolete */
}
if(auto callback{this->callback_disconnected}; callback) {
slock.unlock();
callback(disconnect_expected, disconnect_reason);
slock.lock();
}
if(this->connection_state != ConnectionState::UNCONNECTED) {
this->cleanup_network_resources();
this->connection_state = ConnectionState::UNCONNECTED;
}
return;
}
buffer->offset += (size_t) written_bytes;
if(buffer->offset >= buffer->fill) {
assert(buffer->offset == buffer->fill);
block.lock();
TAILQ_REMOVE(&this->buffers.write, buffer, tail);
if(!TAILQ_FIRST(&this->buffers.write)) {
this->buffers.notify_empty.notify_all();
} else {
add_write_event = true;
}
block.unlock();
Buffer::free(buffer);
}
}
if(this->network.event_write && add_write_event) {
readd_event:
auto timeout = this->disconnect_timeout;
if(timeout.time_since_epoch().count() == 0)
event_add(this->network.event_write, nullptr);
else {
auto now = std::chrono::system_clock::now();
struct timeval t{0, 1};
if(now > timeout) {
this->callback_write(EV_TIMEOUT);
return;
} else {
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(timeout - now);
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(microseconds);
microseconds -= seconds;
t.tv_usec = microseconds.count();
t.tv_sec = seconds.count();
}
event_add(this->network.event_write, &t);
}
}
}
void LicenseServerClient::handle_data(void *recv_buffer, size_t length) {
auto& buffer = this->buffers.read;
assert(buffer);
if(buffer->capacity - buffer->offset - buffer->fill < length) {
if(buffer->capacity - buffer->fill > length) {
memcpy(buffer->data, (char*) buffer->data + buffer->offset, buffer->fill);
buffer->offset = 0;
} else {
auto new_buffer = Buffer::allocate(buffer->fill + length);
memcpy(new_buffer->data, (char*) buffer->data + buffer->offset, buffer->fill);
new_buffer->fill = buffer->fill;
Buffer::free(buffer);
buffer = new_buffer;
}
}
auto buffer_ptr = (char*) buffer->data;
auto& buffer_offset = buffer->offset;
auto& buffer_length = buffer->fill;
memcpy((char*) buffer_ptr + buffer_offset + buffer_length, recv_buffer, length);
buffer_length += length;
while(true) {
if(buffer_length < sizeof(protocol::packet_header)) return;
auto header = reinterpret_cast<protocol::packet_header*>(buffer_ptr + buffer_offset);
if(header->length > 1024 * 8) {
if(auto callback{this->callback_disconnected}; callback)
callback(false, "received a too large message");
this->disconnect("received too large message", std::chrono::system_clock::time_point{});
return;
}
if(buffer_length < header->length + sizeof(protocol::packet_header)) return;
this->handle_raw_packet(header->packetId, buffer_ptr + buffer_offset + sizeof(protocol::packet_header), header->length);
buffer_offset += header->length + sizeof(protocol::packet_header);
buffer_length -= header->length + sizeof(protocol::packet_header);
}
}
void LicenseServerClient::send_message(protocol::PacketType type, const void *payload, size_t size) {
const auto packet_size = size + sizeof(protocol::packet_header);
auto buffer = Buffer::allocate(packet_size);
buffer->fill = packet_size;
auto header = (protocol::packet_header*) buffer->data;
header->length = packet_size;
header->packetId = type;
memcpy((char*) buffer->data + sizeof(protocol::packet_header), payload, size);
if(this->communication.initialized)
xorBuffer((char*) buffer->data + sizeof(protocol::packet_header), size, this->communication.crypt_key.data(), this->communication.crypt_key.length());
std::lock_guard clock{this->connection_lock};
if(this->connection_state == ConnectionState::UNCONNECTED || !this->network.event_write) {
Buffer::free(buffer);
return;
}
{
std::lock_guard block{this->buffers.lock};
TAILQ_INSERT_TAIL(&this->buffers.write, buffer, tail);
}
event_add(this->network.event_write, nullptr);
}
void LicenseServerClient::disconnect(const std::string &message, std::chrono::system_clock::time_point timeout) {
auto now = std::chrono::system_clock::now();
if(now > timeout)
timeout = now + std::chrono::seconds{timeout.time_since_epoch().count() ? 1 : 0};
std::unique_lock clock{this->connection_lock};
if(this->connection_state == ConnectionState::DISCONNECTING) {
this->disconnect_timeout = std::min(this->disconnect_timeout, timeout);
if(this->network.event_write)
event_add(this->network.event_write, nullptr); /* let the write update the timeout */
return;
}
this->disconnect_timeout = timeout;
if(this->connection_state != ConnectionState::INITIALIZING && this->connection_state != ConnectionState::CONNECTED) {
clock.unlock();
this->close_connection();
return;
}
this->connection_state = ConnectionState::DISCONNECTING;
if(this->network.event_read)
event_del_noblock(this->network.event_read);
clock.unlock();
this->send_message(protocol::PACKET_DISCONNECT, message.data(), message.length());
}
bool LicenseServerClient::await_disconnect() {
{
std::lock_guard clock{this->connection_lock};
if(this->connection_state != ConnectionState::DISCONNECTING)
return this->connection_state == ConnectionState::UNCONNECTED;
}
/* state might change here, but when we're disconnected the write buffer will be empty */
std::unique_lock block{this->buffers.lock};
while(TAILQ_FIRST(&this->buffers.write))
this->buffers.notify_empty.wait(block);
return std::chrono::system_clock::now() <= this->disconnect_timeout;
}
void LicenseServerClient::callback_socket_connected() {
{
std::lock_guard clock{this->connection_lock};
if(this->connection_state != ConnectionState::CONNECTING) return;
this->connection_state = ConnectionState::INITIALIZING;
}
uint8_t handshakeBuffer[4];
handshakeBuffer[0] = 0xC0;
handshakeBuffer[1] = 0xFF;
handshakeBuffer[2] = 0xEE;
handshakeBuffer[3] = this->protocol_version;
this->send_message(protocol::PACKET_CLIENT_HANDSHAKE, handshakeBuffer, 4);
}
void LicenseServerClient::handle_raw_packet(license::protocol::PacketType type, void * buffer, size_t length) {
/* decrypt packet */
if(this->communication.initialized)
xorBuffer((char*) buffer, length, this->communication.crypt_key.data(), this->communication.crypt_key.length());
if(type == protocol::PACKET_DISCONNECT) {
if(auto callback{this->callback_disconnected}; callback)
callback(false, std::string{(const char*) buffer, length});
this->close_connection();
return;
}
if(!this->communication.initialized) {
if(type != protocol::PACKET_SERVER_HANDSHAKE) {
if(auto callback{this->callback_disconnected}; callback)
callback(false, "expected handshake packet");
this->disconnect("expected handshake packet", std::chrono::system_clock::time_point{});
return;
}
this->handle_handshake_packet(buffer, length);
this->communication.initialized = true;
return;
}
if(auto callback{this->callback_message}; callback)
callback(type, buffer, length);
else
; //TODO: Print error?
}
void LicenseServerClient::handle_handshake_packet(void *buffer, size_t length) {
const auto data_ptr = (const char*) buffer;
std::string error{};
if(this->connection_state != ConnectionState::INITIALIZING) {
error = "invalid protocol state";
goto handle_error;
}
if(length < 5) {
error = "invalid packet size";
goto handle_error;
}
if((uint8_t) data_ptr[0] != 0xAF || (uint8_t) data_ptr[1] != 0xFE) {
error = "invalid handshake signature";
goto handle_error;
}
if((uint8_t) data_ptr[2] != this->protocol_version) {
error = "Invalid license protocol version. Please update TeaSpeak!";
goto handle_error;
}
{
auto key_length = be2le16(data_ptr, 3);
if(length < key_length + 5) {
error = "invalid packet size";
goto handle_error;
}
this->communication.crypt_key = std::string(data_ptr + 5, key_length);
this->communication.initialized = true;
}
if(auto callback{this->callback_connected}; callback)
callback();
return;
handle_error:
if(auto callback{this->callback_disconnected}; callback)
callback(false, error);
this->disconnect(error, std::chrono::system_clock::time_point{});
}

View File

@ -0,0 +1,97 @@
#pragma once
#include <protocol/buffers.h>
#include <netinet/in.h>
#include <functional>
#include <mutex>
#include "./License.h"
namespace license::client {
class LicenseServerClient {
public:
enum ConnectionState {
CONNECTING,
INITIALIZING,
CONNECTED,
DISCONNECTING,
UNCONNECTED
};
typedef std::function<void()> callback_connected_t;
typedef std::function<void(protocol::PacketType /* type */, const void* /* payload */, size_t /* length */)> callback_message_t;
typedef std::function<void(bool /* expected */, const std::string& /* reason */)> callback_disconnect_t;
explicit LicenseServerClient(const sockaddr_in&, int /* protocol version */);
virtual ~LicenseServerClient();
bool start_connection(std::string& /* error */);
void send_message(protocol::PacketType /* type */, const void* /* buffer */, size_t /* length */);
void close_connection();
void disconnect(const std::string& /* reason */, std::chrono::system_clock::time_point /* timeout */);
bool await_disconnect();
/*
* Events will be called within the event loop.
* All methods are save to call.
* When close_connection or await_disconnect has been called these methods will not be called anymore.
*/
callback_message_t callback_message{nullptr};
callback_connected_t callback_connected{nullptr};
callback_disconnect_t callback_disconnected{nullptr};
const int protocol_version;
private:
std::mutex connection_lock{};
ConnectionState connection_state{ConnectionState::UNCONNECTED};
std::chrono::system_clock::time_point disconnect_timeout{};
struct Buffer {
static Buffer* allocate(size_t /* capacity */);
static void free(Buffer* /* ptr */);
void* data;
size_t capacity;
size_t fill;
size_t offset;
TAILQ_ENTRY(Buffer) tail;
};
/* modify everything here only within the event base, or when exited when connection_lock is locked */
struct {
sockaddr_in address{};
int file_descriptor{0};
std::thread event_dispatch{};
struct event_base* event_base{nullptr}; /* will be cleaned up by the event loop! */
struct event* event_read{nullptr};
struct event* event_write{nullptr};
} network;
struct {
std::mutex lock{};
std::condition_variable notify_empty{};
Buffer* read{nullptr}; /* must noch be accessed via lock because only the event loop uses it */
TAILQ_HEAD(, Buffer) write;
} buffers;
struct {
bool initialized{false};
std::string crypt_key{};
} communication;
void callback_read(short /* events */);
void callback_write(short /* events */);
void callback_socket_connected();
void cleanup_network_resources();
void handle_data(void*, size_t);
void handle_raw_packet(protocol::PacketType /* type */, void* /* payload */, size_t /* length */);
void handle_handshake_packet(void* /* payload */, size_t /* length */);
};
}

View File

@ -124,7 +124,6 @@ set(SERVER_SOURCE_FILES
src/server/WebIoManager.cpp src/server/WebIoManager.cpp
src/client/SpeakingClient.cpp src/client/SpeakingClient.cpp
src/lincense/LicenseHelper.cpp
../shared/src/ssl/SSLManager.cpp ../shared/src/ssl/SSLManager.cpp
src/manager/SqlDataManager.cpp src/manager/SqlDataManager.cpp
@ -151,7 +150,7 @@ if (COMPILE_WEB_CLIENT)
src/client/web/WSWebClient.cpp src/client/web/WSWebClient.cpp
src/client/web/SampleHandler.cpp src/client/web/SampleHandler.cpp
src/client/web/VoiceBridge.cpp src/client/web/VoiceBridge.cpp
src/client/command_handler/helpers.h src/music/PlaylistPermissions.cpp src/music/PlaylistPermissions.h) src/client/command_handler/helpers.h src/music/PlaylistPermissions.cpp src/music/PlaylistPermissions.h src/lincense/LicenseService.cpp src/lincense/LicenseService.h)
endif () endif ()
add_executable(PermHelper helpers/permgen.cpp) add_executable(PermHelper helpers/permgen.cpp)

View File

@ -3,3 +3,8 @@ Build opus:
Build libevent 2.1.8 (with -fPIC) Build libevent 2.1.8 (with -fPIC)
Local licenses test:
Base: AQDm4ty6AAAAACnNuqvy++NGfMJU+Bvo5412Wi5jM5/JmmYyDbcxWKNaJV0FF1jpXtSrss3Gm7RUEQ6CdEEhPdyH5OLcugAAAAAp9cb+t1r6P5zGt9fi+QbAOroWM7LqY0B+iy055XrZxX+r8bfP50esuU9qypU9DUtnE9+qJ2gZmOgHcVkHJ4JhN4Db2ZmOkA4dpjGWfY2L6QJanPCy6HsVOm1/1LasZv9Om1/4uWHYLfab0n2+FNajxbcyDY3FAI6+rLBlQzGErtE=
New: AQCJON26AAAAACnNuqvy++NGfMJU+Bvo5412Wi5jM5/JmmYyDbcxWKNaJV0FF1jpXtSrss3Gm7RUEQ6CdEEhPdyHizjdugAAAAAp6d+xUgRteE54jqv3OXoRS946xsWKUtJBFkB4pPMT1+pIrXhM3/CTWjpXG8KDLoa1DEjwguolTXJ+RCvySR7Bd4Db2ZmOkA4dpjGWfY2L6QJ3bxZH8OTPFlV/1lU9x1r5QPPOpFmZt68h6VZDz4y9AVNXO+JZPHu24Pre7hTnyhg=

View File

@ -1,6 +1,5 @@
#include <client/linux/handler/exception_handler.h> #include <client/linux/handler/exception_handler.h>
#include <iostream> #include <iostream>
#include <misc/endianness.h>
#include <misc/strobf.h> #include <misc/strobf.h>
#include <CXXTerminal/QuickTerminal.h> #include <CXXTerminal/QuickTerminal.h>
#include <event2/thread.h> #include <event2/thread.h>
@ -14,7 +13,6 @@
#include "src/server/file/FileServer.h" #include "src/server/file/FileServer.h"
#include "src/terminal/CommandHandler.h" #include "src/terminal/CommandHandler.h"
#include "src/client/InternalClient.h" #include "src/client/InternalClient.h"
#include "src/music/MusicBotManager.h"
#include "src/SignalHandler.h" #include "src/SignalHandler.h"
#include "src/build.h" #include "src/build.h"
@ -126,8 +124,11 @@ int main(int argc, char** argv) {
assert(evthread_use_pthreads_result == 0); assert(evthread_use_pthreads_result == 0);
(void) evthread_use_pthreads_result; (void) evthread_use_pthreads_result;
} }
terminal::install();
if(!terminal::active()){ cerr << "could not setup terminal!" << endl; return -1; } if(!arguments.cmdOptionExists("--no-terminal")) {
terminal::install();
if(!terminal::active()){ cerr << "could not setup terminal!" << endl; return -1; }
}
assert(ts::property::impl::validateUnique()); assert(ts::property::impl::validateUnique());
if(arguments.cmdOptionExists("--help") || arguments.cmdOptionExists("-h")) { if(arguments.cmdOptionExists("--help") || arguments.cmdOptionExists("-h")) {
@ -276,6 +277,8 @@ int main(int argc, char** argv) {
logConfig->file_colored = ts::config::log::logfileColored; logConfig->file_colored = ts::config::log::logfileColored;
logConfig->logPath = ts::config::log::path; logConfig->logPath = ts::config::log::path;
logConfig->vs_group_size = ts::config::log::vs_size; logConfig->vs_group_size = ts::config::log::vs_size;
logConfig->sync = !terminal::instance();
logger::setup(logConfig); logger::setup(logConfig);
threads::timer::function_log = [](const std::string& message, bool debug) { threads::timer::function_log = [](const std::string& message, bool debug) {
auto msg = message.find('\n') == std::string::npos ? message : message.substr(0, message.find('\n')); auto msg = message.find('\n') == std::string::npos ? message : message.substr(0, message.find('\n'));
@ -309,13 +312,13 @@ int main(int argc, char** argv) {
logMessage(LOG_GENERAL, "Starting TeaSpeak-Server v{}", build::version()->string(true)); logMessage(LOG_GENERAL, "Starting TeaSpeak-Server v{}", build::version()->string(true));
logMessage(LOG_GENERAL, "Starting music providers"); logMessage(LOG_GENERAL, "Starting music providers");
terminal::instance()->setPrompt("§aStarting server. §7[§aloading music§7]"); if(terminal::instance()) terminal::instance()->setPrompt("§aStarting server. §7[§aloading music§7]");
if(ts::config::music::enabled && !arguments.cmdOptionExists("--no-providers")) { if(ts::config::music::enabled && !arguments.cmdOptionExists("--no-providers")) {
::music::manager::loadProviders("providers"); ::music::manager::loadProviders("providers");
::music::manager::register_provider(::music::provider::ChannelProvider::create_provider()); ::music::manager::register_provider(::music::provider::ChannelProvider::create_provider());
} }
terminal::instance()->setPrompt("§aStarting server. §7[§aloading geoloc§7]"); if(terminal::instance()) terminal::instance()->setPrompt("§aStarting server. §7[§aloading geoloc§7]");
if(!ts::config::geo::staticFlag) { if(!ts::config::geo::staticFlag) {
if(ts::config::geo::type == geoloc::PROVIDER_SOFTWARE77) if(ts::config::geo::type == geoloc::PROVIDER_SOFTWARE77)
@ -342,7 +345,7 @@ int main(int argc, char** argv) {
errorMessage = ""; errorMessage = "";
} }
} }
terminal::instance()->setPrompt("§aStarting server. §7[§aloading sql§7]"); if(terminal::instance()) terminal::instance()->setPrompt("§aStarting server. §7[§aloading sql§7]");
sql = new ts::server::SqlDataManager(); sql = new ts::server::SqlDataManager();
if(!sql->initialize(errorMessage)) { if(!sql->initialize(errorMessage)) {
@ -358,7 +361,7 @@ int main(int argc, char** argv) {
goto stopApp; goto stopApp;
} }
terminal::instance()->setPrompt("§aStarting server. §7[§astarting instance§7]"); if(terminal::instance()) terminal::instance()->setPrompt("§aStarting server. §7[§astarting instance§7]");
serverInstance = new ts::server::InstanceHandler(sql); //if error than mainThreadActive = false serverInstance = new ts::server::InstanceHandler(sql); //if error than mainThreadActive = false
if(!mainThreadActive || !serverInstance->startInstance()) if(!mainThreadActive || !serverInstance->startInstance())
@ -384,13 +387,15 @@ int main(int argc, char** argv) {
} }
} }
terminal::instance()->setPrompt("§7> §f"); if(terminal::instance()) terminal::instance()->setPrompt("§7> §f");
while(mainThreadActive) { while(mainThreadActive) {
usleep(5 * 1000); usleep(5 * 1000);
if(terminal::instance()->linesAvailable() > 0){ if(terminal::instance()) {
while(!(line = terminal::instance()->readLine("§7> §f")).empty()) if(terminal::instance()->linesAvailable() > 0){
threads::Thread(THREAD_DETACHED, [line](){ terminal::chandler::handleCommand(line); }); while(!(line = terminal::instance()->readLine("§7> §f")).empty())
threads::Thread(THREAD_DETACHED, [line](){ terminal::chandler::handleCommand(line); });
}
} }
} }
@ -411,7 +416,8 @@ int main(int argc, char** argv) {
logMessageFmt(true, LOG_GENERAL, "Application suspend successful!"); logMessageFmt(true, LOG_GENERAL, "Application suspend successful!");
logger::uninstall(); logger::uninstall();
terminal::uninstall(); if(terminal::active())
terminal::uninstall();
mainThreadDone = true; mainThreadDone = true;
return 0; return 0;
} }

View File

@ -341,7 +341,7 @@ vector<string> config::parseConfig(const std::string& path) {
} }
cfgStream.close(); cfgStream.close();
map<string,deque<string>> comments; std::map<std::string, std::deque<std::string>> comments;
try { try {
int config_version; int config_version;
string teaspeak_license; string teaspeak_license;
@ -351,9 +351,9 @@ vector<string> config::parseConfig(const std::string& path) {
build_comments(comments, bindings); build_comments(comments, bindings);
} }
if(config_version > CURRENT_CONFIG_VERSION) { if(config_version > CURRENT_CONFIG_VERSION) {
errors.push_back("Given config version is higher that currently supported config version!"); errors.emplace_back("Given config version is higher that currently supported config version!");
errors.push_back("Decrease the version by hand to " + to_string(CURRENT_CONFIG_VERSION)); errors.push_back("Decrease the version by hand to " + to_string(CURRENT_CONFIG_VERSION));
errors.push_back("Attention: Decreasing the version could may lead to data loss!"); errors.emplace_back("Attention: Decreasing the version could may lead to data loss!");
return errors; return errors;
} }
{ {
@ -462,15 +462,13 @@ vector<string> config::parseConfig(const std::string& path) {
} }
if(!config::license){ if(!config::license){
logErrorFmt(true, LOG_GENERAL, strobf("The given license isn't valid!").string()); logErrorFmt(true, LOG_GENERAL, strobf("The given license could not be parsed!").string());
logErrorFmt(true, LOG_GENERAL, strobf("Falling back to the default license.").string()); logErrorFmt(true, LOG_GENERAL, strobf("Falling back to the default license.").string());
teaspeak_license = "none"; teaspeak_license = "none";
goto license_parsing; goto license_parsing;
} }
if(!config::license){
errors.emplace_back(strobf("Invalid license code!").string()); /*
return errors;
}
if(!config::license->isValid()) { if(!config::license->isValid()) {
if(config::license->data.type == license::LicenseType::INVALID) { if(config::license->data.type == license::LicenseType::INVALID) {
errors.emplace_back(strobf("Give license isn't valid!").string()); errors.emplace_back(strobf("Give license isn't valid!").string());
@ -482,6 +480,7 @@ vector<string> config::parseConfig(const std::string& path) {
teaspeak_license = "none"; teaspeak_license = "none";
goto license_parsing; goto license_parsing;
} }
*/
} }
{ {
@ -548,8 +547,7 @@ vector<string> config::parseConfig(const std::string& path) {
} }
std::vector<std::string> config::reload() { std::vector<std::string> config::reload() {
std::vector<std::string> errors;
vector<string> errors;
saveConfig = false; saveConfig = false;
ifstream cfgStream(_config_path); ifstream cfgStream(_config_path);
@ -589,6 +587,53 @@ std::vector<std::string> config::reload() {
return errors; return errors;
} }
bool config::update_license(std::string &error, const std::string &new_license) {
std::vector<std::string> lines{};
{
lines.reserve(1024);
std::ifstream icfg_stream{_config_path};
if(!icfg_stream) {
error = "failed to open config file";
return false;
}
std::string line{};
while(std::getline(icfg_stream, line))
lines.push_back(line);
icfg_stream.close();
}
bool license_found{false};
for(auto& line : lines) {
if(!line.starts_with(" license:")) continue;
line = " license: \"" + new_license + "\"";
license_found = true;
break;
}
if(!license_found) {
error = "missing license config key";
return false;
}
{
std::ofstream ocfg_stream{_config_path};
if(!ocfg_stream) {
error = "failed to write to config file";
return false;
}
for(const auto& line : lines)
ocfg_stream << line << "\n";
ocfg_stream << std::flush;
ocfg_stream.close();
}
return true;
}
void bind_string_description(const shared_ptr<EntryBinding>& _entry, std::string& target, const std::string& default_value) { void bind_string_description(const shared_ptr<EntryBinding>& _entry, std::string& target, const std::string& default_value) {
_entry->default_value = [default_value]() -> std::deque<std::string> { return { default_value }; }; _entry->default_value = [default_value]() -> std::deque<std::string> { return { default_value }; };
_entry->value_description = [] { return "The value must be a string"; }; _entry->value_description = [] { return "The value must be a string"; };

View File

@ -29,6 +29,7 @@ namespace ts::config {
std::function<void(const std::string&)> read_argument; std::function<void(const std::string&)> read_argument;
}; };
extern bool update_license(std::string& /* error */, const std::string& /* new license */);
extern std::vector<std::string> parseConfig(const std::string& /* path */); extern std::vector<std::string> parseConfig(const std::string& /* path */);
extern std::vector<std::string> reload(); extern std::vector<std::string> reload();
extern std::deque<std::shared_ptr<EntryBinding>> create_bindings(); extern std::deque<std::shared_ptr<EntryBinding>> create_bindings();

View File

@ -44,7 +44,11 @@ InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) {
this->statistics = make_shared<stats::ConnectionStatistics>(nullptr, true); this->statistics = make_shared<stats::ConnectionStatistics>(nullptr, true);
this->statistics->measure_bandwidths(true); this->statistics->measure_bandwidths(true);
this->licenseHelper = make_shared<license::LicenseHelper>(); std::string error_message{};
this->license_service_ = std::make_shared<license::LicenseService>();
if(!this->license_service_->initialize(error_message)) {
logCritical(LOG_INSTANCE, strobf("Failed to the license service: {}").string(), error_message);
}
this->dbHelper = new DatabaseHelper(this->getSql()); this->dbHelper = new DatabaseHelper(this->getSql());
this->_properties = new Properties(); this->_properties = new Properties();
@ -214,7 +218,6 @@ InstanceHandler::~InstanceHandler() {
globalServerAdmin = nullptr; globalServerAdmin = nullptr;
_musicRoot = nullptr; _musicRoot = nullptr;
licenseHelper = nullptr;
statistics = nullptr; statistics = nullptr;
tick_manager = nullptr; tick_manager = nullptr;
} }
@ -453,6 +456,8 @@ void InstanceHandler::stopInstance() {
this->sslMgr = nullptr; this->sslMgr = nullptr;
this->web_event_loop = nullptr; this->web_event_loop = nullptr;
this->license_service_->shutdown();
} }
void InstanceHandler::tickInstance() { void InstanceHandler::tickInstance() {
@ -470,7 +475,7 @@ void InstanceHandler::tickInstance() {
} }
{ {
ALARM_TIMER(t, strobf("InstanceHandler::tickInstance -> license tick").string(), milliseconds(5)); ALARM_TIMER(t, strobf("InstanceHandler::tickInstance -> license tick").string(), milliseconds(5));
this->licenseHelper->tick(); this->license_service_->execute_tick();
} }
} }
{ {
@ -637,36 +642,35 @@ string get_mac_address() {
} }
#define SN_BUFFER 1024 #define SN_BUFFER 1024
std::shared_ptr<license::LicenseRequestData> InstanceHandler::generateLicenseData() { std::shared_ptr<ts::server::license::InstanceLicenseInfo> InstanceHandler::generateLicenseData() {
auto request = make_shared<license::LicenseRequestData>(); auto request = std::make_shared<license::InstanceLicenseInfo>();
request->license = config::license; request->license = config::license;
request->servers_online = this->voiceServerManager->runningServers(); request->metrics.servers_online = this->voiceServerManager->runningServers();
auto report = this->voiceServerManager->clientReport(); auto report = this->voiceServerManager->clientReport();
request->client_online = report.clients_ts; request->metrics.client_online = report.clients_ts;
request->web_clients_online = report.clients_web; request->metrics.web_clients_online = report.clients_web;
request->bots_online = report.bots; request->metrics.bots_online = report.bots;
request->queries_online = report.queries; request->metrics.queries_online = report.queries;
request->speach_total = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_TOTAL].as<uint64_t>(); request->metrics.speech_total = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_TOTAL].as<uint64_t>();
request->speach_varianz = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as<uint64_t>(); request->metrics.speech_varianz = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as<uint64_t>();
request->speach_online = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE].as<uint64_t>(); request->metrics.speech_online = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE].as<uint64_t>();
request->speach_dead = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as<uint64_t>(); request->metrics.speech_dead = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as<uint64_t>();
static std::string null_str{"\0\0\0\0\0\0\0\0", 8}; /* we need at least some characters */ static std::string null_str{"\0\0\0\0\0\0\0\0", 8}; /* we need at least some characters */
request->web_certificate_revision = this->web_cert_revision.empty() ? null_str : this->web_cert_revision; request->web_certificate_revision = this->web_cert_revision.empty() ? null_str : this->web_cert_revision;
{ {
auto info = make_shared<license::ServerInfo>(); request->info.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
info->timestamp = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); request->info.version = build::version()->string(true);
info->version = build::version()->string(true);
{ /* uname */ { /* uname */
utsname retval{}; utsname retval{};
if(uname(&retval) < 0) { if(uname(&retval) < 0) {
info->uname = "unknown (" + string(strerror(errno)) + ")"; request->info.uname = "unknown (" + string(strerror(errno)) + ")";
} else { } else {
char buffer[SN_BUFFER]; char buffer[SN_BUFFER];
snprintf(buffer, SN_BUFFER, "sys:%s version:%s release:%s", retval.sysname, retval.version, retval.release); snprintf(buffer, SN_BUFFER, "sys:%s version:%s release:%s", retval.sysname, retval.version, retval.release);
info->uname = string(buffer); request->info.uname = string(buffer);
} }
} }
@ -676,12 +680,10 @@ std::shared_ptr<license::LicenseRequestData> InstanceHandler::generateLicenseDat
if(property_unique_id.as<string>().empty()) if(property_unique_id.as<string>().empty())
property_unique_id = rnd_string(64); property_unique_id = rnd_string(64);
auto hash = digest::sha256(info->uname); auto hash = digest::sha256(request->info.uname);
hash = digest::sha256(hash + property_unique_id.as<string>() + get_mac_address()); hash = digest::sha256(hash + property_unique_id.as<string>() + get_mac_address());
info->unique_identifier = base64::encode(hash); request->info.unique_id = base64::encode(hash);
} }
request->info = info;
} }
return request; return request;
} }

View File

@ -3,9 +3,8 @@
#include <sql/SqlQuery.h> #include <sql/SqlQuery.h>
#include <Properties.h> #include <Properties.h>
#include "VirtualServerManager.h" #include "VirtualServerManager.h"
#include "../../license/shared/LicenseRequest.h"
#include "lincense/LicenseHelper.h"
#include <ssl/SSLManager.h> #include <ssl/SSLManager.h>
#include <src/lincense/LicenseService.h>
#include "manager/SqlDataManager.h" #include "manager/SqlDataManager.h"
#include "lincense/TeamSpeakLicense.h" #include "lincense/TeamSpeakLicense.h"
#include "server/WebIoManager.h" #include "server/WebIoManager.h"
@ -20,6 +19,10 @@ namespace ts {
} }
namespace server { namespace server {
namespace license {
class LicenseService;
}
class InstanceHandler { class InstanceHandler {
public: public:
explicit InstanceHandler(SqlDataManager*); explicit InstanceHandler(SqlDataManager*);
@ -63,7 +66,7 @@ namespace ts {
std::shared_ptr<stats::ConnectionStatistics> getStatistics(){ return statistics; } std::shared_ptr<stats::ConnectionStatistics> getStatistics(){ return statistics; }
std::shared_ptr<threads::Scheduler> scheduler(){ return this->tick_manager; } std::shared_ptr<threads::Scheduler> scheduler(){ return this->tick_manager; }
std::shared_ptr<license::LicenseRequestData> generateLicenseData(); std::shared_ptr<license::InstanceLicenseInfo> generateLicenseData();
std::shared_ptr<TeamSpeakLicense> getTeamSpeakLicense() { return this->teamspeak_license; } std::shared_ptr<TeamSpeakLicense> getTeamSpeakLicense() { return this->teamspeak_license; }
std::shared_ptr<ts::Properties> getDefaultServerProperties() { return this->default_server_properties; } std::shared_ptr<ts::Properties> getDefaultServerProperties() { return this->default_server_properties; }
@ -90,6 +93,8 @@ namespace ts {
bool granted = false, bool granted = false,
std::shared_ptr<CalculateCache> cache = nullptr std::shared_ptr<CalculateCache> cache = nullptr
); );
[[nodiscard]] inline std::shared_ptr<license::LicenseService> license_service() { return this->license_service_; }
private: private:
std::mutex activeLock; std::mutex activeLock;
std::condition_variable activeCon; std::condition_variable activeCon;
@ -126,7 +131,7 @@ namespace ts {
std::shared_ptr<ts::server::InternalClient> globalServerAdmin = nullptr; std::shared_ptr<ts::server::InternalClient> globalServerAdmin = nullptr;
std::shared_ptr<ConnectedClient> _musicRoot = nullptr; std::shared_ptr<ConnectedClient> _musicRoot = nullptr;
std::shared_ptr<license::LicenseHelper> licenseHelper = nullptr; std::shared_ptr<license::LicenseService> license_service_{nullptr};
std::shared_ptr<stats::ConnectionStatistics> statistics = nullptr; std::shared_ptr<stats::ConnectionStatistics> statistics = nullptr;
std::shared_ptr<threads::Scheduler> tick_manager = nullptr; std::shared_ptr<threads::Scheduler> tick_manager = nullptr;

View File

@ -39,7 +39,7 @@ bool SpeakingClient::shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedCl
if(!this->shouldReceiveVoice(sender)) if(!this->shouldReceiveVoice(sender))
return false; return false;
return permission::v2::permission_granted(this->cpmerission_needed_whisper_power, sender->cpmerission_whisper_power); return permission::v2::permission_granted(this->cpmerission_needed_whisper_power, sender->cpmerission_whisper_power, false);
} }
void SpeakingClient::handlePacketVoice(const pipes::buffer_view& data, bool head, bool fragmented) { void SpeakingClient::handlePacketVoice(const pipes::buffer_view& data, bool head, bool fragmented) {
@ -127,6 +127,15 @@ enum WhisperTarget {
CHANNEL_SUBCHANNELS = 6 CHANNEL_SUBCHANNELS = 6
}; };
inline bool update_whisper_error(std::chrono::system_clock::time_point& last) {
auto now = std::chrono::system_clock::now();
if(last + std::chrono::milliseconds{500} < now) {
last = now;
return true;
}
return false;
}
//All clients => type := SERVER_GROUP and target_id := 0 //All clients => type := SERVER_GROUP and target_id := 0
//Server group => type := SERVER_GROUP and target_id := <server group id> //Server group => type := SERVER_GROUP and target_id := <server group id>
//Channel group => type := CHANNEL_GROUP and target_id := <channel group id> //Channel group => type := CHANNEL_GROUP and target_id := <channel group id>
@ -239,7 +248,27 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
return target->currentChannel->parent() != current; return target->currentChannel->parent() != current;
}), available_clients.end()); }), available_clients.end());
} }
if(available_clients.empty()) return;
auto self_lock = this->_this.lock();
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const std::shared_ptr<ConnectedClient>& cl) {
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
return !speakingClient->shouldReceiveVoiceWhisper(self_lock);
}), available_clients.end());
if(available_clients.empty()) {
if(update_whisper_error(this->speak_last_no_whisper_target)) {
command_result result{error::whisper_no_targets};
this->notifyError(result);
}
return;
}
if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) {
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
command_result result{error::whisper_too_many_targets};
this->notifyError(result);
}
return;
}
//Create the packet data //Create the packet data
char packet_buffer[OUT_WHISPER_PKT_OFFSET + data_length]; char packet_buffer[OUT_WHISPER_PKT_OFFSET + data_length];
@ -253,7 +282,6 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
VoicePacketFlags flags{}; VoicePacketFlags flags{};
auto data = pipes::buffer_view(packet_buffer, OUT_WHISPER_PKT_OFFSET + data_length); auto data = pipes::buffer_view(packet_buffer, OUT_WHISPER_PKT_OFFSET + data_length);
for(const auto& cl : available_clients){ for(const auto& cl : available_clients){
if(cl->shouldReceiveVoiceWhisper(_this.lock()))
cl->send_voice_whisper_packet(data, flags); cl->send_voice_whisper_packet(data, flags);
} }
@ -275,6 +303,44 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
for(uint8_t index = 0; index < channelCount; index++) for(uint8_t index = 0; index < channelCount; index++)
clientIds[index] = be2le16((char*) data.data_ptr(), offset, &offset); clientIds[index] = be2le16((char*) data.data_ptr(), offset, &offset);
auto available_clients = this->server->getClients();
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const std::shared_ptr<ConnectedClient>& cl) {
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
if(!speakingClient || cl == this || !speakingClient->currentChannel) return true;
auto clientChannelId = cl->currentChannel->channelId();
auto clientId = cl->getClientId();
for(uint8_t index = 0; index < clientCount; index++)
if(channelIds[index] == clientChannelId) return false;
for(uint8_t index = 0; index < channelCount; index++)
if(clientIds[index] == clientId) return false;
return true;
}), available_clients.end());
auto self_lock = this->_this.lock();
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const std::shared_ptr<ConnectedClient>& cl) {
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
return !speakingClient->shouldReceiveVoiceWhisper(self_lock);
}), available_clients.end());
if(available_clients.empty()) {
if(update_whisper_error(this->speak_last_no_whisper_target)) {
command_result result{error::whisper_no_targets};
this->notifyError(result);
}
return;
}
if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) {
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
command_result result{error::whisper_too_many_targets};
this->notifyError(result);
}
return;
}
size_t dataLength = data.length() - offset; size_t dataLength = data.length() - offset;
#ifdef PKT_LOG_WHISPER #ifdef PKT_LOG_WHISPER
logTrace(this->getServerId(), "{} Whisper data length: {}. Client count: {}. Channel count: {}.", CLIENT_STR_LOG_PREFIX, dataLength, clientCount, channelCount); logTrace(this->getServerId(), "{} Whisper data length: {}. Client count: {}. Channel count: {}.", CLIENT_STR_LOG_PREFIX, dataLength, clientCount, channelCount);
@ -291,21 +357,9 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
VoicePacketFlags flags{}; VoicePacketFlags flags{};
auto data = pipes::buffer_view(packetBuffer, OUT_WHISPER_PKT_OFFSET + dataLength); auto data = pipes::buffer_view(packetBuffer, OUT_WHISPER_PKT_OFFSET + dataLength);
for(const auto& cl : this->server->getClients()){ //Faster? for(const auto& cl : available_clients){ //Faster?
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl); auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
if(!speakingClient || cl == this) continue; assert(speakingClient);
if(!cl->currentChannel) continue;
auto clientChannelId = cl->currentChannel->channelId();
auto clientId = cl->getClientId();
for(uint8_t index = 0; index < clientCount; index++)
if(channelIds[index] == clientChannelId) goto handleSend;
for(uint8_t index = 0; index < channelCount; index++)
if(clientIds[index] == clientId) goto handleSend;
continue;
handleSend:
if(speakingClient->shouldReceiveVoiceWhisper(_this.lock())) if(speakingClient->shouldReceiveVoiceWhisper(_this.lock()))
speakingClient->send_voice_whisper_packet(data, flags); speakingClient->send_voice_whisper_packet(data, flags);
} }

View File

@ -81,6 +81,9 @@ namespace ts {
std::chrono::system_clock::time_point speak_begin; std::chrono::system_clock::time_point speak_begin;
std::chrono::system_clock::time_point speak_last_packet; std::chrono::system_clock::time_point speak_last_packet;
std::chrono::system_clock::time_point speak_last_no_whisper_target;
std::chrono::system_clock::time_point speak_last_too_many_whisper_targets;
permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value}; permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value};
struct { struct {
HandshakeState state{HandshakeState::BEGIN}; HandshakeState state{HandshakeState::BEGIN};

View File

@ -1,11 +1,11 @@
#include "SpeakingClient.h" #include "SpeakingClient.h"
#include <misc/endianness.h>
#include <src/VirtualServerManager.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <src/InstanceHandler.h>
#include <misc/base64.h> #include <misc/base64.h>
#include <misc/digest.h> #include <misc/digest.h>
#include <misc/rnd.h> #include <misc/rnd.h>
#include <log/LogUtils.h>
#include "../VirtualServerManager.h"
#include "../InstanceHandler.h"
#if defined(TCP_CORK) && !defined(TCP_NOPUSH) #if defined(TCP_CORK) && !defined(TCP_NOPUSH)
#define TCP_NOPUSH TCP_CORK #define TCP_NOPUSH TCP_CORK

View File

@ -1247,6 +1247,8 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) {
if(conversation) if(conversation)
conversation->set_history_length(cmd[key->name]); conversation->set_history_length(cmd[key->name]);
} }
} else if(*key == property::CHANNEL_NEEDED_TALK_POWER) {
channel->permissions()->set_permission(permission::i_client_needed_talk_power, {cmd[key->name].as<int>(), 0}, permission::v2::set_value, permission::v2::do_nothing);
} }
channel->properties()[key] = cmd[key->name].string(); channel->properties()[key] = cmd[key->name].string();

View File

@ -1906,7 +1906,7 @@ command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) {
} }
string command = "cat \"" + log_path + "\""; string command = "cat \"" + log_path + "\"";
command += " | grep -E "; command += " | grep -E ";
command += "\"\\] \\[.*\\]( ){0,6}?" + server_identifier + " \\|\""; command += R"("\] \[.*\]( ){0,6}?)" + server_identifier + " \\|\"";
size_t beginpos = cmd[0].has("begin_pos") ? cmd["begin_pos"].as<size_t>() : 0ULL; //TODO test it? size_t beginpos = cmd[0].has("begin_pos") ? cmd["begin_pos"].as<size_t>() : 0ULL; //TODO test it?
size_t file_index = 0; size_t file_index = 0;
@ -1927,7 +1927,7 @@ command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) {
if(beginpos != 0 && file_index + read > beginpos) { //We're done we just want to get the size later if(beginpos != 0 && file_index + read > beginpos) { //We're done we just want to get the size later
line_buffer += string(buffer.data(), beginpos - file_index); line_buffer += string(buffer.data(), beginpos - file_index);
lines.push_back({file_index, line_buffer}); lines.emplace_back(file_index, line_buffer);
if(lines.size() > max_lines) lines.pop_front(); if(lines.size() > max_lines) lines.pop_front();
//debugMessage(LOG_GENERAL, "Final line {}", line_buffer); //debugMessage(LOG_GENERAL, "Final line {}", line_buffer);
line_buffer = ""; line_buffer = "";
@ -1955,7 +1955,7 @@ command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) {
if(length == 0) length = 1; if(length == 0) length = 1;
//debugMessage(LOG_GENERAL, "Got line {}", line_buffer.substr(0, index)); //debugMessage(LOG_GENERAL, "Got line {}", line_buffer.substr(0, index));
lines.push_back({file_index + cut_offset, line_buffer.substr(0, index)}); lines.emplace_back(file_index + cut_offset, line_buffer.substr(0, index));
if(lines.size() > max_lines) lines.pop_front(); if(lines.size() > max_lines) lines.pop_front();
cut_offset += index + length; cut_offset += index + length;
@ -1968,7 +1968,7 @@ command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) {
} }
if(!line_buffer.empty()) { if(!line_buffer.empty()) {
lines.push_back({file_index - line_buffer.length(), line_buffer}); lines.emplace_back(file_index - line_buffer.length(), line_buffer);
if(lines.size() > max_lines) lines.pop_front(); if(lines.size() > max_lines) lines.pop_front();
} }
} }
@ -2004,8 +2004,13 @@ command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) {
ts += type + " | | |" + line.substr(line.find('|') + 1); ts += type + " | | |" + line.substr(line.find('|') + 1);
} }
if(ts.length() > 1024)
ts = ts.substr(0, 1024) + "...";
result[index++]["l"] = ts; result[index++]["l"] = ts;
} else { } else {
if(line.length() > 1024)
line = line.substr(0, 1024) + "...";
result[index++]["l"] = line; result[index++]["l"] = line;
} }
} }

View File

@ -7,6 +7,7 @@
#include <misc/memtracker.h> #include <misc/memtracker.h>
#include <misc/base64.h> #include <misc/base64.h>
#include "src/client/ConnectedClient.h" #include "src/client/ConnectedClient.h"
#include <netinet/tcp.h>
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;

View File

@ -1,200 +0,0 @@
#include <log/LogUtils.h>
#include <misc/strobf.h>
#include <misc/hex.h>
#include <src/Configuration.h>
#include <arpa/inet.h>
#include <src/ShutdownHelper.h>
#include "src/InstanceHandler.h"
#include "LicenseHelper.h"
using namespace license;
using namespace std;
using namespace std::chrono;
using namespace ts;
using namespace ts::server;
LicenseHelper::LicenseHelper() {
this->scheduled_request = system_clock::now() + seconds(rand() % 30); //Check in one minute
}
LicenseHelper::~LicenseHelper() {
if(this->request.prepare_thread.joinable())
this->request.prepare_thread.join();
{
unique_lock lock(this->request.current_lock);
if(this->request.current) {
auto request = move(this->request.current);
lock.unlock();
request->abortRequest();
request->callback_update_certificate = nullptr;
}
}
}
inline string format_time(const system_clock::time_point& time) {
std::time_t now = system_clock::to_time_t(time);
std::tm * ptm = std::localtime(&now);
char buffer[128];
const auto length = std::strftime(buffer, 128, "%a, %d.%m.%Y %H:%M:%S", ptm);
return string(buffer, length);
}
void LicenseHelper::tick() {
lock_guard tick_lock(this->license_tick_lock);
bool verbose = config::license->isPremium();
{
lock_guard request_lock(this->request.current_lock);
if(this->request.current) {
auto promise = this->request.current->requestInfo();
if(promise.state() != threads::FutureState::WORKING){
auto exception = this->request.current->exception();
if(promise.state() == threads::FutureState::FAILED) {
this->handle_request_failed(verbose, exception ? exception->what() : strobf("unknown").c_str());
this->request.current = nullptr; /* connection should be already closed */
return;
} else {
auto response = promise.waitAndGet(nullptr);
this->request.current = nullptr; /* connection should be already closed */
if(!response){
this->handle_request_failed(verbose, exception ? exception->what() : strobf("invalid result (null)").c_str());
return;
}
if(!response->license_valid || !response->properties_valid){
if(!response->license_valid) {
if(config::license->isPremium()) logCritical(LOG_INSTANCE, strobf("Could not validate license.").c_str());
else logCritical(LOG_INSTANCE, strobf("Your server has been shutdown remotely!").c_str());
} else if(!response->properties_valid) {
logCritical(LOG_INSTANCE, strobf("Property adjustment failed!").c_str());
} else
logCritical(LOG_INSTANCE, strobf("Your license expired!").c_str());
logCritical(LOG_INSTANCE, strobf("Stopping application!").c_str());
ts::server::shutdownInstance();
return;
} else {
this->scheduled_request = this->last_request + hours(2);
logMessage(LOG_INSTANCE, strobf("License successfully validated! Scheduling next check at {}").c_str(), format_time(this->scheduled_request));
if(response->speach_reset)
serverInstance->resetSpeechTime();
serverInstance->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ] = response->speach_varianz_adjustment;
{
lock_guard lock(this->request.info_lock);
this->request.info = response->license;
}
this->request_fail_count = 0;
this->last_successful_request = system_clock::now();
}
}
}
}
}
if(system_clock::now() > scheduled_request){
this->do_request(verbose);
}
}
void LicenseHelper::do_request(bool verbose) {
if(config::license && config::license->isPremium())
logMessage(LOG_INSTANCE, strobf("Validating license").c_str());
else
logMessage(LOG_INSTANCE, strobf("Validating instance integrity").c_str());
this->last_request = system_clock::now();
this->scheduled_request = this->last_request + minutes(10); /* some kind of timeout */
{
unique_lock lock(this->request.current_lock);
if(this->request.current) {
auto request = move(this->request.current);
lock.unlock();
if(verbose) {
auto promise = request->requestInfo();
if(promise.state() == threads::FutureState::WORKING) {
logMessage(LOG_INSTANCE, strobf("Old check timed out (10 min). Running new one!").c_str());
}
}
request->abortRequest();
}
}
if(this->request.prepare_thread.joinable()) /* usually the preparation should not take too long */
this->request.prepare_thread.join();
this->request.prepare_thread = std::thread([&]{
sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
#ifdef DO_LOCAL_REQUEST
auto license_host = gethostbyname(strobf("localhost").c_str());
server_addr.sin_addr.s_addr = ((in_addr*) license_host->h_addr)->s_addr;
#else
auto license_host = gethostbyname(strobf("license.teaspeak.de").c_str());
if(!license_host){
if(verbose) logError(LOG_INSTANCE, strobf("Could not valid license! (1)").c_str());
return;
}
if(!license_host->h_addr){
if(verbose) logError(LOG_INSTANCE, strobf("Could not valid license! (2)").c_str());
return;
}
server_addr.sin_addr.s_addr = ((in_addr*) license_host->h_addr)->s_addr;
int first = server_addr.sin_addr.s_addr >> 24;
if(first == 0 || first == 127 || first == 255) {
if(config::license->isPremium()) {
logError(LOG_INSTANCE, strobf("You tried to nullroot 'license.teaspeak.de'!").c_str());
logCritical(LOG_INSTANCE, strobf("Could not validate license!").c_str());
logCritical(LOG_INSTANCE, strobf("Stopping server!").c_str());
ts::server::shutdownInstance();
return;
}
return;
}
#endif
server_addr.sin_port = htons(27786);
auto license_data = serverInstance->generateLicenseData();
auto request = make_shared<license::LicenceRequest>(license_data, server_addr);
request->verbose = false;
request->callback_update_certificate = [&](const auto& update) { this->callback_certificate_update(update); };
{
lock_guard lock(this->request.current_lock);
this->request.current = request;
request->requestInfo();
}
});
}
void LicenseHelper::handle_request_failed(bool verbose, const std::string& error) {
if(config::license && config::license->isPremium())
logError(LOG_INSTANCE, strobf("License validation failed: {}").c_str(), error);
else
logError(LOG_INSTANCE, strobf("Instance integrity check failed: {}").c_str(), error);
this->request_fail_count++;
milliseconds next_request;
if(this->request_fail_count <= 1)
next_request = minutes(1);
else if(this->request_fail_count <= 5)
next_request = minutes(5);
else if(this->request_fail_count <= 10)
next_request = minutes(10);
else
next_request = minutes(30);
this->scheduled_request = this->last_request + next_request;
if(verbose)
logMessage(LOG_INSTANCE, strobf("Scheduling next check at {}").c_str(), format_time(this->scheduled_request));
}
void LicenseHelper::callback_certificate_update(const license::WebCertificate &certificate) {
serverInstance->setWebCertRoot(certificate.key, certificate.certificate, certificate.revision);
}

View File

@ -1,40 +0,0 @@
#pragma once
#include <ThreadPool/Mutex.h>
#include "../../../license/shared/License.h"
#include "../../../license/shared/LicenseRequest.h"
namespace license {
class LicenseHelper {
public:
LicenseHelper();
~LicenseHelper();
void tick();
std::shared_ptr<license::LicenseInfo> getLicenseInfo() {
std::lock_guard lock(this->request.info_lock);
return this->request.info;
}
private:
std::mutex license_tick_lock;
std::chrono::system_clock::time_point scheduled_request;
std::chrono::system_clock::time_point last_request;
std::chrono::system_clock::time_point last_successful_request;
size_t request_fail_count = 0;
struct {
std::shared_ptr<license::LicenceRequest> current = nullptr;
std::mutex current_lock;
std::shared_ptr<license::LicenseInfo> info;
std::mutex info_lock;
std::thread prepare_thread;
} request;
void do_request(bool /* verbose */);
void handle_request_failed(bool /* verbose */, const std::string& /* error */);
void callback_certificate_update(const license::WebCertificate&);
};
}

View File

@ -0,0 +1,540 @@
//
// Created by WolverinDEV on 27/02/2020.
//
#include <netdb.h>
#include <cassert>
#include <misc/strobf.h>
#include <log/LogUtils.h>
#include <src/Configuration.h>
#include <src/ShutdownHelper.h>
#include <misc/base64.h>
#include "../../../license/shared/LicenseServerClient.h"
#include "../../../cmake-build-debug-wsl/license/LicenseRequest.pb.h"
#include "src/InstanceHandler.h"
#include "../../../license/shared/License.h"
#define DO_LOCAL_REQUEST
using namespace ts::server::license;
LicenseService::LicenseService() {
this->dns.lock = std::make_shared<std::recursive_mutex>();
}
LicenseService::~LicenseService() {
{
std::lock_guard lock{this->request_lock};
this->abort_request(lock, "");
}
}
bool LicenseService::initialize(std::string &error) {
//this->verbose_ = true;
this->startup_timepoint_ = std::chrono::steady_clock::now();
this->timings.next_request = std::chrono::system_clock::now() + std::chrono::seconds(rand() % 20);
return true;
}
bool LicenseService::execute_request_sync(const std::chrono::milliseconds& timeout) {
std::unique_lock slock{this->sync_request_lock};
this->begin_request();
if(this->sync_request_cv.wait_for(slock, timeout) == std::cv_status::timeout)
return false;
return this->timings.failed_count == 0;
}
void LicenseService::shutdown() {
std::lock_guard lock{this->request_lock};
if(this->request_state_ == request_state::empty) return;
this->abort_request(lock, "shutdown");
}
void LicenseService::begin_request() {
std::lock_guard lock{this->request_lock};
if(this->request_state_ != request_state::empty)
this->abort_request(lock, "last request has been aborted");
if(this->verbose_)
debugMessage(LOG_INSTANCE, strobf("Executing license request.").string());
this->timings.last_request = std::chrono::system_clock::now();
this->request_state_ = request_state::dns_lookup;
this->execute_dns_request();
}
void LicenseService::abort_request(std::lock_guard<std::recursive_timed_mutex> &, const std::string& reason) {
if(this->request_state_ == request_state::dns_lookup) {
this->abort_dns_request();
return;
} else if(this->current_client) {
this->current_client->callback_connected = nullptr;
this->current_client->callback_message = nullptr;
this->current_client->callback_disconnected = nullptr;
if(!reason.empty()) {
this->current_client->disconnect(reason, std::chrono::system_clock::now() + std::chrono::seconds{1});
/* Lets not wait here because we might be within the event loop. */
//if(!this->current_client->await_disconnect())
// this->current_client->close_connection();
} else {
this->current_client->close_connection();
}
this->current_client.release();
}
}
void LicenseService::abort_dns_request() {
std::unique_lock llock{*this->dns.lock};
if(!this->dns.current_lookup) return;
this->dns.current_lookup->handle = nullptr;
this->dns.current_lookup = nullptr;
}
void LicenseService::execute_dns_request() {
std::unique_lock llock{*this->dns.lock};
assert(!this->dns.current_lookup);
auto lookup = new _dns::_lookup{};
lookup->lock = this->dns.lock;
lookup->handle = this;
lookup->thread = std::thread([lookup] {
bool success{false};
std::string error{};
sockaddr_in server_addr{};
{
server_addr.sin_family = AF_INET;
#ifdef DO_LOCAL_REQUEST
auto license_host = gethostbyname(strobf("localhost").c_str());
#else
auto license_host = gethostbyname(strobf("license.teaspeak.de").c_str());
#endif
if(!license_host) {
error = strobf("result is null").string();
goto handle_result;
}
if(!license_host->h_addr){
error = strobf("missing h_addr in result").string();
goto handle_result;
}
server_addr.sin_addr.s_addr = ((in_addr*) license_host->h_addr)->s_addr;
#ifndef DO_LOCAL_REQUEST
int first = server_addr.sin_addr.s_addr >> 24;
if(first == 0 || first == 127 || first == 255) {
error = strobf("local response address").string();
goto handle_result;
}
#endif
server_addr.sin_port = htons(27786);
success = true;
}
handle_result:
{
std::unique_lock llock{*lookup->lock};
if(lookup->handle) {
lookup->handle->dns.current_lookup = nullptr;
if(success) {
debugMessage(LOG_INSTANCE, strobf("Successfully resolved the hostname to {}").string(), net::to_string(server_addr.sin_addr));
lookup->handle->handle_dns_lookup_result(true, server_addr);
} else {
debugMessage(LOG_INSTANCE, strobf("Failed to resolve hostname for license server: {}").string(), error);
lookup->handle->handle_dns_lookup_result(false, error);
}
}
assert(lookup->thread.get_id() == std::this_thread::get_id());
if(lookup->thread.joinable())
lookup->thread.detach();
delete lookup;
}
});
this->dns.current_lookup = lookup;
}
void LicenseService::handle_check_succeeded() {
{
std::lock_guard rlock{this->request_lock};
this->abort_request(rlock, strobf("request succeeded").string());
this->schedule_next_request(true);
this->request_state_ = request_state::empty;
if(config::license->isPremium()) {
logMessage(LOG_INSTANCE, strobf("License has been validated.").string());
} else {
logMessage(LOG_INSTANCE, strobf("Instance integrity has been validated.").string());
}
}
{
std::unique_lock slock{this->sync_request_lock};
this->sync_request_cv.notify_all();
}
}
void LicenseService::handle_check_fail(const std::string &error) {
{
std::lock_guard rlock{this->request_lock};
this->abort_request(rlock, "request failed");
if(config::license->isPremium()) {
logCritical(LOG_INSTANCE, strobf("Failed to validate license:").string());
logCritical(LOG_INSTANCE, error);
logCritical(LOG_INSTANCE, strobf("Stopping server!").string());
ts::server::shutdownInstance();
} else {
logError(LOG_INSTANCE, strobf("Failed to validate instance integrity:").string());
logError(LOG_INSTANCE, error);
}
this->schedule_next_request(false);
this->request_state_ = request_state::empty;
}
{
std::unique_lock slock{this->sync_request_lock};
this->sync_request_cv.notify_all();
}
}
void LicenseService::handle_dns_lookup_result(bool success, const std::variant<std::string, sockaddr_in> &result) {
if(!success) {
this->handle_check_fail(std::get<std::string>(result));
return;
}
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::dns_lookup) {
logError(LOG_INSTANCE, strobf("Request state isn't dns lookup anymore. Aborting dns lookup result callback.").string());
return;
}
this->request_state_ = request_state::connecting;
assert(!this->current_client);
this->current_client = std::make_unique<::license::client::LicenseServerClient>(std::get<sockaddr_in>(result), 3);
this->current_client->callback_connected = [&]{ this->handle_client_connected(); };
this->current_client->callback_disconnected = [&](bool expected, const std::string& error) {
this->handle_client_disconnected(error);
};
this->current_client->callback_message = [&](auto a, auto b, auto c) {
this->handle_message(a, b, c);
};
std::string error{};
if(!this->current_client->start_connection(error))
this->handle_check_fail(strobf("connect failed: ").string() + error);
}
void LicenseService::client_send_message(::license::protocol::PacketType type, ::google::protobuf::Message &message) {
auto buffer = message.SerializeAsString();
assert(this->current_client);
this->current_client->send_message(type, buffer.data(), buffer.length());
}
void LicenseService::handle_client_connected() {
{
if(this->verbose_)
debugMessage(LOG_INSTANCE, strobf("License client connected").string());
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::connecting) {
logError(LOG_INSTANCE, strobf("Request state isn't connecting anymore. Aborting client connect callback.").string());
return;
}
this->request_state_ = request_state::license_validate;
}
this->send_license_validate_request();
}
void LicenseService::handle_message(::license::protocol::PacketType type, const void *buffer, size_t size) {
switch (type) {
case ::license::protocol::PACKET_SERVER_VALIDATION_RESPONSE:
this->handle_message_license_info(buffer, size);
return;
case ::license::protocol::PACKET_SERVER_PROPERTY_ADJUSTMENT:
this->handle_message_property_adjustment(buffer, size);
return;
case ::license::protocol::PACKET_SERVER_LICENSE_UPGRADE_RESPONSE:
this->handle_message_license_update(buffer, size);
return;
default:
this->handle_check_fail(strobf("received unknown packet").string());
return;
}
}
void LicenseService::handle_client_disconnected(const std::string& message) {
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::finishing) {
this->handle_check_fail(strobf("unexpected disconnect: ").string() + message);
return;
}
this->abort_request(rlock, "");
}
void LicenseService::send_license_validate_request() {
this->license_request_data = serverInstance->generateLicenseData();
ts::proto::license::ServerValidation request{};
if(this->license_request_data->license) {
request.set_licensed(true);
request.set_license_info(true);
request.set_license(exportLocalLicense(this->license_request_data->license));
} else {
request.set_licensed(false);
request.set_license_info(false);
}
request.mutable_info()->set_uname(this->license_request_data->info.uname);
request.mutable_info()->set_version(this->license_request_data->info.version);
request.mutable_info()->set_timestamp(this->license_request_data->info.timestamp.count());
request.mutable_info()->set_unique_id(this->license_request_data->info.unique_id);
this->client_send_message(::license::protocol::PACKET_CLIENT_SERVER_VALIDATION, request);
}
void LicenseService::handle_message_license_info(const void *buffer, size_t buffer_length) {
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::license_validate) {
this->handle_check_fail(strobf("finvalid request state for license response packet").string());
return;
}
ts::proto::license::LicenseResponse response{};
if(!response.ParseFromArray(buffer, buffer_length)) {
this->handle_check_fail(strobf("failed to parse license response packet").string());
return;
}
if(response.has_blacklist()) {
auto blacklist_state = response.blacklist().state();
if(blacklist_state == ::ts::proto::license::BLACKLISTED) {
this->abort_request(rlock, strobf("blacklist action").string());
logCritical(LOG_INSTANCE, strobf("This TeaSpeak-Server instance has been blacklisted by TeaSpeak.").string());
logCritical(LOG_INSTANCE, strobf("Stopping server!").string());
ts::server::shutdownInstance();
return;
}
}
if(!response.valid()) {
std::string reason{};
if(response.has_invalid_reason())
reason = response.invalid_reason();
else
reason = strobf("no reason given").string();
license_invalid_reason = reason;
} else {
license_invalid_reason.reset();
}
if(response.has_update_pending() && response.update_pending()) {
if(this->send_license_update_request()) {
this->request_state_ = request_state::license_upgrade;
return;
}
}
if(this->license_invalid_reason.has_value()) {
this->handle_check_fail(strobf("Failed to verify license (").string() + *this->license_invalid_reason + ")");
return;
}
this->send_property_update_request();
this->request_state_ = request_state::property_update;
}
void LicenseService::send_property_update_request() {
auto data = this->license_request_data;
if(!data) {
this->handle_check_fail(strobf("missing property data").string());
return;
}
ts::proto::license::PropertyUpdateRequest infos{};
infos.set_speach_total(this->license_request_data->metrics.speech_total);
infos.set_speach_dead(this->license_request_data->metrics.speech_dead);
infos.set_speach_online(this->license_request_data->metrics.speech_online);
infos.set_speach_varianz(this->license_request_data->metrics.speech_varianz);
infos.set_clients_online(this->license_request_data->metrics.client_online);
infos.set_bots_online(this->license_request_data->metrics.bots_online);
infos.set_queries_online(this->license_request_data->metrics.queries_online);
infos.set_servers_online(this->license_request_data->metrics.servers_online);
infos.set_web_clients_online(this->license_request_data->metrics.web_clients_online);
infos.set_web_cert_revision(this->license_request_data->web_certificate_revision);
this->client_send_message(::license::protocol::PACKET_CLIENT_PROPERTY_ADJUSTMENT, infos);
}
void LicenseService::handle_message_property_adjustment(const void *buffer, size_t buffer_length) {
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::property_update) {
this->handle_check_fail(strobf("invalid request state for property update packet").string());
return;
}
ts::proto::license::PropertyUpdateResponse response{};
if(!response.ParseFromArray(buffer, buffer_length)) {
this->handle_check_fail(strobf("failed to parse property update packet").string());
return;
}
if(response.has_web_certificate()) {
auto& certificate = response.web_certificate();
serverInstance->setWebCertRoot(certificate.key(), certificate.certificate(), certificate.revision());
}
if(response.has_reset_speach())
serverInstance->resetSpeechTime();
serverInstance->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ] = response.speach_varianz_corrector();
this->request_state_ = request_state::finishing;
this->handle_check_succeeded();
}
bool LicenseService::send_license_update_request() {
ts::proto::license::RequestLicenseUpgrade request{};
this->client_send_message(::license::protocol::PACKET_CLIENT_LICENSE_UPGRADE, request);
return true;
}
inline std::string format_time(const std::chrono::system_clock::time_point& time);
void LicenseService::handle_message_license_update(const void *buffer, size_t buffer_length) {
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::license_upgrade) {
this->handle_check_fail(strobf("invalid request state for license upgrade packet").string());
return;
}
ts::proto::license::LicenseUpgradeResponse response{};
if(!response.ParseFromArray(buffer, buffer_length)) {
this->handle_check_fail(strobf("failed to parse license upgrade packet").string());
return;
}
if(!response.valid()) {
logError(LOG_INSTANCE, strobf("Failed to upgrade license: {}").string(), response.error_message());
goto error_exit;
} else {
std::string error{};
auto license_data = response.license_key();
auto license = ::license::readLocalLicence(license_data, error);
if(!license) {
logError(LOG_INSTANCE, strobf("Failed to parse received upgraded license key: {}").string(), error);
goto error_exit;
}
if(!license->isValid()) {
logError(LOG_INSTANCE, strobf("Received license seems to be invalid.").string());
goto error_exit;
}
auto end = std::chrono::system_clock::time_point{} + std::chrono::milliseconds{license->data.endTimestamp};
logMessage(LOG_INSTANCE, strobf("Received new license registered to {}, valid until {}").string(), license->data.licenceOwner, format_time(end));
if(!config::update_license(error, license_data))
logError(LOG_INSTANCE, strobf("Failed to write new license key to config file: {}").string(), error);
config::license = license;
this->send_license_validate_request();
this->request_state_ = request_state::license_validate;
}
return;
error_exit:
logError(LOG_INSTANCE, strobf("License upgrade failed. Using old key.").string());
if(this->license_invalid_reason.has_value()) {
this->handle_check_fail(strobf("Failed to verify license (").string() + *this->license_invalid_reason + ")");
return;
}
this->send_property_update_request();
this->request_state_ = request_state::property_update;
}
/* request scheduler */
inline std::string format_time(const std::chrono::system_clock::time_point& time) {
std::time_t now = std::chrono::system_clock::to_time_t(time);
std::tm * ptm = std::localtime(&now);
char buffer[128];
const auto length = std::strftime(buffer, 128, "%a, %d.%m.%Y %H:%M:%S", ptm);
return std::string{buffer, length};
}
void LicenseService::schedule_next_request(bool request_success) {
auto& fail_count = this->timings.failed_count;
if(request_success)
fail_count = 0;
else
fail_count++;
std::chrono::milliseconds next_request;
if(fail_count == 0)
next_request = std::chrono::hours{2};
if(fail_count <= 1)
next_request = std::chrono::minutes(1);
else if(fail_count <= 5)
next_request = std::chrono::minutes(5);
else if(fail_count <= 10)
next_request = std::chrono::minutes(10);
else
next_request = std::chrono::minutes(30);
#ifdef DO_LOCAL_REQUEST
next_request = std::chrono::seconds(30);
#endif
this->timings.next_request = this->timings.last_request + next_request;
if(this->verbose_)
logMessage(LOG_INSTANCE, strobf("Scheduling next check at {}").c_str(), format_time(this->timings.next_request));
}
void LicenseService::execute_tick() {
std::unique_lock rlock{this->request_lock, std::try_to_lock}; /* It will be slightly blocking when its within the message hendeling */
if(!rlock) return;
/* do it not above because if we might have a deadlock here we don't want to punish the user */
if(this->timings.last_succeeded.time_since_epoch().count() == 0) {
auto difference = config::license->isPremium() ? std::chrono::hours{24 * 4} : std::chrono::hours{24 * 7};
if(std::chrono::steady_clock::now() - difference > this->startup_timepoint_) {
this->startup_timepoint_ = std::chrono::steady_clock::now(); /* shut down only once */
if(config::license->isPremium())
logCritical(LOG_INSTANCE, strobf("Failed to validate license within 4 days.").string());
else
logCritical(LOG_INSTANCE, strobf("Failed to validate instance integrity within 7 days.").string());
logCritical(LOG_INSTANCE, strobf("Stopping server!").string());
ts::server::shutdownInstance();
return;
}
}
auto now = std::chrono::system_clock::now();
if(this->request_state_ != request_state::empty) {
if(this->timings.last_request + std::chrono::minutes{5} < now) {
this->handle_check_fail(strobf("Scheduling next check at {}").string());
} else {
return;
}
}
if(std::chrono::system_clock::now() > this->timings.next_request)
this->begin_request();
}

View File

@ -0,0 +1,134 @@
#pragma once
#include <variant>
#include <thread>
#include <mutex>
#include <memory>
namespace license::client {
class LicenseServerClient;
}
namespace google::protobuf {
class Message;
}
namespace ts::server::license {
struct InstanceLicenseInfo {
std::shared_ptr<::license::License> license{nullptr};
std::string web_certificate_revision{};
struct metrics_ {
size_t servers_online{0};
size_t client_online{0};
size_t web_clients_online{0};
size_t bots_online{0};
size_t queries_online{0};
size_t speech_total{0};
size_t speech_varianz{0};
size_t speech_online{0};
size_t speech_dead{0};
} metrics;
struct info_ {
std::chrono::milliseconds timestamp{};
std::string version{};
std::string uname{};
std::string unique_id{};
} info;
};
class LicenseService {
public:
LicenseService();
~LicenseService();
[[nodiscard]] bool initialize(std::string& /* error */);
void shutdown();
/* whatever it failed/succeeded */
bool execute_request_sync(const std::chrono::milliseconds& /* timeout */);
[[nodiscard]] inline bool verbose() const { return this->verbose_; }
void execute_tick(); /* should not be essential to the core functionality! */
private:
std::chrono::steady_clock::time_point startup_timepoint_;
enum struct request_state {
empty,
/* initializing */
dns_lookup,
connecting,
/* connected states */
license_validate,
license_upgrade,
property_update,
/* disconnecting */
finishing
};
bool verbose_{false};
std::recursive_timed_mutex request_lock{};
request_state request_state_{request_state::empty};
std::unique_ptr<::license::client::LicenseServerClient> current_client{nullptr};
std::shared_ptr<InstanceLicenseInfo> license_request_data{nullptr};
std::condition_variable sync_request_cv;
std::mutex sync_request_lock;
struct _timings {
std::chrono::system_clock::time_point last_request{};
std::chrono::system_clock::time_point next_request{};
std::chrono::system_clock::time_point last_succeeded{};
size_t failed_count{0};
} timings;
struct _dns {
std::shared_ptr<std::recursive_mutex> lock{nullptr};
struct _lookup {
std::shared_ptr<std::recursive_mutex> lock{nullptr};
std::thread thread{};
LicenseService* handle{nullptr}; /* may be null, locked via lock */
}* current_lookup{nullptr};
} dns;
std::optional<std::string> license_invalid_reason{}; /* set if the last license is invalid */
void schedule_next_request(bool /* last request succeeded */);
void begin_request();
void client_send_message(::license::protocol::PacketType /* type */, ::google::protobuf::Message& /* message */);
void handle_check_fail(const std::string& /* error */); /* might be called form the DNS loop */
void handle_check_succeeded();
/* if not disconnect message has been set it will just close the connection */
void abort_request(std::lock_guard<std::recursive_timed_mutex>& /* request lock */, const std::string& /* disconnect message */);
void abort_dns_request();
void execute_dns_request();
/* will be called while dns lock has been locked! */
void handle_dns_lookup_result(bool /* success */, const std::variant<std::string, sockaddr_in>& /* data */);
/* all callbacks bellow are called from the current_client. It will not be null while being within the callback. */
void handle_client_connected();
void handle_client_disconnected(const std::string& /* error */);
void handle_message(::license::protocol::PacketType /* type */, const void* /* buffer */, size_t /* length */);
void handle_message_license_info(const void* /* buffer */, size_t /* length */);
void handle_message_license_update(const void* /* buffer */, size_t /* length */);
void handle_message_property_adjustment(const void* /* buffer */, size_t /* length */);
void send_license_validate_request();
bool send_license_update_request();
void send_property_update_request();
};
}

View File

@ -1,17 +1,20 @@
#include "./CommandHandler.h"
#include <csignal> #include <csignal>
#include <src/SignalHandler.h>
#include <src/VirtualServer.h>
#include <src/client/ConnectedClient.h>
#include <src/VirtualServerManager.h>
#include <src/InstanceHandler.h>
#include <log/LogUtils.h> #include <log/LogUtils.h>
#include <src/ShutdownHelper.h>
#include <misc/time.h> #include <misc/time.h>
#include <misc/memtracker.h> #include <misc/memtracker.h>
#include <sql/sqlite/SqliteSQL.h> #include <sql/sqlite/SqliteSQL.h>
#include <sys/resource.h> #include <sys/resource.h>
#include "CommandHandler.h" #include <protocol/buffers.h>
#include "src/server/QueryServer.h"
#include "../SignalHandler.h"
#include "../client/ConnectedClient.h"
#include "../InstanceHandler.h"
#include "../VirtualServerManager.h"
#include "../VirtualServer.h"
#include "../ShutdownHelper.h"
#include "../server/QueryServer.h"
#ifdef HAVE_JEMALLOC #ifdef HAVE_JEMALLOC
#include <jemalloc/jemalloc.h> #include <jemalloc/jemalloc.h>

2
shared

@ -1 +1 @@
Subproject commit 51d9a002e3311a2079b25253b7946a4b03a9baf4 Subproject commit fd8aba2ede8675f4feb9fcfc01ada3822f2d4780