207 lines
8.2 KiB
C++
207 lines
8.2 KiB
C++
#include <log/LogUtils.h>
|
|
#include <misc/strobf.h>
|
|
#include <misc/hex.h>
|
|
#include <src/Configuration.h>
|
|
#include <arpa/inet.h>
|
|
#include <src/SignalHandler.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;
|
|
|
|
#define DO_LOCAL_REQUEST
|
|
|
|
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(strobf("Could not validate license.").c_str());
|
|
else logCritical(strobf("Your server has been shutdown remotely!").c_str());
|
|
} else if(!response->properties_valid) {
|
|
logCritical(strobf("Property adjustment failed!").c_str());
|
|
} else
|
|
logCritical(strobf("Your license expired!").c_str());
|
|
logCritical(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(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(strobf("Could not valid license! (1)").c_str());
|
|
return;
|
|
}
|
|
if(!license_host->h_addr){
|
|
if(verbose) logError(strobf("Could not valid license! (2)").c_str());
|
|
return;
|
|
}
|
|
server_addr.sin_addr.s_addr = ((in_addr*) license_host->h_addr)->s_addr;
|
|
logger::logger(0)->debug(strobf("Requesting server {}").c_str(), inet_ntoa(server_addr.sin_addr));
|
|
int first = server_addr.sin_addr.s_addr >> 24;
|
|
if(first == 0 || first == 127 || first == 255) {
|
|
if(config::license->isPremium()) {
|
|
logError(strobf("You tried to nullroot 'license.teaspeak.de'!").c_str());
|
|
logCritical(strobf("Could not validate license!").c_str());
|
|
logCritical(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(verbose) {
|
|
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);
|
|
} |