#include #include #include #include #include #include #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(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_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); }