feat: enhance ConfigManager with dynamic proxy port resolution and availability check

- Added functionality to resolve and validate the local proxy port within a specified range.
- Implemented a method to check if a port is available before applying it to the configuration.
- Updated ProxyService to handle the new port resolution logic and cache the parsed configuration.
This commit is contained in:
aiamnezia
2026-01-27 12:45:38 +04:00
parent 5fab8363e7
commit e946ee2430
3 changed files with 54 additions and 8 deletions

View File

@@ -9,6 +9,7 @@
#include "settings.h"
#include "version.h"
#include <QHostAddress>
#include <QDir>
#include <QFile>
#include <QJsonArray>
@@ -17,6 +18,7 @@
#include <QSaveFile>
#include <QSysInfo>
#include <QStandardPaths>
#include <QTcpServer>
#include <QUuid>
ConfigManager::ConfigManager(const std::shared_ptr<Settings> &settings)
@@ -40,6 +42,8 @@ constexpr char vless[] = "vless";
} // namespace gateway_key
constexpr quint16 kDefaultProxyPort = 10808;
constexpr int kProxyPortMin = 1024;
constexpr int kProxyPortMax = 65535;
int resolveProxyPort(const std::shared_ptr<Settings> &settings)
{
@@ -48,14 +52,16 @@ int resolveProxyPort(const std::shared_ptr<Settings> &settings)
}
const quint16 port = settings->localProxyPort();
if (port < 1024 || port > 65535) {
if (port < kProxyPortMin || port > kProxyPortMax) {
return kDefaultProxyPort;
}
return static_cast<int>(port);
}
bool applyProxyPortToConfig(QJsonObject &config, int port)
} // namespace
bool ConfigManager::applyProxyPortToConfig(QJsonObject &config, int port) const
{
if (!config.contains("inbounds") || !config.value("inbounds").isArray()) {
return false;
@@ -73,11 +79,22 @@ bool applyProxyPortToConfig(QJsonObject &config, int port)
return true;
}
QString serializeConfig(const QJsonObject &config)
QString ConfigManager::serializeConfig(const QJsonObject &config) const
{
return QString::fromUtf8(QJsonDocument(config).toJson(QJsonDocument::Compact));
}
} // namespace
bool ConfigManager::isPortAvailable(int port) const
{
if (port < kProxyPortMin || port > kProxyPortMax) {
return false;
}
QTcpServer server;
const bool success = server.listen(QHostAddress::LocalHost, static_cast<quint16>(port));
server.close();
return success;
}
std::optional<ConfigManager::ConfigData> ConfigManager::buildConfig(QString &errorDescription) const
{
@@ -204,9 +221,31 @@ std::optional<ConfigManager::ConfigData> ConfigManager::buildConfigWithFetch(QSt
data.ownerUuid = ownerUuid;
data.serverName = ownerServer->value(amnezia::config_key::name).toString();
data.parsedConfig = doc.object();
const int proxyPort = resolveProxyPort(m_settings);
if (applyProxyPortToConfig(data.parsedConfig, proxyPort)) {
int selectedPort = resolveProxyPort(m_settings);
const int startPort = selectedPort;
bool found = false;
for (int port = selectedPort; port <= kProxyPortMax; ++port) {
if (isPortAvailable(port)) {
selectedPort = port;
found = true;
break;
}
}
if (!found) {
errorDescription = QStringLiteral("No available local proxy port in range %1-%2")
.arg(startPort)
.arg(kProxyPortMax);
ProxyLogger::getInstance().error(errorDescription);
return std::nullopt;
}
if (applyProxyPortToConfig(data.parsedConfig, selectedPort)) {
data.serializedConfig = serializeConfig(data.parsedConfig);
if (m_settings && m_settings->localProxyPort() != static_cast<quint16>(selectedPort)) {
m_settings->setLocalProxyPort(static_cast<quint16>(selectedPort));
}
} else {
ProxyLogger::getInstance().warning(QStringLiteral("Failed to override local proxy inbound port; using original config"));
data.serializedConfig = *serializedConfig;

View File

@@ -30,6 +30,9 @@ private:
std::optional<QString> extractSerializedXrayConfig(const QJsonObject &server) const;
std::optional<QString> fetchSerializedXrayConfigFromGateway(const QJsonObject &server, QString &errorDescription) const;
QString tempDirectory() const;
bool applyProxyPortToConfig(QJsonObject &config, int port) const;
QString serializeConfig(const QJsonObject &config) const;
bool isPortAvailable(int port) const;
std::shared_ptr<Settings> m_settings;
};

View File

@@ -42,6 +42,11 @@ bool ProxyService::startXray()
{
ProxyLogger::getInstance().info("Starting Xray");
if (m_xrayController->isXrayRunning()) {
ProxyLogger::getInstance().info("Xray is already running");
return true;
}
QString error;
const auto configData = m_configManager->buildConfigWithFetch(error);
if (!configData) {
@@ -49,10 +54,9 @@ bool ProxyService::startXray()
return false;
}
m_cachedConfig = configData->parsedConfig;
const bool success = m_xrayController->start(configData->serializedConfig);
if (success) {
m_cachedConfig = configData->parsedConfig;
ProxyLogger::getInstance().info("Xray started successfully");
emit xrayStatusChanged(true);
return true;