Compare commits

..

9 Commits

Author SHA1 Message Date
MrMirDan ce6d926f48 protected connectionUiController 2026-07-01 15:27:31 +03:00
Yaroslav Gurov 890103a16a fix: update amneziawg (#2743)
* chore(conan): update amneziawg

* fix(conan): use cmake 4.2+ to support MSVC26

* fix(ci/cd): use the latest cmake generator available on windows
2026-06-17 19:56:53 +07:00
yp 56ab82f87f fix: Use shared OpenSSL on Android (#2736) 2026-06-16 10:57:32 +07:00
lunardunno 3984acbb44 feat: updating install_docker.sh script (#2661)
* Updating install_docker.sh script

Implementing a Docker service status check.
The Docker reinstall step has been removed due to the implementation of Docker service checking.
Implementing locale checking and assignment.
Implementation of execution of some actions through commands with sudo, to reduce delays caused by differences in the values ​​of the PATH variable for the root user and the user included in the sudo group.
Implementation of a verification step for the install containerization app to avoid installing unsupported podman-docker applications.

* adding message handling to install controller

Adding handling for "Containerization app is not supported" and "Service status not active" messages to the controller.

* Error Codes added

Error Codes added for ServerContainerizationNotSupported & DockerServiceNotActive

* Adding extended descriptions of new errors

* fix last line in errorCodes.h

* fix last line in errorStrings.cpp

* Changing the names of errors

* various changes in the script

The messages output for processing by the server controller have been changed: "Container runtime is not supported" and "Container runtime service is not running."
The redundant check and output of the "Packet manager not found" message, as well as the interruption of script execution, have been eliminated, as this situation is handled by the server controller at an earlier stage (check_server_is_busy.sh) and only there.
Added installation of the whish package if it is missing from the OS, for subsequent re-execution of the install_docker.sh and check_server_is_busy.sh scripts.
Implemented an alternative method for detecting the package manager if the whish package is initially missing from the OS.
The algorithm for setting the $pm variable (package manager) has been changed.

* processed phrases have been changed

The phrases processed by the server controller have been changed.

* Attempting to use "command -v"

Switching to using "command -v" instead of "which".

* "which" as main, "command" as backup.

* "which" as main, "command" as backup for check user

* which  LOCK_CMD with sudo

Run the "which" with sudo to check the $LOCK_CMD variable in case the user's PATH variable has incorrect values ​​if the user is not root and is only a member of the sudo group.

* suppressing sudo password prompt

* suppressing sudo password prompt

* suppressing sudo password prompt install_docker.sh

* Changing the phrase for check stdout

"sudo:" with "not found" instead of "command not found"

* Changing phrases for check stdout check_user_in_sudo.sh‎

* sudo|docker and not found, in one line

* check only sudoers
2026-06-15 22:28:38 +07:00
yp cc404378f9 fix: remove only amnezia- prefixed docker volumes (#2728) 2026-06-15 13:12:19 +07:00
yp 594635e5cf fix: script remove docker volume (#2686)
* move sudo docker volume rm -f

* fix: remove unnecessary function

---------

Co-authored-by: vkamn <vk@amnezia.org>
2026-06-04 22:58:39 +08:00
vkamn f9b106cf5b fix: various fixes (#2693)
* fix: fixed country model update

* fix: fixed context menu crush on ios

* fix: fixed passphrase dialog freeze

* fix: fixed country switch

* fix: fixed start minimized

* fix: fixed black screen after remove container

* refactor: return cloak and ss only for view

* fix: fixed default server change after improt while connected

* fix: divider visibility

* fix: fixed revoke admin user

* fix: fixed language restore after backup

* fix: link hover for tor settings page

* fix: fixed openvpn connecntion status

* fix: fixed free color status

* fix: fixed client config update

* chore: bump version
2026-06-04 22:45:53 +08:00
yp a9861d18b7 fix: wrong index on xray pages (#2669)
* test crash xray

* fixed save config xray

* reset file

* fixed text port & reset file

* fixed textFieldWithHeaderType.textField
2026-06-01 12:22:54 +08:00
lunardunno c14138f031 fix: deleting volumes when cleaning the server (#2673)
* Deleting volumes when cleaning the server

* force the remove volumes
2026-06-01 11:54:34 +08:00
91 changed files with 808 additions and 825 deletions
+1 -1
View File
@@ -157,7 +157,7 @@ jobs:
run: pip install "conan==2.28.0"
- name: 'Build dependencies'
run: cmake -S . -B build -G "Visual Studio 17 2022" -DPREBUILTS_ONLY=1
run: cmake -S . -B build -DPREBUILTS_ONLY=1
- name: 'Authorize in remote'
if: github.ref == 'refs/heads/dev'
+2 -2
View File
@@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(PROJECT AmneziaVPN)
set(AMNEZIAVPN_VERSION 4.9.0.1)
set(AMNEZIAVPN_VERSION 4.9.0.2)
set(QT_CREATOR_SKIP_PACKAGE_MANAGER_SETUP ON CACHE BOOL "" FORCE)
set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES
@@ -28,7 +28,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 2122)
set(APP_ANDROID_VERSION_CODE 2123)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
@@ -88,68 +88,33 @@ open class Wireguard : Protocol() {
addDnsServer(parseInetAddress(dns.trim()))
}
val defRoutes = hashSetOf(
InetNetwork("0.0.0.0", 0),
InetNetwork("::", 0)
)
val routes = hashSetOf<InetNetwork>()
configData.getJSONArray("allowed_ips").asSequence<String>().map { route ->
InetNetwork.parse(route.trim())
}.forEach(routes::add)
// if the allowed IPs list contains at least one non-default route, disable global split tunneling
if (routes.any { it !in defRoutes }) disableSplitTunneling()
addRoutes(routes)
configData.optStringOrNull("mtu")?.let { setMtu(it.toInt()) }
configData.getString("client_priv_key").let { setPrivateKeyHex(it.base64ToHex()) }
val host = configData.getString("hostName").let { parseInetAddress(it.trim()) }
val port = configData.getInt("port")
setEndpoint(InetEndpoint(host, port))
if (configData.optBoolean("isObfuscationEnabled")) {
setUseProtocolExtension(true)
configExtensionParameters(configData)
}
val defRoutes = hashSetOf(InetNetwork("0.0.0.0", 0), InetNetwork("::", 0))
val peersArray = configData.optJSONArray("peers")
if (peersArray != null && peersArray.length() > 0) {
// Multi-peer: collect union of all peers' allowed IPs for the VPN interface routing table
val allRoutes = hashSetOf<InetNetwork>()
for (i in 0 until peersArray.length()) {
peersArray.getJSONObject(i).getJSONArray("allowed_ips").asSequence<String>()
.map { InetNetwork.parse(it.trim()) }.forEach(allRoutes::add)
}
if (allRoutes.any { it !in defRoutes }) disableSplitTunneling()
addRoutes(allRoutes)
// Primary peer from first entry
val firstPeer = peersArray.getJSONObject(0)
val firstAllowedIps = firstPeer.getJSONArray("allowed_ips").asSequence<String>()
.map { InetNetwork.parse(it.trim()) }.toList()
setPeerAllowedIps(firstAllowedIps)
setEndpoint(InetEndpoint(parseInetAddress(firstPeer.getString("hostName").trim()), firstPeer.getInt("port")))
firstPeer.optStringOrNull("persistent_keep_alive")?.let { setPersistentKeepalive(it.toInt()) }
firstPeer.getString("server_pub_key").let { setPublicKeyHex(it.base64ToHex()) }
firstPeer.optStringOrNull("psk_key")?.let { setPreSharedKeyHex(it.base64ToHex()) }
// Additional peers
for (i in 1 until peersArray.length()) {
val peerData = peersArray.getJSONObject(i)
val peerAllowedIps = peerData.getJSONArray("allowed_ips").asSequence<String>()
.map { InetNetwork.parse(it.trim()) }.toList()
addPeer(
PeerConfig(
publicKeyHex = peerData.getString("server_pub_key").base64ToHex(),
preSharedKeyHex = peerData.optStringOrNull("psk_key")?.base64ToHex(),
persistentKeepalive = peerData.optStringOrNull("persistent_keep_alive")?.toInt() ?: 0,
endpoint = InetEndpoint(parseInetAddress(peerData.getString("hostName").trim()), peerData.getInt("port")),
allowedIps = peerAllowedIps
)
)
}
} else {
// Single peer (original behavior)
val routes = hashSetOf<InetNetwork>()
configData.getJSONArray("allowed_ips").asSequence<String>().map { route ->
InetNetwork.parse(route.trim())
}.forEach(routes::add)
if (routes.any { it !in defRoutes }) disableSplitTunneling()
addRoutes(routes)
val host = configData.getString("hostName").let { parseInetAddress(it.trim()) }
val port = configData.getInt("port")
setEndpoint(InetEndpoint(host, port))
configData.optStringOrNull("persistent_keep_alive")?.let { setPersistentKeepalive(it.toInt()) }
configData.getString("server_pub_key").let { setPublicKeyHex(it.base64ToHex()) }
configData.optStringOrNull("psk_key")?.let { setPreSharedKeyHex(it.base64ToHex()) }
}
configData.optStringOrNull("persistent_keep_alive")?.let { setPersistentKeepalive(it.toInt()) }
configData.getString("client_priv_key").let { setPrivateKeyHex(it.base64ToHex()) }
configData.getString("server_pub_key").let { setPublicKeyHex(it.base64ToHex()) }
configData.optStringOrNull("psk_key")?.let { setPreSharedKeyHex(it.base64ToHex()) }
}
protected fun WireguardConfig.Builder.configExtensionParameters(configData: JSONObject) {
@@ -236,11 +201,7 @@ open class Wireguard : Protocol() {
Log.e(TAG, "Failed to get tunnel config")
return -2
}
// For multi-peer: take the max handshake time across all peers (any connected peer = tunnel active)
val lastHandshake = config.lines()
.filter { it.startsWith("last_handshake_time_sec=") }
.mapNotNull { it.substring(24).toLongOrNull() }
.maxOrNull()
val lastHandshake = config.lines().find { it.startsWith("last_handshake_time_sec=") }?.substring(24)?.toLong()
if (lastHandshake == null) {
Log.e(TAG, "Failed to get last_handshake_time_sec")
return -2
@@ -4,18 +4,9 @@ import android.util.Base64
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.ProtocolConfig
import org.amnezia.vpn.util.net.InetEndpoint
import org.amnezia.vpn.util.net.InetNetwork
private const val WIREGUARD_DEFAULT_MTU = 1280
data class PeerConfig(
val publicKeyHex: String,
val preSharedKeyHex: String?,
val persistentKeepalive: Int,
val endpoint: InetEndpoint,
val allowedIps: List<InetNetwork>
)
open class WireguardConfig protected constructor(
protocolConfigBuilder: ProtocolConfig.Builder,
val endpoint: InetEndpoint,
@@ -40,8 +31,6 @@ open class WireguardConfig protected constructor(
var i3: String?,
var i4: String?,
var i5: String?,
val peerAllowedIps: List<InetNetwork>?,
val additionalPeers: List<PeerConfig>,
) : ProtocolConfig(protocolConfigBuilder) {
protected constructor(builder: Builder) : this(
@@ -68,8 +57,6 @@ open class WireguardConfig protected constructor(
builder.i3,
builder.i4,
builder.i5,
builder.peerAllowedIps,
builder.additionalPeers.toList(),
)
fun toWgUserspaceString(): String = with(StringBuilder()) {
@@ -116,22 +103,14 @@ open class WireguardConfig protected constructor(
open fun appendPeerLine(sb: StringBuilder) = with(sb) {
appendLine("public_key=$publicKeyHex")
val primaryIps = peerAllowedIps ?: routes.filter { it.include }.map { it.inetNetwork }
primaryIps.forEach { net -> appendLine("allowed_ip=$net") }
routes.filter { it.include }.forEach { route ->
appendLine("allowed_ip=${route.inetNetwork}")
}
appendLine("endpoint=$endpoint")
if (persistentKeepalive != 0)
appendLine("persistent_keepalive_interval=$persistentKeepalive")
if (preSharedKeyHex != null)
appendLine("preshared_key=$preSharedKeyHex")
for (peer in additionalPeers) {
appendLine("public_key=${peer.publicKeyHex}")
peer.allowedIps.forEach { net -> appendLine("allowed_ip=$net") }
appendLine("endpoint=${peer.endpoint}")
if (peer.persistentKeepalive != 0)
appendLine("persistent_keepalive_interval=${peer.persistentKeepalive}")
if (peer.preSharedKeyHex != null)
appendLine("preshared_key=${peer.preSharedKeyHex}")
}
}
open class Builder : ProtocolConfig.Builder(true) {
@@ -171,9 +150,6 @@ open class WireguardConfig protected constructor(
internal var i4: String? = null
internal var i5: String? = null
internal var peerAllowedIps: List<InetNetwork>? = null
internal val additionalPeers: MutableList<PeerConfig> = mutableListOf()
fun setEndpoint(endpoint: InetEndpoint) = apply { this.endpoint = endpoint }
fun setPersistentKeepalive(persistentKeepalive: Int) = apply { this.persistentKeepalive = persistentKeepalive }
@@ -203,9 +179,6 @@ open class WireguardConfig protected constructor(
fun setI4(i4: String) = apply { this.i4 = i4 }
fun setI5(i5: String) = apply { this.i5 = i5 }
fun setPeerAllowedIps(ips: List<InetNetwork>) = apply { this.peerAllowedIps = ips }
fun addPeer(peer: PeerConfig) = apply { this.additionalPeers += peer }
override fun build(): WireguardConfig = configBuild().run { WireguardConfig(this@Builder) }
}
@@ -49,14 +49,92 @@ void ConnectionController::setConnectionState(Vpn::ConnectionState state)
}
}
ErrorCode ConnectionController::prepareConnection(const QString &serverId,
QJsonObject& vpnConfiguration,
DockerContainer& container)
ErrorCode ConnectionController::defaultContainerForServer(const QString &serverId, DockerContainer &container) const
{
const auto kind = m_serversRepository->serverKind(serverId);
switch (kind) {
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
const auto cfg = m_serversRepository->selfHostedAdminConfig(serverId);
if (!cfg.has_value()) {
return ErrorCode::InternalError;
}
container = cfg->defaultContainer;
return ErrorCode::NoError;
}
case serverConfigUtils::ConfigType::SelfHostedUser: {
const auto cfg = m_serversRepository->selfHostedUserConfig(serverId);
if (!cfg.has_value()) {
return ErrorCode::InternalError;
}
container = cfg->defaultContainer;
return ErrorCode::NoError;
}
case serverConfigUtils::ConfigType::Native: {
const auto cfg = m_serversRepository->nativeConfig(serverId);
if (!cfg.has_value()) {
return ErrorCode::InternalError;
}
container = cfg->defaultContainer;
return ErrorCode::NoError;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV2:
case serverConfigUtils::ConfigType::AmneziaFreeV3:
case serverConfigUtils::ConfigType::ExternalPremium: {
const auto cfg = m_serversRepository->apiV2Config(serverId);
if (!cfg.has_value()) {
return ErrorCode::InternalError;
}
container = cfg->defaultContainer;
return ErrorCode::NoError;
}
case serverConfigUtils::ConfigType::AmneziaPremiumV1:
case serverConfigUtils::ConfigType::AmneziaFreeV2:
return ErrorCode::LegacyApiV1NotSupportedError;
case serverConfigUtils::ConfigType::Invalid:
default:
return ErrorCode::InternalError;
}
}
ErrorCode ConnectionController::isConnectionSupported(const QString &serverId) const
{
if (serverId.isEmpty()) {
return ErrorCode::InternalError;
}
if (!isServiceReady()) {
return ErrorCode::AmneziaServiceNotRunning;
}
if (serverConfigUtils::isLegacyApiSubscription(m_serversRepository->serverKind(serverId))) {
return ErrorCode::LegacyApiV1NotSupportedError;
}
DockerContainer container = DockerContainer::None;
const ErrorCode errorCode = defaultContainerForServer(serverId, container);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
if (container == DockerContainer::None) {
return ErrorCode::NoInstalledContainersError;
}
if (ContainerUtils::isUnsupportedContainer(container)) {
return ErrorCode::LegacyContainerNotSupportedError;
}
if (!isContainerSupported(container)) {
return ErrorCode::NotSupportedOnThisPlatform;
}
return ErrorCode::NoError;
}
ErrorCode ConnectionController::prepareConnection(const QString &serverId,
QJsonObject& vpnConfiguration,
DockerContainer& container)
{
ContainerConfig containerConfigModel;
QPair<QString, QString> dns;
QString hostName;
@@ -120,10 +198,6 @@ ErrorCode ConnectionController::prepareConnection(const QString &serverId,
return ErrorCode::InternalError;
}
if (!isContainerSupported(container)) {
return ErrorCode::NotSupportedOnThisPlatform;
}
vpnConfiguration = createConnectionConfiguration(dns, isApiConfig, hostName, description, configVersion,
containerConfigModel, container);
@@ -34,6 +34,8 @@ public:
QJsonObject& vpnConfiguration,
DockerContainer& container);
ErrorCode isConnectionSupported(const QString &serverId) const;
ErrorCode openConnection(const QString &serverId);
void closeConnection();
@@ -73,6 +75,8 @@ signals:
#endif
private:
ErrorCode defaultContainerForServer(const QString &serverId, DockerContainer &container) const;
SecureServersRepository* m_serversRepository;
SecureAppSettingsRepository* m_appSettingsRepository;
VpnConnection* m_vpnConnection;
+1 -1
View File
@@ -191,7 +191,7 @@ void CoreController::initControllers()
m_languageUiController = new LanguageUiController(m_settingsController, m_languageModel, this);
setQmlContextProperty("LanguageUiController", m_languageUiController);
m_settingsUiController = new SettingsUiController(m_settingsController, m_serversController, m_languageUiController, this);
m_settingsUiController = new SettingsUiController(m_settingsController, m_serversController, this);
setQmlContextProperty("SettingsController", m_settingsUiController);
m_pageController = new PageController(m_serversController, m_settingsController, this);
+1
View File
@@ -115,6 +115,7 @@ protected:
IpSplitTunnelingModel* ipSplitTunnelingModelProtected() const { return m_ipSplitTunnelingModel; }
LanguageModel* languageModelProtected() const { return m_languageModel; }
ConnectionUiController* connectionUiControllerProtected() const { return m_connectionUiController; }
InstallUiController* installUiControllerProtected() const { return m_installUiController; }
ImportController* importCoreControllerProtected() const { return m_importCoreController; }
ExportController* exportControllerProtected() const { return m_exportController; }
+23 -19
View File
@@ -33,7 +33,6 @@
#include "core/controllers/connectionController.h"
#include "ui/models/clientManagementModel.h"
#include "ui/controllers/api/apiNewsUiController.h"
#include "ui/models/api/apiCountryModel.h"
#include "ui/models/containersModel.h"
#include "core/utils/containerEnum.h"
@@ -156,15 +155,17 @@ void CoreSignalHandlers::initExportControllerHandler()
void CoreSignalHandlers::initImportControllerHandler()
{
connect(m_coreController->m_importCoreController, &ImportController::importFinished, this, [this]() {
if (!m_coreController->m_connectionController->isConnected()) {
int newServerIndex = m_coreController->m_serversController->getServersCount() - 1;
const QString serverId = m_coreController->m_serversController->getServerId(newServerIndex);
if (!serverId.isEmpty()) {
m_coreController->m_serversController->setDefaultServer(serverId);
}
if (m_coreController->m_serversUiController) {
m_coreController->m_serversUiController->setProcessedServerId(serverId);
}
if (m_coreController->m_connectionUiController->isConnected()) {
return;
}
const int newServerIndex = m_coreController->m_serversController->getServersCount() - 1;
const QString serverId = m_coreController->m_serversController->getServerId(newServerIndex);
if (!serverId.isEmpty()) {
m_coreController->m_serversController->setDefaultServer(serverId);
}
if (m_coreController->m_serversUiController) {
m_coreController->m_serversUiController->setProcessedServerId(serverId);
}
});
}
@@ -176,17 +177,14 @@ void CoreSignalHandlers::initApiCountryModelUpdateHandler()
if (processedServerId.isEmpty()) {
return;
}
QJsonArray availableCountries;
QString serverCountryCode;
const auto apiV2 = m_coreController->m_serversRepository->apiV2Config(processedServerId);
if (apiV2.has_value()) {
availableCountries = apiV2->apiConfig.availableCountries;
serverCountryCode = apiV2->apiConfig.serverCountryCode;
if (!apiV2.has_value()) {
return;
}
m_coreController->m_apiCountryModel->updateModel(availableCountries, serverCountryCode);
m_coreController->m_apiCountryModel->updateModel(apiV2->apiConfig.availableCountries,
apiV2->apiConfig.serverCountryCode);
});
}
@@ -237,13 +235,16 @@ void CoreSignalHandlers::initLanguageHandler()
connect(m_coreController->m_settingsUiController, &SettingsUiController::resetLanguageToSystem, m_coreController->m_languageUiController, [this]() {
m_coreController->m_languageUiController->changeLanguage(m_coreController->m_languageUiController->getSystemLanguageEnum());
});
connect(m_coreController->m_settingsUiController, &SettingsUiController::appLanguageChanged, m_coreController->m_languageUiController, [this]() {
m_coreController->m_languageUiController->onAppLanguageChanged(m_coreController->m_settingsController->getAppLanguage());
});
}
void CoreSignalHandlers::initAutoConnectHandler()
{
if (m_coreController->m_settingsUiController->isAutoConnectEnabled()
&& !m_coreController->m_serversController->getDefaultServerId().isEmpty()) {
QTimer::singleShot(1000, this, [this]() { m_coreController->m_connectionUiController->openConnection(); });
QTimer::singleShot(1000, this, [this]() { m_coreController->m_connectionUiController->toggleConnection(); });
}
}
@@ -348,6 +349,9 @@ void CoreSignalHandlers::initUnsupportedConnectDrawerHandler()
{
connect(m_coreController->m_subscriptionUiController, &SubscriptionUiController::unsupportedConnectDrawerRequested,
m_coreController->m_pageController, &PageController::unsupportedConnectDrawerRequested);
connect(m_coreController->m_connectionUiController, &ConnectionUiController::unsupportedConnectDrawerRequested,
m_coreController->m_pageController, &PageController::unsupportedConnectDrawerRequested);
}
void CoreSignalHandlers::initStrictKillSwitchHandler()
@@ -504,45 +504,24 @@ QJsonObject ImportController::extractOpenVpnConfig(const QString &data) const
QJsonObject ImportController::extractWireGuardConfig(const QString &data, ConfigTypes &configType) const
{
QMap<QString, QString> interfaceMap;
QList<QMap<QString, QString>> peerList;
enum class WgSection { None, Interface, Peer };
WgSection currentSection = WgSection::None;
const auto configByLines = data.split("\n");
QMap<QString, QString> configMap;
auto configByLines = data.split("\n");
for (const QString &line : configByLines) {
const QString trimmedLine = line.trimmed();
if (trimmedLine == "[Interface]") {
currentSection = WgSection::Interface;
} else if (trimmedLine == "[Peer]") {
currentSection = WgSection::Peer;
peerList.append(QMap<QString, QString>());
} else if (!trimmedLine.isEmpty() && !trimmedLine.startsWith("#")) {
const QStringList parts = trimmedLine.split(" = ");
QString trimmedLine = line.trimmed();
if (trimmedLine.startsWith("[") && trimmedLine.endsWith("]")) {
continue;
} else {
QStringList parts = trimmedLine.split(" = ");
if (parts.count() == 2) {
const QString key = parts.at(0).trimmed();
const QString value = parts.at(1).trimmed();
if (currentSection == WgSection::Interface) {
interfaceMap[key] = value;
} else if (currentSection == WgSection::Peer && !peerList.isEmpty()) {
peerList.last()[key] = value;
}
configMap[parts.at(0).trimmed()] = parts.at(1).trimmed();
}
}
}
if (peerList.isEmpty()) {
qDebug() << "No [Peer] section found in WireGuard config";
return QJsonObject();
}
const QMap<QString, QString> &firstPeerMap = peerList.first();
QJsonObject lastConfig;
lastConfig[configKey::config] = data;
auto url { QUrl::fromUserInput(firstPeerMap.value(protocols::wireguard::Endpoint)) };
auto url { QUrl::fromUserInput(configMap.value(protocols::wireguard::Endpoint)) };
QString hostName;
QString port;
if (!url.host().isEmpty()) {
@@ -561,55 +540,37 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data, Config
lastConfig[configKey::hostName] = hostName;
lastConfig[configKey::port] = port.toInt();
if (!interfaceMap.value(protocols::wireguard::PrivateKey).isEmpty()
&& !interfaceMap.value(protocols::wireguard::Address).isEmpty()
&& !firstPeerMap.value(protocols::wireguard::PublicKey).isEmpty()) {
lastConfig[configKey::clientPrivKey] = interfaceMap.value(protocols::wireguard::PrivateKey);
lastConfig[configKey::clientIp] = interfaceMap.value(protocols::wireguard::Address);
if (!configMap.value(protocols::wireguard::PrivateKey).isEmpty()
&& !configMap.value(protocols::wireguard::Address).isEmpty()
&& !configMap.value(protocols::wireguard::PublicKey).isEmpty()) {
lastConfig[configKey::clientPrivKey] = configMap.value(protocols::wireguard::PrivateKey);
lastConfig[configKey::clientIp] = configMap.value(protocols::wireguard::Address);
if (!firstPeerMap.value(protocols::wireguard::PresharedKey).isEmpty()) {
lastConfig[configKey::pskKey] = firstPeerMap.value(protocols::wireguard::PresharedKey);
} else if (!firstPeerMap.value(protocols::wireguard::PreSharedKey).isEmpty()) {
lastConfig[configKey::pskKey] = firstPeerMap.value(protocols::wireguard::PreSharedKey);
if (!configMap.value(protocols::wireguard::PresharedKey).isEmpty()) {
lastConfig[configKey::pskKey] = configMap.value(protocols::wireguard::PresharedKey);
} else if (!configMap.value(protocols::wireguard::PreSharedKey).isEmpty()) {
lastConfig[configKey::pskKey] = configMap.value(protocols::wireguard::PreSharedKey);
}
lastConfig[configKey::serverPubKey] = firstPeerMap.value(protocols::wireguard::PublicKey);
lastConfig[configKey::serverPubKey] = configMap.value(protocols::wireguard::PublicKey);
} else {
qDebug() << "One of the key parameters is missing (PrivateKey, Address, PublicKey)";
return QJsonObject();
}
if (!firstPeerMap.value(protocols::wireguard::PersistentKeepalive).isEmpty()) {
lastConfig[configKey::persistentKeepAlive] = firstPeerMap.value(protocols::wireguard::PersistentKeepalive);
if (!configMap.value(protocols::wireguard::MTU).isEmpty()) {
lastConfig[configKey::mtu] = configMap.value(protocols::wireguard::MTU);
}
if (!configMap.value(protocols::wireguard::PersistentKeepalive).isEmpty()) {
lastConfig[configKey::persistentKeepAlive] = configMap.value(protocols::wireguard::PersistentKeepalive);
}
QJsonArray allowedIpsJsonArray = QJsonArray::fromStringList(
firstPeerMap.value(protocols::wireguard::AllowedIPs).split(", "));
configMap.value(protocols::wireguard::AllowedIPs).split(", "));
lastConfig[configKey::allowedIps] = allowedIpsJsonArray;
if (peerList.size() > 1) {
QJsonArray peersArray;
for (const auto &peerMap : std::as_const(peerList)) {
QJsonObject peerObj;
const auto peerUrl = QUrl::fromUserInput(peerMap.value(protocols::wireguard::Endpoint));
peerObj[configKey::serverPubKey] = peerMap.value(protocols::wireguard::PublicKey);
if (!peerMap.value(protocols::wireguard::PresharedKey).isEmpty()) {
peerObj[configKey::pskKey] = peerMap.value(protocols::wireguard::PresharedKey);
} else if (!peerMap.value(protocols::wireguard::PreSharedKey).isEmpty()) {
peerObj[configKey::pskKey] = peerMap.value(protocols::wireguard::PreSharedKey);
}
peerObj[configKey::hostName] = peerUrl.host();
peerObj[configKey::port] = peerUrl.port() != -1 ? peerUrl.port() : QString(protocols::wireguard::defaultPort).toInt();
peerObj[configKey::allowedIps] = QJsonArray::fromStringList(peerMap.value(protocols::wireguard::AllowedIPs).split(", "));
if (!peerMap.value(protocols::wireguard::PersistentKeepalive).isEmpty()) {
peerObj[configKey::persistentKeepAlive] = peerMap.value(protocols::wireguard::PersistentKeepalive);
}
peersArray.append(peerObj);
}
lastConfig["peers"] = peersArray;
}
QString protocolName = configKey::wireguard;
QString protocolVersion;
ConfigTypes detectedType = ConfigTypes::WireGuard;
@@ -627,25 +588,25 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data, Config
};
bool hasAllRequiredFields = std::all_of(requiredJunkFields.begin(), requiredJunkFields.end(),
[&interfaceMap](const QString &field) { return !interfaceMap.value(field).isEmpty(); });
[&configMap](const QString &field) { return !configMap.value(field).isEmpty(); });
if (hasAllRequiredFields) {
for (const QString &field : requiredJunkFields) {
lastConfig[field] = interfaceMap.value(field);
lastConfig[field] = configMap.value(field);
}
for (const QString &field : optionalJunkFields) {
if (!interfaceMap.value(field).isEmpty()) {
lastConfig[field] = interfaceMap.value(field);
if (!configMap.value(field).isEmpty()) {
lastConfig[field] = configMap.value(field);
}
}
bool hasCookieReplyPacketJunkSize = !interfaceMap.value(configKey::cookieReplyPacketJunkSize).isEmpty();
bool hasTransportPacketJunkSize = !interfaceMap.value(configKey::transportPacketJunkSize).isEmpty();
bool hasSpecialJunk = !interfaceMap.value(configKey::specialJunk1).isEmpty() ||
!interfaceMap.value(configKey::specialJunk2).isEmpty() ||
!interfaceMap.value(configKey::specialJunk3).isEmpty() ||
!interfaceMap.value(configKey::specialJunk4).isEmpty() ||
!interfaceMap.value(configKey::specialJunk5).isEmpty();
bool hasCookieReplyPacketJunkSize = !configMap.value(configKey::cookieReplyPacketJunkSize).isEmpty();
bool hasTransportPacketJunkSize = !configMap.value(configKey::transportPacketJunkSize).isEmpty();
bool hasSpecialJunk = !configMap.value(configKey::specialJunk1).isEmpty() ||
!configMap.value(configKey::specialJunk2).isEmpty() ||
!configMap.value(configKey::specialJunk3).isEmpty() ||
!configMap.value(configKey::specialJunk4).isEmpty() ||
!configMap.value(configKey::specialJunk5).isEmpty();
if (hasCookieReplyPacketJunkSize && hasTransportPacketJunkSize) {
protocolVersion = "2";
@@ -656,8 +617,8 @@ QJsonObject ImportController::extractWireGuardConfig(const QString &data, Config
detectedType = ConfigTypes::Awg;
}
if (!interfaceMap.value(protocols::wireguard::MTU).isEmpty()) {
lastConfig[configKey::mtu] = interfaceMap.value(protocols::wireguard::MTU);
if (!configMap.value(protocols::wireguard::MTU).isEmpty()) {
lastConfig[configKey::mtu] = configMap.value(protocols::wireguard::MTU);
} else {
lastConfig[configKey::mtu] = (protocolName == configKey::awg)
? protocols::awg::defaultMtu
@@ -72,6 +72,16 @@ namespace
}
return false;
}
QString buildRemoveContainerScript(const amnezia::ScriptVars &vars, bool removeDataVolume)
{
QString script = SshSession::replaceVars(amnezia::scriptData(SharedScriptType::remove_container), vars);
if (removeDataVolume) {
script += QLatin1String("\nsudo docker volume rm -f $CONTAINER_NAME-data 2>/dev/null || true");
script = SshSession::replaceVars(script, vars);
}
return script;
}
}
InstallController::InstallController(SecureServersRepository *serversRepository,
@@ -120,14 +130,10 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials
return e;
qDebug().noquote() << "InstallController::setupContainer prepareHostWorker finished";
amnezia::ScriptVars removeContainerVars =
const amnezia::ScriptVars removeContainerVars =
amnezia::genBaseVars(credentials, container, QString(), QString());
if (!isUpdate) {
removeContainerVars.append({ { "$REMOVE_CONTAINER_DATA", QStringLiteral("1") } });
}
sshSession.runScript(credentials,
sshSession.replaceVars(amnezia::scriptData(SharedScriptType::remove_container),
removeContainerVars));
const bool removeDataVolume = !isUpdate && (container == DockerContainer::MtProxy || container == DockerContainer::Telemt);
sshSession.runScript(credentials, buildRemoveContainerScript(removeContainerVars, removeDataVolume));
qDebug().noquote() << "InstallController::setupContainer removeContainer finished";
qDebug().noquote() << "buildContainerWorker start";
@@ -152,8 +158,8 @@ ErrorCode InstallController::setupContainer(const ServerCredentials &credentials
return startupContainerWorker(credentials, container, config, sshSession);
}
ErrorCode InstallController::updateContainer(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig,
ContainerConfig &newConfig)
ErrorCode InstallController::updateServerConfig(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig,
ContainerConfig &newConfig)
{
if (!isUpdateDockerContainerRequired(container, oldConfig, newConfig)) {
auto adminConfig = m_serversRepository->selfHostedAdminConfig(serverId);
@@ -185,7 +191,7 @@ ErrorCode InstallController::updateContainer(const QString &serverId, DockerCont
SshSession sshSession(this);
bool reinstallRequired = isReinstallContainerRequired(container, oldConfig, newConfig);
qDebug() << "InstallController::updateContainer for container" << container << "reinstall required is" << reinstallRequired;
qDebug() << "InstallController::updateServerConfig for container" << container << "reinstall required is" << reinstallRequired;
bool xrayServerSettingsChanged = false;
if (container == DockerContainer::Xray || container == DockerContainer::SSXray) {
@@ -213,11 +219,11 @@ ErrorCode InstallController::updateContainer(const QString &serverId, DockerCont
if (errorCode == ErrorCode::NoError && xrayServerSettingsChanged && !skipXrayInboundSync) {
DnsSettings dnsSettings = { m_appSettingsRepository->primaryDns(), m_appSettingsRepository->secondaryDns() };
XrayConfigurator xrayConfigurator(&sshSession);
qDebug() << "InstallController::updateContainer applying Xray server inbound sync, reinstall="
qDebug() << "InstallController::updateServerConfig applying Xray server inbound sync, reinstall="
<< reinstallRequired;
errorCode = xrayConfigurator.applyServerSettingsToRemote(credentials, container, newConfig, dnsSettings, false);
if (errorCode != ErrorCode::NoError) {
qDebug() << "InstallController::updateContainer Xray inbound sync failed, error="
qDebug() << "InstallController::updateServerConfig Xray inbound sync failed, error="
<< static_cast<int>(errorCode);
}
}
@@ -236,6 +242,41 @@ ErrorCode InstallController::updateContainer(const QString &serverId, DockerCont
return errorCode;
}
ErrorCode InstallController::updateClientConfig(const QString &serverId, DockerContainer container, ContainerConfig &newConfig)
{
switch (m_serversRepository->serverKind(serverId)) {
case serverConfigUtils::ConfigType::SelfHostedAdmin: {
auto config = m_serversRepository->selfHostedAdminConfig(serverId);
if (!config.has_value()) {
return ErrorCode::InternalError;
}
config->updateContainerConfig(container, newConfig);
m_serversRepository->editServer(serverId, config->toJson(), serverConfigUtils::ConfigType::SelfHostedAdmin);
return ErrorCode::NoError;
}
case serverConfigUtils::ConfigType::SelfHostedUser: {
auto config = m_serversRepository->selfHostedUserConfig(serverId);
if (!config.has_value()) {
return ErrorCode::InternalError;
}
config->updateContainerConfig(container, newConfig);
m_serversRepository->editServer(serverId, config->toJson(), serverConfigUtils::ConfigType::SelfHostedUser);
return ErrorCode::NoError;
}
case serverConfigUtils::ConfigType::Native: {
auto config = m_serversRepository->nativeConfig(serverId);
if (!config.has_value()) {
return ErrorCode::InternalError;
}
config->updateContainerConfig(container, newConfig);
m_serversRepository->editServer(serverId, config->toJson(), serverConfigUtils::ConfigType::Native);
return ErrorCode::NoError;
}
default:
return ErrorCode::InternalError;
}
}
void InstallController::clearCachedProfile(const QString &serverId, DockerContainer container)
{
if (ContainerUtils::containerService(container) == ServiceType::Other) {
@@ -795,8 +836,8 @@ ErrorCode InstallController::installDockerWorker(const ServerCredentials &creden
qDebug().noquote() << "InstallController::installDockerWorker" << stdOut;
if (container == DockerContainer::Awg2) {
QRegularExpression regex(R"(Linux\s+(\d+)\.(\d+)[^\d]*)");
QRegularExpressionMatch match = regex.match(stdOut);
QRegularExpression kernelVersionRegex(R"(Linux\s+(\d+)\.(\d+)[^\d]*)");
QRegularExpressionMatch match = kernelVersionRegex.match(stdOut);
if (match.hasMatch()) {
int majorVersion = match.captured(1).toInt();
int minorVersion = match.captured(2).toInt();
@@ -809,8 +850,19 @@ ErrorCode InstallController::installDockerWorker(const ServerCredentials &creden
if (stdOut.contains("lock"))
return ErrorCode::ServerPacketManagerError;
if (stdOut.contains("command not found"))
if (stdOut.contains("Container runtime is not supported"))
return ErrorCode::ServerContainerRuntimeNotSupported;
QRegularExpression notFoundRegex(
R"(^.*(?:sudo:|docker:).*not found.*$)",
QRegularExpression::MultilineOption);
if (notFoundRegex.match(stdOut).hasMatch()) {
return ErrorCode::ServerDockerFailedError;
}
if (stdOut.contains("Container runtime service not running"))
return ErrorCode::ContainerRuntimeServiceNotRunning;
return error;
}
@@ -847,7 +899,7 @@ ErrorCode InstallController::isUserInSudo(const ServerCredentials &credentials,
return ErrorCode::ServerUserNotInSudo;
if (stdOut.contains("can't cd to") || stdOut.contains("Permission denied") || stdOut.contains("No such file or directory"))
return ErrorCode::ServerUserDirectoryNotAccessible;
if (stdOut.contains("sudoers") || stdOut.contains("is not allowed to run sudo on"))
if (stdOut.contains(QRegularExpression(R"(\bsudoers\b)")) || stdOut.contains("is not allowed to") || stdOut.contains("can't do that"))
return ErrorCode::ServerUserNotAllowedInSudoers;
if (stdOut.contains("password is required") || stdOut.contains("authentication is required"))
return ErrorCode::ServerUserPasswordRequired;
@@ -980,12 +1032,11 @@ ErrorCode InstallController::removeContainer(const QString &serverId, DockerCont
return ErrorCode::InternalError;
}
SshSession sshSession(this);
amnezia::ScriptVars removeContainerVars =
const amnezia::ScriptVars removeContainerVars =
amnezia::genBaseVars(credentials, container, QString(), QString());
removeContainerVars.append({ { "$REMOVE_CONTAINER_DATA", QStringLiteral("1") } });
ErrorCode errorCode = sshSession.runScript(
credentials,
sshSession.replaceVars(amnezia::scriptData(SharedScriptType::remove_container), removeContainerVars));
const bool removeDataVolume = (container == DockerContainer::MtProxy || container == DockerContainer::Telemt);
ErrorCode errorCode =
sshSession.runScript(credentials, buildRemoveContainerScript(removeContainerVars, removeDataVolume));
if (errorCode == ErrorCode::NoError) {
QMap<DockerContainer, ContainerConfig> containers = adminConfig->containers;
@@ -1463,7 +1514,7 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
QString transportProtoStr = containerAndPortMatch.captured(3);
DockerContainer container = ContainerUtils::containerFromString(name);
if (container == DockerContainer::None) {
if (container == DockerContainer::None || ContainerUtils::isUnsupportedContainer(container)) {
continue;
}
@@ -1488,7 +1539,7 @@ ErrorCode InstallController::getAlreadyInstalledContainers(const ServerCredentia
QString transportProtoStr = torOrDnsRegMatch.captured(3);
DockerContainer container = ContainerUtils::containerFromString(name);
if (container == DockerContainer::None) {
if (container == DockerContainer::None || ContainerUtils::isUnsupportedContainer(container)) {
continue;
}
@@ -34,7 +34,12 @@ public:
~InstallController();
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container, ContainerConfig &config, bool isUpdate = false);
ErrorCode updateContainer(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig);
// Updates server-side container settings (admin self-hosted only): reconfigures the container over SSH.
ErrorCode updateServerConfig(const QString &serverId, DockerContainer container, const ContainerConfig &oldConfig, ContainerConfig &newConfig);
// Updates client-local settings only: rewrites the stored container config for any self-hosted/native server. No SSH.
ErrorCode updateClientConfig(const QString &serverId, DockerContainer container, ContainerConfig &newConfig);
ErrorCode rebootServer(const QString &serverId);
ErrorCode removeAllContainers(const QString &serverId);
@@ -205,11 +205,7 @@ QJsonObject AwgClientConfig::toJson() const
if (isObfuscationEnabled) {
obj[configKey::isObfuscationEnabled] = isObfuscationEnabled;
}
if (!peers.isEmpty()) {
obj["peers"] = peers;
}
return obj;
}
@@ -254,9 +250,7 @@ AwgClientConfig AwgClientConfig::fromJson(const QJsonObject& json)
config.specialJunk5 = json.value(configKey::specialJunk5).toString();
config.isObfuscationEnabled = json.value(configKey::isObfuscationEnabled).toBool(false);
config.peers = json.value("peers").toArray();
return config;
}
@@ -1,7 +1,6 @@
#ifndef AWGPROTOCOLCONFIG_H
#define AWGPROTOCOLCONFIG_H
#include <QJsonArray>
#include <QJsonObject>
#include <QString>
#include <QStringList>
@@ -61,7 +60,6 @@ struct AwgClientConfig {
QStringList allowedIps;
QString persistentKeepAlive;
QString mtu;
QJsonArray peers;
QString junkPacketCount;
QString junkPacketMinSize;
QString junkPacketMaxSize;
@@ -29,6 +29,11 @@ ContainerConfig NativeServerConfig::containerConfig(DockerContainer container) c
return containers.value(container);
}
void NativeServerConfig::updateContainerConfig(DockerContainer container, const ContainerConfig &config)
{
containers[container] = config;
}
QPair<QString, QString> NativeServerConfig::getDnsPair(const QString &primaryDns, const QString &secondaryDns) const
{
QString d1 = dns1;
@@ -27,6 +27,8 @@ struct NativeServerConfig {
bool hasContainers() const;
ContainerConfig containerConfig(DockerContainer container) const;
void updateContainerConfig(DockerContainer container, const ContainerConfig &config);
QPair<QString, QString> getDnsPair(const QString &primaryDns, const QString &secondaryDns) const;
QJsonObject toJson() const;
@@ -43,6 +43,11 @@ ContainerConfig SelfHostedUserServerConfig::containerConfig(DockerContainer cont
return containers.value(container);
}
void SelfHostedUserServerConfig::updateContainerConfig(DockerContainer container, const ContainerConfig &config)
{
containers[container] = config;
}
QPair<QString, QString> SelfHostedUserServerConfig::getDnsPair(const QString &primaryDns,
const QString &secondaryDns) const
{
@@ -32,6 +32,8 @@ struct SelfHostedUserServerConfig {
bool hasContainers() const;
ContainerConfig containerConfig(DockerContainer container) const;
void updateContainerConfig(DockerContainer container, const ContainerConfig &config);
QPair<QString, QString> getDnsPair(const QString &primaryDns, const QString &secondaryDns) const;
QJsonObject toJson() const;
+24 -13
View File
@@ -39,33 +39,44 @@ QString OpenVpnProtocol::defaultConfigPath()
return p;
}
void OpenVpnProtocol::stop()
void OpenVpnProtocol::cleanupResources()
{
qDebug() << "OpenVpnProtocol::stop()";
setConnectionState(Vpn::ConnectionState::Disconnecting);
// TODO: need refactoring
// sendTermSignal() will even return true while server connected ???
if ((m_connectionState == Vpn::ConnectionState::Preparing) || (m_connectionState == Vpn::ConnectionState::Connecting)
|| (m_connectionState == Vpn::ConnectionState::Connected)
|| (m_connectionState == Vpn::ConnectionState::Reconnecting)) {
if (m_openVpnProcess || openVpnProcessIsRunning()) {
if (!sendTermSignal()) {
killOpenVpnProcess();
}
QThread::msleep(10);
m_managementServer.stop();
}
m_managementServer.stop();
#if defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
IpcClient::withInterface([](QSharedPointer<IpcInterfaceReplica> iface) {
QRemoteObjectPendingReply<bool> reply = iface->disableKillSwitch();
if (!reply.waitForFinished(1000) && !reply.returnValue()) {
qWarning() << "OpenVpnProtocol::stop(): Failed to disable killswitch";
qWarning() << "OpenVpnProtocol::cleanupResources(): Failed to disable killswitch";
}
});
#endif
}
setConnectionState(Vpn::ConnectionState::Disconnected);
void OpenVpnProtocol::stop()
{
qDebug() << "OpenVpnProtocol::stop()";
const bool wasActive = m_connectionState == Vpn::ConnectionState::Preparing
|| m_connectionState == Vpn::ConnectionState::Connecting
|| m_connectionState == Vpn::ConnectionState::Connected
|| m_connectionState == Vpn::ConnectionState::Reconnecting;
if (wasActive) {
setConnectionState(Vpn::ConnectionState::Disconnecting);
}
cleanupResources();
if (wasActive || m_connectionState == Vpn::ConnectionState::Disconnecting) {
setConnectionState(Vpn::ConnectionState::Disconnected);
}
}
ErrorCode OpenVpnProtocol::prepare()
@@ -168,7 +179,7 @@ void OpenVpnProtocol::updateRouteGateway(QString line)
ErrorCode OpenVpnProtocol::start()
{
OpenVpnProtocol::stop();
cleanupResources();
if (!QFileInfo::exists(configPath())) {
setLastError(ErrorCode::OpenVpnConfigMissing);
+1
View File
@@ -29,6 +29,7 @@ protected slots:
void onReadyReadDataFromManagementServer();
private:
void cleanupResources();
QString configPath() const;
bool openVpnProcessIsRunning() const;
bool sendTermSignal();
+2
View File
@@ -15,6 +15,8 @@ namespace amnezia
Awg2,
WireGuard,
OpenVpn,
Cloak,
ShadowSocks,
Ipsec,
Xray,
SSXray,
@@ -21,6 +21,8 @@ QString ContainerUtils::containerToString(DockerContainer c)
{
if (c == DockerContainer::None)
return "none";
if (c == DockerContainer::Cloak)
return "amnezia-openvpn-cloak";
if (c == DockerContainer::Awg)
return "amnezia-awg";
if (c == DockerContainer::Awg2)
@@ -62,6 +64,8 @@ QMap<DockerContainer, QString> ContainerUtils::containerHumanNames()
{
return { { DockerContainer::None, "Not installed" },
{ DockerContainer::OpenVpn, "OpenVPN" },
{ DockerContainer::ShadowSocks, "OpenVPN over SS" },
{ DockerContainer::Cloak, "OpenVPN over Cloak" },
{ DockerContainer::WireGuard, "WireGuard" },
{ DockerContainer::Awg, "AmneziaWG" },
{ DockerContainer::Awg2, "AmneziaWG" },
@@ -83,6 +87,10 @@ QMap<DockerContainer, QString> ContainerUtils::containerDescriptions()
return { { DockerContainer::OpenVpn,
QObject::tr("OpenVPN is the most popular VPN protocol, with flexible configuration options. It uses its "
"own security protocol with SSL/TLS for key exchange.") },
{ DockerContainer::ShadowSocks,
QObject::tr("This protocol is no longer supported.") },
{ DockerContainer::Cloak,
QObject::tr("This protocol is no longer supported.") },
{ DockerContainer::WireGuard,
QObject::tr("WireGuard - popular VPN protocol with high performance, high speed and low power "
"consumption.") },
@@ -194,6 +202,9 @@ QMap<DockerContainer, QString> ContainerUtils::containerDetailedDescriptions()
ServiceType ContainerUtils::containerService(DockerContainer c)
{
if (isUnsupportedContainer(c)) {
return ServiceType::Vpn;
}
return ProtocolUtils::protocolService(defaultProtocol(c));
}
@@ -202,6 +213,8 @@ Proto ContainerUtils::defaultProtocol(DockerContainer c)
switch (c) {
case DockerContainer::None: return Proto::Unknown;
case DockerContainer::OpenVpn: return Proto::OpenVpn;
case DockerContainer::Cloak:
case DockerContainer::ShadowSocks: return Proto::Unknown;
case DockerContainer::WireGuard: return Proto::WireGuard;
case DockerContainer::Awg2: return Proto::Awg;
case DockerContainer::Awg: return Proto::Awg;
@@ -252,6 +265,8 @@ bool ContainerUtils::isSupportedByCurrentPlatform(DockerContainer c)
// macOS build using Network Extension – allow OpenVPN for parity with iOS.
switch (c) {
case DockerContainer::OpenVpn: return true;
case DockerContainer::Cloak: return false;
case DockerContainer::ShadowSocks: return false;
case DockerContainer::WireGuard: return true;
case DockerContainer::Awg2: return true;
case DockerContainer::Awg: return true;
@@ -336,6 +351,10 @@ int ContainerUtils::easySetupOrder(DockerContainer container)
bool ContainerUtils::isShareable(DockerContainer container)
{
if (isUnsupportedContainer(container)) {
return false;
}
switch (container) {
case DockerContainer::TorWebSite: return false;
case DockerContainer::Dns: return false;
@@ -352,6 +371,11 @@ bool ContainerUtils::isAwgContainer(DockerContainer container)
return container == DockerContainer::Awg || container == DockerContainer::Awg2;
}
bool ContainerUtils::isUnsupportedContainer(DockerContainer container)
{
return container == DockerContainer::Cloak || container == DockerContainer::ShadowSocks;
}
QJsonObject ContainerUtils::getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig)
{
QString protocolConfigString = containerConfig.value(ProtocolUtils::protoToString(protocol))
@@ -45,6 +45,8 @@ namespace amnezia
bool isAwgContainer(DockerContainer container);
bool isUnsupportedContainer(DockerContainer container);
QJsonObject getProtocolConfigFromContainer(const Proto protocol, const QJsonObject &containerConfig);
int installPageOrder(DockerContainer container);
+3 -2
View File
@@ -38,6 +38,8 @@ namespace amnezia
XrayServerConfigInvalid = 215,
XrayServerNoVlessClients = 216,
XrayRealityKeysReadFailed = 217,
ServerContainerRuntimeNotSupported = 218,
ContainerRuntimeServiceNotRunning = 219,
// Ssh connection errors
SshRequestDeniedError = 300,
@@ -79,6 +81,7 @@ namespace amnezia
ImportBackupFileUseRestoreInstead = 903,
RestoreBackupInvalidError = 904,
LegacyApiV1NotSupportedError = 905,
LegacyContainerNotSupportedError = 906,
// Android errors
AndroidError = 1000,
@@ -123,5 +126,3 @@ namespace amnezia
Q_DECLARE_METATYPE(amnezia::ErrorCode)
#endif // ERRORCODES_H
+3
View File
@@ -39,6 +39,8 @@ QString errorString(ErrorCode code) {
case(ErrorCode::XrayRealityKeysReadFailed):
errorMessage = QObject::tr("Server error: failed to read XRay Reality keys from the server");
break;
case(ErrorCode::ServerContainerRuntimeNotSupported): errorMessage = QObject::tr("Server error: The default container runtime available for installation on this server is not supported.\n Install Docker Engine on the server manually and try again."); break;
case(ErrorCode::ContainerRuntimeServiceNotRunning): errorMessage = QObject::tr("Container runtime error: The container runtime service is not running.\n Check the container runtime service on the server, or wait about a minute and try again."); break;
// Libssh errors
case(ErrorCode::SshRequestDeniedError): errorMessage = QObject::tr("SSH request was denied"); break;
@@ -69,6 +71,7 @@ QString errorString(ErrorCode code) {
case (ErrorCode::ImportBackupFileUseRestoreInstead): errorMessage = QObject::tr("Backup files cannot be imported here. Use 'Restore from backup' instead."); break;
case (ErrorCode::RestoreBackupInvalidError): errorMessage = QObject::tr("Backup file is corrupted or has invalid format"); break;
case (ErrorCode::LegacyApiV1NotSupportedError): errorMessage = QObject::tr("This legacy Amnezia subscription format is no longer supported"); break;
case (ErrorCode::LegacyContainerNotSupportedError): errorMessage = QObject::tr("This protocol is no longer supported. Please select another protocol or remove this container from the server settings."); break;
case (ErrorCode::ImportOpenConfigError): errorMessage = QObject::tr("Unable to open config file"); break;
case (ErrorCode::NoInstalledContainersError): errorMessage = QObject::tr("VPN Protocols is not installed.\n Please install VPN container at first"); break;
-31
View File
@@ -441,37 +441,6 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
config.m_specialJunk["I5"] = obj.value("I5").toString();
}
if (obj.contains("primaryPeerAllowedIPAddressRanges") &&
obj.value("primaryPeerAllowedIPAddressRanges").isArray()) {
for (const QJsonValue& ipVal : obj.value("primaryPeerAllowedIPAddressRanges").toArray()) {
if (!ipVal.isObject()) continue;
QJsonObject ipObj = ipVal.toObject();
config.m_primaryPeerAllowedIPRanges.append(
IPAddress(QHostAddress(ipObj.value("address").toString()),
ipObj.value("range").toInt()));
}
}
if (obj.contains("additionalPeers") && obj.value("additionalPeers").isArray()) {
for (const QJsonValue& peerVal : obj.value("additionalPeers").toArray()) {
if (!peerVal.isObject()) continue;
QJsonObject peerObj = peerVal.toObject();
InterfaceConfig::AdditionalPeerConfig peer;
peer.m_serverPublicKey = peerObj.value("serverPublicKey").toString();
peer.m_serverPskKey = peerObj.value("serverPskKey").toString();
peer.m_serverIpv4AddrIn = peerObj.value("serverIpv4AddrIn").toString();
peer.m_serverPort = peerObj.value("serverPort").toInt();
for (const QJsonValue& ipVal : peerObj.value("allowedIPAddressRanges").toArray()) {
if (!ipVal.isObject()) continue;
QJsonObject ipObj = ipVal.toObject();
peer.m_allowedIPAddressRanges.append(
IPAddress(QHostAddress(ipObj.value("address").toString()),
ipObj.value("range").toInt()));
}
config.m_additionalPeers.append(peer);
}
}
return true;
}
-12
View File
@@ -37,9 +37,6 @@ class InterfaceConfig {
int m_serverPort = 0;
int m_deviceMTU = 1420;
QList<IPAddress> m_allowedIPAddressRanges;
// For multi-peer: primary peer's own IPs only (used for UAPI allowed_ips).
// Empty for single-peer (falls back to m_allowedIPAddressRanges).
QList<IPAddress> m_primaryPeerAllowedIPRanges;
QStringList m_excludedAddresses;
QStringList m_vpnDisabledApps;
QStringList m_allowedDnsServers;
@@ -61,15 +58,6 @@ class InterfaceConfig {
QString m_transportPacketMagicHeader;
QMap<QString, QString> m_specialJunk;
struct AdditionalPeerConfig {
QString m_serverPublicKey;
QString m_serverPskKey;
QString m_serverIpv4AddrIn;
int m_serverPort = 0;
QList<IPAddress> m_allowedIPAddressRanges;
};
QList<AdditionalPeerConfig> m_additionalPeers;
QJsonObject toJson() const;
QString toWgConf(
const QMap<QString, QString>& extra = QMap<QString, QString>()) const;
+46 -74
View File
@@ -169,96 +169,68 @@ void LocalSocketController::activate(const QJsonObject &rawConfig) {
QJsonArray jsAllowedIPAddesses;
auto ipRangeToJson = [](const QString& ipRange) -> QJsonObject {
QJsonObject range;
const QStringList parts = ipRange.split('/');
range.insert("address", parts[0]);
range.insert("range", parts.size() > 1 ? parts[1].toInt() : 32);
range.insert("isIpv6", ipRange.contains(':'));
return range;
};
QJsonArray plainAllowedIP = wgConfig.value(amnezia::configKey::allowedIps).toArray();
QJsonArray defaultAllowedIP = { "0.0.0.0/0", "::/0" };
QJsonArray peersArray = wgConfig.value("peers").toArray();
bool isMultiPeer = peersArray.size() > 1;
if (isMultiPeer) {
// Union of all peers' IPs goes into allowedIPAddressRanges (used for route setup).
QSet<QString> seenIps;
for (const QJsonValue& peerVal : std::as_const(peersArray)) {
for (const QJsonValue& ipVal : peerVal.toObject().value(amnezia::configKey::allowedIps).toArray()) {
const QString ipRange = ipVal.toString().trimmed();
if (seenIps.contains(ipRange)) continue;
seenIps.insert(ipRange);
jsAllowedIPAddesses.append(ipRangeToJson(ipRange));
if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) {
// Use AllowedIP list from WG config because of higher priority
for (auto v : plainAllowedIP) {
QString ipRange = v.toString();
if (ipRange.split('/').size() > 1){
QJsonObject range;
range.insert("address", ipRange.split('/')[0]);
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit()));
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
} else {
QJsonObject range;
range.insert("address",ipRange);
range.insert("range", 32);
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
}
}
// Primary peer's own IPs only — used for UAPI allowed_ips to avoid trie conflicts.
QJsonArray primaryPeerIpsJson;
for (const QJsonValue& ipVal : peersArray[0].toObject().value(amnezia::configKey::allowedIps).toArray()) {
primaryPeerIpsJson.append(ipRangeToJson(ipVal.toString().trimmed()));
}
json.insert("primaryPeerAllowedIPAddressRanges", primaryPeerIpsJson);
QJsonArray additionalPeersJson;
for (int i = 1; i < peersArray.size(); ++i) {
const QJsonObject peerObj = peersArray[i].toObject();
QJsonObject additionalPeer;
additionalPeer.insert("serverPublicKey", peerObj.value(amnezia::configKey::serverPubKey));
additionalPeer.insert("serverPskKey", peerObj.value(amnezia::configKey::pskKey));
additionalPeer.insert("serverIpv4AddrIn", peerObj.value(amnezia::configKey::hostName));
additionalPeer.insert("serverPort", peerObj.value(amnezia::configKey::port).toInt());
QJsonArray additionalPeerIps;
for (const QJsonValue& ipVal : peerObj.value(amnezia::configKey::allowedIps).toArray()) {
additionalPeerIps.append(ipRangeToJson(ipVal.toString().trimmed()));
}
additionalPeer.insert("allowedIPAddressRanges", additionalPeerIps);
additionalPeersJson.append(additionalPeer);
}
json.insert("additionalPeers", additionalPeersJson);
} else {
QJsonArray plainAllowedIP = wgConfig.value(amnezia::configKey::allowedIps).toArray();
QJsonArray defaultAllowedIP = { "0.0.0.0/0", "::/0" };
if (plainAllowedIP != defaultAllowedIP && !plainAllowedIP.isEmpty()) {
// Use AllowedIP list from WG config because of higher priority
for (auto v : plainAllowedIP) {
jsAllowedIPAddesses.append(ipRangeToJson(v.toString().trimmed()));
}
} else {
// Use APP split tunnel
// Use APP split tunnel
if (splitTunnelType == 0 || splitTunnelType == 2) {
QJsonObject range_ipv4;
range_ipv4.insert("address", "0.0.0.0");
range_ipv4.insert("range", 0);
range_ipv4.insert("isIpv6", false);
jsAllowedIPAddesses.append(range_ipv4);
QJsonObject range_ipv4;
range_ipv4.insert("address", "0.0.0.0");
range_ipv4.insert("range", 0);
range_ipv4.insert("isIpv6", false);
jsAllowedIPAddesses.append(range_ipv4);
QJsonObject range_ipv6;
range_ipv6.insert("address", "::");
range_ipv6.insert("range", 0);
range_ipv6.insert("isIpv6", true);
jsAllowedIPAddesses.append(range_ipv6);
QJsonObject range_ipv6;
range_ipv6.insert("address", "::");
range_ipv6.insert("range", 0);
range_ipv6.insert("isIpv6", true);
jsAllowedIPAddesses.append(range_ipv6);
}
if (splitTunnelType == 1) {
for (auto v : splitTunnelSites) {
jsAllowedIPAddesses.append(ipRangeToJson(v.toString().trimmed()));
}
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
if (ipRange.split('/').size() > 1){
QJsonObject range;
range.insert("address", ipRange.split('/')[0]);
range.insert("range", atoi(ipRange.split('/')[1].toLocal8Bit()));
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
} else {
QJsonObject range;
range.insert("address",ipRange);
range.insert("range", 32);
range.insert("isIpv6", false);
jsAllowedIPAddesses.append(range);
}
}
}
}
}
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
QJsonArray jsExcludedAddresses;
if (isMultiPeer) {
for (const QJsonValue& peerVal : std::as_const(peersArray)) {
jsExcludedAddresses.append(peerVal.toObject().value(amnezia::configKey::hostName));
}
} else {
jsExcludedAddresses.append(wgConfig.value(amnezia::configKey::hostName));
}
jsExcludedAddresses.append(wgConfig.value(amnezia::configKey::hostName));
if (splitTunnelType == 2) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
@@ -20,7 +20,7 @@ extension PacketTunnelProvider {
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: wgConfigStr)
if tunnelConfiguration.peers.first?.allowedIPs
if tunnelConfiguration.peers.first!.allowedIPs
.map({ $0.stringRepresentation })
.joined(separator: ", ") == "0.0.0.0/0, ::/0" {
if wgConfig.splitTunnelType == 1 {
+9 -52
View File
@@ -1,23 +1,5 @@
import Foundation
struct WGPeerConfig: Decodable {
let serverPublicKey: String
let presharedKey: String?
let allowedIPs: [String]
let hostName: String
let port: Int
let persistentKeepAlive: String?
enum CodingKeys: String, CodingKey {
case serverPublicKey = "server_pub_key"
case presharedKey = "psk_key"
case allowedIPs = "allowed_ips"
case hostName
case port
case persistentKeepAlive = "persistent_keep_alive"
}
}
struct WGConfig: Decodable {
let initPacketMagicHeader, responsePacketMagicHeader: String?
let underloadPacketMagicHeader, transportPacketMagicHeader: String?
@@ -37,7 +19,6 @@ struct WGConfig: Decodable {
var persistentKeepAlive: String
let splitTunnelType: Int
let splitTunnelSites: [String]
let peers: [WGPeerConfig]?
enum CodingKeys: String, CodingKey {
case initPacketMagicHeader = "H1", responsePacketMagicHeader = "H2"
@@ -58,7 +39,6 @@ struct WGConfig: Decodable {
case persistentKeepAlive = "persistent_keep_alive"
case splitTunnelType
case splitTunnelSites
case peers
}
var settings: String {
@@ -123,7 +103,7 @@ struct WGConfig: Decodable {
return settingsLines.joined(separator: "\n")
}
private var interfaceSection: String {
var str: String {
"""
[Interface]
Address = \(clientIP)
@@ -131,30 +111,9 @@ struct WGConfig: Decodable {
MTU = \(mtu)
PrivateKey = \(clientPrivateKey)
\(settings)
"""
}
var str: String {
if let peers = peers, !peers.isEmpty {
let peerSections = peers.map { peer -> String in
var lines = ["[Peer]", "PublicKey = \(peer.serverPublicKey)"]
if let psk = peer.presharedKey, !psk.isEmpty {
lines.append("PresharedKey = \(psk)")
}
lines.append("AllowedIPs = \(peer.allowedIPs.joined(separator: ", "))")
lines.append("Endpoint = \(peer.hostName):\(peer.port)")
if let ka = peer.persistentKeepAlive {
lines.append("PersistentKeepalive = \(ka)")
}
return lines.joined(separator: "\n")
}.joined(separator: "\n")
return interfaceSection + "\n" + peerSections
}
return """
\(interfaceSection)
[Peer]
PublicKey = \(serverPublicKey)
\((presharedKey?.isEmpty ?? true) ? "" : "PresharedKey = \(presharedKey!)")
\(presharedKey == nil ? "" : "PresharedKey = \(presharedKey!)")
AllowedIPs = \(allowedIPs.joined(separator: ", "))
Endpoint = \(hostName):\(port)
PersistentKeepalive = \(persistentKeepAlive)
@@ -162,21 +121,19 @@ struct WGConfig: Decodable {
}
var redux: String {
let peerCount = peers?.count ?? 1
let peerInfo = peers.map { peers in
peers.enumerated().map { i, peer in
"[Peer \(i + 1)] Endpoint = \(peer.hostName):\(peer.port), AllowedIPs = \(peer.allowedIPs.joined(separator: ", "))"
}.joined(separator: "\n")
} ?? "Endpoint = \(hostName):\(port), AllowedIPs = \(allowedIPs.joined(separator: ", "))"
return """
"""
[Interface]
Address = \(clientIP)
DNS = \(dns1), \(dns2)
MTU = \(mtu)
PrivateKey = ***
\(settings)
PeerCount = \(peerCount)
\(peerInfo)
[Peer]
PublicKey = ***
PresharedKey = ***
AllowedIPs = \(allowedIPs.joined(separator: ", "))
Endpoint = \(hostName):\(port)
PersistentKeepalive = \(persistentKeepAlive)
SplitTunnelType = \(splitTunnelType)
SplitTunnelSites = \(splitTunnelSites.joined(separator: ", "))
+7 -20
View File
@@ -595,10 +595,6 @@ bool IosController::setupWireGuard()
wgConfig.insert(configKey::persistentKeepAlive, "25");
}
if (config.contains("peers") && config["peers"].isArray()) {
wgConfig.insert("peers", config["peers"]);
}
if (config.contains(configKey::isObfuscationEnabled) && config.value(configKey::isObfuscationEnabled).toBool()) {
wgConfig.insert(configKey::initPacketMagicHeader, config[configKey::initPacketMagicHeader]);
wgConfig.insert(configKey::responsePacketMagicHeader, config[configKey::responsePacketMagicHeader]);
@@ -678,23 +674,7 @@ bool IosController::setupAwg()
wgConfig.insert(configKey::hostName, config[configKey::hostName]);
wgConfig.insert(configKey::port, config[configKey::port]);
bool isMultiPeer = config.contains("peers") && config["peers"].isArray()
&& !config["peers"].toArray().isEmpty();
wgConfig.insert(configKey::clientIp, config[configKey::clientIp]);
if (isMultiPeer) {
wgConfig.insert("peers", config["peers"]);
wgConfig.insert(configKey::allowedIps, QJsonArray{}); // required by WGConfig decoder, unused in multi-peer path
} else {
if (config.contains(configKey::allowedIps) && config[configKey::allowedIps].isArray()) {
wgConfig.insert(configKey::allowedIps, config[configKey::allowedIps]);
} else {
QJsonArray allowed_ips { "0.0.0.0/0", "::/0" };
wgConfig.insert(configKey::allowedIps, allowed_ips);
}
}
wgConfig.insert(configKey::clientPrivKey, config[configKey::clientPrivKey]);
wgConfig.insert(configKey::serverPubKey, config[configKey::serverPubKey]);
wgConfig.insert(configKey::pskKey, config[configKey::pskKey]);
@@ -708,6 +688,13 @@ bool IosController::setupAwg()
wgConfig.insert(configKey::splitTunnelSites, splitTunnelSites);
if (config.contains(configKey::allowedIps) && config[configKey::allowedIps].isArray()) {
wgConfig.insert(configKey::allowedIps, config[configKey::allowedIps]);
} else {
QJsonArray allowed_ips { "0.0.0.0/0", "::/0" };
wgConfig.insert(configKey::allowedIps, allowed_ips);
}
if (config.contains(configKey::persistentKeepAlive)) {
wgConfig.insert(configKey::persistentKeepAlive, config[configKey::persistentKeepAlive]);
} else {
+23 -92
View File
@@ -5,12 +5,8 @@
#include "iputilslinux.h"
#include <arpa/inet.h>
#include <linux/if_addr.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <QHostAddress>
@@ -75,104 +71,39 @@ bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) {
return true;
}
static bool addIPv4AddressNetlink(int ifindex, const QHostAddress& addr,
int prefixlen) {
int nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (nlsock < 0) return false;
auto guard = qScopeGuard([&] { close(nlsock); });
bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) {
struct ifreq ifr;
struct sockaddr_in* ifrAddr = (struct sockaddr_in*)&ifr.ifr_addr;
char buf[512];
memset(buf, 0, sizeof(buf));
// Name the interface and set family
strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
struct nlmsghdr* nlmsg = reinterpret_cast<struct nlmsghdr*>(buf);
nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
nlmsg->nlmsg_type = RTM_NEWADDR;
nlmsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK;
nlmsg->nlmsg_seq = 1;
nlmsg->nlmsg_pid = 0;
// Get the device address to add to interface
QPair<QHostAddress, int> parsedAddr =
QHostAddress::parseSubnet(config.m_deviceIpv4Address);
QByteArray _deviceAddr = parsedAddr.first.toString().toLocal8Bit();
char* deviceAddr = _deviceAddr.data();
inet_pton(AF_INET, deviceAddr, &ifrAddr->sin_addr);
struct ifaddrmsg* ifa = static_cast<struct ifaddrmsg*>(NLMSG_DATA(nlmsg));
ifa->ifa_family = AF_INET;
ifa->ifa_prefixlen = prefixlen;
ifa->ifa_flags = IFA_F_PERMANENT;
ifa->ifa_scope = RT_SCOPE_UNIVERSE;
ifa->ifa_index = ifindex;
struct in_addr ip4;
QByteArray addrBytes = addr.toString().toLocal8Bit();
inet_pton(AF_INET, addrBytes.constData(), &ip4);
auto appendAttr = [](struct nlmsghdr* nlmsg, size_t maxlen, int type,
const void* data, size_t len) {
size_t newlen = NLMSG_ALIGN(nlmsg->nlmsg_len) + RTA_SPACE(len);
if (newlen > maxlen) return;
char* p = reinterpret_cast<char*>(nlmsg) + NLMSG_ALIGN(nlmsg->nlmsg_len);
struct rtattr* rta = reinterpret_cast<struct rtattr*>(p);
rta->rta_type = type;
rta->rta_len = RTA_LENGTH(len);
memcpy(RTA_DATA(rta), data, len);
nlmsg->nlmsg_len = newlen;
};
appendAttr(nlmsg, sizeof(buf), IFA_LOCAL, &ip4, sizeof(ip4));
appendAttr(nlmsg, sizeof(buf), IFA_ADDRESS, &ip4, sizeof(ip4));
struct sockaddr_nl nladdr;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
if (sendto(nlsock, buf, nlmsg->nlmsg_len, 0,
reinterpret_cast<struct sockaddr*>(&nladdr),
sizeof(nladdr)) < 0) {
// Create IPv4 socket to perform the ioctl operations on
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sockfd < 0) {
logger.error() << "Failed to create ioctl socket.";
return false;
}
auto guard = qScopeGuard([&] { close(sockfd); });
char ackbuf[1024];
ssize_t acklen = recv(nlsock, ackbuf, sizeof(ackbuf), 0);
if (acklen >= static_cast<ssize_t>(sizeof(struct nlmsghdr))) {
struct nlmsghdr* ackmsg = reinterpret_cast<struct nlmsghdr*>(ackbuf);
if (ackmsg->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr* err = static_cast<struct nlmsgerr*>(NLMSG_DATA(ackmsg));
if (err->error != 0) {
errno = -err->error;
return false;
}
}
// Set ifr to interface
int ret = ioctl(sockfd, SIOCSIFADDR, &ifr);
if (ret) {
logger.error() << "Failed to set IPv4: " << deviceAddr
<< "error:" << strerror(errno);
return false;
}
return true;
}
bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) {
if (config.m_deviceIpv4Address.isEmpty()) return true;
int ifindex = if_nametoindex(WG_INTERFACE);
if (ifindex == 0) {
logger.error() << "Failed to get ifindex for" << WG_INTERFACE;
return false;
}
bool ok = false;
const QStringList addresses =
config.m_deviceIpv4Address.split(',', Qt::SkipEmptyParts);
for (const QString& entry : addresses) {
QPair<QHostAddress, int> parsed =
QHostAddress::parseSubnet(entry.trimmed());
if (parsed.first.isNull()) {
logger.warning() << "Failed to parse IPv4 address:" << entry.trimmed();
continue;
}
if (!addIPv4AddressNetlink(ifindex, parsed.first, parsed.second)) {
logger.error() << "Failed to add IPv4" << parsed.first.toString() << "/"
<< parsed.second << ":" << strerror(errno);
} else {
logger.debug() << "Added IPv4" << parsed.first.toString() << "/"
<< parsed.second << "to" << WG_INTERFACE;
ok = true;
}
}
return ok;
}
bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) {
// Set up the ifr and the companion ifr6
struct in6_ifreq ifr6;
@@ -230,10 +230,7 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
out << "replace_allowed_ips=true\n";
out << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
const QList<IPAddress>& primaryIPs = config.m_primaryPeerAllowedIPRanges.isEmpty()
? config.m_allowedIPAddressRanges
: config.m_primaryPeerAllowedIPRanges;
for (const IPAddress& ip : primaryIPs) {
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
out << "allowed_ip=" << ip.toString() << "\n";
}
@@ -247,38 +244,8 @@ bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) {
int err = uapiErrno(uapiCommand(message));
if (err != 0) {
logger.error() << "Peer configuration failed:" << strerror(err);
return false;
}
for (const InterfaceConfig::AdditionalPeerConfig& peer : config.m_additionalPeers) {
QByteArray pubKey = QByteArray::fromBase64(peer.m_serverPublicKey.toUtf8());
QByteArray pskKey = QByteArray::fromBase64(peer.m_serverPskKey.toUtf8());
QString peerMsg;
QTextStream peerOut(&peerMsg);
peerOut << "set=1\n";
peerOut << "public_key=" << QString(pubKey.toHex()) << "\n";
if (!peer.m_serverPskKey.isEmpty()) {
peerOut << "preshared_key=" << QString(pskKey.toHex()) << "\n";
}
peerOut << "endpoint=" << peer.m_serverIpv4AddrIn << ":" << peer.m_serverPort << "\n";
peerOut << "replace_allowed_ips=true\n";
peerOut << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
for (const IPAddress& ip : peer.m_allowedIPAddressRanges) {
peerOut << "allowed_ip=" << ip.toString() << "\n";
}
if ((config.m_hopType != InterfaceConfig::MultiHopExit) && m_rtmonitor) {
m_rtmonitor->addExclusionRoute(IPAddress(peer.m_serverIpv4AddrIn));
}
int peerErr = uapiErrno(uapiCommand(peerMsg));
if (peerErr != 0) {
logger.error() << "Additional peer configuration failed:" << strerror(peerErr);
}
}
return true;
return (err == 0);
}
bool WireguardUtilsLinux::deletePeer(const InterfaceConfig& config) {
+9 -14
View File
@@ -80,9 +80,7 @@ bool IPUtilsMacos::setMTUAndUp(const InterfaceConfig& config) {
}
bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) {
if (config.m_deviceIpv4Address.isEmpty()) {
return true;
}
Q_UNUSED(config);
QString ifname = MacOSDaemon::instance()->m_wgutils->interfaceName();
struct ifaliasreq ifr;
struct sockaddr_in* ifrAddr = (struct sockaddr_in*)&ifr.ifra_addr;
@@ -93,28 +91,25 @@ bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) {
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifra_name, qPrintable(ifname), IFNAMSIZ);
// Extract the host IP from CIDR notation (e.g. "10.8.0.2/24" → "10.8.0.2").
// parseSubnet() zeroes host bits so we split manually to preserve the host address.
QByteArray _deviceAddr = config.m_deviceIpv4Address.split('/').first().toLocal8Bit();
// Get the device address to add to interface
QPair<QHostAddress, int> parsedAddr =
QHostAddress::parseSubnet(config.m_deviceIpv4Address);
QByteArray _deviceAddr = parsedAddr.first.toString().toLocal8Bit();
char* deviceAddr = _deviceAddr.data();
ifrAddr->sin_family = AF_INET;
ifrAddr->sin_len = sizeof(struct sockaddr_in);
if (inet_pton(AF_INET, deviceAddr, &ifrAddr->sin_addr) != 1) {
logger.error() << "Failed to parse IPv4 address:" << deviceAddr;
return false;
}
inet_pton(AF_INET, deviceAddr, &ifrAddr->sin_addr);
// Set the netmask to /32
ifrMask->sin_family = AF_INET;
ifrMask->sin_len = sizeof(struct sockaddr_in);
memset(&ifrMask->sin_addr, 0xff, sizeof(ifrMask->sin_addr));
// For P2P (utun) interfaces, ifra_broadaddr is the destination address.
// Set it equal to the local address to create only a host route (not a network
// route that would cause a routing loop).
// Set the broadcast address.
ifrBcast->sin_family = AF_INET;
ifrBcast->sin_len = sizeof(struct sockaddr_in);
ifrBcast->sin_addr.s_addr = ifrAddr->sin_addr.s_addr;
ifrBcast->sin_addr.s_addr =
(ifrAddr->sin_addr.s_addr | ~ifrMask->sin_addr.s_addr);
// Create an IPv4 socket to perform the ioctl operations on
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
@@ -230,11 +230,7 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
out << "replace_allowed_ips=true\n";
out << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
// For multi-peer use only the primary peer's own IPs to avoid routing trie conflicts.
const QList<IPAddress>& primaryIPs = config.m_primaryPeerAllowedIPRanges.isEmpty()
? config.m_allowedIPAddressRanges
: config.m_primaryPeerAllowedIPRanges;
for (const IPAddress& ip : primaryIPs) {
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
out << "allowed_ip=" << ip.toString() << "\n";
}
@@ -248,38 +244,8 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
int err = uapiErrno(uapiCommand(message));
if (err != 0) {
logger.error() << "Peer configuration failed:" << strerror(err);
return false;
}
for (const InterfaceConfig::AdditionalPeerConfig& peer : config.m_additionalPeers) {
QByteArray pubKey = QByteArray::fromBase64(peer.m_serverPublicKey.toUtf8());
QByteArray pskKey = QByteArray::fromBase64(peer.m_serverPskKey.toUtf8());
QString peerMsg;
QTextStream peerOut(&peerMsg);
peerOut << "set=1\n";
peerOut << "public_key=" << QString(pubKey.toHex()) << "\n";
if (!peer.m_serverPskKey.isEmpty()) {
peerOut << "preshared_key=" << QString(pskKey.toHex()) << "\n";
}
peerOut << "endpoint=" << peer.m_serverIpv4AddrIn << ":" << peer.m_serverPort << "\n";
peerOut << "replace_allowed_ips=true\n";
peerOut << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
for (const IPAddress& ip : peer.m_allowedIPAddressRanges) {
peerOut << "allowed_ip=" << ip.toString() << "\n";
}
if ((config.m_hopType != InterfaceConfig::MultiHopExit) && m_rtmonitor) {
m_rtmonitor->addExclusionRoute(IPAddress(peer.m_serverIpv4AddrIn));
}
int peerErr = uapiErrno(uapiCommand(peerMsg));
if (peerErr != 0) {
logger.error() << "Additional peer configuration failed:" << strerror(peerErr);
}
}
return true;
return (err == 0);
}
bool WireguardUtilsMacos::deletePeer(const InterfaceConfig& config) {
@@ -181,10 +181,7 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
out << "replace_allowed_ips=true\n";
out << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
const QList<IPAddress>& primaryIPs = config.m_primaryPeerAllowedIPRanges.isEmpty()
? config.m_allowedIPAddressRanges
: config.m_primaryPeerAllowedIPRanges;
for (const IPAddress& ip : primaryIPs) {
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
out << "allowed_ip=" << ip.toString() << "\n";
}
@@ -196,33 +193,6 @@ bool WireguardUtilsWindows::updatePeer(const InterfaceConfig& config) {
QString reply = m_tunnel.uapiCommand(message);
logger.debug() << "DATA:" << reply;
for (const InterfaceConfig::AdditionalPeerConfig& peer : config.m_additionalPeers) {
QByteArray pubKey = QByteArray::fromBase64(peer.m_serverPublicKey.toUtf8());
QByteArray pskKey = QByteArray::fromBase64(peer.m_serverPskKey.toUtf8());
QString peerMsg;
QTextStream peerOut(&peerMsg);
peerOut << "set=1\n";
peerOut << "public_key=" << QString(pubKey.toHex()) << "\n";
if (!peer.m_serverPskKey.isEmpty()) {
peerOut << "preshared_key=" << QString(pskKey.toHex()) << "\n";
}
peerOut << "endpoint=" << peer.m_serverIpv4AddrIn << ":" << peer.m_serverPort << "\n";
peerOut << "replace_allowed_ips=true\n";
peerOut << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
for (const IPAddress& ip : peer.m_allowedIPAddressRanges) {
peerOut << "allowed_ip=" << ip.toString() << "\n";
}
if (m_routeMonitor && config.m_hopType != InterfaceConfig::MultiHopExit) {
m_routeMonitor->addExclusionRoute(IPAddress(peer.m_serverIpv4AddrIn));
}
QString peerReply = m_tunnel.uapiCommand(peerMsg);
logger.debug() << "Additional peer DATA:" << peerReply;
}
return true;
}
@@ -1,7 +1,8 @@
if which apt-get > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/dpkg/lock-frontend";\
elif which dnf > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/cache/dnf/* /var/run/dnf/* /var/lib/dnf/* /var/lib/rpm/*";\
elif which yum > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/yum.pid";\
elif which zypper > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/zypp.pid";\
elif which pacman > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/pacman/db.lck";\
else echo "Packet manager not found"; echo "Internal error"; exit 1; fi;\
if command -v $LOCK_CMD > /dev/null 2>&1; then sudo $LOCK_CMD $LOCK_FILE 2>/dev/null; else echo "$LOCK_CMD not installed"; fi
if which apt-get > /dev/null 2>&1 || command -v apt-get > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/dpkg/lock-frontend";\
elif which dnf > /dev/null 2>&1 || command -v dnf > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/cache/dnf/* /var/run/dnf/* /var/lib/dnf/* /var/lib/rpm/*";\
elif which yum > /dev/null 2>&1 || command -v yum > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/yum.pid";\
elif which zypper > /dev/null 2>&1 || command -v zypper > /dev/null 2>&1; then LOCK_CMD="cat"; LOCK_FILE="/var/run/zypp.pid";\
elif which pacman > /dev/null 2>&1 || command -v pacman > /dev/null 2>&1; then LOCK_CMD="fuser"; LOCK_FILE="/var/lib/pacman/db.lck";\
else echo "Packet manager not found"; echo "Internal error"; exit 1;\
fi;\
if sudo -n which $LOCK_CMD > /dev/null 2>&1 || command -v $LOCK_CMD > /dev/null 2>&1; then sudo -n $LOCK_CMD $LOCK_FILE 2>/dev/null; else echo "$LOCK_CMD not installed"; fi
+5 -5
View File
@@ -1,8 +1,8 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); opt="--version";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); opt="--version";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); opt="--version";\
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); opt="--version";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); opt="--version";\
if pm=$(which apt-get 2>/dev/null || command -v apt-get 2>/dev/null); then opt="--version";\
elif pm=$(which dnf 2>/dev/null || command -v dnf 2>/dev/null); then opt="--version";\
elif pm=$(which yum 2>/dev/null || command -v yum 2>/dev/null); then opt="--version";\
elif pm=$(which zypper 2>/dev/null || command -v zypper 2>/dev/null); then opt="--version";\
elif pm=$(which pacman 2>/dev/null || command -v pacman 2>/dev/null); then opt="--version";\
else pm="uname"; opt="-a";\
fi;\
CUR_USER=$(whoami 2>/dev/null || echo $HOME | sed 's/.*\///');\
+28 -19
View File
@@ -1,25 +1,34 @@
if which apt-get > /dev/null 2>&1; then pm=$(which apt-get); silent_inst="-yq install --install-recommends"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
elif which dnf > /dev/null 2>&1; then pm=$(which dnf); silent_inst="-yq install"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
elif which yum > /dev/null 2>&1; then pm=$(which yum); silent_inst="-y -q install"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
elif which zypper > /dev/null 2>&1; then pm=$(which zypper); silent_inst="-nq install"; check_pkgs="-nq refresh"; docker_pkg="docker"; dist="opensuse";\
elif which pacman > /dev/null 2>&1; then pm=$(which pacman); silent_inst="-S --noconfirm --noprogressbar --quiet"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\
else echo "Packet manager not found"; exit 1; fi;\
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg";\
if pm=$(which apt-get 2>/dev/null || command -v apt-get 2>/dev/null); then silent_inst="-yq install --install-recommends"; what_pkg="-s install"; check_pkgs="-yq update"; docker_pkg="docker.io"; dist="debian";\
elif pm=$(which dnf 2>/dev/null || command -v dnf 2>/dev/null); then silent_inst="-yq install"; what_pkg="--assumeno install --setopt=tsflags=test"; check_pkgs="-yq check-update"; docker_pkg="docker"; dist="fedora";\
elif pm=$(which yum 2>/dev/null || command -v yum 2>/dev/null); then silent_inst="-y -q install"; what_pkg="--assumeno install --setopt=tsflags=test"; check_pkgs="-y -q check-update"; docker_pkg="docker"; dist="centos";\
elif pm=$(which zypper 2>/dev/null || command -v zypper 2>/dev/null); then silent_inst="-nq install"; what_pkg="--dry-run install"; check_pkgs="-nq refresh"; docker_pkg="docker"; dist="suse";\
elif pm=$(which pacman 2>/dev/null || command -v pacman 2>/dev/null); then silent_inst="-S --noconfirm --noprogressbar --quiet"; what_pkg="-Sp"; check_pkgs="-Sup"; docker_pkg="docker"; dist="archlinux";\
fi;\
echo "Dist: $dist, Packet manager: $pm, Install command: $silent_inst, What pkg command: $what_pkg, Check pkgs command: $check_pkgs, Docker pkg: $docker_pkg, Language: $LANG";\
echo $LANG | grep -qE '^(en_US.UTF-8|C.UTF-8|C)$' || export LC_ALL=C;\
if [ "$dist" = "debian" ]; then export DEBIAN_FRONTEND=noninteractive; fi;\
if ! command -v sudo > /dev/null 2>&1; then $pm $check_pkgs; $pm $silent_inst sudo; fi;\
if ! command -v fuser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst psmisc; fi;\
if ! command -v lsof > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst lsof; fi;\
if ! command -v docker > /dev/null 2>&1; then \
sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
sleep 5; sudo systemctl enable --now docker; sleep 5;\
if ! sudo -n sh -c 'command -v which > /dev/null 2>&1'; then sudo -n $pm $check_pkgs; sudo -n $pm $silent_inst which; fi;\
if ! sudo -n sh -c 'command -v fuser > /dev/null 2>&1'; then sudo -n $pm $check_pkgs; sudo -n $pm $silent_inst psmisc; fi;\
if ! sudo -n sh -c 'command -v lsof > /dev/null 2>&1'; then sudo -n $pm $check_pkgs; sudo -n $pm $silent_inst lsof; fi;\
if ! sudo -n sh -c 'command -v docker > /dev/null 2>&1'; then \
sudo -n $pm $check_pkgs;\
if ! sudo -n $pm $what_pkg $docker_pkg 2>/dev/null | grep -qi podman; then \
sudo -n $pm $silent_inst $docker_pkg;\
sleep 5; sudo -n systemctl enable --now docker; sleep 5;\
else \
echo "Container runtime is not supported";\
exit 1;\
fi;\
fi;\
if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = "Y" ]; then \
if ! command -v apparmor_parser > /dev/null 2>&1; then sudo $pm $check_pkgs; sudo $pm $silent_inst apparmor; fi;\
if [ "$(sudo -n cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = "Y" ]; then \
if ! sudo -n sh -c 'command -v apparmor_parser > /dev/null 2>&1'; then \
sudo -n $pm $check_pkgs; sudo -n $pm $silent_inst apparmor;\
fi;\
fi;\
if [ "$(systemctl is-active docker)" != "active" ]; then \
sudo $pm $check_pkgs; sudo $pm $silent_inst $docker_pkg;\
sleep 5; sudo systemctl start docker; sleep 5;\
if [ "$(sudo -n systemctl is-active docker)" != "active" ]; then \
sleep 5; sudo -n systemctl start docker; sleep 5;\
if [ "$(sudo -n systemctl is-active docker)" != "active" ]; then echo "Container runtime service not running"; fi;\
fi;\
if ! command -v sudo > /dev/null 2>&1; then echo "Failed to install sudo, command not found"; exit 1; fi;\
docker --version;\
sudo -n docker --version || docker --version;\
uname -sr
@@ -1,5 +1,6 @@
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker stop;\
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker rm -fv;\
sudo docker images -a --format table | grep amnezia | awk '{print $3, $1 ":" $2}' | xargs sudo docker rmi;\
sudo docker volume ls --format '{{.Name}}' | grep '^amnezia-' | xargs -r sudo docker volume rm -f;\
sudo docker network ls | grep amnezia-dns-net | awk '{print $1}' | xargs sudo docker network rm;\
sudo rm -frd /opt/amnezia
+1 -2
View File
@@ -1,4 +1,3 @@
sudo docker stop $CONTAINER_NAME;\
sudo docker rm -fv $CONTAINER_NAME;\
sudo docker rmi $CONTAINER_NAME;\
test "$REMOVE_CONTAINER_DATA" = "1" && sudo docker volume rm -f ${CONTAINER_NAME}-data 2>/dev/null || true
sudo docker rmi $CONTAINER_NAME;
@@ -1,9 +1,5 @@
#include "subscriptionUiController.h"
#ifdef Q_OS_IOS
#include "platforms/ios/ios_controller.h"
#endif
#include "amneziaApplication.h"
#include "core/configurators/wireguardConfigurator.h"
#include "core/utils/serverConfigUtils.h"
@@ -479,8 +475,7 @@ bool SubscriptionUiController::deactivateExternalDevice(const QString &serverId,
void SubscriptionUiController::validateConfig()
{
const QString serverId = m_serversController->getDefaultServerId();
if (!serverId.isEmpty() && m_serversController->isLegacyApiV1Server(serverId)) {
emit unsupportedConnectDrawerRequested();
if (serverId.isEmpty()) {
emit configValidated(false);
return;
}
@@ -8,6 +8,8 @@
#include "amneziaApplication.h"
#include "core/controllers/serversController.h"
#include "core/models/containerConfig.h"
#include "core/utils/containerEnum.h"
ConnectionUiController::ConnectionUiController(ConnectionController* connectionController,
ServersController* serversController,
@@ -33,7 +35,7 @@ void ConnectionUiController::openConnection()
ErrorCode errorCode = m_connectionController->openConnection(serverId);
if (errorCode != ErrorCode::NoError) {
emit connectionErrorOccurred(errorCode);
notifyConnectionBlocked(errorCode);
return;
}
}
@@ -130,10 +132,36 @@ void ConnectionUiController::toggleConnection()
} else if (isConnected()) {
closeConnection();
} else {
const QString serverId = m_serversController->getDefaultServerId();
if (serverId.isEmpty()) {
return;
}
const ErrorCode errorCode = m_connectionController->isConnectionSupported(serverId);
if (errorCode != ErrorCode::NoError) {
notifyConnectionBlocked(errorCode);
return;
}
emit prepareConfig();
}
}
void ConnectionUiController::notifyConnectionBlocked(ErrorCode errorCode)
{
if (errorCode == ErrorCode::LegacyApiV1NotSupportedError) {
emit unsupportedConnectDrawerRequested();
return;
}
if (errorCode == ErrorCode::NoInstalledContainersError) {
emit noInstalledContainers();
return;
}
emit connectionErrorOccurred(errorCode);
}
bool ConnectionUiController::isConnectionInProgress() const
{
return m_isConnectionInProgress;
@@ -143,3 +171,32 @@ bool ConnectionUiController::isConnected() const
{
return m_isConnected;
}
bool ConnectionUiController::isRevokeBlockedDuringActiveConnection(const QString &serverId, int containerIndex,
const QString &clientId) const
{
if (clientId.isEmpty() || (!isConnected() && !isConnectionInProgress())) {
return false;
}
if (m_serversController->getDefaultServerId() != serverId) {
return false;
}
if (static_cast<int>(m_serversController->getDefaultContainer(serverId)) != containerIndex) {
return false;
}
const auto adminConfig = m_serversController->selfHostedAdminConfig(serverId);
if (!adminConfig.has_value()) {
return false;
}
const QString connectionClientId =
adminConfig->containerConfig(static_cast<DockerContainer>(containerIndex)).protocolConfig.clientId();
if (connectionClientId.isEmpty()) {
return false;
}
return connectionClientId == clientId || connectionClientId.contains(clientId);
}
@@ -35,6 +35,8 @@ public slots:
void openConnection();
void closeConnection();
bool isRevokeBlockedDuringActiveConnection(const QString &serverId, int containerIndex, const QString &clientId) const;
ErrorCode getLastConnectionError();
void onConnectionStateChanged(Vpn::ConnectionState state);
@@ -48,9 +50,12 @@ signals:
void connectButtonClicked();
void preparingConfig();
void prepareConfig();
void unsupportedConnectDrawerRequested();
void noInstalledContainers();
private:
Vpn::ConnectionState getCurrentConnectionState();
void notifyConnectionBlocked(ErrorCode errorCode);
ConnectionController* m_connectionController;
ServersController* m_serversController;
@@ -75,13 +75,7 @@ InstallUiController::InstallUiController(InstallController *installController,
m_connectionController(connectionController)
{
connect(m_installController, &InstallController::configValidated, this, &InstallUiController::configValidated);
connect(m_installController, &InstallController::validationErrorOccurred, this, [this](ErrorCode errorCode) {
if (errorCode == ErrorCode::NoInstalledContainersError) {
emit noInstalledContainers();
} else {
emit installationErrorOccurred(errorCode);
}
});
connect(m_installController, &InstallController::validationErrorOccurred, this, &InstallUiController::installationErrorOccurred);
}
InstallUiController::~InstallUiController()
@@ -217,15 +211,13 @@ void InstallUiController::scanServerForInstalledContainers(const QString &server
emit installationErrorOccurred(errorCode);
}
void InstallUiController::updateContainer(const QString &serverId, int containerIndex, int protocolIndex, bool closePage)
bool InstallUiController::buildContainerConfigFromModel(int containerIndex, int protocolIndex, ContainerConfig &containerConfig)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
Proto protocolType = static_cast<Proto>(protocolIndex);
ContainerConfig containerConfig;
containerConfig.container = container;
switch (protocolType) {
case Proto::Awg: {
containerConfig.protocolConfig = m_awgConfigModel->getProtocolConfig();
@@ -271,6 +263,41 @@ void InstallUiController::updateContainer(const QString &serverId, int container
}
#endif
default:
return false;
}
return true;
}
void InstallUiController::updateClientConfig(const QString &serverId, int containerIndex, int protocolIndex, bool closePage)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
Proto protocolType = static_cast<Proto>(protocolIndex);
ContainerConfig containerConfig;
if (!buildContainerConfigFromModel(containerIndex, protocolIndex, containerConfig)) {
return;
}
ErrorCode errorCode = m_installController->updateClientConfig(serverId, container, containerConfig);
if (errorCode == ErrorCode::NoError) {
ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverId, container);
m_protocolModel->updateModel(updatedConfig);
updateProtocolConfigModel(serverId, static_cast<int>(container), static_cast<int>(protocolType));
emit updateContainerFinished(tr("Settings updated successfully"), closePage);
return;
}
emit installationErrorOccurred(errorCode);
}
void InstallUiController::updateServerConfig(const QString &serverId, int containerIndex, int protocolIndex, bool closePage)
{
DockerContainer container = static_cast<DockerContainer>(containerIndex);
Proto protocolType = static_cast<Proto>(protocolIndex);
ContainerConfig containerConfig;
if (!buildContainerConfigFromModel(containerIndex, protocolIndex, containerConfig)) {
return;
}
ContainerConfig oldContainerConfig = m_serversController->getContainerConfig(serverId, container);
@@ -305,13 +332,13 @@ void InstallUiController::updateContainer(const QString &serverId, int container
QFuture<ErrorCode> future =
QtConcurrent::run([installController, serverId, container, oldConfigCopy,
newConfigCopy]() mutable -> ErrorCode {
return installController->updateContainer(serverId, container, oldConfigCopy, newConfigCopy);
return installController->updateServerConfig(serverId, container, oldConfigCopy, newConfigCopy);
});
watcher->setFuture(future);
return;
}
ErrorCode errorCode = m_installController->updateContainer(serverId, container, oldContainerConfig, containerConfig);
ErrorCode errorCode = m_installController->updateServerConfig(serverId, container, oldContainerConfig, containerConfig);
if (errorCode == ErrorCode::NoError) {
ContainerConfig updatedConfig = m_serversController->getContainerConfig(serverId, container);
@@ -64,7 +64,8 @@ public slots:
void scanServerForInstalledContainers(const QString &serverId);
void updateContainer(const QString &serverId, int containerIndex, int protocolIndex, bool closePage = true);
void updateServerConfig(const QString &serverId, int containerIndex, int protocolIndex, bool closePage = true);
void updateClientConfig(const QString &serverId, int containerIndex, int protocolIndex, bool closePage = true);
void removeServer(const QString &serverId);
void rebootServer(const QString &serverId);
@@ -132,7 +133,6 @@ signals:
void cachedProfileCleared(const QString &message);
void apiConfigRemoved(const QString &message);
void noInstalledContainers();
void configValidated(bool isValid);
private:
@@ -162,6 +162,8 @@ private:
QString m_privateKeyPassphrase;
void updateProtocolConfigModel(const QString &serverId, int containerIndex, int protocolIndex);
bool buildContainerConfigFromModel(int containerIndex, int protocolIndex, ContainerConfig &containerConfig);
};
#endif // INSTALLUICONTROLLER_H
+18 -13
View File
@@ -156,7 +156,17 @@ void ServersUiController::updateModel()
m_serversModel->updateModel(m_orderedServerDescriptions, defaultServerId);
updateContainersModel();
if (!m_processedServerId.isEmpty()) {
if (isServerFromApi(m_processedServerId)) {
const auto &description = serverDescriptionById(m_processedServerId);
if (description.isApiV2 && description.isCountrySelectionAvailable
&& !description.apiAvailableCountries.isEmpty()) {
emit updateApiCountryModel();
}
} else {
updateContainersModel();
}
}
updateDefaultServerContainersModel();
if (hadServersFromGatewayBefore != hasServersFromGatewayNow) {
@@ -350,19 +360,14 @@ void ServersUiController::setProcessedServerId(const QString &serverId)
m_processedServerId = normalizedServerId;
if (newIndex >= 0) {
updateContainersModel();
for (const auto &description : m_orderedServerDescriptions) {
if (description.serverId != normalizedServerId) {
continue;
if (isServerFromApi(m_processedServerId)) {
const auto &description = serverDescriptionById(m_processedServerId);
if (description.isApiV2 && description.isCountrySelectionAvailable
&& !description.apiAvailableCountries.isEmpty()) {
emit updateApiCountryModel();
}
if (description.isApiV2) {
if (description.isCountrySelectionAvailable && !description.apiAvailableCountries.isEmpty()) {
emit updateApiCountryModel();
}
emit updateApiServicesModel();
}
break;
} else {
updateContainersModel();
}
}
@@ -113,7 +113,6 @@ signals:
void processedContainerIndexChanged(int index);
void hasServersFromGatewayApiChanged();
void updateApiCountryModel();
void updateApiServicesModel();
public:
void updateModel();
@@ -22,12 +22,10 @@
SettingsUiController::SettingsUiController(SettingsController* settingsController,
ServersController* serversController,
LanguageUiController* languageUiController,
QObject *parent)
: QObject(parent),
m_settingsController(settingsController),
m_serversController(serversController),
m_languageUiController(languageUiController)
m_serversController(serversController)
{
#ifdef Q_OS_ANDROID
connect(AndroidController::instance(), &AndroidController::notificationStateChanged, this, &SettingsUiController::onNotificationStateChanged);
@@ -157,13 +155,13 @@ void SettingsUiController::restoreAppConfigFromData(const QByteArray &data)
{
ErrorCode errorCode = m_settingsController->restoreAppConfigFromData(data);
if (errorCode == ErrorCode::NoError) {
emit appLanguageChanged(
static_cast<LanguageSettings::AvailableLanguageEnum>(m_languageUiController->getCurrentLanguageIndex()));
emit appLanguageChanged();
bool amneziaDnsEnabled = m_settingsController->isAmneziaDnsEnabled();
emit amneziaDnsToggled(amneziaDnsEnabled);
emit restoreBackupFinished();
emit autoStartChanged();
emit startMinimizedChanged();
} else {
emit errorOccurred(errorCode);
@@ -178,6 +176,7 @@ QString SettingsUiController::getAppVersion()
void SettingsUiController::clearSettings()
{
m_settingsController->clearSettings();
emit autoStartChanged();
emit startMinimizedChanged();
emit resetLanguageToSystem();
@@ -206,9 +205,8 @@ bool SettingsUiController::isAutoStartEnabled()
void SettingsUiController::toggleAutoStart(bool enable)
{
m_settingsController->toggleAutoStart(enable);
if (!enable) {
emit startMinimizedChanged();
}
emit autoStartChanged();
emit startMinimizedChanged();
}
bool SettingsUiController::isStartMinimizedEnabled()
+3 -5
View File
@@ -5,8 +5,6 @@
#include "core/controllers/settingsController.h"
#include "core/controllers/serversController.h"
#include "ui/controllers/languageUiController.h"
#include "ui/models/languageModel.h"
#include "core/utils/errorCodes.h"
#include "core/utils/routeModes.h"
#include "core/utils/commonStructs.h"
@@ -17,7 +15,6 @@ class SettingsUiController : public QObject
public:
explicit SettingsUiController(SettingsController* settingsController,
ServersController* serversController,
LanguageUiController* languageUiController,
QObject *parent = nullptr);
Q_PROPERTY(QString primaryDns READ getPrimaryDns WRITE setPrimaryDns NOTIFY primaryDnsChanged)
@@ -32,6 +29,7 @@ public:
Q_PROPERTY(bool isDevGatewayEnv READ isDevGatewayEnv WRITE toggleDevGatewayEnv NOTIFY devGatewayEnvChanged)
Q_PROPERTY(bool isHomeAdLabelVisible READ isHomeAdLabelVisible NOTIFY isHomeAdLabelVisibleChanged)
Q_PROPERTY(bool autoStartEnabled READ isAutoStartEnabled NOTIFY autoStartChanged)
Q_PROPERTY(bool startMinimized READ isStartMinimizedEnabled NOTIFY startMinimizedChanged)
public slots:
@@ -122,7 +120,7 @@ signals:
void loggingDisableByWatcher();
void appLanguageChanged(const LanguageSettings::AvailableLanguageEnum language);
void appLanguageChanged();
void resetLanguageToSystem();
void onNotificationStateChanged();
@@ -135,12 +133,12 @@ signals:
void activityResumed();
void isHomeAdLabelVisibleChanged(bool visible);
void autoStartChanged();
void startMinimizedChanged();
private:
SettingsController* m_settingsController;
ServersController* m_serversController;
LanguageUiController* m_languageUiController;
};
#endif
+1 -1
View File
@@ -30,7 +30,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const
switch (role) {
case SubscriptionStatusRole: {
if (m_accountInfoData.configType == serverConfigUtils::ConfigType::AmneziaFreeV3) {
return tr("Active");
return QStringLiteral("<p><a style=\"color: #28c840;\">%1</a>").arg(tr("Active"));
}
return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate)
@@ -27,6 +27,7 @@ QVariant ClientManagementModel::data(const QModelIndex &index, int role) const
auto userData = client.value(configKey::userData).toObject();
switch (role) {
case ClientIdRole: return client.value(configKey::clientId).toString();
case ClientNameRole: return userData.value(configKey::clientName).toString();
case CreationDateRole: return userData.value(configKey::creationDate).toString();
case LatestHandshakeRole: return userData.value(configKey::latestHandshake).toString();
@@ -62,6 +63,7 @@ void ClientManagementModel::updateClientName(int row, const QString &newName)
QHash<int, QByteArray> ClientManagementModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[ClientIdRole] = "clientId";
roles[ClientNameRole] = "clientName";
roles[CreationDateRole] = "creationDate";
roles[LatestHandshakeRole] = "latestHandshake";
+2 -1
View File
@@ -10,7 +10,8 @@ class ClientManagementModel : public QAbstractListModel
public:
enum Roles {
ClientNameRole = Qt::UserRole + 1,
ClientIdRole = Qt::UserRole + 1,
ClientNameRole,
CreationDateRole,
LatestHandshakeRole,
DataReceivedRole,
+4
View File
@@ -23,6 +23,10 @@ public:
Q_INVOKABLE int containerFromString(const QString &container) const {
return static_cast<int>(amnezia::ContainerUtils::containerFromString(container));
}
Q_INVOKABLE bool isUnsupportedContainer(int containerIndex) const {
return amnezia::ContainerUtils::isUnsupportedContainer(static_cast<amnezia::DockerContainer>(containerIndex));
}
};
#endif // CONTAINERPROPS_H
+4 -1
View File
@@ -67,6 +67,7 @@ QVariant ContainersModel::data(const QModelIndex &index, int role) const
case IsCurrentlyProcessedRole: return container == static_cast<DockerContainer>(m_processedContainerIndex);
case IsSupportedRole: return ContainerUtils::isSupportedByCurrentPlatform(container);
case IsShareableRole: return ContainerUtils::isShareable(container);
case IsUnsupportedContainerRole: return ContainerUtils::isUnsupportedContainer(container);
case IsVpnContainerRole: return ContainerUtils::containerService(container) == ServiceType::Vpn;
case IsServiceContainerRole: return ContainerUtils::containerService(container) == ServiceType::Other;
case IsIpsecRole: return container == DockerContainer::Ipsec;
@@ -142,7 +143,8 @@ bool ContainersModel::hasInstalledProtocols()
bool ContainersModel::isInstallationAllowed(DockerContainer container)
{
return container != DockerContainer::Awg;
return container != DockerContainer::Awg
&& !ContainerUtils::isUnsupportedContainer(container);
}
void ContainersModel::openContainerSettings(int containerIndex)
@@ -176,6 +178,7 @@ QHash<int, QByteArray> ContainersModel::roleNames() const
roles[IsCurrentlyProcessedRole] = "isCurrentlyProcessed";
roles[IsSupportedRole] = "isSupported";
roles[IsShareableRole] = "isShareable";
roles[IsUnsupportedContainerRole] = "isUnsupportedContainer";
roles[IsInstallationAllowedRole] = "isInstallationAllowed";
roles[InstallPageOrderRole] = "installPageOrder";
+2
View File
@@ -39,6 +39,8 @@ public:
IsSupportedRole,
IsShareableRole,
IsUnsupportedContainerRole,
InstallPageOrderRole,
// Container type check roles
@@ -56,14 +56,17 @@ ListViewType {
return
}
if (checked) {
containersDropDown.closeTriggered()
ServersUiController.setDefaultContainer(ServersUiController.defaultServerId, proxyDefaultServerContainersModel.mapToSource(index))
} else {
ServersUiController.processedContainerIndex = proxyDefaultServerContainersModel.mapToSource(index)
var containerIndex = proxyDefaultServerContainersModel.mapToSource(index)
if (!isInstalled) {
ServersUiController.processedContainerIndex = containerIndex
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
containersDropDown.closeTriggered()
return
}
containersDropDown.closeTriggered()
ServersUiController.setDefaultContainer(ServersUiController.defaultServerId, containerIndex)
}
MouseArea {
@@ -5,7 +5,6 @@ import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerProps 1.0
import "../Controls2"
import "../Controls2/TextTypes"
+30 -9
View File
@@ -6,8 +6,36 @@ Menu {
popupType: Popup.Native
onAboutToShow: blocker.enabled = true
onClosed: blocker.enabled = false
property Item inputBlocker: null
Component {
id: inputBlockerComponent
MouseArea {
anchors.fill: parent
preventStealing: true
}
}
onAboutToShow: {
if (!textObj || !textObj.window) {
return
}
const contentItem = textObj.window.contentItem
if (!inputBlocker) {
inputBlocker = inputBlockerComponent.createObject(contentItem)
} else {
inputBlocker.parent = contentItem
}
}
onClosed: {
if (inputBlocker) {
inputBlocker.destroy()
inputBlocker = null
}
}
MenuItem {
text: qsTr("C&ut")
@@ -31,11 +59,4 @@ Menu {
enabled: textObj.length > 0
onTriggered: textObj.selectAll()
}
MouseArea {
id: blocker
z: 2
enabled: false
preventStealing: true
}
}
+2 -2
View File
@@ -25,8 +25,8 @@ PageType {
filters: [
ValueFilter {
roleName: "isCurrentlyProcessed"
value: true
roleName: "serverId"
value: ServersUiController.processedServerId
}
]
}
@@ -440,8 +440,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Awg)
InstallController.updateClientConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Awg)
}
var noButtonFunction = function() {}
@@ -561,7 +561,7 @@ PageType {
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Awg)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Awg)
}
var noButtonFunction = function() {}
@@ -434,7 +434,7 @@ PageType {
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.OpenVpn)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.OpenVpn)
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
@@ -128,8 +128,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard)
InstallController.updateClientConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard)
}
var noButtonFunction = function() {}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@@ -129,7 +129,7 @@ PageType {
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.WireGuard)
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
@@ -112,7 +112,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {
@@ -279,7 +279,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {
@@ -17,6 +17,10 @@ import "../Components"
PageType {
id: root
enableTimer: false
property bool portDirty: false
function formatTransport(value) {
if (value === "raw") return "RAW (TCP)"
if (value === "xhttp") return "XHTTP"
@@ -39,8 +43,8 @@ PageType {
anchors.right: parent.right
anchors.topMargin: 20 + PageController.safeAreaTopMargin
onFocusChanged: {
if (this.activeFocus) {
onActiveFocusChanged: {
if (backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
@@ -60,8 +64,6 @@ PageType {
delegate: ColumnLayout {
width: listView.width
property alias focusItemId: textFieldWithHeaderType.textField
spacing: 0
Text {
@@ -107,13 +109,32 @@ PageType {
Layout.rightMargin: 16
enabled: listView.enabled
headerText: qsTr("Port")
textField.text: port
Binding {
target: textFieldWithHeaderType.textField
property: "text"
value: port
when: !textFieldWithHeaderType.textField.activeFocus
restoreMode: Binding.RestoreNone
}
textField.maximumLength: 5
textField.validator: IntValidator {
bottom: 1; top: 65535
}
textField.onActiveFocusChanged: {
if (textField.activeFocus && textField.text === "" && port !== "") {
textField.text = port
}
}
textField.onTextChanged: {
root.portDirty = (textField.text !== port)
}
textField.onEditingFinished: {
if (textField.text !== port) port = textField.text
if (textField.text !== port) {
port = textField.text
}
root.portDirty = false
}
checkEmptyText: true
}
@@ -172,9 +193,8 @@ PageType {
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: listView.enabled
&& (XrayConfigModel.hasUnsavedChanges
|| textFieldWithHeaderType.textField.text !== port)
enabled: visible && textFieldWithHeaderType.errorText === ""
&& (XrayConfigModel.hasUnsavedChanges || root.portDirty)
enabled: visible && textFieldWithHeaderType.textField.text !== ""
text: qsTr("Save")
onClicked: function() {
forceActiveFocus()
@@ -193,7 +213,7 @@ PageType {
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function() {
if (!GC.isMobile()) saveButton.forceActiveFocus()
@@ -742,7 +742,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {
@@ -95,7 +95,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {
@@ -211,7 +211,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {
@@ -208,7 +208,7 @@ PageType {
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedIndex, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Xray)
}
var noButtonFunction = function () {
if (typeof GC !== "undefined" && !GC.isMobile()) {
@@ -179,7 +179,7 @@ PageType {
function mtProxyScheduleUpdate(closePage) {
var cp = closePage === undefined ? false : closePage
Qt.callLater(function () {
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.MtProxy, cp)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.MtProxy, cp)
})
}
@@ -285,7 +285,7 @@ PageType {
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling)
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Socks5Proxy)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Socks5Proxy)
tempPort = portTextField.textField.text
tempUsername = usernameTextField.textField.text
tempPassword = passwordTextField.textField.text
@@ -154,7 +154,7 @@ PageType {
function telemtScheduleUpdate(closePage) {
var cp = closePage === undefined ? false : closePage
Qt.callLater(function () {
InstallController.updateContainer(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Telemt, cp)
InstallController.updateServerConfig(ServersUiController.processedServerId, ServersUiController.processedContainerIndex, ProtocolEnum.Telemt, cp)
})
}
@@ -100,6 +100,12 @@ PageType {
onLinkActivated: Qt.openUrlExternally(link)
textFormat: Text.RichText
text: qsTr("Use <a href=\"https://www.torproject.org/download/\" style=\"color: #FBB26A;\">Tor Browser</a> to open this URL.")
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
}
}
ParagraphTextType {
@@ -30,6 +30,16 @@ PageType {
root.isInAppPurchase = ApiAccountInfoModel.data("isInAppPurchase")
}
function selectConnectionCountry(countryIndex, countryCode, countryName) {
if (countryIndex === ApiCountryModel.currentIndex) {
return
}
PageController.showBusyIndicator(true)
SubscriptionUiController.updateServiceFromGateway(ServersUiController.processedServerId, countryCode, countryName)
PageController.showBusyIndicator(false)
}
Component.onCompleted: {
root.updateSubscriptionState()
}
@@ -83,7 +93,7 @@ PageType {
model: ApiCountryModel
currentIndex: 0
currentIndex: ApiCountryModel.currentIndex
ButtonGroup {
id: containersRadioButtonGroup
@@ -204,15 +214,7 @@ PageType {
return
}
if (index !== ApiCountryModel.currentIndex) {
PageController.showBusyIndicator(true)
var prevIndex = ApiCountryModel.currentIndex
ApiCountryModel.currentIndex = index
if (!SubscriptionUiController.updateServiceFromGateway(ServersUiController.processedServerId, countryCode, countryName)) {
ApiCountryModel.currentIndex = prevIndex
}
PageController.showBusyIndicator(false)
}
root.selectConnectionCountry(index, countryCode, countryName)
}
Keys.onEnterPressed: {
@@ -108,9 +108,9 @@ PageType {
text: qsTr("Auto start")
descriptionText: qsTr("Launch the application every time the device is starts")
checked: SettingsController.isAutoStartEnabled()
checked: SettingsController.autoStartEnabled
onToggled: function() {
if (checked !== SettingsController.isAutoStartEnabled()) {
if (checked !== SettingsController.autoStartEnabled) {
SettingsController.toggleAutoStart(checked)
}
}
@@ -154,10 +154,10 @@ PageType {
text: qsTr("Start minimized")
descriptionText: qsTr("Launch application minimized (works with autostart option turned on)")
enabled: SettingsController.isAutoStartEnabled()
enabled: SettingsController.autoStartEnabled
opacity: enabled ? 1.0 : 0.5
checked: SettingsController.isAutoStartEnabled() && SettingsController.startMinimized
checked: SettingsController.autoStartEnabled && SettingsController.startMinimized
onToggled: function() {
if (checked !== SettingsController.startMinimized) {
SettingsController.toggleStartMinimized(checked)
@@ -166,7 +166,7 @@ PageType {
}
DividerType {
visible: !GC.isMobile()
visible: !GC.isMobile() && ServersUiController.hasServersFromGatewayApi
}
SwitcherType {
@@ -36,17 +36,6 @@ PageType {
function onRebootServerFinished(finishedMessage) {
PageController.showNotificationMessage(finishedMessage)
}
function onRemoveAllContainersFinished(finishedMessage) {
PageController.closePage() // close deInstalling page
PageController.showNotificationMessage(finishedMessage)
}
function onRemoveContainerFinished(finishedMessage) {
PageController.closePage() // close deInstalling page
PageController.closePage() // close page with remove button
PageController.showNotificationMessage(finishedMessage)
}
}
Connections {
@@ -17,7 +17,8 @@ import "../Components"
PageType {
id: root
property bool isClearCacheVisible: ServersUiController.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ServersUiController.processedContainerIndex)
property bool isUnsupportedContainer: ContainerProps.isUnsupportedContainer(ServersUiController.processedContainerIndex)
property bool isClearCacheVisible: !isUnsupportedContainer && ServersUiController.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ServersUiController.processedContainerIndex)
BackButtonType {
id: backButton
@@ -52,10 +53,11 @@ PageType {
Layout.bottomMargin: 32
headerText: ContainersModel.getProcessedContainerName() + qsTr(" settings")
descriptionText: root.isUnsupportedContainer ? qsTr("This protocol is no longer supported.") : ""
}
}
model: ProtocolsModel
model: root.isUnsupportedContainer ? null : ProtocolsModel
delegate: ColumnLayout {
id: delegateContent
@@ -29,6 +29,10 @@ PageType {
ValueFilter {
roleName: "isInstallationAllowed"
value: true
},
ValueFilter {
roleName: "isUnsupportedContainer"
value: false
}
]
sorters: RoleSorter {
+20 -7
View File
@@ -382,6 +382,10 @@ PageType {
ValueFilter {
roleName: "isShareable"
value: true
},
ValueFilter {
roleName: "isUnsupportedContainer"
value: false
}
]
}
@@ -396,9 +400,19 @@ PageType {
target: serverSelector
function onServerSelectorIndexChanged() {
var defaultContainer = proxyContainersModel.mapFromSource(ServersUiController.serverDefaultContainer(ServersUiController.processedServerId))
if (!proxyContainersModel.count) {
root.shareButtonEnabled = false
return
}
var defaultContainer = proxyContainersModel.mapFromSource(
ServersUiController.serverDefaultContainer(ServersUiController.processedServerId))
if (defaultContainer < 0) {
defaultContainer = 0
}
containerSelectorListView.selectedIndex = defaultContainer
containerSelectorListView.positionViewAtIndex(selectedIndex, ListView.Beginning)
containerSelectorListView.positionViewAtIndex(defaultContainer, ListView.Beginning)
containerSelectorListView.triggerCurrentItem()
}
}
@@ -837,11 +851,10 @@ PageType {
var noButtonFunction = function() {
}
var isActiveConfigForCurrentClient = ServersUiController.isDefaultServerCurrentlyProcessed()
&& ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId) === ServersUiController.processedContainerIndex
if ((ConnectionController.isConnectionInProgress || ConnectionController.isConnected)
&& isActiveConfigForCurrentClient) {
if (ConnectionController.isRevokeBlockedDuringActiveConnection(
ServersUiController.processedServerId,
ServersUiController.processedContainerIndex,
clientId)) {
PageController.showNotificationMessage("Unable to revoke current config during active connection")
} else {
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
+25 -4
View File
@@ -105,6 +105,19 @@ PageType {
}
}
Connections {
objectName: "connectionControllerConnections"
target: ConnectionController
function onNoInstalledContainers() {
PageController.setTriggeredByConnectButton(true)
ServersUiController.setProcessedServerId(ServersUiController.defaultServerId)
PageController.goToPage(PageEnum.PageSetupWizardEasy)
}
}
Connections {
objectName: "installControllerConnections"
@@ -153,11 +166,19 @@ PageType {
PageController.showNotificationMessage(finishedMessage)
}
function onNoInstalledContainers() {
PageController.setTriggeredByConnectButton(true)
function onRemoveAllContainersFinished(finishedMessage) {
if (tabBarStackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageDeinstalling)) {
PageController.closePage()
}
PageController.showNotificationMessage(finishedMessage)
}
ServersUiController.setProcessedServerId(ServersUiController.defaultServerId)
PageController.goToPage(PageEnum.PageSetupWizardEasy)
function onRemoveContainerFinished(finishedMessage) {
if (tabBarStackView.currentItem.objectName === PageController.getPagePath(PageEnum.PageDeinstalling)) {
PageController.closePage()
}
PageController.closePage()
PageController.showNotificationMessage(finishedMessage)
}
}
+8
View File
@@ -234,6 +234,8 @@ Window {
DrawerType2 {
id: privateKeyPassphraseDrawer
property bool isCloseByUser: false
anchors.fill: parent
expandedHeight: root.height * 0.35 + PageController.safeAreaBottomMargin + PageController.imeHeight
@@ -253,6 +255,11 @@ Window {
}
function onAboutToHide() {
if (privateKeyPassphraseDrawer.isCloseByUser === false) {
privateKeyPassphraseDrawer.isCloseByUser = true
PageController.passphraseRequestDrawerClosed("")
}
if (passphrase.textField.text !== "") {
PageController.showBusyIndicator(true)
}
@@ -293,6 +300,7 @@ Window {
text: qsTr("Save")
clickedFunc: function() {
privateKeyPassphraseDrawer.isCloseByUser = true
privateKeyPassphraseDrawer.closeTriggered()
PageController.passphraseRequestDrawerClosed(passphrase.textField.text)
}
+4 -4
View File
@@ -19,12 +19,12 @@ class AmneziaVPN(ConanFile):
if has_service:
if os == "Windows":
self.requires("awg-windows/0.1.8")
self.requires("awg-windows/0.1.9")
self.requires("tap-windows6/9.27.0")
self.requires("win-split-tunnel/1.2.5.0")
self.requires("wintun/0.14.1")
else:
self.requires("awg-go/0.2.16")
self.requires("awg-go/0.2.18")
self.requires("amnezia-xray-bindings/1.1.0")
self.requires("tun2socks/2.6.0")
@@ -32,13 +32,13 @@ class AmneziaVPN(ConanFile):
self.requires("v2ray-rules-dat/202603162227")
if has_ne:
self.requires("awg-apple/2.0.1")
self.requires("awg-apple/2.0.2")
self.requires("hev-socks5-tunnel/2.15.0", options={"as_framework": True})
self.requires("openvpnadapter/1.0.0")
if os == "Android":
self.requires("amnezia-libxray/1.0.0")
self.requires("awg-android/1.1.7")
self.requires("awg-android/2.0.1")
self.requires("openvpn-pt-android/1.0.0")
# expicitly use libssh@amnezia to prevent it from being downloaded from conan-center
+1 -1
View File
@@ -9,7 +9,7 @@ import platform
class AwgAndroid(ConanFile):
name = "awg-android"
version = "1.1.7"
version = "2.0.1"
settings = "os", "arch", "build_type", "compiler"
def configure(self):
+2 -2
View File
@@ -9,7 +9,7 @@ import os
class AwgApple(ConanFile):
name = "awg-apple"
version = "2.0.1"
version = "2.0.2"
settings = "os", "arch", "compiler"
@property
@@ -39,7 +39,7 @@ class AwgApple(ConanFile):
def source(self):
get(self, f"https://github.com/amnezia-vpn/amneziawg-apple/archive/refs/tags/v{self.version}.zip",
sha256="9fe4f8cfbb6a751558b54b7979db3a5ea46e49731912aae99f093e84a1433e97", strip_root=True
sha256="a04f49eac9f82bbf5dd9031bab188d44de2b3482efde1b6e970821de1d5a3c5d", strip_root=True
)
def generate(self):
+2 -2
View File
@@ -8,7 +8,7 @@ import os
class AwgGo(ConanFile):
name = "awg-go"
version = "0.2.16"
version = "0.2.18"
package_type = "application"
settings = "os", "arch"
@@ -42,7 +42,7 @@ class AwgGo(ConanFile):
def source(self):
get(self, f"https://github.com/amnezia-vpn/amneziawg-go/archive/refs/tags/v{self.version}.zip",
sha256="34da7d4189f215f3930de441548bc2a0c89d54d347a4fb85cb9c715fce6413aa", strip_root=True
sha256="58eefbd012e79bd1525f0e02d748979e9480acc1a339df8ceb3b9ffafcedb1ba", strip_root=True
)
def generate(self):
+2 -2
View File
@@ -8,7 +8,7 @@ import os
class AwgWindows(ConanFile):
name = "awg-windows"
version = "0.1.8"
version = "0.1.9"
settings = "os", "arch"
@property
@@ -63,7 +63,7 @@ class AwgWindows(ConanFile):
def source(self):
get(self, f"https://github.com/amnezia-vpn/amneziawg-windows/archive/refs/tags/v{self.version}.zip",
sha256="1de472832b332515c96cdf14ea887edde42ed7ad173675280c51baa9a3ef62f2", strip_root=True)
sha256="5c29a75cb2beae291cc51b64840a39f838da5f300b9e956f7964813a687ec74c", strip_root=True)
def generate(self):
tc = AutotoolsToolchain(self)
+3
View File
@@ -650,6 +650,9 @@ class OpenSSLConan(ConanFile):
if self._use_nmake:
self.cpp_info.components["ssl"].libs = ["libssl"]
self.cpp_info.components["crypto"].libs = ["libcrypto"]
elif self.settings.os == "Android" and self.options.shared:
self.cpp_info.components["ssl"].libs = ["ssl_3"]
self.cpp_info.components["crypto"].libs = ["crypto_3"]
else:
self.cpp_info.components["ssl"].libs = ["ssl"]
self.cpp_info.components["crypto"].libs = ["crypto"]
+1 -1
View File
@@ -28,7 +28,7 @@ class Openvpn(ConanFile):
def build_requirements(self):
if self._is_windows:
self.tool_requires("cmake/[>=3.14 <4]")
self.tool_requires("cmake/[>=4.2]")
else:
self.tool_requires("libtool/2.4.7")
self.tool_requires("automake/1.16.5")