aboutsummaryrefslogtreecommitdiffstats
path: root/licenser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'licenser.cpp')
-rw-r--r--licenser.cpp428
1 files changed, 0 insertions, 428 deletions
diff --git a/licenser.cpp b/licenser.cpp
deleted file mode 100644
index ca52e4b..0000000
--- a/licenser.cpp
+++ /dev/null
@@ -1,428 +0,0 @@
-/* Copyright (C) 2022 The Qt Company Ltd.
- *
- * SPDX-License-Identifier: GPL-3.0-only WITH Qt-GPL-exception-1.0
-*/
-
-#include "licenser.h"
-
-Licenser::Licenser(uint16_t tcpPort)
-{
- // Init daemon settings
- settings = new LicdSetup(e_set_type_daemon);
-
- if (tcpPort == 0) {
- tcpPort = utils::strToInt(settings->get("tcp_listening_port"));
- }
- m_mocInterval = utils::strToInt(settings->get("moc_renewal_interval")) * 3600; // 3600 = secs in hour
- // Start the HTTP client
- m_http = new HttpClient(settings->get("server_addr"),
- settings->get("reservation_access_point"),
- settings->get("long-term_access_point"),
- settings->get("version_query_access_point"));
- // Start the TCP/IP m_server
- m_server = new TcpServer(tcpPort);
-
-
-}
-
-Licenser::~Licenser()
-{
- delete(settings);
- delete(m_server);
- delete(m_http);
- std::cout << "Daemon stopped." << std::endl;
-}
-
-int Licenser::listen()
-{
-
- m_infoString = "";
- int socket = 0; // Placeholder for whatever socket gets active
- std::string input = m_server->listenToClients(socket);
- input = utils::trimStr(input);
- if (input.empty()) {
- return 0; //continue;
- }
- std::cout << "Got an request: " << input << std::endl;
- RequestInfo request;
- std::string reply = "";
- if (parseIncoming(input, request) == 0) {
- std::string payload = "";
- if (request.type == RequestType::normal_license_renewal) {
- std::cout << "Standard license renewal request\n";
- // See if it's MOC who's calling. Make sure app name is accepted, no matter of the case.
- // request.appname is set in lowercase already in parseIncoming() method at this point
- if (request.appName == utils::strToLower(MOCWRAPPER_APP_NAME)) {
- // More than 24hrs (licd.ini default) from last successful request?
- if (!isMocRenewalDue(request.licenseFile)) {
- // No - Just send ok message and no further actions needed
- reply = replyString[e_license_granted];
- m_server->sendResponse(socket, reply);
- return 0; //continue;
- }
- }
- if (buildRequestJson(request) == 0) {
- if (sendLicensingRequest(reply, request) != 0) {
- // Bad server connection: Check the existing license file for expiry date
- checkLicenseExpiryDate(reply, request);
- } else {
- parseAndSaveJsonReply(reply, request);
- }
- } else {
- reply = replyString[e_bad_request];
- }
- } else if (request.type == RequestType::long_term_request) {
- std::cout << "Long-term license request\n";
- if (buildRequestJson(request) == 0) {
- if (sendLongTermRequest(reply, request) != 0) {
- reply = replyString[e_bad_connection];
- reply += m_http->error();
- } else {
- parseAndSaveJsonReply(reply, request);
- }
- }
- } else if (request.type == RequestType::reservation_query) {
- reply = checkReservations();
- } else if (request.type == RequestType::server_version) {
- reply = askServerVersion();
- }
- else if (request.type == RequestType::daemon_version) {
- reply = getVersion();
- }
- } else {
- reply = replyString[e_bad_request] + '\n';
- }
- reply += m_infoString;
- reply +="\n";
- m_server->sendResponse(socket, reply);
- std::cout << "Replied to socket " << socket << ": " << reply << std::endl;
- return 0;
-}
-
-std::string Licenser::getVersion()
-{
- std::string version = "Qt License Daemon (licd) v";
- version += DAEMON_VERSION;
- return version;
-}
-
-int Licenser::parseIncoming(const std::string &incoming, RequestInfo &request)
-{
- std::vector<std::string> req = utils::splitStr(incoming, '-');
-
- // First find out the command (and operation for longterm case)
- std::string cmd = utils::trimStr(req[0]);
- if (cmd == LICENSE_REQUEST_CMD) {
- request.type = RequestType::normal_license_renewal;
- } else if (cmd.substr(0, 8) == LONGTERM_REQUEST_CMD) {
- request.type = RequestType::long_term_request;
- std::string op = utils::trimStr(cmd.substr(8, cmd.length() -1));
- if (op != LONGTERM_ADD_OP && op != LONGTERM_REMOVE_OP) {
- m_infoString = "Invalid longterm operation: " + op;
- std::cout << m_infoString;
- return e_bad_request;
- }
- request.operation = op;
- } else if (cmd == SERVER_VERSION_CMD) {
- request.type = RequestType::server_version;
- } else if (cmd == DAEMON_VERSION_CMD) {
- request.type = RequestType::daemon_version;
- return 0;
- } else if (cmd == RESERVATION_QUERY_CMD) {
- request.type = RequestType::reservation_query;
- return 0;
- } else {
- std::cout << "Invalid command: " << cmd << std::endl;
- return e_bad_request;
- }
-
- // Then cycle through parameters. Start from [1] because [0] is the command
- for (int i = 1; i < req.size(); i++) {
- std::string item = req[i];
- char arg = (char)item[0];
- std::string val = utils::trimStr(item.substr(1, item.length() - 1));
- if (val.length() == 0) {
- m_infoString = "No value for argument: -";
- m_infoString.push_back(arg);
- std::cout << m_infoString << std::endl;
- return e_bad_request;
- }
-
- if (arg == 'a') request.appName = utils::strToLower(val);
- else if (arg == 'v') request.appVersion = val;
- else if (arg == 'u') request.userId = val;
- else if (arg == 'i') request.licenseId = val;
- else if (arg == 'l') request.serverAddr = val;
- else {
- m_infoString = "Invalid argument: -";
- m_infoString.push_back(arg);
- return e_bad_request;
- }
- }
-
- // Sanity checks. May not be needed after all, if the clients are made correctly.
- if (request.type == RequestType::server_version) {
- return 0; // version query has no mandatory parameters, no checks
- }
-
- bool fail = false;
- if (request.userId.empty()) {
- std::cout << "Missing argument: Username (-u)\n";
- fail = true;
- }
- if (request.licenseId.empty()) {
- std::cout << "Missing argument: License ID (-i)\n";
- fail = true;
- }
- if (request.type == RequestType::normal_license_renewal) {
- if (request.appName.empty()) {
- std::cout << "Missing argument: Application name (-a)\n";
- fail = true;
- }
- if (request.appVersion.empty()) {
- std::cout << "Missing argument: Application version (-v)\n";
- fail = true;
- }
- } else if (request.type == RequestType::long_term_request) {
- if (request.operation.empty()) {
- m_infoString = "Missing argument: Longterm operation (add/remove)\n";
- std::cout << m_infoString << std::endl; // For logging
- fail = true;
- }
- }
- if (fail) {
- return e_bad_request;
- }
-
- // Add user info to the license filename
- std::stringstream ss;
- ss << WORKING_DIR << DIR_SEPARATOR
- << settings->get("license_file_base") << request.userId
- << "_" << request.licenseId << settings->get("license_file_extension");
- request.licenseFile = ss.str();
-
- return 0;
-}
-
-int Licenser::buildRequestJson(RequestInfo &request)
-{
- std::stringstream pay;
- pay << "{";
- pay<< "\"license_number\":" << "\"" << request.licenseId << "\",";
- pay << "\"user_id\":" << "\"" << request.userId << "\",";
- if (request.type == RequestType::normal_license_renewal) {
- pay << "\"hw_id\":" << "\"" << settings->get("hw_id") << "\",";
- pay << "\"src\":" << "\"" << request.appName << "\",";
- pay << "\"host_os\":" << "\"" << settings->get("host_os") << "\",";
- pay << "\"src_version\":" << "\"" << request.appVersion << "\",";
- pay << "\"email\":" << "\"" << request.email << "\"";
- } else if (request.type == RequestType::long_term_request) {
- pay << "\"operation\":" << "\"" << request.operation << "\"";
- }
- pay << "}";
- request.payload = pay.str();
- return 0;
-}
-
-int Licenser::parseAndSaveJsonReply(std::string &reply, const RequestInfo &request)
-{
- JsonHandler json(reply);
- std::stringstream ss;
- if (request.type == RequestType::long_term_request) {
- // long-term request: Have "message" from response JSON directly as a reply to the user
- reply = json.get("message");
- if (json.get("status") != "true" ) {
- return 1;
- }
- // Nothing to add to the server message
- } else {
- if (json.get("status") == "true") {
- ss << replyString[e_license_granted];
- ss << " expiry_date=" << json.get("expiry_date");
- ss << " license_id=" << json.get("license_number");
- ss << " reservation_id=" << json.get("reservation_id");
- reply = ss.str();
- } else {
- if (json.get("message") == "License fully reserved") {
- reply = replyString[e_license_pool_full];
- } else {
- reply = replyString[e_license_rejected];
- }
- // Do not save the response json, just return
- return 1;
- }
- }
-
- // Add timestamp into license JSON
- struct timeval tp;
- gettimeofday(&tp, NULL);
- uint64_t timeNow = tp.tv_sec;
-
- // Save the JSON, adding timestamp
- json.add("last_timestamp", timeNow);
- int result = utils::writeToFile(request.licenseFile, json.dump(4));
- if (result != 0) {
- std::cout << "ERROR saving license file: '" << request.licenseFile << "': " << strerror(result) << std::endl;
- }
- return 0;
-}
-
-bool Licenser::checkLicenseExpiryDate(std::string &reply, const RequestInfo &request)
-{
- std::cout << "Offline - checking validity from license file " << request.licenseFile << std::endl;
- // Open the license file
- std::string data;
- if (utils::readFile(data, request.licenseFile) != 0) {
- std::cout << "No license file - rejecting license " << request.licenseFile << std::endl;
- reply = replyString[e_bad_connection];
- return false;
- }
- JsonHandler license(data);
- // Check license status
- if (license.get("status") != "true") {
- std::cout << "License status = false: Rejecting license\n";
- reply = replyString[e_bad_connection];
- return false;
- }
- // Expiry date. Add 1 day to expiry date to get the end actually at the beginning of the next day
- std::string expDate = license.get("expiry_date");
- std::time_t expEpoch = utils::stringToEpoch(expDate.c_str()) + SECS_IN_DAY;
- // Current date
- std::time_t current = std::time(0);
- // See if the time has expire
- if (current > expEpoch) {
- std::cout << "Expiry date " << expDate << " exceeded" << std::endl;
- // License expired: Allow some leeway time for MOC and Plugin, but not for qtlicensetool
- if (utils::strToLower(request.appName) != utils::strToLower(QTLICENSETOOL_APP_NAME)) {
- int leewayTimeLeft = expEpoch + (license.getInt("leeway_hours") * SECS_IN_HOUR) - current;
- if (leewayTimeLeft > 0) {
- std::stringstream ss;
- ss << replyString[RequestReply::e_no_conn_leeway]
- << std::fixed << std::setprecision(1)
- << (float)leewayTimeLeft / SECS_IN_DAY << " days";
- reply = ss.str();
- return true;
- } else {
- std::cout << "No leeway time left" << std::endl;
- }
- }
- std::cout << "Rejecting license\n";
- reply = replyString[e_license_rejected];
- return false;
- }
- std::cout << "License granted, expires at " << expDate << std::endl;
- reply = replyString[e_license_granted];
- return true;
-}
-
-bool Licenser::isMocRenewalDue(const std::string &licenseFile)
-{
- std::cout << "MOC calling, checking if renewal is needed\n";
- // Open the license file
- std::string data;
- if (utils::readFile(data, licenseFile) != 0) {
- // If there is no license file yet, have to request anyway
- std::cout << "No license file present (" << licenseFile << ") - requesting license\n";
- return true;
- }
- JsonHandler license(data);
- if (license.get("status") != "true") {
- // Failed license file shouldn't exist - just to be sure:
- return true;
- }
- // Get current time
- struct timeval tp;
- gettimeofday(&tp, NULL);
- uint64_t timeNow = tp.tv_sec;
- if (timeNow > utils::strToInt(license.get("last_timestamp")) + m_mocInterval) {
- // Renewal is due
- std::cout << "Requesting license renewal\n";
- return true;
- }
- std::cout << "Renewal time not due, granting license\n";
-
- return false;
-}
-
-int Licenser::sendLicensingRequest(std::string &reply, const RequestInfo &request)
-{
- // Generate auth hash
- std::string authKey;
- doHmacHashSha256(request.payload, settings->get("server_secret"), authKey); // 19755982232ff7b6f6d0f3c57ffc1c0e4f03060e7175d478f7b146fb1e000507";
-
- // Send the request
- if (m_http->sendRequest(reply, request.payload, request.serverAddr, authKey) != 0) {
- return -1;
- }
- return 0;
-}
-
-int Licenser::sendLongTermRequest(std::string &reply, const RequestInfo &request)
-{
- if (m_http->sendRequest(reply, request.payload, request.serverAddr) != 0) {
- return -1;
- }
- return 0;
-}
-
-void Licenser::doHmacHashSha256(const std::string &payload, const std::string &secret, std::string &authKey)
-{
- std::stringstream ss_result;
- ss_result << "apikey ";
-
- // Allocate memory for the HMAC
- std::vector<uint8_t> out(SHA256_HASH_SIZE);
-
- // Call hmac-sha256 function
- hmac_sha256(secret.data(), secret.size(), payload.data(), payload.size(),
- out.data(), out.size());
-
- // Convert `out` to string with std::hex
- for (uint8_t x : out) {
- ss_result << std::hex << std::setfill('0') << std::setw(2) << (int)x;
- }
-
- authKey = ss_result.str();
- JsonHandler pay(payload);
-
- // Print out the result
- std::cout << "Message: " << pay.dump(4) << std::endl;
- std::cout << "Key: " << secret << std::endl;
- std::cout << "HMAC: " << authKey << std::endl;
-}
-
-std::string Licenser::checkReservations()
-{
- // Open all license files
- std::string dir = WORKING_DIR;
- std::string filter = settings->get("license_file_base");
- std::vector<std::string> files = utils::getDirListing(dir, filter);
- std::stringstream reply;
- reply << "Current reservations: \n";
- for (auto file : files) {
- std::string data;
- if (utils::readFile(data, file) != 0) {
- std::cout << "Couldn't read license file: " << file << std::endl;
- continue;
- }
- JsonHandler json(data);
- reply << "\n--- License ID " << json.get("license_number") << " ---\n";
- reply << " User ID: " << json.get("user_id") << std::endl;
- reply << " Expiry date: " << json.get("expiry_date") << std::endl;
- reply << " Reservation ID: " << json.get("reservation_id") << std::endl;
- }
- return reply.str();
-}
-
-std::string Licenser::askServerVersion()
-{
- std::string reply;
- if (m_http->sendRequest(reply, "", "") != 0) {
- reply = replyString[e_bad_connection];
- } else if (reply.find("Error") == std::string::npos) {
- JsonHandler json(reply);
- reply = "Qt License Server v";
- reply += json.get("version");
- }
- return reply;
-}