diff --git a/client/core/local-proxy/configmanager.cpp b/client/core/local-proxy/configmanager.cpp index ea1935ff0..feeb78a32 100644 --- a/client/core/local-proxy/configmanager.cpp +++ b/client/core/local-proxy/configmanager.cpp @@ -9,6 +9,7 @@ #include "settings.h" #include "version.h" +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include ConfigManager::ConfigManager(const std::shared_ptr &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) { @@ -48,14 +52,16 @@ int resolveProxyPort(const std::shared_ptr &settings) } const quint16 port = settings->localProxyPort(); - if (port < 1024 || port > 65535) { + if (port < kProxyPortMin || port > kProxyPortMax) { return kDefaultProxyPort; } return static_cast(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(port)); + server.close(); + return success; +} std::optional ConfigManager::buildConfig(QString &errorDescription) const { @@ -204,9 +221,31 @@ std::optional 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(selectedPort)) { + m_settings->setLocalProxyPort(static_cast(selectedPort)); + } } else { ProxyLogger::getInstance().warning(QStringLiteral("Failed to override local proxy inbound port; using original config")); data.serializedConfig = *serializedConfig; diff --git a/client/core/local-proxy/configmanager.h b/client/core/local-proxy/configmanager.h index 261a7345f..590cb3846 100644 --- a/client/core/local-proxy/configmanager.h +++ b/client/core/local-proxy/configmanager.h @@ -30,6 +30,9 @@ private: std::optional extractSerializedXrayConfig(const QJsonObject &server) const; std::optional 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 m_settings; }; \ No newline at end of file diff --git a/client/core/local-proxy/proxyservice.cpp b/client/core/local-proxy/proxyservice.cpp index 0650043b7..8e58072d3 100644 --- a/client/core/local-proxy/proxyservice.cpp +++ b/client/core/local-proxy/proxyservice.cpp @@ -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;