305 lines
11 KiB
C++
305 lines
11 KiB
C++
#include "stdafx.h"
|
|
#include "resource.h"
|
|
#include "2015Remote.h"
|
|
#include "LicenseFile.h"
|
|
#include "pwd_gen.h"
|
|
#include "2015RemoteDlg.h"
|
|
#include "CPasswordDlg.h"
|
|
#include "LangManager.h"
|
|
#include "jsoncpp/json.h"
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <ctime>
|
|
#include <cctype>
|
|
#include <cstdio>
|
|
#include <memory>
|
|
|
|
#ifndef _WIN64
|
|
#ifdef _DEBUG
|
|
#pragma comment(lib, "jsoncpp/jsoncppd.lib")
|
|
#else
|
|
#pragma comment(lib, "jsoncpp/jsoncpp.lib")
|
|
#endif
|
|
#else
|
|
#ifdef _DEBUG
|
|
#pragma comment(lib, "jsoncpp/jsoncpp_x64d.lib")
|
|
#else
|
|
#pragma comment(lib, "jsoncpp/jsoncpp_x64.lib")
|
|
#endif
|
|
#endif
|
|
|
|
// Get current time string
|
|
static std::string GetCurrentTimeString() {
|
|
time_t now = time(nullptr);
|
|
struct tm t;
|
|
localtime_s(&t, &now);
|
|
char buf[32];
|
|
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &t);
|
|
return buf;
|
|
}
|
|
|
|
// Build checksum content (includes magic, version, createTime, and license fields)
|
|
static std::string BuildChecksumContent(const std::string& magic,
|
|
int version,
|
|
const std::string& createTime,
|
|
const std::string& sn,
|
|
const std::string& password,
|
|
const std::string& pwdHmac,
|
|
const std::string& authorization,
|
|
const std::string& frpConfig = "") {
|
|
std::string content;
|
|
content += magic + "|";
|
|
content += std::to_string(version) + "|";
|
|
content += createTime + "|";
|
|
content += sn + "|";
|
|
content += password + "|";
|
|
content += pwdHmac + "|";
|
|
content += authorization;
|
|
if (!frpConfig.empty()) {
|
|
content += "|" + frpConfig;
|
|
}
|
|
return content;
|
|
}
|
|
|
|
bool IsIPv4Format(const std::string& sn) {
|
|
// IPv4 format: 4 segments (0-255) separated by dots, no dashes
|
|
if (sn.find('-') != std::string::npos) return false;
|
|
if (sn.empty()) return false;
|
|
|
|
int segmentCount = 0;
|
|
int currentValue = 0;
|
|
int digitCount = 0;
|
|
|
|
for (size_t i = 0; i <= sn.size(); ++i) {
|
|
if (i == sn.size() || sn[i] == '.') {
|
|
if (digitCount == 0) return false; // Empty segment
|
|
if (currentValue > 255) return false; // Value out of range
|
|
segmentCount++;
|
|
currentValue = 0;
|
|
digitCount = 0;
|
|
} else if (isdigit(sn[i])) {
|
|
digitCount++;
|
|
if (digitCount > 3) return false; // Too many digits (prevents overflow)
|
|
currentValue = currentValue * 10 + (sn[i] - '0');
|
|
} else {
|
|
return false; // Invalid character
|
|
}
|
|
}
|
|
|
|
return segmentCount == 4; // Must have exactly 4 segments
|
|
}
|
|
|
|
SNMatchResult ValidateLicenseSN(const std::string& licenseSN) {
|
|
if (IsIPv4Format(licenseSN)) {
|
|
// IP binding: check if matches current machine's public IP
|
|
// For now, we check against configured master IP
|
|
// In real implementation, should get actual public IP
|
|
std::string currentIP = CMy2015RemoteDlg::GetHardwareID(1); // Use IP mode
|
|
if (licenseSN == currentIP) {
|
|
return SNMatchResult::Match;
|
|
}
|
|
return SNMatchResult::IPMismatch;
|
|
} else {
|
|
// Hardware binding: check if matches current device ID
|
|
// Use GetHardwareID() to respect HWIDVersion (V1 or V2)
|
|
std::string hardwareID = CMy2015RemoteDlg::GetHardwareID(0);
|
|
std::string hashedID = hashSHA256(hardwareID);
|
|
std::string currentDeviceID = getFixedLengthID(hashedID);
|
|
if (licenseSN == currentDeviceID) {
|
|
return SNMatchResult::Match;
|
|
}
|
|
return SNMatchResult::HardwareMismatch;
|
|
}
|
|
}
|
|
|
|
bool ExportLicenseFile(const std::string& filePath,
|
|
const std::string& sn,
|
|
const std::string& password,
|
|
const std::string& pwdHmac,
|
|
const std::string& authorization,
|
|
const std::string& frpConfig) {
|
|
// 1. Generate create time
|
|
std::string createTime = GetCurrentTimeString();
|
|
|
|
// 2. Calculate checksum
|
|
std::string checksumContent = BuildChecksumContent(
|
|
LICENSE_MAGIC, LICENSE_FILE_VERSION, createTime,
|
|
sn, password, pwdHmac, authorization, frpConfig);
|
|
std::string checksum = "sha256:" + hashSHA256(checksumContent);
|
|
|
|
// 3. Build JSON using jsoncpp
|
|
Json::Value root;
|
|
root["magic"] = LICENSE_MAGIC;
|
|
root["version"] = LICENSE_FILE_VERSION;
|
|
root["createTime"] = createTime;
|
|
|
|
Json::Value license;
|
|
license["sn"] = sn;
|
|
license["password"] = password;
|
|
license["pwdHmac"] = pwdHmac;
|
|
license["authorization"] = authorization;
|
|
if (!frpConfig.empty()) {
|
|
license["frpConfig"] = frpConfig;
|
|
}
|
|
root["license"] = license;
|
|
|
|
root["checksum"] = checksum;
|
|
|
|
// 4. Write to temp file first (atomic write)
|
|
std::string tempPath = filePath + ".tmp";
|
|
{
|
|
std::ofstream file(tempPath);
|
|
if (!file.is_open()) return false;
|
|
Json::StreamWriterBuilder builder;
|
|
builder["indentation"] = " ";
|
|
std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
|
|
writer->write(root, &file);
|
|
if (!file.good()) {
|
|
remove(tempPath.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 5. Remove existing file and rename temp to target
|
|
remove(filePath.c_str());
|
|
if (rename(tempPath.c_str(), filePath.c_str()) != 0) {
|
|
remove(tempPath.c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
LicenseImportResult ImportLicenseFile(const std::string& filePath,
|
|
LicenseFileData& outData,
|
|
std::string& outError) {
|
|
// 1. Check file exists and read content
|
|
std::ifstream file(filePath);
|
|
if (!file.is_open()) {
|
|
outError = GetImportErrorMessage(LicenseImportResult::FileNotFound);
|
|
return LicenseImportResult::FileNotFound;
|
|
}
|
|
|
|
// 2. Parse JSON using jsoncpp
|
|
Json::Value root;
|
|
Json::Reader reader;
|
|
if (!reader.parse(file, root)) {
|
|
file.close();
|
|
outError = GetImportErrorMessage(LicenseImportResult::InvalidFormat);
|
|
return LicenseImportResult::InvalidFormat;
|
|
}
|
|
file.close();
|
|
|
|
// 3. Extract and validate magic
|
|
std::string magic = root.get("magic", "").asString();
|
|
if (magic != LICENSE_MAGIC) {
|
|
outError = GetImportErrorMessage(LicenseImportResult::InvalidMagic);
|
|
return LicenseImportResult::InvalidMagic;
|
|
}
|
|
|
|
// 4. Extract and validate version
|
|
int version = root.get("version", 0).asInt();
|
|
if (version <= 0) {
|
|
outError = GetImportErrorMessage(LicenseImportResult::InvalidFormat);
|
|
return LicenseImportResult::InvalidFormat;
|
|
}
|
|
if (version > LICENSE_FILE_VERSION) {
|
|
outError = GetImportErrorMessage(LicenseImportResult::VersionTooHigh);
|
|
return LicenseImportResult::VersionTooHigh;
|
|
}
|
|
|
|
// 5. Extract createTime
|
|
std::string createTime = root.get("createTime", "").asString();
|
|
|
|
// 6. Extract license object
|
|
if (!root.isMember("license") || !root["license"].isObject()) {
|
|
outError = GetImportErrorMessage(LicenseImportResult::InvalidFormat);
|
|
return LicenseImportResult::InvalidFormat;
|
|
}
|
|
|
|
const Json::Value& license = root["license"];
|
|
std::string sn = license.get("sn", "").asString();
|
|
std::string password = license.get("password", "").asString();
|
|
std::string pwdHmac = license.get("pwdHmac", "").asString();
|
|
std::string authorization = license.get("authorization", "").asString();
|
|
std::string frpConfig = license.get("frpConfig", "").asString();
|
|
|
|
// 7. Check required fields
|
|
if (sn.empty() || password.empty() || pwdHmac.empty()) {
|
|
outError = GetImportErrorMessage(LicenseImportResult::IncompleteData);
|
|
return LicenseImportResult::IncompleteData;
|
|
}
|
|
|
|
// 8. Verify checksum
|
|
std::string storedChecksum = root.get("checksum", "").asString();
|
|
std::string expectedContent = BuildChecksumContent(
|
|
magic, version, createTime, sn, password, pwdHmac, authorization, frpConfig);
|
|
std::string expectedChecksum = "sha256:" + hashSHA256(expectedContent);
|
|
|
|
if (storedChecksum != expectedChecksum) {
|
|
outError = GetImportErrorMessage(LicenseImportResult::ChecksumMismatch);
|
|
return LicenseImportResult::ChecksumMismatch;
|
|
}
|
|
|
|
// 9. Validate SN
|
|
SNMatchResult snResult = ValidateLicenseSN(sn);
|
|
if (snResult == SNMatchResult::HardwareMismatch) {
|
|
outError = GetImportErrorMessage(LicenseImportResult::SNMismatchHardware);
|
|
return LicenseImportResult::SNMismatchHardware;
|
|
} else if (snResult == SNMatchResult::IPMismatch) {
|
|
outError = GetImportErrorMessage(LicenseImportResult::SNMismatchIP);
|
|
return LicenseImportResult::SNMismatchIP;
|
|
}
|
|
|
|
// 10. Fill output data
|
|
outData.sn = sn;
|
|
outData.password = password;
|
|
outData.pwdHmac = pwdHmac;
|
|
outData.authorization = authorization;
|
|
outData.frpConfig = frpConfig;
|
|
outData.createTime = createTime;
|
|
outData.version = version;
|
|
|
|
return LicenseImportResult::Success;
|
|
}
|
|
|
|
bool ApplyLicenseData(const LicenseFileData& data) {
|
|
// Save to settings
|
|
THIS_CFG.SetStr("settings", "SN", data.sn);
|
|
THIS_CFG.SetStr("settings", "Password", data.password);
|
|
THIS_CFG.SetStr("settings", "PwdHmac", data.pwdHmac);
|
|
|
|
// Always set Authorization (clear old value if empty)
|
|
THIS_CFG.SetStr("settings", "Authorization", data.authorization);
|
|
|
|
// Save FRP config (clear old value if empty)
|
|
THIS_CFG.SetStr("settings", "FrpConfig", data.frpConfig);
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string GetImportErrorMessage(LicenseImportResult result) {
|
|
switch (result) {
|
|
case LicenseImportResult::Success:
|
|
return "";
|
|
case LicenseImportResult::FileNotFound:
|
|
return std::string(CT2A(_L("文件不存在")));
|
|
case LicenseImportResult::InvalidFormat:
|
|
return std::string(CT2A(_L("文件格式错误")));
|
|
case LicenseImportResult::InvalidMagic:
|
|
return std::string(CT2A(_L("不是有效的YAMA授权文件")));
|
|
case LicenseImportResult::VersionTooHigh:
|
|
return std::string(CT2A(_L("授权文件版本过高,请升级程序")));
|
|
case LicenseImportResult::ChecksumMismatch:
|
|
return std::string(CT2A(_L("授权文件已损坏或被篡改")));
|
|
case LicenseImportResult::SNMismatchHardware:
|
|
return std::string(CT2A(_L("此授权不适用于当前设备")));
|
|
case LicenseImportResult::SNMismatchIP:
|
|
return std::string(CT2A(_L("此授权不适用于当前公网IP")));
|
|
case LicenseImportResult::IncompleteData:
|
|
return std::string(CT2A(_L("授权信息不完整")));
|
|
default:
|
|
return std::string(CT2A(_L("未知错误")));
|
|
}
|
|
}
|