Teaspeak-Server/server/src/lincense/LicenseHelper.cpp

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);
}