mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
Feat: Add Telemt (MtProxy)
This commit is contained in:
@@ -37,6 +37,7 @@ set(HEADERS ${HEADERS}
|
||||
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.h
|
||||
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.h
|
||||
${CLIENT_ROOT_DIR}/core/installers/mtProxyInstaller.h
|
||||
${CLIENT_ROOT_DIR}/core/installers/telemtInstaller.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.h
|
||||
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.h
|
||||
@@ -113,6 +114,7 @@ set(SOURCES ${SOURCES}
|
||||
${CLIENT_ROOT_DIR}/core/installers/sftpInstaller.cpp
|
||||
${CLIENT_ROOT_DIR}/core/installers/socks5Installer.cpp
|
||||
${CLIENT_ROOT_DIR}/core/installers/mtProxyInstaller.cpp
|
||||
${CLIENT_ROOT_DIR}/core/installers/telemtInstaller.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/appSplitTunnelingController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/ipSplitTunnelingController.cpp
|
||||
${CLIENT_ROOT_DIR}/core/controllers/allowedDnsController.cpp
|
||||
|
||||
@@ -104,6 +104,9 @@ void CoreController::initModels()
|
||||
m_mtProxyConfigModel = new MtProxyConfigModel(this);
|
||||
setQmlContextProperty("MtProxyConfigModel", m_mtProxyConfigModel);
|
||||
|
||||
m_telemtConfigModel = new TelemtConfigModel(this);
|
||||
setQmlContextProperty("TelemtConfigModel", m_telemtConfigModel);
|
||||
|
||||
m_clientManagementModel = new ClientManagementModel(this);
|
||||
setQmlContextProperty("ClientManagementModel", m_clientManagementModel);
|
||||
|
||||
@@ -173,7 +176,7 @@ void CoreController::initControllers()
|
||||
#ifdef Q_OS_WINDOWS
|
||||
m_ikev2ConfigModel,
|
||||
#endif
|
||||
m_sftpConfigModel, m_socks5ConfigModel, m_mtProxyConfigModel, this);
|
||||
m_sftpConfigModel, m_socks5ConfigModel, m_mtProxyConfigModel, m_telemtConfigModel, this);
|
||||
setQmlContextProperty("InstallController", m_installUiController);
|
||||
|
||||
m_importController = new ImportUiController(m_importCoreController, this);
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
#include "ui/models/services/sftpConfigModel.h"
|
||||
#include "ui/models/services/socks5ProxyConfigModel.h"
|
||||
#include "ui/models/services/mtProxyConfigModel.h"
|
||||
#include "ui/models/services/telemtConfigModel.h"
|
||||
|
||||
#include "ui/models/ipSplitTunnelingModel.h"
|
||||
#include "ui/models/newsModel.h"
|
||||
@@ -215,6 +216,7 @@ private:
|
||||
SftpConfigModel* m_sftpConfigModel;
|
||||
Socks5ProxyConfigModel* m_socks5ConfigModel;
|
||||
MtProxyConfigModel* m_mtProxyConfigModel;
|
||||
TelemtConfigModel* m_telemtConfigModel;
|
||||
|
||||
CoreSignalHandlers* m_signalHandlers;
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "core/installers/sftpInstaller.h"
|
||||
#include "core/installers/socks5Installer.h"
|
||||
#include "core/installers/mtProxyInstaller.h"
|
||||
#include "core/installers/telemtInstaller.h"
|
||||
#include "core/installers/torInstaller.h"
|
||||
#include "core/installers/wireguardInstaller.h"
|
||||
#include "core/installers/xrayInstaller.h"
|
||||
@@ -154,6 +155,10 @@ ErrorCode InstallController::updateContainer(int serverIndex, DockerContainer co
|
||||
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||
SshSession sshSession(this);
|
||||
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
|
||||
} else if (container == DockerContainer::Telemt) {
|
||||
ServerCredentials credentials = m_serversRepository->serverCredentials(serverIndex);
|
||||
SshSession sshSession(this);
|
||||
TelemtInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
|
||||
}
|
||||
m_serversRepository->setContainerConfig(serverIndex, container, newConfig);
|
||||
return ErrorCode::NoError;
|
||||
@@ -178,6 +183,8 @@ ErrorCode InstallController::updateContainer(int serverIndex, DockerContainer co
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
if (container == DockerContainer::MtProxy) {
|
||||
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
|
||||
} else if (container == DockerContainer::Telemt) {
|
||||
TelemtInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, newConfig);
|
||||
}
|
||||
clearCachedProfile(serverIndex, container);
|
||||
m_serversRepository->setContainerConfig(serverIndex, container, newConfig);
|
||||
@@ -410,6 +417,8 @@ ErrorCode InstallController::configureContainerWorker(const ServerCredentials &c
|
||||
|
||||
if (container == DockerContainer::MtProxy) {
|
||||
MtProxyInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, config);
|
||||
} else if (container == DockerContainer::Telemt) {
|
||||
TelemtInstaller::uploadClientSettingsSnapshot(sshSession, credentials, container, config);
|
||||
}
|
||||
|
||||
return ErrorCode::NoError;
|
||||
@@ -591,6 +600,53 @@ bool InstallController::isReinstallContainerRequired(DockerContainer container,
|
||||
}
|
||||
}
|
||||
|
||||
if (container == DockerContainer::Telemt) {
|
||||
const auto *oldT = oldConfig.getTelemtProtocolConfig();
|
||||
const auto *newT = newConfig.getTelemtProtocolConfig();
|
||||
if (oldT && newT) {
|
||||
const QString oldPort =
|
||||
oldT->port.isEmpty() ? QString(protocols::telemt::defaultPort) : oldT->port;
|
||||
const QString newPort =
|
||||
newT->port.isEmpty() ? QString(protocols::telemt::defaultPort) : newT->port;
|
||||
if (oldPort != newPort) {
|
||||
return true;
|
||||
}
|
||||
const QString oldTransport = oldT->transportMode.isEmpty()
|
||||
? QString(protocols::telemt::transportModeStandard)
|
||||
: oldT->transportMode;
|
||||
const QString newTransport = newT->transportMode.isEmpty()
|
||||
? QString(protocols::telemt::transportModeStandard)
|
||||
: newT->transportMode;
|
||||
if (oldTransport != newTransport) {
|
||||
return true;
|
||||
}
|
||||
if (oldT->tlsDomain != newT->tlsDomain) {
|
||||
return true;
|
||||
}
|
||||
if (oldT->maskEnabled != newT->maskEnabled) {
|
||||
return true;
|
||||
}
|
||||
if (oldT->tlsEmulation != newT->tlsEmulation) {
|
||||
return true;
|
||||
}
|
||||
if (oldT->useMiddleProxy != newT->useMiddleProxy) {
|
||||
return true;
|
||||
}
|
||||
if (oldT->tag != newT->tag) {
|
||||
return true;
|
||||
}
|
||||
const QString oldUser = oldT->userName.isEmpty()
|
||||
? QString::fromUtf8(protocols::telemt::defaultUserName)
|
||||
: oldT->userName;
|
||||
const QString newUser = newT->userName.isEmpty()
|
||||
? QString::fromUtf8(protocols::telemt::defaultUserName)
|
||||
: newT->userName;
|
||||
if (oldUser != newUser) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (container == DockerContainer::Socks5Proxy) {
|
||||
return true;
|
||||
}
|
||||
@@ -837,6 +893,7 @@ QScopedPointer<InstallerBase> InstallController::createInstaller(DockerContainer
|
||||
case DockerContainer::Sftp: return QScopedPointer<InstallerBase>(new SftpInstaller(this));
|
||||
case DockerContainer::Socks5Proxy: return QScopedPointer<InstallerBase>(new Socks5Installer(this));
|
||||
case DockerContainer::MtProxy: return QScopedPointer<InstallerBase>(new MtProxyInstaller(this));
|
||||
case DockerContainer::Telemt: return QScopedPointer<InstallerBase>(new TelemtInstaller(this));
|
||||
default: return QScopedPointer<InstallerBase>(new InstallerBase(this));
|
||||
}
|
||||
}
|
||||
@@ -882,6 +939,13 @@ bool InstallController::isUpdateDockerContainerRequired(DockerContainer containe
|
||||
return true;
|
||||
}
|
||||
return !oldMt->equalsDockerDeploymentSettings(*newMt);
|
||||
} else if (container == DockerContainer::Telemt) {
|
||||
const auto *oldT = oldConfig.getTelemtProtocolConfig();
|
||||
const auto *newT = newConfig.getTelemtProtocolConfig();
|
||||
if (!oldT || !newT) {
|
||||
return true;
|
||||
}
|
||||
return !oldT->equalsDockerDeploymentSettings(*newT);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1190,6 +1254,31 @@ void InstallController::updateContainerConfigAfterInstallation(DockerContainer c
|
||||
mtProxyConfig->tmeLink = mTmeLink.captured(1);
|
||||
}
|
||||
}
|
||||
} else if (container == DockerContainer::Telemt) {
|
||||
if (auto *telemtConfig = containerConfig.getTelemtProtocolConfig()) {
|
||||
qDebug() << "amnezia-telemt configure stdout" << stdOut;
|
||||
|
||||
static const QRegularExpression reSecret(
|
||||
QStringLiteral(R"(\[\*\]\s+Secret:\s+([0-9a-fA-F]{32}))"),
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
static const QRegularExpression reTgLink(QStringLiteral(R"(\[\*\]\s+tg://\s+link:\s+(tg://proxy\?[^\s]+))"));
|
||||
static const QRegularExpression reTmeLink(
|
||||
QStringLiteral(R"(\[\*\]\s+t\.me\s+link:\s+(https://t\.me/proxy\?[^\s]+))"));
|
||||
|
||||
const QRegularExpressionMatch mSecret = reSecret.match(stdOut);
|
||||
const QRegularExpressionMatch mTgLink = reTgLink.match(stdOut);
|
||||
const QRegularExpressionMatch mTmeLink = reTmeLink.match(stdOut);
|
||||
|
||||
if (mSecret.hasMatch()) {
|
||||
telemtConfig->secret = mSecret.captured(1);
|
||||
}
|
||||
if (mTgLink.hasMatch()) {
|
||||
telemtConfig->tgLink = mTgLink.captured(1);
|
||||
}
|
||||
if (mTmeLink.hasMatch()) {
|
||||
telemtConfig->tmeLink = mTmeLink.captured(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
20
client/core/diagnostics/telemtDiagnostics.h
Normal file
20
client/core/diagnostics/telemtDiagnostics.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef TELEMTDIAGNOSTICS_H
|
||||
#define TELEMTDIAGNOSTICS_H
|
||||
|
||||
#include "containerDiagnostics.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace amnezia
|
||||
{
|
||||
struct TelemtDiagnostics : ContainerDiagnostics
|
||||
{
|
||||
bool upstreamReachable = false;
|
||||
int clientsConnected = -1;
|
||||
QString lastConfigRefresh;
|
||||
QString statsEndpoint;
|
||||
};
|
||||
|
||||
} // namespace amnezia
|
||||
|
||||
#endif // TELEMTDIAGNOSTICS_H
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/models/protocols/sftpProtocolConfig.h"
|
||||
#include "core/models/protocols/socks5ProxyProtocolConfig.h"
|
||||
#include "core/models/protocols/mtProxyProtocolConfig.h"
|
||||
#include "core/models/protocols/telemtProtocolConfig.h"
|
||||
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
||||
#include "core/models/protocols/torProtocolConfig.h"
|
||||
|
||||
@@ -98,6 +99,12 @@ ContainerConfig InstallerBase::createBaseConfig(DockerContainer container, int p
|
||||
config.protocolConfig = mtConfig;
|
||||
break;
|
||||
}
|
||||
case Proto::Telemt: {
|
||||
TelemtProtocolConfig telemtConfig;
|
||||
telemtConfig.port = portStr;
|
||||
config.protocolConfig = telemtConfig;
|
||||
break;
|
||||
}
|
||||
case Proto::Ikev2: {
|
||||
Ikev2ProtocolConfig ikev2Config;
|
||||
config.protocolConfig = ikev2Config;
|
||||
|
||||
79
client/core/installers/telemtInstaller.cpp
Normal file
79
client/core/installers/telemtInstaller.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "telemtInstaller.h"
|
||||
|
||||
#include "core/utils/containerEnum.h"
|
||||
#include "core/utils/containers/containerUtils.h"
|
||||
#include "core/utils/selfhosted/sshSession.h"
|
||||
#include "core/models/containerConfig.h"
|
||||
#include "core/models/protocols/telemtProtocolConfig.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
namespace {
|
||||
constexpr QLatin1String kTelemtClientJsonPath("/data/amnezia-telemt-client.json");
|
||||
constexpr QLatin1String kTelemtClientJsonUploadPath("data/amnezia-telemt-client.json");
|
||||
constexpr QLatin1String kTelemtSecretPath("/data/.amnezia-secret");
|
||||
}
|
||||
|
||||
TelemtInstaller::TelemtInstaller(QObject *parent) : InstallerBase(parent) {}
|
||||
|
||||
ErrorCode TelemtInstaller::extractConfigFromContainer(DockerContainer container, const ServerCredentials &credentials,
|
||||
SshSession *sshSession, ContainerConfig &config) {
|
||||
if (container != DockerContainer::Telemt || !sshSession) {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
TelemtProtocolConfig *tc = config.getTelemtProtocolConfig();
|
||||
if (!tc) {
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
ErrorCode jsonErr = ErrorCode::NoError;
|
||||
const QByteArray jsonRaw =
|
||||
sshSession->getTextFileFromContainer(container, credentials, QString(kTelemtClientJsonPath), jsonErr);
|
||||
if (jsonErr == ErrorCode::NoError && !jsonRaw.trimmed().isEmpty()) {
|
||||
QJsonParseError parseError;
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(jsonRaw.trimmed(), &parseError);
|
||||
if (parseError.error == QJsonParseError::NoError && doc.isObject()) {
|
||||
QJsonObject merged = tc->toJson();
|
||||
const QJsonObject snap = doc.object();
|
||||
for (auto it = snap.constBegin(); it != snap.constEnd(); ++it) {
|
||||
merged.insert(it.key(), it.value());
|
||||
}
|
||||
*tc = TelemtProtocolConfig::fromJson(merged);
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCode secretErr = ErrorCode::NoError;
|
||||
const QByteArray secretRaw =
|
||||
sshSession->getTextFileFromContainer(container, credentials, QString(kTelemtSecretPath), secretErr);
|
||||
const QString sec = QString::fromUtf8(secretRaw).trimmed();
|
||||
if (sec.length() == 32) {
|
||||
static const QRegularExpression hex32(QStringLiteral("^[0-9a-fA-F]{32}$"));
|
||||
if (hex32.match(sec).hasMatch()) {
|
||||
tc->secret = sec;
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
void TelemtInstaller::uploadClientSettingsSnapshot(SshSession &sshSession, const ServerCredentials &credentials,
|
||||
DockerContainer container, const ContainerConfig &config) {
|
||||
const TelemtProtocolConfig *tc = config.getTelemtProtocolConfig();
|
||||
if (!tc) {
|
||||
return;
|
||||
}
|
||||
const QByteArray payload = QJsonDocument(tc->toJson()).toJson(QJsonDocument::Compact);
|
||||
const ErrorCode err = sshSession.uploadTextFileToContainer(container, credentials, QString::fromUtf8(payload),
|
||||
QString(kTelemtClientJsonUploadPath));
|
||||
if (err != ErrorCode::NoError) {
|
||||
qWarning() << "TelemtInstaller::uploadClientSettingsSnapshot failed" << err;
|
||||
}
|
||||
}
|
||||
20
client/core/installers/telemtInstaller.h
Normal file
20
client/core/installers/telemtInstaller.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef TELEMTINSTALLER_H
|
||||
#define TELEMTINSTALLER_H
|
||||
|
||||
#include "installerBase.h"
|
||||
|
||||
class TelemtInstaller : public InstallerBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TelemtInstaller(QObject *parent = nullptr);
|
||||
|
||||
amnezia::ErrorCode
|
||||
extractConfigFromContainer(amnezia::DockerContainer container, const amnezia::ServerCredentials &credentials,
|
||||
SshSession *sshSession, amnezia::ContainerConfig &config) override;
|
||||
|
||||
static void uploadClientSettingsSnapshot(SshSession &sshSession, const amnezia::ServerCredentials &credentials,
|
||||
amnezia::DockerContainer container,
|
||||
const amnezia::ContainerConfig &config);
|
||||
};
|
||||
|
||||
#endif // TELEMTINSTALLER_H
|
||||
@@ -123,6 +123,16 @@ const MtProxyProtocolConfig* ContainerConfig::getMtProxyProtocolConfig() const
|
||||
return protocolConfig.as<MtProxyProtocolConfig>();
|
||||
}
|
||||
|
||||
TelemtProtocolConfig* ContainerConfig::getTelemtProtocolConfig()
|
||||
{
|
||||
return protocolConfig.as<TelemtProtocolConfig>();
|
||||
}
|
||||
|
||||
const TelemtProtocolConfig* ContainerConfig::getTelemtProtocolConfig() const
|
||||
{
|
||||
return protocolConfig.as<TelemtProtocolConfig>();
|
||||
}
|
||||
|
||||
Ikev2ProtocolConfig* ContainerConfig::getIkev2ProtocolConfig()
|
||||
{
|
||||
return protocolConfig.as<Ikev2ProtocolConfig>();
|
||||
|
||||
@@ -60,6 +60,9 @@ struct ContainerConfig {
|
||||
MtProxyProtocolConfig* getMtProxyProtocolConfig();
|
||||
const MtProxyProtocolConfig* getMtProxyProtocolConfig() const;
|
||||
|
||||
TelemtProtocolConfig* getTelemtProtocolConfig();
|
||||
const TelemtProtocolConfig* getTelemtProtocolConfig() const;
|
||||
|
||||
Ikev2ProtocolConfig* getIkev2ProtocolConfig();
|
||||
const Ikev2ProtocolConfig* getIkev2ProtocolConfig() const;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "core/models/protocols/ikev2ProtocolConfig.h"
|
||||
#include "core/models/protocols/dnsProtocolConfig.h"
|
||||
#include "core/models/protocols/mtProxyProtocolConfig.h"
|
||||
#include "core/models/protocols/telemtProtocolConfig.h"
|
||||
|
||||
namespace amnezia
|
||||
{
|
||||
@@ -41,6 +42,8 @@ Proto ProtocolConfig::type() const
|
||||
return Proto::Dns;
|
||||
} else if constexpr (std::is_same_v<T, MtProxyProtocolConfig>) {
|
||||
return Proto::MtProxy;
|
||||
} else if constexpr (std::is_same_v<T, TelemtProtocolConfig>) {
|
||||
return Proto::Telemt;
|
||||
}
|
||||
return Proto::Unknown;
|
||||
}, data);
|
||||
@@ -70,6 +73,8 @@ QString ProtocolConfig::port() const
|
||||
return QString();
|
||||
} else if constexpr (std::is_same_v<T, MtProxyProtocolConfig>) {
|
||||
return arg.port.isEmpty() ? QString(protocols::mtProxy::defaultPort) : arg.port;
|
||||
} else if constexpr (std::is_same_v<T, TelemtProtocolConfig>) {
|
||||
return arg.port.isEmpty() ? QString(protocols::telemt::defaultPort) : arg.port;
|
||||
}
|
||||
return QString();
|
||||
}, data);
|
||||
@@ -95,6 +100,8 @@ QString ProtocolConfig::transportProto() const
|
||||
return QString();
|
||||
} else if constexpr (std::is_same_v<T, MtProxyProtocolConfig>) {
|
||||
return QStringLiteral("tcp");
|
||||
} else if constexpr (std::is_same_v<T, TelemtProtocolConfig>) {
|
||||
return QStringLiteral("tcp");
|
||||
}
|
||||
return QString();
|
||||
}, data);
|
||||
@@ -308,6 +315,8 @@ ProtocolConfig ProtocolConfig::fromJson(const QJsonObject& json, Proto type)
|
||||
return ProtocolConfig{DnsProtocolConfig::fromJson(json)};
|
||||
case Proto::MtProxy:
|
||||
return ProtocolConfig{MtProxyProtocolConfig::fromJson(json)};
|
||||
case Proto::Telemt:
|
||||
return ProtocolConfig{TelemtProtocolConfig::fromJson(json)};
|
||||
default:
|
||||
return ProtocolConfig{AwgProtocolConfig{}};
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "core/models/protocols/torProtocolConfig.h"
|
||||
#include "core/models/protocols/dnsProtocolConfig.h"
|
||||
#include "core/models/protocols/mtProxyProtocolConfig.h"
|
||||
#include "core/models/protocols/telemtProtocolConfig.h"
|
||||
|
||||
namespace amnezia
|
||||
{
|
||||
@@ -38,6 +39,7 @@ struct ProtocolConfig {
|
||||
SftpProtocolConfig,
|
||||
Socks5ProxyProtocolConfig,
|
||||
MtProxyProtocolConfig,
|
||||
TelemtProtocolConfig,
|
||||
Ikev2ProtocolConfig,
|
||||
TorProtocolConfig,
|
||||
DnsProtocolConfig
|
||||
|
||||
162
client/core/models/protocols/telemtProtocolConfig.cpp
Normal file
162
client/core/models/protocols/telemtProtocolConfig.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
#include "telemtProtocolConfig.h"
|
||||
|
||||
#include "core/utils/constants/configKeys.h"
|
||||
#include "core/utils/constants/protocolConstants.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
QJsonObject TelemtProtocolConfig::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
if (!port.isEmpty()) {
|
||||
obj[QString(configKey::port)] = port;
|
||||
}
|
||||
if (!secret.isEmpty()) {
|
||||
obj[protocols::telemt::secretKey] = secret;
|
||||
}
|
||||
if (!tag.isEmpty()) {
|
||||
obj[protocols::telemt::tagKey] = tag;
|
||||
}
|
||||
if (!tgLink.isEmpty()) {
|
||||
obj[protocols::telemt::tgLinkKey] = tgLink;
|
||||
}
|
||||
if (!tmeLink.isEmpty()) {
|
||||
obj[protocols::telemt::tmeLinkKey] = tmeLink;
|
||||
}
|
||||
obj[protocols::telemt::isEnabledKey] = isEnabled;
|
||||
if (!publicHost.isEmpty()) {
|
||||
obj[protocols::telemt::publicHostKey] = publicHost;
|
||||
}
|
||||
if (!transportMode.isEmpty()) {
|
||||
obj[protocols::telemt::transportModeKey] = transportMode;
|
||||
}
|
||||
if (!tlsDomain.isEmpty()) {
|
||||
obj[protocols::telemt::tlsDomainKey] = tlsDomain;
|
||||
}
|
||||
obj[protocols::telemt::maskEnabledKey] = maskEnabled;
|
||||
obj[protocols::telemt::tlsEmulationKey] = tlsEmulation;
|
||||
obj[protocols::telemt::useMiddleProxyKey] = useMiddleProxy;
|
||||
if (!userName.isEmpty()) {
|
||||
obj[protocols::telemt::userNameKey] = userName;
|
||||
}
|
||||
if (!additionalSecrets.isEmpty()) {
|
||||
obj[protocols::telemt::additionalSecretsKey] = QJsonArray::fromStringList(additionalSecrets);
|
||||
}
|
||||
if (!workersMode.isEmpty()) {
|
||||
obj[protocols::telemt::workersModeKey] = workersMode;
|
||||
}
|
||||
if (!workers.isEmpty()) {
|
||||
obj[protocols::telemt::workersKey] = workers;
|
||||
}
|
||||
obj[protocols::telemt::natEnabledKey] = natEnabled;
|
||||
if (!natInternalIp.isEmpty()) {
|
||||
obj[protocols::telemt::natInternalIpKey] = natInternalIp;
|
||||
}
|
||||
if (!natExternalIp.isEmpty()) {
|
||||
obj[protocols::telemt::natExternalIpKey] = natExternalIp;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
TelemtProtocolConfig TelemtProtocolConfig::fromJson(const QJsonObject &json)
|
||||
{
|
||||
TelemtProtocolConfig c;
|
||||
c.port = json.value(QString(configKey::port)).toString();
|
||||
c.secret = json.value(protocols::telemt::secretKey).toString();
|
||||
c.tag = json.value(protocols::telemt::tagKey).toString();
|
||||
c.tgLink = json.value(protocols::telemt::tgLinkKey).toString();
|
||||
c.tmeLink = json.value(protocols::telemt::tmeLinkKey).toString();
|
||||
c.isEnabled = json.value(protocols::telemt::isEnabledKey).toBool(true);
|
||||
c.publicHost = json.value(protocols::telemt::publicHostKey).toString();
|
||||
c.transportMode = json.value(protocols::telemt::transportModeKey).toString();
|
||||
c.tlsDomain = json.value(protocols::telemt::tlsDomainKey).toString();
|
||||
c.maskEnabled = json.value(protocols::telemt::maskEnabledKey).toBool(true);
|
||||
c.tlsEmulation = json.value(protocols::telemt::tlsEmulationKey).toBool(false);
|
||||
c.useMiddleProxy = json.value(protocols::telemt::useMiddleProxyKey).toBool(true);
|
||||
c.userName = json.value(protocols::telemt::userNameKey).toString();
|
||||
for (const auto &v : json.value(protocols::telemt::additionalSecretsKey).toArray()) {
|
||||
const QString s = v.toString();
|
||||
if (!s.isEmpty()) {
|
||||
c.additionalSecrets.append(s);
|
||||
}
|
||||
}
|
||||
c.workersMode = json.value(protocols::telemt::workersModeKey).toString();
|
||||
c.workers = json.value(protocols::telemt::workersKey).toString();
|
||||
c.natEnabled = json.value(protocols::telemt::natEnabledKey).toBool(false);
|
||||
c.natInternalIp = json.value(protocols::telemt::natInternalIpKey).toString();
|
||||
c.natExternalIp = json.value(protocols::telemt::natExternalIpKey).toString();
|
||||
return c;
|
||||
}
|
||||
|
||||
bool TelemtProtocolConfig::equalsDockerDeploymentSettings(const TelemtProtocolConfig &other) const
|
||||
{
|
||||
const auto normPort = [](const QString &p) {
|
||||
return p.isEmpty() ? QString(protocols::telemt::defaultPort) : p;
|
||||
};
|
||||
const auto normTransport = [](const QString &t) {
|
||||
return t.isEmpty() ? QString(protocols::telemt::transportModeStandard) : t;
|
||||
};
|
||||
const auto normWorkersMode = [](const QString &m) {
|
||||
return m.isEmpty() ? QString(protocols::telemt::workersModeAuto) : m;
|
||||
};
|
||||
|
||||
if (normPort(port) != normPort(other.port)) {
|
||||
return false;
|
||||
}
|
||||
if (normTransport(transportMode) != normTransport(other.transportMode)) {
|
||||
return false;
|
||||
}
|
||||
if (tlsDomain != other.tlsDomain) {
|
||||
return false;
|
||||
}
|
||||
if (secret != other.secret) {
|
||||
return false;
|
||||
}
|
||||
if (tag != other.tag) {
|
||||
return false;
|
||||
}
|
||||
if (publicHost != other.publicHost) {
|
||||
return false;
|
||||
}
|
||||
if (maskEnabled != other.maskEnabled) {
|
||||
return false;
|
||||
}
|
||||
if (tlsEmulation != other.tlsEmulation) {
|
||||
return false;
|
||||
}
|
||||
if (useMiddleProxy != other.useMiddleProxy) {
|
||||
return false;
|
||||
}
|
||||
if (userName != other.userName) {
|
||||
return false;
|
||||
}
|
||||
if (normWorkersMode(workersMode) != normWorkersMode(other.workersMode)) {
|
||||
return false;
|
||||
}
|
||||
if (workers != other.workers) {
|
||||
return false;
|
||||
}
|
||||
if (natEnabled != other.natEnabled) {
|
||||
return false;
|
||||
}
|
||||
if (natInternalIp != other.natInternalIp) {
|
||||
return false;
|
||||
}
|
||||
if (natExternalIp != other.natExternalIp) {
|
||||
return false;
|
||||
}
|
||||
if (isEnabled != other.isEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList aa = additionalSecrets;
|
||||
QStringList bb = other.additionalSecrets;
|
||||
aa.removeAll(QString());
|
||||
bb.removeAll(QString());
|
||||
std::sort(aa.begin(), aa.end());
|
||||
std::sort(bb.begin(), bb.end());
|
||||
return aa == bb;
|
||||
}
|
||||
38
client/core/models/protocols/telemtProtocolConfig.h
Normal file
38
client/core/models/protocols/telemtProtocolConfig.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef TELEMTPROTOCOLCONFIG_H
|
||||
#define TELEMTPROTOCOLCONFIG_H
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
namespace amnezia {
|
||||
|
||||
struct TelemtProtocolConfig {
|
||||
QString port;
|
||||
QString secret;
|
||||
QString tag;
|
||||
QString tgLink;
|
||||
QString tmeLink;
|
||||
bool isEnabled = true;
|
||||
QString publicHost;
|
||||
QString transportMode;
|
||||
QString tlsDomain;
|
||||
bool maskEnabled = true;
|
||||
bool tlsEmulation = false;
|
||||
bool useMiddleProxy = true;
|
||||
QString userName;
|
||||
QStringList additionalSecrets;
|
||||
QString workersMode;
|
||||
QString workers;
|
||||
bool natEnabled = false;
|
||||
QString natInternalIp;
|
||||
QString natExternalIp;
|
||||
|
||||
QJsonObject toJson() const;
|
||||
static TelemtProtocolConfig fromJson(const QJsonObject &json);
|
||||
bool equalsDockerDeploymentSettings(const TelemtProtocolConfig &other) const;
|
||||
};
|
||||
|
||||
} // namespace amnezia
|
||||
|
||||
#endif // TELEMTPROTOCOLCONFIG_H
|
||||
@@ -70,6 +70,7 @@ QMap<Proto, QString> ProtocolUtils::protocolHumanNames()
|
||||
{ Proto::Sftp, QObject::tr("SFTP service") },
|
||||
{ Proto::Socks5Proxy, QObject::tr("SOCKS5 proxy server") },
|
||||
{ Proto::MtProxy, QObject::tr("MTProxy (Telegram)") },
|
||||
{ Proto::Telemt, QObject::tr("Telemt (Telegram)") },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -95,6 +96,7 @@ ServiceType ProtocolUtils::protocolService(Proto p)
|
||||
case Proto::Sftp: return ServiceType::Other;
|
||||
case Proto::Socks5Proxy: return ServiceType::Other;
|
||||
case Proto::MtProxy: return ServiceType::Other;
|
||||
case Proto::Telemt: return ServiceType::Other;
|
||||
default: return ServiceType::Other;
|
||||
}
|
||||
}
|
||||
@@ -108,6 +110,7 @@ int ProtocolUtils::getPortForInstall(Proto p)
|
||||
case Socks5Proxy:
|
||||
return QRandomGenerator::global()->bounded(30000, 50000);
|
||||
case MtProxy:
|
||||
case Telemt:
|
||||
default:
|
||||
return defaultPort(p);
|
||||
}
|
||||
@@ -128,6 +131,7 @@ int ProtocolUtils::defaultPort(Proto p)
|
||||
case Proto::Sftp: return 222;
|
||||
case Proto::Socks5Proxy: return 38080;
|
||||
case Proto::MtProxy: return QString(protocols::mtProxy::defaultPort).toInt();
|
||||
case Proto::Telemt: return QString(protocols::telemt::defaultPort).toInt();
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
@@ -147,6 +151,7 @@ bool ProtocolUtils::defaultPortChangeable(Proto p)
|
||||
case Proto::Sftp: return true;
|
||||
case Proto::Socks5Proxy: return true;
|
||||
case Proto::MtProxy: return true;
|
||||
case Proto::Telemt: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -168,6 +173,7 @@ TransportProto ProtocolUtils::defaultTransportProto(Proto p)
|
||||
case Proto::Sftp: return TransportProto::Tcp;
|
||||
case Proto::Socks5Proxy: return TransportProto::Tcp;
|
||||
case Proto::MtProxy: return TransportProto::Tcp;
|
||||
case Proto::Telemt: return TransportProto::Tcp;
|
||||
default: return TransportProto::Udp;
|
||||
}
|
||||
}
|
||||
@@ -188,9 +194,9 @@ bool ProtocolUtils::defaultTransportProtoChangeable(Proto p)
|
||||
case Proto::Sftp: return false;
|
||||
case Proto::Socks5Proxy: return false;
|
||||
case Proto::MtProxy: return false;
|
||||
case Proto::Telemt: return false;
|
||||
default: return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QString ProtocolUtils::key_proto_config_data(Proto p)
|
||||
|
||||
@@ -93,6 +93,7 @@ namespace amnezia
|
||||
constexpr QLatin1String ssxray("ssxray");
|
||||
constexpr QLatin1String socks5proxy("socks5proxy");
|
||||
constexpr QLatin1String mtproxy("mtproxy");
|
||||
constexpr QLatin1String telemt("telemt");
|
||||
|
||||
constexpr QLatin1String splitTunnelSites("splitTunnelSites");
|
||||
constexpr QLatin1String splitTunnelType("splitTunnelType");
|
||||
|
||||
@@ -205,6 +205,40 @@ namespace amnezia
|
||||
constexpr char defaultTlsDomain[] = "googletagmanager.com";
|
||||
}
|
||||
|
||||
namespace telemt
|
||||
{
|
||||
constexpr char secretKey[] = "telemt_secret";
|
||||
constexpr char tagKey[] = "telemt_tag";
|
||||
constexpr char tgLinkKey[] = "telemt_tg_link";
|
||||
constexpr char tmeLinkKey[] = "telemt_tme_link";
|
||||
constexpr char isEnabledKey[] = "telemt_is_enabled";
|
||||
constexpr char publicHostKey[] = "telemt_public_host";
|
||||
constexpr char transportModeKey[] = "telemt_transport_mode";
|
||||
constexpr char tlsDomainKey[] = "telemt_tls_domain";
|
||||
constexpr char maskEnabledKey[] = "telemt_mask_enabled";
|
||||
constexpr char tlsEmulationKey[] = "telemt_tls_emulation";
|
||||
constexpr char useMiddleProxyKey[] = "telemt_use_middle_proxy";
|
||||
constexpr char userNameKey[] = "telemt_user_name";
|
||||
// Stored for UI only (Telemt server ignores these; same controls as MTProxy page)
|
||||
constexpr char additionalSecretsKey[] = "telemt_additional_secrets";
|
||||
constexpr char workersKey[] = "telemt_workers";
|
||||
constexpr char workersModeKey[] = "telemt_workers_mode";
|
||||
constexpr char natEnabledKey[] = "telemt_nat_enabled";
|
||||
constexpr char natInternalIpKey[] = "telemt_nat_internal_ip";
|
||||
constexpr char natExternalIpKey[] = "telemt_nat_external_ip";
|
||||
|
||||
constexpr char transportModeStandard[] = "standard";
|
||||
constexpr char transportModeFakeTLS[] = "faketls";
|
||||
|
||||
constexpr char defaultPort[] = "443";
|
||||
constexpr char defaultTlsDomain[] = "googletagmanager.com";
|
||||
constexpr char defaultUserName[] = "amnezia";
|
||||
constexpr char defaultWorkers[] = "2";
|
||||
constexpr char workersModeAuto[] = "auto";
|
||||
constexpr char workersModeManual[] = "manual";
|
||||
constexpr int maxWorkers = 32;
|
||||
}
|
||||
|
||||
} // namespace protocols
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace amnezia
|
||||
Sftp,
|
||||
Socks5Proxy,
|
||||
MtProxy,
|
||||
Telemt,
|
||||
};
|
||||
Q_ENUM_NS(DockerContainer)
|
||||
} // namespace ContainerEnumNS
|
||||
|
||||
@@ -74,6 +74,7 @@ QMap<DockerContainer, QString> ContainerUtils::containerHumanNames()
|
||||
{ DockerContainer::Sftp, QObject::tr("SFTP file sharing service") },
|
||||
{ DockerContainer::Socks5Proxy, QObject::tr("SOCKS5 proxy server") },
|
||||
{ DockerContainer::MtProxy, QObject::tr("MTProxy (Telegram)") },
|
||||
{ DockerContainer::Telemt, QObject::tr("Telemt (Telegram)") },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,6 +108,8 @@ QMap<DockerContainer, QString> ContainerUtils::containerDescriptions()
|
||||
QObject::tr("") },
|
||||
{ DockerContainer::MtProxy,
|
||||
QObject::tr("Telegram MTProto proxy server") },
|
||||
{ DockerContainer::Telemt,
|
||||
QObject::tr("Telegram MTProto proxy (Telemt, Rust)") },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -183,6 +186,9 @@ QMap<DockerContainer, QString> ContainerUtils::containerDetailedDescriptions()
|
||||
"Allows Telegram clients to connect through your server "
|
||||
"using the MTProto protocol. Supports FakeTLS mode for "
|
||||
"bypassing DPI-based blocking.") },
|
||||
{ DockerContainer::Telemt,
|
||||
QObject::tr("Telegram MTProto proxy powered by Telemt (Rust). "
|
||||
"Supports secure and TLS fronting modes with optional traffic masking.") }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -208,6 +214,7 @@ Proto ContainerUtils::defaultProtocol(DockerContainer c)
|
||||
case DockerContainer::Sftp: return Proto::Sftp;
|
||||
case DockerContainer::Socks5Proxy: return Proto::Socks5Proxy;
|
||||
case DockerContainer::MtProxy: return Proto::MtProxy;
|
||||
case DockerContainer::Telemt: return Proto::Telemt;
|
||||
default: return Proto::Unknown;
|
||||
}
|
||||
}
|
||||
@@ -236,6 +243,7 @@ bool ContainerUtils::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
case DockerContainer::Xray: return true;
|
||||
case DockerContainer::SSXray: return true;
|
||||
case DockerContainer::MtProxy: return true;
|
||||
case DockerContainer::Telemt: return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -250,6 +258,7 @@ bool ContainerUtils::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
case DockerContainer::Xray: return true;
|
||||
case DockerContainer::SSXray: return true;
|
||||
case DockerContainer::MtProxy: return true;
|
||||
case DockerContainer::Telemt: return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -269,6 +278,7 @@ bool ContainerUtils::isSupportedByCurrentPlatform(DockerContainer c)
|
||||
case DockerContainer::Xray: return true;
|
||||
case DockerContainer::SSXray: return true;
|
||||
case DockerContainer::MtProxy: return true;
|
||||
case DockerContainer::Telemt: return true;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
@@ -332,6 +342,7 @@ bool ContainerUtils::isShareable(DockerContainer container)
|
||||
case DockerContainer::Sftp: return false;
|
||||
case DockerContainer::Socks5Proxy: return false;
|
||||
case DockerContainer::MtProxy: return false;
|
||||
case DockerContainer::Telemt: return false;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
@@ -361,6 +372,7 @@ int ContainerUtils::installPageOrder(DockerContainer container)
|
||||
case DockerContainer::Ipsec: return 7;
|
||||
case DockerContainer::SSXray: return 8;
|
||||
case DockerContainer::MtProxy:
|
||||
case DockerContainer::Telemt:
|
||||
return 20;
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace amnezia
|
||||
Sftp,
|
||||
Socks5Proxy,
|
||||
MtProxy,
|
||||
Telemt,
|
||||
};
|
||||
Q_ENUM_NS(Proto)
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "core/models/protocols/sftpProtocolConfig.h"
|
||||
#include "core/models/protocols/socks5ProxyProtocolConfig.h"
|
||||
#include "core/models/protocols/mtProxyProtocolConfig.h"
|
||||
#include "core/models/protocols/telemtProtocolConfig.h"
|
||||
|
||||
using namespace amnezia;
|
||||
using namespace ProtocolUtils;
|
||||
@@ -39,6 +40,7 @@ QString amnezia::scriptFolder(amnezia::DockerContainer container)
|
||||
case DockerContainer::Sftp: return QLatin1String("sftp");
|
||||
case DockerContainer::Socks5Proxy: return QLatin1String("socks5_proxy");
|
||||
case DockerContainer::MtProxy: return QLatin1String("mtproxy");
|
||||
case DockerContainer::Telemt: return QLatin1String("telemt");
|
||||
default: return QString();
|
||||
}
|
||||
}
|
||||
@@ -335,6 +337,37 @@ amnezia::ScriptVars amnezia::genMtProxyVars(const ContainerConfig &containerConf
|
||||
return vars;
|
||||
}
|
||||
|
||||
amnezia::ScriptVars amnezia::genTelemtVars(const ContainerConfig &containerConfig)
|
||||
{
|
||||
ScriptVars vars;
|
||||
|
||||
if (auto *telemtProtocolConfig = containerConfig.getTelemtProtocolConfig()) {
|
||||
const TelemtProtocolConfig &c = *telemtProtocolConfig;
|
||||
|
||||
const QString transport = c.transportMode.isEmpty() ? QString(protocols::telemt::transportModeStandard)
|
||||
: c.transportMode;
|
||||
const bool faketls = (transport == QLatin1String(protocols::telemt::transportModeFakeTLS));
|
||||
vars.append({ { "$TELEMT_TOML_SECURE", faketls ? QLatin1String("false") : QLatin1String("true") } });
|
||||
vars.append({ { "$TELEMT_TOML_TLS", faketls ? QLatin1String("true") : QLatin1String("false") } });
|
||||
vars.append({ { "$TELEMT_PORT", c.port.isEmpty() ? QString(protocols::telemt::defaultPort) : c.port } });
|
||||
vars.append({ { "$TELEMT_SECRET", c.secret } });
|
||||
vars.append({ { "$TELEMT_TAG", c.tag } });
|
||||
QString tlsDomain = c.tlsDomain;
|
||||
if (tlsDomain.isEmpty()) {
|
||||
tlsDomain = QString(protocols::telemt::defaultTlsDomain);
|
||||
}
|
||||
vars.append({ { "$TELEMT_TLS_DOMAIN", tlsDomain } });
|
||||
vars.append({ { "$TELEMT_PUBLIC_HOST", c.publicHost } });
|
||||
vars.append({ { "$TELEMT_USER_NAME",
|
||||
c.userName.isEmpty() ? QString::fromUtf8(protocols::telemt::defaultUserName) : c.userName } });
|
||||
vars.append({ { "$TELEMT_USE_MIDDLE_PROXY", c.useMiddleProxy ? QLatin1String("true") : QLatin1String("false") } });
|
||||
vars.append({ { "$TELEMT_MASK", c.maskEnabled ? QLatin1String("true") : QLatin1String("false") } });
|
||||
vars.append({ { "$TELEMT_TLS_EMULATION", c.tlsEmulation ? QLatin1String("true") : QLatin1String("false") } });
|
||||
}
|
||||
|
||||
return vars;
|
||||
}
|
||||
|
||||
amnezia::ScriptVars amnezia::genProtocolVarsForContainer(DockerContainer container, const ContainerConfig &containerConfig)
|
||||
{
|
||||
ScriptVars vars;
|
||||
@@ -362,6 +395,9 @@ amnezia::ScriptVars amnezia::genProtocolVarsForContainer(DockerContainer contain
|
||||
case Proto::MtProxy:
|
||||
vars.append(genMtProxyVars(containerConfig));
|
||||
break;
|
||||
case Proto::Telemt:
|
||||
vars.append(genTelemtVars(containerConfig));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ ScriptVars genAwgVars(const ContainerConfig &containerConfig);
|
||||
ScriptVars genSftpVars(const ContainerConfig &containerConfig);
|
||||
ScriptVars genSocks5ProxyVars(const ContainerConfig &containerConfig);
|
||||
ScriptVars genMtProxyVars(const ContainerConfig &containerConfig);
|
||||
ScriptVars genTelemtVars(const ContainerConfig &containerConfig);
|
||||
|
||||
ScriptVars genProtocolVarsForContainer(DockerContainer container, const ContainerConfig &containerConfig);
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ ErrorCode SshSession::runContainerScript(const ServerCredentials &credentials, D
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
const bool useSh = container == DockerContainer::Socks5Proxy || container == DockerContainer::MtProxy;
|
||||
const bool useSh = container == DockerContainer::Socks5Proxy || container == DockerContainer::MtProxy || container == DockerContainer::Telemt;
|
||||
QString runner = QString("sudo docker exec -i $CONTAINER_NAME %2 %1 ").arg(fileName, useSh ? "sh" : "bash");
|
||||
e = runScript(credentials, replaceVars(runner, amnezia::genBaseVars(credentials, container, QString(), QString())), cbReadStdOut, cbReadStdErr);
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
<file>mtproxy/Dockerfile</file>
|
||||
<file>mtproxy/run_container.sh</file>
|
||||
<file>mtproxy/start.sh</file>
|
||||
<file>telemt/configure_container.sh</file>
|
||||
<file>telemt/Dockerfile</file>
|
||||
<file>telemt/run_container.sh</file>
|
||||
<file>telemt/start.sh</file>
|
||||
<file>openvpn/configure_container.sh</file>
|
||||
<file>openvpn/Dockerfile</file>
|
||||
<file>openvpn/run_container.sh</file>
|
||||
|
||||
42
client/server_scripts/telemt/Dockerfile
Normal file
42
client/server_scripts/telemt/Dockerfile
Normal file
@@ -0,0 +1,42 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
# Debian-based image with Telemt binary (shell + jq for Amnezia configure scripts).
|
||||
# Binary from https://github.com/telemt/telemt releases (same pattern as upstream Dockerfile minimal stage).
|
||||
|
||||
FROM debian:12-slim
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
binutils \
|
||||
ca-certificates \
|
||||
curl \
|
||||
jq \
|
||||
openssl \
|
||||
tar \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Use machine arch (works with classic `docker build`; TARGETARCH is only set with BuildKit).
|
||||
RUN set -eux; \
|
||||
ARCH="$(uname -m)"; \
|
||||
case "$ARCH" in \
|
||||
x86_64) ASSET="telemt-x86_64-linux-musl.tar.gz" ;; \
|
||||
aarch64|arm64) ASSET="telemt-aarch64-linux-musl.tar.gz" ;; \
|
||||
*) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; \
|
||||
esac; \
|
||||
curl -fL --retry 5 --retry-delay 3 --connect-timeout 10 --max-time 120 \
|
||||
-o "/tmp/${ASSET}" "https://github.com/telemt/telemt/releases/latest/download/${ASSET}"; \
|
||||
curl -fL --retry 5 --retry-delay 3 --connect-timeout 10 --max-time 120 \
|
||||
-o "/tmp/${ASSET}.sha256" "https://github.com/telemt/telemt/releases/latest/download/${ASSET}.sha256"; \
|
||||
cd /tmp && sha256sum -c "${ASSET}.sha256"; \
|
||||
tar -xzf "${ASSET}" -C /tmp; \
|
||||
test -f /tmp/telemt; \
|
||||
install -m 0755 /tmp/telemt /usr/local/bin/telemt; \
|
||||
strip --strip-unneeded /usr/local/bin/telemt || true; \
|
||||
rm -f "/tmp/${ASSET}" "/tmp/${ASSET}.sha256" /tmp/telemt
|
||||
|
||||
RUN mkdir -p /opt/amnezia /data
|
||||
RUN printf '#!/bin/sh\ntail -f /dev/null\n' > /opt/amnezia/start.sh && \
|
||||
chmod a+x /opt/amnezia/start.sh
|
||||
|
||||
VOLUME /data
|
||||
ENTRYPOINT ["/bin/sh", "/opt/amnezia/start.sh"]
|
||||
CMD [""]
|
||||
73
client/server_scripts/telemt/configure_container.sh
Normal file
73
client/server_scripts/telemt/configure_container.sh
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
# Do not use set -e: Telemt / curl / kill edge cases should not abort the whole configure step.
|
||||
|
||||
echo "[*] Amnezia Telemt: configure script start"
|
||||
mkdir -p /data/tlsfront
|
||||
|
||||
# Secret: substituted $TELEMT_SECRET -> saved file -> openssl (same rules as MTProxy configure)
|
||||
if [ -n "$TELEMT_SECRET" ]; then
|
||||
SECRET="$TELEMT_SECRET"
|
||||
elif [ -f /data/.amnezia-secret ]; then
|
||||
SECRET=$(cat /data/.amnezia-secret)
|
||||
else
|
||||
SECRET=$(openssl rand -hex 16)
|
||||
fi
|
||||
# Must be exactly 32 hex chars
|
||||
echo "$SECRET" | grep -qE '^[0-9a-fA-F]{32}$' || SECRET=$(openssl rand -hex 16)
|
||||
|
||||
# Build config.toml (other variables substituted on the host by Amnezia before upload)
|
||||
rm -f /data/config.toml
|
||||
|
||||
{
|
||||
echo "### Amnezia Telemt — generated"
|
||||
echo "[general]"
|
||||
echo "use_middle_proxy = $TELEMT_USE_MIDDLE_PROXY"
|
||||
echo "log_level = \"normal\""
|
||||
if [ -n "$TELEMT_TAG" ]; then
|
||||
echo "ad_tag = \"$TELEMT_TAG\""
|
||||
fi
|
||||
echo ""
|
||||
echo "[general.modes]"
|
||||
echo "classic = false"
|
||||
echo "secure = $TELEMT_TOML_SECURE"
|
||||
echo "tls = $TELEMT_TOML_TLS"
|
||||
echo ""
|
||||
echo "[general.links]"
|
||||
echo "show = \"*\""
|
||||
if [ -n "$TELEMT_PUBLIC_HOST" ]; then
|
||||
echo "public_host = \"$TELEMT_PUBLIC_HOST\""
|
||||
fi
|
||||
echo "public_port = $TELEMT_PORT"
|
||||
echo ""
|
||||
echo "[server]"
|
||||
echo "port = $TELEMT_PORT"
|
||||
echo ""
|
||||
echo "[server.api]"
|
||||
echo "enabled = true"
|
||||
echo "listen = \"0.0.0.0:9091\""
|
||||
# Match upstream Telemt default: localhost API only (curl in this script uses 127.0.0.1).
|
||||
echo "whitelist = [\"127.0.0.0/8\"]"
|
||||
echo ""
|
||||
echo "[[server.listeners]]"
|
||||
echo "ip = \"0.0.0.0\""
|
||||
echo ""
|
||||
echo "[censorship]"
|
||||
echo "tls_domain = \"$TELEMT_TLS_DOMAIN\""
|
||||
echo "mask = $TELEMT_MASK"
|
||||
echo "tls_emulation = $TELEMT_TLS_EMULATION"
|
||||
echo "tls_front_dir = \"/data/tlsfront\""
|
||||
echo ""
|
||||
echo "[access.users]"
|
||||
echo "$TELEMT_USER_NAME = \"$SECRET\""
|
||||
} > /data/config.toml
|
||||
|
||||
echo "$SECRET" > /data/.amnezia-secret
|
||||
chmod 600 /data/.amnezia-secret 2>/dev/null || true
|
||||
|
||||
# Do not start telemt here: a long-lived process + curl loop inside `docker exec` can confuse SSH/Docker
|
||||
# timing and is unnecessary — start.sh runs telemt after configure. Links can be empty until the service
|
||||
# is up; the client still parses Secret below.
|
||||
echo "[*] Telemt configuration"
|
||||
echo "[*] Secret: $SECRET"
|
||||
echo "[*] tg:// link: "
|
||||
echo "[*] t.me link: "
|
||||
9
client/server_scripts/telemt/run_container.sh
Normal file
9
client/server_scripts/telemt/run_container.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
# Run container (ulimit per Telemt docs — avoids "Too many open files" under load)
|
||||
sudo docker run -d \
|
||||
--log-driver none \
|
||||
--restart always \
|
||||
--ulimit nofile=65536:65536 \
|
||||
-p $TELEMT_PORT:$TELEMT_PORT/tcp \
|
||||
-v amnezia-telemt-data:/data \
|
||||
--name $CONTAINER_NAME \
|
||||
$CONTAINER_NAME
|
||||
12
client/server_scripts/telemt/start.sh
Normal file
12
client/server_scripts/telemt/start.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Container startup (Telemt)"
|
||||
|
||||
if [ ! -f /data/config.toml ]; then
|
||||
echo "ERROR: /data/config.toml not found — run configure_container first"
|
||||
tail -f /dev/null
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p /data/tlsfront
|
||||
exec /usr/local/bin/telemt /data/config.toml
|
||||
@@ -51,6 +51,7 @@ namespace PageLoader
|
||||
PageServiceDnsSettings,
|
||||
PageServiceSocksProxySettings,
|
||||
PageServiceMtProxySettings,
|
||||
PageServiceTelemtSettings,
|
||||
|
||||
PageSetupWizardStart,
|
||||
PageSetupWizardCredentials,
|
||||
|
||||
@@ -73,6 +73,7 @@ InstallUiController::InstallUiController(InstallController *installController,
|
||||
SftpConfigModel *sftpConfigModel,
|
||||
Socks5ProxyConfigModel *socks5ConfigModel,
|
||||
MtProxyConfigModel* mtConfigModel,
|
||||
TelemtConfigModel *telemtConfigModel,
|
||||
QObject *parent)
|
||||
: QObject(parent),
|
||||
m_installController(installController),
|
||||
@@ -90,7 +91,8 @@ InstallUiController::InstallUiController(InstallController *installController,
|
||||
#endif
|
||||
m_sftpConfigModel(sftpConfigModel),
|
||||
m_socks5ConfigModel(socks5ConfigModel),
|
||||
m_mtProxyConfigModel(mtConfigModel)
|
||||
m_mtProxyConfigModel(mtConfigModel),
|
||||
m_telemtConfigModel(telemtConfigModel)
|
||||
{
|
||||
connect(m_installController, &InstallController::configValidated, this, &InstallUiController::configValidated);
|
||||
connect(m_installController, &InstallController::validationErrorOccurred, this, [this](ErrorCode errorCode) {
|
||||
@@ -250,6 +252,10 @@ void InstallUiController::updateContainer(int serverIndex, int containerIndex, i
|
||||
containerConfig.protocolConfig = m_mtProxyConfigModel->getProtocolConfig();
|
||||
break;
|
||||
}
|
||||
case Proto::Telemt: {
|
||||
containerConfig.protocolConfig = m_telemtConfigModel->getProtocolConfig();
|
||||
break;
|
||||
}
|
||||
#ifdef Q_OS_WINDOWS
|
||||
case Proto::Ikev2: {
|
||||
containerConfig.protocolConfig = m_ikev2ConfigModel->getProtocolConfig();
|
||||
@@ -261,7 +267,7 @@ void InstallUiController::updateContainer(int serverIndex, int containerIndex, i
|
||||
}
|
||||
ContainerConfig oldContainerConfig = m_serversController->getContainerConfig(serverIndex, container);
|
||||
|
||||
if (container == DockerContainer::MtProxy) {
|
||||
if (container == DockerContainer::MtProxy || container == DockerContainer::Telemt) {
|
||||
emit serverIsBusy(true);
|
||||
auto *watcher = new QFutureWatcher<ErrorCode>(this);
|
||||
QObject::connect(watcher, &QFutureWatcher<ErrorCode>::finished, this,
|
||||
@@ -338,6 +344,10 @@ void InstallUiController::setContainerEnabled(int serverIndex, int containerInde
|
||||
mtConfig->isEnabled = enabled;
|
||||
m_serversController->updateContainerConfig(serverIndex, container, currentConfig);
|
||||
m_protocolModel->updateModel(currentConfig);
|
||||
} else if (auto *telemtConfig = currentConfig.getTelemtProtocolConfig()) {
|
||||
telemtConfig->isEnabled = enabled;
|
||||
m_serversController->updateContainerConfig(serverIndex, container, currentConfig);
|
||||
m_protocolModel->updateModel(currentConfig);
|
||||
}
|
||||
emit setContainerEnabledFinished(enabled);
|
||||
return;
|
||||
@@ -447,7 +457,8 @@ void InstallUiController::fetchContainerSecret(int serverIndex, int containerInd
|
||||
};
|
||||
|
||||
SshSession sshSession(this);
|
||||
const QString path = QStringLiteral("/data/secret");
|
||||
const QString path = container == DockerContainer::Telemt ? QStringLiteral("/data/.amnezia-secret")
|
||||
: QStringLiteral("/data/secret");
|
||||
const QString cmd =
|
||||
QStringLiteral("sudo docker exec %1 cat %2").arg(containerName, path);
|
||||
const ErrorCode errorCode = sshSession.runScript(credentials, cmd, cbReadStdOut);
|
||||
@@ -675,6 +686,7 @@ void InstallUiController::updateProtocolConfigModel(int serverIndex, int contain
|
||||
case Proto::Sftp: updateIfPresent(m_sftpConfigModel, containerConfig.getSftpProtocolConfig()); break;
|
||||
case Proto::Socks5Proxy: updateIfPresent(m_socks5ConfigModel, containerConfig.getSocks5ProxyProtocolConfig()); break;
|
||||
case Proto::MtProxy: updateIfPresent(m_mtProxyConfigModel, containerConfig.getMtProxyProtocolConfig()); break;
|
||||
case Proto::Telemt: updateIfPresent(m_telemtConfigModel, containerConfig.getTelemtProtocolConfig()); break;
|
||||
#ifdef Q_OS_WINDOWS
|
||||
case Proto::Ikev2: updateIfPresent(m_ikev2ConfigModel, containerConfig.getIkev2ProtocolConfig()); break;
|
||||
#endif
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "core/models/protocols/sftpProtocolConfig.h"
|
||||
#include "core/models/protocols/socks5ProxyProtocolConfig.h"
|
||||
#include "ui/models/services/mtProxyConfigModel.h"
|
||||
#include "ui/models/services/telemtConfigModel.h"
|
||||
|
||||
class InstallUiController : public QObject
|
||||
{
|
||||
@@ -50,6 +51,7 @@ public:
|
||||
SftpConfigModel* sftpConfigModel,
|
||||
Socks5ProxyConfigModel* socks5ConfigModel,
|
||||
MtProxyConfigModel* mtConfigModel,
|
||||
TelemtConfigModel* telemtConfigModel,
|
||||
QObject *parent = nullptr);
|
||||
~InstallUiController();
|
||||
|
||||
@@ -152,6 +154,7 @@ private:
|
||||
SftpConfigModel* m_sftpConfigModel;
|
||||
Socks5ProxyConfigModel* m_socks5ConfigModel;
|
||||
MtProxyConfigModel* m_mtProxyConfigModel;
|
||||
TelemtConfigModel* m_telemtConfigModel;
|
||||
|
||||
ServerCredentials m_processedServerCredentials;
|
||||
|
||||
|
||||
@@ -484,6 +484,8 @@ QStringList ServersUiController::getAllInstalledServicesName(int serverIndex) co
|
||||
servicesName.append("SOCKS5");
|
||||
} else if (container == DockerContainer::MtProxy) {
|
||||
servicesName.append("MTProxy");
|
||||
} else if (container == DockerContainer::Telemt) {
|
||||
servicesName.append("Telemt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ QVariant ContainersModel::data(const QModelIndex &index, int role) const
|
||||
case IsTorWebsiteRole: return container == DockerContainer::TorWebSite;
|
||||
case IsSocks5ProxyRole: return container == DockerContainer::Socks5Proxy;
|
||||
case IsMtProxyRole: return container == DockerContainer::MtProxy;
|
||||
case IsTelemtRole: return container == DockerContainer::Telemt;
|
||||
case InstallPageOrderRole: return ContainerUtils::installPageOrder(container);
|
||||
}
|
||||
|
||||
@@ -186,5 +187,6 @@ QHash<int, QByteArray> ContainersModel::roleNames() const
|
||||
roles[IsTorWebsiteRole] = "isTorWebsite";
|
||||
roles[IsSocks5ProxyRole] = "isSocks5Proxy";
|
||||
roles[IsMtProxyRole] = "isMtProxy";
|
||||
roles[IsTelemtRole] = "isTelemt";
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
IsTorWebsiteRole,
|
||||
IsSocks5ProxyRole,
|
||||
IsMtProxyRole,
|
||||
IsTelemtRole,
|
||||
};
|
||||
|
||||
Q_INVOKABLE void openContainerSettings(int containerIndex);
|
||||
|
||||
@@ -43,6 +43,7 @@ QHash<int, QByteArray> ProtocolsModel::roleNames() const
|
||||
roles[IsIpsecRole] = "isIpsec";
|
||||
roles[IsSocks5ProxyRole] = "isSocks5Proxy";
|
||||
roles[IsMtProxyRole] = "isMtProxy";
|
||||
roles[IsTelemtRole] = "isTelemt";
|
||||
|
||||
return roles;
|
||||
}
|
||||
@@ -73,6 +74,7 @@ QVariant ProtocolsModel::data(const QModelIndex &index, int role) const
|
||||
case IsIpsecRole: return proto == Proto::Ikev2;
|
||||
case IsSocks5ProxyRole: return proto == Proto::Socks5Proxy;
|
||||
case IsMtProxyRole: return proto == Proto::MtProxy;
|
||||
case IsTelemtRole: return proto == Proto::Telemt;
|
||||
case RawConfigRole:
|
||||
return getRawConfig();
|
||||
case IsClientProtocolExistsRole:
|
||||
@@ -127,6 +129,7 @@ PageLoader::PageEnum ProtocolsModel::serverProtocolPage(Proto protocol) const
|
||||
case Proto::Sftp: return PageLoader::PageEnum::PageServiceSftpSettings;
|
||||
case Proto::Socks5Proxy: return PageLoader::PageEnum::PageServiceSocksProxySettings;
|
||||
case Proto::MtProxy: return PageLoader::PageEnum::PageServiceMtProxySettings;
|
||||
case Proto::Telemt: return PageLoader::PageEnum::PageServiceTelemtSettings;
|
||||
default: return PageLoader::PageEnum::PageProtocolOpenVpnSettings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
IsIpsecRole,
|
||||
IsSocks5ProxyRole,
|
||||
IsMtProxyRole,
|
||||
IsTelemtRole,
|
||||
};
|
||||
|
||||
explicit ProtocolsModel(QObject *parent = nullptr);
|
||||
|
||||
406
client/ui/models/services/telemtConfigModel.cpp
Normal file
406
client/ui/models/services/telemtConfigModel.cpp
Normal file
@@ -0,0 +1,406 @@
|
||||
#include "telemtConfigModel.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "core/utils/qrCodeUtils.h"
|
||||
#include "core/utils/constants/configKeys.h"
|
||||
#include "core/utils/constants/protocolConstants.h"
|
||||
#include "qrcodegen.hpp"
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
TelemtConfigModel::TelemtConfigModel(QObject *parent) : QAbstractListModel(parent) {}
|
||||
|
||||
void TelemtConfigModel::applyDefaults(TelemtProtocolConfig &c) {
|
||||
if (c.port.isEmpty()) {
|
||||
c.port = QString::fromUtf8(protocols::telemt::defaultPort);
|
||||
}
|
||||
if (c.transportMode.isEmpty()) {
|
||||
c.transportMode = QString::fromUtf8(protocols::telemt::transportModeStandard);
|
||||
}
|
||||
if (c.workersMode.isEmpty()) {
|
||||
c.workersMode = QString::fromUtf8(protocols::telemt::workersModeAuto);
|
||||
}
|
||||
if (c.workers.isEmpty()) {
|
||||
c.workers = QString::fromUtf8(protocols::telemt::defaultWorkers);
|
||||
}
|
||||
if (c.userName.isEmpty()) {
|
||||
c.userName = QString::fromUtf8(protocols::telemt::defaultUserName);
|
||||
}
|
||||
}
|
||||
|
||||
int TelemtConfigModel::rowCount(const QModelIndex &parent) const {
|
||||
Q_UNUSED(parent);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool TelemtConfigModel::setData(const QModelIndex &index, const QVariant &value, int role) {
|
||||
if (!index.isValid() || index.row() != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Roles::PortRole: {
|
||||
m_protocolConfig.port = value.toString();
|
||||
break;
|
||||
}
|
||||
case Roles::SecretRole: {
|
||||
m_protocolConfig.secret = value.toString();
|
||||
break;
|
||||
}
|
||||
case Roles::TagRole: {
|
||||
m_protocolConfig.tag = value.toString();
|
||||
break;
|
||||
}
|
||||
case Roles::IsEnabledRole: {
|
||||
m_protocolConfig.isEnabled = value.toBool();
|
||||
break;
|
||||
}
|
||||
case Roles::PublicHostRole: {
|
||||
m_protocolConfig.publicHost = value.toString();
|
||||
break;
|
||||
}
|
||||
case Roles::TransportModeRole: {
|
||||
m_protocolConfig.transportMode = value.toString();
|
||||
break;
|
||||
}
|
||||
case Roles::TlsDomainRole: {
|
||||
m_protocolConfig.tlsDomain = value.toString();
|
||||
break;
|
||||
}
|
||||
case Roles::AdditionalSecretsRole: {
|
||||
m_protocolConfig.additionalSecrets = value.toStringList();
|
||||
break;
|
||||
}
|
||||
case Roles::WorkersModeRole: {
|
||||
m_protocolConfig.workersMode = value.toString();
|
||||
break;
|
||||
}
|
||||
case Roles::WorkersRole: {
|
||||
m_protocolConfig.workers = value.toString();
|
||||
break;
|
||||
}
|
||||
case Roles::NatEnabledRole: {
|
||||
m_protocolConfig.natEnabled = value.toBool();
|
||||
break;
|
||||
}
|
||||
case Roles::NatInternalIpRole: {
|
||||
m_protocolConfig.natInternalIp = value.toString();
|
||||
break;
|
||||
}
|
||||
case Roles::NatExternalIpRole: {
|
||||
m_protocolConfig.natExternalIp = value.toString();
|
||||
break;
|
||||
}
|
||||
case Roles::MaskEnabledRole: {
|
||||
m_protocolConfig.maskEnabled = value.toBool();
|
||||
break;
|
||||
}
|
||||
case Roles::UseMiddleProxyRole: {
|
||||
m_protocolConfig.useMiddleProxy = value.toBool();
|
||||
break;
|
||||
}
|
||||
case Roles::TlsEmulationRole: {
|
||||
m_protocolConfig.tlsEmulation = value.toBool();
|
||||
break;
|
||||
}
|
||||
case Roles::UserNameRole: {
|
||||
m_protocolConfig.userName = value.toString();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
emit dataChanged(index, index, QList{role});
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant TelemtConfigModel::data(const QModelIndex &index, int role) const {
|
||||
if (!index.isValid() || index.row() != 0) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Roles::PortRole: {
|
||||
return m_protocolConfig.port.isEmpty() ? QString::fromUtf8(protocols::telemt::defaultPort)
|
||||
: m_protocolConfig.port;
|
||||
}
|
||||
case Roles::SecretRole: {
|
||||
return m_protocolConfig.secret;
|
||||
}
|
||||
case Roles::TagRole: {
|
||||
return m_protocolConfig.tag;
|
||||
}
|
||||
case Roles::TgLinkRole: {
|
||||
return m_protocolConfig.tgLink;
|
||||
}
|
||||
case Roles::TmeLinkRole: {
|
||||
return m_protocolConfig.tmeLink;
|
||||
}
|
||||
case Roles::IsEnabledRole: {
|
||||
return m_protocolConfig.isEnabled;
|
||||
}
|
||||
case Roles::PublicHostRole: {
|
||||
return m_protocolConfig.publicHost.isEmpty() ? m_fullConfig.value(QString(configKey::hostName)).toString()
|
||||
: m_protocolConfig.publicHost;
|
||||
}
|
||||
case Roles::TransportModeRole: {
|
||||
return m_protocolConfig.transportMode.isEmpty() ? QString::fromUtf8(
|
||||
protocols::telemt::transportModeStandard)
|
||||
: m_protocolConfig.transportMode;
|
||||
}
|
||||
case Roles::TlsDomainRole: {
|
||||
return m_protocolConfig.tlsDomain;
|
||||
}
|
||||
case Roles::AdditionalSecretsRole: {
|
||||
return m_protocolConfig.additionalSecrets;
|
||||
}
|
||||
case Roles::WorkersModeRole: {
|
||||
return m_protocolConfig.workersMode.isEmpty() ? QString::fromUtf8(protocols::telemt::workersModeAuto)
|
||||
: m_protocolConfig.workersMode;
|
||||
}
|
||||
case Roles::WorkersRole: {
|
||||
return m_protocolConfig.workers.isEmpty() ? QString::fromUtf8(protocols::telemt::defaultWorkers)
|
||||
: m_protocolConfig.workers;
|
||||
}
|
||||
case Roles::NatEnabledRole: {
|
||||
return m_protocolConfig.natEnabled;
|
||||
}
|
||||
case Roles::NatInternalIpRole: {
|
||||
return m_protocolConfig.natInternalIp;
|
||||
}
|
||||
case Roles::NatExternalIpRole: {
|
||||
return m_protocolConfig.natExternalIp;
|
||||
}
|
||||
case Roles::MaskEnabledRole: {
|
||||
return m_protocolConfig.maskEnabled;
|
||||
}
|
||||
case Roles::UseMiddleProxyRole: {
|
||||
return m_protocolConfig.useMiddleProxy;
|
||||
}
|
||||
case Roles::TlsEmulationRole: {
|
||||
return m_protocolConfig.tlsEmulation;
|
||||
}
|
||||
case Roles::UserNameRole: {
|
||||
return m_protocolConfig.userName.isEmpty() ? QString::fromUtf8(protocols::telemt::defaultUserName)
|
||||
: m_protocolConfig.userName;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void TelemtConfigModel::updateModel(DockerContainer container, const TelemtProtocolConfig &protocolConfig) {
|
||||
beginResetModel();
|
||||
m_container = container;
|
||||
m_protocolConfig = protocolConfig;
|
||||
applyDefaults(m_protocolConfig);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void TelemtConfigModel::updateModel(const QJsonObject &config) {
|
||||
beginResetModel();
|
||||
|
||||
m_fullConfig = config;
|
||||
m_protocolConfig = TelemtProtocolConfig::fromJson(config.value(QString(configKey::telemt)).toObject());
|
||||
applyDefaults(m_protocolConfig);
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QJsonObject TelemtConfigModel::getConfig() {
|
||||
m_fullConfig.insert(QString(configKey::telemt), m_protocolConfig.toJson());
|
||||
return m_fullConfig;
|
||||
}
|
||||
|
||||
TelemtProtocolConfig TelemtConfigModel::getProtocolConfig() {
|
||||
return m_protocolConfig;
|
||||
}
|
||||
|
||||
void TelemtConfigModel::generateSecret() {
|
||||
QString secret;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
quint32 byte = QRandomGenerator::global()->bounded(256);
|
||||
secret += QString("%1").arg(byte, 2, 16, QChar('0'));
|
||||
}
|
||||
|
||||
m_protocolConfig.secret = secret;
|
||||
emit dataChanged(index(0), index(0), QList<int>{SecretRole});
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setSecret(const QString &secret) {
|
||||
if (secret.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
setData(index(0), secret, SecretRole);
|
||||
}
|
||||
|
||||
bool TelemtConfigModel::validateAndSetSecret(const QString &rawSecret) {
|
||||
if (!QRegularExpression(QStringLiteral("^[0-9a-fA-F]{32}$")).match(rawSecret).hasMatch()) {
|
||||
return false;
|
||||
}
|
||||
setData(index(0), rawSecret, SecretRole);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setPort(const QString &port) {
|
||||
setData(index(0), port, PortRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setTag(const QString &tag) {
|
||||
setData(index(0), tag, TagRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setPublicHost(const QString &host) {
|
||||
setData(index(0), host, PublicHostRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setTransportMode(const QString &mode) {
|
||||
setData(index(0), mode, TransportModeRole);
|
||||
}
|
||||
|
||||
QString TelemtConfigModel::getTransportMode() const {
|
||||
return m_protocolConfig.transportMode.isEmpty() ? QString::fromUtf8(protocols::telemt::transportModeStandard)
|
||||
: m_protocolConfig.transportMode;
|
||||
}
|
||||
|
||||
QString TelemtConfigModel::getTlsDomain() const {
|
||||
return m_protocolConfig.tlsDomain.isEmpty() ? QString::fromUtf8(protocols::telemt::defaultTlsDomain)
|
||||
: m_protocolConfig.tlsDomain;
|
||||
}
|
||||
|
||||
QString TelemtConfigModel::getPublicHost() const {
|
||||
return m_protocolConfig.publicHost;
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setTlsDomain(const QString &domain) {
|
||||
setData(index(0), domain, TlsDomainRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setWorkersMode(const QString &mode) {
|
||||
setData(index(0), mode, WorkersModeRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setWorkers(const QString &workers) {
|
||||
setData(index(0), workers, WorkersRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setNatEnabled(bool enabled) {
|
||||
setData(index(0), enabled, NatEnabledRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setNatInternalIp(const QString &ip) {
|
||||
setData(index(0), ip, NatInternalIpRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setNatExternalIp(const QString &ip) {
|
||||
setData(index(0), ip, NatExternalIpRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setMaskEnabled(bool enabled) {
|
||||
setData(index(0), enabled, MaskEnabledRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setUseMiddleProxy(bool enabled) {
|
||||
setData(index(0), enabled, UseMiddleProxyRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setTlsEmulation(bool enabled) {
|
||||
setData(index(0), enabled, TlsEmulationRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setUserName(const QString &name) {
|
||||
setData(index(0), name, UserNameRole);
|
||||
}
|
||||
|
||||
void TelemtConfigModel::addAdditionalSecret() {
|
||||
QString newSecret;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
quint32 byte = QRandomGenerator::global()->bounded(256);
|
||||
newSecret += QString("%1").arg(byte, 2, 16, QChar('0'));
|
||||
}
|
||||
|
||||
m_protocolConfig.additionalSecrets.append(newSecret);
|
||||
emit dataChanged(index(0), index(0), QList<int>{AdditionalSecretsRole});
|
||||
}
|
||||
|
||||
void TelemtConfigModel::removeAdditionalSecret(int idx) {
|
||||
if (idx < 0 || idx >= m_protocolConfig.additionalSecrets.size()) {
|
||||
return;
|
||||
}
|
||||
m_protocolConfig.additionalSecrets.removeAt(idx);
|
||||
emit dataChanged(index(0), index(0), QList<int>{AdditionalSecretsRole});
|
||||
}
|
||||
|
||||
void TelemtConfigModel::setEnabled(bool enabled) {
|
||||
m_protocolConfig.isEnabled = enabled;
|
||||
emit dataChanged(index(0), index(0), QList<int>{IsEnabledRole});
|
||||
}
|
||||
|
||||
QString TelemtConfigModel::generateQrCode(const QString &text) {
|
||||
if (text.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
auto qr = qrCodeUtils::generateQrCode(text.toUtf8());
|
||||
return qrCodeUtils::svgToBase64(QString::fromStdString(toSvgString(qr, 1)));
|
||||
}
|
||||
|
||||
QString TelemtConfigModel::defaultTlsDomain() const {
|
||||
return QString::fromUtf8(protocols::telemt::defaultTlsDomain);
|
||||
}
|
||||
|
||||
QString TelemtConfigModel::defaultPort() const {
|
||||
return QString::fromUtf8(protocols::telemt::defaultPort);
|
||||
}
|
||||
|
||||
QString TelemtConfigModel::defaultWorkers() const {
|
||||
return QString::fromUtf8(protocols::telemt::defaultWorkers);
|
||||
}
|
||||
|
||||
int TelemtConfigModel::maxWorkers() const {
|
||||
return protocols::telemt::maxWorkers;
|
||||
}
|
||||
|
||||
QString TelemtConfigModel::transportModeStandard() const {
|
||||
return QString::fromUtf8(protocols::telemt::transportModeStandard);
|
||||
}
|
||||
|
||||
QString TelemtConfigModel::transportModeFakeTLS() const {
|
||||
return QString::fromUtf8(protocols::telemt::transportModeFakeTLS);
|
||||
}
|
||||
|
||||
QString TelemtConfigModel::workersModeAuto() const {
|
||||
return QString::fromUtf8(protocols::telemt::workersModeAuto);
|
||||
}
|
||||
|
||||
QString TelemtConfigModel::workersModeManual() const {
|
||||
return QString::fromUtf8(protocols::telemt::workersModeManual);
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> TelemtConfigModel::roleNames() const {
|
||||
QHash<int, QByteArray> roles;
|
||||
|
||||
roles[PortRole] = "port";
|
||||
roles[SecretRole] = "secret";
|
||||
roles[TagRole] = "tag";
|
||||
roles[TgLinkRole] = "tgLink";
|
||||
roles[TmeLinkRole] = "tmeLink";
|
||||
roles[IsEnabledRole] = "isEnabled";
|
||||
roles[PublicHostRole] = "publicHost";
|
||||
roles[TransportModeRole] = "transportMode";
|
||||
roles[TlsDomainRole] = "tlsDomain";
|
||||
roles[AdditionalSecretsRole] = "additionalSecrets";
|
||||
roles[WorkersModeRole] = "workersMode";
|
||||
roles[WorkersRole] = "workers";
|
||||
roles[NatEnabledRole] = "natEnabled";
|
||||
roles[NatInternalIpRole] = "natInternalIp";
|
||||
roles[NatExternalIpRole] = "natExternalIp";
|
||||
roles[MaskEnabledRole] = "maskEnabled";
|
||||
roles[UseMiddleProxyRole] = "useMiddleProxy";
|
||||
roles[TlsEmulationRole] = "tlsEmulation";
|
||||
roles[UserNameRole] = "userName";
|
||||
|
||||
return roles;
|
||||
}
|
||||
130
client/ui/models/services/telemtConfigModel.h
Normal file
130
client/ui/models/services/telemtConfigModel.h
Normal file
@@ -0,0 +1,130 @@
|
||||
#ifndef TELEMTCONFIGMODEL_H
|
||||
#define TELEMTCONFIGMODEL_H
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QJsonObject>
|
||||
#include <QRandomGenerator>
|
||||
|
||||
#include "core/models/protocols/telemtProtocolConfig.h"
|
||||
#include "core/utils/containerEnum.h"
|
||||
|
||||
class TelemtConfigModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
PortRole = Qt::UserRole + 1,
|
||||
SecretRole,
|
||||
TagRole,
|
||||
TgLinkRole,
|
||||
TmeLinkRole,
|
||||
IsEnabledRole,
|
||||
PublicHostRole,
|
||||
TransportModeRole,
|
||||
TlsDomainRole,
|
||||
AdditionalSecretsRole,
|
||||
WorkersModeRole,
|
||||
WorkersRole,
|
||||
NatEnabledRole,
|
||||
NatInternalIpRole,
|
||||
NatExternalIpRole,
|
||||
MaskEnabledRole,
|
||||
UseMiddleProxyRole,
|
||||
TlsEmulationRole,
|
||||
UserNameRole
|
||||
};
|
||||
|
||||
explicit TelemtConfigModel(QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
public slots:
|
||||
|
||||
void updateModel(amnezia::DockerContainer container, const amnezia::TelemtProtocolConfig &protocolConfig);
|
||||
|
||||
void updateModel(const QJsonObject &config);
|
||||
|
||||
QJsonObject getConfig();
|
||||
|
||||
amnezia::TelemtProtocolConfig getProtocolConfig();
|
||||
|
||||
Q_INVOKABLE void generateSecret();
|
||||
|
||||
Q_INVOKABLE void setSecret(const QString &secret);
|
||||
|
||||
Q_INVOKABLE bool validateAndSetSecret(const QString &rawSecret);
|
||||
|
||||
Q_INVOKABLE void setPort(const QString &port);
|
||||
|
||||
Q_INVOKABLE void setTag(const QString &tag);
|
||||
|
||||
Q_INVOKABLE void setPublicHost(const QString &host);
|
||||
|
||||
Q_INVOKABLE void setTransportMode(const QString &mode);
|
||||
|
||||
Q_INVOKABLE QString getTransportMode() const;
|
||||
|
||||
Q_INVOKABLE QString getTlsDomain() const;
|
||||
|
||||
Q_INVOKABLE QString getPublicHost() const;
|
||||
|
||||
Q_INVOKABLE void setTlsDomain(const QString &domain);
|
||||
|
||||
Q_INVOKABLE void setWorkersMode(const QString &mode);
|
||||
|
||||
Q_INVOKABLE void setWorkers(const QString &workers);
|
||||
|
||||
Q_INVOKABLE void setNatEnabled(bool enabled);
|
||||
|
||||
Q_INVOKABLE void setNatInternalIp(const QString &ip);
|
||||
|
||||
Q_INVOKABLE void setNatExternalIp(const QString &ip);
|
||||
|
||||
Q_INVOKABLE void addAdditionalSecret();
|
||||
|
||||
Q_INVOKABLE void removeAdditionalSecret(int idx);
|
||||
|
||||
Q_INVOKABLE QString generateQrCode(const QString &text);
|
||||
|
||||
Q_INVOKABLE void setEnabled(bool enabled);
|
||||
|
||||
Q_INVOKABLE void setMaskEnabled(bool enabled);
|
||||
|
||||
Q_INVOKABLE void setUseMiddleProxy(bool enabled);
|
||||
|
||||
Q_INVOKABLE void setTlsEmulation(bool enabled);
|
||||
|
||||
Q_INVOKABLE void setUserName(const QString &name);
|
||||
|
||||
Q_INVOKABLE QString defaultTlsDomain() const;
|
||||
|
||||
Q_INVOKABLE QString defaultPort() const;
|
||||
|
||||
Q_INVOKABLE QString defaultWorkers() const;
|
||||
|
||||
Q_INVOKABLE int maxWorkers() const;
|
||||
|
||||
Q_INVOKABLE QString transportModeStandard() const;
|
||||
|
||||
Q_INVOKABLE QString transportModeFakeTLS() const;
|
||||
|
||||
Q_INVOKABLE QString workersModeAuto() const;
|
||||
|
||||
Q_INVOKABLE QString workersModeManual() const;
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
static void applyDefaults(amnezia::TelemtProtocolConfig &c);
|
||||
|
||||
amnezia::DockerContainer m_container = amnezia::DockerContainer::None;
|
||||
QJsonObject m_fullConfig;
|
||||
amnezia::TelemtProtocolConfig m_protocolConfig;
|
||||
};
|
||||
|
||||
#endif // TELEMTCONFIGMODEL_H
|
||||
@@ -48,6 +48,9 @@ ListViewType {
|
||||
} else if (isMtProxy) {
|
||||
MtProxyConfigModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageServiceMtProxySettings)
|
||||
} else if (isTelemt) {
|
||||
TelemtConfigModel.updateModel(config)
|
||||
PageController.goToPage(PageEnum.PageServiceTelemtSettings)
|
||||
} else {
|
||||
InstallController.updateProtocols(ServersUiController.processedIndex, containerIndex)
|
||||
PageController.goToPage(PageEnum.PageSettingsServerProtocol)
|
||||
|
||||
1447
client/ui/qml/Pages2/PageServiceTelemtSettings.qml
Normal file
1447
client/ui/qml/Pages2/PageServiceTelemtSettings.qml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -79,6 +79,7 @@
|
||||
<file>Pages2/PageProtocolXraySettings.qml</file>
|
||||
<file>Pages2/PageServiceDnsSettings.qml</file>
|
||||
<file>Pages2/PageServiceMtProxySettings.qml</file>
|
||||
<file>Pages2/PageServiceTelemtSettings.qml</file>
|
||||
<file>Pages2/PageServiceSftpSettings.qml</file>
|
||||
<file>Pages2/PageServiceSocksProxySettings.qml</file>
|
||||
<file>Pages2/PageServiceTorWebsiteSettings.qml</file>
|
||||
|
||||
Reference in New Issue
Block a user